Merge wicd:master-takahiro into wicd:master

Proposed by Takahiro Yoshizawa
Status: Merged
Merged at revision: db879f94b34f5432e9388e6271468f3e84639b32
Proposed branch: wicd:master-takahiro
Merge into: wicd:master
Diff against target: 5070 lines (+1944/-796)
27 files modified
AUTHORS (+2/-0)
INSTALL (+74/-57)
in/init=debian=wicd.in (+1/-1)
in/init=plamo=wicd.in (+65/-0)
other/wicd.conf (+47/-0)
po/ja.po (+2/-2)
pyproject.toml (+60/-1)
setup.cfg (+0/-25)
setup.py (+341/-28)
src/wicd/commandline.py (+2/-1)
src/wicd/configmanager.py (+3/-2)
src/wicd/daemon/__main__.py (+149/-67)
src/wicd/daemon/monitor.py (+21/-18)
src/wicd/dbus.py (+6/-3)
src/wicd/errors.py (+34/-1)
src/wicd/frontends/cli.py (+24/-10)
src/wicd/frontends/gtk/__main__.py (+210/-91)
src/wicd/frontends/gtk/configscript.py (+9/-6)
src/wicd/frontends/gtk/gui.py (+75/-57)
src/wicd/frontends/gtk/guiutil.py (+29/-19)
src/wicd/frontends/gtk/netentry.py (+184/-114)
src/wicd/frontends/gtk/prefs.py (+13/-8)
src/wicd/frontends/gtk/resources/wicd.ui (+4/-3)
src/wicd/logfile.py (+11/-3)
src/wicd/misc.py (+66/-12)
src/wicd/networking.py (+59/-46)
src/wicd/wnettools.py (+453/-221)
Reviewer Review Type Date Requested Status
Takahiro Yoshizawa Approve
Review via email: mp+478112@code.launchpad.net

Description of the change

Merge Takahiro Yoshizawa's work for Python 3.x compatibility.

