Compare commits
22 commits
5844e2265e
...
8b3ef2c346
| Author | SHA1 | Date | |
|---|---|---|---|
| 8b3ef2c346 | |||
| 20945c6329 | |||
| d467231321 | |||
| 282bc830ee | |||
| 9a40d38ad8 | |||
| 862594256a | |||
| 438fffbacd | |||
| 1d4d125a93 | |||
| 11b21c8fd9 | |||
| 4b3598d36e | |||
| e7dd97d713 | |||
| 480476f59d | |||
| c115148f4a | |||
| ad3d752b12 | |||
| b03d10b164 | |||
| 92d4fbc5ff | |||
| 4df3b04690 | |||
| 853e6ae454 | |||
| 0418e8bab9 | |||
| 3fc41eef8f | |||
| 47d70ab78d | |||
| 0ab9561d2b |
22 changed files with 574 additions and 198 deletions
|
|
@ -13,8 +13,12 @@ module_arch = enlightenment.get_variable(pkgconfig: 'module_arch',
|
|||
default_value: 'linux-gnu-@0@'.format(host_machine.cpu()))
|
||||
module_dir = join_paths(get_option('libdir'), 'enlightenment', 'modules', 'iwd')
|
||||
|
||||
utf8_args = cc.get_supported_arguments(['-finput-charset=UTF-8',
|
||||
'-fexec-charset=UTF-8'])
|
||||
|
||||
add_project_arguments('-DPACKAGE="e_iwd"',
|
||||
'-DPACKAGE_VERSION="@0@"'.format(meson.project_version()),
|
||||
utf8_args,
|
||||
language : 'c')
|
||||
|
||||
subdir('src')
|
||||
|
|
|
|||
|
|
@ -30,9 +30,8 @@ e_iwd_config_load(void)
|
|||
/* Missing or out-of-date — start fresh with defaults. */
|
||||
if (e_iwd_config)
|
||||
{
|
||||
if (e_iwd_config->preferred_adapter)
|
||||
eina_stringshare_del(e_iwd_config->preferred_adapter);
|
||||
free(e_iwd_config);
|
||||
eina_stringshare_replace(&e_iwd_config->preferred_adapter, NULL);
|
||||
E_FREE(e_iwd_config);
|
||||
}
|
||||
e_iwd_config = E_NEW(E_Iwd_Config, 1);
|
||||
e_iwd_config->version = CONFIG_VERSION;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@
|
|||
#include "iwd/iwd_manager.h"
|
||||
#include "iwd/iwd_device.h"
|
||||
#include "iwd/iwd_network.h"
|
||||
#include "iwd/iwd_labels.h"
|
||||
#include <e_gadcon.h>
|
||||
#include <limits.h>
|
||||
|
||||
/* ----- per-instance gadget data --------------------------------------- */
|
||||
|
||||
|
|
@ -74,30 +76,6 @@ _icon_name_for_state(Iwd_State s)
|
|||
return "network-wireless";
|
||||
}
|
||||
|
||||
static const char *
|
||||
_state_label(Iwd_State s)
|
||||
{
|
||||
switch (s)
|
||||
{
|
||||
case IWD_STATE_OFF: return "Wi-Fi disabled";
|
||||
case IWD_STATE_IDLE: return "Disconnected";
|
||||
case IWD_STATE_SCANNING: return "Scanning";
|
||||
case IWD_STATE_CONNECTING: return "Connecting";
|
||||
case IWD_STATE_CONNECTED: return "Connected";
|
||||
case IWD_STATE_ERROR: return "Error";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static const char *
|
||||
_sec_label(int s)
|
||||
{
|
||||
/* Iwd_Security values, kept in sync with iwd_network.h. */
|
||||
switch (s) { case 0: return "open"; case 1: return "WPA";
|
||||
case 2: return "802.1X"; case 3: return "WEP"; }
|
||||
return "?";
|
||||
}
|
||||
|
||||
static void
|
||||
_build_tooltip(Instance *inst, Iwd_State s)
|
||||
{
|
||||
|
|
@ -108,13 +86,13 @@ _build_tooltip(Instance *inst, Iwd_State s)
|
|||
if (n)
|
||||
snprintf(buf, sizeof(buf), "Wi-Fi: %s — %s — signal %d/4",
|
||||
n->ssid ? n->ssid : "?",
|
||||
_sec_label(n->security),
|
||||
iwd_security_label(n->security),
|
||||
iwd_network_signal_tier(n));
|
||||
else
|
||||
snprintf(buf, sizeof(buf), "Wi-Fi: connected");
|
||||
}
|
||||
else
|
||||
snprintf(buf, sizeof(buf), "Wi-Fi: %s", _state_label(s));
|
||||
snprintf(buf, sizeof(buf), "Wi-Fi: %s", iwd_state_label(s));
|
||||
elm_object_tooltip_text_set(inst->o_base, buf);
|
||||
}
|
||||
|
||||
|
|
@ -173,14 +151,12 @@ _on_mouse_down(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, vo
|
|||
|
||||
/* ----- helpers --------------------------------------------------------- */
|
||||
|
||||
static char *
|
||||
_theme_path(void)
|
||||
static Eina_Bool
|
||||
_theme_path(char *buf, size_t len)
|
||||
{
|
||||
static char buf[4096];
|
||||
if (!e_iwd || !e_iwd->module) return NULL;
|
||||
snprintf(buf, sizeof(buf), "%s/e-module-iwd.edj",
|
||||
e_module_dir_get(e_iwd->module));
|
||||
return buf;
|
||||
if (!e_iwd || !e_iwd->module) return EINA_FALSE;
|
||||
snprintf(buf, len, "%s/e-module-iwd.edj", e_module_dir_get(e_iwd->module));
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
/* ----- gadcon class ---------------------------------------------------- */
|
||||
|
|
@ -189,7 +165,8 @@ static E_Gadcon_Client *
|
|||
_gc_init(E_Gadcon *gc, const char *name, const char *id, const char *style)
|
||||
{
|
||||
Instance *inst = E_NEW(Instance, 1);
|
||||
const char *path = _theme_path();
|
||||
char path[PATH_MAX];
|
||||
if (!_theme_path(path, sizeof(path))) path[0] = '\0';
|
||||
|
||||
/* themed edje is the gadcon o_base — its intrinsic min comes from the
|
||||
* theme group, just like the backlight module. */
|
||||
|
|
@ -224,6 +201,9 @@ _gc_shutdown(E_Gadcon_Client *gcc)
|
|||
Instance *inst = gcc->data;
|
||||
if (!inst) return;
|
||||
_instances = eina_list_remove(_instances, inst);
|
||||
if (inst->o_base)
|
||||
evas_object_event_callback_del_full(inst->o_base, EVAS_CALLBACK_MOUSE_DOWN,
|
||||
_on_mouse_down, inst);
|
||||
if (inst->o_icon) evas_object_del(inst->o_icon);
|
||||
if (inst->o_base) evas_object_del(inst->o_base);
|
||||
E_FREE(inst);
|
||||
|
|
@ -250,19 +230,20 @@ _gc_label(const E_Gadcon_Client_Class *cc EINA_UNUSED) { return "iwd"; }
|
|||
static Evas_Object *
|
||||
_gc_icon(const E_Gadcon_Client_Class *cc EINA_UNUSED, Evas *evas)
|
||||
{
|
||||
const char *path = _theme_path();
|
||||
char path[PATH_MAX];
|
||||
Evas_Object *o = edje_object_add(evas);
|
||||
if (path) edje_object_file_set(o, path, "icon");
|
||||
if (_theme_path(path, sizeof(path)))
|
||||
edje_object_file_set(o, path, "icon");
|
||||
return o;
|
||||
}
|
||||
|
||||
static const char *
|
||||
_gc_id_new(const E_Gadcon_Client_Class *cc)
|
||||
{
|
||||
static char buf[128];
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), "%s.%d", cc->name,
|
||||
eina_list_count(_instances) + 1);
|
||||
return buf;
|
||||
return eina_stringshare_add(buf);
|
||||
}
|
||||
|
||||
static const E_Gadcon_Client_Class _gadcon_class =
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ e_modapi_shutdown(E_Module *m EINA_UNUSED)
|
|||
if (!e_iwd) return 1;
|
||||
|
||||
e_iwd_gadget_shutdown();
|
||||
e_iwd_popup_shutdown();
|
||||
if (e_iwd->manager) iwd_manager_free(e_iwd->manager);
|
||||
e_iwd_config_save();
|
||||
if (e_iwd->conn) eldbus_connection_unref(e_iwd->conn);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "iwd/iwd_device.h"
|
||||
#include "iwd/iwd_network.h"
|
||||
#include "iwd/iwd_agent.h"
|
||||
#include "iwd/iwd_labels.h"
|
||||
#include "ui/wifi_auth.h"
|
||||
#include "ui/wifi_hidden.h"
|
||||
#include <e_gadcon_popup.h>
|
||||
|
|
@ -30,38 +31,42 @@ static Popup *_popup = NULL;
|
|||
static Iwd_Agent_Request *_pending_req = NULL;
|
||||
/* Tracked so iwd's Cancel(reason) can tear down the dialog. */
|
||||
static Evas_Object *_pending_dialog = NULL;
|
||||
/* One-shot passphrase pre-armed by the hidden-network dialog. */
|
||||
static char *_hidden_pending_pass = NULL;
|
||||
/* One-shot passphrase pre-armed by the hidden-network dialog. Bound to the
|
||||
* SSID it was entered for, with a timeout that wipes it if iwd never comes
|
||||
* asking — so a stashed passphrase can never leak to an unrelated network. */
|
||||
static char *_hidden_pending_pass = NULL;
|
||||
static char *_hidden_pending_ssid = NULL;
|
||||
static Ecore_Timer *_hidden_pending_timer = NULL;
|
||||
#define HIDDEN_PASS_TIMEOUT 30.0 /* seconds */
|
||||
|
||||
static void
|
||||
_hidden_pending_clear(void)
|
||||
{
|
||||
if (_hidden_pending_pass)
|
||||
{
|
||||
explicit_bzero(_hidden_pending_pass, strlen(_hidden_pending_pass));
|
||||
free(_hidden_pending_pass);
|
||||
_hidden_pending_pass = NULL;
|
||||
}
|
||||
free(_hidden_pending_ssid);
|
||||
_hidden_pending_ssid = NULL;
|
||||
if (_hidden_pending_timer)
|
||||
{
|
||||
ecore_timer_del(_hidden_pending_timer);
|
||||
_hidden_pending_timer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_hidden_pending_timeout(void *data EINA_UNUSED)
|
||||
{
|
||||
_hidden_pending_timer = NULL;
|
||||
_hidden_pending_clear();
|
||||
return ECORE_CALLBACK_CANCEL;
|
||||
}
|
||||
|
||||
/* ----- helpers --------------------------------------------------------- */
|
||||
|
||||
static const char *
|
||||
_state_label(Iwd_State s)
|
||||
{
|
||||
switch (s) {
|
||||
case IWD_STATE_OFF: return "Wi-Fi disabled";
|
||||
case IWD_STATE_IDLE: return "Disconnected";
|
||||
case IWD_STATE_SCANNING: return "Scanning…";
|
||||
case IWD_STATE_CONNECTING: return "Connecting…";
|
||||
case IWD_STATE_CONNECTED: return "Connected";
|
||||
case IWD_STATE_ERROR: return "Error";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static const char *
|
||||
_sec_label(Iwd_Security s)
|
||||
{
|
||||
switch (s) {
|
||||
case IWD_SEC_OPEN: return "open";
|
||||
case IWD_SEC_PSK: return "WPA";
|
||||
case IWD_SEC_8021X: return "802.1X";
|
||||
case IWD_SEC_WEP: return "WEP";
|
||||
case IWD_SEC_UNKNOWN: return "?";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static int
|
||||
_net_cmp(const void *a, const void *b)
|
||||
{
|
||||
|
|
@ -112,20 +117,95 @@ _active_device(void)
|
|||
|
||||
/* ----- list rendering -------------------------------------------------- */
|
||||
|
||||
/* Click data is a strdup'd object path, freed via the row's EVAS_CALLBACK_DEL.
|
||||
* Holding the Iwd_Network * directly would UAF if iwd vanished (its hash is
|
||||
* cleared in _on_name_vanished) between row paint and click. */
|
||||
static void
|
||||
_row_path_free(void *data, Evas *e EINA_UNUSED,
|
||||
Evas_Object *o EINA_UNUSED, void *ev EINA_UNUSED)
|
||||
{ free(data); }
|
||||
|
||||
static void
|
||||
_on_net_clicked(void *data, Evas_Object *obj EINA_UNUSED, void *ev EINA_UNUSED)
|
||||
{
|
||||
Iwd_Network *n = data;
|
||||
const char *netpath = data;
|
||||
if (!netpath || !e_iwd || !e_iwd->manager) return;
|
||||
const Eina_Hash *h = iwd_manager_networks(e_iwd->manager);
|
||||
Iwd_Network *n = h ? eina_hash_find(h, netpath) : NULL;
|
||||
if (!n) return;
|
||||
iwd_manager_clear_error(e_iwd->manager);
|
||||
iwd_network_connect(n);
|
||||
}
|
||||
|
||||
/* The Iwd_Network captured when the confirmation popup was opened may have
|
||||
* disappeared (scan refresh, iwd restart) by the time the user clicks. We
|
||||
* stash its object path on the popup and re-resolve through the live hash
|
||||
* at click time so a stale pointer is never dereferenced. */
|
||||
static void _forget_confirm_free(void *data, Evas *e EINA_UNUSED,
|
||||
Evas_Object *o EINA_UNUSED, void *ev EINA_UNUSED)
|
||||
{ free(data); }
|
||||
|
||||
static void _forget_confirm_yes(void *data, Evas_Object *obj, void *ev EINA_UNUSED)
|
||||
{
|
||||
const char *netpath = data;
|
||||
Evas_Object *pp = evas_object_data_get(obj, "_eiwd_confirm_popup");
|
||||
if (netpath && e_iwd && e_iwd->manager)
|
||||
{
|
||||
const Eina_Hash *h = iwd_manager_networks(e_iwd->manager);
|
||||
Iwd_Network *n = h ? eina_hash_find(h, netpath) : NULL;
|
||||
if (n) iwd_network_forget(n);
|
||||
}
|
||||
if (pp) evas_object_del(pp);
|
||||
}
|
||||
|
||||
static void _forget_confirm_no(void *data EINA_UNUSED, Evas_Object *obj, void *ev EINA_UNUSED)
|
||||
{
|
||||
Evas_Object *pp = evas_object_data_get(obj, "_eiwd_confirm_popup");
|
||||
if (pp) evas_object_del(pp);
|
||||
}
|
||||
|
||||
static void
|
||||
_on_net_forget(void *data, Evas_Object *obj EINA_UNUSED, void *ev EINA_UNUSED)
|
||||
{
|
||||
Iwd_Network *n = data;
|
||||
const char *netpath = data;
|
||||
if (!netpath || !e_iwd || !e_iwd->manager) return;
|
||||
const Eina_Hash *h = iwd_manager_networks(e_iwd->manager);
|
||||
Iwd_Network *n = h ? eina_hash_find(h, netpath) : NULL;
|
||||
if (!n) return;
|
||||
iwd_network_forget(n);
|
||||
|
||||
/* Forget destroys the saved passphrase irreversibly — confirm first.
|
||||
* A stray click on the ✕ next to a known network would otherwise wipe
|
||||
* credentials with no recovery. */
|
||||
Evas_Object *parent = _popup ? _popup->box : e_comp->elm;
|
||||
Evas_Object *pp = elm_popup_add(parent);
|
||||
char msg[256];
|
||||
snprintf(msg, sizeof(msg),
|
||||
"Forget saved network <b>%s</b>?<br/>"
|
||||
"The passphrase will be permanently deleted.",
|
||||
n->ssid ? n->ssid : "(hidden)");
|
||||
elm_object_part_text_set(pp, "title,text", "Forget network");
|
||||
elm_object_text_set(pp, msg);
|
||||
|
||||
/* Weak ref by netpath — looked up at click time. Freed when the popup
|
||||
* is destroyed (either button or the user closing the window). */
|
||||
char *netpath_ref = n->path ? strdup(n->path) : NULL;
|
||||
if (netpath_ref)
|
||||
evas_object_event_callback_add(pp, EVAS_CALLBACK_DEL,
|
||||
_forget_confirm_free, netpath_ref);
|
||||
|
||||
Evas_Object *yes = elm_button_add(pp);
|
||||
elm_object_text_set(yes, "Forget");
|
||||
evas_object_data_set(yes, "_eiwd_confirm_popup", pp);
|
||||
evas_object_smart_callback_add(yes, "clicked", _forget_confirm_yes, netpath_ref);
|
||||
elm_object_part_content_set(pp, "button1", yes);
|
||||
|
||||
Evas_Object *no = elm_button_add(pp);
|
||||
elm_object_text_set(no, "Cancel");
|
||||
evas_object_data_set(no, "_eiwd_confirm_popup", pp);
|
||||
evas_object_smart_callback_add(no, "clicked", _forget_confirm_no, NULL);
|
||||
elm_object_part_content_set(pp, "button2", no);
|
||||
|
||||
evas_object_show(pp);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -158,13 +238,23 @@ _rebuild_list(Popup *p)
|
|||
evas_object_size_hint_align_set(row, EVAS_HINT_FILL, 0);
|
||||
|
||||
Evas_Object *btn = elm_button_add(row);
|
||||
/* Truncate long SSIDs so the row never forces horizontal scrolling. */
|
||||
/* Truncate long SSIDs so the row never forces horizontal scrolling.
|
||||
* Count codepoints, not bytes — SSIDs may contain UTF-8 and naive
|
||||
* byte truncation would split a multi-byte sequence. */
|
||||
const char *raw_ssid = n->ssid ? n->ssid : "(hidden)";
|
||||
char ssid_buf[32];
|
||||
const int max_ssid = 22;
|
||||
if ((int)strlen(raw_ssid) > max_ssid)
|
||||
char ssid_buf[64];
|
||||
const int max_chars = 21;
|
||||
int iindex = 0, prev = 0, chars = 0;
|
||||
while (chars < max_chars
|
||||
&& eina_unicode_utf8_next_get(raw_ssid, &iindex))
|
||||
{ prev = iindex; chars++; }
|
||||
if (eina_unicode_utf8_next_get(raw_ssid, &iindex))
|
||||
{
|
||||
snprintf(ssid_buf, sizeof(ssid_buf), "%.*s…", max_ssid - 1, raw_ssid);
|
||||
/* more remains — truncate at `prev` and append U+2026 */
|
||||
int copy = prev < (int)sizeof(ssid_buf) - 4
|
||||
? prev : (int)sizeof(ssid_buf) - 4;
|
||||
memcpy(ssid_buf, raw_ssid, copy);
|
||||
memcpy(ssid_buf + copy, "\xe2\x80\xa6", 4); /* "…" + NUL */
|
||||
raw_ssid = ssid_buf;
|
||||
}
|
||||
char label[256];
|
||||
|
|
@ -172,12 +262,32 @@ _rebuild_list(Popup *p)
|
|||
_signal_bars(iwd_network_signal_tier(n)),
|
||||
n->known_path ? "★ " : " ",
|
||||
raw_ssid,
|
||||
_sec_label(n->security),
|
||||
iwd_security_label(n->security),
|
||||
n->connected ? " ✔" : "");
|
||||
elm_object_text_set(btn, label);
|
||||
/* Spoken label: avoids the ▂▄▆█ glyphs and ★/✔ markers which
|
||||
* screen readers announce as raw Unicode. */
|
||||
char access[256];
|
||||
snprintf(access, sizeof(access),
|
||||
"%s, signal %d of 4, %s%s%s",
|
||||
raw_ssid,
|
||||
iwd_network_signal_tier(n),
|
||||
iwd_security_label(n->security),
|
||||
n->known_path ? ", saved" : "",
|
||||
n->connected ? ", connected" : "");
|
||||
elm_object_access_info_set(btn, access);
|
||||
evas_object_size_hint_weight_set(btn, EVAS_HINT_EXPAND, 0);
|
||||
evas_object_size_hint_align_set(btn, EVAS_HINT_FILL, 0);
|
||||
evas_object_smart_callback_add(btn, "clicked", _on_net_clicked, n);
|
||||
/* Pass a copy of the object path, not the Iwd_Network *: the network
|
||||
* may disappear (iwd_dbus name-vanished, scan refresh) before the
|
||||
* user clicks, freeing the struct. The path is re-resolved through
|
||||
* the live hash at click time. The buffer is freed via the button's
|
||||
* EVAS_CALLBACK_DEL when the row is rebuilt or popup torn down. */
|
||||
char *click_path = n->path ? strdup(n->path) : NULL;
|
||||
if (click_path)
|
||||
evas_object_event_callback_add(btn, EVAS_CALLBACK_DEL,
|
||||
_row_path_free, click_path);
|
||||
evas_object_smart_callback_add(btn, "clicked", _on_net_clicked, click_path);
|
||||
elm_box_pack_end(row, btn);
|
||||
evas_object_show(btn);
|
||||
|
||||
|
|
@ -186,7 +296,14 @@ _rebuild_list(Popup *p)
|
|||
Evas_Object *fb = elm_button_add(row);
|
||||
elm_object_text_set(fb, "✕");
|
||||
elm_object_tooltip_text_set(fb, "Forget network");
|
||||
evas_object_smart_callback_add(fb, "clicked", _on_net_forget, n);
|
||||
char facc[128];
|
||||
snprintf(facc, sizeof(facc), "Forget %s", raw_ssid);
|
||||
elm_object_access_info_set(fb, facc);
|
||||
char *forget_path = n->path ? strdup(n->path) : NULL;
|
||||
if (forget_path)
|
||||
evas_object_event_callback_add(fb, EVAS_CALLBACK_DEL,
|
||||
_row_path_free, forget_path);
|
||||
evas_object_smart_callback_add(fb, "clicked", _on_net_forget, forget_path);
|
||||
elm_box_pack_end(row, fb);
|
||||
evas_object_show(fb);
|
||||
}
|
||||
|
|
@ -202,8 +319,21 @@ _refresh(Popup *p)
|
|||
{
|
||||
if (!p || !e_iwd || !e_iwd->manager) return;
|
||||
Iwd_State s = iwd_manager_state(e_iwd->manager);
|
||||
/* Radio went off: the stash can no longer be useful (iwd won't ask)
|
||||
* and we'd rather not keep a passphrase resident across a toggle. */
|
||||
if (s == IWD_STATE_OFF) _hidden_pending_clear();
|
||||
if (p->status_lbl)
|
||||
elm_object_text_set(p->status_lbl, _state_label(s));
|
||||
{
|
||||
const char *err = iwd_manager_last_error(e_iwd->manager);
|
||||
if (err)
|
||||
{
|
||||
char buf[320];
|
||||
snprintf(buf, sizeof(buf), "%s — %s", iwd_state_label(s), err);
|
||||
elm_object_text_set(p->status_lbl, buf);
|
||||
}
|
||||
else
|
||||
elm_object_text_set(p->status_lbl, iwd_state_label(s));
|
||||
}
|
||||
if (p->btn_toggle)
|
||||
elm_object_text_set(p->btn_toggle, s == IWD_STATE_OFF ? "Enable" : "Disable");
|
||||
if (p->btn_scan)
|
||||
|
|
@ -229,18 +359,23 @@ _on_manager_change(void *data, Iwd_Manager *m EINA_UNUSED)
|
|||
|
||||
static void _on_rescan (void *d EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *e EINA_UNUSED)
|
||||
{
|
||||
if (e_iwd && e_iwd->manager) iwd_manager_scan_request(e_iwd->manager);
|
||||
if (!e_iwd || !e_iwd->manager) return;
|
||||
iwd_manager_clear_error(e_iwd->manager);
|
||||
iwd_manager_scan_request(e_iwd->manager);
|
||||
}
|
||||
static void _on_toggle(void *d EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *e EINA_UNUSED)
|
||||
{
|
||||
if (!e_iwd || !e_iwd->manager) return;
|
||||
iwd_manager_clear_error(e_iwd->manager);
|
||||
Eina_Bool off = (iwd_manager_state(e_iwd->manager) == IWD_STATE_OFF);
|
||||
iwd_manager_set_powered(e_iwd->manager, off);
|
||||
}
|
||||
static void _on_disconnect(void *d EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *e EINA_UNUSED)
|
||||
{
|
||||
Iwd_Device *dev = _active_device();
|
||||
if (dev) iwd_device_disconnect(dev);
|
||||
if (!dev) return;
|
||||
if (e_iwd && e_iwd->manager) iwd_manager_clear_error(e_iwd->manager);
|
||||
iwd_device_disconnect(dev);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -249,10 +384,19 @@ _on_hidden_done(void *data EINA_UNUSED, const char *ssid, const char *pass, Eina
|
|||
if (!ok || !ssid || !*ssid) return;
|
||||
Iwd_Device *dev = _active_device();
|
||||
if (!dev) return;
|
||||
/* Pre-arm the agent reply so the next RequestPassphrase from iwd is
|
||||
* answered automatically. If the network turns out to be open, the
|
||||
* stashed passphrase is simply never consumed. */
|
||||
if (pass && *pass) _hidden_pending_pass = strdup(pass);
|
||||
/* Pre-arm the agent reply so the next RequestPassphrase from iwd for
|
||||
* *this SSID* is answered automatically. The stash is bound to the SSID
|
||||
* and self-clears after HIDDEN_PASS_TIMEOUT seconds — so a typo'd or
|
||||
* out-of-range SSID cannot cause the passphrase to leak to a later,
|
||||
* unrelated network whose RequestPassphrase happens to land first. */
|
||||
_hidden_pending_clear();
|
||||
if (pass && *pass)
|
||||
{
|
||||
_hidden_pending_pass = strdup(pass);
|
||||
_hidden_pending_ssid = strdup(ssid);
|
||||
_hidden_pending_timer = ecore_timer_add(HIDDEN_PASS_TIMEOUT,
|
||||
_hidden_pending_timeout, NULL);
|
||||
}
|
||||
iwd_device_connect_hidden(dev, ssid);
|
||||
}
|
||||
|
||||
|
|
@ -267,7 +411,12 @@ static void
|
|||
_on_auth_done(void *data EINA_UNUSED, const char *pass, Eina_Bool ok)
|
||||
{
|
||||
_pending_dialog = NULL;
|
||||
if (!_pending_req) return;
|
||||
if (!_pending_req)
|
||||
{
|
||||
/* Request was already canceled by iwd; nothing to do. The caller
|
||||
* (wifi_auth) wipes its own copy of `pass` after we return. */
|
||||
return;
|
||||
}
|
||||
if (ok) iwd_agent_reply (_pending_req, pass ? pass : "");
|
||||
else iwd_agent_cancel(_pending_req);
|
||||
_pending_req = NULL;
|
||||
|
|
@ -303,15 +452,27 @@ e_iwd_popup_install_passphrase_handler(void)
|
|||
static void
|
||||
_on_passphrase_request(void *data EINA_UNUSED, Iwd_Agent_Request *req, const char *netpath)
|
||||
{
|
||||
/* If the user just kicked off a hidden-network connect with a passphrase,
|
||||
* answer this request automatically without prompting. */
|
||||
if (_hidden_pending_pass)
|
||||
/* Resolve netpath -> network so we can both match the hidden stash
|
||||
* against the requested SSID *and* show a friendly label in the dialog. */
|
||||
Iwd_Network *n = NULL;
|
||||
if (e_iwd && e_iwd->manager)
|
||||
{
|
||||
const Eina_Hash *h = iwd_manager_networks(e_iwd->manager);
|
||||
if (h) n = eina_hash_find(h, netpath);
|
||||
}
|
||||
const char *req_ssid = (n && n->ssid) ? n->ssid : NULL;
|
||||
|
||||
/* Use the hidden-network stash *only* if iwd is asking for the same
|
||||
* SSID we entered it for. Anything else: drop the stash on the floor
|
||||
* and prompt normally. */
|
||||
if (_hidden_pending_pass && _hidden_pending_ssid &&
|
||||
req_ssid && !strcmp(req_ssid, _hidden_pending_ssid))
|
||||
{
|
||||
iwd_agent_reply(req, _hidden_pending_pass);
|
||||
free(_hidden_pending_pass);
|
||||
_hidden_pending_pass = NULL;
|
||||
_hidden_pending_clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_pending_req)
|
||||
{
|
||||
iwd_agent_cancel(req);
|
||||
|
|
@ -319,27 +480,8 @@ _on_passphrase_request(void *data EINA_UNUSED, Iwd_Agent_Request *req, const cha
|
|||
}
|
||||
_pending_req = req;
|
||||
|
||||
/* Look up the network for a friendly SSID, if we have it. */
|
||||
const char *ssid = "network";
|
||||
if (e_iwd && e_iwd->manager)
|
||||
{
|
||||
const Eina_Hash *h = iwd_manager_networks(e_iwd->manager);
|
||||
if (h)
|
||||
{
|
||||
Iwd_Network *n = eina_hash_find(h, netpath);
|
||||
if (n && n->ssid) ssid = n->ssid;
|
||||
}
|
||||
}
|
||||
const char *sec = NULL;
|
||||
if (e_iwd && e_iwd->manager)
|
||||
{
|
||||
const Eina_Hash *h = iwd_manager_networks(e_iwd->manager);
|
||||
if (h)
|
||||
{
|
||||
Iwd_Network *n = eina_hash_find(h, netpath);
|
||||
if (n) sec = _sec_label(n->security);
|
||||
}
|
||||
}
|
||||
const char *ssid = req_ssid ? req_ssid : "network";
|
||||
const char *sec = n ? iwd_security_label(n->security) : NULL;
|
||||
_pending_dialog = wifi_auth_prompt(_popup ? _popup->box : e_comp->elm,
|
||||
ssid, sec, _on_auth_done, NULL);
|
||||
}
|
||||
|
|
@ -355,6 +497,17 @@ _destroy(void)
|
|||
if (_popup->gp) e_object_del(E_OBJECT(_popup->gp));
|
||||
free(_popup);
|
||||
_popup = NULL;
|
||||
/* Drop any pre-armed hidden-network passphrase: if the user closes the
|
||||
* popup before iwd asks, the stash would otherwise sit in the heap until
|
||||
* the 30 s timer fires. The stash is process-global, not popup-scoped. */
|
||||
_hidden_pending_clear();
|
||||
}
|
||||
|
||||
void
|
||||
e_iwd_popup_shutdown(void)
|
||||
{
|
||||
_destroy();
|
||||
_hidden_pending_clear();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -370,6 +523,7 @@ e_iwd_popup_toggle(E_Gadcon_Client *gcc)
|
|||
if (!gcc || !e_iwd) return;
|
||||
|
||||
Popup *p = calloc(1, sizeof(*p));
|
||||
if (!p) return;
|
||||
_popup = p;
|
||||
|
||||
p->gp = e_gadcon_popup_new(gcc, EINA_FALSE);
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@
|
|||
#include <e_gadcon.h>
|
||||
|
||||
void e_iwd_popup_install_passphrase_handler(void);
|
||||
void e_iwd_popup_toggle (E_Gadcon_Client *gcc);
|
||||
void e_iwd_popup_close (void);
|
||||
void e_iwd_popup_refresh(void);
|
||||
void e_iwd_popup_toggle (E_Gadcon_Client *gcc);
|
||||
void e_iwd_popup_close (void);
|
||||
void e_iwd_popup_refresh (void);
|
||||
void e_iwd_popup_shutdown(void);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -61,6 +61,28 @@ iwd_adapter_free(Iwd_Adapter *a)
|
|||
free(a);
|
||||
}
|
||||
|
||||
/* Reply context captures the *manager* (which outlives all sub-objects) and
|
||||
* a strdup'd adapter path, never the Iwd_Adapter — on iwd disconnect the
|
||||
* adapter hash is freed, and a raw back-ref would UAF when the local-error
|
||||
* reply lands. Mirrors the pattern in iwd_network.c / iwd_device.c. */
|
||||
typedef struct
|
||||
{
|
||||
Iwd_Manager *m;
|
||||
char *path;
|
||||
} _Adapter_Reply_Ctx;
|
||||
|
||||
static void
|
||||
_on_set_powered_reply(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EINA_UNUSED)
|
||||
{
|
||||
_Adapter_Reply_Ctx *ctx = data;
|
||||
const char *en, *em;
|
||||
if (eldbus_message_error_get(msg, &en, &em) && ctx->m)
|
||||
iwd_manager_report_error(ctx->m,
|
||||
"Set Adapter.Powered failed: %s", em ? em : en);
|
||||
free(ctx->path);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
void
|
||||
iwd_adapter_set_powered(Iwd_Adapter *a, Eina_Bool on)
|
||||
{
|
||||
|
|
@ -83,7 +105,13 @@ iwd_adapter_set_powered(Iwd_Adapter *a, Eina_Bool on)
|
|||
eldbus_message_iter_basic_append(variant, 'b', v);
|
||||
eldbus_message_iter_container_close(iter, variant);
|
||||
|
||||
eldbus_proxy_send(props, msg, NULL, NULL, -1);
|
||||
_Adapter_Reply_Ctx *ctx = calloc(1, sizeof(*ctx));
|
||||
if (ctx)
|
||||
{
|
||||
ctx->m = a->manager;
|
||||
ctx->path = a->path ? strdup(a->path) : NULL;
|
||||
}
|
||||
eldbus_proxy_send(props, msg, _on_set_powered_reply, ctx, -1);
|
||||
/* Keep the props proxy alive on the adapter so the call isn't canceled. */
|
||||
if (a->_props_proxy_keepalive) eldbus_proxy_unref(a->_props_proxy_keepalive);
|
||||
a->_props_proxy_keepalive = props;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
#include "iwd_agent.h"
|
||||
#include "iwd_dbus.h"
|
||||
#include "iwd_manager.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
|
@ -71,6 +73,9 @@ _request_passphrase_cb(const Eldbus_Service_Interface *iface EINA_UNUSED,
|
|||
"No UI handler");
|
||||
|
||||
Iwd_Agent_Request *req = calloc(1, sizeof(*req));
|
||||
if (!req)
|
||||
return eldbus_message_error_new(msg, "net.connman.iwd.Agent.Error.Canceled",
|
||||
"Out of memory");
|
||||
req->agent = _self;
|
||||
req->msg = eldbus_message_ref((Eldbus_Message *)msg);
|
||||
_self->cb(_self->data, req, path);
|
||||
|
|
@ -113,6 +118,12 @@ void
|
|||
iwd_agent_reply(Iwd_Agent_Request *req, const char *passphrase)
|
||||
{
|
||||
if (!req) return;
|
||||
/* The passphrase is copied into the eldbus/libdbus marshalled message
|
||||
* buffer here. We can't wipe that buffer ourselves — eldbus owns it and
|
||||
* frees it asynchronously after the call is sent. Callers are expected
|
||||
* to explicit_bzero their own copy of `passphrase` after this returns;
|
||||
* the residue inside the outbound D-Bus message is unavoidable at this
|
||||
* boundary. */
|
||||
Eldbus_Message *r = eldbus_message_method_return_new(req->msg);
|
||||
eldbus_message_arguments_append(r, "s", passphrase ? passphrase : "");
|
||||
eldbus_connection_send(req->agent->conn, r, NULL, NULL, -1);
|
||||
|
|
@ -135,14 +146,29 @@ iwd_agent_cancel(Iwd_Agent_Request *req)
|
|||
/* ----- Registration with iwd ------------------------------------------ */
|
||||
|
||||
static void
|
||||
_on_register(void *data EINA_UNUSED, const Eldbus_Message *msg,
|
||||
_on_register(void *data, const Eldbus_Message *msg,
|
||||
Eldbus_Pending *p EINA_UNUSED)
|
||||
{
|
||||
/* `data` is the manager — same pointer the trampoline carries. */
|
||||
Iwd_Manager *m = data;
|
||||
const char *en, *em;
|
||||
if (eldbus_message_error_get(msg, &en, &em))
|
||||
if (!eldbus_message_error_get(msg, &en, &em)) return;
|
||||
if (m)
|
||||
iwd_manager_report_error(m,
|
||||
"Wi-Fi agent registration refused (another agent running?): %s",
|
||||
em ? em : en);
|
||||
else
|
||||
fprintf(stderr, "e_iwd: agent register failed: %s: %s\n", en, em);
|
||||
}
|
||||
|
||||
void
|
||||
iwd_agent_register(Iwd_Agent *a)
|
||||
{
|
||||
if (!a || !a->am_proxy) return;
|
||||
eldbus_proxy_call(a->am_proxy, "RegisterAgent", _on_register, a->data, -1,
|
||||
"o", IWD_AGENT_PATH);
|
||||
}
|
||||
|
||||
Iwd_Agent *
|
||||
iwd_agent_new(Eldbus_Connection *conn, Iwd_Agent_Passphrase_Cb cb, void *data)
|
||||
{
|
||||
|
|
@ -158,12 +184,8 @@ iwd_agent_new(Eldbus_Connection *conn, Iwd_Agent_Passphrase_Cb cb, void *data)
|
|||
|
||||
a->am_obj = eldbus_object_get(conn, IWD_BUS_NAME, "/net/connman/iwd");
|
||||
if (a->am_obj)
|
||||
{
|
||||
a->am_proxy = eldbus_proxy_get(a->am_obj, IWD_IFACE_AGENT_MANAGER);
|
||||
if (a->am_proxy)
|
||||
eldbus_proxy_call(a->am_proxy, "RegisterAgent", _on_register, NULL, -1,
|
||||
"o", IWD_AGENT_PATH);
|
||||
}
|
||||
a->am_proxy = eldbus_proxy_get(a->am_obj, IWD_IFACE_AGENT_MANAGER);
|
||||
iwd_agent_register(a);
|
||||
return a;
|
||||
}
|
||||
|
||||
|
|
@ -179,6 +201,13 @@ void
|
|||
iwd_agent_free(Iwd_Agent *a)
|
||||
{
|
||||
if (!a) return;
|
||||
/* Politely deregister so iwd doesn't keep dispatching to a dead service
|
||||
* during shutdown. Fire-and-forget: the connection may already be torn
|
||||
* down by the time the call would land, and there's nothing to do with
|
||||
* the reply anyway. */
|
||||
if (a->am_proxy)
|
||||
eldbus_proxy_call(a->am_proxy, "UnregisterAgent", NULL, NULL, -1,
|
||||
"o", IWD_AGENT_PATH);
|
||||
if (a->svc) eldbus_service_interface_unregister(a->svc);
|
||||
if (a->am_proxy) eldbus_proxy_unref(a->am_proxy);
|
||||
if (a->am_obj) eldbus_object_unref(a->am_obj);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,10 @@ Iwd_Agent *iwd_agent_new (Eldbus_Connection *conn,
|
|||
Iwd_Agent_Passphrase_Cb cb, void *data);
|
||||
|
||||
void iwd_agent_set_cancel_cb(Iwd_Agent *a, Iwd_Agent_Cancel_Cb cb, void *data);
|
||||
/* Re-issue RegisterAgent. Call after iwd reappears on the bus
|
||||
* (NameOwnerChanged) — without this, every PSK connect silently hangs
|
||||
* because no agent is registered against the new iwd instance. */
|
||||
void iwd_agent_register(Iwd_Agent *a);
|
||||
void iwd_agent_free(Iwd_Agent *a);
|
||||
|
||||
void iwd_agent_reply (Iwd_Agent_Request *req, const char *passphrase);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
#include "iwd_props.h"
|
||||
#include "iwd_manager.h"
|
||||
#include "iwd_network.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
|
@ -149,10 +148,15 @@ iwd_device_free(Iwd_Device *d)
|
|||
|
||||
/* Reply to Station.GetOrderedNetworks: a(on) — list of (object_path, RSSI).
|
||||
* RSSI is a 16-bit signed value in 100*dBm units. */
|
||||
/* Reply callbacks must not hold a raw Iwd_Device back-ref: a device can be
|
||||
* removed (rfkill, hot-unplug, iwd restart) while a call is in flight, and
|
||||
* the reply would then UAF. The manager outlives every sub-object, so we
|
||||
* pass it directly. Network lookups go through the live hash, which is
|
||||
* safe even after the originating device is gone. */
|
||||
static void
|
||||
_on_ordered_networks(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EINA_UNUSED)
|
||||
{
|
||||
Iwd_Device *d = data;
|
||||
Iwd_Manager *m = data;
|
||||
const char *en, *em;
|
||||
if (eldbus_message_error_get(msg, &en, &em)) return;
|
||||
|
||||
|
|
@ -160,7 +164,7 @@ _on_ordered_networks(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EI
|
|||
if (!eldbus_message_arguments_get(msg, "a(on)", &array) || !array)
|
||||
return;
|
||||
|
||||
const Eina_Hash *nets = d->manager ? iwd_manager_networks(d->manager) : NULL;
|
||||
const Eina_Hash *nets = m ? iwd_manager_networks(m) : NULL;
|
||||
Eldbus_Message_Iter *entry;
|
||||
Eina_Bool any = EINA_FALSE;
|
||||
while (eldbus_message_iter_get_and_next(array, 'r', &entry))
|
||||
|
|
@ -175,7 +179,7 @@ _on_ordered_networks(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EI
|
|||
n->have_signal = EINA_TRUE;
|
||||
any = EINA_TRUE;
|
||||
}
|
||||
if (any && d->manager) iwd_manager_notify(d->manager);
|
||||
if (any && m) iwd_manager_notify(m);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -183,38 +187,74 @@ _refresh_signals(Iwd_Device *d)
|
|||
{
|
||||
if (!d || !d->station_proxy) return;
|
||||
eldbus_proxy_call(d->station_proxy, "GetOrderedNetworks",
|
||||
_on_ordered_networks, d, -1, "");
|
||||
_on_ordered_networks, d->manager, -1, "");
|
||||
}
|
||||
|
||||
static void
|
||||
_on_scan_reply(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EINA_UNUSED)
|
||||
{
|
||||
Iwd_Manager *m = data;
|
||||
const char *en, *em;
|
||||
if (!eldbus_message_error_get(msg, &en, &em)) return;
|
||||
/* "AlreadyExists" / "InProgress" is the normal race when two scan
|
||||
* triggers fire close together — don't spam the user with that. */
|
||||
if (en && (strstr(en, "InProgress") || strstr(en, "Busy") ||
|
||||
strstr(en, "AlreadyExists")))
|
||||
return;
|
||||
if (m) iwd_manager_report_error(m, "Scan failed: %s", em ? em : en);
|
||||
}
|
||||
|
||||
void
|
||||
iwd_device_scan(Iwd_Device *d)
|
||||
{
|
||||
if (!d || !d->station_proxy) return;
|
||||
eldbus_proxy_call(d->station_proxy, "Scan", NULL, NULL, -1, "");
|
||||
eldbus_proxy_call(d->station_proxy, "Scan", _on_scan_reply, d->manager, -1, "");
|
||||
}
|
||||
|
||||
static void
|
||||
_on_disconnect_reply(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EINA_UNUSED)
|
||||
{
|
||||
Iwd_Manager *m = data;
|
||||
const char *en, *em;
|
||||
if (eldbus_message_error_get(msg, &en, &em) && m)
|
||||
iwd_manager_report_error(m, "Disconnect failed: %s", em ? em : en);
|
||||
}
|
||||
|
||||
void
|
||||
iwd_device_disconnect(Iwd_Device *d)
|
||||
{
|
||||
if (!d || !d->station_proxy) return;
|
||||
eldbus_proxy_call(d->station_proxy, "Disconnect", NULL, NULL, -1, "");
|
||||
eldbus_proxy_call(d->station_proxy, "Disconnect",
|
||||
_on_disconnect_reply, d->manager, -1, "");
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Iwd_Manager *m;
|
||||
char *ssid;
|
||||
} _Connect_Hidden_Ctx;
|
||||
|
||||
static void
|
||||
_on_connect_hidden_reply(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EINA_UNUSED)
|
||||
{
|
||||
_Connect_Hidden_Ctx *ctx = data;
|
||||
const char *en, *em;
|
||||
char *ssid = data;
|
||||
if (eldbus_message_error_get(msg, &en, &em))
|
||||
fprintf(stderr, "e_iwd: ConnectHiddenNetwork('%s') failed: %s: %s\n",
|
||||
ssid ? ssid : "?", en, em);
|
||||
free(ssid);
|
||||
if (eldbus_message_error_get(msg, &en, &em) && ctx->m)
|
||||
iwd_manager_report_error(ctx->m,
|
||||
"Connect to hidden '%s' failed: %s",
|
||||
ctx->ssid ? ctx->ssid : "?", em ? em : en);
|
||||
free(ctx->ssid);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
void
|
||||
iwd_device_connect_hidden(Iwd_Device *d, const char *ssid)
|
||||
{
|
||||
if (!d || !d->station_proxy || !ssid || !*ssid) return;
|
||||
_Connect_Hidden_Ctx *ctx = calloc(1, sizeof(*ctx));
|
||||
if (!ctx) return;
|
||||
ctx->m = d->manager;
|
||||
ctx->ssid = strdup(ssid);
|
||||
eldbus_proxy_call(d->station_proxy, "ConnectHiddenNetwork",
|
||||
_on_connect_hidden_reply, strdup(ssid), -1, "s", ssid);
|
||||
_on_connect_hidden_reply, ctx, -1, "s", ssid);
|
||||
}
|
||||
|
|
|
|||
28
src/iwd/iwd_labels.c
Normal file
28
src/iwd/iwd_labels.c
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#include "iwd_labels.h"
|
||||
|
||||
const char *
|
||||
iwd_state_label(Iwd_State s)
|
||||
{
|
||||
switch (s) {
|
||||
case IWD_STATE_OFF: return "Wi-Fi disabled";
|
||||
case IWD_STATE_IDLE: return "Disconnected";
|
||||
case IWD_STATE_SCANNING: return "Scanning…";
|
||||
case IWD_STATE_CONNECTING: return "Connecting…";
|
||||
case IWD_STATE_CONNECTED: return "Connected";
|
||||
case IWD_STATE_ERROR: return "Error";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
const char *
|
||||
iwd_security_label(Iwd_Security s)
|
||||
{
|
||||
switch (s) {
|
||||
case IWD_SEC_OPEN: return "open";
|
||||
case IWD_SEC_PSK: return "WPA";
|
||||
case IWD_SEC_8021X: return "802.1X";
|
||||
case IWD_SEC_WEP: return "WEP";
|
||||
case IWD_SEC_UNKNOWN: return "?";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
12
src/iwd/iwd_labels.h
Normal file
12
src/iwd/iwd_labels.h
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef IWD_LABELS_H
|
||||
#define IWD_LABELS_H
|
||||
|
||||
#include "iwd_manager.h"
|
||||
#include "iwd_network.h"
|
||||
|
||||
/* Short, user-facing labels for state and security enums. The pointers
|
||||
* returned are static literals — do not free. */
|
||||
const char *iwd_state_label (Iwd_State s);
|
||||
const char *iwd_security_label(Iwd_Security s);
|
||||
|
||||
#endif
|
||||
|
|
@ -4,6 +4,8 @@
|
|||
#include "iwd_device.h"
|
||||
#include "iwd_network.h"
|
||||
#include <Ecore.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
|
@ -23,6 +25,7 @@ struct _Iwd_Manager
|
|||
Eina_List *listeners; /* Listener * */
|
||||
Iwd_State state;
|
||||
Ecore_Job *notify_job;
|
||||
char *last_error;
|
||||
|
||||
Iwd_Agent_Passphrase_Cb pass_cb;
|
||||
void *pass_data;
|
||||
|
|
@ -60,6 +63,7 @@ iwd_manager_listener_add(Iwd_Manager *m, Iwd_Manager_Cb cb, void *data)
|
|||
{
|
||||
if (!m || !cb) return;
|
||||
Listener *l = calloc(1, sizeof(*l));
|
||||
if (!l) return;
|
||||
l->cb = cb; l->data = data;
|
||||
m->listeners = eina_list_append(m->listeners, l);
|
||||
}
|
||||
|
|
@ -102,6 +106,33 @@ iwd_manager_notify(Iwd_Manager *m)
|
|||
m->notify_job = ecore_job_add(_notify_job_cb, m);
|
||||
}
|
||||
|
||||
const char *
|
||||
iwd_manager_last_error(const Iwd_Manager *m) { return m ? m->last_error : NULL; }
|
||||
|
||||
void
|
||||
iwd_manager_clear_error(Iwd_Manager *m)
|
||||
{
|
||||
if (!m || !m->last_error) return;
|
||||
free(m->last_error);
|
||||
m->last_error = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
iwd_manager_report_error(Iwd_Manager *m, const char *fmt, ...)
|
||||
{
|
||||
if (!m || !fmt) return;
|
||||
char buf[256];
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
/* stderr keeps the dev-visible trail; the stashed copy drives the UI. */
|
||||
fprintf(stderr, "e_iwd: %s\n", buf);
|
||||
free(m->last_error);
|
||||
m->last_error = strdup(buf);
|
||||
iwd_manager_notify(m);
|
||||
}
|
||||
|
||||
/* ----- state aggregation ---------------------------------------------- */
|
||||
|
||||
static void
|
||||
|
|
@ -222,7 +253,14 @@ _on_iface_removed(void *data, const char *path, const char *iface)
|
|||
}
|
||||
|
||||
static void
|
||||
_on_name_appeared(void *data EINA_UNUSED) { /* GetManagedObjects will populate */ }
|
||||
_on_name_appeared(void *data)
|
||||
{
|
||||
/* GetManagedObjects will repopulate adapters/devices/networks; we just
|
||||
* need to re-register our agent against the new iwd instance. Without
|
||||
* this, PSK connects silently hang after `systemctl restart iwd`. */
|
||||
Iwd_Manager *m = data;
|
||||
if (m && m->agent) iwd_agent_register(m->agent);
|
||||
}
|
||||
|
||||
static void
|
||||
_on_name_vanished(void *data)
|
||||
|
|
@ -274,6 +312,7 @@ iwd_manager_free(Iwd_Manager *m)
|
|||
eina_hash_free(m->networks);
|
||||
Listener *li;
|
||||
EINA_LIST_FREE(m->listeners, li) free(li);
|
||||
free(m->last_error);
|
||||
free(m);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,15 @@ void iwd_manager_listener_del (Iwd_Manager *m, Iwd_Manager_Cb cb, void *data);
|
|||
/* Internal: invoked by sub-objects when their state changes. */
|
||||
void iwd_manager_notify (Iwd_Manager *m);
|
||||
|
||||
/* Latest user-facing error string, or NULL. Owned by the manager.
|
||||
* Cleared on next successful state change or by iwd_manager_clear_error. */
|
||||
const char *iwd_manager_last_error (const Iwd_Manager *m);
|
||||
void iwd_manager_clear_error(Iwd_Manager *m);
|
||||
|
||||
/* Stash a one-shot error message and notify listeners. Used by D-Bus reply
|
||||
* callbacks when iwd refuses a Connect/Forget/Set(Powered)/etc. */
|
||||
void iwd_manager_report_error(Iwd_Manager *m, const char *fmt, ...);
|
||||
|
||||
/* The UI installs its passphrase prompt here. The handler must
|
||||
* eventually call iwd_agent_reply()/iwd_agent_cancel() with the request. */
|
||||
void iwd_manager_set_passphrase_handler(Iwd_Manager *m,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
#include "iwd_dbus.h"
|
||||
#include "iwd_props.h"
|
||||
#include "iwd_manager.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
|
@ -80,15 +79,39 @@ iwd_network_free(Iwd_Network *n)
|
|||
free(n);
|
||||
}
|
||||
|
||||
/* Reply context captures the *manager* (which outlives all sub-objects) and
|
||||
* a strdup'd SSID, never the Iwd_Network — the network may disappear from
|
||||
* the next scan before iwd's reply lands, and a raw back-ref would UAF. */
|
||||
typedef struct
|
||||
{
|
||||
Iwd_Manager *m;
|
||||
char *ssid;
|
||||
} _Net_Reply_Ctx;
|
||||
|
||||
static void
|
||||
_on_connect_reply(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EINA_UNUSED)
|
||||
{
|
||||
_Net_Reply_Ctx *ctx = data;
|
||||
const char *en, *em;
|
||||
const char *ssid = data;
|
||||
if (eldbus_message_error_get(msg, &en, &em))
|
||||
fprintf(stderr, "e_iwd: connect to '%s' failed: %s: %s\n",
|
||||
ssid ? ssid : "?", en, em);
|
||||
free(data);
|
||||
if (eldbus_message_error_get(msg, &en, &em) && ctx->m)
|
||||
iwd_manager_report_error(ctx->m,
|
||||
"Connect to '%s' failed: %s",
|
||||
ctx->ssid ? ctx->ssid : "?", em ? em : en);
|
||||
free(ctx->ssid);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
_on_forget_reply(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EINA_UNUSED)
|
||||
{
|
||||
_Net_Reply_Ctx *ctx = data;
|
||||
const char *en, *em;
|
||||
if (eldbus_message_error_get(msg, &en, &em) && ctx->m)
|
||||
iwd_manager_report_error(ctx->m,
|
||||
"Forget '%s' failed: %s",
|
||||
ctx->ssid ? ctx->ssid : "?", em ? em : en);
|
||||
free(ctx->ssid);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
int
|
||||
|
|
@ -104,6 +127,16 @@ iwd_network_signal_tier(const Iwd_Network *n)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static _Net_Reply_Ctx *
|
||||
_reply_ctx_new(Iwd_Network *n)
|
||||
{
|
||||
_Net_Reply_Ctx *ctx = calloc(1, sizeof(*ctx));
|
||||
if (!ctx) return NULL;
|
||||
ctx->m = n->manager;
|
||||
ctx->ssid = n->ssid ? strdup(n->ssid) : NULL;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void
|
||||
iwd_network_connect(Iwd_Network *n)
|
||||
{
|
||||
|
|
@ -111,7 +144,7 @@ iwd_network_connect(Iwd_Network *n)
|
|||
/* Network.Connect() takes no args; iwd will dial the registered Agent
|
||||
* for a passphrase if needed. */
|
||||
eldbus_proxy_call(n->proxy, "Connect", _on_connect_reply,
|
||||
n->ssid ? strdup(n->ssid) : NULL, -1, "");
|
||||
_reply_ctx_new(n), -1, "");
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -124,7 +157,7 @@ iwd_network_forget(Iwd_Network *n)
|
|||
Eldbus_Proxy *kp = eldbus_proxy_get(kobj, IWD_IFACE_KNOWN_NETWORK);
|
||||
if (kp)
|
||||
{
|
||||
eldbus_proxy_call(kp, "Forget", NULL, NULL, -1, "");
|
||||
eldbus_proxy_call(kp, "Forget", _on_forget_reply, _reply_ctx_new(n), -1, "");
|
||||
eldbus_proxy_unref(kp);
|
||||
}
|
||||
eldbus_object_unref(kobj);
|
||||
|
|
|
|||
|
|
@ -10,10 +10,9 @@ e_iwd_sources = [
|
|||
'iwd/iwd_manager.c',
|
||||
'iwd/iwd_device.c',
|
||||
'iwd/iwd_network.c',
|
||||
'ui/wifi_list.c',
|
||||
'iwd/iwd_labels.c',
|
||||
'ui/wifi_auth.c',
|
||||
'ui/wifi_hidden.c',
|
||||
'ui/wifi_status.c',
|
||||
]
|
||||
|
||||
shared_module('module',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "wifi_auth.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct _Auth_Ctx
|
||||
{
|
||||
|
|
@ -12,11 +13,27 @@ typedef struct _Auth_Ctx
|
|||
} Auth_Ctx;
|
||||
|
||||
static void
|
||||
_finish(Auth_Ctx *c, Eina_Bool ok, const char *pass)
|
||||
_finish(Auth_Ctx *c, Eina_Bool ok)
|
||||
{
|
||||
if (c->fired) return;
|
||||
c->fired = EINA_TRUE;
|
||||
|
||||
/* Copy the passphrase into a buffer we own so we can wipe it
|
||||
* after the callback returns. The elm_entry's internal buffer
|
||||
* is then overwritten before the window (and entry) are destroyed. */
|
||||
char *pass = NULL;
|
||||
if (ok && c->entry)
|
||||
{
|
||||
const char *raw = elm_entry_entry_get(c->entry);
|
||||
if (raw) pass = strdup(raw);
|
||||
}
|
||||
if (c->cb) c->cb(c->data, pass, ok);
|
||||
if (pass)
|
||||
{
|
||||
explicit_bzero(pass, strlen(pass));
|
||||
free(pass);
|
||||
}
|
||||
if (c->entry) elm_entry_entry_set(c->entry, "");
|
||||
if (c->win) evas_object_del(c->win);
|
||||
free(c);
|
||||
}
|
||||
|
|
@ -24,21 +41,25 @@ _finish(Auth_Ctx *c, Eina_Bool ok, const char *pass)
|
|||
static void
|
||||
_on_ok(void *data, Evas_Object *o EINA_UNUSED, void *ev EINA_UNUSED)
|
||||
{
|
||||
Auth_Ctx *c = data;
|
||||
_finish(c, EINA_TRUE, elm_entry_entry_get(c->entry));
|
||||
_finish(data, EINA_TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
_on_cancel(void *data, Evas_Object *o EINA_UNUSED, void *ev EINA_UNUSED)
|
||||
{
|
||||
_finish(data, EINA_FALSE, NULL);
|
||||
_finish(data, EINA_FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
_on_del(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *ev EINA_UNUSED)
|
||||
{
|
||||
/* Window closed without cancel/ok — treat as cancel. */
|
||||
_finish(data, EINA_FALSE, NULL);
|
||||
Auth_Ctx *c = data;
|
||||
/* The window (and entry) are being destroyed; null entry to skip the
|
||||
* post-cb entry_set in _finish. */
|
||||
c->win = NULL;
|
||||
c->entry = NULL;
|
||||
_finish(c, EINA_FALSE);
|
||||
}
|
||||
|
||||
Evas_Object *
|
||||
|
|
@ -47,6 +68,7 @@ wifi_auth_prompt(Evas_Object *parent EINA_UNUSED, const char *ssid,
|
|||
Wifi_Auth_Cb cb, void *data)
|
||||
{
|
||||
Auth_Ctx *c = calloc(1, sizeof(*c));
|
||||
if (!c) return NULL;
|
||||
c->cb = cb; c->data = data;
|
||||
|
||||
/* A floating top-level window so the popup is actually visible —
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "wifi_hidden.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct _Hidden_Ctx
|
||||
{
|
||||
|
|
@ -17,9 +18,39 @@ _finish(Hidden_Ctx *c, Eina_Bool ok)
|
|||
{
|
||||
if (c->fired) return;
|
||||
c->fired = EINA_TRUE;
|
||||
const char *ssid = ok ? elm_entry_entry_get(c->e_ssid) : NULL;
|
||||
const char *pass = ok ? elm_entry_entry_get(c->e_pass) : NULL;
|
||||
|
||||
/* Copy SSID + passphrase into buffers we own; wipe the passphrase
|
||||
* (and overwrite the entry) before the window is destroyed. */
|
||||
char *ssid = NULL, *pass = NULL;
|
||||
if (ok)
|
||||
{
|
||||
if (c->e_ssid)
|
||||
{
|
||||
const char *r = elm_entry_entry_get(c->e_ssid);
|
||||
if (r) ssid = strdup(r);
|
||||
}
|
||||
if (c->e_pass)
|
||||
{
|
||||
const char *r = elm_entry_entry_get(c->e_pass);
|
||||
if (r) pass = strdup(r);
|
||||
}
|
||||
}
|
||||
if (c->cb) c->cb(c->data, ssid, pass, ok);
|
||||
if (pass)
|
||||
{
|
||||
explicit_bzero(pass, strlen(pass));
|
||||
free(pass);
|
||||
}
|
||||
/* SSIDs aren't secret, but wiping keeps the heap consistent with the
|
||||
* passphrase handling and avoids leaving identifiable network names in
|
||||
* freed memory after a hidden-network prompt. */
|
||||
if (ssid)
|
||||
{
|
||||
explicit_bzero(ssid, strlen(ssid));
|
||||
free(ssid);
|
||||
}
|
||||
if (c->e_ssid) elm_entry_entry_set(c->e_ssid, "");
|
||||
if (c->e_pass) elm_entry_entry_set(c->e_pass, "");
|
||||
if (c->win) evas_object_del(c->win);
|
||||
free(c);
|
||||
}
|
||||
|
|
@ -42,7 +73,11 @@ _on_cancel(void *data, Evas_Object *o EINA_UNUSED, void *ev EINA_UNUSED)
|
|||
static void
|
||||
_on_del(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *ev EINA_UNUSED)
|
||||
{
|
||||
_finish(data, EINA_FALSE);
|
||||
Hidden_Ctx *c = data;
|
||||
c->win = NULL;
|
||||
c->e_ssid = NULL;
|
||||
c->e_pass = NULL;
|
||||
_finish(c, EINA_FALSE);
|
||||
}
|
||||
|
||||
static Evas_Object *
|
||||
|
|
@ -69,6 +104,7 @@ void
|
|||
wifi_hidden_prompt(Evas_Object *parent EINA_UNUSED, Wifi_Hidden_Cb cb, void *data)
|
||||
{
|
||||
Hidden_Ctx *c = calloc(1, sizeof(*c));
|
||||
if (!c) { if (cb) cb(data, NULL, NULL, EINA_FALSE); return; }
|
||||
c->cb = cb; c->data = data;
|
||||
|
||||
/* Floating top-level so the popup actually shows. */
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
#include "wifi_list.h"
|
||||
|
||||
/* TODO: Genlist of networks, sorted (known first, then signal desc),
|
||||
* with security icon, signal bars, and click → connect/auth flow. */
|
||||
|
||||
Evas_Object *
|
||||
wifi_list_add(Evas_Object *parent)
|
||||
{
|
||||
Evas_Object *gl = elm_genlist_add(parent);
|
||||
return gl;
|
||||
}
|
||||
|
||||
void wifi_list_refresh(Evas_Object *list EINA_UNUSED) { /* TODO */ }
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#ifndef WIFI_LIST_H
|
||||
#define WIFI_LIST_H
|
||||
|
||||
#include <Elementary.h>
|
||||
|
||||
Evas_Object *wifi_list_add(Evas_Object *parent);
|
||||
void wifi_list_refresh(Evas_Object *list);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
#include "wifi_status.h"
|
||||
|
||||
/* TODO: current connection summary widget (SSID, signal, IP, Disconnect). */
|
||||
|
||||
Evas_Object *
|
||||
wifi_status_add(Evas_Object *parent)
|
||||
{
|
||||
Evas_Object *box = elm_box_add(parent);
|
||||
return box;
|
||||
}
|
||||
|
||||
void wifi_status_refresh(Evas_Object *o EINA_UNUSED) { /* TODO */ }
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#ifndef WIFI_STATUS_H
|
||||
#define WIFI_STATUS_H
|
||||
|
||||
#include <Elementary.h>
|
||||
|
||||
Evas_Object *wifi_status_add(Evas_Object *parent);
|
||||
void wifi_status_refresh(Evas_Object *o);
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue