diff --git a/src/e_mod_main.h b/src/e_mod_main.h index 1d7cafb..da354ce 100644 --- a/src/e_mod_main.h +++ b/src/e_mod_main.h @@ -83,6 +83,9 @@ void e_iwd_gadget_shutdown(void); void iwd_popup_new(Instance *inst); void iwd_popup_del(Instance *inst); +/* Auth dialog functions */ +#include "ui/wifi_auth.h" + /* D-Bus functions */ #include "iwd/iwd_dbus.h" #include "iwd/iwd_device.h" diff --git a/src/e_mod_popup.c b/src/e_mod_popup.c index 1c01417..a4f912d 100644 --- a/src/e_mod_popup.c +++ b/src/e_mod_popup.c @@ -5,6 +5,7 @@ static void _popup_comp_del_cb(void *data, Evas_Object *obj); static void _button_rescan_cb(void *data, Evas_Object *obj, void *event_info); static void _button_disconnect_cb(void *data, Evas_Object *obj, void *event_info); static Eina_Bool _popup_reopen_cb(void *data); +static void _network_selected_cb(void *data, Evas_Object *obj, void *event_info); /* Create popup */ void @@ -113,7 +114,7 @@ iwd_popup_new(Instance *inst) net->name, security, net->known ? " *" : ""); - elm_list_item_append(list, item_text, NULL, NULL, NULL, net); + elm_list_item_append(list, item_text, NULL, NULL, _network_selected_cb, net); count++; } } @@ -123,6 +124,8 @@ iwd_popup_new(Instance *inst) elm_list_item_append(list, "No networks found", NULL, NULL, NULL, NULL); } + /* Set select mode to always */ + elm_list_select_mode_set(list, ELM_OBJECT_SELECT_MODE_ALWAYS); elm_list_go(list); elm_object_content_set(frame, list); evas_object_show(list); @@ -236,3 +239,40 @@ _button_disconnect_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info /* Close popup */ iwd_popup_del(inst); } + +/* Network selected callback */ +static void +_network_selected_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + IWD_Network *net = data; + + if (!net || !net->name) + { + DBG("Invalid network selected"); + return; + } + + INF("Network selected: %s (type: %s)", net->name, net->type ? net->type : "unknown"); + + /* Check if network requires authentication */ + if (net->type && (strcmp(net->type, "psk") == 0 || strcmp(net->type, "8021x") == 0)) + { + /* Secured network - need to show auth dialog first */ + /* Get instance from module */ + 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); + } + } + } + else + { + /* Open network - connect directly */ + DBG("Connecting to open network"); + iwd_network_connect(net); + } +} diff --git a/src/iwd/iwd_agent.c b/src/iwd/iwd_agent.c index 55113fe..15cd654 100644 --- a/src/iwd/iwd_agent.c +++ b/src/iwd/iwd_agent.c @@ -126,26 +126,64 @@ iwd_agent_shutdown(void) iwd_agent = NULL; } -/* Set passphrase for pending request */ +/* Set passphrase for pending request and send reply */ void iwd_agent_set_passphrase(const char *passphrase) { - if (!iwd_agent) return; + Eldbus_Message *reply; - eina_stringshare_replace(&iwd_agent->pending_passphrase, passphrase); - DBG("Passphrase set for pending request"); + 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"); } @@ -197,6 +235,7 @@ _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)) { @@ -206,13 +245,33 @@ _agent_request_passphrase(const Eldbus_Service_Interface *iface EINA_UNUSED, INF("Passphrase requested for network: %s", network_path); - /* Store network path for reference */ + /* Store network path and message for later reply */ eina_stringshare_replace(&iwd_agent->pending_network_path, network_path); + iwd_agent->pending_msg = msg; - /* TODO: Show passphrase dialog (Phase 4) */ - /* For now, just return an error to indicate we're not ready */ + /* 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"); + } - return eldbus_message_error_new(msg, "net.connman.iwd.Agent.Error.Canceled", "UI not implemented yet"); + /* 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 */ diff --git a/src/iwd/iwd_agent.h b/src/iwd/iwd_agent.h index 52fc4c8..37df935 100644 --- a/src/iwd/iwd_agent.h +++ b/src/iwd/iwd_agent.h @@ -12,6 +12,7 @@ typedef struct _IWD_Agent Eldbus_Service_Interface *iface; const char *pending_network_path; const char *pending_passphrase; + const Eldbus_Message *pending_msg; /* Stored message to reply to */ } IWD_Agent; /* Global agent */ diff --git a/src/meson.build b/src/meson.build index f674384..1c3dde0 100644 --- a/src/meson.build +++ b/src/meson.build @@ -6,10 +6,10 @@ module_sources = files( 'iwd/iwd_device.c', 'iwd/iwd_network.c', 'iwd/iwd_agent.c', + 'ui/wifi_auth.c', ) -# TODO: Add more source files as they are created in later phases -# Phase 4: ui/wifi_auth.c for passphrase dialog +# All core functionality now implemented module_deps = [ enlightenment, diff --git a/src/ui/wifi_auth.c b/src/ui/wifi_auth.c new file mode 100644 index 0000000..ab72f25 --- /dev/null +++ b/src/ui/wifi_auth.c @@ -0,0 +1,171 @@ +#include "../e_mod_main.h" + +/* Auth dialog structure */ +typedef struct _Auth_Dialog +{ + Instance *inst; + IWD_Network *network; + E_Dialog *dialog; + Evas_Object *entry; + char *passphrase; +} Auth_Dialog; + +/* Global auth dialog (only one at a time) */ +static Auth_Dialog *auth_dialog = NULL; + +/* Forward declarations */ +static void _auth_dialog_ok_cb(void *data, E_Dialog *dialog); +static void _auth_dialog_cancel_cb(void *data, E_Dialog *dialog); +static void _auth_dialog_free(Auth_Dialog *ad); + +/* Show authentication dialog */ +void +wifi_auth_dialog_show(Instance *inst, IWD_Network *net) +{ + Auth_Dialog *ad; + E_Dialog *dia; + Evas_Object *o, *entry; + char buf[512]; + + if (!inst || !net) return; + + /* Only one auth dialog at a time */ + if (auth_dialog) + { + WRN("Auth dialog already open"); + return; + } + + DBG("Showing auth dialog for network: %s", net->name ? net->name : net->path); + + ad = E_NEW(Auth_Dialog, 1); + if (!ad) return; + + ad->inst = inst; + ad->network = net; + auth_dialog = ad; + + /* Create dialog */ + dia = e_dialog_new(NULL, "E", "iwd_passphrase"); + if (!dia) + { + _auth_dialog_free(ad); + return; + } + + ad->dialog = dia; + + e_dialog_title_set(dia, "Wi-Fi Authentication"); + e_dialog_icon_set(dia, "network-wireless", 48); + + /* Message */ + snprintf(buf, sizeof(buf), + "Enter passphrase for network:
" + "%s

