#!/bin/bash

# Free implementation of nxserver components
#
# nxnode does accept (for now):
#
#	--startsession
#	--terminate
#	--smbmount
#		(smbmount is not implemented yet)
#
# Copyright (c) 2004 by Fabian Franz.
#
# License: GNU GPL, version 2
#
# SVN: $Id: nxnode 613 2008-09-01 20:42:31Z fabianx $
#
# 21.06.2004: - Full reconnection support

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

#
# -----------------------------------------------------------------------------
# Startup of nxnode
# -----------------------------------------------------------------------------
#

DELIM="NX>"

[ -n "$2" ] && DELIM="NX-$2>"

echo "$DELIM 1000 NXNODE - Version $NX_VERSION $NX_LICENSE"

if [ "$1" != "--check" -a "$1" != "--setkey" -a "$1" != "--agent" -a "$1" != "--slave" ]
then
	read CMDLINE

	CMDLINE="a=b&$CMDLINE"
fi

#
# -----------------------------------------------------------------------------
# Various helper functions
# -----------------------------------------------------------------------------
#

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

getparam()
{
	stringinstring "&$1=" "$CMDLINE" || return 1
	echo "$CMDLINE" |  tr "&" "\n" | egrep "^"$1"=" | awk -F= '{ VAL=$2 } END { print VAL }' | sed 's|%24|$|'
	return 0
}

find_app()
{
	set -- $*
	which $1 2>/dev/null
}

getparam_sessionid()
{
	sessionid=$(getparam sessionid)
	
	[ -n "$sessionid" ] || sessionid=$(getparam session_id)
	if [ -z "$sessionid" ]
	then
		echo "NX> 500 Error: Fatal - Missing parameter session id." 1>&2
		exit 1
	fi
	
	echo $sessionid
}

nxlog() {
if [ "$NX_LOG_LEVEL" -ge 6 ] ; then
	[ -n "$password" ] || password=$(getparam password)
	[ -n "$sessionid" ] || sessionid=$(getparam sessionid)
	[ -n "$sessionid" ] || sessionid=$(getparam session_id)
	[ -n "$sessionid" ] || sessionid=$sess_id
	if [ "$USER" = "nx" ] ; then
		#guest
		echo "$(date "+%d.%m %X"): $@" | $COMMAND_PERL -pi -e 's/--cookie=".+?"/--cookie="******"/g;  s/agent_password=.+?&/agent_password=******&/g; s/password=.+?&/password=******&/g;' >> "/var/log/nx/nxnode.log"
	else
		mkdir -p "$USER_FAKE_HOME/.nx"
		if [ -n "$sessionid" ] ; then
			sessionid="$(echo "$sessionid" |  awk 'BEGIN {FS="-"} {i=NF; print $i}')"
			if [ -n "$password" ] ; then
			    echo "$(date "+%d.%m %X"): $@" | $COMMAND_PERL -pi -e 's/--cookie=".+?"/--cookie="******"/g;  s/agent_password=.+?&/agent_password=******&/g; s/'$password'/****/g' >> "$USER_FAKE_HOME/.nx/nxnode-$sessionid.log"
			else
			    echo "$(date "+%d.%m %X"): $@" | $COMMAND_PERL -pi -e 's/--cookie=".+?"/--cookie="******"/g;  s/agent_password=.+?&/agent_password=******&/g; s/password=.+?&/password=******&/g;' >> "$USER_FAKE_HOME/.nx/nxnode-$sessionid.log"
			fi
		else
			echo "$(date "+%d.%m %X"): $@" | $COMMAND_PERL -pi -e 's/--cookie=".+?"/--cookie="******"/g;  s/agent_password=.+?&/agent_password=******&/g; s/password=.+?&/password=******&/g;' >> "$USER_FAKE_HOME/.nx/nxnode.log"
		fi
	fi
fi
return 0
}

if [ -n "$CMDLINE" ] ; then
	nxlog "$0 ($$): run nxnode with CMDLINE \"$CMDLINE\"" &
else
	nxlog "$0 ($$): run nxnode with \"$@\"" &
fi

#
# -----------------------------------------------------------------------------
# Node functions module
# -----------------------------------------------------------------------------
#

norm_param()
# arg: string
# dimbor: normalize string from hex (thnx 2Civil at unixforum.org) and convert
# charset if needed
{
    #nxlog "$FUNCNAME ($$): starting with args \"$@\"" &
    res="$(echo "$1" | $COMMAND_PERL -pe 's/%([A-Fa-f0-9]{2})/pack('C',hex($1))/seg;')"
    #nxlog "$FUNCNAME ($$): after normalize res == \"$res\"" &
    #nxlog "$FUNCNAME ($$): WIN_CP_CONVERT_CHAIN== \"$WIN_CP_CONVERT_CHAIN\"" &
    which iconv 1>/dev/null 2>&1; RC=$?
    [ -n "$WIN_CP_CONVERT_CHAIN" -a $RC -ne 0 ] && {
	nxlog "$FUNCNAME ($$): iconv not found, codepages converting is disabled." &
	echo $res; return
    }
    for cp_pair in $WIN_CP_CONVERT_CHAIN ; do
	cp_from="$(echo "$cp_pair" | cut -d\> -f1)"
        [ -n "$cp_from" ] || cp_from="UTF-8"
	cp_to="$(echo "$cp_pair" | cut -d\> -f2)"
	[ -n "$cp_to" ] || cp_to="UTF-8"
	res="$(echo "$res" | iconv -f $cp_from -t $cp_to)"
	#nxlog "$FUNCNAME ($$): converting $cp_from > $cp_to == \"$res\"" &
    done
    #nxlog "$FUNCNAME ($$): return res === \"$res\"" &
    echo $res
}


#
# dimbor: functions to control user's shares and printers in mode "per user"
# instead of "per session".
#

get_mport2sessdir()
# arg:	session dir
#	type - "smb"/"ipp"/""
# returns cups listening port or samba mount port
{
    type=$2
    smbport="$(cat $1/scripts/mport 2>/dev/null)" # check smb mount port
    cport="$(cat $1/session 2>/dev/null | grep "Listening to CUPS" | tail -n1 | cut -d\' -f2)"
    mport=""
    if [ "$type" != "smb" -a -n "$cport" ]; then mport=$cport; type="ipp";
    elif [ "$type" != "ipp" -a  -n "$smbport" ]; then mport=$smbport; type="smb";
    fi
    nxlog "$FUNCNAME ($$): sessdir \"$1\"; mport == $mport \"$type\"" &
    echo $mport
}

get_sessdir4res()
# args: port - session's smb/ipp mount port OR filename of printer/share (flag-file)
#	fl_current - if set then includes in search current session-dir
# if share is nx-controlled and nxagent from his session are leave,
# then returns name of session dir (except dir of own sesson)
# functuion used with $ENABLE_SHARE_MULTIMOUNT=1 or
# $ENABLE_CUPS_SERVER_MODE=1 modes only
{
    nxlog "$FUNCNAME ($$): starting with args \"$@\"; sess_id == \"$sess_id\"" &
    [ "$1" -gt 0 2>/dev/null ] && digarg=1 || digarg=0
    AGENT_PIDS="$(ps -wo pid=,cmd= -C "nxagent" | awk '{print($1)}')"
    for sessdir in $USER_FAKE_HOME/.nx/C-* ; do
	[ -z "$2" -a "$sessdir" == "$USER_FAKE_HOME/.nx/C-$sess_id" ] && continue
	NODE_AGENT_PID=$(cat "$sessdir/pids/agent" 2>/dev/null)
	[ -z "$NODE_AGENT_PID" ] && continue
	[ -z "$(echo "$AGENT_PIDS" | grep $NODE_AGENT_PID)" ] && continue
	#nxlog "$FUNCNAME ($$): browse sessdir \"$sessdir\" for filename = \"$sessdir/scripts/$1\", digarg=$digarg" &
	if [ "$digarg" = "1" ] ; then
	    curport=$(get_mport2sessdir "$sessdir")
	    [ "$curport" = "$1" ] && { nxlog "$FUNCNAME ($$): found sessdir $sessdir for mport = $1" &
		echo "$sessdir"; return; }
	else
	    [ -f "$sessdir/scripts/$1" ] && { nxlog "$FUNCNAME ($$): found sessdir $sessdir for filename = $1" &
		echo "$sessdir"; return; }
	fi
    done
    nxlog "$FUNCNAME ($$): target sessdir not found" &
}

check_remote_printer()
# args:	type - "smb" or "ipp"
#	short sharename/printername
#	port - nx controlled mount port
#	name - user name
#	pass - password (optional)
# if shared printer accessible, returns 1, else - 0
{
    nxlog "$FUNCNAME ($$): starting with args \"$@\"" &
    if [ "$1" = "ipp" ]; then # checking ipp printer
	# temporary checks CUPS port only
	[ -n "$(get_sessdir4res $3 "current")" ] && res=1 || res=0
	nxlog "$FUNCNAME ($$): end with res = $res" &
	echo $res; return 0;
    fi
    PATH_LIB="/usr/lib/freenx-server" # this is correct on all systems?
    NXREDIR_LIBRARY="$PATH_LIB/libnxredir.so.0"
    [ -n "$5" ] && upass="$4%$5" || upass="$4 -N"
    dport=445 # cifs
    CMDSTR="/usr/bin/rpcclient -U $upass -c 'openprinter $2' -p $dport -I 127.0.0.1 127.0.0.1 2>&1"
    nxlog "$FUNCNAME ($$): invoke command \"$CMDSTR\"" &
    res=""; numa="0"; ret="0"
    while [ "$ret" = "0" ] ; do
    # addprinter and addmount try to use one mport at the same time, we need to endure ;)
	(( numa++ ))
	res="$(NXSAMBA_PORT=$3 LD_PRELOAD=$NXREDIR_LIBRARY eval $CMDSTR)"
	nxlog "$FUNCNAME ($$): on attempt $numa says \"$res\"" &
	[ -n "$(echo "$res" | grep successfully)" ] && { ret="1"; continue; }
	[ "$numa" -gt 4 ] && ret="1" && res="timeout"
    done
    [ -n "$(echo $res | grep successfully)" ] && res=1 || res=0
    nxlog "$FUNCNAME ($$): end with res = $res" &
    echo $res
}

#
# node_terminate_agent <session id>
#

node_terminate_agent()
{
	nxlog "$FUNCNAME ($$): starting" &
	NODE_AGENT_PID=$(cat "$USER_FAKE_HOME/.nx/C-$1/pids/agent" 2>/dev/null)
	[ -n "$NODE_AGENT_PID" ] && { nxlog "$FUNCNAME ($$): killing NODE_AGENT_PID" &
	    kill $NODE_AGENT_PID 2>/dev/null; }
	if [ -z $(ps x| grep "nxagent" | awk '{print($1)}' | grep $NODE_AGENT_PID) ] ; then #'
		nxlog "$FUNCNAME ($$): process list for 'nxagent' empty" &
	else
		nxlog "$FUNCNAME ($$): killing NODE_AGENT_PID. Try #2" &
		kill $NODE_AGENT_PID 2>/dev/null
	fi
	nxlog "$FUNCNAME ($$): end" &
}

#
# node_terminate_session <session id>
#
#	Used local vars: $virtualdesktop, $rootless
#
#	Used config vars: $COMMAND_XAUTH, $SESSION_LOG_CLEAN
#

node_terminate_session()
{
	#
	# Cleanup session
	#

	nxlog "$FUNCNAME ($$): starting" &
	[ -d "$USER_FAKE_HOME/.nx/C-$1/" ] || { nxlog "$FUNCNAME ($$): Session directory not found." &
	    return; }

	nxlog "$FUNCNAME ($$): Start terminating session \"$1\" with status \"$2\"" &
	# Kill nxagent
	
	NODE_AGENT_PID=$(cat "$USER_FAKE_HOME/.nx/C-$1/pids/agent" 2>/dev/null)
	
	if [ -n "$NODE_AGENT_PID" ]
	then
		nxlog "$FUNCNAME ($$): kill nxagent" &
		kill $NODE_AGENT_PID 2>/dev/null
		if ! [ "$virtualdesktop" = "0" -a "$rootless" != "1" ]
		then
			nxlog "$FUNCNAME ($$): force kill nxagent" &
			sleep 1
			kill -0 $NODE_AGENT_PID 2>/dev/null && kill -9 $NODE_AGENT_PID 2>/dev/null
		fi
	fi

	# Kill tail process
	
	NODE_TAIL_PID=$(cat "$USER_FAKE_HOME/.nx/C-$sess_id/pids/tail" 2>/dev/null)
	[ -n "$NODE_TAIL_PID" ] && { nxlog "$FUNCNAME ($$): kill tail process" &
	    kill $NODE_TAIL_PID 2>/dev/null; }

	# JJK: Kill running services
	# FF: Seems this is needed also here ...
	nxlog "$FUNCNAME ($$): call node_stop_services" &
	node_stop_services

	# Remove display information
	
	nxlog "$FUNCNAME ($$): Remove display information" &
	NODE_DISPLAY=$(echo $1 | awk 'BEGIN {FS="-"} {i=NF-1; print $i}')
	rm -f /tmp/.X$NODE_DISPLAY-lock
	rm -f /tmp/.X11-unix/X$NODE_DISPLAY
	
	# Remove magic cookie information
	
	nxlog "$FUNCNAME ($$): Remove magic cookie information" &
	$COMMAND_XAUTH -v source "$USER_FAKE_HOME/.nx/C-$1/scripts/authority" >/dev/null 2>&1

	# Preserve or remove session information
	
	nxlog "$FUNCNAME ($$): Preserve or remove session information" &
	if [ "$SESSION_LOG_CLEAN" = "1" ] ; then
		nxlog "$FUNCNAME ($$): Clean session information." &
		rm -rf "$USER_FAKE_HOME/.nx/C-$1/"
		rm -f  "$USER_FAKE_HOME/.nx/nxnode-$1.log"
		rm -f  "$USER_FAKE_HOME/.nx/nxnode.log"
	fi
	
	if [ "$SESSION_LOG_CLEAN" = "0" -a "$2" = "failed" ] ; then
		nxlog "$FUNCNAME ($$): Session failed. Rename session directory from \"$USER_FAKE_HOME/.nx/C-$1/\" to \"$USER_FAKE_HOME/.nx/F-C-$1\"" &
		mv "$USER_FAKE_HOME/.nx/C-$1/" "$USER_FAKE_HOME/.nx/F-C-$1"
	elif [ "$SESSION_LOG_CLEAN" = "0" -a "$2" != "failed" ] ; then
		nxlog "$FUNCNAME ($$): Session terminated. Rename session directory from \"$USER_FAKE_HOME/.nx/C-$1/\" to \"$USER_FAKE_HOME/.nx/T-C-$1\"" &
		mv "$USER_FAKE_HOME/.nx/C-$1/" "$USER_FAKE_HOME/.nx/T-C-$1"
	fi
	nxlog "$FUNCNAME ($$): end" &
}

