/* Copyright (c) 1996-1998 Ruslan R. Laishev (@RRL) */ #include "nntp.h" struct NNTP_cmd { char *cmd; ushort len; int (*nntp_fun) (wctx_t *,ushort); char *hlp; } NNTP_cmd [] ={ {ASCIC("help"), nntp_help, "Help"}, {ASCIC("quit"), nntp_quit, "Quit"}, {ASCIC("article"), nntp_article, "Article [MessageID | Number]"}, {ASCIC("head"), nntp_head, "Head [MessageID | Number]"}, {ASCIC("body"), nntp_body, "Body [MessageID | Number]"}, {ASCIC("stat"), nntp_stat, "Stat [MessageID | Number]"}, {ASCIC("post"), nntp_post, "Post"}, {ASCIC("ihave"), nntp_ihave, "Ihave MessageID"}, {ASCIC("list"), nntp_list, "List"}, {ASCIC("group"), nntp_group, "Group newsgroupname"}, {ASCIC("newgroups"), nntp_newgroups, "NewGroups date time [GMT] []"}, {ASCIC("slave"), nntp_slave, "Slave"}, {ASCIC("xover"), nntp_xover, "Xover [First - Last]"}, {ASCIC("last"), nntp_last, "Last"}, {ASCIC("next"), nntp_next, "Next"}, {ASCIC("newnews"), nntp_newnews, "NewNews date time [GMT] []"}, /* {ascici("authinfo user"),nntp_auth, "authinfo user username"}, {ascici("authinfo pass"),nntp_auth, "authinfo pass password"}, */ {NULL, 0, NULL, "UnRecognized"} }; /* *-------------------------------------------------------------------------------- */ $DESCRIPTOR(dsc_CRLFCRLF,"\r\n\r\n"); $DESCRIPTOR(dsc_CRLF,"\r\n"); char XOVER_DATA[] = "%s\t%.*s\t%.*s\t%s\t%.*\r\n"; char LIST_OF_CMD[] = "100 list of legal commands follows"; char SLAVE_RESP[] = "202 slave status noted"; char GOODBYE[] = "205 closing connection - goodbye !"; char GRP_SELECT[] = "211 %d %d %d %.*s"; char LIST_GRP[] = "215 list of newsgroups follows"; char ARTI_FOLLOW[] = "220 %.*s Article text follows"; char ARTI_N_FOLW[] = "220 %u %.*s article retrived-head follows"; char HEAD_FOLLOW[] = "221 %.*s Article text follows"; char HEAD_N_FOLLOW[] = "221 %u %.*s article retrived-head follows"; char BODY_FOLLOW[] = "222 %.*s Article text follows"; char BODY_N_FOLLOW[] = "222 %u %.*s article retrived-head follows"; char STAT_FOLLOW[] = "223 %.*s status"; char STAT_N_FOLLOW[] = "223 %u %.*s article status"; char NEXT_ARTI[] = "223 %u %.*s Article retrived; request text."; char DATA_FOLLOWS[] = "224 data follows"; char NEW_NEWS[] = "230 list of news since %.*s follows"; char NEW_GRP[] = "231 list of new newsgroups follows"; char POST_OK[] = "240 article posting OK"; char NEWS_TO_ME[] = "335 News to me!End-.."; char SEND_TO_POST[] = "340 send article to be posted.End-."; char NO_SUCH_GRP[] = "411 no such news group"; char NO_GRP_SEL[] = "412 no newsgroup has been selected"; char NO_ART_SEL[] = "420 no current article has been slected"; char NO_SUCH_ARTS[] = "421 no such next article in this group"; char NO_SUCH_ARTP[] = "422 no such previous article in this group"; char NO_SUCH_ARTN[] = "423 no such article number in this group"; char NO_SUCH_ART[] = "430 no such article found"; char ALRDY_SEEN[] = "435 Already seen that one,where you been"; char ART_REJECT[] = "437 Article rejected - do not try again"; char NO_VALD_SIZE[] = "441 length article is not valid"; char POST_FAIL[] = "442 posting failed"; char BAD_CMD[] = "500 UnRecognized command"; /* *-------------------------------------------------------------------------------- *-------------------------------------------------------------------------------- *-------------------------------------------------------------------------------- */ int nntp_cmd_exec (wctx_t *Wctxp) { long status; struct NNTP_cmd *cmdp; struct dsc$descriptor dsc_cmd,dsc_sts; ushort sz; INIT_SDESC(dsc_cmd,0,NULL); INIT_SDESC(dsc_sts,0,NULL); while (1) { sz = BUFPSZ; status = net_read_line (Wctxp->_a_chan,Wctxp->_t_buf,&sz,Wctxp->_d_tmo); if (!$VMS_STATUS_SUCCESS(status)) break; NNTP_LOGT(Wctxp,LOGD,"%.*s",sz,Wctxp->_t_buf); cmdp = &NNTP_cmd[0]; do { SET_SDESC(dsc_cmd,cmdp->len,cmdp->cmd); SET_SDESC(dsc_sts,min (sz,cmdp->len),Wctxp->_t_buf); if ( !str$case_blind_compare(&dsc_cmd,&dsc_sts) ) break; cmdp++; } while (cmdp->cmd != NULL); if ( !cmdp->nntp_fun ) { net_send_line (Wctxp->_a_chan,ASCIC(BAD_CMD)); continue; } status = cmdp->nntp_fun (Wctxp,sz); if ( !$VMS_STATUS_SUCCESS(status) ) break; } return status; } /* *-------------------------------------------------------------------------------- */ int nntp_help ( wctx_t *Wctxp, ushort sz ) { struct NNTP_cmd *t; net_send_line(Wctxp->_a_chan,ASCIC(LIST_OF_CMD)); for(t = NNTP_cmd;t->cmd;t++) { sz = sprintf(Wctxp->_t_buf,"->%s",t->hlp); net_send_line(Wctxp->_a_chan,Wctxp->_t_buf,sz); } net_send_line(Wctxp->_a_chan,nntp_conf._s_localmgr.dsc$a_pointer, nntp_conf._s_localmgr.dsc$w_length); return net_send_line(Wctxp->_a_chan,".",1); } /* *-------------------------------------------------------------------------------- */ int nntp_quit ( wctx_t *Wctxp, ushort sz ) { net_send_line(Wctxp->_a_chan,ASCIC(GOODBYE)); return SS$_DISCONNECT; } /* *-------------------------------------------------------------------------------- */ int nntp_slave ( wctx_t *Wctxp, ushort sz ) { return net_send_line(Wctxp->_a_chan,ASCIC(SLAVE_RESP)); } /* *-------------------------------------------------------------------------------- */ int nntp_getarticle ( wctx_t *Wctxp, ushort sz, ushort *szA, ushort *flag ) { long status; char *cp0,*cp1; ulong n; /* ** Get by Message ID */ *flag = 0; if( cp0 = memchr(Wctxp->_t_buf,'<',sz) ) { Wctxp->_l_msgnum = 0; if ( !(cp1 = memchr(cp0,'>',sz-(Wctxp->_t_buf-cp0))) ) return net_send_line (Wctxp->_a_chan,ASCIC(BAD_CMD)); if ( (sz = (++cp1) - cp0) > MSGID$_LEN ) return net_send_line (Wctxp->_a_chan,ASCIC(NO_SUCH_ARTN)); status = MsgDBget_byId(&Wctxp->_s_msgrab,cp0,sz,&Wctxp->mrec,szA); if ( !$VMS_STATUS_SUCCESS(status) ) { *szA = 0; net_send_line(Wctxp->_a_chan,ASCIC(NO_SUCH_ART)); return (status == RMS$_RNF?SS$_NORMAL:status); } return SS$_NORMAL; } /* ** Get by Message number */ *flag = 1; if ( !Wctxp->grec._b_len ) return net_send_line(Wctxp->_a_chan,ASCIC(NO_GRP_SEL)); n = Wctxp->_l_msgnum; if( cp0 = memchr(Wctxp->_t_buf,' ',sz) ) { if ( sz - (++cp0 - Wctxp->_t_buf) ) if ( !lib$cvt_dtb(sz - (cp0 - Wctxp->_t_buf),cp0,&n) ) { *szA = 0; return net_send_line(Wctxp->_a_chan,ASCIC(NO_SUCH_ART)); } } status = MsgDBget_byNum(&Wctxp->_s_msgrab,&Wctxp->grec, n,&Wctxp->mrec,szA); if ( !$VMS_STATUS_SUCCESS(status) ) { *szA = 0; net_send_line(Wctxp->_a_chan,ASCIC(NO_SUCH_ART)); return (status == RMS$_RNF?SS$_NORMAL:status); } Wctxp->_l_msgnum = n; return SS$_NORMAL; } /* *-------------------------------------------------------------------------------- */ int nntp_article ( wctx_t *Wctxp, ushort sz ) { long status; ushort szA,flag; /* ** */ status = nntp_getarticle (Wctxp,sz,&szA,&flag); if ( !$VMS_STATUS_SUCCESS(status) ) return status; if ( !szA ) return SS$_NORMAL; /* ** */ if ( !flag ) sz = sprintf(Wctxp->_t_buf,ARTI_FOLLOW, Wctxp->mrec._w_midlen, Wctxp->mrec._t_mid); else sz = sprintf(Wctxp->_t_buf,ARTI_N_FOLW,Wctxp->_l_msgnum, Wctxp->mrec._w_midlen, Wctxp->mrec._t_mid); net_send_line(Wctxp->_a_chan,Wctxp->_t_buf,sz); return net_send_mline(Wctxp->_a_chan,Wctxp->mrec._t_body,szA); } /* *-------------------------------------------------------------------------------- */ int nntp_head ( wctx_t *Wctxp, ushort sz ) { long status; ushort szA,flag; struct dsc$descriptor dsc_msg; /* ** */ status = nntp_getarticle (Wctxp,sz,&szA,&flag); if ( !$VMS_STATUS_SUCCESS(status) ) return status; if ( !szA ) return SS$_NORMAL; /* ** */ INIT_SDESC(dsc_msg,szA,Wctxp->mrec._t_body); szA = lib$index(&dsc_msg,&dsc_CRLFCRLF); szA--; if ( !flag ) sz = sprintf(Wctxp->_t_buf,HEAD_FOLLOW, Wctxp->mrec._w_midlen, Wctxp->mrec._t_mid); else sz = sprintf(Wctxp->_t_buf,HEAD_N_FOLLOW,Wctxp->_l_msgnum, Wctxp->mrec._w_midlen, Wctxp->mrec._t_mid); net_send_line(Wctxp->_a_chan,Wctxp->_t_buf,sz); return net_send_mline(Wctxp->_a_chan,Wctxp->mrec._t_body,szA); } /* *-------------------------------------------------------------------------------- */ int nntp_body ( wctx_t *Wctxp, ushort sz ) { int status; ushort szA,szH,flag; struct dsc$descriptor dsc_msg; /* ** */ status = nntp_getarticle (Wctxp,sz,&szA,&flag); if ( !$VMS_STATUS_SUCCESS(status) ) return status; if ( !szA ) return SS$_NORMAL; /* ** */ INIT_SDESC(dsc_msg,szA,Wctxp->mrec._t_body); szH = lib$index(&dsc_msg,&dsc_CRLFCRLF); szH--; szH+= dsc_CRLFCRLF.dsc$w_length; szA-= szH; if ( !flag ) sz = sprintf(Wctxp->_t_buf,BODY_FOLLOW, Wctxp->mrec._w_midlen, Wctxp->mrec._t_mid); else sz = sprintf(Wctxp->_t_buf,BODY_N_FOLLOW,Wctxp->_l_msgnum, Wctxp->mrec._w_midlen, Wctxp->mrec._t_mid); net_send_line(Wctxp->_a_chan,Wctxp->_t_buf,sz); return net_send_mline(Wctxp->_a_chan,&Wctxp->mrec._t_body[szH],szA); } /* *-------------------------------------------------------------------------------- */ int nntp_stat ( wctx_t *Wctxp, ushort sz ) { int status; ushort flag,szA; /* ** */ status = nntp_getarticle (Wctxp,sz,&szA,&flag); if ( !$VMS_STATUS_SUCCESS(status) ) return status; if ( !szA ) return SS$_NORMAL; if ( !flag ) sz = sprintf(Wctxp->_t_buf,STAT_FOLLOW, Wctxp->mrec._w_midlen, Wctxp->mrec._t_mid); else sz = sprintf(Wctxp->_t_buf,STAT_N_FOLLOW,Wctxp->_l_msgnum, Wctxp->mrec._w_midlen, Wctxp->mrec._t_mid); return net_send_line(Wctxp->_a_chan,Wctxp->_t_buf,sz); } /* *-------------------------------------------------------------------------------- */ int nntp_post ( wctx_t *Wctxp, ushort sz ) { int status; ushort szA; net_send_line(Wctxp->_a_chan,SEND_TO_POST,sizeof(SEND_TO_POST)-1); szA = MSGMAXSZ; status = net_read_mline(Wctxp->_a_chan,Wctxp->mrec._t_body, &szA,Wctxp->_d_tmo); if ( SS$_BUFFEROVF == status) return net_send_line(Wctxp->_a_chan,ASCIC(NO_VALD_SIZE)); if (!$VMS_STATUS_SUCCESS(status)) return net_send_line(Wctxp->_a_chan,ASCIC(POST_FAIL)); status = msg_to_db (Wctxp,szA); if ( !$VMS_STATUS_SUCCESS(status) ) { NNTP_LOGT(Wctxp,LOGE,"nntp_post/msg_to_db,(status = %d)",status); return net_send_line(Wctxp->_a_chan,ASCIC(POST_FAIL)); } return net_send_line(Wctxp->_a_chan,ASCIC(POST_OK)); } /* *-------------------------------------------------------------------------------- *-------------------------------------------------------------------------------- */ #define C_STR(a) sizeof(a)-1,(a) struct FSArg RFCFields[] = { {C_STR("From:")}, {C_STR("Newsgroups:")}, {C_STR("Subject:")}, {C_STR("Message-ID:")}, {C_STR("Date:")}, {C_STR("Path:")}, {C_STR("Approved:")}, {C_STR("Reply-To:")}, {C_STR("References:")}, {C_STR("Lines:")}, {0,NULL}}; #define MAXFIELD 10 #define FROMFIELD 0 #define GRPFIELD 1 #define SUBJFIELD 2 #define MIDFIELD 3 #define DATEFIELD 4 #define PATHFIELD 5 #define APPRFIELD 6 #define REPLYTOFIELD 7 #define REFFIELD 8 #define LINEFIELD 9 int msg_hdr_parse ( wctx_t *Wctxp, ushort sz, struct FSArg *PL, struct FSArg *AL ) { int cnt,ret_cnt; struct dsc$descriptor dsc_hdr,dsc_fld; ushort pos; INIT_SDESC(dsc_fld,0,NULL); /* ** */ INIT_SDESC(dsc_hdr,sz,Wctxp->mrec._t_body); if ( !(dsc_hdr.dsc$w_length = lib$index(&dsc_hdr,&dsc_CRLFCRLF)) ) dsc_hdr.dsc$w_length = sz; else dsc_hdr.dsc$w_length++; /* ** */ for ( ret_cnt,cnt = 0;(PL+cnt)->_w_len;cnt++) { SET_SDESC(dsc_fld,(PL+cnt)->_w_len,(PL+cnt)->_a_arg); (AL+cnt)->_w_len = 0; if ( !(pos = lib$index(&dsc_hdr,&dsc_fld)) ) continue; dsc_fld.dsc$w_length = dsc_hdr.dsc$w_length - pos - (PL+cnt)->_w_len; dsc_fld.dsc$a_pointer = dsc_hdr.dsc$a_pointer + pos + (PL+cnt)->_w_len; if ( !((AL+cnt)->_w_len = lib$index(&dsc_fld,&dsc_CRLF)) ) (AL+cnt)->_w_len = dsc_fld.dsc$w_length; else (AL+cnt)->_w_len--; (AL+cnt)->_a_arg = dsc_fld.dsc$a_pointer; ret_cnt++; } return ret_cnt; } /* *-------------------------------------------------------------------------------- */ int msg_hdr_valid ( wctx_t *Wctxp, ushort *sz ) { struct dsc$descriptor dsc_hdr,dsc_fld; char buf [1024]; time_t t; ushort sz0,pos,len; /* ** Extract RFC-822 header */ INIT_SDESC(dsc_hdr,*sz,Wctxp->mrec._t_body); if ( !(dsc_hdr.dsc$w_length = lib$index(&dsc_hdr,&dsc_CRLFCRLF)) ) return SS$_BADPARAM; /* ** Check 'Date:' field */ time(&t); INIT_SDESC(dsc_fld,RFCFields[DATEFIELD]._w_len,RFCFields[DATEFIELD]._a_arg); if ( !lib$index(&dsc_hdr,&dsc_fld) ) { sz0 = sprintf(buf,"Date: %s\r\n",cvt_vms_to_rfc(t,&buf[128], nntp_conf._s_localtz.dsc$a_pointer)); if ( (sz0 + (*sz)) > MSGMAXSZ ) return SS$_INSFMEM; strinsert(Wctxp->mrec._t_body,*sz,buf,sz0); *sz +=sz0; dsc_hdr.dsc$w_length += sz0; NNTP_LOGT(Wctxp,LOGW,"msg_hdr_valid:add 'Date:'."); } /* ** Check 'Message-Id:' field */ INIT_SDESC(dsc_fld,RFCFields[MIDFIELD]._w_len,RFCFields[MIDFIELD]._a_arg); if ( !lib$index(&dsc_hdr,&dsc_fld) ) { MDString(0,Wctxp->mrec._t_body,*sz,NULL,0,buf); MDbin2hex(Wctxp->_t_buf,buf); sz0 = sprintf(Wctxp->_t_buf,"Message-ID: <%32s>\r\n",buf); if ( (sz0 + *sz) > MSGMAXSZ ) return SS$_INSFMEM; strinsert(Wctxp->mrec._t_body,*sz,Wctxp->_t_buf,sz0); *sz +=sz0; dsc_hdr.dsc$w_length += sz0; NNTP_LOGT(Wctxp,LOGW,"msg_hdr_valid:add 'Message-ID:'."); } /* ** Check 'Path:' field */ INIT_SDESC(dsc_fld,RFCFields[PATHFIELD]._w_len,RFCFields[PATHFIELD]._a_arg); if ( !(pos = lib$index(&dsc_hdr,&dsc_fld)) ) { sz0 = sprintf(buf,"X-NNTP-Srv: %s\r\nPath: %.*s\r\n", ID$IDsrv, nntp_conf._s_localpath.dsc$w_length, nntp_conf._s_localpath.dsc$a_pointer); if ( (sz0 + *sz) > MSGMAXSZ ) return SS$_INSFMEM; strinsert(Wctxp->mrec._t_body,*sz,buf,sz0); *sz +=sz0; dsc_hdr.dsc$w_length += sz0; } else { pos += RFCFields[PATHFIELD]._w_len; len = *sz - pos; sz0 = sprintf(buf,"%.*s!", nntp_conf._s_localpath.dsc$w_length, nntp_conf._s_localpath.dsc$a_pointer); if ( (sz0 + *sz) > MSGMAXSZ ) return SS$_INSFMEM; strinsert(Wctxp->mrec._t_body+pos,*sz,buf,sz0); *sz +=sz0; dsc_hdr.dsc$w_length += sz0; } NNTP_LOGT(Wctxp,LOGW,"msg_hdr_valid:modifie 'Path:'."); return SS$_NORMAL; } /* *-------------------------------------------------------------------------------- */ int msg_to_db ( wctx_t *Wctxp, ushort sz ) { long status; struct dsc$descriptor dsc_tmp; char buf [1024],*cp; int len,n,rc,elem; time_t t,told; struct FSArg MsgFields[MAXFIELD]; grprec_t grec; /* ** Validation RFC's fields, and parsing */ NNTP_LOGT(Wctxp,LOGD,"msg_to_db:Article size %d.",sz); status = msg_hdr_valid (Wctxp,&sz); if ( !$VMS_STATUS_SUCCESS(status) ) { NNTP_LOGT(Wctxp,LOGE,"msg_to_db:article header is not valid.\n"); return status; } NNTP_LOGT(Wctxp,LOGD,"msg_to_db:Article size after validation %d.",sz); msg_hdr_parse (Wctxp,sz,RFCFields,MsgFields); for (n = 0;RFCFields[n]._w_len;n++) { NNTP_LOGT(Wctxp,LOGD,"msg_to_db:'%.*s%.*s'", RFCFields[n]._w_len,RFCFields[n]._a_arg, MsgFields[n]._w_len,MsgFields[n]._a_arg); }; /* ** Check for age of article */ t = cvt_rfc_to_vms (MsgFields[DATEFIELD]._a_arg,MsgFields[DATEFIELD]._w_len); time(&told); told = told - (24*60*60*nntp_conf._w_msgold); if ( t < told ) { NNTP_LOGT(Wctxp,LOGW,"msg_to_db:Article is too old (Date:%.*s).", MsgFields[DATEFIELD]._w_len, MsgFields[DATEFIELD]._a_arg); return SS$_ABORT; } /* ** Filling of msgrec_t data structure */ memcpy(Wctxp->mrec._t_mid,MsgFields[MIDFIELD]._a_arg,MsgFields[MIDFIELD]._w_len); Wctxp->mrec._w_midlen = MsgFields[MIDFIELD]._w_len; /* ** Loop for each newsgroups in the 'Newsgroups:' field */ n = 0; INIT_SDESC(dsc_tmp,MsgFields[GRPFIELD]._w_len,MsgFields[GRPFIELD]._a_arg); while(1) { /* ** Get pointer and length of next newsgroups in the list */ if ( !(len = strelem(&dsc_tmp,',',n++,&cp)) ) break; /* ** Check for valid length */ if ( len > GRPNAME$_LEN) { NNTP_LOGT(Wctxp,LOGW,"msg_to_db:Skip (is too long) '%.*s',%.*s", Wctxp->mrec._w_midlen,Wctxp->mrec._t_mid, len,cp); continue; } /* ** Check for entry in the GrpME list */ if ( !strmatch (nntp_conf._s_grpme_list.dsc$a_pointer, nntp_conf._s_grpme_list.dsc$w_length,cp,len) ) { NNTP_LOGT(Wctxp,LOGW,"msg_to_db:Skip (is not in GrpME) '%.*s',%.*s", Wctxp->mrec._w_midlen,Wctxp->mrec._t_mid, len,cp); continue; } /* ** Check for entry in the moderated groups */ if ( nntp_conf._s_modgrp_list.dsc$w_length && !MsgFields[APPRFIELD]._w_len && (elem = strmatch (nntp_conf._s_modgrp_list.dsc$a_pointer, nntp_conf._s_modgrp_list.dsc$w_length,cp,len)) ) { char *mod; ushort modlen; NNTP_LOGT(Wctxp,LOGD,"msg_to_db:Sending to moderator '%.*s',%.*s", Wctxp->mrec._w_midlen,Wctxp->mrec._t_mid,len,cp); elem--; if ( !(modlen = strelem(&nntp_conf._s_mod_list,'|',elem,&mod)) ) { NNTP_LOGT(Wctxp,LOGF,"msg_to_db:can't get elem(%d) from %.*s", nntp_conf._s_modgrp_list.dsc$w_length, nntp_conf._s_modgrp_list.dsc$a_pointer); return SS$_ABORT; } status = smtp_send (Wctxp,MsgFields,Wctxp->mrec._t_body,sz,cp,len,mod,modlen); if ( !$VMS_STATUS_SUCCESS(status) ) NNTP_LOGT(Wctxp,LOGF,"msg_to_db/smtp_send,(status = %d)",status); continue; } /* ** Inserting article to Messages' database */ memcpy(&grec._t_name,cp,len); grec._b_len = len; NNTP_LOGT(Wctxp,LOGD,"msg_to_db:Inserting '%.*s','%.*s'", Wctxp->mrec._w_midlen,Wctxp->mrec._t_mid, grec._b_len,grec._t_name); status = DBins (&Wctxp->_s_grprab,&Wctxp->_s_msgrab,&grec, t,&Wctxp->mrec,sz); if (!$VMS_STATUS_SUCCESS(status) && (status != RMS$_RNF) ) { NNTP_LOGT(Wctxp,LOGE,"msg_to_db:'%.*s','%.*s',(status = %d)", Wctxp->mrec._w_midlen,Wctxp->mrec._t_mid, grec._b_len,grec._t_name,status); } } return status; } /* *-------------------------------------------------------------------------------- */ int nntp_list ( wctx_t *Wctxp, ushort sz ) { long status; int rewindf = 0; if ( sz > 4 ) return net_send_line (Wctxp->_a_chan,ASCIC(BAD_CMD)); net_send_line (Wctxp->_a_chan,ASCIC(LIST_GRP)); while( 1 & (status=GrpDBget(&Wctxp->_s_grprab,&Wctxp->grec,0,rewindf++,1)) ) { sz = sprintf(Wctxp->_t_buf,"%.*s %d %d %c\r\n", Wctxp->grec._b_len, Wctxp->grec._t_name, Wctxp->grec._l_last, Wctxp->grec._l_first, Wctxp->grec._c_postflag); net_send_line (Wctxp->_a_chan,Wctxp->_t_buf,sz-2); } net_send_line (Wctxp->_a_chan,".",1); return (status==RMS$_EOF || status==SS$_NORMAL)?SS$_NORMAL:status; } /* *-------------------------------------------------------------------------------- */ int nntp_ihave ( wctx_t *Wctxp, ushort sz ) { long status; ushort szA; char *cp0,*cp1; /* ** Extract Mess-ID, and check for duplicate */ if ( !(cp0 = memchr(Wctxp->_t_buf,'<',sz)) || !(cp1 = memchr(cp0,'>',sz - (cp0-Wctxp->_t_buf))) || ((sz = (++cp1) - cp0) > MSGID$_LEN ) ) return net_send_line(Wctxp->_a_chan,ASCIC(ART_REJECT)); if ( 1 & MsgDBfind_byId(&Wctxp->_s_msgrab,cp0,sz) ) return net_send_line(Wctxp->_a_chan,ASCIC(ALRDY_SEEN)); /* ** Send 'welcome' to post, and get message */ net_send_line(Wctxp->_a_chan,ASCIC(NEWS_TO_ME)); szA = MSGMAXSZ; status = net_read_mline(Wctxp->_a_chan,Wctxp->mrec._t_body,&szA, Wctxp->_d_tmo); if ( SS$_BUFFEROVF == status) return net_send_line(Wctxp->_a_chan,ASCIC(NO_VALD_SIZE)); /* ** Put message in the database and check status code */ status = msg_to_db (Wctxp,szA); if ( !$VMS_STATUS_SUCCESS(status) ) { NNTP_LOGT(Wctxp,LOGE,"nntp_ihave/msg_to_db,(status = %d)",status); return net_send_line(Wctxp->_a_chan,ASCIC(POST_FAIL)); } return net_send_line(Wctxp->_a_chan,ASCIC(POST_OK)); } /* *-------------------------------------------------------------------------------- */ int nntp_group ( wctx_t *Wctxp, ushort sz ) { long status; char *cp; Wctxp->_l_msgnum = 0; /* ** Extract newsgroups name and get record from group's database */ if ( !(cp = memchr(Wctxp->_t_buf,' ',sz)) ) return net_send_line (Wctxp->_a_chan,ASCIC(BAD_CMD)); Wctxp->grec._b_len = sz - ((++cp) - Wctxp->_t_buf); memcpy(Wctxp->grec._t_name,cp,Wctxp->grec._b_len); status = GrpDBget (&Wctxp->_s_grprab,&Wctxp->grec,0,1,0); if ( status == RMS$_RNF ) return net_send_line(Wctxp->_a_chan,ASCIC(NO_SUCH_GRP)); if (!$VMS_STATUS_SUCCESS(status)) return status; sz = sprintf(Wctxp->_t_buf,GRP_SELECT, (Wctxp->grec._l_last || Wctxp->grec._l_first)?(Wctxp->grec._l_last-Wctxp->grec._l_first)+1:0, Wctxp->grec._l_first,Wctxp->grec._l_last, Wctxp->grec._b_len,Wctxp->grec._t_name); return net_send_line(Wctxp->_a_chan,Wctxp->_t_buf,sz); } /* *-------------------------------------------------------------------------------- */ int nntp_newgroups ( wctx_t *Wctxp, ushort sz ) { char *cp; int rewindf = 0; time_t t = 0; net_send_line (Wctxp->_a_chan,ASCIC(NEW_GRP)); Wctxp->_l_msgnum = 0; /* ** Extract date and time field and convert to time_t format */ if ( cp = memchr (Wctxp->_t_buf,' ',sz) ) t = cvt_nntp_to_vms(cp); /* ** Get group and compare creation date with 't' */ while( 1 & GrpDBget(&Wctxp->_s_grprab,&Wctxp->grec,0,rewindf++,0) ) { if ( t > Wctxp->grec._l_datecr ) continue; sz = sprintf(cp,"%.*s %d %d %c", Wctxp->grec._b_len, Wctxp->grec._t_name, Wctxp->grec._l_last, Wctxp->grec._l_first, Wctxp->grec._c_postflag); net_send_line(Wctxp->_a_chan,cp,sz); } return net_send_line (Wctxp->_a_chan,".",1); } /* *-------------------------------------------------------------------------------- */ int nntp_last ( wctx_t *Wctxp, ushort sz ) { int status; ulong n; ushort szA; if ( !Wctxp->grec._b_len ) return net_send_line(Wctxp->_a_chan,ASCIC(NO_GRP_SEL)); if ( !Wctxp->_l_msgnum ) return net_send_line(Wctxp->_a_chan,ASCIC(NO_ART_SEL)); n = Wctxp->_l_msgnum; n--; status = MsgDBget_byNum(&Wctxp->_s_msgrab, &Wctxp->grec,n,&Wctxp->mrec,&szA); if ( !$VMS_STATUS_SUCCESS(status) ) { net_send_line(Wctxp->_a_chan,ASCIC(NO_SUCH_ARTP)); return (status == RMS$_RNF?SS$_NORMAL:status); } Wctxp->_l_msgnum = n; sz = sprintf(Wctxp->_t_buf,STAT_N_FOLLOW,n, Wctxp->mrec._w_midlen,Wctxp->mrec._t_mid); return net_send_mline(Wctxp->_a_chan,Wctxp->_t_buf,sz); } /* *-------------------------------------------------------------------------------- */ int nntp_next ( wctx_t *Wctxp, ushort sz ) { int status; ulong n; if ( !Wctxp->grec._b_len ) return net_send_line(Wctxp->_a_chan,ASCIC(NO_GRP_SEL)); if ( !Wctxp->_l_msgnum ) return net_send_line(Wctxp->_a_chan,ASCIC(NO_ART_SEL)); n = Wctxp->_l_msgnum; n++; status = MsgDBget_byNum(&Wctxp->_s_msgrab,&Wctxp->grec, n,&Wctxp->mrec,&sz); if ( !$VMS_STATUS_SUCCESS(status) ) { net_send_line(Wctxp->_a_chan,ASCIC(NO_SUCH_ARTS)); return (status == RMS$_RNF?SS$_NORMAL:status); } Wctxp->_l_msgnum = n; sz = sprintf(Wctxp->_t_buf,NEXT_ARTI,n, Wctxp->mrec._w_midlen,Wctxp->mrec._t_mid); return net_send_mline(Wctxp->_a_chan,Wctxp->_t_buf,sz); } /* *-------------------------------------------------------------------------------- */ int nntp_newnews ( wctx_t *Wctxp, ushort sz ) { long status; ulong n; char *cp0,*cp1,*gmask; ushort gmasklen; time_t t; int rewindf = 0; char lkey [MSGGRP$_LEN]; ushort lkeysz = 0; char buf [ 1024 ]; /* ** Extract newsgroups mask */ if ( !(gmask = memchr (Wctxp->_t_buf,' ',sz)) ) return net_send_line (Wctxp->_a_chan,ASCIC(BAD_CMD)); gmask++; if ( !(cp0 = memchr (gmask,' ',sz - (gmask - Wctxp->_t_buf))) ) return net_send_line (Wctxp->_a_chan,ASCIC(BAD_CMD)); gmasklen = cp0 - gmask; /* ** Extract datetime field of the command */ cp0++; if ( !(cp0 = memchr (gmask,' ',sz - (cp0 - Wctxp->_t_buf))) ) return net_send_line (Wctxp->_a_chan,ASCIC(BAD_CMD)); cp0++; if ( !(t = cvt_nntp_to_vms(cp0)) ) return net_send_line (Wctxp->_a_chan,ASCIC(BAD_CMD)); /* ** */ sz = sprintf(buf,NEW_NEWS,13,cp0); net_send_line (Wctxp->_a_chan,buf,sz); /* ** */ while( RMS$_EOF != GrpDBget(&Wctxp->_s_grprab,&Wctxp->grec,0,rewindf++,1) ) { if ( !strmatch (gmask,gmasklen,Wctxp->grec._t_name,Wctxp->grec._b_len) ) continue; while ( 1 & (status = MsgDBget_byRange(&Wctxp->_s_msgrab, &Wctxp->grec,Wctxp->grec._l_first, Wctxp->grec._l_last,&Wctxp->mrec, &sz,lkey,&lkeysz)) ) { if ( t >= Wctxp->mrec._l_date ) continue; Wctxp->mrec._t_mid[0] = '<'; status = net_send_line(Wctxp->_a_chan, Wctxp->mrec._t_mid, Wctxp->mrec._w_midlen); if ( !$VMS_STATUS_SUCCESS(status) ) return status; } if ( status == RMS$_OK_LIM ) continue; if ( !$VMS_STATUS_SUCCESS(status) ) break; } NNTP_LOGT(Wctxp,LOGD,"nntp_newnews,(status = %d)",status); return net_send_line(Wctxp->_a_chan,".",1); } /* *-------------------------------------------------------------------------------- */ int nntp_xover ( wctx_t *Wctxp, ushort sz ) { long status; char *cp0,*cp1; ulong from = 0,to = 0,i; ushort szM; struct FSArg MsgFields[MAXFIELD]; /* ** */ if ( !Wctxp->grec._b_len ) return net_send_line(Wctxp->_a_chan,ASCIC(NO_GRP_SEL)); from = to = Wctxp->grec._l_first?Wctxp->grec._l_first:1; if ( cp0 = memchr(Wctxp->_t_buf,' ',sz) ) { cp0++; if ( cp1 = memchr(cp0,'-',sz - (cp0 - Wctxp->_t_buf)) ) { cp1++; lib$cvt_dtb(sz - (cp1 - Wctxp->_t_buf),cp1,&to); lib$cvt_dtb(cp1-cp0-1,cp0,&from); } else lib$cvt_dtb(sz - (cp0 - Wctxp->_t_buf),cp0,&from); } /* ** first < from < last; first < to < last && from < to < last, */ from = max (from,Wctxp->grec._l_first); from = min (from,Wctxp->grec._l_last); to = min (to ,Wctxp->grec._l_last); to = max (from,to); status = net_send_line(Wctxp->_a_chan,ASCIC(DATA_FOLLOWS)); if (!$VMS_STATUS_SUCCESS(status)) return status; NNTP_LOGT(Wctxp,LOGD,"XOVER From:%d, To:%d",from,to); for (i = from; i <= to; i++) { status = MsgDBget_byNum(&Wctxp->_s_msgrab,&Wctxp->grec,i, &Wctxp->mrec,&szM); if ( status == RMS$_RNF ) continue; if ( !$VMS_STATUS_SUCCESS(status) ) break; msg_hdr_parse (Wctxp,szM,RFCFields,MsgFields); sz = sprintf(Wctxp->_t_buf,"%lu\t%.*s\t%.*s\t%.*s\t%.*s\t%.*s\t%lu\t%.*s", i, MsgFields[SUBJFIELD]._w_len,MsgFields[SUBJFIELD]._a_arg, MsgFields[FROMFIELD]._w_len,MsgFields[FROMFIELD]._a_arg, MsgFields[DATEFIELD]._w_len,MsgFields[DATEFIELD]._a_arg, MsgFields[MIDFIELD]._w_len,MsgFields[MIDFIELD]._a_arg, MsgFields[REFFIELD]._w_len,MsgFields[REFFIELD]._a_arg, szM, MsgFields[LINEFIELD]._w_len,MsgFields[LINEFIELD]._a_arg); net_send_line(Wctxp->_a_chan,Wctxp->_t_buf,sz); } if ( (RMS$_OK_LIM != status) && (RMS$_EOF != status) && !$VMS_STATUS_SUCCESS(status)) NNTP_LOGT(Wctxp,LOGD,"nntp_xover,(status = %d)",status); return net_send_line(Wctxp->_a_chan,".",1); } /* *-------------------------------------------------------------------------------- */ char SMTP_HELO [] = "HELO %.*s", SMTP_FROM [] = "MAIL FROM:%.*s", SMTP_TO [] = "RCPT TO:%.*s", SMTP_DATA [] = "DATA", SMTP_QUIT [] = "QUIT"; ushort SMTP_Port = 25; int smtp_send ( wctx_t *Wctxp, struct FSArg *AL, char *msg, ushort msglen, char *grp, ushort grplen, char *mod, ushort modlen ) { long status; void *chan; char buf[1024],*cp0,*cp1; ushort len,rc,sz,i; NNTP_LOGT(Wctxp,LOGD,"smtp_send -- start"); /* ** Connect to SMTP host, get 'welcome' message */ status = net_connect_out (&chan, nntp_conf._s_smtprelay.dsc$a_pointer, nntp_conf._s_smtprelay.dsc$w_length, SMTP_Port); if (!$VMS_STATUS_SUCCESS(status)) return status; while(1) { len = sizeof(buf); status = net_read_line(chan,buf,&len,Wctxp->_d_tmo); if (!$VMS_STATUS_SUCCESS(status)) break; NNTP_LOGT(Wctxp,LOGD,"Got from SMTP Relay '%.*s'",len,buf); /* ** Send "HELO:"... */ sz = sprintf(Wctxp->_t_buf,SMTP_HELO,nntp_conf._s_localhost.dsc$w_length, nntp_conf._s_localhost.dsc$a_pointer); NNTP_LOGT(Wctxp,LOGD,"smtp_send:'%.*s'",sz,Wctxp->_t_buf); len = sizeof(buf); status = net_send_cmd (chan,Wctxp->_t_buf,sz,buf,&len,&rc,0); if (!$VMS_STATUS_SUCCESS(status)) break; NNTP_LOGT(Wctxp,LOGD,"Got from SMTP Relay '%.*s'",len,buf); if ( rc != 250 ) { status = SS$_ABORT; break; } /* ** Send "FROM:"... */ cp0 = memchr((AL+FROMFIELD)->_a_arg,'<',(AL+FROMFIELD)->_w_len); cp1 = memchr((AL+FROMFIELD)->_a_arg,'>',(AL+FROMFIELD)->_w_len); sz = sprintf(Wctxp->_t_buf,SMTP_FROM,(++cp1)-cp0,cp0); NNTP_LOGT(Wctxp,LOGD,"smtp_send:'%.*s'",sz,Wctxp->_t_buf); len = sizeof(buf); status = net_send_cmd (chan,Wctxp->_t_buf,sz,buf,&len,&rc,0); if (!$VMS_STATUS_SUCCESS(status)) break; NNTP_LOGT(Wctxp,LOGD,"Got from SMTP Relay '%.*s'",len,buf); if ( rc != 250 ) { status = SS$_ABORT; break; } /* ** "fido7.testing" -> "fido7-testing" */ memcpy(Wctxp->_t_buf,grp,grplen); for ( i = 0;i < grplen;i++) Wctxp->_t_buf[i] = (Wctxp->_t_buf[i]=='.'?'-':Wctxp->_t_buf[i]); /* ** "fido7-testing@fido7.ru" */ cp0 = memchr(mod,'@',modlen); memcpy(&Wctxp->_t_buf[grplen],cp0,modlen-(cp0-mod)); len = grplen + modlen-(cp0-mod); sz = sprintf(buf,SMTP_TO,len,Wctxp->_t_buf); NNTP_LOGT(Wctxp,LOGD,"smtp_send:'%.*s'",sz,buf); len = sizeof(buf); status = net_send_cmd (chan,buf,sz,buf,&len,&rc,0); if (!$VMS_STATUS_SUCCESS(status)) break; NNTP_LOGT(Wctxp,LOGD,"Got from SMTP Relay '%.*s'",len,buf); if ( rc != 250 ) { status = SS$_ABORT; break; } /* ** Send "DATA" and whole article */ len = sizeof(buf); status = net_send_cmd (chan,ASCIC(SMTP_DATA),buf,&len,&rc,0); if (!$VMS_STATUS_SUCCESS(status)) break; NNTP_LOGT(Wctxp,LOGD,"Got from SMTP Relay '%.*s'",len,buf); if ( rc != 354 ) { status = SS$_ABORT; break; } status = net_send_mline(chan,msg,msglen); if (!$VMS_STATUS_SUCCESS(status)) break; NNTP_LOGT(Wctxp,LOGD,"Got from SMTP Relay '%.*s'",len,buf); /* ** */ len = sizeof(buf); status = net_send_cmd (chan,ASCIC(SMTP_QUIT),buf,&len,&rc,0); NNTP_LOGT(Wctxp,LOGD,"Got from SMTP Relay '%.*s'",len,buf); break; } net_close(chan); return status; }