/*************************************************************************/
/*                                                                       */
/*  REXX Control Script for Modem Handling and User Access Control       */
/*                                                                       */
/* (C) 1995, 1996   Axel Mueller (amueller@stargate.rz.fh-offenburg.de)  */
/*     Version 1.11 Last modified: 10/01/96                              */
/*************************************************************************/

/* ##########################*/
/* ModemIndex = 1 means COM1 */   
/* ModemIndex = 2 means COM2 */
/* ModemIndex = 3 means COM3 */
/* ModemIndex = 4 means COM4 */
/* ##########################*/

parse arg ModemIndex

call on error
call on NOTREADY name eof_reached


/************************************************************************/
/*                                                                      */
/* Setup                                                                */
/*                                                                      */
/************************************************************************/

/* boolean variables */
True			= 1
False		= 0


/* INI file for settings */



/**************/
/* Path setup */
/**************/

/* Root directory: c:\userctrl */
Path			= value('USERCTRLDIR',,'OS2ENVIRONMENT')

INIFile = Path'\userctrl.ini'

/* Path to log files */
LogPath		= read_from_INI(LogPath)
UserList        = read_from_INI(UserListFile)

/* Path to IBMs TCP/IP ETC directory */
ETCPath		= value('ETC',,'OS2ENVIRONMENT')


/* ##### Log files ##### */

/* Log file for all messages generated by this REXX script */
SystemLog	= LogPath'\UserCtl'ModemIndex'.log'


/* ##### Files needed for login procedure ##### */

/* File to be send to modem user after CONNECT but before login */
WelcomeFile	= read_from_INI(WelcomeFile)


/* ##### TCP/IP related ##### */

/* Configuration file for PPP driver of IBM's TCP/IP */
PPP_CFGFile	= ETCPath'\ppp'ModemIndex'.cfg'


/* read settings from INI file */
FirstModemCOM		= read_from_INI(FirstModemCOM)
LastModemCOM			= read_from_INI(LastModemCOM)
ModemInitString		= read_from_INI(ModemInitString)
ShowWelcomeScreen	= read_from_INI(ShowWelcomeScreen)
ShowLastLogin		= read_from_INI(ShowLastLogin)
ShowMailboxStatus	= read_from_INI(ShowMailboxStatus)
ShowLineUsage		= read_from_INI(ShowLineUsage)

if ShowMailboxStatus then
 do
	/* Directory under which all the POP mail directories are situated */
	MailPath		= value('MAILDIR',,'OS2ENVIRONMENT')
 end




/******************/
/* COM port setup */
/******************/


PortName		= 'com'ModemIndex
PortHandle	= ''

WriteLim		= 50
ReadLim		= 50                     /* 0,5 seconds timout */
/* Flags1		= '00001001' */
/* Flags2		= '10100000' */
/* Flags3		= '11010010' */
ErrChar		= '00'
BrkChar		= '00'
XonChar		= '11'
XoffChar		= '13'

Timeout		= 15                     /* Wait 15 seconds for com port */
UserMaxLen	= 25
PwdMaxLen	= 25

crlf			= D2C(13)''D2C(10)


'@echo off'
'cd' Path

call flagfile('START')



/************************************************************************/
/*********************                ***********************************/
/********************* Main function  ***********************************/
/*********************                ***********************************/
/************************************************************************/
do forever

	/* open COM port */
	call open_com_port

	/* initialize  variables */
	dummy1=''
	dummy2=''
	dummy3=''
	dummy4=''
	dummy5=''
	dummy6=''
	Flags1=''
	Flags2=''
	Flags3=''

	/* get original Device Control Block (dcb) */
	rc = RxIOCtlGetDcbParm( PortHandle, 'dummy1', 'dummy2', 'Flags1', 'Flags2', 'Flags3', 'dummy3', 'dummy4', 'dummy5', 'dummy6' )
	if rc <> 0 then call log('MAINFUNCT : ###ERROR### RxIOCtlGetDcbParm failed with rc='rc)

	Flags1 = X2B(Flags1)
	Flags2 = X2B(Flags2)
	Flags3 = X2B(Flags3)

	/* set modified Device Control Block (dcb) */
	rc = RxIOCtlSetDcbParm( PortHandle, WriteLim, ReadLim, Flags1, Flags2, Flags3, ErrChar, BrkChar, XonChar, XoffChar )
	if rc <> 0 then call log('MAINFUNCT : ###ERROR### RxIOCtlSetDcbParm failed with rc='rc)

	/* initialize modem */
	call modem_init
