#!/usr/bin/env python

#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
#                                                                             #
# ndisgtk - A GTK frontend for the ndiswrapper wireless driver tool           #
#                                                                             #
# Copyright (C) 2005, Sam Pohlenz <retrix@internode.on.net>                   #
# Copyright (C) 2007-2008 Julian Andres Klode <jak@jak-linux.org>             #
#                                                                             #
# 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              #
# of the License, 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
import os
import subprocess
import re


def getoutput(*cmd):
	'''Like commands.getoutput, but uses subprocess'''
	myproc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
	value  =  myproc.stdout.read()
	retcode = myproc.wait()
	return value, retcode

# Attempt to load GTK bindings
try:
	import pygtk
	pygtk.require("2.0")
	import gtk
	import gtk.glade
	import gobject
except ImportError:
	print "Failed to load GTK bindings. Please check your Gnome installation."
	sys.exit(1)

# Internationalization
import locale
import gettext
locale.setlocale(locale.LC_ALL, "")
gettext.bindtextdomain("ndisgtk", "/usr/share/locale")
gettext.textdomain("ndisgtk")
gettext.install("ndisgtk", "/usr/share/locale", unicode=1)
gtk.glade.bindtextdomain("ndisgtk", "/usr/share/locale")
gtk.glade.textdomain("ndisgtk")

# Data directory
DATA_DIR = "/usr/share/ndisgtk"

def error_dialog(message, parent = None):
	"""
	Displays an error message.
	"""

	dialog = gtk.MessageDialog(parent = parent, type = gtk.MESSAGE_ERROR, buttons = gtk.BUTTONS_OK, flags = gtk.DIALOG_MODAL)
	dialog.set_markup(message)

	result = dialog.run()
	dialog.destroy()

