1
""" misc - miscellaneous functions for wicd
3
This module contains a large variety of utility functions used
9
# Copyright (C) 2007 - 2009 Adam Blackburn
10
# Copyright (C) 2007 - 2009 Dan O'Reilly
12
# This program is free software; you can redistribute it and/or modify
13
# it under the terms of the GNU General Public License Version 2 as
14
# published by the Free Software Foundation.
16
# This program is distributed in the hope that it will be useful,
17
# but WITHOUT ANY WARRANTY; without even the implied warranty of
18
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
# GNU General Public License for more details.
21
# You should have received a copy of the GNU General Public License
22
# along with this program. If not, see <http://www.gnu.org/licenses/>.
30
from threading import Thread
31
from subprocess import Popen, STDOUT, PIPE, call
32
from commands import getoutput
33
from itertools import repeat, chain, izip
34
from pipes import quote
39
# Connection state constants
46
# Automatic app selection constant
55
# Link detection tools
59
# Route flushing tools
74
class WicdError(Exception):
78
def Run(cmd, include_stderr=False, return_pipe=False,
79
return_obj=False, return_retcode=True):
82
Runs the given command, returning either the output
83
of the program, or a pipe to read output from.
86
cmd - The command to execute
87
include_std_err - Boolean specifying if stderr should
88
be included in the pipe to the cmd.
89
return_pipe - Boolean specifying if a pipe to the
90
command should be returned. If it is
91
False, all that will be returned is
92
one output string from the command.
93
return_obj - If True, Run will return the Popen object
94
for the command that was run.
97
if not isinstance(cmd, list):
98
cmd = to_unicode(str(cmd))
111
# We need to make sure that the results of the command we run
112
# are in English, so we set up a temporary environment.
113
tmpenv = os.environ.copy()
114
tmpenv["LC_ALL"] = "C"
118
f = Popen(cmd, shell=False, stdout=PIPE, stdin=std_in, stderr=err,
119
close_fds=fds, cwd='/', env=tmpenv)
121
print "Running command %s failed: %s" % (str(cmd), str(e))
129
return f.communicate()[0]
131
def LaunchAndWait(cmd):
132
""" Launches the given program with the given arguments, then blocks.
134
cmd : A list contained the program name and its arguments.
136
returns: The exit code of the process.
139
if not isinstance(cmd, list):
140
cmd = to_unicode(str(cmd))
142
p = Popen(cmd, shell=False, stdout=PIPE, stderr=STDOUT, stdin=None)
146
""" Make sure an entered IP is valid. """
148
if ip.count('.') == 3:
149
ipNumbers = ip.split('.')
150
for number in ipNumbers:
151
if not number.isdigit() or int(number) > 255:
156
def PromptToStartDaemon():
157
""" Prompt the user to start the daemon """
158
daemonloc = wpath.sbin + 'wicd'
159
sudo_prog = choose_sudo_prog()
162
if "gksu" in sudo_prog or "ktsuss" in sudo_prog:
166
sudo_args = [sudo_prog, msg,
167
'Wicd needs to access your computer\'s network cards.',
169
os.spawnvpe(os.P_WAIT, sudo_prog, sudo_args, os.environ)
172
def RunRegex(regex, string):
173
""" runs a regex search on a string """
174
m = regex.search(string)
180
def WriteLine(my_file, text):
181
""" write a line to a file """
182
my_file.write(text + "\n")
184
def ExecuteScripts(scripts_dir, verbose=False, extra_parameters=()):
185
""" Execute every executable file in a given directory. """
186
if not os.path.exists(scripts_dir):
188
for obj in sorted(os.listdir(scripts_dir)):
189
obj = os.path.abspath(os.path.join(scripts_dir, obj))
190
if os.path.isfile(obj) and os.access(obj, os.X_OK):
191
ExecuteScript(os.path.abspath(obj), verbose=verbose,
192
extra_parameters=extra_parameters)
194
def ExecuteScript(script, verbose=False, extra_parameters=()):
195
""" Execute a command and send its output to the bit bucket. """
196
extra_parameters = [ quote(s) for s in extra_parameters ]
197
params = ' '.join(extra_parameters)
199
script = quote(script)
201
print "Executing %s with params %s" % (script, params)
202
ret = call('%s %s > /dev/null 2>&1' % (script, params), shell=True)
204
print "%s returned %s" % (script, ret)
206
def ReadFile(filename):
207
""" read in a file and return it's contents as a string """
208
if not os.path.exists(filename):
210
my_file = open(filename,'r')
211
data = my_file.read().strip()
216
""" Convert a string to type bool, but make "False"/"0" become False. """
217
if var in ("False", "0"):
223
def Noneify(variable):
224
""" Convert string types to either None or booleans"""
225
#set string Nones to real Nones
226
if variable in ("None", "", None):
228
if variable in ("False", "0"):
230
if variable in ("True", "1"):
234
def ParseEncryption(network):
235
""" Parse through an encryption template file
237
Parses an encryption template, reading in a network's info
238
and creating a config file for it
241
enctemplate = open(wpath.encryption + network["enctype"])
242
template = enctemplate.readlines()
243
config_file = "ap_scan=1\n"
244
should_replace = False
245
for index, line in enumerate(template):
246
if not should_replace:
247
if line.strip().startswith('---'):
248
should_replace = True
250
if line.strip().startswith("}"):
251
# This is the last line, so we just write it.
252
config_file = ''.join([config_file, line])
254
cur_val = re.findall('\$_([A-Z0-9_]+)', line)
256
if cur_val[0] == 'SCAN':
257
#TODO should this be hardcoded?
258
line = line.replace("$_SCAN", "0")
259
config_file = ''.join([config_file, line])
261
rep_val = network.get(cur_val[0].lower())
263
line = line.replace("$_%s" % cur_val[0],
265
config_file = ''.join([config_file, line])
267
print "Ignoring template line: '%s'" % line
269
print "Weird parsing error occurred"
270
else: # Just a regular entry.
271
config_file = ''.join([config_file, line])
273
# Write the data to the files then chmod them so they can't be read
275
file_loc = os.path.join(wpath.networks,
276
network['bssid'].replace(":", "").lower())
277
f = open(file_loc, "w")
278
os.chmod(file_loc, 0600)
279
os.chown(file_loc, 0, 0)
280
# We could do this above, but we'd like to read protect
281
# them before we write, so that it can't be read.
285
def LoadEncryptionMethods():
286
""" Load encryption methods from configuration files
288
Loads all the encryption methods from the template files
289
in /encryption/templates into a data structure. To be
290
loaded, the template must be listed in the "active" file.
294
enctypes = open(wpath.encryption + "active","r").readlines()
296
print "Fatal Error: template index file is missing."
299
# Parse each encryption method
301
for enctype in enctypes:
302
parsed_template = _parse_enc_template(enctype.strip())
304
encryptionTypes.append(parsed_template)
305
return encryptionTypes
307
def __parse_field_ent(fields, field_type='require'):
308
fields = fields.split(" ")
310
# We need an even number of entries in the line for it to be valid.
311
if (len(fields) % 2) != 0:
314
for val, disp_val in grouper(2, fields, fillvalue=None):
315
if val.startswith("*") or not disp_val.startswith("*"):
317
ret.append([val, disp_val[1:]])
320
def _parse_enc_template(enctype):
321
""" Parse an encryption template. """
322
def parse_ent(line, key):
323
return line.replace(key, "").replace("=", "").strip()
326
f = open(os.path.join(wpath.encryption, enctype), "r")
328
print "Failed to open template file %s" % enctype
332
cur_type["type"] = enctype
333
cur_type["fields"] = []
334
cur_type['optional'] = []
335
cur_type['required'] = []
336
cur_type['name'] = ""
337
for index, line in enumerate(f):
338
if line.startswith("name") and not cur_type["name"]:
339
cur_type["name"] = parse_ent(line, "name")
340
elif line.startswith("require"):
341
cur_type["required"] = __parse_field_ent(parse_ent(line, "require"))
342
if not cur_type["required"]:
343
# An error occured parsing the require line.
344
print "Invalid 'required' line found in template %s" % enctype
346
elif line.startswith("optional"):
347
cur_type["optional"] = __parse_field_ent(parse_ent(line,
349
field_type="optional")
350
if not cur_type["optional"]:
351
# An error occured parsing the optional line.
352
print "Invalid 'optional' line found in template %s" % enctype
354
elif line.startswith("----"):
358
if not cur_type["required"]:
359
print "Failed to find a 'require' line in template %s" % enctype
361
if not cur_type["name"]:
362
print "Failed to find a 'name' line in template %s" % enctype
367
def noneToString(text):
368
""" Convert None, "None", or "" to string type "None"
370
Used for putting text in a text box. If the value to put in is 'None',
371
the box will be blank.
374
if text in (None, ""):
380
""" Attempts to convert a string to utf-8. """
381
# If this is a unicode string, encode it and return
382
if not isinstance(x, basestring):
384
if isinstance(x, unicode):
385
return x.encode('utf-8')
386
encoding = locale.getpreferredencoding()
388
ret = x.decode(encoding).encode('utf-8')
391
ret = x.decode('utf-8').encode('utf-8')
394
ret = x.decode('latin-1').encode('utf-8')
396
ret = x.decode('utf-8', 'replace').encode('utf-8')
400
def RenameProcess(new_name):
401
""" Renames the process calling the function to the given name. """
402
if sys.platform != 'linux2':
403
print 'Unsupported platform'
407
is_64 = os.path.exists('/lib64/libc.so.6')
409
libc = ctypes.CDLL('/lib64/libc.so.6')
411
libc = ctypes.CDLL('/lib/libc.so.6')
412
libc.prctl(15, new_name, 0, 0, 0)
415
print "rename failed"
418
def detect_desktop_environment():
419
""" Try to determine which desktop environment is in use.
421
Choose between kde, gnome, or xfce based on environment
422
variables and a call to xprop.
425
desktop_environment = 'generic'
426
if os.environ.get('KDE_FULL_SESSION') == 'true':
427
desktop_environment = 'kde'
428
elif os.environ.get('GNOME_DESKTOP_SESSION_ID'):
429
desktop_environment = 'gnome'
432
info = getoutput('xprop -root _DT_SAVE_MODE')
433
if ' = "xfce4"' in info:
434
desktop_environment = 'xfce'
435
except (OSError, RuntimeError):
437
return desktop_environment
439
def get_sudo_cmd(msg, prog_num=0):
440
""" Returns a graphical sudo command for generic use. """
441
sudo_prog = choose_sudo_prog(prog_num)
442
if not sudo_prog: return None
443
if re.search("(ktsuss|gksu|gksudo)$", sudo_prog):
446
msg_flag = "--caption"
447
return [sudo_prog, msg_flag, msg]
449
def choose_sudo_prog(prog_num=0):
450
""" Try to intelligently decide which graphical sudo program to use. """
452
return find_path(_sudo_dict[prog_num])
453
desktop_env = detect_desktop_environment()
454
env_path = os.environ['PATH'].split(":")
457
if desktop_env == "kde":
458
progs = ["kdesu", "kdesudo", "ktsuss"]
460
progs = ["gksudo", "gksu", "ktsuss"]
463
paths.extend([os.path.join(p, prog) for p in env_path])
466
if os.path.exists(path):
471
""" Try to find a full path for a given file name.
473
Search the all the paths in the environment variable PATH for
474
the given file name, or return None if a full path for
475
the file can not be found.
478
paths = os.getenv("PATH").split(':')
480
paths = ["/usr/local/sbin", "/usr/local/bin", "/usr/sbin", "/usr/bin",
483
if os.path.exists(os.path.join(path, cmd)):
484
return os.path.join(path, cmd)
487
def noneToBlankString(text):
488
""" Converts NoneType or "None" to a blank string. """
489
if text in (None, "None"):
494
def stringToNone(text):
495
""" Performs opposite function of noneToString. """
496
if text in ("", None, "None"):
501
def checkboxTextboxToggle(checkbox, textboxes):
502
for textbox in textboxes:
503
textbox.set_sensitive(checkbox.get_active())
506
""" A decorator that will make any function run in a new thread. """
508
def wrapper(*args, **kwargs):
509
t = Thread(target=f, args=args, kwargs=kwargs)
513
wrapper.__name__ = f.__name__
514
wrapper.__dict__ = f.__dict__
515
wrapper.__doc__ = f.__doc__
516
wrapper.__module__ = f.__module__
520
def timeout_add(time, func, milli=False):
521
""" Convience function for running a function on a timer. """
522
if hasattr(gobject, "timeout_add_seconds") and not milli:
523
return gobject.timeout_add_seconds(time, func)
525
if not milli: time = time * 1000
526
return gobject.timeout_add(time, func)
528
def izip_longest(*args, **kwds):
529
""" Implement the itertools.izip_longest method.
531
We implement the method here because its new in Python 2.6.
534
# izip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-
535
fillvalue = kwds.get('fillvalue')
536
def sentinel(counter = ([fillvalue]*(len(args)-1)).pop):
537
yield counter() # yields the fillvalue, or raises IndexError
538
fillers = repeat(fillvalue)
539
iters = [chain(it, sentinel(), fillers) for it in args]
541
for tup in izip(*iters):
546
def grouper(n, iterable, fillvalue=None):
547
""" Iterate over several elements at once
549
"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
552
args = [iter(iterable)] * n
553
return izip_longest(fillvalue=fillvalue, *args)