if m_init=1 then 
do   
	/* create flag file indicating that the system is waiting for caller */
	call flagfile('WAIT')
   
	/* wait for input from buffer; RING will be assumed */
	Buffer = ""
	Ring = False
 	rc = RxIOCtlRead(PortHandle, 0, -1, 'Buffer')
	if rc <> 0 then call log('MAINFUNCT : ###ERROR### RxIOCtlRead failed with rc='rc)
	BufferEmpty = True
	do while BufferEmpty
		if length(Buffer) > 3 then
			BufferEmpty = False
		else
			do
				Buffer = ''
				rc = RxIOCtlRead(PortHandle, 0, 1, 'Buffer')
				if rc <> 0 then
					call log('MAINFUNCT : ###ERROR### RxIOCtlRead failed with rc='rc)
			end
	end
	Buffer = strip(Buffer,,D2C(10))
	Buffer = strip(Buffer,,D2C(13))

	/* create flag file indicating that login is in process */
	call flagfile('LOGIN')

	if Buffer == 'RING' then
		do               /* pick up the line */
			Rem = 0
			rc = RxIOCtlWrite( PortHandle, 'ATA'||D2C(13), 'Rem' )
			if rc <> 0 then
				call log('MAINFUNCT : ###ERROR### RxIOCtlWrite (ATA) failed with rc='rc)
       
			/* wait for CONNECT or NO CARRIER; Timeout = 70 seconds */
			Connect = False
			Cycle = 0
			MaxCycle = 70 * 100 / ReadLim
			do while (Connect == False) & (Cycle < MaxCycle)
				Cycle = Cycle + 1
				rc = RxIOCtlRead( PortHandle, 0, 1, 'Buffer' )
				if length(Buffer) > 3 then
					do
						Buffer = strip(Buffer,,D2C(10))
						Buffer = strip(Buffer,,D2C(13))
						if left(Buffer, 7) == "CONNECT" then
								Connect = True
						if Buffer == "NO CARRIER" then
								Cycle = MaxCycle
					end
			end

			call check_carrier    
			if Carrier then
				call handle_login           /* start login procedure */

		end
end

	/* close COM-Port */
	call close_com_port

	/* wait 15 seconds to force line down */
	call SysSleep 15

end
/* exit programm */
exit




/************************************************************************/
/*********************                ***********************************/
/*********************   Functions    ***********************************/
/*********************                ***********************************/
/************************************************************************/



/************************************************************************/
/* handle_login()                                                       */
/*                                                                      */
/* main fuction handling the login procedure                            */
/************************************************************************/

handle_login:
          
	call check_carrier    
	if Carrier then
		if ShowWelcomeScreen then
			call welcome_screen					/* send welcome screen */

	call check_carrier
	LoginOkay = False
	Attempt = 1
	do while (LoginOkay = False) & (Attempt<=3) & Carrier

		call check_carrier
		if Carrier then
			User = get_user()

		call check_carrier
		if Carrier then
			Password = get_password()

		call flush_buffer

		call check_buffer
		call check_carrier
		if Carrier & BufferIsEmpty then
			LoginOkay = check( User, Password )

		Attempt = Attempt + 1

	end
    
	if LoginOkay = True then
		do
			call check_carrier
			if Carrier then
				do

					call send( '$%' )
					call send( crlf )

					/* built file name for user log */
					UserLog = LogPath'\'substr(date(european),4,2)||substr(date(european),7,2)'_'ModemIndex'.log'

					StartTime = time(normal)
					StartDate = date(european)
					rc = time('R')

					'PPP file' PPP_CFGFile

					LoginTime = trunc(time('R'),0)

					EndTime = time(normal)


					/* Log connection data */
					LogString = substr(User,1,20)||StartDate'   'StartTime'   'EndTime
					rc = lineout( UserLog, LogString )
					rc = lineout( UserLog )

					rc = RxPrfAddLoginTime(User, LoginTime)

					/* create flag file indicating that a user is active */
					call flagfile('LGOUT')

				end
		end
	else
            do
                call hangup
		call log( 'HNDLELOGIN: Login failed. PPP was not started.' )
            end 
	return



