/* * XOVER.C - general nntp reader commands * * Overview-related NNTP commands * * (c)Copyright 1998, Matthew Dillon, All Rights Reserved. Refer to * the COPYRIGHT file in the base directory of this distribution * for specific rights granted. */ #include "defs.h" Prototype void NNTPNewNews(Connection *conn, char **pptr); Prototype void NNTPXHdr(Connection *conn, char **pptr); Prototype void NNTPXOver(Connection *conn, char **pptr); Prototype void NNTPXPat(Connection *conn, char **pptr); Prototype void NNTPXPath(Connection *conn, char **pptr); void NNListNewNews(Connection *conn); void NNStartListOverview(Connection *conn, const char *hdr, const char *ran, const char *pat, int mode); void NNStartListOverviewRange(Connection *conn); void NNStartListOverviewMsgId(Connection *conn, const char *msgid); void NNListOverviewRange(Connection *conn); int OutputOverview(Connection *conn, const char *res, int resLen, int artSize); Prototype char *OverViewFmt; char *OverViewFmt = OVERVIEW_FMT; void NNTPNewNews(Connection *conn, char **pptr) { char *newsgroups = NULL; const char *yymmdd = NULL; const char *hhmmss = NULL; const char *gmt = NULL; const char *distributions = NULL; if (!conn->co_Auth.dr_ReaderDef->rd_AllowNewnews) { MBLogPrintf(conn, &conn->co_TMBuf, "500 \"newnews\" not implemented\r\n"); NNCommand(conn); return; } if (pptr) { newsgroups = parseword(pptr, " \t"); yymmdd = parseword(pptr, " \t"); hhmmss = parseword(pptr, " \t"); gmt = parseword(pptr, " \t"); distributions = parseword(pptr, " \t"); } if (SetTimeRestrict(&conn->co_TimeRestrict, yymmdd, hhmmss, gmt) < 0) { MBLogPrintf(conn, &conn->co_TMBuf, "501 newsgroups yymmdd hhmmss [\"GMT\"] []\r\n"); NNCommand(conn); } else { if (conn->co_Auth.dr_Flags & DF_GROUPLOG) logit(LOG_INFO, "info %s%s%s%s%s newnews %s %s %s %s %s", *conn->co_Auth.dr_AuthUser ? conn->co_Auth.dr_AuthUser : "", *conn->co_Auth.dr_AuthUser ? "/" : "", *conn->co_Auth.dr_IdentUser ? conn->co_Auth.dr_IdentUser : "", *conn->co_Auth.dr_IdentUser ? "@" : "", conn->co_Auth.dr_Host, newsgroups, yymmdd, hhmmss, gmt ? gmt : "-", distributions ? distributions : "-"); MBLogPrintf(conn, &conn->co_TMBuf, "230 New news follows.\r\n"); zfreeStr(&conn->co_MemPool, &conn->co_ListPat); zfreeStr(&conn->co_MemPool, &conn->co_ListHdrs); conn->co_ListPat = (distributions) ? zallocStr(&conn->co_MemPool, distributions) : NULL; conn->co_ListHdrs = zallocStr(&conn->co_MemPool, "Message-ID"); conn->co_ArtMode = COM_NEWNEWS; conn->co_ListCacheGroups = NULL; ListActiveGroups(conn, newsgroups); NNListNewNews(conn); } } void NNListNewNews(Connection *conn) { conn->co_Func = NNListNewNews; conn->co_State = "listnewnews"; if (conn->co_ListCacheGroups == NULL) { MBPrintf(&conn->co_TMBuf, ".\r\n"); if (conn->co_GroupName) zfreeStr(&conn->co_MemPool, &conn->co_GroupName); NNCommand(conn); } else { GroupList *gl = conn->co_ListCacheGroups; if (conn->co_GroupName) zfreeStr(&conn->co_MemPool, &conn->co_GroupName); conn->co_GroupName = gl->group; conn->co_ListBegNo = 0; conn->co_ListEndNo = INT_MAX; conn->co_ListCacheGroups = gl->next; zfree(&conn->co_MemPool, gl, sizeof(GroupList)); NNStartListOverviewRange(conn); } } void NNTPXHdr(Connection *conn, char **pptr) { char *hdr; if ((hdr = parseword(pptr, " \t")) == NULL) { MBLogPrintf(conn, &conn->co_TMBuf, "501 header [range|MessageID]\r\n"); NNCommand(conn); return; } NNStartListOverview(conn, hdr, parseword(pptr, " \t"), NULL, COM_XHDR); return; } void NNTPXOver(Connection *conn, char **pptr) { NNStartListOverview(conn, OverViewFmt, parseword(pptr, " \t"), NULL, COM_XOVER); return; } void NNTPXPat(Connection *conn, char **pptr) { char *hdr; char *ran; char *pat; if ((hdr = parseword(pptr, " \t")) == NULL || (ran = parseword(pptr, " \t")) == NULL || *(pat = *pptr) == '\0' ) { MBLogPrintf(conn, &conn->co_TMBuf, "501 header range|MessageID pat\r\n"); NNCommand(conn); return; } NNStartListOverview(conn, hdr, ran, pat, COM_XPAT); } void NNTPXPath(Connection *conn, char **pptr) { MBLogPrintf(conn, &conn->co_TMBuf, "500 What? (xpath not implemented yet)\r\n"); NNCommand(conn); } /* * NNStartListOverview() - list selected overview for specified article range * or message-id */ void NNStartListOverview(Connection *conn, const char *hdr, const char *ran, const char *pat, int mode) { zfreeStr(&conn->co_MemPool, &conn->co_ListPat); zfreeStr(&conn->co_MemPool, &conn->co_ListHdrs); conn->co_ListPat = (pat) ? zallocStr(&conn->co_MemPool, pat) : NULL; conn->co_ListHdrs = zallocStr(&conn->co_MemPool, hdr); if (ran == NULL || ran[0] != '<') { if (conn->co_GroupName == NULL) { MBLogPrintf(conn, &conn->co_TMBuf, "412 Not in a newsgroup\r\n"); NNCommand(conn); return; } } conn->co_ArtMode = mode; switch(mode) { case COM_XPAT: MBLogPrintf(conn, &conn->co_TMBuf, "221 %s matches follow.\r\n", hdr); break; case COM_XHDR: MBLogPrintf(conn, &conn->co_TMBuf, "221 %s data follows\r\n", hdr); break; case COM_XOVER: MBLogPrintf(conn, &conn->co_TMBuf, "224 data follows\r\n"); break; default: MBLogPrintf(conn, &conn->co_TMBuf, "500 software error\r\n"); NNCommand(conn); return; } if (ran == NULL) { conn->co_ListBegNo = conn->co_ArtNo; conn->co_ListEndNo = conn->co_ArtNo; NNStartListOverviewRange(conn); } else if (ran[0] != '<') { char *p; conn->co_ListBegNo = strtol(ran, &p, 10); if (*p == '-') { if (p[1] == 0) conn->co_ListEndNo = INT_MAX; else conn->co_ListEndNo = strtol(p + 1, NULL, 10); } else { conn->co_ListEndNo = conn->co_ListBegNo; } NNStartListOverviewRange(conn); } else if ((ran = MsgId(ran, NULL)) && strcmp(ran, "<>") != 0) { NNStartListOverviewMsgId(conn, ran); } else { MBPrintf(&conn->co_TMBuf, ".\r\n"); NNCommand(conn); } } void NNStartListOverviewRange(Connection *conn) { /* * trim list range */ const char *rec; int recLen; int lbeg; int lend; /* * Handle user bogosity */ if (conn->co_ListBegNo < 0) conn->co_ListBegNo = 0; if (conn->co_ListEndNo < 0) conn->co_ListEndNo = 0; if ((rec = KPDBReadRecord(KDBActive, conn->co_GroupName, KP_LOCK, &recLen)) == NULL) { MBPrintf(&conn->co_TMBuf, ".\r\n"); NNCommand(conn); return; } lbeg = strtol(KPDBGetField(rec, recLen, "NB", NULL, "0"), NULL, 10); lend = strtol(KPDBGetField(rec, recLen, "NE", NULL, "0"), NULL, 10); KPDBUnlock(KDBActive, rec); /* * Handle some stupid case begin and end numbers */ if ((conn->co_ListBegNo < lbeg && conn->co_ListEndNo < lbeg) || conn->co_ListBegNo > lend || conn->co_ListEndNo < lbeg || conn->co_ListBegNo > conn->co_ListEndNo) { MBPrintf(&conn->co_TMBuf, ".\r\n"); NNCommand(conn); return; } if (conn->co_ListBegNo < lbeg) conn->co_ListBegNo = lbeg; if (conn->co_ListEndNo > lend) conn->co_ListEndNo = lend; NNListOverviewRange(conn); } void NNListOverviewRange(Connection *conn) { OverInfo *ov; int xpat_count = 0; conn->co_Func = NNListOverviewRange; conn->co_State = "listover"; if ((ov = GetOverInfo(conn->co_GroupName)) == NULL) { MBPrintf(&conn->co_TMBuf, ".\r\n"); NNCommand(conn); return; } if (conn->co_TMBuf.mh_WError) { logit(LOG_ERR, "%s mh_WError (co_ArtMode = %d)", conn->co_Auth.dr_Host, conn->co_ArtMode); PutOverInfo(ov); NNCommand(conn); return; } while (conn->co_TMBuf.mh_Bytes < 800 && conn->co_ListBegNo <= conn->co_ListEndNo ) { int resLen; int artSize; const char *res; TimeRestrict *tr = NULL; if (conn->co_ArtMode == COM_XPAT && ++xpat_count > 50) { FD_SET(conn->co_Desc->d_Fd, &WFds); break; } if (conn->co_ArtMode == COM_NEWNEWS) tr = &conn->co_TimeRestrict; if ((res = GetOverRecord(ov, conn->co_ListBegNo, &resLen, &artSize, tr, NULL)) != NULL) { OutputOverview(conn, res, resLen, artSize); } ++conn->co_ListBegNo; } PutOverInfo(ov); if (conn->co_ListBegNo > conn->co_ListEndNo) { if (conn->co_ArtMode != COM_NEWNEWS) MBPrintf(&conn->co_TMBuf, ".\r\n"); NNCommand(conn); if (conn->co_ArtMode == COM_NEWNEWS) { NNListNewNews(conn); } } } void NNStartListOverviewMsgId(Connection *conn, const char *msgid) { /* XXX StartListOverviewMsgId */ MBPrintf(&conn->co_TMBuf, ".\r\n"); NNCommand(conn); } int OutputOverview(Connection *conn, const char *res, int resLen, int artSize) { const char *hdr = conn->co_ListHdrs; int didIndex = 0; while (*hdr) { int hlen; int hhlen; int rleft; int printHdr = 0; const char *rline; char hch; char ch; /* * locate next header */ while (*hdr == '\r' || *hdr == '\n') ++hdr; for (hlen = 0; hdr[hlen]; ++hlen) { if (hdr[hlen] != '\n') continue; if (hdr[hlen+1] == ' ' || hdr[hlen+1] == '\t') /* multiline */ continue; ++hlen; break; } if (hlen == 0) break; /* * locate header name non-inclusive of colon */ for (hhlen = 0; hhlen < hlen && hdr[hhlen] != ':'; ++hhlen) ; /* * scan overview info for header */ rline = res; rleft = resLen; hch = tolower(*hdr); while (rleft) { int rlen; int rrlen; /* * get entire header */ for (rlen = 0; rline[rlen]; ++rlen) { if (rline[rlen] != '\n') continue; if (rline[rlen+1] == ' ' || rline[rlen+1] == '\t')/* multiln */ continue; ++rlen; break; } /* * no more headers, don't scan article (if article data passed) */ if (rlen == 2 && rline[0] == '\r' && rline[1] == '\n') break; /* * This occurs if the map file containing the headers is boshed */ if (rlen == 0) break; /* * get just header portion, non inclusive of colon */ for (rrlen = 0; rrlen < rlen && rline[rrlen] != ':'; ++rrlen) ; ch = tolower(*rline); if (rrlen == hhlen && ch == hch && strncasecmp(rline, hdr, hhlen) == 0) { switch(conn->co_ArtMode) { case COM_XPAT: { char hdr[128]; char *p = hdr; int glen = rrlen; if (glen < rlen && rline[glen] == ':') ++glen; while (glen < rlen && (rline[glen] == ' ' || rline[glen] == '\t') ) { ++glen; } if (rlen >= sizeof(hdr)) p = zalloc(&conn->co_MemPool, rlen + 1); memcpy(p, rline, rlen); /* Handle CR/LF in overview data */ if (rlen > 2 && p[rlen - 2] == '\r' && p[rlen - 1] == '\n') { p[rlen - 2] = 0; } p[rlen] = 0; if (wildmat(p + glen, conn->co_ListPat)) printHdr = ' '; if (p != hdr) zfree(&conn->co_MemPool, p, rlen + 1); } break; case COM_XHDR: printHdr = ' '; break; case COM_NEWNEWS: printHdr = ' '; didIndex = 1; break; case COM_XOVER: printHdr = '\t'; break; } if (printHdr) { int b; int i; int doSpace = 0; if (didIndex == 0) { MBPrintf(&conn->co_TMBuf, "%d", conn->co_ListBegNo); didIndex = 1; } if (conn->co_ArtMode != COM_NEWNEWS) MBPrintf(&conn->co_TMBuf, "%c", printHdr); /* * hack for overview format options, only deal with * 'full' at the moment. */ if (hhlen < hlen && strncmp(hdr + hhlen + 1, "full", 4) == 0) { MBWrite(&conn->co_TMBuf, hdr, hhlen + 1); MBWrite(&conn->co_TMBuf, " ", 1); } /* * Compress whitespace if necessary. Mainly applies * to folded lines. */ for (b = i = rrlen + 1; i < rlen; ++i) { if (rline[i] == '\r' || rline[i] == '\n' || rline[i] == ' ' || rline[i] == '\t' ) { /* * Skip first whitespace */ if (b == i) { ++b; } else { /* * Don't compress spaces * We will compress space for OVER when it * gets implemented */ if (rline[i] == ' ') continue; /* * Only compress tabs if doing XOVER */ if (conn->co_ArtMode != COM_XOVER && rline[i] == '\t') continue; if (doSpace) MBWrite(&conn->co_TMBuf, " ", 1); MBWrite(&conn->co_TMBuf, rline + b, i - b); b = i + 1; doSpace = 1; } } } if (b != i) { if (doSpace) MBWrite(&conn->co_TMBuf, " ", 1); MBWrite(&conn->co_TMBuf, rline + b, i - b); } } break; } rleft -= rlen; rline += rlen; } /* while */ if (printHdr == 0 && conn->co_ArtMode == COM_XOVER) { if (didIndex == 0) { MBPrintf(&conn->co_TMBuf, "%d", conn->co_ListBegNo); didIndex = 1; } if (hhlen == 5 && hch == 'b' && strncasecmp(hdr, "Bytes", 5) == 0) { MBPrintf(&conn->co_TMBuf, "\t%d", artSize + 1); } else { MBPrintf(&conn->co_TMBuf, "\t"); } } hdr += hlen; } if (didIndex == 0 && (/* conn->co_ArtMode == COM_XPAT || */ conn->co_ArtMode == COM_XHDR) ) { MBPrintf(&conn->co_TMBuf, "%d (none)", conn->co_ListBegNo); didIndex = 1; } if (didIndex) { MBPrintf(&conn->co_TMBuf, "\r\n"); return(0); } return(-1); }