#
# node_fail_restore_session <session id>
#

# TODO: Kill still running tail -f process.

node_fail_restore_session()
{
	nxlog "$FUNCNAME ($$): starting" &
	echo "NX> 1004 Error: Could not resume session. nxagent process could not be found."
	
	NODE_TAIL_PID=$(cat "$USER_FAKE_HOME/.nx/C-$sess_id/pids/tail" 2>/dev/null)
	[ -n "$NODE_TAIL_PID" ] && kill $NODE_TAIL_PID
	[ -n "$NODE_TAIL_PID" ] && echo "NX 1004> kill $NODE_TAIL_PID"
	
	nxlog "$FUNCNAME ($$): call 'node_terminate_session \"$1\" \"failed\"'" &
	node_terminate_session "$1" "failed"
	nxlog "$FUNCNAME ($$): end. Next is 'exit 1'" &
	exit 1
}

#
# node_suspend_session <session id>
#

node_suspend_session()
{
	nxlog "$FUNCNAME ($$): starting" &
	NODE_AGENT_PID=$(cat "$USER_FAKE_HOME/.nx/C-$1/pids/agent" 2>/dev/null)

	if [ -n "$NODE_AGENT_PID" ]
	then
		nxlog "$FUNCNAME ($$): NODE_AGENT_PID=\"$NODE_AGENT_PID\". Killing..." &
		kill -0 $NODE_AGENT_PID || { nxlog "$FUNCNAME ($$): end" &
		    return 1; }
		kill -HUP $NODE_AGENT_PID && { nxlog "$FUNCNAME ($$): end" &
		    return 0; }
	else
		nxlog "$FUNCNAME ($$): NODE_AGENT_PID is empty" &
		nxlog "$FUNCNAME ($$): end" &
	fi

	return 1
}

#
# node_find_application <type>
#
#	Used config vars: $COMMAND_START_KDE, $COMMAND_START_GNOME,
#			  $COMMAND_START_CDE, $COMMAND_XTERM, $USER_X_STARTUP_SCRIPT,
#			  $DEFAULT_X_SESSION

node_find_application()
{
	nxlog "$FUNCNAME ($$): starting with args \"$@\"" &
	NODE_STARTX=""

	case $1 in
		shadow|windows|vnc)
			:
		;;
		unix-kde)
			NODE_STARTX=$COMMAND_START_KDE
		;;
		unix-gnome)
			NODE_STARTX=$COMMAND_START_GNOME
		;;
		unix-cde)
			NODE_STARTX=$COMMAND_START_CDE
		;;
		windows-helper)
			application="$PATH_BIN/nxdesktop_helper"
			NODE_STARTX=$application
		;;
		unix-application|vnc-helper)
			[ "$application" = "xterm" ] && application=$COMMAND_XTERM
			NODE_STARTX=$application
		;;
		unix-console)
			NODE_STARTX=$COMMAND_XTERM
		;;
		unix-default|*)
			if [ -x "$HOME/$USER_X_STARTUP_SCRIPT" ]; then
				NODE_STARTX="$HOME/$USER_X_STARTUP_SCRIPT"
			elif which "$DEFAULT_X_SESSION" >/dev/null 2>&1 ; then
				NODE_STARTX="$DEFAULT_X_SESSION"
			else
				NODE_STARTX=$COMMAND_XTERM
			fi
		;;
	esac

	# dimbor: another personalyzed way to ACLS control and replace X-application
	[ -d "$NX_ACL_DIR" -a -x "$PATH_BIN/nxacl.app" ] && {
	    nxlog "$FUNCNAME ($$): !!! call nxacl.app with source NODE_STARTX=\"$NODE_STARTX\"" &
	    NODE_STARTX="$($PATH_BIN/nxacl.app "$NODE_STARTX" "$CMDLINE")"
	}

	nxlog "$FUNCNAME ($$): return NODE_STARTX=\"$NODE_STARTX\"" &
	echo "$NODE_STARTX"
	nxlog "$FUNCNAME ($$): end" &
}

#
# node_start_applications
#
#	Used local vars: $type, $application, $sess_id, $mediahelper,
#		         $virtualdesktop, $rootless, $display
#
#	Used config vars: <several>
#

node_start_applications()
{
	nxlog "$FUNCNAME ($$): starting" &

	# close input and output file descriptors
	exec 0<&-
	exec 1>&-
	exec 2>&-

	#
	# Prepare application startup
	#
	
	export DISPLAY=:$display

	if [ "$ENABLE_SOURCE_PROFILE" = "1" ] ; then
	    nxlog "$FUNCNAME ($$): source profile" &
	    . /etc/profile
	fi

	if [ "$ENABLE_SOURCE_BASH_PROFILE" = "1" ] ; then
	    nxlog "$FUNCNAME ($$): source bash profile" &
	    [ -f ~/.bash_profile ] && . ~/.bash_profile
	    [ -f /etc/bashrc ] && . /etc/bashrc
	fi

	nxlog "$FUNCNAME ($$): DISPLAY is $display" &
	cntr=$AGENT_STARTUP_TIMEOUT
	while [ -z "$(xdpyinfo  2> /dev/null | head)" ]; do
		let "cntr -= 2"
		[ $cntr -lt 0 ] && return
		sleep 2
		echo "sleep while get a valid DISPLAY"
	done

	#numlockx
	if [ "$NUMLOCKX_STATUS" != "system" ] ; then
	    nxlog "$FUNCNAME ($$): Run \"$NUMLOCKX $NUMLOCKX_STATUS\"" &
	    "$NUMLOCKX" "$NUMLOCKX_STATUS"
	fi

	mkdir -p "$USER_FAKE_HOME/.nx/C-$sess_id/pids/apps/"

	#
	# Which application do we start?
	#

	NODE_APPLICATION=$(node_find_application "$type")
	
	# For rdesktop/VNC, there is no application to start
	if [ -n "$NODE_APPLICATION" ] ; then
	    nxlog "$FUNCNAME ($$): Got NODE_APPLICATION is \"$NODE_APPLICATION\"" &
	else
		nxlog "$FUNCNAME ($$): NODE_APPLICATION is empty" &
		return
	fi

	#
	# Check if we want to use a mediahelper
	#

	if [ "$mediahelper" = "esd" ]
	then
		# Set Espeaker variable
		let ESPEAKER=$display+7000
		export ESPEAKER="127.0.0.1:$ESPEAKER"
		
		# Do not spawn new ESD daemons
		export ESD_NO_SPAWN="yes"
			
		# Check for config file directive
		if [ "$ENABLE_ESD_PRELOAD" = "1" -a -x "$(find_app $ESD_BIN_PRELOAD)" ]
		then
			nxlog "$FUNCNAME ($$): Preload \"$ESD_BIN_PRELOAD\"" &
			NODE_APPLICATION="$ESD_BIN_PRELOAD $NODE_APPLICATION"
			nxlog "$FUNCNAME ($$): NODE_APPLICATION is \"$NODE_APPLICATION\"" &
			echo "Info: NXNODE - Using $ESD_BIN_PRELOAD wrapper script." >> "$USER_FAKE_HOME/.nx/C-$sess_id/session"
		fi
	elif [ "$mediahelper" = "artsd" ]
	then
		# Overwrite users mcoprc
		echo -n "GlobalComm=Arts::X11GlobalComm" > $HOME/.mcoprc
		if [ "$ENABLE_ARTSD_PRELOAD" = "1" -a -x "$(find_app $ARTSD_BIN_PRELOAD)" ]
		then
			NODE_APPLICATION="$ARTSD_BIN_PRELOAD $NODE_APPLICATION"
			echo "Info: NXNODE - Using $ARTSD_BIN_PRELOAD wrapper script." >> "$USER_FAKE_HOME/.nx/C-$sess_id/session"
		fi
	fi

	[ "$ENABLE_CUPS_SERVER_MODE" = "1" ] && {
	    CUPS_SERVER_SOCKET="$(LC_ALL=C /usr/bin/lpstat -H 2>/dev/null)"
	    [ -r $CUPS_SERVER_SOCKET ] || CUPS_SERVER_SOCKET=""
	} || CUPS_SERVER_SOCKET="$USER_FAKE_HOME/.nx/C-$sess_id/cups/cups.sock"
	[ "$ENABLE_CUPS_SERVER_EXPORT" = "1" -a -n "$CUPS_SERVER_SOCKET" ] && {
	    [ "$cups" = "1" -o "$samba" = "1" ] && {
		nxlog "$FUNCNAME ($$): export CUPS_SERVER=$CUPS_SERVER_SOCKET" &
		export CUPS_SERVER="$CUPS_SERVER_SOCKET"
	    }
	}
	if [ "$ENABLE_SAMBA_PRELOAD" = "1" -a -x "$PATH_BIN/nxredir" ]
	then
		let NXSAMBA_PORT=$display+3000
		export NXSAMBA_PORT
		nxlog "$FUNCNAME ($$): Preload SAMBA using nxredir. NXSAMBA_PORT is \"$NXSAMBA_PORT\"" &
		NODE_APPLICATION="$PATH_BIN/nxredir $NODE_APPLICATION"
		echo "Info: NXNODE - Using nxredir wrapper script to forward SMB ports 139 and 445 to port $NXSAMBA_PORT." >> "$USER_FAKE_HOME/.nx/C-$sess_id/session"
	fi

	#
	# Do we need to PRELOAD any libraries?
	#	

	[ "$virtualdesktop" = "0" -a "$rootless" != "1" ] && export LD_PRELOAD="$APPLICATION_LIBRARY_PRELOAD:$LD_PRELOAD"

	#
	# Should we start a window manager?
	#
	
	if [ "$virtualdesktop" = "1" -a "$type" = "unix-application" -a "$DEFAULT_X_WM" != "" -a -x "$(find_app $DEFAULT_X_WM)" ]
	then
		nxlog "$FUNCNAME ($$): start a window manager - \"DISPLAY=:$display $DEFAULT_X_WM\"" &
		DISPLAY=:$display $DEFAULT_X_WM >>"$USER_FAKE_HOME/.nx/C-$sess_id/session" 2>&1 &
		NODE_WM_PID=$!
		nxlog "$FUNCNAME ($$): NODE_WM_PID=\"$NODE_WM_PID\"" &
	fi

	#
	# Use Xsession to execute the Desktop session
	#

	case $type in
		unix-gnome)
			export STARTUP="$NODE_APPLICATION"
			if [ "$login_method" = "GUEST" ]
			then
				NODE_APPLICATION=$COMMAND_GUEST_X_SESSION
			elif [ "$BOOTSTRAP_X_SESSION" = "1" ]
			then
				NODE_APPLICATION=$COMMAND_GDM_X_SESSION
			fi
		;;
		unix-kde|unix-cde)
			export STARTUP="$NODE_APPLICATION"
			if [ "$login_method" = "GUEST" ]
			then
				NODE_APPLICATION=$COMMAND_GUEST_X_SESSION
			elif [ "$BOOTSTRAP_X_SESSION" = "1" ]
			then
				NODE_APPLICATION=$DEFAULT_X_SESSION
			fi
		;;
	esac

	#
	# Startup the application
	#
	nxlog "$FUNCNAME ($$): Starting NODE_APPLICATION with /etc/nxserver/Xsession" &
	DISPLAY=:$display /etc/nxserver/Xsession $NODE_APPLICATION >>"$USER_FAKE_HOME/.nx/C-$sess_id/session" 2>&1 &
	NODE_APP_PID=$!
	nxlog "$FUNCNAME ($$): Start successful. NODE_APP_PID=$NODE_APP_PID" &
	
	mkdir -p "$USER_FAKE_HOME/.nx/C-$sess_id/pids/"
	echo "$NODE_APP_PID" >"$USER_FAKE_HOME/.nx/C-$sess_id/pids/apps/$NODE_APP_PID"
	nxlog "$FUNCNAME ($$): Waiting for NODE_APP_PID" &
	wait $NODE_APP_PID
	nxlog "$FUNCNAME ($$): NODE_APP_PID finished" &

	#
	# Kill or wait for the started window manager
	#
	
	[ -n "$NODE_WM_PID" ] && {
		nxlog "$FUNCNAME ($$): NODE_WM_PID is not empty" &
		# kill the WM after application is finished?
		[ "$KILL_DEFAULT_X_WM" = "1" ] && { nxlog "$FUNCNAME ($$): killing $NODE_WM_PID" &
		    kill $NODE_WM_PID 2>/dev/null; }
		# or just wait until it finishes?
		[ "$KILL_DEFAULT_X_WM" = "1" ] || { nxlog "$FUNCNAME ($$): wait for $NODE_WM_PID is dead" &
		    wait $NODE_WM_PID; }
	}
	nxlog "$FUNCNAME ($$): Clean pids/apps/$NODE_APP_PID" &
	rm -f "$USER_FAKE_HOME/.nx/C-$sess_id/pids/apps/$NODE_APP_PID"

	sleep "$NODE_APP_WAIT_TIMEOUT"s

	# Do not terminate agent in case of rootless agent mode.
	# The agent times out after a while by itself anyway.
	
	if [ "$virtualdesktop" = "1" -o "$rootless" != "1" ] ; then
		nxlog "$FUNCNAME ($$): Call node_terminate_agent for non-rootless or virtualdesktop session type" &
		nxlog "$FUNCNAME ($$): Call 'node_terminate_agent \"$sess_id\"'" &
		node_terminate_agent "$sess_id"
	fi
	# Kill wineserver
	nxlog "Kill wineserver" &
	killall wineserver 2>/dev/null
	nxlog "$FUNCNAME ($$): end" &
}

