Annotation of libwww/Library/src/HTNewsLs.c, revision 2.17
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.17 ! frystyk 6: ** @(#) $Id: HTNewsLs.c,v 2.16 1998/05/04 19:37:09 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);
182: if (PROT_TRACE) HTTrace("News Cache.. Deleted cache %p\n", me);
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) {
218: if (PROT_TRACE)
219: HTTrace("News Cache.. No information for `%s\'\n", url);
220: return NULL;
221: }
222:
223: /* Find a cache element (if any) */
224: element = (HTNewsCache *) HTUTree_findNode(tree, "", "/");
225: return element;
226: }
227: }
228: return NULL;
229: }
230:
231: PRIVATE BOOL HTNewsCache_update (HTRequest * request,
232: const char * url, HTArray * array)
233: {
234: HTUTree * tree = NULL;
235: if (request && url) {
236: char * newshost = NULL;
237: if (!strncasecomp(url, "news:", 5)) {
238: HTUserProfile * up = HTRequest_userProfile(request);
239: StrAllocCopy(newshost, HTUserProfile_news(up));
240: } else if (!strncasecomp(url, "nntp:", 5)) {
241: newshost = HTParse(url, "", PARSE_HOST);
242: }
243:
244: /*
245: ** If the news server was found then update the data entry. Otherwise
246: ** create a new entry
247: */
248: if (newshost) {
249: char * colon = strchr(newshost, ':');
250: int port = NEWS_PORT;
251: if (colon ) {
252: *(colon++) = '\0'; /* Chop off port number */
253: port = atoi(colon);
254: }
255: tree = HTUTree_new(NEWS_TREE, newshost, port, HTNewsCache_delete);
256: HT_FREE(newshost);
257: if (!tree) {
258: if (PROT_TRACE)HTTrace("News Cache.. Can't create tree\n");
259: return NO;
260: }
261:
262: /* Add new cache information to the tree */
263: {
264: HTNewsCache * element = NULL;
265: BOOL status;
266: if ((element=(HTNewsCache *) HTUTree_findNode(tree, "", "/"))){
267: element->cache = array;
268: status = YES;
269: } else {
270: element = HTNewsCache_new(url, array);
271: status = HTUTree_addNode(tree, "", "/", element);
272: }
273: return status;
274: }
275: }
276: }
277: return NO;
278: }
279:
280: /*
281: ** Before filter: Check whether we have a cache entry for this host
282: */
2.14 frystyk 283: PUBLIC int HTNewsCache_before (HTRequest * request, void * context, int mode)
2.12 frystyk 284: {
285: char * url = HTAnchor_address((HTAnchor *) HTRequest_anchor(request));
286: HTNewsCache * element = HTNewsCache_find(request, url);
287: HT_FREE(url);
288: /*
289: ** If we have found a cache object then create a new dir obejct and fill
290: ** it with data from the cache
291: */
292: if (element) {
293: char * title = GetNewsGroupTitle(request);
294: HTNewsDir * dir = HTNewsDir_new(request, title, list_key, NO);
295: void ** data;
296: char * line = (char *) HTArray_firstObject(element->cache, data);
297: while (line) {
298: HTNewsDir_addGroupElement(dir, line, NO);
299: line = (char *) HTArray_nextObject(element->cache, data);
300: }
301:
302: /*
303: ** After filling the new dir object we write it out and free it again
304: */
305: HTNewsDir_free(dir);
306: HT_FREE(title);
307: return HT_LOADED;
308: }
309: return HT_OK;
310: }
311:
312: /*
313: ** After filter: Update the cache entry for this host
314: */
2.14 frystyk 315: PUBLIC int HTNewsCache_after (HTRequest * request, HTResponse * response,
316: void * context, int status)
2.12 frystyk 317: {
318: HTArray * array = (HTArray *) context;
319: if (PROT_TRACE) HTTrace("News Cache.. AFTER filter\n");
320: if (request && array) {
321: char * url = HTAnchor_address((HTAnchor *) HTRequest_anchor(request));
322: HTNewsCache_update(request, url, array);
323: HT_FREE(url);
324: }
325: return HT_OK;
326: }
327:
328: /* ------------------------------------------------------------------------- */
329:
330: /*
2.1 frystyk 331: ** Searches for News line until buffer fills up or a CRLF or LF is found
332: */
2.8 frystyk 333: PRIVATE int HTNewsList_put_block (HTStream * me, const char * b, int l)
2.1 frystyk 334: {
335: while (l-- > 0) {
336: if (me->state == EOL_FCR) {
337: if (*b == LF && me->buflen) {
338: if (!me->junk) {
339: *(me->buffer+me->buflen) = '\0';
2.12 frystyk 340: me->group ? ParseGroup(me->request, me->dir, me->buffer) :
2.1 frystyk 341: ParseList(me->dir, me->buffer);
342: } else
343: me->junk = NO; /* back to normal */
344: }
345: me->buflen = 0;
346: me->state = EOL_BEGIN;
347: } else if (*b == CR) {
348: me->state = EOL_FCR;
349: } else if (*b == LF && me->buflen) {
350: if (!me->junk) {
351: *(me->buffer+me->buflen) = '\0';
2.12 frystyk 352: me->group ? ParseGroup(me->request, me->dir, me->buffer) :
2.1 frystyk 353: ParseList(me->dir, me->buffer);
354: } else
355: me->junk = NO; /* back to normal */
356: me->buflen = 0;
357: me->state = EOL_BEGIN;
358: } else {
359: *(me->buffer+me->buflen++) = *b;
360: if (me->buflen >= MAX_NEWS_LINE) {
361: if (PROT_TRACE)
2.7 eric 362: HTTrace("News Dir.... Line too long - chopped\n");
2.1 frystyk 363: *(me->buffer+me->buflen) = '\0';
2.12 frystyk 364: me->group ? ParseGroup(me->request, me->dir, me->buffer) :
2.1 frystyk 365: ParseList(me->dir, me->buffer);
366: me->buflen = 0;
367: me->junk = YES;
368: }
369: }
370: b++;
371: }
372: return HT_OK;
373: }
374:
375: PRIVATE int HTNewsList_put_character (HTStream * me, char ch)
376: {
377: return HTNewsList_put_block(me, &ch, 1);
378: }
379:
2.8 frystyk 380: PRIVATE int HTNewsList_put_string (HTStream * me, const char * s)
2.1 frystyk 381: {
382: return HTNewsList_put_block(me, s, (int) strlen(s));
383: }
384:
385: PRIVATE int HTNewsList_flush (HTStream * me)
386: {
387: return HT_OK;
388: }
389:
390: PRIVATE int HTNewsList_free (HTStream * me)
391: {
2.12 frystyk 392: HTNewsList_put_character (me, '\n'); /* to flush the last item; added by MP. */
2.1 frystyk 393: HTNewsDir_free(me->dir);
2.6 frystyk 394: HT_FREE(me);
2.1 frystyk 395: return HT_OK;
396: }
397:
2.4 frystyk 398: PRIVATE int HTNewsList_abort (HTStream * me, HTList * e)
2.1 frystyk 399: {
2.7 eric 400: if (PROT_TRACE) HTTrace("News Dir.... ABORTING...\n");
2.1 frystyk 401: HTNewsList_free(me);
402: return HT_ERROR;
403: }
404:
2.8 frystyk 405: PRIVATE const HTStreamClass HTNewsListClass =
2.1 frystyk 406: {
407: "NewsList",
408: HTNewsList_flush,
409: HTNewsList_free,
410: HTNewsList_abort,
411: HTNewsList_put_character,
412: HTNewsList_put_string,
413: HTNewsList_put_block
414: };
415:
416: PUBLIC HTStream *HTNewsList (HTRequest * request,
417: void * param,
418: HTFormat input_format,
419: HTFormat output_format,
420: HTStream * output_stream)
421: {
2.6 frystyk 422: HTStream *me;
423: if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
424: HT_OUTOFMEM("HTNewsList_new");
2.1 frystyk 425: me->isa = &HTNewsListClass;
426: me->request = request;
427: me->state = EOL_BEGIN;
2.12 frystyk 428: {
429: char * title = GetNewsGroupTitle(request);
430: me->dir = HTNewsDir_new(request, title, list_key,YES);
431: HT_FREE(title);
432: }
433: /* Modified by MP. */
2.6 frystyk 434: if (me->dir == NULL) HT_FREE(me);
2.1 frystyk 435: return me;
436: }
437:
438: PUBLIC HTStream *HTNewsGroup (HTRequest * request,
439: void * param,
440: HTFormat input_format,
441: HTFormat output_format,
442: HTStream * output_stream)
443: {
2.12 frystyk 444: HTStream * me;
2.6 frystyk 445: if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
446: HT_OUTOFMEM("HTNewsList_new");
2.1 frystyk 447: me->isa = &HTNewsListClass;
448: me->request = request;
449: me->state = EOL_BEGIN;
450: me->group = YES;
2.12 frystyk 451: {
452: char * title = GetNewsGroupTitle(request);
453: me->dir = HTNewsDir_new(request, title, dir_key, YES);
454: HT_FREE(title);
455: }
456: /* Modified by MP. */
2.6 frystyk 457: if (me->dir == NULL) HT_FREE(me);
2.1 frystyk 458: return me;
459: }
Webmaster