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

网络编程

开发平台:

Unix_Linux

  1. }
  2. /* Mail message n is expunged
  3.  * Accepts: mail stream
  4.  *     message #
  5.  */
  6. void mail_expunged (MAILSTREAM *stream,unsigned long msgno)
  7. {
  8.   MESSAGECACHE *elt = (MESSAGECACHE *) (*mailcache) (stream,msgno,CH_ELT);
  9. /* notify main program of change */
  10.   if (!stream->silent) mm_expunged (stream,msgno);
  11.   if (elt) { /* if an element is there */
  12.     elt->msgno = 0; /* invalidate its message number and free */
  13.     (*mailcache) (stream,msgno,CH_FREE);
  14.     (*mailcache) (stream,msgno,CH_FREESORTCACHE);
  15.   }
  16. /* expunge the slot */
  17.   (*mailcache) (stream,msgno,CH_EXPUNGE);
  18.   --stream->nmsgs; /* update stream status */
  19.   if (stream->msgno) { /* have stream pointers? */
  20. /* make sure the short cache is nuked */
  21.     if (stream->scache) mail_gc (stream,GC_ENV | GC_TEXTS);
  22.     else stream->msgno = 0; /* make sure invalidated in any case */
  23.   }
  24. }
  25. /* Mail stream status routines */
  26. /* Mail lock stream
  27.  * Accepts: mail stream
  28.  */
  29. void mail_lock (MAILSTREAM *stream)
  30. {
  31.   if (stream->lock) fatal ("Lock when already locked");
  32.   else stream->lock = T; /* lock stream */
  33. }
  34. /* Mail unlock stream
  35.  * Accepts: mail stream
  36.  */
  37.  
  38. void mail_unlock (MAILSTREAM *stream)
  39. {
  40.   if (!stream->lock) fatal ("Unlock when not locked");
  41.   else stream->lock = NIL; /* unlock stream */
  42. }
  43. /* Mail turn on debugging telemetry
  44.  * Accepts: mail stream
  45.  */
  46. void mail_debug (MAILSTREAM *stream)
  47. {
  48.   stream->debug = T; /* turn on debugging telemetry */
  49. }
  50. /* Mail turn off debugging telemetry
  51.  * Accepts: mail stream
  52.  */
  53. void mail_nodebug (MAILSTREAM *stream)
  54. {
  55.   stream->debug = NIL; /* turn off debugging telemetry */
  56. }
  57. /* Mail parse UID sequence
  58.  * Accepts: mail stream
  59.  *     sequence to parse
  60.  * Returns: T if parse successful, else NIL
  61.  */
  62. long mail_uid_sequence (MAILSTREAM *stream,char *sequence)
  63. {
  64.   unsigned long i,j,k,x,y;
  65.   for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->sequence = NIL;
  66.   while (sequence && *sequence){/* while there is something to parse */
  67.     if (*sequence == '*') { /* maximum message */
  68.       i = stream->nmsgs ? mail_uid (stream,stream->nmsgs) : stream->uid_last;
  69.       sequence++; /* skip past * */
  70.     }
  71. /* parse and validate message number */
  72.     else if (!(i = strtoul ((const char *) sequence,&sequence,10))) {
  73.       mm_log ("UID sequence invalid",ERROR);
  74.       return NIL;
  75.     }
  76.     switch (*sequence) { /* see what the delimiter is */
  77.     case ':': /* sequence range */
  78.       if (*++sequence == '*') { /* maximum message */
  79. j = stream->nmsgs ? mail_uid (stream,stream->nmsgs) : stream->uid_last;
  80. sequence++; /* skip past * */
  81.       }
  82. /* parse end of range */
  83.       else if (!(j = strtoul ((const char *) sequence,&sequence,10))) {
  84. mm_log ("UID sequence range invalid",ERROR);
  85. return NIL;
  86.       }
  87.       if (*sequence && *sequence++ != ',') {
  88. mm_log ("UID sequence range syntax error",ERROR);
  89. return NIL;
  90.       }
  91.       if (i > j) { /* swap the range if backwards */
  92. x = i; i = j; j = x;
  93.       }
  94.       x = mail_msgno (stream,i);/* get msgnos */
  95.       y = mail_msgno (stream,j);/* for both UIDS (don't && it) */
  96. /* easy if both UIDs valid */
  97.       if (x && y) while (x <= y) mail_elt (stream,x++)->sequence = T;
  98. /* start UID valid, end is not */
  99.       else if (x) while ((x <= stream->nmsgs) && (mail_uid (stream,x) <= j))
  100. mail_elt (stream,x++)->sequence = T;
  101. /* end UID valid, start is not */
  102.       else if (y) for (x = 1; x <= y; x++) {
  103. if (mail_uid (stream,x) >= i) mail_elt (stream,x)->sequence = T;
  104.       }
  105. /* neither is valid, ugh */
  106.       else for (x = 1; x <= stream->nmsgs; x++)
  107. if (((k = mail_uid (stream,x)) >= i) && (k <= j))
  108.   mail_elt (stream,x)->sequence = T;
  109.       break;
  110.     case ',': /* single message */
  111.       ++sequence; /* skip the delimiter, fall into end case */
  112.     case '': /* end of sequence, mark this message */
  113.       if (x = mail_msgno (stream,i)) mail_elt (stream,x)->sequence = T;
  114.       break;
  115.     default: /* anything else is a syntax error! */
  116.       mm_log ("UID sequence syntax error",ERROR);
  117.       return NIL;
  118.     }
  119.   }
  120.   return T; /* successfully parsed sequence */
  121. }
  122. /* Mail see if line list matches that in cache
  123.  * Accepts: candidate line list
  124.  *     cached line list
  125.  *     matching flags
  126.  * Returns: T if match, NIL if no match
  127.  */
  128. long mail_match_lines (STRINGLIST *lines,STRINGLIST *msglines,long flags)
  129. {
  130.   unsigned long i;
  131.   unsigned char *s,*t;
  132.   STRINGLIST *m;
  133.   if (!msglines) return T; /* full header is in cache */
  134. /* need full header but filtered in cache */
  135.   if ((flags & FT_NOT) || !lines) return NIL;
  136.   do { /* make sure all present & accounted for */
  137.     for (m = msglines; m; m = m->next) if (lines->text.size == m->text.size) {
  138.       for (s = lines->text.data,t = m->text.data,i = lines->text.size;
  139.    i && ((islower (*s) ? (*s-('a'-'A')) : *s) ==
  140.  (islower (*t) ? (*t-('a'-'A')) : *t)); s++,t++,i--);
  141.       if (!i) break; /* this line matches */
  142.     }
  143.     if (!m) return NIL; /* didn't find in the list */
  144.   }
  145.   while (lines = lines->next);
  146.   return T; /* all lines found */
  147. }
  148. /* Mail filter text by header lines
  149.  * Accepts: text to filter
  150.  *     length of text
  151.  *     list of lines
  152.  *     fetch flags
  153.  * Returns: new text size
  154.  */
  155. unsigned long mail_filter (char *text,unsigned long len,STRINGLIST *lines,
  156.    long flags)
  157. {
  158.   STRINGLIST *hdrs;
  159.   int notfound;
  160.   unsigned long i;
  161.   char c,*s,*e,*t,tmp[MAILTMPLEN],tst[MAILTMPLEN];
  162.   char *src = text;
  163.   char *dst = src;
  164.   char *end = text + len;
  165.   while (src < end) { /* process header */
  166. /* slurp header line name */
  167.     for (s = src,e = s + MAILTMPLEN - 1,e = (e < end ? e : end),t = tmp;
  168.  (s < e) && ((c = *s) != ':') &&
  169.  ((c > ' ') ||
  170.   ((c != ' ') && (c != 't') && (c != '15') && (c != '12')));
  171.  *t++ = *s++);
  172.     *t = ''; /* tie off */
  173.     notfound = T; /* not found yet */
  174.     if (i = t - ucase (tmp)) /* see if found in header */
  175.       for (hdrs = lines,tst[i] = ''; hdrs && notfound; hdrs = hdrs->next)
  176. if ((hdrs->text.size == i) &&
  177.     !strncmp (tmp,ucase (strncpy (tst,(char *) hdrs->text.data,
  178.   (size_t) i)),(size_t) i))
  179.   notfound = NIL;
  180. /* skip header line if not wanted */
  181.     if (i && ((flags & FT_NOT) ? !notfound : notfound))
  182.       while (((*src++ != '12') && (*src++ != '12') && (*src++ != '12') &&
  183.       (*src++ != '12') && (*src++ != '12') && (*src++ != '12') &&
  184.       (*src++ != '12') && (*src++ != '12') && (*src++ != '12') &&
  185.       (*src++ != '12')) || ((*src == ' ') || (*src == 't')));
  186.     else if (src == dst) { /* copy to self */
  187.       while (((*src++ != '12') && (*src++ != '12') && (*src++ != '12') &&
  188.       (*src++ != '12') && (*src++ != '12') && (*src++ != '12') &&
  189.       (*src++ != '12') && (*src++ != '12') && (*src++ != '12') &&
  190.       (*src++ != '12')) || ((*src == ' ') || (*src == 't')));
  191.       dst = src; /* update destination */
  192.     }
  193.     else /* copy line and any continuation line */
  194.       while ((((*dst++ = *src++) != '12') && ((*dst++ = *src++) != '12') &&
  195.       ((*dst++ = *src++) != '12') && ((*dst++ = *src++) != '12') &&
  196.       ((*dst++ = *src++) != '12') && ((*dst++ = *src++) != '12') &&
  197.       ((*dst++ = *src++) != '12') && ((*dst++ = *src++) != '12') &&
  198.       ((*dst++ = *src++) != '12') && ((*dst++ = *src++) != '12'))||
  199.      ((*src == ' ') || (*src == 't')));
  200.   }
  201.   *dst = ''; /* tie off destination */
  202.   return dst - text;
  203. }
  204. /* Local mail search message
  205.  * Accepts: MAIL stream
  206.  *     message number
  207.  *     optional section specification
  208.  *     search program
  209.  * Returns: T if found, NIL otherwise
  210.  */
  211. long mail_search_msg (MAILSTREAM *stream,unsigned long msgno,char *section,
  212.       SEARCHPGM *pgm)
  213. {
  214.   unsigned short d;
  215.   char tmp[MAILTMPLEN];
  216.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  217.   SEARCHHEADER *hdr;
  218.   SEARCHOR *or;
  219.   SEARCHPGMLIST *not;
  220.   if (pgm->msgno || pgm->uid) { /* message set searches */
  221.     SEARCHSET *set;
  222. /* message sequences */
  223.     if (set = pgm->msgno) { /* must be inside this sequence */
  224.       while (set) { /* run down until find matching range */
  225. if (set->last ? ((msgno < set->first) || (msgno > set->last)) :
  226.     msgno != set->first) set = set->next;
  227. else break;
  228.       }
  229.       if (!set) return NIL; /* not found within sequence */
  230.     }
  231.     if (set = pgm->uid) { /* must be inside this sequence */
  232.       unsigned long uid = mail_uid (stream,msgno);
  233.       while (set) { /* run down until find matching range */
  234. if (set->last ? ((uid < set->first) || (uid > set->last)) :
  235.     uid != set->first) set = set->next;
  236. else break;
  237.       }
  238.       if (!set) return NIL; /* not found within sequence */
  239.     }
  240.   }
  241.   /* Fast data searches */
  242. /* need to fetch fast data? */
  243.   if ((!elt->rfc822_size && (pgm->larger || pgm->smaller)) ||
  244.       (!elt->year && (pgm->before || pgm->on || pgm->since)) ||
  245.       (!elt->valid && (pgm->answered || pgm->unanswered ||
  246.        pgm->deleted || pgm->undeleted ||
  247.        pgm->draft || pgm->undraft ||
  248.        pgm->flagged || pgm->unflagged ||
  249.        pgm->recent || pgm->old ||
  250.        pgm->seen || pgm->unseen ||
  251.        pgm->keyword || pgm->unkeyword))) {
  252.     sprintf (tmp,"%lu",elt->msgno);
  253.     mail_fetch_fast (stream,tmp,NIL);
  254.   }
  255. /* size ranges */
  256.   if ((pgm->larger && (elt->rfc822_size <= pgm->larger)) ||
  257.       (pgm->smaller && (elt->rfc822_size >= pgm->smaller))) return NIL;
  258. /* message flags */
  259.   if ((pgm->answered && !elt->answered) ||
  260.       (pgm->unanswered && elt->answered) ||
  261.       (pgm->deleted && !elt->deleted) ||
  262.       (pgm->undeleted && elt->deleted) ||
  263.       (pgm->draft && !elt->draft) ||
  264.       (pgm->undraft && elt->draft) ||
  265.       (pgm->flagged && !elt->flagged) ||
  266.       (pgm->unflagged && elt->flagged) ||
  267.       (pgm->recent && !elt->recent) ||
  268.       (pgm->old && elt->recent) ||
  269.       (pgm->seen && !elt->seen) ||
  270.       (pgm->unseen && elt->seen)) return NIL;
  271. /* keywords */
  272.   if ((pgm->keyword && !mail_search_keyword (stream,elt,pgm->keyword)) ||
  273.       (pgm->unkeyword && mail_search_keyword (stream,elt,pgm->unkeyword)))
  274.     return NIL;
  275. /* internal date ranges */
  276.   if (pgm->before || pgm->on || pgm->since) {
  277.     d = (elt->year << 9) + (elt->month << 5) + elt->day;
  278.     if (pgm->before && (d >= pgm->before)) return NIL;
  279.     if (pgm->on && (d != pgm->on)) return NIL;
  280.     if (pgm->since && (d < pgm->since)) return NIL;
  281.   }
  282. /* envelope searches */
  283.   if (pgm->sentbefore || pgm->senton || pgm->sentsince ||
  284.       pgm->bcc || pgm->cc || pgm->from || pgm->to || pgm->subject ||
  285.       pgm->return_path || pgm->sender || pgm->reply_to || pgm->in_reply_to ||
  286.       pgm->message_id || pgm->newsgroups || pgm->followup_to ||
  287.       pgm->references) {
  288.     ENVELOPE *env;
  289.     MESSAGECACHE delt;
  290.     if (section) { /* use body part envelope */
  291.       BODY *body = mail_body (stream,msgno,section);
  292.       env = (body && (body->type == TYPEMESSAGE) && body->subtype &&
  293.      !strcmp (body->subtype,"RFC822")) ? body->nested.msg->env : NIL;
  294.     }
  295. /* use top level envelope if no section */
  296.     else env = mail_fetchenvelope (stream,msgno);
  297.     if (!env) return NIL; /* no envelope obtained */
  298. /* sent date ranges */
  299.     if ((pgm->sentbefore || pgm->senton || pgm->sentsince) &&
  300. (!mail_parse_date (&delt,env->date) ||
  301.  !(d = (delt.year << 9) + (delt.month << 5) + delt.day) ||
  302.  (pgm->sentbefore && (d >= pgm->sentbefore)) ||
  303.  (pgm->senton && (d != pgm->senton)) ||
  304.  (pgm->sentsince && (d < pgm->sentsince)))) return NIL;
  305. /* search headers */
  306.     if ((pgm->bcc && !mail_search_addr (env->bcc,pgm->bcc)) ||
  307. (pgm->cc && !mail_search_addr (env->cc,pgm->cc)) ||
  308. (pgm->from && !mail_search_addr (env->from,pgm->from)) ||
  309. (pgm->to && !mail_search_addr (env->to,pgm->to)) ||
  310. (pgm->subject && !mail_search_header_text (env->subject,pgm->subject)))
  311.       return NIL;
  312.     /* These criteria are not supported by IMAP and have to be emulated */
  313.     if ((pgm->return_path &&
  314.  !mail_search_addr (env->return_path,pgm->return_path)) ||
  315. (pgm->sender && !mail_search_addr (env->sender,pgm->sender)) ||
  316. (pgm->reply_to && !mail_search_addr (env->reply_to,pgm->reply_to)) ||
  317. (pgm->in_reply_to &&
  318.  !mail_search_header_text (env->in_reply_to,pgm->in_reply_to)) ||
  319. (pgm->message_id &&
  320. !mail_search_header_text (env->message_id,pgm->message_id)) ||
  321. (pgm->newsgroups &&
  322.  !mail_search_header_text (env->newsgroups,pgm->newsgroups)) ||
  323. (pgm->followup_to &&
  324.  !mail_search_header_text (env->followup_to,pgm->followup_to)) ||
  325. (pgm->references &&
  326.  !mail_search_header_text (env->references,pgm->references)))
  327.       return NIL;
  328.   }
  329. /* search header lines */
  330.   for (hdr = pgm->header; hdr; hdr = hdr->next) {
  331.     char *t,*e,*v;
  332.     SIZEDTEXT s;
  333.     STRINGLIST sth,stc;
  334.     sth.next = stc.next = NIL; /* only one at a time */
  335.     sth.text.data = hdr->line.data;
  336.     sth.text.size = hdr->line.size;
  337. /* get the header text */
  338.     if ((t = mail_fetch_header (stream,msgno,NIL,&sth,&s.size,
  339. FT_INTERNAL | FT_PEEK)) && strchr (t,':')) {
  340.       if (hdr->text.size) { /* anything matches empty search string */
  341. /* non-empty, copy field data */
  342. s.data = (unsigned char *) fs_get (s.size + 1);
  343. /* for each line */
  344. for (v = (char *) s.data, e = t + s.size; t < e;) switch (*t) {
  345. default: /* non-continuation, skip leading field name */
  346.   while ((t < e) && (*t++ != ':'));
  347.   if ((t < e) && (*t == ':')) t++;
  348. case 't': case ' ': /* copy field data  */
  349.   while ((t < e) && (*t != '15') && (*t != '12')) *v++ = *t++;
  350.   *v++ = 'n'; /* tie off line */
  351.   while (((*t == '15') || (*t == '12')) && (t < e)) t++;
  352. }
  353. /* calculate true size */
  354. s.size = v - (char *) s.data;
  355. *v = ''; /* tie off results */
  356. stc.text.data = hdr->text.data;
  357. stc.text.size = hdr->text.size;
  358. /* search header */
  359. if (mail_search_header (&s,&stc)) fs_give ((void **) &s.data);
  360. else { /* search failed */
  361.   fs_give ((void **) &s.data);
  362.   return NIL;
  363. }
  364.       }
  365.     }
  366.     else return NIL; /* no matching header text */
  367.   }
  368. /* search strings */
  369.   if ((pgm->text && !mail_search_text (stream,msgno,section,pgm->text,LONGT))||
  370.       (pgm->body && !mail_search_text (stream,msgno,section,pgm->body,NIL)))
  371.     return NIL;
  372. /* logical conditions */
  373.   for (or = pgm->or; or; or = or->next)
  374.     if (!(mail_search_msg (stream,msgno,section,or->first) ||
  375.   mail_search_msg (stream,msgno,section,or->second))) return NIL;
  376.   for (not = pgm->not; not; not = not->next)
  377.     if (mail_search_msg (stream,msgno,section,not->pgm)) return NIL;
  378.   return T;
  379. }
  380. /* Mail search message header null-terminated text
  381.  * Accepts: header text
  382.  *     strings to search
  383.  * Returns: T if search found a match
  384.  */
  385. long mail_search_header_text (char *s,STRINGLIST *st)
  386. {
  387.   SIZEDTEXT h;
  388. /* have any text? */
  389.   if (h.data = (unsigned char *) s) {
  390.     h.size = strlen (s); /* yes, get its size */
  391.     return mail_search_header (&h,st);
  392.   }
  393.   return NIL;
  394. }
  395. /* Mail search message header
  396.  * Accepts: header as sized text
  397.  *     strings to search
  398.  * Returns: T if search found a match
  399.  */
  400. long mail_search_header (SIZEDTEXT *hdr,STRINGLIST *st)
  401. {
  402.   SIZEDTEXT h;
  403.   long ret = LONGT;
  404.   utf8_mime2text (hdr,&h); /* make UTF-8 version of header */
  405.   while (h.size && ((h.data[h.size-1]=='15') || (h.data[h.size-1]=='12')))
  406.     --h.size; /* slice off trailing newlines */
  407.   do if (h.size ? /* search non-empty string */
  408.  !search (h.data,h.size,st->text.data,st->text.size) : st->text.size)
  409.     ret = NIL;
  410.   while (ret && (st = st->next));
  411.   if (h.data != hdr->data) fs_give ((void **) &h.data);
  412.   return ret;
  413. }
  414. /* Mail search message body
  415.  * Accepts: MAIL stream
  416.  *     message number
  417.  *     optional section specification
  418.  *     string list
  419.  *     flags
  420.  * Returns: T if search found a match
  421.  */
  422. long mail_search_text (MAILSTREAM *stream,unsigned long msgno,char *section,
  423.        STRINGLIST *st,long flags)
  424. {
  425.   BODY *body;
  426.   long ret = NIL;
  427.   STRINGLIST *s = mail_newstringlist ();
  428.   mailgets_t omg = mailgets;
  429.   if (stream->dtb->flags & DR_LOWMEM) mailgets = mail_search_gets;
  430. /* strings to search */
  431.   for (stream->private.search.string = s; st;) {
  432.     s->text.data = st->text.data;
  433.     s->text.size = st->text.size;
  434.     if (st = st->next) s = s->next = mail_newstringlist ();
  435.   }
  436.   stream->private.search.text = NIL;
  437.   if (flags) { /* want header? */
  438.     SIZEDTEXT s,t;
  439.     s.data = (unsigned char *)
  440.       mail_fetch_header (stream,msgno,section,NIL,&s.size,FT_INTERNAL|FT_PEEK);
  441.     utf8_mime2text (&s,&t);
  442.     ret = mail_search_string (&t,"UTF-8",&stream->private.search.string);
  443.     if (t.data != s.data) fs_give ((void **) &t.data);
  444.   }
  445.   if (!ret) { /* still looking for match? */
  446. /* no section, get top-level body */
  447.     if (!section) mail_fetchstructure (stream,msgno,&body);
  448. /* get body of nested message */
  449.     else if ((body = mail_body (stream,msgno,section)) &&
  450.      (body->type == TYPEMULTIPART) && body->subtype &&
  451.      !strcmp (body->subtype,"RFC822")) body = body->nested.msg->body;
  452.     if (body) ret = mail_search_body (stream,msgno,body,NIL,1,flags);
  453.   }
  454.   mailgets = omg; /* restore former gets routine */
  455. /* clear searching */
  456.   for (s = stream->private.search.string; s; s = s->next) s->text.data = NIL;
  457.   mail_free_stringlist (&stream->private.search.string);
  458.   stream->private.search.text = NIL;
  459.   return ret;
  460. }
  461. /* Mail search message body text parts
  462.  * Accepts: MAIL stream
  463.  *     message number
  464.  *     current body pointer
  465.  *     hierarchical level prefix
  466.  *     position at current hierarchical level
  467.  *     string list
  468.  *     flags
  469.  * Returns: T if search found a match
  470.  */
  471. long mail_search_body (MAILSTREAM *stream,unsigned long msgno,BODY *body,
  472.        char *prefix,unsigned long section,long flags)
  473. {
  474.   long ret = NIL;
  475.   unsigned long i;
  476.   char *s,*t,sect[MAILTMPLEN];
  477.   SIZEDTEXT st,h;
  478.   PART *part;
  479.   PARAMETER *param;
  480.   if (prefix && (strlen (prefix) > (MAILTMPLEN - 20))) return NIL;
  481.   sprintf (sect,"%s%lu",prefix ? prefix : "",section++);
  482.   if (flags && prefix) { /* want to search MIME header too? */
  483.     st.data = (unsigned char *) mail_fetch_mime (stream,msgno,sect,&st.size,
  484.  FT_INTERNAL | FT_PEEK);
  485.     if (stream->dtb->flags & DR_LOWMEM) ret = stream->private.search.result;
  486.     else {
  487.       utf8_mime2text (&st,&h); /* make UTF-8 version of header */
  488.       ret = mail_search_string (&h,"UTF-8",&stream->private.search.string);
  489.       if (h.data != st.data) fs_give ((void **) &h.data);
  490.     }
  491.   }
  492.   if (!ret) switch (body->type) {
  493.   case TYPEMULTIPART:
  494. /* extend prefix if not first time */
  495.     s = prefix ? strcat (sect,".") : "";
  496.     for (i = 1,part = body->nested.part; part && !ret; i++,part = part->next)
  497.       ret = mail_search_body (stream,msgno,&part->body,s,i,flags);
  498.     break;
  499.   case TYPEMESSAGE:
  500.     if (flags) { /* want to search nested message header? */
  501.       st.data = (unsigned char *)
  502. mail_fetch_header (stream,msgno,sect,NIL,&st.size,
  503.    FT_INTERNAL | FT_PEEK);
  504.       if (stream->dtb->flags & DR_LOWMEM) ret = stream->private.search.result;
  505.       else {
  506. utf8_mime2text (&st,&h);/* make UTF-8 version of header */
  507. ret = mail_search_string (&h,"UTF-8",&stream->private.search.string);
  508. if (h.data != st.data) fs_give ((void **) &h.data);
  509.       }
  510.     }
  511.     if (body = body->nested.msg->body)
  512.       ret = (body->type == TYPEMULTIPART) ?
  513. mail_search_body (stream,msgno,body,(prefix ? prefix : ""),section - 1,
  514.   flags) :
  515.   mail_search_body (stream,msgno,body,strcat (sect,"."),1,flags);
  516.     break;
  517.   case TYPETEXT:
  518.     s = mail_fetch_body (stream,msgno,sect,&i,FT_INTERNAL | FT_PEEK);
  519.     if (stream->dtb->flags & DR_LOWMEM) ret = stream->private.search.result;
  520.     else {
  521.       for (t = NIL,param = body->parameter; param && !t; param = param->next)
  522. if (!strcmp (param->attribute,"CHARSET")) t = param->value;
  523.       switch (body->encoding) { /* what encoding? */
  524.       case ENCBASE64:
  525. if (st.data = (unsigned char *)
  526.     rfc822_base64 ((unsigned char *) s,i,&st.size)) {
  527.   ret = mail_search_string (&st,t,&stream->private.search.string);
  528.   fs_give ((void **) &st.data);
  529. }
  530. break;
  531.       case ENCQUOTEDPRINTABLE:
  532. if (st.data = rfc822_qprint ((unsigned char *) s,i,&st.size)) {
  533.   ret = mail_search_string (&st,t,&stream->private.search.string);
  534.   fs_give ((void **) &st.data);
  535. }
  536. break;
  537.       default:
  538. st.data = (unsigned char *) s;
  539. st.size = i;
  540. ret = mail_search_string (&st,t,&stream->private.search.string);
  541. break;
  542.       }
  543.     }
  544.     break;
  545.   }
  546.   return ret;
  547. }
  548. /* Mail search text
  549.  * Accepts: sized text to search
  550.  *     character set of sized text
  551.  *     string list of search keys
  552.  * Returns: T if search found a match
  553.  */
  554. long mail_search_string (SIZEDTEXT *s,char *charset,STRINGLIST **st)
  555. {
  556.   void *t;
  557.   SIZEDTEXT u;
  558.   STRINGLIST **sc = st;
  559.   if (utf8_text (s,charset,&u,NIL)) {
  560.     while (*sc) { /* run down criteria list */
  561.       if (search (u.data,u.size,(*sc)->text.data,(*sc)->text.size)) {
  562. t = (void *) (*sc); /* found one, need to flush this */
  563. *sc = (*sc)->next; /* remove it from the list */
  564. fs_give (&t); /* flush the buffer */
  565.       }
  566.       else sc = &(*sc)->next; /* move to next in list */
  567.     }
  568.     if (u.data != s->data) fs_give ((void **) &u.data);
  569.   }
  570.   return *st ? NIL : LONGT;
  571. }
  572. /* Mail search keyword
  573.  * Accepts: MAIL stream
  574.  *     elt to get flags from
  575.  *     keyword list
  576.  * Returns: T if search found a match
  577.  */
  578. long mail_search_keyword (MAILSTREAM *stream,MESSAGECACHE *elt,STRINGLIST *st)
  579. {
  580.   int i;
  581.   char tmp[MAILTMPLEN],tmp2[MAILTMPLEN];
  582.   do { /* get uppercase form of flag */
  583.     sprintf (tmp,"%.900s",(char *) st->text.data);
  584.     ucase (tmp);
  585.     for (i = 0;; ++i) { /* check each possible keyword */
  586.       if (i >= NUSERFLAGS || !stream->user_flags[i]) return NIL;
  587.       if (elt->user_flags & (1 << i)) {
  588. sprintf (tmp2,"%.901s",stream->user_flags[i]);
  589. if (!strcmp (tmp,ucase (tmp2))) break;
  590.       }
  591.     }
  592.   }
  593.   while (st = st->next); /* try next keyword */
  594.   return T;
  595. }
  596. /* Mail search an address list
  597.  * Accepts: address list
  598.  *     string list
  599.  * Returns: T if search found a match
  600.  */
  601. #define SEARCHBUFLEN (size_t) 2000
  602. #define SEARCHBUFSLOP (size_t) 5
  603. long mail_search_addr (ADDRESS *adr,STRINGLIST *st)
  604. {
  605.   ADDRESS *a,tadr;
  606.   SIZEDTEXT txt;
  607.   char tmp[MAILTMPLEN];
  608.   size_t i = SEARCHBUFLEN;
  609.   size_t k;
  610.   long ret = NIL;
  611.   if (adr) {
  612.     txt.data = (unsigned char *) fs_get (i + SEARCHBUFSLOP);
  613. /* never an error or next */
  614.     tadr.error = NIL,tadr.next = NIL;
  615. /* write address list */
  616.     for (txt.size = 0,a = adr; a; a = a->next) {
  617.       k = (tadr.mailbox = a->mailbox) ? 2*strlen (a->mailbox) : 3;
  618.       if (tadr.personal = a->personal) k += 3 + 2*strlen (a->personal);
  619.       if (tadr.adl = a->adl) k += 1 + 2*strlen (a->adl);
  620.       if (tadr.host = a->host) k += 1 + 2*strlen (a->host);
  621.       if (k < MAILTMPLEN) { /* ignore ridiculous addresses */
  622. tmp[0] = '';
  623. rfc822_write_address (tmp,&tadr);
  624. /* resize buffer if necessary */
  625. if (((k = strlen (tmp)) + txt.size) > i)
  626.   fs_resize ((void **) &txt.data,SEARCHBUFSLOP + (i += SEARCHBUFLEN));
  627. /* add new address */
  628. memcpy (txt.data + txt.size,tmp,k);
  629. txt.size += k;
  630. /* another address follows */
  631. if (a->next) txt.data[txt.size++] = ',';
  632.       }
  633.     }
  634.     txt.data[txt.size] = ''; /* tie off string */
  635.     ret = mail_search_header (&txt,st);
  636.     fs_give ((void **) &txt.data);
  637.   }
  638.   return ret;
  639. }
  640. /* Get string for low-memory searching
  641.  * Accepts: readin function pointer
  642.  *     stream to use
  643.  *     number of bytes
  644.  *     gets data packet
  645.  *     mail stream
  646.  *     message number
  647.  *     descriptor string
  648.  *     option flags
  649.  * Returns: NIL, always
  650.  */
  651. #define SEARCHSLOP 128
  652. char *mail_search_gets (readfn_t f,void *stream,unsigned long size,
  653. GETS_DATA *md)
  654. {
  655.   unsigned long i;
  656.   char tmp[MAILTMPLEN+SEARCHSLOP+1];
  657.   SIZEDTEXT st;
  658. /* better not be called unless searching */
  659.   if (!md->stream->private.search.string) {
  660.     sprintf (tmp,"Search botch, mbx = %.80s, %s = %lu[%.80s]",
  661.      md->stream->mailbox,
  662.      (md->flags & FT_UID) ? "UID" : "msg",md->msgno,md->what);
  663.     fatal (tmp);
  664.   }
  665. /* initially no match for search */
  666.   md->stream->private.search.result = NIL;
  667. /* make sure buffer clear */
  668.   memset (st.data = (unsigned char *) tmp,'',
  669.   (size_t) MAILTMPLEN+SEARCHSLOP+1);
  670. /* read first buffer */
  671.   (*f) (stream,st.size = i = min (size,(long) MAILTMPLEN),tmp);
  672. /* search for text */
  673.   if (mail_search_string (&st,NIL,&md->stream->private.search.string))
  674.     md->stream->private.search.result = T;
  675.   else if (size -= i) { /* more to do, blat slop down */
  676.     memmove (tmp,tmp+MAILTMPLEN-SEARCHSLOP,(size_t) SEARCHSLOP);
  677.     do { /* read subsequent buffers one at a time */
  678.       (*f) (stream,i = min (size,(long) MAILTMPLEN),tmp+SEARCHSLOP);
  679.       st.size = i + SEARCHSLOP;
  680.       if (mail_search_string (&st,NIL,&md->stream->private.search.string))
  681. md->stream->private.search.result = T;
  682.       else memmove (tmp,tmp+MAILTMPLEN,(size_t) SEARCHSLOP);
  683.     }
  684.     while ((size -= i) && !md->stream->private.search.result);
  685.   }
  686.   if (size) { /* toss out everything after that */
  687.     do (*f) (stream,i = min (size,(long) MAILTMPLEN),tmp);
  688.     while (size -= i);
  689.   }
  690.   return NIL;
  691. }
  692. /* Mail parse search criteria
  693.  * Accepts: criteria
  694.  * Returns: search program if parse successful, else NIL
  695.  */
  696. SEARCHPGM *mail_criteria (char *criteria)
  697. {
  698.   SEARCHPGM *pgm;
  699.   char tmp[MAILTMPLEN];
  700.   int f = NIL;
  701.   if (!criteria) return NIL; /* return if no criteria */
  702.   pgm = mail_newsearchpgm (); /* make a basic search program */
  703. /* for each criterion */
  704.   for (criteria = strtok (criteria," "); criteria;
  705.        (criteria = strtok (NIL," "))) {
  706.     f = NIL; /* init then scan the criterion */
  707.     switch (*ucase (criteria)) {
  708.     case 'A': /* possible ALL, ANSWERED */
  709.       if (!strcmp (criteria+1,"LL")) f = T;
  710.       else if (!strcmp (criteria+1,"NSWERED")) f = pgm->answered = T;
  711.       break;
  712.     case 'B': /* possible BCC, BEFORE, BODY */
  713.       if (!strcmp (criteria+1,"CC")) f = mail_criteria_string (&pgm->bcc);
  714.       else if (!strcmp (criteria+1,"EFORE"))
  715. f = mail_criteria_date (&pgm->before);
  716.       else if (!strcmp (criteria+1,"ODY")) f=mail_criteria_string (&pgm->body);
  717.       break;
  718.     case 'C': /* possible CC */
  719.       if (!strcmp (criteria+1,"C")) f = mail_criteria_string (&pgm->cc);
  720.       break;
  721.     case 'D': /* possible DELETED */
  722.       if (!strcmp (criteria+1,"ELETED")) f = pgm->deleted = T;
  723.       break;
  724.     case 'F': /* possible FLAGGED, FROM */
  725.       if (!strcmp (criteria+1,"LAGGED")) f = pgm->flagged = T;
  726.       else if (!strcmp (criteria+1,"ROM")) f=mail_criteria_string (&pgm->from);
  727.       break;
  728.     case 'K': /* possible KEYWORD */
  729.       if (!strcmp (criteria+1,"EYWORD"))
  730. f = mail_criteria_string (&pgm->keyword);
  731.       break;
  732.     case 'N': /* possible NEW */
  733.       if (!strcmp (criteria+1,"EW")) f = pgm->recent = pgm->unseen = T;
  734.       break;
  735.     case 'O': /* possible OLD, ON */
  736.       if (!strcmp (criteria+1,"LD")) f = pgm->old = T;
  737.       else if (!strcmp (criteria+1,"N")) f = mail_criteria_date (&pgm->on);
  738.       break;
  739.     case 'R': /* possible RECENT */
  740.       if (!strcmp (criteria+1,"ECENT")) f = pgm->recent = T;
  741.       break;
  742.     case 'S': /* possible SEEN, SINCE, SUBJECT */
  743.       if (!strcmp (criteria+1,"EEN")) f = pgm->seen = T;
  744.       else if (!strcmp (criteria+1,"INCE")) f=mail_criteria_date (&pgm->since);
  745.       else if (!strcmp (criteria+1,"UBJECT"))
  746. f = mail_criteria_string (&pgm->subject);
  747.       break;
  748.     case 'T': /* possible TEXT, TO */
  749.       if (!strcmp (criteria+1,"EXT")) f = mail_criteria_string (&pgm->text);
  750.       else if (!strcmp (criteria+1,"O")) f = mail_criteria_string (&pgm->to);
  751.       break;
  752.     case 'U': /* possible UN* */
  753.       if (criteria[1] == 'N') {
  754. if (!strcmp (criteria+2,"ANSWERED")) f = pgm->unanswered = T;
  755. else if (!strcmp (criteria+2,"DELETED")) f = pgm->undeleted = T;
  756. else if (!strcmp (criteria+2,"FLAGGED")) f = pgm->unflagged = T;
  757. else if (!strcmp (criteria+2,"KEYWORD"))
  758.   f = mail_criteria_string (&pgm->unkeyword);
  759. else if (!strcmp (criteria+2,"SEEN")) f = pgm->unseen = T;
  760.       }
  761.       break;
  762.     default: /* we will barf below */
  763.       break;
  764.     }
  765.     if (!f) { /* if can't determine any criteria */
  766.       sprintf (tmp,"Unknown search criterion: %.30s",criteria);
  767.       mm_log (tmp,ERROR);
  768.       mail_free_searchpgm (&pgm);
  769.       break;
  770.     }
  771.   }
  772.   return pgm;
  773. }
  774. /* Parse a date
  775.  * Accepts: pointer to date integer to return
  776.  * Returns: T if successful, else NIL
  777.  */
  778. int mail_criteria_date (unsigned short *date)
  779. {
  780.   STRINGLIST *s = NIL;
  781.   MESSAGECACHE elt;
  782. /* parse the date and return fn if OK */
  783.   int ret = (mail_criteria_string (&s) &&
  784.      mail_parse_date (&elt,(char *) s->text.data) &&
  785.      (*date = (elt.year << 9) + (elt.month << 5) + elt.day)) ?
  786.        T : NIL;
  787.   if (s) mail_free_stringlist (&s);
  788.   return ret;
  789. }
  790. /* Parse a string
  791.  * Accepts: pointer to stringlist
  792.  * Returns: T if successful, else NIL
  793.  */
  794. int mail_criteria_string (STRINGLIST **s)
  795. {
  796.   unsigned long n;
  797.   char e,*d,*end = " ",*c = strtok (NIL,"");
  798.   if (!c) return NIL; /* missing argument */
  799.   switch (*c) { /* see what the argument is */
  800.   case '{': /* literal string */
  801.     n = strtoul (c+1,&d,10); /* get its length */
  802.     if ((*d++ == '}') && (*d++ == '15') && (*d++ == '12') &&
  803. (!(*(c = d + n)) || (*c == ' '))) {
  804.       e = *--c; /* store old delimiter */
  805.       *c = DELIM; /* make sure not a space */
  806.       strtok (c," "); /* reset the strtok mechanism */
  807.       *c = e; /* put character back */
  808.       break;
  809.     }
  810.   case '': /* catch bogons */
  811.   case ' ':
  812.     return NIL;
  813.   case '"': /* quoted string */
  814.     if (strchr (c+1,'"')) end = """;
  815.     else return NIL; /* falls through */
  816.   default: /* atomic string */
  817.     if (d = strtok (c,end)) n = strlen (d);
  818.     else return NIL;
  819.     break;
  820.   }
  821.   while (*s) s = &(*s)->next; /* find tail of list */
  822.   *s = mail_newstringlist (); /* make new entry */
  823. /* return the data */
  824.   (*s)->text.data = (unsigned char *) cpystr (d);
  825.   (*s)->text.size = n;
  826.   return T;
  827. }
  828. /* Mail sort messages
  829.  * Accepts: mail stream
  830.  *     character set
  831.  *     search program
  832.  *     sort program
  833.  *     option flags
  834.  * Returns: vector of sorted message sequences or NIL if error
  835.  */
  836. unsigned long *mail_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
  837.   SORTPGM *pgm,long flags)
  838. {
  839.   unsigned long *ret = NIL;
  840.   if (stream->dtb) /* do the driver's action */
  841.     ret = (*(stream->dtb->sort ? stream->dtb->sort : mail_sort_msgs))
  842.       (stream,charset,spg,pgm,flags);
  843. /* flush search/sort programs if requested */
  844.   if (spg && (flags & SE_FREE)) mail_free_searchpgm (&spg);
  845.   if (flags & SO_FREE) mail_free_sortpgm (&pgm);
  846.   return ret;
  847. }
  848. /* Mail sort messages work routine
  849.  * Accepts: mail stream
  850.  *     character set
  851.  *     search program
  852.  *     sort program
  853.  *     option flags
  854.  * Returns: vector of sorted message sequences or NIL if error
  855.  */
  856. unsigned long *mail_sort_msgs (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
  857.        SORTPGM *pgm,long flags)
  858. {
  859.   unsigned long i;
  860.   SORTCACHE **sc;
  861.   unsigned long *ret = NIL;
  862.   if (spg) { /* only if a search needs to be done */
  863.     int silent = stream->silent;
  864.     stream->silent = T; /* don't pass up mm_searched() events */
  865. /* search for messages */
  866.     mail_search_full (stream,charset,spg,NIL);
  867.     stream->silent = silent; /* restore silence state */
  868.   }
  869. /* initialize progress counters */
  870.   pgm->nmsgs = pgm->progress.cached = 0;
  871. /* pass 1: count messages to sort */
  872.   for (i = 1; i <= stream->nmsgs; ++i)
  873.     if (mail_elt (stream,i)->searched) pgm->nmsgs++;
  874.   if (pgm->nmsgs) { /* pass 2: sort cache */
  875.     sc = mail_sort_loadcache (stream,pgm);
  876. /* pass 3: sort messages */
  877.     if (!pgm->abort) ret = mail_sort_cache (stream,pgm,sc,flags);
  878.     fs_give ((void **) &sc); /* don't need sort vector any more */
  879.   }
  880. /* empty sort results */
  881.   else ret = (unsigned long *) memset (fs_get (sizeof (unsigned long)),0,
  882.        sizeof (unsigned long));
  883. /* also return via callback if requested */
  884.   if (mailsortresults) (*mailsortresults) (stream,ret,pgm->nmsgs);
  885.   return ret; /* return sort results */
  886. }
  887. /* Mail sort sortcache vector
  888.  * Accepts: mail stream
  889.  *     sort program
  890.  *     sortcache vector
  891.  *     option flags
  892.  * Returns: vector of sorted message sequences or NIL if error
  893.  */
  894. unsigned long *mail_sort_cache (MAILSTREAM *stream,SORTPGM *pgm,SORTCACHE **sc,
  895. long flags)
  896. {
  897.   unsigned long i,*ret;
  898. /* pass 3: sort messages */
  899.   qsort ((void *) sc,pgm->nmsgs,sizeof (SORTCACHE *),mail_sort_compare);
  900. /* optional post sorting */
  901.   if (pgm->postsort) (*pgm->postsort) ((void *) sc);
  902. /* pass 4: return results */
  903.   ret = (unsigned long *) fs_get ((pgm->nmsgs+1) * sizeof (unsigned long));
  904.   if (flags & SE_UID) /* UID or msgno? */
  905.     for (i = 0; i < pgm->nmsgs; i++) ret[i] = mail_uid (stream,sc[i]->num);
  906.   else for (i = 0; i < pgm->nmsgs; i++) ret[i] = sc[i]->num;
  907.   ret[pgm->nmsgs] = 0; /* tie off message list */
  908.   return ret;
  909. }
  910. /* Mail load sortcache
  911.  * Accepts: mail stream, already searched
  912.  *     sort program
  913.  * Returns: vector of sortcache pointers matching search
  914.  */
  915. static STRINGLIST maildateline = {{(unsigned char *) "date",4},NIL};
  916. static STRINGLIST mailrnfromline = {{(unsigned char *) ">from",5},NIL};
  917. static STRINGLIST mailfromline = {{(unsigned char *) "from",4},
  918.     &mailrnfromline};
  919. static STRINGLIST mailtonline = {{(unsigned char *) "to",2},NIL};
  920. static STRINGLIST mailccline = {{(unsigned char *) "cc",2},NIL};
  921. static STRINGLIST mailsubline = {{(unsigned char *) "subject",7},NIL};
  922. SORTCACHE **mail_sort_loadcache (MAILSTREAM *stream,SORTPGM *pgm)
  923. {
  924.   char *t,*v,*x,tmp[MAILTMPLEN];
  925.   SORTPGM *pg;
  926.   SORTCACHE *s,**sc;
  927.   MESSAGECACHE *elt,telt;
  928.   ENVELOPE *env;
  929.   ADDRESS *adr = NIL;
  930.   unsigned long i = (pgm->nmsgs) * sizeof (SORTCACHE *);
  931.   sc = (SORTCACHE **) memset (fs_get ((size_t) i),0,(size_t) i);
  932. /* see what needs to be loaded */
  933.   for (i = 1; !pgm->abort && (i <= stream->nmsgs); i++)
  934.     if ((elt = mail_elt (stream,i))->searched) {
  935.       sc[pgm->progress.cached++] =
  936. s = (SORTCACHE *) (*mailcache) (stream,i,CH_SORTCACHE);
  937.       s->pgm = pgm; /* note sort program */
  938.       s->num = i;
  939. /* get envelope if cached */
  940.       if (stream->scache) env = (i == stream->msgno) ? stream->env : NIL;
  941.       else env = elt->private.msg.env;
  942.       for (pg = pgm; pg; pg = pg->next) switch (pg->function) {
  943.       case SORTARRIVAL: /* sort by arrival date */
  944. if (!s->arrival) {
  945.   if (!elt->day) { /* if date unknown */
  946.     sprintf (tmp,"%lu",i);
  947.     mail_fetch_fast (stream,tmp,NIL);
  948.   }
  949. /* wrong thing before 3-Jan-1970 */
  950.   s->arrival = elt->day ? mail_longdate (elt) : 1;
  951. }
  952. break;
  953.       case SORTSIZE: /* sort by message size */
  954. if (!s->size) {
  955.   if (!elt->rfc822_size) {
  956.     sprintf (tmp,"%lu",i);
  957.     mail_fetch_fast (stream,tmp,NIL);
  958.   }
  959.   s->size = elt->rfc822_size ? elt->rfc822_size : 1;
  960. }
  961. break;
  962.       case SORTDATE: /* sort by date */
  963. if (!s->date) {
  964.   if (env) t = env->date;
  965.   else if ((t = mail_fetch_header (stream,i,NIL,&maildateline,NIL,
  966.    FT_INTERNAL | FT_PEEK)) &&
  967.    (t = strchr (t,':')))
  968.     for (x = ++t; x = strpbrk (x,"1215"); x++)
  969.       switch (*(v = ((*x == '15') && (x[1] == '12')) ? x+2 : x+1)){
  970.       case ' ': /* erase continuation newlines */
  971.       case 't':
  972. memmove (x,v,strlen (v));
  973. break;
  974.       default: /* tie off extraneous text */
  975. *x = x[1] = '';
  976.       }
  977. /* skip leading whitespace */
  978.   if (t) while ((*t == ' ') || (*t == 't')) t++;
  979. /* wrong thing before 3-Jan-1970 */
  980.   if (!(t && mail_parse_date (&telt,t) &&
  981. (s->date = mail_longdate (&telt)))) s->date = 1;
  982. }
  983. break;
  984.       case SORTFROM: /* sort by first from */
  985. if (!s->from) {
  986.   if (env) s->from = env->from && env->from->mailbox ?
  987.     cpystr (env->from->mailbox) : NIL;
  988.   else if ((t = mail_fetch_header (stream,i,NIL,&mailfromline,NIL,
  989.    FT_INTERNAL | FT_PEEK)) &&
  990.    (t = strchr (t,':'))) {
  991.     for (x = ++t; x = strpbrk (x,"1215"); x++)
  992.       switch (*(v = ((*x == '15') && (x[1] == '12')) ? x+2 : x+1)){
  993.       case ' ': /* erase continuation newlines */
  994.       case 't':
  995. memmove (x,v,strlen (v));
  996. break;
  997.       case 'f': /* continuation but with extra "From:" */
  998.       case 'F':
  999. if (v = strchr (v,':')) {
  1000.   memmove (x,v+1,strlen (v+1));
  1001.   break;
  1002. }
  1003.       default: /* tie off extraneous text */
  1004. *x = x[1] = '';
  1005.       }
  1006.     if (adr = rfc822_parse_address (&adr,adr,&t,BADHOST,0)) {
  1007.       s->from = adr->mailbox;
  1008.       adr->mailbox = NIL;
  1009.       mail_free_address (&adr);
  1010.     }
  1011.   }
  1012.   if (!s->from) s->from = cpystr ("");
  1013. }
  1014. break;
  1015.       case SORTTO: /* sort by first to */
  1016. if (!s->to) {
  1017.   if (env) s->to = env->to && env->to->mailbox ?
  1018.     cpystr (env->to->mailbox) : NIL;
  1019.   else if ((t = mail_fetch_header (stream,i,NIL,&mailtonline,NIL,
  1020.    FT_INTERNAL | FT_PEEK)) &&
  1021.    (t = strchr (t,':'))) {
  1022.     for (x = ++t; x = strpbrk (x,"1215"); x++)
  1023.       switch (*(v = ((*x == '15') && (x[1] == '12')) ? x+2 : x+1)){
  1024.       case ' ': /* erase continuation newlines */
  1025.       case 't':
  1026. memmove (x,v,strlen (v));
  1027. break;
  1028.       case 't': /* continuation but with extra "To:" */
  1029.       case 'T':
  1030. if (v = strchr (v,':')) {
  1031.   memmove (x,v+1,strlen (v+1));
  1032.   break;
  1033. }
  1034.       default: /* tie off extraneous text */
  1035. *x = x[1] = '';
  1036.       }
  1037.     if (adr = rfc822_parse_address (&adr,adr,&t,BADHOST,0)) {
  1038.       s->to = adr->mailbox;
  1039.       adr->mailbox = NIL;
  1040.       mail_free_address (&adr);
  1041.     }
  1042.   }
  1043.   if (!s->to) s->to = cpystr ("");
  1044. }
  1045. break;
  1046.       case SORTCC: /* sort by first cc */
  1047. if (!s->cc) {
  1048.   if (env) s->cc = env->cc && env->cc->mailbox ?
  1049.     cpystr (env->cc->mailbox) : NIL;
  1050.   else if ((t = mail_fetch_header (stream,i,NIL,&mailccline,NIL,
  1051.    FT_INTERNAL | FT_PEEK)) &&
  1052.    (t = strchr (t,':'))) {
  1053.     for (x = ++t; x = strpbrk (x,"1215"); x++)
  1054.       switch (*(v = ((*x == '15') && (x[1] == '12')) ? x+2 : x+1)){
  1055.       case ' ': /* erase continuation newlines */
  1056.       case 't':
  1057. memmove (x,v,strlen (v));
  1058. break;
  1059.       case 't': /* continuation but with extra "To:" */
  1060.       case 'T':
  1061. if (v = strchr (v,':')) {
  1062.   memmove (x,v+1,strlen (v+1));
  1063.   break;
  1064. }
  1065.       default: /* tie off extraneous text */
  1066. *x = x[1] = '';
  1067.       }
  1068.     if (adr = rfc822_parse_address (&adr,adr,&t,BADHOST,0)) {
  1069.       s->cc = adr->mailbox;
  1070.       adr->mailbox = NIL;
  1071.       mail_free_address (&adr);
  1072.     }
  1073.   }
  1074.   if (!s->cc) s->cc = cpystr ("");
  1075. }
  1076. break;
  1077.       case SORTSUBJECT: /* sort by subject */
  1078. if (!s->subject) {
  1079. /* get subject from envelope if have one */
  1080.   if (env) t = env->subject ? env->subject : "";
  1081. /* otherwise snarf from header text */
  1082.   else if ((t = mail_fetch_header (stream,i,NIL,&mailsubline,
  1083.    NIL,FT_INTERNAL | FT_PEEK)) &&
  1084.    (t = strchr (t,':')))
  1085.     for (x = ++t; x = strpbrk (x,"1215"); x++)
  1086.       switch (*(v = ((*x == '15') && (x[1] == '12')) ? x+2 : x+1)){
  1087.       case ' ': /* erase continuation newlines */
  1088.       case 't':
  1089. memmove (x,v,strlen (v));
  1090. break;
  1091.       default: /* tie off extraneous text */
  1092. *x = x[1] = '';
  1093.       }
  1094.   else t = ""; /* empty subject */
  1095. /* strip and cache subject */
  1096.   s->subject = mail_strip_subject (t);
  1097. }
  1098. break;
  1099.       default:
  1100. fatal ("Unknown sort function");
  1101.       }
  1102.     }
  1103.   return sc;
  1104. }
  1105. /* Strip subjects of extra spaces and leading and trailing cruft for sorting
  1106.  * Accepts: unstripped subject
  1107.  * Returns: stripped subject, in cpystr form
  1108.  */
  1109. char *mail_strip_subject (char *t)
  1110. {
  1111.   char c,*s,*x;
  1112.   SIZEDTEXT src,dst;
  1113. /* flush leading whitespace, [, and "re" */
  1114.   for (x = t; x; ) switch (*t) {
  1115.   case ' ': case 't':
  1116.     t++; /* leading whitespace */
  1117.     break;
  1118.   case '[': /* leading [ */
  1119.     for (x = ++t; x;) switch (*x) {
  1120.     case ']': /* closing ] */
  1121.       while (x) switch (*++x) {
  1122.       case '(': /* possible trailing (fwd) */
  1123. if (((x[1] == 'F') || (x[1] == 'f')) &&
  1124.     ((x[2] == 'W') || (x[2] == 'w')) &&
  1125.     ((x[3] == 'D') || (x[3] == 'd')) && x[4] == ')') {
  1126.   x += 4; /* yes, skip past it */
  1127.   break; /* continue scan */
  1128. }
  1129. /* else fall through */
  1130.       default:
  1131. t = x; /* found usable text */
  1132.       case '': /* end of string */
  1133. x = NIL; /* terminate search here */
  1134.       case ' ': case 't': /* skip whitespace */
  1135. break;
  1136.       }
  1137.       break;
  1138.     case '': case '[': /* end of string or nesting */
  1139.       x = NIL; /* just punt */
  1140.       break;
  1141.     default: /* any other character */
  1142.       x++; /* sniff at next */
  1143.       break;
  1144.     }
  1145.     x = t; /* finished handling [...] */
  1146.     break;
  1147.   case 'R': case 'r': /* possible "re" */
  1148.     if ((t[1] != 'E') && (t[1] != 'e')) x = NIL;
  1149.     else { /* found re, skip leading whitespace */
  1150.       for (x = t + 2; (*x == ' ') || (*x == 't'); x++);
  1151.       switch (*x++) { /* what comes after? */
  1152.       case ':': /* "re:" */
  1153. t = x;
  1154. break;
  1155.       case '[': /* possible "re[babble]:" */
  1156. if (x = strchr (x,']')) {
  1157.   for (x++; ((*x == ' ') || (*x == 't')); x++);
  1158.   if (*x == ':') t = ++x;
  1159.   else x = NIL;
  1160. }
  1161. break;
  1162.       default: /* something significant starting with "re" */
  1163. x = NIL; /* terminate the loop */
  1164. break;
  1165.       }
  1166.     }
  1167.     break;
  1168.   case 'F': case 'f': /* possible "fw" or "fwd" */
  1169.     if ((t[1] != 'W') && (t[1] != 'w')) x = NIL;
  1170.     else { /* found fw, skip "D" and leading whitespace */
  1171.       for (x = ((t[2] == 'D') || (t[2] == 'd')) ? t + 3 : t + 2;
  1172.    (*x == ' ') || (*x == 't'); x++);
  1173.       switch (*x++) { /* what comes after? */
  1174.       case ':': /* "fwd:" */
  1175. t = x;
  1176. break;
  1177.       case '[': /* possible "fwd[babble]:" */
  1178. if (x = strchr (x,']')) {
  1179.   for (x++; ((*x == ' ') || (*x == 't')); x++);
  1180.   if (*x == ':') t = ++x;
  1181.   else x = NIL;
  1182. }
  1183. break;
  1184.       default: /* something significant starting with "fwd" */
  1185. x = NIL; /* terminate the loop */
  1186. break;
  1187.       }
  1188.     }
  1189.     break;
  1190.   default: /* something significant */
  1191.     x = NIL; /* terminate the loop */
  1192.     break;
  1193.   }
  1194. /* have an empty subject? */
  1195.   if (!(src.size = strlen (t))) return cpystr ("");
  1196.   src.data = (unsigned char *) t;
  1197. /* make copy, convert MIME2 if needed */
  1198.   if (utf8_mime2text (&src,&dst) && (src.data != dst.data))
  1199.     t = (x = (char *) dst.data) + dst.size;
  1200.   else t = (x = cpystr ((char *) src.data)) + src.size;
  1201.   while (t > x) { /* flush trailing "(fwd)", ], and whitespace */
  1202.     while ((t[-1] == ' ') || (t[-1] == 't') || (t[-1] == ']')) t--;
  1203.     if ((t >= (x + 5)) && (t[-5] == '(') &&
  1204. ((t[-4] == 'F') || (t[-4] == 'f')) &&
  1205. ((t[-3] == 'W') || (t[-3] == 'w')) &&
  1206. ((t[-2] == 'D') || (t[-2] == 'd')) && (t[-1] == ')')) t -= 5;
  1207.     else break;
  1208.   }
  1209.   *t = ''; /* tie off subject string */
  1210. /* convert spaces to tab, strip extra spaces */
  1211.   for (s = t = x, c = 'x'; *t; t++) {
  1212.     if (c != ' ') c = *s++ = ((*t == 't') ? ' ' : *t);
  1213.     else if ((*t != 't') && (*t != ' ')) c = *s++ = *t;
  1214.   }
  1215.   *s = ''; /* tie off string again */
  1216.   return x; /* return stripped subject */
  1217. }
  1218. /* Sort compare messages
  1219.  * Accept: first message sort cache element
  1220.  *    second message sort cache element
  1221.  * Returns: -1 if a1 < a2, 0 if a1 == a2, 1 if a1 > a2
  1222.  */
  1223. int mail_sort_compare (const void *a1,const void *a2)
  1224. {
  1225.   int i = 0;
  1226.   SORTCACHE *s1 = *(SORTCACHE **) a1;
  1227.   SORTCACHE *s2 = *(SORTCACHE **) a2;
  1228.   SORTPGM *pgm = s1->pgm;
  1229.   if (!s1->sorted) { /* this one sorted yet? */
  1230.     s1->sorted = T;
  1231.     pgm->progress.sorted++; /* another sorted message */
  1232.   }
  1233.   if (!s2->sorted) { /* this one sorted yet? */
  1234.     s2->sorted = T;
  1235.     pgm->progress.sorted++; /* another sorted message */
  1236.   }
  1237.   do {
  1238.     switch (pgm->function) { /* execute search program */
  1239.     case SORTDATE: /* sort by date */
  1240.       i = mail_compare_ulong (s1->date,s2->date);
  1241.       break;
  1242.     case SORTARRIVAL: /* sort by arrival date */
  1243.       i = mail_compare_ulong (s1->arrival,s2->arrival);
  1244.       break;
  1245.     case SORTSIZE: /* sort by message size */
  1246.       i = mail_compare_ulong (s1->size,s2->size);
  1247.       break;
  1248.     case SORTFROM: /* sort by first from */
  1249.       i = mail_compare_cstring (s1->from,s2->from);
  1250.       break;
  1251.     case SORTTO: /* sort by first to */
  1252.       i = mail_compare_cstring (s1->to,s2->to);
  1253.       break;
  1254.     case SORTCC: /* sort by first cc */
  1255.       i = mail_compare_cstring (s1->cc,s2->cc);
  1256.       break;
  1257.     case SORTSUBJECT: /* sort by subject */
  1258.       i = mail_compare_cstring (s1->subject,s2->subject);
  1259.       break;
  1260.     }
  1261.     if (pgm->reverse) i = -i; /* flip results if necessary */
  1262.   }
  1263.   while (pgm = i ? NIL : pgm->next);
  1264.   return i ? i : (s1->num - s2->num);
  1265. }
  1266. /* Compare two unsigned longs
  1267.  * Accepts: first value
  1268.  *     second value
  1269.  * Returns: -1 if l1 < l2, 0 if l1 == l2, 1 if l1 > l2
  1270.  */
  1271. int mail_compare_ulong (unsigned long l1,unsigned long l2)
  1272. {
  1273.   if (l1 < l2) return -1;
  1274.   if (l1 > l2) return 1;
  1275.   return 0;
  1276. }
  1277. /* Compare two case-independent strings
  1278.  * Accepts: first string
  1279.  *     second string
  1280.  * Returns: -1 if s1 < s2, 0 if s1 == s2, 1 if s1 > s2
  1281.  */
  1282. int mail_compare_cstring (char *s1,char *s2)
  1283. {
  1284.   int i;
  1285.   if (!s1) return s2 ? -1 : 0; /* empty string cases */
  1286.   else if (!s2) return 1;
  1287.   for (; *s1 && *s2; s1++, s2++)
  1288.     if (i = (mail_compare_ulong (isupper (*s1) ? tolower (*s1) : *s1,
  1289.  isupper (*s2) ? tolower (*s2) : *s2)))
  1290.       return i; /* found a difference */
  1291.   if (*s1) return 1; /* first string is longer */
  1292.   return *s2 ? -1 : 0; /* second string longer : strings identical */
  1293. }
  1294. /* Return message date as an unsigned long seconds since time began
  1295.  * Accepts: message cache pointer
  1296.  * Returns: unsigned long of date
  1297.  */
  1298. unsigned long mail_longdate (MESSAGECACHE *elt)
  1299. {
  1300.   unsigned long yr = elt->year + BASEYEAR;
  1301. /* number of days since time began */
  1302. #ifndef USEORTHODOXCALENDAR /* Gregorian calendar */
  1303.   unsigned long ret = (elt->day - 1) + 30 * (elt->month - 1) +
  1304.     ((unsigned long) ((elt->month + (elt->month > 8))) / 2) +
  1305.       elt->year * 365 + (((unsigned long) (elt->year + (BASEYEAR % 4))) / 4) +
  1306. ((yr / 400) - (BASEYEAR / 400)) - ((yr / 100) - (BASEYEAR / 100)) -
  1307. #ifdef Y4KBUGFIX
  1308.   ((yr / 4000) - (BASEYEAR / 4000)) -
  1309. #endif
  1310.     ((elt->month < 3) ? !(yr % 4) && ((yr % 100) || !(yr % 400)) : 2);
  1311. #else /* Orthodox calendar */
  1312.   unsigned long ret = (elt->day - 1) + 30 * (elt->month - 1) +
  1313.     ((unsigned long) ((elt->month + (elt->month > 8))) / 2) +
  1314.       elt->year * 365 + (((unsigned long) (elt->year + (BASEYEAR % 4))) / 4) +
  1315. ((2*(yr / 900)) - (2*(BASEYEAR / 900))) +
  1316.   (((yr % 900) >= 200) - ((BASEYEAR % 900) >= 200)) +
  1317.     (((yr % 900) >= 600) - ((BASEYEAR % 900) >= 600)) -
  1318.       ((yr / 100) - (BASEYEAR / 100)) -
  1319. ((elt->month < 3) ? !(yr % 4) && 
  1320.  ((yr % 100) || ((yr % 900) == 200) || ((yr % 900) == 600)) :
  1321.    2);
  1322. #endif
  1323.   ret *= 24; ret += elt->hours; /* date value in hours */
  1324.   ret *= 60; ret +=elt->minutes;/* date value in minutes */
  1325.   yr = (elt->zhours * 60) + elt->zminutes;
  1326.   if (elt->zoccident) ret += yr;/* cretinous TinyFlaccid C compiler! */
  1327.   else ret -= yr;
  1328.   ret *= 60; ret += elt->seconds;
  1329.   return ret;
  1330. }
  1331. /* Mail thread messages
  1332.  * Accepts: mail stream
  1333.  *     thread type
  1334.  *     character set
  1335.  *     search program
  1336.  *     option flags
  1337.  * Returns: thread node tree
  1338.  */
  1339. THREADNODE *mail_thread (MAILSTREAM *stream,char *type,char *charset,
  1340.  SEARCHPGM *spg,long flags)
  1341. {
  1342.   THREADNODE *ret = NIL;
  1343.   if (stream->dtb) /* must have a live driver */
  1344.     ret = stream->dtb->thread ? /* do driver's action if available */
  1345.       (*stream->dtb->thread) (stream,type,charset,spg,flags) :
  1346. mail_thread_msgs (stream,type,charset,spg,flags,mail_sort_msgs);
  1347. /* flush search/sort programs if requested */
  1348.   if (spg && (flags & SE_FREE)) mail_free_searchpgm (&spg);
  1349.   return ret;
  1350. }
  1351. /* Mail thread messages
  1352.  * Accepts: mail stream
  1353.  *     thread type
  1354.  *     character set
  1355.  *     search program
  1356.  *     option flags
  1357.  *     sorter routine
  1358.  * Returns: thread node tree
  1359.  */
  1360. THREADNODE *mail_thread_msgs (MAILSTREAM *stream,char *type,char *charset,
  1361.       SEARCHPGM *spg,long flags,sorter_t sorter)
  1362. {
  1363.   THREADER *t;
  1364.   for (t = &mailthreadlist; t; t = t->next) if (!strcmp (type,t->name)) {
  1365.     THREADNODE *ret = (*t->dispatch) (stream,charset,spg,flags,sorter);
  1366.     if (mailthreadresults) (*mailthreadresults) (stream,ret);
  1367.     return ret;
  1368.   }
  1369.   mm_log ("No such thread type",ERROR);
  1370.   return NIL;
  1371. }
  1372. /* Mail thread ordered subject
  1373.  * Accepts: mail stream
  1374.  *     character set
  1375.  *     search program
  1376.  *     option flags
  1377.  *     sorter routine
  1378.  * Returns: thread node tree
  1379.  */
  1380. THREADNODE *mail_thread_orderedsubject (MAILSTREAM *stream,char *charset,
  1381. SEARCHPGM *spg,long flags,
  1382. sorter_t sorter)
  1383. {
  1384.   THREADNODE *thr = NIL;
  1385.   THREADNODE *cur,*top,**tc;
  1386.   SORTPGM pgm,pgm2;
  1387.   SORTCACHE *s;
  1388.   unsigned long i,j,*lst,*ls;
  1389. /* sort by subject+date */
  1390.   memset (&pgm,0,sizeof (SORTPGM));
  1391.   memset (&pgm2,0,sizeof (SORTPGM));
  1392.   pgm.function = SORTSUBJECT;
  1393.   pgm.next = &pgm2;
  1394.   pgm2.function = SORTDATE;
  1395.   if (lst = (*sorter) (stream,charset,spg,&pgm,flags & ~(SE_FREE | SE_UID))){
  1396.     if (*(ls = lst)) { /* create thread */
  1397. /* note first subject */
  1398.       cur = top = thr = mail_newthreadnode
  1399. ((SORTCACHE *) (*mailcache) (stream,*ls++,CH_SORTCACHE));
  1400. /* note its number */
  1401.       cur->num = (flags & SE_UID) ? mail_uid (stream,*lst) : *lst;
  1402.       i = 1; /* number of threads */
  1403.       while (*ls) { /* build tree */
  1404. /* subjects match? */
  1405. s = (SORTCACHE *) (*mailcache) (stream,*ls++,CH_SORTCACHE);
  1406. if (mail_compare_cstring (top->sc->subject,s->subject)) {
  1407.   i++; /* have a new thread */
  1408.   top = top->branch = cur = mail_newthreadnode (s);
  1409. }
  1410. /* another node on this branch */
  1411. else cur = cur->next = mail_newthreadnode (s);
  1412. /* set to msgno or UID as needed */
  1413. cur->num = (flags & SE_UID) ? mail_uid (stream,s->num) : s->num;
  1414.       }
  1415. /* size of threadnode cache */
  1416.       j = (i + 1) * sizeof (THREADNODE *);
  1417. /* load threadnode cache */
  1418.       tc = (THREADNODE **) memset (fs_get ((size_t) j),0,(size_t) j);
  1419.       for (j = 0, cur = thr; cur; cur = cur->branch) tc[j++] = cur;
  1420.       if (i != j) fatal ("Threadnode cache confusion");
  1421.       qsort ((void *) tc,i,sizeof (THREADNODE *),mail_thread_compare_date);
  1422.       for (j = 0; j < i; j++) tc[j]->branch = tc[j+1];
  1423.       thr = tc[0]; /* head of data */
  1424.       fs_give ((void **) &tc);
  1425.     }
  1426.     fs_give ((void **) &lst);
  1427.   }
  1428.   return thr;
  1429. }
  1430. /* Thread compare date
  1431.  * Accept: first message sort cache element
  1432.  *    second message sort cache element
  1433.  * Returns: -1 if a1 < a2, 0 if a1 == a2, 1 if a1 > a2
  1434.  */
  1435. int mail_thread_compare_date (const void *a1,const void *a2)
  1436. {
  1437.   return mail_compare_ulong ((*(THREADNODE **) a1)->sc->date,
  1438.      (*(THREADNODE **) a2)->sc->date);
  1439. }
  1440. /* Mail parse sequence
  1441.  * Accepts: mail stream
  1442.  *     sequence to parse
  1443.  * Returns: T if parse successful, else NIL
  1444.  */
  1445. long mail_sequence (MAILSTREAM *stream,char *sequence)
  1446. {
  1447.   unsigned long i,j,x;
  1448.   for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->sequence = NIL;
  1449.   while (sequence && *sequence){/* while there is something to parse */
  1450.     if (*sequence == '*') { /* maximum message */
  1451.       if (stream->nmsgs) i = stream->nmsgs;
  1452.       else {
  1453. mm_log ("No messages, so no maximum message number",ERROR);
  1454. return NIL;
  1455.       }
  1456.       sequence++; /* skip past * */
  1457.     }
  1458. /* parse and validate message number */
  1459.     else if (!(i = strtoul ((const char *) sequence,&sequence,10)) ||
  1460.      (i > stream->nmsgs)) {
  1461.       mm_log ("Sequence invalid",ERROR);
  1462.       return NIL;
  1463.     }
  1464.     switch (*sequence) { /* see what the delimiter is */
  1465.     case ':': /* sequence range */
  1466.       if (*++sequence == '*') { /* maximum message */
  1467. if (stream->nmsgs) j = stream->nmsgs;
  1468. else {
  1469.   mm_log ("No messages, so no maximum message number",ERROR);
  1470.   return NIL;
  1471. }
  1472. sequence++; /* skip past * */
  1473.       }
  1474. /* parse end of range */
  1475.       else if (!(j = strtoul ((const char *) sequence,&sequence,10)) ||
  1476.        (j > stream->nmsgs)) {
  1477. mm_log ("Sequence range invalid",ERROR);
  1478. return NIL;
  1479.       }
  1480.       if (*sequence && *sequence++ != ',') {
  1481. mm_log ("Sequence range syntax error",ERROR);
  1482. return NIL;
  1483.       }
  1484.       if (i > j) { /* swap the range if backwards */
  1485. x = i; i = j; j = x;
  1486.       }
  1487. /* mark each item in the sequence */
  1488.       while (i <= j) mail_elt (stream,j--)->sequence = T;
  1489.       break;
  1490.     case ',': /* single message */
  1491.       ++sequence; /* skip the delimiter, fall into end case */
  1492.     case '': /* end of sequence, mark this message */
  1493.       mail_elt (stream,i)->sequence = T;
  1494.       break;
  1495.     default: /* anything else is a syntax error! */
  1496.       mm_log ("Sequence syntax error",ERROR);
  1497.       return NIL;
  1498.     }
  1499.   }
  1500.   return T; /* successfully parsed sequence */
  1501. }
  1502. /* Parse flag list
  1503.  * Accepts: MAIL stream
  1504.  *     flag list as a character string
  1505.  *     pointer to user flags to return
  1506.  * Returns: system flags
  1507.  */
  1508. long mail_parse_flags (MAILSTREAM *stream,char *flag,unsigned long *uf)
  1509. {
  1510.   char *t,*n,*s,tmp[MAILTMPLEN],flg[MAILTMPLEN],key[MAILTMPLEN];
  1511.   short f = 0;
  1512.   long i;
  1513.   short j;
  1514.   *uf = 0; /* initially no user flags */
  1515.   if (flag && *flag) { /* no-op if no flag string */
  1516. /* check if a list and make sure valid */
  1517.     if (((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) ||
  1518. (strlen (flag) >= MAILTMPLEN)) {
  1519.       mm_log ("Bad flag list",ERROR);
  1520.       return NIL;
  1521.     }
  1522. /* copy the flag string w/o list construct */
  1523.     strncpy (n = tmp,flag+i,(j = strlen (flag) - (2*i)));
  1524.     tmp[j] = '';
  1525.     while ((t = n) && *t) { /* parse the flags */
  1526.       i = 0; /* flag not known yet */
  1527. /* find end of flag */
  1528.       if (n = strchr (t,' ')) *n++ = '';
  1529.       ucase (strcpy (flg,t));
  1530.       if (flg[0] == '\') { /* system flag? */
  1531. switch (flg[1]) { /* dispatch based on first character */
  1532. case 'S': /* possible Seen flag */
  1533.   if (flg[2] == 'E' && flg[3] == 'E' && flg[4] == 'N' && !flg[5])
  1534.     i = fSEEN;
  1535.   break;
  1536. case 'D': /* possible Deleted or Draft flag */
  1537.   if (flg[2] == 'E' && flg[3] == 'L' && flg[4] == 'E' &&
  1538.       flg[5] == 'T' && flg[6] == 'E' && flg[7] == 'D' && !flg[8])
  1539.     i = fDELETED;
  1540.   else if (flg[2] == 'R' && flg[3] == 'A' && flg[4] == 'F' &&
  1541.    flg[5] == 'T' && !flg[6]) i = fDRAFT;
  1542.   break;
  1543. case 'F': /* possible Flagged flag */
  1544.   if (flg[2] == 'L' && flg[3] == 'A' && flg[4] == 'G' &&
  1545.       flg[5] == 'G' && flg[6] == 'E' && flg[7] == 'D' && !flg[8])
  1546.     i = fFLAGGED;
  1547.   break;
  1548. case 'A': /* possible Answered flag */
  1549.   if (flg[2] == 'N' && flg[3] == 'S' && flg[4] == 'W' &&
  1550.       flg[5] == 'E' && flg[6] == 'R' && flg[7] == 'E' &&
  1551.       flg[8] == 'D' && !flg[9]) i = fANSWERED;
  1552.   break;
  1553. default: /* unknown */
  1554.   break;
  1555. }
  1556. if (i) f |= i; /* add flag to flags list */
  1557.       }
  1558. /* user flag, search through table */
  1559.       else for (j = 0; !i && j < NUSERFLAGS && (s=stream->user_flags[j]); ++j){
  1560. sprintf (key,"%.900s",s);
  1561. if (!strcmp (flg,ucase (key))) *uf |= i = 1 << j;
  1562.       }
  1563.       if (!i) { /* didn't find a matching flag? */
  1564. /* can we create it? */
  1565. if (stream->kwd_create && (j < NUSERFLAGS)) {
  1566.   *uf |= 1 << j; /* set the bit */
  1567.   stream->user_flags[j] = cpystr (t);
  1568. /* if out of user flags */
  1569.   if (j == NUSERFLAGS - 1) stream->kwd_create = NIL;
  1570. }
  1571. else {
  1572.   sprintf (key,"Unknown flag: %.80s",t);
  1573.   mm_log (key,ERROR);
  1574. }
  1575.       }
  1576.     }
  1577.   }
  1578.   return f;
  1579. }
  1580. /* Mail check network stream for usability with new name
  1581.  * Accepts: MAIL stream
  1582.  *     candidate new name
  1583.  * Returns: T if stream can be used, NIL otherwise
  1584.  */
  1585. long mail_usable_network_stream (MAILSTREAM *stream,char *name)
  1586. {
  1587.   NETMBX smb,nmb;
  1588.   return (stream && stream->dtb && !(stream->dtb->flags & DR_LOCAL) &&
  1589.   mail_valid_net_parse (name,&nmb) &&
  1590.   mail_valid_net_parse (stream->mailbox,&smb) &&
  1591.   !strcmp (lcase (smb.host),lcase (tcp_canonical (nmb.host))) &&
  1592.   !strcmp (smb.service,nmb.service) &&
  1593.   (!nmb.port || (smb.port == nmb.port)) &&
  1594.   (nmb.anoflag == stream->anonymous) &&
  1595.   (!nmb.user[0] || !strcmp (smb.user,nmb.user))) ? LONGT : NIL;
  1596. }
  1597. /* Mail data structure instantiation routines */
  1598. /* Mail instantiate cache elt
  1599.  * Accepts: initial message number
  1600.  * Returns: new cache elt
  1601.  */
  1602. MESSAGECACHE *mail_new_cache_elt (unsigned long msgno)
  1603. {
  1604.   MESSAGECACHE *elt = (MESSAGECACHE *) memset (fs_get (sizeof (MESSAGECACHE)),
  1605.        0,sizeof (MESSAGECACHE));
  1606.   elt->lockcount = 1; /* initially only cache references it */
  1607.   elt->msgno = msgno; /* message number */
  1608.   return elt;
  1609. }
  1610. /* Mail instantiate envelope
  1611.  * Returns: new envelope
  1612.  */
  1613. ENVELOPE *mail_newenvelope (void)
  1614. {
  1615.   return (ENVELOPE *) memset (fs_get (sizeof (ENVELOPE)),0,sizeof (ENVELOPE));
  1616. }
  1617. /* Mail instantiate address
  1618.  * Returns: new address
  1619.  */
  1620. ADDRESS *mail_newaddr (void)
  1621. {
  1622.   return (ADDRESS *) memset (fs_get (sizeof (ADDRESS)),0,sizeof (ADDRESS));
  1623. }
  1624. /* Mail instantiate body
  1625.  * Returns: new body
  1626.  */
  1627. BODY *mail_newbody (void)
  1628. {
  1629.   return mail_initbody ((BODY *) fs_get (sizeof (BODY)));
  1630. }
  1631. /* Mail initialize body
  1632.  * Accepts: body
  1633.  * Returns: body
  1634.  */
  1635. BODY *mail_initbody (BODY *body)
  1636. {
  1637.   memset ((void *) body,0,sizeof (BODY));
  1638.   body->type = TYPETEXT; /* content type */
  1639.   body->encoding = ENC7BIT; /* content encoding */
  1640.   return body;
  1641. }
  1642. /* Mail instantiate body parameter
  1643.  * Returns: new body part
  1644.  */
  1645. PARAMETER *mail_newbody_parameter (void)
  1646. {
  1647.   return (PARAMETER *) memset (fs_get (sizeof(PARAMETER)),0,sizeof(PARAMETER));
  1648. }
  1649. /* Mail instantiate body part
  1650.  * Returns: new body part
  1651.  */
  1652. PART *mail_newbody_part (void)
  1653. {
  1654.   PART *part = (PART *) memset (fs_get (sizeof (PART)),0,sizeof (PART));
  1655.   mail_initbody (&part->body); /* initialize the body */
  1656.   return part;
  1657. }
  1658. /* Mail instantiate body message part
  1659.  * Returns: new body message part
  1660.  */
  1661. MESSAGE *mail_newmsg (void)
  1662. {
  1663.   return (MESSAGE *) memset (fs_get (sizeof (MESSAGE)),0,sizeof (MESSAGE));
  1664. }
  1665. /* Mail instantiate string list
  1666.  * Returns: new string list
  1667.  */
  1668. STRINGLIST *mail_newstringlist (void)
  1669. {
  1670.   return (STRINGLIST *) memset (fs_get (sizeof (STRINGLIST)),0,
  1671. sizeof (STRINGLIST));
  1672. }
  1673. /* Mail instantiate new search program
  1674.  * Returns: new search program
  1675.  */
  1676. SEARCHPGM *mail_newsearchpgm (void)
  1677. {
  1678.   return (SEARCHPGM *) memset (fs_get (sizeof(SEARCHPGM)),0,sizeof(SEARCHPGM));
  1679. }
  1680. /* Mail instantiate new search program
  1681.  * Accepts: header line name   
  1682.  * Returns: new search program
  1683.  */
  1684. SEARCHHEADER *mail_newsearchheader (char *line,char *text)
  1685. {
  1686.   SEARCHHEADER *hdr = (SEARCHHEADER *) memset (fs_get (sizeof (SEARCHHEADER)),
  1687.        0,sizeof (SEARCHHEADER));
  1688.   hdr->line.size = strlen ((char *) (hdr->line.data =
  1689.      (unsigned char *) cpystr (line)));
  1690.   hdr->text.size = strlen ((char *) (hdr->text.data =
  1691.      (unsigned char *) cpystr (text)));
  1692.   return hdr;
  1693. }
  1694. /* Mail instantiate new search set
  1695.  * Returns: new search set
  1696.  */
  1697. SEARCHSET *mail_newsearchset (void)
  1698. {
  1699.   return (SEARCHSET *) memset (fs_get (sizeof(SEARCHSET)),0,sizeof(SEARCHSET));
  1700. }
  1701. /* Mail instantiate new search or
  1702.  * Returns: new search or
  1703.  */
  1704. SEARCHOR *mail_newsearchor (void)
  1705. {
  1706.   SEARCHOR *or = (SEARCHOR *) memset (fs_get (sizeof (SEARCHOR)),0,
  1707.       sizeof (SEARCHOR));
  1708.   or->first = mail_newsearchpgm ();
  1709.   or->second = mail_newsearchpgm ();
  1710.   return or;
  1711. }
  1712. /* Mail instantiate new searchpgmlist
  1713.  * Returns: new searchpgmlist
  1714.  */
  1715. SEARCHPGMLIST *mail_newsearchpgmlist (void)
  1716. {
  1717.   SEARCHPGMLIST *pgl = (SEARCHPGMLIST *)
  1718.     memset (fs_get (sizeof (SEARCHPGMLIST)),0,sizeof (SEARCHPGMLIST));
  1719.   pgl->pgm = mail_newsearchpgm ();
  1720.   return pgl;
  1721. }
  1722. /* Mail instantiate new sortpgm
  1723.  * Returns: new sortpgm
  1724.  */
  1725. SORTPGM *mail_newsortpgm (void)
  1726. {
  1727.   return (SORTPGM *) memset (fs_get (sizeof (SORTPGM)),0,sizeof (SORTPGM));
  1728. }
  1729. /* Mail instantiate new threadnode
  1730.  * Accepts: sort cache for thread node
  1731.  * Returns: new threadnode
  1732.  */
  1733. THREADNODE *mail_newthreadnode (SORTCACHE *sc)
  1734. {
  1735.   THREADNODE *thr = (THREADNODE *) memset (fs_get (sizeof (THREADNODE)),0,
  1736.    sizeof (THREADNODE));
  1737.   if (sc) thr->sc = sc; /* initialize sortcache */
  1738.   return thr;
  1739. }
  1740. /* Mail garbage collection routines */
  1741. /* Mail garbage collect body
  1742.  * Accepts: pointer to body pointer
  1743.  */
  1744. void mail_free_body (BODY **body)
  1745. {
  1746.   if (*body) { /* only free if exists */
  1747.     mail_free_body_data (*body);/* free its data */
  1748.     fs_give ((void **) body); /* return body to free storage */
  1749.   }
  1750. }
  1751. /* Mail garbage collect body data
  1752.  * Accepts: body pointer
  1753.  */
  1754. void mail_free_body_data (BODY *body)
  1755. {
  1756.   switch (body->type) { /* free contents */
  1757.   case TYPEMULTIPART: /* multiple part */
  1758.     mail_free_body_part (&body->nested.part);
  1759.     break;
  1760.   case TYPEMESSAGE: /* encapsulated message */
  1761.     if (body->subtype && !strcmp (body->subtype,"RFC822")) {
  1762.       mail_free_stringlist (&body->nested.msg->lines);
  1763.       mail_gc_msg (body->nested.msg,GC_ENV | GC_TEXTS);
  1764.       fs_give ((void **) &body->nested.msg);
  1765.     }
  1766.     break;
  1767.   default:
  1768.     break;
  1769.   }
  1770.   if (body->subtype) fs_give ((void **) &body->subtype);
  1771.   mail_free_body_parameter (&body->parameter);
  1772.   if (body->id) fs_give ((void **) &body->id);
  1773.   if (body->description) fs_give ((void **) &body->description);
  1774.   if (body->disposition.type) fs_give ((void **) &body->disposition);
  1775.   if (body->disposition.parameter)
  1776.     mail_free_body_parameter (&body->disposition.parameter);
  1777.   if (body->language) mail_free_stringlist (&body->language);
  1778.   if (body->mime.text.data) fs_give ((void **) &body->mime.text.data);
  1779.   if (body->contents.text.data) fs_give ((void **) &body->contents.text.data);
  1780.   if (body->md5) fs_give ((void **) &body->md5);
  1781. }
  1782. /* Mail garbage collect body parameter
  1783.  * Accepts: pointer to body parameter pointer
  1784.  */
  1785. void mail_free_body_parameter (PARAMETER **parameter)
  1786. {
  1787.   if (*parameter) { /* only free if exists */
  1788.     if ((*parameter)->attribute) fs_give ((void **) &(*parameter)->attribute);
  1789.     if ((*parameter)->value) fs_give ((void **) &(*parameter)->value);
  1790. /* run down the list as necessary */
  1791.     mail_free_body_parameter (&(*parameter)->next);
  1792. /* return body part to free storage */
  1793.     fs_give ((void **) parameter);
  1794.   }
  1795. }
  1796. /* Mail garbage collect body part
  1797.  * Accepts: pointer to body part pointer
  1798.  */
  1799. void mail_free_body_part (PART **part)
  1800. {
  1801.   if (*part) { /* only free if exists */
  1802.     mail_free_body_data (&(*part)->body);
  1803. /* run down the list as necessary */
  1804.     mail_free_body_part (&(*part)->next);
  1805.     fs_give ((void **) part); /* return body part to free storage */
  1806.   }
  1807. }
  1808. /* Mail garbage collect message cache
  1809.  * Accepts: mail stream
  1810.  *
  1811.  * The message cache is set to NIL when this function finishes.
  1812.  */
  1813. void mail_free_cache (MAILSTREAM *stream)
  1814. {
  1815. /* do driver specific stuff first */
  1816.   mail_gc (stream,GC_ELT | GC_ENV | GC_TEXTS);
  1817. /* flush the cache */
  1818.   (*mailcache) (stream,(long) 0,CH_INIT);
  1819. }
  1820. /* Mail garbage collect cache element
  1821.  * Accepts: pointer to cache element pointer
  1822.  */
  1823. void mail_free_elt (MESSAGECACHE **elt)
  1824. {
  1825. /* only free if exists and no sharers */
  1826.   if (*elt && !--(*elt)->lockcount) {
  1827.     mail_gc_msg (&(*elt)->private.msg,GC_ENV | GC_TEXTS);
  1828.     fs_give ((void **) elt);
  1829.   }
  1830.   else *elt = NIL; /* else simply drop pointer */
  1831. }
  1832. /* Mail garbage collect envelope
  1833.  * Accepts: pointer to envelope pointer
  1834.  */
  1835. void mail_free_envelope (ENVELOPE **env)
  1836. {
  1837.   if (*env) { /* only free if exists */
  1838.     if ((*env)->remail) fs_give ((void **) &(*env)->remail);
  1839.     mail_free_address (&(*env)->return_path);
  1840.     if ((*env)->date) fs_give ((void **) &(*env)->date);
  1841.     mail_free_address (&(*env)->from);
  1842.     mail_free_address (&(*env)->sender);
  1843.     mail_free_address (&(*env)->reply_to);
  1844.     if ((*env)->subject) fs_give ((void **) &(*env)->subject);
  1845.     mail_free_address (&(*env)->to);
  1846.     mail_free_address (&(*env)->cc);
  1847.     mail_free_address (&(*env)->bcc);
  1848.     if ((*env)->in_reply_to) fs_give ((void **) &(*env)->in_reply_to);
  1849.     if ((*env)->message_id) fs_give ((void **) &(*env)->message_id);
  1850.     if ((*env)->newsgroups) fs_give ((void **) &(*env)->newsgroups);
  1851.     if ((*env)->followup_to) fs_give ((void **) &(*env)->followup_to);
  1852.     if ((*env)->references) fs_give ((void **) &(*env)->references);
  1853.     fs_give ((void **) env); /* return envelope to free storage */
  1854.   }
  1855. }
  1856. /* Mail garbage collect address
  1857.  * Accepts: pointer to address pointer
  1858.  */
  1859. void mail_free_address (ADDRESS **address)
  1860. {
  1861.   if (*address) { /* only free if exists */
  1862.     if ((*address)->personal) fs_give ((void **) &(*address)->personal);
  1863.     if ((*address)->adl) fs_give ((void **) &(*address)->adl);
  1864.     if ((*address)->mailbox) fs_give ((void **) &(*address)->mailbox);
  1865.     if ((*address)->host) fs_give ((void **) &(*address)->host);
  1866.     if ((*address)->error) fs_give ((void **) &(*address)->error);
  1867.     mail_free_address (&(*address)->next);
  1868.     fs_give ((void **) address);/* return address to free storage */
  1869.   }
  1870. }
  1871. /* Mail garbage collect stringlist
  1872.  * Accepts: pointer to stringlist pointer
  1873.  */
  1874. void mail_free_stringlist (STRINGLIST **string)
  1875. {
  1876.   if (*string) { /* only free if exists */
  1877.     if ((*string)->text.data) fs_give ((void **) &(*string)->text.data);
  1878.     mail_free_stringlist (&(*string)->next);
  1879.     fs_give ((void **) string); /* return string to free storage */
  1880.   }
  1881. }
  1882. /* Mail garbage collect searchpgm
  1883.  * Accepts: pointer to searchpgm pointer
  1884.  */
  1885. void mail_free_searchpgm (SEARCHPGM **pgm)
  1886. {
  1887.   if (*pgm) { /* only free if exists */
  1888.     mail_free_searchset (&(*pgm)->msgno);
  1889.     mail_free_searchset (&(*pgm)->uid);
  1890.     mail_free_searchor (&(*pgm)->or);
  1891.     mail_free_searchpgmlist (&(*pgm)->not);
  1892.     mail_free_searchheader (&(*pgm)->header);
  1893.     mail_free_stringlist (&(*pgm)->bcc);
  1894.     mail_free_stringlist (&(*pgm)->body);
  1895.     mail_free_stringlist (&(*pgm)->cc);
  1896.     mail_free_stringlist (&(*pgm)->from);
  1897.     mail_free_stringlist (&(*pgm)->keyword);
  1898.     mail_free_stringlist (&(*pgm)->subject);
  1899.     mail_free_stringlist (&(*pgm)->text);
  1900.     mail_free_stringlist (&(*pgm)->to);
  1901.     fs_give ((void **) pgm); /* return program to free storage */
  1902.   }
  1903. }
  1904. /* Mail garbage collect searchheader
  1905.  * Accepts: pointer to searchheader pointer
  1906.  */
  1907. void mail_free_searchheader (SEARCHHEADER **hdr)
  1908. {
  1909.   if (*hdr) { /* only free if exists */
  1910.     if ((*hdr)->line.data) fs_give ((void **) &(*hdr)->line.data);
  1911.     if ((*hdr)->text.data) fs_give ((void **) &(*hdr)->text.data);
  1912.     mail_free_searchheader (&(*hdr)->next);
  1913.     fs_give ((void **) hdr); /* return header to free storage */
  1914.   }
  1915. }
  1916. /* Mail garbage collect searchset
  1917.  * Accepts: pointer to searchset pointer
  1918.  */
  1919. void mail_free_searchset (SEARCHSET **set)
  1920. {
  1921.   if (*set) { /* only free if exists */
  1922.     mail_free_searchset (&(*set)->next);
  1923.     fs_give ((void **) set); /* return set to free storage */
  1924.   }
  1925. }
  1926. /* Mail garbage collect searchor
  1927.  * Accepts: pointer to searchor pointer
  1928.  */
  1929. void mail_free_searchor (SEARCHOR **orl)
  1930. {
  1931.   if (*orl) { /* only free if exists */
  1932.     mail_free_searchpgm (&(*orl)->first);
  1933.     mail_free_searchpgm (&(*orl)->second);
  1934.     mail_free_searchor (&(*orl)->next);
  1935.     fs_give ((void **) orl); /* return searchor to free storage */
  1936.   }
  1937. }
  1938. /* Mail garbage collect search program list
  1939.  * Accepts: pointer to searchpgmlist pointer
  1940.  */
  1941. void mail_free_searchpgmlist (SEARCHPGMLIST **pgl)
  1942. {
  1943.   if (*pgl) { /* only free if exists */
  1944.     mail_free_searchpgm (&(*pgl)->pgm);
  1945.     mail_free_searchpgmlist (&(*pgl)->next);
  1946.     fs_give ((void **) pgl); /* return searchpgmlist to free storage */
  1947.   }
  1948. }
  1949. /* Mail garbage collect namespace
  1950.  * Accepts: poiner to namespace
  1951.  */
  1952. void mail_free_namespace (NAMESPACE **n)
  1953. {
  1954.   if (*n) {
  1955.     fs_give ((void **) &(*n)->name);
  1956.     mail_free_namespace (&(*n)->next);
  1957.     mail_free_body_parameter (&(*n)->param);
  1958.     fs_give ((void **) n); /* return namespace to free storage */
  1959.   }
  1960. }
  1961. /* Mail garbage collect sort program
  1962.  * Accepts: pointer to sortpgm pointer
  1963.  */
  1964. void mail_free_sortpgm (SORTPGM **pgm)
  1965. {
  1966.   if (*pgm) { /* only free if exists */
  1967.     mail_free_sortpgm (&(*pgm)->next);
  1968.     fs_give ((void **) pgm); /* return sortpgm to free storage */
  1969.   }
  1970. }
  1971. /* Mail garbage collect thread node
  1972.  * Accepts: pointer to threadnode pointer
  1973.  */
  1974. void mail_free_threadnode (THREADNODE **thr)
  1975. {
  1976.   if (*thr) { /* only free if exists */
  1977.     mail_free_threadnode (&(*thr)->branch);
  1978.     mail_free_threadnode (&(*thr)->next);
  1979.     fs_give ((void **) thr); /* return threadnode to free storage */
  1980.   }
  1981. }
  1982. /* Link authenicator
  1983.  * Accepts: authenticator to add to list
  1984.  */
  1985. void auth_link (AUTHENTICATOR *auth)
  1986. {
  1987.   if (!auth->valid || (*auth->valid) ()) {
  1988.     AUTHENTICATOR **a = &mailauthenticators;
  1989.     while (*a) a = &(*a)->next; /* find end of list of authenticators */
  1990.     *a = auth; /* put authenticator at the end */
  1991.     auth->next = NIL; /* this authenticator is the end of the list */
  1992.   }
  1993. }
  1994. /* Authenticate access
  1995.  * Accepts: mechanism name
  1996.  *     responder function
  1997.  *     argument count
  1998.  *     argument vector
  1999.  * Returns: authenticated user name or NIL
  2000.  */
  2001. char *mail_auth (char *mechanism,authresponse_t resp,int argc,char *argv[])
  2002. {
  2003.   char tmp[MAILTMPLEN];
  2004.   AUTHENTICATOR *auth;
  2005. /* cretins still haven't given up */
  2006.   if (strlen (mechanism) >= MAILTMPLEN)
  2007.     syslog (LOG_ALERT|LOG_AUTH,"System break-in attempt, host=%.80s",
  2008.     tcp_clienthost ());
  2009.   else { /* make upper case copy of mechanism name */
  2010.     ucase (strcpy (tmp,mechanism));
  2011.     for (auth = mailauthenticators; auth; auth = auth->next)
  2012.       if (auth->server && !strcmp (auth->name,tmp))
  2013. return (*auth->server) (resp,argc,argv);
  2014.   }
  2015.   return NIL; /* no authenticator found */
  2016. }
  2017. /* Lookup authenticator index
  2018.  * Accepts: authenticator index
  2019.  * Returns: authenticator, or 0 if not found
  2020.  */
  2021. AUTHENTICATOR *mail_lookup_auth (unsigned long i)
  2022. {
  2023.   AUTHENTICATOR *auth = mailauthenticators;
  2024.   while (auth && --i) auth = auth->next;
  2025.   return auth;
  2026. }
  2027. /* Lookup authenticator name
  2028.  * Accepts: authenticator name
  2029.  *     security flag (non-zero to ignore non-secure authenticators)
  2030.  * Returns: index in authenticator chain, or 0 if not found
  2031.  */
  2032. unsigned int mail_lookup_auth_name (char *mechanism,long secflag)
  2033. {
  2034.   char tmp[MAILTMPLEN];
  2035.   int i;
  2036.   AUTHENTICATOR *auth;
  2037.   if (strlen (mechanism) < MAILTMPLEN) {
  2038. /* make upper case copy of mechanism name */
  2039.     ucase (strcpy (tmp,mechanism));
  2040.     for (i = 1, auth = mailauthenticators; auth; i++, auth = auth->next)
  2041.       if (auth->client && (!secflag || auth->secflag) &&
  2042.   !strcmp (auth->name,tmp)) return i;
  2043.   }
  2044.   return 0;
  2045. }
  2046. /* Standard TCP/IP network driver */
  2047. static NETDRIVER tcpdriver = {
  2048.   tcp_open, /* open connection */
  2049.   tcp_aopen, /* open preauthenticated connection */
  2050.   tcp_getline, /* get a line */
  2051.   tcp_getbuffer, /* get a buffer */
  2052.   tcp_soutr, /* output pushed data */
  2053.   tcp_sout, /* output string */
  2054.   tcp_close, /* close connection */
  2055.   tcp_host, /* return host name */
  2056.   tcp_remotehost, /* return remote host name */
  2057.   tcp_port, /* return port number */
  2058.   tcp_localhost /* return local host name */
  2059. };
  2060. /* Network open
  2061.  * Accepts: NETMBX specifier to open
  2062.  *     default network driver
  2063.  *     default port
  2064.  *     alternate network driver
  2065.  *     alternate driver service name
  2066.  *     alternate driver port
  2067.  * Returns: Network stream if success, else NIL
  2068.  */
  2069. NETSTREAM *net_open (NETMBX *mb,NETDRIVER *dv,unsigned long port,
  2070.      NETDRIVER *altd,char *alts,unsigned long altp)
  2071. {
  2072.   NETSTREAM *stream = NIL;
  2073.   char tmp[MAILTMPLEN];
  2074.   if (strlen (mb->host) >= NETMAXHOST) {
  2075.     sprintf (tmp,"Invalid host name: %.80s",mb->host);
  2076.     mm_log (tmp,ERROR);
  2077.   }
  2078. /* use designated driver if given */
  2079.   else if (dv) stream = net_open_work (dv,mb->host,mb->service,port,mb->port,
  2080.        NIL);
  2081.   else if (mb->altflag && altd) /* use alt if altflag lit */
  2082.     stream = net_open_work (altd,mb->host,alts,altp,mb->port,NIL);
  2083. /* if tryaltfirst and can open alt... */
  2084.   else if ((mb->tryaltflag || tryaltfirst) && altd &&
  2085.    (stream = net_open_work (altd,mb->host,alts,altp,mb->port,T)))
  2086.     mb->altflag = T; /* ...light altflag */
  2087. /* default to TCP driver */
  2088.   else stream = net_open_work (&tcpdriver,mb->host,mb->service,port,mb->port,
  2089.        NIL);
  2090.   return stream;
  2091. }
  2092. /* Network open worker routine
  2093.  * Accepts: network driver
  2094.  *     host name
  2095.  *     service name to look up port
  2096.  *     port number if service name not found
  2097.  *     port number to override service name
  2098.  *     flags (non-zero means silent failure)
  2099.  * Returns: Network stream if success, else NIL
  2100.  */
  2101. NETSTREAM *net_open_work (NETDRIVER *dv,char *host,char *service,
  2102.   unsigned long port,unsigned long portoverride,
  2103.   long flags)
  2104. {
  2105.   NETSTREAM *stream = NIL;
  2106.   void *tstream;
  2107.   if (portoverride) { /* explicit port number? */
  2108.     service = NIL; /* yes, override service name */
  2109.     port = portoverride; /* use that instead of default port */
  2110.   }
  2111.   if (flags) port |= 0x80000000;/* want silent open? */
  2112.   if (tstream = (*dv->open) (host,service,port)) {
  2113.     stream = (NETSTREAM *) fs_get (sizeof (NETSTREAM));
  2114.     stream->stream = tstream;
  2115.     stream->dtb = dv;
  2116.   }
  2117.   return stream;
  2118. }
  2119. /* Network authenticated open
  2120.  * Accepts: network driver
  2121.  *     NETMBX specifier
  2122.  *     service specifier
  2123.  *     return user name buffer
  2124.  * Returns: Network stream if success else NIL
  2125.  */
  2126. NETSTREAM *net_aopen (NETDRIVER *dv,NETMBX *mb,char *service,char *user)
  2127. {
  2128.   NETSTREAM *stream = NIL;
  2129.   void *tstream;
  2130.   if (!dv) dv = &tcpdriver; /* default to TCP driver */
  2131.   if (tstream = (*dv->aopen) (mb,service,user)) {
  2132.     stream = (NETSTREAM *) fs_get (sizeof (NETSTREAM));
  2133.     stream->stream = tstream;
  2134.     stream->dtb = dv;
  2135.   }
  2136.   return stream;
  2137. }
  2138. /* Network receive line
  2139.  * Accepts: Network stream
  2140.  * Returns: text line string or NIL if failure
  2141.  */
  2142. char *net_getline (NETSTREAM *stream)
  2143. {
  2144.   return (*stream->dtb->getline) (stream->stream);
  2145. }
  2146. /* Network receive buffer
  2147.  * Accepts: Network stream (must be void * for use as readfn_t)
  2148.  *     size in bytes
  2149.  *     buffer to read into
  2150.  * Returns: T if success, NIL otherwise
  2151.  */
  2152. long net_getbuffer (void *st,unsigned long size,char *buffer)
  2153. {
  2154.   NETSTREAM *stream = (NETSTREAM *) st;
  2155.   return (*stream->dtb->getbuffer) (stream->stream,size,buffer);
  2156. }
  2157. /* Network send null-terminated string
  2158.  * Accepts: Network stream
  2159.  *     string pointer
  2160.  * Returns: T if success else NIL
  2161.  */
  2162. long net_soutr (NETSTREAM *stream,char *string)
  2163. {
  2164.   return (*stream->dtb->soutr) (stream->stream,string);
  2165. }
  2166. /* Network send string
  2167.  * Accepts: Network stream
  2168.  *     string pointer
  2169.  *     byte count
  2170.  * Returns: T if success else NIL
  2171.  */
  2172. long net_sout (NETSTREAM *stream,char *string,unsigned long size)
  2173. {
  2174.   return (*stream->dtb->sout) (stream->stream,string,size);
  2175. }
  2176. /* Network close
  2177.  * Accepts: Network stream
  2178.  */
  2179. void net_close (NETSTREAM *stream)
  2180. {
  2181.   (*stream->dtb->close) (stream->stream);
  2182.   fs_give ((void **) &stream);
  2183. }
  2184. /* Network get host name
  2185.  * Accepts: Network stream
  2186.  * Returns: host name for this stream
  2187.  */
  2188. char *net_host (NETSTREAM *stream)
  2189. {
  2190.   return (*stream->dtb->host) (stream->stream);
  2191. }
  2192. /* Network get remote host name
  2193.  * Accepts: Network stream
  2194.  * Returns: host name for this stream
  2195.  */
  2196. char *net_remotehost (NETSTREAM *stream)
  2197. {
  2198.   return (*stream->dtb->remotehost) (stream->stream);
  2199. }
  2200. /* Network return port for this stream
  2201.  * Accepts: Network stream
  2202.  * Returns: port number for this stream
  2203.  */
  2204. unsigned long net_port (NETSTREAM *stream)
  2205. {
  2206.   return (*stream->dtb->port) (stream->stream);
  2207. }
  2208. /* Network get local host name
  2209.  * Accepts: Network stream
  2210.  * Returns: local host name
  2211.  */
  2212. char *net_localhost (NETSTREAM *stream)
  2213. {
  2214.   return (*stream->dtb->localhost) (stream->stream);
  2215. }