/* * * Wireless daemon for Linux * * Copyright (C) 2017 Intel Corporation. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #define _XOPEN_SOURCE 700 #include #include #include "command.h" #include "dbus-proxy.h" #include "display.h" #include "network.h" #define IWD_KNOWN_NETWORKS_PATH "/" struct known_network { char *name; char *type; char *last_seen; char *last_connected; }; static void known_network_destroy(void *data) { struct known_network *network = data; l_free(network->last_connected); l_free(network->last_seen); l_free(network->name); l_free(network->type); l_free(network); } static const char *format_iso8601(const char *time_str, const char *format) { struct tm tm; time_t time; static char buf[255]; if (!time_str) return NULL; memset(&tm, 0, sizeof(struct tm)); strptime(time_str, "%FT%TZ", &tm); time = mktime(&tm); strftime(buf, sizeof(buf), format, localtime(&time)); return buf; } static void known_networks_display(struct l_queue *known_networks) { const struct l_queue_entry *entry; display_table_header("Known Networks", " %-*s%-*s%-*s%-*s", 32, "Name", 11, "Security", 19, "Last connected", 17, "Last seen"); if (!l_queue_length(known_networks)) display(MARGIN "No known networks\n"); for (entry = l_queue_get_entries(known_networks); entry; entry = entry->next) { struct known_network *network = entry->data; char *last_connected = l_strdup(format_iso8601(network->last_connected, "%b %e, %l:%M %p")); char *last_seen = l_strdup(format_iso8601(network->last_seen, "%b %e, %l:%M %p")); display(" %-*s%-*s%-*s%-*s" "\n", 32, network->name, 11, network->type, 19, last_connected ? : "-", 17, last_seen ? : "-"); l_free(last_connected); l_free(last_seen); } display_table_footer(); } static void set_name(void *data, struct l_dbus_message_iter *variant) { struct known_network *network = data; const char *value; if (!l_dbus_message_iter_get_variant(variant, "s", &value)) { network->name = NULL; return; } network->name = l_strdup(value); } static void set_type(void *data, struct l_dbus_message_iter *variant) { struct known_network *network = data; const char *value; if (!l_dbus_message_iter_get_variant(variant, "s", &value)) { network->type = NULL; return; } network->type = l_strdup(value); } static void set_last_connected(void *data, struct l_dbus_message_iter *variant) { struct known_network *network = data; const char *value; if (!l_dbus_message_iter_get_variant(variant, "s", &value)) { network->last_connected = NULL; return; } network->last_connected = l_strdup(value); } static void set_last_seen(void *data, struct l_dbus_message_iter *variant) { struct known_network *network = data; const char *value; if (!l_dbus_message_iter_get_variant(variant, "s", &value)) { network->last_seen = NULL; return; } network->last_seen = l_strdup(value); } static const struct proxy_interface_property known_network_properties[] = { { "Name", "s", set_name }, { "Type", "s", set_type }, { "LastConnectedTime", "s", set_last_connected }, { "LastSeenTime", "s", set_last_seen }, { }, }; static void populate_known_network(struct known_network *network, struct l_dbus_message_iter *network_iter) { const char *name; size_t i; struct l_dbus_message_iter variant; while (l_dbus_message_iter_next_entry(network_iter, &name, &variant)) { for (i = 0; known_network_properties[i].name; i++) { if (strcmp(known_network_properties[i].name, name)) continue; if (!known_network_properties[i].set) break; known_network_properties[i].set(network, &variant); break; } } } static void list_networks_callback(struct l_dbus_message *message, void *proxy) { struct l_queue *known_networks = proxy_interface_get_data(proxy); struct l_dbus_message_iter network_iter; struct l_dbus_message_iter iter; if (dbus_message_has_error(message)) return; if (!l_dbus_message_get_arguments(message, "aa{sv}", &iter)) { l_error("Failed to parse 'list known networks' callback " "message"); return; } l_queue_clear(known_networks, known_network_destroy); while (l_dbus_message_iter_next_entry(&iter, &network_iter)) { struct known_network *network = l_new(struct known_network, 1); populate_known_network(network, &network_iter); l_queue_push_tail(known_networks, network); } known_networks_display(known_networks); } static void *known_networks_create(void) { return l_queue_new(); } static void known_networks_destroy(void *data) { struct l_queue *networks = data; l_queue_destroy(networks, known_network_destroy); } static const struct proxy_interface_type_ops known_networks_ops = { .create = known_networks_create, .destroy = known_networks_destroy, }; static struct proxy_interface_type known_networks_interface_type = { .interface = IWD_KNOWN_NETWORKS_INTREFACE, .ops = &known_networks_ops, }; static enum cmd_status cmd_list(const char *entity, char *args) { struct proxy_interface *proxy = proxy_interface_find(IWD_KNOWN_NETWORKS_INTREFACE, IWD_KNOWN_NETWORKS_PATH); if (!proxy) return CMD_STATUS_FAILED; proxy_interface_method_call(proxy, "ListKnownNetworks", "", list_networks_callback); return CMD_STATUS_OK; } static enum cmd_status cmd_forget(const char *entity, char *args) { struct network_args *network_args; const struct l_queue_entry *entry; struct known_network *network = NULL; struct known_network *net; struct l_queue *known_networks; struct l_queue *match; struct proxy_interface *proxy = proxy_interface_find(IWD_KNOWN_NETWORKS_INTREFACE, IWD_KNOWN_NETWORKS_PATH); if (!proxy) return CMD_STATUS_FAILED; network_args = network_parse_args(args); if (!network_args || !network_args->name) { network_args_destroy(network_args); return CMD_STATUS_INVALID_ARGS; } known_networks = proxy_interface_get_data(proxy); match = NULL; for (entry = l_queue_get_entries(known_networks); entry; entry = entry->next) { net = entry->data; if (strcmp(net->name, network_args->name)) continue; if (!match) match = l_queue_new(); l_queue_push_tail(match, net); } if (!match) { display("Invalid network name '%s'\n", network_args->name); network_args_destroy(network_args); return CMD_STATUS_INVALID_VALUE; } if (l_queue_length(match) > 1) { if (!network_args->type) { display("Provided network name is ambiguous. " "Please specify security type.\n"); l_queue_destroy(match, NULL); network_args_destroy(network_args); return CMD_STATUS_INVALID_VALUE; } for (entry = l_queue_get_entries(match); entry; entry = entry->next) { net = entry->data; if (!strcmp(net->type, network_args->type)) { network = net; break; } } } else { network = l_queue_pop_head(match); } l_queue_destroy(match, NULL); network_args_destroy(network_args); if (!network) { display("No network with specified parameters was found\n"); return CMD_STATUS_INVALID_VALUE; } proxy_interface_method_call(proxy, "ForgetNetwork", "ss", NULL, network->name, network->type); return CMD_STATUS_OK; } static const struct command known_networks_commands[] = { { NULL, "list", NULL, cmd_list, "List known networks", true }, { NULL, "forget", "<\"network name\"> [security]", cmd_forget, "Forget known network" }, { } }; static char *family_arg_completion(const char *text, int state) { static size_t index; static size_t len; const char *cmd; if (!state) { index = 0; len = strlen(text); } while ((cmd = known_networks_commands[index].cmd)) { if (known_networks_commands[index++].entity) continue; if (strncmp(cmd, text, len)) continue; return l_strdup(cmd); } return NULL; } static struct command_family known_networks_command_family = { .caption = "Known Networks", .name = "known-networks", .command_list = known_networks_commands, .family_arg_completion = family_arg_completion, }; static int known_networks_command_family_init(void) { command_family_register(&known_networks_command_family); return 0; } static void known_networks_command_family_exit(void) { command_family_unregister(&known_networks_command_family); } COMMAND_FAMILY(known_networks_command_family, known_networks_command_family_init, known_networks_command_family_exit) static int known_networks_interface_init(void) { proxy_interface_type_register(&known_networks_interface_type); return 0; } static void known_networks_interface_exit(void) { proxy_interface_type_unregister(&known_networks_interface_type); } INTERFACE_TYPE(known_networks_interface_type, known_networks_interface_init, known_networks_interface_exit)