#!/usr/bin/env python
#############################################
# Prey Linux Lock
# By Tomas Pollak - (c) 2010 Fork Ltd.
# http://preyproject.com
# GPLv3 Licence
#############################################

import os, sys
from hashlib import md5
import gtk
import pango

image_file = "/../../lib/bg-lock-with-input.png"

class Lock:
	def get_md5(self, string):
		return md5(string).hexdigest()

	def enter_callback(self, widget, entry):
		hashed_text = self.get_md5(entry.get_text())
		# print hashed_text

		if hashed_text != passwd:
			print "Invalid password"
			sys.stdout.flush()
			self.label.show()
			return True
		else:
			enable_XF86Switch()
			print 'Correctomondo. PC Unlocked.'
			# self.label.set_markup('<span foreground="green">Very good. Access granted.</span>');
			# self.label.show()
			os._exit(66)

	def on_delete_event(self, widget, event):
		return True
		# self.window.set_keep_above(True)

	def on_focus_change(self, widget, event):
		print " -- Focus changed."
		return True

	def on_window_state_change(self, widget, event):
		self.window.activate_focus()
		print " -- Something happened."
		return False

	def on_key_press(self, widget, event):
		keyname = gtk.gdk.keyval_name(event.keyval)
		# print "Key %s (%d) was pressed" % (keyname, event.keyval)
		if event.keyval > 65470 and event.keyval < 65481: # F1 through F12
			print "Key %s (%d) was pressed" % (keyname, event.keyval)
			# return True
		if event.state & gtk.gdk.CONTROL_MASK:
			print "Control was being held down"
			# return True
		if event.state & gtk.gdk.MOD1_MASK:
			print "Alt was being held down"
			# return True
		if event.state & gtk.gdk.SHIFT_MASK:
			print "Shift was being held down"

	def __init__(self):

		# calculate number of screens
		width = gtk.gdk.screen_width()
		height = gtk.gdk.screen_height()

		black = gtk.gdk.color_parse("black")

		###################################
		# black bg
		###################################

		self.bg_window = gtk.Window(gtk.WINDOW_POPUP)
		self.bg_window.modify_bg(gtk.STATE_NORMAL, black)
		self.bg_window.resize(width, height)
		self.bg_window.set_deletable(False)
		self.bg_window.show()

		# monitors = self.bg_window.get_screen().get_n_monitors()

		###################################
		# window
		###################################

		self.window = gtk.Window(gtk.WINDOW_POPUP)
		self.window.set_title("Prey Lock")
		self.window.modify_bg(gtk.STATE_NORMAL, black)

		# prevents window from being closed
		self.window.connect("delete_event", self.on_delete_event)
		# capture keypresses
		self.window.connect("key_press_event", self.on_key_press)

		self.window.stick()
		self.window.set_deletable(False)
		# self.window.set_focus_on_map(True)
		self.window.set_decorated(False)
		self.window.set_border_width(0)
		self.window.set_keep_above(True)
		self.window.set_resizable(False)
		# self.window.fullscreen()

		main_screen_width = self.window.get_screen().get_monitor_geometry(0).width
		main_screen_height = self.window.get_screen().get_monitor_geometry(0).height
		main_screen_middle = main_screen_width/2

		# print "Main screen size: %s x %s" % (main_screen_width, main_screen_height)
		# print "Main screen middle: %s" % main_screen_middle

		vbox = gtk.VBox(False, 0)
		vbox.set_size_request(main_screen_width, main_screen_height)
		self.window.add(vbox)
		# vbox.show()

		###################################
		# background color and image
		###################################

		image = gtk.Image()
		script_path = sys.path[0]
		bg_path = script_path + image_file
		pixbuf = gtk.gdk.pixbuf_new_from_file(bg_path)
		image_width = pixbuf.get_width()
		image_height = pixbuf.get_height()

		# print 'Image size: %s x %s' % (image_width, image_height)
		scale = 1

		if image_width > main_screen_width or image_height > main_screen_height:
			scale = min(float(main_screen_width)/image_width, float(main_screen_height)/image_height)
			new_width = int(scale*image_width)
			new_height = int(scale*image_height)
			# print "Scaled image to: %s x %s" % (new_width, new_height)
			pixbuf = pixbuf.scale_simple(new_width, new_height, gtk.gdk.INTERP_BILINEAR)

		image.set_from_pixbuf(pixbuf)
		image.set_size_request(main_screen_width, main_screen_height)
		image.show()
		vbox.add(image)

		###################################
		# label
		###################################

		self.entry = gtk.Entry(max=0)
		self.entry.set_max_length(40)

		self.entry.set_inner_border(None)
		self.entry.set_width_chars(24)
		self.entry.set_visibility(False)
		self.entry.set_has_frame(False)
		self.entry.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse("#FFFFFF"))
		self.entry.modify_font(pango.FontDescription("sans 16"))
		self.entry.set_flags(gtk.CAN_FOCUS | gtk.HAS_FOCUS | gtk.HAS_GRAB | gtk.CAN_DEFAULT | gtk.SENSITIVE)
		self.entry.connect("activate", self.enter_callback, self.entry)
		# self.entry.show()

		input_size = 210

		padding_left = int(input_size*scale)
		padding_top = -3
		# print 'Padding: left %s, top %s' % (padding_left, padding_top)

		# if image was scaled down, calculate offset for positioning elements
		if image_height > main_screen_height:
			padding_top = 18 - int((image_height - main_screen_height)/8)

		x_position = main_screen_middle-padding_left
		y_position = -(main_screen_height/2)+padding_top
		# print "Fixed position: %s, %s" % (x_position, y_position)

		fixed = gtk.Fixed()
		fixed.put(self.entry, x_position, y_position)
		fixed.show()
		vbox.pack_start(fixed, False, False, 0)

		text = 'Invalid password. Access denied.'
		self.label = gtk.Label()
		self.label.set_markup('<span foreground="#aa0000">'+text+'</span>');
		self.label.modify_font(pango.FontDescription("sans 15"))
		# self.label.set_size_request(main_screen_width, 30)

		fixed2 = gtk.Fixed()
		# push label down a bit, and left a bit so it remains centered
		fixed2.put(self.label, x_position + 26, y_position + 44)
		vbox.pack_start(fixed2, False, False, 0)

		disable_XF86Switch()

		self.window.show_all()
		# self.window.set_focus(self.entry)
		gtk.gdk.keyboard_grab(fixed.window, True)
		self.label.hide()