#
# node_persistent_session
#
#	Is the user allowed to run a persistent session?
#

node_agent_persistent_session()
{
	P="-nopersistent"
	# Guest sessions are always nonpersistent
	if [ "$login_method" = "GUEST" ]
	then
		echo "$P"
		return
	fi
	OLD_IFS=$IFS
	IFS=","
	[ "$ENABLE_PERSISTENT_SESSION" = "all" ] && P="-persistent"
	[ "$ENABLE_PERSISTENT_SESSION" = "all" ] || for USERNAME in $ENABLE_PERSISTENT_SESSION; do
		[ "${USERNAME:0:1}" != "@" ] && [ "$USER" = "$USERNAME" ] && P="-persistent" && break ;
		[ "${USERNAME:0:1}" = "@" ] && [ -z $(groups "$USER" | egrep "^${USERNAME:1}:") ] && P="-persistent" && break ;
	done
	for USERNAME in $DISABLE_PERSISTENT_SESSION; do
		[ "${USERNAME:0:1}" != "@" ] && [ "$USER" = "$USERNAME" ] && P="-nopersistent" && break ;
		[ "${USERNAME:0:1}" = "@" ] && [ -z $(groups "$USER" | egrep "^${USERNAME:1}:") ] && P="-nopersistent" && break ;
	done
	IFS=$OLD_IFS
	echo "$P"
}

#
# node_start_agent
#

node_start_agent()
{
	# Ok, now we do some wicked fd magic.
	#
	# first part:
	#
	# nxagent's fd #2 -> fd #3
	
	# second part:
	#
	# fd #1 -> #4
	# fd #3 -> #1
	# tee | node_start_monitor

	# third part
	# fd #4 -> #1

	# => all output of nxagent goes to tee | node_start_monitor, while
	#    leaving all other output flow through like normally.
	
	# preparations
	nxlog "$FUNCNAME ($$): starting" &
	exec 3>&2
	exec 4>&1

	{
	
	{

	#
	# Setup environment
	#

	if [ "$ENABLE_SOURCE_PROFILE" = "1" ] ; then
	    nxlog "$FUNCNAME ($$): source profile" &
	    . /etc/profile
	fi

	if [ "$ENABLE_SOURCE_BASH_PROFILE" = "1" ] ; then
	    nxlog "$FUNCNAME ($$): source bash profile" &
	    [ -f ~/.bash_profile ] && . ~/.bash_profile
	    [ -f /etc/bashrc ] && . /etc/bashrc
	fi

	export DISPLAY="nx/nx,options=$USER_FAKE_HOME/.nx/C-$sess_id/options:$display"
	export XAUTHORITY="$USER_FAKE_HOME/.nx/C-$sess_id/authority"
	export HOME="$USER_FAKE_HOME"
	export NX_CLIENT="$PATH_BIN/nxdialog"

	#
	# Setup optional parameters for nxagent
	#

	# keyboard
	
	K=""
	# backwards compatibility
	[ -n "$keyboard" ] && K="-keyboard $keyboard"
	[ -n "$kbtype" ] && K="-kbtype $kbtype"

	# backingstore
	
	B=""
	# was only for 1.5.0 backend

	# geometry
	
	G=""
	[ -n "$geometry" ] && G="-geometry $geometry"

	# type of session
	
	R="-D"
	[ "$rootless" = "1" ] && R="-R"

	# Setup fullscreen parameters
	
	vncfullscreen=""
	[ "$geometry" = "fullscreen" -a "$type" = "vnc" ] && vncfullscreen="-fullscreen" && G=""
	
	[ "$geometry" = "fullscreen" -a "$type" = "windows" ] && G="-geometry `echo $screeninfo | cut -d"x" -f1,2`"

	#
	# Start the wanted nxagent
	#

	if [ "$type" = "windows" ]
	then
		nxlog "$FUNCNAME ($$): Type \"windows\"" &
		# nxdesktop session (Windows RDP)

		[ "$SET_LD_LIBRARY_PATH" = "1" ] && export LD_LIBRARY_PATH="$AGENT_LIBRARY_PATH:$LD_LIBRARY_PATH"
		# Setup optional parameters
		
		U=""
		P=""
		D=""
		[ -n "$agent_user" ] && U="-u $agent_user"
		[ -n "$agent_password" ] && P="-p -"
		[ -n "$agent_domain" ] && D="-d $agent_domain"

		# Start the agent
		
		echo "$agent_password" | $PATH_BIN/nxdesktop -name "NX - $user@$SERVER_NAME:$display - $session (GPL Edition)" -option "$USER_FAKE_HOME/.nx/C-$sess_id/options" $K $G $U $P $D $agent_server $AGENT_EXTRA_OPTIONS_RDP 2>&3 &

	elif [ "$type" = "vnc" ]
	then
		nxlog "$FUNCNAME ($$): Type \"vnc\"" &
		# nxviewer session (VNC RFB)
		
		[ "$SET_LD_LIBRARY_PATH" = "1" ] && export LD_LIBRARY_PATH="$AGENT_LIBRARY_PATH:$LD_LIBRARY_PATH"
		# Setup password
		
		mkdir -p "$USER_FAKE_HOME/.nx/C-$sess_id/scripts/"
		echo "$agent_password" | $PATH_BIN/nxpasswd "$USER_FAKE_HOME/.nx/C-$sess_id/scripts/.passwd" doit

		# Start x11vnc
		if [ -n "$shadowdisplay" ]
		then
			(
				viewonly=""
				[ "$ENABLE_INTERACTIVE_SESSION_SHADOWING" != "1" ] && viewonly="-viewonly"
				
				DISPLAY="$shadowhost:$shadowdisplay" x11vnc -localhost $viewonly -timeout 120 -rfbauth "$USER_FAKE_HOME/.nx/C-$sess_id/scripts/.passwd" >"$USER_FAKE_HOME/.nx/C-$sess_id/scripts/.vnc_port" 2>&3 &
			)
			sleep 2
			agent_port=$(cat "$USER_FAKE_HOME/.nx/C-$sess_id/scripts/.vnc_port" | egrep "^PORT=" | cut -d'=' -f 2)
			[ -z "agent_port" ] && agent_port="0"
			# note the :: is not a mistake, but rather a hint for nxviewer to use this as a port and not
			# interpret it as a display.
			agent_server="127.0.0.1::$agent_port"
			rm -f "$USER_FAKE_HOME/.nx/C-$sess_id/scripts/.vnc_port"
		fi
		
		# Start the agent
		
		$PATH_BIN/nxviewer -encodings tight hextile copyrect raw -passwd "$USER_FAKE_HOME/.nx/C-$sess_id/scripts/.passwd" -name "NX - $user@$SERVER_NAME:$display - $session (GPL Edition)" -option "$USER_FAKE_HOME/.nx/C-$sess_id/options" $vncfullscreen $G $K $agent_server $AGENT_EXTRA_OPTIONS_RFB 2>&3 &

	elif [ "$R" = "-R" -a "$rootless" != "1" ]
	then
		# nxproxy single application mode session
		nxlog "$FUNCNAME ($$): Start nxproxy for single application session mode" &
		[ "$SET_LD_LIBRARY_PATH" = "1" ] && export LD_LIBRARY_PATH="$PROXY_LIBRARY_PATH:$LD_LIBRARY_PATH"
		nxlog "$FUNCNAME ($$): Start nxproxy by command: '$PATH_BIN/nxproxy -C :$display $PROXY_EXTRA_OPTIONS'" &
		$PATH_BIN/nxproxy -C :$display $PROXY_EXTRA_OPTIONS 2>&3 &
	else
		nxlog "$FUNCNAME ($$): NXAgent session type" &
		# nxagent session (X11)
		[ "$SET_LD_LIBRARY_PATH" = "1" ] && export LD_LIBRARY_PATH="$AGENT_LIBRARY_PATH:$LD_LIBRARY_PATH"
		
		# Setup optional parameters

		P=$(node_agent_persistent_session)
		FP=""
		[ -n "$AGENT_FONT_SERVER" ] && FP="-fp $AGENT_FONT_SERVER"
		
		if [ "$type" = "shadow" ]
		then
			nxlog "$FUNCNAME ($$): Type \"shadow\". Add some args to nxagent" &
			R="-S -shadow $shadowhost:$shadowdisplay -shadowmode $ENABLE_INTERACTIVE_SESSION_SHADOWING"
			P="-nopersistent"
		fi
		
		# Start the agent
		
		nxlog "$FUNCNAME ($$): env start `env`" &
		nxlog "$FUNCNAME ($$): env end" &
		
		nxlog "$FUNCNAME ($$): Start nxagent by command: '$COMMAND_NXAGENT $P $R -name \"NX - $user@$SERVER_NAME:$display - $session (GPL Edition)\" -option \"$USER_FAKE_HOME/.nx/C-$sess_id/options\" $B $FP $AGENT_EXTRA_OPTIONS_X :$display'" &
		#PATH="$PATH_BIN:$PATH" $COMMAND_NXAGENT $P $R -name "NX - $user@$SERVER_NAME:$display - $session (GPL Edition)" -option "$USER_FAKE_HOME/.nx/C-$sess_id/options" $K $G $B $FP $AGENT_EXTRA_OPTIONS_X :$display 2>&3 &
		PATH="$PATH_BIN:$PATH" $COMMAND_NXAGENT $P $R -name "NX - $user@$SERVER_NAME:$display - $session (GPL Edition)" -option "$USER_FAKE_HOME/.nx/C-$sess_id/options" $B $FP $AGENT_EXTRA_OPTIONS_X :$display 2>&3 &
	fi
	
	#
	# Wait for the agent
	#
	
	NODE_AGENT_PID=$!
	mkdir -p "$USER_FAKE_HOME/.nx/C-$sess_id/pids/"
	echo "$NODE_AGENT_PID" >"$USER_FAKE_HOME/.nx/C-$sess_id/pids/agent"
	nxlog "$FUNCNAME ($$): Wait for NODE_AGENT_PID ($NODE_AGENT_PID)" &
	wait $NODE_AGENT_PID

	NODE_AGENT_EXIT_STATUS=$?
	nxlog "$FUNCNAME ($$): NODE_AGENT_EXIT_STATUS = \"$NODE_AGENT_EXIT_STATUS\"" &
	NODE_FAILED=""
	if [ $NODE_AGENT_EXIT_STATUS -ne 0 ]
	then
		echo "NX> 1004 Error: NX Agent exited with exit status 1. To troubleshoot set SESSION_LOG_CLEAN=0 in node.conf and investigate \"$USER_FAKE_HOME/.nx/F-C-$sess_id/session\". You might also want to try: ssh -X myserver; $PATH_BIN/nxnode --agent to test the basic functionality. Session log follows:"
		cat "$USER_FAKE_HOME/.nx/C-$sess_id/session" 1>&2
		NODE_FAILED="failed"
		nxlog "$FUNCNAME ($$): NODE_FAILED = \"$NODE_FAILED\"" &
	fi
	nxlog "$FUNCNAME ($$): close session" &
	echo "NX> 1006 Session status: closed"
	
	#
	# Cleanup session information
	#	
	
	nxlog "$FUNCNAME ($$): cleanup session information '$sess_id'" &
	nxlog "$FUNCNAME ($$): remove agent pidfile -- '$USER_FAKE_HOME/.nx/C-$sess_id/pids/agent'" &
	rm -f "$USER_FAKE_HOME/.nx/C-$sess_id/pids/agent"
	nxlog "$FUNCNAME ($$): call 'node_terminate_session \"$sess_id\" \"$NODE_FAILED\"'" &
	node_terminate_session "$sess_id" "$NODE_FAILED"
	
	# remove possible leftovers of nxagent
	nxlog "$FUNCNAME ($$):remove /tmp/.X$display-lock" &
	rm -f /tmp/.X$display-lock
	nxlog "$FUNCNAME ($$): remove /tmp/.X11-unix/X$display" &
	rm -f /tmp/.X11-unix/X$display
	} 3>&1 1>&4 | tee "$USER_FAKE_HOME/.nx/C-$sess_id/session" | node_start_monitor; } 4>&1
}

