/*
** Module   :SETUP.CMD
** Abstract :SafeFire Firewall 1.1 Install/Remove script
**
** Copyright (C) Link Guard Solutions Ltd.
**
** Log: Fri  07/01/2000 Created
**      Tue  11/01/2000 rev. 1
*/

/*-----------------------------------------------------------------------
** Variables
*/

rc=RxFuncAdd('BootDrive', 'bootdrv', 'BootDrive')

boot_drive=BootDrive()':'
mptn_drive=''
tab='9'x
config_sys=''
protocol_ini=''
iface='LAN0'
work_mode='UNDEF'
lan_list='LAN0 LAN1 LAN2 LAN3 LAN4 LAN5 LAN6 LAN7'
aMAC='\IBMCOM\MACS\SFMAC.SYS'
aPROT='\IBMCOM\PROTOCOL\SFPROT.SYS'
aMAC_NIF='[SFMAC_nif]'
aPROT_NIF='[ASFPROT_nif]'

aMAC_sys=''
aPROT_sys=''

aMAC_sec_name=''
aPROT_sec_name=''

/*-----------------------------------------------------------------------
** MAIN
*/

say 'SafeFire Firewall V1.1 NDIS Setup'
say 'Copyright (C) 2000  Link Guard Solutions Ltd.'
say ''

parse arg arg_list

if words(arg_list) = 0 then
do
    say 'Usage: setup {-i | -r | -v} [<iface>] [<drive>]'
    say tab'-i'tab'install drivers into PROTOCOL.INI and CONFIG.SYS'
    say tab'-r'tab'remove drivers from PROTOCOL.INI and CONFIG.SYS'
    say tab'-v'tab'verify correctness of the installation'
    say tab'<iface>'tab'Name of the TCP/IP LAN interface such as: lan0, lan1, etc.'
    say tab'<drive>'tab'Disk drive where MPTN is installed'
    exit
end

/* cut a mode from args */
parse value arg_list with mode arg_list

select
    when translate(mode) = '-I' then work_mode = 'INSTALL';
    when translate(mode) = '/I' then work_mode = 'INSTALL';
    when translate(mode) = '-R' then work_mode = 'REMOVE';
    when translate(mode) = '/R' then work_mode = 'REMOVE';
    when translate(mode) = '-V' then work_mode = 'VERIFY';
    when translate(mode) = '/V' then work_mode = 'VERIFY';
    otherwise ;
end

if arg_list <> '' then
do
    parse value arg_list with tmp_iface arg_list

    if wordpos(translate(tmp_iface), lan_list) > 0 then
    do
        /* parameter is a lan interface name */
        iface=translate(tmp_iface)
    end
    else do
        /* parameter is probably a drive letter */
        mptn_drive=strip(left(tmp_iface, 1))
    end
end

if arg_list <> '' then
do
    /* yet another parameter */
    mptn_drive=strip(left(arg_list, 1))
end

mptn_drive=translate(mptn_drive)

if mptn_drive<>'' & mptn_drive >= 'A' & mptn_drive <= 'Z' then
    mptn_drive = mptn_drive':'
else
    mptn_drive = boot_drive


config_sys   = boot_drive'\CONFIG.SYS'
protocol_ini = mptn_drive'\IBMCOM\PROTOCOL.INI'
/*
config_sys   = 'CONFIG.SYS'
protocol_ini = 'PROTOCOL.INI'
*/

aMAC_sec_name = substr(aMAC_NIF, 2, length(aMAC_NIF) - 2)
aPROT_sec_name = substr(aPROT_NIF, 2, length(aPROT_NIF) - 2)

if stream(config_sys, 'C', 'QUERY EXISTS') = '' then
do
    call abort 'file 'config_sys 'does not exists'
end

if stream(protocol_ini, 'C', 'QUERY EXISTS') = '' then
do
    call abort 'file 'protocol_ini 'does not exists'
end

say 'CONFIG.SYS  :' config_sys
say 'PROTOCOL.INI:' protocol_ini
say 'LANxx       :' tolower(iface)
say 'Executing   :' work_mode
say ''

/* load work files into memory */

call load_file config_sys.   , config_sys
call load_file protocol_ini. , protocol_ini

/* Common part is complete, choose procedure */

interpret 'call do_'work_mode

exit
/* end of main */

/*-----------------------------------------------------------------------
** Processors
*/

/* Process incorrect command */

do_undef:
    call abort 'unknown command specified'

/* Process verification */

do_verify:
    say 'Verifying...'

    rc = check_installed()

    select
        when rc = 0 then
            say 'Not installed.'
        when rc = 1 then
            say 'Installation is broken.'
        when rc = 2 then
            say 'Installed correctly, but device drivers are not copied.'
        when rc = 3 then
            say 'Installed correctly.'
        otherwise
            say 'Unknown problem oocured'
    end