/************************************************************************/
/* welcome_screen ()                                                    */
/*                                                                      */
/* sends a text file (Welcom Screen) to modem user BEFORE user and      */
/* password are requested                                               */
/************************************************************************/

welcome_screen:


	if length(stream(WelcomeFile, 'c', 'query exists')) > 0 then
		do
			Line = linein(WelcomeFile, 1, 1)
			call check_carrier    
			LineCount = 0
			eof = FALSE
			do while (\ eof) & Carrier
				Line = linein(WelcomeFile)

				DatePos = pos('$DATE$', Line)
				if DatePos > 0 then
					Line = substr(Line, 1, DatePos-1)||date('L')||substr(Line, DatePos+6)
				TimePos = pos('$TIME$', Line)
				if TimePos > 0 then
					Line = substr(Line, 1, TimePos-1)||time('N')||substr(Line, TimePos+6)

				call check_carrier    
				if Carrier then
					do
						call send(Line)
						call send(crlf)
						LineCount = LineCount + 1
					end
			end
			rc = lineout(WelcomeFile)
			
		end

    return



/************************************************************************/
/* get_user()                                                           */
/*                                                                      */
/* requests the username from modem user; there is a timeout after      */
/* which the login procedure is canceled and the line is dropped        */
/* (set in "Timeout"); there is also a maximum number of characters     */
/* read from com port for username (set in "UserMaxLen")                */
/************************************************************************/

get_user:

	call check_buffer
	if BufferIsEmpty then
		call send('   Login:')

	Dummy = 0
	User = ''
	UserLen = 0
	Cycle = 0
	UserTimeout = False
	TooLong = False
	Buffer = ''
	do while (Buffer <> D2C(13)) & (UserTimeout == False) & (TooLong == False) & Carrier & BufferIsEmpty
		call check_carrier
		if Carrier then
			do
				Buffer = ''
				rc = RxIOCtlReadChar( PortHandle, 1, 'Buffer' )
				select
					when rc = 0 then
						if UserLen < UserMaxLen then
							do
								User = User||Buffer
								UserLen = UserLen + 1
								Cycle = 0
							end
						else
							TooLong = True
					when rc = -1 then                 /* send no echo for CR */
						Dummy = 1
					when rc = -2 then
						Cycle = Cycle + 1
				end
				MaxCycle = Timeout * 100 / ReadLim
				if Cycle > MaxCycle then
					UserTimeout = True

				call check_carrier
			end
	end

	if Carrier & BufferIsEmpty then
		call send( crlf )

	if UserTimeout = True then
		do
			call check_carrier
			if Carrier then
				call hangup
		end
	if TooLong = True then
		do
			call check_carrier
			if Carrier then
				call hangup
		end
	else
		if Carrier then
			call log( 'GETUSER   : User =' User )

	return User



/************************************************************************/
/* get_password()                                                       */
/*                                                                      */
/* requests the password from modem user; there is a timeout after      */
/* which the login procedure is canceled and the line is dropped        */
/* (set in "Timeout"); there is also a maximum number of characters     */
/* read from com port for username (set in "PwdMaxLen")                 */
/************************************************************************/

