#!/bin/bash

# Free implementation of nxserver components
#
# To use nxserver add the user "nx" 
# and use nxserver as default shell.
#
# Also make sure that hostkey based authentification works.
# 
# Copyright (c) 2004 by Fabian Franz <FreeNX@fabian-franz.de>.
#
# License: GNU GPL, version 2
#
# SVN: $Id: nxserver 405 2007-10-14 22:25:14Z fabianx $
#

# Read the config file
. $(PATH=$(cd $(dirname $0) && pwd):$PATH which nxloadconfig) --

# following two functions are Copyright by Klaus Knopper

stringinstring(){
case "$2" in *$1*) return 0;; esac
return 1
}

# Reread boot command line; echo last parameter's argument or return false.
getparam(){
stringinstring "&$1=" "$CMDLINE" || return 1
echo "$CMDLINE" |  tr "&" "\n" | egrep "^"$1"=" | awk -F= '{ VAL=$2 } END { print VAL }'
return 0
}


############### PACKAGE passdb.bm #######################
#
# Library of passdb functions (outsource)
#

# Policy: Variable and function names _must_ start with passdb_ / PASSDB_

# Needed global vars: $NX_ETC_DIR, $PATH_BIN

# Needed nonstd functions: md5sum


passdb_get_crypt_pass()
{
	echo "$@" | $COMMAND_MD5SUM | cut -d" " -f1
}

passdb_get_pass()
{
	PASSDB_CHUSER="$1"
	PASSDB_PASS=$(egrep "^$PASSDB_CHUSER:" $NX_ETC_DIR/passwords 2>/dev/null | cut -d":" -f2)
	if [ "$ENABLE_PASSDB_AUTHENTICATION" = "1" ]
	then
		egrep -q "^$PASSDB_CHUSER:" $NX_ETC_DIR/passwords 2>/dev/null && echo $PASSDB_PASS
		egrep -q "^$PASSDB_CHUSER:" $NX_ETC_DIR/passwords 2>/dev/null || echo "NOT_VALID"
	else
		echo "NOT_VALID"
	fi
}

passdb_chpass()
{
	PASSDB_CHUSER="$1"
	PASSDB_ENC_PASS="$2"
	cp -f $NX_ETC_DIR/passwords $NX_ETC_DIR/passwords.orig
	$COMMAND_PERL -pi -e "s/$PASSDB_CHUSER:.*/$PASSDB_CHUSER:$PASSDB_ENC_PASS/g" $NX_ETC_DIR/passwords
}

passdb_user_exists()
{
	PASSDB_CHUSER="$1"
	egrep -q "^$PASSDB_CHUSER:" $NX_ETC_DIR/passwords 2>/dev/null
}


passdb_remove_user()
{
	PASSDB_CHUSER="$1"
	cp -f $NX_ETC_DIR/passwords $NX_ETC_DIR/passwords.orig
	$COMMAND_PERL -pi -e "s/$PASSDB_CHUSER:.*\n//g" $NX_ETC_DIR/passwords
}

passdb_add_user()
{
	PASSDB_CHUSER="$1"
	cp -f $NX_ETC_DIR/passwords $NX_ETC_DIR/passwords.orig
	echo "$PASSDB_CHUSER:*" >> $NX_ETC_DIR/passwords
	# deactivated to avoid problems with comm-server
	su - $PASSDB_CHUSER -c "$PATH_BIN/nxnode --setkey"
}

passdb_list_user()
{
	cat $NX_ETC_DIR/passwords | cut -d":" -f1
}

#
# End of passdb Library
#

############### PACKAGE session.bm #######################
#
# Library of session management functions
#

# Needed global vars: $NX_SESS_DIR

session_list()
{
	cat $NX_SESS_DIR/running/sessionId"{$1}"
}

# Find all running session-filenames 

session_find_all()
{
	for i in $NX_SESS_DIR/running/*
	do
		[ -f $i ] || break
		echo $i
	done
}

# Find all running sessions of a id
session_find_id()
{
	[ -f $NX_SESS_DIR/running/sessionId"{$1}" ] && echo $NX_SESS_DIR/running/sessionId"{$1}"
}

# finds out if a session belongs to a user

session_find_id_user()
{
	[ -f $NX_SESS_DIR/running/sessionId"{$1}" ] && egrep -q "^userName=$2$" $NX_SESS_DIR/running/sessionId"{$1}" && return 0
	return 1
}

# Find all running sessions of a user
session_find_user()
{
	for i in $NX_SESS_DIR/running/*
	do
		[ -f $i ] || break
		egrep -q "^userName=$1$" $i && echo $i
	done
}

# Find all running sessions of a display
session_find_display()
{	
	for i in $NX_SESS_DIR/running/*
	do
		[ -f $i ] || break
		egrep -q "^display=$1$" $i && echo $i
	done
}

# session_get_cmdline <session filename>

session_get_cmdline()
{
	echo "a=b" | cat - $1 | tr '\n' '&'
}

# session_get <uniqueid>

session_get()
{
	session_get_cmdline $NX_SESS_DIR/running/sessionId"{$1}"
}


# Get the first session, which can be resumed

session_get_user_suspended()
{
	for i in $NX_SESS_DIR/running/*
	do
		[ -f $i ] || break
		if egrep -q "^userName=$1$" $i && egrep -q "^status=$2$" $i
		then
			CMDLINE=$(session_get_cmdline $i)
			echo "$(getparam sessionId)"
			break
		fi
	done
}

# Count all sessions of a user
# and save it in SESSION_COUNT and SESSION_COUNT_USER

session_count_user()
{
	SESSION_COUNT=0
	SESSION_COUNT_USER=0

	for i in $NX_SESS_DIR/running/*
	do
		[ -f $i ] || break
		let SESSION_COUNT=$SESSION_COUNT+1
		egrep -q "^userName=$1$" $i && let SESSION_COUNT_USER=$SESSION_COUNT_USER+1
	done
}

# List all sessions of a user

session_list_user_suspended()
{
	SESSION_COUNT=0
	SESSION_COUNT_USER=0

	TMPFILE=$(mktemp /tmp/nxserver_tmp.XXXXXXXXX)
	echo "NX> 127 Sessions list of user '$1' for reconnect:" > $TMPFILE
	echo >> $TMPFILE
	if [ -z "$4" ]
	then
		
		echo "Display Type             Session ID                       Options  Depth Screensize     Available Session Name" >> $TMPFILE
		echo "------- ---------------- -------------------------------- -------- ----- -------------- --------- ----------------------" >> $TMPFILE
	else
		echo "Display Type             Session ID                       Options  Depth Screen         Status      Session Name" >> $TMPFILE
		echo "------- ---------------- -------------------------------- -------- ----- -------------- ----------- ------------------------------" >> $TMPFILE
	fi
	for i in $NX_SESS_DIR/running/*
	do
		[ -f $i ] || break
		let SESSION_COUNT=$SESSION_COUNT+1
		if egrep -q "^userName=$1$" $i && egrep -q "^status=$2$" $i #&& grep -q "screeninfo=$3" $i
		then
			CMDLINE=$(session_get_cmdline $i)
			depth=$(getparam screeninfo | cut -d "x" -f3 | cut -d "+" -f1 )
			geom=$(getparam screeninfo | cut -d "x" -f1,2) 
			render=$(getparam screeninfo | cut -d "+" -f2 )
			available="N/A"
			udepth=$(echo $3 | cut -d "x" -f3 | cut -d "+" -f1 )
			urender=$(echo $3 | cut -d "+" -f2 )

			mode="D"
			[ "$(getparam sessionRootlessMode)" = "1" ] && mode="-"
			
			options="-"
			stringinstring "fullscreen" "$3" && options="F"
			[ "$(getparam geometry)" = "fullscreen" ] || options="-"
			[ "$urender" = "render" ] && options="${options}R${mode}--PSA"
			[ "$urender" = "render" ] || options="${options}-${mode}--PSA"
			[ "$udepth" = "$depth" -a "$urender" = "$render" ] && available=$(getparam status)
			# FIXME: HACK !!! to keep compatibility with old snapshot version (Knoppix 3.6 based for example)
			if [ -z "$4" -a "$available" != "N/A" ] 
			then
				available="Yes"
			fi
			
			# We automatically offer VNC mirrored sessions for "remote" support
			if [ "$4" = "vnc" -a "$ENABLE_MIRROR_VIA_VNC" = "1" ] && stringinstring "unix-" "$(getparam type)"
			then
				printf "%-7s %-16s %32s %8s %5s %-14s %-11s %s\n" "$(getparam display)" "vnc-mirrored" "$(getparam sessionId)" "$options" "$depth" "$geom" "$available" "$(getparam sessionName) (Mirrored)" >> $TMPFILE
			else
				# only unix-* sessions can be resumed, but other session types can still be terminated
				stringinstring "unix-" "$4" || available="N/A"
				printf "%-7s %-16s %32s %8s %5s %-14s %-11s %s\n" "$(getparam display)" "$(getparam type)" "$(getparam sessionId)" "$options" "$depth" "$geom" "$available" "$(getparam sessionName)" >> $TMPFILE
			fi
		fi
		egrep -q "^userName=$1$" $i && let SESSION_COUNT_USER=$SESSION_COUNT_USER+1
	done
	
	if [ "$ENABLE_DESKTOP_SHARING" = "1" ]
	then
		export DESKTOP_SHARING_IDS=""
		for i in $(LC_ALL=C netstat -ln --protocol=unix | egrep 'X11-unix/X[0-9]$' | sed 's/.*X\(.*\)/\1/g')
		do
			uniqueid=$(echo $[$RANDOM*$RANDOM] | $COMMAND_MD5SUM | cut -d" " -f1 | tr "[a-z]" "[A-Z]")
			DESKTOP_SHARING_IDS="$DESKTOP_SHARING_IDS $uniqueid=$i"
			printf "%-7s %-16s %32s %8s %5s %-14s %-11s %s\n" "$i" "vnc-local" "$uniqueid" "--------" "$udepth" "$(echo $3 | cut -d'x' -f1,2)" "Running" "X$i (Local)" >> $TMPFILE
		done
	fi
	
	echo "" >> $TMPFILE
	echo "" >> $TMPFILE
	
	if [ "$SESSION_COUNT" -ge "$SESSION_LIMIT" -o "$SESSION_COUNT_USER" -ge "$SESSION_USER_LIMIT" ]
	then
		echo "NX> 147 Server capacity: reached for user: $1" >> $TMPFILE
	else
		echo "NX> 148 Server capacity: not reached for user: $1" >> $TMPFILE
	fi
	
	cat $TMPFILE | log_tee
	rm -f $TMPFILE
}

session_list_user()
{
	echo "NX> 127 Sessions list of user '$1'"
	echo
	echo "Server     Display Username        Remote IP       Session ID"
	echo "------ ------- --------------- --------------- --------------------------------"
	for i in $NX_SESS_DIR/running/*
	do
		[ -f $i ] || break
		if egrep -q "^userName=$1$" $i
		then
			CMDLINE=$(session_get_cmdline $i)
			echo -e "$(getparam host)\t$(getparam display)\t$(getparam userName)\t$(getparam foreignAddress)\t$(getparam sessionId)"
		fi
	done
}

session_history()
{
	userName=$1
	sessionId=$2
	echo "NX> 127 Session list:"
	echo
	echo "Display Username        Remote IP       Session ID                       Date                Status"
	echo "------- --------------- --------------- -------------------------------- ------------------- -----------"
	for j in $(ls --time-style +%s -la "$NX_SESS_DIR"/{closed,failed,running} | awk '/sessionId/ { print $6 " " $7 }' | sort -n | cut -d" " -f2)
	do
		if [ -n "$sessionId" ]
		then
			[ "$j" = "sessionId{$sessionId}" ] || continue
		fi
		i="$NX_SESS_DIR"/*/"$j"
		[ -f $i ] || break
		CMDLINE=$(session_get_cmdline $i)
		if [ -n "$userName" ]
		then
			[ "$userName" = "$(getparam userName)" ] || continue
		fi
		echo -e "$(getparam display)\t$(getparam userName)\t$(getparam foreignAddress)\t$(getparam sessionId)\t$(ls --time-style="+%F %X" -l $i | awk '/sessionId/ { print $6 " " $7 }')\t$(getparam status)"
	done
}

