Standardize on single quotes
[wifi-radar.git] / wifiradar / gui / g2 / profile.py
blob9b0a2e0fc15c8abe9bfbc181f6cc72d9c589b699
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
4 # gui/g2/profile.py - profile editor, etc. for PyGTK UI
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,2014 Sean Robinson <robinson@tuxfamily.org>
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:
29 # Free Software Foundation, Inc.
30 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
34 from __future__ import unicode_literals
36 import gtk
37 import logging
38 import re
40 from wifiradar.misc import _
42 # create a logger
43 logger = logging.getLogger(__name__)
46 class ProfileEditor(gtk.Dialog, object):
47 """ Edit and return an AP profile.
48 """
49 def __init__(self, parent, profile):
50 """ Create a new :class:`ProfileEditor`, with :data:`parent` window,
51 to edit :data:`profile`.
52 """
53 gtk.Dialog.__init__(self, _('WiFi Profile'), parent,
54 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
55 (gtk.STOCK_CANCEL, False, gtk.STOCK_SAVE, True))
57 self.profile = profile.copy()
58 self.WIFI_MODES = [_('auto'), _('Managed'), _('Ad-Hoc'), _('Master'),
59 _('Repeater'), _('Secondary'), _('Monitor')]
60 self.WEP_MODE = [_('open'), _('restricted')]
61 self.WIFI_CHANNELS = [_('auto'), '1', '2', '3', '4', '5', '6', '7',
62 '8', '9', '10', '11', '12', '13', '14']
64 self.icon = gtk.gdk.pixbuf_new_from_file('pixmaps/wifi-radar.png')
65 self.set_icon(self.icon)
66 self.set_resizable(False)
67 #self.set_size_request(400, 400)
68 #################
69 # build everything in a tabbed notebook
70 self.profile_notebook = gtk.Notebook()
72 self.general_table = gtk.Table()
73 self.general_table.set_row_spacings(3)
74 self.general_table.set_col_spacings(3)
75 # The essid labels
76 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
77 self.general_table.attach(gtk.Label(_('Network Name')), 0, 1, 0, 1, gtk.FILL|gtk.EXPAND, 0, 5, 0)
78 # The essid textboxes
79 self.essid_entry = gtk.Entry(32)
80 self.essid_entry.set_text(self.profile['essid'])
81 self.general_table.attach(self.essid_entry, 1, 2, 0, 1, gtk.FILL|gtk.EXPAND, 0, 5, 0)
82 # Add the essid table to the dialog
83 self.essid_entry.set_tooltip_text(_('The name of the network with '
84 'which to connect.'))
86 # The bssid labels
87 self.general_table.attach(gtk.Label(_('Network Address')), 0, 1, 1, 2, gtk.FILL|gtk.EXPAND, 0, 5, 0)
88 # The bssid textboxes
89 self.bssid_entry = gtk.Entry(32)
90 self.bssid_entry.set_text(self.profile['bssid'])
91 self.bssid_entry.set_sensitive(not self.profile['roaming'])
92 # Add the bssid table to the dialog
93 self.general_table.attach(self.bssid_entry, 1, 2, 1, 2, gtk.FILL|gtk.EXPAND, 0, 5, 0)
94 self.bssid_entry.set_tooltip_text(_('The address of the network '
95 'with which to connect.'))
96 # Add the roaming checkbox
97 self.roaming_cb = gtk.CheckButton(_('Roaming'))
98 self.roaming_cb.set_active(self.profile['roaming'])
99 self.roaming_cb.connect('toggled', self.toggle_roaming)
100 self.general_table.attach(self.roaming_cb, 1, 2, 2, 3, gtk.FILL|gtk.EXPAND, 0, 5, 0)
101 self.roaming_cb.set_tooltip_text(_('Use the AP in this network '
102 'which provides strongest signal?'))
104 self.general_table.attach(gtk.Label(_('Mode')), 0, 1, 3, 4, gtk.FILL|gtk.EXPAND, 0, 5, 0)
105 self.mode_combo = gtk.combo_box_new_text()
106 for mode in self.WIFI_MODES:
107 self.mode_combo.append_text(mode)
108 try:
109 self.mode_combo.set_active(self.WIFI_MODES.index(self.profile['mode']))
110 except ValueError:
111 # If the preferred mode is not recognized,
112 # do not set an active item.
113 self.mode_combo.set_active(-1)
114 self.general_table.attach(self.mode_combo, 1, 2, 3, 4, gtk.FILL|gtk.EXPAND, 0, 5, 0)
115 self.mode_combo.set_tooltip_text(_('Method to use for connection. '
116 'You probably want auto mode.'))
118 self.general_table.attach(gtk.Label(_('Channel')), 0, 1, 4, 5, gtk.FILL|gtk.EXPAND, 0, 5, 0)
119 self.channel_combo = gtk.combo_box_new_text()
120 for channel in self.WIFI_CHANNELS:
121 self.channel_combo.append_text(channel)
122 try:
123 self.channel_combo.set_active(self.WIFI_CHANNELS.index(self.profile['channel']))
124 except ValueError:
125 # If the preferred channel is not recognized,
126 # do not set an active item.
127 self.channel_combo.set_active(-1)
128 self.general_table.attach(self.channel_combo, 1, 2, 4, 5, gtk.FILL|gtk.EXPAND, 0, 5, 0)
129 self.channel_combo.set_tooltip_text(_('Channel the network uses. '
130 'You probably want auto mode.'))
132 self.general_table.attach(gtk.HSeparator(), 0, 2, 5, 6, gtk.FILL|gtk.EXPAND, 0, 5, 10)
134 self.security_setting = gtk.Label(_('Security: {SEC}').format(
135 SEC=self.profile['security'].upper()))
136 self.general_table.attach(self.security_setting, 0, 1, 6, 7, gtk.FILL|gtk.EXPAND, 0, 5, 0)
137 if self.profile['use_dhcp']:
138 self.ip_config_setting = gtk.Label(_('IP Configuration: DHCP'))
139 else:
140 self.ip_config_setting = gtk.Label(_('IP Configuration: Manual'))
141 self.general_table.attach(self.ip_config_setting, 1, 2, 6, 7, gtk.FILL|gtk.EXPAND, 0, 5, 0)
142 self.profile_notebook.append_page(self.general_table, gtk.Label(_('General')))
144 # create the WiFi security page
145 self.security_table = gtk.Table()
146 self.security_table.set_row_spacings(3)
147 self.security_table.set_col_spacings(3)
149 self.security_radio_none = gtk.RadioButton(label=_('None'))
150 self.security_radio_wep = gtk.RadioButton(self.security_radio_none, label=_('WEP'))
151 self.security_radio_wpa = gtk.RadioButton(self.security_radio_none, label=_('WPA'))
152 self.security_radio_none.connect('toggled', self.toggle_security)
153 self.security_radio_wep.connect('toggled', self.toggle_security)
154 self.security_radio_wpa.connect('toggled', self.toggle_security)
155 if self.profile['security'] == 'none':
156 self.security_radio_none.set_active(True)
157 if self.profile['security'] == 'wep':
158 self.security_radio_wep.set_active(True)
159 if self.profile['security'] == 'wpa':
160 self.security_radio_wpa.set_active(True)
161 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
162 self.security_table.attach(self.security_radio_none, 0, 1, 0, 1, gtk.FILL|gtk.EXPAND, 0, 5, 0)
163 self.security_table.attach(self.security_radio_wep, 0, 1, 1, 2, gtk.FILL|gtk.EXPAND, 0, 5, 0)
165 # ToggleButton to select ASCII/hex WEP key type
166 self.wep_key_cb = gtk.CheckButton(_('Hex Key'))
167 if self.profile['key'].startswith('s:'):
168 self.wep_key_cb.set_active(False)
169 #self.wep_key_cb.set_label(_('ASCII Key'))
170 self.profile['key'] = self.profile['key'][2:]
171 else:
172 self.wep_key_cb.set_active(True)
173 self.security_table.attach(self.wep_key_cb, 1, 2, 1, 2, gtk.FILL|gtk.EXPAND, 0, 5, 0)
174 #self.wep_key_cb.connect('toggled', self.toggle_wep_key_type)
175 self.key_entry = gtk.Entry(64)
176 self.key_entry.set_text(self.profile['key'])
177 self.security_table.attach(self.key_entry, 2, 3, 1, 2, gtk.FILL|gtk.EXPAND, 0, 5, 0)
178 self.key_entry.set_tooltip_text(_('WEP key: Plain language or hex '
179 'string to use for encrypted communication with the network.'))
181 self.wep_mode_label = gtk.Label(_('WEP Mode'))
182 self.wep_mode_label.set_alignment(1, 0.5)
183 self.security_table.attach(self.wep_mode_label, 1, 2, 2, 3, gtk.FILL|gtk.EXPAND, 0, 5, 0)
184 self.wep_mode_combo = gtk.combo_box_new_text()
185 for security in self.WEP_MODE:
186 self.wep_mode_combo.append_text(security)
187 try:
188 self.wep_mode_combo.set_active(self.WEP_MODE.index(self.profile['security']))
189 except ValueError:
190 # If the preferred security mode is not recognized,
191 # do not set an active item.
192 self.wep_mode_combo.set_active(-1)
193 self.security_table.attach(self.wep_mode_combo, 2, 3, 2, 3, gtk.FILL|gtk.EXPAND, 0, 5, 0)
194 self.wep_mode_combo.set_tooltip_text(_('Use Open to allow '
195 'unencrypted communication and Restricted to force '
196 'encrypted-only communication.'))
198 self.security_table.attach(self.security_radio_wpa, 0, 1, 3, 4, gtk.FILL|gtk.EXPAND, 0, 5, 0)
199 # The labels
200 self.psk_label = gtk.Label(_('PSK'))
201 self.psk_label.set_alignment(1, 0.5)
202 self.security_table.attach(self.psk_label, 1, 2, 3, 4, gtk.FILL|gtk.EXPAND, 0, 5, 0)
203 # The text boxes
204 self.wpa_psk_entry = gtk.Entry()
205 self.wpa_psk_entry.set_text(self.profile['wpa_psk'])
206 self.security_table.attach(self.wpa_psk_entry, 2, 3, 3, 4, gtk.FILL|gtk.EXPAND, 0, 5, 0)
207 self.profile_notebook.append_page(self.security_table, gtk.Label(_('Security')))
209 # create the network config page
210 self.net_config_page = gtk.VBox()
211 # DHCP toggle
212 self.dhcp_cb = gtk.CheckButton(_('Automatic Network Configuration '
213 '(DHCP)'))
214 self.dhcp_cb.set_active(self.profile['use_dhcp'])
215 self.dhcp_cb.set_alignment(0.5, 0.5)
216 self.dhcp_cb.connect('toggled', self.toggle_dhcp)
217 self.net_config_page.pack_start(self.dhcp_cb, True, True, 5)
219 # Non-DHCP table
220 self.ip_table = gtk.Table()
221 self.ip_table.set_row_spacings(3)
222 self.ip_table.set_col_spacings(3)
223 # static IP
224 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
225 self.ip_table.attach(gtk.Label(_('IP')), 0, 1, 1, 2, gtk.FILL|gtk.EXPAND, 0, 5, 0)
226 self.ip_entry = gtk.Entry(15)
227 self.ip_entry.set_text(self.profile['ip'])
228 self.ip_table.attach(self.ip_entry, 1, 2, 1, 2, gtk.FILL|gtk.EXPAND, 0, 5, 0)
229 self.ip_table.attach(gtk.Label(_('Netmask')), 0, 1, 2, 3, gtk.FILL|gtk.EXPAND, 0, 5, 0)
230 self.netmask_entry = gtk.Entry(15)
231 self.netmask_entry.set_text(self.profile['netmask'])
232 self.ip_table.attach(self.netmask_entry, 1, 2, 2, 3, gtk.FILL|gtk.EXPAND, 0, 5, 0)
233 self.ip_table.attach(gtk.Label(_('Gateway')), 0, 1, 3, 4, gtk.FILL|gtk.EXPAND, 0, 5, 0)
234 self.gw_entry = gtk.Entry(15)
235 self.gw_entry.set_text(self.profile['gateway'])
236 self.ip_table.attach(self.gw_entry, 1, 2, 3, 4, gtk.FILL|gtk.EXPAND, 0, 5, 0)
237 self.ip_table.attach(gtk.Label(_('Domain')), 0, 1, 4, 5, gtk.FILL|gtk.EXPAND, 0, 5, 0)
238 self.domain_entry = gtk.Entry(32)
239 self.domain_entry.set_text(self.profile['domain'])
240 self.ip_table.attach(self.domain_entry, 1, 2, 4, 5, gtk.FILL|gtk.EXPAND, 0, 5, 0)
241 self.ip_table.attach(gtk.Label(_('First DNS')), 0, 1, 5, 6, gtk.FILL|gtk.EXPAND, 0, 5, 0)
242 self.dns1_entry = gtk.Entry(15)
243 self.dns1_entry.set_text(self.profile['dns1'])
244 self.ip_table.attach(self.dns1_entry, 1, 2, 5, 6, gtk.FILL|gtk.EXPAND, 0, 5, 0)
245 self.ip_table.attach(gtk.Label(_('Second DNS')), 0, 1, 6, 7, gtk.FILL|gtk.EXPAND, 0, 5, 0)
246 self.dns2_entry = gtk.Entry(15)
247 self.dns2_entry.set_text(self.profile['dns2'])
248 self.ip_table.attach(self.dns2_entry, 1, 2, 6, 7, gtk.FILL|gtk.EXPAND, 0, 5, 0)
250 self.net_config_page.pack_start(self.ip_table, False, False, 0)
251 self.profile_notebook.append_page(self.net_config_page, gtk.Label(_('IP Configuration')))
253 # create the scripting page
254 con_pp_table = gtk.Table()
255 con_pp_table.set_row_spacings(3)
256 con_pp_table.set_col_spacings(3)
257 # The labels
258 self.conn_before = gtk.Label(_('Before Connection'))
259 self.conn_before.set_alignment(1, 0.5)
260 # (widget, l, r, t, b, xopt, yopt, xpad, ypad)
261 con_pp_table.attach(self.conn_before, 0, 1, 0, 1, gtk.FILL|gtk.EXPAND, 0, 5, 0)
262 self.conn_after = gtk.Label(_('After Connection'))
263 self.conn_after.set_alignment(1, 0.5)
264 con_pp_table.attach(self.conn_after, 0, 1, 1, 2, gtk.FILL|gtk.EXPAND, 0, 5, 0)
265 # The text boxes
266 self.con_prescript_entry = gtk.Entry()
267 self.con_prescript_entry.set_text(self.profile['con_prescript'])
268 con_pp_table.attach(self.con_prescript_entry, 1, 2, 0, 1, gtk.FILL|gtk.EXPAND, 0, 5, 0)
269 self.con_prescript_entry.set_tooltip_text(_('The local command to '
270 'execute before trying to connect to the network.'))
271 self.con_postscript_entry = gtk.Entry()
272 self.con_postscript_entry.set_text(self.profile['con_postscript'])
273 con_pp_table.attach(self.con_postscript_entry, 1, 2, 1, 2, gtk.FILL|gtk.EXPAND, 0, 5, 0)
274 self.con_postscript_entry.set_tooltip_text(_('The local command to '
275 'execute after connecting to the network.'))
276 # The labels
277 self.dis_before = gtk.Label(_('Before Disconnection'))
278 self.dis_before.set_alignment(1, 0.5)
279 con_pp_table.attach(self.dis_before, 0, 1, 2, 3, gtk.FILL|gtk.EXPAND, 0, 5, 0)
280 self.dis_after = gtk.Label(_('After Disconnection'))
281 self.dis_after.set_alignment(1, 0.5)
282 con_pp_table.attach(self.dis_after, 0, 1, 3, 4, gtk.FILL|gtk.EXPAND, 0, 5, 0)
283 # The text boxes
284 self.dis_prescript_entry = gtk.Entry()
285 self.dis_prescript_entry.set_text(self.profile['dis_prescript'])
286 con_pp_table.attach(self.dis_prescript_entry, 1, 2, 2, 3, gtk.FILL|gtk.EXPAND, 0, 5, 0)
287 self.dis_prescript_entry.set_tooltip_text(_('The local command to '
288 'execute before disconnecting from the network.'))
289 self.dis_postscript_entry = gtk.Entry()
290 self.dis_postscript_entry.set_text(self.profile['dis_postscript'])
291 con_pp_table.attach(self.dis_postscript_entry, 1, 2, 3, 4, gtk.FILL|gtk.EXPAND, 0, 5, 0)
292 self.dis_postscript_entry.set_tooltip_text(_('The local command to '
293 'execute after disconnecting from the network.'))
294 self.profile_notebook.append_page(con_pp_table, gtk.Label(_('Scripting')))
296 self.vbox.pack_start(self.profile_notebook, False, False, 5)
298 def run(self):
299 """ Display profile dialog and return an edited profile or None.
301 self.set_security_sensitivity(self.profile['security'])
302 self.ip_table.set_sensitive(not self.profile['use_dhcp'])
303 self.show_all()
304 error = True
305 while error:
306 error = False
307 result = gtk.Dialog.run(self)
308 if result is not None:
309 if self.essid_entry.get_text().strip() == '':
310 raise ValueError
311 self.profile['known'] = True
312 self.profile['essid'] = self.essid_entry.get_text().strip()
313 if self.roaming_cb.get_active():
314 self.profile['bssid'] = ''
315 else:
316 self.profile['bssid'] = self.bssid_entry.get_text().strip()
317 self.profile['roaming'] = self.roaming_cb.get_active()
318 if self.wep_key_cb.get_active():
319 self.profile['key'] = '' # hex WEP key
320 if re.compile('[^0-9a-fA-F]').search(self.key_entry.get_text().strip()):
321 dlg = gtk.MessageDialog(self,
322 gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
323 gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
324 _('Invalid hex WEP key.'))
325 dlg.run()
326 dlg.destroy()
327 del dlg
328 error = True
329 else:
330 self.profile['key'] = 's:' # ASCII WEP key
331 self.profile['key'] = self.profile['key'] + self.key_entry.get_text().strip()
332 if self.mode_combo.get_active_text() is None:
333 self.profile['mode'] = _('auto')
334 else:
335 self.profile['mode'] = self.mode_combo.get_active_text()
336 self.profile['encrypted'] = (self.profile['security'] != '')
337 if self.channel_combo.get_active_text() is None:
338 self.profile['channel'] = _('auto')
339 else:
340 self.profile['channel'] = self.channel_combo.get_active_text()
341 self.profile['protocol'] = 'g'
342 self.profile['available'] = (self.profile['signal'] > 0)
343 self.profile['con_prescript'] = self.con_prescript_entry.get_text().strip()
344 self.profile['con_postscript'] = self.con_postscript_entry.get_text().strip()
345 self.profile['dis_prescript'] = self.dis_prescript_entry.get_text().strip()
346 self.profile['dis_postscript'] = self.dis_postscript_entry.get_text().strip()
347 # security
348 if self.wep_mode_combo.get_active_text() is None:
349 self.profile['wep_mode'] = _('none')
350 else:
351 self.profile['wep_mode'] = self.wep_mode_combo.get_active_text()
352 self.profile['wpa_psk'] = self.wpa_psk_entry.get_text().strip()
353 # dhcp
354 self.profile['use_dhcp'] = self.dhcp_cb.get_active()
355 self.profile['ip'] = self.ip_entry.get_text().strip()
356 self.profile['netmask'] = self.netmask_entry.get_text().strip()
357 self.profile['gateway'] = self.gw_entry.get_text().strip()
358 self.profile['domain'] = self.domain_entry.get_text().strip()
359 self.profile['dns1'] = self.dns1_entry.get_text().strip()
360 self.profile['dns2'] = self.dns2_entry.get_text().strip()
361 else:
362 return None
363 return self.profile
365 def toggle_roaming(self, roaming_toggle, data=None):
366 """ Respond to a roaming checkbox toggle by activating or de-activating
367 the BSSID text entry. :data:`roaming_toggle` is the
368 :class:`gtk.CheckButton` sending the signal. :data:`data` is a
369 (not used) list of arbitrary arguments.
371 self.bssid_entry.set_sensitive(not roaming_toggle.get_active())
373 def toggle_security(self, security_radio, data=None):
374 """ Respond to security radio toggles by activating or de-activating
375 the various security text entries. :data:`security_radio` is the
376 :class:`gtk.RadioButton` sending the signal. :data:`data` is a
377 (not used) list of arbitrary arguments.
379 if security_radio == self.security_radio_none:
380 self.profile['security'] = 'none'
381 elif security_radio == self.security_radio_wep:
382 self.profile['security'] = 'wep'
383 elif security_radio == self.security_radio_wpa:
384 self.profile['security'] = 'wpa'
385 self.set_security_sensitivity(self.profile['security'])
386 self.security_setting.set_text(_('Security: {SEC}').format(
387 SEC=self.profile['security'].upper()))
389 def set_security_sensitivity(self, security_mode):
390 """ Set the sensitivity of the various security text entries.
391 :data:`security_mode` is the security mode: 'none', 'wep', or
392 'wpa'.
394 if security_mode == 'none':
395 self.wep_key_cb.set_sensitive(False)
396 self.key_entry.set_sensitive(False)
397 self.wep_mode_label.set_sensitive(False)
398 self.wep_mode_combo.set_sensitive(False)
399 self.psk_label.set_sensitive(False)
400 self.wpa_psk_entry.set_sensitive(False)
401 elif security_mode == 'wep':
402 self.wep_key_cb.set_sensitive(True)
403 self.key_entry.set_sensitive(True)
404 self.wep_mode_label.set_sensitive(True)
405 self.wep_mode_combo.set_sensitive(True)
406 self.psk_label.set_sensitive(False)
407 self.wpa_psk_entry.set_sensitive(False)
408 elif security_mode == 'wpa':
409 self.wep_key_cb.set_sensitive(False)
410 self.key_entry.set_sensitive(False)
411 self.wep_mode_label.set_sensitive(False)
412 self.wep_mode_combo.set_sensitive(False)
413 self.psk_label.set_sensitive(True)
414 self.wpa_psk_entry.set_sensitive(True)
416 def toggle_dhcp(self, widget=None, data=None):
417 """ Respond to clicking the DHCP check button. :data:`widget`
418 is the widget sending the signal and :data:`data` is a list
419 of arbitrary arguments, both are ignored.
421 self.ip_table.set_sensitive(not self.dhcp_cb.get_active())
424 # Make so we can be imported
425 if __name__ == '__main__':
426 pass