Annotation of libwww/Library/src/HTNewsLs.c, revision 2.19
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.19 ! kahan 6: ** @(#) $Id: HTNewsLs.c,v 2.18 1999/02/22 22:10:11 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.16 frystyk 19: #include "wwwsys.h"
2.11 frystyk 20: #include "WWWUtil.h"
21: #include "WWWCore.h"
2.17 frystyk 22: #include "HTUTree.h"
2.1 frystyk 23: #include "HTNDir.h"
24: #include "HTNews.h"
25: #include "HTNewsLs.h" /* Implemented here */
26:
27: #define DELIMITER '\t'
2.5 frystyk 28: #define ATSIGN '@'
2.1 frystyk 29:
2.12 frystyk 30: #define NEWS_TREE "w3c-news"
31:
2.1 frystyk 32: struct _HTStream {
2.8 frystyk 33: const HTStreamClass * isa;
2.1 frystyk 34: HTRequest * request;
2.9 frystyk 35: HTEOLState state;
2.1 frystyk 36: HTNewsDir * dir;
37: BOOL group;
38: BOOL junk;
39: char buffer[MAX_NEWS_LINE+1];
40: int buflen;
41: };
42:
2.12 frystyk 43: typedef struct _HTNewsCache {
44: char * host;
45: HTArray * cache;
46: } HTNewsCache;
47:
48: PRIVATE HTNewsDirKey dir_key = HT_NDK_REFTHREAD;
49: PRIVATE HTNewsDirKey list_key = HT_NDK_GROUP; /* Added by MP. */
2.1 frystyk 50:
51: /* ------------------------------------------------------------------------- */
52:
2.12 frystyk 53: /* Helper function added by MP. */
54: PRIVATE char* GetNewsGroupTitle (HTRequest* request)
55: {
56: char * url = HTAnchor_physical(HTRequest_anchor(request));
57: char * title = NULL;
58: if (strrchr(url, '*'))
59: StrAllocCopy(title, "Newsgroups: ");
60: else
61: StrAllocCopy(title, "Newsgroup: ");
62: if (!strncasecomp(url, "news:", 5))
63: StrAllocCat(title, url+5);
64: else
65: StrAllocCat(title, HTParse(url, "", PARSE_PATH));
66: return title;
67: }
2.1 frystyk 68:
2.12 frystyk 69: PRIVATE BOOL ParseList (HTNewsDir * dir, char * line)
2.1 frystyk 70: {
71: char *ptr = line;
2.15 frystyk 72: while (*ptr && !isspace((int) *ptr)) ptr++;
2.1 frystyk 73: *ptr = '\0';
2.12 frystyk 74: /* Changed by MP */
75: return (HTNewsDir_addGroupElement(dir, line, NO) != NULL);
2.1 frystyk 76: }
77:
78: /* ParseGroup
79: ** ----------
80: ** Extract the index number, subject etc, from a XOVER command. Expects
81: ** the following format of the line:
82: **
83: ** <index> <subject> <from> <data> <msgid> [*<thread>] ...
84: **
85: ** Returns YES if OK, NO on error
86: */
2.12 frystyk 87: PRIVATE BOOL ParseGroup (HTRequest * request, HTNewsDir *dir, char * line)
2.1 frystyk 88: {
89: int index;
2.3 frystyk 90: int refcnt=0;
2.5 frystyk 91: time_t t=0;
92: char *subject = line;
93: char *from;
94: char *date;
2.1 frystyk 95: char *msgid;
2.5 frystyk 96: char *ptr=NULL;
2.12 frystyk 97: HTList* reflist = NULL; /* Added by MP. */
2.1 frystyk 98: while (*subject && *subject != DELIMITER) subject++;
2.5 frystyk 99: *subject++ = '\0'; /* Index */
2.1 frystyk 100: index = atoi(line);
2.5 frystyk 101: from = subject;
2.1 frystyk 102: while (*from && *from != DELIMITER) from++;
2.5 frystyk 103: *from++ = '\0'; /* Subject */
104: date = from;
105: while (*date && *date != DELIMITER) {
106: if (*date=='<' || *date=='(') {
107: ptr = date+1;
108: *date = '\0';
109: }
110: if (*date=='>' || *date==')') *date = '\0';
111: date++;
112: }
113: *date++ = '\0';
114: if (strchr(from, ATSIGN) && ptr) from = ptr; /* From */
115: msgid = date;
2.1 frystyk 116: while (*msgid && *msgid != DELIMITER) msgid++;
2.5 frystyk 117: *msgid++ = '\0'; /* Date */
118: if (*msgid=='<') msgid++;
2.13 frystyk 119: t = HTParseTime(date, HTRequest_userProfile(request), YES);
2.5 frystyk 120: ptr = msgid;
121: while (*ptr && *ptr != DELIMITER) {
122: if (*ptr=='>') *ptr = '\0';
123: ptr++;
124: }
125: *ptr++ = '\0'; /* MsgId */
2.15 frystyk 126: while (ptr && *ptr && !isdigit((int) *ptr)) {
2.12 frystyk 127: char* refstart = ptr; /* Added by MP. */
128: char* refcopy = NULL;
129: char* refstop;
130: while (*ptr && *ptr != DELIMITER && *ptr != ' ') ptr++;
131: refstop = ptr - 1;
132: *ptr++ = '\0';
133: if (strlen(refstart) > 0) /* Added by MP. */
134: {
135: refcnt++;
136: if (*refstart == '<') refstart++;
137: if (*refstop == '>') *refstop = '\0';
138: if (reflist == NULL) reflist = HTList_new();
139: StrAllocCopy (refcopy, refstart);
140: HTList_addObject (reflist, (void*) refcopy);
141: }
142: }
143: /* Changed by MP. */
144: return (HTNewsDir_addElement(dir, index, subject, from, t, msgid,
145: refcnt, reflist) != NULL);
146: }
147:
148: /* ------------------------------------------------------------------------- */
149: /* NEWS CACHE */
150: /* ------------------------------------------------------------------------- */
151:
152: PRIVATE HTNewsCache * HTNewsCache_new (const char * newshost, HTArray * array)
153: {
154: if (newshost && array) {
155: HTNewsCache * me;
156: if ((me = (HTNewsCache *) HT_CALLOC(1, sizeof(HTNewsCache))) == NULL)
157: HT_OUTOFMEM("HTNewsCache_new");
158: StrAllocCopy(me->host, newshost);
159: me->cache = array;
160: return me;
161: }
162: return NULL;
163: }
164:
165: /*
166: ** Instead of just deleting we could save it to file.
167: */
168: PRIVATE int HTNewsCache_delete (void * context)
169: {
170: HTNewsCache * me = (HTNewsCache *) context;
171: if (me) {
172: if (me->cache) {
173: void ** data;
174: char * line = (char *) HTArray_firstObject(me->cache, data);
175: while (line) {
176: HT_FREE(line);
177: line = (char *) HTArray_nextObject(me->cache, data);
178: }
179: HTArray_delete(me->cache);
180: }
181: HT_FREE(me->host);
2.18 frystyk 182: HTTRACE(PROT_TRACE, "News Cache.. Deleted cache %p\n" _ me);
2.12 frystyk 183: HT_FREE(me);
184: return YES;
2.1 frystyk 185: }
2.12 frystyk 186: return NO;
2.1 frystyk 187: }
188:
189: /*
2.12 frystyk 190: ** Look for cached information for this news server
191: ** We store the information in a URL Tree so that we can have multiple
192: ** servers stored simultanously
193: */
194: PRIVATE HTNewsCache * HTNewsCache_find (HTRequest * request, const char * url)
195: {
196: HTUTree * tree = NULL;
197: if (request && url) {
198: char * newshost = NULL;
199: HTNewsCache * element = NULL;
200: if (!strncasecomp(url, "news:", 5)) {
201: HTUserProfile * up = HTRequest_userProfile(request);
202: StrAllocCopy(newshost, HTUserProfile_news(up));
203: } else if (!strncasecomp(url, "nntp:", 5)) {
204: newshost = HTParse(url, "", PARSE_HOST);
205: }
206:
207: /* If we have a news server then continue to find a URL tree */
208: if (newshost) {
209: char * colon = strchr(newshost, ':');
210: int port = NEWS_PORT;
211: if (colon ) {
212: *(colon++) = '\0'; /* Chop off port number */
213: port = atoi(colon);
214: }
215: tree = HTUTree_find(NEWS_TREE, newshost, port);
216: HT_FREE(newshost);
217: if (!tree) {
2.18 frystyk 218: HTTRACE(PROT_TRACE, "News Cache.. No information for `%s\'\n" _ url);
2.12 frystyk 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) {
2.18 frystyk 257: HTTRACE(PROT_TRACE, "News Cache.. Can't create tree\n");
2.12 frystyk 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: */
2.14 frystyk 282: PUBLIC int HTNewsCache_before (HTRequest * request, void * context, int mode)
2.12 frystyk 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);
2.19 ! kahan 294: void ** data = NULL;
2.12 frystyk 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: */
2.14 frystyk 314: PUBLIC int HTNewsCache_after (HTRequest * request, HTResponse * response,
315: void * context, int status)
2.12 frystyk 316: {
317: HTArray * array = (HTArray *) context;
2.18 frystyk 318: HTTRACE(PROT_TRACE, "News Cache.. AFTER filter\n");
2.12 frystyk 319: if (request && array) {
320: char * url = HTAnchor_address((HTAnchor *) HTRequest_anchor(request));
321: HTNewsCache_update(request, url, array);
322: HT_FREE(url);
323: }
324: return HT_OK;
325: }
326:
327: /* ------------------------------------------------------------------------- */
328:
329: /*
2.1 frystyk 330: ** Searches for News line until buffer fills up or a CRLF or LF is found
331: */
2.8 frystyk 332: PRIVATE int HTNewsList_put_block (HTStream * me, const char * b, int l)
2.1 frystyk 333: {
334: while (l-- > 0) {
335: if (me->state == EOL_FCR) {
336: if (*b == LF && me->buflen) {
337: if (!me->junk) {
338: *(me->buffer+me->buflen) = '\0';
2.12 frystyk 339: me->group ? ParseGroup(me->request, me->dir, me->buffer) :
2.1 frystyk 340: ParseList(me->dir, me->buffer);
341: } else
342: me->junk = NO; /* back to normal */
343: }
344: me->buflen = 0;
345: me->state = EOL_BEGIN;
346: } else if (*b == CR) {
347: me->state = EOL_FCR;
348: } else if (*b == LF && me->buflen) {
349: if (!me->junk) {
350: *(me->buffer+me->buflen) = '\0';
2.12 frystyk 351: me->group ? ParseGroup(me->request, me->dir, me->buffer) :
2.1 frystyk 352: ParseList(me->dir, me->buffer);
353: } else
354: me->junk = NO; /* back to normal */
355: me->buflen = 0;
356: me->state = EOL_BEGIN;
357: } else {
358: *(me->buffer+me->buflen++) = *b;
359: if (me->buflen >= MAX_NEWS_LINE) {
2.18 frystyk 360: HTTRACE(PROT_TRACE, "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.18 frystyk 398: HTTRACE(PROT_TRACE, "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