# remove all sessions older than $SESSION_HISTORY seconds in failed/closed.

session_cleanup()
{
	[ "$SESSION_HISTORY" -gt "-1" ] || return
	let SESSION_HISTORY_MINUTES=$SESSION_HISTORY/60

	# If you need posix find compatibility, use the outcommented variant below.
	find $NX_SESS_DIR/closed/ $NX_SESS_DIR/failed/ -type f -mmin +"$SESSION_HISTORY_MINUTES" -exec rm -f '{}' ';'
	#let SESSION_HISTORY_DAYS=$SESSION_HISTORY_MINUTES/1440
	#find $NX_SESS_DIR/closed/ $NX_SESS_DIR/failed/ -type f -mtime +"$SESSION_HISTORY_DAYS" -exec rm -f '{}' ';'
}

session_list_all()
{
	echo "NX> 127 Sessions list:"
	echo
	echo "Server     Display Username        Remote IP       Session ID"
	echo "------ ------- --------------- --------------- --------------------------------"
	for i in $NX_SESS_DIR/running/*
	do
		[ -f $i ] || break
		CMDLINE=$(session_get_cmdline $i)
		echo -e "$(getparam host)\t$(getparam display)\t$(getparam userName)\t$(getparam foreignAddress)\t$(getparam sessionId)"
	done
}


# session_add <session_id> <options>

session_add()
{
	id=$1
	shift
	echo "$@" | tr '&' '\n' > $NX_SESS_DIR/running/sessionId'{'$id'}'
}

# session_change <session_id> <parameter> <new_value>

session_change()
{
	[ -f $NX_SESS_DIR/running/sessionId'{'$1'}' ] && $COMMAND_PERL -pi -e "s/$2=.*/$2=$3/" $NX_SESS_DIR/running/sessionId'{'$1'}'
}

# session_id <new status>

session_status()
{
	session_change "$1" "status" "$2"
}

# session_running <session_id>
# return: true if running, false if not

session_running()
{
	test -f $NX_SESS_DIR/running/sessionId'{'$1'}'
}

# session_close <session_id> <end-time>

session_close()
{
	$COMMAND_PERL -pi -e "s/startTime=\(.*\)/startTime=\1\nendTime=$(date +%s)/" $NX_SESS_DIR/running/sessionId'{'$1'}'
	session_status $1 "Finished"
	[ "$SESSION_HISTORY" = "0" ] && rm -f $NX_SESS_DIR/running/sessionId'{'$1'}'
	[ "$SESSION_HISTORY" = "0" ] || mv -f $NX_SESS_DIR/running/sessionId'{'$1'}' $NX_SESS_DIR/closed/sessionId'{'$1'}'
}

session_fail()
{
	$COMMAND_PERL -pi -e "s/startTime=\(.*\)/startTime=\1\nendTime=$(date +%s)/" $NX_SESS_DIR/running/sessionId'{'$1'}'
	session_status $1 "Failed"
	[ "$SESSION_HISTORY" = "0" ] && rm -f $NX_SESS_DIR/running/sessionId'{'$1'}'
	[ "$SESSION_HISTORY" = "0" ] || mv -f $NX_SESS_DIR/running/sessionId'{'$1'}' $NX_SESS_DIR/failed/sessionId'{'$1'}'
}

session_suspend()
{
	session_status $1 "Suspended"
	session_change $1 foreignAddress "-"
}

#
# end of library
#


#
# Main nxserver <-> nxclient communication module
#

if [ $USER = "nxfree" -o "$USER" = "nx" -o "$ENABLE_USERMODE_AUTHENTICATION" = "1" ]
then

setup_usermode_auth()
{

	[ $USER = "nxfree" -o "$USER" = "nx" ] && ENABLE_USERMODE_AUTHENTICATION="0"

	if [ "$ENABLE_USERMODE_AUTHENTICATION" = "1" ]
	then
		export NX_SESS_DIR="$USER_FAKE_HOME/.nx/db/"
		export NX_LOGFILE="$USER_FAKE_HOME/.nx/temp/nxserver.log"
		mkdir -p $NX_SESS_DIR/{closed,running,failed}
	fi
}

