#!/usr/bin/env python
# -*- coding: UTF-8 -*-
##########################################################################
# Tcos writen by MarioDebian <mariodebian@gmail.com>
#
#    tcos-gdm-autologin
#
# Copyright (c) 2006 Mario Izquierdo <mariodebian@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.
###########################################################################


"""
Usage:

  *  from GDM this app is called to retrieve username based on hostname

  *  Exec standalone, configure GDM /etc/gdm/gdm.conf file with ThinClient values
  
"""


import os, sys
import getopt
from subprocess import Popen, PIPE, STDOUT
from socket import gethostbyaddr as gethostname
import pwd

GDM_CONF_FILE="/etc/gdm/gdm.conf"
# try to edit correct file (Ubuntu use -custom file)
if os.path.isfile(GDM_CONF_FILE + "-custom"):
    GDM_CONF_FILE=GDM_CONF_FILE+"-custom"

if os.path.isfile("/etc/gdm/gdm-cdd.conf"):
    GDM_CONF_FILE="/etc/gdm/gdm-cdd.conf"

# in new Ubuntu create empty file and use it
if os.path.isfile("/etc/gdm/gdm.schemas") and not os.path.isfile("/etc/gdm/custom.conf"):
    # create empty file
    f=open("/etc/gdm/custom.conf", 'w')
    f.write('\n')
    f.close()

# new GDM in Ubuntu Karmic
if os.path.isfile("/etc/gdm/custom.conf"):
    GDM_CONF_FILE="/etc/gdm/custom.conf"

# in Debian unstable GDM is installed in /etc/gdm3 create empty file and use it
if os.path.isdir("/etc/gdm3") and not os.path.isfile("/etc/gdm3/daemon.conf"):
    # create empty file
    f=open("/etc/gdm3/daemon.conf", 'w')
    f.write('\n')
    f.close()

# new GDM in Debian unstable
if os.path.isfile("/etc/gdm3/daemon.conf"):
    GDM_CONF_FILE="/etc/gdm3/daemon.conf"

HOME_TEMPLATE="/root/skel.tar.gz"
USER_GROUPS=["fuse"]


debug=True
configure=False
add=False
delete_home=True
noaction=False
local=""

# this is the values needed to connect to GDM from thin clients
TCOS_GDM={
"daemon":[
    {"TimedLoginEnable":"true"},
    {"TimedLogin":"/usr/sbin/tcos-gdm-autologin|"},
    {"TimedLoginDelay":"5"}
    ],
"security":[
    {"AllowRemoteAutoLogin":"true"},
    {"DisallowTCP":"false"}
    ],
"xdmcp":[
    {"Enable":"true"},
    {"MaxPending":"60"},
    {"MaxWaitIndirect":"80"},
    {"MaxWait":"75"},
    {"PingIntervalSeconds":"35"},
    {"MaxPendingIndirect":"30"},
    {"MaxSessions":"40"},
    {"DisplaysPerHost":"4"},
    ]
}

def print_debug(txt):
    if debug:
        try:
            f=open('/var/log/tcos-gdm-autologin.log', 'a')
            f.write("[%s] %s::%s\n" %(os.getpid(), "tcos-gdm-autologin", txt) )
            f.close()
        except:
            sys.stderr.write("%s::%s\n" %("tcos-gdm-autologin", txt))
        ##print "%s::%s" %("tcos-gdm-autologin", txt)

def usage():
    print "tcos-gdm-autologin help:"
    print ""
    print "    [no options]  (write username to login, used from GDM)"
    print "    -d [--debug]  (write debug data to stdout)"
    print "    -h [--help]   (this help)"
    print "    --configure   (configure gdm with ThinClient values)"
    print "    --skel=/foo/bar/file.tar.gz (skel template, default %s)" %(HOME_TEMPLATE)
    print "    --add         (add users and hostnames to system files)"
    print "    --nodelete    (NOT delete HOME and uncompress HOME_TEMPLATE)"
    print "    --noaction    (NOT perform any action, enable debug)"


try:
    opts, args = getopt.getopt(sys.argv[1:], ":hd", 
        ["help", "debug", "configure", "skel=", "add", "nodelete", "noaction", "local="])
except getopt.error, msg:
    print msg
    print "for command line options use tcos-gdm-autologin --help"
    sys.exit(2)

# process options
for o, a in opts:
    if o in ("-d", "--debug"):
        #print "DEBUG ACTIVE"
        debug = True
    if o == "--configure":
        configure=True
        
    if o == "--add":
        add=True
        
    if o == "--nodelete":
        delete_home=False

    if o == "--noaction":
        noaction=True
        debug=True
        
    if o == "--skel":
        HOME_TEMPLATE=a

    if o == "--local":
        local=a

    if o in ("-h", "--help"):
        usage()
        sys.exit(0)


print_debug("--------------------------------------------------------")
if "DISPLAY" in os.environ:
    print_debug( "DISPLAY=%s"%(os.environ['DISPLAY']) )
else:
    print_debug("DISPLAY not set")



########################################################
#
#  Extends python-configobj class to use '=' instead of  ' = ' 
#
########################################################
from configobj import ConfigObj
class MyConfigObj (ConfigObj):

    def _write_line(self, indent_string, entry, this_entry, comment):
        """Write an individual line, for the write method"""
        # NOTE: the calls to self._quote here handles non-StringType values.
        if not self.unrepr:
            val = self._decode_element(self._quote(this_entry))
        else:
            val = repr(this_entry)
        return '%s%s%s%s%s' % (
            indent_string,
            self._decode_element(self._quote(entry, multiline=False)),
            self._a_to_u('='),
            val,
            self._decode_element(comment))




######## some functions #############

def execmd(cmd):
    if not noaction:
        print_debug(cmd)
    try:
        if not noaction:
            os.system(cmd)
        else:
            print_debug("NOACTION: execmd() cmd=%s" %(cmd) )
    except:
        print("Error while exec: \"%s\"" %(cmd) )


def is_ip(host):
    try:
        if len( host.split('.') ) == 4:
            print_debug("is_ip: host=%s seems to be an IP address" %(host) )
            return True
        return False
    except:
        return False
    
def show_login():
    username=None
    host=None
    home=None
    display=None
    # FIXME use tcosmonitor.shared.parseIPAddress
    host, display =  os.environ["DISPLAY"].rsplit(':', 1)
    if host == "":
        print_debug ( "localdisplay return local='%s'"%local )
        print "%s"%local
        sys.exit(0)
    if is_ip(host):
        try:
            host=gethostname(host)[0]
            print_debug("show_login() convert IP to hostname host=%s" %(host))
        except:
            print_debug("show_login() error converting IP to hostname")
    
    
    try:
        data=pwd.getpwnam(host)
    except:
        print "no_username_configured_for_host_%s" %(host)
        print_debug("ERROR, host (%s) not found in users." %(host))
        sys.exit(1)
    
    home=data.pw_dir
    username=data.pw_name
    
    # kill all user processes
    execmd("pkill -9 -u %s" %(username))
    if delete_home:
        restore_home(home, username)
    else:
        print_debug("Not restoring home...")
    print_debug("show_login() returning '%s'"%username)
    print username    

def read_exe_output(cmd, verbose=0):
    print_debug("read_exe_output() cmd='%s'"%cmd)
    p = Popen(cmd, shell=True, bufsize=0, stdout=PIPE, stderr=STDOUT, close_fds=True)
    output=[]
    for line in p.stdout.readlines():
        if line != '\n':
            line=line.replace('\n', '')
            output.append(line)
    print_debug("read_exe_output() output=%s"%output)
    if len(output) == 0:
        return ""
    elif len(output) >= 1:
        if verbose==1:
            print_debug ( "read_exe_output(%s) %s" %(cmd, output) )
        return output
    return []


def restore_home(home, username):
    if noaction:
        print_debug("NOACTION: restore_home() home=%s username=%s" %(home,username) )
        return
    
    if not os.path.isfile(HOME_TEMPLATE):
        print_debug ( "skel don't exists: %s" %(HOME_TEMPLATE) )
        return

    # umount all fuse devices
    if int(read_exe_output("grep -c \"%s\" /proc/mounts" %(home) )[0] ) > 0:
        print_debug("user %s have fuse mounted devices" %(username) )
        for mount_dev in read_exe_output("grep \"%s\" /proc/mounts | awk '{print $2}'" %(home) ):
            print_debug("umounting %s" %(mount_dev) )
            execmd("fusermount -u %s" %(mount_dev) )

    execmd("rm -rf %s 2>/dev/null" %(home) )
    try:
        os.mkdir(home)
    except:
        pass
    execmd("tar -zxf %s -C %s 2>/dev/null" %(HOME_TEMPLATE, home) )
    execmd("chown -R %s:%s %s 2>/dev/null" %(username, username, home) )
    
    
