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

网络编程

开发平台:

Unix_Linux

  1. /*
  2.  * Program: News routines
  3.  *
  4.  * Author: Mark Crispin
  5.  * Networks and Distributed Computing
  6.  * Computing & Communications
  7.  * University of Washington
  8.  * Administration Building, AG-44
  9.  * Seattle, WA  98195
  10.  * Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date: 4 September 1991
  13.  * Last Edited: 30 September 1999
  14.  *
  15.  * Copyright 1999 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35. #include <stdio.h>
  36. #include <ctype.h>
  37. #include <errno.h>
  38. extern int errno; /* just in case */
  39. #include "mail.h"
  40. #include "osdep.h"
  41. #include <sys/stat.h>
  42. #include <sys/time.h>
  43. #include "news.h"
  44. #include "misc.h"
  45. #include "newsrc.h"
  46. /* News routines */
  47. /* Driver dispatch used by MAIL */
  48. DRIVER newsdriver = {
  49.   "news", /* driver name */
  50. /* driver flags */
  51.   DR_NEWS|DR_READONLY|DR_NOFAST|DR_NAMESPACE,
  52.   (DRIVER *) NIL, /* next driver */
  53.   news_valid, /* mailbox is valid for us */
  54.   news_parameters, /* manipulate parameters */
  55.   news_scan, /* scan mailboxes */
  56.   news_list, /* find mailboxes */
  57.   news_lsub, /* find subscribed mailboxes */
  58.   news_subscribe, /* subscribe to mailbox */
  59.   news_unsubscribe, /* unsubscribe from mailbox */
  60.   news_create, /* create mailbox */
  61.   news_delete, /* delete mailbox */
  62.   news_rename, /* rename mailbox */
  63.   NIL, /* status of mailbox */
  64.   news_open, /* open mailbox */
  65.   news_close, /* close mailbox */
  66.   news_fast, /* fetch message "fast" attributes */
  67.   news_flags, /* fetch message flags */
  68.   NIL, /* fetch overview */
  69.   NIL, /* fetch message envelopes */
  70.   news_header, /* fetch message header */
  71.   news_text, /* fetch message body */
  72.   NIL, /* fetch partial message text */
  73.   NIL, /* unique identifier */
  74.   NIL, /* message number */
  75.   NIL, /* modify flags */
  76.   news_flagmsg, /* per-message modify flags */
  77.   NIL, /* search for message based on criteria */
  78.   NIL, /* sort messages */
  79.   NIL, /* thread messages */
  80.   news_ping, /* ping mailbox to see if still alive */
  81.   news_check, /* check for new messages */
  82.   news_expunge, /* expunge deleted messages */
  83.   news_copy, /* copy messages to another mailbox */
  84.   news_append, /* append string message to mailbox */
  85.   NIL /* garbage collect stream */
  86. };
  87. /* prototype stream */
  88. MAILSTREAM newsproto = {&newsdriver};
  89. /* News validate mailbox
  90.  * Accepts: mailbox name
  91.  * Returns: our driver if name is valid, NIL otherwise
  92.  */
  93. DRIVER *news_valid (char *name)
  94. {
  95.   int fd;
  96.   char *s,*t,*u;
  97.   struct stat sbuf;
  98.   if ((name[0] == '#') && (name[1] == 'n') && (name[2] == 'e') &&
  99.       (name[3] == 'w') && (name[4] == 's') && (name[5] == '.') &&
  100.       !strchr (name,'/') &&
  101.       !stat ((char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),&sbuf) &&
  102.       ((fd = open ((char *) mail_parameters (NIL,GET_NEWSACTIVE,NIL),O_RDONLY,
  103.    NIL)) >= 0)) {
  104.     fstat (fd,&sbuf); /* get size of active file */
  105. /* slurp in active file */
  106.     read (fd,t = s = (char *) fs_get (sbuf.st_size+1),sbuf.st_size);
  107.     s[sbuf.st_size] = ''; /* tie off file */
  108.     close (fd); /* flush file */
  109.     while (*t && (u = strchr (t,' '))) {
  110.       *u++ = ''; /* tie off at end of name */
  111.       if (!strcmp (name+6,t)) {
  112. fs_give ((void **) &s); /* flush data */
  113. return &newsdriver;
  114.       }
  115.       t = 1 + strchr (u,'n'); /* next line */
  116.     }
  117.     fs_give ((void **) &s); /* flush data */
  118.   }
  119.   return NIL; /* return status */
  120. }
  121. /* News manipulate driver parameters
  122.  * Accepts: function code
  123.  *     function-dependent value
  124.  * Returns: function-dependent return value
  125.  */
  126. void *news_parameters (long function,void *value)
  127. {
  128.   return NIL;
  129. }
  130. /* News scan mailboxes
  131.  * Accepts: mail stream
  132.  *     reference
  133.  *     pattern to search
  134.  *     string to scan
  135.  */
  136. void news_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
  137. {
  138.   char tmp[MAILTMPLEN];
  139.   if (news_canonicalize (ref,pat,tmp))
  140.     mm_log ("Scan not valid for news mailboxes",ERROR);
  141. }
  142. /* News find list of newsgroups
  143.  * Accepts: mail stream
  144.  *     reference
  145.  *     pattern to search
  146.  */
  147. void news_list (MAILSTREAM *stream,char *ref,char *pat)
  148. {
  149.   int fd;
  150.   int i;
  151.   char *s,*t,*u,pattern[MAILTMPLEN],name[MAILTMPLEN];
  152.   struct stat sbuf;
  153.   if (!pat || !*pat) { /* empty pattern? */
  154.     if (news_canonicalize (ref,"*",pattern)) {
  155. /* tie off name at root */
  156.       if (s = strchr (pattern,'.')) *++s = '';
  157.       else pattern[0] = '';
  158.       mm_list (stream,'.',pattern,LATT_NOSELECT);
  159.     }
  160.   }
  161.   if (news_canonicalize (ref,pat,pattern) &&
  162.       !stat ((char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),&sbuf) &&
  163.       ((fd = open ((char *) mail_parameters (NIL,GET_NEWSACTIVE,NIL),O_RDONLY,
  164.    NIL)) >= 0)) {
  165.     fstat (fd,&sbuf); /* get file size and read data */
  166.     read (fd,s = (char *) fs_get (sbuf.st_size + 1),sbuf.st_size);
  167.     close (fd); /* close file */
  168.     s[sbuf.st_size] = ''; /* tie off string */
  169.     strcpy (name,"#news."); /* write initial prefix */
  170.     i = strlen (pattern); /* length of pattern */
  171.     if (pattern[--i] != '%') i = 0;
  172.     if (t = strtok (s,"n")) do if (u = strchr (t,' ')) {
  173.       *u = ''; /* tie off at end of name */
  174.       strcpy (name + 6,t); /* make full form of name */
  175.       if (pmatch_full (name,pattern,'.')) mm_list (stream,'.',name,NIL);
  176.       else if (i && (u = strchr (name + i,'.'))) {
  177. *u = ''; /* tie off at delimiter, see if matches */
  178. if (pmatch_full (name,pattern,'.'))
  179.   mm_list (stream,'.',name,LATT_NOSELECT);
  180.       }
  181.     } while (t = strtok (NIL,"n"));
  182.     fs_give ((void **) &s);
  183.   }
  184. }
  185. /* News find list of subscribed newsgroups
  186.  * Accepts: mail stream
  187.  *     reference
  188.  *     pattern to search
  189.  */
  190. void news_lsub (MAILSTREAM *stream,char *ref,char *pat)
  191. {
  192.   char pattern[MAILTMPLEN];
  193. /* return data from newsrc */
  194.   if (news_canonicalize (ref,pat,pattern)) newsrc_lsub (stream,pattern);
  195. }
  196. /* News canonicalize newsgroup name
  197.  * Accepts: reference
  198.  *     pattern
  199.  *     returned single pattern
  200.  * Returns: T on success, NIL on failure
  201.  */
  202. long news_canonicalize (char *ref,char *pat,char *pattern)
  203. {
  204.   if (ref && *ref) { /* have a reference */
  205.     strcpy (pattern,ref); /* copy reference to pattern */
  206. /* # overrides mailbox field in reference */
  207.     if (*pat == '#') strcpy (pattern,pat);
  208. /* pattern starts, reference ends, with . */
  209.     else if ((*pat == '.') && (pattern[strlen (pattern) - 1] == '.'))
  210.       strcat (pattern,pat + 1); /* append, omitting one of the period */
  211.     else strcat (pattern,pat); /* anything else is just appended */
  212.   }
  213.   else strcpy (pattern,pat); /* just have basic name */
  214.   return ((pattern[0] == '#') && (pattern[1] == 'n') && (pattern[2] == 'e') &&
  215.   (pattern[3] == 'w') && (pattern[4] == 's') && (pattern[5] == '.') &&
  216.   !strchr (pattern,'/')) ? T : NIL;
  217. }
  218. /* News subscribe to mailbox
  219.  * Accepts: mail stream
  220.  *     mailbox to add to subscription list
  221.  * Returns: T on success, NIL on failure
  222.  */
  223. long news_subscribe (MAILSTREAM *stream,char *mailbox)
  224. {
  225.   return news_valid (mailbox) ? newsrc_update (stream,mailbox+6,':') : NIL;
  226. }
  227. /* NEWS unsubscribe to mailbox
  228.  * Accepts: mail stream
  229.  *     mailbox to delete from subscription list
  230.  * Returns: T on success, NIL on failure
  231.  */
  232. long news_unsubscribe (MAILSTREAM *stream,char *mailbox)
  233. {
  234.   return news_valid (mailbox) ? newsrc_update (stream,mailbox+6,'!') : NIL;
  235. }
  236. /* News create mailbox
  237.  * Accepts: mail stream
  238.  *     mailbox name to create
  239.  * Returns: T on success, NIL on failure
  240.  */
  241. long news_create (MAILSTREAM *stream,char *mailbox)
  242. {
  243.   return NIL; /* never valid for News */
  244. }
  245. /* News delete mailbox
  246.  *     mailbox name to delete
  247.  * Returns: T on success, NIL on failure
  248.  */
  249. long news_delete (MAILSTREAM *stream,char *mailbox)
  250. {
  251.   return NIL; /* never valid for News */
  252. }
  253. /* News rename mailbox
  254.  * Accepts: mail stream
  255.  *     old mailbox name
  256.  *     new mailbox name
  257.  * Returns: T on success, NIL on failure
  258.  */
  259. long news_rename (MAILSTREAM *stream,char *old,char *newname)
  260. {
  261.   return NIL; /* never valid for News */
  262. }
  263. /* News status of mailbox default handler
  264.  * Accepts: mail stream
  265.  *     mailbox name
  266.  *     status flags
  267.  * Returns: T on success, NIL on failure
  268.  */
  269. long news_status (MAILSTREAM *stream,char *mbx,long flags)
  270. {
  271.   MAILSTATUS status;
  272.   unsigned long i;
  273.   MAILSTREAM *tstream = NIL;
  274. /* make temporary stream (unless this mbx) */
  275.   if ((!stream || strcmp (stream->mailbox,mbx)) &&
  276.       !(stream = tstream = mail_open (NIL,mbx,OP_READONLY|OP_SILENT)))
  277.     return NIL;
  278.   status.flags = flags; /* return status values */
  279.   status.messages = stream->nmsgs;
  280.   status.recent = stream->recent;
  281.   if (flags & SA_UNSEEN) /* must search to get unseen messages */
  282.     for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
  283.       if (!mail_elt (stream,i)->deleted) status.unseen++;
  284.   status.uidnext = stream->uid_last + 1;
  285.   status.uidvalidity = stream->uid_validity;
  286. /* pass status to main program */
  287.   mm_status (stream,mbx,&status);
  288.   if (tstream) mail_close (tstream);
  289.   return T; /* success */
  290. }
  291. /* News open
  292.  * Accepts: stream to open
  293.  * Returns: stream on success, NIL on failure
  294.  */
  295. MAILSTREAM *news_open (MAILSTREAM *stream)
  296. {
  297.   long i,nmsgs;
  298.   char *s,tmp[MAILTMPLEN];
  299.   struct direct **names;
  300.    /* return prototype for OP_PROTOTYPE call */
  301.   if (!stream) return &newsproto;
  302.   if (stream->local) fatal ("news recycle stream");
  303. /* build directory name */
  304.   sprintf (s = tmp,"%s/%s",(char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),
  305.    stream->mailbox + 6);
  306.   while (s = strchr (s,'.')) *s = '/';
  307. /* scan directory */
  308.   if ((nmsgs = scandir (tmp,&names,news_select,news_numsort)) >= 0) {
  309.     mail_exists (stream,nmsgs); /* notify upper level that messages exist */
  310.     stream->local = fs_get (sizeof (NEWSLOCAL));
  311.     LOCAL->dirty = NIL; /* no update to .newsrc needed yet */
  312.     LOCAL->dir = cpystr (tmp); /* copy directory name for later */
  313. /* make temporary buffer */
  314.     LOCAL->buf = (char *) fs_get ((LOCAL->buflen = MAXMESSAGESIZE) + 1);
  315.     LOCAL->name = cpystr (stream->mailbox + 6);
  316.     for (i = 0; i < nmsgs; ++i) {
  317.       stream->uid_last = mail_elt (stream,i+1)->private.uid =
  318. atoi (names[i]->d_name);
  319.       fs_give ((void **) &names[i]);
  320.     }
  321.     s = (void *) &names; /* stupid language */
  322.     fs_give ((void **) &s); /* free directory */
  323.     LOCAL->cachedtexts = 0; /* no cached texts */
  324.     stream->sequence++; /* bump sequence number */
  325.     stream->rdonly = stream->perm_deleted = T;
  326. /* UIDs are always valid */
  327.     stream->uid_validity = 0xbeefface;
  328. /* read .newsrc entries */
  329.     mail_recent (stream,newsrc_read (LOCAL->name,stream));
  330. /* notify if empty newsgroup */
  331.     if (!(stream->nmsgs || stream->silent)) {
  332.       sprintf (tmp,"Newsgroup %s is empty",LOCAL->name);
  333.       mm_log (tmp,WARN);
  334.     }
  335.   }
  336.   else mm_log ("Unable to scan newsgroup spool directory",ERROR);
  337.   return LOCAL ? stream : NIL; /* if stream is alive, return to caller */
  338. }
  339. /* News file name selection test
  340.  * Accepts: candidate directory entry
  341.  * Returns: T to use file name, NIL to skip it
  342.  */
  343. int news_select (struct direct *name)
  344. {
  345.   char c;
  346.   char *s = name->d_name;
  347.   while (c = *s++) if (!isdigit (c)) return NIL;
  348.   return T;
  349. }
  350. /* News file name comparision
  351.  * Accepts: first candidate directory entry
  352.  *     second candidate directory entry
  353.  * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2
  354.  */
  355. int news_numsort (const void *d1,const void *d2)
  356. {
  357.   return atoi ((*(struct direct **) d1)->d_name) -
  358.     atoi ((*(struct direct **) d2)->d_name);
  359. }
  360. /* News close
  361.  * Accepts: MAIL stream
  362.  *     option flags
  363.  */
  364. void news_close (MAILSTREAM *stream,long options)
  365. {
  366.   if (LOCAL) { /* only if a file is open */
  367.     news_check (stream); /* dump final checkpoint */
  368.     if (LOCAL->dir) fs_give ((void **) &LOCAL->dir);
  369. /* free local scratch buffer */
  370.     if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  371.     if (LOCAL->name) fs_give ((void **) &LOCAL->name);
  372. /* nuke the local data */
  373.     fs_give ((void **) &stream->local);
  374.     stream->dtb = NIL; /* log out the DTB */
  375.   }
  376. }
  377. /* News fetch fast information
  378.  * Accepts: MAIL stream
  379.  *     sequence
  380.  *     option flags
  381.  */
  382. void news_fast (MAILSTREAM *stream,char *sequence,long flags)
  383. {
  384.   unsigned long i,j;
  385. /* ugly and slow */
  386.   if (stream && LOCAL && ((flags & FT_UID) ?
  387.   mail_uid_sequence (stream,sequence) :
  388.   mail_sequence (stream,sequence)))
  389.     for (i = 1; i <= stream->nmsgs; i++)
  390.       if (mail_elt (stream,i)->sequence) news_header (stream,i,&j,NIL);
  391. }
  392. /* News fetch flags
  393.  * Accepts: MAIL stream
  394.  *     sequence
  395.  *     option flags
  396.  */
  397. void news_flags (MAILSTREAM *stream,char *sequence,long flags)
  398. {
  399.   unsigned long i;
  400.   if ((flags & FT_UID) ? /* validate all elts */
  401.       mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))
  402.     for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->valid = T;
  403. }
  404. /* News fetch message header
  405.  * Accepts: MAIL stream
  406.  *     message # to fetch
  407.  *     pointer to returned header text length
  408.  *     option flags
  409.  * Returns: message header in RFC822 format
  410.  */
  411. char *news_header (MAILSTREAM *stream,unsigned long msgno,
  412.    unsigned long *length,long flags)
  413. {
  414.   unsigned long i,hdrsize;
  415.   int fd;
  416.   char *t;
  417.   struct stat sbuf;
  418.   struct tm *tm;
  419.   MESSAGECACHE *elt;
  420.   *length = 0; /* default to empty */
  421.   if (flags & FT_UID) return "";/* UID call "impossible" */
  422. /* get elt */
  423.   (elt = mail_elt (stream,msgno))->valid = T;
  424.   if (!elt->private.msg.header.text.data) {
  425. /* purge cache if too big */
  426.     if (LOCAL->cachedtexts > max (stream->nmsgs * 4096,2097152)) {
  427.       mail_gc (stream,GC_TEXTS);/* just can't keep that much */
  428.       LOCAL->cachedtexts = 0;
  429.     }
  430. /* build message file name */
  431.     sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
  432.     if ((fd = open (LOCAL->buf,O_RDONLY,NIL)) < 0) return "";
  433.     fstat (fd,&sbuf); /* get size of message */
  434. /* make plausible IMAPish date string */
  435.     tm = gmtime (&sbuf.st_mtime);
  436.     elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
  437.     elt->year = tm->tm_year + 1900 - BASEYEAR;
  438.     elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
  439.     elt->seconds = tm->tm_sec;
  440.     elt->zhours = 0; elt->zminutes = 0;
  441. /* is buffer big enough? */
  442.     if (sbuf.st_size > LOCAL->buflen) {
  443.       fs_give ((void **) &LOCAL->buf);
  444.       LOCAL->buf = (char *) fs_get ((LOCAL->buflen = sbuf.st_size) + 1);
  445.     }
  446. /* slurp message */
  447.     read (fd,LOCAL->buf,sbuf.st_size);
  448. /* tie off file */
  449.     LOCAL->buf[sbuf.st_size] = '';
  450.     close (fd); /* flush message file */
  451. /* find end of header */
  452.     for (i = 0,t = LOCAL->buf; *t && !(i && (*t == 'n')); i = (*t++ == 'n'));
  453. /* number of header bytes */
  454.     hdrsize = (*t ? ++t : t) - LOCAL->buf;
  455.     elt->rfc822_size = /* size of entire message in CRLF form */
  456.       (elt->private.msg.header.text.size =
  457.        strcrlfcpy ((char **) &elt->private.msg.header.text.data,&i,LOCAL->buf,
  458.    hdrsize)) +
  459.  (elt->private.msg.text.text.size =
  460.   strcrlfcpy ((char **) &elt->private.msg.text.text.data,&i,t,
  461.       sbuf.st_size - hdrsize));
  462. /* add to cached size */
  463.     LOCAL->cachedtexts += elt->rfc822_size;
  464.   }
  465.   *length = elt->private.msg.header.text.size;
  466.   return (char *) elt->private.msg.header.text.data;
  467. }
  468. /* News fetch message text (body only)
  469.  * Accepts: MAIL stream
  470.  *     message # to fetch
  471.  *     pointer to returned stringstruct
  472.  *     option flags
  473.  * Returns: T on success, NIL on failure
  474.  */
  475. long news_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
  476. {
  477.   unsigned long i;
  478.   MESSAGECACHE *elt;
  479. /* UID call "impossible" */
  480.   if (flags & FT_UID) return NIL;
  481.   elt = mail_elt (stream,msgno);/* get elt */
  482. /* snarf message if don't have it yet */
  483.   if (!elt->private.msg.text.text.data) {
  484.     news_header (stream,msgno,&i,flags);
  485.     if (!elt->private.msg.text.text.data) return NIL;
  486.   }
  487.   if (!(flags & FT_PEEK)) { /* mark as seen */
  488.     mail_elt (stream,msgno)->seen = T;
  489.     mm_flags (stream,msgno);
  490.   }
  491.   if (!elt->private.msg.text.text.data) return NIL;
  492.   INIT (bs,mail_string,elt->private.msg.text.text.data,
  493. elt->private.msg.text.text.size);
  494.   return T;
  495. }
  496. /* News per-message modify flag
  497.  * Accepts: MAIL stream
  498.  *     message cache element
  499.  */
  500. void news_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
  501. {
  502.   if (!LOCAL->dirty) { /* only bother checking if not dirty yet */
  503.     if (elt->valid) { /* if done, see if deleted changed */
  504.       if (elt->sequence != elt->deleted) LOCAL->dirty = T;
  505.       elt->sequence = T; /* leave the sequence set */
  506.     }
  507. /* note current setting of deleted flag */
  508.     else elt->sequence = elt->deleted;
  509.   }
  510. }
  511. /* News ping mailbox
  512.  * Accepts: MAIL stream
  513.  * Returns: T if stream alive, else NIL
  514.  */
  515. long news_ping (MAILSTREAM *stream)
  516. {
  517.   return T; /* always alive */
  518. }
  519. /* News check mailbox
  520.  * Accepts: MAIL stream
  521.  */
  522. void news_check (MAILSTREAM *stream)
  523. {
  524. /* never do if no updates */
  525.   if (LOCAL->dirty) newsrc_write (LOCAL->name,stream);
  526.   LOCAL->dirty = NIL;
  527. }
  528. /* News expunge mailbox
  529.  * Accepts: MAIL stream
  530.  */
  531. void news_expunge (MAILSTREAM *stream)
  532. {
  533.   if (!stream->silent) mm_log ("Expunge ignored on news",NIL);
  534. }
  535. /* News copy message(s)
  536.  * Accepts: MAIL stream
  537.  *     sequence
  538.  *     destination mailbox
  539.  *     option flags
  540.  * Returns: T if copy successful, else NIL
  541.  */
  542. long news_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
  543. {
  544.   mailproxycopy_t pc =
  545.     (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
  546.   if (pc) return (*pc) (stream,sequence,mailbox,options);
  547.   mm_log ("Copy not valid for News",ERROR);
  548.   return NIL;
  549. }
  550. /* News append message from stringstruct
  551.  * Accepts: MAIL stream
  552.  *     destination mailbox
  553.  *     stringstruct of messages to append
  554.  * Returns: T if append successful, else NIL
  555.  */
  556. long news_append (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
  557.   STRING *message)
  558. {
  559.   mm_log ("Append not valid for News",ERROR);
  560.   return NIL;
  561. }