+++ /dev/null
-/* $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, ×tamp, &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();
-}