#!/usr/bin/env python
# -*- coding: UTF-8 -*-
##########################################################################
# TcosMonitor writen by MarioDebian <mariodebian@gmail.com>
#
#    TcosMonitor version __VERSION__
#
# 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.
###########################################################################

import sys, os
import getopt
import socket
from time import time, ctime, localtime
from subprocess import Popen, PIPE, STDOUT

import pwd,grp
import re
import ipaddr
import binascii
import IPy

import utmp
from UTMPCONST import *
"""
ACCOUNTING = 9
BOOT_TIME = 2
DEAD_PROCESS = 8
EMPTY = 0
INIT_PROCESS = 5
LOGIN_PROCESS = 6
NEW_TIME = 3
OLD_TIME = 4
RUN_LVL = 1
USER_PROCESS = 7
UT_HOSTSIZE = 256
UT_IDSIZE = 4
UT_LINESIZE = 32
UT_NAMESIZE = 32
UT_UNKNOWN = 0
"""

debug=False

def print_debug(txt):
    if debug:
        print >> sys.stderr, "tcos-last DEBUG: %s"%(txt)

def is_bin(txt):
    if txt in ['3a', '2e', '61', '62', '63', '64', '65', '66']:
        # txt is ':' or '.' or a letter between a-f
        return False
    try:
        txt=int(txt)
    except ValueError:
        # can't convert txt to int, txt is hexadecimal aka binary
        return True
    
    if txt >= 30 and txt <= 39:
        # txt is between 0(0x30) and 9(0x39)
        return False
    # return binary by default
    return True


def parseIPAddress(ipstr, return_ipv4=True):
    """
    pass an string or binary IP and return IPV4
    """
    newip=[]
    isBin=False

    if ipstr == "::1":
        # localhost in IPV6
        return "127.0.0.1"

    # GDM when change username use a $DISPLAY like this "::12"
    if re.match("::([0-9]{1,9})$", ipstr):
        return ""

    # XEPHYR NAME ':20.0'
    if re.match(":([0-9]{1,9}|[0-9]{1,9}.[0-9]{1,9})$", ipstr):
        return ""

    # match x.x.x.x:0 or x.x.x.x:0.0
    if re.match(".*:([0-9]{1,9}|[0-9{1-9}].[0-9{1-9}])$", ipstr):
        #print_debug("delete display from IP\n\n")
        ipstr=ipstr.rsplit(":", 1)[0]

    # match "localhost:10"
    if re.match("[a-zA-Z].*:([0-9]{1,9})", ipstr):
        ipstr=ipstr.rsplit(":", 1)[0]
        return ipstr

    # hostname must start with letter and contain letters numbers and '-' or '.'
    if re.match("^[a-zA-Z][a-zA-Z0-9.-]+$", ipstr):
        # ipstr is a hostname
        return ipstr

    for it in ipstr:
        eol=is_bin(binascii.hexlify(it))
        if eol:
            isBin=True
        #print_debug("%s => %s string=%s"%(it, binascii.hexlify(it), eol) )
        newip.append(binascii.hexlify(it))
    
    if ipstr == '':
        return ''
    
    if isBin:
        try:
            ip=ipaddr.IPAddress(IPy.parseAddress("0x" + "".join(newip) )[0])
        except:
            return ipstr
    else:
        try:
            ip=ipaddr.IPAddress(ipstr)
        #except Exception:
        except Exception, err:
            print_debug("      parseIPAddress() Exception, error=%s"%err)
            return ipstr
    
    ipv4=ip
    if return_ipv4 and ip.version == 6 and ip.ipv4_mapped:
        #print_debug("  IPV6 found  %s"%ip)
        ipv4=ip.ipv4_mapped.exploded
    
    return str(ipv4)



try:
    opts, args = getopt.getopt(sys.argv[1:], ":hd", ["host=", "debug", "tcosdisplay", "user", "userid", "time", "pid", "ingroup="])
except getopt.error, msg:
    print msg
    print "for command line options use tcos-last --help"
    sys.exit(2)

ACTION=None
HOST=""
INGROUP=""
# process options
for o, a in opts:
    if o == "--debug":
        debug=True
    if o == "--host":
        HOST=a
    if o == "--tcosdisplay":
        HOST=parseIPAddress(os.environ["DISPLAY"])
    
    if o == "--user":
        ACTION="user"
    elif o == "--ingroup":
        ACTION="ingroup"
        INGROUP=a
    elif o == "--userid":
        ACTION="userid"
    elif o == "--time":
        ACTION="time"
    elif o == "--pid":
        ACTION="pid"


def ipValid(ip):
    # ip is XXX.XXX.XXX.XXX
    # http://mail.python.org/pipermail/python-list/2006-March/333963.html
    try:
        xip=ip.split('.')
        if len(xip) != 4:
            return False
        for block in xip:
            if int(block) < 0 or int(block) >= 255:
                return False
        return True
    except:
        return False

    
def GetIpAddress(hostname):
    print_debug("GetIpAddress() hostname=%s "%(hostname) )
    try:
        return socket.getaddrinfo(hostname, None)[0][4][0]  
    except:
        return None


def GetHostname(ip):
    print_debug("GetHostname() ip=%s "%(ip) )
    try:
        hostname = socket.gethostbyaddr(ip)[0]
        return hostname
    except:
        return None

