#!/bin/bash
#script uses arrays which are supported by bash but possibly not other shells
#Pnethood
#Copyright Will Davies 15/4/2008
#with thanks to Dougal and Disciple for various contributions and criticism
#Pnethood is released under the GNU General Public License (GPL).
#You have the right to use and modify this software in any way you like,
# so long as any derivative works remain under a GPL license.
#This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY.
#See the GNU General Public License homepage for more details. <http://www.gnu.org/licenses/>.
#------------------------------

FILER=rox
VERSION=0.62
PROGPATH=$(dirname $0)
export TMP=/tmp/pnethood/
MOUNTBASE=/mnt/network/
[ -e "$/tmp/pnethood/log" -a $(du /tmp/pnethood/log | sed 's/\([0-9]*\).*/\1/') -gt 100 ] && : > ${TMP}log #clear log at 100K

SAVEPASSWORDS=false
SCANSPEED=1
USECIFS=true
source "$HOME/.pnethood/pnethood.rc"

export MSG_SCANNING_NETWORK="
<window title=\"Pnethood Scanning\" icon-name=\"gtk-network\">
 <hbox>
  <pixmap icon_size=\"5\">
   <input file stock=\"gtk-dialog-info\"></input>
  </pixmap>
  <text>
   <label>ネットワークをスキャン中, お待ちください</label>
  </text>
 </hbox>
</window>"
export MSG_NO_NETWORK="
<window title=\"Pnethood Error\" icon-name=\"gtk-network\">
 <vbox>
   <text wrap=\"true\">
    <label>有効なネットワークインターフェースが見付かりません, スキャンするものがありません. Pnethood を終了します. あらかじめネットワークを設定してください.</label>
   </text>
  <hbox homogeneous=\"true\">
   <pixmap icon_size=\"5\">
    <input file stock=\"gtk-dialog-error\"></input>
   </pixmap><button ok></button>
  </hbox>
 </vbox>
</window>"

modprobe cifs
[ "$PROGPATH" = "." ] && PROGPATH="`pwd`"
mkdir -p "${HOME}/.pnethood/"
mkdir -p $TMP
touch "${HOME}/.pnethood/passwords"

[ `which $FILER` ] || echo rox not found show button will not work
[ `which rxvt` ] || echo rxvt not found info button will not work
[ `which nbtscan` ] || echo nbtscan not found pnethood will not work
[ `which smbclient` ] || echo smbclient not found pnethood will not work
if [ "$(lsmod | grep cifs)" ]; then
echo experimental cifs support enabled
else
[ `which smbmount` ] || echo no cifs support and smbmount not found pnethood will not work
fi

while [ "anything" ]; do #big loop
echo ------------------------- >> ${TMP}log

##### write a list of servers to file
if [ ! "$1" = "" ]; then
: > ${TMP}temp
  SUBNETS="$1" # this is checked later to ensure networks is up, initialising it disables the check
  echo "$1" | tr ',' '\n' | sed 's/[\\\/]//g' > ${TMP}specified-servers
  shift #ensures that rescan doesn't just use the input server
  while read SERVERNAME ; do
  	if [ "$(echo $SERVERNAME | grep '^[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}$')" ]; then
  		echo "${SERVERNAME}:${SERVERNAME}" >> ${TMP}temp && continue
  	fi
#requirement for nmblookup
IP=$(nmblookup "$SERVERNAME" |  tail -1 | cut -d ' ' -f 1)
[ "$IP" = "name_query" ] && IP=$(ping iamfilesrv.ecs.soton.ac.uk | head -1 | sed 's/.*(\(.*\)).*/\1/')
  	echo "$SERVERNAME:$IP"  >> ${TMP}temp
  done < ${TMP}specified-servers
