Annotation of libwww/Library/src/HTNewsLs.c, revision 2.14
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.14 ! frystyk 6: ** @(#) $Id: HTNewsLs.c,v 2.13 1996/09/08 22:08:37 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.13 frystyk 118: t = HTParseTime(date, HTRequest_userProfile(request), YES);
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: */
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);
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: */
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;
318: if (PROT_TRACE) HTTrace("News Cache.. AFTER filter\n");
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) {
360: if (PROT_TRACE)
2.7 eric 361: HTTrace("News Dir.... Line too long - chopped\n");
2.1 frystyk 362: *(me->buffer+me->buflen) = '\0';
2.12 frystyk 363: me->group ? ParseGroup(me->request, me->dir, me->buffer) :
2.1 frystyk 364: ParseList(me->dir, me->buffer);
365: me->buflen = 0;
366: me->junk = YES;
367: }
368: }
369: b++;
370: }
371: return HT_OK;
372: }
373:
374: PRIVATE int HTNewsList_put_character (HTStream * me, char ch)
375: {
376: return HTNewsList_put_block(me, &ch, 1);
377: }
378:
2.8 frystyk 379: PRIVATE int HTNewsList_put_string (HTStream * me, const char * s)
2.1 frystyk 380: {
381: return HTNewsList_put_block(me, s, (int) strlen(s));
382: }
383:
384: PRIVATE int HTNewsList_flush (HTStream * me)
385: {
386: return HT_OK;
387: }
388:
389: PRIVATE int HTNewsList_free (HTStream * me)
390: {
2.12 frystyk 391: HTNewsList_put_character (me, '\n'); /* to flush the last item; added by MP. */
2.1 frystyk 392: HTNewsDir_free(me->dir);
2.6 frystyk 393: HT_FREE(me);
2.1 frystyk 394: return HT_OK;
395: }
396:
2.4 frystyk 397: PRIVATE int HTNewsList_abort (HTStream * me, HTList * e)
2.1 frystyk 398: {
2.7 eric 399: if (PROT_TRACE) HTTrace("News Dir.... ABORTING...\n");
2.1 frystyk 400: HTNewsList_free(me);
401: return HT_ERROR;
402: }
403:
2.8 frystyk 404: PRIVATE const HTStreamClass HTNewsListClass =
2.1 frystyk 405: {
406: "NewsList",
407: HTNewsList_flush,
408: HTNewsList_free,
409: HTNewsList_abort,
410: HTNewsList_put_character,
411: HTNewsList_put_string,
412: HTNewsList_put_block
413: };
414:
415: PUBLIC HTStream *HTNewsList (HTRequest * request,
416: void * param,
417: HTFormat input_format,
418: HTFormat output_format,
419: HTStream * output_stream)
420: {
2.6 frystyk 421: HTStream *me;
422: if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
423: HT_OUTOFMEM("HTNewsList_new");
2.1 frystyk 424: me->isa = &HTNewsListClass;
425: me->request = request;
426: me->state = EOL_BEGIN;
2.12 frystyk 427: {
428: char * title = GetNewsGroupTitle(request);
429: me->dir = HTNewsDir_new(request, title, list_key,YES);
430: HT_FREE(title);
431: }
432: /* Modified by MP. */
2.6 frystyk 433: if (me->dir == NULL) HT_FREE(me);
2.1 frystyk 434: return me;
435: }
436:
437: PUBLIC HTStream *HTNewsGroup (HTRequest * request,
438: void * param,
439: HTFormat input_format,
440: HTFormat output_format,
441: HTStream * output_stream)
442: {
2.12 frystyk 443: HTStream * me;
2.6 frystyk 444: if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
445: HT_OUTOFMEM("HTNewsList_new");
2.1 frystyk 446: me->isa = &HTNewsListClass;
447: me->request = request;
448: me->state = EOL_BEGIN;
449: me->group = YES;
2.12 frystyk 450: {
451: char * title = GetNewsGroupTitle(request);
452: me->dir = HTNewsDir_new(request, title, dir_key, YES);
453: HT_FREE(title);
454: }
455: /* Modified by MP. */
2.6 frystyk 456: if (me->dir == NULL) HT_FREE(me);
2.1 frystyk 457: return me;
458: }
Webmaster