Annotation of libwww/Library/src/HTNewsLs.c, revision 2.12
2.1 frystyk 1: /* HTNewsLs.c
2: ** NEWS (NNTP) GROUP LISTINGS
3: **
4: ** (c) COPYRIGHT MIT 1995.
5: ** Please first read the full copyright statement in the file COPYRIGH.
2.12 ! frystyk 6: ** @(#) $Id: HTNewsLs.c,v 2.11 1996/05/20 15:07:04 frystyk Exp $
2.1 frystyk 7: **
8: ** Authors
9: ** FTLO Felix Lo
10: ** HFN Henrik Frystyk <frystyk@w3.org>
2.12 ! frystyk 11: ** MP Maciej Puzio <puzio@zodiac1.mimuw.edu.pl>
2.1 frystyk 12: **
13: ** History:
14: ** Oct 95 HFN Written
2.12 ! frystyk 15: ** Mar 96 MP Modified
2.1 frystyk 16: */
17:
18: /* Library include files */
2.8 frystyk 19: #include "sysdep.h"
2.11 frystyk 20: #include "WWWUtil.h"
21: #include "WWWCore.h"
2.1 frystyk 22: #include "HTNDir.h"
23: #include "HTNews.h"
24: #include "HTNewsLs.h" /* Implemented here */
25:
26: #define DELIMITER '\t'
2.5 frystyk 27: #define ATSIGN '@'
2.1 frystyk 28:
2.12 ! frystyk 29: #define NEWS_TREE "w3c-news"
! 30:
2.1 frystyk 31: struct _HTStream {
2.8 frystyk 32: const HTStreamClass * isa;
2.1 frystyk 33: HTRequest * request;
2.9 frystyk 34: HTEOLState state;
2.1 frystyk 35: HTNewsDir * dir;
36: BOOL group;
37: BOOL junk;
38: char buffer[MAX_NEWS_LINE+1];
39: int buflen;
40: };
41:
2.12 ! frystyk 42: typedef struct _HTNewsCache {
! 43: char * host;
! 44: HTArray * cache;
! 45: } HTNewsCache;
! 46:
! 47: PRIVATE HTNewsDirKey dir_key = HT_NDK_REFTHREAD;
! 48: PRIVATE HTNewsDirKey list_key = HT_NDK_GROUP; /* Added by MP. */
2.1 frystyk 49:
50: /* ------------------------------------------------------------------------- */
51:
2.12 ! frystyk 52: /* Helper function added by MP. */
! 53: PRIVATE char* GetNewsGroupTitle (HTRequest* request)
! 54: {
! 55: char * url = HTAnchor_physical(HTRequest_anchor(request));
! 56: char * title = NULL;
! 57: if (strrchr(url, '*'))
! 58: StrAllocCopy(title, "Newsgroups: ");
! 59: else
! 60: StrAllocCopy(title, "Newsgroup: ");
! 61: if (!strncasecomp(url, "news:", 5))
! 62: StrAllocCat(title, url+5);
! 63: else
! 64: StrAllocCat(title, HTParse(url, "", PARSE_PATH));
! 65: return title;
! 66: }
2.1 frystyk 67:
2.12 ! frystyk 68: PRIVATE BOOL ParseList (HTNewsDir * dir, char * line)
2.1 frystyk 69: {
70: char *ptr = line;
71: while (*ptr && !WHITE(*ptr)) ptr++;
72: *ptr = '\0';
2.12 ! frystyk 73: /* Changed by MP */
! 74: return (HTNewsDir_addGroupElement(dir, line, NO) != NULL);
2.1 frystyk 75: }
76:
77: /* ParseGroup
78: ** ----------
79: ** Extract the index number, subject etc, from a XOVER command. Expects
80: ** the following format of the line:
81: **
82: ** <index> <subject> <from> <data> <msgid> [*<thread>] ...
83: **
84: ** Returns YES if OK, NO on error
85: */
2.12 ! frystyk 86: PRIVATE BOOL ParseGroup (HTRequest * request, HTNewsDir *dir, char * line)
2.1 frystyk 87: {
88: int index;
2.3 frystyk 89: int refcnt=0;
2.5 frystyk 90: time_t t=0;
91: char *subject = line;
92: char *from;
93: char *date;
2.1 frystyk 94: char *msgid;
2.5 frystyk 95: char *ptr=NULL;
2.12 ! frystyk 96: HTList* reflist = NULL; /* Added by MP. */
2.1 frystyk 97: while (*subject && *subject != DELIMITER) subject++;
2.5 frystyk 98: *subject++ = '\0'; /* Index */
2.1 frystyk 99: index = atoi(line);
2.5 frystyk 100: from = subject;
2.1 frystyk 101: while (*from && *from != DELIMITER) from++;
2.5 frystyk 102: *from++ = '\0'; /* Subject */
103: date = from;
104: while (*date && *date != DELIMITER) {
105: if (*date=='<' || *date=='(') {
106: ptr = date+1;
107: *date = '\0';
108: }
109: if (*date=='>' || *date==')') *date = '\0';
110: date++;
111: }
112: *date++ = '\0';
113: if (strchr(from, ATSIGN) && ptr) from = ptr; /* From */
114: msgid = date;
2.1 frystyk 115: while (*msgid && *msgid != DELIMITER) msgid++;
2.5 frystyk 116: *msgid++ = '\0'; /* Date */
117: if (*msgid=='<') msgid++;
2.12 ! frystyk 118: t = HTParseTime(date, HTRequest_userProfile(request));
2.5 frystyk 119: ptr = msgid;
120: while (*ptr && *ptr != DELIMITER) {
121: if (*ptr=='>') *ptr = '\0';
122: ptr++;
123: }
124: *ptr++ = '\0'; /* MsgId */
2.12 ! frystyk 125: while (ptr && *ptr && !isdigit(*ptr)) {
! 126: char* refstart = ptr; /* Added by MP. */
! 127: char* refcopy = NULL;
! 128: char* refstop;
! 129: while (*ptr && *ptr != DELIMITER && *ptr != ' ') ptr++;
! 130: refstop = ptr - 1;
! 131: *ptr++ = '\0';
! 132: if (strlen(refstart) > 0) /* Added by MP. */
! 133: {
! 134: refcnt++;
! 135: if (*refstart == '<') refstart++;
! 136: if (*refstop == '>') *refstop = '\0';
! 137: if (reflist == NULL) reflist = HTList_new();
! 138: StrAllocCopy (refcopy, refstart);
! 139: HTList_addObject (reflist, (void*) refcopy);
! 140: }
! 141: }
! 142: /* Changed by MP. */
! 143: return (HTNewsDir_addElement(dir, index, subject, from, t, msgid,
! 144: refcnt, reflist) != NULL);
! 145: }
! 146:
! 147: /* ------------------------------------------------------------------------- */
! 148: /* NEWS CACHE */
! 149: /* ------------------------------------------------------------------------- */
! 150:
! 151: PRIVATE HTNewsCache * HTNewsCache_new (const char * newshost, HTArray * array)
! 152: {
! 153: if (newshost && array) {
! 154: HTNewsCache * me;
! 155: if ((me = (HTNewsCache *) HT_CALLOC(1, sizeof(HTNewsCache))) == NULL)
! 156: HT_OUTOFMEM("HTNewsCache_new");
! 157: StrAllocCopy(me->host, newshost);
! 158: me->cache = array;
! 159: return me;
! 160: }
! 161: return NULL;
! 162: }
! 163:
! 164: /*
! 165: ** Instead of just deleting we could save it to file.
! 166: */
! 167: PRIVATE int HTNewsCache_delete (void * context)
! 168: {
! 169: HTNewsCache * me = (HTNewsCache *) context;
! 170: if (me) {
! 171: if (me->cache) {
! 172: void ** data;
! 173: char * line = (char *) HTArray_firstObject(me->cache, data);
! 174: while (line) {
! 175: HT_FREE(line);
! 176: line = (char *) HTArray_nextObject(me->cache, data);
! 177: }
! 178: HTArray_delete(me->cache);
! 179: }
! 180: HT_FREE(me->host);
! 181: if (PROT_TRACE) HTTrace("News Cache.. Deleted cache %p\n", me);
! 182: HT_FREE(me);
! 183: return YES;
2.1 frystyk 184: }
2.12 ! frystyk 185: return NO;
2.1 frystyk 186: }
187:
188: /*
2.12 ! frystyk 189: ** Look for cached information for this news server
! 190: ** We store the information in a URL Tree so that we can have multiple
! 191: ** servers stored simultanously
! 192: */
! 193: PRIVATE HTNewsCache * HTNewsCache_find (HTRequest * request, const char * url)
! 194: {
! 195: HTUTree * tree = NULL;
! 196: if (request && url) {
! 197: char * newshost = NULL;
! 198: HTNewsCache * element = NULL;
! 199: if (!strncasecomp(url, "news:", 5)) {
! 200: HTUserProfile * up = HTRequest_userProfile(request);
! 201: StrAllocCopy(newshost, HTUserProfile_news(up));
! 202: } else if (!strncasecomp(url, "nntp:", 5)) {
! 203: newshost = HTParse(url, "", PARSE_HOST);
! 204: }
! 205:
! 206: /* If we have a news server then continue to find a URL tree */
! 207: if (newshost) {
! 208: char * colon = strchr(newshost, ':');
! 209: int port = NEWS_PORT;
! 210: if (colon ) {
! 211: *(colon++) = '\0'; /* Chop off port number */
! 212: port = atoi(colon);
! 213: }
! 214: tree = HTUTree_find(NEWS_TREE, newshost, port);
! 215: HT_FREE(newshost);
! 216: if (!tree) {
! 217: if (PROT_TRACE)
! 218: HTTrace("News Cache.. No information for `%s\'\n", url);
! 219: return NULL;
! 220: }
! 221:
! 222: /* Find a cache element (if any) */
! 223: element = (HTNewsCache *) HTUTree_findNode(tree, "", "/");
! 224: return element;
! 225: }
! 226: }
! 227: return NULL;
! 228: }
! 229:
! 230: PRIVATE BOOL HTNewsCache_update (HTRequest * request,
! 231: const char * url, HTArray * array)
! 232: {
! 233: HTUTree * tree = NULL;
! 234: if (request && url) {
! 235: char * newshost = NULL;
! 236: if (!strncasecomp(url, "news:", 5)) {
! 237: HTUserProfile * up = HTRequest_userProfile(request);
! 238: StrAllocCopy(newshost, HTUserProfile_news(up));
! 239: } else if (!strncasecomp(url, "nntp:", 5)) {
! 240: newshost = HTParse(url, "", PARSE_HOST);
! 241: }
! 242:
! 243: /*
! 244: ** If the news server was found then update the data entry. Otherwise
! 245: ** create a new entry
! 246: */
! 247: if (newshost) {
! 248: char * colon = strchr(newshost, ':');
! 249: int port = NEWS_PORT;
! 250: if (colon ) {
! 251: *(colon++) = '\0'; /* Chop off port number */
! 252: port = atoi(colon);
! 253: }
! 254: tree = HTUTree_new(NEWS_TREE, newshost, port, HTNewsCache_delete);
! 255: HT_FREE(newshost);
! 256: if (!tree) {
! 257: if (PROT_TRACE)HTTrace("News Cache.. Can't create tree\n");
! 258: return NO;
! 259: }
! 260:
! 261: /* Add new cache information to the tree */
! 262: {
! 263: HTNewsCache * element = NULL;
! 264: BOOL status;
! 265: if ((element=(HTNewsCache *) HTUTree_findNode(tree, "", "/"))){
! 266: element->cache = array;
! 267: status = YES;
! 268: } else {
! 269: element = HTNewsCache_new(url, array);
! 270: status = HTUTree_addNode(tree, "", "/", element);
! 271: }
! 272: return status;
! 273: }
! 274: }
! 275: }
! 276: return NO;
! 277: }
! 278:
! 279: /*
! 280: ** Before filter: Check whether we have a cache entry for this host
! 281: */
! 282: PUBLIC int HTNewsCache_before (HTRequest * request, void * context, int status)
! 283: {
! 284: char * url = HTAnchor_address((HTAnchor *) HTRequest_anchor(request));
! 285: HTNewsCache * element = HTNewsCache_find(request, url);
! 286: HT_FREE(url);
! 287: /*
! 288: ** If we have found a cache object then create a new dir obejct and fill
! 289: ** it with data from the cache
! 290: */
! 291: if (element) {
! 292: char * title = GetNewsGroupTitle(request);
! 293: HTNewsDir * dir = HTNewsDir_new(request, title, list_key, NO);
! 294: void ** data;
! 295: char * line = (char *) HTArray_firstObject(element->cache, data);
! 296: while (line) {
! 297: HTNewsDir_addGroupElement(dir, line, NO);
! 298: line = (char *) HTArray_nextObject(element->cache, data);
! 299: }
! 300:
! 301: /*
! 302: ** After filling the new dir object we write it out and free it again
! 303: */
! 304: HTNewsDir_free(dir);
! 305: HT_FREE(title);
! 306: return HT_LOADED;
! 307: }
! 308: return HT_OK;
! 309: }
! 310:
! 311: /*
! 312: ** After filter: Update the cache entry for this host
! 313: */
! 314: PUBLIC int HTNewsCache_after (HTRequest * request, void * context, int status)
! 315: {
! 316: HTArray * array = (HTArray *) context;
! 317: if (PROT_TRACE) HTTrace("News Cache.. AFTER filter\n");
! 318: if (request && array) {
! 319: char * url = HTAnchor_address((HTAnchor *) HTRequest_anchor(request));
! 320: HTNewsCache_update(request, url, array);
! 321: HT_FREE(url);
! 322: }
! 323: return HT_OK;
! 324: }
! 325:
! 326: /* ------------------------------------------------------------------------- */
! 327:
! 328: /*
2.1 frystyk 329: ** Searches for News line until buffer fills up or a CRLF or LF is found
330: */
2.8 frystyk 331: PRIVATE int HTNewsList_put_block (HTStream * me, const char * b, int l)
2.1 frystyk 332: {
333: while (l-- > 0) {
334: if (me->state == EOL_FCR) {
335: if (*b == LF && me->buflen) {
336: if (!me->junk) {
337: *(me->buffer+me->buflen) = '\0';
2.12 ! frystyk 338: me->group ? ParseGroup(me->request, me->dir, me->buffer) :
2.1 frystyk 339: ParseList(me->dir, me->buffer);
340: } else
341: me->junk = NO; /* back to normal */
342: }
343: me->buflen = 0;
344: me->state = EOL_BEGIN;
345: } else if (*b == CR) {
346: me->state = EOL_FCR;
347: } else if (*b == LF && me->buflen) {
348: if (!me->junk) {
349: *(me->buffer+me->buflen) = '\0';
2.12 ! frystyk 350: me->group ? ParseGroup(me->request, me->dir, me->buffer) :
2.1 frystyk 351: ParseList(me->dir, me->buffer);
352: } else
353: me->junk = NO; /* back to normal */
354: me->buflen = 0;
355: me->state = EOL_BEGIN;
356: } else {
357: *(me->buffer+me->buflen++) = *b;
358: if (me->buflen >= MAX_NEWS_LINE) {
359: if (PROT_TRACE)
2.7 eric 360: HTTrace("News Dir.... Line too long - chopped\n");
2.1 frystyk 361: *(me->buffer+me->buflen) = '\0';
2.12 ! frystyk 362: me->group ? ParseGroup(me->request, me->dir, me->buffer) :
2.1 frystyk 363: ParseList(me->dir, me->buffer);
364: me->buflen = 0;
365: me->junk = YES;
366: }
367: }
368: b++;
369: }
370: return HT_OK;
371: }
372:
373: PRIVATE int HTNewsList_put_character (HTStream * me, char ch)
374: {
375: return HTNewsList_put_block(me, &ch, 1);
376: }
377:
2.8 frystyk 378: PRIVATE int HTNewsList_put_string (HTStream * me, const char * s)
2.1 frystyk 379: {
380: return HTNewsList_put_block(me, s, (int) strlen(s));
381: }
382:
383: PRIVATE int HTNewsList_flush (HTStream * me)
384: {
385: return HT_OK;
386: }
387:
388: PRIVATE int HTNewsList_free (HTStream * me)
389: {
2.12 ! frystyk 390: HTNewsList_put_character (me, '\n'); /* to flush the last item; added by MP. */
2.1 frystyk 391: HTNewsDir_free(me->dir);
2.6 frystyk 392: HT_FREE(me);
2.1 frystyk 393: return HT_OK;
394: }
395:
2.4 frystyk 396: PRIVATE int HTNewsList_abort (HTStream * me, HTList * e)
2.1 frystyk 397: {
2.7 eric 398: if (PROT_TRACE) HTTrace("News Dir.... ABORTING...\n");
2.1 frystyk 399: HTNewsList_free(me);
400: return HT_ERROR;
401: }
402:
2.8 frystyk 403: PRIVATE const HTStreamClass HTNewsListClass =
2.1 frystyk 404: {
405: "NewsList",
406: HTNewsList_flush,
407: HTNewsList_free,
408: HTNewsList_abort,
409: HTNewsList_put_character,
410: HTNewsList_put_string,
411: HTNewsList_put_block
412: };
413:
414: PUBLIC HTStream *HTNewsList (HTRequest * request,
415: void * param,
416: HTFormat input_format,
417: HTFormat output_format,
418: HTStream * output_stream)
419: {
2.6 frystyk 420: HTStream *me;
421: if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
422: HT_OUTOFMEM("HTNewsList_new");
2.1 frystyk 423: me->isa = &HTNewsListClass;
424: me->request = request;
425: me->state = EOL_BEGIN;
2.12 ! frystyk 426: {
! 427: char * title = GetNewsGroupTitle(request);
! 428: me->dir = HTNewsDir_new(request, title, list_key,YES);
! 429: HT_FREE(title);
! 430: }
! 431: /* Modified by MP. */
2.6 frystyk 432: if (me->dir == NULL) HT_FREE(me);
2.1 frystyk 433: return me;
434: }
435:
436: PUBLIC HTStream *HTNewsGroup (HTRequest * request,
437: void * param,
438: HTFormat input_format,
439: HTFormat output_format,
440: HTStream * output_stream)
441: {
2.12 ! frystyk 442: HTStream * me;
2.6 frystyk 443: if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
444: HT_OUTOFMEM("HTNewsList_new");
2.1 frystyk 445: me->isa = &HTNewsListClass;
446: me->request = request;
447: me->state = EOL_BEGIN;
448: me->group = YES;
2.12 ! frystyk 449: {
! 450: char * title = GetNewsGroupTitle(request);
! 451: me->dir = HTNewsDir_new(request, title, dir_key, YES);
! 452: HT_FREE(title);
! 453: }
! 454: /* Modified by MP. */
2.6 frystyk 455: if (me->dir == NULL) HT_FREE(me);
2.1 frystyk 456: return me;
457: }
Webmaster