/* Copyright (c) 1996-2006, Ruslan R. Laishev (@RRL) */ #include "nntp.h" extern int exit_flag; char IHAVE[] = "IHAVE %.*s"; char POST[] = "POST"; int nntp_feed_group (WCTX *,FSREC *,ulong,char *,ushort,int); int nntp_feed_exclude(MSGREC *,ushort,char *,ushort); /* *-------------------------------------------------------------------------------- */ int nntp_feed ( WCTX *Wctxp, struct FSArg *AL ) { long status; FSREC frec; int rewindf = 0; ulong lmsg; ushort sz,rc; int PostingType; char MODE_READER[] = "MODE READER"; char QUIT[] = "QUIT"; /* ** Get first line from remote NNTP host */ 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; /* ** For posting type as 'POST' send MODE READER */ if ( (AL+FEED$K_POSTING)->_w_len ) PostingType = tolower(*(AL+FEED$K_POSTING)->_a_arg) == 'p'?1:0; if ( PostingType ) { 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; } /* ** */ while ( !exit_flag && (1 & (status=GrpDBget(&Wctxp->wctx$r_grab,&Wctxp->wctx$r_grec,0,rewindf++,1))) ) { /* ** Get first group with Posting allowed and matched in list */ if ( ('n' == Wctxp->wctx$r_grec.grp$b_post) || !Wctxp->wctx$r_grec.grp$b_suck || (!strmatch ((AL+FEED$K_GROUPS)->_a_arg,(AL+FEED$K_GROUPS)->_w_len, Wctxp->wctx$r_grec.grp$t_grpname,Wctxp->wctx$r_grec.grp$b_grplen)) || (!Wctxp->wctx$r_grec.grp$l_last) ) continue; /* ** Get info in Feed database about last feeded message */ lmsg = Wctxp->wctx$r_grec.grp$l_first; NNTP_LOGT(Wctxp,LOG$K_WAR,"'%.*s' first ARTICLE #%u.", Wctxp->wctx$r_grec.grp$b_grplen,Wctxp->wctx$r_grec.grp$t_grpname,lmsg); MDString (FEED$K_TYPE,(AL+FEED$K_NAME)->_a_arg,(AL+FEED$K_NAME)->_w_len, Wctxp->wctx$r_grec.grp$t_grpname,Wctxp->wctx$r_grec.grp$b_grplen,frec.fs$t_hash); frec.fs$l_last = 1; status = FeedSuckDBget(&Wctxp->wctx$r_fsrab,&frec); if ( $VMS_STATUS_SUCCESS(status) ) { NNTP_LOGT(Wctxp,LOG$K_WAR,"'%.*s' last feeded ARTICLE #%u.", Wctxp->wctx$r_grec.grp$b_grplen,Wctxp->wctx$r_grec.grp$t_grpname,frec.fs$l_last); if ( Wctxp->wctx$r_grec.grp$l_last == frec.fs$l_last ) continue; lmsg = min (Wctxp->wctx$r_grec.grp$l_last,frec.fs$l_last); } /* * Start FeedUp for one group */ if ( !(1 & (status = nntp_feed_group (Wctxp,&frec, lmsg,(AL+FEED$K_EXCLUDE)->_a_arg,(AL+FEED$K_EXCLUDE)->_w_len, PostingType))) ) NNTP_LOGT(Wctxp,LOG$K_ERR,"nntp_feed_group,(status = %d)",status); /* * Check and update feed record */ if ( lmsg == frec.fs$l_last ) continue; NNTP_LOGT(Wctxp,LOG$K_DBG,"Update FeedDB for %.*s,last ARTICLE #%u", Wctxp->wctx$r_grec.grp$b_grplen,Wctxp->wctx$r_grec.grp$t_grpname,frec.fs$l_last); if ( !(1 & (status = FeedSuckDBput(&Wctxp->wctx$r_fsrab,&frec))) ) { NNTP_LOGT(Wctxp,LOG$K_SER,"Update feed record,(status = %d).",status); break; } } net_send_line (Wctxp->wctx$a_chan,ASCIC(QUIT)); return status == RMS$_EOF?SS$_NORMAL:status; } /* *-------------------------------------------------------------------------------- */ int nntp_feed_group ( WCTX *Wctxp, FSREC *frec, ulong lmsg, char *ExcList, ushort ExcListLen, int PostingType ) { unsigned status,rc,cmsg, Posted = 0,Rejected = 0,Skiped = 0; unsigned short sz,szA,szM; lmsg++; NNTP_LOGT(Wctxp,LOG$K_WAR,"Start uploading of '%.*s',ARTICLE #%u.", Wctxp->wctx$r_grec.grp$b_grplen,Wctxp->wctx$r_grec.grp$t_grpname,lmsg); status = SS$_NORMAL; for (lmsg;!exit_flag && (lmsg <= Wctxp->wctx$r_grec.grp$l_last); frec->fs$l_last = lmsg,lmsg++) { /* * Get ARTICLE # in the buffer */ if ( !(1 & (status = MsgDBget_byNum(&Wctxp->wctx$r_mrab,&Wctxp->wctx$r_grec,lmsg,&Wctxp->wctx$r_mrec,&szM))) ) { NNTP_LOGT(Wctxp,LOG$K_ERR,"'%.*s'->ARTICLE #%u-Can't be retrived,(status = %d).", Wctxp->wctx$r_grec.grp$b_grplen,Wctxp->wctx$r_grec.grp$t_grpname,lmsg,status); continue; } NNTP_LOGT(Wctxp,LOG$K_DBG,"ARTICLE #%u %.*s (%d bytes)-Retrived.",lmsg, Wctxp->wctx$r_mrec.msg$w_gidlen,Wctxp->wctx$r_mrec.msg$t_gid, szM); /* ** Additional checking for legal 'Path:' field */ if ( STR$_MATCH == (status = nntp_feed_exclude(&Wctxp->wctx$r_mrec,szM,ExcList,ExcListLen)) ) { Skiped++; NNTP_LOGT(Wctxp,LOG$K_DBG,"ARTICLE #%u %.*s-Skiped by exclude 'Path:' rulez.",lmsg, Wctxp->wctx$r_mrec.msg$w_gidlen,Wctxp->wctx$r_mrec.msg$t_gid); continue; } /* ** */ if ( PostingType ) {sz = sprintf(Wctxp->wctx$t_buf,POST);} else {sz = sprintf(Wctxp->wctx$t_buf,IHAVE, Wctxp->wctx$r_mrec.msg$w_midlen, Wctxp->wctx$r_mrec.msg$t_mid);} NNTP_LOGT(Wctxp,LOG$K_DBG,"Send '%.*s'.",sz,Wctxp->wctx$t_buf); if ( !(1 & (status = net_send_line (Wctxp->wctx$a_chan,Wctxp->wctx$t_buf,sz))) ) break; /* * Get response from feeder */ 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))) ) break; if ( PostingType ) NNTP_LOGT(Wctxp,LOG$K_WAR,"Answer from a feeder to 'POST':'%.*s'.", sz,Wctxp->wctx$t_buf); else NNTP_LOGT(Wctxp,LOG$K_WAR,"Answer from a feeder to 'IHAVE':'%.*s'.", sz,Wctxp->wctx$t_buf); /* * Get status code & dispatch */ if ( !lib$cvt_dtb(3,Wctxp->wctx$t_buf,&rc) ) { NNTP_LOGT(Wctxp,LOG$K_SER,"Can't get nntp status code"); break; } if ( rc == 435 ) { NNTP_LOGT(Wctxp,LOG$K_ERR,"ARTICLE #%u %.*s is rejected (nntp_status = %d).",lmsg, Wctxp->wctx$r_mrec.msg$w_gidlen, Wctxp->wctx$r_mrec.msg$t_gid,rc); Rejected++; continue; } if ( rc >= 400 ) { NNTP_LOGT(Wctxp,LOG$K_ERR,"ARTICLE #%u %.*s was not sent (nntp_status = %d).",lmsg, Wctxp->wctx$r_mrec.msg$w_gidlen, Wctxp->wctx$r_mrec.msg$t_gid,rc); break; } /* * If nntp status = 335 or 340 - 'Ok',send article to feeder host */ NNTP_LOGT(Wctxp,LOG$K_DBG,"Send ARTICLE #%u %.*s.",lmsg, Wctxp->wctx$r_mrec.msg$w_gidlen, Wctxp->wctx$r_mrec.msg$t_gid); if ( !(1 & (status = net_send_mline (Wctxp->wctx$a_chan,Wctxp->wctx$r_mrec.msg$t_body,szM))) ) { NNTP_LOGT(Wctxp,LOG$K_ERR,"error sending an ARTICLE #%u %.*s,(status = %d).",lmsg, Wctxp->wctx$r_mrec.msg$w_gidlen, Wctxp->wctx$r_mrec.msg$t_gid,status); break; } 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))) ) break; NNTP_LOGT(Wctxp,LOG$K_DBG,"ARTICLE sent with status:'%.*s'.",sz,Wctxp->wctx$t_buf); /* * Get status code & check it */ if ( !lib$cvt_dtb(3,Wctxp->wctx$t_buf,&rc) ) { NNTP_LOGT(Wctxp,LOG$K_SER,"Can't get nntp status code"); break; } if ( (rc == 235) || (rc == 240) ) { Posted++; continue; } if ( (rc == 435 ) || ( rc == 437) || ( rc == 441) ) { NNTP_LOGT(Wctxp,LOG$K_ERR,"'%.*s',ARTICLE #%u,(nntp_status = %d).", Wctxp->wctx$r_grec.grp$b_grplen,Wctxp->wctx$r_grec.grp$t_grpname,lmsg,rc); Rejected++; continue; } NNTP_LOGT(Wctxp,LOG$K_ERR,"'%.*s',ARTICLE #%u,(nntp_status = %d).", Wctxp->wctx$r_grec.grp$b_grplen,Wctxp->wctx$r_grec.grp$t_grpname,lmsg,rc); break; } NNTP_LOGT(Wctxp,LOG$K_WAR,"'%.*s' statistic:Post:%lu,Rej:%lu,Skip:%u.", Wctxp->wctx$r_grec.grp$b_grplen,Wctxp->wctx$r_grec.grp$t_grpname, Posted,Rejected,Skiped); return status; } /* *-------------------------------------------------------------------------------- */ int nntp_feed_exclude ( MSGREC *wctx$r_mrec, ushort msgreclen, char *exclist, ushort exclistlen ) { struct dsc$descriptor dsc_tmp0; $DESCRIPTOR(dsc_CRLFCRLF,"\r\n\r\n"); $DESCRIPTOR(dsc_CRLF,"\r\n"); $DESCRIPTOR(dsc_path,"Path: "); ushort pos,elem; char *cp; /* ** Extract RFC-822's header from message */ INIT_SDESC(dsc_tmp0,msgreclen,wctx$r_mrec->msg$t_body); if ( !(dsc_tmp0.dsc$w_length = lib$index(&dsc_tmp0,&dsc_CRLFCRLF)) ) return SS$_ABORT; dsc_tmp0.dsc$w_length--; /* ** Extract 'Path:' field */ if ( !(pos = lib$index(&dsc_tmp0,&dsc_path)) ) return STR$_NOMATCH; pos--; dsc_tmp0.dsc$a_pointer += (pos + dsc_path.dsc$w_length); dsc_tmp0.dsc$w_length -= (pos + dsc_path.dsc$w_length); if ( lib$index(&dsc_tmp0,&dsc_CRLF) ) dsc_tmp0.dsc$w_length -= 2; /* ** */ for (elem = 0; pos=strelem(&dsc_tmp0,'!',elem,&cp); elem++) if ( strmatch (exclist,exclistlen,cp,pos) ) return STR$_MATCH; return STR$_NOMATCH; }