Update get_opt_as_bool to new ConfigFile API
[wifi-radar.git] / wifiradar / misc.py
blobc8a221c3aa1574268dad88d5f0626909b44e3b46
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
4 # misc.py - collection of misccellaneous functions and classes
6 # Part of WiFi Radar: A utility for managing WiFi profiles on GNU/Linux.
8 # Copyright (C) 2004-2005 Ahmad Baitalmal <ahmad@baitalmal.com>
9 # Copyright (C) 2005 Nicolas Brouard <nicolas.brouard@mandrake.org>
10 # Copyright (C) 2005-2009 Brian Elliott Finley <brian@thefinleys.com>
11 # Copyright (C) 2006 David Decotigny <com.d2@free.fr>
12 # Copyright (C) 2006 Simon Gerber <gesimu@gmail.com>
13 # Copyright (C) 2006-2007 Joey Hurst <jhurst@lucubrate.org>
14 # Copyright (C) 2006, 2009 Ante Karamatic <ivoks@ubuntu.com>
15 # Copyright (C) 2009-2010 Sean Robinson <seankrobinson@gmail.com>
16 # Copyright (C) 2010 Prokhor Shuchalov <p@shuchalov.ru>
18 # This program is free software; you can redistribute it and/or modify
19 # it under the terms of the GNU General Public License as published by
20 # the Free Software Foundation; version 2 of the License.
22 # This program is distributed in the hope that it will be useful,
23 # but WITHOUT ANY WARRANTY; without even the implied warranty of
24 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 # GNU General Public License in LICENSE.GPL for more details.
27 # You should have received a copy of the GNU General Public License
28 # along with this program; if not, write to the Free Software
29 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 import os
33 import Queue
34 import re
36 from subprocess import call, Popen, PIPE, STDOUT
37 from time import sleep
39 WIFI_RADAR_VERSION = "0.0.0"
41 def scanning_thread(confFile, apQueue, commandQueue, logger, exit_event):
42 """ Scan for a limited time and return AP names and bssid found.
43 Access points we find will be put on the outgoing Queue, apQueue.
45 Parameters:
47 'confFile' -- ConfigFile - Config file object
49 'apQueue' -- Queue - Queue on which to put AP profiles
51 'commandQueue' -- Queue - Queue from which to read commands
53 'logger' -- Logger - Python's logging facility
55 Returns:
57 nothing
58 """
59 logger.info("Begin thread.")
60 # Setup our essid pattern matcher
61 essid_pattern = re.compile("ESSID\s*(:|=)\s*\"([^\"]+)\"", re.I | re.M | re.S)
62 bssid_pattern = re.compile("Address\s*(:|=)\s*([a-zA-Z0-9:]+)", re.I | re.M | re.S)
63 protocol_pattern = re.compile("Protocol\s*(:|=)\s*IEEE 802.11\s*([abgn]+)", re.I | re.M | re.S)
64 mode_pattern = re.compile("Mode\s*(:|=)\s*([^\n]+)", re.I | re.M | re.S)
65 channel_pattern = re.compile("Channel\s*(:|=)*\s*(\d+)", re.I | re.M | re.S)
66 enckey_pattern = re.compile("Encryption key\s*(:|=)\s*(on|off)", re.I | re.M | re.S)
67 signal_pattern = re.compile("Signal level\s*(:|=)\s*(-?[0-9]+)", re.I | re.M | re.S)
69 access_points = {}
70 command = "scan"
71 while True:
72 # check for exit call before trying to process
73 if exit_event.isSet():
74 logger.info("Exiting.")
75 return
76 try:
77 command = commandQueue.get_nowait()
78 logger.info("received command: %s" % (command, ))
79 command_read = True
80 except Queue.Empty:
81 command_read = False
82 device = get_network_device(confFile.get_opt('DEFAULT.interface'), confFile.get_opt('DEFAULT.iwconfig_command'), logger)
83 if ( device and command == "scan" ):
84 logger.debug("Beginning scan pass")
85 # Some cards need to have the interface up to scan
86 if confFile.get_opt_as_bool('DEFAULT.ifup_required'):
87 # call ifconfig command and wait for return
88 shellcmd([confFile.get_opt('DEFAULT.ifconfig_command'), device, 'up'])
89 # update the signal strengths
90 try:
91 scandata = Popen([confFile.get_opt('DEFAULT.iwlist_command'), device, 'scan'], stdout=PIPE).stdout.read()
92 except OSError as e:
93 if e.errno == 2:
94 logger.critical("iwlist command not found, please set this in the preferences.")
95 scandata = ""
96 # zero out the signal levels for all access points
97 for bssid in access_points:
98 access_points[bssid]['signal'] = 0
99 # split the scan data based on the address line
100 hits = scandata.split(' - ')
101 for hit in hits:
102 # set the defaults for profile template
103 profile = get_new_profile()
104 m = essid_pattern.search(hit)
105 if m:
106 # we found an essid
107 profile['essid'] = m.groups()[1]
108 m = bssid_pattern.search(hit) # get BSSID from scan
109 if m: profile['bssid'] = m.groups()[1]
110 m = protocol_pattern.search(hit) # get protocol from scan
111 if m: profile['protocol'] = m.groups()[1]
112 m = mode_pattern.search(hit) # get mode from scan
113 if m: profile['mode'] = m.groups()[1]
114 m = channel_pattern.search(hit) # get channel from scan
115 if m: profile['channel'] = m.groups()[1]
116 m = enckey_pattern.search(hit) # get encryption key from scan
117 if m: profile['encrypted'] = (m.groups()[1] == 'on')
118 m = signal_pattern.search(hit) # get signal strength from scan
119 if m: profile['signal'] = m.groups()[1]
120 access_points[ profile['bssid'] ] = profile
121 for bssid in access_points:
122 access_points[bssid]['available'] = (access_points[bssid]['signal'] > 0)
123 # Put all, now or previously, sensed access_points into apQueue
124 try:
125 logger.debug("Scanned profile: %s" % (access_points[ bssid ], ))
126 apQueue.put_nowait(access_points[bssid])
127 except Queue.Full:
128 pass
129 if command_read:
130 commandQueue.task_done()
131 sleep(1)
133 def get_network_device(device, iwconfig_command, logger):
134 """ Gets the network interface device.
136 Parameters:
138 'device' -- string - The proposed network device to use
140 Returns:
142 string -- The actual network device to use
144 #print "get_network_device: %s" % (device, )
145 if device != "auto_detect":
146 return device
147 else:
148 # auto detect network device
149 # Get a list of 802.11 enabled devices by parsing the output of iwconfig.
150 # If no devices are found, default to eth1.
151 # call iwconfig command and read output
152 try:
153 iwconfig_info = Popen(iwconfig_command, shell=True, stdout=PIPE, stderr=STDOUT).stdout
154 except OSError as e:
155 logger.critical("problem running iwconfig: {}".format(e))
156 wireless_devices = [(x[0:x.find(" ")]) for x in iwconfig_info if("ESSID" in x)]
157 if len(wireless_devices) > 0:
158 return wireless_devices[0]
159 logger.critical("No WiFi device found, please set this in the preferences.")
160 return False
162 def get_new_profile():
163 """ Return a blank profile.
165 Parameters:
167 none
169 Returns:
171 dictionary -- An AP profile with defaults set.
173 return { 'known': False,
174 'available': False,
175 'encrypted': False,
176 'essid': '',
177 'bssid': '',
178 'roaming': False,
179 'protocol': 'g',
180 'signal': -193,
181 'channel': 'auto',
182 'con_prescript': '',
183 'con_postscript': '',
184 'dis_prescript': '',
185 'dis_postscript': '',
186 'key': '',
187 'mode': 'auto',
188 'security': 'none',
189 'wep_mode': '',
190 'wpa_psk': '',
191 'use_dhcp': True,
192 'ip': '',
193 'netmask': '',
194 'gateway': '',
195 'domain': '',
196 'dns1': '',
197 'dns2': ''
200 def make_section_name( essid, bssid ):
201 """ Combine essid and bssid to make a config file section name.
203 Parameters:
205 'essid' -- string - AP ESSID
207 'bssid' -- string - AP BSSID
209 Returns:
211 string -- the bssid concatenated to a colon, concatenated to the essid
213 return essid + ':' + bssid
215 def split_section_name( section ):
216 """ Split a config file section name into an essid and a bssid
218 Parameters:
220 'section' -- string - Config file section name
222 Returns:
224 list -- the essid and bssid
226 parts = re.split(':', section)
227 return [ ':'.join(parts[0:len(parts)-6]), ':'.join(parts[len(parts)-6:len(parts)]) ]
229 def shellcmd( command, environment=None ):
230 """ Run commands through the shell
232 Parameters:
234 'command' -- tuple - The command and arguments to run.
236 'environment' -- dictionary - Environment variables (as keys) and their values.
238 Returns:
240 boolean -- True on success, otherwise, False
242 try:
243 env_tmp = os.environ
244 env_tmp.update(environment)
245 command = ' '.join(command)
246 return_code = call(command, shell=True, env=env_tmp)
247 if return_code >= 0:
248 return True
249 else:
250 print >>sys.stderr, "Child was terminated by signal", -return_code
251 except OSError as e:
252 print >>sys.stderr, "Execution failed:", e
253 return False
255 def say(words, speak_command):
256 """ Speak feedback message to user
258 Parameters:
260 'words' -- string - Message to speak to user
262 Returns:
264 nothing
266 words = words.replace( "\"", "\\\"" )
267 shellcmd([speak_command, words])
270 # Make so we can be imported
271 if __name__ == "__main__":
272 pass