/* Copyright (c) 1996-2001, Ruslan R. Laishev (@RRL) */ #include "nntp.h" char NEXT[] = "NEXT"; char ARTICLE[] = "ARTICLE %u"; char MODE_READER[] = "MODE READER"; char QUIT[] = "QUIT"; char GROUP [] = "GROUP %.*s"; int nntp_suck_group (wctx_t *,fsrec_t *,ulong); int nntp_suck_list (wctx_t *,fsrec_t *,char *,ushort); /* *-------------------------------------------------------------------------------- */ int nntp_suck_msg ( wctx_t *Wctxp, char *buf0, ushort *sz0, char *buf1, ushort *sz1 ) { long status; /* ** Get first line (answer to ARTICLE #command) */ if ( !(1 & (status = net_read_line (Wctxp->_a_chan,buf0,sz0,Wctxp->_d_tmo))) ) return status; /* ** Get whole ARTICLE */ return net_read_mline (Wctxp->_a_chan,buf1,sz1,Wctxp->_d_tmo); } /* *-------------------------------------------------------------------------------- */ int nntp_suck ( wctx_t *Wctxp, struct FSArg *AL ) { long status; fsrec_t srec; int rewindf = 0; ushort rc,sz; ulong nmsg,fmsg,lmsg; /* ** For Dumb INN and other *ix... send MODE READER */ sz = BUFPSZ; if ( !(1 & (status = net_read_line (Wctxp->_a_chan,Wctxp->_t_buf,&sz,Wctxp->_d_tmo))) ) return status; if ( !(1 & (status = net_send_line (Wctxp->_a_chan,ASCIC(MODE_READER)))) ) return status; sz = BUFPSZ; if ( !(1 & (status = net_read_line (Wctxp->_a_chan,Wctxp->_t_buf,&sz,Wctxp->_d_tmo))) ) return status; /* ** */ nntp_suck_list (Wctxp,&srec,(AL+SUCK$NAME)->_a_arg,(AL+SUCK$NAME)->_w_len); /* ** */ while ( 1 & (status=GrpDBget (&Wctxp->_s_grprab,&Wctxp->grec,0,rewindf++,1)) ) { if ( (!Wctxp->grec._c_suckflag) || (!strmatch((AL+SUCK$GROUPS)->_a_arg,(AL+SUCK$GROUPS)->_w_len, Wctxp->grec._t_name,Wctxp->grec._b_len)) ) continue; NNTP_LOGT(Wctxp,LOGD,"Group '%.*s'-Has been cached.", Wctxp->grec._b_len,Wctxp->grec._t_name); sz = sprintf(Wctxp->_t_buf,GROUP,Wctxp->grec._b_len,Wctxp->grec._t_name); if ( !(1 & (status = net_send_line (Wctxp->_a_chan,Wctxp->_t_buf,sz))) ) return status; NNTP_LOGT(Wctxp,LOGD,"Request info-'%.*s'.", Wctxp->grec._b_len,Wctxp->grec._t_name); sz = BUFPSZ-1; if ( !(1 & (status = net_read_line (Wctxp->_a_chan,Wctxp->_t_buf,&sz,Wctxp->_d_tmo))) ) { NNTP_LOGT(Wctxp,LOGE,"Request info-%.*s.",Wctxp->grec._b_len,Wctxp->grec._t_name); return status; } NNTP_LOGT(Wctxp,LOGW,"Group info-'%.*s'",sz,Wctxp->_t_buf); Wctxp->_t_buf[sz] = 0; sscanf(Wctxp->_t_buf,"%u %u %u %u",&rc,&nmsg,&fmsg,&lmsg); if ( rc != 211) continue; /* * */ MDString (SUCK$TYPE,(AL+SUCK$NAME)->_a_arg,(AL+SUCK$NAME)->_w_len, Wctxp->grec._t_name,Wctxp->grec._b_len,srec._t_hash); status = FeedSuckDBget(&Wctxp->_s_feedsuckrab,&srec); if ( !$VMS_STATUS_SUCCESS(status) && status != RMS$_RNF ) NNTP_LOGT(Wctxp,LOGF,"nntp_suck/FeedSuckDBget,(status = %d)",status); if ( $VMS_STATUS_SUCCESS(status) ) { NNTP_LOGT(Wctxp,LOGW,"'%.*s' last gotten ARTICLE #%u.", Wctxp->grec._b_len,Wctxp->grec._t_name,srec._l_last); if ( lmsg == srec._l_last) continue; fmsg = max(min(lmsg,srec._l_last),fmsg); } if ( !(1 & (status = nntp_suck_group ( Wctxp,&srec,fmsg))) ) NNTP_LOGT(Wctxp,LOGE,"nntp_suck_group,(status = %d)",status); /* ** Check and update suck record */ if ( fmsg == srec._l_last ) continue; NNTP_LOGT(Wctxp,LOGW,"Update SuckDB for %.*s,last gotten ARTICLE #%u", Wctxp->grec._b_len,Wctxp->grec._t_name,srec._l_last); if ( !(1 & (status = FeedSuckDBput(&Wctxp->_s_feedsuckrab,&srec))) ) { NNTP_LOGT(Wctxp,LOGS,"Update suck record,(status = %d).",status); break; } } net_send_line (Wctxp->_a_chan,ASCIC(QUIT)); return status == RMS$_EOF?SS$_NORMAL:status; } /* *-------------------------------------------------------------------------------- */ int nntp_suck_group ( wctx_t *Wctxp, fsrec_t *srec, ulong cmsg ) { long status; ushort rc,sz,szA,szM; char *cp0,*cp1; ulong Sucked,Duplicated,Skiped; char STAT[] = "STAT %u"; Sucked = Duplicated = Skiped = 0; /* ** Send 'STAT' command for get information about article */ sz = sprintf(Wctxp->_t_buf,STAT,cmsg); if ( !(1 & (status = net_send_line (Wctxp->_a_chan,Wctxp->_t_buf,sz))) ) return status; sz = BUFPSZ; if ( !(1 & (status = net_read_line (Wctxp->_a_chan,Wctxp->_t_buf,&sz,Wctxp->_d_tmo))) ) return status; NNTP_LOGT(Wctxp,LOGD,"Answer to 'STAT %u' from Suck '%.*s'.", cmsg,sz,Wctxp->_t_buf); NNTP_LOGT(Wctxp,LOGD,"Start download '%.*s' at ARTICLE #%u.", Wctxp->grec._b_len,Wctxp->grec._t_name,cmsg); while (1) { srec->_l_last = cmsg; cmsg++; /* ** Go to next available message ID by 'NEXT' command */ if ( !(1 & (status = net_send_line (Wctxp->_a_chan,ASCIC(NEXT)))) ) break; sz = BUFPSZ-1; if ( !(1 & (status = net_read_line (Wctxp->_a_chan,Wctxp->_t_buf,&sz,Wctxp->_d_tmo))) ) break; NNTP_LOGT(Wctxp,LOGD,"Answer to NEXT from Suck '%.*s'.", sz,Wctxp->_t_buf); Wctxp->_t_buf[sz] = 0; /* ** Check return code */ if ( 2 != sscanf(Wctxp->_t_buf,"%u %u",&rc,&cmsg) ) { NNTP_LOGT(Wctxp,LOGD,"Can't get rc cmsg"); break; }; if ( rc > 400 ) break; if ( rc != 223 ) continue; /* ** Extract Message-ID field and checki it for length */ if ( (!(cp0 = memchr(Wctxp->_t_buf,'<',sz))) || (!(cp1 = memchr(cp0,'>',sz-(cp0-Wctxp->_t_buf)))) ) break; sz = (++cp1) - cp0; if ( sz > MSGID$_LEN ) { NNTP_LOGT(Wctxp,LOGE,"Message-ID %.*s - is too long (%u bytes)-Skip.", sz,cp0,sz); Skiped++; continue; } NNTP_LOGT(Wctxp,LOGD,"ARTICLE #%u %.*s:check for dup.",cmsg,sz,cp0); /* ** Check for duplicate */ if ( (1 & (status = MsgDBfind_byId(&Wctxp->_s_msgrab,cp0,sz))) ) { NNTP_LOGT(Wctxp,LOGW,"ARTICLE #%u %.*s:Found in database.",cmsg,sz,cp0); Duplicated++; continue; } if ( !$VMS_STATUS_SUCCESS(status) && (status != RMS$_RNF) ) break; NNTP_LOGT(Wctxp,LOGD,"ARTICLE #%u %.*s-Retrieving.",cmsg,sz,cp0); /* ** Get message with Message-ID */ sz = sprintf(Wctxp->_t_buf,ARTICLE,cmsg); if ( !(1 & (status = net_send_line (Wctxp->_a_chan,Wctxp->_t_buf,sz))) ) break; szA = BUFPSZ; szM = MSGMAXSZ; if ( !(1 & (status = nntp_suck_msg (Wctxp, Wctxp->_t_buf,&szA, Wctxp->mrec._t_body,&szM))) ) { NNTP_LOGT(Wctxp,LOGE,"ARTICLE #%u:size is not valid-Skip.", srec->_l_last); Skiped++; continue; } if ( (!(cp0 = memchr(Wctxp->_t_buf,'<',szA))) || (!(cp1 = memchr(cp0,'>',szA-(cp0-Wctxp->_t_buf)))) ) { NNTP_LOGT(Wctxp,LOGE,"Can't find Mess-ID"); break; } sz = (++cp1) - cp0; /* ** Additional checking of article and put it into the database */ if ( (1 & (status = msg_to_db (Wctxp,szM))) ) Sucked++; else Skiped++; } /* ** Some statistic */ NNTP_LOGT(Wctxp,LOGW,"'%.*s' statistic:Suck:%lu,Dup:%lu,Skip:%lu.", Wctxp->grec._b_len,Wctxp->grec._t_name,Sucked,Duplicated,Skiped); if (!$VMS_STATUS_SUCCESS(status)) NNTP_LOGT(Wctxp,LOGS,"End of sucking newsgroups,(status = %d).",status); else NNTP_LOGT(Wctxp,LOGW,"End of sucking newsgroups."); return status; } /* *-------------------------------------------------------------------------------- */ int nntp_suck_list ( wctx_t *Wctxp, fsrec_t *srec, char *host, ushort hostlen ) { long status; char *cp0; char buf[128]; ushort rc,sz = sizeof(buf); char NEWGROUPS[] = "NEWGROUPS %.*s"; char LIST[] = "LIST"; /* ** */ NNTP_LOGT(Wctxp,LOGI,"Update newsgroups list."); MDString (0,host,hostlen,"",0,srec->_t_hash); status = FeedSuckDBget(&Wctxp->_s_feedsuckrab,srec); if ( !$VMS_STATUS_SUCCESS(status) && status != RMS$_RNF ) return status; if ( status & 1 ) { cvt_vms_to_nntp(srec->_l_last,buf,&sz); sz = sprintf(Wctxp->_t_buf,NEWGROUPS,sz,buf); net_send_line (Wctxp->_a_chan,Wctxp->_t_buf,sz); } else { net_send_line (Wctxp->_a_chan,ASCIC(LIST)); } time(&srec->_l_last); sz = BUFPSZ; if ( !(1 & (status = net_read_line (Wctxp->_a_chan,Wctxp->_t_buf,&sz,Wctxp->_d_tmo))) ) return status; NNTP_LOGT(Wctxp,LOGD,"Got answer from Suck '%.*s'.",sz,Wctxp->_t_buf); if ( !lib$cvt_dtb(3,Wctxp->_t_buf,&rc) ) { NNTP_LOGT(Wctxp,LOGS,"Can't get nntp status code"); return SS$_BADPARAM; } if ( (rc != 215) && (rc != 231) ) return SS$_CANCEL; while (1) { sz = BUFPSZ - 1; if ( !(1 & (status = net_read_line (Wctxp->_a_chan,Wctxp->_t_buf,&sz,Wctxp->_d_tmo))) ) break; Wctxp->_t_buf[sz] = 0; if ( (sz == 1) && (Wctxp->_t_buf[0] == '.') ) break; if ( !(cp0 = memchr(Wctxp->_t_buf,' ',sz)) ) continue; memset(Wctxp->grec._t_name,0,sizeof(Wctxp->grec._t_name)); memcpy(Wctxp->grec._t_name,Wctxp->_t_buf,cp0 - Wctxp->_t_buf); Wctxp->grec._b_len = cp0 - Wctxp->_t_buf; if ( Wctxp->grec._b_len > GRPNAME$_LEN ) continue; /* ** Get posting flag ('y'|'n'|'m') */ if ( 3 != sscanf (cp0,"%u %u %c",&Wctxp->grec._l_last, &Wctxp->grec._l_first,&Wctxp->grec._c_postflag) ) continue; tolower(Wctxp->grec._c_postflag); /* ** Check for GrpME rules */ NNTP_LOGT(Wctxp,LOGD,"Group '%.*s':check for GrpME.", Wctxp->grec._b_len,Wctxp->grec._t_name); if ( !strmatch (nntp_conf._s_grpme_list.dsc$a_pointer, nntp_conf._s_grpme_list.dsc$w_length, Wctxp->grec._t_name,Wctxp->grec._b_len) ) continue; /* ** Check for duplicate and create newsgroup if it not found. */ status = GrpDBget (&Wctxp->_s_grprab,&Wctxp->grec,0,0,0); if ( status == RMS$_RNF ) { NNTP_LOGT(Wctxp,LOGW,"Creating group '%.*s'.",Wctxp->grec._b_len, Wctxp->grec._t_name); Wctxp->grec._l_first = Wctxp->grec._l_last = 0; Wctxp->grec._c_suckflag = 0; time(&Wctxp->grec._l_datecr); Wctxp->grec._l_dateup = 0; status = GrpDBput (&Wctxp->_s_grprab,&Wctxp->grec); } if (!$VMS_STATUS_SUCCESS(status)) break; } if (!$VMS_STATUS_SUCCESS(status)) NNTP_LOGT(Wctxp,LOGS,"End of sucking newsgroups list,(status = %d).",status); else { NNTP_LOGT(Wctxp,LOGI,"End of sucking newsgroups list."); status = FeedSuckDBput(&Wctxp->_s_feedsuckrab,srec); } return status; }