#!/bin/bash
# etc/wlan/shared
#
# Copyright (C) 2002 AbsoluteValue Systems, Inc.  All Rights Reserved.
# --------------------------------------------------------------------
#
# linux-wlan
#
#   The contents of this file are subject to the Mozilla Public
#   License Version 1.1 (the "License"); you may not use this file
#   except in compliance with the License. You may obtain a copy of
#   the License at http://www.mozilla.org/MPL/
#
#   Software distributed under the License is distributed on an "AS
#   IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
#   implied. See the License for the specific language governing
#   rights and limitations under the License.
#
#   Alternatively, the contents of this file may be used under the
#   terms of the GNU Public License version 2 (the "GPL"), in which
#   case the provisions of the GPL are applicable instead of the
#   above.  If you wish to allow the use of your version of this file
#   only under the terms of the GPL and not to allow others to use
#   your version of this file under the MPL, indicate your decision
#   by deleting the provisions above and replace them with the notice
#   and other provisions required by the GPL.  If you do not delete
#   the provisions above, a recipient may use your version of this
#   file under either the MPL or the GPL.
#
# --------------------------------------------------------------------
#
# Inquiries regarding the linux-wlan Open Source project can be
# made directly to:
#
# AbsoluteValue Systems Inc.
# info@linux-wlan.com
# http://www.linux-wlan.com
#
# --------------------------------------------------------------------
#
# Portions of the development of this software were funded by 
# Intersil Corporation as part of PRISM(R) chipset product development.
#
# --------------------------------------------------------------------

if [ ! -n "$ECHO" ]; then
	ECHO=echo
fi

if [ -x /sbin/modprobe ] ; then
	MODPROBE=/sbin/modprobe
else
	${ECHO} "/sbin/modprobe not found."
	exit 1
fi

if [ -x /sbin/wlanctl-ng ] ; then 
	WLANCTL=/sbin/wlanctl-ng
else
	${ECHO} "/sbin/wlanctl-ng not found."
	exit 1
fi

if [ -x /sbin/wland ] ; then
    WLAND=/sbin/wland
else
    ${ECHO} "/sbin/wland not found."
    exit 1
fi

if [ -f /proc/sys/kernel/hotplug -a \
	-x `cat /proc/sys/kernel/hotplug` -a \
	-f /etc/hotplug/wlan.agent ] ; then
	HAS_HOTPLUG=y
else
	HAS_HOTPLUG=n
fi

# Source the wlan configuration
if [ -f /etc/wlan/wlan.conf ] ; then
	. /etc/wlan/wlan.conf
else
	${ECHO} "/etc/wlan/wlan.conf not found."
	exit 0
fi

# Source NSD specific functions
# nsd_fwload
# nsd_mibset
for i in /etc/wlan/shared.* ; do 
	. $i
done

is_true ()
{
	# $1 == string containing a t/f indicator.

	[ "$1" = "y" -o "$1" = "Y" -o "$1" = "yes" -o "$1" = "YES" \
	  -o "$1" = "t" -o "$1" = "T" -o "$1" = "true" -o "$1" = "TRUE" ]
}

wlan_nsdname ()
{
	# $1 == wlandev
	# Writes the given device's name to stdout
	grep 'nsd name' /proc/net/p80211/$1/wlandev | sed -e 's/.*: \(.*\)_.*/\1/'
}

wlan_enable ()
{
	# $1 == wlandev

	#=======ENABLE IFSTATE=============================
	# Bring the device into its operable state

	$MODPROBE $1
	
	# First, make sure the driver is loaded....
	if ! ifconfig $1 > /dev/null 2>&1 ; then
		${ECHO} "Error: Device $1 does not seem to be present."
		${ECHO} "Make sure you've inserted the appropriate"
		${ECHO} "modules or that your modules.conf file contains"
		${ECHO} "the appropriate aliase(s)."
		return 1
	fi

	# Call the nsd script's fwload function, in case the card needs
	# a firmware load, or could use an optional one.

	nsdname=`wlan_nsdname $1`
	if ! ${nsdname}_fwload $1 ; then
		${ECHO} "Firmware failed to load for device $1"
		return 1
	fi

	# Enable the interface
	result=`$WLANCTL $1 lnxreq_ifstate ifstate=enable`
	if [ $? = 0 ] ; then
		eval $result
		if [ $resultcode != "success" ]; then
			${ECHO} "Failed to enable the device, resultcode=" \
				$resultcode "."
			return 1
		fi
	else
		${ECHO} "Failed to enable the device, exitcode=" $? "."
		return 1
	fi

	# Set any NSD specific MIBs
	${nsdname}_mibset $1
}

wlan_user_mibs ()
{
	# $1 == wlandev

	#=======USER MIB SETTINGS=============================
	# Set the user specified MIB items.
	for i in $USER_MIBS ; do
		result=`$WLANCTL $1 dot11req_mibset "mibattribute=$i"`
		if [ $? = 0 ] ; then
			eval $result
			if [ $resultcode != "success" ] ; then 
				${ECHO} "Failed to set user MIB $i."
				return 1
			fi
		else
			${ECHO} "Failed to set user MIB $i."
			return 1
		fi
	done
}

wlan_source_config ()
{
	# $1 == wlandev

	# XXX what about stray singlequotes.
	eval 'GOSSID="$SSID_'$1'"'
	wlan_source_config_for_ssid "$GOSSID"
}

wlan_source_config_for_ssid ()
{
	# $1 == ssid[:bssid]
        # $2 == bssid (optional)

        DesiredSSID="$1"
        DesiredBSSID="$2"

        if [ -n "$2" ] ; then
               token_ssid=`echo $1 | sed -ne 's/\(.*\)\(:..:..:..:..:..:..\).*/\1/p'`
               token_bssid=`echo $1 | sed -ne 's/\(.*\):\(..:..:..:..:..:..\).*/\2/p'`
	       if [ -n "$token_ssid" ] ; then
		   DesiredSSID="$token_ssid"
		   DesiredBSSID="$token_bssid"
	       fi

	fi

	if [ -f "/etc/wlan/wlancfg-$DesiredSSID:$DesiredBSSID" ] ; then
		. "/etc/wlan/wlancfg-$DesiredSSID:$DesiredBSSID"
	elif [ -f "/etc/wlan/wlancfg-$DesiredSSID" ] ; then
		. "/etc/wlan/wlancfg-$DesiredSSID"
	else 

		if [ -n "$1" ] ; then
			${ECHO} "Failed to open network config file /etc/wlan/wlancfg-$1, using default."
		fi

		. "/etc/wlan/wlancfg-DEFAULT"
	fi
}

wlan_disable ()
{
	# $1 == wlandev

	$WLANCTL $1 lnxreq_ifstate ifstate=disable
}	

