
/*	KERMSRV		Kermit-11 server for Bitnet on node UOFT02	*/
/*			Interface to Jnet				*/
/*									*/
/*	23-APR-1985 09:02 Brian Nelson					*/
/*									*/
/*	Edits:								*/
/*									*/
/*	29-APR-1985 BDN	Fix DIR command if no argument (force *.*)	*/
/*	30-APR-1985 BDN	Check $STATUS after DIR and SEND. Add SET NOON	*/
/*	11-SEP-1985 BDN Fix bugs introduced with VAX C v2 (and bad code)*/
/*	02-Apr-1986 BDN Jnet version 2 mods				*/
/*	21-OCT-1986 BDN Change from Daemon to run as a hook.		*/
/*	21-OCT-1986 BDN PUNCH command (include :READ File Type     Date)*/
/*	 4-NOV-1986 BDN Remove trailing spaces from USER and NODE	*/
/*	10-NOV-1986 BDN Remove call to STAT(), replace with FSTAT()	*/
/*			due to bug in C RTL version of STAT.		*/
/*	20-APR-1987 BDN Treat SYSTEM messages as no-ops. Got into a loop*/
/*			with ARIZMIS sending messages telling KERMSRV	*/
/*			to send messages to explicit vaxcluster nodes.	*/
/*									*/
/*									*/
/*									*/
/*									*/
/*	$ cc kermsrv							*/
/*	$ link kermsrv+jan_sys:bitlib/lib				*/
/*	$ cop kermsrv.exe jan_sys:					*/
/*	$ run/det/proc=kermsrv jan_sys:kermsrv
/*	$ exit								*/
/*									*/
/*	Runs as a detached process for user KERMSRV.  All replies  are	*/
/*	either terminal messages or batch jobs accessing the jnet SEND	*/
/*	command.							*/

#include	<descrip.h>
#include	<stdio.h>
#include	<opcdef>
#include	<stat>

#define	then


extern char *getmsg(),*malloc(),*strchr() , *strcat() , *strcpy() , *tmpnam() ;
extern char *upcase() ;

int vmserror ;

#define	QUE_SIZE	200	/* Size of file to defer sending */

struct dsc {
		int len ;
		char *addr ;
	   } ;

FILE *infile ,*header_file ;

char sysmsg[257] ;

char kermsrv[] = "KERBATCH    " ;
char kermsrv_dir[] = "SYS$SYSROOT:[KERMSRV]" ;
char note_file[] = "SYS$SYSROOT:[KERMSRV]XFER.INFO" ;

main(argc,argv)
int argc ;
char *argv[] ;
{
	char msg[100],buffer[80],opmsg[140], *cp ;
	int msglen,status ;
	char node[10],user[10] ;
	struct dsc nodedsc = { 8,&node } ;
	struct dsc userdsc = { 8,&user } ;
	struct dsc msgbuf  = { 100,&msg } ;
	struct dsc opdsc   = { 120,&opmsg } ;
	$DESCRIPTOR(nulldsc,"\0") ;
	$DESCRIPTOR(local_node,"UOFT02") ;
	$DESCRIPTOR(local_user,"KERMSRV") ;
	$DESCRIPTOR(local_msguser,"BRIAN") ;
	int mode ;

	mode = 2 ;
	
/*	if ( jan_daemon_init(&local_user) == 0 ) then return(0) ;*/

	if ( jan_hook_init(&mode,&local_user) == 0 ) return(0) ;

	while (1) {

	  if ( (status=jan_receive_msg(&mode,&nodedsc,&userdsc
				      ,&msgbuf,&msglen))==0 )
		then  sys$hiber() ;
		else
		  if ( mode != 2 )
			{
			msgbuf.len = msglen&0377 ;
			str$concat(&opdsc,&nodedsc,&userdsc,&msgbuf,&nulldsc) ;
			sendopcom(opdsc.addr) ; 
			*(nodedsc.addr+(nodedsc.len&0377)) = 0 ;
			*(userdsc.addr+(userdsc.len&0377)) = 0 ;
			*(msgbuf.addr+ (msgbuf.len&0377))  = 0 ;
			cp = userdsc.addr ;
			while ( *cp ) { if ( *cp == ' ' ) *cp = 0; cp++; } ;
			cp = nodedsc.addr ;
			while ( *cp ) { if ( *cp == ' ' ) *cp = 0; cp++; } ;
			upcase(msgbuf.addr) ; 
			if ( ! ( *userdsc.addr == 0 || *userdsc.addr == ' ' ) )
			  docommand(nodedsc.addr,userdsc.addr,msgbuf.addr) ;
			msgbuf.len = 100 ;
			}
		  }


}