setup_usermode_auth

# Loglevels:
# 1: Errors
# 2: Warnings
# 3: Important information
# 4: Server - Client communication
# 5: Information
# 6: Debugging information
# 7: stderror-channel of some applications

log()
{
	[ "$NX_LOG_LEVEL" -ge "$1" -a -w "$NX_LOGFILE" ] && shift && echo "$@" >> "$NX_LOGFILE"
}

# Log in a way that is secure for passwords / cookies / ...

echo_secure()
{
	echo "$@ " | $COMMAND_PERL -pi -e 's/--cookie=".+?"/--cookie="******"/g; s/--agent_password=".+?"/agent_password="******"/g; s/--password=".+?"/password="******"/g; s/cookie=.+?&/cookie=******&/g; s/agent_password=.+?&/agent_password=******&/g; s/password=.+?&/password=******&/g;'
}

log_secure()
{
	if [ "$NX_LOG_SECURE" = "0" ]
	then
		log "$@"
	else
		[ "$NX_LOG_LEVEL" -ge "$1" -a -w "$NX_LOGFILE" ] && shift && echo_secure "$@" >> "$NX_LOGFILE"
	fi
}

log_tee()
{
	[ "$NX_LOG_LEVEL" -ge "4" -a -w "$NX_LOGFILE" ] && exec tee -a "$NX_LOGFILE"
	[ "$NX_LOG_LEVEL" -ge "4" -a -w "$NX_LOGFILE" ] || exec cat -
}

log_error()
{
	[ "$NX_LOG_LEVEL" -ge "7" -a -w "$NX_LOGFILE" ] && exec tee -a "$NX_LOGFILE"
	[ "$NX_LOG_LEVEL" -ge "7" -a -w "$NX_LOGFILE" ] || exec cat - 
}

echo_x()
{
	log "4" "$@"
	echo "$@"
}


#
# needed for slave mode
#

nxnode_login_stop_slave()
{
	if [ -n "$NXNODE_LOGIN_SLAVE" ]
	then
		kill "$NXNODE_LOGIN_SLAVE"
		sleep 2
		kill -0 "$NXNODE_LOGIN_SLAVE" && kill -9 "$NXNODE_LOGIN_SLAVE"
	fi
}

nxnode_login()
{
	PASS="$1"
	shift

	if [ "$NXNODE_LOGIN_SLAVE_ENABLED" != "1" ]
	then
		NXNODE_TOSEND="$NXNODE_TOSEND" echo $PASS | $PATH_BIN/nxnode-login "$@"
	else
		if [ -z "$NXNODE_LOGIN_SLAVE" ]
		then
			# Send password
			echo "$PASS" >&4 

			# Connect to NXNODE
			
			$PATH_BIN/nxnode-login "$1" "$2" "$3" "$4" "$5" "--slave" <&3 >&3 2>&3 &
			NXNODE_LOGIN_SLAVE="$!"
			disown $!

			trap nxnode_login_stop_slave EXIT
			
			while read line <&4
			do
				log 6 "$line"
				case "$line" in
					"NX> 716 Slave mode started successfully.")
						break
					;;
				esac
			done

			# TODO: KILL the slave node at end of session
		fi
		
		#send CMD to nxnode
		
		echo "$6" >&4
		[ -n "$NXNODE_TOSEND" ] && echo "$NXNODE_TOSEND" >&4

		NXNODE_RETURN="1"

		if [ -z "$NXNODE_READER" ]
		then
			while read line <&4
			do
				echo "$line"
				case "$line" in
					"NX> 716"*) 
						NXNODE_RETURN="0"
					;;
					"NX> 1001"*)
						break
					;;
				esac
			done
		fi
		test "$NXNODE_RETURN" = "0"
	fi
}

nxnode_login_register_reader()
{
	# Register a reader
	NXNODE_READER="1"
}

#ENABLE_NXNODE_SLAVE_MODE="1"

# Start!
[ "$NX_LOG_LEVEL" -ge "1" ] && touch "$NX_LOGFILE" >/dev/null 2>&1
log 3 "-- NX SERVER START: $@ - ORIG_COMMAND=$SSH_ORIGINAL_COMMAND"

if [ "$ENABLE_SERVER_FORWARD" = "1" -a -n "$SERVER_FORWARD_HOST" ]
then
	log 3 "Info: Forwarding connection to $SERVER_FORWARD_HOST with secret key $SERVER_FORWARD_KEY."
	$COMMAND_SSH -i "$SERVER_FORWARD_KEY" "-p$SERVER_FORWARD_PORT" "nx@$SERVER_FORWARD_HOST" "host=$SERVER_NAME"
	exit 0
fi

# Get the hostname out of SSH_ORIGINAL_COMMAND
PREFERRED_HOST=$(echo $SSH_ORIGINAL_COMMAND | tr '&' '\n' | grep "^host=" | cut -d'=' -f2)

# forward the connection to commercial NoMachine server?
if [ "$ENABLE_NOMACHINE_FORWARD_PORT" = "1" -a "$NOMACHINE_FORWARD_PORT" = "$(echo $SSH_CLIENT $SSH2_CLIENT| cut -d' ' -f3)" -a -n "$NOMACHINE_SERVER" ]
then
	log 3 "Info: Detected SSH destination port $NOMACHINE_FORWARD_PORT. Forwarding connection to commercial NoMachine server."
	exec $NOMACHINE_SERVER
	log 1 "Error: Forwarding to NoMachine Server $NOMACHINE_SERVER failed. Using FreeNX server instead."
fi

#
# nxnode slave mode preparations
#

NXNODE_LOGIN_SLAVE_ENABLED="0"
NXNODE_LOGIN_SLAVE=""

if [ "$ENABLE_NXNODE_SLAVE_MODE" = "1" -a "$NXSERVER_HELPER_ACTIVE" != "1" ]
then
	export SSH_ORIGINAL_COMMAND
	export NXSERVER_HELPER_ACTIVE="1"
	exec $PATH_BIN/nxserver-helper "$0"
	log 1 "Error: Execution of $PATH_BIN/nxserver-helper failed. Disabling slave mode of nxnode."
fi

if [ "$ENABLE_NXNODE_SLAVE_MODE" = "1" -a "$NXSERVER_HELPER_ACTIVE" = "1" ]
then
	log 3 "Info: Using fds #3 and #4 for communication with nxnode."
	NXNODE_LOGIN_SLAVE_ENABLED="1"
fi

echo_x "HELLO NXSERVER - Version $NX_VERSION $NX_LICENSE"

