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
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.
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
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
)
72 # check for exit call before trying to process
73 if exit_event
.isSet():
74 logger
.info("Exiting.")
77 command
= commandQueue
.get_nowait()
78 logger
.info("received command: %s" % (command
, ))
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
91 scandata
= Popen([confFile
.get_opt('DEFAULT.iwlist_command'), device
, 'scan'], stdout
=PIPE
).stdout
.read()
94 logger
.critical("iwlist command not found, please set this in the preferences.")
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(' - ')
102 # set the defaults for profile template
103 profile
= get_new_profile()
104 m
= essid_pattern
.search(hit
)
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
125 logger
.debug("Scanned profile: %s" % (access_points
[ bssid
], ))
126 apQueue
.put_nowait(access_points
[bssid
])
130 commandQueue
.task_done()
133 def get_network_device(device
, iwconfig_command
, logger
):
134 """ Gets the network interface device.
138 'device' -- string - The proposed network device to use
142 string -- The actual network device to use
144 #print "get_network_device: %s" % (device, )
145 if device
!= "auto_detect":
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
153 iwconfig_info
= Popen(iwconfig_command
, shell
=True, stdout
=PIPE
, stderr
=STDOUT
).stdout
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.")
162 def get_new_profile():
163 """ Return a blank profile.
171 dictionary -- An AP profile with defaults set.
173 return { 'known': False,
183 'con_postscript': '',
185 'dis_postscript': '',
200 def make_section_name( essid
, bssid
):
201 """ Combine essid and bssid to make a config file section name.
205 'essid' -- string - AP ESSID
207 'bssid' -- string - AP BSSID
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
220 'section' -- string - Config file section name
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
234 'command' -- tuple - The command and arguments to run.
236 'environment' -- dictionary - Environment variables (as keys) and their values.
240 boolean -- True on success, otherwise, False
244 env_tmp
.update(environment
)
245 command
= ' '.join(command
)
246 return_code
= call(command
, shell
=True, env
=env_tmp
)
250 print >>sys
.stderr
, "Child was terminated by signal", -return_code
252 print >>sys
.stderr
, "Execution failed:", e
255 def say(words
, speak_command
):
256 """ Speak feedback message to user
260 'words' -- string - Message to speak to user
266 words
= words
.replace( "\"", "\\\"" )
267 shellcmd([speak_command
, words
])
270 # Make so we can be imported
271 if __name__
== "__main__":