+++ /dev/null
-/* $Id: article.c 7285 2005-06-07 06:38:24Z eagle $
-**
-** The Article class for innfeed.
-**
-** Written by James Brister <brister@vix.com>
-**
-** The implementation of the Article class. Articles are the abstraction for
-** the actual news articles. They are a reference counted object because they
-** need to be shared by multiple Host and Connection objects. When an Article
-** is created it verifies that the actual file exists. When a Connection
-** wants the article's contents for transmission the Article reads the data
-** off disk and returns a set of Buffer objects. The Article holds onto these
-** Buffers so that the next Connection that wants to transmit it won't have
-** to wait for a disk read to be done again.
-*/
-
-#include "innfeed.h"
-#include "config.h"
-#include "clibrary.h"
-#include "portable/mmap.h"
-
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#if HAVE_LIMITS_H
-# include <limits.h>
-#endif
-#include <sys/stat.h>
-#include <syslog.h>
-
-#include "inn/messages.h"
-#include "libinn.h"
-#include "storage.h"
-
-#include "article.h"
-#include "buffer.h"
-#include "endpoint.h"
-
-#if defined (NDEBUG)
-#define VALIDATE_HASH_TABLE() (void (0))
-#else
-#define VALIDATE_HASH_TABLE() hashValidateTable ()
-#endif
-
-extern bool useMMap ;
-
-struct map_info_s {
- ARTHANDLE *arthandle;
- const void *mMapping;
- size_t size;
- int refCount;
- struct map_info_s *next;
-};
-
-typedef struct map_info_s *MapInfo;
-
-struct article_s
-{
- int refCount ; /* the reference count on this article */
- char *fname ; /* the file name of the article */
- char *msgid ; /* the msgid of the article (INN tells us) */
- Buffer contents ; /* the buffer of the actual on disk stuff */
- Buffer *nntpBuffers ; /* list of buffers for transmisson */
- MapInfo mapInfo; /* arthandle and mMapping */
- bool loggedMissing ; /* true if article is missing and we logged */
- bool articleOk ; /* true until we know otherwise. */
- bool inWireFormat ; /* true if ->contents is \r\n/dot-escaped */
-} ;
-
-struct hash_entry_s {
- struct hash_entry_s *next ;
- struct hash_entry_s *prev ;
- struct hash_entry_s *nextTime ;
- struct hash_entry_s *prevTime ;
- unsigned int hash ;
- Article article ;
-};
-
-typedef struct hash_entry_s *HashEntry ;
-
- /*
- * Private functions
- */
-
-static Buffer artGetContents (Article article) ; /* Return the buffer that
- fillContents() filled
- up. */
-
- /* Log statistics on article memory usage. */
-static void logArticleStats (TimeoutId id, void *data) ;
-
-static bool fillContents (Article article) ; /* Read the article's bits
- off the disk. */
-
- /* Append buffer B to the buffer array BUFFS. */
-static void appendBuffer (Buffer b, Buffer **buffs, int *newSpot, int *curLen);
-
-static bool prepareArticleForNNTP (Article article) ; /* Do the necessary
- CR-LF stuff */
-
-static bool artFreeContents (Article art) ; /* Tell the Article to release
- its contents buffer if
- possible. */
-
-static void artUnmap (Article art) ; /* munmap an mmap()ed
- article */
-
-
- /*
- * Hash table routine declarations.
- */
-
- /* Returns a has value for the given string */
-static unsigned int hashString (const char *string) ;
-
- /* Locates the article with the given message ID, in the has table. */
-static Article hashFindArticle (const char *msgid) ;
-
- /* Puts the given article in the hash table. */
-static void hashAddArticle (Article article) ;
-
- /* Removes the given article from the has table */
-static bool hashRemoveArticle (Article article) ;
-
- /* Does some simple-minded hash table validation */
-static void hashValidateTable (void) ;
-
-
-
- /*
- * Private data
- */
-
-
-static unsigned int missingArticleCount ; /* Number of articles that were missing */
-
-static bool logMissingArticles ; /* if true then we log the details on a
- missing article. */
-
-static unsigned int preparedBytes ; /* The number of areticle bytes read in so
- far of disk (will wrap around) */
-
-static unsigned int preparedNewlines ; /* The number of newlines found in all the
- preparedBytes */
-
-static unsigned int avgCharsPerLine ; /* The average number of characters per
- line over all articles. */
-
-static bool rolledOver ; /* true if preparedBytes wrapped around */
-
-static unsigned int bytesInUse ; /* count of article contents bytes in use--just
- the amount read from the article files, not
- all memory involved in. */
-
-static unsigned int maxBytesInUse ; /* the limit we want to stay under for total
- bytes resident in memory dedicated to
- article contents. */
-
-static unsigned int articlesInUse ; /* number of articles currently allocated. */
-
-static unsigned int byteTotal ; /* number of bytes for article contents
- allocated totally since last log. */
-
-static unsigned int articleTotal ; /* number of articles alloced since last log. */
-
-static TimeoutId articleStatsId ; /* The timer callback id. */
-
-static MapInfo mapInfo;
-
- /*
- * Hash Table data
- */
-
-static HashEntry *hashTable ; /* the has table itself */
-static HashEntry chronList ; /* chronologically ordered. Points at newest */
-
-#define TABLE_SIZE 2048 /* MUST be a power of 2 */
-#define HASH_MASK (TABLE_SIZE - 1)
-#define TABLE_ENTRY(hash) ((hash) & HASH_MASK)
-
-
-
- /*******************************************************************/
- /** PUBLIC FUNCTIONS **/
- /*******************************************************************/
-
- /* Create a new article object. First looks to see if one with the given
- message id already exists in the hash table and if so returns that
- (after incrementing the reference count). */
-Article newArticle (const char *filename, const char *msgid)
-{
- Article newArt = NULL ;
-
- TMRstart(TMR_NEWARTICLE);
- if (hashTable == NULL)
- { /* first-time through initialization. */
- int i ;
-
- ASSERT ((TABLE_SIZE & HASH_MASK) == 0) ;
- hashTable = xmalloc (sizeof(HashEntry) * TABLE_SIZE) ;
-
- addPointerFreedOnExit ((char *)hashTable) ;
-
- for (i = 0 ; i < TABLE_SIZE ; i++)
- hashTable [i] = NULL ;
-
- if (ARTICLE_STATS_PERIOD > 0)
- articleStatsId = prepareSleep (logArticleStats,ARTICLE_STATS_PERIOD,0);
- }
-
- /* now look for it in the hash table. We presume the disk file is still
- ok */
- if ((newArt = hashFindArticle (msgid)) == NULL)
- {
- newArt = xcalloc (1, sizeof(struct article_s)) ;
-
- newArt->fname = xstrdup (filename) ;
- newArt->msgid = xstrdup (msgid) ;
-
- newArt->contents = NULL ;
- newArt->mapInfo = NULL ;
- newArt->refCount = 1 ;
- newArt->loggedMissing = false ;
- newArt->articleOk = true ;
- newArt->inWireFormat = false ;
-
- d_printf (3,"Adding a new article(%p): %s\n", (void *)newArt, msgid) ;
-
- articlesInUse++ ;
- articleTotal++ ;
-
- hashAddArticle (newArt) ;
- }
- else
- {
- if (strcmp (filename,newArt->fname) != 0)
- warn ("ME two filenames for same article: %s, %s", filename,
- newArt->fname) ;
-
- newArt->refCount++ ;
- d_printf (2,"Reusing existing article for %s\nx",msgid) ;
- }
- TMRstop(TMR_NEWARTICLE);
- return newArt ;
-}
-
-
- /* Decrement the reference count on the article and free up its memory if
- the ref count gets to 0. */
-void delArticle (Article article)
-{
- if (article == NULL)
- return ;
-
- ASSERT (article->refCount > 0) ;
-
- if (--(article->refCount) == 0)
- {
- bool removed = hashRemoveArticle (article) ;
-
- ASSERT (removed == true) ;
-
- d_printf (2,"Cleaning up article (%p): %s\n",
- (void *)article, article->msgid) ;
-
- if (article->contents != NULL)
- {
- if (article->mapInfo)
- artUnmap(article);
- else
- bytesInUse -= bufferDataSize (article->contents) ;
-
- if (article->nntpBuffers != NULL)
- freeBufferArray (article->nntpBuffers) ;
-
- delBuffer (article->contents) ;
- }
-
- articlesInUse-- ;
-
- free (article->fname) ;
- free (article->msgid) ;
- free (article) ;
- }
-
- VALIDATE_HASH_TABLE () ;
-}
-
-
-void gPrintArticleInfo (FILE *fp, unsigned int indentAmt)
-{
- char indent [INDENT_BUFFER_SIZE] ;
- unsigned int i ;
-
- for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++)
- indent [i] = ' ' ;
- indent [i] = '\0' ;
-
- fprintf (fp,"%sGlobal Article information : (count %d) {\n",
- indent, articlesInUse) ;
-
- fprintf (fp,"%s missingArticleCount : %d\n",indent,missingArticleCount) ;
- fprintf (fp,"%s logMissingArticles : %d\n",indent,logMissingArticles) ;
- fprintf (fp,"%s preparedBytes : %d\n",indent,preparedBytes) ;
- fprintf (fp,"%s preparedNewlines : %d\n",indent,preparedNewlines) ;
- fprintf (fp,"%s avgCharsPerLine : %d\n",indent,avgCharsPerLine) ;
- fprintf (fp,"%s rolledOver : %s\n",indent,boolToString (rolledOver)) ;
- fprintf (fp,"%s bytesInUse : %d\n",indent,bytesInUse) ;
- fprintf (fp,"%s maxBytesInUse : %d\n",indent,maxBytesInUse) ;
- fprintf (fp,"%s articlesInUse : %d\n",indent,articlesInUse) ;
- fprintf (fp,"%s byteTotal : %d\n",indent,byteTotal) ;
- fprintf (fp,"%s articleTotal : %d\n",indent,articleTotal) ;
- fprintf (fp,"%s articleStatsId : %d\n",indent,articleStatsId) ;
-
- {
- HashEntry he ;
-
- for (he = chronList ; he != NULL ; he = he->nextTime)
- printArticleInfo (he->article,fp,indentAmt + INDENT_INCR) ;
- }
-
- fprintf (fp,"%s}\n",indent) ;
-}
-
-
-void printArticleInfo (Article art, FILE *fp, unsigned int indentAmt)
-{
- Buffer *b ;
- char indent [INDENT_BUFFER_SIZE] ;
- unsigned int i ;
-
- for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++)
- indent [i] = ' ' ;
- indent [i] = '\0' ;
-
- fprintf (fp,"%sArticle : %p {\n",indent,(void *) art) ;
- fprintf (fp,"%s article ok : %s\n",indent,boolToString (art->articleOk)) ;
- fprintf (fp,"%s refcount : %d\n",indent,art->refCount) ;
- fprintf (fp,"%s filename : %s\n",indent,art->fname) ;
- fprintf (fp,"%s msgid : %s\n",indent,art->msgid) ;
-
- fprintf (fp,"%s contents buffer : {\n",indent) ;
-#if 0
- printBufferInfo (art->contents,fp,indentAmt + INDENT_INCR) ;
-#else
- fprintf (fp,"%s %p\n",indent,(void *) art->contents) ;
-#endif
-
- fprintf (fp,"%s }\n",indent) ;
-
- fprintf (fp,"%s nntp buffers : {\n",indent) ;
- for (b = art->nntpBuffers ; b != NULL && *b != NULL ; b++)
-#if 0
- printBufferInfo (*b,fp,indentAmt + INDENT_INCR) ;
-#else
- fprintf (fp,"%s %p\n",indent,(void *) *b) ;
-#endif
-
- fprintf (fp,"%s }\n", indent) ;
-
- fprintf (fp,"%s logged missing : %s\n",
- indent,boolToString (art->loggedMissing));
- fprintf (fp,"%s}\n", indent) ;
-
-}
-
- /* return true if we have or are able to get the contents off the disk */
-bool artContentsOk (Article article)
-{
- bool rval = false ;
-
- if ( prepareArticleForNNTP (article) )
- rval = true ;
-
- return rval ;
-}
-
-
- /* bump reference count on the article. */
-Article artTakeRef (Article article)
-{
- if (article != NULL)
- article->refCount++ ;
-
- return article ;
-}
-
-
- /* return the filename of the article */
-const char *artFileName (Article article)
-{
- if (article == NULL)
- return NULL ;
- else
- return article->fname ;
-}
-
-
- /* Get a NULL terminated array of Buffers that is ready for sending via NNTP */
-
-Buffer *artGetNntpBuffers (Article article)
-{
- if ( !prepareArticleForNNTP (article) )
- return NULL ;
-
- return dupBufferArray (article->nntpBuffers) ;
-}
-
-
- /* return the message id of the article */
-const char *artMsgId (Article article)
-{
- return article->msgid ;
-}
-
- /* return size of the article */
-int artSize (Article article)
-{
- if (article == NULL || article->contents == NULL)
- return (int)0 ;
- return (int)bufferDataSize(article->contents);
-}
-
-
- /* return how many NNTP-ready buffers the article contains */
-unsigned int artNntpBufferCount (Article article)
-{
- if ( !prepareArticleForNNTP (article) )
- return 0 ;
-
- return bufferArrayLen (article->nntpBuffers) ;
-}
-
-
- /* if VAL is true then all missing articles will be logged. */
-void artLogMissingArticles (bool val)
-{
- logMissingArticles = val ;
-}
-
-
- /* set the limit we want to stay under. */
-void artSetMaxBytesInUse (unsigned int val)
-{
- ASSERT (maxBytesInUse > 0) ; /* can only set one time. */
- ASSERT (val > 0) ;
-
- maxBytesInUse = val ;
-}
-
-
- /**********************************************************************/
- /** STATIC FUNCTIONS **/
- /**********************************************************************/
-
-
- /* return a single buffer that contains the disk image of the article (i.e.
- not fixed up for NNTP). */
-static Buffer artGetContents (Article article)
-{
- Buffer rval = NULL ;
-
- if (article->articleOk)
- {
- if (article->contents == NULL)
- fillContents (article) ;
-
- if (article->contents != NULL)
- rval = bufferTakeRef (article->contents) ;
- }
-
- return rval ;
-}
-
- /* arthandle/mMapping needs to be refcounted since a buffer
- may exist referencing it after delArticle if the remote
- host sends the return status too soon (diablo, 439). */
-
-static MapInfo getMapInfo(ARTHANDLE *arthandle,
- const void *mMapping, size_t size)
-{
- MapInfo m;
-
- for (m = mapInfo; m; m = m->next) {
- if (m->arthandle == arthandle &&
- m->mMapping == mMapping) {
- m->refCount++;
- return m;
- }
- }
-
- m = xmalloc(sizeof(struct map_info_s));
- m->refCount = 1;
- m->arthandle = arthandle;
- m->mMapping = mMapping;
- m->size = size;
- m->next = mapInfo;
- mapInfo = m;
-
- return m;
-}
-
-static void delMapInfo(void *vm)
-{
- MapInfo i, prev;
- MapInfo m = (MapInfo)vm;
-
- if (m == NULL)
- return;
-
- if (--(m->refCount) > 0)
- return;
-
- if (m->arthandle)
- SMfreearticle(m->arthandle);
- else
- if (munmap(m->mMapping, m->size) < 0)
- syslog (LOG_NOTICE, "munmap article: %m");
-
- prev = NULL;
- for (i = mapInfo; i != m; i = i->next)
- prev = i;
-
- if (prev)
- prev->next = m->next;
- else
- mapInfo = m->next;
-
- free(m);
-}
-
-static void artUnmap (Article article) {
-
- delMapInfo(article->mapInfo);
- article->mapInfo = NULL;
-}
-
-
-
-static void logArticleStats (TimeoutId id, void *data UNUSED)
-{
- ASSERT (id == articleStatsId) ;
-
- notice ("ME articles active %d bytes %d", articlesInUse, bytesInUse) ;
- notice ("ME articles total %d bytes %d", articleTotal, byteTotal) ;
-
- byteTotal = 0 ;
- articleTotal = 0 ;
-
- articleStatsId = prepareSleep (logArticleStats,ARTICLE_STATS_PERIOD,0) ;
-}
-
-
- /* do the actual read of the article off disk into a Buffer that is stored
- in the Article object. The Article will end up with its contents field
- having a buffer with the article data in it. This buffer may be
- holding a mmapped pointer, or it may be simply a regular buffer with
- the data read off disk into it. In the regular buffer case the
- contents may be copied around after reading to insert a carriage
- return before each newline. */
-
-static bool fillContents (Article article)
-{
- int fd = -1;
- char *p;
- static bool maxLimitNotified ;
- bool opened;
- size_t articlesize = 0;
- char *buffer = NULL ;
- int amt = 0 ;
- size_t idx = 0, amtToRead ;
- size_t newBufferSize ;
- HashEntry h ;
- ARTHANDLE *arthandle = NULL;
- const void *mMapping = NULL;
-
- ASSERT (article->contents == NULL) ;
-
- TMRstart(TMR_READART);
- if (maxBytesInUse == 0)
- maxBytesInUse = SOFT_ARTICLE_BYTE_LIMIT ;
-
- if (avgCharsPerLine == 0)
- avgCharsPerLine = 75 ; /* roughly number of characters per line */
-
- if (IsToken(article->fname)) {
- opened = ((arthandle = SMretrieve(TextToToken(article->fname), RETR_ALL)) != NULL) ? true : false;
- if (opened)
- articlesize = arthandle->len;
- else {
- if (SMerrno != SMERR_NOENT && SMerrno != SMERR_UNINIT) {
- syslog(LOG_ERR, "Could not retrieve %s: %s",
- article->fname, SMerrorstr);
- article->articleOk = false;
- TMRstop(TMR_READART);
- return false;
- }
- }
- } else {
- struct stat sb ;
-
- opened = ((fd = open (article->fname,O_RDONLY,0)) >= 0) ? true : false;
- arthandle = NULL;
- if (opened) {
- if (fstat (fd, &sb) < 0) {
- article->articleOk = false ;
- syswarn ("ME oserr fstat %s", article->fname) ;
- TMRstop(TMR_READART);
- return false;
- }
- if (!S_ISREG (sb.st_mode)) {
- article->articleOk = false ;
- warn ("ME article file-type: %s", article->fname) ;
- TMRstop(TMR_READART);
- return false;
- }
- if (sb.st_size == 0) {
- article->articleOk = false ;
- warn ("ME article 0 bytes: %s", article->fname) ;
- TMRstop(TMR_READART);
- return false;
- }
- articlesize = sb.st_size;
- }
- }
-
- if (!opened) {
- article->articleOk = false ;
- missingArticleCount++ ;
-
- if (logMissingArticles && !article->loggedMissing)
- {
- notice ("ME article missing: %s, %s", article->msgid,
- article->fname) ;
- article->loggedMissing = true ;
- }
- TMRstop(TMR_READART);
- return false;
- }
- amtToRead = articlesize ;
- newBufferSize = articlesize ;
-
- if (arthandle || useMMap) {
- if (arthandle)
- mMapping = arthandle->data;
- else
- mMapping = mmap(NULL, articlesize, PROT_READ,
- MAP_SHARED, fd, 0);
-
- if (mMapping == MAP_FAILED) {
- /* dunno, but revert to plain reading */
- mMapping = NULL ;
- syswarn ("ME mmap failure %s (%s)",
- article->fname,
- strerror(errno)) ;
- } else {
- article->contents = newBufferByCharP(mMapping,
- articlesize,
- articlesize);
- article->mapInfo = getMapInfo(arthandle, mMapping, articlesize);
- article->mapInfo->refCount++; /* one more for the buffer */
- bufferSetDeletedCbk(article->contents, delMapInfo, article->mapInfo);
-
- buffer = bufferBase (article->contents) ;
- if ((p = strchr(buffer, '\n')) == NULL) {
- article->articleOk = false;
- delBuffer (article->contents) ;
- article->contents = NULL ;
- warn ("ME munged article %s", article->fname) ;
- } else {
- if (p[-1] == '\r') {
- article->inWireFormat = true ;
- } else {
- /* we need to copy the contents into a buffer below */
- delBuffer (article->contents) ;
- article->contents = NULL ;
- }
- }
- }
- }
-
- if (article->contents == NULL && article->articleOk) {
- /* an estimate to give some room for nntpPrepareBuffer to use. */
- newBufferSize *= (1.0 + (1.0 / avgCharsPerLine)) ;
- newBufferSize ++ ;
-
- /* if we're going over the limit try to free up some older article's
- contents. */
- if (amtToRead + bytesInUse > maxBytesInUse)
- {
- for (h = chronList ; h != NULL ; h = h->nextTime)
- {
- if (artFreeContents (h->article))
- if (amtToRead + bytesInUse <= maxBytesInUse)
- break ;
- }
- }
-
- /* we we couldn't get below, then log it (one time only) */
- if ((amtToRead + bytesInUse) > maxBytesInUse && maxLimitNotified == false) {
- maxLimitNotified = true ;
- notice ("ME exceeding maximum article byte limit: %d (max),"
- " %lu (cur)", maxBytesInUse,
- (unsigned long) (amtToRead + bytesInUse)) ;
- }
-
- if ((article->contents = newBuffer (newBufferSize)) == NULL)
- amtToRead = 0 ;
- else {
- buffer = bufferBase (article->contents) ;
- bytesInUse += articlesize ;
- byteTotal += articlesize ;
- }
-
- if (mMapping && buffer != NULL) {
- memcpy(buffer, mMapping, articlesize);
- artUnmap(article) ;
- amtToRead = 0;
- }
-
- while (amtToRead > 0) {
- if ((amt = read (fd, buffer + idx,amtToRead)) <= 0) {
- syswarn ("ME article read error: %s", article->fname) ;
- bytesInUse -= articlesize ;
- byteTotal -= articlesize ;
- amtToRead = 0 ;
-
- delBuffer (article->contents) ;
- article->contents = NULL ;
- }
- else {
- idx += amt ;
- amtToRead -= amt ;
- }
- }
-
- if (article->contents != NULL) {
- bufferSetDataSize (article->contents, articlesize) ;
-
- if ((p = strchr(buffer, '\n')) == NULL) {
- article->articleOk = false;
- warn ("ME munged article %s", article->fname) ;
- }
- else if (p[-1] == '\r') {
- article->inWireFormat = true ;
- }
- else {
- if ( nntpPrepareBuffer (article->contents) ) {
- size_t diff =
- (bufferDataSize (article->contents) - articlesize) ;
-
- if (((unsigned int) UINT_MAX) - diff <= preparedBytes) {
- d_printf (2,"Newline ratio so far: %02.2f\n",
- ((double) preparedBytes / preparedNewlines)) ;
- notice ("ME newline to file size ratio: %0.2f (%d/%d)",
- ((double) preparedBytes)/preparedNewlines,
- preparedBytes,preparedNewlines) ;
- preparedBytes = 0 ;
- preparedNewlines = 0 ;
- rolledOver = true ;
- }
-
- preparedBytes += articlesize ;
- preparedNewlines += diff ;
- bytesInUse += diff ;
- byteTotal += diff ;
-
- if (preparedBytes > (1024 * 1024)) {
- avgCharsPerLine =
- ((double) preparedBytes) / preparedNewlines ;
- avgCharsPerLine++ ;
- }
- article->inWireFormat = true ;
- } else {
- warn ("ME internal failed to prepare buffer for NNTP") ;
- bytesInUse -= articlesize ;
- byteTotal -= articlesize ;
-
- delBuffer (article->contents) ;
- article->contents = NULL ;
- }
- }
- }
- }
-
-
- /* If we're not useing storage api, we should close a valid file descriptor */
- if (!arthandle && (fd >= 0))
- close (fd) ;
-
- TMRstop(TMR_READART);
- return (article->contents != NULL ? true : false) ;
-}
-
-
-
- /* stick the buffer B into the Buffer array pointed at by BUFFS *BUFFS is
- reallocated if necessary. NEWSPOT points at the index where B should be
- put (presumably the end). CURLEN points at the length of the BUFFS and
- it will get updated if BUFFS is reallocated. */
-static void appendBuffer (Buffer b, Buffer **buffs, int *newSpot, int *curLen)
-{
- if (*newSpot == *curLen)
- {
- *curLen += 10 ;
- *buffs = xrealloc (*buffs, sizeof(Buffer) * *curLen) ;
- }
- (*buffs) [(*newSpot)++] = b ;
-}
-
-
-
- /* Takes the articles contents buffer and overlays a set of new buffers on
- top of it. These buffers insert the required carriage return and dot
- characters as needed */
-static bool prepareArticleForNNTP (Article article)
-{
- static Buffer dotFirstBuffer ;
- static Buffer dotBuffer ;
- static Buffer crlfBuffer ;
- Buffer *nntpBuffs = NULL ;
- int buffLen = 0 ;
- int buffIdx = 0 ;
- char *start, *end ;
- Buffer contents ;
-
- contents = artGetContents (article) ; /* returns a reference */
-
- TMRstart(TMR_PREPART);
- if (contents == NULL) {
- TMRstop(TMR_PREPART);
- return false ;
- }
- else if (article->nntpBuffers != NULL)
- {
- delBuffer (contents) ;
- TMRstop(TMR_PREPART);
- return true ; /* already done */
- }
-
- if (dotBuffer == NULL)
- {
- dotBuffer = newBufferByCharP (".\r\n",3,3) ;
- dotFirstBuffer = newBufferByCharP ("\r\n.",3,3) ;
- crlfBuffer = newBufferByCharP ("\r\n",2,2) ;
- }
-
- /* overlay a set of buffers on top of the articles contents buffer. This
- is a real speed loss at the moment, so by default it's disabled (by
- calling artBitFiddleContents(true) in main(). */
- if (article->inWireFormat == false)
- {
- end = bufferBase (contents) ;
- do
- {
- start = end ;
-
- while (*end && *end != '\n')
- end++ ;
-
- appendBuffer (newBufferByCharP (start, (size_t) (end - start),
- (size_t) (end - start)),
- &nntpBuffs,&buffIdx,&buffLen) ;
-
- if (*end != '\0')
- end++ ;
-
- }
- while (*end != '\0') ;
-
- appendBuffer (bufferTakeRef (dotBuffer), &nntpBuffs,&buffIdx,&buffLen) ;
- appendBuffer (NULL,&nntpBuffs,&buffIdx,&buffLen) ;
- }
- else
- {
- /* we already fixed the contents up when we read in the article */
- nntpBuffs = xmalloc (sizeof(Buffer) * 3) ;
- nntpBuffs [0] = newBufferByCharP (bufferBase (contents),
- bufferDataSize (contents),
- bufferDataSize (contents)) ;
- if (article->mapInfo) {
- article->mapInfo->refCount++;
- bufferSetDeletedCbk(nntpBuffs[0], delMapInfo, article->mapInfo);
- }
- nntpBuffs [1] = NULL ;
- }
-
-
- delBuffer (contents) ; /* the article is still holding a reference */
- article->nntpBuffers = nntpBuffs ;
- TMRstop(TMR_PREPART);
- return true ;
-}
-
-
- /* free the contents of the buffers if article is the only thing holding a
- reference. Returns true if it could, false if it couldn't */
-static bool artFreeContents (Article art)
-{
- if (art->contents == NULL)
- return false ;
-
- if (art->nntpBuffers != NULL)
- {
- if (bufferRefCount (art->nntpBuffers[0]) > 1)
- return false ;
- else
- {
- freeBufferArray (art->nntpBuffers) ;
- art->nntpBuffers = NULL ;
- }
- }
-
- ASSERT (bufferRefCount (art->contents) == 1) ;
-
- if (art->mapInfo)
- artUnmap(art);
- else
- bytesInUse -= bufferDataSize (art->contents) ;
-
- delBuffer (art->contents) ;
-
- art->contents = NULL ;
-
- return true ;
-}
-
-
-
-
-
-
-
-
- /**********************************************************************/
- /* Private hash table and routines for storing articles */
- /**********************************************************************/
-
-
-
-
- /* Hash function lifted from perl 5 */
-static unsigned int hashString (const char *string)
-{
- unsigned int i ;
-
- for (i = 0 ; string && *string ; string++)
- i = 33 * i + (u_char) *string ;
-
- return i ;
-}
-
-
- /* find the article in the has table and return it. */
-static Article hashFindArticle (const char *msgid)
-{
- unsigned int hash = hashString (msgid) ;
- HashEntry h ;
-
- for (h = hashTable [TABLE_ENTRY(hash)] ; h != NULL ; h = h->next)
- if (hash == h->hash && strcmp (msgid,h->article->msgid) == 0)
- break ;
-
- return (h == NULL ? NULL : h->article) ;
-}
-
-
- /* add the article to the hash table. */
-static void hashAddArticle (Article article)
-{
- unsigned int hash = hashString (article->msgid) ;
- HashEntry h ;
- HashEntry ne ;
-
- h = hashTable [TABLE_ENTRY(hash)] ;
-
- ne = xmalloc (sizeof(struct hash_entry_s));
-
- ne->article = article ;
- ne->hash = hash ;
- ne->next = hashTable [TABLE_ENTRY(hash)] ;
- ne->prev = NULL ;
-
- if (h != NULL)
- h->prev = ne ;
-
- hashTable [TABLE_ENTRY(hash)] = ne ;
-
- ne->nextTime = chronList ;
- ne->prevTime = NULL ;
-
- if (chronList != NULL)
- chronList->prevTime = ne ;
-
- chronList = ne ;
-}
-
-
- /* remove the article from the hash table and chronological list.
- Does not delete the article itself. */
-static bool hashRemoveArticle (Article article)
-{
- unsigned int hash = hashString (article->msgid) ;
- HashEntry h ;
-
- for (h = hashTable [TABLE_ENTRY(hash)] ; h != NULL ; h = h->next)
- if (hash == h->hash && strcmp (article->msgid,h->article->msgid) == 0)
- break ;
-
- if (h == NULL)
- return false ;
-
- if (h == hashTable [TABLE_ENTRY(hash)])
- {
- hashTable [TABLE_ENTRY(hash)] = h->next ;
- if (h->next != NULL)
- h->next->prev = NULL ;
- }
- else
- {
- h->prev->next = h->next ;
- if (h->next != NULL)
- h->next->prev = h->prev ;
- }
-
- if (chronList == h)
- {
- chronList = h->nextTime ;
- if (chronList != NULL)
- chronList->prevTime = NULL ;
- }
- else
- {
- h->prevTime->nextTime = h->nextTime ;
- if (h->nextTime != NULL)
- h->nextTime->prevTime = h->prevTime ;
- }
-
- free (h) ;
-
- return true ;
-}
-
-#define HASH_VALIDATE_BUCKET_COUNT 1 /* hash buckets to check per call */
-
-static void hashValidateTable (void)
-{
- static int hbn = 0 ;
- int i ;
- HashEntry he ;
-
-#if ! defined (NDEBUG)
- for (i = 0 ; i < HASH_VALIDATE_BUCKET_COUNT ; i++)
- {
- for (he = hashTable [hbn] ; he != NULL ; he = he->next)
- ASSERT (he->article->refCount > 0) ;
- if (++hbn >= TABLE_SIZE)
- hbn = 0 ;
- }
-#endif
-}