class NdisGTK:
	"""
	Main application class.
	"""

	def __init__(self, kde=False):
		"""
		Initializes the application.
		"""

		# Setup glade and signals
		self.signals = { "gtk_main_quit": gtk.main_quit,
						 "on_install_driver_clicked": self.install_driver_open,
						 "on_remove_driver_clicked": self.remove_driver,
						 "on_driver_list_cursor_changed": self.cursor_changed,
						 "install_dialog_close": self.install_dialog_close,
						 "install_button_clicked": self.install_driver,
						 "network_button_clicked": self.config_network,
						 "help_button_clicked": self.show_help,
						 "drag_motion": self.drag_motion,
						 "drag_data_received": self.drag_data_received }

		self.widgets = gtk.glade.XML(DATA_DIR + "/ndisgtk.glade", domain="ndisgtk")
		self.widgets.signal_autoconnect(self.signals)

		# Get handle to window
		self.window = self.widgets.get_widget("ndiswrapper_main")

		# Load icon
		icon_theme = gtk.icon_theme_get_default()
		self.wifi_icon = icon_theme.load_icon('ndisgtk', 48, 0)
		self.wifi_error_icon = icon_theme.load_icon('ndisgtk-error', 48, 0)
		self.window.set_icon(self.wifi_icon)

		# Get handle to 'Remove Driver' button
		self.remove_driver = self.widgets.get_widget("remove_driver")

		# Get handle to 'Install Driver' dialog
		self.install_dialog = self.widgets.get_widget("install_dialog")
		self.install_dialog.set_transient_for(self.window)

		# Get handle to file chooser
		self.file_chooser = self.widgets.get_widget("filechooser")

		# Enable drag-and-drop
		self.window.drag_dest_set(gtk.DEST_DEFAULT_DROP, [("text/plain", 0, 80)], gtk.gdk.ACTION_COPY)

		# Setup driver list
		self.setup_driver_list()

		# Use KDE network admin?
		self.kde = kde

		gtk.main()

	def setup_driver_list(self):
		"""
		Sets up the driver list and list widget.
		"""

		# Initialize lists
		self.driver_list = []
		self.driver_list_store = gtk.ListStore(gtk.gdk.Pixbuf, gobject.TYPE_STRING)
		self.driver_list_widget = self.widgets.get_widget("driver_list")

		# Set up columns
		first = gtk.TreeViewColumn("icon", gtk.CellRendererPixbuf(), pixbuf = 0)
		second = gtk.TreeViewColumn("desc", gtk.CellRendererText(), markup = 1)

		self.driver_list_widget.append_column(first)
		self.driver_list_widget.append_column(second)

		# Set list model for widget
		self.driver_list_widget.set_model(self.driver_list_store)

		# Load the list of drivers
		self.get_driver_list()

	def get_driver_list(self):
		"""
		Gets the list of drivers from ndiswrapper.
		"""

		# Clear driver list
		self.driver_list_store.clear()
		self.driver_list = []

		# Run the ndiswrapper list command
		output,retcode = getoutput("ndiswrapper", "-l")

		if "WARNING" in output:
			error_dialog(_("Unable to see if hardware is present."), self.window)

		if ": driver" in output or ": invalid" in output:
			# Newer ndiswrapper versions
			# Drivers found
			output = output.splitlines()

			for i in range(0, len(output)):
				line = output[i]
				if len(output) > i+1: line2 = output[i+1]
				else: line2=""

				if "present" in line2: hardware_present = _("Yes")
				else: hardware_present = _("No")

				# Get driver name
				p = re.compile(".*:")						# match up to first tab
				driver_name = p.search(line).group()[:-1].strip()	# strip trailing space

				# Add to list
				if "installed" in line:
					self.driver_list.append(driver_name)
					self.driver_list_store.append([self.wifi_icon,
						_("<b>%s</b>\nHardware present: %s") % (driver_name, hardware_present)])
				elif "invalid" in line:
					self.driver_list.append(driver_name)
					self.driver_list_store.append([self.wifi_error_icon,
						_("<b>%s</b>\nInvalid Driver!") % driver_name])

		elif "installed" in output or "Installed" in output or "invalid" in output:
			# Drivers found
			output = output.splitlines()
			for i in range(1, len(output)):
				line = output[i]

				if "hardware" in line: hardware_present = _("Yes")
				else: hardware_present = _("No")

				# Get driver name
				p = re.compile(".*\\t")						# match up to first tab
				driver_name = p.search(line).group()[:-1].strip()	# strip trailing space

				# Add to list

				if "installed" in line:
					self.driver_list.append(driver_name)
					self.driver_list_store.append([self.wifi_icon,
						_("<b>%s</b>\nHardware present: %s") % (driver_name, hardware_present)])
				elif "invalid" in line:
					self.driver_list.append(driver_name)
					self.driver_list_store.append([self.wifi_icon,
						_("<b>%s</b>\nInvalid Driver!") % driver_name])
		else:
			# No drivers installed
			pass

	def drag_motion(self, window, context, x, y, time):
		"""
		Called whenever a drag motion is made.
		"""

		context.drag_status(gtk.gdk.ACTION_COPY, time)
		return True

	def drag_data_received(self, window, context, x, y, selection, info, timestamp):
		"""
		Called when a file is dragged onto the main window.
		"""

		file = selection.get_text().strip()

		if file.startswith("file://"):
			if file.endswith(".inf"):
				self.file_chooser.set_uri(file)
				self.install_driver_open()
			else:
				error_dialog(_("Please drag an '.inf' file instead."), self.window)

		return True

	def show_help(self, *args):
		"""
		Displays the help window.
		Called when the 'Help' button is clicked.
		"""

		# TODO: Implement
		error_dialog("Help", self.window)

	def config_network(self, *args):
		"""
		Opens the network configuration tool.
		"""

		if self.kde:
			subprocess.Popen(["kcmshell", "kcm_knetworkconfmodule"])
		else:
			subprocess.Popen(["network-admin"])

	def install_driver_open(self, *args):
		"""
		Opens the install driver dialog.
		"""

		self.install_dialog.show()

	def install_dialog_close(self, *args):
		"""
		Closes the install driver dialog.
		"""

		self.install_dialog.hide()
		return True;

	def install_driver(self, *args):
		"""
		Installs a selected wireless driver.
		Called when the install dialog's 'Install Driver' button is clicked.
		"""

		inf_file = self.file_chooser.get_filename()

		if inf_file == None:
			error_dialog(_("No file selected."), self.install_dialog)
		elif not inf_file.lower().endswith(".inf"):
			error_dialog(_("Not a valid driver .inf file."), self.install_dialog)
		else:
			# Attempt to install driver
			output, retcode = getoutput("ndiswrapper", "-i", inf_file)

			# Attempt to detect errors
			if "already" in output:
				#driver_name = output.split()[0]
				error_dialog(_("Driver is already installed."), self.install_dialog)
			elif retcode != 0:
				error_dialog(_("Error while installing.") , self.install_dialog)
			else:
				# Assume driver installed successfully. Set up and reload module
				subprocess.call(["ndiswrapper",  "-ma"])
				subprocess.call(["modprobe", "-r", "ndiswrapper"])
				subprocess.call(["modprobe", "ndiswrapper"])

				self.get_driver_list()
				self.install_dialog_close()

	def remove_driver(self, *args):
		"""
		Removes a driver after asking for confirmation.
		Called when the 'Remove Driver' button is clicked.
		"""

		# Get the first selected driver
		cursor = self.driver_list_widget.get_cursor()[0][0]
		driver_name = self.driver_list[cursor]

		# Get confirmation
		confirm = gtk.MessageDialog(type = gtk.MESSAGE_WARNING, buttons = gtk.BUTTONS_YES_NO)
		confirm.set_markup(_("Are you sure you want to remove the <b>%s</b> driver?") % driver_name)
		result = confirm.run()

		if result == gtk.RESPONSE_YES:
			# Remove driver
			output,retcode = getoutput("ndiswrapper", "-e", driver_name)

			# Reload driver list
			self.get_driver_list()

		# Destroy the confirmation dialog
		confirm.destroy()

	def cursor_changed(self, *args):
		"""
		Called when the currently selected driver changes.
		"""

		# Allow the 'Remove Driver' button to be clicked
		self.remove_driver.set_sensitive(True)


if __name__ == '__main__':
	# Check for root privileges
	if os.getuid() != 0:
		error_dialog(_("Root or sudo privileges required!"))
		sys.exit(1)

	# Parse options and load GUI
	if "--kde" in sys.argv:
		NdisGTK(kde=True)
	else:
		NdisGTK()
