#pragma module NNTP_COMMANDS "NNTP_COMMANDS-1-F" /* **++ ** FACILITY: NNTP server for OpenVMS ** ** MODULE DESCRIPTION: ** ** This module contains all NNTP protocol related routines. ** ** AUTHORS: ** ** Copyright (c) 1996-2006 Ruslan R. Laishev (@RRL) ** ** CREATION DATE: ??-???-1996 ** ** ** MODIFICATION HISTORY: ** ** 10-FEB-2002 RRL Fixed sending to moderator, smtp_send(). ** 18-MAY-2002 RRL Fixed "Date:" field. ** 20-MAY-2002 RRL Added NON-RFC "Delete" command. ** 22-MAY-2002 RRL Fixed a bug in the nntp_newnews(). ** 4-DEC-2002 RRL Added NNTP-Posting-Host field validation. ** 28-AUG-2003 RRL Fixed msg_hdr_valid (), Now Message ID is ** 26-DEC-2003 RRL Fixed problem with CR,LF,HT in subject field in the nntp_xover(). ** 15-FEB-2006 RRL Added SHUTDOWN. ** 4-MAY-2006 RRL Fixed a dumb ACCVIO bug in the msg_to_db(). ** 8-JUN-2009 TMR MODE READER command support ** ** {@tbs@}... **-- */ /* ** ** INCLUDE FILES ** */ #include "nntp.h" #include "rfc822def.h" extern int exit_flag; /* ** ** NNTP server commands and functions table ** */ struct NNTP_cmd { char *cmd; ushort len; int (*nntp_fun) (WCTX *,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"}, */ {ASCIC("delete"), nntp_delete, "Delete MessageID"}, {ASCIC("mode"), nntp_mode, "Set mode "}, {NULL, 0, NULL, "UnRecognized"} }; /* ** ** NNTP server responses ** */ $DESCRIPTOR(dsc_CRLFCRLF,"\r\n\r\n"); $DESCRIPTOR(dsc_CRLF,"\r\n"); const char XOVER_DATA[] = "%s\t%.*s\t%.*s\t%s\t%.*\r\n", LIST_OF_CMD[] = "100 list of legal commands follows", SLAVE_RESP[] = "202 slave status noted", GOODBYE[] = "205 closing connection - goodbye !", GRP_SELECT[] = "211 %d %d %d %.*s", LIST_GRP[] = "215 list of newsgroups follows", ARTI_FOLLOW[] = "220 %.*s Article text follows", ARTI_N_FOLW[] = "220 %u %.*s article text follows", HEAD_FOLLOW[] = "221 %.*s Head of the article follows", HEAD_N_FOLLOW[] = "221 %u %.*s Head of the article follows", BODY_FOLLOW[] = "222 %.*s Body of the article follows", BODY_N_FOLLOW[] = "222 %u %.*s Body of the article follows", STAT_FOLLOW[] = "223 %.*s status", STAT_N_FOLLOW[] = "223 %u %.*s article status", NEXT_ARTI[] = "223 %u %.*s Article retrived; request text.", DATA_FOLLOWS[] = "224 data follows", NEW_NEWS[] = "230 list of news since %.*s follows", NEW_GRP[] = "231 list of new newsgroups follows", POST_OK[] = "240 article posting OK", IPOST_OK[] = "235 article posting OK", ARTI_DLTD[] = "250 article %.* deleted from local database", NEWS_TO_ME[] = "335 News to me!End-..", SEND_TO_POST[] = "340 send article to be posted.End-.", NO_SUCH_GRP[] = "411 no such news group", NO_GRP_SEL[] = "412 no newsgroup has been selected", NO_ART_SEL[] = "420 no current article has been slected", NO_SUCH_ARTS[] = "421 no such next article in this group", NO_SUCH_ARTP[] = "422 no such previous article in this group", NO_SUCH_ARTN[] = "423 no such article number in this group", NO_SUCH_ART[] = "430 no such article found", ALRDY_SEEN[] = "435 Already seen that one,where you been", ART_REJECT[] = "437 Article rejected - do not try again", NO_VALD_SIZE[] = "437 length article is not valid", POST_FAIL[] = "442 posting failed", 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 *Wctxp) { long status; struct NNTP_cmd *cmdp; struct dsc$descriptor dsc_cmd,dsc_sts; ushort sz = 0,csz = 0; unsigned char *cp,*bufp = &Wctxp->wctx$t_buf; INIT_SDESC(dsc_cmd,0,NULL); INIT_SDESC(dsc_sts,0,NULL); /* ** Main "get command, parsing and dispatching" loop */ do { /* ** Read with timeout a next command from client */ 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,"'%.*s'",sz,Wctxp->wctx$t_buf); if ( cp = memchr(Wctxp->wctx$t_buf,' ',sz) ) csz = cp - bufp; else csz = sz; /* ** 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,csz,Wctxp->wctx$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 ) { status = net_send_line (Wctxp->wctx$a_chan,ASCIC(BAD_CMD)); continue; } /* ** A command is found & supported, dispatch to function, ** check return status */ status = cmdp->nntp_fun (Wctxp,sz); } while ( (1 & status) && !exit_flag ); 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 *Wctxp, ushort sz ) { struct NNTP_cmd *t; net_send_line(Wctxp->wctx$a_chan,ASCIC(LIST_OF_CMD)); for(t = NNTP_cmd;t->cmd;t++) { if ( !t->hlp ) continue; sz = sprintf(Wctxp->wctx$t_buf,"->%s",t->hlp); net_send_line(Wctxp->wctx$a_chan,Wctxp->wctx$t_buf,sz); } net_send_line(Wctxp->wctx$a_chan,nntp_conf._s_localmgr.dsc$a_pointer, nntp_conf._s_localmgr.dsc$w_length); return net_send_line(Wctxp->wctx$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 *Wctxp, ushort sz ) { net_send_line(Wctxp->wctx$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 *Wctxp, ushort sz ) { return net_send_line(Wctxp->wctx$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 *Wctxp, ushort sz, ushort *szA, ushort *flag ) { unsigned status,n; unsigned char *cp0,*cp1,*bufp = &Wctxp->wctx$t_buf; /* ** Get by Message ID */ *flag = 0; if( cp0 = memchr(Wctxp->wctx$t_buf,'<',sz) ) { Wctxp->wctx$l_mnum = 0; if ( !(cp1 = memchr(cp0,'>',sz-(bufp-cp0))) ) return net_send_line (Wctxp->wctx$a_chan,ASCIC(BAD_CMD)); if ( (sz = (++cp1) - cp0) > MSGID$_LEN ) return net_send_line (Wctxp->wctx$a_chan,ASCIC(NO_SUCH_ARTN)); if ( !(1 & (status = MsgDBget_byId(&Wctxp->wctx$r_mrab,cp0,sz,&Wctxp->wctx$r_mrec,szA))) ) { *szA = 0; NNTP_LOGT(Wctxp,LOG$K_WAR,"nntp_getarticle:'%.*s','%.*s',(status = %d)", Wctxp->wctx$r_mrec.msg$w_midlen,Wctxp->wctx$r_mrec.msg$t_mid, Wctxp->wctx$r_grec.grp$b_grplen,Wctxp->wctx$r_grec.grp$t_grpname,status); return net_send_line(Wctxp->wctx$a_chan,ASCIC(NO_SUCH_ART)); } return SS$_NORMAL; } /* ** Get by Message number */ *flag = 1; if ( !Wctxp->wctx$r_grec.grp$b_grplen ) return net_send_line(Wctxp->wctx$a_chan,ASCIC(NO_GRP_SEL)); n = Wctxp->wctx$l_mnum; if( cp0 = memchr(Wctxp->wctx$t_buf,' ',sz) ) { if ( sz - (++cp0 - bufp) ) if ( !lib$cvt_dtb(sz - (cp0 - bufp),cp0,&n) ) { *szA = 0; return net_send_line(Wctxp->wctx$a_chan,ASCIC(NO_SUCH_ART)); } } if ( !(1 & (status = MsgDBget_byNum(&Wctxp->wctx$r_mrab,&Wctxp->wctx$r_grec, n,&Wctxp->wctx$r_mrec,szA))) ) { *szA = 0; NNTP_LOGT(Wctxp,LOG$K_WAR,"nntp_getarticle:#%u,'%.*s',(status = %d)", n,Wctxp->wctx$r_grec.grp$b_grplen,Wctxp->wctx$r_grec.grp$t_grpname,status); return net_send_line(Wctxp->wctx$a_chan,ASCIC(NO_SUCH_ART)); } Wctxp->wctx$l_mnum = 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 *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->wctx$t_buf,ARTI_FOLLOW, Wctxp->wctx$r_mrec.msg$w_midlen, Wctxp->wctx$r_mrec.msg$t_mid); else sz = sprintf(Wctxp->wctx$t_buf,ARTI_N_FOLW,Wctxp->wctx$l_mnum, Wctxp->wctx$r_mrec.msg$w_midlen, Wctxp->wctx$r_mrec.msg$t_mid); net_send_line(Wctxp->wctx$a_chan,Wctxp->wctx$t_buf,sz); /* ** Send the article */ return net_send_mline(Wctxp->wctx$a_chan,Wctxp->wctx$r_mrec.msg$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 *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->wctx$r_mrec.msg$t_body); szA = lib$index(&dsc_msg,&dsc_CRLFCRLF); szA--; /* ** Format and send standart response */ if ( !flag ) sz = sprintf(Wctxp->wctx$t_buf,HEAD_FOLLOW, Wctxp->wctx$r_mrec.msg$w_midlen, Wctxp->wctx$r_mrec.msg$t_mid); else sz = sprintf(Wctxp->wctx$t_buf,HEAD_N_FOLLOW,Wctxp->wctx$l_mnum, Wctxp->wctx$r_mrec.msg$w_midlen, Wctxp->wctx$r_mrec.msg$t_mid); net_send_line(Wctxp->wctx$a_chan,Wctxp->wctx$t_buf,sz); /* ** Send the article's header */ return net_send_mline(Wctxp->wctx$a_chan,Wctxp->wctx$r_mrec.msg$t_body,szA); } /* *-------------------------------------------------------------------------------- */ int nntp_body ( WCTX *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->wctx$r_mrec.msg$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->wctx$t_buf,BODY_FOLLOW, Wctxp->wctx$r_mrec.msg$w_midlen, Wctxp->wctx$r_mrec.msg$t_mid); else sz = sprintf(Wctxp->wctx$t_buf,BODY_N_FOLLOW,Wctxp->wctx$l_mnum, Wctxp->wctx$r_mrec.msg$w_midlen, Wctxp->wctx$r_mrec.msg$t_mid); net_send_line(Wctxp->wctx$a_chan,Wctxp->wctx$t_buf,sz); /* ** Send the article's header */ return net_send_mline(Wctxp->wctx$a_chan,&Wctxp->wctx$r_mrec.msg$t_body[szH],szA); } /* *-------------------------------------------------------------------------------- */ int nntp_stat ( WCTX *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->wctx$t_buf,STAT_FOLLOW, Wctxp->wctx$r_mrec.msg$w_midlen, Wctxp->wctx$r_mrec.msg$t_mid); else sz = sprintf(Wctxp->wctx$t_buf,STAT_N_FOLLOW,Wctxp->wctx$l_mnum, Wctxp->wctx$r_mrec.msg$w_midlen, Wctxp->wctx$r_mrec.msg$t_mid); return net_send_line(Wctxp->wctx$a_chan,Wctxp->wctx$t_buf,sz); } /* *-------------------------------------------------------------------------------- */ int nntp_post ( WCTX *Wctxp, ushort sz ) { int status; ushort szA = MSG$K_BODY_SZ; net_send_line(Wctxp->wctx$a_chan,SEND_TO_POST,sizeof(SEND_TO_POST)-1); status = net_read_mline(Wctxp->wctx$a_chan,Wctxp->wctx$r_mrec.msg$t_body, &szA,&Wctxp->wctx$q_tmo); if ( SS$_BUFFEROVF == status) return net_send_line(Wctxp->wctx$a_chan,ASCIC(NO_VALD_SIZE)); NNTP_LOGT(Wctxp,LOG$K_DBG,"POST '%.*s'",szA,Wctxp->wctx$r_mrec.msg$t_body); if (!$VMS_STATUS_SUCCESS(status)) return net_send_line(Wctxp->wctx$a_chan,ASCIC(POST_FAIL)); if ( !(1 & (status = msg_to_db (Wctxp,szA))) ) { NNTP_LOGT(Wctxp,LOG$K_ERR,"nntp_post/msg_to_db,(status = %d)",status); return net_send_line(Wctxp->wctx$a_chan,ASCIC(POST_FAIL)); } return net_send_line(Wctxp->wctx$a_chan,ASCIC(POST_OK)); } /* *-------------------------------------------------------------------------------- *-------------------------------------------------------------------------------- */ int msg_hdr_parse ( WCTX *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->wctx$r_mrec.msg$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->wctx$r_mrec.msg$t_body,*hdrsz,itmlst); } /* *-------------------------------------------------------------------------------- */ int msg_hdr_valid ( WCTX *Wctxp, ushort *sz, ile2 *itmlst ) { int status; unsigned 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 'X-NNTP-Posting-Host:' field */ time(&t); if ( !itmlst[RFC822$K_POSTHOST-1].ile2$w_length ) { sz0 = sprintf(buf,"\r\nX-NNTP-Posting-Host: %.*s", Wctxp->wctx$b_ipadrlen,Wctxp->wctx$t_ipadr); if ( (sz0 + (*sz)) > MSG$K_BODY_SZ ) return SS$_INSFMEM; strinsert(Wctxp->wctx$r_mrec.msg$t_body+hdrsz,*sz,buf,sz0); *sz += sz0; hdrsz += sz0; NNTP_LOGT(Wctxp,LOG$K_WAR,"msg_hdr_valid:add 'X-NNTP-Posting-Host:'."); } /* ** Check 'Date:' field */ time(&t); if ( !itmlst[RFC822$K_DATE-1].ile2$w_length ) { sz0 = sprintf(buf,"\r\nDate: %s",ctime(&t)); sz0 += sprintf(buf+sz0 - 1," %.*s", nntp_conf._s_localtz.dsc$w_length, nntp_conf._s_localtz.dsc$a_pointer); if ( (sz0 + (*sz)) > MSG$K_BODY_SZ ) return SS$_INSFMEM; strinsert(Wctxp->wctx$r_mrec.msg$t_body+hdrsz,*sz,buf,sz0); *sz += sz0; hdrsz += sz0; NNTP_LOGT(Wctxp,LOG$K_WAR,"msg_hdr_valid:add 'Date:'."); } /* ** Check 'Message-Id:' field */ if ( !itmlst[RFC822$K_MESSID-1].ile2$w_length ) { MDString(0,Wctxp->wctx$r_mrec.msg$t_body,*sz,NULL,0,Wctxp->wctx$t_buf); MDbin2hex(Wctxp->wctx$t_buf,buf); sz0 = sprintf(Wctxp->wctx$t_buf,"\r\nMessage-ID: <%32s@%.*s>",buf, nntp_conf._s_localhost.dsc$w_length, nntp_conf._s_localhost.dsc$a_pointer); if ( (sz0 + *sz) > MSG$K_BODY_SZ ) return SS$_INSFMEM; strinsert(&Wctxp->wctx$r_mrec.msg$t_body[hdrsz],*sz,Wctxp->wctx$t_buf,sz0); *sz += sz0; hdrsz += sz0; NNTP_LOGT(Wctxp,LOG$K_WAR,"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) > MSG$K_BODY_SZ ) return SS$_INSFMEM; strinsert(Wctxp->wctx$r_mrec.msg$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) > MSG$K_BODY_SZ ) return SS$_INSFMEM; strinsert(itmlst[RFC822$K_PATH-1].ile2$ps_bufaddr,*sz,buf,sz0); *sz += sz0; hdrsz += sz0; } NNTP_LOGT(Wctxp,LOG$K_WAR,"msg_hdr_valid:modify 'Path:'."); return SS$_NORMAL; } /* *-------------------------------------------------------------------------------- */ int msg_to_db ( WCTX *Wctxp, ushort sz ) { long status; struct dsc$descriptor dsc_tmp; unsigned char buf [1024],*cp; int len,n,rc,elem; time_t t,told; GRPREC 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,RFC822$K_POSTHOST,0}, {0,0,0}}; /* ** Validation RFC's fields, and parsing */ NNTP_LOGT(Wctxp,LOG$K_DBG,"msg_to_db:Article size %d.",sz); if ( !(1 & (status = msg_hdr_valid (Wctxp,&sz,itmlst))) ) { NNTP_LOGT(Wctxp,LOG$K_ERR,"msg_to_db:article header is not valid, (status = %u).\n",status); return status; } NNTP_LOGT(Wctxp,LOG$K_DBG,"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,LOG$K_WAR,"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 data structure */ memcpy(Wctxp->wctx$r_mrec.msg$t_mid,itmlst[RFC822$K_MESSID-1].ile2$ps_bufaddr, itmlst[RFC822$K_MESSID-1].ile2$w_length); Wctxp->wctx$r_mrec.msg$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 ( !exit_flag ) { /* ** Get pointer and length of next newsgroups in the list */ if ( !(len = strelem(&dsc_tmp,',',n++,&cp)) ) break; /* ** Check for valid length */ if ( len > GRPID$_LEN ) { NNTP_LOGT(Wctxp,LOG$K_WAR,"msg_to_db:Skip (is too long) '%.*s',%.*s", Wctxp->wctx$r_mrec.msg$w_midlen,Wctxp->wctx$r_mrec.msg$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,LOG$K_WAR,"msg_to_db:Skip (is not in GrpME) '%.*s',%.*s", Wctxp->wctx$r_mrec.msg$w_midlen,Wctxp->wctx$r_mrec.msg$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,LOG$K_DBG,"msg_to_db:Sending to moderator '%.*s',%.*s", Wctxp->wctx$r_mrec.msg$w_midlen,Wctxp->wctx$r_mrec.msg$t_mid,len,cp); elem--; if ( !(modlen = strelem(&nntp_conf._s_mod_list,'|',elem,&mod)) ) { NNTP_LOGT(Wctxp,LOG$K_FAT,"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; } if ( !(1 & (status = smtp_send (Wctxp,itmlst,Wctxp->wctx$r_mrec.msg$t_body,sz,cp,len,mod,modlen))) ) NNTP_LOGT(Wctxp,LOG$K_FAT,"msg_to_db/smtp_send,(status = %d)",status); continue; } /* ** Inserting article to Messages database */ memcpy(&grec.grp$t_grpname,cp,len); grec.grp$b_grplen = len; NNTP_LOGT(Wctxp,LOG$K_DBG,"msg_to_db:Inserting '%.*s','%.*s'", Wctxp->wctx$r_mrec.msg$w_midlen,Wctxp->wctx$r_mrec.msg$t_mid, grec.grp$b_grplen,grec.grp$t_grpname); if ( !(1 & (status = DBins (&Wctxp->wctx$r_grab,&Wctxp->wctx$r_mrab,&grec,t,&Wctxp->wctx$r_mrec,sz))) && (status != RMS$_RNF) ) { NNTP_LOGT(Wctxp,LOG$K_ERR,"msg_to_db:'%.*s','%.*s',(status = %d)", Wctxp->wctx$r_mrec.msg$w_midlen,Wctxp->wctx$r_mrec.msg$t_mid, grec.grp$b_grplen,grec.grp$t_grpname,status); } } return status; } /* *-------------------------------------------------------------------------------- */ int nntp_list ( WCTX *Wctxp, ushort sz ) { long status; int rewindf = 0; net_send_line (Wctxp->wctx$a_chan,ASCIC(LIST_GRP)); while( 1 & (status = GrpDBget(&Wctxp->wctx$r_grab,&Wctxp->wctx$r_grec,0,rewindf++,1)) ) { if ( !Wctxp->wctx$r_grec.grp$b_suck && !Wctxp->wctx$r_grec.grp$b_post ) continue; sz = sprintf(Wctxp->wctx$t_buf,"%.*s %d %d %c\r\n", Wctxp->wctx$r_grec.grp$b_grplen, Wctxp->wctx$r_grec.grp$t_grpname, Wctxp->wctx$r_grec.grp$l_last, Wctxp->wctx$r_grec.grp$l_first, Wctxp->wctx$r_grec.grp$b_post); if ( !(1 & (status = net_send_line (Wctxp->wctx$a_chan,Wctxp->wctx$t_buf,sz-2))) ) break; } net_send_line (Wctxp->wctx$a_chan,".",1); return (status==RMS$_EOF || status==SS$_NORMAL)?SS$_NORMAL:status; } /* *-------------------------------------------------------------------------------- */ int nntp_listfmt ( WCTX *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->wctx$a_chan,ASCIC(listfmt)); } /* *-------------------------------------------------------------------------------- */ int nntp_ihave ( WCTX *Wctxp, ushort sz ) { long status; ushort szA = MSG$K_BODY_SZ; unsigned char *cp0,*cp1,*bufp = &Wctxp->wctx$t_buf; /* ** Extract Mess-ID, and check for duplicate */ if ( !(cp0 = memchr(Wctxp->wctx$t_buf,'<',sz)) || !(cp1 = memchr(cp0,'>',sz - (cp0-bufp))) || ((sz = (++cp1) - cp0) > MSGID$_LEN ) ) return net_send_line(Wctxp->wctx$a_chan,ASCIC(ART_REJECT)); if ( 1 & MsgDBfind_byId(&Wctxp->wctx$r_mrab,cp0,sz) ) return net_send_line(Wctxp->wctx$a_chan,ASCIC(ALRDY_SEEN)); /* ** Send 'welcome' to post, and get message */ net_send_line(Wctxp->wctx$a_chan,ASCIC(NEWS_TO_ME)); status = net_read_mline(Wctxp->wctx$a_chan,Wctxp->wctx$r_mrec.msg$t_body,&szA, &Wctxp->wctx$q_tmo); if ( SS$_BUFFEROVF == status) return net_send_line(Wctxp->wctx$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,LOG$K_ERR,"nntp_ihave/msg_to_db,(status = %d)",status); return net_send_line(Wctxp->wctx$a_chan,ASCIC(NO_VALD_SIZE)); } return net_send_line(Wctxp->wctx$a_chan,ASCIC(IPOST_OK)); } /* *-------------------------------------------------------------------------------- */ int nntp_group ( WCTX *Wctxp, ushort sz ) { long status; unsigned char *cp,*bufp = &Wctxp->wctx$t_buf; Wctxp->wctx$l_mnum = 0; /* ** Extract newsgroups name and get record from group's database */ if ( !(cp = memchr(Wctxp->wctx$t_buf,' ',sz)) ) return net_send_line (Wctxp->wctx$a_chan,ASCIC(BAD_CMD)); Wctxp->wctx$r_grec.grp$b_grplen = sz - ((++cp) - bufp); memcpy(Wctxp->wctx$r_grec.grp$t_grpname,cp,Wctxp->wctx$r_grec.grp$b_grplen); status = GrpDBget (&Wctxp->wctx$r_grab,&Wctxp->wctx$r_grec,0,1,0); if ( status == RMS$_RNF ) return net_send_line(Wctxp->wctx$a_chan,ASCIC(NO_SUCH_GRP)); if (!$VMS_STATUS_SUCCESS(status)) return status; sz = sprintf(Wctxp->wctx$t_buf,GRP_SELECT, (Wctxp->wctx$r_grec.grp$l_last || Wctxp->wctx$r_grec.grp$l_first)?(Wctxp->wctx$r_grec.grp$l_last-Wctxp->wctx$r_grec.grp$l_first)+1:0, Wctxp->wctx$r_grec.grp$l_first,Wctxp->wctx$r_grec.grp$l_last, Wctxp->wctx$r_grec.grp$b_grplen,Wctxp->wctx$r_grec.grp$t_grpname); return net_send_line(Wctxp->wctx$a_chan,Wctxp->wctx$t_buf,sz); } /* *-------------------------------------------------------------------------------- */ int nntp_newgroups ( WCTX *Wctxp, ushort sz ) { unsigned char *cp; int rewindf = 0; time_t t = 0; net_send_line (Wctxp->wctx$a_chan,ASCIC(NEW_GRP)); Wctxp->wctx$l_mnum = 0; /* ** Extract date and time field and convert to time_t format */ if ( cp = memchr (Wctxp->wctx$t_buf,' ',sz) ) t = cvt_nntp_to_vms(cp); /* ** Get group and compare creation date with 't' */ while( 1 & GrpDBget(&Wctxp->wctx$r_grab,&Wctxp->wctx$r_grec,0,rewindf++,1) ) { if ( t > Wctxp->wctx$r_grec.grp$l_datecr ) continue; sz = sprintf(cp,"%.*s %d %d %c", Wctxp->wctx$r_grec.grp$b_grplen, Wctxp->wctx$r_grec.grp$t_grpname, Wctxp->wctx$r_grec.grp$l_last, Wctxp->wctx$r_grec.grp$l_first, Wctxp->wctx$r_grec.grp$b_post); net_send_line(Wctxp->wctx$a_chan,cp,sz); } return net_send_line (Wctxp->wctx$a_chan,".",1); } /* *-------------------------------------------------------------------------------- */ int nntp_last ( WCTX *Wctxp, ushort sz ) { int status; ulong n; ushort szA; /* ** Check for current group and current article */ if ( !Wctxp->wctx$r_grec.grp$b_grplen ) return net_send_line(Wctxp->wctx$a_chan,ASCIC(NO_GRP_SEL)); if ( !Wctxp->wctx$l_mnum ) return net_send_line(Wctxp->wctx$a_chan,ASCIC(NO_ART_SEL)); n = Wctxp->wctx$l_mnum; n--; if ( !(1 & (status = MsgDBget_byNum(&Wctxp->wctx$r_mrab, &Wctxp->wctx$r_grec,n,&Wctxp->wctx$r_mrec,&szA))) ) { net_send_line(Wctxp->wctx$a_chan,ASCIC(NO_SUCH_ARTP)); return (status == RMS$_RNF?SS$_NORMAL:status); } Wctxp->wctx$l_mnum = n; sz = sprintf(Wctxp->wctx$t_buf,STAT_N_FOLLOW,n, Wctxp->wctx$r_mrec.msg$w_midlen,Wctxp->wctx$r_mrec.msg$t_mid); return net_send_line(Wctxp->wctx$a_chan,Wctxp->wctx$t_buf,sz); } /* *-------------------------------------------------------------------------------- */ int nntp_next ( WCTX *Wctxp, ushort sz ) { int status; ulong n; /* ** Check for current group and current article */ if ( !Wctxp->wctx$r_grec.grp$b_grplen ) return net_send_line(Wctxp->wctx$a_chan,ASCIC(NO_GRP_SEL)); if ( !Wctxp->wctx$l_mnum ) return net_send_line(Wctxp->wctx$a_chan,ASCIC(NO_ART_SEL)); n = Wctxp->wctx$l_mnum; n++; if ( !(1 & (status = MsgDBget_byNum(&Wctxp->wctx$r_mrab,&Wctxp->wctx$r_grec, n,&Wctxp->wctx$r_mrec,&sz))) ) { net_send_line(Wctxp->wctx$a_chan,ASCIC(NO_SUCH_ARTS)); return (status == RMS$_RNF?SS$_NORMAL:status); } Wctxp->wctx$l_mnum = n; sz = sprintf(Wctxp->wctx$t_buf,NEXT_ARTI,n, Wctxp->wctx$r_mrec.msg$w_midlen,Wctxp->wctx$r_mrec.msg$t_mid); return net_send_line(Wctxp->wctx$a_chan,Wctxp->wctx$t_buf,sz); } /* *-------------------------------------------------------------------------------- */ int nntp_newnews ( WCTX *Wctxp, ushort sz ) { long status; ulong n; unsigned char *cp0,*cp1,*gmask,*bufp = &Wctxp->wctx$t_buf; ushort gmasklen; time_t t; int rewindf = 0; char lkey [GRPID$_LEN]; ushort lkeysz = 0; char buf [ 1024 ]; /* ** Extract newsgroups mask */ if ( !(gmask = memchr (Wctxp->wctx$t_buf,' ',sz)) ) return net_send_line (Wctxp->wctx$a_chan,ASCIC(BAD_CMD)); gmask++; if ( !(cp0 = memchr (gmask,' ',sz - (gmask - bufp))) ) return net_send_line (Wctxp->wctx$a_chan,ASCIC(BAD_CMD)); gmasklen = cp0 - gmask; /* ** Extract datetime field of the command */ cp0++; if ( !(cp0 = memchr (gmask,' ',sz - (cp0 - bufp))) ) return net_send_line (Wctxp->wctx$a_chan,ASCIC(BAD_CMD)); cp0++; if ( !(t = cvt_nntp_to_vms(cp0)) ) return net_send_line (Wctxp->wctx$a_chan,ASCIC(BAD_CMD)); /* ** */ sz = sprintf(buf,NEW_NEWS,13,cp0); net_send_line (Wctxp->wctx$a_chan,buf,sz); /* ** */ while( RMS$_EOF != GrpDBget(&Wctxp->wctx$r_grab,&Wctxp->wctx$r_grec,0,rewindf++,1) ) { int netsts; if ( !strmatch (gmask,gmasklen,Wctxp->wctx$r_grec.grp$t_grpname,Wctxp->wctx$r_grec.grp$b_grplen) ) continue; while ( 1 & (status = MsgDBget_byRange(&Wctxp->wctx$r_mrab, &Wctxp->wctx$r_grec,Wctxp->wctx$r_grec.grp$l_first, Wctxp->wctx$r_grec.grp$l_last,&Wctxp->wctx$r_mrec, &sz,lkey,&lkeysz)) && status != RMS$_OK_LIM ) { if ( t >= Wctxp->wctx$r_mrec.msg$l__date ) continue; Wctxp->wctx$r_mrec.msg$t_mid[0] = '<'; netsts = net_send_line(Wctxp->wctx$a_chan, Wctxp->wctx$r_mrec.msg$t_mid, Wctxp->wctx$r_mrec.msg$w_midlen); if ( !$VMS_STATUS_SUCCESS(netsts) ) return netsts; } if ( status == RMS$_OK_LIM ) continue; if ( !$VMS_STATUS_SUCCESS(status) ) break; if ( !$VMS_STATUS_SUCCESS(netsts) ) break; } NNTP_LOGT(Wctxp,LOG$K_DBG,"nntp_newnews,(status = %d)",status); return net_send_line(Wctxp->wctx$a_chan,".",1); } /* *-------------------------------------------------------------------------------- */ int nntp_xover ( WCTX *Wctxp, ushort sz ) { int status,n; unsigned char *cp0,*cp1,*bufp = &Wctxp->wctx$t_buf; ulong from = 0,to = 0,i,j; 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->wctx$r_grec.grp$b_grplen ) return net_send_line(Wctxp->wctx$a_chan,ASCIC(NO_GRP_SEL)); /* ** Validate start and end article number */ from = to = Wctxp->wctx$r_grec.grp$l_first?Wctxp->wctx$r_grec.grp$l_first:1; if ( cp0 = memchr(Wctxp->wctx$t_buf,' ',sz) ) { cp0++; if ( cp1 = memchr(cp0,'-',sz - (cp0 - bufp)) ) { cp1++; lib$cvt_dtb(sz - (cp1 - bufp),cp1,&to); lib$cvt_dtb(cp1-cp0-1,cp0,&from); } else lib$cvt_dtb(sz - (cp0 - bufp),cp0,&from); } /* ** first < from < last; first < to < last && from < to < last, */ from = max (from,Wctxp->wctx$r_grec.grp$l_first); from = min (from,Wctxp->wctx$r_grec.grp$l_last); to = min (to ,Wctxp->wctx$r_grec.grp$l_last); to = max (from,to); /* ** Send standard NNTP header */ if ( !(1 & (status = net_send_line(Wctxp->wctx$a_chan,ASCIC(DATA_FOLLOWS)))) ) return status; NNTP_LOGT(Wctxp,LOG$K_DBG,"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->wctx$r_mrab,&Wctxp->wctx$r_grec,i, &Wctxp->wctx$r_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 (j = 0, cp0 = itmlst[RFC822$K_SUBJECT-1].ile2$ps_bufaddr; j < itmlst[RFC822$K_SUBJECT-1].ile2$w_length;j++,cp0++) *cp0 = (*cp0 > ' ')?*cp0:' '; /* ** Format and send a line for the article */ sz = sprintf(Wctxp->wctx$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); if ( !(1 & (status = net_send_line(Wctxp->wctx$a_chan,Wctxp->wctx$t_buf,sz))) ) break; } if ( (RMS$_EOF != status) && !$VMS_STATUS_SUCCESS(status)) NNTP_LOGT(Wctxp,LOG$K_DBG,"nntp_xover,(status = %d)",status); return net_send_line(Wctxp->wctx$a_chan,".",1); } /* *-------------------------------------------------------------------------------- */ char SMTP_HELO [] = "HELO %.*s", SMTP_FROM [] = "MAIL FROM:%.*s", SMTP_TO [] = "RCPT TO:<%.*s>", SMTP_DATA [] = "DATA", SMTP_QUIT [] = "QUIT"; short SMTP_Port = 25; int smtp_send ( WCTX *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,LOG$K_DBG,"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 ( !exit_flag ) { len = sizeof(buf); if ( !(1 & (status = net_read_line(chan,buf,&len,&Wctxp->wctx$q_tmo))) ) break; NNTP_LOGT(Wctxp,LOG$K_DBG,"Got from SMTP Relay '%.*s'",len,buf); /* ** Send "HELO:"... */ sz = sprintf(Wctxp->wctx$t_buf,SMTP_HELO,nntp_conf._s_localhost.dsc$w_length, nntp_conf._s_localhost.dsc$a_pointer); NNTP_LOGT(Wctxp,LOG$K_DBG,"smtp_send:'%.*s'",sz,Wctxp->wctx$t_buf); len = sizeof(buf); if ( !(1 & (status = net_send_cmd (chan,Wctxp->wctx$t_buf,sz,buf,&len,&rc,0))) ) break; NNTP_LOGT(Wctxp,LOG$K_DBG,"Got from SMTP Relay '%.*s'",len,buf); if ( rc != 250 ) { NNTP_LOGT(Wctxp,LOG$K_ERR,"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->wctx$t_buf,SMTP_FROM,(++cp1)-cp0,cp0); NNTP_LOGT(Wctxp,LOG$K_DBG,"smtp_send:'%.*s'",sz,Wctxp->wctx$t_buf); len = sizeof(buf); if ( !(1 & (status = net_send_cmd (chan,Wctxp->wctx$t_buf,sz,buf,&len,&rc,0))) ) break; NNTP_LOGT(Wctxp,LOG$K_DBG,"Got from SMTP Relay '%.*s'",len,buf); if ( rc != 250 ) { NNTP_LOGT(Wctxp,LOG$K_ERR,"Got from SMTP Relay '%.*s'",len,buf); status = SS$_ABORT; break; } /* ** "fido7.testing" -> "fido7-testing" */ memcpy(Wctxp->wctx$t_buf,grp,grplen); for ( i = 0;i < grplen;i++) Wctxp->wctx$t_buf[i] = (Wctxp->wctx$t_buf[i]=='.'?'-':Wctxp->wctx$t_buf[i]); /* ** "fido7-testing@fido7.ru" */ cp0 = memchr(mod,'@',modlen); memcpy(&Wctxp->wctx$t_buf[grplen],cp0,modlen-(cp0-mod)); len = grplen + modlen-(cp0-mod); sz = sprintf(buf,SMTP_TO,len,Wctxp->wctx$t_buf); NNTP_LOGT(Wctxp,LOG$K_DBG,"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,LOG$K_DBG,"Got from SMTP Relay '%.*s'",len,buf); if ( rc != 250 ) { NNTP_LOGT(Wctxp,LOG$K_ERR,"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,LOG$K_DBG,"Got from SMTP Relay '%.*s'",len,buf); if ( rc != 354 ) { NNTP_LOGT(Wctxp,LOG$K_ERR,"Got from SMTP Relay '%.*s'",len,buf); status = SS$_ABORT; break; } if ( !(1 & (status = net_send_mline(chan,msg,msglen))) ) break; NNTP_LOGT(Wctxp,LOG$K_DBG,"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,LOG$K_DBG,"Got from SMTP Relay '%.*s'",len,buf); break; } net_close(chan); return status; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Processing a NON-RFC NNTP/REMOVE command, this command deletes an article identified ** by Message-Id. ** ** FORMAL PARAMETERS: ** ** Wctxp: A worker's context buffer ** sz: A size of gotten command line ** ** RETURN VALUE: ** ** VMS condition code ** **-- */ int nntp_delete ( WCTX *Wctxp, ushort sz ) { long status; unsigned char *cp0,*cp1,*bufp = &Wctxp->wctx$t_buf; /* ** Delete by Message ID */ if( cp0 = memchr(Wctxp->wctx$t_buf,'<',sz) ) { Wctxp->wctx$l_mnum = 0; if ( !(cp1 = memchr(cp0,'>',sz-(bufp-cp0))) ) return net_send_line (Wctxp->wctx$a_chan,ASCIC(BAD_CMD)); if ( (sz = (++cp1) - cp0) > MSGID$_LEN ) return net_send_line (Wctxp->wctx$a_chan,ASCIC(NO_SUCH_ARTN)); if ( !(1 & (status = MsgDBdel_byId(&Wctxp->wctx$r_mrab,cp0,sz))) ) { NNTP_LOGT(Wctxp,LOG$K_ERR,"nntp_delete:'%.*s','%.*s',(status = %d)", Wctxp->wctx$r_mrec.msg$w_midlen,Wctxp->wctx$r_mrec.msg$t_mid, Wctxp->wctx$r_grec.grp$b_grplen,Wctxp->wctx$r_grec.grp$t_grpname,status); return net_send_line(Wctxp->wctx$a_chan,ASCIC(NO_SUCH_ART)); } } else return net_send_line (Wctxp->wctx$a_chan,ASCIC(BAD_CMD)); /* ** Format and send standart response */ sz = sprintf(Wctxp->wctx$t_buf,ARTI_DLTD, Wctxp->wctx$r_mrec.msg$w_midlen, Wctxp->wctx$r_mrec.msg$t_mid); return net_send_line(Wctxp->wctx$a_chan,Wctxp->wctx$t_buf,sz); } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Formal support for MODE READER which some clients depends on ** ** FORMAL PARAMETERS: ** ** Wctxp: A worker's context buffer ** sz: A size of gotten command line ** ** RETURN VALUE: ** ** VMS condition code ** **-- */ int nntp_mode ( WCTX *Wctxp, ushort sz ) { sz = sprintf(Wctxp->wctx$t_buf,"200 OK"); net_send_line(Wctxp->wctx$a_chan,Wctxp->wctx$t_buf,sz); return SS$_NORMAL; }