#define	TEXTFILE 1
#define	JNETDUMP 2
#define	PUNCHFILE 3

docommand(node,user,msgbuf)

char *node,*user,*msgbuf ;

{

#define	SEND	1
#define	HELP	2
#define	DIR	3
#define	SENT	4
#define VMSDUMP	5
#define PUNCH	6

	struct cmdtype	{
			char command[10] ;
			int index ;
			} ;

	static struct cmdtype cmdlist[]
				 = {	"SEND",SEND,
					"HELP",HELP,
					"DIR" ,DIR ,
					"DIRECTORY",DIR,
					"INDEX",DIR,
					"SENT",SENT,
					"VMS" ,VMSDUMP,
					"VMSDUMP",VMSDUMP,
					"SENDME",SEND,
					"SEN",SEND,
					"GET",SEND,
					"PUNCH",PUNCH,
					"PUN",PUNCH,
					"?",HELP,
					""    ,0
				   } ;
	struct cmdtype *cmdp ;
	char *s,*d,cmd[100],arg[100] ;
	int i ;

	d = cmd ;
	s = msgbuf ;
	while ( *s == ' ' ) s++ ;
	while ( *s && *s != ' ' ) *d++ = *s++ ;
	*d = 0 ;
	while ( *s == ' ' ) s++ ;
	d = arg ;
	while ( (*d = *s++) != 0 && *d != ' ') d++ ;
	if ( *d == ' ' ) {
          *d++ = '.' ;
          while ( (*d = *s++) != 0 && *d != ' ') d++ ;
	} ;
	*d = 0 ;
	i = 0 ;

	cmdp = &cmdlist[0] ;
	while ( cmdp->command[0] != 0 && strcmp(cmd,cmdp->command) != 0 )
		cmdp++ ;
	switch( cmdp->index ) {
		case SEND:
			if (strcmp(arg,"DIR") == 0 || strcmp(arg,"INDEX") == 0)
			  senddir("*.*",node,user) ;
			  else sendfile(arg,node,user,TEXTFILE) ;
			break ;
		case VMSDUMP:
			sendfile(arg,node,user,JNETDUMP) ;
			break ;
		case PUNCH:
			sendfile(arg,node,user,PUNCHFILE) ;
			break ;
		case DIR:
			senddir(arg,node,user) ;
			break ;
		case HELP:
			sendhelp(arg,node,user) ;
			break ;
		case SENT:	/* Ignore messages for file forwarding */
			break ;
		default:
			sendbad(cmd,node,user) ;
			break ;
			}
}






/*	SENDDIR		send a directory listing to remote		*/