return

/* Process installation */

do_install:
    say 'Installation...'

    rc=check_installed()

    if rc = 1 then /* partially installed */
    do
        say ''
        say 'Previous setup was incomplete. '
        say 'Please, use backup copies of PROTOCOL.INI and CONFIG.SYS'
        say 'or refer to SETUP.TXT for instructions how clean up installation manually.'
        say ''
        return
    end

    if rc = 2 | rc = 3 then /* installation is complete. */
    do
        say ''
        say 'Already installed.'
        say 'Installation of multiple instances is not supported.'
        say ''
        return
    end

    if rc > 0 then /* just something wrong */
    do
        say ''
        say 'Unknown problem occured.'
        say ''
        return
    end

    /* find bindings for TCP/IP */

    bind_list = find_bindings_str('[TCPIP_nif]')

    call parse_binding one., bind_list

    /* find matching adapter (LANxx) and replace it with MAC */

    parse value iface with .'LAN'num

    if num >= one.0 then
        call abort 'Invalid LAN adapter specified.'

    num = num+1
    save_adapter=one.num

    one.num=aMAC_sec_name

    call replace_bindings_str '[TCPIP_nif]', build_binding(one.)

    /* Add sections to PROTOCOL.INI */
    i = protocol_ini.0 + 1
    protocol_ini.i = aMAC_NIF;                    i = i+1;
    protocol_ini.i = '';                          i = i+1;
    protocol_ini.i = '   DriverName = SFMAC$';    i = i+1;
    protocol_ini.i = '';                          i = i+1;
    protocol_ini.i = aPROT_NIF;                   i = i+1;
    protocol_ini.i = '';                          i = i+1;
    protocol_ini.i = '   DriverName = SFPROT$';   i = i+1;
    protocol_ini.i = '   Bindings = 'save_adapter;i = i+1;
    protocol_ini.i = '';

    protocol_ini.0 = i;

    /* Add appropriate lines into CONFIG.SYS */
    i = config_sys.0 + 1

    config_sys.i = 'DEVICE='mptn_drive||aMAC;   i = i+1;
    config_sys.i = 'DEVICE='mptn_drive||aPROT;

    config_sys.0 = i

    call save_file config_sys.  , config_sys
    call save_file protocol_ini., protocol_ini

return

/* Process removal */

do_remove:
    say 'Removing...'

    rc=check_installed()

    if rc <> 2 & rc <> 3 then
    do
        str = ''
        if rc <> 0 then
            str = ' or installation is broken'

        say ''
        say 'Software is not installed'str'.'
        say ''
        return
    end

    /* restore TCP/IP bindings */

    bind_list = find_bindings_str('[TCPIP_nif]')

    call parse_binding one., bind_list

    bind_list_p = find_bindings_str(aPROT_NIF)

    call replace_binding one., aMAC_sec_name, bind_list_p

    call replace_bindings_str '[TCPIP_nif]', build_binding(one.)

    /* mark sections as deleted */

    call remove_section aPROT_NIF
    call remove_section aMAC_NIF

    call remove_device aMAC
    call remove_device aPROT

    call save_file config_sys.  , config_sys
    call save_file protocol_ini., protocol_ini

return

/*-----------------------------------------------------------------------
** Utility procedures
*/

/*
** Abort script with given error message
*/

abort:
    say 'ERROR:: 'arg(1)
    say 'SETUP stopped'
exit

/*
** Convert string to lower case
*/

tolower: procedure
return translate(arg(1), xrange('a','z'), xrange('A', 'Z'))

/*
** Check existence and validness of the installation
** Returns:
**  	0 - not installed
**  	1 - partially installed (broken)
**      2 - installed
**      3 - installed and device drivers are exists
*/