get_password:

	call check_buffer
	if BufferIsEmpty then
		call send('Password:')

	Dummy = 0
	Password = ''
	PwdLen = 0
	Cycle = 0
	PwdTimeout = False
	TooLong = False
	Buffer = ''
	do while (Buffer <> D2C(13)) & (PwdTimeout == False) & (TooLong == False) & Carrier & BufferIsEmpty
		call check_carrier
		if Carrier then
			do
				Buffer = ''
				rc = RxIOCtlReadChar( PortHandle, -1, 'Buffer' )
				select
					when rc = 0 then
						if PwdLen < PwdMaxLen then
							do
								Password = Password||Buffer
								PwdLen = PwdLen + 1
								Cycle = 0
							end
						else
							TooLong = True
					when rc = -1 then                 /* send no echo for CR */
						Dummy = 1
					when rc = -2 then
						Cycle = Cycle + 1
				end
				MaxCycle = Timeout * 100 / ReadLim
				if Cycle > MaxCycle then
					PwdTimeout = True

				call check_carrier
			end
	end

	if Carrier & BufferIsEmpty then
		call send( crlf )

	if PwdTimeout = True then
		do
			call check_carrier
			if Carrier then
				call hangup
		end
	if TooLong = True then
		do
			call check_carrier
			if Carrier then
				call hangup
		end

	return Password



/************************************************************************/
/* check(User,Password)                                                 */
/*                                                                      */
/* checks username and password given by modem user                     */
/************************************************************************/

check:

    parse arg User, Password

        LoginOkay=0
	if (RxPrfCheckPassword(User, Password)) then 
        do
        if(find_user(User)>=ModemIndex) then LoginOkay=1
        end
	if LoginOkay then
		do
			 call send( crlf )
			 call send('Login OK.')
			 call send( crlf )

			 if ShowLastLogin then
				do
				 call send( crlf )
				 LastLogin = 'Last logout on 'RxPrfGetLastLoginDate(User)
				 call send(LastLogin)
				 call send( crlf )
				end

			 if ShowMailboxStatus then
				do
				 UserMail = MailPath'\'User'\*.msg'   
				 call SysFileTree UserMail, 'MailFiles', 'O'
				 call send( crlf )
				 if MailFiles.0 > 0 then
					call send('*** You have 'MailFiles.0' new mail(s) ***')
				 else
					call send('No new mail.')

				 call send( crlf )
				end

			 call send( crlf )

			 if ShowLineUsage then
				do
				 UserRealName = RxPrfGetRealName(User)

				 /* create flag file indicating that a user is active */
				 call flagfile('ACTIV')

				 ActiveUsers = 0
				 do Index=FirstModemCOM to LastModemCOM
					ActivFlagFile = Path'\L'Index'_ACTIV.FLG'
					if length(stream(ActivFlagFile, 'c', 'query exists')) > 0 then
						do
						 ActiveUsers = ActiveUsers + 1
						 Line = linein(ActivFlagFile)
						 rc = lineout(ActivFlagFile)
						end
					else
						Line = 'not used'

					call send( 'Modem 'Index' : 'Line )
					call send( crlf )
				 end
				 call send( crlf )
				end
		end
	else
		do
			call log( 'CHECK     : !!WARNING!! User authentication failed' )
			call send('Login failed.')
			call send( crlf )

			FailLog = LogPath'\'substr(date(european),4,2)||substr(date(european),7,2)'_'ModemIndex'.log'
			LogString = date(european)'   'time(normal)'   U='User'   P ='Password

			rc = lineout( FailLog, LogString )
			rc = lineout( FailLog )

		end

    return LoginOkay



/************************************************************************/
/* send ( sendstring )                                                  */
/*                                                                      */
/* send a character string off to the modem                             */
/************************************************************************/

send:

	parse arg SendString

	Rem = 0
	call check_carrier
	if Carrier then
		do
			rc = RxIOCtlWrite( PortHandle, SendString, 'Rem' )
			if rc <> 0 then
				say 'SEND      : ###ERROR### RxIOCtlWrite failed with rc='rc
		end
	else
		do
			if SendString == crlf then
				say 'SEND      : !!WARNING!! crlf not sent'
			else
				say 'SEND      : !!WARNING!! 'SendString' not sent'

		end
	return



/************************************************************************/
/* log ( logstring )                                                    */
/*                                                                      */
/* logs the main activities of this REXX script                         */
/************************************************************************/

log:

	parse arg LogString
	LogString = strip(LogString,,D2C(13))

	rc = lineout( SystemLog, date(european)'-'time(normal)'' LogString )
	if rc <> 0 then
		say 'LOG       : ###ERROR### lineout failed with rc='rc

	say date(european)' 'time(normal)' 'left(LogString,61)
	rc = lineout( SystemLog)

	return