try_remount_printers()
{
# args: none
# if found another running session with same smb/ipp-printers,
# try to remount from current, else - umount
    sessdir="$USER_FAKE_HOME/.nx/C-$sess_id"
    scr_dir="$sessdir/scripts"
    [ ! -d "$sessdir" ] && { nxlog "$FUNCNAME ($$): Session dir NOT FOUND! Calling twice? Why?" &
	return 1; }
    nxlog "$FUNCNAME ($$): starting." &
    for spname in $scr_dir/@p@* ; do
	PNAME="$(echo "$spname" | awk 'BEGIN {FS="@p@"} {i=2; print $i}')"
	[ "$spname" = "*" ] && { nxlog "$FUNCNAME ($$): No printers, stop." &
	    return 1; }
        nxlog "$FUNCNAME ($$): process printer \"$PNAME\", search in system cups." &
        prstr="$(LC_ALL=C /usr/bin/lpstat -v 2>/dev/null| grep "$PNAME")"
	[ -z "$prstr" ] && { nxlog "$FUNCNAME ($$): printer \"$PNAME\" NOT FOUND in system cups" &
	    continue; }
        [ -n "$(echo "$prstr" | grep "ipp://")" ] && type="ipp" || type="smb"
        mport=$(get_mport2sessdir "$sessdir" "$type")
	[ -z "$(echo "$prstr" | grep ":$mport")" ] && { nxlog "$FUNCNAME ($$): printer \"$PNAME\" has mport rather than $mport, skiping." &
	    continue; }
	# printer already present at own mport
	nxlog "$FUNCNAME ($$): printer \"$PNAME\" (port:$mport) found - deleting" &
	/usr/bin/sudo /usr/sbin/lpadmin -x "$PNAME"
	new_dir="$(get_sessdir4res "@p@$PNAME")"
	[ -z "$new_dir" ] && { nxlog "$FUNCNAME ($$): no running sessions found for printer \"$PNAME\", stop remounting" &
	    continue; }
	new_port="$(get_mport2sessdir "$new_dir" "$type")"
	desc_fn="$USER_FAKE_HOME/.nx/shares_priv/@p@$PNAME"
	[ -r "$desc_fn" ] || { nxlog "$FUNCNAME ($$): description file \"$desc_fn\" for printer \"$PNAME\" NOT ACCESSIBLE, stop remounting" &
	    continue; }
	descstr="$(cat "$desc_fn" 2>/dev/null)"
	DEVICE_URI="$(echo "$descstr" | cut -d\& -f2 | sed 's/:'$mport'/:'$new_port'/')"
	share="$(echo "$DEVICE_URI" |  awk 'BEGIN {FS="/"} {i=NF; print $i}')"
	upass="$(echo "$DEVICE_URI" | cut -d\/ -f3 | cut -d@ -f1)" && username="$(echo "$upass"| cut -d: -f1)" && password="$(echo "$upass"| cut -d: -f2)"
	#[ "$type" = "ipp" ] && {
	#    newpass="$(cat "$new_dir/scripts/@p@$PNAME" 2>/dev/null)"
	#    [ ! -z "$newpass" ] && { nxlog "$FUNCNAME ($$): new password for printer \"$PNAME\" ARE LOST, stop remounting" &
	#    continue; }
	#    DEVICE_URI="$(echo "$DEVICE_URI" | sed 's/:'$password'/:'$newpass'/')"
	#    password="$newpass"
	#}
	[ "$(check_remote_printer $type $share $new_port $username $password)" = "0" ] && { nxlog "$FUNCNAME ($$): smb/cifs-share \"$share\" for printer \"$PNAME\" NOT ACCESSIBLE, stop remounting" &
	    continue; }
	MODEL="$(echo "$descstr" | cut -d\& -f1)"
	defaultPrinter="$(echo "$descstr" | cut -d\& -f4)"
	if [ -r "$MODEL" ] ; then # ppd only
	    CMDSTR="/usr/bin/sudo /usr/sbin/lpadmin -p $PNAME -P $MODEL -v $DEVICE_URI -E"
	else # foomatic
	    ID="$MODEL"
	    DRV="$(echo "$descstr" | cut -d\& -f5)"
	    CMDSTR="/usr/bin/sudo /usr/bin/foomatic-configure -s cups -n $PNAME -p $ID -d $DRV -c $DEVICE_URI -q"
	fi
	nxlog "$FUNCNAME ($$): invoke $CMDSTR" &
	$CMDSTR
	if [ $? -eq 0 ] ; then
	    nxlog "$FUNCNAME ($$): printer \"$PNAME\" installed" &
	    [ "$type" = "ipp" ] && {
		descstr="$(echo "$descstr" | sed 's/:'$mport'/:'$new_port'/')"
		echo "$descstr" > "$desc_fn"
	    }
	    PUBLIC="$(echo "$descstr" | cut -d\& -f3)"
	    [ -n "$PUBLIC" ] && { /usr/bin/sudo /usr/sbin/lpadmin -p $PNAME $PUBLIC;
		[ $? -ne 0 ] && nxlog "$FUNCNAME ($$): FAILED to set options \"$PUBLIC\" for printer \"$PNAME\"" &
		}
	    [ "$defaultPrinter" = "1" ] && { /usr/bin/sudo /usr/sbin/lpadmin -d "$PNAME";
		[ $? -ne 0 ] && nxlog "$FUNCNAME ($$): FAILED to set default printer \"$PNAME\"" &
		}
	else
	    nxlog "$FUNCNAME ($$): FAILED to reinstall printer \"$PNAME\"" &
        fi
    done
    nxlog "$FUNCNAME ($$): end." &
}

#
# node_cupsd_stop
#
#	Used local vars: $sess_id
#

node_cupsd_stop()
{
    #  dimbor: if system cupsd is used, we try to remove/remount printers
    if [ "$ENABLE_CUPS_SERVER_MODE" = "1" ] ; then
	nxlog "$FUNCNAME ($$): starting with  ENABLE_CUPS_SERVER_MODE = 1, call try_remount_printers()" &
	try_remount_printers
	nxlog "$FUNCNAME ($$): end" &
	return
    fi

    # original behaviour
    #
    # Cleanup userspace cups daemon
    #
   
    [ -e "$USER_FAKE_HOME/.nx/C-$sess_id/pids/cupsd" ] || return
   
    NODE_CUPSD_PID=$(cat "$USER_FAKE_HOME/.nx/C-$sess_id/pids/cupsd")

    # Check for a running userspace cupsd, look if its still active
    # and kill it if so
    ( [ -n "$NODE_CUPSD_PID" ] && kill -0 $NODE_CUPSD_PID && kill $NODE_CUPSD_PID && sleep 2 && kill -0 $NODE_CUPSD_PID && kill -9 $NODE_CUPSD_PID ) 2>/dev/null

    # delete pid file
    rm -f "$USER_FAKE_HOME/.nx/C-$sess_id/pids/cupsd"

    # remove all printers
    echo >"$USER_FAKE_HOME/.nx/C-$sess_id/cups/printers.conf"
    [ -z "$KDE_PRINTRC" ] || sed "s|^Host=$USER_FAKE_HOME/.nx/C-$sess_id/cups/cups.sock||" -i "$KDE_PRINTRC"
}

#
# node_cupsd_setup
#
#	Used local vars: $sess_id, $display
#

node_cupsd_setup()
{
	let NODE_CUPSD_PORT=$display+9000 # offset 9000 for userspace cupsd's
	export NODE_CUPSD_PORT
	
	export NODE_CUPSD_SOCKET="$USER_FAKE_HOME/.nx/C-$sess_id/cups/cups.sock"
	
	mkdir -p "$USER_FAKE_HOME/.nx/C-$sess_id/pids/"
	[ -e "$USER_FAKE_HOME/.nx/C-$sess_id/pids/cupsd" ] && return
	touch "$USER_FAKE_HOME/.nx/C-$sess_id/pids/cupsd"
	
	mkdir -p "$USER_FAKE_HOME/.nx/C-$sess_id/cups/spool/tmp" "$USER_FAKE_HOME/.nx/C-$sess_id/cups/spool/certs" "$USER_FAKE_HOME/.nx/C-$sess_id/cups/ppd" "$USER_FAKE_HOME/.nx/C-$sess_id/cups/cache"
	mkdir -p "$USER_FAKE_HOME/.nx/C-$sess_id/cups/log" #JJK cups log file home

#JJK: Modifications to cupsd.conf
#JJK:   - Added SystemGroup line in order to add $USER to SystemGroup
#JJK:   - Moved all the log files to log/<log>
#JJK:   - Set AccessLog to: log/access_log (was /dev/null)
#JJK:   - Added listening on $NODE_CUPSD_PORT
#JJK:	         Listen localhost: $NODE_CUPSD_PORT
#JJK:   - Removed following line because directive is specific to Debian
#JJK:       PidFile $USER_FAKE_HOME/.nx/C-$sess_id/pids/cupsd
#JJK:   -  Access restrictions borrowed from /etc/cups/cupsd.conf
#JJK:   -  Default policy borrowed from /etc/cups/cupsd.conf but modified
#JJK:        to allow Add, Delete, and Default printer without (password)
#JJK:        authentication
#JJK:   - Note for more detailed logging set: LogLevel debug

CUPSPidFileString="PidFile $USER_FAKE_HOME/.nx/C-$sess_id/pids/cupsd"
if [ "$CUPS_PidFile" == "0" ] ; then
    CUPSPidFileString=""
fi

cat <<EOF > $USER_FAKE_HOME/.nx/C-$sess_id/cups/cupsd.conf
SystemGroup sys root $USER
AccessLog log/access_log
ErrorLog log/error_log
PageLog log/page_log
LogLevel $CUPSLogLevel
TempDir $USER_FAKE_HOME/.nx/C-$sess_id/cups/spool/tmp
RequestRoot $USER_FAKE_HOME/.nx/C-$sess_id/cups/spool
ServerRoot $USER_FAKE_HOME/.nx/C-$sess_id/cups/
StateDir $USER_FAKE_HOME/.nx/C-$sess_id/cups/
CacheDir $USER_FAKE_HOME/.nx/C-$sess_id/cups/cache

Listen localhost:$NODE_CUPSD_PORT
Listen $NODE_CUPSD_SOCKET
Browsing Off
ServerName localhost
$CUPSPidFileString

#JJK:  Restrict access to the server...
<Location />
Order Deny,Allow
Deny From All
Allow from 127.0.0.1
</Location>

#JJK: Restrict access to the admin pages...
<Location /admin>
  Encryption Required
  Order allow,deny
  Allow localhost
</Location>

#JJK: Restrict access to configuration files...
<Location /admin/conf>
  AuthType Basic
  Require user @SYSTEM
  Order allow,deny
  Allow localhost
</Location>

# Allow everything for anonymous, because we are protected through UNIX socket
#JJK: Since allowing access via $NODE_CUPSD_PORT, need to add protection
<Policy default>
  #JJK: Job-related operations must be done by the owner or an adminstrator...
  <Limit Send-Document Send-URI Hold-Job Release-Job Restart-Job Purge-Jobs Set-Job-Attributes Create-Job-Subscription Renew-Subscription Cancel-Subscription Get-Notifications Reprocess-Job Cancel-Current-Job Suspend-Current-Job Resume-Job CUPS-Move-Job>
    Require user @OWNER @SYSTEM
    Order deny,allow
  </Limit>

  #JJK:All administration operations require an adminstrator to authenticate...
  <Limit Pause-Printer Resume-Printer Set-Printer-Attributes Enable-Printer Disable-Printer Pause-Printer-After-Current-Job Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer Activate-Printer Restart-Printer Shutdown-Printer Startup-Printer Promote-Job Schedule-Job-After CUPS-Add-Class CUPS-Delete-Class CUPS-Accept-Jobs CUPS-Reject-Jobs>
    AuthType Basic
    Require user @SYSTEM
    Order deny,allow
  </Limit>

  #JJK: Except need to allow these for nxnode to work
  <Limit CUPS-Add-Printer CUPS-Delete-Printer CUPS-Set-Default>
    Order deny,allow
  </Limit>

  # Only the owner or an administrator can cancel or authenticate a job...
  <Limit Cancel-Job CUPS-Authenticate-Job>
    Require user @OWNER @SYSTEM
    Order deny,allow
  </Limit>

  <Limit All>
    AuthType None
    Order deny,allow
  </Limit>
</Policy>
EOF

	touch "$USER_FAKE_HOME/.nx/C-$sess_id/cups/printers.conf" "$USER_FAKE_HOME/.nx/C-$sess_id/cups/classes.conf"

	#JJK: Also copy over pstoraster.convs, LAV: Also copy gstoraster.convs
	cp -af "$CUPS_ETC"/mime.* "$CUPS_ETC"/pstoraster.convs "$CUPS_ETC"/gstoraster.convs "$USER_FAKE_HOME/.nx/C-$sess_id/cups/"

	# start cupsd
#JJK: Note the directive PidFile in the original cupsd.conf intended for
#JJK: recording the pid is a Debianism. Instead, we will use the non-daemon
#JJK: form of cupsd and capture the pid directly
#JJK:	$COMMAND_CUPSD -c "$USER_FAKE_HOME/.nx/C-$sess_id/cups/cupsd.conf" &>/dev/null </dev/null
	$COMMAND_CUPSD -F -c "$USER_FAKE_HOME/.nx/C-$sess_id/cups/cupsd.conf" &>/dev/null </dev/null &
	NODE_CUPSD_PID=$!
	echo $NODE_CUPSD_PID >"$USER_FAKE_HOME/.nx/C-$sess_id/pids/cupsd"

	# setup KDE
	if [ "$ENABLE_KDE_CUPS" = "1" -a -e "$KDE_PRINTRC" ]
	then
		if egrep -q "^Host=" "$KDE_PRINTRC"
		then
			[ "$ENABLE_KDE_CUPS_DYNAMIC" = "1" ] && $COMMAND_PERL -pi -e 's,^Host=.*,Host[\$ie]=\$\('"$PATH_BIN/nxcups-gethost"'),g' "$KDE_PRINTRC"
			[ "$ENABLE_KDE_CUPS_DYNAMIC" != "1" ] && $COMMAND_PERL -pi -e 's,^Host=.*,Host='"$NODE_CUPSD_SOCKET"',g' "$KDE_PRINTRC"
		else
			echo "[CUPS]" >> "$KDE_PRINTRC"
			[ "$ENABLE_KDE_CUPS_DYNAMIC" = "1" ] && echo "Host[\$ie]=\$($PATH_BIN/nxcups-gethost)" >> "$KDE_PRINTRC"
			[ "$ENABLE_KDE_CUPS_DYNAMIC" != "1" ] && echo "Host=$NODE_CUPSD_SOCKET" >> "$KDE_PRINTRC"
		fi
	fi
}

#
# node_cupsd_reload
#
#	Used local vars: $sess_id
#

node_cupsd_reload()
{
	[ -e "$USER_FAKE_HOME/.nx/C-$sess_id/pids/cupsd" ] || return
	NODE_CUPSD_PID=$(cat "$USER_FAKE_HOME/.nx/C-$sess_id/pids/cupsd")
	[ -n "$NODE_CUPSD_PID" ] && kill -0 $NODE_CUPSD_PID && kill -HUP $NODE_CUPSD_PID
}

node_cupsd_get_socket()
{
	# dimbor: for use system cupsd
	[ "$ENABLE_CUPS_SERVER_MODE" = "1" ] && { echo "$CUPS_DEFAULT_SOCK"; return; }

        node_cupsd_setup
        echo $NODE_CUPSD_SOCKET
}

create_mountpoint()
{
    # creates directory and checks its attrs
    # params: dir owner group
    [ ! -e "$1" ] && {
	errstr="$(mkdir -p "$1" 2>&1)"
	[ $? -ne 0 ] && {
	    echo "Unable to create \"$1\". Error: \"$errstr\""; return 1
	} || echo "\"$1\" successfully created."
    } || echo "\"$1\" already exist."
    DUSER="$(stat -c %U "$1")"; DGROUP="$(stat -c %G "$1")"
    [ "$DUSER" != "$2" -o "$DGROUP" != "$3" ] && {
	logstr="\"$1\" ($DUSER:$DGROUP). Attempt to change owners to $2:$3 - "
	errstr="$(chown "$2:$3" "$1" 2>&1)"
	[ $? -ne 0 ] && {
	    echo "$logstr - FAILED! Error: \"$errstr\""; return 1
	} || echo "$logstr - successfully completed."
    }
    DACR="$(stat -c %a "$1")"
    [ "$DACR" != "770" ] && {
	logstr="\"$1\" ($DACR). Attempt to change access rights to 0770 - "
	errstr="$(chmod 0770 "$1" 2>&1)"
	[ $? -ne 0 ] && {
	    echo "$logstr - FAILED! Error: \"$errstr\""; return 1
	} || echo "$logstr - successfully completed."
    }
    return 0
}

usermount_enable()
{
    # check if available starting mode of mount/umount.cifs
    ret=1; logstr0="$FUNCNAME ($$):"
    ostr="$(stat -c %a "$COMMAND_SMBMOUNT_CIFS" 2>&1)"; RC=$?
    [ $RC -ne 0 ] && { nxlog "$logstr0 $ostr" &
	ret="0"; }
    [ "$ret" != "0" -a "$ostr" != "4711" ] && { ret="0"
	nxlog "$logstr0 Permissions of  $COMMAND_SMBMOUNT_CIFS ($ostr) != 4711. mounting in user-mode is disabled." &
    }
    [ "$ret" != "0" -a $RC -eq 0 ] && {
	ostr="$($COMMAND_SMBMOUNT_CIFS 2>&1)"
	[ -n "$(echo "$ostr" | grep "setuid root program disabled")" ] && { nxlog "$logstr0 $ostr" &
	    ret="0"; }
    }
    ostr="$(stat -c %a "$COMMAND_SMBUMOUNT_CIFS" 2>&1)"; RC2=$?
    [ $RC2 -ne 0 ] && { nxlog "$logstr0 $ostr" &
	ret="0"; RC=$RC2; }
    [ "$ret" != "0" -a "$ostr" != "4711" ] && { ret=0
	nxlog "$logstr0 Permissions of  $COMMAND_SMBUMOUNT_CIFS ($ostr) != 4711. umounting in user-mode is disabled." &
    }
    echo $ret; return $RC
}

sudomount_enable()
{
    # checks available starting mode with sudo of mount/umount.cifs
    ret=1; logstr0="$FUNCNAME ($$):"
    ostr="$(/usr/bin/sudo $COMMAND_SMBMOUNT_CIFS -V 2>&1)"; RC=$?
    [ $RC -ne 0 ] && { ret=0
        nxlog "$logstr0 Mounting with sudo is disabled due $ostr. To enable, you must to change /etc/sudo.d/nxserver or /etc/sudoers file." &
    }

    ostr="$(/usr/bin/sudo $COMMAND_SMBUMOUNT_CIFS -V 2>&1)"; RC=$?
    [ $RC -ne 0 ] && { ret=0
        nxlog "$logstr0 Umounting with sudo is disabled due $ostr. To enable, you must to change /etc/sudo.d/nxserver or /etc/sudoers file." &
    }
    echo $ret; return $RC
}

try_mount_smb()
{
    # params: sharename(1), mountpoint(2), smb-username(3), port(4),
    # ex-options/uid,gid/(5), smb-password(6)
    RC=1; nxlog "$FUNCNAME ($$): Starting: TRY_USER=$TRY_USER, TRY_SUDO=$TRY_SUDO" &
    [ -n "$SMB_MOUNT_OPTIONS" -a "${SMB_MOUNT_OPTIONS:0:1}" != "," ] && SMB_MOUNT_OPTIONS=",$SMB_MOUNT_OPTIONS,"
    SMB_OPTS="ip=127.0.0.1,port=$4$SMB_MOUNT_OPTIONS"
    SMB_OPTS="$SMB_OPTS""username=$3,"
    [ -n "$6" ] && SMB_OPTS="$SMB_OPTS""password=$6" || SMB_OPTS="$SMB_OPTS""password="
    [ $TRY_USER -ne 0 ] && {
	MNTSTR="$COMMAND_SMBMOUNT_CIFS $1 $2 -o $SMB_OPTS"
	nxlog "$FUNCNAME ($$): invoke $MNTSTR 2>&1" &
	error="$($MNTSTR 2>&1)"; RC=$?
	[ $RC -ne 0 ] && nxlog "$FUNCNAME ($$): Mount error: \"$error\"" &
    }
    [ $TRY_SUDO -ne 0 -a $RC -ne 0 ] && {
	MNTSTR="/usr/bin/sudo $COMMAND_SMBMOUNT_CIFS $1 $2 -o $5$SMB_OPTS"
    	nxlog "$FUNCNAME ($$): invoke $MNTSTR 2>&1" &
	error="$($MNTSTR 2>&1)"; RC=$?
	[ $RC -ne 0 ] && nxlog "$FUNCNAME ($$): Mount error: \"$error\"" &
    }
    [ $RC -eq 0 ] && nxlog "$FUNCNAME ($$): Finished successfully." &
    return $RC
}

try_umount_smb()
{
    # param: mountpoint
    amnt=3; cn=0; logstr0="$FUNCNAME ($$):"; RC=0
    nxlog "$logstr0 Starting arg \"$1\". TRY_USER=$TRY_USER, TRY_SUDO=$TRY_SUDO, remain $amnt attempts." &
    for (( i=$amnt; $i; --i )) ; do
	if [ -n "$(mount | grep "$1")" ] ; then
	    [ $i -lt $((amnt/2)) ] && { FFL="-f"; logstr1="$logstr0 force (-f)";
		} || { FFL=""; logstr1="$logstr0"; }
	    RC=1; (( cn++ ))
	    [ $TRY_USER -ne 0 ] && {
		logstr2="$logstr1"" umount attempt #$cn \"$1\" in user-mode"
		ostr="$($COMMAND_SMBUMOUNT_CIFS $FFL "$1" 2>&1)"; RC=$?
		[ $RC -eq 0 ] && { nxlog "$logstr2 completed succesfully." &
		    break; } || nxlog "$logstr2 FAILED. Error: \"$ostr\"" &
	    }
	    [ $TRY_SUDO -ne 0 -a $RC -ne 0 ] && {
		logstr2="$logstr1"" umount attempt #$cn \"$1\" with sudo"
		ostr="$(/usr/bin/sudo $COMMAND_SMBUMOUNT_CIFS $FFL "$1" 2>&1)"; RC=$?
		[ $RC -eq 0 ] && { nxlog "$logstr2 completed succesfully." &
		    break; } || nxlog "$logstr2 FAILED. Error: \"$ostr\"" &
	    }
	else
	    nxlog "$logstr0 Mountpoint \"$1\" not mounted" &
	    return 0;
	fi
	sleep 2s
    done
    [ $RC -eq 0 ] && nxlog "$FUNCNAME ($$): Finished successfully." &
    return $RC
}

node_umount_smb()
{
    nxlog "$FUNCNAME ($$): starting with ENABLE_SHARE_MULTIMOUNT == $ENABLE_SHARE_MULTIMOUNT" &
    smbport=$(cat "$USER_FAKE_HOME/.nx/C-$sess_id/scripts/smbport" 2>/dev/null)

    TRY_USER=1; TRY_SUDO=0;
    [ "$smbport" = "445" ] && {
	TRY_USER=$(usermount_enable)
	TRY_SUDO=$(sudomount_enable)
	[ $TRY_USER -eq 0 -a $TRY_SUDO -eq 0 ] && { nxlog "$FUNCNAME ($$): Any methods of (u)mounting are not available. Stop umounting." &
	    return 1; }
    }
    #  dimbor: if sessions with same shares found, we try to remount them
    if [ "$ENABLE_SHARE_MULTIMOUNT" = "1" ] ; then
	scr_dir="$USER_FAKE_HOME/.nx/C-$sess_id/scripts"
	mport=$(cat "$scr_dir/mport" 2>/dev/null)
	mpoints="$(cat "$USER_FAKE_HOME/.nx/C-$sess_id/scripts/mpoint" 2>/dev/null)"
	[ -z "$mpoints" ] && { nxlog "$FUNCNAME ($$): No shares controlled by session found. Stop remounting." &
	    return; }
	for ssname in $scr_dir/@s@* ; do
	    SNAME="$(echo "$ssname" | awk 'BEGIN {FS="@s@"} {i=2; print $i}')"
	    desc_fn="$USER_FAKE_HOME/.nx/shares_priv/@s@$SNAME"
	    [ ! -r "$desc_fn" ] && { nxlog "$FUNCNAME ($$): description file \"$desc_fn\" for share \"$SNAME\" NOT ACCESSIBLE, stop remounting it" &
		continue; }
	    descstr="$(cat "$desc_fn")"
	    mpoint="$(echo "$descstr" | cut -d\& -f2)"
	    [ -z "$(echo "$mpoints" | grep "$mpoint")" ] && { nxlog "$FUNCNAME ($$): share \"$SNAME\" at \"$mpoint\" is not controlled by session. Stop remounting." &
		continue; }
	    try_umount_smb "$mpoint"; RC=$? # umount here
	    [ $RC -ne 0 ] && {
		nxlog "$FUNCNAME ($$): mountpoint \"$mpoint\" failed to unmount." &
		return
	    }
	    new_dir="$(get_sessdir4res "@s@$SNAME")"
	    [ -z "$new_dir" ] && { nxlog "$FUNCNAME ($$): no running sessions found for share \"$SNAME\", stop remounting" &
		rmdir "$mpoint" >/dev/null 2>/dev/null; continue; }
	    new_port="$(cat "$new_dir/scripts/mport" 2>/dev/null)"
	    fsname="$(echo "$descstr" | cut -d\& -f1)"
	    username="$(echo "$descstr" | cut -d\& -f3)"
	    password="$(echo "$descstr" | cut -d\& -f4)"
	    IDS_OPTS="$(echo "$descstr" | cut -d\& -f5)"
	    try_mount_smb "$fsname" "$mpoint" "$username" $new_port "$IDS_OPTS" "$password"
	    RC=$?
	    [ $RC -eq 0 ] && {
		nxlog "$FUNCNAME ($$): share \"$fsname\" remount to \"$mpoint\"" &
		echo "$mpoint" >> "$new_dir/scripts/mpoint"
	    } || {
		nxlog "$FUNCNAME ($$): share \"$fsname\" failed to remount: $error" &
		rmdir "$mpoint" >/dev/null 2>/dev/null
	    }
	done
        nxlog "$FUNCNAME ($$): end" &
	return
    fi

    if [ -e "$USER_FAKE_HOME/.nx/C-$sess_id/scripts/mpoint" ] ; then
        nxlog "$FUNCNAME ($$): File scripts/mpoint present. Continue umounting..." &
    else
	nxlog "$FUNCNAME ($$): File scripts/mpoint not present. Abort umounting..." &
	return 1
    fi
    cat "$USER_FAKE_HOME/.nx/C-$sess_id/scripts/mpoint" | while read mpoint
    do
	try_umount_smb "$mpoint"; RC=$? # umount here
	[ $RC -ne 0 ] && {
	    nxlog "$FUNCNAME ($$): mountpoint \"$mpoint\" failed to unmount." &
	    continue;
	}
	nxlog "$FUNCNAME ($$): Remove mountpoint \"$mpoint\"" &
	rmdir "$mpoint" >/dev/null 2>/dev/null #JJK:Remove mount point if empty
    done
    nxlog "$FUNCNAME ($$): end" &
}

node_stop_services()
{
	nxlog "$FUNCNAME ($$): starting" &
	nxlog "$FUNCNAME ($$): call node_umount_smb" &
	node_umount_smb
	nxlog "$FUNCNAME ($$): call node_cupsd_stop" &
	node_cupsd_stop
	nxlog "$FUNCNAME ($$): end" &
}

node_emergency_exit()
{
	nxlog "$FUNCNAME ($$): starting" &
	# umount shares & stop printers
	nxlog "$FUNCNAME ($$): call node_stop_services" &
	node_stop_services

	# kill the session
	nxlog "$FUNCNAME ($$): call 'node_terminate_session \"$sess_id\" \"failed\"'" &
	node_terminate_session "$sess_id" "failed"

	echo "NX> 1004 Error: Emergency exit due to kill signal."
	nxlog "$FUNCNAME ($$): end" &
}


#
# node_start_monitor <start|restore> <Running|Suspended>
#
#
# Monitoring the nxagent: Its also kind of a "state-machine"
#                         as it has to keep track of different
#                         connection states and react differently.
#

node_start_monitor()
{
	nxlog "$FUNCNAME ($$): starting" &
	NODE_TAIL_PID=""
	NODE_SUSPEND_STATUS="$2"

	while read line
	do
		#
		# Catch tail pid
		#
		
		if stringinstring "Info: tail -f running with pid" "$line"
		then
			NODE_TAIL_PID=$(echo $line | cut -d"'" -f2)
			echo "$NODE_TAIL_PID" >"$USER_FAKE_HOME/.nx/C-$sess_id/pids/tail"
		fi

		#
		# Catch NXAGENT SMB Port (sometimes the port differs from what we got from nxserver)
		#
		
		if stringinstring "Info: Listening * SMB connections on port" "$line"
		then
			SMBMOUNT_PORT=$(echo $line | cut -d"'" -f2)
			echo "$SMBMOUNT_PORT" >"$USER_FAKE_HOME/.nx/C-$sess_id/scripts/mport"
		fi

		#
		# Session messages
		#

		if stringinstring "Session: Starting session at" "$line"
		then
			echo "NX> 1009 Session status: starting"
		fi
		
		if stringinstring "Session: Suspending session at" "$line"
		then
			echo "NX> 1009 Session status: suspending"
		fi
		
		if stringinstring "Session: Terminating session at" "$line"
		then
			echo "NX> 1009 Session status: terminating"
		fi
		
		if stringinstring "Session: Resuming session at" "$line"
		then
			echo "NX> 1009 Session status: resuming"
		fi


		#
		# Session suspend
		#

		if stringinstring "Session: Session suspended at" "$line"
		then
			echo "NX> 1005 Session status: suspended"
			# umount shares & stop printers

			if [ "$NODE_SUSPEND_STATUS" = "Running" ]
			then
				nxlog "$FUNCNAME ($$): call 'node_suspend_session \"$sess_id\"'" &
				node_suspend_session "$sess_id"
				NODE_SUSPEND_STATUS=""
			else
				nxlog "$FUNCNAME ($$): call node_stop_services" &
				node_stop_services
			fi
		fi

		#
		# Watchdog termination
		#

		if stringinstring "Info: Watchdog running with pid" "$line"
		then
			NODE_WATCHDOG_PID=$(echo $line | cut -d"'" -f2)
		fi

		if stringinstring "Info: Waiting the watchdog process to complete." "$line"
		then
			# Kill the watchdog
			kill $NODE_WATCHDOG_PID 2>/dev/null
		fi
		
		#
		# Session is running
		#
		
		if stringinstring "Info: Waiting for connection from" "$line"
		then
			echo "NX> 710 Session status: running"
			echo "NX> 1002 Commit"
			echo "NX> 1006 Session status: running"
		fi

		#
		# Reconnection success!
		#
		
		if stringinstring "Session: Session resumed at" "$line"
		then
			echo "NX> 718 Session restore succeded"
			if [ "$1" = "restore" ]
			then
				kill $NODE_TAIL_PID
				break
			fi
		fi

		#
		# Reconnection failure
		#
		
		if stringinstring "Session: Display failure detected at" "$line"
		then
			if [ "$1" = "restore" ]
			then
				echo "NX> 596 Error: Session $1 failed. Reason was: $line"
				kill $NODE_TAIL_PID
				break
			fi
		fi
	done
	
	trap "" EXIT
	
	[ "$1" = "restore" ] || { nxlog "$FUNCNAME ($$): call node_stop_services" &
	    node_stop_services; }
	# close all open file descriptors
	exec 0<&-
	exec 1>&-
	exec 2>&-
	nxlog "$FUNCNAME ($$): end" &
	exit 0
}


#
# -----------------------------------------------------------------------------
# startsession - Start a new session.
# -----------------------------------------------------------------------------
#

startsession()
{

	# user=knoppix&userip=192.168.1.66&uniqueid=6A8269CC467264EAEF6349D062689755&display=1000&session=lappi%5ffull&type=unix%2dkde&cache=8M&images=32M&cookie=84765070afee043cf83f85d21130145f&link=lan&render=1&backingstore=when_requested&imagecompressionmethod=0&geometry=fullscreen&keyboard=fr&media=0&samba=1&agent_server=&agent_user=&agent_password=

	user=$(getparam user)
	userip=$(getparam userip)
	uniqueid=$(getparam uniqueid)
	display=$(getparam display)
	session=$(getparam session)
	type=$(getparam type | sed 's/%2d/-/g')
	application=$(getparam application)
	windows_app=$(getparam application)
	cache=$(getparam cache)
	images=$(getparam images)
	cookie=$(getparam cookie)
	link=$(getparam link)
	virtualdesktop=$(getparam virtualdesktop)
	render=$(getparam render)
	backingstore=$(getparam backingstore)
	imagecompressionmethod=$(getparam imagecompressionmethod)
	imagecompressionlevel=$(getparam imagecompressionlevel)
	geometry=$(getparam geometry)
	keyboard=$(getparam keyboard)
	kbtype=$(getparam kbtype)
	media=$(getparam media)
	mediahelper=$(getparam mediahelper)
	sync=$(getparam sync)
	samba=$(getparam samba)
	cups=$(getparam cups)
	agent_server=$(getparam agent_server | sed 's/%3A/:/g')
	agent_user=$(getparam agent_user)
	agent_password=$(getparam agent_password)
	agent_domain=$(getparam agent_domain)
	screeninfo=$(getparam screeninfo)
	nodelay=$(getparam nodelay)
	
	sess_id="$SERVER_NAME-$display-$uniqueid"
	NXSESSION_DIRECTORY="$USER_FAKE_HOME/.nx/C-$sess_id"
	
	nxlog "$FUNCNAME ($$): starting with args \"$@\"" &
	[ "$PROXY_TCP_NODELAY" = "0" ] && nodelay=0

	# 1.5.0 options
	rdpcolors=$(getparam rdpcolors)
	rdpcache=$(getparam rdpcache)
	http=$(getparam http)
	
	# nxclient > 1.5.0-106 variables
	resize=$(getparam resize)
	keybd=$(getparam keybd)

	# backwards compatibility for keybd parameter
	[ -z "$keybd" ] && keybd=$(getparam aux)
	aux=$(getparam aux)

	kbload=$(getparam kbload)
	keymap=$(getparam keymap)

	rootless=0
	# Its still the clients decision
	[ "$ENABLE_ROOTLESS_MODE" = "1" ] &&  rootless=$(getparam rootless)

	# Rootless fix from 2x nxserver 1.5.0
	realtype=$type
	[ "$type" = "unix-application" -o "$type" = "unix-default" ] && realtype="unix-desktop"
	[ "$type" = "unix-gnome" ] && realtype="gnome"
	[ "$type" = "unix-kde" ] && realtype="kde"

	# NX 2.1.0 file-sharing port options
	client=$(getparam client)

	smbport=""

	if [ "$samba" = "1" ]
	then
		smbport=445 # cifs
	fi
	
	# RX@Etersoft specific variables
	clientproto=$(getparam clientproto)
	status=$(getparam status)
	host=$(getparam host)
	
	# New NX 3.0 shadow mode related variables
	shadowusername=$(getparam shadowusername)
	shadowcookie=$(getparam shadowcookie)
	shadowdisplay=$(getparam shadowdisplay)
	shadowhost=$(getparam shadowhost)

	# Authentication method needed by guest mode.
	login_method=$(getparam login_method)
	
	# export the agent_* options for the helper scripts
	if [ "$(getparam freenx_export_agents)" = "1" ]
	then
		export agent_user
		export agent_password
		export agent_server
		export agent_domain
		export windows_app
		agent_keyboard=""
		[ "$ENABLE_EXTERNAL_NXDESKTOP_KEYBOARD" = "1" ] && agent_keyboard=$(echo "$keyboard" | cut -d'/' -f2)
		export agent_keyboard
		export NXSESSION_DIRECTORY
		export AGENT_EXTRA_OPTIONS_RFB
		export AGENT_EXTRA_OPTIONS_RDP
		export COMMAND_RDESKTOP
		export COMMAND_VNCVIEWER
		export COMMAND_VNCPASSWD
		export COMMAND_X11VNC
		export PATH_BIN
		export shadowdisplay
		export shadowhost
		export shadowuser
		export shadowcookie

		export ENABLE_SESSION_SHADOWING_AUTHORIZATION
		export ENABLE_INTERACTIVE_SESSION_SHADOWING

		# We do not want to suspend such a session
		# as RDP/RFB are both suspendable as well
		ENABLE_PERSISTENT_SESSION=""
	fi

	fullscreen=$(getparam fullscreen)
	[ "$geometry" = "fullscreen" ] && fullscreen="1"

	[ "$EXPORT_USERIP" = "1" ] && export NXUSERIP="$userip"

	ssl_tunnel=$(getparam encryption)
	[ -z "$ssl_tunnel" ] && ssl_tunnel=0
	
	if [ "$ssl_tunnel" = "1" ]
	then
		# we need to use the IP of the "calling" server now
		userip=$(echo $SSH_CLIENT $SSH2_CLIENT | cut -d" " -f1 | sed 's/::ffff://g')
		# If host is the same, use 127.0.0.1, else fallback to default
		[ -z "$userip" -a "$host" = "127.0.0.1" ] && userip="127.0.0.1"
		[ -z "$userip" ] && userip="*"
	fi

	# We need our own external IP
	proxyip="$EXTERNAL_PROXY_IP"

	if [ -z "$proxyip" -a -n "$host" ]
	then
		[ "$host" = "127.0.0.1" ] && host=$(hostname)
		proxyip=$(ping -c1 "$host" | grep 'PING' | cut -d'(' -f2 | cut -d')' -f1)
	fi
	
	# ' clean mc.syntax
	
	[ -z "$proxyip" ] && proxyip="127.0.0.1"
	
	# ok, lets make the session dir first:
	
	sess_id="$SERVER_NAME-$display-$uniqueid"
	[ "$EXPORT_SESSIONID" = "1" ] && export NXSESSIONID="$sess_id"
	
	OLD_UMASK=$(umask)
	umask 0022
	mkdir -p $USER_FAKE_HOME
	umask 0077
	mkdir -p "$USER_FAKE_HOME/.nx/C-$sess_id"
	umask $OLD_UMASK
	
	# cache=8M,images=32M,pack=nopack,link=lan,type=unix-kde,cleanup=0,accept=192.168.1.66,cookie=E38A94A77F975443AF04EC911881B120,id=Knoppix-1000-6A8269CC467264EAEF6349D062689755,samba=1,render=1:1000
	
	PACK=""
	[ -z "$imagecompressionlevel" ] && imagecompressionlevel="9"
	
	[ "$imagecompressionmethod" = "0" ] && PACK="pack=nopack,"
	[ "$imagecompressionmethod" = "1" ] && PACK="pack=16m-jpeg-$imagecompressionlevel,"
	[ "$imagecompressionmethod" = "2" ] && PACK="pack=16m-png-9,"
	
	proxy_cookie=$(echo $[$RANDOM*$RANDOM] | $COMMAND_MD5SUM | cut -d" " -f1)

	# all newer clients support fake cookie authentication
	if [ "$clientproto" != "1.4.0" ]
	then
		# enable fake cookie authentication
		cookie=$proxy_cookie
	fi

	# write options file
	[ -z "$samba" ] && samba=0
	[ -z "$media" ] && media=0
	[ -z "$nodelay" ] && nodelay=1

	clipboard="$ENABLE_CLIPBOARD"
	menu="$ENABLE_PULLDOWN_MENU"

	CACHE="cache=$cache,"
	[ -z "$cache" ] && CACHE=""
	IMAGES="images=$images,"
	[ -z "$images" ] && IMAGES=""

	ACCEPT="accept=$userip,"
	[ "$userip" = "*" ] && ACCEPT=""

if [ "$1" = "application" ]
then
	# This needs to be set, else nxagent is terminated
	rootless="1"
	virtualdesktop="0"
	nxlog "$FUNCNAME ($$): call 'node_start_applications'" &
	node_start_applications &
	echo "NX> 596 Application $application started successfully."
	return
fi

	OLD_UMASK=$(umask)
	umask 0077

	nxlog "$FUNCNAME ($$): generate \"$USER_FAKE_HOME/.nx/C-$sess_id/options\"" &
cat << EOF > "$USER_FAKE_HOME/.nx/C-$sess_id/options"
nx/nx,${keyboard:+keyboard=$keyboard,}${kbtype:+kbtype=$kbtype,}${kbload:+kbload=$kbload,}${keymap:+keymap=$keymap,}${geometry:+geometry=$geometry,}${client:+client=$client,}${resize:+resize=$resize,}${CACHE}${IMAGES}${PACK}link=$link,nodelay=$nodelay,type=$realtype${clipboard:+,clipboard=$clipboard}${composite:+composite=$composite},cleanup=10,product=LFE/None/LFEN/None,shmem=1,${backingstore:+backingstore=$backingstore,}shpix=1,${ACCEPT}cookie=$proxy_cookie,id=$sess_id,samba=$samba,media=$media${sync:+,sync=$sync}${cups:+,cups=$cups}${keybd:+,keybd=$keybd}${aux:+,aux=$aux}${http:+,http=$http}${rdpcolors:+,rdpcolors=$rdpcolors}${rdpcache:+,rdpcache=$rdpcache}${fullscreen:+,fullscreen=1}${menu:+,menu=$menu}:$display
EOF
	umask $OLD_UMASK
#samba=$samba,
	#cache=$cache,images=$images,pack=nopack,link=$link,type=$type,cleanup=0,accept=$userip,cookie=$proxy_cookie,id=$sess_id
#samba=$samba,media=$media,render=$render:$display

	# write xauth script file
	nxlog "$FUNCNAME ($$): write xauth script file" &

$COMMAND_XAUTH >/dev/null 2>&1 <<EOF
add localhost:$display MIT-MAGIC-COOKIE-1 $cookie
add :$display MIT-MAGIC-COOKIE-1 $cookie
exit
EOF

$COMMAND_XAUTH -f "$USER_FAKE_HOME/.nx/C-$sess_id/authority" >/dev/null 2>&1 <<EOF
add localhost:$display MIT-MAGIC-COOKIE-1 $cookie
add :$display MIT-MAGIC-COOKIE-1 $cookie
exit
EOF

	mkdir -m700 "$USER_FAKE_HOME/.nx/C-$sess_id/scripts/" 2>/dev/null || chmod 700 "$USER_FAKE_HOME/.nx/C-$sess_id/scripts/"

cat << EOF >"$USER_FAKE_HOME/.nx/C-$sess_id/scripts/authority"
remove localhost:$display
remove :$display
exit
EOF

	export SHADOW_XAUTHORITY="$USER_FAKE_HOME/.nx/C-$sess_id/authority"

	# If we have a shadow cookie, we add it to xauth session authority file as well
	if [ -n "$shadowcookie" ]
	then
		nxlog "$FUNCNAME ($$): If we have a shadow cookie, we add it to xauth session authority file as well" &
		$COMMAND_XAUTH -f "$SHADOW_XAUTHORITY" add "$shadowhost:$shadowdisplay" MIT-MAGIC-COOKIE-1 "$shadowcookie"
	elif [ -n "$shadowdisplay" ]
	then
		# we need to merge in the normal .Xauthority file
		nxlog "$FUNCNAME ($$): we need to merge in the normal .Xauthority file" &
		$COMMAND_XAUTH -f "$SHADOW_XAUTHORITY" merge "$HOME/.Xauthority"
	fi

if [ "$1" = "restore" ]
then
	nxlog "$FUNCNAME ($$): restore session" &
	echo > "$USER_FAKE_HOME/.nx/C-$sess_id/session"
	sh -c 'echo "Info: tail -f running with pid '\'\$$\''."; exec tail -n1 -f '"$USER_FAKE_HOME"'/.nx/C-'"$sess_id"'/session' | node_start_monitor "$1" "$status" &

	MONITOR_PID=$!
	export MONITOR_PID

	mkdir -p "$USER_FAKE_HOME/.nx/C-$sess_id/pids/"
	echo "$MONITOR_PID" > "$USER_FAKE_HOME/.nx/C-$sess_id/pids/monitor"

	nxlog "$FUNCNAME ($$): call 'node_suspend_session \"$sess_id\"'" &
	node_suspend_session "$sess_id" || { echo "Info: Reconnection failed: NX Agent process could not be found." >>"$USER_FAKE_HOME/.nx/C-$sess_id/session"; node_fail_restore_session "$sess_id"; }
else
	nxlog "$FUNCNAME ($$): call 'node_start_agent'" &
	node_start_agent &
	for (( i=60; $i; --i )) ; do
	    if [ -e "$USER_FAKE_HOME/.nx/C-$sess_id/pids/agent" ] ; then
		nxlog "$FUNCNAME ($$): wait for nxagent pid'" &
		sleep 0.5s
		break
	    fi
	done
	nxlog "$FUNCNAME ($$): call 'node_start_applications'" &
	node_start_applications &
fi

if which "$NODE_AUTOSTART" >/dev/null 2>&1
then
	# go into background immediately
	NXSESSIONID="$sess_id" DISPLAY=:$display "$NODE_AUTOSTART" "$1" >/dev/null 2>&1 &
	# dont't wait for this child!
	disown $!
fi
	
cat << EOF
NX> 700 Session id: $sess_id
NX> 705 Session display: $display
NX> 703 Session type: $type
NX> 701 Proxy cookie: $proxy_cookie
NX> 702 Proxy IP: $proxyip
NX> 706 Agent cookie: $cookie
NX> 704 Session cache: $type
NX> 707 SSL tunneling: $ssl_tunnel
EOF

# File-sharing port options
if [ "$samba" = "1" -a -n "$smbport" ]
then
	echo "NX> 709 File-sharing port: $smbport"
	echo "$smbport" >"$USER_FAKE_HOME/.nx/C-$sess_id/scripts/smbport"
fi

# collection ...

# NX> 1004 Error:
#Session 'Knoppix-1000-40EFB9F64FA55C64C41C72CA39EBD720' has failed after reaching usable state. Session directory '/home/knoppix/.nx/F-C-Knoppix-1000-40EFB9F64FA55C64C41C72CA39EBD720' will be not deleted to allow for further investigation.

if [ -n "$MONITOR_PID" ]
then
	wait "$MONITOR_PID"
	rm -f "$USER_FAKE_HOME/.nx/C-$sess_id/pids/monitor"
	rm -f "$USER_FAKE_HOME/.nx/C-$sess_id/pids/tail"
fi
wait # for all children
nxlog "$FUNCNAME ($$): end" &
}

#
# -----------------------------------------------------------------------------
# cmd_node functions - changes lots of small variables
# -----------------------------------------------------------------------------
#

cmd_node_terminate()
{
	sessionid=$(getparam_sessionid)
	echo "$DELIM 716 Terminating session $sessionid on user request."
	display=$(cd $USER_FAKE_HOME/.nx/; echo C-$SERVER_NAME-*-$sessionid | awk 'BEGIN {FS="-"} {i=NF-1; print $i}')
	node_terminate_session "$SERVER_NAME-$display-$sessionid"
}

cmd_node_suspend()
{
	sessionid=$(getparam_sessionid)
	echo "$DELIM 716 Suspending session $sessionid on user request."
	display=$(cd $USER_FAKE_HOME/.nx/; echo C-$SERVER_NAME-*-$sessionid | awk 'BEGIN {FS="-"} {i=NF-1; print $i}')
	node_suspend_session "$SERVER_NAME-$display-$sessionid"
}

cmd_node_smbmount()
{
	sessionid=$(getparam_sessionid)
	display=$(cd $USER_FAKE_HOME/.nx/; echo C-$SERVER_NAME-*-$sessionid | awk 'BEGIN {FS="-"} {i=NF-1; print $i}')
	sess_id="$SERVER_NAME-$display-$sessionid" # dimbor: for called functions
	sess_dir="$USER_FAKE_HOME/.nx/C-$sess_id"
	
	nxlog "$FUNCNAME ($$): starting with sessionid = $sessionid" &
	port=$(getparam port)
	username=$(getparam username)
	password=$(getparam password)
	share=$(getparam share)
	computername=$(getparam computername)
	dir=$(getparam dir | sed 's|$(SHARES)|MyShares|g')

	# normalize '
	username=$(norm_param $username)
    	password=$(norm_param $password)
	share=$(norm_param $share)
	dir=$(norm_param $dir)
	computername=$(norm_param $computername)
	
	# http://bugs.etersoft.ru/show_bug.cgi?id=8841
	computername=127.0.0.1

	[ -n "$(echo "$dir" | grep MyShares)" ] && dir="$HOME/$dir"

	# dimbor: checking mountpoint
	EGROUP=$(id -gn "$USER"); RC=$?
	outstr="$(create_mountpoint "$dir" "$USER" "$EGROUP")"
	[ -n "$outstr" ] && nxlog "$FUNCNAME ($$): create_mountpoint says: \"$outstr\"" &
	[ $RC -ne 0 ] && { nxlog "$FUNCNAME ($$): Stop mounting." &
	    return 1; }
	
	smbport=""; smbport=$(cat "$sess_dir/scripts/smbport" 2>/dev/null)
	IDS_OPTS="uid=$USER,gid=$EGROUP,";

	if [ "$smbport" = "445" ] ; then # cifs
	    TRY_USER=$(usermount_enable)
	    TRY_SUDO=$(sudomount_enable)
	    [ $TRY_USER -eq 0 -a $TRY_SUDO -eq 0 ] && { nxlog "$FUNCNAME ($$): Any methods of (u)mounting are not available. Stop mounting." &
		return 1; }
	elif [ "x$smbport" = "x" ] ; then
	    COMMAND_SMBMOUNT_CIFS=/bin/true
	fi
	
	#do not mount twice
	if [ -n "$(mount | grep "$dir")" ] ; then
    	    nxlog "$FUNCNAME ($$): share $(mount | grep "$dir" | awk '{print($1)}') already mounted on $dir" &
	    if [ "$ENABLE_SHARE_MULTIMOUNT" = "1" ] ; then
		if [ -z "$(get_sessdir4res "@s@$share")" ] ; then
		    nxlog "$FUNCNAME ($$): Try to force umount dead mountpoint \"$dir\"" &
		    try_umount_smb "$dir"; RC=$?
    		    [ $RC -ne 0 ] && { nxlog "$FUNCNAME ($$): Umounting FAILED! Stop mounting." &
    			return 1; }
		else
		    touch "$sess_dir/scripts/@s@$share"; return;
		fi
	    else
		echo "$dir" >> "$sess_dir/scripts/mpoint"
		return
	    fi
	fi
	# wait up to 30 sec until nxagent has opened the listener port for samba connections
	for (( i=30; $i; --i )) ; do
	    [ -f "$sess_dir/scripts/mport" ] && break
	    sleep 1
	done
	# sometimes the samba port we get from nxserver is not the right one, so let's get it from nxagent
	realport=$(cat "$sess_dir/scripts/mport")
	[ -n "$realport" ] && port=$realport
	try_mount_smb "//$computername/$share" "$dir" "$username" $port "$IDS_OPTS" "$password"
	RC=$?
	if [ $RC -eq 0 ] ; then
	    if [ "$ENABLE_SHARE_MULTIMOUNT" = "1" ] ; then
    		nxlog "$FUNCNAME ($$): share \"//$computername/$share\" mount on \"$dir\"" &
		SFN="@s@$share"
		touch "$sess_dir/scripts/$SFN"
		desc_dir="$USER_FAKE_HOME/.nx/shares_priv" # directory with connection parameters to use sessions together
		[ -d "$desc_dir" ] || { mkdir "$desc_dir" && chmod 700 "$desc_dir"; }
		[ ! -e "$desc_dir/$SFN" ] && touch "$desc_dir/$SFN" && chmod 600 "$desc_dir/$SFN" # create "top-secret" description file
		echo "//$computername/$share&$dir&$username&$password&$IDS_OPTS" > "$desc_dir/$SFN"
	    else # not multimount case
    		nxlog "$FUNCNAME ($$): share \"//$computername/$share\" mount on \"$dir\"" &
		$PATH_BIN/nxdialog -dialog ok -caption "NXServer Message" -message "Info: Share: '//$computername/$share' mounted on: '$dir'" -display :$display &
	    fi
	    echo "$dir" >> "$USER_FAKE_HOME/.nx/C-$SERVER_NAME-$display-$sessionid/scripts/mpoint"
	else
	    nxlog "$FUNCNAME ($$): Share //$computername/$share mount FAILED." &
	    rmdir "$dir" >/dev/null 2>/dev/null #JJK: Remove mount point if empty
	    [ "$ENABLE_SHARE_MULTIMOUNT" = "1" ] || $PATH_BIN/nxdialog -dialog ok -caption "NXServer Message" -message "Info: Share: '//$computername/$share' failed to mount." -display :$display &
	fi
}

cmd_node_addprinter()
{
	nxlog "$FUNCNAME ($$): starting" &
	sessionid=$(getparam_sessionid)
	type=$(getparam type)
	port=$(getparam port)
	username=$(getparam username)
	password=$(getparam password)
	share=$(getparam share)
	printer=$(getparam printer)
	computername=$(getparam computername)
	public=$(getparam public)
	model=$(getparam model)
	defaultPrinter=$(getparam defaultPrinter)
	display=$(cd $USER_FAKE_HOME/.nx/; echo C-$SERVER_NAME-*-$sessionid | awk 'BEGIN {FS="-"} {i=NF-1; print $i}')
	sess_id="$SERVER_NAME-$display-$sessionid"

	# normalize
	username=$(norm_param $username)
	password=$(norm_param $password)
	share=$(norm_param $share)
	computername=$(norm_param $computername)

	# this will also setup the userspace cupsd
	export CUPS_SERVER=$(node_cupsd_get_socket)

	if [ "$type" = "smb" ]
	then
	    if [ -x "$CUPS_BACKEND/nxsmb" ] ; then
		smbport=$(cat "$USER_FAKE_HOME/.nx/C-$sess_id/scripts/smbport" 2>/dev/null)
		[ -z "$smbport" ] && smbport=139 # should not happen
		if [ "$smbport" = "445" ] ; then smbproto="cifs"
		else smbproto="smb"
		fi
		DEVICE_URI="nxsmb://$username:$password@127.0.0.1:$port/$smbproto/$share"
	    else
		DEVICE_URI="smb://$username:$password@127.0.0.1:$port/$share"
	    fi
	    NAME="$share"
	    ENABLE_CUPS_SEAMLESS="0"
	    nxlog "$FUNCNAME ($$): set NAME = $NAME; smbport=$smbport; DEVICE_URI=$DEVICE_URI" &
	else
	    printer=$(norm_param $printer)
	    DEVICE_URI="ipp://$username:$password@127.0.0.1:$port/printers/$printer"
	    NAME="$printer"
	    nxlog "$FUNCNAME ($$): set NAME = $NAME; DEVICE_URI=$DEVICE_URI" &
	fi

#JJK: I like to also allow 'guest' so you can do things like print
#JJK: testpages from the CUPS web interface. Note this is required
#JJK: even for the original user to print test pages
#JJK:	PUBLIC="-u allow:$USER"
	PUBLIC="-u allow:$USER,guest,root"
	[ "$public" == "1" ] && PUBLIC=""
	export PATH_BIN
	USER_PPD_DIR="$USER_FAKE_HOME/.nx/config/ppd"
	mkdir -p "$USER_PPD_DIR"
	nxlog "$FUNCNAME ($$): run with ENABLE_CUPS_SERVER_MODE=$ENABLE_CUPS_SERVER_MODE" &
    # dimbor: We use system cupsd
    if [ "$ENABLE_CUPS_SERVER_MODE" = "1" ] ; then
	# dimbor: Name of windows share or ipp-printer may be set to name
	# of driver.ppd from $NX_ETC_DIR/ppd/ (without ".ppd"). Also checks
	# $USER_FAKE_HOME/.nx/config/ppd/  For ipp if driver not found
	# we try it to download like oriiginal behaviour.
	# In case of ENABLE_FOOMATIC="1" share/printer name may set to
	# [@]<printer id>[#<N>][__<driver>]
	# <printer id>: printer id from foomatic db.
	# <N>:Number of printer instance
	# "__": char "_" twice
	# <driver>: driver from foomatic db.
	# @: if sharename started with char "@", force setup to anyone
	# share name  printer id "HP-LaserJet_4M" and driver "Postscript-HP"
	# http://www.nixp.ru/articles/win_printing_in_linux (hello to DjSpike)
	MODEL="$NX_ETC_DIR/ppd/$NAME.ppd"
	[ -r "$MODEL" ] || { # check cached users driver
	    MODEL="$USER_PPD_DIR/$NAME.ppd"
	    [ -r "$MODEL" ] && [ -n "$(head -n1 "$MODEL" | grep "*PPD-Adobe")" ] || rm -f "$MODEL"
	}
	[ "$type" = "smb" ] || [ -r "$MODEL" ] || {
	    # try to download "left-nx" ppd (original scheme), needs to use
	    # wrapper for nxprint on client side
	    curl --proxy "" --digest -o "$MODEL" "http://$username:$password@127.0.0.1:$port/ppd/${NAME}_nxdl.ppd" 2>&1 > /dev/null
	    [ -r "$MODEL" -a -n "$(head -n1 "$MODEL" 2>/dev/null | grep "*PPD-Adobe")" ] && {
		nxlog "$FUNCNAME ($$): $MODEL download SUCCESSFUL."
	    } || { nxlog "$FUNCNAME ($$): $MODEL is NOT a PPD-file! Contents: \"$(cat $MODEL 2>/dev/null)\""
		rm -f "$MODEL"
	    }
	}
	if [ -r "$MODEL" ] ; then
	    PNAME="$USER""_""$NAME" # user's printer name
	    nxlog "$FUNCNAME ($$): set PNAME = \"$PNAME\", try to install \"$MODEL\"" &
	elif [ "$ENABLE_FOOMATIC" != "0" ] ; then  # calculated
	    PRE="$(echo "$NAME" | grep "@")"
	    HASDRV="$(echo "$NAME" | grep "__")"
	    HASNN="$(echo "$NAME" | grep "#")"
	    ID="$NAME"
	    NN=""
	    [ -n "$HASDRV" ] && ID="$(echo "$NAME" | awk 'BEGIN {FS="__"} {print $1}')"
	    if [ -n "$HASNN" ] ; then
	        NN="_""$(echo "$ID" | cut -d"#" -f2)"
	        ID="$(echo "$ID" | cut -d"#" -f1)"
	    fi
	    if [ -n "$HASDRV" ] ; then
		DRV="$(echo "$NAME" | awk 'BEGIN {FS="__"} {i=NF; print $i}')"
	    elif [ -z "$PRE" ] ; then
		drvs="$(/usr/bin/sudo /usr/bin/foomatic-configure -X -p "$ID" -q)"
		[ $? -ne 0 ] && { nxlog "$FUNCNAME ($$): FAILED getting list of drivers from foomatic db for printer id \"$ID\". Printer not installed." &
		    return 1; }
		DRV="$(echo "$drvs" | grep "<driver>" | head -1 | cut -d">" -f2 | cut -d"<" -f1)"
		[ -z "$DRV" ] && { nxlog "$FUNCNAME ($$): Driver for printer id \"$ID\" NOT FOUND in foomatic db. Printer not installed." &
		    return 1; }
	    fi
	    if [ -z "$PRE" ] ; then
		PNAME="$USER""_""$(echo $ID | awk 'BEGIN {FS="_"} {i=NF; print $i}')$NN" # user's printer name
	    else
		PNAME="$USER""_""$(echo $ID | cut -d"@" -f2 | awk 'BEGIN {FS="_"} {i=NF; print $i}')$NN" # user's printer name
		ID="HP-LaserJet_4M"
		DRV="Postscript-HP"
	    fi
	    nxlog "$FUNCNAME ($$): set PNAME = \"$PNAME\", try foomatic ID = \"$ID\"; DRV =\"$DRV\"" &
	else
	    nxlog "$FUNCNAME ($$): Name of driver for \"$NAME\" not defined. STOP installing." &
	    return 1
	fi
	PFN="@p@$PNAME" # filename of flag and description files
	sess_dir="$USER_FAKE_HOME/.nx/C-$sess_id"
	desc_dir="$USER_FAKE_HOME/.nx/shares_priv" # directory with connection parameters to use sessions together
	[ -d "$desc_dir" ] || { mkdir "$desc_dir" && chmod 700 "$desc_dir"; }
	[ "$type" = "smb" ] && {
	    for (( i=30; $i; --i )) ; do
		nxlog "$FUNCNAME ($$): wait for smb-port $((30-i))"
		[ -f "$sess_dir/scripts/mport" ] && break
		sleep 1
	    done
	} || share=$printer
	# check existing printers
	pstr="$(LC_ALL=C /usr/bin/lpstat -v 2>/dev/null| grep "$PNAME")"
	if [ -n "$pstr" ] ; then # printer present
	    iport=$(echo "$pstr" | cut -d/ -f3 | cut -d: -f2)
	    [ "$iport" -gt 0 2>/dev/null ] && drop=0 || { drop=1; dbs="wrong port"; }
	    if [ "$drop" = "0" ] ; then
	     [ "$(check_remote_printer $type $share $iport $username $password)" = "0" ] && { drop=1; dbs="not accessible"; }
	    fi
	    [ "$drop" = "0" ] && [ "$type" = "ipp" -o "$iport" = "$port" ] && { drop=1; dbs="for reinstall"; }
	    if [ "$drop" = "1" ] ; then
		nxlog "$FUNCNAME ($$): delete printer \"$PNAME\" (port:$iport) - $dbs" &
		/usr/bin/sudo /usr/sbin/lpadmin -x "$PNAME"
	    else
		nxlog "$FUNCNAME ($$): printer \"$PNAME\" already exist at port:$iport - stop adding, mark only" &
		echo $password > "$sess_dir/scripts/$PFN"; chmod 600 "$sess_dir/scripts/$PFN"
		return 0
	    fi
	fi
	[ "$(check_remote_printer $type $share $port $username $password)" = "0" ] && {
	    nxlog "$FUNCNAME ($$): \"$share\" (mport:$port) NOT ACCESSIBLE stop adding." &
	    return 1; }
	echo $password > "$sess_dir/scripts/$PFN"; chmod 600 "$sess_dir/scripts/$PFN" # set share present flag
	if [ -r "$MODEL" ] ; then
	    [ ! -e "$desc_dir/$PFN" ] && touch "$desc_dir/$PFN" && chmod 600 "$desc_dir/$PFN" # create "top-secret" description file
	    echo "$MODEL&$DEVICE_URI&$PUBLIC&$defaultPrinter" > "$desc_dir/$PFN" # confidential access data
	    CMDSTR="/usr/bin/sudo /usr/sbin/lpadmin -p $PNAME -P $MODEL -v $DEVICE_URI -E"
	elif [ "$ENABLE_FOOMATIC" != "0" ] ; then # foomatic printer is here
	    [ ! -e "$desc_dir/$PFN" ] && touch "$desc_dir/$PFN" && chmod 600 "$desc_dir/$PFN" # create "top-secret" description file
	    echo "$ID&$DEVICE_URI&$PUBLIC&$defaultPrinter&$DRV" > "$desc_dir/$PFN" # confidential access data
	    CMDSTR="/usr/bin/sudo /usr/bin/foomatic-configure -s cups -n $PNAME -p $ID -d $DRV -c $DEVICE_URI -q 1>/dev/null 2>&1"
	else
	    nxlog "$FUNCNAME ($$): $MODEL driver not found! Printer $NAME not installed." &
	    return 1
	fi
	nxlog "$FUNCNAME ($$): invoke $CMDSTR" &
	error="$($CMDSTR 2>&1)"
	if [ $? -eq 0 ] ; then
	    nxlog "$FUNCNAME ($$): printer \"$PNAME\" installed" &
	    [ -n "$PUBLIC" ] && { /usr/bin/sudo /usr/sbin/lpadmin -p $PNAME $PUBLIC;
		[ $? -ne 0 ] && nxlog "$FUNCNAME ($$): FAILED to set options \"$PUBLIC\" for printer \"$PNAME\"" &
	    }
	    [ "$defaultPrinter" = "1" ] && { /usr/bin/sudo /usr/sbin/lpadmin -d "$PNAME";
		[ $? -ne 0 ] && nxlog "$FUNCNAME ($$): FAILED to set default printer \"$PNAME\"" &
	    }
	else
	    nxlog "$FUNCNAME ($$): FAILED to install printer \"$PNAME\": $error" &
	fi
   else # original behaviour
	mkdir -p "$USER_FAKE_HOME/.nx/config/ppd/"

	if [ "$ENABLE_CUPS_SEAMLESS" != "1" ]
	then
        #JJK: Export the following variables for use by nxdialog/nxprint
        #JJK: Note they are also exported in nxdialog but doesn't help there
	    export ENABLE_FOOMATIC COMMAND_FOOMATIC PPD_DIR
		MODEL=$($PATH_BIN/nxdialog -printer "$NAME" -display :$display)
		[ -z "$MODEL" -o "$MODEL" = "cancel: aborted" ] && return
	else
		sleep $CUPS_SEAMLESS_DELAY
		MODEL="download_cached"
	fi
	
	if [ "$MODEL" = "download_new" -o "$MODEL" = "download_cached" ]
	then
		if [ "$MODEL" = "download_new" -o ! -r "$USER_FAKE_HOME/.nx/config/ppd/$printer.ppd" ]
		then
			curl --proxy "" --digest -o "$USER_FAKE_HOME/.nx/config/ppd/$printer.ppd" "http://$username:$password@127.0.0.1:$port/ppd/${printer}_nxdl.ppd" >>"$USER_FAKE_HOME/.nx/C-$sess_id/cups/curl.log" 2>&1
		RC=$?
		[ $RC -eq 0 -a "$ENABLE_CUPS_SEAMLESS" != "1" ] && $PATH_BIN/nxdialog --message "Download successful." -display :$display
		[ $RC -ne 0 -a "$ENABLE_CUPS_SEAMLESS" != "1" ] && $PATH_BIN/nxdialog --message "Download failed. Log is: $USER_FAKE_HOME/.nx/C-$sess_id/cups/curl.log" -display :$display
		fi
		MODEL="$USER_FAKE_HOME/.nx/config/ppd/$printer.ppd"
	fi
	
	if [ ! -r "$MODEL" ]
	then
		# Foomatic and co
		/usr/sbin/lpadmin -p "$NAME" -E -v "$DEVICE_URI" -m "$MODEL" $PUBLIC
	else
		/usr/sbin/lpadmin -p "$NAME" -E -v "$DEVICE_URI" -P "$MODEL" $PUBLIC
	fi

	[ "$defaultPrinter" = "1" ] && /usr/sbin/lpadmin -d "$NAME"
   fi # end of original
}

nxnode_func()
{

case "$1" in
	--startsession)
		startsession
	;;
	--resumesession)
		startsession "restore"
	;;
	--applicationsession)
		startsession "application"
	;;
	--terminate)
		cmd_node_terminate
	;;
	--suspend)
		cmd_node_suspend
	;;
	--smbmount)
		cmd_node_smbmount
	;;
	--addprinter)
        	cmd_node_addprinter
	;;
	--check)
		echo "NX> 716 finished"
	;;
	--agent)
		echo "NX> 716 Starting NX Agent ..."
		shift
		[ "$SET_LD_LIBRARY_PATH" = "1" ] && export LD_LIBRARY_PATH="$AGENT_LIBRARY_PATH:$LD_LIBRARY_PATH"
		PATH="$PATH:$PATH_BIN" $COMMAND_NXAGENT -name "NX Agent Test - Args: $@" $@
		echo "NX> 716 NX Agent exited with status: $?"
	;;
	--setkey)
		mkdir -m 700 -p $HOME/.ssh
		if ! grep -q "$(cat $NX_ETC_DIR/users.id_dsa.pub)" $HOME/.ssh/$SSH_AUTHORIZED_KEYS 2>/dev/null
		then
			cat $NX_ETC_DIR/users.id_dsa.pub >> $HOME/.ssh/$SSH_AUTHORIZED_KEYS
			chmod 600 $HOME/.ssh/$SSH_AUTHORIZED_KEYS
			echo "NX> 716 Public key added to: $HOME/.ssh/$SSH_AUTHORIZED_KEYS"
		else
			echo "NX> 716 Public key is already present in: $HOME/.ssh/$SSH_AUTHORIZED_KEYS"
		fi
	;;
	*)
		echo "NX> 500 Error: Command not found"
	;;
esac

}


if [ "$1" = "--slave" ]
then
	# New slave mode accepts more than 1 command at a time
	echo "NX> 716 Slave mode started successfully."
	NODE_SESSION=""

	while read CMD
	do
		[ "$CMD" = "--quit" ] && break
		
		if [ "$CMD" != "--check" -a "$CMD" != "--setkey" -a "$CMD" != "--agent" ]
		then
			read CMDLINE

			CMDLINE="a=b&$CMDLINE"
		fi
		
		if [ "$CMD" = "--startsession" -o "$CMD" = "--resumesession" ]
		then
			export SLAVE_PID="$$"
			( echo "$CMDLINE" | "$0" "$CMD"; kill "$SLAVE_PID"; ) &
			NODE_SESSION="$!"
		else
			DELIM=""
			[ -n "$NODE_SESSION" ] && DELIM="$NODE_SESSION->$CMD"
			( echo "$CMDLINE" | "$0" "$CMD" "$DELIM"; ) &
		fi

	done
else
	nxnode_func "$@"
fi

echo "$DELIM 1001 Bye."