# Login stage
while true
do
	echo_x -n "NX> 105 "
	read CMD
	# FIXME?
	[ "$CMD" = "" ] && CMD="quit"
	echo_x "$CMD"
	
	case "$CMD" in 
		quit|QUIT)
			echo_x "Quit"
			echo_x "NX> 999 Bye"
			exit 0
		;;
		exit|EXIT)
			echo_x "Exit"
			echo_x "NX> 999 Bye"
			exit 0
		;;
		bye|BYE)
			echo_x "Bye"
			echo_x "NX> 999 Bye"
			exit 0
		;;
		hello*|HELLO*)
			PROTO=$(echo $CMD | sed 's/.*Version \(.*\)/\1/g')
			echo_x "NX> 134 Accepted protocol: $PROTO"
			if [ "$PROTO" = "1.3.0" -o "$PROTO" = "1.3.2" ]
			then
				[ "$ENABLE_AUTORECONNECT_BEFORE_140" = "1" ] && ENABLE_AUTORECONNECT="1"
			fi
		;;
		"set auth_mode*"|"SET AUTH_MODE*")
			if [ "$CMD" = "set auth_mode password" -o "$CMD" = "SET AUTH_MODE PASSWORD" ]
			then
				echo_x "Set auth_mode: password"
			else
				echo_x "NX> 500 ERROR: unknown auth mode ''"
			fi
		;;
		login|LOGIN)
			LOGIN_SUCCESS="0"
			
			echo_x -n "NX> 101 User: "
			read USER
			echo_x $USER
			
			echo_x -n "NX> 102 Password: "
			read -s PASS
			echo_x ""
			log 6 -n "Info: Auth method: "
			
			# USER already logged in?
			if [ "$ENABLE_USERMODE_AUTHENTICATION" = "1" ]
			then
				LOGIN_SUCCESS="1"
				LOGIN_METHOD="USERMODE"
				USER=$(whoami)
			fi

			# PASSDB based auth
			if [ "$ENABLE_PASSDB_AUTHENTICATION" = "1" -a "$LOGIN_SUCCESS" = "0" ]
			then
				log 6 -n "passdb "
				if [ $(passdb_get_crypt_pass "$PASS") = $(passdb_get_pass "$USER") ]
				then
					LOGIN_SUCCESS="1"
					LOGIN_METHOD="PASSDB"
				fi
			fi

			# SSH based auth
			if [ "$ENABLE_SSH_AUTHENTICATION" = "1" -a "$LOGIN_SUCCESS" = "0" ]
			then
				log 6 -n "ssh "
				export COMMAND_SSH			
				nxnode_login "$PASS" -- ssh "$USER" "$SSHD_PORT" "$PATH_BIN/nxnode" --check 2>&1 >/dev/null
				if [ $? -eq 0 ]
				then
					LOGIN_SUCCESS="1"
					LOGIN_METHOD="SSH"
				fi
			fi
			
			# SU based auth
			if [ "$ENABLE_SU_AUTHENTICATION" = "1" -a "$LOGIN_SUCCESS" = "0" ]
			then
				log 6 -n "su "
				nxnode_login "$PASS" -- su "$USER" "" "$PATH_BIN/nxnode" --check 2>&1 >/dev/null
				if [ $? -eq 0 ]
				then
					LOGIN_SUCCESS="1"
					LOGIN_METHOD="SU"
				fi
			fi
			
			# Check if user in passdb
			if [ "$ENABLE_USER_DB" = "1" ]
			then
				log 6 "userdb check"
				passdb_user_exists "$USER" || LOGIN_SUCCESS="0"
			fi
			log 6 ""

			if [ "$LOGIN_SUCCESS" = "1" ]
			then
				# Reread the config files (so that $USER.node.conf get sourced)
				. $(PATH=$(cd $(dirname $0) && pwd):$PATH which nxloadconfig) --userconf
				setup_usermode_auth

				echo_x "NX> 103 Welcome to: $SERVER_NAME user: $USER"
				break
			else
				echo_x "NX> 404 ERROR: wrong password or login"
				echo_x "NX> 999 Bye"
				exit 1
			fi
		;;
	esac
done

# remove old session infos from history
session_cleanup

#
# call it with: server_get_params $CMD # no ""!
#

server_get_params()
{
	SERVER_PARAMS=$(echo "$@" | sed "s/^$1/\"/g; s/\" --/\&/g; s/\"//g; s/%20/ /g")
	if [ "$SERVER_PARAMS" = "" ]
	then
		echo_x -n "NX> 106 Parameters: "
		read SERVER_PARAMS2
		SERVER_PARAMS=$(echo $SERVER_PARAMS2 | sed 's/%2B/+/g; s/%20/ /g')
		echo_x
	fi
}

nxnode_start()
{
	:
	#CMD="$1"
	#shift
	#echo "$@" | $PATH_BIN/nxnode "$CMD"
}

#NX> 1002 Commit
#NX> 1006 Session status: running

server_nxnode_start()
{
	CMD="$1"
	USER="$2"
	shift
	shift

	# Find NODE_HOSTNAME
	
	NODE_HOSTNAME=""
	CMDLINE="$@"
	uniqueid=$(getparam uniqueid)
	[ -z "$uniqueid" ] && uniqueid=$(getparam sessionid)
	[ -z "$uniqueid" ] && uniqueid=$(getparam session_id)
	CMDLINE=$(session_get "$uniqueid")
	
	NODE_HOSTNAME="$(getparam host)"
	[ -z "$NODE_HOSTNAME" ] && NODE_HOSTNAME="127.0.0.1"
	export NODE_HOSTNAME
	
	# Use nxnode-login?
	if [ "$LOGIN_METHOD" = "SSH" ]
	then
	    export COMMAND_SSH
	    NXNODE_TOSEND="$@" nxnode_login "$PASS" -- ssh "$USER" "$SSHD_PORT" "$PATH_BIN/nxnode" "$CMD" 2>&1 | log_tee
	elif [ "$LOGIN_METHOD" = "SU" ]
	then
	    NXNODE_TOSEND="$@" nxnode_login "$PASS" -- su "$USER" "" "$PATH_BIN/nxnode" "$CMD" 2>&1 | log_tee
	elif [ "$LOGIN_METHOD" = "USERMODE" ]
	then
	    echo "$@" | $PATH_BIN/nxnode "$CMD" 2>&1 | log_tee
	else
	    echo "$@" | $COMMAND_SSH -l "$USER" "$NODE_HOSTNAME" -p $SSHD_PORT -x -2 -i $NX_ETC_DIR/users.id_dsa -o 'PubkeyAuthentication yes' -o 'RSAAuthentication yes' -o 'RhostsAuthentication no' -o 'PasswordAuthentication no' -o 'RhostsRSAAuthentication no' -o 'StrictHostKeyChecking no' $PATH_BIN/nxnode "$CMD" | log_tee
	fi
}

server_add_usession()
{
	[ "$ENABLE_USESSION" = "1" ] || return
	
	$COMMAND_SESSREG -l ":$SESS_DISPLAY" -h "$USERIP" -a $USER 2>&1 | log_error
}

server_remove_usession()
{
	[ "$ENABLE_USESSION" = "1" ] || return
	$COMMAND_SESSREG -l ":$SESS_DISPLAY" -h "$USERIP" -d $USER 2>&1 | log_error
}

server_nxnode_echo()
{
	log 6 "server_nxnode_echo: $@"
	[ "$SERVER_CHANNEL" = "1" ] && echo "$@"
	[ "$SERVER_CHANNEL" = "2" ] && echo "$@" >&2
}

server_nxnode_exit_func()
{
	log 1 "Info: Emergency-Shutting down due to kill signal ..."
	
	session_fail $uniqueid
	
	server_remove_usession

	# remove lock file
	[ -e "/tmp/.nX$SESS_DISPLAY-lock" ] && rm -f /tmp/.nX$SESS_DISPLAY-lock

	# Kill possible slave node
	nxnode_login_stop_slave
}

