#pragma module NNTP_COMMANDS "NNTP_COMMANDS-1-B" /* **++ ** FACILITY: NNTP server for OpenVMS ** ** MODULE DESCRIPTION: ** ** This module contains all NNTP protocol related routines. ** ** AUTHORS: ** ** Copyright (c) 1996-2002 Ruslan R. Laishev (@RRL) ** ** CREATION DATE: ??-???-1996 ** ** ** MODIFICATION HISTORY: ** ** 10-FEB-2002 RRL Fixed sending to moderator, smtp_send(). ** ** {@tbs@}... **-- */ /* ** ** INCLUDE FILES ** */ #include "nntp.h" #include "rfc822def.h" /* ** ** NNTP server commands and functions table ** */ 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 newsgroups"),nntp_list, "List NewsGroups"}, {ASCIC("list overview.fmt"),nntp_listfmt,"List OverView.FMT"}, {ASCIC("list extensions"),nntp_listfmt, NULL}, {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"} }; /* ** ** NNTP server responses ** */ $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 text follows"; char HEAD_FOLLOW[] = "221 %.*s Head of the article follows"; char HEAD_N_FOLLOW[] = "221 %u %.*s Head of the article follows"; char BODY_FOLLOW[] = "222 %.*s Body of the article follows"; char BODY_N_FOLLOW[] = "222 %u %.*s Body of the article 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"; /* **++ ** FUNCTIONAL DESCRIPTION: ** ** A main loop to get a command from a client, search the command against command table, ** dispatch a processing to particular function. ** ** FORMAL PARAMETERS: ** ** Wctxp: A worker's context buffer ** ** RETURN VALUE: ** ** VMS condition code ** **-- */ int nntp_cmd_exec (wctx_t *Wctxp) { long status; struct NNTP_cmd *cmdp; struct dsc$descriptor dsc_cmd,dsc_sts; ushort sz; /* ** Initalization */ INIT_SDESC(dsc_cmd,0,NULL); INIT_SDESC(dsc_sts,0,NULL); /* ** Main "get command, parsing and dispatche" loop */ while (1) { /* ** Read with timeout a next command from client */ sz = BUFPSZ; if ( !(1 & (status = net_read_line (Wctxp->_a_chan,Wctxp->_t_buf,&sz,Wctxp->_d_tmo))) ) break; NNTP_LOGT(Wctxp,LOGD,"%.*s",sz,Wctxp->_t_buf); /* ** Find a gottent command in the command/function table */ 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); /* ** A gotten command is not found/supported - send "bad command" */ if ( !cmdp->nntp_fun ) { net_send_line (Wctxp->_a_chan,ASCIC(BAD_CMD)); continue; } /* ** A comand is found /supported dispatch to function */ if ( !(1 & (status = cmdp->nntp_fun (Wctxp,sz))) ) break; } return status; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Processes a NNTP/HELP command. Print a help text and return. ** ** FORMAL PARAMETERS: ** ** Wctxp: A worker's context buffer ** sz: A size of gotten command line ** ** RETURN VALUE: ** ** VMS condition code ** **-- */ 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++) { if ( !t->hlp ) continue; 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); } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Processes a NNTP/QUIT command. Say goodbay, and return SS$_DISCONNECT status. ** ** FORMAL PARAMETERS: ** ** Wctxp: A worker's context buffer ** sz: A size of gotten command line ** ** RETURN VALUE: ** ** SS$_DISCONNECT ** **-- */ int nntp_quit ( wctx_t *Wctxp, ushort sz ) { net_send_line(Wctxp->_a_chan,ASCIC(GOODBYE)); return SS$_DISCONNECT; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Processes a NNTP/SLAVE command. Say OK, and return. ** ** FORMAL PARAMETERS: ** ** Wctxp: A worker's context buffer ** sz: A size of gotten command line ** ** RETURN VALUE: ** ** VMS condition code ** **-- */ int nntp_slave ( wctx_t *Wctxp, ushort sz ) { return net_send_line(Wctxp->_a_chan,ASCIC(SLAVE_RESP)); } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Retrive an article by message id or by number. Store information ** about the article to use it in next request. ** ** FORMAL PARAMETERS: ** ** Wctxp: A worker's context buffer ** sz: A size of gotten command line ** szA: A size of a retreived article ** flag: A flag to designate a variant of the NNTP/ARTICLE comman ** ** RETURN VALUE: ** ** VMS condition code ** **-- */ 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)); if ( !(1 & (status = MsgDBget_byId(&Wctxp->_s_msgrab,cp0,sz,&Wctxp->mrec,szA))) ) { *szA = 0; NNTP_LOGT(Wctxp,LOGE,"nntp_getarticle:'%.*s','%.*s',(status = %d)", Wctxp->mrec._w_midlen,Wctxp->mrec._t_mid, Wctxp->grec._b_len,Wctxp->grec._t_name,status); return net_send_line(Wctxp->_a_chan,ASCIC(NO_SUCH_ART)); } 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)); } } if ( !(1 & (status = MsgDBget_byNum(&Wctxp->_s_msgrab,&Wctxp->grec, n,&Wctxp->mrec,szA))) ) { *szA = 0; NNTP_LOGT(Wctxp,LOGE,"nntp_getarticle:#%u,'%.*s',(status = %d)", n,Wctxp->grec._b_len,Wctxp->grec._t_name,status); return net_send_line(Wctxp->_a_chan,ASCIC(NO_SUCH_ART)); } Wctxp->_l_msgnum = n; return SS$_NORMAL; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Processing a NNTP/ARTICLE command. ** ** FORMAL PARAMETERS: ** ** Wctxp: A worker's context buffer ** sz: A size of gotten command line ** ** RETURN VALUE: ** ** VMS condition code ** **-- */ int nntp_article ( wctx_t *Wctxp, ushort sz ) { long status; ushort szA,flag; /* ** Get an article, check status and size */ if ( !(1 & (status = nntp_getarticle (Wctxp,sz,&szA,&flag))) ) return status; if ( !szA ) return SS$_NORMAL; /* ** Format and send standart response */ 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); /* ** Send the article */ return net_send_mline(Wctxp->_a_chan,Wctxp->mrec._t_body,szA); } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Processing a NNTP/HEAD command. ** ** FORMAL PARAMETERS: ** ** Wctxp: A worker's context buffer ** sz: A size of gotten command line ** ** RETURN VALUE: ** ** VMS condition code ** **-- */ int nntp_head ( wctx_t *Wctxp, ushort sz ) { long status; ushort szA,flag; struct dsc$descriptor dsc_msg; /* ** Get an article, check status and size */ if ( !(1 & (status = nntp_getarticle (Wctxp,sz,&szA,&flag))) ) return status; if ( !szA ) return SS$_NORMAL; /* ** Search RFC header/body terminator */ INIT_SDESC(dsc_msg,szA,Wctxp->mrec._t_body); szA = lib$index(&dsc_msg,&dsc_CRLFCRLF); szA--; /* ** Format and send standart response */ 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); /* ** Send the article's header */ 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; /* ** Get an article, check status and size */ if ( !(1 & (status = nntp_getarticle (Wctxp,sz,&szA,&flag))) ) return status; if ( !szA ) return SS$_NORMAL; /* ** Search RFC header/body terminator */ 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; /* ** Format and send standart response */ 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); /* ** Send the article's header */ 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; /* ** Get an article, check status and size */ if ( !(1 & (status = nntp_getarticle (Wctxp,sz,&szA,&flag))) ) return status; if ( !szA ) return SS$_NORMAL; /* ** Fromat and send a standart response */ 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)); if ( !(1 & (status = msg_to_db (Wctxp,szA))) ) { 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)); } /* *-------------------------------------------------------------------------------- *-------------------------------------------------------------------------------- */ int msg_hdr_parse ( wctx_t *Wctxp, ushort sz, ile2 *itmlst, ushort *hdrsz ) { struct dsc$descriptor dsc_hdr; int n; /* ** Initialization */ for (n = 0;itmlst[n].ile2$w_code;n++) itmlst[n].ile2$w_length = 0; /* ** Find a header/body separator */ INIT_SDESC(dsc_hdr,sz,Wctxp->mrec._t_body); if ( !(*hdrsz = lib$index(&dsc_hdr,&dsc_CRLFCRLF)) ) return SS$_ABORT; (*hdrsz)--; /* ** Call a header parser, and return status */ return hdr_parse(Wctxp->mrec._t_body,*hdrsz,itmlst); } /* *-------------------------------------------------------------------------------- */ int msg_hdr_valid ( wctx_t *Wctxp, ushort *sz, ile2 *itmlst ) { int status; char buf [1024]; time_t t; ushort sz0,hdrsz,n; /* ** Parse a RFC-822 header, and extract fields information */ if ( !(1 & (status = msg_hdr_parse(Wctxp,sz,itmlst,&hdrsz))) ) return status; /* ** Check 'Date:' field */ time(&t); if ( !itmlst[RFC822$K_DATE-1].ile2$w_length ) { sz0 = sprintf(buf,"\r\nDate: %s %.*s",ctime(&t), nntp_conf._s_localtz.dsc$w_length, nntp_conf._s_localtz.dsc$a_pointer); if ( (sz0 + (*sz)) > MSGMAXSZ ) return SS$_INSFMEM; strinsert(Wctxp->mrec._t_body+hdrsz,*sz,buf,sz0); *sz += sz0; hdrsz += sz0; NNTP_LOGT(Wctxp,LOGW,"msg_hdr_valid:add 'Date:'."); } /* ** Check 'Message-Id:' field */ if ( !itmlst[RFC822$K_MESSID-1].ile2$w_length ) { MDString(0,Wctxp->mrec._t_body,*sz,NULL,0,Wctxp->_t_buf); MDbin2hex(Wctxp->_t_buf,buf); sz0 = sprintf(Wctxp->_t_buf,"\r\nMessage-ID: <%32s>",buf); if ( (sz0 + *sz) > MSGMAXSZ ) return SS$_INSFMEM; strinsert(Wctxp->mrec._t_body+hdrsz,*sz,Wctxp->_t_buf,sz0); *sz += sz0; hdrsz += sz0; NNTP_LOGT(Wctxp,LOGW,"msg_hdr_valid:add 'Message-ID:'."); } /* ** Check 'Path:' field */ if ( !itmlst[RFC822$K_PATH-1].ile2$w_length ) { sz0 = sprintf(buf,"\r\nX-NNTP-Srv: %s\r\nPath: %.*s", 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+hdrsz,*sz,buf,sz0); *sz += sz0; hdrsz += sz0; } else { 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(itmlst[RFC822$K_PATH-1].ile2$ps_bufaddr,*sz,buf,sz0); *sz += sz0; hdrsz += sz0; } NNTP_LOGT(Wctxp,LOGW,"msg_hdr_valid:modify '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; grprec_t grec; ile2 itmlst[] = {{0,RFC822$K_FROM,0},{0,RFC822$K_GROUP,0}, {0,RFC822$K_SUBJECT,0},{0,RFC822$K_MESSID,0},{0,RFC822$K_DATE,0},{0,RFC822$K_PATH,0}, {0,RFC822$K_APPROVED,0},{0,RFC822$K_REPLYTO,0},{0,RFC822$K_REFERENCES,0},{0,RFC822$K_LINES,0}, {0,0,0}}; /* ** Validation RFC's fields, and parsing */ NNTP_LOGT(Wctxp,LOGD,"msg_to_db:Article size %d.",sz); if ( !(1 & (status = msg_hdr_valid (Wctxp,&sz,itmlst))) ) { 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,itmlst,&len); /* ** Check age of article */ t = cvt_rfc_to_vms (itmlst[RFC822$K_DATE-1].ile2$ps_bufaddr, itmlst[RFC822$K_DATE-1].ile2$w_length); 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).", itmlst[RFC822$K_DATE-1].ile2$w_length, itmlst[RFC822$K_DATE-1].ile2$ps_bufaddr); return SS$_ABORT; } /* ** Filling of msgrec_t data structure */ memcpy(Wctxp->mrec._t_mid,itmlst[RFC822$K_MESSID-1].ile2$ps_bufaddr, itmlst[RFC822$K_MESSID-1].ile2$w_length); Wctxp->mrec._w_midlen = itmlst[RFC822$K_MESSID-1].ile2$w_length; /* ** Loop for each newsgroups in the 'Newsgroups:' field */ n = 0; INIT_SDESC(dsc_tmp,itmlst[RFC822$K_GROUP-1].ile2$w_length, itmlst[RFC822$K_GROUP-1].ile2$ps_bufaddr); 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 && !itmlst[RFC822$K_APPROVED-1].ile2$w_length && (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,itmlst,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_listfmt ( wctx_t *Wctxp, ushort sz ) { char listfmt[] = "215 Order of fields in overview database.\r\n"\ "Subject:\r\n"\ "From:\r\n"\ "Date:\r\n"\ "Message-ID:\r\n"\ "References:\r\n"\ "Bytes:\r\n"\ "Lines:\r\n"\ "Xref:full"; return net_send_mline (Wctxp->_a_chan,ASCIC(listfmt)); } /* *-------------------------------------------------------------------------------- */ 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 */ if ( !(1 & (status = msg_to_db (Wctxp,szA))) ) { 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++,1) ) { 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; /* ** Check for current group and current article */ 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--; if ( !(1 & (status = MsgDBget_byNum(&Wctxp->_s_msgrab, &Wctxp->grec,n,&Wctxp->mrec,&szA))) ) { 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_line(Wctxp->_a_chan,Wctxp->_t_buf,sz); } /* *-------------------------------------------------------------------------------- */ int nntp_next ( wctx_t *Wctxp, ushort sz ) { int status; ulong n; /* ** Check for current group and current article */ 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++; if ( !(1 & (status = MsgDBget_byNum(&Wctxp->_s_msgrab,&Wctxp->grec, n,&Wctxp->mrec,&sz))) ) { 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_line(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 ) { int status,n; char *cp0,*cp1; ulong from = 0,to = 0,i; ushort szM,hdrsz; ile2 itmlst[] = {{0,RFC822$K_FROM,0},{0,RFC822$K_GROUP,0}, {0,RFC822$K_SUBJECT,0},{0,RFC822$K_MESSID,0},{0,RFC822$K_DATE,0},{0,RFC822$K_PATH,0}, {0,RFC822$K_APPROVED,0},{0,RFC822$K_REPLYTO,0},{0,RFC822$K_REFERENCES,0},{0,RFC822$K_LINES,0}, {0,0,0}}; /* ** Check that current group is set */ if ( !Wctxp->grec._b_len ) return net_send_line(Wctxp->_a_chan,ASCIC(NO_GRP_SEL)); /* ** Validate start and end article number */ 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); /* ** Send standard NNTP header */ if ( !(1 & (status = net_send_line(Wctxp->_a_chan,ASCIC(DATA_FOLLOWS)))) ) return status; NNTP_LOGT(Wctxp,LOGD,"XOVER From:%d, To:%d",from,to); /* ** Retrieving articles in the range, extract a header information */ for (i = from; i <= to; i++) { /* ** Retrive an article */ if ( !(1 & (status = MsgDBget_byNum(&Wctxp->_s_msgrab,&Wctxp->grec,i, &Wctxp->mrec,&szM))) ) continue; /* ** Parse header to extract necessary fields */ msg_hdr_parse (Wctxp,szM,itmlst,&hdrsz); /* ** Special checking for HT character in the subject field */ for (; cp0 = memchr(itmlst[RFC822$K_SUBJECT-1].ile2$ps_bufaddr,'\t', itmlst[RFC822$K_SUBJECT-1].ile2$w_length);) *cp0= ' '; /* ** Format and send a line for the article */ sz = sprintf(Wctxp->_t_buf,"%u\t%.*s\t%.*s\t%.*s\t%.*s\t%.*s\t%u\t%.*s", i, itmlst[RFC822$K_SUBJECT-1].ile2$w_length,itmlst[RFC822$K_SUBJECT-1].ile2$ps_bufaddr, itmlst[RFC822$K_FROM-1].ile2$w_length,itmlst[RFC822$K_FROM-1].ile2$ps_bufaddr, itmlst[RFC822$K_DATE-1].ile2$w_length,itmlst[RFC822$K_DATE-1].ile2$ps_bufaddr, itmlst[RFC822$K_MESSID-1].ile2$w_length,itmlst[RFC822$K_MESSID-1].ile2$ps_bufaddr, itmlst[RFC822$K_REFERENCES-1].ile2$w_length,itmlst[RFC822$K_REFERENCES-1].ile2$ps_bufaddr, szM, itmlst[RFC822$K_LINES-1].ile2$w_length,itmlst[RFC822$K_LINES-1].ile2$ps_bufaddr); net_send_line(Wctxp->_a_chan,Wctxp->_t_buf,sz); } if ( (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, ile2 *itmlst, 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 */ if ( !(1 & (status = net_connect_out (&chan, nntp_conf._s_smtprelay.dsc$a_pointer, nntp_conf._s_smtprelay.dsc$w_length, SMTP_Port))) ) return status; while(1) { len = sizeof(buf); if ( !(1 & (status = net_read_line(chan,buf,&len,Wctxp->_d_tmo))) ) 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); if ( !(1 & (status = net_send_cmd (chan,Wctxp->_t_buf,sz,buf,&len,&rc,0))) ) break; NNTP_LOGT(Wctxp,LOGD,"Got from SMTP Relay '%.*s'",len,buf); if ( rc != 250 ) { NNTP_LOGT(Wctxp,LOGE,"Got from SMTP Relay '%.*s'",len,buf); status = SS$_ABORT; break; } /* ** Send "FROM:"... */ cp0 = memchr(itmlst[RFC822$K_FROM-1].ile2$ps_bufaddr,'<',itmlst[RFC822$K_FROM-1].ile2$ps_bufaddr); cp1 = memchr(itmlst[RFC822$K_FROM-1].ile2$ps_bufaddr,'>',itmlst[RFC822$K_FROM-1].ile2$ps_bufaddr); sz = sprintf(Wctxp->_t_buf,SMTP_FROM,(++cp1)-cp0,cp0); NNTP_LOGT(Wctxp,LOGD,"smtp_send:'%.*s'",sz,Wctxp->_t_buf); len = sizeof(buf); if ( !(1 & (status = net_send_cmd (chan,Wctxp->_t_buf,sz,buf,&len,&rc,0))) ) break; NNTP_LOGT(Wctxp,LOGD,"Got from SMTP Relay '%.*s'",len,buf); if ( rc != 250 ) { NNTP_LOGT(Wctxp,LOGE,"Got from SMTP Relay '%.*s'",len,buf); 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); if ( !(1 & (status = net_send_cmd (chan,buf,sz,buf,&len,&rc,0))) ) break; NNTP_LOGT(Wctxp,LOGD,"Got from SMTP Relay '%.*s'",len,buf); if ( rc != 250 ) { NNTP_LOGT(Wctxp,LOGE,"Got from SMTP Relay '%.*s'",len,buf); status = SS$_ABORT; break; } /* ** Send "DATA" and whole article */ len = sizeof(buf); if ( !(1 & (status = net_send_cmd (chan,ASCIC(SMTP_DATA),buf,&len,&rc,0))) ) break; NNTP_LOGT(Wctxp,LOGD,"Got from SMTP Relay '%.*s'",len,buf); if ( rc != 354 ) { NNTP_LOGT(Wctxp,LOGE,"Got from SMTP Relay '%.*s'",len,buf); status = SS$_ABORT; break; } if ( !(1 & (status = net_send_mline(chan,msg,msglen))) ) break; NNTP_LOGT(Wctxp,LOGD,"Got from SMTP Relay '%.*s'",len,buf); /* ** */ len = sizeof(buf); if ( !(1 & (status = net_send_cmd (chan,ASCIC(SMTP_QUIT),buf,&len,&rc,0))) ) break; NNTP_LOGT(Wctxp,LOGD,"Got from SMTP Relay '%.*s'",len,buf); break; } net_close(chan); return status; }