" + "Security: %s", + net->name ? net->name : "Unknown", + net->type ? (strcmp(net->type, "psk") == 0 ? "WPA2/WPA3" : net->type) : "Unknown"); + + o = e_widget_label_add(evas_object_evas_get(dia->win), buf); + e_widget_size_min_set(o, 300, 40); + + /* Entry for passphrase */ + entry = e_widget_entry_add(evas_object_evas_get(dia->win), &ad->passphrase, NULL, NULL, NULL); + e_widget_entry_password_set(entry, 1); + e_widget_size_min_set(entry, 280, 30); + + /* Pack into a list */ + Evas_Object *list = e_widget_list_add(evas_object_evas_get(dia->win), 0, 0); + e_widget_list_object_append(list, o, 1, 1, 0.5); + e_widget_list_object_append(list, entry, 1, 1, 0.5); + + e_dialog_content_set(dia, list, 300, 120); + ad->entry = entry; + + /* Buttons */ + e_dialog_button_add(dia, "Connect", NULL, _auth_dialog_ok_cb, ad); + e_dialog_button_add(dia, "Cancel", NULL, _auth_dialog_cancel_cb, ad); + + e_dialog_button_focus_num(dia, 0); + e_dialog_show(dia); + + INF("Auth dialog shown"); +} + +/* OK button callback */ +static void +_auth_dialog_ok_cb(void *data, E_Dialog *dialog EINA_UNUSED) +{ + Auth_Dialog *ad = data; + + if (!ad) return; + + DBG("Auth dialog OK clicked"); + + if (!ad->passphrase || strlen(ad->passphrase) < 8) + { + e_util_dialog_show("Error", + "Passphrase must be at least 8 characters long."); + return; + } + + /* Store passphrase in agent */ + iwd_agent_set_passphrase(ad->passphrase); + + /* Initiate connection */ + if (ad->network) + { + INF("Connecting to network: %s", ad->network->name); + iwd_network_connect(ad->network); + } + + /* Close dialog */ + _auth_dialog_free(ad); +} + +/* Cancel button callback */ +static void +_auth_dialog_cancel_cb(void *data, E_Dialog *dialog EINA_UNUSED) +{ + Auth_Dialog *ad = data; + + DBG("Auth dialog cancelled"); + + /* Cancel agent request */ + iwd_agent_cancel(); + + _auth_dialog_free(ad); +} + +/* Free auth dialog */ +static void +_auth_dialog_free(Auth_Dialog *ad) +{ + if (!ad) return; + + DBG("Freeing auth dialog"); + + if (ad->dialog) + e_object_del(E_OBJECT(ad->dialog)); + + /* Clear passphrase from memory */ + if (ad->passphrase) + { + memset(ad->passphrase, 0, strlen(ad->passphrase)); + E_FREE(ad->passphrase); + } + + E_FREE(ad); + auth_dialog = NULL; +} + +/* Cancel any open auth dialog */ +void +wifi_auth_dialog_cancel(void) +{ + if (auth_dialog) + { + DBG("Cancelling auth dialog from external request"); + _auth_dialog_free(auth_dialog); + } +} diff --git a/src/ui/wifi_auth.h b/src/ui/wifi_auth.h new file mode 100644 index 0000000..07b3121 --- /dev/null +++ b/src/ui/wifi_auth.h @@ -0,0 +1,16 @@ +#ifndef WIFI_AUTH_H +#define WIFI_AUTH_H + +#include + +/* Forward declarations */ +typedef struct _Instance Instance; +typedef struct _IWD_Network IWD_Network; + +/* Show authentication dialog for a network */ +void wifi_auth_dialog_show(Instance *inst, IWD_Network *net); + +/* Cancel/close authentication dialog */ +void wifi_auth_dialog_cancel(void); + +#endif