server_nxnode_start_wait()
{
	if [ "$1" = "--startsession" ]
	then
	
	server_add_usession

	# We need to stop sending things when a SIGPIPE arrives
	trap "SERVER_CHANNEL=0" SIGPIPE
	
	trap server_nxnode_exit_func EXIT
	
	SERVER_CHANNEL=1
	KILL_WAIT_PID=1
	server_nxnode_start "$@" | while read CMD
	do
		case "$CMD" in 
			"NX> 1006"*|"NX> 1005"*|"NX> 1009"*)
				case "$CMD" in 
					*running*)
						[ "$KILL_WAIT_PID" = "1" ] && kill $SERVER_WAIT_PID
						KILL_WAIT_PID=0
						log 6 session_status $uniqueid "Running"
						session_status $uniqueid "Running"
						[ "$SERVER_CHANNEL" = "1" ] && SERVER_CHANNEL=2
					;;
					*closed*)
						log 6 session_close $uniqueid
						session_close $uniqueid
					;;
					*suspended*)
						[ "$KILL_WAIT_PID" = "1" ] && kill $SERVER_WAIT_PID
						KILL_WAIT_PID=0
						log 6 session_suspend $uniqueid
						session_suspend $uniqueid
					;;
					*suspending*)
						log 6 session_status $uniqueid "Suspending"
						session_status $uniqueid "Suspending"
						# we need to stop sending to client as it will have already
						# closed his side of the channel and this will lead to not 
						# closed sessions.
						SERVER_CHANNEL=0
					;;
					*terminating*)
						log 6 session_status $uniqueid "Terminating"
						session_status $uniqueid "Terminating"
						# we need to stop sending to client as it will have already
						# closed his side of the channel and this will lead to not 
						# closed sessions.
						SERVER_CHANNEL=0
				esac
			;;
			"NX> 1004"*)
				[ "$KILL_WAIT_PID" = "1" ] && kill $SERVER_WAIT_PID
				KILL_WAIT_PID=0
				session_fail $uniqueid
				server_nxnode_echo "NX> 596 Session startup failed."
				log 4 "NX> 596 Session startup failed."
			;;
		esac

		case $CMD in
			"NX> "*)
				server_nxnode_echo $CMD
			;;
		esac
	done

	trap - EXIT
	trap - SIGPIPE
	
	# Close it in case the session is still running
	session_running $uniqueid && session_close $uniqueid
	
	server_remove_usession

	# remove lock file
	[ -e "/tmp/.nX$SESS_DISPLAY-lock" ] && rm -f /tmp/.nX$SESS_DISPLAY-lock

	nxnode_login_stop_slave

	else # $1 = restore
	
	KILL_WAIT_PID=1
	SERVER_CHANNEL=1
	server_nxnode_start "$@" | while read CMD
	do
		case "$CMD" in 
			"NX> 1006"*|"NX> 1005"*|"NX> 1009"*)
				case "$CMD" in 
					*running*)
						[ "$KILL_WAIT_PID" = "1" ] && kill $SERVER_WAIT_PID
						KILL_WAIT_PID=0
						SERVER_CHANNEL=2
					;;
				esac
			;;
			"NX> 1004"*)
				[ "$KILL_WAIT_PID" = "1" ] && kill $SERVER_WAIT_PID
				KILL_WAIT_PID=0
				
				# This fail is correct here as somehow the 
				# monitor process might have died and we don't 
				# want the session to be resumed again.
				
				session_fail $uniqueid
				
				server_nxnode_echo "NX> 596 Session startup failed."
				log 4 "NX> 596 Session startup failed."
				break;
			;;
		esac
	
		case $CMD in
			"NX> "*)
				server_nxnode_echo $CMD
			;;
		esac
	done
	
	nxnode_login_stop_slave
	
	fi # $1 = start
}

server_check_session_count()
{
	session_count_user "$USER"
	
	if [ "$SESSION_COUNT" -ge "$SESSION_LIMIT" ]
	then
		echo_x "NX> 599 Reached the maximum number of concurrent sessions on this server."
		echo_x "NX> 500 ERROR: Last operation failed."
		return 1
	fi
	
	if [ "$SESSION_COUNT_USER" -ge "$SESSION_USER_LIMIT" ]
	then
		echo_x "NX> 599 Server capacity: reached for user: $USER"
		echo_x "NX> 500 ERROR: Last operation failed."
		return 1
	fi

	return 0
}

server_loadbalance_random()
{
	# Pick one based on "random" algorithm
	SERVER_LB_HOSTS=( $LOAD_BALANCE_SERVERS )
	SERVER_LB_NR_OF_HOSTS=${#SERVER_LB_HOSTS[@]}
	let SERVER_LB_NR=(RANDOM % SERVER_LB_NR_OF_HOSTS)
	SERVER_LB_HOST=${SERVER_LB_HOSTS[$SERVER_LB_NR]}
	echo $SERVER_LB_HOST
}

# run in subshell!

server_loadbalance_round_robin()
{
	SERVER_LB_HOSTS=( $LOAD_BALANCE_SERVERS )
	SERVER_LB_NR_OF_HOSTS=${#SERVER_LB_HOSTS[@]}
	
	# Atomic incrementation:

	# Enter critical section
	# - Create .lock file
	
	SERVER_LB_LOCKFILE=$(mktemp "$NX_SESS_DIR/round-robin.lock.XXXXXXXXX")

	trap "rm -f $SERVER_LB_LOCKFILE" EXIT
	
	i=0
	while [ $i -lt 200 ]
	do
		# ln is an atomic operation
		ln $SERVER_LB_LOCKFILE "$NX_SESS_DIR/round-robin.lock" && break
		LANG=C sleep 0.01
		let i=i+1
	done

	if [ $i -ge 200 ]
	then
		log 1 "Load-Balancing: Round-Robin failed to gain lock file in 200 tries. Falling back to random."
		server_loadbalance_random
		return
	fi
	
	trap "rm -f $SERVER_LB_LOCKFILE $NX_SESS_DIR/round-robin.lock" EXIT

	# Lock held

	SERVER_LB_NR=$(cat $NX_SESS_DIR/round-robin 2>/dev/null)
	let SERVER_LB_NR=(SERVER_LB_NR+1) % SERVER_LB_NR_OF_HOSTS
	echo $SERVER_LB_NR >$NX_SESS_DIR/round-robin

	# Exit critical section
	rm -f "$SERVER_LB_LOCKFILE $NX_SESS_DIR/round-robin.lock"

	trap - EXIT

	SERVER_LB_HOST=${SERVER_LB_HOSTS[$SERVER_LB_NR]}
	echo $SERVER_LB_HOST
}

server_loadbalance_load()
{
	SERVER_LB_MAX=0
	SERVER_LB_HOST=""
	
	for i in $LOAD_BALANCE_SERVERS
	do
		SERVER_LB_LOAD=$($NX_DIR/bin/nxcheckload $i)
		[ -z "$SERVER_LB_LOAD" ] && continue
		
		if [ $SERVER_LB_LOAD -gt $SERVER_LB_MAX ]
		then
			SERVER_LB_MAX=$SERVER_LB_LOAD
			SERVER_LB_HOST=$i
		fi
	done
	echo $SERVER_LB_HOST
}

server_loadbalance()
{
	SERVER_HOST="127.0.0.1"
	if [ -n "$LOAD_BALANCE_SERVERS" ]
	then
		SERVER_HOST=""
		if [ -n "$PREFERRED_HOST" -a "$ENABLE_LOAD_BALANCE_PREFERENCE" = "1" ]
		then
			stringinstring " $PREFERRED_HOST " " $LOAD_BALANCE_SERVERS " && SERVER_HOST="$PREFERRED_HOST"
		fi
		
		# Fallback if still empty
		if [ -z "$SERVER_HOST" ]
		then
			case "$LOAD_BALANCE_ALGORITHM" in
				random)
					SERVER_HOST=$(server_loadbalance_random)
				;;
				round-robin)
					SERVER_HOST=$(server_loadbalance_round_robin)
				;;
				load)
					SERVER_HOST=$(server_loadbalance_load)
				;;
			esac
		fi
		
		[ -z "$SERVER_HOST" ] && SERVER_HOST="127.0.0.1"
		[ -n "$SERVER_HOST" ] && log 5 "Info: Load-Balancing (if possible) to $SERVER_HOST ..."
	fi
	echo "$SERVER_HOST"
}	

