/* Copyright (c) 1996-2001, Ruslan R. Laishev (@RRL) */ #include "nntp.h" char IHAVE[] = "IHAVE %.*s"; char POST[] = "POST"; int nntp_feed_group (wctx_t *,fsrec_t *,ulong,char *,ushort,int); int nntp_feed_exclude(msgrec_t *,ushort,char *,ushort); /* *-------------------------------------------------------------------------------- */ int nntp_feed ( wctx_t *Wctxp, struct FSArg *AL ) { long status; fsrec_t 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 = BUFPSZ; if ( !(1 & (status = net_read_line (Wctxp->_a_chan,Wctxp->_t_buf,&sz,Wctxp->_d_tmo))) ) return status; /* ** For posting type as 'POST' send MODE READER */ if ( (AL+FEED$POSTING)->_w_len ) PostingType = tolower(*(AL+FEED$POSTING)->_a_arg) == 'p'?1:0; if ( PostingType ) { 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; } /* ** */ while ( 1 & (status=GrpDBget(&Wctxp->_s_grprab,&Wctxp->grec,0,rewindf++,1)) ) { /* ** Get first group with Posting allowed and matched in list */ if ( ('n' == Wctxp->grec._c_postflag) || !Wctxp->grec._c_suckflag || (!strmatch ((AL+FEED$GROUPS)->_a_arg,(AL+FEED$GROUPS)->_w_len, Wctxp->grec._t_name,Wctxp->grec._b_len)) || (!Wctxp->grec._l_last) ) continue; /* ** Get info in Feed database about last feeded message */ lmsg = Wctxp->grec._l_first; NNTP_LOGT(Wctxp,LOGW,"'%.*s' first ARTICLE #%u.", Wctxp->grec._b_len,Wctxp->grec._t_name,lmsg); MDString (FEED$TYPE,(AL+FEED$NAME)->_a_arg,(AL+FEED$NAME)->_w_len, Wctxp->grec._t_name,Wctxp->grec._b_len,frec._t_hash); frec._l_last = 1; status = FeedSuckDBget(&Wctxp->_s_feedsuckrab,&frec); if ( $VMS_STATUS_SUCCESS(status) ) { NNTP_LOGT(Wctxp,LOGW,"'%.*s' last feeded ARTICLE #%u.", Wctxp->grec._b_len,Wctxp->grec._t_name,frec._l_last); if ( Wctxp->grec._l_last == frec._l_last ) continue; lmsg = min (Wctxp->grec._l_last,frec._l_last); } /* * Start FeedUp for one group */ if ( !(1 & (status = nntp_feed_group (Wctxp,&frec, lmsg,(AL+FEED$EXCLUDE)->_a_arg,(AL+FEED$EXCLUDE)->_w_len, PostingType))) ) NNTP_LOGT(Wctxp,LOGE,"nntp_feed_group,(status = %d)",status); /* * Check and update feed record */ if ( lmsg == frec._l_last ) continue; NNTP_LOGT(Wctxp,LOGD,"Update FeedDB for %.*s,last ARTICLE #%u", Wctxp->grec._b_len,Wctxp->grec._t_name,frec._l_last); if ( !(1 & (status = FeedSuckDBput(&Wctxp->_s_feedsuckrab,&frec))) ) { NNTP_LOGT(Wctxp,LOGS,"Update feed record,(status = %d).",status); break; } } net_send_line (Wctxp->_a_chan,ASCIC(QUIT)); return status == RMS$_EOF?SS$_NORMAL:status; } /* *-------------------------------------------------------------------------------- */ int nntp_feed_group ( wctx_t *Wctxp, fsrec_t *frec, ulong lmsg, char *ExcList, ushort ExcListLen, int PostingType ) { long status; ulong rc,cmsg; ushort sz,szA,szM; ulong Posted = 0,Rejected = 0,Skiped = 0; char lkey [MSGGRP$_LEN]; ushort lkeysz = 0; lmsg++; NNTP_LOGT(Wctxp,LOGW,"Start uploading of '%.*s',ARTICLE #%u.", Wctxp->grec._b_len,Wctxp->grec._t_name,lmsg); status = SS$_NORMAL; for (lmsg;lmsg <= Wctxp->grec._l_last;frec->_l_last = lmsg,lmsg++) { /* * Get ARTICLE # in the buffer */ if ( !(1 & (status = MsgDBget_byNum(&Wctxp->_s_msgrab,&Wctxp->grec,lmsg,&Wctxp->mrec,&szM))) ) { NNTP_LOGT(Wctxp,LOGE,"'%.*s'->ARTICLE #%u-Can't be retrived,(status = %d).", Wctxp->grec._b_len,Wctxp->grec._t_name,lmsg,status); continue; } NNTP_LOGT(Wctxp,LOGD,"ARTICLE #%u %.*s (%d bytes)-Retrived.",lmsg, Wctxp->mrec._w_gidlen,Wctxp->mrec._t_gid, szM); /* ** Additional checking for legal 'Path:' field */ if ( STR$_MATCH == (status = nntp_feed_exclude(&Wctxp->mrec,szM,ExcList,ExcListLen)) ) { Skiped++; NNTP_LOGT(Wctxp,LOGD,"ARTICLE #%u %.*s-Skiped by exclude 'Path:' rulez.",lmsg, Wctxp->mrec._w_gidlen,Wctxp->mrec._t_gid); continue; } /* ** */ if ( PostingType ) {sz = sprintf(Wctxp->_t_buf,POST);} else {sz = sprintf(Wctxp->_t_buf,IHAVE, Wctxp->mrec._w_midlen, Wctxp->mrec._t_mid);} NNTP_LOGT(Wctxp,LOGD,"Send '%.*s'.",sz,Wctxp->_t_buf); if ( !(1 & (status = net_send_line (Wctxp->_a_chan,Wctxp->_t_buf,sz))) ) break; /* * Get response from feeder */ sz = BUFPSZ; if ( !(1 & (status = net_read_line (Wctxp->_a_chan,Wctxp->_t_buf,&sz,Wctxp->_d_tmo))) ) break; if ( PostingType ) NNTP_LOGT(Wctxp,LOGW,"Answer from a feeder to 'POST':'%.*s'.", sz,Wctxp->_t_buf); else NNTP_LOGT(Wctxp,LOGW,"Answer from a feeder to 'IHAVE':'%.*s'.", sz,Wctxp->_t_buf); /* * Get status code & dispatch */ if ( !lib$cvt_dtb(3,Wctxp->_t_buf,&rc) ) { NNTP_LOGT(Wctxp,LOGS,"Can't get nntp status code"); break; } if ( rc == 435 ) { NNTP_LOGT(Wctxp,LOGE,"ARTICLE #%u %.*s is rejected (nntp_status = %d).",lmsg, Wctxp->mrec._w_gidlen, Wctxp->mrec._t_gid,rc); Rejected++; continue; } if ( rc >= 400 ) { NNTP_LOGT(Wctxp,LOGE,"ARTICLE #%u %.*s was not sent (nntp_status = %d).",lmsg, Wctxp->mrec._w_gidlen, Wctxp->mrec._t_gid,rc); break; } /* * If nntp status = 335 or 340 - 'Ok',send article to feeder host */ NNTP_LOGT(Wctxp,LOGD,"Send ARTICLE #%u %.*s.",lmsg, Wctxp->mrec._w_gidlen, Wctxp->mrec._t_gid); if ( !(1 & (status = net_send_mline (Wctxp->_a_chan,Wctxp->mrec._t_body,szM))) ) { NNTP_LOGT(Wctxp,LOGE,"error sending an ARTICLE #%u %.*s,(status = %d).",lmsg, Wctxp->mrec._w_gidlen, Wctxp->mrec._t_gid,status); break; } sz = BUFPSZ; if ( !(1 & (status = net_read_line (Wctxp->_a_chan,Wctxp->_t_buf,&sz,Wctxp->_d_tmo))) ) break; NNTP_LOGT(Wctxp,LOGD,"ARTICLE sent with status:'%.*s'.",sz,Wctxp->_t_buf); /* * Get status code & check it */ if ( !lib$cvt_dtb(3,Wctxp->_t_buf,&rc) ) { NNTP_LOGT(Wctxp,LOGS,"Can't get nntp status code"); break; } if ( (rc == 235) || (rc == 240) ) { Posted++; continue; } if ( (rc == 435 ) || ( rc == 437) || ( rc == 441) ) { NNTP_LOGT(Wctxp,LOGE,"'%.*s',ARTICLE #%u,(nntp_status = %d).", Wctxp->grec._b_len,Wctxp->grec._t_name,lmsg,rc); Rejected++; continue; } NNTP_LOGT(Wctxp,LOGE,"'%.*s',ARTICLE #%u,(nntp_status = %d).", Wctxp->grec._b_len,Wctxp->grec._t_name,lmsg,rc); break; } NNTP_LOGT(Wctxp,LOGW,"'%.*s' statistic:Post:%lu,Rej:%lu,Skip:%u.", Wctxp->grec._b_len,Wctxp->grec._t_name, Posted,Rejected,Skiped); return status; } /* *-------------------------------------------------------------------------------- */ int nntp_feed_exclude ( msgrec_t *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,mrec->_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; }