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

网络编程

开发平台:

Unix_Linux

  1. /*
  2.  * Program: Newsrc manipulation 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: 12 September 1994
  13.  * Last Edited: 28 May 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 available
  24.  * "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 <ctype.h>
  36. #include <stdio.h>
  37. #include "mail.h"
  38. #include "osdep.h"
  39. #include "misc.h"
  40. #include "newsrc.h"
  41. #ifndef OLDFILESUFFIX
  42. #define OLDFILESUFFIX ".old"
  43. #endif
  44. /* Error message
  45.  * Accepts: message format
  46.  *     additional message string
  47.  *     message level
  48.  * Returns: NIL, always
  49.  */
  50. long newsrc_error (char *fmt,char *text,long errflg)
  51. {
  52.   char tmp[MAILTMPLEN];
  53.   sprintf (tmp,fmt,text);
  54.   mm_log (tmp,errflg);
  55.   return NIL;
  56. }
  57. /* Write error message
  58.  * Accepts: newsrc name
  59.  *     file designator
  60.  *     file designator
  61.  * Returns: NIL, always
  62.  */
  63. long newsrc_write_error (char *name,FILE *f1,FILE *f2)
  64. {
  65.   fclose (f1); /* close file designators */
  66.   fclose (f2);
  67.   return newsrc_error ("Error writing to %s",name,ERROR);
  68. }
  69. /* Create newsrc file in local form
  70.  * Accepts: MAIL stream
  71.  *     notification flag
  72.  * Returns: file designator of newsrc
  73.  */
  74. FILE *newsrc_create (MAILSTREAM *stream,int notify)
  75. {
  76.   char *newsrc = (char *) mail_parameters (stream,GET_NEWSRC,NIL);
  77.   FILE *f = fopen (newsrc,"wb");
  78.   if (!f) newsrc_error ("Unable to create news state %s",newsrc,ERROR);
  79.   else if (notify) newsrc_error ("Creating news state %s",newsrc,WARN);
  80.   return f;
  81. }
  82. /* Write new state in newsrc
  83.  * Accepts: file designator of newsrc
  84.  *     group
  85.  *     new subscription status character
  86.  *     newline convention
  87.  * Returns: T if successful, NIL otherwise
  88.  */
  89. long newsrc_newstate (FILE *f,char *group,char state,char *nl)
  90. {
  91.   return (f && (fputs (group,f) != EOF) && ((putc (state,f)) != EOF) &&
  92.   ((putc (' ',f)) != EOF) && (fputs (nl,f) != EOF) &&
  93.   (fclose (f) != EOF)) ? LONGT : NIL;
  94. }
  95. /* Write messages in newsrc
  96.  * Accepts: file designator of newsrc
  97.  *     MAIL stream
  98.  *     message number/newsgroup message map
  99.  *     newline convention
  100.  * Returns: T if successful, NIL otherwise
  101.  */
  102. long newsrc_newmessages (FILE *f,MAILSTREAM *stream,char *nl)
  103. {
  104.   unsigned long i,j,k;
  105.   char tmp[MAILTMPLEN];
  106.   MESSAGECACHE *elt;
  107.   int c = ' ';
  108.   for (i = 1,j = 1,k = 0; i <= stream->nmsgs; ++i) {
  109. /* deleted message? */
  110.     if ((elt = mail_elt (stream,i))->deleted) {
  111.       k = elt->private.uid; /* this is the top of the current range */
  112.       if (!j) j = k; /* if no range in progress, start one */
  113.     }
  114.     else if (j) { /* unread message, ending a range */
  115. /* calculate end of range */
  116.       if (k = elt->private.uid - 1) {
  117. /* dump range */
  118. sprintf (tmp,(j == k) ? "%c%ld" : "%c%ld-%ld",c,j,k);
  119. if (fputs (tmp,f) == EOF) return NIL;
  120. c = ','; /* need a comma after the first time */
  121.       }
  122.       j = 0; /* no more range in progress */
  123.     }
  124.   }
  125.   if (j) { /* dump trailing range */
  126.     sprintf (tmp,(j == k) ? "%c%ld" : "%c%ld-%ld",c,j,k);
  127.     if (fputs (tmp,f) == EOF) return NIL;
  128.   }
  129. /* write trailing newline, return */
  130.   return (fputs (nl,f) == EOF) ? NIL : LONGT;
  131. }
  132. /* List subscribed newsgroups
  133.  * Accepts: MAIL stream
  134.  *     prefix to append name
  135.  *      pattern to search
  136.  */
  137. void newsrc_lsub (MAILSTREAM *stream,char *pattern)
  138. {
  139.   char *s,*t,*lcl,name[MAILTMPLEN];
  140.   int c = ' ';
  141.   int showuppers = pattern[strlen (pattern) - 1] == '%';
  142.   FILE *f = fopen ((char *) mail_parameters (stream,GET_NEWSRC,NIL),"rb");
  143.   if (f) { /* got file? */
  144. /* remote name? */
  145.     if (*(lcl = strcpy (name,pattern)) == '{') lcl = strchr (lcl,'}') + 1;
  146.     if (*lcl == '#') lcl += 6; /* namespace format name? */
  147.     while (c != EOF) { /* yes, read newsrc */
  148.       for (s = lcl; (s < (name + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
  149.    (c != ':') && (c != '!') && (c != '15') && (c != '12');
  150.    *s++ = c);
  151.       if (c == ':') { /* found a subscribed newsgroup? */
  152. *s = ''; /* yes, tie off name */
  153. /* report if match */
  154. if (pmatch_full (name,pattern,'.')) mm_lsub (stream,'.',name,NIL);
  155. else while (showuppers && (t = strrchr (lcl,'.'))) {
  156.   *t = ''; /* tie off the name */
  157.   if (pmatch_full (name,pattern,'.'))
  158.     mm_lsub (stream,'.',name,LATT_NOSELECT);
  159. }
  160.       }
  161.       while ((c != '15') && (c != '12') && (c != EOF)) c = getc (f);
  162.     }
  163.     fclose (f);
  164.   }
  165. }
  166. /* Update subscription status of newsrc
  167.  * Accepts: MAIL stream
  168.  *      group
  169.  *     new subscription status character
  170.  * Returns: T if successful, NIL otherwise
  171.  */
  172. long newsrc_update (MAILSTREAM *stream,char *group,char state)
  173. {
  174.   char tmp[MAILTMPLEN];
  175.   char *newsrc = (char *) mail_parameters (stream,GET_NEWSRC,NIL);
  176.   long ret = NIL;
  177.   FILE *f = fopen (newsrc,"r+b");
  178.   if (f) { /* update existing file */
  179.     int c = 0;
  180.     char *s,nl[3];
  181.     long pos = 0;
  182.     nl[0] = nl[1] = nl[2]=''; /* no newline known yet */
  183.     do { /* read newsrc */
  184.       for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
  185.    (c != ':') && (c != '!') && (c != '15') && (c != '12');
  186.    *s++ = c) pos = ftell (f);
  187.       *s = ''; /* tie off name */
  188. /* found the newsgroup? */
  189.       if (((c == ':') || (c == '!')) && !strcmp (tmp,group)) {
  190. if (c == state) { /* already at that state? */
  191.   if (c == ':') newsrc_error ("Already subscribed to %s",group,WARN);
  192.   ret = LONGT; /* noop the update */
  193. }
  194. /* write the character */
  195. else if (!fseek (f,pos,0)) ret = ((putc (state,f)) == EOF) ? NIL:LONGT;
  196. if (fclose (f) == EOF) ret = NIL;
  197. f = NIL; /* done with file */
  198. break;
  199.       }
  200. /* gobble remainder of this line */
  201.       while ((c != '15') && (c != '12') && (c != EOF)) c = getc (f);
  202. /* need to know about newlines and found it? */
  203.       if (!nl[0] && ((c == '15') || (c == '12')) && ((nl[0]=c) == '15')){
  204. /* sniff and see if an LF */
  205. if ((c = getc (f)) == '12') nl[1] = c;
  206. else ungetc (c,f); /* nope, push it back */
  207.       }
  208.     } while (c != EOF);
  209.     if (f) { /* still haven't written it yet? */
  210.       if (nl[0]) { /* know its newline convention? */
  211. fseek (f,0L,2); /* yes, seek to end of file */
  212. ret = newsrc_newstate (f,group,state,nl);
  213.       }
  214.       else { /* can't find a newline convention */
  215. fclose (f); /* punt the file */
  216. /* can't win if read something */
  217. if (pos) newsrc_error("Unknown newline convention in %s",newsrc,ERROR);
  218. /* file must have been empty, rewrite it */
  219. else ret = newsrc_newstate(newsrc_create(stream,NIL),group,state,"n");
  220.       }
  221.     }
  222.   }
  223. /* create new file */
  224.   else ret = newsrc_newstate (newsrc_create (stream,T),group,state,"n");
  225.   return ret; /* return with update status */
  226. }
  227. /* Update newsgroup status in stream
  228.  * Accepts: newsgroup name
  229.  *     MAIL stream
  230.  * Returns: number of recent messages
  231.  */
  232. long newsrc_read (char *group,MAILSTREAM *stream)
  233. {
  234.   int c = 0;
  235.   char *s,tmp[MAILTMPLEN];
  236.   unsigned long i,j;
  237.   MESSAGECACHE *elt;
  238.   unsigned long m = 1,recent = 0,unseen = 0;
  239.   FILE *f = fopen ((char *) mail_parameters (stream,GET_NEWSRC,NIL),"rb");
  240.   if (f) do { /* read newsrc */
  241.     for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
  242.  (c != ':') && (c != '!') && (c != '15') && (c != '12'); *s++ = c);
  243.     *s = ''; /* tie off name */
  244.     if ((c==':') || (c=='!')) { /* found newsgroup? */
  245.       if (strcmp (tmp,group)) /* group name match? */
  246. while ((c != '15') && (c != '12') && (c != EOF)) c = getc (f);
  247.       else { /* yes, skip leading whitespace */
  248. while ((c = getc (f)) == ' ');
  249. /* only if unprocessed messages */
  250. if (stream->nmsgs) while (f && (m <= stream->nmsgs)) {
  251. /* collect a number */
  252.   if (isdigit (c)) { /* better have a number */
  253.     for (i = 0,j = 0; isdigit (c); c = getc (f)) i = i*10 + (c-'0');
  254.     if (c == '-') for (c = getc (f); isdigit (c); c = getc (f))
  255.       j = j*10 +(c-'0');/* collect second value if range */
  256.     if (!unseen && (mail_elt (stream,m)->private.uid < i)) unseen = m;
  257. /* skip messages before first value */
  258.     while ((m <= stream->nmsgs) &&
  259.    (mail_elt (stream,m)->private.uid < i)) m++;
  260. /* do all messages in range */
  261.     while ((m <= stream->nmsgs) && (elt = mail_elt (stream,m)) &&
  262.    (j ? ((elt->private.uid >= i) && (elt->private.uid <= j)) :
  263.     (elt->private.uid == i)) && m++)
  264.       elt->valid = elt->deleted = T;
  265.   }
  266.   switch (c) { /* what is the delimiter? */
  267.   case ',': /* more to come */
  268.     c = getc (f); /* get first character of number */
  269.     break;
  270.   default: /* bogus character */
  271.     sprintf (tmp,"Bogus character 0x%x in news state",(unsigned int)c);
  272.     mm_log (tmp,ERROR);
  273.   case EOF: case '15': case '12':
  274.     fclose (f); /* all done - close the file */
  275.     f = NIL;
  276.     break;
  277.   }
  278. }
  279. else { /* empty newsgroup */
  280.   while ((c != '15') && (c != '12') && (c != EOF)) c = getc (f);
  281.   fclose (f); /* all done - close the file */
  282.   f = NIL;
  283. }
  284.       }
  285.     }
  286.   } while (f && (c != EOF)); /* until file closed or EOF */
  287.   if (f) { /* still have file open? */
  288.     sprintf (tmp,"No state for newsgroup %s found, reading as new",group);
  289.     mm_log (tmp,WARN);
  290.     fclose (f); /* close the file */
  291.   }
  292.   if (m <= stream->nmsgs) { /* any messages beyond newsrc range? */
  293.     if (!unseen) unseen = m; /* then this must be the first unseen one */
  294.     do {
  295.       elt = mail_elt (stream,m++);
  296.       elt->valid = elt->recent = T;
  297.       ++recent; /* count another recent message */
  298.     }
  299.     while (m <= stream->nmsgs);
  300.   }
  301.   if (unseen) { /* report first unseen message */
  302.     sprintf (tmp,"[UNSEEN] %lu is first unseen message in %s",unseen,group);
  303.     mm_notify (stream,tmp,(long) NIL);
  304.   }
  305.   return recent;
  306. }
  307. /* Update newsgroup entry in newsrc
  308.  * Accepts: newsgroup name
  309.  *     MAIL stream
  310.  * Returns: T if successful, NIL otherwise
  311.  */
  312. long newsrc_write (char *group,MAILSTREAM *stream)
  313. {
  314.   int c = 0,d = EOF;
  315.   char *newsrc = (char *) mail_parameters (stream,GET_NEWSRC,NIL);
  316.   char *s,tmp[MAILTMPLEN],backup[MAILTMPLEN],nl[3];
  317.   FILE *f,*bf;
  318.   nl[0] = nl[1] = nl[2] = ''; /* no newline known yet */
  319.   if (f = fopen (newsrc,"rb")) {/* have existing newsrc file? */
  320.     if (!(bf = fopen ((strcat (strcpy (backup,newsrc),OLDFILESUFFIX)),"wb"))) {
  321.       fclose (f); /* punt input file */
  322.       return newsrc_error ("Can't create backup news state %s",backup,ERROR);
  323.     }
  324. /* copy to backup file */
  325.     while ((c = getc (f)) != EOF) {
  326. /* need to know about newlines and found it? */
  327.       if (!nl[0] && ((c == '15') || (c == '12')) && ((nl[0]=c) == '15')){
  328. /* sniff and see if an LF */
  329. if ((c = getc (f)) == '12') nl[1] = c;
  330. ungetc (c,f); /* push it back */
  331.       }
  332. /* write to backup file */
  333.       if ((d = putc (c,bf)) == EOF) {
  334. fclose (f); /* punt input file */
  335. return newsrc_error("Error writing backup news state %s",newsrc,ERROR);
  336.       }
  337.     }
  338.     fclose (f); /* close existing file */
  339.     if (fclose (bf) == EOF) /* and backup file */
  340.       return newsrc_error ("Error closing backup news state %s",newsrc,ERROR);
  341.     if (d == EOF) { /* open for write if empty file */
  342.       if (f = newsrc_create (stream,NIL)) bf = NIL;
  343.       else return NIL;
  344.     }
  345.     else if (!nl[0]) /* make sure newlines valid */
  346.       return newsrc_error ("Unknown newline convention in %s",newsrc,ERROR);
  347. /* now read backup file */
  348.     else if (!(bf = fopen (backup,"rb")))
  349.       return newsrc_error ("Error reading backup news state %s",backup,ERROR);
  350. /* open newsrc for writing */
  351.     else if (!(f = fopen (newsrc,"wb"))) {
  352.       fclose (bf); /* punt backup */
  353.       return newsrc_error ("Can't rewrite news state %s",newsrc,ERROR);
  354.     }
  355.   }
  356.   else { /* create new newsrc file */
  357.     if (f = newsrc_create (stream,T)) bf = NIL;
  358.     else return NIL; /* can't create newsrc */
  359.   }
  360.   
  361.   while (bf) { /* read newsrc */
  362.     for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (bf)) != EOF) &&
  363.  (c != ':') && (c != '!') && (c != '15') && (c != '12'); *s++ = c);
  364.     *s = ''; /* tie off name */
  365. /* saw correct end of group delimiter? */
  366.     if (tmp[0] && ((c == ':') || (c == '!'))) {
  367. /* yes, write newsgroup name and delimiter */
  368.       if ((tmp[0] && (fputs (tmp,f) == EOF)) || ((putc (c,f)) == EOF))
  369. return newsrc_write_error (newsrc,bf,f);
  370.       if (!strcmp (tmp,group)) {/* found correct group? */
  371. /* yes, write new status */
  372. if (!newsrc_newmessages (f,stream,nl[0] ? nl : "n"))
  373.   return newsrc_write_error (newsrc,bf,f);
  374. /* skip past old data */
  375. while (((c = getc (bf)) != EOF) && (c != '15') && (c != '12'));
  376. /* skip past newline */
  377. while ((c == '15') || (c == '12')) c = getc (bf);
  378. while (c != EOF) { /* copy remainder of file */
  379.   if (putc (c,f) == EOF) return newsrc_write_error (newsrc,bf,f);
  380.   c = getc (bf); /* get next character */
  381. }
  382. /* done with file */
  383. if (fclose (f) == EOF) return newsrc_write_error (newsrc,bf,f);
  384. f = NIL;
  385.       }
  386. /* copy remainder of line */
  387.       else while (((c = getc (bf)) != EOF) && (c != '15') && (c != '12'))
  388. if (putc (c,f) == EOF) return newsrc_write_error (newsrc,bf,f);
  389.       if (c == '15') { /* write CR if seen */
  390. if (putc (c,f) == EOF) return newsrc_write_error (newsrc,bf,f);
  391. /* sniff to see if LF */
  392. if (((c = getc (bf)) != EOF) && (c != '12')) ungetc (c,bf);
  393.       }
  394. /* write LF if seen */
  395.       if ((c == '12') && (putc (c,f) == EOF))
  396. return newsrc_write_error (newsrc,bf,f);
  397.     }
  398.     if (c == EOF) { /* hit end of file? */
  399.       fclose (bf); /* yup, close the file */
  400.       bf = NIL;
  401.     }
  402.   }
  403.   if (f) { /* still have newsrc file open? */
  404.     if ((fputs (group,f) == EOF) || ((putc (':',f)) == EOF) ||
  405. (!newsrc_newmessages (f,stream,nl[0] ? nl : "n")) ||
  406. (fclose (f) == EOF)) return newsrc_write_error (newsrc,bf,f);
  407.   }
  408.   return LONGT;
  409. }
  410. /* Get newsgroup state as text stream
  411.  * Accepts: MAIL stream
  412.  *     newsgroup name
  413.  * Returns: string containing newsgroup state, or NIL if not found
  414.  */
  415. char *newsrc_state (MAILSTREAM *stream,char *group)
  416. {
  417.   int c = 0;
  418.   char *s,tmp[MAILTMPLEN];
  419.   long pos;
  420.   size_t size;
  421.   FILE *f = fopen ((char *) mail_parameters (stream,GET_NEWSRC,NIL),"rb");
  422.   if (f) do { /* read newsrc */
  423.     for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
  424.  (c != ':') && (c != '!') && (c != '15') && (c != '12'); *s++ = c);
  425.     *s = ''; /* tie off name */
  426.     if ((c==':') || (c=='!')) { /* found newsgroup? */
  427.       if (strcmp (tmp,group)) /* group name match? */
  428. while ((c != '15') && (c != '12') && (c != EOF)) c = getc (f);
  429.       else { /* yes, skip leading whitespace */
  430. do pos = ftell (f);
  431. while ((c = getc (f)) == ' ');
  432. /* count characters in state */
  433. for (size = 0; (c != '15') && (c != '12') && (c != EOF); size++)
  434.   c = getc (f);
  435. /* now copy it */
  436. s = (char *) fs_get (size + 1);
  437. fseek (f,pos,L_SET);
  438. fread (s,(size_t) 1,size,f);
  439. s[size] = ''; /* tie off string */
  440. fclose (f); /* all done - close the file */
  441. return s;
  442.       }
  443.     }
  444.   } while (f && (c != EOF)); /* until file closed or EOF */
  445.   sprintf (tmp,"No state for newsgroup %s found",group);
  446.   mm_log (tmp,WARN);
  447.   if (f) fclose (f); /* close the file */
  448.   return NIL; /* not found return */
  449. }
  450. /* Check UID in newsgroup state
  451.  * Accepts: newsgroup state string
  452.  *     uid
  453.  *     returned recent count
  454.  *     returned unseen count
  455.  */
  456. void newsrc_check_uid (char *state,unsigned long uid,unsigned long *recent,
  457.        unsigned long *unseen)
  458. {
  459.   unsigned long i,j;
  460.   while (*state) { /* until run out of state string */
  461. /* collect a number */
  462.     for (i = 0; isdigit (*state); i = i*10 + (*state++ - '0'));
  463.     if (*state != '-') j = i; /* coerce single mesage into range */
  464.     else { /* have a range */
  465.       for (j = 0; isdigit (*++state); j = j*10 + (*state - '0'));
  466.       if (j < i) return; /* bogon if end less than start */
  467.     }
  468.     if (*state == ',') state++; /* skip past comma */
  469.     else if (*state) return; /* otherwise it's a bogon */
  470.     if (uid <= j) { /* covered by upper bound? */
  471.       if (uid < i) ++*unseen; /* unseen if not covered by lower bound */
  472.       return; /* don't need to look further */
  473.     }
  474.   }
  475.   ++*unseen; /* not found in any range, message is unseen */
  476.   ++*recent; /* and recent */
  477. }