/* Copyright (c) 1996, Ruslan R. Laishev (@RRL) */ #include "nntp.h" #include "nntp_log.h" #include "nntp_lib.h" char MODE_READER[] = "MODE READER\r\n"; char GROUP [] = "GROUP %.*s\r\n"; char ARTICLE[] = "ARTICLE %u\r\n"; char QUIT[] = "QUIT\r\n"; char NEXT[] = "NEXT\r\n"; char LIST[] = "LIST\r\n"; char STAT[] = "STAT %u\r\n"; int nntp_suck_group ( WorkerContext *Wctxp, SuckRec *srec ); int nntp_suck_list ( WorkerContext *Wctxp, char *GrpList ); /* *-------------------------------------------------------------------------------- */ int nntp_line_get (int chan,char *bufp,int bufl) { int sz; int pos; pos = 0; do { if ( 0 > (sz = recv(chan,bufp+pos,1, 0)) ) { NNTP_LOG(LOGE,"[Ch:%d]nntp_line_get:%s",chan,strerror(errno)); return -1; } pos += sz; if ( (pos >= 2) && *(bufp+pos-2) == '\r' && *(bufp+pos-1) == '\n' ) break; } while (pos < bufl); *(bufp+pos-2) = '\0'; return pos; } /* *-------------------------------------------------------------------------------- */ 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_LOG(LOGE,"[Th:Suck,Ch:%d]nntp_suck_msg:%s", Wctxp->chan,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; sz = pos - (cp-bufA) - 2 ; memcpy(bufM,cp+2,sz); *(bufM + sz) = 0; if ( sz && (cp = strstr (bufM,"\r\n.\r\n")) ) { *(cp+5) = 0; *sz1 = 5 + (cp - bufM); 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 ]; char lIP [ 17 ]; int sz; strcpy (lGrpList,GrpList); strcpy (lIP,IP); /* For Dump INN and other *ix... send MODE READER */ if ( 0 > nntp_cmd_get (Wctxp->chan,Wctxp->bufp,sizeof(Wctxp->bufp)) ) 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,sizeof(Wctxp->bufp)) ) return -1; /* */ while ( GrpDBget_seq (&Wctxp->Grprab,&gkey,flag++) ) { NNTP_LOG(LOGD,"[Th:Suck,Ch:%d]Check group '%.*s'.", Wctxp->chan,GrpName$_len,gkey.GrpName); gkey.GrpName[GrpName$_len] = 0; if ( (!gkey.SuckFlag) || (!match_in_list (lGrpList,&gkey.GrpName[0])) ) { NNTP_LOG(LOGD,"[Th:Suck,Ch:%d]Skip group '%.*s'.", Wctxp->chan,GrpName$_len,gkey.GrpName); continue; } NNTP_LOG(LOGW,"[Th:Suck,Ch:%d]Group '%.*s'-Cached.", Wctxp->chan,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_LOG(LOGD,"[Th:Suck,Ch:%d]Request info-'%.*s'.", Wctxp->chan,GrpName$_len,gkey.GrpName); if ( 0 > (sz = nntp_cmd_get (Wctxp->chan,Wctxp->bufp,sizeof(Wctxp->bufp))) ) { NNTP_LOG(LOGE,"[Th:Suck,Ch:%d]Request info-%.*s.", Wctxp->chan,GrpName$_len,gkey.GrpName); return -1; } NNTP_LOG(LOGD,"[Th:Suck,Ch:%d]Group info-'%.*s'", Wctxp->chan,sz,Wctxp->bufp); sscanf(&Wctxp->bufp[0],"%u %u %u %u",&rc,&nmsg,&fmsg,&lmsg); if ( rc != 211) continue; /* */ sprintf(&srec.Host_Grp,"%s,%.*s",lIP,GrpName$_len,gkey.GrpName); if ( SuckDBget(&Wctxp->Suckrab,&srec) ) { NNTP_LOG(LOGW,"[Th:Suck,Ch:%d]'%.*s' last getted ARTICLE %u.", Wctxp->chan,GrpName$_len,gkey.GrpName,srec.Last); if ( lmsg <= srec.Last) continue; fmsg = max (fmsg,srec.Last); } /* */ srec.Last = fmsg; NNTP_LOG(LOGW,"[Th:Suck,Ch:%d]Start update '%.*s' at ARTICLE %u.", Wctxp->chan,GrpName$_len,gkey.GrpName,srec.Last); nntp_suck_group ( Wctxp,&srec); /* */ NNTP_LOG(LOGW,"[Th:Suck,Ch:%d]Update SuckDB for %.*s,last ARTICLE %u", Wctxp->chan,Suck$_len,srec.Host_Grp,srec.Last); if ( !SuckDBput(&Wctxp->Suckrab,&srec) ) NNTP_LOG(LOGE,"[Th:Suck,Ch:%d]Update SuckDB-Error.",Wctxp->chan); /* */ } if ( flag == 1 ) nntp_suck_list (Wctxp,nntp_conf.GrpME); send(Wctxp->chan,QUIT,sizeof (QUIT)-1,0); NNTP_LOG(LOGW,"[Th:Suck,Ch:%d]All Group is Up-to-Date.",Wctxp->chan); return 0; } /* *-------------------------------------------------------------------------------- */ int nntp_suck_group ( WorkerContext *Wctxp, SuckRec *srec ) { unsigned long rc,cmsg; int sz,szA,szM; char *cp0,*cp1; cmsg = srec->Last; 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,sizeof(Wctxp->bufp))) ) return -1; NNTP_LOG(LOGD,"[Th:Suck,Ch:%d]Response to STAT %u from Suck '%.*s'.", Wctxp->chan,cmsg,sz,Wctxp->bufp); while (1) { /* Go to first available messages by 'NEXT' command */ if ( 0 > send (Wctxp->chan,&NEXT,sizeof(NEXT)-1,0) ) return -1; if ( 0 > (sz = nntp_cmd_get(Wctxp->chan,Wctxp->bufp,sizeof(Wctxp->bufp))) ) return -1; NNTP_LOG(LOGD,"[Th:Suck,Ch:%d]Response to NEXT from Suck '%.*s'.", Wctxp->chan,sz,Wctxp->bufp); if ( 2 != sscanf(Wctxp->bufp,"%u %u",&rc,&cmsg) ) return -1; if ( rc > 400 ) return 0; if ( rc != 223 ) continue; if ( (!(cp0 = strchr(Wctxp->bufp,'<'))) || (!(cp1 = strchr(cp0,'>'))) ) continue; if ( (cp1 - cp0) > MsgId$_len ) continue; *(++cp1) = 0; NNTP_LOG(LOGD,"[Th:Suck,Ch:%d]ARTICLE #%u %.*s:check for dup.", Wctxp->chan,cmsg,MsgId$_len,cp0); if (!MsgDBfind_byId(&Wctxp->Msgrab,cp0,cp1-cp0) ) { NNTP_LOG(LOGW,"[Th:Suck,Ch:%d]ARTICLE #%u %.*s:duplicate.", Wctxp->chan,cmsg,MsgId$_len,cp0); continue; } NNTP_LOG(LOGD,"[Th:Suck,Ch:%d]ARTICLE #%u %.*s-Get.", Wctxp->chan,cmsg,MsgId$_len,cp0); sz = sprintf(Wctxp->bufp,ARTICLE,cmsg); if ( 0 > send (Wctxp->chan,Wctxp->bufp,sz,0) ) return -1; if ( 0 >= nntp_suck_msg (Wctxp,&szA,&szM) ) continue; /* Wctxp->bufp = first line of response to ARTICLE # command Wctxp->MsgBuff[sizeof(MsgKey)] = body of message */ NNTP_LOG(LOGD,"[Th:Suck,Ch:%d]Response to ARTICLE %u from Suck '%.*s'.", Wctxp->chan,cmsg,szA,Wctxp->bufp); if ( (!(cp0 = strchr(Wctxp->bufp,'<'))) || (!(cp1 = strchr(cp0,'>'))) ) continue; if ( (cp1 - cp0) > MsgId$_len ) continue; *(++cp1) = 0; if ( szM <= 0 ) { NNTP_LOG(LOGD,"[Th:Suck,Ch:%d]ARTICLE %u:size is not valid-Skip.", Wctxp->chan,srec->Last); continue; } srec->Last = cmsg; if ( !nntp_msg_to_db (Wctxp,szM) ) { NNTP_LOG(LOGD,"[Th:Suck,Ch:%d]Save ARTICLE #%u.", Wctxp->chan,srec->Last); } else { NNTP_LOG(LOGE,"[Th:Suck,Ch:%d]Save ARTICLE #%u.", Wctxp->chan,srec->Last); } } return 0; } /* *-------------------------------------------------------------------------------- */ int nntp_suck_list ( WorkerContext *Wctxp, char *GrpList ) { unsigned long rc; int sz; char *cp0; GrpKey *gkeyp = (GrpKey *) &Wctxp->bufp[0]; if ( 0 > send (Wctxp->chan,&LIST,sizeof(LIST)-1,0) ) return -1; if ( 0 > (sz = nntp_line_get(Wctxp->chan,Wctxp->bufp,sizeof(Wctxp->bufp))) ) return -1; NNTP_LOG(LOGD,"[Th:Suck,Ch:%d]Response to LIST from Suck '%.*s'.", Wctxp->chan,sz,Wctxp->bufp); if ( !sscanf(&Wctxp->bufp[0],"%u",&rc) ) return -1; if ( rc != 215 ) return -1; NNTP_LOG(LOGD,"[Th:Suck,Ch:%d]Start sucking groups list from '%.*s'.", Wctxp->chan,sz,Wctxp->bufp); while (1) { if ( 0 > (sz = nntp_line_get(Wctxp->chan,Wctxp->bufp,sizeof(Wctxp->bufp))) ) return -1; if ( !(cp0 = strchr(&Wctxp->bufp[0],' ')) ) continue; if ( (cp0 - &Wctxp->bufp[0]) > GrpName$_len ) continue; *(cp0) = 0; /* */ if ( !strcmp (&Wctxp->bufp[0],".") ) break; /* */ NNTP_LOG(LOGD,"[Th:Suck,Ch:%d]Group '%.*s':check for GrpME.", Wctxp->chan,GrpName$_len,Wctxp->bufp); if ( !match_in_list (GrpList,Wctxp->bufp) ) continue; /* */ NNTP_LOG(LOGD,"[Th:Suck,Ch:%d]Group '%.*s':check for dup.", Wctxp->chan,GrpName$_len,Wctxp->bufp); if ( 0 >= GrpDBget (&Wctxp->Grprab,Wctxp->bufp,Wctxp->MsgBuff),0 ) { NNTP_LOG(LOGD,"[Th:Suck,Ch:%d]Creating group '%.*s'.", Wctxp->chan,GrpName$_len,Wctxp->bufp); memset (cp0,0,GrpName$_len - (cp0-&Wctxp->bufp[0])); gkeyp->First = 0; gkeyp->Last = 0; gkeyp->PostFlag = 1; gkeyp->SuckFlag = 0; time(&gkeyp->DateCr); gkeyp->DateUp = 0; if ( 0 >= GrpDBput (&Wctxp->Grprab,gkeyp,gkeyp) ) NNTP_LOG(LOGE,"[Th:Suck,Ch:%d]Creating group '%.*s'.", Wctxp->chan,GrpName$_len,Wctxp->bufp); } } NNTP_LOG(LOGW,"[Th:Suck,Ch:%d]End sucking groups list.",Wctxp->chan); return 0; }