/*
* NNTP.C - general nntp reader commands
*
* Reader-specific 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 NNTPQuit(Connection *conn, char **pptr);
Prototype void NNTPAuthInfo(Connection *conn, char **pptr);
Prototype void NNTPArticle(Connection *conn, char **pptr);
Prototype void NNTPHead(Connection *conn, char **pptr);
Prototype void NNTPBody(Connection *conn, char **pptr);
Prototype void NNTPDate(Connection *conn, char **pptr);
Prototype void NNTPGroup(Connection *conn, char **pptr);
Prototype void NNTPLast(Connection *conn, char **pptr);
Prototype void NNTPNext(Connection *conn, char **pptr);
Prototype void NNTPStat(Connection *conn, char **pptr);
Prototype void NNExecuteOnRange(Connection *conn, const char *artid, int mode);
Prototype int GoodRC(Connection *conn);
Prototype const char *GoodResId(Connection *conn);
Prototype void DumpOVHeaders(Connection *conn, const char *ovdata, int ovlen);
void NNArticleRetrieve(Connection *conn, const char *artid, int mode);
void NNArticleRetrieveByArtNo(Connection *conn, int artNo);
void
NNTPQuit(Connection *conn, char **pptr)
{
/*
* Print closing banner
*/
MBLogPrintf(conn, &conn->co_TMBuf, "205 Transferred %.0f bytes in %lu article%s, %lu group%s. Disconnecting.\r\n", conn->co_ClientTotalByteCount, conn->co_ClientTotalArticleCount, conn->co_ClientTotalArticleCount != 1 ? "s" : "", conn->co_ClientGroupCount, conn->co_ClientGroupCount != 1 ? "s" : "");
NNTerminate(conn);
}
void
NNTPAuthInfo(Connection *conn, char **pptr)
{
char *type = parseword(pptr, " \t");
char *args = (type) ? parseword(pptr, " \t") : NULL;
int ok = 0;
if (type && args && strlen(args) < 64) {
if (strcasecmp(type, "user") == 0) {
strncpy(conn->co_Auth.dr_AuthUser, args,
sizeof(conn->co_Auth.dr_AuthUser) - 1);
conn->co_Auth.dr_AuthUser[sizeof(conn->co_Auth.dr_AuthUser) - 1] = '\0';
ok = 1;
conn->co_Auth.dr_Flags |= DF_AUTHREQUIRED;
MBLogPrintf(conn, &conn->co_TMBuf, "381 PASS required\r\n");
} else if (strcasecmp(type, "pass") == 0 &&
*conn->co_Auth.dr_AuthUser) {
strncpy(conn->co_Auth.dr_AuthPass, args,
sizeof(conn->co_Auth.dr_AuthPass) - 1);
conn->co_Auth.dr_AuthPass[sizeof(conn->co_Auth.dr_AuthPass) - 1] = '\0';
conn->co_Auth.dr_Flags &= ~DF_AUTHREQUIRED;
conn->co_Auth.dr_ResultFlags = DR_REQUIRE_DNS;
return;
}
}
if (ok == 0)
MBLogPrintf(conn, &conn->co_TMBuf, "501 user Name|pass Password\r\n");
if (ok > 0)
NNCommand(conn);
}
void
NNTPArticle(Connection *conn, char **pptr)
{
NNArticleRetrieve(conn, parseword(pptr, " \t"), COM_ARTICLEWVF);
}
void
NNTPHead(Connection *conn, char **pptr)
{
NNArticleRetrieve(conn, parseword(pptr, " \t"), COM_HEAD);
}
void
NNTPBody(Connection *conn, char **pptr)
{
NNArticleRetrieve(conn, parseword(pptr, " \t"), COM_BODYWVF);
}
void
GroupStats(Connection *conn)
{
if ((conn->co_GroupName != NULL) &&
(conn->co_Auth.dr_Flags & DF_GROUPLOG)) {
char statbuf[1024];
snprintf(statbuf, sizeof(statbuf), "group %s articles %lu bytes %.0f", conn->co_GroupName, conn->co_ClientGroupArticleCount, conn->co_ClientGroupByteCount);
LogCmd(conn, '$', statbuf);
if (conn->co_ClientGroupArticleCount)
logit(LOG_INFO, "info %s%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,
statbuf);
conn->co_ClientGroupArticleCount = 0;
conn->co_ClientGroupByteCount = 0.0;
}
}
void
NNTPGroup(Connection *conn, char **pptr)
{
char *group = parseword(pptr, " \t");
int recLen;
int artBeg;
int artEnd;
int adjustedArtBeg = 0;
const char *rec;
OverInfo *ov;
struct GroupList *groups = conn->co_Auth.dr_GroupDef->gr_Groups;
++conn->co_Auth.dr_GrpCount;
if (group == NULL || strlen(group) > MAXGNAME ||
parseword(pptr, " \t") != NULL
) {
MBLogPrintf(conn, &conn->co_TMBuf, "501 newsgroup\r\n");
NNCommand(conn);
return;
}
if (groups != NULL && !GroupFindWild(group, groups)) {
MBLogPrintf(conn, &conn->co_TMBuf, "411 No such group %s\r\n", group);
NNCommand(conn);
return;
}
if ((rec = KPDBReadRecord(KDBActive, group, KP_LOCK, &recLen)) == NULL) {
MBLogPrintf(conn, &conn->co_TMBuf, "411 No such group %s\r\n", group);
NNCommand(conn);
return;
}
artBeg = strtol(KPDBGetField(rec, recLen, "NB", NULL, "-1"), NULL,10);
artEnd = strtol(KPDBGetField(rec, recLen, "NE", NULL, "-1"), NULL,10);
KPDBUnlock(KDBActive, rec);
/*
* Handle range fixup
*/
if (artBeg > artEnd) {
artBeg = artEnd;
adjustedArtBeg = 1;
}
if ((ov = GetOverInfo(group)) != NULL) {
if (artBeg < artEnd - ov->ov_Head->oh_MaxArts) {
artBeg = artEnd - ov->ov_Head->oh_MaxArts;
adjustedArtBeg = 1;
}
PutOverInfo(ov);
} else {
if (artBeg < artEnd - DEFMAXARTSINGROUP) {
artBeg = artEnd - DEFMAXARTSINGROUP;
adjustedArtBeg = 1;
}
}
if (artEnd == 0 && artBeg != 1) {
artBeg = 1;
adjustedArtBeg = 1;
}
GroupStats(conn);
conn->co_ClientGroupCount++;
zfreeStr(&conn->co_MemPool, &conn->co_GroupName);
conn->co_GroupName = zallocStr(&conn->co_MemPool, group);
MBLogPrintf(conn, &conn->co_TMBuf, "211 %d %d %d %s\r\n",
artEnd - artBeg + 1,
artBeg,
artEnd,
group
);
conn->co_ArtNo = artBeg;
conn->co_ArtBeg = artBeg;
conn->co_ArtEnd = artEnd;
NNCommand(conn);
}
void
NNTPLast(Connection *conn, char **pptr)
{
int saveArtNo = conn->co_ArtNo;
if (parseword(pptr, " \t") != NULL) {
MBLogPrintf(conn, &conn->co_TMBuf, "501 Usage error\r\n");
NNCommand(conn);
return;
}
while (conn->co_ArtNo > conn->co_ArtBeg) {
const char *ovdata;
const char *msgid;
int ovlen;
--conn->co_ArtNo;
if ((ovdata = NNRetrieveHead(conn, &ovlen, &msgid, NULL, NULL, NULL)) != NULL) {
MBLogPrintf(conn, &conn->co_TMBuf,
"223 %d %s Article retrieved; request text separately.\r\n",
conn->co_ArtNo,
msgid
);
NNCommand(conn);
return;
}
}
MBLogPrintf(conn, &conn->co_TMBuf, "422 No previous to retrieve.\r\n");
conn->co_ArtNo = saveArtNo;
NNCommand(conn);
}
void
NNTPNext(Connection *conn, char **pptr)
{
int saveArtNo = conn->co_ArtNo;
if (parseword(pptr, " \t") != NULL) {
MBLogPrintf(conn, &conn->co_TMBuf, "501 Usage error\r\n");
NNCommand(conn);
return;
}
while (conn->co_ArtNo < conn->co_ArtEnd) {
const char *ovdata;
const char *msgid;
int ovlen;
++conn->co_ArtNo;
if ((ovdata = NNRetrieveHead(conn, &ovlen, &msgid, NULL, NULL, NULL)) != NULL) {
MBLogPrintf(conn, &conn->co_TMBuf,
"223 %d %s Article retrieved; request text separately.\r\n",
conn->co_ArtNo,
msgid
);
NNCommand(conn);
return;
}
}
MBLogPrintf(conn, &conn->co_TMBuf, "421 No next to retrieve.\r\n");
conn->co_ArtNo = saveArtNo;
NNCommand(conn);
}
void
NNTPStat(Connection *conn, char **pptr)
{
NNArticleRetrieve(conn, parseword(pptr, " \t"), COM_STAT);
}
void
NNArticleRetrieve(Connection *conn, const char *artid, int mode)
{
conn->co_ArtMode = mode;
++conn->co_Auth.dr_ArtCount;
conn->co_RequestFlags = ARTFETCH_MSGID;
if (artid == NULL) {
if (conn->co_GroupName == NULL) {
MBLogPrintf(conn, &conn->co_TMBuf, "412 Not in a newsgroup\r\n");
NNCommand(conn);
return;
}
NNArticleRetrieveByArtNo(conn, conn->co_ArtNo);
} else if (isdigit((int)artid[0])) {
if (conn->co_GroupName == NULL) {
MBLogPrintf(conn, &conn->co_TMBuf, "412 Not in a newsgroup\r\n");
NNCommand(conn);
return;
}
NNArticleRetrieveByArtNo(conn, strtol(artid, NULL, 10));
} else {
artid = MsgId(artid, NULL);
if (strcmp(artid, "<>") == 0) {
MBLogPrintf(conn, &conn->co_TMBuf, "430 No such article\r\n");
NNCommand(conn);
} else {
/*
* XXX this isn't clean. We have to turn off verify mode
* when retrieving articles by message-id. It works anyway.
* We also use WVF to ensure we don't use headers for
* retrieval by artno and not msgid
*/
if (mode == COM_ARTICLEWVF)
conn->co_ArtMode = COM_ARTICLE;
NNArticleRetrieveByMessageId(conn, artid, 0, 0, -1, 0);
}
}
}
void
NNArticleRetrieveByArtNo(Connection *conn, int artNo)
{
const char *ovdata;
const char *msgid;
int ovlen, timeRcvd=0, grpIter=-1, endNo=0;
if (DebugOpt)
printf("ArtNo %d\n", artNo);
conn->co_RequestFlags = ARTFETCH_ARTNO;
conn->co_ArtNo = artNo;
if ((ovdata = NNRetrieveHead(conn, &ovlen, &msgid, &timeRcvd, &grpIter, &endNo)) != NULL) {
/*
* COM_ARTICLEWVF requires that we verify that we have a valid article
* body prior to dumping a valid response code. At this point we only
* have headers. COM_ARTICLE allows us to dump the status & headers
* and then dump (article not available) as the body if we cannot
* retrieve the article.
*/
if (conn->co_ArtMode != COM_ARTICLEWVF &&
conn->co_ArtMode != COM_BODYWVF
) {
/*
* Dump status header
*/
MBLogPrintf(conn, &conn->co_TMBuf,
"%03d %d %s %s\r\n",
GoodRC(conn),
conn->co_ArtNo,
msgid,
GoodResId(conn)
);
}
/*
* Dump headers. Do not dump them yet for COM_ARTICLEWVF.
*/
if (conn->co_ArtMode == COM_HEAD || conn->co_ArtMode == COM_ARTICLE) {
DumpOVHeaders(conn, ovdata, ovlen);
}
/*
* Blank line if both header & body (article)
*/
if (conn->co_ArtMode == COM_ARTICLE)
MBPrintf(&conn->co_TMBuf, "\r\n");
/*
* Dump body (for COM_ARTICLEWVF, this also dumps the headers when
* we have verified that the body can be retrieved)
*/
if (conn->co_ArtMode == COM_ARTICLEWVF ||
conn->co_ArtMode == COM_BODYWVF
) {
NNArticleRetrieveByMessageId(conn, msgid, 1, timeRcvd, grpIter, endNo);
} else if (
conn->co_ArtMode == COM_ARTICLE ||
conn->co_ArtMode == COM_BODY
) {
conn->co_ArtMode = COM_BODYNOSTAT;
NNArticleRetrieveByMessageId(conn, msgid, 1, timeRcvd, grpIter, endNo);
} else {
if (conn->co_ArtMode != COM_STAT)
MBPrintf(&conn->co_TMBuf, ".\r\n");
NNCommand(conn);
}
} else {
MBLogPrintf(conn, &conn->co_TMBuf, "423 No such article number in this group\r\n");
NNCommand(conn);
}
}
void
NNExecuteOnRange(Connection *conn, const char *artid, int mode)
{
printf("ExecuteOnRange not implemented yet\n");
}
int
GoodRC(Connection *conn)
{
switch(conn->co_ArtMode) {
case COM_STAT:
return(223);
case COM_HEAD:
return(221);
case COM_ARTICLE:
case COM_ARTICLEWVF:
conn->co_ClientTotalArticleCount++;
conn->co_ClientGroupArticleCount++;
return(220);
case COM_BODY:
case COM_BODYWVF:
conn->co_ClientTotalArticleCount++;
conn->co_ClientGroupArticleCount++;
return(222);
}
return(0);
}
const char *
GoodResId(Connection *conn)
{
switch(conn->co_ArtMode) {
case COM_STAT:
return("status");
case COM_HEAD:
return("head");
case COM_ARTICLE:
case COM_ARTICLEWVF:
return("article");
case COM_BODY:
case COM_BODYWVF:
return("body");
}
return("?");
}
void
DumpOVHeaders(Connection *conn, const char *ovdata, int ovlen)
{
char *vserver;
int vslen;
if (conn->co_Auth.dr_VServerDef)
vserver = conn->co_Auth.dr_VServerDef->vs_ClusterName;
else
vserver = "";
vslen = strlen(vserver);
while (ovlen) {
int i;
char ch;
for (i = 0; i < ovlen && ovdata[i] != '\n'; ++i)
;
if (i == 1 && ovdata[0] == '\r' && ovdata[1] == '\n')
break;
if (ovdata[0] == '.')
MBPrintf(&conn->co_TMBuf, ".");
ch = tolower(ovdata[0]);
if (*vserver && ch == 'p' && strncasecmp(ovdata, "Path:", 5) == 0) {
int b = 5;
char newpath[256];
sprintf(newpath, "%s!", vserver);
while (b < i && (ovdata[b] == ' ' || ovdata[b] == '\t'))
++b;
MBWrite(&conn->co_TMBuf, ovdata, b);
i -= b;
ovlen -= b;
ovdata += b;
if (strncmp(newpath, ovdata, vslen + 1) != 0)
MBWrite(&conn->co_TMBuf, newpath, vslen + 1);
} else if (*vserver && ch == 'x' && strncasecmp(ovdata, "Xref:", 5) == 0) {
int b = 5;
while (b < i && (ovdata[b] == ' ' || ovdata[b] == '\t'))
++b;
MBWrite(&conn->co_TMBuf, ovdata, b);
MBWrite(&conn->co_TMBuf, vserver, vslen);
while (b < i && (ovdata[b] != ' ' && ovdata[b] != '\t'))
++b;
i -= b;
ovlen -= b;
ovdata += b;
}
MBWrite(&conn->co_TMBuf, ovdata, i);
if (i == 0 || ovdata[i-1] != '\r')
MBWrite(&conn->co_TMBuf, "\r\n", 2);
else
MBWrite(&conn->co_TMBuf, "\n", 1);
if (i < ovlen)
++i;
ovlen -= i;
ovdata += i;
}
}
syntax highlighted by Code2HTML, v. 0.9.1