check_installed:

    /* do a quick scan of the files */

    /* complete setup should have both files in config.sys,
    ** sections for MAC  and PROT in protocol.ini,
    ** binding from TCP/IP to MAC and from PROT to some MAC.
    */

    conf_mac_found   = 0
    conf_prot_found  = 0

    sect_mac_found   = 0
    sect_prot_found  = 0

    bind_tcpip_found = 0
    bind_prot_found  = 0

    /* config.sys */

    do i = 1 to config_sys.0

        if verify_devstring(config_sys.i, aMAC) = 1 then
        do
            conf_mac_found = conf_mac_found+1;
            parse value config_sys.i with . '=' aMAC_sys
        end

        if verify_devstring(config_sys.i, aPROT) = 1 then
        do
            conf_prot_found = conf_prot_found+1;
            parse value config_sys.i with . '=' aPROT_sys
        end
    end

    /* protocol.ini */

    /* look for sections */

    do i = 1 to protocol_ini.0

        if translate(strip(protocol_ini.i))=translate(aMAC_NIF) then
            sect_mac_found = sect_mac_found+1;

        if translate(strip(protocol_ini.i))=translate(aPROT_NIF) then
            sect_prot_found = sect_prot_found+1;
    end

    /* look for bindings */

    /* find bindings for TCP/IP */

    bind_list = find_bindings_str('[TCPIP_NIF]')

    call parse_binding one., bind_list

    if find_binding(one., aMAC_sec_name) > 0 then
    	bind_tcpip_found=bind_tcpip_found+1

    /* find bindings for PROT */

    bind_list = find_bindings_str(aPROT_NIF)

    if bind_list<>'' then
    do
        call parse_binding one., bind_list

        do i = 1 to one.0
            if one.i<>'' /* & find_sect_start('['one.i']') > 0*/ then
                bind_prot_found=bind_prot_found+1;
        end

        if bind_prot_found<>one.0 then /* not all bindings valid? */
            bind_prot_found = 0
    end

    /* total score:
    **
    ** Variable          Valid value
    **
    ** conf_mac_found    1
    ** conf_prot_found   1
    **
    ** sect_mac_found    1
    ** sect_prot_found   1
    **
    ** bind_tcpip_found  1
    ** bind_prot_found   >0     - values greater than 1 is not supported by
    **                            SFPROT, but this may change in the future
    */

    if conf_mac_found = 1   & conf_prot_found = 1 & sect_mac_found = 1 & sect_prot_found = 1 & bind_tcpip_found = 1 & bind_prot_found = 1 then
    do
        if aMAC_sys<>'' & aPROT_sys<>'' & stream(aMAC_sys, 'c', 'query exists') <> '' & stream(aPROT_sys, 'c', 'query exists') <> '' then
        do
            return 3; /* correctly installed and device drivers are exists */
        end

    	return 2;  /* correctly installed */
    end

    if conf_mac_found+conf_prot_found+sect_mac_found+sect_prot_found+bind_tcpip_found+bind_prot_found = 0
        then return 0

    say 'Installation status:'
    say tab'sfmac.sys in config.sys  ->'tab''conf_mac_found
    say tab'sfprot.sys in config.sys ->'tab''conf_prot_found
    say tab'MAC in PROTOCOL.INI      ->'tab''sect_mac_found
    say tab'protocol in PROTOCOL.INI ->'tab''sect_prot_found
    say tab'tcp/ip is bound properly ->'tab''bind_tcpip_found
    say tab'sfprot is bound properly ->'tab''bind_prot_found
    say tab'---------------------------'

return 1 /* installation is broken */

/*
** verify correctness of the DEVICE= string in CONFIG.SYS
** for a given device driver
*/

verify_devstring: procedure

    str = translate(arg(1))
    dev = translate(arg(2))

	parse value str with device'='dev_path

    if strip(device)='DEVICE' & substr(dev_path,3)=dev then
        return 1
return 0

/*
** Load file into stem
*/

load_file:

    ld_stem = arg(1)
    ld_fin  = arg(2)

    call value ld_stem'0', 0
    ld_i=0

    do while lines(ld_fin) > 0
        ld_i = ld_i+1
        call value ld_stem||ld_i, linein(ld_fin)
    end

    call value ld_stem'0', ld_i

    call stream ld_fin, 'c', 'close'

return ld_i

/*
** Save stem into file
*/

save_file:

    ld_j    = arg(1)
    ld_fout = arg(2)
    ld_tgt  = ld_fout

    if stream(ld_fout, 'c', 'query exists') <> '' then
    do
        ld_fout = backup_file(ld_fout) /* make a backup */

        if ld_fout = '' then
            return 1
    end

    do ld_i = 1 to value(ld_j'0')

        str=value(ld_j||ld_i)

        if left(str,1)='7F'x then
            iterate

        call lineout ld_fout, str
    end

    call stream ld_fout, 'c', 'close'

    if ld_fout <> ld_tgt then
    do
        '@copy 'ld_fout ld_tgt '2>nul >nul'
        '@del 'ld_fout
    end

return 0

/*
** Parse list of bindings into stem.
** Special care is taken in order to preserve
** number of the commas at the end of the list of bindings.
*/