senddir(f,node,user)
char *f ;
char *node,*user ;
{
	char tmpout[100],tmpcom[100] ;
	char cmd[133] ;
	char as_name[80],s[256],*cp,*dp ;
	struct dsc cmdline ;
	int flags,i,m ;

	m = 2 ;
	cp = f ;
	dp = s ;
	for ( i=strlen(f); i > 0; i-- ) if ( (*dp = *cp++) > ' ' ) then dp++ ;
	*dp = 0 ;
	if ( s[0] == 0 ) then strcpy(s,"*.*") ;
	if ( checksyntax(s) == 0 )
	  then	{
		sendmsg(node,user,"Bad filename syntax or access violation");
		return(vmserror=0) ;
		}
	  else	{
		strcpy(as_name,tmpnam(cmd)) ;
		strcpy(tmpcom,kermsrv_dir) ;
		strcpy(tmpout,kermsrv_dir) ;
		strcat(tmpcom,tmpnam(cmd)) ;
		strcat(tmpout,tmpnam(cmd)) ;
		strcat(tmpcom,".COM") ;
		strcat(tmpout,".LIS") ;
		if ( ( infile = fopen(tmpcom,"w") ) == NULL )
		  then	{
			vmserror = 0 ;
			return(0) ;
			}
		  else	{	
			sendmsg(node,user,"The file will be sent shortly") ;
			fputs("$ SET NOON\012",infile) ;
			fputs("$ dq = \"\"\"\012",infile) ;
			sprintf(cmd,"$ DIR/SIZE/DATE/OUT=%s KERFILES_ALL:%s\n",
				tmpout,s) ;
			fputs(cmd,infile) ;
			fputs("$ dirstatus = $STATUS\012",infile) ;
			fputs("$ if dirstatus then goto sendit\012",infile) ;
			fputs("$ erm = dq + f$mes(dirstatus) + dq\012",infile);
			sprintf(cmd,"$ SEND %s@%s 'erm\n",user,node) ;
			fputs(cmd,infile) ;
			sprintf(cmd,"$ SEN/FI NOTFOUND.TEXT %s@%s\n",user,node);
			fputs(cmd,infile) ;	
			fputs("$ exit\012",infile) ;
			fputs("$sendit:\012",infile) ;
			sprintf(cmd,"$SEND/FILE/CON/NAME=%s %s,%s %s@%s\n",
				    as_name,note_file,tmpout,user,node);
			fputs(cmd,infile) ;
			fclose(infile) ;
			vmserror = sendbatch(tmpcom,0) ;
			}
		}
	return(vmserror & 1) ;
}