To post a comment you must log in.
Revision history for this message
Takahiro Yoshizawa (hanaguro) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/AUTHORS b/AUTHORS
2index a629671..80b34dd 100644
3--- a/AUTHORS
4+++ b/AUTHORS
5@@ -3,3 +3,5 @@ Adam Blackburn (compwiz18@gmail.com)
6 Dan O'Reilly (oreilldf@gmail.com)
7 Andrew Psaltis (ampsaltis@gmail.com)
8 David Paleino (d.paleino@gmail.com)
9+Andreas Messer (andi@bastelmap.de)
10+Takahiro Yoshizawa (kuro@takahiro.org)
11diff --git a/INSTALL b/INSTALL
12index 5af7ce3..b64166e 100644
13--- a/INSTALL
14+++ b/INSTALL
15@@ -1,58 +1,75 @@
16-Installation of Wicd should be done using your distribution package if one
17-exists. If not, the installation is relatively straightforward, but there
18-are a few dependencies:
19- 1. python (>=2.4, <3.0)
20- #2. pygtk (>=2.10)
21- 2. dbus and its glib and python bindings
22- 3. a dhcp client (dhclient, dhcpcd, and pump are supported)
23- 4. wireless-tools (iwlist, iwconfig, etcetera)
24- 5. net-tools (ip, route, etcetera)
25- 6. a graphical sudo application (gksu, kdesu, and ktsuss are supported),
26- while optional, is strongly recommended
27- 7. urwid (if you want to use the curses client - needs version >=0.9.8.3)
28- 8. pm-utils (optional for suspend/resume integration)
29- Wicd supports using versions >=1.2.4 -- earlier versions may work just
30- fine, but they are completely unsupported here.
31- 9. pybabel
32- 10. rfkill
33- 11. python-appindicator if you want the Unity-compatible appindicator notifications
34-
35-Next, configure Wicd for installation. Wicd will, by default, follow the
36-FHS guidelines <http://www.pathname.com/fhs/> (or distribution standards
37-where applicable if we know about them and it's feasible to implement them).
38-You can specify exactly where every non-Python file (and some Python files)
39-in Wicd will be placed. Pass "--help" as an option to the following command
40-for more information, otherwise run it as is to configure Wicd for installation.
41- python setup.py configure
42-
43-Note that setup.py will try to determine if and where to install the autostart
44-desktop file for kde (either kde3 or kde4, but not both) and where to install
45-the sleep hook for pm-utils.
46-
47-If you want native language translations, please remember to run:
48- python setup.py compile_translations
49-before the next step.
50-
51-Finally, do the actual installation. This step will need to be done as
52-root or with sudo in most cases:
53- python setup.py install
54-If you are packaging Wicd, you will almost surely want to use the "--root"
55-option; for example:
56- python setup.py install --root=/package-dir
57-
58-To uninstall, you can use (using root or sudo):
59- python setup.py uninstall
60-
61-You *MUST* run "python setup.py configure" before "python setup.py install" -
62-the "configure" step generates wpath.py from wpath.py.in using the paths
63-specified from the arguments to "python setup.py configure".
64-As noted above in the configure step, "python setup.py configure" will use
65-acceptable defaults, so it is usually not necessary to specify any arguments
66-at all.
67-
68-After installation, especially if Wicd has not been installed before, you
69-will probably need to restart the system message bus (dbus) or reload its
70-configuration. You will also need to make sure the Wicd init script is
71-started at boot. How to do those things is distribution-dependent, so if
72-you're not sure, ask in your distribution's support area(s).
73+INSTALL
74
75+Note: It is generally recommended to install Wicd using your distribution’s package manager if a package is available. If not, follow the instructions below.
76+
77+Requirements
78+1. Python 3.10 or later
79+2. D-Bus with GLib and Python bindings
80+3. A DHCP client:
81+ - On Ubuntu 24.04: Must use isc-dhcp-client (provides dhclient).
82+ - On Fedora 41: Either dhclient (provided by isc-dhcp-client) or dhcpcd can be used.
83+4. net-tools (provides ifconfig, route, etc.)
84+5. iw
85+6. AyatanaAppIndicator3 bindings:
86+ - On Ubuntu 24.04: gir1.2-ayatanaappindicator3-0.1
87+ - On Fedora 41: libayatana-appindicator-gtk3
88+7. A graphical sudo application (optional but recommended: gksu, kdesu, or ktsuss)
89+8. rfkill (if not already available)
90+9. pip (Python package installer)
91+
92+
93+General Installation Steps
94+
95+1. Install Dependencies
96+Use your distribution’s package manager to install the required dependencies. For example:
97+
98+Ubuntu 24.04:
99+ sudo apt install git python3-pip net-tools iw isc-dhcp-client gir1.2-ayatanaappindicator3-0.1
100+
101+Fedora 41:
102+ sudo dnf install git python3-pip net-tools iw libayatana-appindicator-gtk3
103+
104+(Adjust package names as needed for other distributions.)
105+
106+
107+2. Obtain and Install Wicd from Source
108+ git clone https://git.launchpad.net/wicd -b {branch_name}
109+ cd wicd
110+
111+For Ubuntu 24.04:
112+ sudo pip install . --break-system-packages
113+
114+For Fedora 41 (and most other distributions):
115+ sudo pip install .
116+
117+
118+3. Adjust Init/Startup Scripts
119+After installing Wicd, template init files (e.g., SysV init scripts, systemd unit templates) are placed in the `template/init` directory within the source tree.
120+You may need to edit these templates to match your system's paths and conventions. For example, if the scripts reference `/usr/bin` but you installed Wicd into `/usr/local/bin`, update the paths accordingly. Once adjusted, install these scripts in the appropriate locations for your distribution’s init system (e.g., `/etc/init.d` for SysV, `/etc/systemd/system` for systemd).
121+
122+
123+4. Configure D-Bus
124+After installation, create a symbolic link for the D-Bus configuration:
125+ sudo ln -s /usr/local/share/dbus-1/system.d/wicd.conf /usr/share/dbus-1/system.d/wicd.conf
126+
127+
128+5. Disable Conflicting Services
129+Wicd can conflict with other network managers. Stop and disable them before using Wicd:
130+ sudo systemctl stop NetworkManager.service
131+ sudo systemctl disable NetworkManager.service
132+
133+ sudo systemctl stop systemd-resolved.service
134+ sudo systemctl disable systemd-resolved.service
135+
136+Adjust resolv.conf if necessary:
137+ sudo mv /etc/resolv.conf /etc/resolv.conf.backup
138+ sudo touch /etc/resolv.conf
139+
140+
141+6. Desktop Environment Integration (Optional)
142+If using wicd-gtk on a GNOME-based desktop (such as Fedora GNOME), install gnome-shell-extension-appindicator and enable the "KStatusNotifierItem/AppIndicator Support" extension.
143+
144+
145+7. Reboot
146+After making these changes, it is recommended to restart your system:
147+ sudo reboot
148diff --git a/in/init=debian=wicd.in b/in/init=debian=wicd.in
149index 879695b..2968c99 100755
150--- a/in/init=debian=wicd.in
151+++ b/in/init=debian=wicd.in
152@@ -19,7 +19,7 @@ PATH=/usr/sbin:/usr/bin:/sbin:/bin
153 DESC="Network connection manager"
154 NAME=wicd
155 RUNDIR=/var/run/$NAME
156-DAEMON=%SBIN%$NAME
157+DAEMON=%BIN%$NAME
158 DAEMON_ARGS=""
159 PIDFILE=$RUNDIR/wicd.pid
160 SCRIPTNAME=%INIT%%INITFILENAME%
161diff --git a/in/init=plamo=wicd.in b/in/init=plamo=wicd.in
162new file mode 100755
163index 0000000..4f72b51
164--- /dev/null
165+++ b/in/init=plamo=wicd.in
166@@ -0,0 +1,65 @@
167+#!/bin/sh
168+########################################################################
169+# Begin scriptname : wicd
170+#
171+# Description : start/stop wicd
172+#
173+# Authors : KOJIMA
174+#
175+# Version : Plamo-7.0
176+#
177+# Notes :
178+#
179+########################################################################
180+
181+### BEGIN INIT INFO
182+# Provides: wicd
183+# Required-Start: dbus
184+# Required-Stop: sendsignals
185+# Default-Start: 2 3 4 5
186+# Default-Stop: 0 2 6
187+# Short-Description: starts wicd network manager
188+# Description: Wicd is a wireless and wired network manager for Linux.
189+# X-LFS-Provided-By:
190+### END INIT INFO
191+
192+. /lib/lsb/init-functions
193+
194+WICD=/usr/bin/wicd
195+if [ -x /usr/bin/wicd ]; then
196+ WICD=/usr/bin/wicd
197+fi
198+
199+PIDFILE="%PIDFILE%"
200+
201+case "${1}" in
202+ start)
203+ log_info_msg "Starting wicd network manager"
204+ start_daemon $WICD
205+ evaluate_retval
206+ ;;
207+
208+ stop)
209+ log_info_msg "Stopping wicd network manager"
210+ $WICD -k
211+ evaluate_retval
212+ if [ -f $PIDFILE ]; then
213+ rm -f $PIDFILE
214+ fi
215+ ;;
216+
217+ restart)
218+ ${0} stop
219+ sleep 1
220+ ${0} start
221+ ;;
222+
223+ *)
224+ echo "Usage: ${0} {start|stop|restart}"
225+ exit 1
226+ ;;
227+esac
228+
229+exit 0
230+
231+# End scriptname
232diff --git a/in/wicd=wpath.py.in b/in/wpath.py.in
233similarity index 100%
234rename from in/wicd=wpath.py.in
235rename to in/wpath.py.in
236diff --git a/other/wicd.conf b/other/wicd.conf
237new file mode 100644
238index 0000000..598d12e
239--- /dev/null
240+++ b/other/wicd.conf
241@@ -0,0 +1,47 @@
242+<!-- /etc/dbus-1/system.d/wicd.conf -->
243+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
244+"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
245+<busconfig>
246+
247+ <policy user="root">
248+ <allow own="org.wicd.daemon"/>
249+ <allow send_destination="org.wicd.daemon"/>
250+ <allow send_interface="org.wicd.daemon"/>
251+ <allow send_destination="org.wicd.daemon.wireless"/>
252+ <allow send_interface="org.wicd.daemon.wireless"/>
253+ <allow send_destination="org.wicd.daemon.wired"/>
254+ <allow send_interface="org.wicd.daemon.wired"/>
255+ </policy>
256+
257+ <policy context="default">
258+ <deny own="org.wicd.daemon"/>
259+ </policy>
260+
261+ <!-- This Unix group will have permission to use Wicd's gui -->
262+ <policy group="users">
263+ <allow send_destination="org.wicd.daemon"/>
264+ <allow send_interface="org.wicd.daemon"/>
265+ <allow send_interface="org.freedesktop.DBus.Introspectable"/>
266+ <!-- The Introspectable allow shouldn't be needed here, but
267+ it seems that a few distributions aren't yet allowing
268+ it in their hal configs, and we need it here, so... -->
269+ </policy>
270+
271+ <!-- Comment the block below if you do not want all users logged in
272+ locally to have permission to use wicd-client. This ignores the
273+ group based permission model defined above for the "users"
274+ group. Note that this only applies if you are using ConsoleKit -
275+ if you do not have ConsoleKit installed and in use, then this
276+ block makes no difference either way. -->
277+
278+ <policy at_console="true">
279+ <allow send_destination="org.wicd.daemon"/>
280+ <allow send_interface="org.wicd.daemon"/>
281+ <allow send_destination="org.wicd.daemon.wireless"/>
282+ <allow send_interface="org.wicd.daemon.wireless"/>
283+ <allow send_destination="org.wicd.daemon.wired"/>
284+ <allow send_interface="org.wicd.daemon.wired"/>
285+ <allow send_interface="org.freedesktop.DBus.Introspectable"/>
286+ </policy>
287+
288+</busconfig>
289diff --git a/po/ja.po b/po/ja.po
290index 4af6a37..b8ca798 100644
291--- a/po/ja.po
292+++ b/po/ja.po
293@@ -519,7 +519,7 @@ msgstr ""
294
295 #: curses/configscript_curses.py:57 gtk/configscript.py:129
296 msgid "Post-connection Script"
297-msgstr "切断後にスクリプトを実行する"
298+msgstr "接続後にスクリプトを実行する"
299
300 #: curses/configscript_curses.py:59 gtk/configscript.py:132
301 msgid "Post-disconnection Script"
302@@ -527,7 +527,7 @@ msgstr "切断後にスクリプトを実行する"
303
304 #: curses/configscript_curses.py:56 gtk/configscript.py:128
305 msgid "Pre-connection Script"
306-msgstr "切断前にスクリプトを実行する"
307+msgstr "接続前にスクリプトを実行する"
308
309 #: curses/configscript_curses.py:58 gtk/configscript.py:130
310 msgid "Pre-disconnection Script"
311diff --git a/pyproject.toml b/pyproject.toml
312index 433b209..370f2d9 100644
313--- a/pyproject.toml
314+++ b/pyproject.toml
315@@ -3,8 +3,67 @@ requires = [
316 "setuptools>45",
317 "setuptools_scm",
318 "wheel",
319+ "babel",
320+ "pybabel",
321 ]
322 build-backend = "setuptools.build_meta"
323
324 [tool.setuptools_scm]
325-write_to = "pkg/_version.py"
326\ No newline at end of file
327+write_to = "src/wicd/_version.py"
328+
329+[project]
330+name = "wicd"
331+dynamic = ["version"]
332+description = "A wireless and wired network manager"
333+readme = "README.rst"
334+authors = [
335+{name = "Tom Van Braeckel", email = "tomvanbraeckel@gmail.com"},
336+{name = "Adam Blackburn", email = "compwiz18@gmail.com"},
337+{name = "Dan O'Reilly", email = "oreilldf@gmail.com"},
338+{name = "Andrew Psaltis", email = "ampsaltis@gmail.com"},
339+{name = "David Paleino", email = "d.paleino@gmail.com"},
340+{name = "Andreas Messer", email = "andi@bastelmap.de"},
341+{name = "Takahiro Yoshizawa", email = "kuro@takahiro.org"},
342+]
343+license = {file = "LICENSE"}
344+requires-python = ">=3.9"
345+dependencies = [
346+ "python-daemon",
347+ "lockfile",
348+]
349+
350+[tool.setuptools]
351+package-dir = {"" = "src"}
352+
353+[project.optional-dependencies]
354+gui = ["notify2"]
355+
356+[project.urls]
357+Homepage = "https://launchpad.net/wicd"
358+
359+[tool.setuptools.data-files]
360+"share/icons/hicolor/16x16/status" = ["images/16x16/status/*"]
361+"share/icons/hicolor/22x22/status" = ["images/22x22/status/*"]
362+"share/icons/hicolor/24x24/status" = ["images/24x24/status/*"]
363+"share/icons/hicolor/32x32/status" = ["images/32x32/status/*"]
364+"share/icons/hicolor/36x36/status" = ["images/36x36/status/*"]
365+"share/icons/hicolor/48x48/status" = ["images/48x48/status/*"]
366+"share/icons/hicolor/48x48/devices" = ["images/48x48/devices/*"]
367+"share/icons/hicolor/scalable/devices" = ["images/scalable/devices/*"]
368+"share/icons/hicolor/original/status" = ["images/original/status/*"]
369+"share/wicd/encryption/templates" = ["encryption/templates/*"]
370+"share/dbus-1/system.d" = ["other/wicd.conf"]
371+
372+[tool.setuptools.package-data]
373+wicd = [
374+ "encryption/templates/*",
375+]
376+
377+[project.scripts]
378+wicd = "wicd.daemon.__main__:main"
379+wicd-gtk = "wicd.frontends.gtk.__main__:main"
380+wicd-cli = "wicd.frontends.cli:main"
381+
382+[project.entry-points."wicd.backends"]
383+external = "wicd.backends.external"
384+ioctl = "wicd.backends.ioctl"
385diff --git a/setup.cfg b/setup.cfg
386index 8a5d1ed..4bd9609 100644
387--- a/setup.cfg
388+++ b/setup.cfg
389@@ -1,27 +1,2 @@
390-[metadata]
391-name = wicd
392-version = 1.8.0.dev1
393-
394-[options]
395-packages = find_namespace:
396-package_dir =
397- =src
398-include_package_data = True
399-
400 [install]
401 record = install.log
402-[bdist_rpm]
403-group = Productivity/Networking/System
404-
405-[options.packages.find]
406-where = src
407-
408-[options.entry_points]
409-console_scripts =
410- wicd = wicd.daemon.__main__:main
411- wicd-cli = wicd.frontends.cli:main
412- wicd-gtk = wicd.frontends.gtk.__main__:main
413-wicd.backends =
414- external = wicd.backends.external
415- ioctl = wicd.backends.ioctl
416-
417diff --git a/setup.py b/setup.py
418old mode 100755
419new mode 100644
420index 510efca..d36e814
421--- a/setup.py
422+++ b/setup.py
423@@ -5,6 +5,7 @@
424 # Copyright (C) 2007 - 2009 Dan O'Reilly
425 # Copyright (C) 2009 Andrew Psaltis
426 # Copyright (C) 2021 - 2022 Andreas Messer
427+# Copyright (C) 2024 Takahiro Yoshizawa
428 #
429 #
430 # This program is free software; you can redistribute it and/or modify
431@@ -19,9 +20,18 @@
432 # You should have received a copy of the GNU General Public License
433 # along with this program. If not, see <http://www.gnu.org/licenses/>.
434 #
435-from setuptools import setup, find_packages
436+from setuptools import setup, find_namespace_packages
437 from setuptools.command.build_py import build_py
438+from setuptools.command.install import install as _install
439+
440+
441+from setuptools import setup
442+from setuptools.command.build_py import build_py as _build_py
443 import os
444+import subprocess
445+import shutil
446+import re
447+from glob import glob
448
449 config_file_template = '''
450 import os
451@@ -38,41 +48,344 @@ pidfile_path = rundir_path + "wicd.pid"
452 resolvconf_backup_path = rundir_path + "resolv.conf.orig"
453 '''
454
455-class wicd_build_py(build_py):
456+
457+class build_py(_build_py):
458 def run(self):
459 config_file = os.path.join(self.build_lib, 'wicd', 'config.py')
460-
461+ os.makedirs(os.path.dirname(config_file), exist_ok=True)
462 with open(config_file, 'wt') as f:
463 print('generating ' + config_file)
464- f.write(config_file_template);
465+ f.write(config_file_template)
466
467+ self.compile_translations()
468+ self.process_templates()
469 super().run()
470
471-def version_scheme(version):
472- if version.exact:
473- return version.format_with("{tag}")
474- else:
475- return "git"
476+ def compile_translations(self):
477+ print('Compiling translations...')
478+ threshold = 0.8
479
480-setup(
481- description = "A wireless and wired network manager",
482- long_description = """A complete network connection manager
483-Wicd supports wired and wireless networks, and capable of
484-creating and tracking profiles for both. It has a
485-template-based wireless encryption system, which allows the user
486-to easily add encryption methods used. It ships with some common
487-encryption types, such as WPA and WEP. Wicd will automatically
488-connect at startup to any preferred network within range.
489-""",
490- setup_requires = ['setuptools_scm'],
491- use_scm_version = {
492- "version_scheme" : version_scheme,
493- },
494+ print("Update wicd.pot with new strings")
495+ os.system('pybabel extract . -o po/wicd.pot --sort-output')
496+
497+ print("Update po-files with new strings from wicd.pot")
498+ for pofile in glob('po/*.po'):
499+ lang = os.path.splitext(os.path.basename(pofile))[0]
500+ os.system(f'pybabel update -N -i po/wicd.pot -d po -D wicd -l {lang}')
501+
502+ print('Compile po-files to binary mo')
503+ if os.path.exists('translations'):
504+ shutil.rmtree('translations/')
505+ os.makedirs('translations')
506+
507+ oldlang = os.environ.get('LANG', 'C')
508+ os.environ['LANG'] = 'C'
509+
510+ for pofile in sorted(glob('po/*.po')):
511+ lang = os.path.splitext(os.path.basename(pofile))[0]
512+ compile_po = False
513+ try:
514+ result = subprocess.run(['msgfmt', '--statistics', pofile, '-o', '/dev/null'],
515+ stderr=subprocess.PIPE, text=True)
516+ output = result.stderr.strip()
517+ if result.returncode != 0 or not output:
518+ print(f"Error processing {pofile}: {result.stderr}")
519+ raise ValueError(f"Error processing {pofile}")
520+ else:
521+ m = re.match(
522+ r'^(?:'
523+ r'(?:(\d+) translated messages)'
524+ r'(?:, )?'
525+ r'(?:(\d+) fuzzy translation(?:s)?)?'
526+ r'(?:, )?'
527+ r'(?:(\d+) untranslated messages?)?'
528+ r')\.?$',
529+ output
530+ )
531+ if m:
532+ done = int(m.group(1) or 0)
533+ fuzzy = int(m.group(2) or 0)
534+ missing = int(m.group(3) or 0)
535+ total = done + fuzzy + missing
536+ completeness = done / total if total > 0 else 0
537+ if completeness >= threshold:
538+ compile_po = True
539+ else:
540+ print(f'Disabled {lang} ({completeness*100:.1f}% < {threshold*100:.1f}%).')
541+ continue
542+ except Exception as e:
543+ print(f'An error occurred while processing {pofile}: {e}')
544+
545+ if compile_po:
546+ mo_dir = os.path.join('translations', lang, 'LC_MESSAGES')
547+ os.makedirs(mo_dir, exist_ok=True)
548+ os.system(f'pybabel compile -D wicd -i {pofile} -l {lang} -d translations/')
549+ os.environ['LANG'] = oldlang
550+
551+ new_data_files = self.find_mo_files()
552+ existing_data_files = self.distribution.data_files or []
553+ self.distribution.data_files = existing_data_files + new_data_files
554+
555+ def find_mo_files(self):
556+ data_files = []
557+ for root, dirs, files in os.walk('translations'):
558+ for file in files:
559+ if file.endswith('.mo'):
560+ lang = os.path.relpath(root, 'translations').split(os.sep)[0]
561+ mo_file = os.path.join(root, file)
562+ install_dir = os.path.join('share', 'locale', lang, 'LC_MESSAGES')
563+ data_files.append((install_dir, [mo_file]))
564+ return data_files
565+
566+ def initialize_options(self):
567+ super().initialize_options()
568+ self.init = ''
569+ self.lib = '/usr/lib/wicd/'
570+ self.share = '/usr/share/wicd/'
571+ self.etc = '/etc/wicd/'
572+ self.scripts = self.share + "scripts/"
573+ self.encryption = self.share + 'encryption/templates/'
574+ self.bin = '/usr/bin/'
575+ self.sbin = '/usr/sbin/'
576+ self.daemon = self.share + 'daemon'
577+ self.backends = self.share + 'backends'
578+ self.curses = self.share + 'curses'
579+ self.gtk = self.share + 'gtk'
580+ self.cli = self.share + 'cli'
581+ self.gnome_shell_extensions = '/usr/share/gnome-shell/extensions/'
582+ self.icons = '/usr/share/icons/hicolor/'
583+ self.pixmaps = '/usr/share/pixmaps/'
584+ self.images = '/usr/share/icons/'
585+ self.varlib = '/var/lib/wicd/'
586+ self.networks = self.varlib + 'configurations/'
587+ self.log = '/var/log/wicd/'
588+ self.resume = '/etc/acpi/resume.d/'
589+ self.suspend = '/etc/acpi/suspend.d/'
590+ self.pmutils = '/usr/lib/pm-utils/sleep.d/'
591+ self.dbus = '/etc/dbus-1/system.d/'
592+ self.dbus_service = '/usr/share/dbus-1/system-services/'
593+ self.systemd = '/lib/systemd/system/'
594+ self.logrotate = '/etc/logrotate.d/'
595+ self.desktop = '/usr/share/applications/'
596+ self.translations = '/usr/share/locale/'
597+ self.autostart = '/etc/xdg/autostart/'
598+ self.docdir = '/usr/share/doc/wicd/'
599+ self.mandir = '/usr/share/man/'
600+ self.kdedir = '/usr/share/autostart/'
601+ self.distro = 'auto'
602+
603+ self.no_install_init = False
604+ self.no_install_man = False
605+ self.no_install_i18n = False
606+ self.no_install_i18n_man = False
607+ self.no_install_kde = False
608+ self.no_install_acpi = False
609+ self.no_install_pmutils = False
610+ self.no_install_docs = False
611+ self.no_install_gtk = False
612+ self.no_install_ncurses = False
613+ self.no_install_cli = False
614+ self.no_install_gnome_shell_extensions = False
615+ self.no_use_notifications = False
616+
617+ self.initfile = 'init/default/wicd'
618+ # ddistro is the detected distro
619+ if os.path.exists('/etc/redhat-release'):
620+ self.ddistro = 'redhat'
621+ elif os.path.exists('/etc/SuSE-release'):
622+ self.ddistro = 'suse'
623+ elif os.path.exists('/etc/fedora-release'):
624+ self.ddistro = 'redhat'
625+ elif os.path.exists('/etc/gentoo-release'):
626+ self.ddistro = 'gentoo'
627+ elif os.path.exists('/etc/debian_version'):
628+ self.ddistro = 'debian'
629+ elif os.path.exists('/etc/arch-release'):
630+ self.ddistro = 'arch'
631+ elif os.path.exists('/etc/slackware-version') or \
632+ os.path.exists('/etc/slamd64-version') or \
633+ os.path.exists('/etc/bluewhite64-version'):
634+ self.ddistro = 'slackware'
635+ elif os.path.exists('/etc/pld-release'):
636+ self.ddistro = 'pld'
637+ elif os.path.exists('/etc/lunar.release'):
638+ self.distro = 'lunar'
639+ elif os.path.exists('/etc/plamo-release'):
640+ self.ddistro = 'plamo'
641+ else:
642+ self.ddistro = 'FAIL'
643+ #self.no_install_init = True
644+ #self.distro_detect_failed = True
645+ print('WARNING: Unable to detect the distribution in use. ' + \
646+ 'If you have specified --distro or --init and --initfile, configure will continue. ' + \
647+ 'Please report this warning, along with the name of your ' + \
648+ 'distribution, to the wicd developers.')
649+
650+ # Try to get the pm-utils sleep hooks directory from pkg-config and
651+ # the kde prefix from kde-config
652+ # Don't run these in a shell because it's not needed and because shell
653+ # swallows the OSError we would get if {pkg,kde}-config do not exist
654+ # If we don't get anything from *-config, or it didn't run properly,
655+ # or the path is not a proper absolute path, raise an error
656+ try:
657+ pmtemp = subprocess.Popen(["pkg-config", "--variable=pm_sleephooks",
658+ "pm-utils"], stdout=subprocess.PIPE)
659+ returncode = pmtemp.wait() # let it finish, and get the exit code
660+ pmutils_candidate = str(pmtemp.stdout.readline().strip()) # read stdout
661+ if len(pmutils_candidate) == 0 or returncode != 0 or \
662+ not os.path.isabs(pmutils_candidate):
663+ raise ValueError
664+ else:
665+ self.pmutils = pmutils_candidate
666+ except (OSError, ValueError, FileNotFoundError):
667+ pass # use our default
668+
669+ try:
670+ kdetemp = subprocess.Popen(["kde-config","--prefix"], stdout=subprocess.PIPE)
671+ returncode = kdetemp.wait() # let it finish, and get the exit code
672+ kdedir_candidate = str(kdetemp.stdout.readline().strip()) # read stdout
673+ if len(kdedir_candidate) == 0 or returncode != 0 or \
674+ not os.path.isabs(kdedir_candidate):
675+ raise ValueError
676+ else:
677+ self.kdedir = kdedir_candidate + '/share/autostart'
678+ except (OSError, ValueError, FileNotFoundError):
679+ # If kde-config isn't present, we'll check for kde-4.x
680+ try:
681+ kde4temp = subprocess.Popen(["kde4-config","--prefix"], stdout=subprocess.PIPE)
682+ returncode = kde4temp.wait() # let it finish, and get the exit code
683+ kde4dir_candidate = str(kde4temp.stdout.readline().strip()) # read stdout
684+ if len(kde4dir_candidate) == 0 or returncode != 0 or \
685+ not os.path.isabs(kde4dir_candidate):
686+ raise ValueError
687+ else:
688+ self.kdedir = kde4dir_candidate + '/share/autostart'
689+ except (OSError, ValueError, FileNotFoundError):
690+ # If neither kde-config nor kde4-config are not present or
691+ # return an error, then we can assume that kde isn't installed
692+ # on the user's system
693+ self.no_install_kde = True
694+ # If the assumption above turns out to be wrong, do this:
695+ #pass # use our default
696+
697+ self.python = '/usr/bin/python3'
698+ self.pidfile = '/var/run/wicd/wicd.pid'
699+ self.initfilename = os.path.basename(self.initfile)
700+ self.wicdgroup = 'users'
701+ self.loggroup = ''
702+ self.logperms = '0600'
703+
704+ def process_templates(self):
705+ # Retrieve version information for curses
706+ try:
707+ import curses
708+ print("curses version: ", curses.version)
709+ if isinstance(curses.version, bytes):
710+ curses_version = curses.version.decode('utf-8').strip().split()[0].lstrip("b'")
711+ else:
712+ curses_version = curses.version.split()[0]
713+ except (AttributeError, IndexError):
714+ curses_version = 'unknown'
715
716- author = "Tom Van Braeckel, Adam Blackburn, Dan O'Reilly, Andrew Psaltis, David Paleino, Andreas Messer",
717- author_email = "tomvanbraeckel@gmail.com, compwiz18@gmail.com, oreilldf@gmail.com, ampsaltis@gmail.com, d.paleino@gmail.com, andi@bastelmap.de",
718- url = "https://launchpad.net/wicd",
719- license = "http://www.gnu.org/licenses/old-licenses/gpl-2.0.html",
720+ # Retrieve version information from setuptools.
721+ version = self.distribution.get_version()
722
723- cmdclass = { "build_py" : wicd_build_py },
724+ # Retrieve the revision from setup.cfg.
725+ revision_num = getattr(self.distribution.metadata, 'revision', '0')
726+
727+ # Convert the attributes of self into values.
728+ values = {attr.upper(): getattr(self, attr) for attr in dir(self)
729+ if not callable(getattr(self, attr)) and not attr.startswith("_")}
730+
731+ # Additional placeholder information.
732+ values.update({
733+ 'VERSION': version,
734+ 'REVISION': revision_num,
735+ 'CURSES_REVNO': curses_version,
736+ })
737+
738+ print('Processing templates...')
739+ in_dir = 'in'
740+
741+ for item in os.listdir(in_dir):
742+ if item.endswith('.in'):
743+ original_name = os.path.join(in_dir, item)
744+ final_name = item[:-3].replace('=', '/')
745+
746+ if final_name == 'wpath.py':
747+ full_path = os.path.join(self.build_lib, 'wicd', final_name)
748+ else:
749+ full_path = os.path.join('template', final_name)
750+
751+ os.makedirs(os.path.dirname(full_path), exist_ok=True)
752+
753+ try:
754+ with open(original_name, 'r') as infile, open(full_path, 'w') as outfile:
755+ for line in infile:
756+ for key, value in values.items():
757+ placeholder = f'%{key.upper()}%'
758+ line = line.replace(placeholder, str(value))
759+
760+ line = line.replace('%VERSION%', str(version))
761+ line = line.replace('%REVNO%', str(revision_num))
762+ line = line.replace('%CURSES_REVNO%', str(curses_version))
763+
764+ outfile.write(line)
765+
766+ shutil.copymode(original_name, full_path)
767+ print(f"Processed template: {original_name} -> {full_path}")
768+
769+ except FileNotFoundError:
770+ print(f"ERROR: Template file {original_name} not found.")
771+ except Exception as e:
772+ print(f"ERROR: Failed to process {original_name}: {e}")
773+
774+# new_data_files = self.find_templates()
775+# existing_data_files = self.distribution.data_files or []
776+# self.distribution.data_files = existing_data_files + new_data_files
777+
778+# def find_templates(self):
779+# data_files = []
780+# if self.ddistro == "redhat":
781+# init = '/etc/rc.d/init.d/'
782+# initfile = 'init/redhat/wicd'
783+# elif self.ddistro == "suse":
784+# init = '/etc/init.d/'
785+# initfile = 'init/suse/wicd'
786+# elif self.ddistro == "gentoo":
787+# init = '/etc/init.d/'
788+# initfile = 'init/gentoo/wicd'
789+# elif self.ddistro == "debian":
790+# init = '/etc/init.d/'
791+# initfile = 'init/debian/wicd'
792+# elif self.ddistro == "arch":
793+# init = '/etc/rc.d/'
794+# initfile = 'init/arch/wicd'
795+# elif self.ddistro == "slackware":
796+# init = '/etc/rc.d/'
797+# initfile = 'init/slackware/rc.wicd'
798+# elif self.ddistro == "pld":
799+# init = '/etc/rc.d/init.d/'
800+# initfile = 'init/pld/wicd'
801+# elif self.ddistro == "lunar":
802+# init='/etc/init.d/'
803+# initfile = 'init/lunar/wicd'
804+# elif self.ddistro == "plamo":
805+# init='/etc/rc.d/init.d/'
806+# initfile = 'init/plamo/wicd'
807+# else:
808+# print("Distro not supported for init script installation.")
809+# init='/etc/rc.d/init.d/'
810+# initfile = 'init/redhat/wicd'
811+#
812+# initfile_path = os.path.join('template', initfile)
813+# data_files.append((init, [initfile_path]))
814+#
815+# return data_files
816+
817+
818+setup(
819+ cmdclass={
820+ 'build_py': build_py,
821+ },
822 )
823diff --git a/src/wicd/commandline.py b/src/wicd/commandline.py
824index 02bbb57..f7b1a90 100644
825--- a/src/wicd/commandline.py
826+++ b/src/wicd/commandline.py
827@@ -4,6 +4,7 @@
828 # wicd commandline handling
829 #
830 # Copyright (C) 2021 Andreas Messer
831+# Copyright (C) 2024 Takahiro Yoshizawa
832 #
833 # This program is free software; you can redistribute it and/or modify
834 # it under the terms of the GNU General Public License Version 2 as
835@@ -29,7 +30,7 @@ class CommandlineManager(object):
836 @classmethod
837 def get_args(cls):
838 if hasattr(cls, "parser"):
839- cls.args = cls.parser.parse_args()
840+ cls.args, unknown = cls.parser.parse_known_args()
841 del cls.parser
842
843 return cls.args
844diff --git a/src/wicd/configmanager.py b/src/wicd/configmanager.py
845index b5851dd..2699b97 100644
846--- a/src/wicd/configmanager.py
847+++ b/src/wicd/configmanager.py
848@@ -191,6 +191,7 @@ class ConfigManager(RawConfigParser):
849 print("_write_one", to_unicode(section))
850 if not to_unicode(section):
851 self.remove_section(to_unicode(section))
852+ os.makedirs(os.path.dirname(self.config_file), exist_ok=True)
853 configfile = open(self.config_file, 'w')
854 RawConfigParser.write(self, configfile)
855 configfile.close()
856@@ -216,7 +217,7 @@ class ConfigManager(RawConfigParser):
857 entries in these files override entries in the main file.
858 """
859 if os.path.exists(path):
860- RawConfigParser.readfp(self, codecs.open(path, 'r', 'utf-8'))
861+ RawConfigParser.read_file(self, codecs.open(path, 'r', 'utf-8'))
862
863 path_d = path + ".d"
864 files = []
865@@ -227,7 +228,7 @@ class ConfigManager(RawConfigParser):
866
867 for fname in files:
868 p = RawConfigParser()
869- p.readfp(codecs.open(fname, 'r', 'utf-8'))
870+ p.read_file(codecs.open(fname, 'r', 'utf-8'))
871 for section_name in p.sections():
872 # New files override old, so remove first to avoid
873 # DuplicateSectionError.
874diff --git a/src/wicd/daemon/__main__.py b/src/wicd/daemon/__main__.py
875index e51c541..71e9bf6 100644
876--- a/src/wicd/daemon/__main__.py
877+++ b/src/wicd/daemon/__main__.py
878@@ -20,6 +20,7 @@ class WirelessDaemon() -- DBus interface to managed the wireless network.
879 # Copyright (C) 2007 - 2009 Byron Hillis
880 # Copyright (C) 2009 Andrew Psaltis
881 # Copyright (C) 2021 Andreas Messer
882+# Copyright (C) 2024 Takahiro Yoshizawa
883 #
884 # This program is free software; you can redistribute it and/or modify
885 # it under the terms of the GNU General Public License Version 2 as
886@@ -33,9 +34,9 @@ class WirelessDaemon() -- DBus interface to managed the wireless network.
887 # You should have received a copy of the GNU General Public License
888 # along with this program. If not, see <http://www.gnu.org/licenses/>.
889 #
890-
891 import wicd.log
892 import wicd.config
893+import wicd.errors
894 import lockfile
895 import daemon
896 import os
897@@ -47,6 +48,7 @@ import signal
898 import atexit
899 from subprocess import Popen
900 from operator import itemgetter
901+from wicd.dbus import dbus_manager
902
903 # DBUS
904 from gi.repository import GLib as gobject
905@@ -65,6 +67,11 @@ import wicd.dbus
906
907 import wicd.pkg_helpers
908
909+from wicd import wpath
910+
911+redirect_stderr = False
912+redirect_stdout = False
913+
914 misc.RenameProcess("wicd")
915
916 class WicdDaemon(wicd.dbus.service.Object, wicd.log.LogAble):
917@@ -1013,10 +1020,16 @@ class WirelessDaemon(dbus.service.Object, object):
918 self.ReadWirelessNetworkProfile(i)
919 self.SendEndScanSignal()
920
921+# @dbus.service.method('org.wicd.daemon.wireless')
922+# def GetIwconfig(self):
923+# """ Calls and returns the output of iwconfig"""
924+# return misc.to_unicode(self.wifi.GetIwconfig())
925+
926 @dbus.service.method('org.wicd.daemon.wireless')
927- def GetIwconfig(self):
928+ def GetIwLink(self):
929 """ Calls and returns the output of iwconfig"""
930- return misc.to_unicode(self.wifi.GetIwconfig())
931+ return misc.to_unicode(self.wifi.GetIwLink())
932+
933
934 @dbus.service.method('org.wicd.daemon.wireless')
935 def GetNumberOfNetworks(self):
936@@ -1029,19 +1042,19 @@ class WirelessDaemon(dbus.service.Object, object):
937 return self.wifi.GetBSSID()
938
939 @dbus.service.method('org.wicd.daemon.wireless')
940- def GetCurrentBitrate(self, iwconfig):
941+ def GetCurrentBitrate(self, iwlink):
942 """ Returns the current bitrate for the active network. """
943- return self.wifi.GetCurrentBitrate(iwconfig)
944+ return self.wifi.GetCurrentBitrate(iwlink)
945
946 @dbus.service.method('org.wicd.daemon.wireless')
947- def GetOperationalMode(self, iwconfig):
948+ def GetOperationalMode(self, iwlink):
949 """ Returns the operational mode for the iwconfig parameter """
950- return misc.to_unicode(self.wifi.GetOperationalMode(iwconfig))
951+ return misc.to_unicode(self.wifi.GetOperationalMode(iwlink))
952
953- @dbus.service.method('org.wicd.daemon.wireless')
954- def GetAvailableAuthMethods(self, iwlistauth):
955- """ Returns the operational mode for the iwlistauth parameter """
956- return misc.to_unicode(self.wifi.GetAvailableAuthMethods(iwlistauth))
957+# @dbus.service.method('org.wicd.daemon.wireless')
958+# def GetAvailableAuthMethods(self, iwlistauth):
959+# """ Returns the operational mode for the iwlistauth parameter """
960+# return misc.to_unicode(self.wifi.GetAvailableAuthMethods(iwlistauth))
961
962 @dbus.service.method('org.wicd.daemon.wireless')
963 def GetAvailableBitrates(self):
964@@ -1066,6 +1079,12 @@ class WirelessDaemon(dbus.service.Object, object):
965 self.wifi.CreateAdHocNetwork(essid, channel, ip, enctype, key, encused)
966
967 @dbus.service.method('org.wicd.daemon.wireless')
968+ def GetMode(self):
969+ """ Gets the MAC address for the active network. """
970+ return self.wifi.GetMode()
971+
972+
973+ @dbus.service.method('org.wicd.daemon.wireless')
974 def GetKillSwitchEnabled(self):
975 """ Returns true if kill switch is pressed. """
976 status = self.wifi.GetKillSwitchStatus()
977@@ -1129,33 +1148,33 @@ class WirelessDaemon(dbus.service.Object, object):
978 return self.wifi.IsUp()
979
980 @dbus.service.method('org.wicd.daemon.wireless')
981- def GetCurrentSignalStrength(self, iwconfig=None):
982+ def GetCurrentSignalStrength(self, iwlink=None):
983 """ Returns the current signal strength. """
984 try:
985- strength = int(self.wifi.GetSignalStrength(iwconfig))
986+ strength = int(self.wifi.GetSignalStrength(iwlink))
987 except TypeError:
988 strength = 0
989 return strength
990
991 @dbus.service.method('org.wicd.daemon.wireless')
992- def GetCurrentDBMStrength(self, iwconfig=None):
993+ def GetCurrentDBMStrength(self, iwlink=None):
994 """ Returns the current dbm signal strength. """
995 try:
996- dbm_strength = int(self.wifi.GetDBMStrength(iwconfig))
997+ dbm_strength = int(self.wifi.GetDBMStrength(iwlink))
998 except:
999 dbm_strength = 0
1000 return dbm_strength
1001
1002 @dbus.service.method('org.wicd.daemon.wireless')
1003- def GetCurrentNetwork(self, iwconfig=None):
1004+ def GetCurrentNetwork(self, iwlink=None):
1005 """ Returns the current network. """
1006- current_network = str(self.wifi.GetCurrentNetwork(iwconfig))
1007+ current_network = str(self.wifi.GetCurrentNetwork(iwlink))
1008 return current_network
1009
1010 @dbus.service.method('org.wicd.daemon.wireless')
1011- def GetCurrentNetworkID(self, iwconfig=None):
1012+ def GetCurrentNetworkID(self, iwlink=None):
1013 """ Returns the id of the current network, or -1 if its not found. """
1014- currentESSID = self.GetCurrentNetwork(iwconfig)
1015+ currentESSID = self.GetCurrentNetwork(iwlink)
1016 for x in range(0, len(self.LastScan)):
1017 if self.LastScan[x]['essid'] == currentESSID:
1018 return x
1019@@ -1351,6 +1370,8 @@ class WirelessDaemon(dbus.service.Object, object):
1020 ret.append((section.replace('essid:', ''), 'None'))
1021 else:
1022 essid = self.config.get(section, 'essid')
1023+ if essid is None:
1024+ essid = 'None'
1025 ret.append((essid, section))
1026 return sorted(ret, key=itemgetter(0))
1027
1028@@ -1402,6 +1423,13 @@ class WirelessDaemon(dbus.service.Object, object):
1029 return
1030 print("Unable to autoconnect, you'll have to manually connect")
1031
1032+ @dbus.service.method('org.wicd.daemon.wireless')
1033+ def SetLastScan(self, key, value):
1034+ if self.LastScan:
1035+ self.LastScan[key] = value
1036+
1037+
1038+
1039 ###########################
1040 ###### Wired Daemon #######
1041 ###########################
1042@@ -1695,6 +1723,51 @@ def spawn_monitor():
1043
1044 atexit.register(daemon_killmonitor)
1045
1046+def determine_root():
1047+ if os.getuid() != 0:
1048+ print(("Root privileges are required for the daemon to run properly." +
1049+ " Exiting."))
1050+ sys.exit(1)
1051+
1052+ if not os.path.exists(wpath.networks):
1053+ os.makedirs(wpath.networks)
1054+
1055+def check_duplicates():
1056+ if os.path.exists(wpath.pidfile):
1057+ print('It seems like the daemon is already running.')
1058+ print('If it is not, please remove %s and try again.' % wpath.pidfile)
1059+ sys.exit(1)
1060+
1061+def redirect_output():
1062+ global redirect_stderr, redirect_stdout
1063+ output = None
1064+ group = [""]
1065+ if redirect_stderr or redirect_stdout:
1066+ logpath = os.path.join(wpath.log, 'wicd.log')
1067+ if not os.path.exists(wpath.log):
1068+ os.makedirs(wpath.log)
1069+ os.chmod(wpath.log, 0o755)
1070+ output = ManagedStdio(logpath)
1071+ if os.path.exists(logpath):
1072+ try:
1073+ os.chmod(logpath, int(wpath.log_perms, 8))
1074+ except OSError:
1075+ print('unable to chmod log file to %s' % wpath.log_perms)
1076+
1077+ try:
1078+ if wpath.log_group:
1079+ import grp
1080+ group = grp.getgrnam(wpath.log_group)
1081+ os.chown(logpath, 0, group[2])
1082+ except OSError:
1083+ print('unable to chown log file to %s' % group[2])
1084+
1085+ if redirect_stdout:
1086+ sys.stdout = output
1087+ if redirect_stderr:
1088+ sys.stderr= output
1089+
1090+
1091 def daemon_main():
1092 print('---------------------------')
1093 print('wicd initializing...')
1094@@ -1717,31 +1790,69 @@ def daemon_main():
1095 daemon.DaemonClosing()
1096
1097 def run_daemon():
1098+ determine_root()
1099+ check_duplicates()
1100+ redirect_output()
1101+
1102 os.makedirs(wicd.config.rundir_path, exist_ok=True)
1103
1104- context = daemon.DaemonContext(
1105- working_directory = wicd.config.rundir_path,
1106- umask = 0o077,
1107- pidfile = lockfile.FileLock(wicd.config.pidfile_path)
1108- )
1109+ std_out = sys.stdout
1110+ std_err = sys.stderr
1111+
1112+ try:
1113+ with daemon.DaemonContext(
1114+ working_directory = wicd.config.rundir_path,
1115+ umask = 0o077,
1116+ pidfile = lockfile.FileLock(wicd.config.pidfile_path),
1117+ stdout = std_out,
1118+ stderr = std_err,
1119+ signal_map = {
1120+ signal.SIGTERM: on_exit,
1121+ signal.SIGINT: on_exit,
1122+ }
1123+ ):
1124+
1125+ try:
1126+ pid = os.getpid()
1127+ with open(wicd.config.pidfile_path, 'w') as pid_file:
1128+ pid_file.write(f"{pid}\n")
1129+ daemon_main()
1130+ except Exception as e:
1131+ print(f"Error in daemon: {e}")
1132+
1133+ except lockfile.AlreadyLocked:
1134+ print("wicd is already running, aborting.", file=sys.stderr)
1135+ sys.exit(1)
1136
1137- with context:
1138- daemon_main()
1139+ except lockfile.LockFailed as e:
1140+ print(f"Error locking pidfile: {e}")
1141
1142 def run_foreground():
1143+ determine_root()
1144+ check_duplicates()
1145 daemon_main()
1146
1147 def kill_daemon():
1148+ determine_root()
1149+ redirect_output()
1150+
1151 try:
1152 f = open(wicd.config.pidfile_path)
1153 except IOError:
1154- #print >> sys.stderr, "No wicd instance active, aborting."
1155+ print("No wicd instance active, aborting.", file=sys.stderr)
1156 sys.exit(1)
1157
1158- # connect to dbus, trigger a disconnect, then knock out the daemon
1159- from wicd import dbusmanager
1160- bus = dbusmanager.connect_to_dbus()
1161- dbus_ifaces = dbusmanager.get_dbus_ifaces()
1162+# bus = dbus_manager.connect_to_dbus()
1163+# dbus_ifaces = dbus_manager.get_dbus_ifaces()
1164+ try:
1165+ dbus_manager.connect()
1166+ except wicd.errors.WiCDDaemonNotFound:
1167+ pid = int(f.readline())
1168+ f.close()
1169+ os.kill(pid, signal.SIGTERM)
1170+ return
1171+
1172+ dbus_ifaces = dbus_manager.get_dbus_ifaces()
1173 dbus_ifaces['daemon'].Disconnect()
1174 pid = int(f.readline())
1175 f.close()
1176@@ -1775,45 +1886,16 @@ def main():
1177 parser.add_argument('-o', '--no-stdout', dest='redirect_stdout', action='store_false', default=True,
1178 help=u"Don't redirect stdout")
1179
1180+ args = parser.parse_args()
1181+
1182+ global redirect_stderr, redirect_stdout
1183+ redirect_stderr = args.redirect_stderr
1184+ redirect_stdout = args.redirect_stdout
1185
1186 sys.exit(wicd.commandline.get_args().action() or 0)
1187
1188- if os.getuid() != 0:
1189- print(("Root privileges are required for the daemon to run properly." +
1190- " Exiting."))
1191- sys.exit(1)
1192-
1193- if not os.path.exists(wpath.networks):
1194- os.makedirs(wpath.networks)
1195-
1196- if redirect_stderr or redirect_stdout:
1197- logpath = os.path.join(wpath.log, 'wicd.log')
1198- if not os.path.exists(wpath.log):
1199- os.makedirs(wpath.log)
1200- os.chmod(wpath.log, 0o755)
1201- output = ManagedStdio(logpath)
1202- if os.path.exists(logpath):
1203- try:
1204- os.chmod(logpath, int(wpath.log_perms, 8))
1205- except OSError:
1206- print('unable to chmod log file to %s' % wpath.log_perms)
1207-
1208- try:
1209- if wpath.log_group:
1210- import grp
1211- group = grp.getgrnam(wpath.log_group)
1212- os.chown(logpath, 0, group[2])
1213- except OSError:
1214- print('unable to chown log file to %s' % group[2])
1215-
1216- if redirect_stdout:
1217- sys.stdout = output
1218- if redirect_stderr:
1219- sys.stderr = output
1220-
1221-
1222
1223-def on_exit(child_pid):
1224+def on_exit(signum, frame):
1225 """ Called when a SIGTERM is caught, kills monitor.py before exiting. """
1226 print('Removing PID file...')
1227 if os.path.exists(wpath.pidfile):
1228@@ -1822,4 +1904,4 @@ def on_exit(child_pid):
1229 sys.exit(0)
1230
1231 if __name__ == '__main__':
1232- main()
1233\ No newline at end of file
1234+ main()
1235diff --git a/src/wicd/daemon/monitor.py b/src/wicd/daemon/monitor.py
1236index 89f4e6b..20c0142 100755
1237--- a/src/wicd/daemon/monitor.py
1238+++ b/src/wicd/daemon/monitor.py
1239@@ -10,6 +10,7 @@ when appropriate.
1240 #
1241 # Copyright (C) 2007 - 2009 Adam Blackburn
1242 # Copyright (C) 2007 - 2009 Dan O'Reilly
1243+# Copyright (C) 2024 Takahiro Yoshizawa
1244 #
1245 # This program is free software; you can redistribute it and/or modify
1246 # it under the terms of the GNU General Public License Version 2 as
1247@@ -84,7 +85,7 @@ class ConnectionStatus(object):
1248 self.reconnecting = False
1249 self.reconnect_tries = 0
1250 self.signal_changed = False
1251- self.iwconfig = ""
1252+ self.iwlink = ""
1253 self.trigger_reconnect = False
1254 self.__lost_dbus_count = 0
1255 self._to_time = daemon.GetBackendUpdateInterval()
1256@@ -162,7 +163,7 @@ class ConnectionStatus(object):
1257 if the signal strength is 0, and if it remains there
1258 for too long, triggers a wireless disconnect.
1259
1260- Returns True if wireless connection is active, and
1261+ Returns True if wireless connection is active, and
1262 False otherwise.
1263
1264 """
1265@@ -172,9 +173,9 @@ class ConnectionStatus(object):
1266 return False
1267
1268 if daemon.NeedsExternalCalls():
1269- self.iwconfig = wireless.GetIwconfig()
1270+ self.iwlink = wireless.GetIwLink()
1271 else:
1272- self.iwconfig = ''
1273+ self.iwlink = ''
1274 # Reset this, just in case.
1275 self.tried_reconnect = False
1276 bssid = wireless.GetApBssid()
1277@@ -196,11 +197,11 @@ class ConnectionStatus(object):
1278 self.connection_lost_counter = 0
1279
1280 if (wifi_signal != self.last_strength or
1281- self.network != self.last_network):
1282+ self.network != self.last_network):
1283 self.last_strength = wifi_signal
1284 self.last_network = self.network
1285 self.signal_changed = True
1286- daemon.SetCurrentInterface(daemon.GetWirelessInterface())
1287+ daemon.SetCurrentInterface(daemon.GetWirelessInterface())
1288
1289 return True
1290
1291@@ -255,12 +256,14 @@ class ConnectionStatus(object):
1292 daemon.AutoConnect(False, reply_handler=lambda *a:None,
1293 error_handler=lambda *a:None)
1294 return self.update_state(misc.NOT_CONNECTED)
1295+
1296+ wireless.SetLastScan('operating_mode', wireless.GetMode())
1297 return self.update_state(misc.WIRELESS, wifi_ip=wifi_ip)
1298
1299 state = misc.NOT_CONNECTED
1300 if self.last_state == misc.WIRELESS:
1301 from_wireless = True
1302- else:
1303+ elif not wireless.GetMode().lower() == "ibss":
1304 from_wireless = False
1305 self.auto_reconnect(from_wireless)
1306 return self.update_state(state)
1307@@ -268,7 +271,7 @@ class ConnectionStatus(object):
1308 def update_state(self, state, wired_ip=None, wifi_ip=None):
1309 """ Set the current connection state. """
1310 # Set our connection state/info.
1311- iwconfig = self.iwconfig
1312+ iwlink = self.iwlink
1313 if state == misc.NOT_CONNECTED:
1314 info = [""]
1315 elif state == misc.SUSPENDED:
1316@@ -278,14 +281,14 @@ class ConnectionStatus(object):
1317 info = ["wired"]
1318 else:
1319 info = ["wireless",
1320- misc.noneToBlankString(wireless.GetCurrentNetwork(iwconfig))]
1321+ misc.noneToBlankString(wireless.GetCurrentNetwork(iwlink))]
1322 elif state == misc.WIRELESS:
1323 self.reconnect_tries = 0
1324 info = [str(wifi_ip),
1325- misc.noneToBlankString(wireless.GetCurrentNetwork(iwconfig)),
1326+ misc.noneToBlankString(wireless.GetCurrentNetwork(iwlink)),
1327 str(self._get_printable_sig_strength()),
1328- str(wireless.GetCurrentNetworkID(iwconfig)),
1329- misc.noneToBlankString(wireless.GetCurrentBitrate(iwconfig))]
1330+ str(wireless.GetCurrentNetworkID(iwlink)),
1331+ misc.noneToBlankString(wireless.GetCurrentBitrate(iwlink))]
1332 elif state == misc.WIRED:
1333 self.reconnect_tries = 0
1334 info = [str(wired_ip)]
1335@@ -296,12 +299,12 @@ class ConnectionStatus(object):
1336 daemon.SetConnectionStatus(state, info)
1337
1338 # Send a D-Bus signal announcing status has changed if necessary.
1339- if (state != self.last_state or (state == misc.WIRELESS and
1340+ if (state != self.last_state or (state == misc.WIRELESS and
1341 self.signal_changed)):
1342 daemon.EmitStatusChanged(state, info)
1343
1344 if (state != self.last_state) and (state == misc.NOT_CONNECTED) and \
1345- (not daemon.GetForcedDisconnect()):
1346+ (not daemon.GetForcedDisconnect()) and (wireless.GetMode(iwlink).lower() != 'ibss'):
1347 daemon.Disconnect()
1348 # Disconnect() sets forced disconnect = True
1349 # so we'll revert that
1350@@ -313,10 +316,10 @@ class ConnectionStatus(object):
1351 """ Get the correct signal strength format. """
1352 try:
1353 if daemon.GetSignalDisplayType() == 0:
1354- signal = wireless.GetCurrentSignalStrength(self.iwconfig)
1355+ signal = wireless.GetCurrentSignalStrength(self.iwlink)
1356 wifi_signal = int(signal)
1357 else:
1358- signal = wireless.GetCurrentDBMStrength(self.iwconfig)
1359+ signal = wireless.GetCurrentDBMStrength(self.iwlink)
1360 if always_positive:
1361 # because dBm is negative, add 99 to the signal. This way,
1362 # if the signal drops below -99, wifi_signal will == 0, and
1363@@ -326,7 +329,7 @@ class ConnectionStatus(object):
1364 else:
1365 wifi_signal = int(signal)
1366 except TypeError:
1367- wifi_signal = 0
1368+ wifi_signal = 0
1369
1370 return wifi_signal
1371
1372@@ -357,7 +360,7 @@ class ConnectionStatus(object):
1373
1374 # If we just lost a wireless connection, try to connect to that
1375 # network again. Otherwise just call Autoconnect.
1376- cur_net_id = wireless.GetCurrentNetworkID(self.iwconfig)
1377+ cur_net_id = wireless.GetCurrentNetworkID(self.iwlink)
1378 if from_wireless and cur_net_id > -1:
1379 # make sure disconnect scripts are run
1380 # before we reconnect
1381diff --git a/src/wicd/dbus.py b/src/wicd/dbus.py
1382index a98d275..e017cec 100644
1383--- a/src/wicd/dbus.py
1384+++ b/src/wicd/dbus.py
1385@@ -30,8 +30,11 @@ wicd.commandline.get_parser().add_argument('--session-dbus', dest='DBus',
1386 if getattr(dbus, "version", (0, 0, 0)) < (0, 80, 0):
1387 import dbus.glib
1388 else:
1389- from dbus.mainloop.glib import DBusGMainLoop
1390- DBusGMainLoop(set_as_default=True)
1391+ import sys
1392+
1393+ if not any(mod.startswith('wicd.frontends.gtk') for mod in sys.modules):
1394+ from dbus.mainloop.glib import DBusGMainLoop
1395+ DBusGMainLoop(set_as_default=True)
1396
1397
1398
1399@@ -101,4 +104,4 @@ class DBusManager(object):
1400 dbus_manager = DBusManager()
1401 del DBusManager
1402
1403-method = service.method
1404\ No newline at end of file
1405+method = service.method
1406diff --git a/encryption/templates/active b/src/wicd/encryption/templates/active
1407similarity index 100%
1408rename from encryption/templates/active
1409rename to src/wicd/encryption/templates/active
1410diff --git a/encryption/templates/active_wired b/src/wicd/encryption/templates/active_wired
1411similarity index 100%
1412rename from encryption/templates/active_wired
1413rename to src/wicd/encryption/templates/active_wired
1414diff --git a/encryption/templates/eap b/src/wicd/encryption/templates/eap
1415similarity index 100%
1416rename from encryption/templates/eap
1417rename to src/wicd/encryption/templates/eap
1418diff --git a/encryption/templates/eap-tls b/src/wicd/encryption/templates/eap-tls
1419similarity index 100%
1420rename from encryption/templates/eap-tls
1421rename to src/wicd/encryption/templates/eap-tls
1422diff --git a/encryption/templates/leap b/src/wicd/encryption/templates/leap
1423similarity index 100%
1424rename from encryption/templates/leap
1425rename to src/wicd/encryption/templates/leap
1426diff --git a/encryption/templates/peap b/src/wicd/encryption/templates/peap
1427similarity index 100%
1428rename from encryption/templates/peap
1429rename to src/wicd/encryption/templates/peap
1430diff --git a/encryption/templates/peap-tkip b/src/wicd/encryption/templates/peap-tkip
1431similarity index 100%
1432rename from encryption/templates/peap-tkip
1433rename to src/wicd/encryption/templates/peap-tkip
1434diff --git a/encryption/templates/psu b/src/wicd/encryption/templates/psu
1435similarity index 100%
1436rename from encryption/templates/psu
1437rename to src/wicd/encryption/templates/psu
1438diff --git a/encryption/templates/ttls b/src/wicd/encryption/templates/ttls
1439similarity index 100%
1440rename from encryption/templates/ttls
1441rename to src/wicd/encryption/templates/ttls
1442diff --git a/encryption/templates/wep-hex b/src/wicd/encryption/templates/wep-hex
1443similarity index 100%
1444rename from encryption/templates/wep-hex
1445rename to src/wicd/encryption/templates/wep-hex
1446diff --git a/encryption/templates/wep-passphrase b/src/wicd/encryption/templates/wep-passphrase
1447similarity index 100%
1448rename from encryption/templates/wep-passphrase
1449rename to src/wicd/encryption/templates/wep-passphrase
1450diff --git a/encryption/templates/wep-shared b/src/wicd/encryption/templates/wep-shared
1451similarity index 100%
1452rename from encryption/templates/wep-shared
1453rename to src/wicd/encryption/templates/wep-shared
1454diff --git a/encryption/templates/wired_8021x b/src/wicd/encryption/templates/wired_8021x
1455similarity index 100%
1456rename from encryption/templates/wired_8021x
1457rename to src/wicd/encryption/templates/wired_8021x
1458diff --git a/encryption/templates/wpa b/src/wicd/encryption/templates/wpa
1459similarity index 100%
1460rename from encryption/templates/wpa
1461rename to src/wicd/encryption/templates/wpa
1462diff --git a/encryption/templates/wpa-peap b/src/wicd/encryption/templates/wpa-peap
1463similarity index 100%
1464rename from encryption/templates/wpa-peap
1465rename to src/wicd/encryption/templates/wpa-peap
1466diff --git a/encryption/templates/wpa-psk b/src/wicd/encryption/templates/wpa-psk
1467similarity index 100%
1468rename from encryption/templates/wpa-psk
1469rename to src/wicd/encryption/templates/wpa-psk
1470diff --git a/encryption/templates/wpa-psk-hex b/src/wicd/encryption/templates/wpa-psk-hex
1471similarity index 100%
1472rename from encryption/templates/wpa-psk-hex
1473rename to src/wicd/encryption/templates/wpa-psk-hex
1474diff --git a/encryption/templates/wpa2-leap b/src/wicd/encryption/templates/wpa2-leap
1475similarity index 100%
1476rename from encryption/templates/wpa2-leap
1477rename to src/wicd/encryption/templates/wpa2-leap
1478diff --git a/encryption/templates/wpa2-peap b/src/wicd/encryption/templates/wpa2-peap
1479similarity index 100%
1480rename from encryption/templates/wpa2-peap
1481rename to src/wicd/encryption/templates/wpa2-peap
1482diff --git a/src/wicd/errors.py b/src/wicd/errors.py
1483index b5539c7..994bb9d 100644
1484--- a/src/wicd/errors.py
1485+++ b/src/wicd/errors.py
1486@@ -46,4 +46,37 @@ class transform_exception(object):
1487 except self.in_exc_type as e:
1488 raise self.out_exc_type(e)
1489
1490- return transform_exception_wrapper
1491\ No newline at end of file
1492+ return transform_exception_wrapper
1493+
1494+class WiCDCommandNotFound(WiCDError):
1495+ def __init__(self, path_list):
1496+ # Store the path list for later use in the error message
1497+ self.path_list = path_list
1498+
1499+ def __str__(self):
1500+ # Return the formatted error message
1501+ return f'Executable not found in {self.path_list}'
1502+
1503+
1504+class WiCDCommandFailure(WiCDError):
1505+ def __init__(self, command, returncode, stdout, stderr):
1506+ # Store the command, return code, stdout, and stderr for error reporting
1507+ self.command = command
1508+ self.returncode = returncode
1509+ self.stdout = stdout
1510+ self.stderr = stderr
1511+
1512+ def __str__(self):
1513+ # Return a formatted error message including the command, return code, and output
1514+ return (f"Command '{self.command}' failed with return code {self.returncode}.\n"
1515+ f"Output: {self.stdout}\n"
1516+ f"Error: {self.stderr}")
1517+
1518+class WiCDPopenFailure(WiCDError):
1519+ def __init__(self, err):
1520+ # Store the command, return code, stdout, and stderr for error reporting
1521+ self.error = err
1522+
1523+ def __str__(self):
1524+ # Return a formatted error message including the command, return code, and output
1525+ return (f"Error: {self.error}")
1526diff --git a/src/wicd/frontends/cli.py b/src/wicd/frontends/cli.py
1527index 425e7e7..288e9b4 100755
1528--- a/src/wicd/frontends/cli.py
1529+++ b/src/wicd/frontends/cli.py
1530@@ -2,6 +2,7 @@
1531 # vim: set fileencoding=utf8
1532 #
1533 # Copyright (C) 2021 Andreas Messer
1534+# Copyright (C) 2024 Takahiro Yoshizawa
1535 #
1536 # This program is free software; you can redistribute it and/or modify
1537 # it under the terms of the GNU General Public License as published by
1538@@ -52,7 +53,7 @@ def main():
1539 daemon = wicd.dbus.dbus_manager.ifaces['daemon']
1540 wireless = wicd.dbus.dbus_manager.ifaces['wireless']
1541 wired = wicd.dbus.dbus_manager.ifaces['wired']
1542- config = wicd.dbus.dbus_manager.ifaces['config']
1543+# config = wicd.dbus.dbus_manager.ifaces['config']
1544
1545 if not daemon:
1546 print(('Error connecting to wicd via D-Bus. ' + \
1547@@ -69,6 +70,7 @@ def main():
1548
1549 if args.status:
1550 status, info = daemon.GetConnectionStatus()
1551+ conn_type = ""
1552 if status in (misc.WIRED, misc.WIRELESS):
1553 connected = True
1554 status_msg = _('Connected')
1555@@ -176,8 +178,10 @@ def main():
1556 wireless.GetWirelessProperty(network_id, "mode")))
1557 print(("Channel: %s" % \
1558 wireless.GetWirelessProperty(network_id, "channel")))
1559+ bitrates = wireless.GetWirelessProperty(network_id, "bitrates")
1560+ bitrates_str = ', '.join(str(bitrate) for bitrate in bitrates)
1561 print(("Bit Rates: %s" % \
1562- wireless.GetWirelessProperty(network_id, "bitrates")))
1563+ bitrates_str))
1564 op_performed = True
1565
1566 # network properties
1567@@ -192,20 +196,29 @@ def main():
1568 network_id = wireless.GetCurrentNetworkID(0)
1569 is_valid_wireless_network_id(network_id)
1570 if not args.set_to:
1571- print((wireless.GetWirelessProperty(network_id,
1572- args.network_property)))
1573+ result = wireless.GetWirelessProperty(network_id, args.network_property)
1574+ if isinstance(result, list):
1575+ property_str = ', '.join(str(item) for item in result)
1576+ else:
1577+ property_str = result
1578+ print(property_str)
1579 else:
1580 wireless.SetWirelessProperty(network_id, \
1581 args.network_property, args.set_to)
1582 elif args.wired:
1583 if not args.set_to:
1584- print((wired.GetWiredProperty(args.network_property)))
1585+ result = wired.GetWiredProperty(args.network_property)
1586+ if isinstance(result, list):
1587+ property_str = ', '.join(str(item) for item in result)
1588+ else:
1589+ property_str = result
1590+ print(property_str)
1591 else:
1592 wired.SetWiredProperty(args.network_property, args.set_to)
1593 op_performed = True
1594
1595 if args.disconnect:
1596- daemon.Disconnect()
1597+# daemon.Disconnect()
1598 if args.wireless:
1599 if wireless.GetCurrentNetworkID(0) > -1:
1600 print(("Disconnecting from %s on %s" % \
1601@@ -215,6 +228,7 @@ def main():
1602 if wired.CheckPluggedIn():
1603 print(("Disconnecting from wired connection on %s" % \
1604 wired.DetectWiredInterface()))
1605+ daemon.Disconnect()
1606 op_performed = True
1607
1608 if args.connect:
1609@@ -265,7 +279,7 @@ def main():
1610 if len(prop) == 0:
1611 return "None"
1612 else:
1613- tmp = [(x[0], x[1].replace('_', ' ')) for x in type['required']]
1614+ tmp = [(x[0], x[1].replace('_', ' ')) for x in prop]
1615 return ', '.join("%s (%s)" % (x, y) for x, y in tmp)
1616
1617 if args.wireless and args.list_encryption_types:
1618@@ -282,12 +296,12 @@ def main():
1619 i += 1
1620 op_performed = True
1621
1622- if args.save and args.network > -1:
1623+ if args.save and ((args.wireless and args.network > -1) or (args.wired and args.name)):
1624 if args.wireless:
1625 is_valid_wireless_network_id(args.network)
1626- config.SaveWirelessNetworkProfile(args.network)
1627+ wireless.SaveWirelessNetworkProfile(args.network)
1628 elif args.wired:
1629- config.SaveWiredNetworkProfile(args.name)
1630+ wired.SaveWiredNetworkProfile(args.name)
1631 op_performed = True
1632
1633 if not op_performed:
1634diff --git a/src/wicd/frontends/gtk/__main__.py b/src/wicd/frontends/gtk/__main__.py
1635index b8ccd5b..49021b7 100644
1636--- a/src/wicd/frontends/gtk/__main__.py
1637+++ b/src/wicd/frontends/gtk/__main__.py
1638@@ -4,6 +4,7 @@
1639 # Copyright (C) 2007 - 2009 Adam Blackburn
1640 # Copyright (C) 2007 - 2009 Dan O'Reilly
1641 # Copyright (C) 2021 Andreas Messer
1642+# Copyright (C) 2024 Takahiro Yoshizawa
1643 #
1644 # This program is free software; you can redistribute it and/or modify
1645 # it under the terms of the GNU General Public License Version 2 as
1646@@ -30,14 +31,15 @@ import os
1647 import atexit
1648 from dbus import DBusException
1649
1650+gi.require_version('AyatanaAppIndicator3', '0.1')
1651 try:
1652- import appindicator
1653+ from gi.repository import AyatanaAppIndicator3 as appindicator
1654 USE_APP_INDICATOR = True
1655 except ImportError:
1656 USE_APP_INDICATOR = False
1657
1658 try:
1659- import pynotify
1660+ import notify2 as pynotify
1661 except ImportError:
1662 HAS_NOTIFY = False
1663 else:
1664@@ -104,13 +106,18 @@ class NetworkMenuItem(gtk.ImageMenuItem):
1665 """ Network menu item. """
1666 def __init__(self, lbl, is_active=False):
1667 gtk.ImageMenuItem.__init__(self)
1668- self.label = gtk.Label(lbl)
1669+ self.label = gtk.Label()
1670+ self.label.set_label(lbl)
1671 if is_active:
1672 atrlist = pango.AttrList()
1673- atrlist.insert(pango.AttrWeight(pango.WEIGHT_BOLD, 0, 50))
1674+ attr = pango.attr_weight_new(pango.Weight.BOLD)
1675+ attr.start_index = 0
1676+ attr.end_index = 50
1677+ atrlist.insert(attr)
1678 self.label.set_attributes(atrlist)
1679- self.label.set_justify(gtk.JUSTIFY_LEFT)
1680- self.label.set_alignment(0, 0)
1681+ self.label.set_justify(gtk.Justification.LEFT)
1682+ self.label.set_halign(gtk.Align.START)
1683+ self.label.set_valign(gtk.Align.START)
1684 self.add(self.label)
1685 self.label.show()
1686
1687@@ -138,6 +145,8 @@ class TrayIcon(object):
1688 if displayapp:
1689 self.tr.toggle_wicd_gui()
1690 self.icon_info = self.TrayConnectionInfo(self, self.tr, animate)
1691+
1692+ # Use prefs.py
1693 self.tr.icon_info = self.icon_info
1694 print(('displaytray %s' % displaytray))
1695 self.tr.visible(displaytray)
1696@@ -213,15 +222,15 @@ class TrayIcon(object):
1697 if (self.network_type == "none"):
1698 self.tr.tooltip = (_('Not connected'))
1699 elif (self.network_type == "wireless"):
1700- self.tr.tooltip = (_(f'Connected to $A at $B (IP: $C)')
1701- .replace('$A', self.network_name)
1702- .replace('$B', self.network_str)
1703+ self.tr.tooltip = (_(f'Connected to $A at $B (IP: $C)') \
1704+ .replace('$A', self.network_name) \
1705+ .replace('$B', self.network_str) \
1706 .replace('$C', self.network_addr))
1707 elif (self.network_type == "wired"):
1708- self.tr.tooltip = (_('Connected to wired network (IP: $A)')
1709+ self.tr.tooltip = (_('Connected to wired network (IP: $A)') \
1710 .replace('$A', self.network_addr))
1711 elif (self.network_type == "killswitch"):
1712- self.tr.tooltip = (_('Not connected') + "(" +
1713+ self.tr.tooltip = (_('Not connected') + "(" + \
1714 _('Wireless Kill Switch Enabled') + ")")
1715 elif (self.network_type == "no_daemon"):
1716 self.tr.tooltip = (_('Wicd daemon unreachable'))
1717@@ -229,6 +238,11 @@ class TrayIcon(object):
1718 return True
1719
1720 def _show_notification(self, title, details, image=None):
1721+ if title is None:
1722+ title = ""
1723+ if details is None:
1724+ details = ""
1725+
1726 if self.should_notify:
1727 try:
1728 if not self._last_bubble:
1729@@ -236,8 +250,8 @@ class TrayIcon(object):
1730 title, details, image)
1731 self._last_bubble.show()
1732 else:
1733- self._last_bubble.clear_actions()
1734- self._last_bubble.clear_hints()
1735+# self._last_bubble.clear_actions()
1736+# self._last_bubble.clear_hints()
1737 self._last_bubble.update(title, details, image)
1738 self._last_bubble.show()
1739 except Exception as e:
1740@@ -263,9 +277,9 @@ class TrayIcon(object):
1741 self.network_addr = str(info[0])
1742 self.network_type = "wired"
1743 self.tr.set_from_name('wired')
1744- #status_string = _('Connected to wired network (IP: $A)'). \
1745- # replace('$A',wired_ip)
1746- #self.tr.set_tooltip(status_string)
1747+ status_string = _('Connected to wired network (IP: $A)'). \
1748+ replace('$A',wired_ip)
1749+ self.tr.tooltip = status_string
1750 self._show_notification(_('Wired Network'),
1751 _('Connection established'),
1752 'network-wired')
1753@@ -275,6 +289,8 @@ class TrayIcon(object):
1754 @catchdbus
1755 def set_wireless_state(self, info):
1756 """ Sets the icon info for a wireless state. """
1757+ if len(info) < 5:
1758+ return
1759 lock = ''
1760 wireless_ip = info[0]
1761 self.network = info[1]
1762@@ -288,13 +304,14 @@ class TrayIcon(object):
1763 self.network_br = info[4]
1764 self.set_signal_image(int(info[2]), lock)
1765
1766+ status_string = (_("Not connected"))
1767 if wireless.GetWirelessProperty(cur_net_id, "encryption"):
1768 lock = "-lock"
1769- # status_string = (_('Connected to $A at $B (IP: $C)')
1770- #.replace('$A', self.network)
1771- # .replace('$B', sig_string)
1772- # .replace('$C', str(wireless_ip)))
1773- #self.tr.set_tooltip(status_string)
1774+ status_string = (_('Connected to $A at $B (IP: $C)')
1775+ .replace('$A', self.network)
1776+ .replace('$B', sig_string)
1777+ .replace('$C', str(wireless_ip)))
1778+ self.tr.tooltip = status_string
1779 self.set_signal_image(int(strength), lock)
1780 self._show_notification(self.network,
1781 _('Connection established'),
1782@@ -306,14 +323,14 @@ class TrayIcon(object):
1783 """ Sets the icon info for a connecting state. """
1784 wired = False
1785 if info[0] == 'wired' and len(info) == 1:
1786- cur_network = _('Wired Network')
1787+ cur_network = (_('Wired Network'))
1788 wired = True
1789 else:
1790 cur_network = info[1]
1791 status_string = _('Connecting') + " to " + \
1792 cur_network + "..."
1793 self.update_tooltip()
1794- # self.tr.set_tooltip(status_string)
1795+ self.tr.tooltip = status_string
1796 self.tr.set_from_name('no-signal')
1797 if wired:
1798 self._show_notification(cur_network,
1799@@ -335,7 +352,7 @@ class TrayIcon(object):
1800 _('Wireless Kill Switch Enabled') + ")")
1801 else:
1802 status = _('Not connected')
1803- # self.tr.set_tooltip(status)
1804+ self.tr.tooltip = status
1805 self._show_notification(_('Disconnected'), None, 'stop')
1806 self.update_tooltip()
1807
1808@@ -345,6 +362,9 @@ class TrayIcon(object):
1809 if not DBUS_AVAIL:
1810 return False
1811
1812+ if self.tr.submenu_open:
1813+ return False
1814+
1815 if not state or not info:
1816 [state, info] = daemon.GetConnectionStatus()
1817
1818@@ -480,51 +500,61 @@ class TrayIcon(object):
1819 def __init__(self, parent):
1820 super().__init__()
1821
1822+ self.ind = None
1823+
1824+ self.xdg_current_desktop = os.environ.get("XDG_CURRENT_DESKTOP", "").lower()
1825+
1826 self.list = []
1827 self.label = None
1828 self.data = None
1829
1830- menu = """
1831- <ui>
1832- <menubar name="Menubar">
1833- <menu action="Menu">
1834- <menu action="Connect">
1835- </menu>
1836- <separator/>
1837- <menuitem action="Info"/>
1838- <menuitem action="Quit"/>
1839- </menu>
1840- </menubar>
1841- </ui>
1842- """
1843- actions = [
1844- ('Menu', None, 'Menu'),
1845- ('Connect', gtk.STOCK_CONNECT, _('Connect')),
1846- ('Info', gtk.STOCK_INFO, _('_Connection Info'), None,
1847- _('Information about the current connection'),
1848- self.on_conn_info),
1849- ('Quit', gtk.STOCK_QUIT, _('_Quit'), None,
1850- _('Quit wicd-tray-icon'), self.on_quit),
1851- ]
1852- actg = gtk.ActionGroup(name='Actions')
1853- actg.add_actions(actions)
1854- self.manager = gtk.UIManager()
1855- self.manager.insert_action_group(actg, 0)
1856- self.manager.add_ui_from_string(menu)
1857- self.menu = (self.manager.get_widget('/Menubar/Menu/Quit').
1858- props.parent)
1859+ self.get_new_menu()
1860+
1861 self.gui_win = None
1862 self.current_icon_name = None
1863 self._is_scanning = False
1864- net_menuitem = self.manager.get_widget("/Menubar/Menu/Connect/")
1865 if not USE_APP_INDICATOR:
1866- net_menuitem.connect("activate", self.on_net_menu_activate)
1867+ self.connect_item.connect("activate", self.on_net_menu_activate)
1868
1869 self.parent = parent
1870 self.time = 2 # Time between updates
1871 self.cont = 'Stop'
1872 self.conn_info_txt = ''
1873
1874+ def get_new_menu(self):
1875+ self.menu = gtk.Menu()
1876+
1877+ self.connect_item = gtk.MenuItem(label=_('Connect'))
1878+ self.connect_submenu = gtk.Menu()
1879+ self.connect_item.set_submenu(self.connect_submenu)
1880+ self.menu.append(self.connect_item)
1881+
1882+ info_item = gtk.MenuItem(label=_('Connection Info'))
1883+ info_item.connect('activate', self.on_conn_info)
1884+ self.menu.append(info_item)
1885+
1886+ separator = gtk.SeparatorMenuItem()
1887+ self.menu.append(separator)
1888+
1889+ quit_item = gtk.MenuItem(label=_('Quit'))
1890+ quit_item.connect('activate', self.on_quit)
1891+ self.menu.append(quit_item)
1892+ self.connect_submenu.connect('show', self.on_connect_submenu_show)
1893+ self.connect_submenu.connect('hide', self.on_connect_submenu_hide)
1894+
1895+ self.menu.show_all()
1896+ self.connect_submenu.show_all()
1897+
1898+ self.submenu_open = False
1899+
1900+ self.populate_network_menu()
1901+
1902+ def on_connect_submenu_show(self, menu):
1903+ self.submenu_open = True
1904+
1905+ def on_connect_submenu_hide(self, menu):
1906+ self.submenu_open = False
1907+
1908 def tray_scan_started(self):
1909 """ Callback for when a wireless scan is started. """
1910 if not DBUS_AVAIL:
1911@@ -549,6 +579,24 @@ class TrayIcon(object):
1912 # 'cannot be completed'))
1913 pass
1914
1915+ @catchdbus
1916+ def update_tray(self, state=None, info=None):
1917+ if not DBUS_AVAIL:
1918+ return False
1919+
1920+ if self.submenu_open:
1921+ return False
1922+
1923+ info_list = [""]
1924+ if info is not None:
1925+ info_list = [str(item) for item in info]
1926+
1927+ if len(info_list) < 3:
1928+ return
1929+ if info_list != [''] and self._is_scanning == False:
1930+ for item in self.connect_submenu.get_children():
1931+ item.set_sensitive(True)
1932+
1933 def on_quit(self, widget=None):
1934 """ Closes the tray icon. """
1935 sys.exit(0)
1936@@ -569,7 +617,7 @@ class TrayIcon(object):
1937 "Wicd Connection Info",
1938 None,
1939 0,
1940- (gtk.STOCK_OK, gtk.RESPONSE_CLOSE)
1941+ (gtk.STOCK_OK, gtk.ResponseType.CLOSE)
1942 )
1943
1944 # Create labels
1945@@ -686,18 +734,31 @@ TX:'''))
1946 else:
1947 wireless.ConnectWireless(net_id)
1948
1949- item = NetworkMenuItem(lbl, is_active)
1950+ item = gtk.MenuItem()
1951 image = gtk.Image()
1952+ label = gtk.Label()
1953+ label.set_label(lbl)
1954+ box = gtk.Box(orientation=gtk.Orientation.HORIZONTAL, spacing=6)
1955+
1956+ if is_active:
1957+ atrlist = pango.AttrList()
1958+ attr = pango.attr_weight_new(pango.Weight.BOLD)
1959+ attr.start_index = 0
1960+ attr.end_index = 50
1961+ atrlist.insert(attr)
1962+ label.set_attributes(atrlist)
1963
1964 if type_ == "__wired__":
1965 image.set_from_icon_name("network-wired",
1966- gtk.ICON_SIZE_SMALL_TOOLBAR)
1967+ gtk.IconSize.SMALL_TOOLBAR)
1968 else:
1969 image.set_from_icon_name(self._get_img(n_id),
1970- gtk.ICON_SIZE_SMALL_TOOLBAR)
1971- item.set_image(image)
1972+ gtk.IconSize.SMALL_TOOLBAR)
1973+ box.pack_start(image, False, False, 0)
1974 del image
1975 item.connect("activate", network_selected, type_, n_id)
1976+ box.pack_start(label, False, False, 0)
1977+ item.add(box)
1978 net_menu.append(item)
1979 item.show()
1980 if is_connecting:
1981@@ -709,7 +770,7 @@ TX:'''))
1982 """ Determines which image to use for the wireless entries. """
1983 def fix_strength(val, default):
1984 """ Assigns given strength to a default value if needed. """
1985- return val and int(val) or default
1986+ return val and int(float(val)) or default
1987
1988 def get_prop(prop):
1989 return wireless.GetWirelessProperty(net_id, prop)
1990@@ -771,14 +832,14 @@ TX:'''))
1991 @catchdbus
1992 def populate_network_menu(self, data=None):
1993 """ Populates the network list submenu. """
1994+
1995 def get_prop(net_id, prop):
1996 return wireless.GetWirelessProperty(net_id, prop)
1997
1998- net_menuitem = self.manager.get_widget("/Menubar/Menu/Connect/")
1999- submenu = net_menuitem.get_submenu()
2000- self._clear_menu(submenu)
2001+ self._clear_menu(self.connect_submenu)
2002+
2003 if not DBUS_AVAIL:
2004- net_menuitem.show()
2005+ self.connect_item.show()
2006 return
2007
2008 is_connecting = daemon.CheckIfConnecting()
2009@@ -791,10 +852,10 @@ TX:'''))
2010 is_active = True
2011 else:
2012 is_active = False
2013- self._add_item_to_menu(submenu, "Wired Network", "__wired__", 0,
2014+ self._add_item_to_menu(self.connect_submenu, "Wired Network", "__wired__", 0,
2015 is_connecting, is_active)
2016 sep = gtk.SeparatorMenuItem()
2017- submenu.append(sep)
2018+ self.connect_submenu.append(sep)
2019 sep.show()
2020
2021 if num_networks > 0:
2022@@ -808,28 +869,29 @@ TX:'''))
2023 is_active = True
2024 else:
2025 is_active = False
2026- self._add_item_to_menu(submenu, essid, "wifi", x,
2027+ self._add_item_to_menu(self.connect_submenu, essid, "wifi", x,
2028 is_connecting, is_active)
2029 else:
2030 no_nets_item = gtk.MenuItem(_('No wireless networks found.'))
2031 no_nets_item.set_sensitive(False)
2032 no_nets_item.show()
2033- submenu.append(no_nets_item)
2034+ self.connect_submenu.append(no_nets_item)
2035
2036- submenu.reposition()
2037- net_menuitem.show()
2038+ self.connect_submenu.reposition()
2039+ self.connect_submenu.show_all()
2040+ self.connect_item.show()
2041+ self.menu.show_all()
2042
2043 def init_network_menu(self):
2044 """ Set the right-click network menu to the scanning state. """
2045- net_menuitem = self.manager.get_widget("/Menubar/Menu/Connect/")
2046- submenu = net_menuitem.get_submenu()
2047- self._clear_menu(submenu)
2048+ self._clear_menu(self.connect_submenu)
2049
2050- loading_item = gtk.MenuItem(_('Scanning') + "...")
2051+ loading_item = gtk.MenuItem()
2052+ loading_item.set_label(_('Scanning') + "...")
2053 loading_item.set_sensitive(False)
2054 loading_item.show()
2055- submenu.append(loading_item)
2056- net_menuitem.show()
2057+ self.connect_submenu.append(loading_item)
2058+ self.connect_item.show()
2059
2060 def _clear_menu(self, menu):
2061 """ Clear the right-click menu. """
2062@@ -876,6 +938,8 @@ TX:'''))
2063 self.set_from_name('no-signal')
2064 self.tooltip = "Initializing wicd..."
2065
2066+ self.icon_info: Optional[TrayConnectionInfo] = None
2067+
2068 def on_popup_menu(self, status, button, timestamp):
2069 """ Opens the right click menu for the tray icon. """
2070 self.init_network_menu()
2071@@ -898,30 +962,36 @@ TX:'''))
2072 self.set_visible(val)
2073
2074 if USE_APP_INDICATOR:
2075- class IndicatorTrayIconGUI(TrayIconGUI, gtk.StatusIcon):
2076+ class IndicatorTrayIconGUI(TrayIconGUI):
2077 """ Class for creating the wicd AppIndicator.
2078- This is required on recent versions of Unity (>=13.04).
2079-
2080 Uses appindicator.Indicator to implement a tray icon.
2081-
2082 """
2083 def __init__(self, parent):
2084 super().__init__(parent)
2085+ self.current_icon_name = ''
2086+ self.ind = appindicator.Indicator.new(
2087+ "wicd", "network-idle", appindicator.IndicatorCategory.APPLICATION_STATUS)
2088+ self.ind.set_status(appindicator.IndicatorStatus.ACTIVE)
2089+
2090+ self.get_new_menu()
2091+ self.get_new_indicator_menu()
2092+ self.ind.set_menu(self.menu)
2093
2094- self.ind = appindicator.Indicator(
2095- "wicd", "wicd-gtk", appindicator.CATEGORY_SYSTEM_SERVICES, wpath.images)
2096+ self.icon_info: Optional[TrayConnectionInfo] = None
2097
2098+ def get_new_indicator_menu(self):
2099 # Rescaning when hovering over the net_menu doesn't work.
2100 # (AFAICT, AppIndicator menus don't report PRELIGHT status.)
2101 # We use a separate menu item instead.
2102- rescan = gtk.MenuItem("Rescan")
2103+ rescan = gtk.MenuItem()
2104+ rescan.set_label(_("Rescan"))
2105 self.menu.prepend(rescan)
2106 rescan.connect("activate", self.on_rescan)
2107 rescan.show()
2108
2109 sep = gtk.SeparatorMenuItem()
2110- self.menu.prepend(sep)
2111 sep.show()
2112+ self.menu.prepend(sep)
2113
2114 # AppIndicator does not support tooltips so we use a
2115 # menu item to contain what was the tooltip.
2116@@ -929,12 +999,45 @@ TX:'''))
2117 # Also, since AppIndicator does not support actions on
2118 # clicking the tray icon, we use this tooltip menu
2119 # item to toggle the GUI
2120- self.tooltip_item = gtk.MenuItem("Initializing wicd...")
2121+ self.tooltip_item = gtk.MenuItem()
2122+ self.tooltip_item.set_label(_("Initializing wicd..."))
2123 self.menu.prepend(self.tooltip_item)
2124 self.tooltip_item.connect("activate", self.on_activate)
2125 self.tooltip_item.show()
2126+ if self.ind:
2127+ self.ind.set_menu(self.menu)
2128
2129- self.ind.set_menu(self.menu)
2130+ def populate_network_menu(self, data=None):
2131+ super().populate_network_menu()
2132+ if self.ind:
2133+ self.ind.set_menu(self.menu)
2134+
2135+ def _clear_menu(self, menu):
2136+ """ Clear the right-click menu in the context of Ayatana AppIndicator. """
2137+ self.menu = gtk.Menu()
2138+ self.get_new_indicator_menu()
2139+ self.connect_item = gtk.MenuItem(label=_('Connect'))
2140+ self.connect_submenu = gtk.Menu()
2141+ self.connect_item.set_submenu(self.connect_submenu)
2142+ self.menu.append(self.connect_item)
2143+ info_item = gtk.MenuItem(label=_('Connection Info'))
2144+ info_item.connect('activate', self.on_conn_info)
2145+ self.menu.append(info_item)
2146+
2147+ separator = gtk.SeparatorMenuItem()
2148+ self.menu.append(separator)
2149+
2150+ quit_item = gtk.MenuItem(label=_('Quit'))
2151+ quit_item.connect('activate', self.on_quit)
2152+ self.menu.append(quit_item)
2153+
2154+ self.connect_submenu.connect('show', self.on_connect_submenu_show)
2155+ self.connect_submenu.connect('hide', self.on_connect_submenu_hide)
2156+
2157+ self.menu.show_all()
2158+
2159+ if self.ind:
2160+ self.ind.set_menu(self.menu)
2161
2162 def on_rescan(self, *data):
2163 """ Triggers a network rescan that updates the 'Connect' menu when the 'Rescan' menu item is selected. """
2164@@ -945,13 +1048,13 @@ TX:'''))
2165 """ Sets a new tray icon picture. """
2166 if path != self.current_icon_path:
2167 self.current_icon_path = path
2168- self.ind.set_icon(path)
2169-
2170+ self.ind.set_icon_full(path, "")
2171+
2172 def set_from_name(self, name=None):
2173 """ Sets a new tray icon picture. """
2174 if name != self.current_icon_name:
2175 self.current_icon_name = name
2176- self.ind.set_icon(name)
2177+ self.ind.set_icon_full(name, "")
2178
2179 def visible(self, val):
2180 """ Set if the icon is visible or not.
2181@@ -960,7 +1063,14 @@ TX:'''))
2182 hides the tray icon.
2183
2184 """
2185- self.ind.set_status(val and appindicator.STATUS_ACTIVE or appindicator.STATUS_PASSIVE)
2186+ if val:
2187+ self.ind.set_status(appindicator.IndicatorStatus.ACTIVE)
2188+ else:
2189+ self.ind.set_status(appindicator.IndicatorStatus.PASSIVE)
2190+
2191+ def tray_scan_ended(self):
2192+ super().tray_scan_ended()
2193+ self.ind.set_menu(self.menu)
2194
2195 @property
2196 def tooltip(self):
2197@@ -989,6 +1099,13 @@ Arguments:
2198
2199 def setup_dbus(force=True):
2200 """ Initialize DBus. """
2201+ import dbus
2202+ if getattr(dbus, "version", (0, 0, 0)) < (0, 80, 0):
2203+ import dbus.glib
2204+ else:
2205+ from dbus.mainloop.glib import DBusGMainLoop
2206+ DBusGMainLoop(set_as_default=True)
2207+
2208 global daemon, wireless, wired, DBUS_AVAIL, lost_dbus_id
2209 print("Connecting to daemon...")
2210 try:
2211@@ -1090,6 +1207,8 @@ def main():
2212 'LaunchChooser', 'org.wicd.daemon')
2213 bus.add_signal_receiver(tray_icon.icon_info.update_tray_icon,
2214 'StatusChanged', 'org.wicd.daemon')
2215+ bus.add_signal_receiver(tray_icon.tr.update_tray,
2216+ 'StatusChanged', 'org.wicd.daemon')
2217 bus.add_signal_receiver(tray_icon.tr.tray_scan_ended, 'SendEndScanSignal',
2218 'org.wicd.daemon.wireless')
2219 bus.add_signal_receiver(tray_icon.tr.tray_scan_started,
2220diff --git a/src/wicd/frontends/gtk/configscript.py b/src/wicd/frontends/gtk/configscript.py
2221index 5603af4..dcd3b26 100755
2222--- a/src/wicd/frontends/gtk/configscript.py
2223+++ b/src/wicd/frontends/gtk/configscript.py
2224@@ -28,20 +28,22 @@ run as the current user.
2225
2226 import sys
2227 import os
2228+import importlib.resources as resources
2229 import gi
2230 gi.require_version('Gtk', '3.0')
2231 from gi.repository import Gtk as gtk
2232
2233 from wicd import wpath
2234 from wicd.translations import _
2235-from wicd import dbusmanager
2236+from wicd.dbus import dbus_manager
2237 from wicd.configmanager import ConfigManager
2238
2239-dbus = dbusmanager.DBusManager()
2240-dbus.connect_to_dbus()
2241+#dbus = dbus_manager.DBusManager()
2242+#dbus.connect_to_dbus()
2243
2244-wireless = dbus.get_interface("wireless")
2245-wired = dbus.get_interface("wired")
2246+#dbus_manager.connect()
2247+wireless = dbus_manager.get_interface("wireless")
2248+wired = dbus_manager.get_interface("wired")
2249
2250
2251 def none_to_blank(text):
2252@@ -127,7 +129,8 @@ def main (argv):
2253
2254 script_info = get_script_info(network, network_type)
2255
2256- gladefile = os.path.join(wpath.gtk, "wicd.ui")
2257+ gladefile = str(resources.files("wicd.frontends.gtk.resources") / "wicd.ui")
2258+
2259 wTree = gtk.Builder()
2260 wTree.set_translation_domain('wicd')
2261 wTree.add_from_file(gladefile)
2262diff --git a/src/wicd/frontends/gtk/gui.py b/src/wicd/frontends/gtk/gui.py
2263index 140a2f0..48e3218 100644
2264--- a/src/wicd/frontends/gtk/gui.py
2265+++ b/src/wicd/frontends/gtk/gui.py
2266@@ -9,6 +9,7 @@ Module containing the code for the main wicd GUI.
2267 #
2268 # Copyright (C) 2007-2009 Adam Blackburn
2269 # Copyright (C) 2007-2009 Dan O'Reilly
2270+# Copyright (C) 2024 Takahiro Yoshizawa
2271 #
2272 # This program is free software; you can redistribute it and/or modify
2273 # it under the terms of the GNU General Public License Version 2 as
2274@@ -22,14 +23,13 @@ Module containing the code for the main wicd GUI.
2275 # You should have received a copy of the GNU General Public License
2276 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2277 #
2278-
2279 import os
2280 import sys
2281 import time
2282 import gi
2283 gi.require_version('Gtk', '3.0')
2284 from gi.repository import Gtk as gtk
2285-from gi.repository import Gdk
2286+from gi.repository import Gdk as gdk
2287 from gi.repository import GLib as gobject
2288 from itertools import chain
2289 from dbus import DBusException
2290@@ -112,10 +112,10 @@ class WiredProfileChooser:
2291
2292 dialog = gtk.Dialog(
2293 title=_('Wired connection detected'),
2294- flags=gtk.DIALOG_MODAL,
2295- buttons=(gtk.STOCK_CONNECT, 1, gtk.STOCK_CANCEL, 2)
2296+ flags=gtk.DialogFlags.MODAL,
2297+ buttons=("network-connect", 1, "process-stop", 2)
2298 )
2299- dialog.set_has_separator(False)
2300+# dialog.set_has_separator(False)
2301 dialog.set_size_request(400, 150)
2302 instruct_label = gtk.Label(
2303 _('Select or create a wired profile to connect with') + ':\n'
2304@@ -125,7 +125,9 @@ class WiredProfileChooser:
2305 )
2306
2307 wired_net_entry.is_full_gui = False
2308- instruct_label.set_alignment(0, 0)
2309+# instruct_label.set_alignment(0, 0)
2310+ instruct_label.set_xalign(0)
2311+ instruct_label.set_yalign(0)
2312 stoppopcheckbox.set_active(False)
2313
2314 # Remove widgets that were added to the normal WiredNetworkEntry
2315@@ -134,13 +136,13 @@ class WiredProfileChooser:
2316 wired_net_entry.vbox_top.remove(wired_net_entry.profile_help)
2317
2318 # pylint: disable-msg=E1101
2319- dialog.vbox.pack_start(instruct_label, fill=False, expand=False)
2320+ dialog.vbox.pack_start(instruct_label, fill=False, expand=False, padding=0)
2321 # pylint: disable-msg=E1101
2322- dialog.vbox.pack_start(wired_net_entry.profile_help, False, False)
2323+ dialog.vbox.pack_start(wired_net_entry.profile_help, False, False, padding=0)
2324 # pylint: disable-msg=E1101
2325- dialog.vbox.pack_start(wired_net_entry.hbox_temp, False, False)
2326+ dialog.vbox.pack_start(wired_net_entry.hbox_temp, False, False, padding=0)
2327 # pylint: disable-msg=E1101
2328- dialog.vbox.pack_start(stoppopcheckbox, False, False)
2329+ dialog.vbox.pack_start(stoppopcheckbox, False, False, padding=0)
2330 dialog.show_all()
2331
2332 wired_profiles = wired_net_entry.combo_profile_names
2333@@ -186,6 +188,7 @@ class appGui(WicdGtkUi):
2334 self.wTree.set_translation_domain('wicd')
2335 self.wTree.add_from_file(self.gladefile)
2336 self.window = self.wTree.get_object("window1")
2337+ self._connect_thread_started = False
2338
2339 dic = {
2340 "refresh_clicked": self.refresh_clicked,
2341@@ -217,7 +220,7 @@ class appGui(WicdGtkUi):
2342 self.wired_network_box.show_all()
2343 self.network_list = gtk.VBox(False, 0)
2344 self.all_network_list.pack_start(self.wired_network_box, False, False, 0)
2345- self.all_network_list.pack_start(self.network_list, True, True, 0)
2346+ self.all_network_list.pack_start(self.network_list, True, False, 0)
2347 self.network_list.show_all()
2348 self.status_area = self.wTree.get_object("connecting_hbox")
2349 self.status_bar = self.wTree.get_object("statusbar")
2350@@ -247,6 +250,7 @@ class appGui(WicdGtkUi):
2351 self.window.connect('key-release-event', self.key_event)
2352
2353 bus = dbus_manager.bus
2354+ setup_dbus()
2355
2356 bus.add_signal_receiver(self.dbus_scan_finished, 'SendEndScanSignal',
2357 'org.wicd.daemon.wireless')
2358@@ -279,10 +283,10 @@ class appGui(WicdGtkUi):
2359 print("Starting the Ad-Hoc Network Creation Process...")
2360 dialog = gtk.Dialog(
2361 title=_('Create an Ad-Hoc Network'),
2362- flags=gtk.DIALOG_MODAL,
2363- buttons=(gtk.STOCK_CANCEL, 2, gtk.STOCK_OK, 1)
2364+ flags=gtk.DialogFlags.MODAL,
2365+ buttons=("process-stop", 2, gtk.STOCK_OK, 1)
2366 )
2367- dialog.set_has_separator(False)
2368+# dialog.set_has_separator(False)
2369 dialog.set_size_request(400, -1)
2370 self.chkbox_use_encryption = \
2371 gtk.CheckButton(_('Use Encryption (WEP only)'))
2372@@ -305,19 +309,19 @@ class appGui(WicdGtkUi):
2373
2374 vbox_ah = gtk.VBox(False, 0)
2375 self.wired_network_box = gtk.VBox(False, 0)
2376- vbox_ah.pack_start(self.chkbox_use_encryption, False, False)
2377- vbox_ah.pack_start(self.key_entry, False, False)
2378+ vbox_ah.pack_start(self.chkbox_use_encryption, False, False, 0)
2379+ vbox_ah.pack_start(self.key_entry, False, False, 0)
2380 vbox_ah.show()
2381 # pylint: disable-msg=E1101
2382- dialog.vbox.pack_start(essid_entry)
2383+ dialog.vbox.pack_start(essid_entry, False, False, 0)
2384 # pylint: disable-msg=E1101
2385- dialog.vbox.pack_start(ip_entry)
2386+ dialog.vbox.pack_start(ip_entry, False, False, 0)
2387 # pylint: disable-msg=E1101
2388- dialog.vbox.pack_start(channel_entry)
2389+ dialog.vbox.pack_start(channel_entry, False, False, 0)
2390 # pylint: disable-msg=E1101
2391- dialog.vbox.pack_start(chkbox_use_ics)
2392+ dialog.vbox.pack_start(chkbox_use_ics, False, False, 0)
2393 # pylint: disable-msg=E1101
2394- dialog.vbox.pack_start(vbox_ah)
2395+ dialog.vbox.pack_start(vbox_ah, False, False, 0)
2396 # pylint: disable-msg=E1101
2397 dialog.vbox.set_spacing(5)
2398 dialog.show_all()
2399@@ -342,10 +346,10 @@ class appGui(WicdGtkUi):
2400 wireless.ReloadConfig()
2401 dialog = gtk.Dialog(
2402 title=_('List of saved networks'),
2403- flags=gtk.DIALOG_MODAL,
2404+ flags=gtk.DialogFlags.MODAL,
2405 buttons=(gtk.STOCK_DELETE, 1, gtk.STOCK_OK, 2)
2406 )
2407- dialog.set_has_separator(True)
2408+# dialog.set_has_separator(True)
2409 dialog.set_size_request(400, 200)
2410
2411 networks = gtk.ListStore(str, str)
2412@@ -355,7 +359,7 @@ class appGui(WicdGtkUi):
2413 else:
2414 networks.append((entry[0], _('Global settings for this ESSID')))
2415 tree = gtk.TreeView(model=networks)
2416- tree.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
2417+ tree.get_selection().set_mode(gtk.SelectionMode.MULTIPLE)
2418
2419 cell = gtk.CellRendererText()
2420
2421@@ -366,10 +370,10 @@ class appGui(WicdGtkUi):
2422 tree.append_column(column)
2423
2424 scroll = gtk.ScrolledWindow()
2425- scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
2426+ scroll.set_policy(gtk.PolicyType.AUTOMATIC, gtk.PolicyType.AUTOMATIC)
2427 scroll.add(tree)
2428 # pylint: disable-msg=E1101
2429- dialog.vbox.pack_start(scroll)
2430+ dialog.vbox.pack_start(scroll, True, True, 0)
2431 # pylint: disable-msg=E1101
2432 dialog.vbox.set_spacing(5)
2433 dialog.show_all()
2434@@ -386,15 +390,15 @@ class appGui(WicdGtkUi):
2435 to_remove['bssid'].append(model.get_value(it, 1))
2436
2437 confirm = gtk.MessageDialog(
2438- flags=gtk.DIALOG_MODAL,
2439- type=gtk.MESSAGE_INFO,
2440- buttons=gtk.BUTTONS_YES_NO,
2441+ flags=gtk.DialogFlags.MODAL,
2442+ type=gtk.MessageType.INFO,
2443+ buttons=gtk.ButtonsType.YES_NO,
2444 message_format=_('Are you sure you want to discard' +
2445 ' settings for the selected networks?')
2446 )
2447 confirm.format_secondary_text('\n'.join(to_remove['essid']))
2448 response = confirm.run()
2449- if response == gtk.RESPONSE_YES:
2450+ if response == gtk.ResponseType.YES:
2451 for x in to_remove['bssid']:
2452 wireless.DeleteWirelessNetwork(x)
2453 wireless.ReloadConfig()
2454@@ -434,6 +438,8 @@ class appGui(WicdGtkUi):
2455 "Dan O'Reilly",
2456 "Andrew Psaltis",
2457 "David Paleino"
2458+ "Andreas Messer"
2459+ "Takahiro Yoshizawa"
2460 ])
2461 dialog.set_website("http://launchpad.net/wicd")
2462 dialog.run()
2463@@ -441,8 +447,9 @@ class appGui(WicdGtkUi):
2464
2465 def key_event(self, widget, event=None):
2466 """ Handle key-release-events. """
2467- if event.state & gtk.gdk.CONTROL_MASK and \
2468- gtk.gdk.keyval_name(event.keyval) in ["w", "q"]:
2469+# if event.state & gtk.gdk.CONTROL_MASK and \
2470+ if event.state & gdk.ModifierType.CONTROL_MASK and \
2471+ gdk.keyval_name(event.keyval) in ["w", "q"]:
2472 self.exit()
2473
2474 def settings_dialog(self, widget, event=None):
2475@@ -459,16 +466,16 @@ class appGui(WicdGtkUi):
2476 """ Prompts the user for a hidden network, then scans for it. """
2477 dialog = gtk.Dialog(
2478 title=('Hidden Network'),
2479- flags=gtk.DIALOG_MODAL,
2480- buttons=(gtk.STOCK_CONNECT, 1, gtk.STOCK_CANCEL, 2)
2481+ flags=gtk.DialogFlags.MODAL,
2482+ buttons=("network-connect", 1, "process-stop", 2)
2483 )
2484- dialog.set_has_separator(False)
2485+# dialog.set_has_separator(False)
2486 lbl = gtk.Label(_('Hidden Network ESSID'))
2487 textbox = gtk.Entry()
2488 # pylint: disable-msg=E1101
2489- dialog.vbox.pack_start(lbl)
2490+ dialog.vbox.pack_start(lbl, False, False, 0)
2491 # pylint: disable-msg=E1101
2492- dialog.vbox.pack_start(textbox)
2493+ dialog.vbox.pack_start(textbox, False, False, 0)
2494 dialog.show_all()
2495 button = dialog.run()
2496 if button == 1:
2497@@ -505,7 +512,8 @@ class appGui(WicdGtkUi):
2498 if not self.is_visible:
2499 return True
2500
2501- daemon.UpdateState()
2502+ if not daemon is None:
2503+ daemon.UpdateState()
2504 if self.connecting:
2505 # If we're connecting, don't wait for the monitor to send
2506 # us a signal, since it won't until the connection is made.
2507@@ -542,10 +550,11 @@ class appGui(WicdGtkUi):
2508 if self.connecting:
2509 # Adjust our state from connecting->connected.
2510 self._set_not_connecting_state()
2511- self.set_status(_('Connected to $A at $B (IP: $C)').replace
2512- ('$A', info[1]).replace
2513- ('$B', daemon.FormatSignalForPrinting(info[2])).replace
2514- ('$C', info[0]))
2515+ if daemon is not None:
2516+ self.set_status(_('Connected to $A at $B (IP: $C)').replace
2517+ ('$A', info[1]).replace
2518+ ('$B', daemon.FormatSignalForPrinting(info[2])).replace
2519+ ('$C', info[0]))
2520 return True
2521
2522 def set_not_connected_state(self, info):
2523@@ -560,21 +569,24 @@ class appGui(WicdGtkUi):
2524 """ Set not-connecting state. """
2525 if self.connecting:
2526 if self.update_cb:
2527- gobject.source_remove(self.update_cb)
2528+ gobject.source_remove(self.update_cb)
2529+ self.update_cb = None
2530+ self.connecting = False
2531 self.update_cb = misc.timeout_add(2, self.update_statusbar)
2532 self.connecting = False
2533 if self.pulse_active:
2534 self.pulse_active = False
2535 gobject.idle_add(self.all_network_list.set_sensitive, True)
2536- gobject.idle_add(self.status_area.hide_all)
2537+ gobject.idle_add(self.status_area.hide)
2538 if self.statusID:
2539- gobject.idle_add(self.status_bar.remove_message, 1, self.statusID)
2540+ gobject.idle_add(self.status_bar.remove, 1, self.statusID)
2541
2542 def set_connecting_state(self, info):
2543 """ Set connecting state. """
2544 if not self.connecting:
2545 if self.update_cb:
2546 gobject.source_remove(self.update_cb)
2547+ self.update_cb = None
2548 self.update_cb = misc.timeout_add(500, self.update_statusbar,
2549 milli=True)
2550 self.connecting = True
2551@@ -584,7 +596,7 @@ class appGui(WicdGtkUi):
2552 gobject.idle_add(self.all_network_list.set_sensitive, False)
2553 gobject.idle_add(self.status_area.show_all)
2554 if self.statusID:
2555- gobject.idle_add(self.status_bar.remove_message, 1, self.statusID)
2556+ gobject.idle_add(self.status_bar.remove, 1, self.statusID)
2557 if info[0] == "wireless":
2558 stat = wireless.CheckWirelessConnectingMessage()
2559 gobject.idle_add(self.set_status, "%s: %s" % (info[1], stat))
2560@@ -652,12 +664,12 @@ class appGui(WicdGtkUi):
2561 self._remove_items_from_vbox(self.wired_network_box)
2562 self._remove_items_from_vbox(self.network_list)
2563 label = gtk.Label("%s..." % _('Scanning'))
2564- self.network_list.pack_start(label)
2565+ self.network_list.pack_start(label, False, False, 10)
2566 self.network_list.show_all()
2567 if wired.CheckPluggedIn() or daemon.GetAlwaysShowWiredInterface():
2568 printLine = True # In this case we print a separator.
2569 wirednet = WiredNetworkEntry()
2570- self.wired_network_box.pack_start(wirednet, False, False)
2571+ self.wired_network_box.pack_start(wirednet, False, False, 10)
2572 wirednet.connect_button.connect("clicked", self.connect,
2573 "wired", 0, wirednet)
2574 wirednet.disconnect_button.connect("clicked", self.disconnect,
2575@@ -713,7 +725,7 @@ class appGui(WicdGtkUi):
2576 else:
2577 printLine = True
2578 tempnet = WirelessNetworkEntry(x)
2579- self.network_list.pack_start(tempnet, False, False)
2580+ self.network_list.pack_start(tempnet, False, False, 10)
2581 tempnet.connect_button.connect("clicked",
2582 self.connect, "wireless", x,
2583 tempnet)
2584@@ -729,7 +741,7 @@ class appGui(WicdGtkUi):
2585 label = gtk.Label(_('Wireless Kill Switch Enabled') + ".")
2586 else:
2587 label = gtk.Label(_('No wireless networks found.'))
2588- self.network_list.pack_start(label)
2589+ self.network_list.pack_start(label, False, False, 0)
2590 label.show()
2591 self.update_connect_buttons(force_check=True)
2592 self.network_list.set_sensitive(True)
2593@@ -803,7 +815,7 @@ class appGui(WicdGtkUi):
2594
2595 """
2596 result = dialog.run()
2597- if result == gtk.RESPONSE_ACCEPT:
2598+ if result == gtk.ResponseType.OK:
2599 if self.save_settings(nettype, networkid, networkentry):
2600 return True
2601 else:
2602@@ -857,7 +869,7 @@ class appGui(WicdGtkUi):
2603 self.all_network_list.set_sensitive(False)
2604 if self.statusID:
2605 gobject.idle_add(
2606- self.status_bar.remove_message, 1, self.statusID)
2607+ self.status_bar.remove, 1, self.statusID)
2608 gobject.idle_add(
2609 self.set_status, _('Disconnecting active connections...'))
2610 gobject.idle_add(self.status_area.show_all)
2611@@ -870,13 +882,14 @@ class appGui(WicdGtkUi):
2612 self.edit_advanced(None, nettype, networkid, networkentry)
2613 return False
2614 setup_interface_for_connection()
2615- wireless.ConnectWireless(networkid, reply_handler=handler,
2616- error_handler=handler)
2617+ wireless.ConnectWireless(networkid)
2618 elif nettype == "wired":
2619 setup_interface_for_connection()
2620 wired.ConnectWired(reply_handler=handler, error_handler=handler)
2621
2622- gobject.source_remove(self.update_cb)
2623+ if self.update_cb:
2624+ gobject.source_remove(self.update_cb)
2625+ self.update_cb = None
2626 misc.timeout_add(100, self._wait_for_connect_thread_start, milli=True)
2627
2628 def disconnect(self, widget, nettype, networkid, networkentry):
2629@@ -902,7 +915,7 @@ class appGui(WicdGtkUi):
2630 wired.DisconnectWired(reply_handler=handler, error_handler=handler)
2631 else:
2632 wireless.DisconnectWireless(reply_handler=handler,
2633- error_handler=handler)
2634+ error_handler=handler)
2635
2636 def wait_for_events(self, amt=0):
2637 """ Wait for any pending gtk events to finish before moving on.
2638@@ -925,7 +938,10 @@ class appGui(WicdGtkUi):
2639
2640 """
2641 self.window.hide()
2642- gobject.source_remove(self.update_cb)
2643+
2644+ if self.update_cb:
2645+ gobject.source_remove(self.update_cb)
2646+ self.update_cb = None
2647
2648 dbus_manager.bus.remove_signal_receiver(self._do_statusbar_update, 'StatusChanged',
2649 'org.wicd.daemon')
2650@@ -934,6 +950,7 @@ class appGui(WicdGtkUi):
2651 sys.exit(0)
2652
2653 self.is_visible = False
2654+ daemon.SetGUIOpen(False)
2655 return True
2656
2657 def show_win(self):
2658@@ -951,6 +968,7 @@ class appGui(WicdGtkUi):
2659 self.wait_for_events(0.1)
2660 gobject.idle_add(self.refresh_clicked)
2661 self._do_statusbar_update(*daemon.GetConnectionStatus())
2662+ bus = dbus_manager.get_bus()
2663 bus.add_signal_receiver(self._do_statusbar_update, 'StatusChanged',
2664 'org.wicd.daemon')
2665 self.update_cb = misc.timeout_add(2, self.update_statusbar)
2666diff --git a/src/wicd/frontends/gtk/guiutil.py b/src/wicd/frontends/gtk/guiutil.py
2667index 08ab8ec..8492971 100644
2668--- a/src/wicd/frontends/gtk/guiutil.py
2669+++ b/src/wicd/frontends/gtk/guiutil.py
2670@@ -25,7 +25,8 @@ import wicd.wpath as wpath
2671
2672 HAS_NOTIFY = True
2673 try:
2674- import pynotify
2675+# import pynotify
2676+ import notify2 as pynotify
2677 if not pynotify.init("Wicd"):
2678 print('Could not initalize pynotify')
2679 HAS_NOTIFY = False
2680@@ -90,9 +91,9 @@ def string_input(prompt, secondary, textbox_label):
2681
2682 dialog = gtk.MessageDialog(
2683 None,
2684- gtk.DIALOG_MODAL,
2685- gtk.MESSAGE_QUESTION,
2686- gtk.BUTTONS_OK_CANCEL,
2687+ gtk.DialogFlags.MODAL,
2688+ gtk.MessageType.QUESTION,
2689+ gtk.ButtonsType.OK_CANCEL,
2690 None)
2691
2692 # set the text
2693@@ -102,19 +103,21 @@ def string_input(prompt, secondary, textbox_label):
2694
2695 entry = gtk.Entry()
2696 # allow the user to press enter instead of clicking OK
2697- entry.connect("activate", dialog_response, dialog, gtk.RESPONSE_OK)
2698+ entry.connect("activate", dialog_response, dialog, gtk.ResponseType.OK)
2699
2700 # create an hbox and pack the label and entry in
2701 hbox = gtk.HBox()
2702- hbox.pack_start(gtk.Label(textbox_label), False, 4, 4)
2703- hbox.pack_start(entry)
2704+# hbox.pack_start(gtk.Label(textbox_label), False, 4, 4)
2705+ hbox.pack_start(gtk.Label(textbox_label), False, False, 4)
2706+# hbox.pack_start(entry)
2707+ hbox.pack_start(entry, False, False, 10)
2708
2709 # pack the boxes and show the dialog
2710 # pylint: disable-msg=E1101
2711 dialog.vbox.pack_end(hbox, True, True, 0)
2712 dialog.show_all()
2713
2714- if dialog.run() == gtk.RESPONSE_OK:
2715+ if dialog.run() == gtk.ResponseType.OK:
2716 text = entry.get_text()
2717 dialog.destroy()
2718 return text
2719@@ -134,7 +137,9 @@ class LeftAlignedLabel(gtk.Label):
2720 """GtkLabel with text aligned to left. """
2721 def __init__(self, label=None):
2722 gtk.Label.__init__(self, label)
2723- self.set_alignment(0.0, 0.5)
2724+# self.set_alignment(0.0, 0.5)
2725+ self.set_xalign(0.0)
2726+ self.set_yalign(0.5)
2727
2728
2729 class LabelEntry(gtk.HBox):
2730@@ -146,8 +151,8 @@ class LabelEntry(gtk.HBox):
2731 self.label = LeftAlignedLabel()
2732 self.label.set_text(text)
2733 self.label.set_size_request(170, -1)
2734- self.pack_start(self.label, fill=True, expand=True)
2735- self.pack_start(self.entry, fill=False, expand=False)
2736+ self.pack_start(self.label, fill=True, expand=True, padding=10)
2737+ self.pack_start(self.entry, fill=False, expand=False, padding=10)
2738 self.label.show()
2739 self.entry.show()
2740 self.entry.connect('focus-out-event', self.hide_characters)
2741@@ -158,7 +163,7 @@ class LabelEntry(gtk.HBox):
2742 def set_text(self, text):
2743 """ Set text of the GtkEntry. """
2744 # For compatibility...
2745- self.entry.set_text(text)
2746+ self.entry.set_text(str(text))
2747
2748 def get_text(self):
2749 """ Get text of the GtkEntry. """
2750@@ -192,7 +197,9 @@ class GreyLabel(gtk.Label):
2751
2752 def set_label(self, text):
2753 self.set_markup(text)
2754- self.set_alignment(0, 0)
2755+# self.set_alignment(0, 0)
2756+ self.set_xalign(0)
2757+ self.set_yalign(0)
2758
2759
2760 class ProtectedLabelEntry(gtk.HBox):
2761@@ -209,9 +216,12 @@ class ProtectedLabelEntry(gtk.HBox):
2762 self.check.set_size_request(5, -1)
2763 self.check.set_active(False)
2764 self.check.set_focus_on_click(False)
2765- self.pack_start(self.label, fill=True, expand=True)
2766- self.pack_start(self.check, fill=True, expand=True)
2767- self.pack_start(self.entry, fill=False, expand=False)
2768+# self.pack_start(self.label, fill=True, expand=True)
2769+ self.pack_start(self.label, fill=True, expand=True, padding=10)
2770+# self.pack_start(self.check, fill=True, expand=True)
2771+ self.pack_start(self.check, fill=True, expand=True, padding=10)
2772+# self.pack_start(self.entry, fill=False, expand=False)
2773+ self.pack_start(self.entry, fill=False, expand=False, padding=10)
2774 self.label.show()
2775 self.check.show()
2776 self.entry.show()
2777@@ -221,7 +231,7 @@ class ProtectedLabelEntry(gtk.HBox):
2778 def set_entry_text(self, text):
2779 """ Set text of the GtkEntry. """
2780 # For compatibility...
2781- self.entry.set_text(text)
2782+ self.entry.set_text(str(text))
2783
2784 def get_entry_text(self):
2785 """ Get text of the GtkEntry. """
2786@@ -252,8 +262,8 @@ class LabelCombo(gtk.HBox):
2787 cell = gtk.CellRendererText()
2788 self.combo.pack_start(cell, True)
2789 self.combo.add_attribute(cell, 'text', 0)
2790- self.pack_start(self.label, fill=True, expand=True)
2791- self.pack_start(self.combo, fill=False, expand=False)
2792+ self.pack_start(self.label, fill=True, expand=True, padding=10)
2793+ self.pack_start(self.combo, fill=False, expand=False, padding=10)
2794 self.label.show()
2795 self.combo.show()
2796 self.show()
2797diff --git a/src/wicd/frontends/gtk/netentry.py b/src/wicd/frontends/gtk/netentry.py
2798index 97a5f31..7e4941b 100644
2799--- a/src/wicd/frontends/gtk/netentry.py
2800+++ b/src/wicd/frontends/gtk/netentry.py
2801@@ -10,6 +10,7 @@ contained within them.
2802 # Copyright (C) 2008-2009 Dan O'Reilly
2803 # Copyright (C) 2009 Andrew Psaltis
2804 # Copyright (C) 2011 David Paleino
2805+# Copyright (C) 2024 Takahiro Yoshizawa
2806 #
2807 # This program is free software; you can redistribute it and/or modify
2808 # it under the terms of the GNU General Public License Version 2 as
2809@@ -26,7 +27,8 @@ contained within them.
2810
2811 import gi
2812 gi.require_version('Gtk', '3.0')
2813-from gi.repository import Gtk as gtk
2814+#from gi.repository import Gtk as gtk
2815+from gi.repository import Gtk as gtk, Gdk as gdk
2816 import os
2817
2818 import wicd.misc as misc
2819@@ -70,12 +72,12 @@ class AdvancedSettingsDialog(gtk.Dialog):
2820 gtk.Dialog.__init__(
2821 self,
2822 title=title,
2823- flags=gtk.DIALOG_MODAL,
2824+ flags=gtk.DialogFlags.MODAL,
2825 buttons=(
2826 gtk.STOCK_CANCEL,
2827- gtk.RESPONSE_REJECT,
2828+ gtk.ResponseType.NO,
2829 gtk.STOCK_OK,
2830- gtk.RESPONSE_ACCEPT
2831+ gtk.ResponseType.OK
2832 )
2833 )
2834
2835@@ -97,14 +99,14 @@ class AdvancedSettingsDialog(gtk.Dialog):
2836 self.chkbox_use_dhcp_hostname = gtk.CheckButton()
2837 self.txt_dhcp_hostname = LabelEntry("DHCP Hostname")
2838 dhcp_hostname_hbox.pack_start(
2839- self.chkbox_use_dhcp_hostname, fill=False, expand=False)
2840- dhcp_hostname_hbox.pack_start(self.txt_dhcp_hostname)
2841+ self.chkbox_use_dhcp_hostname, fill=False, expand=False, padding=0)
2842+ dhcp_hostname_hbox.pack_start(self.txt_dhcp_hostname, False, False, 0)
2843 self.chkbox_static_ip = gtk.CheckButton(_('Use Static IPs'))
2844 self.chkbox_static_dns = gtk.CheckButton(_('Use Static DNS'))
2845 self.chkbox_global_dns = gtk.CheckButton(_('Use global DNS servers'))
2846 self.hbox_dns = gtk.HBox(False, 0)
2847- self.hbox_dns.pack_start(self.chkbox_static_dns)
2848- self.hbox_dns.pack_start(self.chkbox_global_dns)
2849+ self.hbox_dns.pack_start(self.chkbox_static_dns, False, False, 0)
2850+ self.hbox_dns.pack_start(self.chkbox_global_dns, False, False, 0)
2851
2852 # Set up the script settings button
2853 self.script_button = gtk.Button()
2854@@ -117,31 +119,31 @@ class AdvancedSettingsDialog(gtk.Dialog):
2855
2856 self.button_hbox = gtk.HBox(False, 2)
2857 self.button_hbox.pack_start(
2858- self.script_button, fill=False, expand=False)
2859+ self.script_button, fill=False, expand=False, padding=0)
2860 self.button_hbox.show()
2861
2862 self.swindow = gtk.ScrolledWindow()
2863- self.swindow.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
2864+ self.swindow.set_policy(gtk.PolicyType.NEVER, gtk.PolicyType.AUTOMATIC)
2865 self.viewport = gtk.Viewport()
2866- self.viewport.set_shadow_type(gtk.SHADOW_NONE)
2867+ self.viewport.set_shadow_type(gtk.ShadowType.NONE)
2868 self.cvbox = gtk.VBox()
2869 self.viewport.add(self.cvbox)
2870 self.swindow.add(self.viewport)
2871 # pylint: disable-msg=E1101
2872- self.vbox.pack_start(self.swindow)
2873+ self.vbox.pack_start(self.swindow, True, True, 0)
2874
2875 assert(isinstance(self.cvbox, gtk.VBox))
2876- self.cvbox.pack_start(self.chkbox_static_ip, fill=False, expand=False)
2877- self.cvbox.pack_start(self.txt_ip, fill=False, expand=False)
2878- self.cvbox.pack_start(self.txt_netmask, fill=False, expand=False)
2879- self.cvbox.pack_start(self.txt_gateway, fill=False, expand=False)
2880- self.cvbox.pack_start(self.hbox_dns, fill=False, expand=False)
2881- self.cvbox.pack_start(self.txt_domain, fill=False, expand=False)
2882- self.cvbox.pack_start(self.txt_search_dom, fill=False, expand=False)
2883- self.cvbox.pack_start(self.txt_dns_1, fill=False, expand=False)
2884- self.cvbox.pack_start(self.txt_dns_2, fill=False, expand=False)
2885- self.cvbox.pack_start(self.txt_dns_3, fill=False, expand=False)
2886- self.cvbox.pack_start(dhcp_hostname_hbox, fill=False, expand=False)
2887+ self.cvbox.pack_start(self.chkbox_static_ip, fill=False, expand=False, padding=0)
2888+ self.cvbox.pack_start(self.txt_ip, fill=False, expand=False, padding=0)
2889+ self.cvbox.pack_start(self.txt_netmask, fill=False, expand=False, padding=0)
2890+ self.cvbox.pack_start(self.txt_gateway, fill=False, expand=False, padding=0)
2891+ self.cvbox.pack_start(self.hbox_dns, fill=False, expand=False, padding=0)
2892+ self.cvbox.pack_start(self.txt_domain, fill=False, expand=False, padding=0)
2893+ self.cvbox.pack_start(self.txt_search_dom, fill=False, expand=False, padding=0)
2894+ self.cvbox.pack_start(self.txt_dns_1, fill=False, expand=False, padding=0)
2895+ self.cvbox.pack_start(self.txt_dns_2, fill=False, expand=False, padding=0)
2896+ self.cvbox.pack_start(self.txt_dns_3, fill=False, expand=False, padding=0)
2897+ self.cvbox.pack_start(dhcp_hostname_hbox, fill=False, expand=False, padding=0)
2898 self.cvbox.pack_end(
2899 self.button_hbox, fill=False, expand=False, padding=5)
2900
2901@@ -162,7 +164,7 @@ class AdvancedSettingsDialog(gtk.Dialog):
2902 def set_default_size(self):
2903 """ Set default window size. """
2904 width, height = self.get_size()
2905- s_height = gtk.gdk.screen_height()
2906+ s_height = gdk.Screen.height()
2907 if s_height < 768:
2908 height = s_height * .75
2909 else:
2910@@ -350,7 +352,8 @@ class AdvancedSettingsDialog(gtk.Dialog):
2911 else:
2912 box = LabelEntry(field_text)
2913
2914- self.vbox_encrypt_info.pack_start(box)
2915+# self.vbox_encrypt_info.pack_start(box)
2916+ self.vbox_encrypt_info.pack_start(box, False, False, 0)
2917 # Add the data to a dict, so that the information
2918 # can be easily accessed by giving the name of the wanted
2919 # data.
2920@@ -378,7 +381,8 @@ class WiredSettingsDialog(AdvancedSettingsDialog):
2921 ## This section is largely copied from WirelessSettingsDialog, but with
2922 ## some changes
2923 # Set up encryption stuff
2924- self.combo_encryption = gtk.combo_box_new_text()
2925+# self.combo_encryption = gtk.combo_box_new_text()
2926+ self.combo_encryption = gtk.ComboBoxText()
2927 self.chkbox_encryption = gtk.CheckButton(_('Use Encryption'))
2928 # Make the vbox to hold the encryption stuff.
2929 self.vbox_encrypt_info = gtk.VBox(False, 0)
2930@@ -394,9 +398,9 @@ class WiredSettingsDialog(AdvancedSettingsDialog):
2931 self.change_encrypt_method()
2932 self.toggle_encryption()
2933
2934- self.cvbox.pack_start(self.chkbox_encryption, False, False)
2935- self.cvbox.pack_start(self.combo_encryption, False, False)
2936- self.cvbox.pack_start(self.vbox_encrypt_info, False, False)
2937+ self.cvbox.pack_start(self.chkbox_encryption, False, False, 0)
2938+ self.cvbox.pack_start(self.combo_encryption, False, False, 0)
2939+ self.cvbox.pack_start(self.vbox_encrypt_info, False, False, 0)
2940
2941 # Connect signals.
2942 self.chkbox_encryption.connect("toggled", self.toggle_encryption)
2943@@ -413,7 +417,14 @@ class WiredSettingsDialog(AdvancedSettingsDialog):
2944 def edit_scripts(self, widget=None, event=None):
2945 """ Launch the script editting dialog. """
2946 profile = self.prof_name
2947- cmdend = [os.path.join(wpath.gtk, "configscript.py"), profile, "wired"]
2948+ spec = misc.find_configscript()
2949+ cmdend = []
2950+ if spec and spec.origin:
2951+ cmdend.extend(["python3", spec.origin, profile, "wired"])
2952+ else:
2953+ print("configscript not found")
2954+ return
2955+
2956 if os.getuid() != 0:
2957 cmdbase = misc.get_sudo_cmd(
2958 _('You must enter your password to configure scripts'),
2959@@ -520,7 +531,8 @@ class WirelessSettingsDialog(AdvancedSettingsDialog):
2960
2961 # Set up encryption stuff
2962 self.networkID = networkID
2963- self.combo_encryption = gtk.combo_box_new_text()
2964+# self.combo_encryption = gtk.combo_box_new_text()
2965+ self.combo_encryption = gtk.ComboBoxText()
2966 self.chkbox_encryption = gtk.CheckButton(_('Use Encryption'))
2967 self.chkbox_global_settings = gtk.CheckButton(
2968 _('Use these settings for all networks sharing this essid'))
2969@@ -529,14 +541,22 @@ class WirelessSettingsDialog(AdvancedSettingsDialog):
2970
2971 self.combo_rate = LabelCombo(_('Wireless bitrate'))
2972 rates = gtk.ListStore(str)
2973- self.bitrates = wireless.GetAvailableBitrates()
2974- self.bitrates.append('auto')
2975+# self.bitrates = list(wireless.GetAvailableBitrates())
2976+ self.bitrates = [str(item) for item in wireless.GetAvailableBitrates()]
2977+# self.bitrates = wireless.GetWirelessProperty(networkID, 'bitrates')
2978+ # Temporary workaround for the issue where an error occurs if the list has only one item.
2979+ if len(self.bitrates) == 0:
2980+ self.bitrates = ['auto', '']
2981+ elif len(self.bitrates) == 1:
2982+ self.bitrates.append('')
2983+ else:
2984+ self.bitrates.append('auto')
2985 for br in self.bitrates:
2986 rates.append((br,))
2987 self.combo_rate.set_model(rates)
2988- self.chkbox_lower_rate = gtk.CheckButton(_('Allow lower bitrates'))
2989- rate_vbox.pack_start(self.combo_rate)
2990- rate_vbox.pack_start(self.chkbox_lower_rate)
2991+# self.chkbox_lower_rate = gtk.CheckButton(_('Allow lower bitrates'))
2992+ rate_vbox.pack_start(self.combo_rate, False, False, 0)
2993+# rate_vbox.pack_start(self.chkbox_lower_rate, False, False, 0)
2994
2995 # Make the vbox to hold the encryption stuff.
2996 self.vbox_encrypt_info = gtk.VBox(False, 0)
2997@@ -546,7 +566,7 @@ class WirelessSettingsDialog(AdvancedSettingsDialog):
2998 self.encrypt_types = misc.LoadEncryptionMethods()
2999
3000 information_button = gtk.Button(stock=gtk.STOCK_INFO)
3001- self.button_hbox.pack_start(information_button, False, False)
3002+ self.button_hbox.pack_start(information_button, False, False, 0)
3003 information_button.connect(
3004 'clicked',
3005 lambda *a, **k: WirelessInformationDialog(networkID, self)
3006@@ -569,11 +589,11 @@ class WirelessSettingsDialog(AdvancedSettingsDialog):
3007 self.combo_encryption.set_active(0)
3008 self.change_encrypt_method()
3009
3010- self.cvbox.pack_start(rate_vbox, False, False)
3011- self.cvbox.pack_start(self.chkbox_global_settings, False, False)
3012- self.cvbox.pack_start(self.chkbox_encryption, False, False)
3013- self.cvbox.pack_start(self.combo_encryption, False, False)
3014- self.cvbox.pack_start(self.vbox_encrypt_info, False, False)
3015+ self.cvbox.pack_start(rate_vbox, False, False, 0)
3016+ self.cvbox.pack_start(self.chkbox_global_settings, False, False, 0)
3017+ self.cvbox.pack_start(self.chkbox_encryption, False, False, 0)
3018+ self.cvbox.pack_start(self.combo_encryption, False, False, 0)
3019+ self.cvbox.pack_start(self.vbox_encrypt_info, False, False, 0)
3020
3021 # Connect signals.
3022 self.chkbox_encryption.connect("toggled", self.toggle_encryption)
3023@@ -590,8 +610,14 @@ class WirelessSettingsDialog(AdvancedSettingsDialog):
3024
3025 def edit_scripts(self, widget=None, event=None):
3026 """ Launch the script editting dialog. """
3027- cmdend = [os.path.join(wpath.gtk, "configscript.py"),
3028- str(self.networkID), "wireless"]
3029+ spec = misc.find_configscript()
3030+ cmdend = []
3031+ if spec and spec.origin:
3032+ cmdend.extend(["python3", spec.origin, str(self.networkID), "wireless"])
3033+ else:
3034+ print("configscript not found")
3035+ return
3036+
3037 if os.getuid() != 0:
3038 cmdbase = misc.get_sudo_cmd(
3039 _('You must enter your password to configure scripts'),
3040@@ -659,11 +685,11 @@ class WirelessSettingsDialog(AdvancedSettingsDialog):
3041 if chosen_bitrate not in self.bitrates:
3042 chosen_bitrate = 'auto'
3043 self.combo_rate.set_active(self.bitrates.index(chosen_bitrate))
3044- self.chkbox_lower_rate.set_active(
3045- bool(
3046- wireless.GetWirelessProperty(networkID, 'allow_lower_bitrates')
3047- )
3048- )
3049+# self.chkbox_lower_rate.set_active(
3050+# bool(
3051+# wireless.GetWirelessProperty(networkID, 'allow_lower_bitrates')
3052+# )
3053+# )
3054
3055 activeID = -1 # Set the menu to this item when we are done
3056 user_enctype = wireless.GetWirelessProperty(networkID, "enctype")
3057@@ -725,14 +751,17 @@ class WirelessSettingsDialog(AdvancedSettingsDialog):
3058 if self.combo_rate.get_active() == -1:
3059 self.set_net_prop('bitrate', 'auto')
3060 else:
3061+ value = self.bitrates[self.combo_rate.get_active()]
3062+ if value == "":
3063+ value = 'auto'
3064 self.set_net_prop(
3065 'bitrate',
3066- self.bitrates[self.combo_rate.get_active()]
3067+ value
3068 )
3069- self.set_net_prop(
3070- 'allow_lower_bitrates',
3071- bool(self.chkbox_lower_rate.get_active())
3072- )
3073+# self.set_net_prop(
3074+# 'allow_lower_bitrates',
3075+# bool(self.chkbox_lower_rate.get_active())
3076+# )
3077 wireless.SaveWirelessNetworkProfile(networkid)
3078 return True
3079
3080@@ -753,7 +782,7 @@ class NetworkEntry(gtk.HBox):
3081 setup_dbus()
3082 gtk.HBox.__init__(self, False, 2)
3083 self.image = gtk.Image()
3084- self.pack_start(self.image, False, False)
3085+ self.pack_start(self.image, False, False, 0)
3086
3087 # Create an HBox to hold the buttons
3088 self.buttons_hbox = gtk.HBox(False, 6)
3089@@ -761,38 +790,42 @@ class NetworkEntry(gtk.HBox):
3090 # Set up the Connect button
3091 self.connect_button = gtk.Button(stock=gtk.STOCK_CONNECT)
3092 self.connect_hbox = gtk.HBox(False, 2)
3093- self.connect_hbox.pack_start(self.connect_button, False, False)
3094+ self.connect_hbox.pack_start(self.connect_button, False, False, 0)
3095 self.connect_hbox.show()
3096
3097 # Set up the Disconnect button
3098 self.disconnect_button = gtk.Button(stock=gtk.STOCK_DISCONNECT)
3099- self.connect_hbox.pack_start(self.disconnect_button, False, False)
3100+ self.connect_hbox.pack_start(self.disconnect_button, False, False, 0)
3101
3102 # Create a label to hold the name of the entry
3103 self.name_label = gtk.Label()
3104- self.name_label.set_alignment(0, 0.5)
3105+# self.name_label.set_alignment(0, 0.5)
3106+ self.name_label.set_xalign(0)
3107+ self.name_label.set_yalign(0.5)
3108
3109 # Set up the VBox that goes in the gtk.Expander
3110 self.expander_vbox = gtk.VBox(False, 1)
3111 self.expander_vbox.show()
3112- self.pack_end(self.expander_vbox)
3113+ self.pack_end(self.expander_vbox, True, True, 10)
3114
3115 # Set up the advanced settings button
3116 self.advanced_button = gtk.Button()
3117 self.advanced_image = gtk.Image()
3118 self.advanced_image.set_from_stock(gtk.STOCK_EDIT, 4)
3119 self.advanced_image.set_padding(4, 0)
3120- self.advanced_button.set_alignment(.5, .5)
3121+# self.advanced_button.set_alignment(.5, .5)
3122+ self.advanced_button.set_halign(.5)
3123+ self.advanced_button.set_valign(.5)
3124 self.advanced_button.set_label(_('Properties'))
3125 self.advanced_button.set_image(self.advanced_image)
3126
3127- self.buttons_hbox.pack_start(self.connect_hbox, False, False)
3128- self.buttons_hbox.pack_start(self.advanced_button, False, False)
3129+ self.buttons_hbox.pack_start(self.connect_hbox, False, False, 0)
3130+ self.buttons_hbox.pack_start(self.advanced_button, False, False, 0)
3131
3132 self.vbox_top = gtk.VBox(False, 0)
3133- self.expander_vbox.pack_start(self.name_label)
3134- self.expander_vbox.pack_start(self.vbox_top)
3135- self.expander_vbox.pack_start(self.buttons_hbox)
3136+ self.expander_vbox.pack_start(self.name_label, False, False, 0)
3137+ self.expander_vbox.pack_start(self.vbox_top, False, False, 0)
3138+ self.expander_vbox.pack_start(self.buttons_hbox, False, False, 0)
3139
3140 def destroy_called(self, *args):
3141 """ Clean up everything. """
3142@@ -808,9 +841,11 @@ class WiredNetworkEntry(NetworkEntry):
3143 NetworkEntry.__init__(self)
3144 # Center the picture and pad it a bit
3145 self.image.set_padding(0, 0)
3146- self.image.set_alignment(.5, .5)
3147+# self.image.set_alignment(.5, .5)
3148+ self.image.set_halign(.5)
3149+ self.image.set_valign(.5)
3150 self.image.set_size_request(60, -1)
3151- self.image.set_from_icon_name("wired-gui", gtk.ICON_SIZE_DND)
3152+ self.image.set_from_icon_name("wired-gui", gtk.IconSize.SMALL_TOOLBAR)
3153 self.image.show()
3154 self.connect_button.show()
3155
3156@@ -828,22 +863,23 @@ class WiredNetworkEntry(NetworkEntry):
3157 )
3158 self.chkbox_default_profile = gtk.CheckButton(
3159 _('Use as default profile (overwrites any previous default)'))
3160- self.combo_profile_names = gtk.combo_box_new_text()
3161+# self.combo_profile_names = gtk.combo_box_new_text()
3162+ self.combo_profile_names = gtk.ComboBoxText()
3163
3164 # Format the profile help label.
3165- self.profile_help.set_justify(gtk.JUSTIFY_LEFT)
3166+ self.profile_help.set_justify(gtk.Justification.LEFT)
3167 self.profile_help.set_line_wrap(True)
3168
3169 # Pack the various VBox objects.
3170 self.hbox_temp = gtk.HBox(False, 0)
3171 self.hbox_def = gtk.HBox(False, 0)
3172- self.vbox_top.pack_start(self.profile_help, True, True)
3173- self.vbox_top.pack_start(self.hbox_def)
3174- self.vbox_top.pack_start(self.hbox_temp)
3175- self.hbox_temp.pack_start(self.combo_profile_names, True, True)
3176- self.hbox_temp.pack_start(self.button_add, False, False)
3177- self.hbox_temp.pack_start(self.button_delete, False, False)
3178- self.hbox_def.pack_start(self.chkbox_default_profile, False, False)
3179+ self.vbox_top.pack_start(self.profile_help, True, True, 0)
3180+ self.vbox_top.pack_start(self.hbox_def, False, False, 0)
3181+ self.vbox_top.pack_start(self.hbox_temp, False, False, 0)
3182+ self.hbox_temp.pack_start(self.combo_profile_names, True, True, 0)
3183+ self.hbox_temp.pack_start(self.button_add, False, False, 0)
3184+ self.hbox_temp.pack_start(self.button_delete, False, False, 0)
3185+ self.hbox_def.pack_start(self.chkbox_default_profile, False, False, 0)
3186
3187 # Connect events
3188 self.button_add.connect("clicked", self.add_profile)
3189@@ -855,7 +891,8 @@ class WiredNetworkEntry(NetworkEntry):
3190 # Build profile list.
3191 self.profile_list = wired.GetWiredProfileList()
3192 default_prof = wired.GetDefaultWiredNetwork()
3193- if self.profile_list:
3194+ profile_help_flag = False
3195+ if self.profile_list and not all(str(p) == '' for p in self.profile_list):
3196 starting_index = 0
3197 for x, prof in enumerate(self.profile_list):
3198 self.combo_profile_names.append_text(prof)
3199@@ -865,16 +902,24 @@ class WiredNetworkEntry(NetworkEntry):
3200 else:
3201 print("no wired profiles found")
3202 self.profile_help.show()
3203+ profile_help_flag = True
3204+ if self.is_full_gui:
3205+ self.button_delete.set_sensitive(False)
3206+ self.advanced_button.set_sensitive(False)
3207+ self.connect_button.set_sensitive(False)
3208+ self.chkbox_default_profile.set_sensitive(False)
3209+
3210
3211 self.advanced_dialog = \
3212 WiredSettingsDialog(self.combo_profile_names.get_active_text())
3213
3214 # Show everything, but hide the profile help label.
3215 self.show_all()
3216- self.profile_help.hide()
3217+ if not profile_help_flag:
3218+ self.profile_help.hide()
3219
3220 # Toggle the default profile checkbox to the correct state.
3221- if to_bool(wired.GetWiredProperty("default")):
3222+ if to_bool(wired.GetWiredProperty("default")) and self.profile_list and not all(str(p) == '' for p in self.profile_list):
3223 self.chkbox_default_profile.set_active(True)
3224 else:
3225 self.chkbox_default_profile.set_active(False)
3226@@ -919,7 +964,9 @@ class WiredNetworkEntry(NetworkEntry):
3227 "computer. It allows you to easily distinguish between different "
3228 "network profiles.",
3229 "Profile name:"
3230- ).strip()
3231+ )
3232+ if response:
3233+ response = response.strip()
3234
3235 # if response is "" or None
3236 if not response:
3237@@ -941,27 +988,34 @@ class WiredNetworkEntry(NetworkEntry):
3238 self.button_delete.set_sensitive(True)
3239 self.connect_button.set_sensitive(True)
3240 self.advanced_button.set_sensitive(True)
3241+ self.chkbox_default_profile.set_sensitive(True)
3242
3243 def remove_profile(self, widget):
3244 """ Remove a profile from the profile list. """
3245 print("removing profile")
3246- profile_name = self.combo_profile_names.get_active_text()
3247- wired.DeleteWiredNetworkProfile(profile_name)
3248- self.combo_profile_names.remove_text(self.combo_profile_names.
3249+ model = self.combo_profile_names.get_model()
3250+ if len(model) > 0:
3251+ profile_name = self.combo_profile_names.get_active_text()
3252+ wired.DeleteWiredNetworkProfile(profile_name)
3253+ self.combo_profile_names.remove(self.combo_profile_names.
3254 get_active())
3255- self.combo_profile_names.set_active(0)
3256- self.advanced_dialog.prof_name = \
3257- self.combo_profile_names.get_active_text()
3258- if not wired.GetWiredProfileList():
3259- self.profile_help.show()
3260- entry = self.combo_profile_names.child
3261- entry.set_text("")
3262- if self.is_full_gui:
3263- self.button_delete.set_sensitive(False)
3264- self.advanced_button.set_sensitive(False)
3265- self.connect_button.set_sensitive(False)
3266- else:
3267- self.profile_help.hide()
3268+ self.combo_profile_names.set_active(0)
3269+ self.advanced_dialog.prof_name = \
3270+ self.combo_profile_names.get_active_text()
3271+ profile_list = wired.GetWiredProfileList()
3272+ if not profile_list or all(str(p) == '' for p in profile_list):
3273+ self.profile_help.show()
3274+ model_after_remove = self.combo_profile_names.get_model()
3275+ if len(model_after_remove) > 0:
3276+ entry = self.combo_profile_names.child
3277+ entry.set_text("")
3278+ if self.is_full_gui:
3279+ self.button_delete.set_sensitive(False)
3280+ self.advanced_button.set_sensitive(False)
3281+ self.connect_button.set_sensitive(False)
3282+ self.chkbox_default_profile.set_sensitive(False)
3283+ else:
3284+ self.profile_help.hide()
3285
3286 def toggle_default_profile(self, widget):
3287 """ Change the default profile. """
3288@@ -1003,7 +1057,9 @@ class WirelessNetworkEntry(NetworkEntry):
3289
3290 self.networkID = networkID
3291 self.image.set_padding(0, 0)
3292- self.image.set_alignment(.5, .5)
3293+# self.image.set_alignment(.5, .5)
3294+ self.image.set_halign(.5)
3295+ self.image.set_valign(.5)
3296 self.image.set_size_request(60, -1)
3297 self.image.show()
3298 self.essid = noneToBlankString(
3299@@ -1020,7 +1076,7 @@ class WirelessNetworkEntry(NetworkEntry):
3300
3301 self.set_signal_strength(
3302 wireless.GetWirelessProperty(networkID, 'quality'),
3303- wireless.GetWirelessProperty(networkID, 'strength')
3304+ wireless.GetWirelessProperty(networkID, 'strength'),
3305 )
3306 self.set_encryption(
3307 wireless.GetWirelessProperty(networkID, 'encryption'),
3308@@ -1038,8 +1094,8 @@ class WirelessNetworkEntry(NetworkEntry):
3309 )
3310 # Add the wireless network specific parts to the NetworkEntry
3311 # VBox objects.
3312- self.vbox_top.pack_start(self.chkbox_autoconnect, False, False)
3313- self.vbox_top.pack_start(self.chkbox_neverconnect, False, False)
3314+ self.vbox_top.pack_start(self.chkbox_autoconnect, False, False, 0)
3315+ self.vbox_top.pack_start(self.chkbox_neverconnect, False, False, 0)
3316
3317 if to_bool(self.format_entry(networkID, "automatic")):
3318 self.chkbox_autoconnect.set_active(True)
3319@@ -1113,8 +1169,8 @@ class WirelessNetworkEntry(NetworkEntry):
3320 self.connect_button.set_sensitive(False)
3321 if not apbssid:
3322 apbssid = wireless.GetApBssid()
3323- if state == misc.WIRELESS and \
3324- apbssid == wireless.GetWirelessProperty(self.networkID, "bssid"):
3325+ if state == misc.WIRELESS and apbssid is not None and \
3326+ apbssid.upper() == wireless.GetWirelessProperty(self.networkID, "bssid").upper():
3327 self.disconnect_button.show()
3328 self.connect_button.hide()
3329 else:
3330@@ -1128,9 +1184,10 @@ class WirelessNetworkEntry(NetworkEntry):
3331 else:
3332 strength = -1
3333 if dbm_strength:
3334- dbm_strength = int(dbm_strength)
3335+ dbm_strength = int(float(dbm_strength))
3336 else:
3337 dbm_strength = -100
3338+
3339 display_type = daemon.GetSignalDisplayType()
3340 if daemon.GetWPADriver() == 'ralink legacy' or display_type == 1:
3341 # Use the -xx dBm signal strength to display a signal icon
3342@@ -1159,7 +1216,7 @@ class WirelessNetworkEntry(NetworkEntry):
3343 signal_img = 'signal-25'
3344 ending = "%"
3345 disp_strength = str(strength)
3346- self.image.set_from_icon_name(signal_img, gtk.ICON_SIZE_DND)
3347+ self.image.set_from_icon_name(signal_img, gtk.IconSize.SMALL_TOOLBAR)
3348 self.lbl_strength.set_label(disp_strength + ending)
3349 self.image.show()
3350
3351@@ -1181,6 +1238,7 @@ class WirelessNetworkEntry(NetworkEntry):
3352 return noneToBlankString(wireless.GetWirelessProperty(networkid, label))
3353
3354
3355+
3356 class WirelessInformationDialog(gtk.Dialog):
3357 """ Wireless information dialog. """
3358 def __init__(self, networkID, parent):
3359@@ -1188,21 +1246,31 @@ class WirelessInformationDialog(gtk.Dialog):
3360
3361 # Make the combo box.
3362 self.lbl_strength = gtk.Label()
3363- self.lbl_strength.set_alignment(0, 0.5)
3364+# self.lbl_strength.set_alignment(0, 0.5)
3365+ self.lbl_strength.set_xalign(0)
3366+ self.lbl_strength.set_xalign(0.5)
3367 self.lbl_encryption = gtk.Label()
3368- self.lbl_encryption.set_alignment(0, 0.5)
3369+# self.lbl_encryption.set_alignment(0, 0.5)
3370+ self.lbl_encryption.set_xalign(0)
3371+ self.lbl_encryption.set_yalign(0.5)
3372 self.lbl_mac = gtk.Label()
3373- self.lbl_mac.set_alignment(0, 0.5)
3374+# self.lbl_mac.set_alignment(0, 0.5)
3375+ self.lbl_mac.set_xalign(0)
3376+ self.lbl_mac.set_yalign(0.5)
3377 self.lbl_channel = gtk.Label()
3378- self.lbl_channel.set_alignment(0, 0.5)
3379+# self.lbl_channel.set_alignment(0, 0.5)
3380+ self.lbl_channel.set_xalign(0)
3381+ self.lbl_channel.set_yalign(0.5)
3382 self.lbl_mode = gtk.Label()
3383- self.lbl_mode.set_alignment(0, 0.5)
3384+# self.lbl_mode.set_alignment(0, 0.5)
3385+ self.lbl_mode.set_xalign(0)
3386+ self.lbl_mode.set_yalign(0.5)
3387 self.hbox_status = gtk.HBox(False, 5)
3388
3389 # Set the values of the network info labels.
3390 self.set_signal_strength(
3391 wireless.GetWirelessProperty(networkID, 'quality'),
3392- wireless.GetWirelessProperty(networkID, 'strength')
3393+ wireless.GetWirelessProperty(networkID, 'strength'),
3394 )
3395 self.set_mac_address(wireless.GetWirelessProperty(networkID, 'bssid'))
3396 self.set_mode(wireless.GetWirelessProperty(networkID, 'mode'))
3397@@ -1214,11 +1282,12 @@ class WirelessInformationDialog(gtk.Dialog):
3398
3399 self.set_title('Network Information')
3400 vbox = self.vbox
3401- self.set_has_separator(False)
3402+# self.set_has_separator(False)
3403 table = gtk.Table(5, 2)
3404 table.set_col_spacings(12)
3405 # pylint: disable-msg=E1101
3406- vbox.pack_start(table)
3407+# vbox.pack_start(table)
3408+ vbox.pack_start(table, False, False, 0)
3409
3410 # Pack the network status HBox.
3411 table.attach(LeftAlignedLabel('Signal strength:'), 0, 1, 0, 1)
3412@@ -1239,7 +1308,7 @@ class WirelessInformationDialog(gtk.Dialog):
3413 # pylint: disable-msg=E1101
3414 vbox.show_all()
3415
3416- self.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)
3417+ self.add_button(gtk.STOCK_CLOSE, gtk.ResponseType.CLOSE)
3418 self.show()
3419 self.run()
3420 self.destroy()
3421@@ -1251,7 +1320,7 @@ class WirelessInformationDialog(gtk.Dialog):
3422 else:
3423 strength = -1
3424 if dbm_strength is not None:
3425- dbm_strength = int(dbm_strength)
3426+ dbm_strength = int(float(dbm_strength))
3427 else:
3428 dbm_strength = -100
3429 display_type = daemon.GetSignalDisplayType()
3430@@ -1308,3 +1377,4 @@ class WirelessInformationDialog(gtk.Dialog):
3431 def format_entry(self, networkid, label):
3432 """ Helper method for fetching/formatting wireless properties. """
3433 return noneToBlankString(wireless.GetWirelessProperty(networkid, label))
3434+
3435diff --git a/src/wicd/frontends/gtk/prefs.py b/src/wicd/frontends/gtk/prefs.py
3436index 006999c..dc74993 100644
3437--- a/src/wicd/frontends/gtk/prefs.py
3438+++ b/src/wicd/frontends/gtk/prefs.py
3439@@ -10,6 +10,7 @@ handles recieving/sendings the settings from/to the daemon.
3440 #
3441 # Copyright (C) 2008-2009 Adam Blackburn
3442 # Copyright (C) 2008-2009 Dan O'Reilly
3443+# Copyright (C) 2024 Takahiro Yoshizawa
3444 #
3445 # This program is free software; you can redistribute it and/or modify
3446 # it under the terms of the GNU General Public License Version 2 as
3447@@ -27,6 +28,8 @@ handles recieving/sendings the settings from/to the daemon.
3448 import gi
3449 gi.require_version('Gtk', '3.0')
3450 from gi.repository import Gtk as gtk
3451+gi.require_version('Gdk', '3.0')
3452+from gi.repository import Gdk as gdk
3453 from gi.repository import GObject as gobject
3454 import os
3455
3456@@ -211,7 +214,8 @@ class PreferencesDialog(object):
3457
3458 # if pynotify isn't installed disable the option
3459 try:
3460- import pynotify
3461+# import pynotify
3462+ import notify2 as pynotify
3463 except ImportError:
3464 self.notificationscheckbox.set_active(False)
3465 self.notificationscheckbox.set_sensitive(False)
3466@@ -344,7 +348,7 @@ class PreferencesDialog(object):
3467 cell = gtk.CellRendererText()
3468 combobox.pack_start(cell, True)
3469 combobox.add_attribute(cell, 'text', 0)
3470- return combobox
3471+ return liststore, combobox
3472
3473 def setup_label(name, lbl=""):
3474 """ Sets up a label for the given widget name. """
3475@@ -381,10 +385,10 @@ class PreferencesDialog(object):
3476 self.dialog = self.wTree.get_object("pref_dialog")
3477 self.dialog.set_title(_('Preferences'))
3478 self.dialog.set_icon_name('wicd-gtk')
3479- width = int(gtk.gdk.screen_width() / 2.4)
3480+ width = int(gdk.Screen.get_default().get_width() / 2.4)
3481 if width > 450:
3482 width = 450
3483- self.dialog.resize(width, int(gtk.gdk.screen_height() / 2))
3484+ self.dialog.resize(width, int(gdk.Screen.get_default().get_height() / 2))
3485
3486 self.wiredcheckbox = setup_label(
3487 "pref_always_check",
3488@@ -401,6 +405,7 @@ class PreferencesDialog(object):
3489 )
3490 self.debugmodecheckbox = setup_label("pref_debug_check",
3491 _('Enable debug mode'))
3492+# iw {interfac} scan does not allow Linq Quality to be obtained, so it was fixed to dBm.
3493 self.displaytypecheckbox = setup_label(
3494 "pref_dbm_check",
3495 _('Use dBm to measure signal strength')
3496@@ -456,13 +461,13 @@ class PreferencesDialog(object):
3497 self.ktsussradio = setup_label("ktsuss_radio")
3498
3499 # Replacement for the combo box hack
3500- self.wpadrivercombo = build_combobox("pref_wpa_combobox")
3501+ self.wpadriverlist, self.wpadrivercombo = build_combobox("pref_wpa_combobox")
3502 self.wpadrivers = wireless.GetWpaSupplicantDrivers()
3503 self.wpadrivers.append("ralink_legacy")
3504 self.wpadrivers.append('none')
3505
3506 for x in self.wpadrivers:
3507- self.wpadrivercombo.append_text(x)
3508+ self.wpadriverlist.append([x])
3509
3510 self.entryWirelessInterface = self.wTree.get_object("pref_wifi_entry")
3511 self.entryWiredInterface = self.wTree.get_object("pref_wired_entry")
3512@@ -476,7 +481,7 @@ class PreferencesDialog(object):
3513 self.dns2Entry = self.wTree.get_object("pref_dns2_entry")
3514 self.dns3Entry = self.wTree.get_object("pref_dns3_entry")
3515
3516- self.backendcombo = build_combobox("pref_backend_combobox")
3517+ self.backendlist, self.backendcombo = build_combobox("pref_backend_combobox")
3518 self.backendcombo.connect("changed", self.be_combo_changed)
3519 # Load backend combobox
3520 self.backends = daemon.GetBackendList()
3521@@ -486,7 +491,7 @@ class PreferencesDialog(object):
3522 if x:
3523 if x == 'ioctl':
3524 x = 'ioctl NOT SUPPORTED'
3525- self.backendcombo.append_text(x)
3526+ self.backendlist.append([x])
3527
3528 def be_combo_changed(self, combo):
3529 """ Update the description label for the given backend. """
3530diff --git a/src/wicd/frontends/gtk/resources/wicd.ui b/src/wicd/frontends/gtk/resources/wicd.ui
3531index befd477..5c0d3a7 100644
3532--- a/src/wicd/frontends/gtk/resources/wicd.ui
3533+++ b/src/wicd/frontends/gtk/resources/wicd.ui
3534@@ -3,12 +3,12 @@
3535 <requires lib="gtk+" version="2.16"/>
3536 <!-- interface-naming-policy toplevel-contextual -->
3537 <object class="GtkWindow" id="window1">
3538- <property name="width_request">450</property>
3539+<!--- <property name="width_request">450</property> -->
3540 <property name="height_request">400</property>
3541 <property name="visible">True</property>
3542 <property name="title" translatable="yes">Wicd Network Manager</property>
3543 <property name="window_position">center</property>
3544- <property name="default_width">550</property>
3545+ <property name="default_width">380</property>
3546 <property name="icon_name">wicd-gtk</property>
3547 <property name="gravity">center</property>
3548 <accel-groups>
3549@@ -219,7 +219,7 @@
3550 <object class="GtkHBox" id="connecting_hbox">
3551 <property name="border_width">4</property>
3552 <child>
3553- <object class="GtkSpinner" id="progressbar">
3554+ <object class="GtkProgressBar" id="progressbar">
3555 <property name="tooltip_text" translatable="yes">Connecting...</property>
3556 </object>
3557 <packing>
3558@@ -1499,6 +1499,7 @@ to read its description.</property>
3559 <property name="receives_default">False</property>
3560 <property name="draw_indicator">True</property>
3561 </object>
3562+
3563 </child>
3564 </object>
3565 <packing>
3566diff --git a/src/wicd/logfile.py b/src/wicd/logfile.py
3567index 1838d36..c639753 100644
3568--- a/src/wicd/logfile.py
3569+++ b/src/wicd/logfile.py
3570@@ -3,6 +3,7 @@
3571 #
3572 # Copyright (C) 1999-2006 Keith Dart <keith@kdart.com>
3573 # Copyright (C) 2008-2009 Dan O'Reilly <oreilldf@gmail.com>
3574+# Copyright (C) 2024 Takahiro Yoshizawa <kuro@takahiro.org>
3575 #
3576 # This library is free software; you can redistribute it and/or
3577 # modify it under the terms of the GNU Lesser General Public
3578@@ -59,10 +60,17 @@ class LogFile(io.FileIO):
3579 self.eol = True
3580 data = data[:-1]
3581
3582- super(LogFile, self).write(data.replace(
3583- b'\n', b'\n' + self.get_time().encode("utf-8") + b' :: '))
3584+ if isinstance(data, bytes):
3585+ timestamp = self.get_time().encode('utf-8')
3586+ data = data.replace(b'\n', b'\n' + timestamp + b' :: ')
3587+ else:
3588+ timestamp = self.get_time()
3589+ data = data.replace('\n', '\n' + timestamp + ' :: ')
3590+ data = data.encode('utf-8')
3591+ super(LogFile, self).write(data)
3592+
3593 if self.eol:
3594- super(LogFile, self).write('\n')
3595+ super(LogFile, self).write(b'\n')
3596
3597 self.flush()
3598 if self.written > self.maxsize:
3599diff --git a/src/wicd/misc.py b/src/wicd/misc.py
3600index 6f5a5f8..014ce30 100644
3601--- a/src/wicd/misc.py
3602+++ b/src/wicd/misc.py
3603@@ -8,6 +8,7 @@ throughout wicd.
3604 #
3605 # Copyright (C) 2007 - 2009 Adam Blackburn
3606 # Copyright (C) 2007 - 2009 Dan O'Reilly
3607+# Copyright (C) 2024 Takahiro Yoshizawa
3608 #
3609 # This program is free software; you can redistribute it and/or modify
3610 # it under the terms of the GNU General Public License Version 2 as
3611@@ -27,13 +28,15 @@ import locale
3612 import sys
3613 import re
3614 import string
3615+import importlib.util
3616 from gi.repository import GLib as gobject
3617 from threading import Thread
3618-from subprocess import Popen, STDOUT, PIPE, call
3619+from subprocess import Popen, STDOUT, PIPE, call, DEVNULL
3620 from subprocess import getoutput
3621 from itertools import repeat, chain, zip_longest
3622-from pipes import quote
3623+from shlex import quote
3624 import socket
3625+import glob
3626
3627 from wicd.translations import _
3628
3629@@ -140,7 +143,10 @@ def Run(cmd, include_stderr=False, return_pipe=False,
3630 else:
3631 err = None
3632 fds = False
3633- std_in = None
3634+
3635+ # Measures to address the issue of dhcpcd terminating with an error when using dhcpcd on Ubuntu 24.02.
3636+ # Previously, it was set to None.
3637+ std_in = DEVNULL
3638
3639 # We need to make sure that the results of the command we run
3640 # are in English, so we set up a temporary environment.
3641@@ -291,7 +297,9 @@ def ParseEncryption(network):
3642 and creating a config file for it
3643
3644 """
3645- enctemplate = open(wpath.encryption + network["enctype"])
3646+ package_dir = os.path.dirname(os.path.abspath(__file__))
3647+ encryption_path = os.path.join(package_dir, 'encryption', 'templates', '')
3648+ enctemplate = open(encryption_path + network["enctype"])
3649 template = enctemplate.readlines()
3650 if network.get('essid'):
3651 config_file = "ap_scan=1\n"
3652@@ -354,7 +362,9 @@ def LoadEncryptionMethods(wired = False):
3653 else:
3654 active_fname = "active"
3655 try:
3656- enctypes = open(wpath.encryption + active_fname,"r").readlines()
3657+ package_dir = os.path.dirname(os.path.abspath(__file__))
3658+ encryption_path = os.path.join(package_dir, 'encryption', 'templates', '')
3659+ enctypes = open(encryption_path + active_fname,"r").readlines()
3660 except IOError as e:
3661 print("Fatal Error: template index file is missing.")
3662 raise IOError(e)
3663@@ -386,7 +396,9 @@ def _parse_enc_template(enctype):
3664 return line.replace(key, "").replace("=", "").strip()
3665
3666 try:
3667- f = open(os.path.join(wpath.encryption, enctype), "r")
3668+ package_dir = os.path.dirname(os.path.abspath(__file__))
3669+ encryption_path = os.path.join(package_dir, 'encryption', 'templates', '')
3670+ f = open(os.path.join(encryption_path, enctype), "r")
3671 except IOError:
3672 print(("Failed to open template file %s" % enctype))
3673 return None
3674@@ -451,12 +463,17 @@ def noneToString(text):
3675
3676 def sanitize_config(s):
3677 """ Sanitize property names to be used in config-files. """
3678- allowed = str.ascii_letters + '_' + string.digits
3679- table = str.maketrans(allowed, ' ' * len(allowed))
3680+# allowed = string.ascii_letters + '_' + string.digits
3681+# table = str.maketrans(allowed, ' ' * len(allowed))
3682+#
3683+# # s is a dbus.String -- since we don't allow unicode property keys,
3684+# # make it simple.
3685+# return s.encode('ascii', 'replace').translate(None, table)
3686
3687- # s is a dbus.String -- since we don't allow unicode property keys,
3688- # make it simple.
3689- return s.encode('ascii', 'replace').translate(None, table)
3690+ # There was an error with return s.encode('ascii', 'replace').translate(None, table), so it was fixed.
3691+ result = ''.join(c if c in string.ascii_letters + string.digits + '_' else ' ' for c in s)
3692+
3693+ return result
3694
3695 def sanitize_escaped(s):
3696 """ Sanitize double-escaped unicode strings. """
3697@@ -538,9 +555,17 @@ def get_sudo_cmd(msg, prog_num=0):
3698 return None
3699 if re.search("(pkexec|su-to-root)$", sudo_prog):
3700 msg_flag = ""
3701+ elif "kdesu" in sudo_prog:
3702+ msg_flag = ""
3703+ msg = ""
3704 else:
3705 msg_flag = "--caption"
3706- return [sudo_prog, msg_flag, msg]
3707+ if msg_flag == "" and msg == "":
3708+ return [sudo_prog]
3709+ elif msg_flag == "":
3710+ return [sudo_prog, msg]
3711+ else:
3712+ return [sudo_prog, msg_flag, msg]
3713
3714 def choose_sudo_prog(prog_num=0):
3715 """ Try to intelligently decide which graphical sudo program to use. """
3716@@ -548,6 +573,13 @@ def choose_sudo_prog(prog_num=0):
3717 return find_path(_sudo_dict[prog_num])
3718 desktop_env = detect_desktop_environment()
3719 env_path = os.environ['PATH'].split(":")
3720+
3721+ libexec_path = ["/usr/lib/libexec", "/usr/libexec"]
3722+ libexec_path.extend(glob.glob("/usr/lib/*/libexec"))
3723+ for path in libexec_path:
3724+ for dirpath, _, _ in os.walk(path):
3725+ env_path.append(dirpath)
3726+
3727 paths = []
3728
3729 if desktop_env == "kde":
3730@@ -575,6 +607,13 @@ def find_path(cmd):
3731 if not paths:
3732 paths = ["/usr/local/sbin", "/usr/local/bin", "/usr/sbin", "/usr/bin",
3733 "/sbin", "/bin"]
3734+
3735+ libexec_path = ["/usr/lib/libexec", "/usr/libexec"]
3736+ libexec_path.extend(glob.glob("/usr/lib/*/libexec"))
3737+ for path in libexec_path:
3738+ for dirpath, _, _ in os.walk(path):
3739+ paths.append(dirpath)
3740+
3741 for path in paths:
3742 if os.path.exists(os.path.join(path, cmd)):
3743 return os.path.join(path, cmd)
3744@@ -650,3 +689,18 @@ def grouper(n, iterable, fillvalue=None):
3745 """
3746 args = [iter(iterable)] * n
3747 return zip_longest(fillvalue=fillvalue, *args)
3748+
3749+def find_configscript():
3750+ """ Find the path to the config script. """
3751+ original_pythonnousersite = os.environ.get("PYTHONNOUSERSITE")
3752+ os.environ["PYTHONNOUSERSITE"] = "1"
3753+
3754+ try:
3755+ spec = importlib.util.find_spec("wicd.frontends.gtk.configscript")
3756+ finally:
3757+ if original_pythonnousersite is not None:
3758+ os.environ["PYTHONNOUSERSITE"] = original_pythonnousersite
3759+ else:
3760+ del os.environ["PYTHONNOUSERSITE"]
3761+
3762+ return spec
3763diff --git a/src/wicd/networking.py b/src/wicd/networking.py
3764index 9a4a0c5..f1c5a17 100644
3765--- a/src/wicd/networking.py
3766+++ b/src/wicd/networking.py
3767@@ -23,6 +23,7 @@ class WiredConnectThread() -- Connection thread for wired
3768 # Copyright (C) 2007 - 2009 Dan O'Reilly
3769 # Copyright (C) 2007 - 2009 Byron Hillis
3770 # Copyright (C) 2009 Andrew Psaltis
3771+# Copyright (C) 2024 Takahiro Yoshizawa
3772 #
3773 # This program is free software; you can redistribute it and/or modify
3774 # it under the terms of the GNU General Public License Version 2 as
3775@@ -441,7 +442,7 @@ class ConnectThread(threading.Thread):
3776 self.SetStatus('flushing_routing_table')
3777 print('Flushing the routing table...')
3778 iface.FlushRoutes()
3779-
3780+
3781 @abortable
3782 def set_broadcast_address(self, iface):
3783 """ Set the broadcast address for the given interface. """
3784@@ -690,25 +691,25 @@ class Wireless(Controller):
3785 self.connecting_thread.start()
3786 return True
3787
3788- def GetSignalStrength(self, iwconfig=""):
3789+ def GetSignalStrength(self, iwlink=""):
3790 """ Get signal strength of the current network.
3791
3792 Returns:
3793 The current signal strength.
3794
3795 """
3796- return self.wiface.GetSignalStrength(iwconfig)
3797+ return self.wiface.GetSignalStrength(iwlink)
3798
3799- def GetDBMStrength(self, iwconfig=""):
3800+ def GetDBMStrength(self, iwlink=""):
3801 """ Get the dBm signal strength of the current network.
3802
3803 Returns:
3804 The current dBm signal strength.
3805
3806 """
3807- return self.wiface.GetDBMStrength(iwconfig)
3808+ return self.wiface.GetDBMStrength(iwlink)
3809
3810- def GetCurrentNetwork(self, iwconfig=""):
3811+ def GetCurrentNetwork(self, iwlink=""):
3812 """ Get current network name.
3813
3814 Returns:
3815@@ -717,19 +718,20 @@ class Wireless(Controller):
3816 """
3817 if self.connecting_thread and self.connecting_thread.is_connecting:
3818 return self.connecting_thread.network['essid']
3819- return self.wiface.GetCurrentNetwork(iwconfig)
3820+
3821+ return self.wiface.GetCurrentNetwork(iwlink)
3822
3823 def GetBSSID(self):
3824 """ Get the BSSID of the current access point.
3825-
3826+
3827 Returns:
3828 The MAC Adress of the active access point as a string, or
3829 None the BSSID can't be found.
3830-
3831+
3832 """
3833 return self.wiface.GetBSSID()
3834
3835- def GetCurrentBitrate(self, iwconfig):
3836+ def GetCurrentBitrate(self, iwlink):
3837 """ Get the current bitrate of the interface.
3838
3839 Returns:
3840@@ -737,9 +739,9 @@ class Wireless(Controller):
3841 None the bitrate can't be found.
3842
3843 """
3844- return self.wiface.GetCurrentBitrate(iwconfig)
3845+ return self.wiface.GetCurrentBitrate(iwlink)
3846
3847- def GetOperationalMode(self, iwconfig):
3848+ def GetOperationalMode(self, iwlink):
3849 """ Get the current operational mode of the interface.
3850
3851 Returns:
3852@@ -747,17 +749,17 @@ class Wireless(Controller):
3853 None if the operational mode can't be found.
3854
3855 """
3856- return self.wiface.GetOperationalMode(iwconfig)
3857-
3858- def GetAvailableAuthMethods(self, iwlistauth):
3859- """ Get the available authentication methods for the interface.
3860-
3861- Returns:
3862- The available authentication methods of the interface as a string, or
3863- None if the auth methods can't be found.
3864-
3865- """
3866- return self.wiface.GetAvailableAuthMethods(iwlistauth)
3867+ return self.wiface.GetOperationalMode(iwlink)
3868+
3869+# def GetAvailableAuthMethods(self, iwlistauth):
3870+# """ Get the available authentication methods for the interface.
3871+#
3872+# Returns:
3873+# The available authentication methods of the interface as a string, or
3874+# None if the auth methods can't be found.
3875+#
3876+# """
3877+# return self.wiface.GetAvailableAuthMethods(iwlistauth)
3878
3879 def GetAvailableBitrates(self):
3880 """ Get the available bitrates for the interface.
3881@@ -768,10 +770,14 @@ class Wireless(Controller):
3882 """
3883 return self.wiface.GetAvailableBitrates()
3884
3885- def GetIwconfig(self):
3886+# def GetIwconfig(self):
3887+# """ Get the out of iwconfig. """
3888+# return self.wiface.GetIwconfig()
3889+
3890+ def GetIwLink(self):
3891 """ Get the out of iwconfig. """
3892- return self.wiface.GetIwconfig()
3893-
3894+ return self.wiface.GetIwLink()
3895+
3896 def GetWpaSupplicantDrivers(self):
3897 """ Returns all valid wpa_supplicant drivers on the system. """
3898 return BACKEND.GetWpaSupplicantDrivers()
3899@@ -800,16 +806,19 @@ class Wireless(Controller):
3900 wiface.StopWPA()
3901 print('Putting wireless interface down')
3902 wiface.Down()
3903- print('Setting mode, channel, and essid')
3904- wiface.SetMode('ad-hoc')
3905- wiface.SetChannel(channel)
3906- wiface.SetEssid(essid)
3907+ print('Setting mode')
3908+# wiface.SetMode('ad-hoc')
3909+ wiface.SetMode('ibss') # ad-hoc
3910+# wiface.SetChannel(channel)
3911+# wiface.SetEssid(essid)
3912 # Right now it just assumes you're using WEP
3913 if enc_used:
3914 print('Setting encryption key')
3915- wiface.SetKey(key)
3916+ wiface.SetKey(key, essid)
3917 print('Putting interface up')
3918 wiface.Up()
3919+ print('Setting essid and channel')
3920+ wiface.SetAdHocEssid(essid, channel)
3921 print('Setting IP address')
3922 wiface.SetAddress(ip, '255.255.255.0')
3923
3924@@ -874,11 +883,11 @@ class Wireless(Controller):
3925
3926 """
3927 if BACKEND.NeedsExternalCalls():
3928- iwconfig = self.GetIwconfig()
3929+ iwlink = self.GetIwLink()
3930 else:
3931- iwconfig = None
3932- bssid = self.wiface.GetBSSID(iwconfig)
3933- essid = self.wiface.GetCurrentNetwork(iwconfig)
3934+ iwlink = None
3935+ bssid = self.wiface.GetBSSID(iwlink)
3936+ essid = self.wiface.GetCurrentNetwork(iwlink)
3937
3938 Controller.Disconnect(self, 'wireless', essid, bssid)
3939 self.StopWPA()
3940@@ -887,6 +896,9 @@ class Wireless(Controller):
3941 """ Sets the wpa_supplicant driver associated with the interface. """
3942 self.wiface.SetWpaDriver(driver)
3943
3944+ def GetMode(self):
3945+ return self.wiface.GetMode()
3946+
3947
3948 class WirelessConnectThread(ConnectThread):
3949 """ A thread class to perform the connection to a wireless network.
3950@@ -942,7 +954,7 @@ class WirelessConnectThread(ConnectThread):
3951 """
3952 wiface = self.iface
3953 self.is_connecting = True
3954-
3955+
3956 # Run pre-connection script.
3957 self.run_global_scripts_if_needed(wpath.preconnectscripts,
3958 extra_parameters=('wireless',
3959@@ -960,8 +972,9 @@ class WirelessConnectThread(ConnectThread):
3960 self.stop_wpa(wiface)
3961 self.flush_routes(wiface)
3962 self.flush_dns_addresses(wiface)
3963- wiface.SetMode(self.network['mode'])
3964- wiface.SetBitrate(self.bitrate, self.allow_lower_bitrates)
3965+ wiface.SetMode(self.network['operating_mode'])
3966+ wiface.SetBitrate(self.bitrate, essid=self.network['essid'],
3967+ freq=self.network['freq'])
3968
3969 # Put interface up.
3970 self.SetStatus('configuring_interface')
3971@@ -979,7 +992,7 @@ class WirelessConnectThread(ConnectThread):
3972 if self.wpa_driver == 'ralink legacy':
3973 if self.network.get('key'):
3974 wiface.Authenticate(self.network)
3975-
3976+
3977 # Validate Authentication.
3978 if self.network.get('enctype'):
3979 self.SetStatus('validating_authentication')
3980@@ -993,7 +1006,7 @@ class WirelessConnectThread(ConnectThread):
3981 self.set_ip_address(wiface)
3982 self.set_dns_addresses(wiface)
3983 self.verify_association(wiface)
3984-
3985+
3986 # Run post-connection script.
3987 self.run_global_scripts_if_needed(wpath.postconnectscripts,
3988 extra_parameters=('wireless',
3989@@ -1041,14 +1054,14 @@ class WirelessConnectThread(ConnectThread):
3990 self.abort_connection('association_failed')
3991 else:
3992 print('not verifying')
3993-
3994+
3995 @abortable
3996 def generate_psk_and_authenticate(self, wiface):
3997 """ Generates a PSK and authenticates if necessary.
3998-
3999+
4000 Generates a PSK, and starts the authentication process
4001 if encryption is on.
4002-
4003+
4004 """
4005 # Check to see if we need to generate a PSK (only for non-ralink
4006 # cards).
4007@@ -1059,7 +1072,7 @@ class WirelessConnectThread(ConnectThread):
4008 self.SetStatus('generating_psk')
4009 print('Generating psk...')
4010 self.network['psk'] = wiface.GeneratePSK(self.network)
4011-
4012+
4013 if not self.network.get('psk'):
4014 self.network['psk'] = self.network['key']
4015 print(('WARNING: PSK generation failed! Falling back to ' + \
4016@@ -1215,7 +1228,7 @@ class WiredConnectThread(ConnectThread):
4017 # Run pre-connection script.
4018 self.run_global_scripts_if_needed(wpath.preconnectscripts,
4019 extra_parameters=('wired', 'wired',
4020- self.network['profilename'])
4021+ self.network.get('profilename', ()))
4022 )
4023 self.run_script_if_needed(self.before_script, 'pre-connection', 'wired',
4024 'wired')
4025@@ -1243,7 +1256,7 @@ class WiredConnectThread(ConnectThread):
4026 # Run post-connection script.
4027 self.run_global_scripts_if_needed(wpath.postconnectscripts,
4028 extra_parameters=('wired', 'wired',
4029- self.network['profilename'])
4030+ self.network.get('profilename', ()))
4031 )
4032 self.run_script_if_needed(self.after_script, 'post-connection', 'wired',
4033 'wired')
4034diff --git a/src/wicd/wnettools.py b/src/wicd/wnettools.py
4035index 875316a..07f9345 100644
4036--- a/src/wicd/wnettools.py
4037+++ b/src/wicd/wnettools.py
4038@@ -17,6 +17,7 @@ class BaseWirelessInterface() -- Control a wireless network interface.
4039 # Copyright (C) 2007 - 2009 Dan O'Reilly
4040 # Copyright (C) 2007 - 2009 Byron Hillis
4041 # Copyright (C) 2009 Andrew Psaltis
4042+# Copyright (C) 2024 Takahiro Yoshizawa
4043 #
4044 # This program is free software; you can redistribute it and/or modify
4045 # it under the terms of the GNU General Public License Version 2 as
4046@@ -45,36 +46,42 @@ from shlex import quote
4047 from . import wpath
4048 from . import misc
4049 from .misc import find_path
4050+import wicd.config
4051
4052 from wicd import daemon
4053
4054-ifconfig_tool = wicd.tools.ExternalCommand(["/sbin/ifconfig", "/usr/sbin/ifconfig"])
4055+import tempfile
4056+
4057+ifconfig_tool = wicd.tools.ExternalCommand(["/sbin/ifconfig", "/usr/sbin/ifconfig", "/bin/ifconfig", "/usr/bin/ifconfig"])
4058
4059 # Regular expressions.
4060 _re_mode = (re.I | re.M | re.S)
4061 essid_pattern = re.compile('\tSSID: ?(.*?)\n', _re_mode)
4062+essid_pattern2 = re.compile(r"SSID:\s([^\n]+)", _re_mode)
4063 ap_mac_pattern = re.compile('BSS (([0-9a-f]{2}:?){6})+\(', _re_mode)
4064 channel_pattern = re.compile('\tDS Parameter set: channel ?(\d+)\n', _re_mode)
4065 strength_pattern = re.compile('.*Quality:?=? ?(\d+)\s*/?\s*(\d*)', _re_mode)
4066 altstrength_pattern = re.compile('.*Signal level:?=? ?(\d+)\s*/?\s*(\d*)',
4067 _re_mode)
4068 signaldbm_pattern = re.compile('\tsignal: (-[\d.]*) dBm', _re_mode)
4069+signaldbm_pattern2 = re.compile(r"signal:\s(-?\d+\s?)dBm", _re_mode)
4070 bitrates_pattern = re.compile('([\d\.]+)\*?\s+', _re_mode)
4071 mode_pattern = re.compile('\tcapability: (ESS|IBSS) ', _re_mode)
4072 freq_pattern = re.compile('.*freq: (\d+)\n', _re_mode)
4073 wep_pattern = re.compile('\tcapability: \w+ (Privacy) ', _re_mode)
4074 rsn_pattern = re.compile('RSN:\t \* (Version: 1)', _re_mode)
4075 wpa_pattern = re.compile('WPA:\t \* (Version: 1)', _re_mode)
4076+txpower_pattern = re.compile(r"txpower\s+([\d.]+)\s+dBm", _re_mode)
4077+txbitrate_pattern = re.compile(r"tx bitrate:\s*([\d.]+)\s*MBit/s", _re_mode)
4078
4079 #iwconfig-only regular expressions.
4080 ip_up = re.compile(r'flags=[0.9]*<([^>]*)>', re.S)
4081 ip_pattern = re.compile(r'inet [Aa]d?dr[^.]*:([^.]*\.[^.]*\.[^.]*\.[0-9]*)',
4082 re.S)
4083 ip_pattern1 = re.compile(r'inet ([^.]*\.[^.]*\.[^.]*\.[0-9]*)', re.S)
4084-bssid_pattern = re.compile('.*[(Access Point)|(Cell)]: ' + \
4085- '(([0-9A-Z]{2}:){5}[0-9A-Z]{2})', _re_mode)
4086-bitrate_pattern = re.compile('.*Bit Rate[=:](.*?/s)', _re_mode)
4087-opmode_pattern = re.compile('.*Mode:(.*?) ', _re_mode)
4088+bssid_pattern = re.compile(r"Connected to ([0-9A-Fa-f:]{17}) \(on [^)]+\)", _re_mode)
4089+freq_pattern = re.compile(r"freq:\s*(\d+)", _re_mode)
4090+opmode_pattern = re.compile(r"type\s+(\w+)", _re_mode)
4091 authmethods_pattern = re.compile('.*Authentication capabilities ' + \
4092 ':\n(.*?)Current', _re_mode)
4093
4094@@ -90,6 +97,7 @@ blacklist_norm = list(";`$!*|><&\\")
4095 blacklist_strict_t = str.maketrans(dict.fromkeys(blacklist_strict, None))
4096 blacklist_norm_t = str.maketrans(dict.fromkeys(blacklist_norm, None))
4097
4098+
4099 def _sanitize_string(string):
4100 """ Sanitize string. """
4101 if string:
4102@@ -320,11 +328,11 @@ class BaseInterface(object):
4103
4104 def _get_dhcp_command(self, flavor=None, hostname=None, staticdns=False):
4105 """ Returns the correct DHCP client command.
4106-
4107+
4108 Given a type of DHCP request (create or release a lease),
4109 this method will build a command to complete the request
4110 using the correct dhcp client, and cli options.
4111-
4112+
4113 """
4114 def get_client_name(cl):
4115 """ Converts the integer value for a dhcp client to a string. """
4116@@ -501,10 +509,10 @@ class BaseInterface(object):
4117 @neediface(False)
4118 def Up(self):
4119 """ Bring the network interface up.
4120-
4121+
4122 Returns:
4123 True
4124-
4125+
4126 """
4127 ifconfig_tool( self.iface, 'up' )
4128 return True
4129@@ -512,14 +520,14 @@ class BaseInterface(object):
4130 @neediface(False)
4131 def Down(self):
4132 """ Take down the network interface.
4133-
4134+
4135 Returns:
4136 True
4137-
4138+
4139 """
4140 ifconfig_tool( self.iface, 'down' )
4141 return True
4142-
4143+
4144 @timedcache(2)
4145 @neediface("")
4146 def GetIfconfig(self):
4147@@ -547,13 +555,13 @@ class BaseInterface(object):
4148 args = [self.iface]
4149
4150 if ip:
4151- cmd.append(ip)
4152+ args.append(str(ip))
4153 if netmask:
4154- cmd.append(netmask)
4155+ args.extend(['netmask', netmask])
4156 if broadcast:
4157- cmd.extend(['broadcast', broadcast])
4158+ args.extend(['broadcast', broadcast])
4159
4160- ifconfig_tool(args)
4161+ ifconfig_tool([], *args)
4162
4163 def _parse_dhclient(self, pipe):
4164 """ Parse the output of dhclient.
4165@@ -573,11 +581,11 @@ class BaseInterface(object):
4166
4167 while not dhclient_complete:
4168 line = pipe.readline()
4169- if line == '': # Empty string means dhclient is done.
4170+ if line == b'': # Empty string means dhclient is done.
4171 dhclient_complete = True
4172 else:
4173- print(misc.to_unicode(line.strip('\n')))
4174- if line.startswith('bound'):
4175+ print(misc.to_unicode(line.strip(b'\n')))
4176+ if line.startswith(b'bound'):
4177 dhclient_success = True
4178 dhclient_complete = True
4179
4180@@ -598,7 +606,7 @@ class BaseInterface(object):
4181
4182 while not pump_complete:
4183 line = pipe.readline()
4184- if line == '':
4185+ if line == b'':
4186 pump_complete = True
4187 elif line.strip().lower().startswith('Operation failed.'):
4188 pump_success = False
4189@@ -619,13 +627,12 @@ class BaseInterface(object):
4190 """
4191 dhcpcd_complete = False
4192 dhcpcd_success = True
4193-
4194 while not dhcpcd_complete:
4195 line = pipe.readline()
4196- if "Error" in line or "timed out" in line:
4197+ if b"Error" in line or b"timed out" in line:
4198 dhcpcd_success = False
4199 dhcpcd_complete = True
4200- elif line == '':
4201+ elif line == b'':
4202 dhcpcd_complete = True
4203 print(misc.to_unicode(line))
4204
4205@@ -646,10 +653,10 @@ class BaseInterface(object):
4206
4207 while not udhcpc_complete:
4208 line = pipe.readline()
4209- if line.endswith("failing."):
4210+ if line.endswith(b"failing."):
4211 udhcpc_success = False
4212 udhcpc_complete = True
4213- elif line == '':
4214+ elif line == b'':
4215 udhcpc_complete = True
4216 print(line)
4217
4218@@ -688,6 +695,7 @@ class BaseInterface(object):
4219 if self.verbose:
4220 print(cmd)
4221 self.dhcp_object = misc.Run(cmd, include_stderr=True, return_obj=True)
4222+
4223 pipe = self.dhcp_object.stdout
4224 client_dict = { misc.DHCLIENT : self._parse_dhclient,
4225 misc.DHCPCD : self._parse_dhcpcd,
4226@@ -702,6 +710,10 @@ class BaseInterface(object):
4227 print("ERROR: no dhcp client found")
4228 ret = None
4229 self.dhcp_object.wait()
4230+ if os.path.exists(wicd.config.resolvconf_path) and not os.path.islink(wicd.config.resolvconf_path):
4231+ # After running dhclient, addressing the issue
4232+ # where the permissions of /etc/resolv.conf are set to 0600.
4233+ os.chmod(wicd.config.resolvconf_path, 0o644)
4234 return ret
4235
4236 @neediface(False)
4237@@ -733,13 +745,36 @@ class BaseInterface(object):
4238 Only useful for resolvconf-based setups.
4239
4240 """
4241- daemon.resolver.get_manager().flush_dns(self.iface)
4242+# daemon.resolver.get_manager().flush_dns(self.iface)
4243+ pass
4244
4245 @neediface(False)
4246 def SetDNS(self, dns1=None, dns2=None, dns3=None,
4247 dns_dom=None, search_dom=None):
4248- daemon.resolver.get_manager().set_dns(self.iface, dns1 = dns1, dns2 = dns2, dns3 = dns3, dns_dom = dns_dom, search_dom = search_dom)
4249-
4250+# daemon.resolver.get_manager().set_dns(self.iface, dns1 = dns1, dns2 = dns2, dns3 = dns3, dns_dom = dns_dom, search_dom = search_dom)
4251+ resolv_params = ""
4252+ if dns_dom:
4253+ resolv_params += 'domain %s\n' % dns_dom
4254+ if search_dom:
4255+ resolv_params += 'search %s\n' % search_dom
4256+
4257+ valid_dns_list = []
4258+ for dns in (dns1, dns2, dns3):
4259+ if dns:
4260+ if misc.IsValidIP(dns):
4261+ if self.verbose:
4262+ print('Setting DNS : ' + dns)
4263+ valid_dns_list.append("nameserver %s\n" % dns)
4264+ else:
4265+ print('DNS IP %s is not a valid IP address, skipping' % dns)
4266+
4267+ if valid_dns_list:
4268+ resolv_params += ''.join(valid_dns_list)
4269+
4270+ resolv = open("/etc/resolv.conf", "w")
4271+ resolv.write(resolv_params + "\n")
4272+ resolv.close()
4273+
4274 @neediface(False)
4275 def FlushRoutes(self):
4276 """ Flush network routes for this device. """
4277@@ -828,7 +863,7 @@ class BaseInterface(object):
4278 % flags_file)
4279 return self._slow_is_up(ifconfig)
4280 return bool(int(flags, 16) & 1)
4281-
4282+
4283 @neediface(False)
4284 def StopWPA(self):
4285 """ Terminates wpa using wpa_cli"""
4286@@ -836,8 +871,8 @@ class BaseInterface(object):
4287 if self.verbose:
4288 print(cmd)
4289 misc.Run(cmd)
4290-
4291-
4292+
4293+
4294 def _slow_is_up(self, ifconfig=None):
4295 """ Determine if an interface is up using ifconfig. """
4296 if not ifconfig:
4297@@ -1003,7 +1038,16 @@ class BaseWirelessInterface(BaseInterface):
4298 essid -- essid to set the interface to
4299
4300 """
4301- cmd = ['iwconfig', self.iface, 'essid', '--', str(essid)]
4302+# cmd = ['iwconfig', self.iface, 'essid', '--', str(essid)]
4303+ cmd = ['iw', self.iface, 'connect', str(essid)]
4304+ if self.verbose:
4305+ print(str(cmd))
4306+ misc.Run(cmd)
4307+
4308+ @neediface(False)
4309+ def SetAdHocEssid(self, essid, channel):
4310+ freq = self._ChannelToFreq(int(channel))
4311+ cmd = ['iw', self.iface, 'ibss', 'join', str(essid), str(freq), 'fixed-freq']
4312 if self.verbose:
4313 print(str(cmd))
4314 misc.Run(cmd)
4315@@ -1011,26 +1055,56 @@ class BaseWirelessInterface(BaseInterface):
4316 @neediface(False)
4317 def GetKillSwitchStatus(self):
4318 """ Determines if the wireless killswitch is enabled.
4319-
4320+
4321 Returns:
4322 True if the killswitch is enabled, False otherwise.
4323-
4324+
4325 """
4326- output = self.GetIwconfig()
4327+ output = self.GetIwDevInfo()
4328+
4329+ killswitch_pattern = misc.RunRegex(txpower_pattern, output)
4330
4331- killswitch_pattern = re.compile('.*radio off', re.I | re.M | re.S)
4332- if killswitch_pattern.search(output):
4333- radiostatus = True
4334+ if killswitch_pattern:
4335+ if float(killswitch_pattern) != 0:
4336+ radiostatus = True
4337+ else:
4338+ radiostatus = False
4339 else:
4340 radiostatus = False
4341
4342 return radiostatus
4343-
4344+
4345+# @timedcache(2)
4346+# @neediface(False)
4347+# def GetIwconfig(self):
4348+# """ Returns the output of iwconfig for this interface. """
4349+# cmd = "iwconfig " + self.iface
4350+#
4351+# if self.verbose:
4352+# print(cmd)
4353+# return misc.Run(cmd)
4354+
4355+ @timedcache(2)
4356+ @neediface(False)
4357+ def GetIwLink(self):
4358+ cmd = "iw %s link" % self.iface
4359+ if self.verbose:
4360+ print(cmd)
4361+ return misc.Run(cmd)
4362+
4363+ @timedcache(2)
4364+ @neediface(False)
4365+ def GetIwInfo(self):
4366+ cmd = "iw %s info" % self.iface
4367+ if self.verbose:
4368+ print(cmd)
4369+ return misc.Run(cmd)
4370+
4371 @timedcache(2)
4372 @neediface(False)
4373- def GetIwconfig(self):
4374- """ Returns the output of iwconfig for this interface. """
4375- cmd = "iwconfig " + self.iface
4376+ def GetIwDevInfo(self):
4377+ cmd = "iw dev " + self.iface + " info"
4378+
4379 if self.verbose:
4380 print(cmd)
4381 return misc.Run(cmd)
4382@@ -1059,61 +1133,126 @@ class BaseWirelessInterface(BaseInterface):
4383 ret = freq_dict[freq]
4384 except KeyError:
4385 print("Couldn't determine channel number for frequency:", str(freq))
4386-
4387+
4388 return ret
4389
4390- def _GetRalinkInfo(self):
4391- """ Get a network info list used for ralink drivers
4392-
4393- Calls iwpriv <wireless interface> get_site_survey, which
4394- on some ralink cards will return encryption and signal
4395- strength info for wireless networks in the area.
4396-
4397+ def _ChannelToFreq(self, channel: int) -> int:
4398+ """
4399+ A function to convert Wi-Fi channel numbers to frequency (MHz).
4400+
4401+ :param channel: Wi-Fi channel number (1–165)
4402+ :return: Frequency (MHz) (raises an exception if the channel is unsupported)
4403 """
4404- iwpriv = misc.Run('iwpriv ' + self.iface + ' get_site_survey')
4405+ if 1 <= channel <= 13: # 2.4GHz band
4406+ return 2407 + channel * 5
4407+ elif channel == 14: # Japan-specific channel
4408+ return 2484
4409+ elif 36 <= channel <= 165 and channel % 4 == 0: # 5GHz band
4410+ return 5000 + (channel - 36) * 5 + 180
4411+ else:
4412+ raise ValueError(f"Channel {channel} is not supported")
4413+
4414+# def _GetRalinkInfo(self):
4415+# """ Get a network info list used for ralink drivers
4416+#
4417+# Calls iwpriv <wireless interface> get_site_survey, which
4418+# on some ralink cards will return encryption and signal
4419+# strength info for wireless networks in the area.
4420+#
4421+# """
4422+# iwpriv = misc.Run('iwpriv ' + self.iface + ' get_site_survey')
4423+# if self.verbose:
4424+# print(iwpriv)
4425+# lines = iwpriv.splitlines()[2:]
4426+# aps = {}
4427+# patt = re.compile("((?:[0-9A-Z]{2}:){5}[0-9A-Z]{2})")
4428+# for x in lines:
4429+# ap = {}
4430+# info = x.split(" ")
4431+# info = [x.strip() for x in info if x.strip()]
4432+# if len(info) < 5:
4433+# continue
4434+# if re.match(patt, info[2].upper()):
4435+# bssid = info[2].upper()
4436+# offset = -1
4437+# elif re.match(patt, info[3].upper()):
4438+# bssid = info[3].upper()
4439+# offset = 0
4440+# else: # Invalid
4441+# print('Invalid iwpriv line. Skipping it.')
4442+# continue
4443+# ap['nettype'] = info[-1]
4444+# ap['strength'] = info[1]
4445+# if info[4 + offset] == 'WEP':
4446+# ap['encryption_method'] = 'WEP'
4447+# ap['enctype'] = 'WEP'
4448+# ap['keyname'] = 'Key1'
4449+# ap['authmode'] = info[5 + offset]
4450+# elif info[5 + offset] in ['WPA-PSK', 'WPA']:
4451+# ap['encryption_method'] = 'WPA'
4452+# ap['authmode'] = "WPAPSK"
4453+# ap['keyname'] = "WPAPSK"
4454+# ap['enctype'] = info[4 + offset]
4455+# elif info[5 + offset] == 'WPA2-PSK':
4456+# ap['encryption_method'] = 'WPA2'
4457+# ap['authmode'] = "WPA2PSK"
4458+# ap['keyname'] = "WPA2PSK"
4459+# ap['enctype'] = info[4 + offset]
4460+# elif info[4 + offset] == "NONE":
4461+# ap['encryption_method'] = None
4462+# else:
4463+# print("Unknown AuthMode, can't assign encryption_method!")
4464+# ap['encryption_method'] = 'Unknown'
4465+# aps[bssid] = ap
4466+# if self.verbose:
4467+# print(str(aps))
4468+# return aps
4469+ def _GetRalinkInfo(self):
4470+ iw_scan = misc.Run("iw dev " + self.iface + " scan")
4471 if self.verbose:
4472- print(iwpriv)
4473- lines = iwpriv.splitlines()[2:]
4474+ print(iw_scan)
4475+
4476 aps = {}
4477- patt = re.compile("((?:[0-9A-Z]{2}:){5}[0-9A-Z]{2})")
4478- for x in lines:
4479- ap = {}
4480- info = x.split(" ")
4481- info = [x.strip() for x in info if x.strip()]
4482- if len(info) < 5:
4483+ bss_pattern = re.compile(r"BSS\s+([0-9A-Fa-f:]{17})")
4484+ ssid_pattern = re.compile(r"SSID:\s(.+)")
4485+ signal_pattern = re.compile(r"signal:\s(-?\d+)\s+dBm")
4486+ encryption_pattern = re.compile(r"(RSN|WEP|NONE)")
4487+
4488+ current_bssid = None
4489+ for line in iw_scan.splitlines():
4490+ line = line.strip()
4491+
4492+ bssid_match = bss_pattern.match(line)
4493+ if bssid_match:
4494+ current_bssid = bssid_match.group(1).upper()
4495+ aps[current_bssid] = {"nettype": None, "strength": None, "encryption_method": None}
4496 continue
4497- if re.match(patt, info[2].upper()):
4498- bssid = info[2].upper()
4499- offset = -1
4500- elif re.match(patt, info[3].upper()):
4501- bssid = info[3].upper()
4502- offset = 0
4503- else: # Invalid
4504- print('Invalid iwpriv line. Skipping it.')
4505+
4506+ if current_bssid is None:
4507 continue
4508- ap['nettype'] = info[-1]
4509- ap['strength'] = info[1]
4510- if info[4 + offset] == 'WEP':
4511- ap['encryption_method'] = 'WEP'
4512- ap['enctype'] = 'WEP'
4513- ap['keyname'] = 'Key1'
4514- ap['authmode'] = info[5 + offset]
4515- elif info[5 + offset] in ['WPA-PSK', 'WPA']:
4516- ap['encryption_method'] = 'WPA'
4517- ap['authmode'] = "WPAPSK"
4518- ap['keyname'] = "WPAPSK"
4519- ap['enctype'] = info[4 + offset]
4520- elif info[5 + offset] == 'WPA2-PSK':
4521- ap['encryption_method'] = 'WPA2'
4522- ap['authmode'] = "WPA2PSK"
4523- ap['keyname'] = "WPA2PSK"
4524- ap['enctype'] = info[4 + offset]
4525- elif info[4 + offset] == "NONE":
4526- ap['encryption_method'] = None
4527- else:
4528- print("Unknown AuthMode, can't assign encryption_method!")
4529- ap['encryption_method'] = 'Unknown'
4530- aps[bssid] = ap
4531+
4532+ ssid_match = ssid_pattern.match(line)
4533+ if ssid_match:
4534+ aps[current_bssid]["nettype"] = ssid_match.group(1)
4535+ continue
4536+
4537+ signal_match = signal_pattern.match(line)
4538+ if signal_match:
4539+ aps[current_bssid]["strength"] = signal_match.group(1)
4540+ continue
4541+
4542+ encryption_match = encryption_pattern.search(line)
4543+ if encryption_match:
4544+ encryption = encryption_match.group(1)
4545+ if encryption == "RSN":
4546+ aps[current_bssid]["encryption_method"] = "WPA2"
4547+ elif encryption == "WEP":
4548+ aps[current_bssid]["encryption_method"] = "WEP"
4549+ elif encryption == "NONE":
4550+ aps[current_bssid]["encryption_method"] = None
4551+ else:
4552+ aps[current_bssid]["encryption_method"] = "Unknown"
4553+
4554 if self.verbose:
4555 print(str(aps))
4556 return aps
4557@@ -1149,15 +1288,34 @@ class BaseWirelessInterface(BaseInterface):
4558
4559 """
4560 mode = _sanitize_string_strict(mode)
4561+# if not mode:
4562+# mode = 'master'
4563+# if mode.lower() == 'master':
4564+# mode = 'managed'
4565+# cmd = 'iwconfig %s mode %s' % (self.iface, mode)
4566+
4567 if not mode:
4568- mode = 'master'
4569- if mode.lower() == 'master':
4570+ mode = '__ap'
4571+ if mode.lower() == '__ap':
4572 mode = 'managed'
4573- cmd = 'iwconfig %s mode %s' % (self.iface, mode)
4574+ cmd = 'iw %s set type %s' % (self.iface, mode)
4575 if self.verbose:
4576 print(cmd)
4577 misc.Run(cmd)
4578
4579+ @neediface("")
4580+ def GetMode(self, iwinfo=None):
4581+ if not iwinfo:
4582+ output = self.GetIwInfo()
4583+ else:
4584+ output = iwinfo
4585+
4586+ opmode = misc.RunRegex(opmode_pattern, output)
4587+ if opmode:
4588+ opmode = opmode.strip()
4589+ return opmode
4590+
4591+
4592 @neediface(False)
4593 def SetChannel(self, channel):
4594 """ Set the channel of the wireless interface.
4595@@ -1170,26 +1328,28 @@ class BaseWirelessInterface(BaseInterface):
4596 print('WARNING: Invalid channel found. Aborting!')
4597 return False
4598
4599- cmd = 'iwconfig %s channel %s' % (self.iface, str(channel))
4600+# cmd = 'iwconfig %s channel %s' % (self.iface, str(channel))
4601+ cmd = 'iw %s set channel %s' % (self.iface, str(channel))
4602 if self.verbose:
4603 print(cmd)
4604 misc.Run(cmd)
4605
4606 @neediface(False)
4607- def SetKey(self, key):
4608+ def SetKey(self, key, essid):
4609 """ Set the encryption key of the wireless interface.
4610
4611 Keyword arguments:
4612 key -- encryption key to set
4613
4614 """
4615- cmd = 'iwconfig %s key %s' % (self.iface, key)
4616+# cmd = 'iwconfig %s key %s' % (self.iface, key)
4617+ cmd = 'iw %s connect %s key 0:%s' % (self.iface, essid, key)
4618 if self.verbose:
4619 print(cmd)
4620 misc.Run(cmd)
4621
4622 @neediface(False)
4623- def SetBitrate(self, bitrate, allow_lower=False):
4624+ def SetBitrate(self, bitrate, essid, freq):
4625 ''' Set the desired bitrate for the interface.
4626
4627 Keyword arguments:
4628@@ -1199,13 +1359,26 @@ class BaseWirelessInterface(BaseInterface):
4629 # FIXME: what if, in future, bitrates won't be "M(egabit per second)"
4630 #+anymore?
4631 if bitrate == 'auto':
4632- cmd = 'iwconfig %s rate auto'
4633+# cmd = 'iwconfig %s rate auto' % self.iface
4634+ pass
4635 else:
4636- if allow_lower:
4637- cmd = 'iwconfig %s rate %sM auto' % (self.iface, bitrate)
4638- else:
4639- cmd = 'iwconfig %s rate %sM fixed' % (self.iface, bitrate)
4640- misc.Run(cmd)
4641+# if allow_lower:
4642+# cmd = 'iwconfig %s rate %sM auto' % (self.iface, bitrate)
4643+# else:
4644+# cmd = 'iwconfig %s rate %sM fixed' % (self.iface, bitrate)
4645+ bitrate_mode = ""
4646+ if freq:
4647+ freq = int(freq)
4648+ if freq >= 2412 and freq <= 2472:
4649+ bitrate_mode = "legacy-2.4"
4650+ elif freq >= 5180 and freq <= 5825:
4651+ bitrate_mode = "legacy-5"
4652+
4653+ if bitrate_mode != "":
4654+ cmd = "iw %s set bitrates %s %s" % (self.iface, bitrate_mode, bitrate)
4655+ if self.verbose:
4656+ print(cmd)
4657+ misc.Run(cmd)
4658
4659 @neediface(False)
4660 def Associate(self, essid, channel=None, bssid=None):
4661@@ -1218,24 +1391,27 @@ class BaseWirelessInterface(BaseInterface):
4662
4663 """
4664 self.SetEssid(essid)
4665- base = "iwconfig %s" % self.iface
4666+# base = "iwconfig %s" % self.iface
4667+ base = "iw %s" % self.iface
4668 if channel and str(channel).isdigit():
4669- cmd = "%s channel %s" % (base, str(channel))
4670+# cmd = "%s channel %s" % (base, str(channel))
4671+ cmd = "%s set channel %s" % (base, str(channel))
4672 if self.verbose:
4673 print(cmd)
4674 misc.Run(cmd)
4675 if bssid:
4676- cmd = "%s ap %s" % (base, bssid)
4677+# cmd = "%s ap %s" % (base, bssid)
4678+ cmd = "%s connect %s %s" % (base, essid, bssid)
4679 if self.verbose:
4680 print(cmd)
4681 misc.Run(cmd)
4682-
4683+
4684 def GeneratePSK(self, network):
4685- """ Generate a PSK using wpa_passphrase.
4686+ """ Generate a PSK using wpa_passphrase.
4687
4688 Keyword arguments:
4689 network -- dictionary containing network info
4690-
4691+
4692 """
4693 wpa_pass_path = misc.find_path('wpa_passphrase')
4694 if not wpa_pass_path:
4695@@ -1271,46 +1447,83 @@ class BaseWirelessInterface(BaseInterface):
4696 print(cmd)
4697 misc.Run(cmd)
4698
4699- def _AuthenticateRalinkLegacy(self, network):
4700- """ Authenticate with the specified wireless network.
4701-
4702- This function handles Ralink legacy cards that cannot use
4703- wpa_supplicant.
4704-
4705- Keyword arguments:
4706- network -- dictionary containing network info
4707+# def _AuthenticateRalinkLegacy(self, network):
4708+# """ Authenticate with the specified wireless network.
4709+#
4710+# This function handles Ralink legacy cards that cannot use
4711+# wpa_supplicant.
4712+#
4713+# Keyword arguments:
4714+# network -- dictionary containing network info
4715+#
4716+# """
4717+# if network.get('key') != None:
4718+# try:
4719+# info = self._GetRalinkInfo()[network.get('bssid')]
4720+# except KeyError:
4721+# print("Could not find current network in iwpriv " + \
4722+# "get_site_survey results. Cannot authenticate.")
4723+# return
4724+#
4725+# if info['enctype'] == "WEP" and info['authtype'] == 'OPEN':
4726+# print('Setting up WEP')
4727+# cmd = ''.join(['iwconfig ', self.iface, ' key ',
4728+# network.get('key')])
4729+# if self.verbose:
4730+# print(cmd)
4731+# misc.Run(cmd)
4732+# else:
4733+# cmd_list = []
4734+# cmd_list.append('NetworkType=' + info['nettype'])
4735+# cmd_list.append('AuthMode=' + info['authmode'])
4736+# cmd_list.append('EncrypType=' + info['enctype'])
4737+# cmd_list.append('SSID="%s"' % network['essid'])
4738+# cmd_list.append('%(keyname)s="%(key)s"' % network)
4739+# if info['nettype'] == 'SHARED' and info['enctype'] == 'WEP':
4740+# cmd_list.append('DefaultKeyID=1')
4741+#
4742+# for cmd in cmd_list:
4743+# cmd = ['iwpriv', self.iface, 'set', cmd]
4744+# if self.verbose:
4745+# print(' '.join(cmd))
4746+# misc.Run(cmd)
4747
4748- """
4749- if network.get('key') != None:
4750+ def _AuthenticateRalinkLegacy(self, network):
4751+ if network.get('key') is not None:
4752 try:
4753 info = self._GetRalinkInfo()[network.get('bssid')]
4754 except KeyError:
4755- print("Could not find current network in iwpriv " + \
4756- "get_site_survey results. Cannot authenticate.")
4757+ print("Could not find current network in iw scan results. Cannot authenticate.")
4758 return
4759-
4760- if info['enctype'] == "WEP" and info['authtype'] == 'OPEN':
4761+
4762+ if info['enctype'] == "WEP" and info['authmode'] == 'OPEN':
4763 print('Setting up WEP')
4764- cmd = ''.join(['iwconfig ', self.iface, ' key ',
4765- network.get('key')])
4766+ cmd = 'iw dev ', self.iface, ' connect ', network['essid'], ' key ', f'0:{network["key"]}'
4767 if self.verbose:
4768 print(cmd)
4769+ result = subprocess.run(cmd, capture_output=True, text=True)
4770 misc.Run(cmd)
4771 else:
4772- cmd_list = []
4773- cmd_list.append('NetworkType=' + info['nettype'])
4774- cmd_list.append('AuthMode=' + info['authmode'])
4775- cmd_list.append('EncrypType=' + info['enctype'])
4776- cmd_list.append('SSID="%s"' % network['essid'])
4777- cmd_list.append('%(keyname)s="%(key)s"' % network)
4778- if info['nettype'] == 'SHARED' and info['enctype'] == 'WEP':
4779- cmd_list.append('DefaultKeyID=1')
4780-
4781- for cmd in cmd_list:
4782- cmd = ['iwpriv', self.iface, 'set', cmd]
4783- if self.verbose:
4784- print(' '.join(cmd))
4785- misc.Run(cmd)
4786+ print('Setting up WPA/WPA2/WPA3')
4787+ wpa_supplicant_conf = f"""
4788+ network={{
4789+ ssid="{network['essid']}"
4790+ psk="{network['key']}"
4791+ key_mgmt={info['authmode']}
4792+ pairwise={info['enctype']}
4793+ }}
4794+ """
4795+ with tempfile.NamedTemporaryFile(delete=True) as temp_file:
4796+ conf_path = temp_file.name
4797+ temp_file.write(wpa_supplicant_conf.encode('utf-8'))
4798+
4799+ if self.verbose:
4800+ print(f"WPA Supplicant Config:\n{wpa_supplicant_conf}")
4801+
4802+ cmd = 'wpa_supplicant -i ', self.iface, ' -c ', conf_path, ' -B '
4803+ if self.verbose:
4804+ print(cmd)
4805+ misc.Run(cmd)
4806
4807 @neediface([])
4808 def GetNetworks(self, essid=None):
4809@@ -1339,6 +1552,12 @@ class BaseWirelessInterface(BaseInterface):
4810 # has an essid named Cell...
4811 networks = results.split( '\nBSS ' )
4812
4813+ # If the first element does not start with 'BSS ', add it
4814+ networks[0] = 'BSS ' + networks[0] if not networks[0].strip().startswith('BSS') else networks[0]
4815+
4816+ # Add 'BSS ' to other elements
4817+ networks = [net if i == 0 else 'BSS ' + net for i, net in enumerate(networks)]
4818+
4819 # Get available network info from iwpriv get_site_survey
4820 # if we're using a ralink card (needed to get encryption info)
4821 if self.wpa_driver == RALINK_DRIVER:
4822@@ -1361,6 +1580,10 @@ class BaseWirelessInterface(BaseInterface):
4823 or not entry['hidden']):
4824 access_points[entry['bssid']] = entry
4825
4826+ operating_mode = self.GetMode()
4827+ for ap in access_points.values():
4828+ ap['operating_mode'] = operating_mode
4829+
4830 return list(access_points.values())
4831
4832 def _ParseAccessPoint(self, cell, ralink_info):
4833@@ -1415,6 +1638,9 @@ class BaseWirelessInterface(BaseInterface):
4834 else:
4835 ap['bitrates'] = None
4836
4837+ # freq
4838+ ap['freq'] = misc.RunRegex(freq_pattern, cell)
4839+
4840 # BSSID
4841 ap['bssid'] = misc.RunRegex(ap_mac_pattern, cell)
4842
4843@@ -1457,6 +1683,7 @@ class BaseWirelessInterface(BaseInterface):
4844 # Link Quality
4845 # Set strength to -1 if the quality is not found
4846 ap['quality'] = self._get_link_quality(cell)
4847+
4848 # FIXME I got no idea what to grab on the "iw scan"
4849 if ap['quality'] is None:
4850 ap['quality'] = -1
4851@@ -1539,133 +1766,138 @@ class BaseWirelessInterface(BaseInterface):
4852 misc.Run(cmd)
4853
4854 @neediface("")
4855- def GetBSSID(self, iwconfig=None):
4856+ def GetBSSID(self, iwlink=None):
4857 """ Get the MAC address for the interface. """
4858- if not iwconfig:
4859- output = self.GetIwconfig()
4860+ if not iwlink:
4861+ output = self.GetIwLink()
4862 else:
4863- output = iwconfig
4864-
4865+ output = iwlink
4866+
4867 bssid = misc.RunRegex(bssid_pattern, output)
4868 return bssid
4869
4870 @neediface("")
4871- def GetCurrentBitrate(self, iwconfig=None):
4872+ def GetCurrentBitrate(self, iwlink=None):
4873 """ Get the current bitrate for the interface. """
4874- if not iwconfig:
4875- output = self.GetIwconfig()
4876+ if not iwlink:
4877+ output = self.GetIwLink()
4878 else:
4879- output = iwconfig
4880-
4881- bitrate = misc.RunRegex(bitrate_pattern, output)
4882- return bitrate
4883+ output = iwlink
4884+
4885+ freq = misc.RunRegex(txbitrate_pattern, output)
4886+ return freq
4887
4888 @neediface("")
4889- def GetOperationalMode(self, iwconfig=None):
4890- """ Get the operational mode for the interface. """
4891- if not iwconfig:
4892- output = self.GetIwconfig()
4893+ def GetCurrentFreq(self, iwlink=None):
4894+ """ Get the current bitrate for the interface. """
4895+ if not iwlink:
4896+ output = self.GetIwLink()
4897 else:
4898- output = iwconfig
4899-
4900- opmode = misc.RunRegex(opmode_pattern, output)
4901- if opmode:
4902- opmode = opmode.strip()
4903- return opmode
4904+ output = iwlink
4905+
4906+ freq = misc.RunRegex(freq_pattern, output)
4907+ return freq
4908
4909 @neediface("")
4910- def GetAvailableAuthMethods(self, iwlistauth=None):
4911- """ Get the available authentication methods for the interface. """
4912- if not iwlistauth:
4913- cmd = 'iwlist ' + self.iface + ' auth'
4914- if self.verbose:
4915- print(cmd)
4916- output = misc.Run(cmd)
4917- else:
4918- output = iwlistauth
4919-
4920- authm = misc.RunRegex(authmethods_pattern, output)
4921- authm_list = [m.strip() for m in authm.split('\n') if m.strip()]
4922- return ';'.join(authm_list)
4923+ def GetOperationalMode(self, iwinfo=None):
4924+ """ Get the operational mode for the interface. """
4925+ return self.GetMode(iwinfo)
4926+
4927+# @neediface("")
4928+# def GetAvailableAuthMethods(self, iwlistauth=None):
4929+# """ Get the available authentication methods for the interface. """
4930+# if not iwlistauth:
4931+# cmd = 'iwlist ' + self.iface + ' auth'
4932+# if self.verbose:
4933+# print(cmd)
4934+# output = misc.Run(cmd)
4935+# else:
4936+# output = iwlistauth
4937+#
4938+# authm = misc.RunRegex(authmethods_pattern, output)
4939+# authm_list = [m.strip() for m in authm.split('\n') if m.strip()]
4940+# return ';'.join(authm_list)
4941
4942 @neediface('')
4943 def GetAvailableBitrates(self):
4944- """ Get the available bitrates the wifi card can use. """
4945-
4946- cmd = 'iwlist ' + self.iface + ' rate'
4947- if self.verbose:
4948- print(cmd)
4949- rates = misc.Run(cmd)
4950-
4951- # process the output
4952- rates = rates.split('\n')
4953- rates = [x.strip().split(' ')[0] for x in rates]
4954- rates = [x for x in rates if x and x[0].isdigit()]
4955- return dbus.Array(rates, signature='v')
4956+ try:
4957+ cmd = "iw list"
4958+ if self.verbose:
4959+ print(cmd)
4960+ output = misc.Run(cmd)
4961+ pattern = r"Bitrates \(non-HT\):((?:\n\s+\*\s[\d.]+(?: Mbps)?(?: \(.*\))?)*)"
4962+ match = re.search(pattern, output)
4963+ if match:
4964+ bitrates_section = match.group(1)
4965+ bitrates = re.findall(r"\*\s([\d.]+)", bitrates_section)
4966+ return dbus.Array(bitrates, signature='v')
4967+ else:
4968+ return dbus.Array([], signature='v')
4969+ except Exception as e:
4970+ print(f"Error: {e}")
4971+ return dbus.Array([], signature='v')
4972
4973 def _get_link_quality(self, output):
4974 """ Parse out the link quality from iwlist scan or iwconfig output. """
4975- try:
4976- [(strength, max_strength)] = strength_pattern.findall(output)
4977- except ValueError:
4978- (strength, max_strength) = (None, None)
4979+ signal_dbm = misc.RunRegex(signaldbm_pattern, output)
4980+ if signal_dbm is None:
4981+ return 101
4982
4983- if strength in ['', None]:
4984- try:
4985- [(strength, max_strength)] = altstrength_pattern.findall(output)
4986- except ValueError:
4987- # if the pattern was unable to match anything
4988- # we'll return 101, which will allow us to stay
4989- # connected even though we don't know the strength
4990- # it also allows us to tell if
4991- return 101
4992- if strength not in ['', None] and max_strength:
4993- return (100 * int(strength) // int(max_strength))
4994- elif strength not in ["", None]:
4995- return int(strength)
4996- else:
4997- return None
4998+ quality = self._convert_dbm_to_quality(float(signal_dbm))
4999+ return quality
5000
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: