chiark / gitweb /
REORG Delete everything that's not innduct or build system or changed for innduct
[inn-innduct.git] / storage / timecaf / timecaf.c
diff --git a/storage/timecaf/timecaf.c b/storage/timecaf/timecaf.c
deleted file mode 100644 (file)
index e053a02..0000000
+++ /dev/null
@@ -1,853 +0,0 @@
-/*  $Id: timecaf.c 7412 2005-10-09 03:44:35Z eagle $
-**
-**  Like the timehash storage method (and heavily inspired by it), but uses
-**  the CAF library to store multiple articles in a single file.
-*/
-
-#include "config.h"
-#include "clibrary.h"
-#include "portable/mmap.h"
-#include <ctype.h>
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <syslog.h>
-#include <sys/stat.h>
-#include <time.h>
-
-#include "caf.h"
-#include "inn/innconf.h"
-#include "inn/wire.h"
-#include "libinn.h"
-#include "methods.h"
-#include "timecaf.h"
-#include "paths.h"
-
-/* Needed for htonl() and friends on AIX 4.1. */
-#include <netinet/in.h>
-
-typedef struct {
-    char               *artdata; /* start of the article data -- may be mmaped */
-    char               *mmapbase; /* actual start of mmaped region (on pagesize bndry, not necessarily == artdaya */
-    unsigned int       artlen; /* art length. */
-    size_t             mmaplen; /* length of mmap region. */
-    DIR                        *top; /* open handle on top level dir. */
-    DIR                        *sec; /* open handle on the 2nd level directory */
-    DIR                *ter; /* open handle on 3rd level dir. */
-    struct dirent      *topde; /* last entry we got from top */
-    struct dirent      *secde; /* last entry we got from sec */ 
-    struct dirent      *terde; /* last entry we got from sec */ 
-    CAFTOCENT          *curtoc; 
-    ARTNUM             curartnum;
-    CAFHEADER          curheader;
-} PRIV_TIMECAF;
-
-/* current path/fd for an open CAF file */
-typedef struct {
-    char       *path; /* path to file. */
-    int                fd; /* open fd -- -1 if no file currently open. */
-} CAFOPENFILE;
-
-static CAFOPENFILE ReadingFile, WritingFile;
-static char *DeletePath;
-static ARTNUM *DeleteArtnums;
-static unsigned int NumDeleteArtnums, MaxDeleteArtnums;
-
-typedef enum {FIND_DIR, FIND_CAF, FIND_TOPDIR} FINDTYPE;
-
-/*
-** Structures for the cache for stat information (to make expireover etc. 
-** faster. 
-**
-** The first structure contains the TOC info for a single CAF file.  The 2nd
-** one has pointers to the info for up to 256 CAF files, indexed
-** by the 2nd least significant byte of the arrival time.
-*/
-
-struct caftoccacheent {
-    CAFTOCENT *toc;
-    CAFHEADER header;
-};
-typedef struct caftoccacheent CAFTOCCACHEENT;
-
-struct caftocl1cache {
-    CAFTOCCACHEENT *entries[256];
-};
-typedef struct caftocl1cache CAFTOCL1CACHE;
-
-/*
-** and similar structures indexed by the 3rd and 4th bytes of the arrival time.
-** pointing to the lower level structures.  Note that the top level structure
-** (the one indexed by the MSByte of the timestamp) is likely to have only
-** one active pointer, unless your spool keeps more than 194 days of articles,
-** but it doesn't cost much to keep that one structure around and keep the
-** code general.
-*/
-
-struct caftocl2cache {
-    CAFTOCL1CACHE *l1ptr[256];
-};
-typedef struct caftocl2cache CAFTOCL2CACHE;
-
-struct caftocl3cache {
-    CAFTOCL2CACHE *l2ptr[256];
-};
-typedef struct caftocl3cache CAFTOCL3CACHE;
-
-static CAFTOCL3CACHE *TOCCache[256]; /* indexed by storage class! */
-static int TOCCacheHits, TOCCacheMisses;
-
-    
-static TOKEN MakeToken(time_t now, int seqnum, STORAGECLASS class, TOKEN *oldtoken) {
-    TOKEN               token;
-    unsigned int        i;
-    unsigned short      s;
-
-    if (oldtoken == (TOKEN *)NULL)
-       memset(&token, '\0', sizeof(token));
-    else 
-       memcpy(&token, oldtoken, sizeof(token));
-    token.type = TOKEN_TIMECAF;
-    token.class = class;
-    i = htonl(now);
-    memcpy(token.token, &i, sizeof(i));
-    if (sizeof(i) > 4)
-       memmove(token.token, &token.token[sizeof(i) - 4], 4);
-    s = htons(seqnum);
-    memcpy(&token.token[4], &s + (sizeof(s) - 2), 2);
-    return token;
-}
-
-
-static void BreakToken(TOKEN token, int *now, int *seqnum) {
-    unsigned int        i;
-    unsigned short      s = 0;
-
-    memcpy(&i, token.token, sizeof(i));
-    memcpy(&s, &token.token[4], sizeof(s));
-    *now = ntohl(i);
-    *seqnum = (int)ntohs(s);
-}
-
-/* 
-** Note: the time here is really "time>>8", i.e. a timestamp that's been
-** shifted right by 8 bits.
-*/
-static char *MakePath(int now, const STORAGECLASS class) {
-    char *path;
-    size_t length;
-    
-    /* innconf->patharticles + '/timecaf-zz/xx/xxxx.CF' */
-    length = strlen(innconf->patharticles) + 32;
-    path = xmalloc(length);
-    snprintf(path, length, "%s/timecaf-%02x/%02x/%02x%02x.CF",
-             innconf->patharticles, class,
-             (now >> 8) & 0xff, (now >> 16) & 0xff, now & 0xff);
-
-    return path;
-}
-
-static TOKEN *PathNumToToken(char *path, ARTNUM artnum) {
-    int                        n;
-    unsigned int       t1, t2, class;
-    unsigned int       timestamp;
-    static TOKEN       token;
-
-    n = sscanf(path, "timecaf-%02x/%02x/%04x.CF", &class, &t1, &t2);
-    if (n != 3)
-       return (TOKEN *)NULL;
-    timestamp = ((t1 << 8) & 0xff00) | ((t2 << 8) & 0xff0000) | ((t2 << 0) & 0xff);
-    token = MakeToken(timestamp, artnum, class, (TOKEN *)NULL);
-    return &token;
-}
-
-
-bool timecaf_init(SMATTRIBUTE *attr) {
-    if (attr == NULL) {
-       syslog(L_ERROR, "timecaf: attr is NULL");
-       SMseterror(SMERR_INTERNAL, "attr is NULL");
-       return false;
-    }
-    attr->selfexpire = false;
-    attr->expensivestat = false;
-    if (STORAGE_TOKEN_LENGTH < 6) {
-       syslog(L_FATAL, "timecaf: token length is less than 6 bytes");
-       SMseterror(SMERR_TOKENSHORT, NULL);
-       return false;
-    }
-    ReadingFile.fd = WritingFile.fd = -1;
-    ReadingFile.path = WritingFile.path = (char *)NULL;
-    return true;
-}
-
-/*
-** Routines for managing the 'TOC cache' (cache of TOCs of various CAF files)
-**
-** Attempt to look up a given TOC entry in the cache.  Takes the timestamp
-** as arguments. 
-*/
-
-static CAFTOCCACHEENT *
-CheckTOCCache(int timestamp, int tokenclass)
-{
-    CAFTOCL2CACHE *l2;
-    CAFTOCL1CACHE *l1;
-    CAFTOCCACHEENT *cent;
-    unsigned char tmp;
-
-    if (TOCCache[tokenclass] == NULL) return NULL; /* cache is empty */
-
-    tmp = (timestamp>>16) & 0xff;
-    l2 = TOCCache[tokenclass]->l2ptr[tmp];
-    if (l2 == NULL) return NULL;
-
-    tmp = (timestamp>>8) & 0xff;
-    l1 = l2->l1ptr[tmp];
-    if (l1 == NULL) return NULL;
-
-    tmp = (timestamp) & 0xff;
-    cent = l1->entries[tmp];
-
-    ++TOCCacheHits;
-    return cent;
-}
-
-/*
-** Add given TOC and header to the cache.  Assume entry is not already in
-** cache.
-*/
-static CAFTOCCACHEENT *
-AddTOCCache(int timestamp, CAFTOCENT *toc, CAFHEADER head, int tokenclass)
-{
-    CAFTOCL2CACHE *l2;
-    CAFTOCL1CACHE *l1;
-    CAFTOCCACHEENT *cent;
-    unsigned char tmp;
-    int i;
-
-    if (TOCCache[tokenclass] == NULL) {
-       TOCCache[tokenclass] = xmalloc(sizeof(CAFTOCL3CACHE));
-       for (i = 0 ; i < 256 ; ++i) TOCCache[tokenclass]->l2ptr[i] = NULL;
-    }
-
-    tmp = (timestamp>>16) & 0xff;
-    l2 = TOCCache[tokenclass]->l2ptr[tmp];
-    if (l2 == NULL) {
-       TOCCache[tokenclass]->l2ptr[tmp] = l2 = xmalloc(sizeof(CAFTOCL2CACHE));
-       for (i = 0 ; i < 256 ; ++i) l2->l1ptr[i] = NULL;
-    }
-
-    tmp = (timestamp>>8) & 0xff;
-    l1 = l2->l1ptr[tmp];
-    if (l1 == NULL) {
-       l2->l1ptr[tmp] = l1 = xmalloc(sizeof(CAFTOCL1CACHE));
-       for (i = 0 ; i < 256 ; ++i) l1->entries[i] = NULL;
-    }
-
-    tmp = (timestamp) & 0xff;
-    cent = xmalloc(sizeof(CAFTOCCACHEENT));
-    l1->entries[tmp] = cent;
-
-    cent->header = head;
-    cent->toc = toc;
-    ++TOCCacheMisses;
-    return cent;
-}
-
-/*
-** Do stating of an article, going thru the TOC cache if possible. 
-*/
-
-static ARTHANDLE *
-StatArticle(int timestamp, ARTNUM artnum, int tokenclass)
-{
-    CAFTOCCACHEENT *cent;
-    CAFTOCENT *toc;
-    CAFHEADER head;
-    char *path;
-    CAFTOCENT *tocentry;
-    ARTHANDLE *art;
-
-    cent = CheckTOCCache(timestamp,tokenclass);
-    if (cent == NULL) {
-       path = MakePath(timestamp, tokenclass);
-       toc = CAFReadTOC(path, &head);
-       if (toc == NULL) {
-           if (caf_error == CAF_ERR_ARTNOTHERE) {
-               SMseterror(SMERR_NOENT, NULL);
-           } else {
-               SMseterror(SMERR_UNDEFINED, NULL);
-           }
-           free(path);
-           return NULL;
-       }
-       cent = AddTOCCache(timestamp, toc, head, tokenclass);
-       free(path);
-    }
-    
-    /* check current TOC for the given artnum. */
-    if (artnum < cent->header.Low || artnum > cent->header.High) {
-       SMseterror(SMERR_NOENT, NULL);
-       return NULL;
-    }
-    
-    tocentry = &(cent->toc[artnum - cent->header.Low]);
-    if (tocentry->Size == 0) {
-       /* no article with that article number present */
-       SMseterror(SMERR_NOENT, NULL);
-       return NULL;
-    }
-
-    /* stat is a success, so build a null art struct to represent that. */
-    art = xmalloc(sizeof(ARTHANDLE));
-    art->type = TOKEN_TIMECAF;
-    art->data = NULL;
-    art->len = 0;
-    art->private = NULL;
-    return art;
-}
-       
-
-static void
-CloseOpenFile(CAFOPENFILE *foo) {
-    if (foo->fd >= 0) {
-       close(foo->fd);
-       foo->fd = -1;
-       free(foo->path);
-       foo->path = NULL;
-    }
-}
-
-TOKEN timecaf_store(const ARTHANDLE article, const STORAGECLASS class) {
-    char                *path;
-    char                *p;
-    time_t              now;
-    int                        timestamp;
-    TOKEN               token;
-    int                 fd;
-    ssize_t             result;
-    ARTNUM             art;
-
-    if (article.arrived == (time_t)0)
-       now = time(NULL);
-    else
-       now = article.arrived;
-
-    timestamp = now>>8;
-    art = 0;  /* magic: 0=="next available article number. */
-
-    path = MakePath(timestamp, class);
-    /* check to see if we have this CAF file already open. */
-    if (WritingFile.fd < 0 || strcmp(WritingFile.path, path) != 0) {
-       /* we're writing to a different file, close old one and start new one. */
-       CloseOpenFile(&WritingFile);
-       fd = CAFOpenArtWrite(path, &art, true, article.len);
-       if (fd < 0) {
-           if (caf_error == CAF_ERR_IO && caf_errno == ENOENT) {
-               /* directories in the path don't exist, try creating them. */
-               p = strrchr(path, '/');
-               *p = '\0';
-               if (!MakeDirectory(path, true)) {
-                   syslog(L_ERROR, "timecaf: could not make directory %s %m", path);
-                   token.type = TOKEN_EMPTY;
-                   free(path);
-                   SMseterror(SMERR_UNDEFINED, NULL);
-                   return token;
-               } else {
-                   *p = '/';
-                   fd = CAFOpenArtWrite(path, &art, true, article.len);
-                   if (fd < 0) {
-                       syslog(L_ERROR, "timecaf: could not OpenArtWrite %s/%ld, %s", path, art, CAFErrorStr());
-                       SMseterror(SMERR_UNDEFINED, NULL);
-                       free(path);
-                       token.type = TOKEN_EMPTY;
-                       return token;
-                   }
-               } 
-           } else {
-               syslog(L_ERROR, "timecaf: could not OpenArtWrite %s/%ld, %s", path, art, CAFErrorStr());
-               SMseterror(SMERR_UNDEFINED, NULL);
-               free(path);
-               token.type = TOKEN_EMPTY;
-               return token;
-           }
-       }
-    } else {
-       /* can reuse existing fd, assuming all goes well. */
-       fd = WritingFile.fd;
-
-       /* nuke extraneous copy of path to avoid mem leaks. */
-       free(path);
-       path = WritingFile.path;
-
-       if (CAFStartWriteFd(fd, &art, article.len) < 0) {
-           syslog(L_ERROR, "timecaf: could not OpenArtWrite %s/%ld, %s", path, art, CAFErrorStr());
-           SMseterror(SMERR_UNDEFINED, NULL);
-           free(path);
-           token.type = TOKEN_EMPTY;
-           return token;
-       }
-    }
-    WritingFile.fd = fd;
-    WritingFile.path = path;
-    close_on_exec(fd, true);
-    result = xwritev(fd, article.iov, article.iovcnt);
-    if (result != (ssize_t) article.len) {
-       SMseterror(SMERR_UNDEFINED, NULL);
-       syslog(L_ERROR, "timecaf error writing %s %m", path);
-       token.type = TOKEN_EMPTY;
-       CloseOpenFile(&WritingFile);
-       return token;
-    }
-    if (CAFFinishArtWrite(fd) < 0) { 
-       SMseterror(SMERR_UNDEFINED, NULL);
-       syslog(L_ERROR, "timecaf error writing %s %s", path, CAFErrorStr());
-       token.type = TOKEN_EMPTY;
-       CloseOpenFile(&WritingFile);
-       return token;
-    }
-    
-    return MakeToken(timestamp, art, class, article.token);
-}
-
-/* Get a handle to article artnum in CAF-file path. */
-static ARTHANDLE *OpenArticle(const char *path, ARTNUM artnum, const RETRTYPE amount) {
-    int                 fd;
-    PRIV_TIMECAF        *private;
-    char                *p;
-    size_t             len;
-    ARTHANDLE           *art;
-    static long                pagesize = 0;
-
-    if (pagesize == 0) {
-        pagesize = getpagesize();
-        if (pagesize < 0) {
-           syslog(L_ERROR, "timecaf getpagesize failed: %m");
-            pagesize = 0;
-           return NULL;
-        }
-    }
-
-/* XXX need to figure some way to cache open fds or something? */
-    if ((fd = CAFOpenArtRead((char *)path, artnum, &len)) < 0) {
-        if (caf_error == CAF_ERR_ARTNOTHERE) {
-           SMseterror(SMERR_NOENT, NULL);
-       } else {
-           SMseterror(SMERR_UNDEFINED, NULL);
-       }
-       return NULL;
-    }
-
-    art = xmalloc(sizeof(ARTHANDLE));
-    art->type = TOKEN_TIMECAF;
-
-    if (amount == RETR_STAT) {
-       art->data = NULL;
-       art->len = 0;
-       art->private = NULL;
-       close(fd);
-       return art;
-    }
-
-    private = xmalloc(sizeof(PRIV_TIMECAF));
-    art->private = (void *)private;
-    private->artlen = len;
-    if (innconf->articlemmap) {
-       off_t curoff, tmpoff;
-       size_t delta;
-
-       curoff = lseek(fd, (off_t) 0, SEEK_CUR);
-       delta = curoff % pagesize;
-       tmpoff = curoff - delta;
-       private->mmaplen = len + delta;
-       if ((private->mmapbase = mmap(NULL, private->mmaplen, PROT_READ, MAP_SHARED, fd, tmpoff)) == MAP_FAILED) {
-           SMseterror(SMERR_UNDEFINED, NULL);
-           syslog(L_ERROR, "timecaf: could not mmap article: %m");
-           free(art->private);
-           free(art);
-           return NULL;
-       }
-       mmap_invalidate(private->mmapbase, private->mmaplen);
-        if (amount == RETR_ALL)
-            madvise(private->mmapbase, private->mmaplen, MADV_WILLNEED);
-        else
-            madvise(private->mmapbase, private->mmaplen, MADV_SEQUENTIAL);
-       private->artdata = private->mmapbase + delta;
-    } else {
-        private->artdata = xmalloc(private->artlen);
-       if (read(fd, private->artdata, private->artlen) < 0) {
-           SMseterror(SMERR_UNDEFINED, NULL);
-           syslog(L_ERROR, "timecaf: could not read article: %m");
-           free(private->artdata);
-           free(art->private);
-           free(art);
-           return NULL;
-       }
-    }
-    close(fd);
-
-    private->top = NULL;
-    private->sec = NULL;
-    private->ter = NULL;
-    private->curtoc = NULL;
-    private->curartnum = 0;
-    private->topde = NULL;
-    private->secde = NULL;
-    private->terde = NULL;
-    
-    if (amount == RETR_ALL) {
-       art->data = private->artdata;
-       art->len = private->artlen;
-       return art;
-    }
-    
-    if ((p = wire_findbody(private->artdata, private->artlen)) == NULL) {
-       SMseterror(SMERR_NOBODY, NULL);
-       if (innconf->articlemmap)
-           munmap(private->mmapbase, private->mmaplen);
-       else
-           free(private->artdata);
-       free(art->private);
-       free(art);
-       return NULL;
-    }
-
-    if (amount == RETR_HEAD) {
-       art->data = private->artdata;
-       art->len = p - private->artdata;
-       return art;
-    }
-
-    if (amount == RETR_BODY) {
-       art->data = p + 4;
-       art->len = art->len - (private->artdata - p - 4);
-       return art;
-    }
-    SMseterror(SMERR_UNDEFINED, "Invalid retrieve request");
-    if (innconf->articlemmap)
-       munmap(private->mmapbase, private->mmaplen);
-    else
-       free(private->artdata);
-    free(art->private);
-    free(art);
-    return NULL;
-}
-
-ARTHANDLE *timecaf_retrieve(const TOKEN token, const RETRTYPE amount) {
-    int                 timestamp;
-    int                        artnum;
-    char                *path;
-    ARTHANDLE           *art;
-    static TOKEN       ret_token;
-    time_t             now;
-    
-    if (token.type != TOKEN_TIMECAF) {
-       SMseterror(SMERR_INTERNAL, NULL);
-       return NULL;
-    }
-
-    BreakToken(token, &timestamp, &artnum);
-
-    /*
-    ** Do a possible shortcut on RETR_STAT requests, going thru the "TOC cache"
-    ** we mentioned above.  We only try to go thru the TOC Cache under these
-    ** conditions:
-    **   1) SMpreopen is true (so we're "preopening" the TOCs.)
-    **   2) the timestamp is older than the timestamp corresponding to current
-    ** time. Any timestamp that matches current time (to within 256 secondsf
-    ** would be in a CAF file that innd is actively 
-    ** writing, in which case we would not want to cache the TOC for that
-    ** CAF file. 
-    */
-
-    if (SMpreopen && amount == RETR_STAT) {
-       now = time(NULL);
-       if (timestamp < ((now >> 8) & 0xffffff)) {
-           return StatArticle(timestamp, artnum, token.class);
-       }
-    }
-
-    path = MakePath(timestamp, token.class);
-    if ((art = OpenArticle(path, artnum, amount)) != (ARTHANDLE *)NULL) {
-       art->arrived = timestamp<<8; /* XXX not quite accurate arrival time,
-                                    ** but getting a more accurate one would 
-                                    ** require more fiddling with CAF innards.
-                                    */
-       ret_token = token;
-       art->token = &ret_token;
-    }
-    free(path);
-    return art;
-}
-
-void timecaf_freearticle(ARTHANDLE *article) {
-    PRIV_TIMECAF       *private;
-
-    if (!article)
-       return;
-    
-    if (article->private) {
-       private = (PRIV_TIMECAF *)article->private;
-       if (innconf->articlemmap)
-           munmap(private->mmapbase, private->mmaplen);
-       else
-           free(private->artdata);
-       if (private->top)
-           closedir(private->top);
-       if (private->sec)
-           closedir(private->sec);
-       if (private->ter)
-           closedir(private->ter);
-       if (private->curtoc) 
-           free(private->curtoc);
-       free(private);
-    }
-    free(article);
-}
-
-/* Do cancels of all the article ids collected for a given pathname. */
-
-static void
-DoCancels(void) {
-    if (DeletePath != NULL) {
-       if (NumDeleteArtnums != 0) {
-           /* 
-           ** Murgle. If we are trying to cancel something out of the
-           ** currently open-for-writing file, we need to close it before
-           ** doing CAFRemove...
-           */
-           if (WritingFile.path != NULL && strcmp(WritingFile.path, DeletePath) == 0) {
-               CloseOpenFile(&WritingFile);
-           }
-           /* XXX should really check err. code here, but not much we can really do. */
-           CAFRemoveMultArts(DeletePath, NumDeleteArtnums, DeleteArtnums);
-           free(DeleteArtnums);
-           DeleteArtnums = NULL;
-           NumDeleteArtnums = MaxDeleteArtnums = 0;
-       }
-       free(DeletePath);
-       DeletePath = NULL;
-    }
-}
-           
-bool timecaf_cancel(TOKEN token) {
-    int                 now;
-    int                 seqnum;
-    char                *path;
-
-    BreakToken(token, &now, &seqnum);
-    path = MakePath(now, token.class);
-    if (DeletePath == NULL) {
-       DeletePath = path;
-    } else if (strcmp(DeletePath, path) != 0) {
-       /* different path, so flush all pending cancels. */
-       DoCancels();
-       DeletePath = path;
-    } else {
-       free(path); /* free redundant copy of path */
-    }
-    if (NumDeleteArtnums >= MaxDeleteArtnums) {
-       /* allocate/expand storage for artnums. */
-       if (MaxDeleteArtnums == 0) {
-           MaxDeleteArtnums = 100;
-       } else {
-           MaxDeleteArtnums *= 2;
-       }
-        DeleteArtnums = xrealloc(DeleteArtnums, MaxDeleteArtnums * sizeof(ARTNUM));
-    }
-    DeleteArtnums[NumDeleteArtnums++] = seqnum;        
-
-    return true;
-}
-
-static struct dirent *FindDir(DIR *dir, FINDTYPE type) {
-    struct dirent       *de;
-    
-    while ((de = readdir(dir)) != NULL) {
-        if (type == FIND_TOPDIR)
-           if ((strlen(de->d_name) == 10) &&
-               (strncmp(de->d_name, "timecaf-", 8) == 0) &&
-               CTYPE(isxdigit, de->d_name[8]) &&
-               CTYPE(isxdigit, de->d_name[9]))
-               return de;
-
-       if (type == FIND_DIR)
-           if ((strlen(de->d_name) == 2)
-                && CTYPE(isxdigit, de->d_name[0])
-                && CTYPE(isxdigit, de->d_name[1]))
-               return de;
-
-       if (type == FIND_CAF)
-           if ((strlen(de->d_name) == 7) &&
-               CTYPE(isxdigit, de->d_name[0]) &&
-               CTYPE(isxdigit, de->d_name[1]) &&
-               CTYPE(isxdigit, de->d_name[2]) &&
-               CTYPE(isxdigit, de->d_name[3]) &&
-               (de->d_name[4] == '.') &&
-               (de->d_name[5] == 'C') &&
-               (de->d_name[6] == 'F'))
-               return de;
-       }
-
-    return NULL;
-}
-
-/* Grovel thru a CAF table-of-contents finding the next still-existing article */
-static int
-FindNextArt(const CAFHEADER *head, CAFTOCENT *toc, ARTNUM *artp)
-{
-    ARTNUM art;
-    CAFTOCENT *tocp;
-    art = *artp;
-    if (art == 0) {
-       art = head->Low - 1; /* we never use art # 0, so 0 is a flag to start
-                              searching at the beginning */
-    }
-    while (true) {
-       art++;
-       if (art > head->High) return false; /* ran off the end of the TOC */
-       tocp = &toc[art - head->Low];
-       if (tocp->Size != 0) {
-           /* got a valid article */
-           *artp = art;
-           return true;
-       }
-    }
-}
-
-
-
-ARTHANDLE *timecaf_next(const ARTHANDLE *article, const RETRTYPE amount) {
-    PRIV_TIMECAF       priv, *newpriv;
-    char                *path;
-    ARTHANDLE           *art;
-    size_t              length;
-
-    length = strlen(innconf->patharticles) + 32;
-    path = xmalloc(length);
-    if (article == NULL) {
-       priv.top = NULL;
-       priv.sec = NULL;
-       priv.ter = NULL;
-       priv.curtoc = NULL;
-       priv.topde = NULL;
-       priv.secde = NULL;
-       priv.terde = NULL;
-    } else {
-       priv = *(PRIV_TIMECAF *)article->private;
-       free(article->private);
-       free((void *)article);
-       if (innconf->articlemmap)
-           munmap(priv.mmapbase, priv.mmaplen);
-       else
-           free(priv.artdata);
-    }
-
-    while (priv.curtoc == NULL || !FindNextArt(&priv.curheader, priv.curtoc, &priv.curartnum)) {
-       if (priv.curtoc) {
-           free(priv.curtoc);
-           priv.curtoc = NULL;
-       }
-       while (!priv.ter || ((priv.terde = FindDir(priv.ter, FIND_CAF)) == NULL)) {
-           if (priv.ter) {
-               closedir(priv.ter);
-               priv.ter = NULL;
-           }
-           while (!priv.sec || ((priv.secde = FindDir(priv.sec, FIND_DIR)) == NULL)) {
-               if (priv.sec) {
-                   closedir(priv.sec);
-                   priv.sec = NULL;
-               }
-               if (!priv.top || ((priv.topde = FindDir(priv.top, FIND_TOPDIR)) == NULL)) {
-                   if (priv.top) {
-                       /* end of search */
-                       closedir(priv.top);
-                       priv.top = NULL;
-                       free(path);
-                       return NULL;
-                   }
-                   snprintf(path, length, "%s", innconf->patharticles);
-                   if ((priv.top = opendir(path)) == NULL) {
-                       SMseterror(SMERR_UNDEFINED, NULL);
-                       free(path);
-                       return NULL;
-                   }
-                   if ((priv.topde = FindDir(priv.top, FIND_TOPDIR)) == NULL) {
-                       SMseterror(SMERR_UNDEFINED, NULL);
-                       closedir(priv.top);
-                       free(path);
-                       return NULL;
-                   }
-               }
-               snprintf(path, length, "%s/%s", innconf->patharticles, priv.topde->d_name);
-               if ((priv.sec = opendir(path)) == NULL)
-                   continue;
-           }
-           snprintf(path, length, "%s/%s/%s", innconf->patharticles, priv.topde->d_name, priv.secde->d_name);
-           if ((priv.ter = opendir(path)) == NULL)
-               continue;
-       }
-       snprintf(path, length, "%s/%s/%s/%s", innconf->patharticles, priv.topde->d_name, priv.secde->d_name, priv.terde->d_name);
-       if ((priv.curtoc = CAFReadTOC(path, &priv.curheader)) == NULL)
-           continue;
-       priv.curartnum = 0;
-    }
-    snprintf(path, length, "%s/%s/%s/%s", innconf->patharticles, priv.topde->d_name, priv.secde->d_name, priv.terde->d_name);
-    art = OpenArticle(path, priv.curartnum, amount);
-    if (art == (ARTHANDLE *)NULL) {
-       art = xmalloc(sizeof(ARTHANDLE));
-       art->type = TOKEN_TIMECAF;
-       art->data = NULL;
-       art->len = 0;
-       art->private = xmalloc(sizeof(PRIV_TIMECAF));
-    }
-    newpriv = (PRIV_TIMECAF *)art->private;
-    newpriv->top = priv.top;
-    newpriv->sec = priv.sec;
-    newpriv->ter = priv.ter;
-    newpriv->topde = priv.topde;
-    newpriv->secde = priv.secde;
-    newpriv->terde = priv.terde;
-    newpriv->curheader = priv.curheader;
-    newpriv->curtoc = priv.curtoc;
-    newpriv->curartnum = priv.curartnum;
-    
-    snprintf(path, length, "%s/%s/%s", priv.topde->d_name, priv.secde->d_name, priv.terde->d_name);
-    art->token = PathNumToToken(path, priv.curartnum);
-    art->arrived = priv.curtoc[priv.curartnum - priv.curheader.Low].ModTime;
-    free(path);
-    return art;
-}
-
-bool timecaf_ctl(PROBETYPE type, TOKEN *token UNUSED, void *value) {
-    struct artngnum *ann;
-
-    switch (type) {
-    case SMARTNGNUM:
-       if ((ann = (struct artngnum *)value) == NULL)
-           return false;
-       /* make SMprobe() call timecaf_retrieve() */
-       ann->artnum = 0;
-       return true;
-    default:
-       return false;
-    }
-}
-
-bool timecaf_flushcacheddata(FLUSHTYPE type) {
-    if (type == SM_ALL || type == SM_CANCELEDART)
-       DoCancels();
-    return true;
-}
-
-void
-timecaf_printfiles(FILE *file, TOKEN token, char **xref UNUSED,
-                   int ngroups UNUSED)
-{
-    fprintf(file, "%s\n", TokenToText(token));
-}
-
-void timecaf_shutdown(void) {
-    CloseOpenFile(&WritingFile);
-    DoCancels();
-}