##always show machines we have shares mounted from
#funky use of sed looks extremely fragile
mount 2>>${TMP}log | grep -E 'smbfs' | sed 's/\/\/\(.*\)\/\(.*\) on \(.*\) type smbfs (.*)/\1:\3/;s/\(.*\):\(.*\)\/\(.*\)\/\(.*\)/\3:\1/' | sort -u >> ${TMP}temp
mount 2>>${TMP}log | grep -E 'cifs' | sed 's/\\\\\(.*\)\\\(.*\) on \(.*\) type cifs (.*)/\1:\3/;s/\(.*\):\(.*\)\/\(.*\)\/\(.*\)/\3:\1/' | sort -u >> ${TMP}temp
sort -u -o ${TMP}servers ${TMP}temp 
unset SERVERIPS
unset SERVERNAMES
oIFS=$IFS; IFS=$':'
while read SERVERNAME SERVERIP ; do
SERVERIPS[${#SERVERIPS[@]}]=$SERVERIP
SERVERNAMES[${#SERVERNAMES[@]}]=$SERVERNAME
done < ${TMP}servers
IFS=$oIFS

MACHINES_ITEMS=""
for SERVERNAME in ${SERVERNAMES[@]}; do
MACHINES_ITEMS=${MACHINES_ITEMS}$(echo $SERVERNAME | sed 's/^/<item>/;s/$/<\/item>/')
done 
else
gtkdialog3 --program=MSG_SCANNING_NETWORK --center &
PID=$!
echo scanning network>> ${TMP}log
  SUBNETS=`ifconfig | grep inet| grep -v "127.0.0.1" | grep -v P-t-P | sed 's/\ *inet addr:\([0-9]\{1,3\}.[0-9]\{1,3\}\.\).*/\10\.0\/16/g;$!N; /^\(.*\)\n\1$/!P; D'`
  if [ ! "${SUBNETS}" ] ; then
    kill $PID
    gtkdialog3 --program=MSG_NO_NETWORK --center
    exit 0
  fi

  ##build list of servers
  :> ${TMP}temp
  for SUBNET in $SUBNETS; do
echo $SUBNET
  nbtscan -t 100 -qs : $SUBNET | cut -d : -f 1-2  | sed 's/ *$//g;s/\(.*\):\(.*\)/\2:\1/' >> ${TMP}temp
  #running the scan twice works better than increasing the timeout, duplicate entries are removed by sort later
  nbtscan -t $(($SCANSPEED*1000)) -qs : $SUBNET | cut -d : -f 1-2  | sed 's/ *$//g;s/\(.*\):\(.*\)/\2:\1/' >> ${TMP}temp
  done
 
 #get servers with already mounted shares 
  mount 2>>${TMP}log | grep -E 'smbfs' | sed 's/\/\/\(.*\)\/\(.*\) on \(.*\) type smbfs (.*)/\1:\3/;s/\(.*\):\(.*\)\/\(.*\)\/\(.*\)/\3:\1/' >> ${TMP}temp
	mount 2>>${TMP}log | grep -E 'cifs' | sed 's/\/\/\(.*\)\/\(.*\) on \(.*\) type cifs \(.*\)/\1:\3/;s/\(.*\):\(.*\)\/\(.*\)\/\(.*\)/\3:\1/'>> ${TMP}temp

##remove duplicates and the stupid server that identifies itself as <unknown> and breaks gtkdialog
grep -v 'unknown' ${TMP}temp | sort -u -o ${TMP}servers

unset SERVERIPS
unset SERVERNAMES
oIFS=$IFS; IFS=$':'
while read SERVERNAME SERVERIP  ; do
SERVERIPS[${#SERVERIPS[@]}]=$SERVERIP
SERVERNAMES[${#SERVERNAMES[@]}]=$SERVERNAME
done < ${TMP}servers
IFS=$oIFS
  
  ## count number of servers, handle too many, run with servers chosen by user
  if [ ${#SERVERNAMES[@]} -gt 9 ] ; then
  	MACHINES_ITEMS=""
	for SERVERNAME in ${SERVERNAMES[@]}; do
		MACHINES_ITEMS=${MACHINES_ITEMS}$(echo $SERVERNAME | sed 's/^/<item>/;s/$/<\/item>/')
	done 
    echo "<window title=\"Pnethood\" icon-name=\"gtk-network\"><vbox>
<text wrap=\"true\"><label>Scan found ${#SERVERNAMES[@]} servers found. Please choose one from the list.</label></text>
    <list><height>300</height>
    <variable>LIST</variable>
    "${MACHINES_ITEMS}"
    </list>
    <hbox>
     <button ok></button>
     <button cancel></button>
    </hbox>
  </vbox>
  </window>"> ${TMP}gui
    export pnethood_machine_chooser=`cat ${TMP}gui`
    kill $PID 2> /dev/null > /dev/null
    
    eval `gtkdialog3 --program=pnethood_machine_chooser` 2> /dev/null
    [ "${EXIT}" = "Cancel" ] && exit 0
    I=0
    :> ${TMP}temp
    while [ $I -lt ${#SERVERNAMES[@]} ]; do
    	if [ "${SERVERNAMES[$I]}" = "${LIST}" ]; then
    		echo ${SERVERNAMES[$I]}:${SERVERIPS[$I]} >> ${TMP}temp
    	fi
    	((I++))
    done
 #get servers with already mounted shares 
	mount 2>>${TMP}log | grep -E 'smbfs' | sed 's/\/\/\(.*\)\/\(.*\) on \(.*\) type smbfs (.*)/\1:\3/;s/\(.*\):\(.*\)\/\(.*\)\/\(.*\)/\3:\1/' >> ${TMP}temp
	mount 2>>${TMP}log | grep -E 'cifs' | sed 's/\/\/\(.*\)\/\(.*\) on \(.*\) type cifs \(.*\)/\1:\3/;s/\(.*\):\(.*\)\/\(.*\)\/\(.*\)/\3:\1/'>> ${TMP}temp
	sort -u -o ${TMP}servers ${TMP}temp
    unset SERVERIPS
	unset SERVERNAMES
	oIFS=$IFS; IFS=$':'
	while read SERVERNAME SERVERIP  ; do
		SERVERIPS[${#SERVERIPS[@]}]=$SERVERIP
		SERVERNAMES[${#SERVERNAMES[@]}]=$SERVERNAME
	done < ${TMP}servers
	IFS=$oIFS
  fi
fi
echo Scan completed: $SUBNETS >> ${TMP}log

while [ "anything" ]; do #smaller loop doesn't rescan network

##### labels for tabs
TABLABELS=${SERVERNAMES[@]/#/|} #put a pipe in front of each machine
TABLABELS=`echo $TABLABELS |sed 's/^|//'` #remove the leading pipe
echo servers found=${TABLABELS} >> ${TMP}log

#### iterate through servers loading passwords and creating save actions for usernames/passwords
#this is made more awkward because we can't guarantee that the servers are listed
#in the same order as the last run. A hashmap would be nice but it is not safe to use the server names as a key
unset USERNAMES
unset PASSWORDS
PASSWORDSAVER=""
I=0
while [ $I -lt ${#SERVERNAMES[@]} ]; do
	PASSWORDSAVER=$PASSWORDSAVER"
<action>echo OLDUSERNAMES[$I]=\$USER$I</action>
<action>echo OLDPASSWORDS[$I]=\$PASSWORD$I</action>"
	PASSWORDLINE="$(grep -E "^${SERVERNAMES[$I]}:" $HOME/.pnethood/passwords)"
	if [ "$PASSWORDLINE" ]; then
		USERNAMES[$I]="$(echo $PASSWORDLINE | cut -f 2 -d ':')"
		PASSWORDS[$I]="$(echo $PASSWORDLINE | cut -f 3 -d ':')"
	fi
	J=0
#values from password file are always overwitten by values entered before hitting refresh
	while [ $J -lt ${#OLDSERVERNAMES[@]} ]; do 
		if [ "${SERVERNAMES[$I]}" = "${OLDSERVERNAMES[$J]}" ];then
			USERNAMES[$I]="${OLDUSERNAMES[$J]}"
			PASSWORDS[$I]="${OLDPASSWORDS[$J]}"
			#echo "SERVERNAMES[$I]=${SERVERNAMES[$I]}
#USERNAMES[$I]=${USERNAMES[$I]}
#PASSWORDS[$I]=${PASSWORDS[$I]}"
		fi
		((J++))
	done
	((I++))
done


PNETHOOD_GUI="<window title=\"Pnethood ${VERSION} - windows共有を開く\" icon-name=\"gtk-network\">
<vbox><hbox>
   <button>
    <variable>RESCAN</variable>
    <input file stock=\"gtk-refresh\"></input>
    <label>ネットワークを再スキャン</label>
    $PASSWORDSAVER
    <action>echo SCANSPEED=\$SCANSPEED</action>
    <action>echo RESCANNETWORK=true</action>
	<action type=\"closewindow\">anything</action>
   </button>
   <button>
    <input file stock=\"gtk-help\"></input>
    <label>ヘルプ</label>
    <action>man pnethood &</action>
    </button>
    <button>
    <input file stock=\"gtk-info\"></input>
    <label>LOG</label>
    <action>rxvt -g 80x10 -bg black -fg orange -e tail -f ${TMP}log &</action>
    </button>
    <button>
    <input file stock=\"gtk-quit\"></input>
    <label>終了</label>
    </button>
   </hbox>
   <hbox>
       <button>
    <variable>REFRESH</variable>
    <input file stock=\"gtk-refresh\"></input>
    <label>共有を再スキャン</label>
    $PASSWORDSAVER
    <action>echo SCANSPEED=\$SCANSPEED</action>
	<action type=\"closewindow\">anything</action>
   </button>
   <checkbox>
	<label>パスワードを保存</label>
	<variable>SAVEPASSWORDS</variable>
	<default>$SAVEPASSWORDS</default>
	<action>echo SAVEPASSWORDS=\$SAVEPASSWORDS</action>
</checkbox>
<checkbox>
	<label>cifsを使用</label>
	<variable>USECIFS</variable>
	<default>$USECIFS</default>
	<action>echo USECIFS=\$USECIFS</action>
</checkbox>
   </hbox>
   <hbox>
<text>
      <label>ネットワークをスキャンする長さ(秒)</label>
    </text>
    <combobox allow-empty=\"false\">
      <variable>SCANSPEED</variable>
      <item>$SCANSPEED</item>
      <item>1</item>
      <item>5</item>
      <item>10</item>
      <item>30</item>
    </combobox></hbox>"
    

[ "$TABLABELS" ] && PNETHOOD_GUI=$PNETHOOD_GUI"<notebook labels=\"$TABLABELS\">"
#### build a pane for each machine

I=0
while [ $I -lt ${#SERVERNAMES[@]} ]; do
kill $PID 2> /dev/null

export MSG_SCANNING_MACHINE="
<window title=\"Pnethood Scanning\" icon-name=\"gtk-network\">
 <hbox>
  <pixmap icon_size=\"5\"><input file stock=\"gtk-dialog-info\"></input></pixmap>
  <text><label>Pnethoodは${SERVERNAMES[$I]}を検出</label></text>
 </hbox>
</window>"
gtkdialog3 --program=MSG_SCANNING_MACHINE --center &
PID=$!

echo >> ${TMP}log
echo "started probing ${SERVERNAMES[@]}" >> ${TMP}log
:> ${TMP}temp
smbclient -N -L "${SERVERNAMES[$I]}" 2>>${TMP}log| grep Disk | sed 's/[\t]\(.*\) *Disk.*/\1/g;s/ *$//' >> ${TMP}temp
smbclient -U guest%guest -L ${SERVERIPS[$I]} 2>>${TMP}log | grep Disk | sed 's/[\t]\(.*\) *Disk.*/\1/g;s/[\t ]*$//g'>> ${TMP}temp
smbclient -U "${USERNAMES[$I]}%${PASSWORDS[$I]}" -L "${SERVERNAMES[$I]}" 2>>${TMP}log | grep Disk | sed 's/[\t]\(.*\) *Disk.*/\1/g;s/ *$//'>> ${TMP}temp

if [ $(wc -w ${TMP}temp | awk '{print $1}') -lt 1 ]; then #no shares reported, check to see if they are all mounted
#if I have all the shares connected I get session setup failed: doing NT_STATUS_REQUEST_NOT_ACCEPTED
	echo ${SERVERNAMES[$I]} still reports no shares, checking mount in case there are shares mounted already >>${TMP}log
mount | grep "${SERVERIPS[$I]}" | sed 's/ type .*//g;s/.*\///g;s/\\040/ /g' >> ${TMP}temp       
fi
sort -u ${TMP}temp > ${TMP}shares
[ $(wc -w ${TMP}shares | awk '{print $1}') -lt 1 ] && echo "Trying to contact ${SERVERNAMES[$I]}, no shares reported" >>${TMP}log

##### load share names into array
oIFS=$IFS
IFS=$'\n'
SHARES=($(cat ${TMP}shares))
IFS=$oIFS
#UNAME and UPASSWORD needed as gtkdialog gets upset if there is an empty <default> element
UNAME=""
UPASSWORD=""
[ "${USERNAMES[$I]}" ] && [ ! "${USERNAMES[$I]}" = "" ] && UNAME="<default>${USERNAMES[$I]}</default>"
[ "${PASSWORDS[$I]}" ] && [ ! "${PASSWORDS[$I]}" = "" ] && UPASSWORD="<default>${PASSWORDS[$I]}</default>"

PNETHOOD_GUI=$PNETHOOD_GUI"<vbox>
<hbox>
   <text><label>username</label></text>
   <entry>
   	<variable>USER$I</variable>$UNAME
   </entry>
   </hbox>
   <hbox>
   <text><label>password</label></text>
   <entry invisible_char=\"*\" visibility=\"false\">
		<variable>PASSWORD$I</variable>$UPASSWORD
	</entry>
      </hbox>"

[ ${#SHARES[@]} -eq 0 ] && PNETHOOD_GUI=$PNETHOOD_GUI"<text><label>no shares</label></text>"

J=0
while [ $J -lt ${#SHARES[@]} ]; do

MOUNTPOINT="${MOUNTBASE}${SERVERNAMES[$I]}/${SHARES[$J]}"
MOUNT="$(mount | grep $(echo $MOUNTPOINT | sed 's/\$/\\$/g;s/ /\\\\040/g'))$(mount | grep "$MOUNTPOINT")"
MOUNTED="false"; NOTMOUNTED="true"
[ "$MOUNT" ] && MOUNTED="true" && NOTMOUNTED="false"
echo "share=${SHARES[$J]}, mountpoint=${MOUNTPOINT}" >> ${TMP}log

PNETHOOD_GUI=$PNETHOOD_GUI"<hbox>
<text><label>${SHARES[$J]}</label></text>
 <button sensitive=\"$MOUNTED\">
   <label>開く</label>
 	<variable>SHOW_${I}_${J}</variable>
    <input file stock=\"gtk-directory\"></input>
    <action>${FILER} \"${MOUNTPOINT}\"</action>
   </button>
 	<button sensitive=\"$NOTMOUNTED\">
   <label>接続</label>
 	<variable>MOUNT_${I}_${J}</variable>
    <input file stock=\"gtk-connect\"></input>
    <action>echo OLDUSERNAMES[$I]=\$USER$I;echo OLDPASSWORDS[$I]=\$PASSWORD$I</action> 
    <action>${PROGPATH}/func_mount \"${SERVERNAMES[$I]}\" \"${SERVERIPS[$I]}\" \"${SHARES[$J]}\" \"${MOUNTPOINT}\" \"\$USER${I}\" \"\$PASSWORD${I}\" \"\${USECIFS}\"</action>
    <action>echo OLDUSERNAMES[$I]=\$USER$I;echo OLDPASSWORDS[$I]=\$PASSWORD$I</action>
    <action type=\"enable\">UNMOUNT_${I}_${J}</action>
    <action type=\"enable\">SHOW_${I}_${J}</action>
    <action type=\"refresh\">STATUS</action>
    <action type=\"disable\">MOUNT_${I}_${J}</action>
   </button>
   <button sensitive=\"$MOUNTED\">
   <label>切断</label>
 	<variable>UNMOUNT_${I}_${J}</variable>
    <input file stock=\"gtk-disconnect\"></input>
    <action>${PROGPATH}/func_unmount \"${MOUNTPOINT}\"</action>
    <action type=\"enable\">MOUNT_${I}_${J}</action>
    <action type=\"disable\">SHOW_${I}_${J}</action>
    <action type=\"refresh\">STATUS</action>
    <action type=\"disable\">UNMOUNT_${I}_${J}</action>
   </button>
   </hbox>"
  ((J++))
done

PNETHOOD_GUI=$PNETHOOD_GUI"</vbox>"
echo "検出終了しました $MACHINE" >> ${TMP}log
((I++))
done
#### end of panes

[ "$TABLABELS" ] && PNETHOOD_GUI=$PNETHOOD_GUI"</notebook>" || echo no servers found, try refreshing >> ${TMP}log

PNETHOOD_GUI=$PNETHOOD_GUI"<entry has_frame=\"false\" editable=\"false\">
   <variable>STATUS</variable>
    <input>tail -n 1 ${TMP}log</input>
      </entry>
  </vbox>
</window>"

export PNETHOOD_GUI
echo $PNETHOOD_GUI > ${TMP}gui #line for debugging, should normally be removed to hide passwords
kill $PID 2> /dev/null >/dev/null
RESULT=($(gtkdialog3 --program=PNETHOOD_GUI --center)) 2> /dev/null

OLDSERVERNAMES=(${SERVERNAMES[@]})
OLDUSERNAMES=(${USERNAMES[@]})
OLDPASSWORDS=(${PASSWORDS[@]})

for FOO in ${RESULT[@]}; do
[ "$(echo $FOO | cut -c 1-16| grep -E 'SCANSPEED|USECIFS|SAVEPASSWORDS|EXIT|OLDUSERNAMES|OLDPASSWORDS|RESCANNETWORK')" ] && eval "$FOO" 
done
I=0
while [ $I -lt ${#SERVERNAMES[@]} ]; do
((I++))
done

I=0
while [ $I -lt ${#OLDSERVERNAMES[@]} -a "$SAVEPASSWORDS" = "true" ]; do

	if [ "$(grep -E "^${OLDSERVERNAMES[$I]}:" $HOME/.pnethood/passwords)" ]; then
		sed -i 's/'${OLDSERVERNAMES[$I]}'.*/'${OLDSERVERNAMES[$I]}:${OLDUSERNAMES[$I]}:${OLDPASSWORDS[$I]}'/g' $HOME/.pnethood/passwords
	else
		echo ${OLDSERVERNAMES[$I]}:${OLDUSERNAMES[$I]}:${OLDPASSWORDS[$I]} >> $HOME/.pnethood/passwords
	fi
	((I++))
done
if [ ! "${EXIT}" = "closewindow" ]; then
echo "SAVEPASSWORDS=$SAVEPASSWORDS
SCANSPEED=$SCANSPEED
USECIFS=$USECIFS" > "$HOME/.pnethood/pnethood.rc"
exit 0
fi
[ "$RESCANNETWORK" = "true" ] && RESCANNETWORK=false && break
done
done