/************************************************************************/
/* flagfile ( Flag )                                                    */
/*                                                                      */
/* creates a flag file indicating current status of UserCtrl program    */
/************************************************************************/

flagfile:

	parse arg Flag

	/* delete old flag file */
	FlagFile = Path'\L'ModemIndex'_START.FLG'
	if length(stream(FlagFile, 'c', 'query exists')) > 0 then
		rc = SysFileDelete(FlagFile)
	FlagFile = Path'\L'ModemIndex'_WAIT.FLG'
	if length(stream(FlagFile, 'c', 'query exists')) > 0 then
		rc = SysFileDelete(FlagFile)
	FlagFile = Path'\L'ModemIndex'_LOGIN.FLG'
	if length(stream(FlagFile, 'c', 'query exists')) > 0 then
		rc = SysFileDelete(FlagFile)
	FlagFile = Path'\L'ModemIndex'_ACTIV.FLG'
	if length(stream(FlagFile, 'c', 'query exists')) > 0 then
		rc = SysFileDelete(FlagFile)
	FlagFile = Path'\L'ModemIndex'_LGOUT.FLG'
	if length(stream(FlagFile, 'c', 'query exists')) > 0 then
		rc = SysFileDelete(FlagFile)


	FlagFile = Path'\L'ModemIndex'_'Flag'.FLG'
	if Flag == "ACTIV" then
		rc = lineout(FlagFile, UserRealName)

	rc = lineout(FlagFile)

	return



/************************************************************************/
/* read_from_INI(Parameter)                                             */
/*                                                                      */
/* Reads settings from INI file                                         */
/************************************************************************/


read_from_INI:
	parse arg Parameter

	if length(stream(INIFile, 'c', 'query exists')) > 0 then
		do
			Finished = FALSE
			do while Finished == FALSE
				Line = linein(INIFile)
				parse upper value Line with Parm . ParmValue
				if length(Line) > 0 then
					do
						if Parm == Parameter then
							do
								ParameterValue = ParmValue
								Finished = TRUE
							end
					end
				else
					do
						say 'ERROR: Paramter 'Parameter' could not be found in 'INIFile'!'
						exit
					end
			end
		end
	else
		do
			say 'ERROR: 'INIFile' not found!'
			exit
		end

	rc = lineout(INIFile)

	return ParameterValue







/************************************************************************/
/* open_com_port()                                                      */
/*                                                                      */
/* opens the com port                                                   */
/************************************************************************/


open_com_port:

	rc = RxIOCtlOpenPort(PortName, 'PortHandle')
	if rc <> 0 then
		do
			call log('COMOPEN   : ###ERROR### 'PortName' RxIOCtlOpenPort failed with rc='rc)
			exit
		end

	return



/************************************************************************/
/* close_com_port()                                                     */
/*                                                                      */
/* closes the com port                                                  */
/************************************************************************/


close_com_port:

	rc = RxIOCtlClosePort( PortHandle )
	if rc <> 0 then
		do
			call log('COMCLOSE  : ###ERROR### 'PortName' RxIOCtlClosePort failed with rc='rc)
			exit
		end

	return



/************************************************************************/
/* modem_init ()                                                        */
/*                                                                      */
/* initializes the modem; note that the modem should be set to NO       */
/* auto answer (ATS0=0) since the script picks up the line if it        */
/* detects a RING signal. Therefore the modem has to be set for         */
/* echo. (ATE=1)                                                        */
/************************************************************************/