server_startrestore_session()
{
	ACTION="$1"
	
	server_get_params $CMD
	PARAMS=$SERVER_PARAMS
	PARAMS="$PARAMS&clientproto=$PROTO"
	CMDLINE=$PARAMS
	echo_x

	# special mirrored type
	[ "$ACTION" != "start" -a "$(getparam type)" = "vnc" ] && ACTION="mirror"

	if [ "$ACTION" = "mirror" ]
	then
		ACTION="start"
		uniqueid=$(getparam restore)
		[ -z "$uniqueid" ] && uniqueid=$(getparam id) # 1.4.0-5 compatibility
		CMDLINE=$(session_get "$uniqueid" 2>/dev/null)

		mirrordisplay=$(getparam display)
		mirrorhost=$(getparam host)

		if [ -z "$mirrordisplay"  ]
		then
			# check for DESKTOP_SHARING_IDS
			mirrordisplay=$(echo $DESKTOP_SHARING_IDS | tr ' ' '\n' | egrep "^$uniqueid=" | cut -d'=' -f2)
			mirrorhost="127.0.0.1"
		fi

		if [ -z "$mirrordisplay" ]
		then
			echo_x "NX> 596 Could not find mirrored session $uniqueid. Session failed."
			echo_x "NX> 596 Sharing: $DESKTOP_SHARING_IDS"
			return 1
		fi

		PARAMS="$PARAMS&mirrordisplay=$mirrordisplay&mirrorhost=$mirrorhost"
		CMDLINE=$PARAMS
	fi
	
	if [ "$ACTION" = "start" ]
	then

		# Hack for external RDP/RFB agents

		if [ "$ENABLE_EXTERNAL_NXDESKTOP" = "1" -a "$(getparam type)" = "windows" ]
		then
			type="unix-application"
			application="$PATH_BIN/nxdesktop_helper"
			PARAMS="$PARAMS&type=$type&application=$application&freenx_export_agents=1"
			CMDLINE=$PARAMS
		fi
		 
		if [ "$ENABLE_EXTERNAL_NXVIEWER" = "1" -a "$(getparam type)" = "vnc" ]
		then
			type="unix-application"
			application="$PATH_BIN/nxviewer_helper"
			PARAMS="$PARAMS&type=$type&application=$application&freenx_export_agents=1"
			CMDLINE=$PARAMS
		fi
	fi
	
	# If we can't get the userip and SSHD_CHECK_IP is set to 1
	# we bail out.
	if [ -z "$SSH_CLIENT" -a -z "$SSH2_CLIENT" ]
	then 
		if [ "$SSHD_CHECK_IP" = "1" ]
		then
			echo_x "NX> 596 Session startup failed. (Missing SSH_CLIENT environment variable)"
			return 1
		else
			log 2 "Warning: Failed to determine the client IP."
			log 2 "Warning: The SSH_CLIENT or SSH2_CLIENT variable was not provided by SSHD."
			log 2 "Warning: Please set SSHD_CHECK_IP=1 if you want to refuse the connection."
		fi
	fi
	
	export ENCRYPTION=$(getparam encryption)
	
	if [ "$ENABLE_FORCE_ENCRYPTION" = "1" -a "$ENCRYPTION" != "1" ]
	then
			echo_x "NX> 596 Unencrypted sessions are not allowed."
			return 1
	fi

	# check if there is a suspended session, which we could resume
	if [ "$ENABLE_AUTORECONNECT" = "1" -a "$ACTION" = "start" ]
	then
		restore=$(session_get_user_suspended "$USER" "Suspended")
		if [ -n "$restore" ]
		then
			PARAMS="$PARAMS&restore=$restore"
			CMDLINE=$PARAMS
			ACTION="resume"
		fi
	fi

	# as only $SSH_CLIENT or $SSH2_CLIENT will be set, this should work
	USERIP=$(echo $SSH_CLIENT $SSH2_CLIENT | cut -d" " -f1 | sed 's/::ffff://g')
	[ -z "$USERIP" ] && USERIP="*"
	if [ "$ACTION" = "start" -o "$ACTION" = "mirror" ]
	then
		server_check_session_count || return 1
		
		# start nxnode
		SESS_DISPLAY=$DISPLAY_BASE
		let SESS_DISPLAY_LIMIT=$DISPLAY_BASE+$DISPLAY_LIMIT
	
		# stupid but working algo ...
			
		# TODO: need to check for _all_ offset and ports :-/
			
		while true
		do
			while [ -e /tmp/.X$SESS_DISPLAY-lock -o -e "/tmp/.nX$SESS_DISPLAY-lock" ]
			do
				let SESS_DISPLAY=$SESS_DISPLAY+1
			done

			# Check if there is already an agent running on that display
			let AGENT_DISPLAY=$SESS_DISPLAY+6000
			if $COMMAND_NETCAT -z 127.0.0.1 $AGENT_DISPLAY 2>/dev/null
			then
				log 2 "Warning: Stray nxagent without .X$SESS_DISPLAY-lock found on port $AGENT_DISPLAY."
				let SESS_DISPLAY=$SESS_DISPLAY+1
				continue
			fi
			

			SESS_LOCKFILE=$(mktemp "/tmp/.nX$SESS_DISPLAY-lock.XXXXXXXXX")
			# ln is an atomic operation
			ln "$SESS_LOCKFILE" "/tmp/.nX$SESS_DISPLAY-lock" 2>/dev/null && break
		done

		rm -f "$SESS_LOCKFILE"
		
		if [ "$SESS_DISPLAY" -gt "$SESS_DISPLAY_LIMIT" ]
		then
			echo_x "NX> 596 Error: Display limit exceeded. Please remove some files from /tmp/.X*-lock."
			rm -f "/tmp/.nX$SESS_DISPLAY-lock"
			return
		fi
	
		uniqueid=$(echo $[$RANDOM*$RANDOM] | $COMMAND_MD5SUM | cut -d" " -f1 | tr "[a-z]" "[A-Z]")

		# Possibly do loadbalancing
		
		SERVER_HOST=$(server_loadbalance)
		
		FULL_PARAMS="$PARAMS&user=$USER&userip=$USERIP&uniqueid=$uniqueid&display=$SESS_DISPLAY&host=$SERVER_HOST"
		log_secure "6" "$FULL_PARAMS"

		# now update the session listing
		sessionRootlessMode=0
		[ "$(getparam rootless)" = "1" ] && sessionRootlessMode=1
		CMDLINE="a=b&$FULL_PARAMS"
		session_add $uniqueid "sessionName=$(getparam session)&display=$(getparam display)&status=Running&startTime=$(date +%s)&foreignAddress=$(getparam userip)&sessionRootlessMode=$sessionRootlessMode&type=$(getparam type)&sessionId=$uniqueid&creationTime=$(date +%s)&userName=$USER&serverPid=$SERVER_PID&screeninfo=$(getparam screeninfo)&geometry=$(getparam geometry)&host=$SERVER_HOST"
	else
		uniqueid=$(getparam restore)
		[ -z "$uniqueid" ] && uniqueid=$(getparam id) # 1.4.0-5 compatibility
		session_change "$uniqueid" "foreignAddress" "$USERIP"

		CMDLINE=$(session_get "$uniqueid")
		FULL_PARAMS="$PARAMS&user=$USER&userip=$(getparam foreignAddress)&uniqueid=$uniqueid&display=$(getparam display)&status=$(getparam status)"
		SESS_DISPLAY=$(getparam display)
		SERVER_HOST=$(getparam host)
		[ -z "$SERVER_HOST" ] && SERVER_HOST="127.0.0.1"
	fi

	# now start the node
	sleep $AGENT_STARTUP_TIMEOUT &
	SERVER_WAIT_PID=$!
	( server_nxnode_start_wait --"$ACTION"session $USER "$FULL_PARAMS" ) &
	SERVER_PID=$!
	disown $SERVER_PID
	
	wait $SERVER_WAIT_PID
	
	if [ $? -eq 0 ]
	then
		# Something went wrong ...
		[ "$ACTION" = "start" ] && session_fail $uniqueid
		echo_x "NX> 1004 Error: Session did not start."
		echo_x "NX> 596 Session $ACTION failed."
		echo_x "NX> 999 Bye"
		# FIXME: Send node signal to terminate
		exit 1
	fi
	
	# We have now an active reader
	nxnode_login_register_reader
}