wlan_ssid_in_list ()
{
	# $1 == wlandev, $2 == ssid,  $3 == bssid

  	eval 'GOSSID="$SSID_'$1'"'
  	
	# This "eval" hackery is to allow escapes in GOSSID...
	cmd="for token in $GOSSID ; do
		ssid_token=\`echo \"\$token\" | sed -ne 's/\(.*\)\(:..:..:..:..:..:..\).*/\1/p'\`
		bssid_token=\`echo \"\$token\" | sed -ne 's/\(.*\):\(..:..:..:..:..:..\).*/\2/p'\`
  
		if [ -z \"\$ssid_token\" ] ; then
			ssid_token="\$token"
  		fi
  
		if [ -n \"\$bssid_token\" ] ; then
			if [ \"\$bssid_token\" = \"\$3\" ] ; then
  				return 0
  			fi
		elif [ \"\$2\" = \"\$ssid_token\" ] ; then
  			return 0	
  		fi
	done"
	eval "$cmd"

	return 1
}

wlan_supports_scan ()
{
	# $1 == wlandev

	if is_true "$WLAN_SCAN" ; then
		cat /proc/net/p80211/$1/wlandev | grep 'scan' > /dev/null
		if [ $? = 0 ] ; then
			return 0
		fi
	fi
	return 1
}

wlan_scan ()
{
	# $1 == wlandev

	# find our allowed SSID list.
	
	# XXX what about stray singlequotes.
	eval 'GOSSID="$SSID_'$1'"'

	# kick off a quick scan with the broadcast SSID.
	wlan_scan_one $1 '' '' n
	if [ $? = 0  -a \
	    "$GOSSID" = "" ] ; then
	    # if successful and our ssid list is null, return.
	    sleep 1
	    return 0
	fi	

	# otherwise we walk through the list, and scan for eacn in turn.
	# this "eval" hackery is to allow escapes in GOSSID
	cmd="for token in $GOSSID ; do
		ssid_token=\`echo \"\$token\" | sed -ne 's/\(.*\)\(:..:..:..:..:..:..\).*/\1/p'\`
		bssid_token=\`echo \"\$token\" | sed -ne 's/\(.*\):\(..:..:..:..:..:..\).*/\2/p'\`

		if [ -z \"\$ssid_token\" ] ; then
			ssid_token=\"\$token\"
		fi

		wlan_scan_one \$1 \"\$ssid_token\" \"\$bssid_token\"
		if [ \$? = 0 ] ; then
			sleep 1
			return 0
		fi
	done"
	eval "$cmd"

	# We got to the end of the list.  Maybe try "any"
	if is_true "$WLAN_ANY" ; then
		wlan_scan_one $1
		sleep 1
		return $?
	fi

	sleep 1
	return 1
}


wlan_scan_one ()
{
	# $1 == wlandev, [ $2 == ssid, $3 == bssid, $4 == append ]

    if [ -z "$4" ] ; then
	append=true
    else
	append=false
    fi

	numbss=0

	result=`$WLANCTL $1 dot11req_scan bsstype=any bssid=ff:ff:ff:ff:ff:ff \
		scantype=active probedelay=0 channellist=$ChannelList ssid="$2" \
		minchanneltime=$ChannelMinTime maxchanneltime=$ChannelMaxTime append=$append`
	eval $result
	if [ $resultcode != 'success' ] ; then
		${ECHO} "Scan failed ($resultcode) "
		return 1
	fi

	## XXX if numbss == 0, repeat with $2 $3 $4?

	i=0
	bssfound=""

	# walk through the results and do first-cut matching.
	while [ $i -lt $numbss ] ; do
		result=`$WLANCTL $1 dot11req_scan_results bssindex=$i`
		eval $result

		if [ -n "$3" ] ; then
                        if [ "$3" = "$bssid" ] ; then
                               bssfound="$bssfound $i"
			fi
		elif [ -z "$2" ] ; then
			# if our ssid is "", then we pick the first entry.
			bssfound="$bssfound $i"
		elif [ "$2" = "$ssid" ] ; then
			bssfound="$bssfound $i"
		fi
		i=`expr $i + 1`
	done

	if [ -z "$bssfound" ]; then	# No BSSs found, bail.
		return 1
	else
		# Now find the closest
		bigsignal=0
		for i in $bssfound ; do
			result=`$WLANCTL $1 dot11req_scan_results bssindex=$i`
			eval $result
			if [ $bigsignal -lt $signal ]; then
				bigsignal=$signal
				bigbssindex=$i
			fi
		done
		result=`$WLANCTL $1 dot11req_scan_results bssindex=$bigbssindex`
		eval $result
		return 0
	fi
}

wlan_wep ()
{
	# $1 == wlandev

	#=======WEP===========================================
	# Setup privacy
	if [ ${dot11PrivacyInvoked:-"false"} = "false" ] ; then
		return 0;
	fi

	result=`$WLANCTL $1 dot11req_mibget mibattribute=dot11PrivacyOptionImplemented`
	if [ $? = 0 ] ; then
		eval $result
		eval $mibattribute
	else
		${ECHO} "dot11PrivacyOptionImplemented mibget failed."
		return 1
	fi

	if [ $dot11PrivacyOptionImplemented = "false" ] ; then
		${ECHO} "Cannot enable privacy, dot11PrivacyOptionImplemented=false."
		return 1
	fi

	# Do we want host-based WEP?
	result=`$WLANCTL $1 lnxreq_hostwep \
	    decrypt="${lnxreq_hostWEPDecrypt:-false}"    \
	    encrypt="${lnxreq_hostWEPEncrypt:-false}"`

	# set up the rest of the parametsrs.
	if [ $dot11PrivacyOptionImplemented = "true" -a \
	     $dot11PrivacyInvoked = "true" ] ; then
		result=`$WLANCTL $1 dot11req_mibset \
			  mibattribute=dot11WEPDefaultKeyID=$dot11WEPDefaultKeyID `
		result=`$WLANCTL $1 dot11req_mibset \
			mibattribute=dot11ExcludeUnencrypted=$dot11ExcludeUnencrypted `
		result=`$WLANCTL $1 dot11req_mibset \
			mibattribute=dot11PrivacyInvoked=$dot11PrivacyInvoked`
		if [ "${PRIV_GENSTR:-empty}" != "empty" ] ; then
			if [ ${PRIV_KEY128:-"false"} = "false" ]; then
				keys=`$PRIV_GENERATOR "$PRIV_GENSTR" 5`
			else
				keys=`$PRIV_GENERATOR "$PRIV_GENSTR" 13`
			fi
				knum=0
			for i in $keys ; do
				result=`$WLANCTL $1 dot11req_mibset \
				mibattribute=dot11WEPDefaultKey$knum=$i`
				knum=`expr $knum + 1`
			done
		else 
			result=`$WLANCTL $1 dot11req_mibset \
			mibattribute=dot11WEPDefaultKey0=$dot11WEPDefaultKey0 `
			result=`$WLANCTL $1 dot11req_mibset \
			mibattribute=dot11WEPDefaultKey1=$dot11WEPDefaultKey1 `
			result=`$WLANCTL $1 dot11req_mibset \
			mibattribute=dot11WEPDefaultKey2=$dot11WEPDefaultKey2 `
			result=`$WLANCTL $1 dot11req_mibset \
			mibattribute=dot11WEPDefaultKey3=$dot11WEPDefaultKey3 `
		fi
	else
		# disable wep explicitly.
		result=`$WLANCTL $1 dot11req_mibset \
			mibattribute=dot11PrivacyInvoked=$dot11PrivacyInvoked `
		result=`$WLANCTL $1 dot11req_mibset \
			mibattribute=dot11ExcludeUnencrypted=false `
	fi
}

wlan_adhoc ()
{
	# $1 == wlandev

	#=======IBSS STARTUP==================================
	startcmd="$WLANCTL $1 dot11req_start "
	startcmd="$startcmd ssid=$DesiredSSID"
	startcmd="$startcmd bsstype=independent"
	startcmd="$startcmd beaconperiod=$BCNINT" 
	startcmd="$startcmd dtimperiod=3"
	startcmd="$startcmd cfpollable=false"
	startcmd="$startcmd cfpollreq=false"
	startcmd="$startcmd cfpperiod=3"
	startcmd="$startcmd cfpmaxduration=100"
	startcmd="$startcmd probedelay=100"
	startcmd="$startcmd dschannel=$CHANNEL"
	j=1
	for i in $BASICRATES ; do
		startcmd="$startcmd basicrate$j=$i"
		j=`expr $j + 1`
		done

	j=1
	for i in $OPRATES ; do
		startcmd="$startcmd operationalrate$j=$i"
		j=`expr $j + 1`
	done

	results=`$startcmd`	# Here's where it runs
	if [ $? = 0 ]; then 
		eval $results
		if [ $resultcode != "success" ] ; then 
			${ECHO} "IBSS not started, resultcode=$resultcode"
			exit 1
		else
			${ECHO} "IBSS mode started."
		fi
	else
		${ECHO} FAILED: $startcmd
		return 1
	fi
	WLAN_SCHEMESSID="$DesiredSSID"
}

wlan_infra ()
{
	# $1 == wlandev

	#==== INFRASTRUCURE STARTUP===========================
	# XXX TODO:  Grok DesiredBSSID

	sleep 5
	results=`$WLANCTL $1 lnxreq_autojoin \
		"ssid=$DesiredSSID" \
		authtype=${AuthType:="opensystem"} | sed 's/\([^=]*\)=\(.*\)/\1="\2"/'`
	eval $results
	if [ ${resultcode:-"failure"} != "success" ] ; then
		${ECHO} 'error: Autojoin indicated failure!'
		return 1;
	fi

	WLAN_SCHEMESSID="$DesiredSSID"
}

wlan_dot11_join ()
{
	# $1 == wlandev

	joincmd="$WLANCTL $1 dot11req_join bssid=$DesiredBSSID"
	joincmd="$joincmd joinfailuretimeout=1"
	
	j=1
	for i in $OPRATES ; do
		joincmd="$joincmd operationalrate$j=$i"
		j=`expr $j + 1`
	done

	results=`$joincmd`

	eval $results
	if [ ${resultcode:-"failure"} != "success" ] ; then
		${ECHO} "$1: JOIN Failure"
		${ECHO} "joincmd=$joincmd"
		${ECHO} "results=$results"
		return 1;
	fi
}

wlan_dot11_auth_assoc ()
{
    # $1 == wlandev
	if [ $bsstype = "infrastructure" ] ; then 
		results=`$WLANCTL $1 dot11req_authenticate \
				peerstaaddress=$DesiredBSSID \
				authenticationtype=$AuthType \
				authenticationfailuretimeout=2000`
		eval $results
		if [ ${resultcode:-"failure"} != "success" ] ; then
			${ECHO} "error:  dot11req_authenticate failed, "\
				"resultcode=$resultcode"
			return 1;
		fi
		results=`$WLANCTL $1 dot11req_associate \
				listeninterval=1000 \
				associatefailuretimeout=2000 `
		if [ ${resultcode:-"failure"} != "success" ] ; then
			${ECHO} 'error:  dot11req_associate failed!'
			return 1;
		fi
	fi
}

wlan_set_ssid_schemefile ()
{
	# $1 == SSID

	# Find the scheme file 
	if [ -r /var/lib/misc/pcmcia-scheme ] ; then
		# Debian
		WLAN_SCHEMEFILE="/var/lib/misc/pcmcia-scheme"
	elif [ -d /var/state/pcmcia ] ; then
		WLAN_SCHEMEFILE="/var/state/pcmcia/scheme"
	elif [ -d /var/lib/pcmcia ] ; then
        	WLAN_SCHEMEFILE="/var/lib/pcmcia/scheme"
	else
		WLAN_SCHEMEFILE="/var/run/pcmcia-scheme"
	fi

	# Collect the current scheme name and save the file
	if [ -r $WLAN_SCHEMEFILE ] ; then
		WLAN_SCHEME=`cat $WLAN_SCHEMEFILE`
		cp $WLAN_SCHEMEFILE /tmp/wlan_scheme_`date +"%T"`.tmp
	else
		touch /tmp/wlan_scheme_`date +"%T"`.tmp
		
	fi

	# Set up the <scheme:SSID> string
	if [ ! "$WLAN_SCHEME" ] ; then 
		WLAN_SCHEME="default"
	fi
	WLAN_SCHEME="$WLAN_SCHEME:$1"

	# Write to schemefile
	echo $WLAN_SCHEME > $WLAN_SCHEMEFILE
}

wlan_restore_schemefile ()
{
	# Find the scheme file 
	if [ -r /var/lib/misc/pcmcia-scheme ] ; then
		# Debian
		WLAN_SCHEMEFILE="/var/lib/misc/pcmcia-scheme"
	elif [ -d /var/state/pcmcia ] ; then
		WLAN_SCHEMEFILE="/var/state/pcmcia/scheme"
	elif [ -d /var/lib/pcmcia ] ; then
        	WLAN_SCHEMEFILE="/var/lib/pcmcia/scheme"
	else
		WLAN_SCHEMEFILE="/var/run/pcmcia-scheme"
	fi

	TMPFILE=`ls /tmp/wlan_scheme*.tmp | tail -n 1`

	if [ -r $TMPFILE ] ; then
		cat $TMPFILE > $WLAN_SCHEMEFILE
		rm -f $TMPFILE
	else
		${ECHO} "wlan_restore_schemefile: No wlan_scheme\*.tmp file found."
	fi
}

wlan_bring_it_up ()
{
    # $1 == wlandev
    # $2 == non-null if wext enabled.

    #=======ENABLE========================================
    # Do we want to init the card at all?
    eval 'WLAN_ENABLE=$ENABLE_'$1

    if ! is_true $WLAN_ENABLE ; then
	return 1
    fi
    
    if wlan_enable $1 ; then
	if [ -z "$2" ] ; then
	    wlan_scan_and_join $1
	    return $?
	else
	    ${ECHO} "Bypassing configuration due to wireless extensions"
	fi	
    else
	return 1;
    fi

    return 1
}

wlan_scan_and_join ()
{
    #=======MAC STARTUP=========================================
    wlan_supports_scan $1
    if [ $? = 0 ] ; then
	wlan_scan $1 
	if [ $? = 0 ] ; then
	    wlan_source_config_for_ssid "$ssid" "$bssid"
	    
	    wlan_user_mibs $1
	    wlan_wep $1
	    
	    wlan_join $1
	else
	    if is_true $IS_ADHOC ; then     
		# start an IBSS; we didn't find one.
		wlan_adhoc $1
	    else
		return 1
	    fi
	fi
    else
	wlan_source_config $1
	
	wlan_user_mibs $1
	wlan_wep $1
	
	if is_true $IS_ADHOC ; then     
	    wlan_adhoc $1
	else
	    wlan_infra $1
	fi
    fi
    
    return $?
}

wlan_join ()
{
    # $1 == wlandev
    grep 'autojoin' /proc/net/p80211/$1/wlandev > /dev/null
    if [ $? = 0 ]; then
	wlan_infra $1
    else
	wlan_dot11_join $1
	wlan_dot11_auth_assoc $1
    fi
}

start_wland ()
{
    $WLAND
}

stop_wland ()
{
    killall wland
}

tmpname ()
{
	# $1 == prefix
	local i=0
	local tname=$TMPDIR/${1}_$$
	if [ -f ${tname}.tmp ] ; then
		for i in 0 1 2 3 4 5 6 7 8 9 ; do
			if [ ! -f ${tname}_${i}.tmp ] ; then
				echo ${tname}_${i}.tmp 
				return 0
			fi
		done
	else
		echo ${tname}.tmp
	fi
	return 0
}

source_procfile ()
{
	# $1 == procfile to source
	local fname=`tmpname wlan_procfile`
	cat "$1" > $fname
	. $fname
	rm -f $fname
}

# This is set by the Configure script as part of 'make install'
#FIRMWARE_DIR="/usr/share/linux-wlan"
FIRMWARE_DIR=/etc/wlan/