sendfile(s,node,user,type)
char *s ;
char *node,*user ;
int type ;
{

struct stat *attrs,*getstat() ;
char tmpcom[100],tmpcmd[100],*cp ;
char cmd[133] ,*cmd_name,read_header[100],read_header_name[100],*fn;
int flags,i,wild ;
char punch_com[]= "$ SEND/FIL/CONC/NAM=%s SYS$SYSROOT:[KERMSRV]%s.HDR,KER:";
char text_com[] = "$ SEND/FILE KER:" ;
char bin_com[]  = "$ SEND/FILE/VMSDUMP KERFILES_ALL:" ;
char notfound[] = "$ SEND/FILE SYS$SYSROOT:[KERMSRV]NOTFOUND.TEXT " ;
char sendsoon[] = "The file(s) will be sent shortly" ;
char sendlate[] = "Due to size of this file, it will be sent at 23:00";
char *chk_time[]= { "$ now = f$cvt(f$tim())\n",
		    "$ endtime = f$cvt(\"08:00:00.0\")\n",
		    "$ if now .ges. f$cvt(\"22:00:00.0\") -\n",
		    "     then endtime = f$cvt(\"TOMORROW+08:00\")\n",
		    "$ if now .lts. endtime then goto doit\n",
		    "" } ;


	if ( checksyntax(s) == 0 )
	  then	{
		sendmsg(node,user,"Bad filename syntax or access violation");
		return(vmserror=0) ;
		} ;
	wild = 0 ;
	if ( strchr(s,'*') != 0 || strchr(s,'%') != 0 )
	  then	{
		if ( type == PUNCHFILE ) {
		  sendmsg(node,user,"Wildcards are not allowed with the PUNCH");
		  sendmsg(node,user,"command.  Use the SEND command for this.") ;;
		  }
		 else {
		  sendmsg(node,user,"Your request will not be honored until") ;
		  sendmsg(node,user,"23:00 hours EST since your filename is") ;
		  sendmsg(node,user,"wildcarded. Please do not abuse policy") ;
		  sendmsg(node,user,"by resubmitting individual filenames.") ;
		  wild = 1 ;
		  } ;
		} ;
	if ( strchr(s,'.') == 0 )
	  then	{
		sendmsg(node,user,"Please include an explicit filetype") ;
		sendmsg(node,user,"As in: SEND K11*.MAC or SEND AA*.TXT") ;
		return(0) ;
		} ;
	if ( strcmp(s,"*.*") == 0 )
	  then	{
	    sendmsg(node,user,"Please wildcard FILENAME or FILETYPE only");
	    sendmsg(node,user,"as there are over 60,000 blocks of Kermit");
	    sendmsg(node,user,"files on this node") ;
	    return(0) ;
	  }
	  else	if ( findfile(s,type) == 0 )
	      then sendmsg(node,user,getmsg(vmserror)) ;
	      else {
		sprintf(cmd,"%s%s","KERFILES_ALL:",s) ;
		if ( wild == 0 ) {
		  attrs = getstat(cmd) ;
		  if ( attrs && (wild = (attrs->st_size/512 > QUE_SIZE)) )
		    then sendmsg(node,user,sendlate) ;
		    else sendmsg(node,user,sendsoon) ; 
		} ;
		strcpy(tmpcom,kermsrv_dir) ;
		strcat(tmpcom,tmpnam(cmd)) ;
		strcat(tmpcom,".COM") ;


		switch( type ) {
		  case JNETDUMP:
			cmd_name = bin_com ;
			break ;
		  case TEXTFILE:
			cmd_name = text_com ;
			break ;
		  case PUNCHFILE:
			sprintf(read_header_name,"%s%s%s",kermsrv_dir,
				tmpnam(read_header),".HDR");
			header_file = fopen(read_header_name,"w") ;
			fn = s ;
			cp = read_header_name ;
			while ( *cp = *fn++ ) 
			  { if (*cp == '.') *cp = ' '; cp++ ;} ;
			if ( attrs )
			  sprintf(tmpcmd,":READ %-30.30sA1 %s",
				  read_header_name,ctime(&attrs->st_ctime)) ;
			  else
			    sprintf(tmpcmd,":READ  %s",read_header_name) ;
			fputs(tmpcmd,header_file) ;
			fclose(header_file) ;
			sprintf(tmpcmd,punch_com,s,read_header) ;
			cmd_name = tmpcmd ;
			break ;
		} ;

		if ( ( infile = fopen(tmpcom,"w") ) == NULL )
		  then	{
			vmserror = 0 ;
			return(0) ;
			}
		  else	{	
			fputs("$ SET NOON\012",infile) ;
			fputs("$ dq = \"\"\"\012",infile) ;
			if ( wild ) {
			  for ( i=0; *chk_time[i] != '\0';i++ )
			    fputs(chk_time[i],infile) ;
			  sprintf(cmd,"$ SUB/AFT=22:00 %s\n",tmpcom);
			  fputs(cmd,infile) ;
			  fputs("$ EXIT\n",infile) ;
			  fputs("$DOIT:\n",infile) ;
			} ;
			sprintf(cmd,"%s%s %s@%s\n",cmd_name,s,user,node);
			fputs(cmd,infile) ;
			fputs("$ sts = $STATUS\012",infile) ;
			fputs("$ if sts .eq. 1 then goto ex\012",infile) ;
			fputs("$ if sts .eq. %X7FF42D70 then goto ex\012",
			      infile) ;
			fputs("$ erm = dq+f$mes(sts)+dq\012",infile);
			sprintf(cmd,"$ SEND %s@%s 'erm\n",user,node);
			fputs(cmd,infile) ;
			strcpy(cmd,notfound);
			strcat(strcat(strcat(cmd,user),"@"),node) ;
			fputs(strcat(cmd,"\012"),infile) ;
			fputs("$ex:\012",infile) ;
			fputs("$ exit\012",infile) ;
			fclose(infile) ;
			vmserror = sendbatch(tmpcom,wild) ;
			}
		}
	return(vmserror & 1) ;
}


sendhelp(s,node,user)
char *s ;
char *node,*user ;

