]> granicus.if.org Git - uw-imap/commitdiff
add files for 2006-12-07T02:38:53Z
authorUnknown <>
Thu, 7 Dec 2006 02:38:53 +0000 (02:38 +0000)
committerNathan Wagner <nw@hydaspes.if.org>
Fri, 7 Sep 2018 00:02:35 +0000 (00:02 +0000)
src/c-client/newsrc.c [new file with mode: 0644]

diff --git a/src/c-client/newsrc.c b/src/c-client/newsrc.c
new file mode 100644 (file)
index 0000000..41f1fd2
--- /dev/null
@@ -0,0 +1,510 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 
+ * ========================================================================
+ */
+
+/*
+ * 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:        30 August 2006
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include "c-client.h"
+#include "newsrc.h"
+
+#ifndef OLDFILESUFFIX
+#define OLDFILESUFFIX ".old"
+#endif
+\f
+/* 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)
+{
+  if (f1) fclose (f1);         /* close file designators */
+  if (f2) fclose (f2);
+  return newsrc_error ("Error writing to %.80s",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,stream);
+  FILE *f = fopen (newsrc,"wb");
+  if (!f) newsrc_error ("Unable to create news state %.80s",newsrc,ERROR);
+  else if (notify) newsrc_error ("Creating news state %.80s",newsrc,WARN);
+  return f;
+}
+\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)
+{
+  long ret = (f && (fputs (group,f) != EOF) && ((putc (state,f)) != EOF) &&
+             ((putc (' ',f)) != EOF) && (fputs (nl,f) != EOF)) ? LONGT : NIL;
+  if (fclose (f) == EOF) ret = NIL;
+  return ret;
+}
+
+
+/* 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 = ' ';
+  if (stream->nmsgs) {         /* have any messages? */
+    for (i = 1,j = k = (mail_elt (stream,i)->private.uid > 1) ? 1 : 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;
+}
+\f
+/* 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,stream),"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 != '\015') && (c != '\012');
+          *s++ = c);
+      if (c == ':') {          /* found a subscribed newsgroup? */
+       *s = '\0';              /* yes, tie off name */
+                               /* report if match */
+       if (pmatch_full (name,pattern,'.')) mm_lsub (stream,'.',name,NIL);
+       else while (showuppers && (t = strrchr (lcl,'.'))) {
+         *t = '\0';            /* tie off the name */
+         if (pmatch_full (name,pattern,'.'))
+           mm_lsub (stream,'.',name,LATT_NOSELECT);
+       }
+      }
+      while ((c != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
+    }
+    fclose (f);
+  }
+}
+\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,stream);
+  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]='\0';        /* no newline known yet */
+    do {                       /* read newsrc */
+      for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
+          (c != ':') && (c != '!') && (c != '\015') && (c != '\012');
+          *s++ = c) pos = ftell (f);
+      *s = '\0';               /* 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 %.80s",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 != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
+                               /* need to know about newlines and found it? */
+      if (!nl[0] && ((c == '\015') || (c == '\012')) && ((nl[0]=c) == '\015')){
+                               /* sniff and see if an LF */
+       if ((c = getc (f)) == '\012') nl[1] = c;
+       else ungetc (c,f);      /* nope, push it back */
+      }
+    } while (c != EOF);
+\f
+    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 %.80s",
+                              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 */
+}
+\f
+/* 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,stream),"rb");
+  if (f) do {                  /* read newsrc */
+    for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
+        (c != ':') && (c != '!') && (c != '\015') && (c != '\012'); *s++ = c);
+    *s = '\0';                 /* tie off name */
+    if ((c==':') || (c=='!')) {        /* found newsgroup? */
+      if (strcmp (tmp,group))  /* group name match? */
+       while ((c != '\015') && (c != '\012') && (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) &&
+                  ((elt = mail_elt (stream,m))->private.uid < i) && m++)
+             elt->valid = T;
+                               /* 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;
+         }
+\f
+         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 '\015': case '\012':
+           fclose (f);         /* all done - close the file */
+           f = NIL;
+           break;
+         }
+       }
+       else {                  /* empty newsgroup */
+         while ((c != '\015') && (c != '\012') && (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 %.80s 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 %.80s",unseen,group);
+    MM_NOTIFY (stream,tmp,(long) NIL);
+  }
+  return recent;
+}
+\f
+/* Update newsgroup entry in newsrc
+ * Accepts: newsgroup name
+ *         MAIL stream
+ * Returns: T if successful, NIL otherwise
+ */
+
+long newsrc_write (char *group,MAILSTREAM *stream)
+{
+  long ret = NIL;
+  int c = 0,d = EOF;
+  char *newsrc = (char *) mail_parameters (stream,GET_NEWSRC,stream);
+  char *s,tmp[MAILTMPLEN],backup[MAILTMPLEN],nl[3];
+  FILE *f,*bf;
+  nl[0] = nl[1] = nl[2] = '\0';        /* 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 %.80s",backup,ERROR);
+    }
+                               /* copy to backup file */
+    while ((c = getc (f)) != EOF) {
+                               /* need to know about newlines and found it? */
+      if (!nl[0] && ((c == '\015') || (c == '\012')) && ((nl[0]=c) == '\015')){
+                               /* sniff and see if an LF */
+       if ((c = getc (f)) == '\012') 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 %.80s",
+                            newsrc,ERROR);
+      }
+    }
+    fclose (f);                        /* close existing file */
+    if (fclose (bf) == EOF)    /* and backup file */
+      return newsrc_error ("Error closing backup news state %.80s",
+                          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 %.80s",newsrc,ERROR);
+                               /* now read backup file */
+    else if (!(bf = fopen (backup,"rb")))
+      return newsrc_error ("Error reading backup news state %.80s",
+                          backup,ERROR);
+                               /* open newsrc for writing */
+    else if (!(f = fopen (newsrc,"wb"))) {
+      fclose (bf);             /* punt backup */
+      return newsrc_error ("Can't rewrite news state %.80s",newsrc,ERROR);
+    }
+  }
+  else {                       /* create new newsrc file */
+    if (f = newsrc_create (stream,T)) bf = NIL;
+    else return NIL;           /* can't create newsrc */
+  }
+\f  
+  while (bf) {                 /* read newsrc */
+    for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (bf)) != EOF) &&
+        (c != ':') && (c != '!') && (c != '\015') && (c != '\012'); *s++ = c);
+    *s = '\0';                 /* 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 != '\015') && (c != '\012'));
+                               /* skip past newline */
+       while ((c == '\015') || (c == '\012')) 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,NIL);
+       f = NIL;
+      }
+                               /* copy remainder of line */
+      else while (((c = getc (bf)) != EOF) && (c != '\015') && (c != '\012'))
+       if (putc (c,f) == EOF) return newsrc_write_error (newsrc,bf,f);
+      if (c == '\015') {       /* 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 != '\012')) ungetc (c,bf);
+      }
+                               /* write LF if seen */
+      if ((c == '\012') && (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? */
+    ret = ((fputs (group,f) != EOF) && ((putc (':',f)) != EOF) &&
+          newsrc_newmessages (f,stream,nl[0] ? nl : "\n")) ? LONGT : NIL;
+    if (fclose (f) == EOF) ret = newsrc_write_error (newsrc,NIL,NIL);
+  }
+  else ret = LONGT;
+  return ret;
+}
+\f
+/* 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,stream),"rb");
+  if (f) do {                  /* read newsrc */
+    for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
+        (c != ':') && (c != '!') && (c != '\015') && (c != '\012'); *s++ = c);
+    *s = '\0';                 /* tie off name */
+    if ((c==':') || (c=='!')) {        /* found newsgroup? */
+      if (strcmp (tmp,group))  /* group name match? */
+       while ((c != '\015') && (c != '\012') && (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 != '\015') && (c != '\012') && (c != EOF); size++)
+         c = getc (f);
+                               /* now copy it */
+       s = (char *) fs_get (size + 1);
+       fseek (f,pos,SEEK_SET);
+       fread (s,(size_t) 1,size,f);
+       s[size] = '\0';         /* 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 %.80s found",group);
+  MM_LOG (tmp,WARN);
+  if (f) fclose (f);           /* close the file */
+  return NIL;                  /* not found return */
+}
+\f
+/* Check UID in newsgroup state
+ * Accepts: newsgroup state string
+ *         uid
+ *         returned recent count
+ *         returned unseen count
+ */
+
+void newsrc_check_uid (unsigned 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) j = i;           /* guard against -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 */
+}