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