/* Copyright (c) 1996, Ruslan R. Laishev (@RRL) */ #include "nntp.h" int nntp_suck_group ( WorkerContext *, SuckRec *, int, char * ); int nntp_suck_list ( WorkerContext *, char * ); /* *-------------------------------------------------------------------------------- */ int nntp_suck_msg ( WorkerContext *Wctxp, int *sz0, int *sz1 ) { int sz; int pos; char *bufA = &Wctxp->bufp[0]; char *bufM = &Wctxp->MsgBuff[sizeof(MsgKey)]; int bufAsz = sizeof(Wctxp->bufp) - 2; int bufMsz = (sizeof(Wctxp->MsgBuff) - sizeof(MsgKey)-2); char *cp; /* */ pos = *sz0 = *sz1 = 0; /* Get first line (answer to ARTICLE #command) */ do { if ( 0 > (sz = recv(Wctxp->chan,bufA+pos,bufAsz-pos, 0)) ) { NNTP_LOGT(Wctxp,LOGE,"'nntp_suck_msg':%s",strerror(errno)); return -1; } pos += sz; *(bufA+pos) = '\0'; if ( (pos >= 2) && (cp = strstr(bufA,"\r\n")) ) break; } while (pos < bufAsz); if ( !cp ) return -1; *cp = 0; *sz0 = cp - bufA; cp += 2; sz = pos - (cp-bufA); memcpy(bufM,cp,sz); *(bufM+sz) = 0; if ( (sz >= 5) && (cp = strstr (bufM,"\r\n.\r\n")) ) { *(cp + 5) = 0; *sz1 = (cp-bufM+5); return (*sz1); } /* Now, in bufA first line of answer to ARTICLE commands, in bufM rest of line (or other hand start of article... */ *sz1 = nntp_txt_get (Wctxp->chan,bufM+sz,bufMsz-sz); if (*sz1 > 0 ) *sz1 =*sz1 + sz; return *sz1; } /* *-------------------------------------------------------------------------------- */ int nntp_suck ( WorkerContext *Wctxp, char *GrpList, char *IP ) { GrpKey gkey; SuckRec srec; int flag = 0; int rc; unsigned long int nmsg,fmsg,lmsg; char lGrpList [ 1024 ]; int sz; char MODE_READER[] = "MODE READER\r\n"; char QUIT[] = "QUIT\r\n"; char GROUP [] = "GROUP %.*s\r\n"; strcpy (lGrpList,GrpList); /* For Dump INN and other *ix... send MODE READER */ if ( 0 > nntp_cmd_get (Wctxp->chan,Wctxp->bufp,BUFPSZ) ) return -1; if ( 0 > send(Wctxp->chan,MODE_READER,sizeof (MODE_READER)-1,0) ) return -1; if ( 0 > nntp_cmd_get (Wctxp->chan,Wctxp->bufp,BUFPSZ) ) return -1; /* */ while ( GrpDBget_seq (&Wctxp->Grprab,&gkey,flag++) ) { /* NNTP_LOGT(Wctxp,LOGD,"Check group '%.*s'.", GrpName$_len,gkey.GrpName); */ gkey.GrpName[GrpName$_len] = 0; if ( (!gkey.SuckFlag) || (!strmatch (lGrpList,&gkey.GrpName[0])) ) { /* NNTP_LOGT(Wctxp,LOGD,"Skip group '%.*s'.", GrpName$_len,gkey.GrpName); */ continue; } NNTP_LOGT(Wctxp,LOGD,"Group '%.*s'-Cached.", GrpName$_len,gkey.GrpName); sz = sprintf(&Wctxp->bufp[0],GROUP,GrpName$_len,gkey.GrpName); if ( 0 > send (Wctxp->chan,Wctxp->bufp,sz,0) ) return -1; NNTP_LOGT(Wctxp,LOGD,"Request info-'%.*s'.", GrpName$_len,gkey.GrpName); if ( 0 > (sz = nntp_cmd_get (Wctxp->chan,Wctxp->bufp,BUFPSZ)) ) { NNTP_LOGT(Wctxp,LOGE,"Request info-%.*s.", GrpName$_len,gkey.GrpName); return -1; } NNTP_LOGT(Wctxp,LOGW,"Group info-'%.*s'",sz,Wctxp->bufp); sscanf(Wctxp->bufp,"%u %u %u %u",&rc,&nmsg,&fmsg,&lmsg); if ( rc != 211) continue; /* * */ sprintf(srec.Host_Grp,"%s,%.*s",IP,GrpName$_len,gkey.GrpName); if ( SuckDBget(&Wctxp->Suckrab,&srec) ) { NNTP_LOGT(Wctxp,LOGW,"'%.*s' last getted ARTICLE #%u.", GrpName$_len,gkey.GrpName,srec.Last); if ( lmsg == srec.Last) continue; fmsg = max (fmsg,srec.Last); } if ( !nntp_suck_group ( Wctxp,&srec,fmsg,gkey.GrpName) ) continue; /* * */ NNTP_LOGT(Wctxp,LOGW,"Update SuckDB for %.*s,last ARTICLE #%u", Suck$_len,srec.Host_Grp,srec.Last); if ( !SuckDBput(&Wctxp->Suckrab,&srec) ) NNTP_LOGT(Wctxp,LOGE,"Update SuckDB."); } if ( flag == 1 ) nntp_suck_list (Wctxp,nntp_conf.GrpME); send(Wctxp->chan,QUIT,sizeof (QUIT)-1,0); NNTP_LOGT(Wctxp,LOGW,"All Group is Up-to-Date."); return 0; } /* *-------------------------------------------------------------------------------- */ int nntp_suck_group ( WorkerContext *Wctxp, SuckRec *srec, int cmsg, char *Grp ) { unsigned long rc; int sz,szA,szM; char *cp0,*cp1; char STAT[] = "STAT %u\r\n"; char NEXT[] = "NEXT\r\n"; char ARTICLE[] = "ARTICLE %u\r\n"; int Sucked,Duplicated,Skiped; Sucked = Duplicated = Skiped = 0; sz = sprintf(Wctxp->bufp,STAT,cmsg); if ( 0 > send (Wctxp->chan,Wctxp->bufp,sz,0) ) return -1; if ( 0 > (sz = nntp_cmd_get(Wctxp->chan,Wctxp->bufp,BUFPSZ)) ) return -1; NNTP_LOGT(Wctxp,LOGW,"Response to STAT %u from Suck '%.*s'.", cmsg,sz,Wctxp->bufp); NNTP_LOGT(Wctxp,LOGW,"Start download '%.*s' at ARTICLE #%u.", GrpName$_len,Grp,cmsg); while (1) { /* Go to next available message ID by 'NEXT' command */ if ( 0 > send (Wctxp->chan,&NEXT,sizeof(NEXT)-1,0) ) break; if ( 0 > (sz = nntp_cmd_get(Wctxp->chan,Wctxp->bufp,BUFPSZ)) ) break; NNTP_LOGT(Wctxp,LOGW,"Response to NEXT from Suck '%.*s'.", sz,Wctxp->bufp); if ( 2 != sscanf(Wctxp->bufp,"%u %u",&rc,&cmsg) ) break; if ( rc > 400 ) break; if ( rc != 223 ) continue; if ( (!(cp0 = memchr(Wctxp->bufp,'<',sz))) || (!(cp1 = memchr(cp0,'>',sz-(cp0-&Wctxp->bufp[0])))) ) break; *(++cp1) = 0; if ( (cp1 - cp0) > MsgId$_len ) { NNTP_LOGT(Wctxp,LOGW,"Message-ID %.*s is too long (%u bytes)-Skip.", cp1-cp0,cp0,cp1-cp0); Skiped++; continue; } NNTP_LOGT(Wctxp,LOGD,"ARTICLE #%u %.*s:check for dup.", cmsg,MsgId$_len,cp0); srec->Last = cmsg; if (!MsgDBfind_byId(&Wctxp->Msgrab,cp0,cp1-cp0) ) { NNTP_LOGT(Wctxp,LOGW,"ARTICLE #%u %.*s:duplicate.", cmsg,MsgId$_len,cp0); Duplicated++; continue; } NNTP_LOGT(Wctxp,LOGD,"ARTICLE #%u %.*s-Get.",cmsg,MsgId$_len,cp0); sz = sprintf(Wctxp->bufp,ARTICLE,cmsg); if ( 0 > send (Wctxp->chan,Wctxp->bufp,sz,0) ) break; if ( 0 >= nntp_suck_msg (Wctxp,&szA,&szM) || szM <= 0 ) { NNTP_LOGT(Wctxp,LOGW,"ARTICLE #%u:size is not valid-Skip.", srec->Last); Skiped++; continue; } /* Wctxp->bufp = first line of response to ARTICLE # command Wctxp->MsgBuff[sizeof(MsgKey)] = body of message */ NNTP_LOGT(Wctxp,LOGW,"Response to ARTICLE %u from Suck '%.*s'.", cmsg,szA,Wctxp->bufp); if ( (!(cp0 = memchr(Wctxp->bufp,'<',szA))) || (!(cp1 = memchr(cp0,'>',szA-(cp0-&Wctxp->bufp[0])))) ) break; *(++cp1) = 0; if ( (cp1 - cp0) > MsgId$_len ) { NNTP_LOGT(Wctxp,LOGW,"Message-ID %.*s is too long (%u bytes)-Skip.", cp1-cp0,cp0,cp1-cp0); Skiped++; continue; } if ( !msg_to_db (Wctxp,szM) ) { Sucked++; } else { Skiped++; } } NNTP_LOGT(Wctxp,LOGW,"'%.*s' statistic:Sucked %u,Dup %u,Skiped %u.", GrpName$_len,Grp,Sucked,Duplicated,Skiped); return (Sucked + Duplicated + Skiped); } /* *-------------------------------------------------------------------------------- */ int nntp_suck_list ( WorkerContext *Wctxp, char *GrpList ) { unsigned long rc; int sz; char *cp0; GrpKey *gkeyp = (GrpKey *) &Wctxp->bufp[0]; char LIST[] = "LIST\r\n"; if ( 0 > send (Wctxp->chan,&LIST,sizeof(LIST)-1,0) ) return -1; if ( 0 > (sz = nntp_line_get(Wctxp->chan,Wctxp->bufp,BUFPSZ)) ) return -1; NNTP_LOGT(Wctxp,LOGW,"Response to LIST from Suck '%.*s'.",sz,Wctxp->bufp); if ( !sscanf(Wctxp->bufp,"%u",&rc) ) return -1; if ( rc != 215 ) return -1; NNTP_LOGT(Wctxp,LOGD,"Start sucking groups list from '%.*s'.", sz,Wctxp->bufp); while (1) { if ( 0 >= (sz = nntp_line_get(Wctxp->chan,Wctxp->bufp,BUFPSZ)) ) return -1; if ( (sz == 1) && (Wctxp->bufp[0] == '.') ) break; if ( !(cp0 = memchr(Wctxp->bufp,' ',sz)) ) continue; if ( (cp0 - &Wctxp->bufp[0]) > GrpName$_len ) continue; *(cp0) = 0; /* * Get posting flag ('y'|'n'|'m') */ if ( 3 != sscanf (cp0+1,"%u %u %c", &gkeyp->Last,&gkeyp->First,&gkeyp->PostFlag) ) continue; tolower(gkeyp->PostFlag); /* */ NNTP_LOGT(Wctxp,LOGD,"Group '%.*s':check for GrpME.", GrpName$_len,Wctxp->bufp); if ( !strmatch (GrpList,Wctxp->bufp) ) continue; /* */ NNTP_LOGT(Wctxp,LOGD,"Group '%.*s':check for dup.", GrpName$_len,Wctxp->bufp); if ( 0 >= GrpDBget (&Wctxp->Grprab,Wctxp->bufp,Wctxp->MsgBuff,0) ) { NNTP_LOGT(Wctxp,LOGD,"Creating group '%.*s'.", GrpName$_len,Wctxp->bufp); memset (cp0,0,GrpName$_len - (cp0-&Wctxp->bufp[0])); gkeyp->First = 0; gkeyp->Last = 0; gkeyp->SuckFlag = 0; time(&gkeyp->DateCr); gkeyp->DateUp = 0; if ( 0 >= GrpDBput (&Wctxp->Grprab,gkeyp,gkeyp) ) NNTP_LOGT(Wctxp,LOGE,"Creating group '%.*s'.", GrpName$_len,Wctxp->bufp); } } NNTP_LOGT(Wctxp,LOGW,"End sucking groups list."); return 0; }