newsrc.c
上传用户:ycwykj01
上传日期:2007-01-04
资源大小:1819k
文件大小:17k
- /*
- * Program: Newsrc manipulation routines
- *
- * Author: Mark Crispin
- * Networks and Distributed Computing
- * Computing & Communications
- * University of Washington
- * Administration Building, AG-44
- * Seattle, WA 98195
- * Internet: MRC@CAC.Washington.EDU
- *
- * Date: 12 September 1994
- * Last Edited: 28 May 1999
- *
- * Copyright 1999 by the University of Washington
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation for any purpose and without fee is hereby granted, provided
- * that the above copyright notice appears in all copies and that both the
- * above copyright notice and this permission notice appear in supporting
- * documentation, and that the name of the University of Washington not be
- * used in advertising or publicity pertaining to distribution of the software
- * without specific, written prior permission. This software is made available
- * "as is", and
- * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
- * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
- * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
- * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
- * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
- * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- */
- #include <ctype.h>
- #include <stdio.h>
- #include "mail.h"
- #include "osdep.h"
- #include "misc.h"
- #include "newsrc.h"
- #ifndef OLDFILESUFFIX
- #define OLDFILESUFFIX ".old"
- #endif
- /* Error message
- * Accepts: message format
- * additional message string
- * message level
- * Returns: NIL, always
- */
- long newsrc_error (char *fmt,char *text,long errflg)
- {
- char tmp[MAILTMPLEN];
- sprintf (tmp,fmt,text);
- mm_log (tmp,errflg);
- return NIL;
- }
- /* Write error message
- * Accepts: newsrc name
- * file designator
- * file designator
- * Returns: NIL, always
- */
- long newsrc_write_error (char *name,FILE *f1,FILE *f2)
- {
- fclose (f1); /* close file designators */
- fclose (f2);
- return newsrc_error ("Error writing to %s",name,ERROR);
- }
- /* Create newsrc file in local form
- * Accepts: MAIL stream
- * notification flag
- * Returns: file designator of newsrc
- */
- FILE *newsrc_create (MAILSTREAM *stream,int notify)
- {
- char *newsrc = (char *) mail_parameters (stream,GET_NEWSRC,NIL);
- FILE *f = fopen (newsrc,"wb");
- if (!f) newsrc_error ("Unable to create news state %s",newsrc,ERROR);
- else if (notify) newsrc_error ("Creating news state %s",newsrc,WARN);
- return f;
- }
- /* Write new state in newsrc
- * Accepts: file designator of newsrc
- * group
- * new subscription status character
- * newline convention
- * Returns: T if successful, NIL otherwise
- */
- long newsrc_newstate (FILE *f,char *group,char state,char *nl)
- {
- return (f && (fputs (group,f) != EOF) && ((putc (state,f)) != EOF) &&
- ((putc (' ',f)) != EOF) && (fputs (nl,f) != EOF) &&
- (fclose (f) != EOF)) ? LONGT : NIL;
- }
- /* Write messages in newsrc
- * Accepts: file designator of newsrc
- * MAIL stream
- * message number/newsgroup message map
- * newline convention
- * Returns: T if successful, NIL otherwise
- */
- long newsrc_newmessages (FILE *f,MAILSTREAM *stream,char *nl)
- {
- unsigned long i,j,k;
- char tmp[MAILTMPLEN];
- MESSAGECACHE *elt;
- int c = ' ';
- for (i = 1,j = 1,k = 0; i <= stream->nmsgs; ++i) {
- /* deleted message? */
- if ((elt = mail_elt (stream,i))->deleted) {
- k = elt->private.uid; /* this is the top of the current range */
- if (!j) j = k; /* if no range in progress, start one */
- }
- else if (j) { /* unread message, ending a range */
- /* calculate end of range */
- if (k = elt->private.uid - 1) {
- /* dump range */
- sprintf (tmp,(j == k) ? "%c%ld" : "%c%ld-%ld",c,j,k);
- if (fputs (tmp,f) == EOF) return NIL;
- c = ','; /* need a comma after the first time */
- }
- j = 0; /* no more range in progress */
- }
- }
- if (j) { /* dump trailing range */
- sprintf (tmp,(j == k) ? "%c%ld" : "%c%ld-%ld",c,j,k);
- if (fputs (tmp,f) == EOF) return NIL;
- }
- /* write trailing newline, return */
- return (fputs (nl,f) == EOF) ? NIL : LONGT;
- }
- /* List subscribed newsgroups
- * Accepts: MAIL stream
- * prefix to append name
- * pattern to search
- */
- void newsrc_lsub (MAILSTREAM *stream,char *pattern)
- {
- char *s,*t,*lcl,name[MAILTMPLEN];
- int c = ' ';
- int showuppers = pattern[strlen (pattern) - 1] == '%';
- FILE *f = fopen ((char *) mail_parameters (stream,GET_NEWSRC,NIL),"rb");
- if (f) { /* got file? */
- /* remote name? */
- if (*(lcl = strcpy (name,pattern)) == '{') lcl = strchr (lcl,'}') + 1;
- if (*lcl == '#') lcl += 6; /* namespace format name? */
- while (c != EOF) { /* yes, read newsrc */
- for (s = lcl; (s < (name + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
- (c != ':') && (c != '!') && (c != ' 15') && (c != ' 12');
- *s++ = c);
- if (c == ':') { /* found a subscribed newsgroup? */
- *s = ' '; /* yes, tie off name */
- /* report if match */
- if (pmatch_full (name,pattern,'.')) mm_lsub (stream,'.',name,NIL);
- else while (showuppers && (t = strrchr (lcl,'.'))) {
- *t = ' '; /* tie off the name */
- if (pmatch_full (name,pattern,'.'))
- mm_lsub (stream,'.',name,LATT_NOSELECT);
- }
- }
- while ((c != ' 15') && (c != ' 12') && (c != EOF)) c = getc (f);
- }
- fclose (f);
- }
- }
- /* Update subscription status of newsrc
- * Accepts: MAIL stream
- * group
- * new subscription status character
- * Returns: T if successful, NIL otherwise
- */
- long newsrc_update (MAILSTREAM *stream,char *group,char state)
- {
- char tmp[MAILTMPLEN];
- char *newsrc = (char *) mail_parameters (stream,GET_NEWSRC,NIL);
- long ret = NIL;
- FILE *f = fopen (newsrc,"r+b");
- if (f) { /* update existing file */
- int c = 0;
- char *s,nl[3];
- long pos = 0;
- nl[0] = nl[1] = nl[2]=' '; /* no newline known yet */
- do { /* read newsrc */
- for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
- (c != ':') && (c != '!') && (c != ' 15') && (c != ' 12');
- *s++ = c) pos = ftell (f);
- *s = ' '; /* tie off name */
- /* found the newsgroup? */
- if (((c == ':') || (c == '!')) && !strcmp (tmp,group)) {
- if (c == state) { /* already at that state? */
- if (c == ':') newsrc_error ("Already subscribed to %s",group,WARN);
- ret = LONGT; /* noop the update */
- }
- /* write the character */
- else if (!fseek (f,pos,0)) ret = ((putc (state,f)) == EOF) ? NIL:LONGT;
- if (fclose (f) == EOF) ret = NIL;
- f = NIL; /* done with file */
- break;
- }
- /* gobble remainder of this line */
- while ((c != ' 15') && (c != ' 12') && (c != EOF)) c = getc (f);
- /* need to know about newlines and found it? */
- if (!nl[0] && ((c == ' 15') || (c == ' 12')) && ((nl[0]=c) == ' 15')){
- /* sniff and see if an LF */
- if ((c = getc (f)) == ' 12') nl[1] = c;
- else ungetc (c,f); /* nope, push it back */
- }
- } while (c != EOF);
- if (f) { /* still haven't written it yet? */
- if (nl[0]) { /* know its newline convention? */
- fseek (f,0L,2); /* yes, seek to end of file */
- ret = newsrc_newstate (f,group,state,nl);
- }
- else { /* can't find a newline convention */
- fclose (f); /* punt the file */
- /* can't win if read something */
- if (pos) newsrc_error("Unknown newline convention in %s",newsrc,ERROR);
- /* file must have been empty, rewrite it */
- else ret = newsrc_newstate(newsrc_create(stream,NIL),group,state,"n");
- }
- }
- }
- /* create new file */
- else ret = newsrc_newstate (newsrc_create (stream,T),group,state,"n");
- return ret; /* return with update status */
- }
- /* Update newsgroup status in stream
- * Accepts: newsgroup name
- * MAIL stream
- * Returns: number of recent messages
- */
- long newsrc_read (char *group,MAILSTREAM *stream)
- {
- int c = 0;
- char *s,tmp[MAILTMPLEN];
- unsigned long i,j;
- MESSAGECACHE *elt;
- unsigned long m = 1,recent = 0,unseen = 0;
- FILE *f = fopen ((char *) mail_parameters (stream,GET_NEWSRC,NIL),"rb");
- if (f) do { /* read newsrc */
- for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
- (c != ':') && (c != '!') && (c != ' 15') && (c != ' 12'); *s++ = c);
- *s = ' '; /* tie off name */
- if ((c==':') || (c=='!')) { /* found newsgroup? */
- if (strcmp (tmp,group)) /* group name match? */
- while ((c != ' 15') && (c != ' 12') && (c != EOF)) c = getc (f);
- else { /* yes, skip leading whitespace */
- while ((c = getc (f)) == ' ');
- /* only if unprocessed messages */
- if (stream->nmsgs) while (f && (m <= stream->nmsgs)) {
- /* collect a number */
- if (isdigit (c)) { /* better have a number */
- for (i = 0,j = 0; isdigit (c); c = getc (f)) i = i*10 + (c-'0');
- if (c == '-') for (c = getc (f); isdigit (c); c = getc (f))
- j = j*10 +(c-'0');/* collect second value if range */
- if (!unseen && (mail_elt (stream,m)->private.uid < i)) unseen = m;
- /* skip messages before first value */
- while ((m <= stream->nmsgs) &&
- (mail_elt (stream,m)->private.uid < i)) m++;
- /* do all messages in range */
- while ((m <= stream->nmsgs) && (elt = mail_elt (stream,m)) &&
- (j ? ((elt->private.uid >= i) && (elt->private.uid <= j)) :
- (elt->private.uid == i)) && m++)
- elt->valid = elt->deleted = T;
- }
- switch (c) { /* what is the delimiter? */
- case ',': /* more to come */
- c = getc (f); /* get first character of number */
- break;
- default: /* bogus character */
- sprintf (tmp,"Bogus character 0x%x in news state",(unsigned int)c);
- mm_log (tmp,ERROR);
- case EOF: case ' 15': case ' 12':
- fclose (f); /* all done - close the file */
- f = NIL;
- break;
- }
- }
- else { /* empty newsgroup */
- while ((c != ' 15') && (c != ' 12') && (c != EOF)) c = getc (f);
- fclose (f); /* all done - close the file */
- f = NIL;
- }
- }
- }
- } while (f && (c != EOF)); /* until file closed or EOF */
- if (f) { /* still have file open? */
- sprintf (tmp,"No state for newsgroup %s found, reading as new",group);
- mm_log (tmp,WARN);
- fclose (f); /* close the file */
- }
- if (m <= stream->nmsgs) { /* any messages beyond newsrc range? */
- if (!unseen) unseen = m; /* then this must be the first unseen one */
- do {
- elt = mail_elt (stream,m++);
- elt->valid = elt->recent = T;
- ++recent; /* count another recent message */
- }
- while (m <= stream->nmsgs);
- }
- if (unseen) { /* report first unseen message */
- sprintf (tmp,"[UNSEEN] %lu is first unseen message in %s",unseen,group);
- mm_notify (stream,tmp,(long) NIL);
- }
- return recent;
- }
- /* Update newsgroup entry in newsrc
- * Accepts: newsgroup name
- * MAIL stream
- * Returns: T if successful, NIL otherwise
- */
- long newsrc_write (char *group,MAILSTREAM *stream)
- {
- int c = 0,d = EOF;
- char *newsrc = (char *) mail_parameters (stream,GET_NEWSRC,NIL);
- char *s,tmp[MAILTMPLEN],backup[MAILTMPLEN],nl[3];
- FILE *f,*bf;
- nl[0] = nl[1] = nl[2] = ' '; /* no newline known yet */
- if (f = fopen (newsrc,"rb")) {/* have existing newsrc file? */
- if (!(bf = fopen ((strcat (strcpy (backup,newsrc),OLDFILESUFFIX)),"wb"))) {
- fclose (f); /* punt input file */
- return newsrc_error ("Can't create backup news state %s",backup,ERROR);
- }
- /* copy to backup file */
- while ((c = getc (f)) != EOF) {
- /* need to know about newlines and found it? */
- if (!nl[0] && ((c == ' 15') || (c == ' 12')) && ((nl[0]=c) == ' 15')){
- /* sniff and see if an LF */
- if ((c = getc (f)) == ' 12') nl[1] = c;
- ungetc (c,f); /* push it back */
- }
- /* write to backup file */
- if ((d = putc (c,bf)) == EOF) {
- fclose (f); /* punt input file */
- return newsrc_error("Error writing backup news state %s",newsrc,ERROR);
- }
- }
- fclose (f); /* close existing file */
- if (fclose (bf) == EOF) /* and backup file */
- return newsrc_error ("Error closing backup news state %s",newsrc,ERROR);
- if (d == EOF) { /* open for write if empty file */
- if (f = newsrc_create (stream,NIL)) bf = NIL;
- else return NIL;
- }
- else if (!nl[0]) /* make sure newlines valid */
- return newsrc_error ("Unknown newline convention in %s",newsrc,ERROR);
- /* now read backup file */
- else if (!(bf = fopen (backup,"rb")))
- return newsrc_error ("Error reading backup news state %s",backup,ERROR);
- /* open newsrc for writing */
- else if (!(f = fopen (newsrc,"wb"))) {
- fclose (bf); /* punt backup */
- return newsrc_error ("Can't rewrite news state %s",newsrc,ERROR);
- }
- }
- else { /* create new newsrc file */
- if (f = newsrc_create (stream,T)) bf = NIL;
- else return NIL; /* can't create newsrc */
- }
-
- while (bf) { /* read newsrc */
- for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (bf)) != EOF) &&
- (c != ':') && (c != '!') && (c != ' 15') && (c != ' 12'); *s++ = c);
- *s = ' '; /* tie off name */
- /* saw correct end of group delimiter? */
- if (tmp[0] && ((c == ':') || (c == '!'))) {
- /* yes, write newsgroup name and delimiter */
- if ((tmp[0] && (fputs (tmp,f) == EOF)) || ((putc (c,f)) == EOF))
- return newsrc_write_error (newsrc,bf,f);
- if (!strcmp (tmp,group)) {/* found correct group? */
- /* yes, write new status */
- if (!newsrc_newmessages (f,stream,nl[0] ? nl : "n"))
- return newsrc_write_error (newsrc,bf,f);
- /* skip past old data */
- while (((c = getc (bf)) != EOF) && (c != ' 15') && (c != ' 12'));
- /* skip past newline */
- while ((c == ' 15') || (c == ' 12')) c = getc (bf);
- while (c != EOF) { /* copy remainder of file */
- if (putc (c,f) == EOF) return newsrc_write_error (newsrc,bf,f);
- c = getc (bf); /* get next character */
- }
- /* done with file */
- if (fclose (f) == EOF) return newsrc_write_error (newsrc,bf,f);
- f = NIL;
- }
- /* copy remainder of line */
- else while (((c = getc (bf)) != EOF) && (c != ' 15') && (c != ' 12'))
- if (putc (c,f) == EOF) return newsrc_write_error (newsrc,bf,f);
- if (c == ' 15') { /* write CR if seen */
- if (putc (c,f) == EOF) return newsrc_write_error (newsrc,bf,f);
- /* sniff to see if LF */
- if (((c = getc (bf)) != EOF) && (c != ' 12')) ungetc (c,bf);
- }
- /* write LF if seen */
- if ((c == ' 12') && (putc (c,f) == EOF))
- return newsrc_write_error (newsrc,bf,f);
- }
- if (c == EOF) { /* hit end of file? */
- fclose (bf); /* yup, close the file */
- bf = NIL;
- }
- }
- if (f) { /* still have newsrc file open? */
- if ((fputs (group,f) == EOF) || ((putc (':',f)) == EOF) ||
- (!newsrc_newmessages (f,stream,nl[0] ? nl : "n")) ||
- (fclose (f) == EOF)) return newsrc_write_error (newsrc,bf,f);
- }
- return LONGT;
- }
- /* Get newsgroup state as text stream
- * Accepts: MAIL stream
- * newsgroup name
- * Returns: string containing newsgroup state, or NIL if not found
- */
- char *newsrc_state (MAILSTREAM *stream,char *group)
- {
- int c = 0;
- char *s,tmp[MAILTMPLEN];
- long pos;
- size_t size;
- FILE *f = fopen ((char *) mail_parameters (stream,GET_NEWSRC,NIL),"rb");
- if (f) do { /* read newsrc */
- for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
- (c != ':') && (c != '!') && (c != ' 15') && (c != ' 12'); *s++ = c);
- *s = ' '; /* tie off name */
- if ((c==':') || (c=='!')) { /* found newsgroup? */
- if (strcmp (tmp,group)) /* group name match? */
- while ((c != ' 15') && (c != ' 12') && (c != EOF)) c = getc (f);
- else { /* yes, skip leading whitespace */
- do pos = ftell (f);
- while ((c = getc (f)) == ' ');
- /* count characters in state */
- for (size = 0; (c != ' 15') && (c != ' 12') && (c != EOF); size++)
- c = getc (f);
- /* now copy it */
- s = (char *) fs_get (size + 1);
- fseek (f,pos,L_SET);
- fread (s,(size_t) 1,size,f);
- s[size] = ' '; /* tie off string */
- fclose (f); /* all done - close the file */
- return s;
- }
- }
- } while (f && (c != EOF)); /* until file closed or EOF */
- sprintf (tmp,"No state for newsgroup %s found",group);
- mm_log (tmp,WARN);
- if (f) fclose (f); /* close the file */
- return NIL; /* not found return */
- }
- /* Check UID in newsgroup state
- * Accepts: newsgroup state string
- * uid
- * returned recent count
- * returned unseen count
- */
- void newsrc_check_uid (char *state,unsigned long uid,unsigned long *recent,
- unsigned long *unseen)
- {
- unsigned long i,j;
- while (*state) { /* until run out of state string */
- /* collect a number */
- for (i = 0; isdigit (*state); i = i*10 + (*state++ - '0'));
- if (*state != '-') j = i; /* coerce single mesage into range */
- else { /* have a range */
- for (j = 0; isdigit (*++state); j = j*10 + (*state - '0'));
- if (j < i) return; /* bogon if end less than start */
- }
- if (*state == ',') state++; /* skip past comma */
- else if (*state) return; /* otherwise it's a bogon */
- if (uid <= j) { /* covered by upper bound? */
- if (uid < i) ++*unseen; /* unseen if not covered by lower bound */
- return; /* don't need to look further */
- }
- }
- ++*unseen; /* not found in any range, message is unseen */
- ++*recent; /* and recent */
- }