parse_binding:
    pb_stem = arg(1)
    pb_str  = arg(2)

    call value pb_stem'0', 0
    call value pb_stem'tail', ''

    if right(pb_str,1) = ',' then
        call value pb_stem'tail', ',';

    pb_i=0

    do while pb_str <> ''

        parse value pb_str with pb_bind','pb_str

        pb_i = pb_i+1

        call value pb_stem||pb_i, pb_bind
    end

    call value pb_stem'0', pb_i

return pb_i

/*
** Build string of bindings for the BINDINGS= statement
*/

build_binding:
    bb_j = arg(1)

    bb_res =''

    do bb_i = 1 to value(bb_j'0')
        if bb_i < value(bb_j'0') then
            bb_res =bb_res||value(bb_j||bb_i)',';
        else
            bb_res =bb_res||value(bb_j||bb_i);
    end

    bb_res =bb_res||value(bb_j'tail')

return bb_res

/*
** Find and replace one _NIF with another in the list of bindings
*/

replace_binding:
    rb_j    = arg(1)
    rb_from = translate(arg(2))
    rb_to   = arg(3)

    do rb_i = 1 to value(rb_j'0')
        if translate(value(rb_j||rb_i)) = rb_from then
        do
            call value rb_j||rb_i, rb_to
            leave
        end
    end
return 0

/*
** Find binding
*/

find_binding:
    rb_j    = arg(1)
    rb_from = translate(arg(2))

    do rb_i = 1 to value(rb_j'0')
        if translate(value(rb_j||rb_i)) = rb_from then
            return rb_i;
    end
return 0

/*
** Find start of the section
*/

find_sect_start: procedure expose protocol_ini.

    target=translate(arg(1))

    do i = 1 to protocol_ini.0
        if translate(strip(protocol_ini.i))=target then
            return i
    end

return 0

/*
** Find bindings for given _NIF
*/

find_bindings_str: procedure expose protocol_ini.

    target = translate(arg(1))

    start  = find_sect_start(arg(1))

    if start = 0 then
        return ''

    do i = start to protocol_ini.0

        if i > start & left(strip(protocol_ini.i),1)='[' then /* start of the next section */
            leave;

        if left(translate(strip(protocol_ini.i)),8)='BINDINGS' then
        do
            parse value protocol_ini.i with beg'='rest

            return strip(rest)
        end
    end

return ''

/*
** Replace 'BINDINGS=...' string with new one for a given section
*/

replace_bindings_str: procedure expose protocol_ini.

    target = translate(arg(1))
    new_str = arg(2)

    start  = find_sect_start(arg(1))

    if start = 0 then
        return 0

    do i = start to protocol_ini.0

        if left(strip(protocol_ini.0),1)='[' then /* start of the next section */
            leave;

        if left(translate(strip(protocol_ini.i)),8)='BINDINGS' then
        do
            parse value protocol_ini.i with beg'='.
            protocol_ini.i = beg'=' new_str
            return 1
        end
    end

return 0

/*
** Mark section as removed
*/

remove_section: procedure expose protocol_ini.

    target = translate(arg(1))

    start  = find_sect_start(arg(1))

    if start = 0 then
        return 0

    do i = start to protocol_ini.0

        if i > start & left(strip(protocol_ini.i),1)='[' then /* start of the next section */
            leave;

        protocol_ini.i='7F'x||protocol_ini.i
    end

return 0

/*
** Mark 'DEVICE=' item as removed
*/

remove_device: procedure expose config_sys.

    target = translate(arg(1))

    do i = 1 to config_sys.0

        if left(translate(strip(config_sys.i)), 6) = 'DEVICE' then
        do
            parse upper value config_sys.i with .'='devname

            if substr(devname,3) = target then
            do
                config_sys.i = '7F'x||config_sys.i
                leave
            end
        end
    end
return 0;

/*
** Make backup of existing file and prepare name temp file
*/
backup_file: procedure

    name = arg(1)

    i = lastpos('.', name)
    j = lastpos('\', name)

    if i > j then /* extension exists */
        basename=substr(name, 1, i - 1)
    else
        basename=name

    do i = 1 to 999

        bk_name = basename'.'right(i,3,'0')

        if stream(bk_name, 'c', 'query exists') = '' then
            leave

        bk_name = ''
    end

    if bk_name = '' then
        call about 'Unable to make backup copy of 'name

    say 'File 'name 'will be saved as 'bk_name

    '@copy 'name bk_name '2>nul >nul'

    if rc <> 0 then
        call abort 'Unable to make backup copy of 'name

    say 'Saved successfully.'

    j = i + 1

    do i = j to 999

        bk_name = basename'.'right(i,3,'0')

        if stream(bk_name, 'c', 'query exists') = '' then
            leave

        bk_name = ''
    end

    if bk_name = '' then
        call about 'Unable to make temporary file'

return bk_name