{

sendmsg(node,user,"The commands are SEND file,  DIR file, PUNCH file and");
sendmsg(node,user,"VMSDUMP file. PUNCH adds a :READ header into the file");
sendmsg(node,user,"VMSDUMP file  can be used to get executable files in") ;
sendmsg(node,user,"jnet VMSDUMP format  (.EXE,.TSK,.SAV files) and will") ;
sendmsg(node,user,"provide protection  from nonstandard EBCDIC  tables.") ;
sendmsg(node,user,"All filenames must be in FILENAME.TYPE or  FILE TYPE") ;
sendmsg(node,user,"format.") ;
}


sendbad(s,node,user)
char *s ;
char *node,*user ;
{
	struct dsc nd,ud,em ;
	char msg[133] ;

	strcpy(msg,"Unknown command UOFT02 Kermsrv - ") ;
	strcat(msg,s) ;
	sendmsg(node,user,msg) ;
}





sendmsg(node,user,msg)
char *node,*user,*msg ;
{
	struct dsc nd,ud,md ;
	int mode ;

	mode = 2 ;
	md.addr = msg ;
	md.len  = strlen(msg) ;
	nd.addr = node ;
	nd.len  = strlen(node) ;
	ud.addr = user ;
	ud.len  = strlen(user) ;
	jan_send_msg(&mode,&nd,&ud,&md) ;
}


checksyntax(s)
char *s ;
{
	extern char *strchr() ;

	return(    strchr(s,'[') == 0 && strchr(s,':') == 0
		&& strchr(s,'<') == 0 ) ;
}

	





findfile(f,type)
char *f ;
int type ;
{

/*	Verify that at least one file will match the filename passed	*/
/*	so that we can have some assurance that the batch job we submit	*/
/*	to do the actual send will succeed.				*/

	char fname[256] ;
	struct dsc$descriptor_s srcbuf ;
	struct dsc$descriptor_d resbuf ;
	int context,status ;

	$DESCRIPTOR(def_spec,"KER:*.*") ;
	$DESCRIPTOR(alt_spec,"KERFILES_ALL:*.*") ;

	strcpy(fname,f) ;
	if ( strchr(fname,'.') == 0 ) then strcat(fname,"*.*") ;
	context = 0 ;
	srcbuf.dsc$w_length = strlen( fname ) ;
	srcbuf.dsc$b_class  = DSC$K_CLASS_S ;
	srcbuf.dsc$b_dtype  = DSC$K_DTYPE_T ;
	srcbuf.dsc$a_pointer = fname ;

	resbuf.dsc$w_length = 0 ;
	resbuf.dsc$b_class  = DSC$K_CLASS_S ;
	resbuf.dsc$b_dtype  = DSC$K_DTYPE_D ;
	resbuf.dsc$a_pointer = 0 ;
	
	switch (type) {
	  case PUNCHFILE:
	  case TEXTFILE:
		vmserror = lib$find_file(&srcbuf,&resbuf,&context,&def_spec) ;
		break ;
	  case JNETDUMP:
		vmserror = lib$find_file(&srcbuf,&resbuf,&context,&alt_spec) ;
		break ;
	} ;
	lib$find_file_end(&context) ;
	return( vmserror & 1 ) ;
	
}




sendopcomdsc(s)
struct dsc *s ;
{
	char *m ;
	int slen ;

	slen = s->len & 0xFFFF ;
	if ( (m=malloc(slen + 1)) != 0 )
	  then	{
		strncpy(m,s->addr,slen) ;
		m[slen] = 0 ;
		sendopcom(m) ;
		free(m) ;
		} ;
}

sendopcom(s)
char *s ;

/*	Send a asciz message to opcom					*/
{

	struct opcfmt {
			unsigned char type ;
			short int target_0_15 ;
			unsigned char target_16_23 ;
			unsigned long rqstid ;
			char msg[80] ;
		      } ;
	struct dsc    {
			long length ;
			struct opcfmt *addr ;
		      } ;

	struct opcfmt msgdsc ;
	struct dsc opmsg = { 80,&msgdsc } ;
	int reply_chan,status ;

	reply_chan = 0 ;
	msgdsc.type = OPC$_RQ_RQST ;
	msgdsc.target_0_15 = OPC$M_NM_CENTRL ;
	msgdsc.target_16_23 = 0 ;
	msgdsc.rqstid = 0 ;
	strcpy(msgdsc.msg,s) ;
	opmsg.length = 8 + strlen(msgdsc.msg) ;
	opmsg.addr = &msgdsc ;
	return( (vmserror=sys$sndopr(&opmsg,reply_chan)) & 1 ) ;

}	

