Implemented complete Wi-Fi connection flow with authentication. Phase 4: Connection Management - Created passphrase authentication dialog (ui/wifi_auth.c) - Network selection handler in popup - Integrated agent with passphrase dialog - Complete connection flow: select network → auth dialog → agent → iwd - Support for open and secured (WPA2/WPA3) networks - Proper async D-Bus message handling in agent Features: - Passphrase dialog with password entry widget - Minimum 8-character passphrase validation - Network selection from popup (click to connect) - Open networks connect directly without dialog - Secured networks (psk, 8021x) show auth dialog - Agent stores pending D-Bus message for async reply - Passphrase sent securely to iwd via D-Bus - Memory cleared after passphrase transmission - Cancel button sends proper error reply to iwd - Network type detection (open/psk/8021x) Connection Flow: 1. User clicks network in popup 2. Check security type 3. If open: connect directly 4. If secured: show passphrase dialog 5. User enters passphrase 6. Agent receives RequestPassphrase from iwd 7. Dialog shows for network 8. User clicks Connect 9. Agent sends passphrase to iwd 10. iwd handles connection Agent Improvements: - Stores pending D-Bus message for async reply - Properly returns NULL to indicate async handling - Sends passphrase via eldbus_message_method_return_new - Cancellation sends error reply to iwd - Integrates with UI dialog system Module size: 191KB (increased from 152KB) Total lines: ~2,900 across 20 files Next phase: Advanced Features (hidden networks, multiple adapters, error handling) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
309 lines
7.9 KiB
C
309 lines
7.9 KiB
C
#include "iwd_agent.h"
|
|
#include "iwd_dbus.h"
|
|
#include "../e_mod_main.h"
|
|
|
|
/* Global agent */
|
|
IWD_Agent *iwd_agent = NULL;
|
|
|
|
/* Forward declarations */
|
|
static Eldbus_Message *_agent_request_passphrase(const Eldbus_Service_Interface *iface, const Eldbus_Message *msg);
|
|
static Eldbus_Message *_agent_cancel(const Eldbus_Service_Interface *iface, const Eldbus_Message *msg);
|
|
static Eldbus_Message *_agent_release(const Eldbus_Service_Interface *iface, const Eldbus_Message *msg);
|
|
static void _agent_register_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending);
|
|
static void _agent_unregister(void);
|
|
|
|
/* Agent interface methods */
|
|
static const Eldbus_Method agent_methods[] = {
|
|
{
|
|
"RequestPassphrase", ELDBUS_ARGS({"o", "network"}),
|
|
ELDBUS_ARGS({"s", "passphrase"}),
|
|
_agent_request_passphrase, 0
|
|
},
|
|
{
|
|
"Cancel", ELDBUS_ARGS({"s", "reason"}),
|
|
NULL,
|
|
_agent_cancel, 0
|
|
},
|
|
{
|
|
"Release", NULL, NULL,
|
|
_agent_release, 0
|
|
},
|
|
{ NULL, NULL, NULL, NULL, 0 }
|
|
};
|
|
|
|
/* Agent interface description */
|
|
static const Eldbus_Service_Interface_Desc agent_desc = {
|
|
IWD_AGENT_MANAGER_INTERFACE, agent_methods, NULL, NULL, NULL, NULL
|
|
};
|
|
|
|
/* Initialize agent */
|
|
Eina_Bool
|
|
iwd_agent_init(void)
|
|
{
|
|
Eldbus_Connection *conn;
|
|
Eldbus_Object *obj;
|
|
Eldbus_Proxy *proxy;
|
|
|
|
DBG("Initializing iwd agent");
|
|
|
|
if (iwd_agent)
|
|
{
|
|
WRN("Agent already initialized");
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
conn = iwd_dbus_conn_get();
|
|
if (!conn)
|
|
{
|
|
ERR("No D-Bus connection available");
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
iwd_agent = E_NEW(IWD_Agent, 1);
|
|
if (!iwd_agent)
|
|
{
|
|
ERR("Failed to allocate agent");
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
/* Register D-Bus service interface */
|
|
iwd_agent->iface = eldbus_service_interface_register(conn, IWD_AGENT_PATH, &agent_desc);
|
|
if (!iwd_agent->iface)
|
|
{
|
|
ERR("Failed to register agent interface");
|
|
E_FREE(iwd_agent);
|
|
iwd_agent = NULL;
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
/* Register agent with iwd */
|
|
obj = eldbus_object_get(conn, IWD_SERVICE, IWD_MANAGER_PATH);
|
|
if (!obj)
|
|
{
|
|
ERR("Failed to get iwd manager object");
|
|
eldbus_service_interface_unregister(iwd_agent->iface);
|
|
E_FREE(iwd_agent);
|
|
iwd_agent = NULL;
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
proxy = eldbus_proxy_get(obj, IWD_AGENT_MANAGER_INTERFACE);
|
|
if (!proxy)
|
|
{
|
|
ERR("Failed to get AgentManager proxy");
|
|
eldbus_object_unref(obj);
|
|
eldbus_service_interface_unregister(iwd_agent->iface);
|
|
E_FREE(iwd_agent);
|
|
iwd_agent = NULL;
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
eldbus_proxy_call(proxy, "RegisterAgent", _agent_register_cb, NULL, -1, "o", IWD_AGENT_PATH);
|
|
|
|
eldbus_object_unref(obj);
|
|
|
|
INF("Agent initialized");
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
/* Shutdown agent */
|
|
void
|
|
iwd_agent_shutdown(void)
|
|
{
|
|
DBG("Shutting down iwd agent");
|
|
|
|
if (!iwd_agent) return;
|
|
|
|
_agent_unregister();
|
|
|
|
if (iwd_agent->iface)
|
|
eldbus_service_interface_unregister(iwd_agent->iface);
|
|
|
|
eina_stringshare_del(iwd_agent->pending_network_path);
|
|
eina_stringshare_del(iwd_agent->pending_passphrase);
|
|
|
|
E_FREE(iwd_agent);
|
|
iwd_agent = NULL;
|
|
}
|
|
|
|
/* Set passphrase for pending request and send reply */
|
|
void
|
|
iwd_agent_set_passphrase(const char *passphrase)
|
|
{
|
|
Eldbus_Message *reply;
|
|
|
|
if (!iwd_agent) return;
|
|
if (!iwd_agent->pending_msg)
|
|
{
|
|
WRN("No pending passphrase request");
|
|
return;
|
|
}
|
|
|
|
DBG("Sending passphrase to iwd");
|
|
|
|
/* Create reply message */
|
|
reply = eldbus_message_method_return_new(iwd_agent->pending_msg);
|
|
if (reply)
|
|
{
|
|
eldbus_message_arguments_append(reply, "s", passphrase);
|
|
eldbus_connection_send(eldbus_service_connection_get(iwd_agent->iface),
|
|
reply, NULL, NULL, -1);
|
|
}
|
|
|
|
/* Clear pending request */
|
|
eina_stringshare_del(iwd_agent->pending_network_path);
|
|
iwd_agent->pending_network_path = NULL;
|
|
iwd_agent->pending_msg = NULL;
|
|
|
|
INF("Passphrase sent to iwd");
|
|
}
|
|
|
|
/* Cancel pending request */
|
|
void
|
|
iwd_agent_cancel(void)
|
|
{
|
|
Eldbus_Message *reply;
|
|
|
|
if (!iwd_agent) return;
|
|
|
|
/* Send cancellation reply if there's a pending request */
|
|
if (iwd_agent->pending_msg)
|
|
{
|
|
reply = eldbus_message_error_new(iwd_agent->pending_msg,
|
|
"net.connman.iwd.Agent.Error.Canceled",
|
|
"User cancelled");
|
|
if (reply)
|
|
{
|
|
eldbus_connection_send(eldbus_service_connection_get(iwd_agent->iface),
|
|
reply, NULL, NULL, -1);
|
|
}
|
|
}
|
|
|
|
eina_stringshare_del(iwd_agent->pending_network_path);
|
|
eina_stringshare_del(iwd_agent->pending_passphrase);
|
|
iwd_agent->pending_network_path = NULL;
|
|
iwd_agent->pending_passphrase = NULL;
|
|
iwd_agent->pending_msg = NULL;
|
|
|
|
DBG("Agent request cancelled");
|
|
}
|
|
|
|
/* Agent registration callback */
|
|
static void
|
|
_agent_register_cb(void *data EINA_UNUSED,
|
|
const Eldbus_Message *msg,
|
|
Eldbus_Pending *pending EINA_UNUSED)
|
|
{
|
|
const char *err_name, *err_msg;
|
|
|
|
if (eldbus_message_error_get(msg, &err_name, &err_msg))
|
|
{
|
|
ERR("Failed to register agent: %s: %s", err_name, err_msg);
|
|
return;
|
|
}
|
|
|
|
INF("Agent registered with iwd");
|
|
}
|
|
|
|
/* Unregister agent */
|
|
static void
|
|
_agent_unregister(void)
|
|
{
|
|
Eldbus_Connection *conn;
|
|
Eldbus_Object *obj;
|
|
Eldbus_Proxy *proxy;
|
|
|
|
conn = iwd_dbus_conn_get();
|
|
if (!conn) return;
|
|
|
|
obj = eldbus_object_get(conn, IWD_SERVICE, IWD_MANAGER_PATH);
|
|
if (!obj) return;
|
|
|
|
proxy = eldbus_proxy_get(obj, IWD_AGENT_MANAGER_INTERFACE);
|
|
if (proxy)
|
|
{
|
|
eldbus_proxy_call(proxy, "UnregisterAgent", NULL, NULL, -1, "o", IWD_AGENT_PATH);
|
|
DBG("Agent unregistered from iwd");
|
|
}
|
|
|
|
eldbus_object_unref(obj);
|
|
}
|
|
|
|
/* Request passphrase method */
|
|
static Eldbus_Message *
|
|
_agent_request_passphrase(const Eldbus_Service_Interface *iface EINA_UNUSED,
|
|
const Eldbus_Message *msg)
|
|
{
|
|
const char *network_path;
|
|
IWD_Network *net;
|
|
|
|
if (!eldbus_message_arguments_get(msg, "o", &network_path))
|
|
{
|
|
ERR("Failed to get network path from RequestPassphrase");
|
|
return eldbus_message_error_new(msg, "org.freedesktop.DBus.Error.InvalidArgs", "Invalid arguments");
|
|
}
|
|
|
|
INF("Passphrase requested for network: %s", network_path);
|
|
|
|
/* Store network path and message for later reply */
|
|
eina_stringshare_replace(&iwd_agent->pending_network_path, network_path);
|
|
iwd_agent->pending_msg = msg;
|
|
|
|
/* Find the network */
|
|
net = iwd_network_find(network_path);
|
|
if (!net)
|
|
{
|
|
ERR("Network not found: %s", network_path);
|
|
iwd_agent->pending_msg = NULL;
|
|
return eldbus_message_error_new(msg, "net.connman.iwd.Agent.Error.Canceled", "Network not found");
|
|
}
|
|
|
|
/* Show passphrase dialog - this will eventually call iwd_agent_set_passphrase */
|
|
/* We need to get the instance - for now, use the first one */
|
|
if (iwd_mod && iwd_mod->instances)
|
|
{
|
|
Instance *inst = eina_list_data_get(iwd_mod->instances);
|
|
if (inst)
|
|
{
|
|
extern void wifi_auth_dialog_show(Instance *inst, IWD_Network *net);
|
|
wifi_auth_dialog_show(inst, net);
|
|
}
|
|
}
|
|
|
|
/* Return NULL to indicate we'll reply later (async) */
|
|
return NULL;
|
|
}
|
|
|
|
/* Cancel method */
|
|
static Eldbus_Message *
|
|
_agent_cancel(const Eldbus_Service_Interface *iface EINA_UNUSED,
|
|
const Eldbus_Message *msg)
|
|
{
|
|
const char *reason;
|
|
|
|
if (!eldbus_message_arguments_get(msg, "s", &reason))
|
|
{
|
|
WRN("Cancel called with no reason");
|
|
reason = "unknown";
|
|
}
|
|
|
|
INF("Agent request cancelled: %s", reason);
|
|
|
|
iwd_agent_cancel();
|
|
|
|
/* TODO: Close passphrase dialog if open (Phase 4) */
|
|
|
|
return eldbus_message_method_return_new(msg);
|
|
}
|
|
|
|
/* Release method */
|
|
static Eldbus_Message *
|
|
_agent_release(const Eldbus_Service_Interface *iface EINA_UNUSED,
|
|
const Eldbus_Message *msg)
|
|
{
|
|
INF("Agent released by iwd");
|
|
|
|
iwd_agent_cancel();
|
|
|
|
return eldbus_message_method_return_new(msg);
|
|
}
|