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