# Session stage
while true
do
	echo_x -n "NX> 105 "
	unset CMD
	read CMD 2>/dev/null
	# FIXME?
	[ "$CMD" = "" ] && CMD="quit"
	
	# Logging 
	case "$CMD" in
		startsession*|restoresession*|addmount*|addprinter*)
			echo_secure "$CMD"
			log_secure "4" "$CMD"
		;;
		*)
			echo "$CMD"
			log "4" "$CMD"
		;;
	esac
	
	case "$CMD" in 
		quit|QUIT)
			echo_x "Quit"
			echo_x "NX> 999 Bye"
			exit 0
		;;
		exit|EXIT)
			echo_x "Exit"
			echo_x "NX> 999 Bye"
			exit 0
		;;
		bye|BYE)
			echo_x "Bye" 1>&2
			echo_x "NX> 999 Bye" 1>&2
			if [ "$ENCRYPTION" = "1" ] 
			then 
				let PROXY_DISPLAY=$SESS_DISPLAY+4000
				exec $COMMAND_NETCAT $SERVER_HOST $PROXY_DISPLAY
			else
				echo_x "NX> 1001 Bye."
			fi
		;;
		startsession*)
			server_startrestore_session "start"
		;;
		list*)
			server_get_params $CMD
			PARAMS=$SERVER_PARAMS
			CMDLINE=$PARAMS
			
			status=$(getparam status)

			if [ "$status" = "Suspended" -a -n "$(getparam screeninfo)" ]
			then
				session_list_user_suspended "$USER" "Suspended" "$(getparam screeninfo)" "$(getparam type)"
			elif [ "$status" = "Suspended,Running" -o "$status" = "Suspended" ] # since 1.4.0-5
			then
				# disabled due to problems with 1.4.0-5 client
				#session_list_user_suspended "$USER" 'Suspended$|^status=Running$' "$(getparam geometry)" "$(getparam type)" | log_tee
				session_list_user_suspended "$USER" 'Suspended' "$(getparam geometry)" "$(getparam type)"
			elif [ "$status" = "suspended,running" -o "$status" = "suspended" ] # since 1.5.0
			then
				status=$(echo $status | sed 's/,/$|^status=/g; s/suspended/Suspended/g; s/running/Running/g')
				session_list_user_suspended "$USER" "$status" "$(getparam geometry)" "$(getparam type)"
			else
				session_list_user "$USER" | log_tee
			fi
		;;
		suspend*)
			server_get_params $CMD
			PARAMS=$SERVER_PARAMS
			CMDLINE=$PARAMS
			if session_find_id_user "$(getparam sessionid)" "$USER"
			then
				server_nxnode_start --suspend "$USER" "$PARAMS"
			fi
		;;
		terminate*)
			server_get_params $CMD
			PARAMS=$SERVER_PARAMS
			CMDLINE=$PARAMS
			if session_find_id_user "$(getparam sessionid)" "$USER"
			then
				server_nxnode_start --terminate "$USER" "$PARAMS"
			fi
		;;
		restoresession*)
			server_startrestore_session "resume"
		;;
		mirrorsession*)
			server_startrestore_session "mirror"
		;;

		passwd)
			echo_x "NX> 113 Changing password of user '$USER'"
			echo_x -n "NX> 102 Current password:"
			read -s PASS
			ENC_PASS=$(passdb_get_crypt_pass "$PASS")
			REAL_PASS=$(passdb_get_pass "$USER")
			echo_x
			if [ "$ENC_PASS" = "$REAL_PASS" ]
			then
				echo_x -n "NX> 102 Password:"
				read -s NEW_PASS1
				
				if [ ${#NEW_PASS1} -lt 5 ]
				then
					echo_x "NX> 500 ERROR: incorrect password format, password must be long at least five characters"
					continue
				fi

				echo_x
				echo_x -n "NX> 102 Confirm password:"
				read -s NEW_PASS1
				echo_x
				if [ "$NEW_PASS1" = "$NEW_PASS2" ]
				then
					ENC_PASS=$(passdb_get_crypt_pass "$NEW_PASS1")
					passdb_chpass "$USER" "$ENC_PASS"
					echo_x "NX> 114 Password of user '$USER' changed"
				else
					echo_x "NX> 537 ERROR: passwords do not match"
				fi
			else
				echo_x "NX> 500 ERROR: current password doesn't match"
			fi
		;;
		addmount*)
			server_get_params $CMD
			PARAMS=$SERVER_PARAMS
			# TODO: This redirection is crap here.
			server_nxnode_start --smbmount "$USER" "$PARAMS" >/dev/null 2>&1 | log_error >/dev/null
			echo_x "NX> 719 SMB filesystem: running"
		;;
		addprinter*)
			server_get_params $CMD
			PARAMS=$SERVER_PARAMS
			# TODO: This redirecion is crap here.
			server_nxnode_start --addprinter "$USER" "$PARAMS" >/dev/null 2>&1 | log_error >/dev/null
			echo_x "NX> 719 CUPS printer: running"
		;;
		*)
			# disabled for 1.4.0-5 snapshot client
			#echo_x "NX> 503 Error: undefined command: '$CMD'"
		;;
	esac
done

fi

#
# End of Main nxserver <--> nxclient communication module
#

################### PACKAGE cmd.bm ############################

#
# library functions for nxserver-commandline cmds
#

# Policy: All functions and variables need to start with CMD_ / cmd_

# Needed global vars: $NX_VERSION, $NX_LICENSE, $NX_ETC_DIR, $PATH_BIN, $NX_HOME_DIR, $SSH_AUTHORIZED_KEYS

# Needed package: passdb

cmd_usage()
{
	echo "NXSERVER - Version $NX_VERSION $NX_LICENSE" 1>&2
	echo "Usage: nxserver <option>" 1>&2

	if [ "$1" = "root" ]
	then
		echo "--adduser <user>: Add a new user" 1>&2
		echo "--passwd <user>: Change password of <user>" 1>&2
		echo "--deluser <user>: Remove a user from nx" 1>&2
		echo "--listuser: List enabled users" 1>&2
		echo "" 1>&2
		echo "--start: Start the nx server" 1>&2
		echo "--stop: Stop the nx server" 1>&2
		echo "--status: Show status of nx server" 1>&2
		echo "--restart: Restart the nx server. (start,stop)" 1>&2
		echo "" 1>&2
		echo "--list [ user | sessionid ]: List running sessions of user or sessionid " 1>&2
		echo "--history [ user | sessionid | clear ]: Show history [ of user | sessionid ] or clear the history" 1>&2
		echo "--terminate <user | :display | sessionid>: Terminate the session pointed to by" 1>&2
		echo "       sessionid or display, or all sessions of the specified user." 1>&2
		echo "       Use * for all sessions." 1>&2
		echo "--force-terminate: Like terminate, but removes also session info." 1>&2
		echo "--suspend <user | :display | sessionid>: Suspend the session pointed to by" 1>&2
		echo "       sessionid or display, or all sessions of the specified user." 1>&2
		echo "       Use * for all sessions." 1>&2
		echo "--cleanup: Terminates all running sessions. Useful after power-outage."
		echo "" 1>&2
		echo "--broadcast <message>: Send a message to all users" 1>&2
		echo "--send <user | :display | sessionid> <message>: Send a message to the specified user or sessionid" 1>&2
	else
		echo "--passwd: Change password" 1>&2
	fi
	exit 1
}


cmd_abort()
{
	echo "NX> 500" "$@" 1>&2
	echo "NX> 999 Bye" 1>&2
	exit 1
}

cmd_user_passwd()
{
	echo "NX> 100 NXSERVER - Version $NX_VERSION $NX_LICENSE"
	echo "Sorry: Password changing for user is _not_ implemented, yet."
	echo "Please login to NX-Server to change password"
	echo "or ask your local system administrator."
	#echo "NX> 113 Changing password of user '$USER'"
	#echo "Old password:"
	#read -s OLDPASS
	#echo "New password:"
	#read -s NEWPASS1
	#echo "Repeat:"
	#read -s NEWPASS2

}

cmd_passwd()
{
	CMD_CHUSER=$2
	egrep -q "^$CMD_CHUSER:" $NX_ETC_DIR/passwords || cmd_abort "Error: User $CMD_CHUSER not found in database."
	echo -n "New password: "
	read -s CMD_NEWPASS
	echo
	CMD_ENC_PASS=$(passdb_get_crypt_pass "$CMD_NEWPASS")
	passdb_chpass "$CMD_CHUSER" "$CMD_ENC_PASS"
	echo "Password changed."
}

