#!/usr/bin/python

import os
import sys
sys.path.append('/usr/lib/ltsp/python')
import subprocess
import signal
import socket
import struct
import fcntl
import tempfile

def get_config(name):
    return os.environ.get(name)

def get_config_bool(name):
    allowed = ['y', 'true', 'yes']
    val = os.environ.get(name)
    for valid in allowed:
        if (val and val.lower() == valid):
            return True
    return False

def get_ip(ifname):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    return socket.inet_ntoa(fcntl.ioctl(
        s.fileno(),
        0x8915,  # SIOCGIFADDR
        struct.pack('256s', ifname[:15])
    )[20:24])

class LDM:
    def __init__(self, vt, display):
        self.vt = vt
        self.display = display
        self.server = get_config('SERVER')
        self.use_xfs = get_config_bool('USE_XFS')
        self.use_sound = get_config_bool('SOUND')
        self.use_localdev = get_config_bool('LOCALDEV')
        self.override_port = get_config('SSH_OVERRIDE_PORT')

        if self.use_sound:
            self.ip = get_ip('eth0')
            self.sound_daemon = get_config('SOUND_DAEMON') or 'pulse'

        if self.use_xfs:
            xfs_server = get_config('XFS_SERVER') or self.server
            self.fontpath = "tcp/%s:7100" % xfs_server
	

        # Save xauth info some writable tmpfs file.
        self.authfile = tempfile.mktemp()

    def run(self):
        null = open('/dev/null', 'w')
        logfile = open('/var/log/ldm.log', 'a')
        
        os.dup2(null.fileno(), sys.stdin.fileno())
        os.dup2(logfile.fileno(), sys.stdout.fileno())
        os.dup2(logfile.fileno(), sys.stderr.fileno())

        while True:
            server_opts = ['-br', '-ac', '-noreset']
            
            if self.use_xfs:
                server_opts += ['-fp', self.fontpath]

            server_command = ['X', '-auth', self.authfile] + server_opts + [self.vt, self.display]
            server = subprocess.Popen(server_command, stdout=sys.stdout, stderr=sys.stderr)
            
            env = os.environ.copy()
            env['DISPLAY'] = self.display
            env['XAUTHORITY'] = self.authfile

            # Generate an X authority key in ~/.Xauthority
            xauth_command = ['xauth', '-i', '-n', '-f', self.authfile, 'generate', self.display]
            xauth = subprocess.Popen(xauth_command, stdout=sys.stdout, stderr=sys.stderr, env=env)
            os.waitpid(xauth.pid, 0)

            greeter = subprocess.Popen(['/usr/lib/ltsp/greeters/gtk'],
                                       stdout=subprocess.PIPE, stderr=sys.stderr, env=env)
            greeter_output = greeter.stdout.readlines()
            if len(greeter_output) == 4:
                username = greeter_output[0][:-1]
                password = greeter_output[1][:-1]
                lang = greeter_output[2][:-1]
                selected_session = greeter_output[3][:-1]

                self.spawn_session(username, password, lang, selected_session)
            else:
                print "Didn't get the right output from the greeter"

            os.kill(server.pid, signal.SIGTERM)
            os.waitpid(server.pid, 0)

    def spawn_session(self, username, password, lang, selected_session):
        pipe_read, pipe_write = os.pipe()
        pid = os.fork()

        if pid == 0:
            os.close(pipe_write)
            sys.stdin.close()
            os.setsid()
            os.environ['DISPLAY'] = self.display
            os.environ['XAUTHORITY'] = self.authfile
            os.environ['LDM_ASKPASS_FD'] = str(pipe_read)
            os.environ['SSH_ASKPASS'] = '/usr/lib/ltsp/ldm-askpass'

            ssh_opts = ['-v',
                       '-X',
                       '-c', 'blowfish-cbc,aes128-cbc,3des-cbc']

            if self.override_port:
                ssh_extra_port=['-p', self.override_port]
                ssh_opts.extend(ssh_extra_port)

            if self.use_localdev:
                       localdev_ssh_opts=['-M',
                          '-S', '/tmp/.ltspfs-socket']
                       ssh_opts.extend(localdev_ssh_opts)

            ssh_auth = ['%s@%s' % (username,self.server)]

            if self.use_sound:
                espeakerport = 16001
                if self.sound_daemon == 'esd':
                    print "info: Enabling esd sound support."
                    # Could start using esddsp, but it is not enabled
                    # because it gave problems when using firefox.
                    #   esddsp', '-m','--server=%s:16001' % (self.ip)
                    sound_cmd = ['ESPEAKER=%s:%d' % (self.ip, espeakerport)]
                    # Make sure esd is ready to accept new
                    # connections/clients.  It need to be reset to
                    # forget the previous users authentication key.
                    # A native python implementation is probably
                    # better. [pere 2006-02-24]
                    os.system("killall esd")
                    os.system("/usr/bin/esd -nobeeps -public -tcp &")
                elif self.sound_daemon == 'pulse':
                    print "info: Enabling PulseAudio sound support."
                    pulseport = 4713
                    sound_cmd = ['PULSE_SERVER=tcp:%s:%d ESPEAKER=%s:%d' % (self.ip, pulseport, self.ip, espeakerport)]
                            
                elif self.sound_daemon == 'nasd':
                    print "info: Enabling nasd sound support."
                    # Could use the audiooss package to set LD_PRELOAD.
                    # Not enabled by default to avoid problems with
                    # firefox.
                    sound_cmd = ['AUDIOSERVER=%s:0' % (self.ip)]
                else:
                    print "error: Unsupported sound daemon: '%s'" % \
                          (self.sound_daemon)
                    sound_cmd = []
            else:
                print "info: Not enabling sound support."
                sound_cmd = []

            # select the right session command
            session_manager = get_config('LDM_REMOTECMD') or '/etc/X11/Xsession'

            if selected_session != 'None' and not get_config('LDM_REMOTECMD'):
                session_manager = session_manager+' '+selected_session

            # make sure we clean up after logout if localdev is used
            if self.use_localdev:
                session_manager = session_manager+' && ltspfsmounter all cleanup'
                print "info: Enabling localdev support."
                if os.access("/var/run/.static-device", os.F_OK):
                    print "info: Enabling static localdev support."
                    static_dev = open("/var/run/.static-device", "r")
                    delayed_mnt = open("/var/run/.delayed-mount", "a")
                    delayed_mnt.writelines(static_dev.readlines())
                    delayed_mnt.close()
                    static_dev.close()

            # set language
            if lang != 'None':
                lang_command = 'LC_ALL='+lang+' LANGUAGE='+lang+' LANG='+lang
            else:
                lang_command = ''
            # Make sure to run a login shell, to get /etc/profile executed
            # and the full PATH content set.  Use bash to make sure the
            # shell used support the --login argument.
            ssh_remote_command = ['/bin/bash',
                                  '--login',
                                  '-c',
                                  " ".join( ['\'',
                                    'env',
									lang_command,
                                    'LTSP_CLIENT="%s"' % (socket.gethostname()),
                                    ] + sound_cmd + [
                                    session_manager,
                                    '\''] ),
                                  ';',
                                  'kill -1 $PPID']

            if 'NETWORK_COMPRESSION' in os.environ:
                ssh_opts.append('-C')
            
            command = ['ssh'] + ssh_opts + ssh_auth + ssh_remote_command
            print "ssh command line:", command
            sys.stdout.flush()

            os.execvp('ssh', command)
            sys.exit(1)

        os.close(pipe_read)
        os.write(pipe_write,password)
        os.close(pipe_write)
        os.waitpid(pid, 0)

if len(sys.argv) < 3:
    sys.stderr.write('Usage: ldm <vt[1-N]> <:[0-N]>\n')
    sys.exit(1)

vt, display = sys.argv[1:]
ldm = LDM(vt, display)
ldm.run()
