mirror of
https://github.com/gryf/wicd.git
synced 2025-12-19 20:38:00 +01:00
584 lines
21 KiB
Python
584 lines
21 KiB
Python
""" Misc - miscellaneous functions for wicd """
|
|
|
|
#
|
|
# Copyright (C) 2007 - 2008 Adam Blackburn
|
|
# Copyright (C) 2007 - 2008 Dan O'Reilly
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License Version 2 as
|
|
# published by the Free Software Foundation.
|
|
#
|
|
# 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, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
import os
|
|
import locale
|
|
import gettext
|
|
import sys
|
|
import re
|
|
from threading import Thread
|
|
from subprocess import Popen, STDOUT, PIPE, call
|
|
from commands import getoutput
|
|
|
|
# wicd imports
|
|
import wpath
|
|
|
|
if __name__ == '__main__':
|
|
wpath.chdir(__file__)
|
|
|
|
NOT_CONNECTED = 0
|
|
CONNECTING = 1
|
|
WIRELESS = 2
|
|
WIRED = 3
|
|
SUSPENDED = 4
|
|
|
|
AUTO = 0
|
|
DHCLIENT = 1
|
|
DHCPCD = 2
|
|
PUMP = 3
|
|
|
|
ETHTOOL = 1
|
|
MIITOOL = 2
|
|
|
|
IP = 1
|
|
ROUTE = 2
|
|
|
|
class WicdError(Exception):
|
|
pass
|
|
|
|
|
|
def Run(cmd, include_stderr=False, return_pipe=False):
|
|
""" Run a command.
|
|
|
|
Runs the given command, returning either the output
|
|
of the program, or a pipe to read output from.
|
|
|
|
keyword arguments --
|
|
cmd - The command to execute
|
|
include_std_err - Boolean specifying if stderr should
|
|
be included in the pipe to the cmd.
|
|
return_pipe - Boolean specifying if a pipe to the
|
|
command should be returned. If it is
|
|
false, all that will be returned is
|
|
one output string from the command.
|
|
|
|
"""
|
|
if not isinstance(cmd, list):
|
|
cmd = to_unicode(str(cmd))
|
|
cmd = cmd.split()
|
|
if include_stderr:
|
|
err = STDOUT
|
|
fds = True
|
|
else:
|
|
err = None
|
|
fds = False
|
|
|
|
tmpenv = os.environ.copy()
|
|
tmpenv["LC_ALL"] = "C"
|
|
tmpenv["LANG"] = "C"
|
|
try:
|
|
f = Popen(cmd, shell=False, stdout=PIPE, stderr=err, close_fds=fds,
|
|
cwd='/', env=tmpenv)
|
|
except OSError, e:
|
|
print "Running command %s failed: %s" % (str(cmd), str(e))
|
|
return ""
|
|
|
|
if return_pipe:
|
|
return f.stdout
|
|
else:
|
|
return f.communicate()[0]
|
|
|
|
def LaunchAndWait(cmd):
|
|
""" Launches the given program with the given arguments, then blocks.
|
|
|
|
cmd : A list contained the program name and its arguments.
|
|
|
|
"""
|
|
call(cmd, shell=False)
|
|
|
|
def IsValidIP(ip):
|
|
""" Make sure an entered IP is valid. """
|
|
if ip != None:
|
|
if ip.count('.') == 3:
|
|
ipNumbers = ip.split('.')
|
|
for number in ipNumbers:
|
|
if not number.isdigit() or int(number) > 255:
|
|
return False
|
|
return ipNumbers
|
|
return False
|
|
|
|
def PromptToStartDaemon():
|
|
""" Prompt the user to start the daemon """
|
|
daemonloc = wpath.sbin + 'wicd'
|
|
sudo_prog = choose_sudo_prog()
|
|
if "gksu" in sudo_prog or "ktsuss" in sudo_prog:
|
|
msg = '--message'
|
|
else:
|
|
msg = '--caption'
|
|
sudo_args = [sudo_prog, msg,
|
|
'Wicd needs to access your computer\'s network cards.',
|
|
daemonloc]
|
|
os.spawnvpe(os.P_WAIT, sudo_prog, sudo_args, os.environ)
|
|
|
|
def RunRegex(regex, string):
|
|
""" runs a regex search on a string """
|
|
m = regex.search(string)
|
|
if m:
|
|
return m.groups()[0]
|
|
else:
|
|
return None
|
|
|
|
def WriteLine(my_file, text):
|
|
""" write a line to a file """
|
|
my_file.write(text + "\n")
|
|
|
|
def ExecuteScript(script):
|
|
""" Execute a command and send its output to the bit bucket. """
|
|
call("%s > /dev/null 2>&1" % script, shell=True)
|
|
|
|
def ReadFile(filename):
|
|
""" read in a file and return it's contents as a string """
|
|
if not os.path.exists(filename):
|
|
return None
|
|
my_file = open(filename,'r')
|
|
data = my_file.read().strip()
|
|
my_file.close()
|
|
return str(data)
|
|
|
|
def to_bool(var):
|
|
""" Convert a string to type bool, but make "False"/"0" become False. """
|
|
if var in ("False", "0"):
|
|
var = False
|
|
else:
|
|
var = bool(var)
|
|
return var
|
|
|
|
def Noneify(variable):
|
|
""" Convert string types to either None or booleans"""
|
|
#set string Nones to real Nones
|
|
if variable in ("None", "", None):
|
|
return None
|
|
if variable in ("False", "0"):
|
|
return False
|
|
if variable in ("True", "1"):
|
|
return True
|
|
return variable
|
|
|
|
def ParseEncryption(network):
|
|
""" Parse through an encryption template file
|
|
|
|
Parses an encryption template, reading in a network's info
|
|
and creating a config file for it
|
|
|
|
"""
|
|
enctemplate = open(wpath.encryption + network["enctype"])
|
|
template = enctemplate.readlines()
|
|
# Set these to nothing so that we can hold them outside the loop
|
|
z = "ap_scan=1\n"
|
|
# Loop through the lines in the template, selecting ones to use
|
|
for y, x in enumerate(template):
|
|
x = x.strip("\n")
|
|
if y > 4:
|
|
# replace values
|
|
x = x.replace("$_SCAN","0")
|
|
for t in network:
|
|
# Don't bother if z's value is None cause it will cause errors
|
|
if Noneify(network[t]) is not None:
|
|
x = x.replace("$_" + str(t).upper(), str(network[t]))
|
|
z = z + "\n" + x
|
|
|
|
# Write the data to the files then chmod them so they can't be read
|
|
# by normal users.
|
|
file = open(wpath.networks + network["bssid"].replace(":", "").lower(), "w")
|
|
os.chmod(wpath.networks + network["bssid"].replace(":", "").lower(), 0600)
|
|
os.chown(wpath.networks + network["bssid"].replace(":", "").lower(), 0, 0)
|
|
# We could do this above, but we'd like to read protect
|
|
# them before we write, so that it can't be read.
|
|
file.write(z)
|
|
file.close()
|
|
|
|
def LoadEncryptionMethods():
|
|
""" Load encryption methods from configuration files
|
|
|
|
Loads all the encryption methods from the template files
|
|
in /encryption/templates into a data structure. To be
|
|
loaded, the template must be listed in the "active" file.
|
|
|
|
"""
|
|
def parse_ent(line, key):
|
|
return line.replace(key, "").replace("=", "").strip()
|
|
|
|
encryptionTypes = []
|
|
try:
|
|
enctypes = open(wpath.encryption + "active","r").readlines()
|
|
except IOError, e:
|
|
print "Fatal Error: template index file is missing."
|
|
raise IOError(e)
|
|
|
|
# Parse each encryption method
|
|
for x in enctypes:
|
|
x = x.strip()
|
|
try:
|
|
f = open(wpath.encryption + x, "r")
|
|
except IOError:
|
|
print 'Failed to load encryption type ' + str(x)
|
|
continue
|
|
line = f.readlines()
|
|
f.close()
|
|
|
|
cur_type = {}
|
|
cur_type[0] = parse_ent(line[0], "name")
|
|
cur_type[1] = x
|
|
cur_type[2] = {}
|
|
|
|
# Find the line containing our required fields.
|
|
i = 1
|
|
try:
|
|
while not line[i].startswith("require"):
|
|
i += 1
|
|
except IndexError:
|
|
print "Bad encryption template: Couldn't find 'require' line"
|
|
requiredFields = parse_ent(line[i], "require")
|
|
requiredFields = requiredFields.split(" ")
|
|
|
|
# Get the required fields.
|
|
index = -1
|
|
for current in requiredFields:
|
|
# The pretty names will start with an * so we can
|
|
# separate them with that.
|
|
if current[0] == "*":
|
|
# Make underscores spaces
|
|
# and remove the *
|
|
cur_type[2][index][0] = current.replace("_", " ").lstrip("*")
|
|
else:
|
|
# Add to the list of things that are required.
|
|
index = len(cur_type[2])
|
|
cur_type[2][index] = {}
|
|
cur_type[2][index][1] = current
|
|
# Add the current type to the dict of encryption types.
|
|
encryptionTypes.append(cur_type)
|
|
return encryptionTypes
|
|
|
|
def noneToString(text):
|
|
""" Convert None, "None", or "" to string type "None"
|
|
|
|
Used for putting text in a text box. If the value to put in is 'None',
|
|
the box will be blank.
|
|
|
|
"""
|
|
if text in (None, ""):
|
|
return "None"
|
|
else:
|
|
return str(text)
|
|
|
|
def get_gettext():
|
|
""" Set up gettext for translations. """
|
|
# Borrowed from an excellent post on how to do this at
|
|
# http://www.learningpython.com/2006/12/03/translating-your-pythonpygtk-application/
|
|
local_path = wpath.translations
|
|
langs = []
|
|
try:
|
|
lc, encoding = locale.getdefaultlocale()
|
|
except ValueError, e:
|
|
print str(e)
|
|
print "Default locale unavailable, falling back to en_US"
|
|
if (lc):
|
|
langs = [lc]
|
|
osLanguage = os.environ.get('LANGUAGE', None)
|
|
if (osLanguage):
|
|
langs += osLanguage.split(":")
|
|
langs += ["en_US"]
|
|
lang = gettext.translation('wicd', local_path, languages=langs,
|
|
fallback=True)
|
|
_ = lang.gettext
|
|
return _
|
|
|
|
def to_unicode(x):
|
|
""" Attempts to convert a string to utf-8. """
|
|
# If this is a unicode string, encode it and return
|
|
if type(x) not in [unicode, str]:
|
|
return x
|
|
if isinstance(x, unicode):
|
|
return x.encode('utf-8')
|
|
encoding = locale.getpreferredencoding()
|
|
try:
|
|
ret = x.decode(encoding).encode('utf-8')
|
|
except UnicodeError:
|
|
try:
|
|
ret = x.decode('utf-8').encode('utf-8')
|
|
except UnicodeError:
|
|
try:
|
|
ret = x.decode('latin-1').encode('utf-8')
|
|
except UnicodeError:
|
|
ret = x.decode('utf-8', 'replace').encode('utf-8')
|
|
|
|
return ret
|
|
|
|
def RenameProcess(new_name):
|
|
""" Renames the process calling the function to the given name. """
|
|
if sys.platform != 'linux2':
|
|
print 'Unsupported platform'
|
|
return False
|
|
try:
|
|
import ctypes
|
|
is_64 = os.path.exists('/lib64/libc.so.6')
|
|
if is_64:
|
|
libc = ctypes.CDLL('/lib64/libc.so.6')
|
|
else:
|
|
libc = ctypes.CDLL('/lib/libc.so.6')
|
|
libc.prctl(15, new_name, 0, 0, 0)
|
|
return True
|
|
except:
|
|
print "rename failed"
|
|
return False
|
|
|
|
def detect_desktop_environment():
|
|
""" Try to determine which desktop environment is in use.
|
|
|
|
Choose between kde, gnome, or xfce based on environment
|
|
variables and a call to xprop.
|
|
|
|
"""
|
|
desktop_environment = 'generic'
|
|
if os.environ.get('KDE_FULL_SESSION') == 'true':
|
|
desktop_environment = 'kde'
|
|
elif os.environ.get('GNOME_DESKTOP_SESSION_ID'):
|
|
desktop_environment = 'gnome'
|
|
else:
|
|
try:
|
|
info = getoutput('xprop -root _DT_SAVE_MODE')
|
|
if ' = "xfce4"' in info:
|
|
desktop_environment = 'xfce'
|
|
except (OSError, RuntimeError):
|
|
pass
|
|
return desktop_environment
|
|
|
|
def get_sudo_cmd(msg):
|
|
""" Returns a graphical sudo command for generic use. """
|
|
sudo_prog = choose_sudo_prog()
|
|
if not sudo_prog: return None
|
|
if re.search("(ktsuss|gksu|gksudo)$", sudo_prog):
|
|
msg_flag = "-m"
|
|
else:
|
|
msg_flag = "--caption"
|
|
return [sudo_prog, msg_flag, msg]
|
|
|
|
def choose_sudo_prog():
|
|
""" Try to intelligently decide which graphical sudo program to use. """
|
|
desktop_env = detect_desktop_environment()
|
|
env_path = os.environ['PATH'].split(":")
|
|
|
|
if desktop_env == "kde":
|
|
paths = []
|
|
for p in env_path:
|
|
paths.extend([p + '/kdesu', p + '/kdesudo', p + '/ktsuss'])
|
|
else:
|
|
paths = []
|
|
for p in env_path:
|
|
paths.extend([p + '/gksudo', p + "/gksu", p + '/ktsuss'])
|
|
for path in paths:
|
|
if os.access(path, os.F_OK):
|
|
return path
|
|
|
|
return None
|
|
|
|
def find_path(cmd):
|
|
""" Try to find a full path for a given file name.
|
|
|
|
Search the all the paths in the environment variable PATH for
|
|
the given file name, or return None if a full path for
|
|
the file can not be found.
|
|
|
|
"""
|
|
paths = os.getenv("PATH", default="/usr/bin:/usr/local/bin").split(':')
|
|
for path in paths:
|
|
if os.access(os.path.join(path, cmd), os.F_OK):
|
|
return os.path.join(path, cmd)
|
|
return None
|
|
|
|
def get_language_list_gui():
|
|
""" Returns a dict of translatable strings used by the GUI.
|
|
|
|
Translations are done at http://wicd.net/translator. Please
|
|
translate if you can.
|
|
|
|
"""
|
|
_ = get_gettext()
|
|
language = {}
|
|
language['connect'] = _("Connect")
|
|
language['ip'] = _("IP")
|
|
language['netmask'] = _("Netmask")
|
|
language['gateway'] = _('Gateway')
|
|
language['dns'] = _('DNS')
|
|
language['use_static_ip'] = _('Use Static IPs')
|
|
language['use_static_dns'] = _('Use Static DNS')
|
|
language['use_encryption'] = _('Use Encryption')
|
|
language['advanced_settings'] = _('Advanced Settings')
|
|
language['wired_network'] = _('Wired Network')
|
|
language['wired_network_instructions'] = _('To connect to a wired network,'
|
|
' you must create a network profile. To create a network profile, type a'
|
|
' name that describes this network, and press Add.')
|
|
language['automatic_connect'] = _('Automatically connect to this network')
|
|
language['secured'] = _('Secured')
|
|
language['unsecured'] = _('Unsecured')
|
|
language['channel'] = _('Channel')
|
|
language['preferences'] = _('Preferences')
|
|
language['wpa_supplicant_driver'] = _('WPA Supplicant Driver')
|
|
language['wireless_interface'] = _('Wireless Interface')
|
|
language['wired_interface'] = _('Wired Interface')
|
|
language['hidden_network'] = _('Hidden Network')
|
|
language['hidden_network_essid'] = _('Hidden Network ESSID')
|
|
language['connected_to_wireless'] = _('Connected to $A at $B (IP: $C)')
|
|
language['connected_to_wired'] = _('Connected to wired network (IP: $A)')
|
|
language['not_connected'] = _('Not connected')
|
|
language['no_wireless_networks_found'] = _('No wireless networks found.')
|
|
language['killswitch_enabled'] = _('Wireless Kill Switch Enabled')
|
|
language['key'] = _('Key')
|
|
language['username'] = _('Username')
|
|
language['password'] = _('Password')
|
|
language['anonymous_identity'] = _('Anonymous Identity')
|
|
language['identity'] = _('Identity')
|
|
language['authentication'] = _('Authentication')
|
|
language['path_to_pac_file'] = _('Path to PAC File')
|
|
language['select_a_network'] = _('Choose from the networks below:')
|
|
language['connecting'] = _('Connecting...')
|
|
language['wired_always_on'] = _('Always show wired interface')
|
|
language['auto_reconnect'] = _('Automatically reconnect on connection loss')
|
|
language['create_adhoc_network'] = _('Create an Ad-Hoc Network')
|
|
language['essid'] = _('ESSID')
|
|
language['use_wep_encryption'] = _('Use Encryption (WEP only)')
|
|
language['before_script'] = _('Run script before connect')
|
|
language['after_script'] = _('Run script after connect')
|
|
language['disconnect_script'] = _('Run disconnect script')
|
|
language['script_settings'] = _('Scripts')
|
|
language['use_ics'] = _('Activate Internet Connection Sharing')
|
|
language['madwifi_for_adhoc'] = _('Check if using madwifi/atheros drivers')
|
|
language['default_wired'] = _('Use as default profile (overwrites any previous default)')
|
|
language['use_debug_mode'] = _('Enable debug mode')
|
|
language['use_global_dns'] = _('Use global DNS servers')
|
|
language['use_default_profile'] = _('Use default profile on wired autoconnect')
|
|
language['show_wired_list'] = _('Prompt for profile on wired autoconnect')
|
|
language['use_last_used_profile'] = _('Use last used profile on wired autoconnect')
|
|
language['choose_wired_profile'] = _('Select or create a wired profile to connect with')
|
|
language['wired_network_found'] = _('Wired connection detected')
|
|
language['stop_showing_chooser'] = _('Stop Showing Autoconnect pop-up temporarily')
|
|
language['display_type_dialog'] = _('Use dBm to measure signal strength')
|
|
language['scripts'] = _('Scripts')
|
|
language['invalid_address'] = _('Invalid address in $A entry.')
|
|
language['global_settings'] = _('Use these settings for all networks sharing this essid')
|
|
language['encrypt_info_missing'] = _('Required encryption information is missing.')
|
|
language['enable_encryption'] = _('This network requires encryption to be enabled.')
|
|
language['wicd_auto_config'] = _('Automatic (recommended)')
|
|
language["gen_settings"] = _("General Settings")
|
|
language["ext_programs"] = _("External Programs")
|
|
language["dhcp_client"] = _("DHCP Client")
|
|
language["wired_detect"] = _("Wired Link Detection")
|
|
language["route_flush"] = _("Route Table Flushing")
|
|
language["backend"] = _("Backend")
|
|
language["backend_alert"] = _("Changes to your backend won't occur until the daemon is restarted.")
|
|
language['search_domain'] = _("Search Domain")
|
|
language['scripts_need_pass'] = _('You must enter your password to configure scripts')
|
|
language['no_sudo_prog'] = _("Could not find a graphical sudo program. The script editor could not be launched." +
|
|
"You'll have to edit scripts directly your configuration file.")
|
|
|
|
language['0'] = _('0')
|
|
language['1'] = _('1')
|
|
language['2'] = _('2')
|
|
language['3'] = _('3')
|
|
language['4'] = _('4')
|
|
language['5'] = _('5')
|
|
language['6'] = _('6')
|
|
language['7'] = _('7')
|
|
language['8'] = _('8')
|
|
language['9'] = _('9')
|
|
|
|
language['interface_down'] = _('Putting interface down...')
|
|
language['resetting_ip_address'] = _('Resetting IP address...')
|
|
language['interface_up'] = _('Putting interface up...')
|
|
language['setting_encryption_info'] = _('Setting encryption info')
|
|
language['removing_old_connection'] = _('Removing old connection...')
|
|
language['generating_psk'] = _('Generating PSK...')
|
|
language['generating_wpa_config'] = _('Generating WPA configuration file...')
|
|
language['flushing_routing_table'] = _('Flushing the routing table...')
|
|
language['configuring_interface'] = _('Configuring wireless interface...')
|
|
language['validating_authentication'] = _('Validating authentication...')
|
|
language['setting_broadcast_address'] = _('Setting broadcast address...')
|
|
language['setting_static_dns'] = _('Setting static DNS servers...')
|
|
language['setting_static_ip'] = _('Setting static IP addresses...')
|
|
language['running_dhcp'] = _('Obtaining IP address...')
|
|
language['dhcp_failed'] = _('Connection Failed: Unable to Get IP Address')
|
|
language['no_dhcp_offers'] = _('Connection Failed: No DHCP offers received.')
|
|
language['aborted'] = _('Connection Cancelled')
|
|
language['bad_pass'] = _('Connection Failed: Could not authenticate (bad password?)')
|
|
language['done'] = _('Done connecting...')
|
|
language['scanning'] = _('Scanning')
|
|
language['cannot_start_daemon'] = _("Unable to connect to wicd daemon DBus interface. " + \
|
|
"This typically means there was a problem starting the daemon. " + \
|
|
"Check the wicd log for more info")
|
|
language['lost_dbus'] = _("The wicd daemon has shut down, the UI will not function properly until it is restarted.")
|
|
|
|
return language
|
|
|
|
def get_language_list_tray():
|
|
""" Returns a dict of translatable strings used by the tray icon.
|
|
|
|
Translations are done at http://wicd.net/translator. Please
|
|
translate if you can.
|
|
|
|
"""
|
|
_ = get_gettext()
|
|
language = {}
|
|
language['connected_to_wireless'] = _('Connected to $A at $B (IP: $C)')
|
|
language['connected_to_wired'] = _('Connected to wired network (IP: $A)')
|
|
language['not_connected'] = _('Not connected')
|
|
language['killswitch_enabled'] = _('Wireless Kill Switch Enabled')
|
|
language['connecting'] = _('Connecting')
|
|
language['wired'] = _('Wired Network')
|
|
language['scanning'] = _('Scanning')
|
|
language['no_wireless_networks_found'] = _('No wireless networks found.')
|
|
language['daemon_unavailable'] = _("The wicd daemon is unavailable, so your request cannot be completed")
|
|
language['cannot_start_daemon'] = _("Unable to connect to wicd daemon DBus interface." + \
|
|
"This typically means there was a problem starting the daemon." + \
|
|
"Check the wicd log for more info")
|
|
language['no_daemon_tooltip'] = _("Wicd daemon unreachable")
|
|
language['lost_dbus'] = _("The wicd daemon has shut down, the UI will not function properly until it is restarted.")
|
|
return language
|
|
|
|
def noneToBlankString(text):
|
|
""" Converts NoneType or "None" to a blank string. """
|
|
if text in (None, "None"):
|
|
return ""
|
|
else:
|
|
return str(text)
|
|
|
|
def stringToNone(text):
|
|
""" Performs opposite function of noneToString. """
|
|
if text in ("", None, "None"):
|
|
return None
|
|
else:
|
|
return str(text)
|
|
|
|
def checkboxTextboxToggle(checkbox, textboxes):
|
|
for textbox in textboxes:
|
|
textbox.set_sensitive(checkbox.get_active())
|
|
|
|
def threaded(f):
|
|
""" A decorator that will make any function run in a new thread. """
|
|
|
|
def wrapper(*args, **kwargs):
|
|
t = Thread(target=f, args=args, kwargs=kwargs)
|
|
t.setDaemon(True)
|
|
t.start()
|
|
|
|
wrapper.__name__ = f.__name__
|
|
wrapper.__dict__ = f.__dict__
|
|
wrapper.__doc__ = f.__doc__
|
|
|
|
return wrapper
|