/* Copyright (c) 1996-2006, Ruslan R. Laishev (@RRL) */ #include "nntp.h" extern int exit_flag; char NEXT[] = "NEXT"; char ARTICLE[] = "ARTICLE %u"; char MODE_READER[] = "MODE READER"; char QUIT[] = "QUIT"; char GROUP [] = "GROUP %.*s"; int nntp_suck_group (WCTX *,FSREC *,ulong); int nntp_suck_list (WCTX *,FSREC *,char *,ushort); /* *-------------------------------------------------------------------------------- */ int nntp_suck_msg ( WCTX *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->wctx$a_chan,buf0,sz0,&Wctxp->wctx$q_tmo))) ) return status; /* ** Get whole ARTICLE */ return net_read_mline (Wctxp->wctx$a_chan,buf1,sz1,&Wctxp->wctx$q_tmo); } /* *-------------------------------------------------------------------------------- */ int nntp_suck ( WCTX *Wctxp, struct FSArg *AL ) { long status; FSREC srec; int rewindf = 0; ushort rc,sz; ulong nmsg,fmsg,lmsg; /* ** For Dumb INN and other *ix... send MODE READER */ sz = sizeof(Wctxp->wctx$t_buf); if ( !(1 & (status = net_read_line (Wctxp->wctx$a_chan,Wctxp->wctx$t_buf,&sz,&Wctxp->wctx$q_tmo))) ) return status; if ( !(1 & (status = net_send_line (Wctxp->wctx$a_chan,ASCIC(MODE_READER)))) ) return status; sz = sizeof(Wctxp->wctx$t_buf); if ( !(1 & (status = net_read_line (Wctxp->wctx$a_chan,Wctxp->wctx$t_buf,&sz,&Wctxp->wctx$q_tmo))) ) return status; /* ** */ nntp_suck_list (Wctxp,&srec,(AL+SUCK$K_NAME)->_a_arg,(AL+SUCK$K_NAME)->_w_len); /* ** */ while ( !exit_flag && (1 & (status=GrpDBget (&Wctxp->wctx$r_grab,&Wctxp->wctx$r_grec,0,rewindf++,1))) ) { if ( (!Wctxp->wctx$r_grec.grp$b_suck) || (!strmatch((AL+SUCK$K_GROUPS)->_a_arg,(AL+SUCK$K_GROUPS)->_w_len, Wctxp->wctx$r_grec.grp$t_grpname,Wctxp->wctx$r_grec.grp$b_grplen)) ) continue; NNTP_LOGT(Wctxp,LOG$K_DBG,"Group '%.*s'-Has been cached.", Wctxp->wctx$r_grec.grp$b_grplen,Wctxp->wctx$r_grec.grp$t_grpname); sz = sprintf(Wctxp->wctx$t_buf,GROUP,Wctxp->wctx$r_grec.grp$b_grplen,Wctxp->wctx$r_grec.grp$t_grpname); if ( !(1 & (status = net_send_line (Wctxp->wctx$a_chan,Wctxp->wctx$t_buf,sz))) ) return status; NNTP_LOGT(Wctxp,LOG$K_DBG,"Request info-'%.*s'.", Wctxp->wctx$r_grec.grp$b_grplen,Wctxp->wctx$r_grec.grp$t_grpname); sz = sizeof(Wctxp->wctx$t_buf)-1; if ( !(1 & (status = net_read_line (Wctxp->wctx$a_chan,Wctxp->wctx$t_buf,&sz,&Wctxp->wctx$q_tmo))) ) { NNTP_LOGT(Wctxp,LOG$K_ERR,"Request info-%.*s.",Wctxp->wctx$r_grec.grp$b_grplen,Wctxp->wctx$r_grec.grp$t_grpname); return status; } NNTP_LOGT(Wctxp,LOG$K_WAR,"Group info-'%.*s'",sz,Wctxp->wctx$t_buf); Wctxp->wctx$t_buf[sz] = 0; sscanf(Wctxp->wctx$t_buf,"%u %u %u %u",&rc,&nmsg,&fmsg,&lmsg); if ( rc != 211) continue; /* * */ MDString (SUCK$K_TYPE,(AL+SUCK$K_NAME)->_a_arg,(AL+SUCK$K_NAME)->_w_len, Wctxp->wctx$r_grec.grp$t_grpname,Wctxp->wctx$r_grec.grp$b_grplen,srec.fs$t_hash); status = FeedSuckDBget(&Wctxp->wctx$r_fsrab,&srec); if ( !$VMS_STATUS_SUCCESS(status) && status != RMS$_RNF ) NNTP_LOGT(Wctxp,LOG$K_FAT,"nntp_suck/FeedSuckDBget,(status = %d)",status); else if ( $VMS_STATUS_SUCCESS(status) ) { NNTP_LOGT(Wctxp,LOG$K_WAR,"'%.*s' last gotten ARTICLE #%u.", Wctxp->wctx$r_grec.grp$b_grplen,Wctxp->wctx$r_grec.grp$t_grpname,srec.fs$l_last); if ( lmsg == srec.fs$l_last) continue; fmsg = max(min(lmsg,srec.fs$l_last),fmsg); } if ( !(1 & (status = nntp_suck_group ( Wctxp,&srec,fmsg))) ) NNTP_LOGT(Wctxp,LOG$K_ERR,"nntp_suck_group,(status = %d)",status); /* ** Check and update suck record */ if ( fmsg == srec.fs$l_last ) continue; NNTP_LOGT(Wctxp,LOG$K_WAR,"Update SuckDB for %.*s,last gotten ARTICLE #%u", Wctxp->wctx$r_grec.grp$b_grplen,Wctxp->wctx$r_grec.grp$t_grpname,srec.fs$l_last); if ( !(1 & (status = FeedSuckDBput(&Wctxp->wctx$r_fsrab,&srec))) ) { NNTP_LOGT(Wctxp,LOG$K_SER,"Update suck record,(status = %d).",status); break; } } net_send_line (Wctxp->wctx$a_chan,ASCIC(QUIT)); return status == RMS$_EOF?SS$_NORMAL:status; } /* *-------------------------------------------------------------------------------- */ int nntp_suck_group ( WCTX *Wctxp, FSREC *srec, ulong cmsg ) { long status; ushort rc,sz,szA,szM; unsigned char *cp0,*cp1,*bufp = &Wctxp->wctx$t_buf, STAT[] = "STAT %u"; ulong Sucked = 0,Duplicated = 0,Skiped = 0; /* ** Send 'STAT' command for get information about article */ sz = sprintf(Wctxp->wctx$t_buf,STAT,cmsg); if ( !(1 & (status = net_send_line (Wctxp->wctx$a_chan,Wctxp->wctx$t_buf,sz))) ) return status; sz = sizeof(Wctxp->wctx$t_buf); if ( !(1 & (status = net_read_line (Wctxp->wctx$a_chan,Wctxp->wctx$t_buf,&sz,&Wctxp->wctx$q_tmo))) ) return status; NNTP_LOGT(Wctxp,LOG$K_DBG,"Answer to 'STAT %u' from Suck '%.*s'.", cmsg,sz,Wctxp->wctx$t_buf); NNTP_LOGT(Wctxp,LOG$K_DBG,"Start download '%.*s' at ARTICLE #%u.", Wctxp->wctx$r_grec.grp$b_grplen,Wctxp->wctx$r_grec.grp$t_grpname,cmsg); while ( !exit_flag ) { srec->fs$l_last = cmsg; cmsg++; /* ** Go to next available message ID by 'NEXT' command */ if ( !(1 & (status = net_send_line (Wctxp->wctx$a_chan,ASCIC(NEXT)))) ) break; sz = sizeof(Wctxp->wctx$t_buf)-1; if ( !(1 & (status = net_read_line (Wctxp->wctx$a_chan,Wctxp->wctx$t_buf,&sz,&Wctxp->wctx$q_tmo))) ) break; NNTP_LOGT(Wctxp,LOG$K_DBG,"Answer to NEXT from Suck '%.*s'.", sz,Wctxp->wctx$t_buf); Wctxp->wctx$t_buf[sz] = 0; /* ** Check return code */ if ( 2 != sscanf(Wctxp->wctx$t_buf,"%u %u",&rc,&cmsg) ) { NNTP_LOGT(Wctxp,LOG$K_DBG,"Can't get rc cmsg"); break; }; if ( rc > 400 ) break; if ( rc != 223 ) continue; /* ** Extract Message-ID field and check it length */ if ( (!(cp0 = memchr(bufp,'<',sz))) || (!(cp1 = memchr(cp0,'>',sz-(cp0 - bufp)))) ) break; sz = (++cp1) - cp0; if ( sz > MSGID$_LEN ) { NNTP_LOGT(Wctxp,LOG$K_ERR,"Message-ID %.*s - is too long (%u bytes)-Skip.", sz,cp0,sz); Skiped++; continue; } NNTP_LOGT(Wctxp,LOG$K_DBG,"ARTICLE #%u %.*s:check for dup.",cmsg,sz,cp0); /* ** Check for duplicate */ if ( (1 & (status = MsgDBfind_byId(&Wctxp->wctx$r_mrab,cp0,sz))) ) { NNTP_LOGT(Wctxp,LOG$K_WAR,"ARTICLE #%u %.*s:Found in database.",cmsg,sz,cp0); Duplicated++; continue; } if ( !$VMS_STATUS_SUCCESS(status) && (status != RMS$_RNF) ) break; NNTP_LOGT(Wctxp,LOG$K_DBG,"ARTICLE #%u %.*s-Retrieving.",cmsg,sz,cp0); /* ** Get message with Message-ID */ sz = sprintf(Wctxp->wctx$t_buf,ARTICLE,cmsg); if ( !(1 & (status = net_send_line (Wctxp->wctx$a_chan,Wctxp->wctx$t_buf,sz))) ) break; szA = sizeof(Wctxp->wctx$t_buf); szM = sizeof(Wctxp->wctx$r_mrec.msg$t_body); if ( !(1 & (status = nntp_suck_msg (Wctxp, Wctxp->wctx$t_buf,&szA, Wctxp->wctx$r_mrec.msg$t_body,&szM))) ) { NNTP_LOGT(Wctxp,LOG$K_ERR,"ARTICLE #%u:size is not valid-Skip.", srec->fs$l_last); Skiped++; continue; } if ( (!(cp0 = memchr(Wctxp->wctx$t_buf,'<',szA))) || (!(cp1 = memchr(cp0,'>',szA-(cp0-bufp)))) ) { NNTP_LOGT(Wctxp,LOG$K_ERR,"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,LOG$K_WAR,"'%.*s' statistic:Suck:%lu,Dup:%lu,Skip:%lu.", Wctxp->wctx$r_grec.grp$b_grplen,Wctxp->wctx$r_grec.grp$t_grpname,Sucked,Duplicated,Skiped); if (!$VMS_STATUS_SUCCESS(status)) NNTP_LOGT(Wctxp,LOG$K_SER,"End of sucking newsgroups,(status = %d).",status); else NNTP_LOGT(Wctxp,LOG$K_WAR,"End of sucking newsgroups."); return status; } /* *-------------------------------------------------------------------------------- */ int nntp_suck_list ( WCTX *Wctxp, FSREC *srec, char *host, ushort hostlen ) { long status; unsigned char *cp0,buf[128],*bufp = &Wctxp->wctx$t_buf, NEWGROUPS[] = "NEWGROUPS %.*s", LIST[] = "LIST"; ushort rc,sz = sizeof(buf); /* ** */ NNTP_LOGT(Wctxp,LOG$K_INF,"Update newsgroups list."); MDString (0,host,hostlen,"",0,srec->fs$t_hash); status = FeedSuckDBget(&Wctxp->wctx$r_fsrab,srec); if ( !$VMS_STATUS_SUCCESS(status) && status != RMS$_RNF ) return status; if ( status & 1 ) { cvt_vms_to_nntp(srec->fs$l_last,buf,&sz); sz = sprintf(Wctxp->wctx$t_buf,NEWGROUPS,sz,buf); net_send_line (Wctxp->wctx$a_chan,Wctxp->wctx$t_buf,sz); } else { net_send_line (Wctxp->wctx$a_chan,ASCIC(LIST)); } time(&srec->fs$l_last); sz = sizeof(Wctxp->wctx$t_buf); if ( !(1 & (status = net_read_line (Wctxp->wctx$a_chan,Wctxp->wctx$t_buf,&sz,&Wctxp->wctx$q_tmo))) ) return status; NNTP_LOGT(Wctxp,LOG$K_DBG,"Got answer from Suck '%.*s'.",sz,Wctxp->wctx$t_buf); if ( !lib$cvt_dtb(3,Wctxp->wctx$t_buf,&rc) ) { NNTP_LOGT(Wctxp,LOG$K_SER,"Can't get nntp status code"); return SS$_BADPARAM; } if ( (rc != 215) && (rc != 231) ) return SS$_CANCEL; while ( !exit_flag ) { sz = sizeof(Wctxp->wctx$t_buf) - 1; if ( !(1 & (status = net_read_line (Wctxp->wctx$a_chan,Wctxp->wctx$t_buf,&sz,&Wctxp->wctx$q_tmo))) ) break; Wctxp->wctx$t_buf[sz] = 0; if ( (sz == 1) && (Wctxp->wctx$t_buf[0] == '.') ) break; if ( !(cp0 = memchr(Wctxp->wctx$t_buf,' ',sz)) ) continue; memset(Wctxp->wctx$r_grec.grp$t_grpname,0,sizeof(Wctxp->wctx$r_grec.grp$t_grpname)); memcpy(Wctxp->wctx$r_grec.grp$t_grpname,Wctxp->wctx$t_buf,cp0 - bufp); Wctxp->wctx$r_grec.grp$b_grplen = cp0 - bufp; if ( Wctxp->wctx$r_grec.grp$b_grplen > sizeof(Wctxp->wctx$r_grec.grp$t_grpname) ) continue; /* ** Get posting flag ('y'|'n'|'m') */ if ( 3 != sscanf (cp0,"%u %u %c",&Wctxp->wctx$r_grec.grp$l_last, &Wctxp->wctx$r_grec.grp$l_first,&Wctxp->wctx$r_grec.grp$b_post) ) continue; tolower(Wctxp->wctx$r_grec.grp$b_post); /* ** Check for GrpME rules */ NNTP_LOGT(Wctxp,LOG$K_DBG,"Group '%.*s':check for GrpME.", Wctxp->wctx$r_grec.grp$b_grplen,Wctxp->wctx$r_grec.grp$t_grpname); if ( !strmatch (nntp_conf._s_grpme_list.dsc$a_pointer, nntp_conf._s_grpme_list.dsc$w_length, Wctxp->wctx$r_grec.grp$t_grpname,Wctxp->wctx$r_grec.grp$b_grplen) ) continue; /* ** Check for duplicate and create newsgroup if it not found. */ status = GrpDBget (&Wctxp->wctx$r_grab,&Wctxp->wctx$r_grec,0,0,0); if ( status == RMS$_RNF ) { NNTP_LOGT(Wctxp,LOG$K_WAR,"Creating group '%.*s'.",Wctxp->wctx$r_grec.grp$b_grplen, Wctxp->wctx$r_grec.grp$t_grpname); Wctxp->wctx$r_grec.grp$l_first = Wctxp->wctx$r_grec.grp$l_last = 0; Wctxp->wctx$r_grec.grp$b_suck = 0; time(&Wctxp->wctx$r_grec.grp$l_datecr); Wctxp->wctx$r_grec.grp$l_dateup = 0; status = GrpDBput (&Wctxp->wctx$r_grab,&Wctxp->wctx$r_grec); } if (!$VMS_STATUS_SUCCESS(status)) break; } if (!$VMS_STATUS_SUCCESS(status)) NNTP_LOGT(Wctxp,LOG$K_SER,"End of sucking newsgroups list,(status = %d).",status); else { NNTP_LOGT(Wctxp,LOG$K_INF,"End of sucking newsgroups list."); status = FeedSuckDBput(&Wctxp->wctx$r_fsrab,srec); } return status; }