/* HTNewsLs.c
** NEWS (NNTP) GROUP LISTINGS
**
** (c) COPYRIGHT MIT 1995.
** Please first read the full copyright statement in the file COPYRIGH.
** @(#) $Id: HTNewsLs.c,v 2.15 1998/02/01 19:04:16 frystyk Exp $
**
** Authors
** FTLO Felix Lo
** HFN Henrik Frystyk <frystyk@w3.org>
** MP Maciej Puzio <puzio@zodiac1.mimuw.edu.pl>
**
** History:
** Oct 95 HFN Written
** Mar 96 MP Modified
*/
/* Library include files */
#include "sysdep.h"
#include "WWWUtil.h"
#include "WWWCore.h"
#include "HTNDir.h"
#include "HTNews.h"
#include "HTNewsLs.h" /* Implemented here */
#define DELIMITER '\t'
#define ATSIGN '@'
#define NEWS_TREE "w3c-news"
struct _HTStream {
const HTStreamClass * isa;
HTRequest * request;
HTEOLState state;
HTNewsDir * dir;
BOOL group;
BOOL junk;
char buffer[MAX_NEWS_LINE+1];
int buflen;
};
typedef struct _HTNewsCache {
char * host;
HTArray * cache;
} HTNewsCache;
PRIVATE HTNewsDirKey dir_key = HT_NDK_REFTHREAD;
PRIVATE HTNewsDirKey list_key = HT_NDK_GROUP; /* Added by MP. */
/* ------------------------------------------------------------------------- */
/* Helper function added by MP. */
PRIVATE char* GetNewsGroupTitle (HTRequest* request)
{
char * url = HTAnchor_physical(HTRequest_anchor(request));
char * title = NULL;
if (strrchr(url, '*'))
StrAllocCopy(title, "Newsgroups: ");
else
StrAllocCopy(title, "Newsgroup: ");
if (!strncasecomp(url, "news:", 5))
StrAllocCat(title, url+5);
else
StrAllocCat(title, HTParse(url, "", PARSE_PATH));
return title;
}
PRIVATE BOOL ParseList (HTNewsDir * dir, char * line)
{
char *ptr = line;
while (*ptr && !isspace((int) *ptr)) ptr++;
*ptr = '\0';
/* Changed by MP */
return (HTNewsDir_addGroupElement(dir, line, NO) != NULL);
}
/* ParseGroup
** ----------
** Extract the index number, subject etc, from a XOVER command. Expects
** the following format of the line:
**
** <index> <subject> <from> <data> <msgid> [*<thread>] ...
**
** Returns YES if OK, NO on error
*/
PRIVATE BOOL ParseGroup (HTRequest * request, HTNewsDir *dir, char * line)
{
int index;
int refcnt=0;
time_t t=0;
char *subject = line;
char *from;
char *date;
char *msgid;
char *ptr=NULL;
HTList* reflist = NULL; /* Added by MP. */
while (*subject && *subject != DELIMITER) subject++;
*subject++ = '\0'; /* Index */
index = atoi(line);
from = subject;
while (*from && *from != DELIMITER) from++;
*from++ = '\0'; /* Subject */
date = from;
while (*date && *date != DELIMITER) {
if (*date=='<' || *date=='(') {
ptr = date+1;
*date = '\0';
}
if (*date=='>' || *date==')') *date = '\0';
date++;
}
*date++ = '\0';
if (strchr(from, ATSIGN) && ptr) from = ptr; /* From */
msgid = date;
while (*msgid && *msgid != DELIMITER) msgid++;
*msgid++ = '\0'; /* Date */
if (*msgid=='<') msgid++;
t = HTParseTime(date, HTRequest_userProfile(request), YES);
ptr = msgid;
while (*ptr && *ptr != DELIMITER) {
if (*ptr=='>') *ptr = '\0';
ptr++;
}
*ptr++ = '\0'; /* MsgId */
while (ptr && *ptr && !isdigit((int) *ptr)) {
char* refstart = ptr; /* Added by MP. */
char* refcopy = NULL;
char* refstop;
while (*ptr && *ptr != DELIMITER && *ptr != ' ') ptr++;
refstop = ptr - 1;
*ptr++ = '\0';
if (strlen(refstart) > 0) /* Added by MP. */
{
refcnt++;
if (*refstart == '<') refstart++;
if (*refstop == '>') *refstop = '\0';
if (reflist == NULL) reflist = HTList_new();
StrAllocCopy (refcopy, refstart);
HTList_addObject (reflist, (void*) refcopy);
}
}
/* Changed by MP. */
return (HTNewsDir_addElement(dir, index, subject, from, t, msgid,
refcnt, reflist) != NULL);
}
/* ------------------------------------------------------------------------- */
/* NEWS CACHE */
/* ------------------------------------------------------------------------- */
PRIVATE HTNewsCache * HTNewsCache_new (const char * newshost, HTArray * array)
{
if (newshost && array) {
HTNewsCache * me;
if ((me = (HTNewsCache *) HT_CALLOC(1, sizeof(HTNewsCache))) == NULL)
HT_OUTOFMEM("HTNewsCache_new");
StrAllocCopy(me->host, newshost);
me->cache = array;
return me;
}
return NULL;
}
/*
** Instead of just deleting we could save it to file.
*/
PRIVATE int HTNewsCache_delete (void * context)
{
HTNewsCache * me = (HTNewsCache *) context;
if (me) {
if (me->cache) {
void ** data;
char * line = (char *) HTArray_firstObject(me->cache, data);
while (line) {
HT_FREE(line);
line = (char *) HTArray_nextObject(me->cache, data);
}
HTArray_delete(me->cache);
}
HT_FREE(me->host);
if (PROT_TRACE) HTTrace("News Cache.. Deleted cache %p\n", me);
HT_FREE(me);
return YES;
}
return NO;
}
/*
** Look for cached information for this news server
** We store the information in a URL Tree so that we can have multiple
** servers stored simultanously
*/
PRIVATE HTNewsCache * HTNewsCache_find (HTRequest * request, const char * url)
{
HTUTree * tree = NULL;
if (request && url) {
char * newshost = NULL;
HTNewsCache * element = NULL;
if (!strncasecomp(url, "news:", 5)) {
HTUserProfile * up = HTRequest_userProfile(request);
StrAllocCopy(newshost, HTUserProfile_news(up));
} else if (!strncasecomp(url, "nntp:", 5)) {
newshost = HTParse(url, "", PARSE_HOST);
}
/* If we have a news server then continue to find a URL tree */
if (newshost) {
char * colon = strchr(newshost, ':');
int port = NEWS_PORT;
if (colon ) {
*(colon++) = '\0'; /* Chop off port number */
port = atoi(colon);
}
tree = HTUTree_find(NEWS_TREE, newshost, port);
HT_FREE(newshost);
if (!tree) {
if (PROT_TRACE)
HTTrace("News Cache.. No information for `%s\'\n", url);
return NULL;
}
/* Find a cache element (if any) */
element = (HTNewsCache *) HTUTree_findNode(tree, "", "/");
return element;
}
}
return NULL;
}
PRIVATE BOOL HTNewsCache_update (HTRequest * request,
const char * url, HTArray * array)
{
HTUTree * tree = NULL;
if (request && url) {
char * newshost = NULL;
if (!strncasecomp(url, "news:", 5)) {
HTUserProfile * up = HTRequest_userProfile(request);
StrAllocCopy(newshost, HTUserProfile_news(up));
} else if (!strncasecomp(url, "nntp:", 5)) {
newshost = HTParse(url, "", PARSE_HOST);
}
/*
** If the news server was found then update the data entry. Otherwise
** create a new entry
*/
if (newshost) {
char * colon = strchr(newshost, ':');
int port = NEWS_PORT;
if (colon ) {
*(colon++) = '\0'; /* Chop off port number */
port = atoi(colon);
}
tree = HTUTree_new(NEWS_TREE, newshost, port, HTNewsCache_delete);
HT_FREE(newshost);
if (!tree) {
if (PROT_TRACE)HTTrace("News Cache.. Can't create tree\n");
return NO;
}
/* Add new cache information to the tree */
{
HTNewsCache * element = NULL;
BOOL status;
if ((element=(HTNewsCache *) HTUTree_findNode(tree, "", "/"))){
element->cache = array;
status = YES;
} else {
element = HTNewsCache_new(url, array);
status = HTUTree_addNode(tree, "", "/", element);
}
return status;
}
}
}
return NO;
}
/*
** Before filter: Check whether we have a cache entry for this host
*/
PUBLIC int HTNewsCache_before (HTRequest * request, void * context, int mode)
{
char * url = HTAnchor_address((HTAnchor *) HTRequest_anchor(request));
HTNewsCache * element = HTNewsCache_find(request, url);
HT_FREE(url);
/*
** If we have found a cache object then create a new dir obejct and fill
** it with data from the cache
*/
if (element) {
char * title = GetNewsGroupTitle(request);
HTNewsDir * dir = HTNewsDir_new(request, title, list_key, NO);
void ** data;
char * line = (char *) HTArray_firstObject(element->cache, data);
while (line) {
HTNewsDir_addGroupElement(dir, line, NO);
line = (char *) HTArray_nextObject(element->cache, data);
}
/*
** After filling the new dir object we write it out and free it again
*/
HTNewsDir_free(dir);
HT_FREE(title);
return HT_LOADED;
}
return HT_OK;
}
/*
** After filter: Update the cache entry for this host
*/
PUBLIC int HTNewsCache_after (HTRequest * request, HTResponse * response,
void * context, int status)
{
HTArray * array = (HTArray *) context;
if (PROT_TRACE) HTTrace("News Cache.. AFTER filter\n");
if (request && array) {
char * url = HTAnchor_address((HTAnchor *) HTRequest_anchor(request));
HTNewsCache_update(request, url, array);
HT_FREE(url);
}
return HT_OK;
}
/* ------------------------------------------------------------------------- */
/*
** Searches for News line until buffer fills up or a CRLF or LF is found
*/
PRIVATE int HTNewsList_put_block (HTStream * me, const char * b, int l)
{
while (l-- > 0) {
if (me->state == EOL_FCR) {
if (*b == LF && me->buflen) {
if (!me->junk) {
*(me->buffer+me->buflen) = '\0';
me->group ? ParseGroup(me->request, me->dir, me->buffer) :
ParseList(me->dir, me->buffer);
} else
me->junk = NO; /* back to normal */
}
me->buflen = 0;
me->state = EOL_BEGIN;
} else if (*b == CR) {
me->state = EOL_FCR;
} else if (*b == LF && me->buflen) {
if (!me->junk) {
*(me->buffer+me->buflen) = '\0';
me->group ? ParseGroup(me->request, me->dir, me->buffer) :
ParseList(me->dir, me->buffer);
} else
me->junk = NO; /* back to normal */
me->buflen = 0;
me->state = EOL_BEGIN;
} else {
*(me->buffer+me->buflen++) = *b;
if (me->buflen >= MAX_NEWS_LINE) {
if (PROT_TRACE)
HTTrace("News Dir.... Line too long - chopped\n");
*(me->buffer+me->buflen) = '\0';
me->group ? ParseGroup(me->request, me->dir, me->buffer) :
ParseList(me->dir, me->buffer);
me->buflen = 0;
me->junk = YES;
}
}
b++;
}
return HT_OK;
}
PRIVATE int HTNewsList_put_character (HTStream * me, char ch)
{
return HTNewsList_put_block(me, &ch, 1);
}
PRIVATE int HTNewsList_put_string (HTStream * me, const char * s)
{
return HTNewsList_put_block(me, s, (int) strlen(s));
}
PRIVATE int HTNewsList_flush (HTStream * me)
{
return HT_OK;
}
PRIVATE int HTNewsList_free (HTStream * me)
{
HTNewsList_put_character (me, '\n'); /* to flush the last item; added by MP. */
HTNewsDir_free(me->dir);
HT_FREE(me);
return HT_OK;
}
PRIVATE int HTNewsList_abort (HTStream * me, HTList * e)
{
if (PROT_TRACE) HTTrace("News Dir.... ABORTING...\n");
HTNewsList_free(me);
return HT_ERROR;
}
PRIVATE const HTStreamClass HTNewsListClass =
{
"NewsList",
HTNewsList_flush,
HTNewsList_free,
HTNewsList_abort,
HTNewsList_put_character,
HTNewsList_put_string,
HTNewsList_put_block
};
PUBLIC HTStream *HTNewsList (HTRequest * request,
void * param,
HTFormat input_format,
HTFormat output_format,
HTStream * output_stream)
{
HTStream *me;
if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
HT_OUTOFMEM("HTNewsList_new");
me->isa = &HTNewsListClass;
me->request = request;
me->state = EOL_BEGIN;
{
char * title = GetNewsGroupTitle(request);
me->dir = HTNewsDir_new(request, title, list_key,YES);
HT_FREE(title);
}
/* Modified by MP. */
if (me->dir == NULL) HT_FREE(me);
return me;
}
PUBLIC HTStream *HTNewsGroup (HTRequest * request,
void * param,
HTFormat input_format,
HTFormat output_format,
HTStream * output_stream)
{
HTStream * me;
if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
HT_OUTOFMEM("HTNewsList_new");
me->isa = &HTNewsListClass;
me->request = request;
me->state = EOL_BEGIN;
me->group = YES;
{
char * title = GetNewsGroupTitle(request);
me->dir = HTNewsDir_new(request, title, dir_key, YES);
HT_FREE(title);
}
/* Modified by MP. */
if (me->dir == NULL) HT_FREE(me);
return me;
}
Webmaster