def GetLast(ip):
    last=[]
    data={}
    if ip != "" and not ipValid(ip):
        ip=GetIpAddress(ip)

    hostname=GetHostname(ip)        
    print_debug("GetLast() ip=%s hostname=%s "%(ip,hostname))

    # try to connect with GDM througth dbus to read all 
    # sessions & display info, better than read wtmp
    try:
        os.environ['TCOSMONITOR_NO_EXTENSIONS']='1'
        import tcosmonitor.Sessions
        app=tcosmonitor.Sessions.Sessions()
        for session in app.sessions:
            if session.remote_host_name == ip:
                print_debug("GetLast() session=%s"%session)
                data= {"pid":0, 
                        "user":session.user, 
                        "host":parseIPAddress(session.remote_host_name),
                        "time":session.since, 
                        "timelogged":session.diff}
                return data
    except Exception, err:
        print_debug("GetLast() Exception, no DBUS Session support, old GDM, err='%s'"%err)
        #import traceback
        #traceback.print_exc(file=sys.stderr)

    for i in range(10):
        last_file=WTMP_FILE
        if i != 0:
            last_file=WTMP_FILE+".%d" %i
        if os.path.isfile(last_file):
            print_debug("GetLast() Searching in %s" %last_file)
            a = utmp.UtmpRecord(last_file)
            while 1:
                b = a.getutent()
                if not b:
                    break
                if b[0] == USER_PROCESS:
                    uthost=str(parseIPAddress(b.ut_host))
                    utline=str(parseIPAddress(b.ut_line))
                    half1="%s" %(uthost[:(len(uthost)/2)])
                    half2="%s" %(uthost[(len(uthost)/2):])
                    if half1 == half2 and str(parseIPAddress(half1)) == utline:
                        b.ut_host = half1
                    else:
                        b.ut_host = uthost
                    #print_debug("  => Searching for host \"%s:0\", hostname=%s found host=%s ut_line=%s user=%s"%(ip, hostname, b.ut_host,b.ut_line, b.ut_user))
                    # 
                    print_debug(" ==> '%s' != '%s' ut_line=%s" %(parseIPAddress(b.ut_host), ip, b.ut_line) )
                    #parseIPAddress(b.ut_line) == "%s:0"%(ip) or \
                    if parseIPAddress(b.ut_host) == "%s"%(ip) or \
                       parseIPAddress(b.ut_host) == "%s"%hostname :
                        # found line ???
                        print_debug("    => found line ?? user=%s"%b.ut_user)
                        if b.ut_line.startswith("pts/") or not \
                           os.path.isdir("/proc/%s"%b.ut_pid): continue
                        print_debug(" Ip \"%s:0\" => found host=%s hostname=%s ut_line=%s user=%s pid=%s"%(ip, hostname, b.ut_host,b.ut_line, b.ut_user, b.ut_pid))
                        last=b
            a.endutent()
            if last and os.path.isdir("/proc/%s"%last.ut_pid): break

    if last and os.path.isdir("/proc/%s"%last.ut_pid):
        print_debug ("diff times now %s - old %s"%(ctime(time()), ctime(last.ut_tv[0])))
        
        # take diff between now and login time
        diff=time()-last.ut_tv[0]
        
        # get days and set diff to rest
        days=int(diff/(3600*24))
        diff=diff-days*3600*24

        # get hours and set diff to rest
        hours=int(diff/3600)
        diff=diff-hours*3600

        # get minutes and set seconds to rest
        minutes=int(diff/60)
        seconds=int(diff-minutes*60)

        print_debug ("days=%s hours=%s minutes=%s seconds=%s"%(days, hours, minutes, diff))

        # only print days if > 0    
        if days == 0:
            timelogged="%02dh:%02dm"%(hours,minutes)
        else:
            timelogged="%dd %02dh:%02dm"%(days,hours,minutes)
            
        uid=pwd.getpwnam(last.ut_user)[2]
        print_debug("username %s uid=%s"%(last.ut_user, uid))
        data={"pid":last.ut_pid, "fulltime":"%dd %02d:%02d:%02d"%(days,hours,minutes,seconds), "userid":uid, "user":last.ut_user, "host":last.ut_host.split(":")[0], "time":last.ut_tv[0], "timelogged":timelogged}
        print_debug(data)
    return data

data=GetLast(HOST)

# print data
if not data:
    print_debug("Error: no data")
    sys.exit(1)

elif not ACTION:
    print_debug("Error need something to retrieve: --user, --uid, --time or --pid")
    sys.exit(1)

elif ACTION == "user":
    print data['user']

elif ACTION == "userid":
    print data['userid']

elif ACTION == "time":
    print data['timelogged']

elif ACTION == "pid":
    print data['pid']

elif ACTION == "ingroup":
    usersingroup=[]
    try:
        # grp not work well with ldap users.
        cmd="groups %s 2>/dev/null" %data['user']
        output=[]
        p = Popen(cmd, shell=True, bufsize=0, stdout=PIPE, stderr=STDOUT, close_fds=True).stdout
        for line in p.readlines():
            if line != '\n':
                line=line.replace('\n', '')
                output.append(line)
        print_debug("cmd %s: %s" %(cmd,output))
        usersingroup=output[0].split()
        #usersingroup=grp.getgrnam(INGROUP)[3]
    except Exception, err:
        usersingroup=[]

    if len(usersingroup) == 0:
        try:
            usersingroup=grp.getgrnam(INGROUP)[3]
        except:
            pass

    exclude="noexclude"
    for group in usersingroup:
        if INGROUP == group:
            exclude="exclude"

    print exclude