cmd_adduser()
{
	CMD_CHUSER=$2
	
	[ ${#CMD_CHUSER} -ge 32 ] && cmd_abort "Error: User $CMD_CHUSER must be shorter than 32 characters."
	egrep -q "^$CMD_CHUSER:" $NX_ETC_DIR/passwords && cmd_abort "Error: User $CMD_CHUSER already in database."
	getent passwd "$CMD_CHUSER" >/dev/null || cmd_abort "Error: User $CMD_CHUSER not existing on local system. Can't add."
	passdb_add_user "$CMD_CHUSER"
}

cmd_deluser()
{
	CMD_CHUSER=$2
	egrep -q "^$CMD_CHUSER:" $NX_ETC_DIR/passwords || cmd_abort "Error: User $CMD_CHUSER not found in database."
	passdb_remove_user "$CMD_CHUSER"
}

cmd_listuser()
{
	echo "NX> 146 NX users list"
	echo
	echo "Username"
	echo "---------------"
	echo
	passdb_list_user
	echo
}

cmd_start()
{
	
	[ -f $NX_HOME_DIR/.ssh/$SSH_AUTHORIZED_KEYS ] && cmd_abort "ERROR: Service already running"
	mv $NX_HOME_DIR/.ssh/$SSH_AUTHORIZED_KEYS.disabled $NX_HOME_DIR/.ssh/$SSH_AUTHORIZED_KEYS
	echo "NX> 122 Service started"
}

cmd_stop()
{
	[ -f $NX_HOME_DIR/.ssh/$SSH_AUTHORIZED_KEYS ] || cmd_abort "Service was already stopped"
	mv $NX_HOME_DIR/.ssh/$SSH_AUTHORIZED_KEYS $NX_HOME_DIR/.ssh/$SSH_AUTHORIZED_KEYS.disabled
	echo "NX> 123 Service stopped"
}

cmd_status()
{
	[ -f $NX_HOME_DIR/.ssh/$SSH_AUTHORIZED_KEYS ] && echo "NX> 110 NX Server is running"
	[ -f $NX_HOME_DIR/.ssh/$SSH_AUTHORIZED_KEYS ] || echo "NX> 110 NX Server is stopped"
}

cmd_restart()
{
	cmd_stop
	cmd_start
}

cmd_parse_2_params()
{
	if [ ${#1} -eq 32 ]
	then
		CMD_APARAMS="sessionid=sessionId{$1}"
	else
	if [ "$1" != "" ]
	then
		#egrep -q "^$1:" $NX_ETC_DIR/passwords || cmd_abort "Error: User $1 not found in database."
		CMD_APARAMS="user=$1"
	fi
	fi
	echo $CMD_APARAMS

}

cmd_parse_3_params()
{
	if [ ${#1} -eq 32 ]
	then
		CMD_APARAMS=$(session_find_id $1)
		[ -n "$CMD_APARAMS" ] || cmd_abort "Error: Session $1 could not be found."
	elif [ "${1:0:1}" = ":" ]
	then
		CMD_APARAMS=$(session_find_display "${1:1}")
		[ -n "$CMD_APARAMS" ] || cmd_abort "Error: No running sessions found for display $1."
	elif [ "$1" = "*" ]
	then
		CMD_APARAMS=$(session_find_all)
		[ -n "$CMD_APARAMS" ] || cmd_abort "Error: No running sessions found."
	elif [ "$1" != "" ]
	then
		#egrep -q "^$1:" $NX_ETC_DIR/passwords || cmd_abort "Error: User $1 not found in database."
		CMD_APARAMS=$(session_find_user "$1")
		[ -n "$CMD_APARAMS" ] || cmd_abort "Error: No running sessions found for user $1."
	else
		cmd_abort "Error: Not enough parameters."
	fi
	echo $CMD_APARAMS
}

cmd_list_suspended()
{
	CMD_PARAMS=$(cmd_parse_2_params "$2")
	[ -n "$2" -a -z "$CMD_PARAMS" ] && exit 1
	case $CMD_PARAMS in
		user=*)
			session_list_user_suspended $2 "Suspended"
		;;
	esac
}
cmd_list()
{
	CMD_PARAMS=$(cmd_parse_2_params "$2")
	[ -n "$2" -a -z "$CMD_PARAMS" ] && exit 1
	case $CMD_PARAMS in
		user=*)
			session_list_user $2
		;;
		sessionid=*)
			session_list $2
		;;
		*)
			session_list_all
		;;
	esac
}

cmd_history_clear()
{
	rm -f $NX_SESS_DIR/closed/*
	rm -f $NX_SESS_DIR/failed/*
}

cmd_history()
{
	if [ "$2" = "clear" ]
	then
		cmd_history_clear
	fi
	
	CMD_PARAMS=$(cmd_parse_2_params "$2")
	user=""
	sessid=""
	case $CMD_PARAMS in
		user=*)
			user="$2"
		;;
		sessionid=*)
			sessid="$2"
		;;
	esac

	session_history "$user" "$sessid"
}

cmd_terminate()
{
	CMD_PARAMS=$(cmd_parse_3_params "$2")
	[ -z "$CMD_PARAMS" ] && exit 1
	for i in $CMD_PARAMS;
	do
			CMDLINE=$(session_get_cmdline $i)
			cmd_sessionid=$(getparam sessionId)
			cmd_user=$(getparam userName)
			cmd_type=$(getparam type)
			cmd_status=$(getparam status)

			# is it a "good" session?
			case "$1" in 
			--suspend)
				if [ "$cmd_status" = "Running" ] && stringinstring "unix-" "$cmd_type"
				then
					echo "sessionid=$cmd_sessionid" | su - "$cmd_user" -c "$PATH_BIN/nxnode --suspend"
				fi
			;;
			--terminate)
				echo "sessionid=$cmd_sessionid" | su - "$cmd_user" -c "$PATH_BIN/nxnode --terminate"
			;;
			--force-terminate)
				echo "sessionid=$cmd_sessionid" | su - "$cmd_user" -c "$PATH_BIN/nxnode --terminate"
				session_close $cmd_sessionid
			;;
			esac
	done

}

cmd_send()
{
	if [ "$1" = "--broadcast" ]
	then
	  CMD_PARAMS=$(session_find_all)
	  [ -z "$CMD_PARAMS" ] && cmd_abort "Error: No running session could be found."
	else
	  CMD_PARAMS=$(cmd_parse_3_params "$2")
	  [ -z "$CMD_PARAMS" ] && exit 1
	  shift
	fi
	shift
	for i in $CMD_PARAMS;
	do
			CMDLINE=$(session_get_cmdline $i)
			cmd_display=$(getparam display)
			cmd_user=$(getparam userName)
			cmd_type=$(getparam type)
			cmd_status=$(getparam status)

			# is it a "good" session?
			if [ "$cmd_status" = "Running" ] && stringinstring "unix-" "$cmd_type"
			then
				su - "$cmd_user" -c "$PATH_BIN/nxdialog --dialog ok --caption \"NX Administrator Message\" --message \"$@\" -display \":$cmd_display\"" &
				disown $!
			fi
	done
	#nxnode_start --send "$CMD_PARAMS"
}

#
# user mode available functions
#

if [ $UID -ne 0 ]
then
	[ "$1" = "--agent" ] && exec $PATH_BIN/nxnode "$@"
	[ "$1" != "--passwd" ] && cmd_usage
	cmd_user_passwd
	exit 0
fi

#
# root mode available functions
#

[ $# -lt 1 ] && cmd_usage "root"
[ "$1" = "--help" ] && cmd_usage "root"

if [ "$1" = "--version" ]
then
  echo "NXSERVER - Version $NX_VERSION $NX_LICENSE"
  exit 0
fi

CMD=$1

echo "NX> 100 NXSERVER - Version $NX_VERSION $NX_LICENSE"

case $CMD in
	# 
	# User functions ...
	# 
	--passwd)
		cmd_passwd "$@"
	;;
	--adduser|--useradd)
		cmd_adduser "$@"
	;;
	--deluser|--userdel)
		cmd_deluser "$@"
	;;
	--listuser|--userlist)
		cmd_listuser
	;;
	--start)
		cmd_start
	;;
	--stop)
		cmd_stop
	;;
	--status)
		cmd_status
	;;
	--restart)
		cmd_restart
	;;
	--list)
		cmd_list "$@"
	;;
	--list-suspended)
		cmd_list_suspended "$@"
	;;
	--history)
		cmd_history "$@"
	;;
	--terminate|--suspend|--force-terminate)
		cmd_terminate "$@"
	;;
	--cleanup)
		cmd_terminate "--force-terminate" "*"
	;;
	--send|--broadcast)
		cmd_send "$@"
	;;
	*)
		cmd_abort "Error: Function $CMD not implemented yet."
esac
echo "NX> 999 Bye"
