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