original_keycodes = []

def enable_XF86Switch():
	import subprocess # need at least python 2.4
	try:
		for x,y,z in original_keycodes:
			cmd = ['xmodmap', '-e', 'keycode %s=%s %s' % (x,y,z)]
			subprocess.Popen(cmd)
	except OSError, subprocess.CalledProcessError:
		return

def disable_XF86Switch():
	import subprocess # need at least python 2.4
	import re

	try:
		cmd = ['xmodmap', '-pk']
		# find all keys that allows switching to a VT
		p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
		output, serror = p.communicate()
		matched = []

		# retrieve the keycode and its value from xmodmap output
		for line in output.split('\n'):
			if re.search('XF86Switch_VT', line):
				items = line.split()
				keycode = items[0]
				keysym  = items[1]
				keysym2  = items[7]
				keyname = items[2][1:-1]
				matched.append((keycode, keyname))
				original_keycodes.append((keycode, keysym, keysym2))

		# disable all key that allow to switch to a VT
		for k,v in matched:
			cmd = ['xmodmap', '-e', 'keycode %s=%s' % (k,v)]
			subprocess.Popen(cmd)

	except IndexError:
		print "Couldn't parse Xmodmap list."

	except OSError, subprocess.CalledProcessError:
		# print "Xmodmap not found"
		# do nothing if xmodmap is not found or return an error
		return


if __name__ == "__main__":

	if len(sys.argv) < 2 or len(sys.argv[1]) != 32:
		print "You need to pass an MD5 hexdigested string."
		sys.exit(1)
	else:
		passwd = sys.argv[1]

	lock = Lock()
	gtk.main()