modem_init:


	/* ATZ		Initialize modem */
	/* Q0		Modem returns result code */
	/* V1     	Display result codes in verbose form */
	/* S0=0		disable auto answer */
	/* S3=13		Carriage return = ASCII 13 */
	/* S4=10		Line feed = ASCII 10 */
	/* S7=60    number of seconds the modem waits for a carrier */
	/* S10=7    wait 7/10 seconds after loss of carrier before hangup */

	Rem = 0
	rc = RxIOCtlWrite( PortHandle, 'ATZ'||D2C(13), 'Rem' )
	if rc <> 0 then
		call log('MODEM_INIT: ###MODEM ERROR### RxIOCtlWrite failed with rc='rc)
	Buffer = ''
	rz = RxIOCtlRead(PortHandle, 0,1, 'Buffer')
	rz = RxIOCtlRead(PortHandle, 0,1, 'Buffer')
	call flush_buffer
	rc = RxIOCtlWrite( PortHandle, ModemInitString||D2C(13), 'Rem' )
	if rc <> 0 then
		call log('MODEM_INIT: ###MODEM ERROR### RxIOCtlWrite failed with rc='rc)

	call SysSleep 1

	InitNotOK = True
	do while InitNotOK

		BufferCheck = 1
		do while BufferCheck > 0
			Buffer = ''
			rz = RxIOCtlRead(PortHandle, 0,1, 'Buffer')
			if rz <> 0 then do
				call log('MODEM_INIT: ###MODEM ERROR### RxIOCtlRead failed with rc='rz)
				m_init=0
			 	return
      				end
					
			else
				if length(Buffer) > 3 then BufferCheck = 0
		end
		if Buffer == "OK"||crlf then InitNotOK = False
	end
 m_init=1
	return



/************************************************************************/
/* hangup()                                                             */
/*                                                                      */
/* forces the modem to drop the line                                    */
/************************************************************************/

hangup:

	call flush_buffer

	Rem = 0
	rc = RxIOCtlWrite(PortHandle, '+++', 'Rem')
	if rc <> 0 then
		call log('HANGUP    : ###ERROR### Could not send escape sequence (+++) to modem')


	call SysSleep 2

	Rem = 0
	rc = RxIOCtlWrite(PortHandle, 'ATH0'||crlf, 'Rem')
	if rc <> 0 then
		call log('HANGUP    : ###ERROR### Could not send hangup command (ATH0) to modem')

	call SysSleep 10

	return


/************************************************************************/
/* check_buffer ()                                                      */
/*                                                                      */
/* checks the input buffer of the com port for characters               */
/************************************************************************/

check_buffer:

	/* there should be NO characters in the input buffer */
	RxCount = 0
	RxSize = 0
	rc = RxIOCtlGetRxCount(PortHandle, 'RxCount', 'RxSize')
	if RxCount > 0 then
		do
			BufferIsEmpty = False
		end

	return



/************************************************************************/
/* flush_buffer ()                                                      */
/*                                                                      */
/* flushes the input buffer                                             */
/************************************************************************/

flush_buffer:

	rc = RxIOCtlFlushInput( PortHandle )


	BufferIsEmpty = True

	return



/************************************************************************/
/* check_carrier ()                                                     */
/*                                                                      */
/* checks if the line is up and there is a carrier                      */
/************************************************************************/

check_carrier:

	rc = RxIOCtlDetectCarrier( PortHandle )
	if rc = 0 then
		Carrier = True
	else
		do
			Carrier = False
		end

	return



/************************************************************************/
/* eof_reached                                                          */
/*                                                                      */
/*                                                                      */
/************************************************************************/

eof_reached:

	eof = TRUE
	return



/************************************************************************/
/* error ()                                                             */
/*                                                                      */
/*                                                                      */
/************************************************************************/

error:

	call log('ERROR     : ###ERROR### error() called')
	call RxIOCtlClosePort PortHandle

	exit

/************************************************************************/
/* find_user                                                            */
/*                                                                      */
/* find user in list                                                    */
/************************************************************************/


find_user:
	parse arg Parameter

	if length(stream(UserList, 'c', 'query exists')) > 0 then
		do
			Finished = FALSE
			do while Finished == FALSE
				Line = linein(UserList)
				parse value Line with Parm . ParmValue
				if length(Line) > 0 then
					do
						if Parm == Parameter then
							do
								ParameterValue = ParmValue
								Finished = TRUE
							end
					end
				else
					do
		                 	rc = lineout(UserList)
			                 return 0
					end
			end
		end
	else
		do
			say 'ERROR: 'UserList' not found!'
			exit
		end

	rc = lineout(UserList)

	return ParameterValue