def SetVar(section, key, value):
    GDMCONF=GDM_CONF_FILE
    # try to edit correct file (Ubuntu use -custom file)
    if os.path.isfile(GDM_CONF_FILE + "-custom"):
        GDMCONF=GDM_CONF_FILE+"-custom"

    if noaction:
        print_debug("NOACTION: SetVar() gdm.conf=%s section=%s key=%s value=%s" 
                    %(GDMCONF, section,key,value) )
        return
        
    config=MyConfigObj( os.path.realpath(GDMCONF) )
    print_debug("setting section=[%s] key=%s value=%s" %(section, key, value) )
    config[section][key] = value
    try:
        config.write()
    except:
        print "Error, can't write in %s" %(GDMCONF)

def SetAutologin():
    for section in TCOS_GDM:
        for var in TCOS_GDM[section]:
            for key in var:
                value=var[key]
                SetVar(section, key, value)
    print ("gdm config file edited")
    return

def AddUserAndHost():
    hosts=[]
    ips=[]
    prefix=raw_input("Please input the common prefix of hosts/users (example host): ")
    ipbase=raw_input("Need network IP Address of thin clients (example 192.168.0): ")
    number=int(raw_input("Number of host/users to generate (number): "))
    fromip=int( raw_input("Start generating from (default 1): ") )
    
    if fromip == "" : fromip = 1

    for i in range(fromip,number+fromip):
        hosts.append("%s%s" %(prefix, i))
        ips.append("%s.%s" %(ipbase, i))
    
    print ""
    print "I will generate users and hostnames from \"%s\" to \"%s\"" %(hosts[0], hosts[-1])
    print "with ip adresses from %s to %s" %(ips[0], ips[-1])
    if os.path.isfile(HOME_TEMPLATE):
        print "and uncompress \"%s\" as HOME_TEMPLATE" %(HOME_TEMPLATE)
    else:
        print "and **** I'M NOT USING ANY HOME TEMPLATE ****"
        print "Cancel now and create %s using a template user" %(HOME_TEMPLATE)
    print ""
    resp=raw_input("Are you sure to continue? [y/n] ")
    if resp != "y" and resp != "Y":
        print "cancelled."
        return
    
    for i in range(fromip,number+fromip):
        AddUser( "%s%s" %(prefix, i) )
        AddHost( "%s%s" %(prefix, i) , "%s.%s" %(ipbase, i) )
    
    print "done."

    
def AddUser(username):
    print ( "Adding user: %s" %(username) )
    execmd("useradd -m %s -p%s -s /bin/bash -d /home/%s" %(username, username, username))
    execmd("echo %s:%s | chpasswd" %(username, username))
    execmd("tar -zxf %s -C /home/%s 2>/dev/null" %(HOME_TEMPLATE, username) )
    execmd("chown %s:%s /home/%s " %(username, username, username) )
    for group in USER_GROUPS:
        execmd("adduser %s %s" %(username, group))

def AddHost(hostname, ip):
    # check if exists
    f=open("/etc/hosts", "r")
    data=f.readlines()
    f.close()
    for line in data:
        line=line.replace('\n','')
        if ip + " " in line or ip + '\t' in line:
            print ( "IP %s is in /etc/hosts" %(ip) )
            return
    try:
        print ("Adding %s %s" %(ip, hostname) )
        if noaction:
            print_debug("NOACTION: AddHost() hostname=%s, ip=%s" %(hostname,ip) )
        else:
            f=open("/etc/hosts", "a")
            f.write("%s\t%s\n" %(ip, hostname) )
            f.close()
    except:
        print "Error editting /etc/hosts, are you root?"
        pass
                

try:
    if configure:    
        SetAutologin()
    elif add:
        AddUserAndHost()
    else:
        show_login()

except KeyboardInterrupt: # Por si se pulsa Ctrl+C
    print "\nCtrl+C event, quit now..."
    sys.exit(1)



# end of file