char *getmsg(n)
int n ;
{
	struct dsc msgd ;
	int mlen ;
	char junk[4] ;

	mlen = 0 ;
	msgd.len = 256 ;
	msgd.addr = sysmsg ;
	sys$getmsg(n,&mlen,&msgd,0,&junk) ;
	sysmsg[mlen] = 0 ;
	return( &sysmsg ) ;
}
	




sendbatch(f,late)
char *f ;
int late ;
{

#define	SJC$_AFTER_TIME		3
#define	SJC$_ENTER_FILE		19
#define	SJC$_QUEUE		134
#define	SJC$_FILE_SPECIFICATION	42
#define	SJC$_NO_LOG_SPOOL	101
#define	SJC$_NO_DELETE_FILE	25
#define	SJC$_USERNAME		159

	struct itmlst	{
			short int buflen;
			short int code	;
			char *bufadr	;
			int *retlen	;
			} ;

	$DESCRIPTOR(latetime,"-- 23:00:00.00") ;
	static struct itmlst dobatch[10] ;
	int status,iosb[2],abstime[2] ;

	static char batch[] = "SYS$KERMIT_BATCH" ;

	sys$bintim(&latetime,&abstime) ;
	dobatch[0].buflen = strlen(batch) ;
	dobatch[0].code   = SJC$_QUEUE ;
	dobatch[0].bufadr = &batch ;
	dobatch[0].retlen = 0 ;
	dobatch[1].buflen = strlen(f) ;
	dobatch[1].code   = SJC$_FILE_SPECIFICATION ;
	dobatch[1].bufadr = f ;
	dobatch[1].retlen = 0 ;
	dobatch[2].buflen = 0 ;
	dobatch[2].code   = SJC$_NO_LOG_SPOOL ;
	dobatch[2].bufadr = 0 ;
	dobatch[2].retlen = 0 ;
	dobatch[3].buflen = 12 ;
	dobatch[3].code   = SJC$_USERNAME ;
	dobatch[3].bufadr = &kermsrv ;
	dobatch[3].retlen = 0 ;
	if (late)
	  then	{
		dobatch[4].buflen = 8 ;
		dobatch[4].code   = SJC$_AFTER_TIME ;
		dobatch[4].bufadr = &abstime ;
		dobatch[4].retlen = 0 ;
		}
	  else	{
		dobatch[4].buflen = 0 ;
		dobatch[4].code   = 0 ;
		} ;
	dobatch[5].buflen = 0 ;
	dobatch[5].code   = 0 ;

	status = sys$sndjbcw(0,SJC$_ENTER_FILE,0,&dobatch,&iosb,0,0) ;
	return(iosb[0]) ;
}
		





char *upcase(s)
char *s ;
{
	char *cp ;
	cp = s ;
	while ( *cp ) { *cp = toupper(*cp) ; cp++ ; } ;
	return(s) ;
}

char *lowcase(s)
char *s ;
{
	char *cp ;
	cp = s ;
	while ( *cp ) { *cp = tolower(*cp) ; cp++ ; } ;
	return(s) ;
}



/*	Due to a bug in the CRTL we will have to open, FSTAT and then
	close. The STAT call never deassigns the I/O channel that the
	file is accessed on.
*/

#include	<file>

struct stat *getstat(f)
char *f ;
{

	static struct stat attrs, *ret_attrs ;
	unsigned int file_d, f_size ;

/*	if ( stat(f,&attrs) ) return(0) ;
	  else return(&attrs) ; */

	if ( ( file_d = open(f,O_RDONLY,0) ) == -1 ) return(0) ;
	if ( fstat(file_d,&attrs) ) ret_attrs == 0 ;
	  else ret_attrs = &attrs ;
	close( file_d ) ;
	return( ret_attrs ) ;
}




