X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=client%2Fcommands.c;h=dab62099edd831a16b7f373a23bc9a8177befebe;hb=5a253ba10bddf14fdc5e598aa8e6621ab21a40f8;hp=ffa45ee292ed375ce12a880f224e6d233bc0e619;hpb=5b128587ec25d575a683b9a37e6ac33ce17bd760;p=platform%2Fupstream%2Fconnman.git diff --git a/client/commands.c b/client/commands.c old mode 100644 new mode 100755 index ffa45ee..dab6209 --- a/client/commands.c +++ b/client/commands.c @@ -2,7 +2,7 @@ * * Connection Manager * - * Copyright (C) 2012-2013 Intel Corporation. All rights reserved. + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,8 +28,10 @@ #include #include #include -#include #include +#include +#include +#include #include #include @@ -37,9 +39,32 @@ #include "dbus_helpers.h" #include "input.h" #include "services.h" +#if defined TIZEN_EXT_INS +#include "ins.h" +#endif +#include "tethering.h" +#include "peers.h" #include "commands.h" +#include "agent.h" +#include "vpnconnections.h" +#if defined TIZEN_EXT_WIFI_MESH +#include "mesh.h" +#endif static DBusConnection *connection; +static GHashTable *service_hash; +static GHashTable *vpnconnection_hash; +static GHashTable *peer_hash; +static GHashTable *technology_hash; +static char *session_notify_path; +static char *session_path; +static bool session_connected; + +struct connman_option { + const char *name; + const char val; + const char *desc; +}; static char *ipv4[] = { "Method", @@ -57,11 +82,32 @@ static char *ipv6[] = { NULL }; -static int cmd_help(char *args[], int num, struct option *options); +static int cmd_help(char *args[], int num, struct connman_option *options); + +static bool check_dbus_name(const char *name) +{ + /* + * Valid dbus chars should be [A-Z][a-z][0-9]_ + * and should not start with number. + */ + unsigned int i; + + if (!name || name[0] == '\0') + return false; + + for (i = 0; name[i] != '\0'; i++) + if (!((name[i] >= 'A' && name[i] <= 'Z') || + (name[i] >= 'a' && name[i] <= 'z') || + (name[i] >= '0' && name[i] <= '9') || + name[i] == '_')) + return false; + + return true; +} static int parse_boolean(char *arg) { - if (arg == NULL) + if (!arg) return -1; if (strcasecmp(arg, "no") == 0 || @@ -85,14 +131,14 @@ static int parse_boolean(char *arg) return -1; } -static int parse_args(char *arg, struct option *options) +static int parse_args(char *arg, struct connman_option *options) { int i; - if (arg == NULL) + if (!arg) return -1; - for (i = 0; options[i].name != NULL; i++) { + for (i = 0; options[i].name; i++) { if (strcmp(options[i].name, arg) == 0 || (strncmp(arg, "--", 2) == 0 && strcmp(&arg[2], options[i].name) == 0)) @@ -102,27 +148,38 @@ static int parse_args(char *arg, struct option *options) return '?'; } -static void enable_return(DBusMessageIter *iter, const char *error, +static int enable_return(DBusMessageIter *iter, int errnum, const char *error, void *user_data) { char *tech = user_data; char *str; str = strrchr(tech, '/'); - if (str != NULL) + if (str) str++; else str = tech; - if (error == NULL) { + switch (errnum) { + case 0: fprintf(stdout, "Enabled %s\n", str); - } else + break; + case -ENODEV: + fprintf(stderr, "%s is not available\n", str); + break; + case -EALREADY: + fprintf(stderr, "%s is already enabled\n", str); + break; + default: fprintf(stderr, "Error %s: %s\n", str, error); + } g_free(user_data); + + return 0; } -static int cmd_enable(char *args[], int num, struct option *options) +static int cmd_enable(char *args[], int num, struct connman_option *options) { char *tech; dbus_bool_t b = TRUE; @@ -133,7 +190,10 @@ static int cmd_enable(char *args[], int num, struct option *options) if (num < 2) return -EINVAL; - if (strcmp(args[1], "offlinemode") == 0) { + if (check_dbus_name(args[1]) == false) + return -EINVAL; + + if (strcmp(args[1], "offline") == 0) { tech = g_strdup(args[1]); return __connmanctl_dbus_set_property(connection, "/", "net.connman.Manager", enable_return, tech, @@ -146,27 +206,38 @@ static int cmd_enable(char *args[], int num, struct option *options) "Powered", DBUS_TYPE_BOOLEAN, &b); } -static void disable_return(DBusMessageIter *iter, const char *error, +static int disable_return(DBusMessageIter *iter, int errnum, const char *error, void *user_data) { char *tech = user_data; char *str; str = strrchr(tech, '/'); - if (str != NULL) + if (str) str++; else str = tech; - if (error == NULL) { - fprintf(stdout, "Disabled %s\n", str); - } else + switch (errnum) { + case 0: + fprintf(stdout, "Disable %s\n", str); + break; + case -ENODEV: + fprintf(stderr, "%s is not available\n", str); + break; + case -EALREADY: + fprintf(stderr, "%s is already disabled\n", str); + break; + default: fprintf(stderr, "Error %s: %s\n", str, error); + } g_free(user_data); + + return 0; } -static int cmd_disable(char *args[], int num, struct option *options) +static int cmd_disable(char *args[], int num, struct connman_option *options) { char *tech; dbus_bool_t b = FALSE; @@ -177,7 +248,10 @@ static int cmd_disable(char *args[], int num, struct option *options) if (num < 2) return -EINVAL; - if (strcmp(args[1], "offlinemode") == 0) { + if (check_dbus_name(args[1]) == false) + return -EINVAL; + + if (strcmp(args[1], "offline") == 0) { tech = g_strdup(args[1]); return __connmanctl_dbus_set_property(connection, "/", "net.connman.Manager", disable_return, tech, @@ -190,50 +264,124 @@ static int cmd_disable(char *args[], int num, struct option *options) "Powered", DBUS_TYPE_BOOLEAN, &b); } -static void state_print(DBusMessageIter *iter, const char *error, +static int state_print(DBusMessageIter *iter, int errnum, const char *error, void *user_data) { DBusMessageIter entry; - if (error != NULL) { - fprintf(stderr, "Error: %s", error); - return; + if (error) { + fprintf(stderr, "Error: %s\n", error); + return 0; + } + + dbus_message_iter_recurse(iter, &entry); + __connmanctl_dbus_print(&entry, " ", " = ", "\n"); + fprintf(stdout, "\n"); + + return 0; +} + +static int cmd_state(char *args[], int num, struct connman_option *options) +{ + if (num > 1) + return -E2BIG; + + return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, + CONNMAN_PATH, "net.connman.Manager", "GetProperties", + state_print, NULL, NULL, NULL); +} + +static int clock_print(DBusMessageIter *iter, int errnum, const char *error, + void *user_data) +{ + DBusMessageIter entry; + + if (error) { + fprintf(stderr, "Error: %s\n", error); + return 0; } dbus_message_iter_recurse(iter, &entry); __connmanctl_dbus_print(&entry, " ", " = ", "\n"); fprintf(stdout, "\n"); + + return 0; } -static int cmd_state(char *args[], int num, struct option *options) +static int cmd_clock(char *args[], int num, struct connman_option *options) { if (num > 1) return -E2BIG; - return __connmanctl_dbus_method_call(connection, "/", - "net.connman.Manager", "GetProperties", - state_print, NULL, DBUS_TYPE_INVALID); + return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, + CONNMAN_PATH, "net.connman.Clock", "GetProperties", + clock_print, NULL, NULL, NULL); } -static void services_list(DBusMessageIter *iter, const char *error, +static int services_list(DBusMessageIter *iter, int errnum, const char *error, void *user_data) { - if (error == NULL) { + if (!error) { __connmanctl_services_list(iter); fprintf(stdout, "\n"); } else { fprintf(stderr, "Error: %s\n", error); } + + return 0; } -static void services_properties(DBusMessageIter *iter, const char *error, +#if defined TIZEN_EXT_INS +static int ins_list(DBusMessageIter *iter, const char *error, void *user_data) { + char *filter = user_data; + + if (!error) { + __connmanctl_ins_list(iter, filter); + fprintf(stdout, "\n"); + } else { + fprintf(stderr, "Error: %s\n", error); + } + + g_free(filter); + + return 0; +} +#endif + +static int peers_list(DBusMessageIter *iter, int errnum, + const char *error, void *user_data) +{ + if (!error) { + __connmanctl_peers_list(iter); + fprintf(stdout, "\n"); + } else + fprintf(stderr, "Error: %s\n", error); + + return 0; +} + +static int tethering_clients_list(DBusMessageIter *iter, int errnum, + const char *error, void *user_data) +{ + if (!error) { + __connmanctl_tethering_clients_list(iter); + fprintf(stdout, "\n"); + } else + fprintf(stderr, "Error: %s\n", error); + + return 0; +} + +static int object_properties(DBusMessageIter *iter, int errnum, + const char *error, void *user_data) +{ char *path = user_data; char *str; DBusMessageIter dict; - if (error == NULL) { + if (!error) { fprintf(stdout, "%s\n", path); dbus_message_iter_recurse(iter, &dict); @@ -243,7 +391,7 @@ static void services_properties(DBusMessageIter *iter, const char *error, } else { str = strrchr(path, '/'); - if (str != NULL) + if (str) str++; else str = path; @@ -252,9 +400,11 @@ static void services_properties(DBusMessageIter *iter, const char *error, } g_free(user_data); + + return 0; } -static int cmd_services(char *args[], int num, struct option *options) +static int cmd_services(char *args[], int num, struct connman_option *options) { char *service_name = NULL; char *path; @@ -279,26 +429,75 @@ static int cmd_services(char *args[], int num, struct option *options) break; } - if (service_name == NULL) { - return __connmanctl_dbus_method_call(connection, "/", - "net.connman.Manager", "GetServices", - services_list, NULL, DBUS_TYPE_INVALID); + if (!service_name) { + return __connmanctl_dbus_method_call(connection, + CONNMAN_SERVICE, CONNMAN_PATH, + "net.connman.Manager", "GetServices", + services_list, NULL, NULL, NULL); } + if (check_dbus_name(service_name) == false) + return -EINVAL; + path = g_strdup_printf("/net/connman/service/%s", service_name); - return __connmanctl_dbus_method_call(connection, path, + return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, path, "net.connman.Service", "GetProperties", - services_properties, path, DBUS_TYPE_INVALID); + object_properties, path, NULL, NULL); +} + +#if defined TIZEN_EXT_INS +static int cmd_ins(char *args[], int num, struct connman_option *options) +{ + char *filter = NULL; + + if (num > 2) + return -E2BIG; + + if (num == 2) + filter = g_strdup(args[1]); + + return __connmanctl_dbus_method_call(connection, + CONNMAN_SERVICE, CONNMAN_PATH, + "net.connman.Manager", "GetINS", + ins_list, filter, NULL, NULL); +} +#endif + +static int cmd_peers(char *args[], int num, struct connman_option *options) +{ + char *peer_name = NULL; + char *path; + + if (num > 2) + return -E2BIG; + + if (num == 2) + peer_name = args[1]; + + if (!peer_name) { + return __connmanctl_dbus_method_call(connection, + CONNMAN_SERVICE, CONNMAN_PATH, + "net.connman.Manager", "GetPeers", + peers_list, NULL, NULL, NULL); + } + + if (check_dbus_name(peer_name) == false) + return -EINVAL; + + path = g_strdup_printf("/net/connman/peer/%s", peer_name); + return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, + path, "net.connman.Peer", "GetProperties", + object_properties, path, NULL, NULL); } -static void technology_print(DBusMessageIter *iter, const char *error, +static int technology_print(DBusMessageIter *iter, int errnum, const char *error, void *user_data) { DBusMessageIter array; - if (error != NULL) { + if (error) { fprintf(stderr, "Error: %s\n", error); - return; + return 0; } dbus_message_iter_recurse(iter, &array); @@ -318,787 +517,3526 @@ static void technology_print(DBusMessageIter *iter, const char *error, dbus_message_iter_next(&array); } + + return 0; } -static int cmd_technologies(char *args[], int num, struct option *options) +static int cmd_technologies(char *args[], int num, + struct connman_option *options) { if (num > 1) return -E2BIG; - return __connmanctl_dbus_method_call(connection, "/", - "net.connman.Manager", "GetTechnologies", - technology_print, NULL, DBUS_TYPE_INVALID); + return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, + CONNMAN_PATH, "net.connman.Manager", "GetTechnologies", + technology_print, NULL, NULL, NULL); } -static void scan_return(DBusMessageIter *iter, const char *error, - void *user_data) +struct tether_enable { + char *path; + dbus_bool_t enable; +}; + +static int tether_set_return(DBusMessageIter *iter, int errnum, + const char *error, void *user_data) { - char *path = user_data; + struct tether_enable *tether = user_data; + char *str; - if (error == NULL) { - char *str = strrchr(path, '/'); + str = strrchr(tether->path, '/'); + if (str) str++; - fprintf(stdout, "Scan completed for %s\n", str); + else + str = tether->path; + + if (!error) { + fprintf(stdout, "%s tethering for %s\n", + tether->enable ? "Enabled" : "Disabled", + str); } else - fprintf(stderr, "Error %s: %s", path, error); + fprintf(stderr, "Error %s %s tethering: %s\n", + tether->enable ? + "enabling" : "disabling", str, error); + g_free(tether->path); g_free(user_data); + + return 0; } -static int cmd_scan(char *args[], int num, struct option *options) +static int tether_set(char *technology, int set_tethering) { - char *path; + struct tether_enable *tether = g_new(struct tether_enable, 1); - if (num > 2) - return -E2BIG; + switch(set_tethering) { + case 1: + tether->enable = TRUE; + break; + case 0: + tether->enable = FALSE; + break; + default: + g_free(tether); + return 0; + } - if (num < 2) - return -EINVAL; + tether->path = g_strdup_printf("/net/connman/technology/%s", + technology); - path = g_strdup_printf("/net/connman/technology/%s", args[1]); - return __connmanctl_dbus_method_call(connection, path, - "net.connman.Technology", "Scan", - scan_return, path, DBUS_TYPE_INVALID); + return __connmanctl_dbus_set_property(connection, tether->path, + "net.connman.Technology", tether_set_return, + tether, "Tethering", DBUS_TYPE_BOOLEAN, + &tether->enable); } -static void connect_return(DBusMessageIter *iter, const char *error, - void *user_data) +struct tether_properties { + int ssid_result; + int passphrase_result; + int set_tethering; +}; + +static int tether_update(struct tether_properties *tether) { - char *path = user_data; + int ret; - if (error == NULL) { - char *str = strrchr(path, '/'); - str++; - fprintf(stdout, "Connected %s\n", str); - } else - fprintf(stderr, "Error %s: %s\n", path, error); + if (tether->ssid_result == 0 && tether->passphrase_result == 0) { + ret = tether_set("wifi", tether->set_tethering); + g_free(tether); + return ret; + } - g_free(user_data); + if (tether->ssid_result != -EINPROGRESS && + tether->passphrase_result != -EINPROGRESS) { + g_free(tether); + return 0; + } + + return -EINPROGRESS; } -static int cmd_connect(char *args[], int num, struct option *options) +static int tether_set_ssid_return(DBusMessageIter *iter, int errnum, + const char *error, void *user_data) { - char *path; - - if (num > 2) - return -E2BIG; + struct tether_properties *tether = user_data; - if (num < 2) - return -EINVAL; + if (!error) { + fprintf(stdout, "Wifi SSID set\n"); + tether->ssid_result = 0; + } else { + fprintf(stderr, "Error setting wifi SSID: %s\n", error); + tether->ssid_result = -EINVAL; + } - path = g_strdup_printf("/net/connman/service/%s", args[1]); - return __connmanctl_dbus_method_call(connection, path, - "net.connman.Service", "Connect", - connect_return, path, DBUS_TYPE_INVALID); + return tether_update(tether); } -static void disconnect_return(DBusMessageIter *iter, const char *error, - void *user_data) +static int tether_set_passphrase_return(DBusMessageIter *iter, int errnum, + const char *error, void *user_data) { - char *path = user_data; + struct tether_properties *tether = user_data; - if (error == NULL) { - char *str = strrchr(path, '/'); - str++; - fprintf(stdout, "Disconnected %s\n", str); - } else - fprintf(stderr, "Error %s: %s\n", path, error); + if (!error) { + fprintf(stdout, "Wifi passphrase set\n"); + tether->passphrase_result = 0; + } else { + fprintf(stderr, "Error setting wifi passphrase: %s\n", error); + tether->passphrase_result = -EINVAL; + } - g_free(user_data); + return tether_update(tether); } -static int cmd_disconnect(char *args[], int num, struct option *options) +static int tether_set_ssid(char *ssid, char *passphrase, int set_tethering) { - char *path; + struct tether_properties *tether = g_new(struct tether_properties, 1); + + tether->set_tethering = set_tethering; + + tether->ssid_result = __connmanctl_dbus_set_property(connection, + "/net/connman/technology/wifi", + "net.connman.Technology", + tether_set_ssid_return, tether, + "TetheringIdentifier", DBUS_TYPE_STRING, &ssid); + + tether->passphrase_result =__connmanctl_dbus_set_property(connection, + "/net/connman/technology/wifi", + "net.connman.Technology", + tether_set_passphrase_return, tether, + "TetheringPassphrase", DBUS_TYPE_STRING, &passphrase); + + if (tether->ssid_result != -EINPROGRESS && + tether->passphrase_result != -EINPROGRESS) { + g_free(tether); + return -ENXIO; + } - if (num > 2) - return -E2BIG; + return -EINPROGRESS; +} - if (num < 2) - return -EINVAL; +#if defined TIZEN_EXT_WIFI_MESH +struct mesh_if_prop { + char *ifname; + char *parent_ifname; + char *bridge_ifname; +}; - path = g_strdup_printf("/net/connman/service/%s", args[1]); - return __connmanctl_dbus_method_call(connection, path, - "net.connman.Service", "Disconnect", - disconnect_return, path, DBUS_TYPE_INVALID); +struct mesh_create_network { + char *name; + unsigned int freq; + char *sec_type; +}; + +struct mesh_specific_scan_params { + char *name; + unsigned int freq; +}; + +struct mesh_gate_params { + bool gate_announce; + int hwmp_rootmode; + int stp; +}; + +static int mesh_return(DBusMessageIter *iter, const char *error, + void *user_data) +{ + char *method = user_data; + + if (error) + fprintf(stderr, "Error %s: %s\n", method, error); + else + fprintf(stderr, "Success %s\n", method); + + g_free(method); return 0; } -static void config_return(DBusMessageIter *iter, const char *error, - void *user_data) +static void mesh_interface_add_append(DBusMessageIter *iter, void *user_data) { - char *service_name = user_data; + struct mesh_if_prop *append = user_data; - if (error != NULL) - fprintf(stderr, "Error %s: %s\n", service_name, error); + /* Append Virtual Interface Name */ + __connmanctl_dbus_append_dict_entry(iter, "Ifname", + DBUS_TYPE_STRING, &append->ifname); - g_free(user_data); + /* Append Parent WiFi Interface Name */ + __connmanctl_dbus_append_dict_entry(iter, "ParentIfname", + DBUS_TYPE_STRING, &append->parent_ifname); + + /* Append Bridge Interface Name */ + if (append->bridge_ifname) + __connmanctl_dbus_append_dict_entry(iter, "BridgeIfname", + DBUS_TYPE_STRING, &append->bridge_ifname); } -struct config_append { - char **opts; - int values; -}; +static void mesh_interface_remove_append(DBusMessageIter *iter, void *user_data) +{ + struct mesh_if_prop *append = user_data; -static void config_append_ipv4(DBusMessageIter *iter, - void *user_data) + /* Append Virtual Interface Name */ + __connmanctl_dbus_append_dict_entry(iter, "Ifname", + DBUS_TYPE_STRING, &append->ifname); +} + +static void mesh_create_network_append(DBusMessageIter *iter, void *user_data) { - struct config_append *append = user_data; - char **opts = append->opts; - int i = 0; + struct mesh_create_network *append = user_data; - if (opts == NULL) - return; + /* Append Mesh Network Name */ + __connmanctl_dbus_append_dict_entry(iter, "Name", + DBUS_TYPE_STRING, &append->name); - while (opts[i] != NULL && ipv4[i] != NULL) { - __connmanctl_dbus_append_dict_entry(iter, ipv4[i], - DBUS_TYPE_STRING, &opts[i]); - i++; - } + /* Append Mesh Network Frequency */ + __connmanctl_dbus_append_dict_entry(iter, "Frequency", + DBUS_TYPE_UINT16, &append->freq); - append->values = i; + /* Append Mesh Network Security Type */ + __connmanctl_dbus_append_dict_entry(iter, "Security", + DBUS_TYPE_STRING, &append->sec_type); } -static void config_append_ipv6(DBusMessageIter *iter, void *user_data) +static void mesh_specific_scan_append(DBusMessageIter *iter, void *user_data) { - struct config_append *append = user_data; - char **opts = append->opts; + struct mesh_specific_scan_params *append = user_data; - if (opts == NULL) - return; + /* Append Mesh Network Name */ + __connmanctl_dbus_append_dict_entry(iter, "Name", + DBUS_TYPE_STRING, &append->name); - append->values = 1; + /* Append Mesh Network Frequency */ + __connmanctl_dbus_append_dict_entry(iter, "Frequency", + DBUS_TYPE_UINT16, &append->freq); +} - if (g_strcmp0(opts[0], "auto") == 0) { - char *str; +static void mesh_set_gate_append(DBusMessageIter *iter, void *user_data) +{ + struct mesh_gate_params *append = user_data; - switch (parse_boolean(opts[1])) { - case 0: - append->values = 2; + /* Append Gate Announce Protocol */ + __connmanctl_dbus_append_dict_entry(iter, "GateAnnounce", + DBUS_TYPE_BOOLEAN, &append->gate_announce); - str = "disabled"; - __connmanctl_dbus_append_dict_entry(iter, "Privacy", - DBUS_TYPE_STRING, &str); - break; + /* Append HWMP Root Mode */ + __connmanctl_dbus_append_dict_entry(iter, "HWMPRootMode", + DBUS_TYPE_UINT16, &append->hwmp_rootmode); - case 1: - append->values = 2; + /* Append STP */ + __connmanctl_dbus_append_dict_entry(iter, "STP", DBUS_TYPE_UINT16, + &append->stp); +} - str = "enabled"; - __connmanctl_dbus_append_dict_entry(iter, "Privacy", - DBUS_TYPE_STRING, &str); - break; +static void mesh_peer_append(DBusMessageIter *iter, void *user_data) +{ + char *peer_addr = user_data; - default: - if (opts[1] != NULL) { - append->values = 2; + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &peer_addr); - if (g_strcmp0(opts[0], "prefered") != 0) { - fprintf(stderr, "Error %s: %s\n", - opts[1], - strerror(-EINVAL)); - return; - } + g_free(peer_addr); +} - str = "prefered"; - __connmanctl_dbus_append_dict_entry(iter, - "Privacy", DBUS_TYPE_STRING, - &str); - } - break; - } - } else if (g_strcmp0(opts[0], "manual") == 0) { - int i = 1; +static int mesh_peers_list(DBusMessageIter *iter, + const char *error, void *user_data) +{ + if (!error) { + __connmanctl_mesh_peers_list(iter); + fprintf(stdout, "\n"); + } else + fprintf(stderr, "Error: %s\n", error); - while (opts[i] != NULL && ipv6[i] != NULL) { - if (i == 2) { - int value = atoi(opts[i]); - __connmanctl_dbus_append_dict_entry(iter, - ipv6[i], DBUS_TYPE_BYTE, - &value); - } else { - __connmanctl_dbus_append_dict_entry(iter, - ipv6[i], DBUS_TYPE_STRING, - &opts[i]); - } - i++; - } + return 0; +} - append->values = i; +static int connected_mesh_peers_list(DBusMessageIter *iter, + const char *error, void *user_data) +{ + if (!error) { + __connmanctl_mesh_connected_peers_list(iter); + fprintf(stdout, "\n"); + } else + fprintf(stderr, "Error: %s\n", error); - } else if (g_strcmp0(opts[0], "off") != 0) { - fprintf(stderr, "Error %s: %s\n", opts[0], strerror(-EINVAL)); + return 0; +} - return; - } +static int disconnected_mesh_peers_list(DBusMessageIter *iter, + const char *error, void *user_data) +{ + if (!error) { + __connmanctl_mesh_disconnected_peers_list(iter); + fprintf(stdout, "\n"); + } else + fprintf(stderr, "Error: %s\n", error); - __connmanctl_dbus_append_dict_entry(iter, "Method", DBUS_TYPE_STRING, - &opts[0]); + return 0; } -static void config_append_str(DBusMessageIter *iter, void *user_data) +static int mesh_connect_return(DBusMessageIter *iter, const char *error, + void *user_data) { - struct config_append *append = user_data; - char **opts = append->opts; - int i = 0; + char *path = user_data; - if (opts == NULL) - return; + if (!error) { + char *str = strrchr(path, '/'); + str++; + fprintf(stdout, "Connected %s\n", str); + } else + fprintf(stderr, "Error %s: %s\n", path, error); - while (opts[i] != NULL) { - dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, - &opts[i]); - i++; - } + g_free(user_data); - append->values = i; + return 0; } -static void append_servers(DBusMessageIter *iter, void *user_data) +static int mesh_disconnect_return(DBusMessageIter *iter, const char *error, + void *user_data) { - struct config_append *append = user_data; - char **opts = append->opts; - int i = 1; + char *path = user_data; - if (opts == NULL) - return; + if (!error) { + char *str = strrchr(path, '/'); + str++; + fprintf(stdout, "Disconnected %s\n", str); + } else + fprintf(stderr, "Error %s: %s\n", path, error); - while (opts[i] != NULL && g_strcmp0(opts[i], "--excludes") != 0) { - dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, - &opts[i]); - i++; - } + g_free(user_data); - append->values = i; + return 0; } -static void append_excludes(DBusMessageIter *iter, void *user_data) +static int mesh_remove_return(DBusMessageIter *iter, const char *error, + void *user_data) { - struct config_append *append = user_data; - char **opts = append->opts; - int i = append->values; + char *path = user_data; - if (opts == NULL || opts[i] == NULL || - g_strcmp0(opts[i], "--excludes") != 0) - return; + if (!error) { + char *str = strrchr(path, '/'); + str++; + fprintf(stdout, "Removed %s\n", str); + } else + fprintf(stderr, "Error %s: %s\n", path, error); - i++; - while (opts[i] != NULL) { - dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, - &opts[i]); - i++; - } + g_free(user_data); - append->values = i; + return 0; } -static void config_append_proxy(DBusMessageIter *iter, void *user_data) +static int mesh_config_return(DBusMessageIter *iter, const char *error, + void *user_data) { - struct config_append *append = user_data; - char **opts = append->opts; + char *path = user_data; + char *str = strrchr(path, '/'); + str++; - if (opts == NULL) - return; + if (error) + fprintf(stderr, "Error %s: %s\n", path, error); + else + fprintf(stdout, "Success SetProperty %s\n", str); - if (g_strcmp0(opts[0], "manual") == 0) { - __connmanctl_dbus_append_dict_string_array(iter, "Servers", - append_servers, append); + g_free(user_data); - __connmanctl_dbus_append_dict_string_array(iter, "Excludes", - append_excludes, append); + return 0; +} - } else if (g_strcmp0(opts[0], "auto") == 0) { - if (opts[1] != NULL) { - __connmanctl_dbus_append_dict_entry(iter, "URL", - DBUS_TYPE_STRING, &opts[1]); - append->values++; +static int cmd_mesh(char *args[], int num, struct connman_option *options) +{ + int result = 0; + int c; + char *path = NULL; + char *method = NULL; + char *mesh_peer_name = NULL; + char *mesh_peer_path = NULL; + char *property = NULL; + char *value = NULL; + struct mesh_if_prop *append; + struct mesh_create_network *network; + struct mesh_specific_scan_params *scan_params; + struct mesh_gate_params *gate_params; + char *mesh_peer_addr = NULL; + + c = parse_args(args[1], options); + + switch (c) { + case 'a': + if (num < 4 || num > 5) { + result = -EINVAL; + break; } + path = g_strdup_printf("/net/connman/technology/mesh"); + + append = dbus_malloc0(sizeof(struct mesh_if_prop)); + append->ifname = g_strdup(args[2]); + append->parent_ifname = g_strdup(args[3]); + if (num == 5) + append->bridge_ifname = g_strdup(args[4]); + method = g_strdup("MeshInterfaceAdd"); + result = __connmanctl_dbus_mesh_dict(connection, path, + "net.connman.Technology", mesh_return, method, + "MeshInterfaceAdd", DBUS_TYPE_STRING, + mesh_interface_add_append, append); + g_free(append->ifname); + g_free(append->parent_ifname); + g_free(append->bridge_ifname); + g_free(append); + break; - } else if (g_strcmp0(opts[0], "direct") != 0) - return; + case 'r': + if (num != 3) { + result = -EINVAL; + break; + } + path = g_strdup_printf("/net/connman/technology/mesh"); + + append = dbus_malloc0(sizeof(struct mesh_if_prop)); + append->ifname = g_strdup(args[2]); + method = g_strdup("MeshInterfaceRemove"); + result = __connmanctl_dbus_mesh_dict(connection, path, + "net.connman.Technology", mesh_return, method, + "MeshInterfaceRemove", DBUS_TYPE_STRING, + mesh_interface_remove_append, append); + g_free(append->ifname); + g_free(append); + break; - __connmanctl_dbus_append_dict_entry(iter, "Method",DBUS_TYPE_STRING, - &opts[0]); + case 'p': + if (num > 3) { + result = -E2BIG; + break; + } - append->values++; -} + if (num == 3) + mesh_peer_name = args[2]; -static int cmd_config(char *args[], int num, struct option *options) -{ - int result = 0, res = 0, index = 2, oldindex = 0; - int c; - char *service_name, *path; - char **opt_start; - dbus_bool_t val; - struct config_append append; + if (!mesh_peer_name) { + result = __connmanctl_dbus_method_call(connection, + CONNMAN_SERVICE, CONNMAN_PATH, + "net.connman.Manager", "GetMeshPeers", + mesh_peers_list, NULL, NULL, NULL); + break; + } - service_name = args[1]; - if (service_name == NULL) - return -EINVAL; + if (check_dbus_name(mesh_peer_name) == false) { + result = -EINVAL; + break; + } - while (index < num && args[index] != NULL) { - c = parse_args(args[index], options); - opt_start = &args[index + 1]; - append.opts = opt_start; - append.values = 0; + mesh_peer_path = g_strdup_printf("/net/connman/mesh/%s", + mesh_peer_name); + result = __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, + mesh_peer_path, "net.connman.Mesh", "GetProperties", + object_properties, mesh_peer_path, NULL, NULL); + break; - res = 0; + case 'c': + if (num < 3) { + result = -EINVAL; + break; + } - oldindex = index; - path = g_strdup_printf("/net/connman/service/%s", service_name); + if (num > 3) { + result = -E2BIG; + break; + } - switch (c) { - case 'a': - switch (parse_boolean(*opt_start)) { - case 1: - val = TRUE; - break; - case 0: - val = FALSE; - break; - default: - res = -EINVAL; - break; - } + mesh_peer_name = args[2]; - index++; + if (check_dbus_name(mesh_peer_name) == false) { + result = -EINVAL; + break; + } - if (res == 0) { - res = __connmanctl_dbus_set_property(connection, - path, "net.connman.Service", - config_return, - g_strdup(service_name), - "AutoConnect", - DBUS_TYPE_BOOLEAN, &val); - } + mesh_peer_path = g_strdup_printf("/net/connman/mesh/%s", + mesh_peer_name); + result = __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, + mesh_peer_path, "net.connman.Mesh", "Connect", + mesh_connect_return, mesh_peer_path, NULL, NULL); + break; + + case 'd': + if (num < 3) { + result = -EINVAL; break; - case 'i': - res = __connmanctl_dbus_set_property_dict(connection, - path, "net.connman.Service", - config_return, g_strdup(service_name), - "IPv4.Configuration", DBUS_TYPE_STRING, - config_append_ipv4, &append); - index += append.values; + } + + if (num > 3) { + result = -E2BIG; break; + } - case 'v': - res = __connmanctl_dbus_set_property_dict(connection, - path, "net.connman.Service", - config_return, g_strdup(service_name), - "IPv6.Configuration", DBUS_TYPE_STRING, - config_append_ipv6, &append); - index += append.values; + mesh_peer_name = args[2]; + + if (check_dbus_name(mesh_peer_name) == false) { + result = -EINVAL; break; + } - case 'n': - res = __connmanctl_dbus_set_property_array(connection, - path, "net.connman.Service", - config_return, g_strdup(service_name), - "Nameservers.Configuration", - DBUS_TYPE_STRING, config_append_str, - &append); - index += append.values; + mesh_peer_path = g_strdup_printf("/net/connman/mesh/%s", + mesh_peer_name); + result = __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, + mesh_peer_path, "net.connman.Mesh", "Disconnect", + mesh_disconnect_return, mesh_peer_path, NULL, NULL); + break; + + case 'f': + if (num < 3) { + result = -EINVAL; break; + } - case 't': - res = __connmanctl_dbus_set_property_array(connection, - path, "net.connman.Service", - config_return, g_strdup(service_name), - "Timeservers.Configuration", - DBUS_TYPE_STRING, config_append_str, - &append); - index += append.values; + if (num > 3) { + result = -E2BIG; break; + } - case 'd': - res = __connmanctl_dbus_set_property_array(connection, - path, "net.connman.Service", - config_return, g_strdup(service_name), - "Domains.Configuration", - DBUS_TYPE_STRING, config_append_str, - &append); - index += append.values; + mesh_peer_name = args[2]; + + if (check_dbus_name(mesh_peer_name) == false) { + result = -EINVAL; break; + } - case 'x': - res = __connmanctl_dbus_set_property_dict(connection, - path, "net.connman.Service", - config_return, g_strdup(service_name), - "Proxy.Configuration", - DBUS_TYPE_STRING, config_append_proxy, - &append); - index += append.values; + mesh_peer_path = g_strdup_printf("/net/connman/mesh/%s", + mesh_peer_name); + result = __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, + mesh_peer_path, "net.connman.Mesh", "Remove", + mesh_remove_return, mesh_peer_path, NULL, NULL); + break; + + case 'C': + if (num > 2) { + result = -E2BIG; break; - case 'r': - res = __connmanctl_dbus_method_call(connection, - path, "net.connman.Service", "Remove", - config_return, g_strdup(service_name), - DBUS_TYPE_INVALID); + } + + result = __connmanctl_dbus_method_call(connection, + CONNMAN_SERVICE, CONNMAN_PATH, + "net.connman.Manager", "GetConnectedMeshPeers", + connected_mesh_peers_list, NULL, NULL, NULL); + break; + + case 'D': + if (num > 2) { + result = -E2BIG; break; - default: - res = -EINVAL; + } + + result = __connmanctl_dbus_method_call(connection, + CONNMAN_SERVICE, CONNMAN_PATH, + "net.connman.Manager", + "GetDisconnectedMeshPeers", + disconnected_mesh_peers_list, NULL, NULL, NULL); + break; + + case 'n': + if (num != 5) { + result = -EINVAL; break; } + path = g_strdup_printf("/net/connman/technology/mesh"); + + network = dbus_malloc0(sizeof(struct mesh_create_network)); + network->name = g_strdup(args[2]); + network->freq = atoi(args[3]); + network->sec_type = g_strdup(args[4]); + method = g_strdup("MeshCreateNetwork"); + result = __connmanctl_dbus_mesh_dict(connection, path, + "net.connman.Technology", mesh_return, method, + "MeshCreateNetwork", DBUS_TYPE_STRING, + mesh_create_network_append, network); + g_free(network->name); + g_free(network->sec_type); + g_free(network); + break; - g_free(path); + case 'A': + if (num != 2) { + result = -EINVAL; + break; + } + path = g_strdup_printf("/net/connman/technology/mesh"); - if (res < 0) { + method = g_strdup("AbortScan"); + result = __connmanctl_dbus_mesh_dict(connection, path, + "net.connman.Technology", mesh_return, method, + "AbortScan", DBUS_TYPE_STRING, + NULL, NULL); + break; + + case 'S': + if (num != 4) { + result = -EINVAL; + break; + } + path = g_strdup_printf("/net/connman/technology/mesh"); + + scan_params = dbus_malloc0(sizeof(struct mesh_specific_scan_params)); + scan_params->name = g_strdup(args[2]); + scan_params->freq = atoi(args[3]); + method = g_strdup("MeshSpecificScan"); + result = __connmanctl_dbus_mesh_dict(connection, path, + "net.connman.Technology", mesh_return, method, + "MeshSpecificScan", DBUS_TYPE_STRING, + mesh_specific_scan_append, scan_params); + g_free(scan_params->name); + g_free(scan_params); + break; + + case 'P': + if (num != 5) { + result = -EINVAL; + break; + } + + mesh_peer_name = args[2]; + property = args[3]; + value = args[4]; + + if (check_dbus_name(mesh_peer_name) == false) { + result = -EINVAL; + break; + } + + mesh_peer_path = g_strdup_printf("/net/connman/mesh/%s", + mesh_peer_name); + + if (g_strcmp0(property, "Passphrase") == 0) { + result = __connmanctl_dbus_set_property(connection, + mesh_peer_path, "net.connman.Mesh", + mesh_config_return, mesh_peer_path, property, + DBUS_TYPE_STRING, &value); + } else { + printf("Invalid property %s\n", property); + result = -EINVAL; + } + + break; + + case 'G': + if (num != 5) { + result = -EINVAL; + break; + } + + path = g_strdup_printf("/net/connman/technology/mesh"); + + gate_params = dbus_malloc0(sizeof(struct mesh_gate_params)); + gate_params->gate_announce = atoi(args[2]); + gate_params->hwmp_rootmode = atoi(args[3]); + gate_params->stp = atoi(args[4]); + + method = g_strdup("SetMeshGate"); + + result = __connmanctl_dbus_mesh_dict(connection, path, + "net.connman.Technology", mesh_return, method, + "SetMeshGate", DBUS_TYPE_STRING, + mesh_set_gate_append, gate_params); + + break; + + case 'z': + if (num != 3) { + result = -EINVAL; + break; + } + + mesh_peer_addr = g_strdup(args[2]); + method = g_strdup("MeshAddPeer"); + + result = __connmanctl_dbus_method_call(connection, + CONNMAN_SERVICE, CONNMAN_PATH, + "net.connman.Manager", "MeshAddPeer", + mesh_return, method, mesh_peer_append, + mesh_peer_addr); + + break; + + case 'y': + if (num != 3) { + result = -EINVAL; + break; + } + + mesh_peer_addr = g_strdup(args[2]); + method = g_strdup("MeshRemovePeer"); + + result = __connmanctl_dbus_method_call(connection, + CONNMAN_SERVICE, CONNMAN_PATH, + "net.connman.Manager", "MeshRemovePeer", + mesh_return, method, mesh_peer_append, + mesh_peer_addr); + + break; + + default: + result = -EINVAL; + break; + } + + g_free(path); + + if (result < 0) { + if (result != -EINPROGRESS) + printf("Error '%s': %s\n", args[1], strerror(-result)); + } + + + return result; +} +#endif + +static int cmd_tether(char *args[], int num, struct connman_option *options) +{ + char *ssid, *passphrase; + int set_tethering; + + if (num < 3) + return -EINVAL; + + passphrase = args[num - 1]; + ssid = args[num - 2]; + + set_tethering = parse_boolean(args[2]); + + if (strcmp(args[1], "wifi") == 0) { + + if (num > 5) + return -E2BIG; + + if (num == 5 && set_tethering == -1) + return -EINVAL; + + if (num == 4) + set_tethering = -1; + + if (num > 3) + return tether_set_ssid(ssid, passphrase, set_tethering); + } + + if (num > 3) + return -E2BIG; + + if (set_tethering == -1) + return -EINVAL; + + if (check_dbus_name(args[1]) == false) + return -EINVAL; + + return tether_set(args[1], set_tethering); +} + +static int cmd_tethering_clients(char *args[], int num, struct connman_option *options) +{ + if (num > 1) + return -E2BIG; + + return __connmanctl_dbus_method_call(connection, + CONNMAN_SERVICE, CONNMAN_PATH, + "net.connman.Manager", "GetTetheringClients", + tethering_clients_list, NULL, NULL, NULL); +} + +static int scan_return(DBusMessageIter *iter, int ernnum, const char *error, + void *user_data) +{ + char *path = user_data; + + if (!error) { + char *str = strrchr(path, '/'); + str++; + fprintf(stdout, "Scan completed for %s\n", str); + } else + fprintf(stderr, "Error %s: %s\n", path, error); + + g_free(user_data); + + return 0; +} + +static int cmd_scan(char *args[], int num, struct connman_option *options) +{ + char *path; + + if (num > 2) + return -E2BIG; + + if (num < 2) + return -EINVAL; + + if (check_dbus_name(args[1]) == false) + return -EINVAL; + + path = g_strdup_printf("/net/connman/technology/%s", args[1]); + return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, path, + "net.connman.Technology", "Scan", + scan_return, path, NULL, NULL); +} + +static int connect_return(DBusMessageIter *iter, int errnum, + const char *error, void *user_data) +{ + char *path = user_data; + + if (!error) { + char *str = strrchr(path, '/'); + str++; + fprintf(stdout, "Connected %s\n", str); + } else + fprintf(stderr, "Error %s: %s\n", path, error); + + g_free(user_data); + + return 0; +} + +static int cmd_connect(char *args[], int num, struct connman_option *options) +{ + const char *iface = "net.connman.Service"; + char *path; + + if (num > 2) + return -E2BIG; + + if (num < 2) + return -EINVAL; + + if (check_dbus_name(args[1]) == false) + return -EINVAL; + + if (g_strstr_len(args[1], 5, "peer_") == args[1]) { + iface = "net.connman.Peer"; + path = g_strdup_printf("/net/connman/peer/%s", args[1]); + } else + path = g_strdup_printf("/net/connman/service/%s", args[1]); + + return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, path, + iface, "Connect", connect_return, path, NULL, NULL); +} + +static int disconnect_return(DBusMessageIter *iter, int errnum, + const char *error, void *user_data) +{ + char *path = user_data; + + if (!error) { + char *str = strrchr(path, '/'); + str++; + fprintf(stdout, "Disconnected %s\n", str); + } else + fprintf(stderr, "Error %s: %s\n", path, error); + + g_free(user_data); + + return 0; +} + +static int cmd_disconnect(char *args[], int num, struct connman_option *options) +{ + const char *iface = "net.connman.Service"; + char *path; + + if (num > 2) + return -E2BIG; + + if (num < 2) + return -EINVAL; + + if (check_dbus_name(args[1]) == false) + return -EINVAL; + + if (g_strstr_len(args[1], 5, "peer_") == args[1]) { + iface = "net.connman.Peer"; + path = g_strdup_printf("/net/connman/peer/%s", args[1]); + } else + path = g_strdup_printf("/net/connman/service/%s", args[1]); + + return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, + path, iface, "Disconnect", + disconnect_return, path, NULL, NULL); +} + +struct move_service { + char *service; + char *target; +}; + +static int move_before_return(DBusMessageIter *iter, int errnum, + const char *error, void *user_data) +{ + struct move_service *services = user_data; + char *service; + char *target; + + if (!error) { + service = strrchr(services->service, '/'); + service++; + target = strrchr(services->target, '/'); + target++; + fprintf(stdout, "Moved %s before %s\n", service, target); + } else + fprintf(stderr, "Error %s: %s\n", services->service, error); + + g_free(services->service); + g_free(services->target); + g_free(user_data); + + return 0; +} + +static void move_before_append_args(DBusMessageIter *iter, void *user_data) +{ + char *path = user_data; + + dbus_message_iter_append_basic(iter, + DBUS_TYPE_OBJECT_PATH, &path); +} + +static int cmd_service_move_before(char *args[], int num, + struct connman_option *options) +{ + const char *iface = "net.connman.Service"; + struct move_service *services; + + if (num > 3) + return -E2BIG; + + if (num < 3) + return -EINVAL; + + if (check_dbus_name(args[1]) == false) + return -EINVAL; + + services = g_new(struct move_service, 1); + + services->service = g_strdup_printf("/net/connman/service/%s", args[1]); + services->target = g_strdup_printf("/net/connman/service/%s", args[2]); + + return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, + services->service, iface, "MoveBefore", + move_before_return, services, + move_before_append_args, + services->target); +} + +static int move_after_return(DBusMessageIter *iter, int errnum, + const char *error, void *user_data) +{ + struct move_service *services = user_data; + char *service; + char *target; + + if (!error) { + service = strrchr(services->service, '/'); + service++; + target = strrchr(services->target, '/'); + target++; + fprintf(stdout, "Moved %s after %s\n", service, target); + } else + fprintf(stderr, "Error %s: %s\n", services->service, error); + + g_free(services->service); + g_free(services->target); + g_free(user_data); + + return 0; +} + +static void move_after_append_args(DBusMessageIter *iter, void *user_data) +{ + char *path = user_data; + + dbus_message_iter_append_basic(iter, + DBUS_TYPE_OBJECT_PATH, &path); +} + +static int cmd_service_move_after(char *args[], int num, + struct connman_option *options) +{ + const char *iface = "net.connman.Service"; + struct move_service *services; + + if (num > 3) + return -E2BIG; + + if (num < 3) + return -EINVAL; + + if (check_dbus_name(args[1]) == false) + return -EINVAL; + + services = g_new(struct move_service, 1); + + services->service = g_strdup_printf("/net/connman/service/%s", args[1]); + services->target = g_strdup_printf("/net/connman/service/%s", args[2]); + + return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, + services->service, iface, "MoveAfter", + move_after_return, services, + move_after_append_args, + services->target); +} + +static int config_return(DBusMessageIter *iter, int errnum, + const char *error, void *user_data) +{ + char *service_name = user_data; + + if (error) + fprintf(stderr, "Error %s: %s\n", service_name, error); + + g_free(user_data); + + return 0; +} + +struct config_append { + char **opts; + int values; +}; + +struct session_options { + char **args; + int num; + char *notify_path; + struct connman_option *options; +}; + +static void config_append_ipv4(DBusMessageIter *iter, + void *user_data) +{ + struct config_append *append = user_data; + char **opts = append->opts; + int i = 0; + + if (!opts) + return; + + while (opts[i] && ipv4[i]) { + __connmanctl_dbus_append_dict_entry(iter, ipv4[i], + DBUS_TYPE_STRING, &opts[i]); + i++; + } + + append->values = i; +} + +static void config_append_ipv6(DBusMessageIter *iter, void *user_data) +{ + struct config_append *append = user_data; + char **opts = append->opts; + + if (!opts) + return; + + append->values = 1; + + if (g_strcmp0(opts[0], "auto") == 0) { + char *str; + + switch (parse_boolean(opts[1])) { + case 0: + append->values = 2; + + str = "disabled"; + __connmanctl_dbus_append_dict_entry(iter, "Privacy", + DBUS_TYPE_STRING, &str); + break; + + case 1: + append->values = 2; + + str = "enabled"; + __connmanctl_dbus_append_dict_entry(iter, "Privacy", + DBUS_TYPE_STRING, &str); + break; + + default: + if (opts[1]) { + append->values = 2; + + if (g_strcmp0(opts[1], "prefered") != 0 && + g_strcmp0(opts[1], + "preferred") != 0) { + fprintf(stderr, "Error %s: %s\n", + opts[1], + strerror(EINVAL)); + return; + } + + str = "prefered"; + __connmanctl_dbus_append_dict_entry(iter, + "Privacy", DBUS_TYPE_STRING, + &str); + } + break; + } + } else if (g_strcmp0(opts[0], "manual") == 0) { + int i = 1; + + while (opts[i] && ipv6[i]) { + if (i == 2) { + int value = atoi(opts[i]); + __connmanctl_dbus_append_dict_entry(iter, + ipv6[i], DBUS_TYPE_BYTE, + &value); + } else { + __connmanctl_dbus_append_dict_entry(iter, + ipv6[i], DBUS_TYPE_STRING, + &opts[i]); + } + i++; + } + + append->values = i; + + } else if (g_strcmp0(opts[0], "off") != 0) { + fprintf(stderr, "Error %s: %s\n", opts[0], strerror(EINVAL)); + + return; + } + + __connmanctl_dbus_append_dict_entry(iter, "Method", DBUS_TYPE_STRING, + &opts[0]); +} + +static void config_append_str(DBusMessageIter *iter, void *user_data) +{ + struct config_append *append = user_data; + char **opts = append->opts; + int i = 0; + + if (!opts) + return; + + while (opts[i]) { + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, + &opts[i]); + i++; + } + + append->values = i; +} + +static void append_servers(DBusMessageIter *iter, void *user_data) +{ + struct config_append *append = user_data; + char **opts = append->opts; + int i = 1; + + if (!opts) + return; + + while (opts[i] && g_strcmp0(opts[i], "--excludes") != 0) { + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, + &opts[i]); + i++; + } + + append->values = i; +} + +static void append_excludes(DBusMessageIter *iter, void *user_data) +{ + struct config_append *append = user_data; + char **opts = append->opts; + int i = append->values; + + if (!opts || !opts[i] || + g_strcmp0(opts[i], "--excludes") != 0) + return; + + i++; + while (opts[i]) { + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, + &opts[i]); + i++; + } + + append->values = i; +} + +static void config_append_proxy(DBusMessageIter *iter, void *user_data) +{ + struct config_append *append = user_data; + char **opts = append->opts; + + if (!opts) + return; + + if (g_strcmp0(opts[0], "manual") == 0) { + __connmanctl_dbus_append_dict_string_array(iter, "Servers", + append_servers, append); + + __connmanctl_dbus_append_dict_string_array(iter, "Excludes", + append_excludes, append); + + } else if (g_strcmp0(opts[0], "auto") == 0) { + if (opts[1]) { + __connmanctl_dbus_append_dict_entry(iter, "URL", + DBUS_TYPE_STRING, &opts[1]); + append->values++; + } + + } else if (g_strcmp0(opts[0], "direct") != 0) + return; + + __connmanctl_dbus_append_dict_entry(iter, "Method",DBUS_TYPE_STRING, + &opts[0]); + + append->values++; +} + +static int cmd_config(char *args[], int num, struct connman_option *options) +{ + int result = 0, res = 0, index = 2, oldindex = 0; + int c; + char *service_name, *path; + char **opt_start; + dbus_bool_t val; + struct config_append append; + + service_name = args[1]; + if (!service_name) + return -EINVAL; + + if (check_dbus_name(service_name) == false) + return -EINVAL; + + while (index < num && args[index]) { + c = parse_args(args[index], options); + opt_start = &args[index + 1]; + append.opts = opt_start; + append.values = 0; + + res = 0; + + oldindex = index; + path = g_strdup_printf("/net/connman/service/%s", service_name); + + switch (c) { + case 'a': + switch (parse_boolean(*opt_start)) { + case 1: + val = TRUE; + break; + case 0: + val = FALSE; + break; + default: + res = -EINVAL; + break; + } + + index++; + + if (res == 0) { + res = __connmanctl_dbus_set_property(connection, + path, "net.connman.Service", + config_return, + g_strdup(service_name), + "AutoConnect", + DBUS_TYPE_BOOLEAN, &val); + } + break; + case 'i': + res = __connmanctl_dbus_set_property_dict(connection, + path, "net.connman.Service", + config_return, g_strdup(service_name), + "IPv4.Configuration", DBUS_TYPE_STRING, + config_append_ipv4, &append); + index += append.values; + break; + + case 'v': + res = __connmanctl_dbus_set_property_dict(connection, + path, "net.connman.Service", + config_return, g_strdup(service_name), + "IPv6.Configuration", DBUS_TYPE_STRING, + config_append_ipv6, &append); + index += append.values; + break; + + case 'n': + res = __connmanctl_dbus_set_property_array(connection, + path, "net.connman.Service", + config_return, g_strdup(service_name), + "Nameservers.Configuration", + DBUS_TYPE_STRING, config_append_str, + &append); + index += append.values; + break; + + case 't': + res = __connmanctl_dbus_set_property_array(connection, + path, "net.connman.Service", + config_return, g_strdup(service_name), + "Timeservers.Configuration", + DBUS_TYPE_STRING, config_append_str, + &append); + index += append.values; + break; + + case 'd': + res = __connmanctl_dbus_set_property_array(connection, + path, "net.connman.Service", + config_return, g_strdup(service_name), + "Domains.Configuration", + DBUS_TYPE_STRING, config_append_str, + &append); + index += append.values; + break; + + case 'x': + res = __connmanctl_dbus_set_property_dict(connection, + path, "net.connman.Service", + config_return, g_strdup(service_name), + "Proxy.Configuration", + DBUS_TYPE_STRING, config_append_proxy, + &append); + index += append.values; + break; + case 'r': + res = __connmanctl_dbus_method_call(connection, + CONNMAN_SERVICE, path, + "net.connman.Service", "Remove", + config_return, g_strdup(service_name), + NULL, NULL); + break; + + case 'm': + switch (parse_boolean(*opt_start)) { + case 1: + val = TRUE; + break; + case 0: + val = FALSE; + break; + default: + res = -EINVAL; + break; + } + if (res == 0) { + res = __connmanctl_dbus_set_property(connection, + path, "net.connman.Service", + config_return, + g_strdup(service_name), + "mDNS.Configuration", + DBUS_TYPE_BOOLEAN, &val); + } + index++; + break; + + default: + res = -EINVAL; + break; + } + + g_free(path); + + if (res < 0) { if (res == -EINPROGRESS) result = -EINPROGRESS; else - printf("Error '%s': %s\n", args[oldindex], - strerror(-res)); - } else - index += res; + printf("Error '%s': %s\n", args[oldindex], + strerror(-res)); + } else + index += res; + + index++; + } + + return result; +} + +static DBusHandlerResult monitor_changed(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + DBusMessageIter iter; + const char *interface, *path; + + interface = dbus_message_get_interface(message); + if (!interface) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (strncmp(interface, "net.connman.", 12) != 0) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (!strcmp(interface, "net.connman.Agent") || + !strcmp(interface, "net.connman.vpn.Agent") || + !strcmp(interface, "net.connman.Session") || + !strcmp(interface, "net.connman.Notification")) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + interface = strrchr(interface, '.'); + if (interface && *interface != '\0') + interface++; + + path = strrchr(dbus_message_get_path(message), '/'); + if (path && *path != '\0') + path++; + + __connmanctl_save_rl(); + + if (dbus_message_is_signal(message, "net.connman.Manager", + "ServicesChanged")) { + + fprintf(stdout, "%-12s %-20s = {\n", interface, + "ServicesChanged"); + dbus_message_iter_init(message, &iter); + __connmanctl_services_list(&iter); + fprintf(stdout, "\n}\n"); + + __connmanctl_redraw_rl(); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } else if (dbus_message_is_signal(message, "net.connman.Manager", + "PeersChanged")) { + fprintf(stdout, "%-12s %-20s = {\n", interface, + "PeersChanged"); + dbus_message_iter_init(message, &iter); + __connmanctl_peers_list(&iter); + fprintf(stdout, "\n}\n"); + + __connmanctl_redraw_rl(); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } else if (dbus_message_is_signal(message, "net.connman.vpn.Manager", + "ConnectionAdded") || + dbus_message_is_signal(message, + "net.connman.vpn.Manager", + "ConnectionRemoved")) { + interface = "vpn.Manager"; + path = dbus_message_get_member(message); + + } else if (dbus_message_is_signal(message, "net.connman.Manager", + "TechnologyAdded") || + dbus_message_is_signal(message, "net.connman.Manager", + "TechnologyRemoved")) + path = dbus_message_get_member(message); + + fprintf(stdout, "%-12s %-20s ", interface, path); + dbus_message_iter_init(message, &iter); + + __connmanctl_dbus_print(&iter, "", " = ", " = "); + fprintf(stdout, "\n"); + + __connmanctl_redraw_rl(); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static struct { + char *interface; + bool enabled; +} monitor[] = { + { "Service", false }, + { "Technology", false }, + { "Manager", false }, + { "vpn.Manager", false }, + { "vpn.Connection", false }, + { NULL, }, +}; + +static void monitor_add(char *interface) +{ + bool add_filter = true, found = false; + int i; + char *rule; + DBusError err; + + for (i = 0; monitor[i].interface; i++) { + if (monitor[i].enabled == true) + add_filter = false; + + if (g_strcmp0(interface, monitor[i].interface) == 0) { + if (monitor[i].enabled == true) + return; + + monitor[i].enabled = true; + found = true; + } + } + + if (found == false) + return; + + if (add_filter == true) + dbus_connection_add_filter(connection, monitor_changed, + NULL, NULL); + + dbus_error_init(&err); + rule = g_strdup_printf("type='signal',interface='net.connman.%s'", + interface); + dbus_bus_add_match(connection, rule, &err); + g_free(rule); + + if (dbus_error_is_set(&err)) + fprintf(stderr, "Error: %s\n", err.message); +} + +static void monitor_del(char *interface) +{ + bool del_filter = true, found = false; + int i; + char *rule; + + + for (i = 0; monitor[i].interface; i++) { + if (g_strcmp0(interface, monitor[i].interface) == 0) { + if (monitor[i].enabled == false) + return; + + monitor[i].enabled = false; + found = true; + } + + if (monitor[i].enabled == true) + del_filter = false; + } + + if (found == false) + return; + + rule = g_strdup_printf("type='signal',interface='net.connman.%s'", + interface); + dbus_bus_remove_match(connection, rule, NULL); + g_free(rule); + + if (del_filter == true) + dbus_connection_remove_filter(connection, monitor_changed, + NULL); +} + +static int cmd_monitor(char *args[], int num, struct connman_option *options) +{ + bool add = true; + int c; + + if (num > 3) + return -E2BIG; + + if (num == 3) { + switch (parse_boolean(args[2])) { + case 0: + add = false; + break; + + default: + break; + } + } + + c = parse_args(args[1], options); + switch (c) { + case -1: + monitor_add("Service"); + monitor_add("Technology"); + monitor_add("Manager"); + monitor_add("vpn.Manager"); + monitor_add("vpn.Connection"); + break; + + case 's': + if (add == true) + monitor_add("Service"); + else + monitor_del("Service"); + break; + + case 'c': + if (add == true) + monitor_add("Technology"); + else + monitor_del("Technology"); + break; + + case 'm': + if (add == true) + monitor_add("Manager"); + else + monitor_del("Manager"); + break; + + case 'M': + if (add == true) + monitor_add("vpn.Manager"); + else + monitor_del("vpn.Manager"); + break; + + case 'C': + if (add == true) + monitor_add("vpn.Connection"); + else + monitor_del("vpn.Connection"); + break; + + default: + switch(parse_boolean(args[1])) { + case 0: + monitor_del("Service"); + monitor_del("Technology"); + monitor_del("Manager"); + monitor_del("vpn.Manager"); + monitor_del("vpn.Connection"); + break; + + case 1: + monitor_add("Service"); + monitor_add("Technology"); + monitor_add("Manager"); + monitor_add("vpn.Manager"); + monitor_add("vpn.Connection"); + break; + + default: + return -EINVAL; + } + } + + if (add == true) + return -EINPROGRESS; + + return 0; +} + +static int cmd_agent(char *args[], int num, struct connman_option *options) +{ + if (!__connmanctl_is_interactive()) { + fprintf(stderr, "Error: Not supported in non-interactive " + "mode\n"); + return 0; + } + + if (num > 2) + return -E2BIG; + + if (num < 2) + return -EINVAL; + + switch(parse_boolean(args[1])) { + case 0: + __connmanctl_agent_unregister(connection); + break; + + case 1: + if (__connmanctl_agent_register(connection) == -EINPROGRESS) + return -EINPROGRESS; + + break; + + default: + return -EINVAL; + break; + } + + return 0; +} + +static int vpnconnections_properties(DBusMessageIter *iter, int errnum, + const char *error, void *user_data) +{ + char *path = user_data; + char *str; + DBusMessageIter dict; + + if (!error) { + fprintf(stdout, "%s\n", path); + + dbus_message_iter_recurse(iter, &dict); + __connmanctl_dbus_print(&dict, " ", " = ", "\n"); + + fprintf(stdout, "\n"); + + } else { + str = strrchr(path, '/'); + if (str) + str++; + else + str = path; + + fprintf(stderr, "Error %s: %s\n", str, error); + } + + g_free(user_data); + + return 0; +} + +static int vpnconnections_list(DBusMessageIter *iter, int errnum, + const char *error, void *user_data) +{ + if (!error) + __connmanctl_vpnconnections_list(iter); + else + fprintf(stderr, "Error: %s\n", error); + + return 0; +} + +static int cmd_vpnconnections(char *args[], int num, + struct connman_option *options) +{ + char *vpnconnection_name, *path; + + if (num > 2) + return -E2BIG; + + vpnconnection_name = args[1]; + + if (!vpnconnection_name) + return __connmanctl_dbus_method_call(connection, + VPN_SERVICE, VPN_PATH, + "net.connman.vpn.Manager", "GetConnections", + vpnconnections_list, NULL, + NULL, NULL); + + if (check_dbus_name(vpnconnection_name) == false) + return -EINVAL; + + path = g_strdup_printf("/net/connman/vpn/connection/%s", + vpnconnection_name); + return __connmanctl_dbus_method_call(connection, VPN_SERVICE, path, + "net.connman.vpn.Connection", "GetProperties", + vpnconnections_properties, path, NULL, NULL); + +} + +static int cmd_vpnagent(char *args[], int num, struct connman_option *options) +{ + if (!__connmanctl_is_interactive()) { + fprintf(stderr, "Error: Not supported in non-interactive " + "mode\n"); + return 0; + } + + if (num > 2) + return -E2BIG; + + if (num < 2) + return -EINVAL; + + switch(parse_boolean(args[1])) { + case 0: + __connmanctl_vpn_agent_unregister(connection); + break; + + case 1: + if (__connmanctl_vpn_agent_register(connection) == + -EINPROGRESS) + return -EINPROGRESS; + + break; + + default: + return -EINVAL; + break; + } + + return 0; +} + +static DBusMessage *session_release(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + __connmanctl_save_rl(); + + fprintf(stdout, "Session %s released\n", session_path); + + __connmanctl_redraw_rl(); + + g_free(session_path); + session_path = NULL; + session_connected = false; + + return g_dbus_create_reply(message, DBUS_TYPE_INVALID); +} + +static DBusMessage *session_update(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + DBusMessageIter iter, dict; + + __connmanctl_save_rl(); + + fprintf(stdout, "Session Update = {\n"); + + dbus_message_iter_init(message, &iter); + dbus_message_iter_recurse(&iter, &dict); + + __connmanctl_dbus_print(&dict, "", " = ", "\n"); + fprintf(stdout, "\n}\n"); + + dbus_message_iter_recurse(&iter, &dict); + + while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter entry, variant; + char *field, *state; + + dbus_message_iter_recurse(&dict, &entry); + + dbus_message_iter_get_basic(&entry, &field); + + if (dbus_message_iter_get_arg_type(&entry) + == DBUS_TYPE_STRING + && !strcmp(field, "State")) { + + dbus_message_iter_next(&entry); + dbus_message_iter_recurse(&entry, &variant); + if (dbus_message_iter_get_arg_type(&variant) + != DBUS_TYPE_STRING) + break; + + dbus_message_iter_get_basic(&variant, &state); + + if (!session_connected && (!strcmp(state, "connected") + || !strcmp(state, "online"))) { + + fprintf(stdout, "Session %s connected\n", + session_path); + session_connected = true; + + break; + } + + if (!strcmp(state, "disconnected") && + session_connected) { + + fprintf(stdout, "Session %s disconnected\n", + session_path); + session_connected = false; + } + break; + } + + dbus_message_iter_next(&dict); + } + + __connmanctl_redraw_rl(); + + return g_dbus_create_reply(message, DBUS_TYPE_INVALID); +} + +static const GDBusMethodTable notification_methods[] = { + { GDBUS_METHOD("Release", NULL, NULL, session_release) }, + { GDBUS_METHOD("Update", GDBUS_ARGS({"settings", "a{sv}"}), + NULL, session_update) }, + { }, +}; + +static int session_notify_add(const char *path) +{ + if (session_notify_path) + return 0; + + if (!g_dbus_register_interface(connection, path, + "net.connman.Notification", + notification_methods, NULL, NULL, + NULL, NULL)) { + fprintf(stderr, "Error: Failed to register VPN Agent " + "callbacks\n"); + return -EIO; + } + + session_notify_path = g_strdup(path); + + return 0; +} + +static void session_notify_remove(void) +{ + if (!session_notify_path) + return; + + g_dbus_unregister_interface(connection, session_notify_path, + "net.connman.Notification"); + + g_free(session_notify_path); + session_notify_path = NULL; +} + +static int session_connect_cb(DBusMessageIter *iter, int errnum, + const char *error, void *user_data) +{ + if (error) { + fprintf(stderr, "Error: %s\n", error); + return 0; + } + + return -EINPROGRESS; +} + + +static int session_connect(void) +{ + return __connmanctl_dbus_method_call(connection, "net.connman", + session_path, "net.connman.Session", "Connect", + session_connect_cb, NULL, NULL, NULL); +} + +static int session_disconnect_cb(DBusMessageIter *iter, int errnum, + const char *error, void *user_data) +{ + if (error) + fprintf(stderr, "Error: %s\n", error); + + return 0; +} + +static int session_disconnect(void) +{ + return __connmanctl_dbus_method_call(connection, "net.connman", + session_path, "net.connman.Session", "Disconnect", + session_disconnect_cb, NULL, NULL, NULL); +} + +static int session_create_cb(DBusMessageIter *iter, int errnum, + const char *error, void *user_data) +{ + gboolean connect = GPOINTER_TO_INT(user_data); + char *str; + + if (error) { + fprintf(stderr, "Error creating session: %s", error); + session_notify_remove(); + return 0; + } + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_OBJECT_PATH) { + fprintf(stderr, "Error creating session: No session path\n"); + return -EINVAL; + } + + g_free(session_path); + + dbus_message_iter_get_basic(iter, &str); + session_path = g_strdup(str); + + fprintf(stdout, "Session %s created\n", session_path); + + if (connect) + return session_connect(); + + return -EINPROGRESS; +} + +static void session_config_append_array(DBusMessageIter *iter, + void *user_data) +{ + struct config_append *append = user_data; + char **opts = append->opts; + int i = 1; + + if (!opts) + return; + + while (opts[i] && strncmp(opts[i], "--", 2) != 0) { + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, + &opts[i]); + i++; + } + + append->values = i; +} + +static void session_create_append_dict(DBusMessageIter *iter, void *user_data) +{ + struct session_options *args_struct = user_data; + int index = 0, res = 0; + struct config_append append; + char c; + char *ifname; + dbus_bool_t source_ip_rule; + + while (index < args_struct->num && args_struct->args[index]) { + append.opts = &args_struct->args[index]; + append.values = 0; + + c = parse_args(args_struct->args[index], args_struct->options); + + switch (c) { + case 'b': + __connmanctl_dbus_append_dict_string_array(iter, "AllowedBearers", + session_config_append_array, + &append); + break; + case 't': + if (! args_struct->args[index + 1]) { + res = -EINVAL; + break; + } + __connmanctl_dbus_append_dict_entry(iter, "ConnectionType", + DBUS_TYPE_STRING, + &args_struct->args[index + 1]); + append.values = 2; + break; + case 'i': + if (index + 1 < args_struct->num) + ifname = args_struct->args[index + 1]; + else + ifname = ""; + __connmanctl_dbus_append_dict_entry(iter, "AllowedInterface", + DBUS_TYPE_STRING, + &ifname); + append.values = 2; + break; + case 's': + if (! args_struct->args[index + 1]) { + res = -EINVAL; + break; + } + switch (parse_boolean( args_struct->args[index + 1])) { + case 1: + source_ip_rule = TRUE; + break; + case 0: + source_ip_rule = FALSE; + break; + default: + res = -EINVAL; + break; + } + __connmanctl_dbus_append_dict_entry(iter, "SourceIPRule", + DBUS_TYPE_BOOLEAN, + &source_ip_rule); + append.values = 2; + break; + case 'c': + if (!args_struct->args[index + 1]) { + res = -EINVAL; + break; + } + __connmanctl_dbus_append_dict_entry(iter, "ContextIdentifier", + DBUS_TYPE_STRING, + &args_struct->args[index + 1]); + append.values = 2; + break; + default: + res = -EINVAL; + } + + if (res < 0 && res != -EINPROGRESS) { + printf("Error '%s': %s\n", args_struct->args[index], + strerror(-res)); + return; + } + + index += append.values; + } +} + +static void session_create_append(DBusMessageIter *iter, void *user_data) +{ + struct session_options *args_struct = user_data; + + __connmanctl_dbus_append_dict(iter, session_create_append_dict, + args_struct); + + dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, + &args_struct->notify_path); +} + +static int session_create(gboolean connect, char *args[], int num, + struct connman_option *options) +{ + int res; + char *notify_path; + struct session_options args_struct; + args_struct.args = args; + args_struct.num = num; + args_struct.options = options; + + notify_path = g_strdup_printf("/net/connman/connmanctl%d", getpid()); + session_notify_add(notify_path); + args_struct.notify_path = notify_path; + + res = __connmanctl_dbus_method_call(connection, "net.connman", "/", + "net.connman.Manager", "CreateSession", + session_create_cb, GINT_TO_POINTER(connect), + session_create_append, &args_struct); + + g_free(notify_path); + + if (res < 0 && res != -EINPROGRESS) + session_notify_remove(); + + return res; +} + +static int session_destroy_cb(DBusMessageIter *iter, int errnum, + const char *error, void *user_data) +{ + if (error) { + fprintf(stderr, "Error destroying session: %s", error); + return 0; + } + + fprintf(stdout, "Session %s ended\n", session_path); + + g_free(session_path); + session_path = NULL; + session_connected = false; + + return 0; +} + +static void session_destroy_append(DBusMessageIter *iter, void *user_data) +{ + const char *path = user_data; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); +} + +static int session_destroy(void) +{ + return __connmanctl_dbus_method_call(connection, "net.connman", "/", + "net.connman.Manager", "DestroySession", + session_destroy_cb, NULL, + session_destroy_append, session_path); +} + +static int session_config_return(DBusMessageIter *iter, int errnum, + const char *error, void *user_data) +{ + char *property_name = user_data; + + if (error) + fprintf(stderr, "Error setting session %s: %s\n", + property_name, error); + + return 0; +} + +static int session_config(char *args[], int num, + struct connman_option *options) +{ + int index = 0, res = 0; + struct config_append append; + char c; + char *ifname; + dbus_bool_t source_ip_rule; + + while (index < num && args[index]) { + append.opts = &args[index]; + append.values = 0; + + c = parse_args(args[index], options); + + switch (c) { + case 'b': + res = __connmanctl_dbus_session_change_array(connection, + session_path, session_config_return, + "AllowedBearers", "AllowedBearers", + session_config_append_array, &append); + break; + case 't': + if (!args[index + 1]) { + res = -EINVAL; + break; + } + + res = __connmanctl_dbus_session_change(connection, + session_path, session_config_return, + "ConnectionType", "ConnectionType", + DBUS_TYPE_STRING, &args[index + 1]); + append.values = 2; + break; + case 'i': + if (index + 1 < num) + ifname = args[index + 1]; + else + ifname = ""; + + res = __connmanctl_dbus_session_change(connection, + session_path, session_config_return, + "AllowedInterface", "AllowedInterface", + DBUS_TYPE_STRING, &ifname); + append.values = 2; + break; + case 's': + if (!args[index + 1]) { + res = -EINVAL; + break; + } + switch (parse_boolean(args[index + 1])) { + case 1: + source_ip_rule = TRUE; + break; + case 0: + source_ip_rule = FALSE; + break; + default: + res = -EINVAL; + break; + } + + res = __connmanctl_dbus_session_change(connection, + session_path, session_config_return, + "SourceIPRule", "SourceIPRule", + DBUS_TYPE_BOOLEAN, &source_ip_rule); + append.values = 2; + break; + case 'c': + if (!args[index + 1]) { + res = -EINVAL; + break; + } + + res = __connmanctl_dbus_session_change(connection, + session_path, session_config_return, + "ctxid", "ctxid", DBUS_TYPE_STRING, + &args[index + 1]); + append.values = 2; + break; + + default: + res = -EINVAL; + } + + if (res < 0 && res != -EINPROGRESS) { + printf("Error '%s': %s\n", args[index], + strerror(-res)); + return 0; + } + + index += append.values; + } + + return 0; +} + +static int cmd_session(char *args[], int num, struct connman_option *options) +{ + char *command; + + if (num < 2) + return -EINVAL; + + command = args[1]; + + switch(parse_boolean(command)) { + case 0: + if (!session_path) + return -EALREADY; + return session_destroy(); + + case 1: + if (session_path) + return -EALREADY; + return session_create(FALSE, &args[2], num - 2, options); + + default: + if (!strcmp(command, "connect")) { + if (!session_path) + return session_create(TRUE, &args[2], num - 2, + options); + + return session_connect(); + + } else if (!strcmp(command, "disconnect")) { + + if (!session_path) { + fprintf(stdout, "Session does not exist\n"); + return 0; + } + + return session_disconnect(); + } else if (!strcmp(command, "config")) { + if (!session_path) { + fprintf(stdout, "Session does not exist\n"); + return 0; + } + + if (num == 2) + return -EINVAL; + + return session_config(&args[2], num - 2, options); + } + + } + + return -EINVAL; +} + +static int cmd_exit(char *args[], int num, struct connman_option *options) +{ + return 1; +} + +static char *lookup_key_from_table(GHashTable *hash, const char *text, + int state) +{ + static int len = 0; + static GHashTableIter iter; + gpointer key, value; + + if (state == 0) { + g_hash_table_iter_init(&iter, hash); + len = strlen(text); + } + + while (g_hash_table_iter_next(&iter, &key, &value)) + if (strncmp(text, key, len) == 0) + return strdup(key); + + return NULL; +} + +static char *lookup_service_arg(const char *text, int state) +{ + if (__connmanctl_input_calc_level() > 1) { + __connmanctl_input_lookup_end(); + return NULL; + } + + return lookup_key_from_table(service_hash, text, state); +} + +static char *lookup_peer(const char *text, int state) +{ + static GHashTableIter iter; + gpointer key, value; + static int len = 0; + + if (state == 0) { + g_hash_table_iter_init(&iter, peer_hash); + len = strlen(text); + } + + while (g_hash_table_iter_next(&iter, &key, &value)) { + const char *peer = key; + if (strncmp(text, peer, len) == 0) + return strdup(peer); + } + + return NULL; +} + +static char *lookup_peer_arg(const char *text, int state) +{ + if (__connmanctl_input_calc_level() > 1) { + __connmanctl_input_lookup_end(); + return NULL; + } + + return lookup_peer(text, state); +} + +static char *lookup_technology(const char *text, int state) +{ + static int len = 0; + static GHashTableIter iter; + gpointer key, value; + + if (state == 0) { + g_hash_table_iter_init(&iter, technology_hash); + len = strlen(text); + } + + while (g_hash_table_iter_next(&iter, &key, &value)) { + const char *technology = key; + if (strncmp(text, technology, len) == 0) + return strdup(technology); + } + + return NULL; +} + +static char *lookup_technology_arg(const char *text, int state) +{ + if (__connmanctl_input_calc_level() > 1) { + __connmanctl_input_lookup_end(); + return NULL; + } + + return lookup_technology(text, state); +} + +static char *lookup_technology_offline(const char *text, int state) +{ + static int len = 0; + static bool end = false; + char *str; + + if (__connmanctl_input_calc_level() > 1) { + __connmanctl_input_lookup_end(); + return NULL; + } + + if (state == 0) { + len = strlen(text); + end = false; + } + + if (end) + return NULL; + + str = lookup_technology(text, state); + if (str) + return str; + + end = true; + + if (strncmp(text, "offline", len) == 0) + return strdup("offline"); + + return NULL; +} + +static char *lookup_on_off(const char *text, int state) +{ + char *onoff[] = { "on", "off", NULL }; + static int idx = 0; + static int len = 0; + + char *str; + + if (!state) { + idx = 0; + len = strlen(text); + } + + while (onoff[idx]) { + str = onoff[idx]; + idx++; + + if (!strncmp(text, str, len)) + return strdup(str); + } + + return NULL; +} + +static char *lookup_tether(const char *text, int state) +{ + int level; + + level = __connmanctl_input_calc_level(); + if (level < 2) + return lookup_technology(text, state); + + if (level == 2) + return lookup_on_off(text, state); + + __connmanctl_input_lookup_end(); + + return NULL; +} + +static char *lookup_agent(const char *text, int state) +{ + if (__connmanctl_input_calc_level() > 1) { + __connmanctl_input_lookup_end(); + return NULL; + } + + return lookup_on_off(text, state); +} + +static char *lookup_vpnconnection_arg(const char *text, int state) +{ + if (__connmanctl_input_calc_level() > 1) { + __connmanctl_input_lookup_end(); + return NULL; + } + + return lookup_key_from_table(vpnconnection_hash, text, state); +} + +static struct connman_option service_options[] = { + {"properties", 'p', "[] (obsolete)"}, + { NULL, } +}; + +#if defined TIZEN_EXT_INS +static struct connman_option ins_options[] = { + {"all", 'a', ""}, + {"filter-ssid", 's', "ssid"}, + {"filter-name", 'n', "[]"}, + { NULL, } +}; +#endif + +static struct connman_option config_options[] = { + {"nameservers", 'n', " [] []"}, + {"timeservers", 't', " [] [...]"}, + {"domains", 'd', " [] [...]"}, + {"mdns", 'm', "yes|no"}, + {"ipv6", 'v', "off|auto [enable|disable|preferred]|\n" + "\t\t\tmanual
"}, + {"proxy", 'x', "direct|auto |manual [] [...]\n" + "\t\t\t[exclude [] [...]]"}, + {"autoconnect", 'a', "yes|no"}, + {"ipv4", 'i', "off|dhcp|manual
"}, + {"remove", 'r', " Remove service"}, + { NULL, } +}; + +static struct connman_option monitor_options[] = { + {"services", 's', "[off] Monitor only services"}, + {"tech", 'c', "[off] Monitor only technologies"}, + {"manager", 'm', "[off] Monitor only manager interface"}, + {"vpnmanager", 'M', "[off] Monitor only VPN manager " + "interface"}, + {"vpnconnection", 'C', "[off] Monitor only VPN " + "connections" }, + { NULL, } +}; + +static struct connman_option session_options[] = { + {"bearers", 'b', " [ [...]]"}, + {"type", 't', "local|internet|any"}, + {"ifname", 'i', "[]"}, + {"srciprule", 's', "yes|no"}, + {"ctxid", 'c', ""}, + { NULL, } +}; + +#if defined TIZEN_EXT_WIFI_MESH +static struct connman_option mesh_options[] = { + {"ifadd", 'a', " \n" + " [bridge_ifname] Add Virtual Mesh " + "interface"}, + {"ifrmv", 'r', " Remove Virtual Mesh " + "interface"}, + {"peers", 'p', "[peer] Display Mesh peer " + "informations"}, + {"connect", 'c', " Connect Mesh Peer"}, + {"disconnect", 'd', " Disconnect Mesh Peer"}, + {"remove", 'f', " Forget Mesh Peer"}, + {"connected_peers", 'C', "[] Displays connected" + " Peer informations"}, + {"disconnected_peers", 'D', "[] Displays " + "Disconnected Peer informations"}, + {"create_network", 'n', " Create New Mesh " + "Network"}, + {"abort_scan", 'A', " Abort ongoing mesh " + "scan"}, + {"specific_scan", 'S', " Create New Mesh " + "Network"}, + {"config", 'P', " Set Mesh Network " + "Configurations\n Passphrase "}, + {"set_gate", 'G', " Set Mesh Gate " + "Option"}, + {"add_peer", 'z', " Add Mesh Peer"}, + {"remove_peer", 'y', " Remove Mesh Peer"}, + { NULL, } +}; +#endif + +static char *lookup_options(struct connman_option *options, const char *text, + int state) +{ + static int idx = 0; + static int len = 0; + const char *str; + + if (state == 0) { + idx = 0; + len = strlen(text); + } + + while (options[idx].name) { + str = options[idx].name; + idx++; + + if (str && strncmp(text, str, len) == 0) + return strdup(str); + } + + return NULL; +} + +static char *lookup_monitor(const char *text, int state) +{ + int level; + + level = __connmanctl_input_calc_level(); + + if (level < 2) + return lookup_options(monitor_options, text, state); + + if (level == 2) + return lookup_on_off(text, state); + + __connmanctl_input_lookup_end(); + return NULL; +} + +static char *lookup_config(const char *text, int state) +{ + if (__connmanctl_input_calc_level() < 2) + return lookup_key_from_table(service_hash, text, state); + + return lookup_options(config_options, text, state); +} + +static char *lookup_session(const char *text, int state) +{ + return lookup_options(session_options, text, state); +} + +#if defined TIZEN_EXT_WIFI_MESH +static char *lookup_mesh(const char *text, int state) +{ + return lookup_options(mesh_options, text, state); +} +#endif + +static int peer_service_cb(DBusMessageIter *iter, int errnum, + const char *error, void *user_data) +{ + bool registration = GPOINTER_TO_INT(user_data); + + if (error) + fprintf(stderr, "Error %s peer service: %s\n", + registration ? "registering" : "unregistering", error); + else + fprintf(stdout, "Peer service %s\n", + registration ? "registered" : "unregistered"); + + return 0; +} + +struct _peer_service { + unsigned char *bjr_query; + int bjr_query_len; + unsigned char *bjr_response; + int bjr_response_len; + unsigned char *wfd_ies; + int wfd_ies_len; + char *upnp_service; + int version; + int master; +}; + +static void append_dict_entry_fixed_array(DBusMessageIter *iter, + const char *property, void *value, int length) +{ + DBusMessageIter dict_entry, variant, array; + + dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, + NULL, &dict_entry); + dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, + &property); + dbus_message_iter_open_container(&dict_entry, DBUS_TYPE_VARIANT, + DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING, + &variant); + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, + DBUS_TYPE_BYTE_AS_STRING, &array); + dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, + value, length); + dbus_message_iter_close_container(&variant, &array); + dbus_message_iter_close_container(&dict_entry, &variant); + dbus_message_iter_close_container(iter, &dict_entry); +} + +static void append_peer_service_dict(DBusMessageIter *iter, void *user_data) +{ + struct _peer_service *service = user_data; + + if (service->bjr_query && service->bjr_response) { + append_dict_entry_fixed_array(iter, "BonjourQuery", + &service->bjr_query, service->bjr_query_len); + append_dict_entry_fixed_array(iter, "BonjourResponse", + &service->bjr_response, service->bjr_response_len); + } else if (service->upnp_service && service->version) { + __connmanctl_dbus_append_dict_entry(iter, "UpnpVersion", + DBUS_TYPE_INT32, &service->version); + __connmanctl_dbus_append_dict_entry(iter, "UpnpService", + DBUS_TYPE_STRING, &service->upnp_service); + } else if (service->wfd_ies) { + append_dict_entry_fixed_array(iter, "WiFiDisplayIEs", + &service->wfd_ies, service->wfd_ies_len); + } +} + +static void peer_service_append(DBusMessageIter *iter, void *user_data) +{ + struct _peer_service *service = user_data; + dbus_bool_t master; + + __connmanctl_dbus_append_dict(iter, append_peer_service_dict, service); + + if (service->master < 0) + return; + + master = service->master == 1 ? TRUE : FALSE; + dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &master); +} + +static struct _peer_service *fill_in_peer_service(unsigned char *bjr_query, + int bjr_query_len, unsigned char *bjr_response, + int bjr_response_len, char *upnp_service, + int version, unsigned char *wfd_ies, + int wfd_ies_len) +{ + struct _peer_service *service; + + service = dbus_malloc0(sizeof(*service)); + +#if defined TIZEN_EXT + if (!service) + return NULL; +#endif + + if (bjr_query_len && bjr_response_len) { + service->bjr_query = dbus_malloc0(bjr_query_len); +#if defined TIZEN_EXT + if(!service->bjr_query) { + dbus_free(service); + return NULL; + } +#endif + memcpy(service->bjr_query, bjr_query, bjr_query_len); + service->bjr_query_len = bjr_query_len; + + service->bjr_response = dbus_malloc0(bjr_response_len); +#if defined TIZEN_EXT + if(!service->bjr_response) { + dbus_free(service->bjr_query); + dbus_free(service); + return NULL; + } +#endif + memcpy(service->bjr_response, bjr_response, bjr_response_len); + service->bjr_response_len = bjr_response_len; + } else if (upnp_service && version) { + service->upnp_service = strdup(upnp_service); + service->version = version; + } else if (wfd_ies && wfd_ies_len) { + service->wfd_ies = dbus_malloc0(wfd_ies_len); +#if defined TIZEN_EXT + if (!service->wfd_ies) { + dbus_free(service); + return NULL; + } +#endif + memcpy(service->wfd_ies, wfd_ies, wfd_ies_len); + service->wfd_ies_len = wfd_ies_len; + } else { + dbus_free(service); + service = NULL; + } + + return service; +} + +static void free_peer_service(struct _peer_service *service) +{ + dbus_free(service->bjr_query); + dbus_free(service->bjr_response); + dbus_free(service->wfd_ies); + free(service->upnp_service); + dbus_free(service); +} + +static int peer_service_register(unsigned char *bjr_query, int bjr_query_len, + unsigned char *bjr_response, int bjr_response_len, + char *upnp_service, int version, + unsigned char *wfd_ies, int wfd_ies_len, int master) +{ + struct _peer_service *service; + bool registration = true; + int ret; + + service = fill_in_peer_service(bjr_query, bjr_query_len, bjr_response, + bjr_response_len, upnp_service, version, + wfd_ies, wfd_ies_len); + if (!service) + return -EINVAL; + + service->master = master; + + ret = __connmanctl_dbus_method_call(connection, "net.connman", "/", + "net.connman.Manager", "RegisterPeerService", + peer_service_cb, GINT_TO_POINTER(registration), + peer_service_append, service); + + free_peer_service(service); + + return ret; +} + +static int peer_service_unregister(unsigned char *bjr_query, int bjr_query_len, + unsigned char *bjr_response, int bjr_response_len, + char *upnp_service, int version, + unsigned char *wfd_ies, int wfd_ies_len) +{ + struct _peer_service *service; + bool registration = false; + int ret; + + service = fill_in_peer_service(bjr_query, bjr_query_len, bjr_response, + bjr_response_len, upnp_service, version, + wfd_ies, wfd_ies_len); + if (!service) + return -EINVAL; + + service->master = -1; + + ret = __connmanctl_dbus_method_call(connection, "net.connman", "/", + "net.connman.Manager", "UnregisterPeerService", + peer_service_cb, GINT_TO_POINTER(registration), + peer_service_append, service); + + free_peer_service(service); + + return ret; +} + +static int parse_spec_array(char *command, unsigned char spec[1024]) +{ + int length, pos, end; + char b[3] = {}; + char *e; + + end = strlen(command); + for (e = NULL, length = pos = 0; command[pos] != '\0'; length++) { + if (pos+2 > end) + return -EINVAL; + + b[0] = command[pos]; + b[1] = command[pos+1]; + + spec[length] = strtol(b, &e, 16); + if (e && *e != '\0') + return -EINVAL; + + pos += 2; + } + + return length; +} + +static int cmd_peer_service(char *args[], int num, + struct connman_option *options) +{ + unsigned char bjr_query[1024] = {}; + unsigned char bjr_response[1024] = {}; + unsigned char wfd_ies[1024] = {}; + char *upnp_service = NULL; + int bjr_query_len = 0, bjr_response_len = 0; + int version = 0, master = 0, wfd_ies_len = 0; + int limit; + + if (num < 4) + return -EINVAL; + + if (!strcmp(args[2], "wfd_ies")) { + wfd_ies_len = parse_spec_array(args[3], wfd_ies); + if (wfd_ies_len == -EINVAL) + return -EINVAL; + limit = 5; + goto master; + } + + if (num < 6) + return -EINVAL; + + limit = 7; + if (!strcmp(args[2], "bjr_query")) { + if (strcmp(args[4], "bjr_response")) + return -EINVAL; + bjr_query_len = parse_spec_array(args[3], bjr_query); + bjr_response_len = parse_spec_array(args[5], bjr_response); + + if (bjr_query_len == -EINVAL || bjr_response_len == -EINVAL) + return -EINVAL; + } else if (!strcmp(args[2], "upnp_service")) { + char *e = NULL; + + if (strcmp(args[4], "upnp_version")) + return -EINVAL; + upnp_service = args[3]; + version = strtol(args[5], &e, 10); + if (*e != '\0') + return -EINVAL; + } + +master: + if (num == limit) { + master = parse_boolean(args[6]); + if (master < 0) + return -EINVAL; + } + + if (!strcmp(args[1], "register")) { + return peer_service_register(bjr_query, bjr_query_len, + bjr_response, bjr_response_len, upnp_service, + version, wfd_ies, wfd_ies_len, master); + } else if (!strcmp(args[1], "unregister")) { + return peer_service_unregister(bjr_query, bjr_query_len, + bjr_response, bjr_response_len, upnp_service, + version, wfd_ies, wfd_ies_len); + } + + return -EINVAL; +} + +static const struct { + const char *cmd; + const char *argument; + struct connman_option *options; + int (*func) (char *args[], int num, struct connman_option *options); + const char *desc; + __connmanctl_lookup_cb cb; +} cmd_table[] = { + { "state", NULL, NULL, cmd_state, + "Shows if the system is online or offline", NULL }, + { "technologies", NULL, NULL, cmd_technologies, + "Display technologies", NULL }, + { "clock", NULL, NULL, cmd_clock, + "Get System Clock Properties", NULL }, + { "enable", "|offline", NULL, cmd_enable, + "Enables given technology or offline mode", + lookup_technology_offline }, + { "disable", "|offline", NULL, cmd_disable, + "Disables given technology or offline mode", + lookup_technology_offline }, +#if defined TIZEN_EXT_WIFI_MESH + { "mesh", "", mesh_options, cmd_mesh, "Mesh specific commands", + lookup_mesh }, +#endif + { "tether", " on|off\n" + " wifi [on|off] ", + NULL, cmd_tether, + "Enable, disable tethering, set SSID and passphrase for wifi", + lookup_tether }, + { "tethering_clients", NULL, NULL, cmd_tethering_clients, + "Display tethering clients", NULL }, + { "services", "[]", service_options, cmd_services, + "Display services", lookup_service_arg }, +#if defined TIZEN_EXT_INS + { "ins", NULL, ins_options, cmd_ins, + "Display intelligent network selection", NULL }, +#endif + { "peers", "[peer]", NULL, cmd_peers, + "Display peers", lookup_peer_arg }, + { "scan", "", NULL, cmd_scan, + "Scans for new services for given technology", + lookup_technology_arg }, + { "connect", "", NULL, cmd_connect, + "Connect a given service or peer", lookup_service_arg }, + { "disconnect", "", NULL, cmd_disconnect, + "Disconnect a given service or peer", lookup_service_arg }, + { "move-before", " ", NULL, + cmd_service_move_before, "Move before ", + lookup_service_arg }, + { "move-after", " ", NULL, + cmd_service_move_after, "Move after ", + lookup_service_arg }, + { "config", "", config_options, cmd_config, + "Set service configuration options", lookup_config }, + { "monitor", "[off]", monitor_options, cmd_monitor, + "Monitor signals from interfaces", lookup_monitor }, + { "agent", "on|off", NULL, cmd_agent, + "Agent mode", lookup_agent }, + { "vpnconnections", "[]", NULL, cmd_vpnconnections, + "Display VPN connections", lookup_vpnconnection_arg }, + { "vpnagent", "on|off", NULL, cmd_vpnagent, + "VPN Agent mode", lookup_agent }, + { "session", "on|off|connect|disconnect|config", session_options, + cmd_session, "Enable or disable a session", lookup_session }, + { "peer_service", "register|unregister \n" + "Where specs are:\n" + "\tbjr_query bjr_response \n" + "\tupnp_service upnp_version \n" + "\twfd_ies \n", NULL, + cmd_peer_service, "(Un)Register a Peer Service", NULL }, + { "help", NULL, NULL, cmd_help, + "Show help", NULL }, + { "exit", NULL, NULL, cmd_exit, + "Exit", NULL }, + { "quit", NULL, NULL, cmd_exit, + "Quit", NULL }, + { NULL, }, +}; + +static int cmd_help(char *args[], int num, struct connman_option *options) +{ + bool interactive = __connmanctl_is_interactive(); + int i, j; + + if (interactive == false) + fprintf(stdout, "Usage: connmanctl [[command] [args]]\n"); + + for (i = 0; cmd_table[i].cmd; i++) { + const char *cmd = cmd_table[i].cmd; + const char *argument = cmd_table[i].argument; + const char *desc = cmd_table[i].desc; + + printf("%-16s%-22s%s\n", cmd? cmd: "", + argument? argument: "", + desc? desc: ""); + + if (cmd_table[i].options) { + for (j = 0; cmd_table[i].options[j].name; + j++) { + const char *options_desc = + cmd_table[i].options[j].desc ? + cmd_table[i].options[j].desc: ""; + + printf(" --%-16s%s\n", + cmd_table[i].options[j].name, + options_desc); + } + } + } + + if (interactive == false) + fprintf(stdout, "\nNote: arguments and output are considered " + "EXPERIMENTAL for now.\n"); + + return 0; +} + +__connmanctl_lookup_cb __connmanctl_get_lookup_func(const char *text) +{ + int i, cmdlen, textlen; + + if (!text) + return NULL; + + textlen = strlen(text); + + for (i = 0; cmd_table[i].cmd; i++) { + cmdlen = strlen(cmd_table[i].cmd); + + if (textlen > cmdlen && text[cmdlen] != ' ') + continue; + + if (strncmp(cmd_table[i].cmd, text, cmdlen) == 0) + return cmd_table[i].cb; + } + + return NULL; +} + +int __connmanctl_commands(DBusConnection *dbus_conn, char *argv[], int argc) +{ + int i, result; + + connection = dbus_conn; + + for (i = 0; cmd_table[i].cmd; i++) { + if (g_strcmp0(cmd_table[i].cmd, argv[0]) == 0 && + cmd_table[i].func) { + result = cmd_table[i].func(argv, argc, + cmd_table[i].options); + if (result < 0 && result != -EINPROGRESS) + fprintf(stderr, "Error '%s': %s\n", argv[0], + strerror(-result)); + return result; + } + } + + fprintf(stderr, "Error '%s': Unknown command\n", argv[0]); + return -EINVAL; +} + +char *__connmanctl_lookup_command(const char *text, int state) +{ + static int i = 0; + static int len = 0; + + if (state == 0) { + i = 0; + len = strlen(text); + } + + while (cmd_table[i].cmd) { + const char *command = cmd_table[i].cmd; + + i++; + + if (strncmp(text, command, len) == 0) + return strdup(command); + } + + return NULL; +} + +static char *get_path(char *full_path) +{ + char *path; + + path = strrchr(full_path, '/'); + if (path && *path != '\0') + path++; + else + path = full_path; + + return path; +} + +static void add_service_id(const char *path) +{ + g_hash_table_replace(service_hash, g_strdup(path), + GINT_TO_POINTER(TRUE)); +} + +static void remove_service_id(const char *path) +{ + g_hash_table_remove(service_hash, path); +} + +static void services_added(DBusMessageIter *iter) +{ + DBusMessageIter array; + char *path = NULL; + + while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRUCT) { + + dbus_message_iter_recurse(iter, &array); + if (dbus_message_iter_get_arg_type(&array) != + DBUS_TYPE_OBJECT_PATH) + return; + + dbus_message_iter_get_basic(&array, &path); + add_service_id(get_path(path)); + + dbus_message_iter_next(iter); + } +} + +static void update_services(DBusMessageIter *iter) +{ + DBusMessageIter array; + char *path; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return; + + dbus_message_iter_recurse(iter, &array); + services_added(&array); + + dbus_message_iter_next(iter); + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return; + + dbus_message_iter_recurse(iter, &array); + while (dbus_message_iter_get_arg_type(&array) == + DBUS_TYPE_OBJECT_PATH) { + dbus_message_iter_get_basic(&array, &path); + remove_service_id(get_path(path)); + + dbus_message_iter_next(&array); + } +} + +static int populate_service_hash(DBusMessageIter *iter, int errnum, + const char *error, void *user_data) +{ + if (error) { + fprintf(stderr, "Error getting services: %s", error); + return 0; + } + + update_services(iter); + return 0; +} + +static void add_vpnconnection_id(const char *path) +{ + g_hash_table_replace(vpnconnection_hash, g_strdup(path), + GINT_TO_POINTER(TRUE)); +} + +static void remove_vpnconnection_id(const char *path) +{ + g_hash_table_remove(vpnconnection_hash, path); +} + +static void vpnconnection_added(DBusMessageIter *iter) +{ + char *path = NULL; + + dbus_message_iter_get_basic(iter, &path); + add_vpnconnection_id(get_path(path)); +} + +static void vpnconnection_removed(DBusMessageIter *iter) +{ + char *path = NULL; + + dbus_message_iter_get_basic(iter, &path); + remove_vpnconnection_id(get_path(path)); +} + +static void add_vpnconnections(DBusMessageIter *iter) +{ + DBusMessageIter array; + char *path = NULL; + + while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRUCT) { + + dbus_message_iter_recurse(iter, &array); + if (dbus_message_iter_get_arg_type(&array) != + DBUS_TYPE_OBJECT_PATH) + return; + + dbus_message_iter_get_basic(&array, &path); + add_vpnconnection_id(get_path(path)); - index++; + dbus_message_iter_next(iter); } - - return result; } -static DBusHandlerResult monitor_changed(DBusConnection *connection, - DBusMessage *message, void *user_data) +static int populate_vpnconnection_hash(DBusMessageIter *iter, int errnum, + const char *error, void *user_data) { - DBusMessageIter iter; - const char *interface, *path; + DBusMessageIter array; - interface = dbus_message_get_interface(message); - if (strncmp(interface, "net.connman.", 12) != 0) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + if (error) { + fprintf(stderr, "Error getting VPN connections: %s", error); + return 0; + } - interface = strrchr(interface, '.'); - if (interface != NULL && *interface != '\0') - interface++; + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return 0; - path = strrchr(dbus_message_get_path(message), '/'); - if (path != NULL && *path != '\0') - path++; + dbus_message_iter_recurse(iter, &array); - __connmanctl_save_rl(); + add_vpnconnections(&array); - if (dbus_message_is_signal(message, "net.connman.Manager", - "ServicesChanged") == TRUE) { + return 0; +} - fprintf(stdout, "%-12s %-20s = {\n", interface, - "ServicesChanged"); - dbus_message_iter_init(message, &iter); - __connmanctl_services_list(&iter); - fprintf(stdout, "\n}\n"); +static void add_peer_id(const char *path) +{ + g_hash_table_replace(peer_hash, g_strdup(path), GINT_TO_POINTER(TRUE)); +} - __connmanctl_redraw_rl(); +static void remove_peer_id(const char *path) +{ + g_hash_table_remove(peer_hash, path); +} + +static void peers_added(DBusMessageIter *iter) +{ + DBusMessageIter array; + char *path = NULL; + + while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRUCT) { + + dbus_message_iter_recurse(iter, &array); + if (dbus_message_iter_get_arg_type(&array) != + DBUS_TYPE_OBJECT_PATH) + return; + + dbus_message_iter_get_basic(&array, &path); + add_peer_id(get_path(path)); - return DBUS_HANDLER_RESULT_HANDLED; + dbus_message_iter_next(iter); } +} - if (dbus_message_is_signal(message, "net.connman.Manager", - "TechnologyAdded") == TRUE) - path = "TechnologyAdded"; +static void update_peers(DBusMessageIter *iter) +{ + DBusMessageIter array; + char *path; - if (dbus_message_is_signal(message, "net.connman.Manager", - "TechnologyRemoved") == TRUE) - path = "TechnologyRemoved"; + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return; - fprintf(stdout, "%-12s %-20s ", interface, path); - dbus_message_iter_init(message, &iter); + dbus_message_iter_recurse(iter, &array); + peers_added(&array); - __connmanctl_dbus_print(&iter, "", " = ", " = "); - fprintf(stdout, "\n"); + dbus_message_iter_next(iter); + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return; - __connmanctl_redraw_rl(); + dbus_message_iter_recurse(iter, &array); + while (dbus_message_iter_get_arg_type(&array) == + DBUS_TYPE_OBJECT_PATH) { + dbus_message_iter_get_basic(&array, &path); + remove_peer_id(get_path(path)); - return DBUS_HANDLER_RESULT_HANDLED; + dbus_message_iter_next(&array); + } } -static bool monitor_s = false; -static bool monitor_t = false; -static bool monitor_m = false; - -static void monitor_add(char *interface) +static int populate_peer_hash(DBusMessageIter *iter, int errnum, + const char *error, void *user_data) { - char *rule; - DBusError err; - - if (monitor_s == false && monitor_t == false && monitor_m == false) - dbus_connection_add_filter(connection, monitor_changed, - NULL, NULL); + if (error) { + fprintf(stderr, "Error getting peers: %s", error); + return 0; + } - if (g_strcmp0(interface, "Service") == 0) { - if (monitor_s == true) - return; - monitor_s = true; - } else if (g_strcmp0(interface, "Technology") == 0) { - if (monitor_t == true) - return; - monitor_t = true; - } else if (g_strcmp0(interface, "Manager") == 0) { - if (monitor_m == true) - return; - monitor_m = true; - } else - return; + update_peers(iter); + return 0; +} - dbus_error_init(&err); - rule = g_strdup_printf("type='signal',interface='net.connman.%s'", - interface); - dbus_bus_add_match(connection, rule, &err); - g_free(rule); +static void add_technology_id(const char *path) +{ + g_hash_table_replace(technology_hash, g_strdup(path), + GINT_TO_POINTER(TRUE)); +} - if (dbus_error_is_set(&err)) - fprintf(stderr, "Error: %s\n", err.message); +static void remove_technology_id(const char *path) +{ + g_hash_table_remove(technology_hash, path); } -static void monitor_del(char *interface) +static void remove_technology(DBusMessageIter *iter) { - char *rule; + char *path = NULL; - if (g_strcmp0(interface, "Service") == 0) { - if (monitor_s == false) - return; - monitor_s = false; - } else if (g_strcmp0(interface, "Technology") == 0) { - if (monitor_t == false) - return; - monitor_t = false; - } else if (g_strcmp0(interface, "Manager") == 0) { - if (monitor_m == false) - return; - monitor_m = false; - } else + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_OBJECT_PATH) return; - rule = g_strdup_printf("type='signal',interface='net.connman.%s'", - interface); - dbus_bus_remove_match(connection, rule, NULL); - g_free(rule); - - if (monitor_s == false && monitor_t == false && monitor_m == false) - dbus_connection_remove_filter(connection, monitor_changed, - NULL); + dbus_message_iter_get_basic(iter, &path); + remove_technology_id(get_path(path)); } -static int cmd_monitor(char *args[], int num, struct option *options) +static void add_technology(DBusMessageIter *iter) { - bool add = true; - int c; + char *path = NULL; - if (num > 3) - return -E2BIG; + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_OBJECT_PATH) + return; - if (num == 3) { - switch (parse_boolean(args[2])) { - case 0: - add = false; - break; + dbus_message_iter_get_basic(iter, &path); + add_technology_id(get_path(path)); +} - default: - break; - } - } +static void update_technologies(DBusMessageIter *iter) +{ + DBusMessageIter array; - c = parse_args(args[1], options); - switch (c) { - case -1: - monitor_add("Service"); - monitor_add("Technology"); - monitor_add("Manager"); - break; + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return; - case 's': - if (add == true) - monitor_add("Service"); - else - monitor_del("Service"); - break; + dbus_message_iter_recurse(iter, &array); - case 'c': - if (add == true) - monitor_add("Technology"); - else - monitor_del("Technology"); - break; + while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) { + DBusMessageIter object_path; - case 'm': - if (add == true) - monitor_add("Manager"); - else - monitor_del("Manager"); - break; + dbus_message_iter_recurse(&array, &object_path); - default: - switch(parse_boolean(args[1])) { - case 0: - monitor_del("Service"); - monitor_del("Technology"); - monitor_del("Manager"); - break; + add_technology(&object_path); - case 1: - monitor_add("Service"); - monitor_add("Technology"); - monitor_add("Manager"); - break; + dbus_message_iter_next(&array); + } +} - default: - return -EINVAL; - } +static int populate_technology_hash(DBusMessageIter *iter, int errnum, + const char *error, void *user_data) +{ + if (error) { + fprintf(stderr, "Error getting technologies: %s\n", error); + return 0; } - if (add == true) - return -EINPROGRESS; + update_technologies(iter); return 0; } -static int cmd_exit(char *args[], int num, struct option *options) +static DBusHandlerResult monitor_completions_changed( + DBusConnection *connection, + DBusMessage *message, void *user_data) { - return 1; -} + bool *enabled = user_data; + DBusMessageIter iter; + DBusHandlerResult handled; -static struct option service_options[] = { - {"properties", required_argument, 0, 'p'}, - { NULL, } -}; + if (*enabled) + handled = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + else + handled = DBUS_HANDLER_RESULT_HANDLED; -static const char *service_desc[] = { - "[] (obsolete)", - NULL -}; + if (dbus_message_is_signal(message, "net.connman.Manager", + "ServicesChanged")) { + dbus_message_iter_init(message, &iter); + update_services(&iter); + return handled; + } -static struct option config_options[] = { - {"nameservers", required_argument, 0, 'n'}, - {"timeservers", required_argument, 0, 't'}, - {"domains", required_argument, 0, 'd'}, - {"ipv6", required_argument, 0, 'v'}, - {"proxy", required_argument, 0, 'x'}, - {"autoconnect", required_argument, 0, 'a'}, - {"ipv4", required_argument, 0, 'i'}, - {"remove", 0, 0, 'r'}, - { NULL, } -}; + if (dbus_message_is_signal(message, "net.connman.vpn.Manager", + "ConnectionAdded")) { + dbus_message_iter_init(message, &iter); + vpnconnection_added(&iter); + return handled; + } -static const char *config_desc[] = { - " [] []", - " [] [...]", - " [] [...]", - "off|auto [enable|disable|prefered]|\n" - "\t\t\tmanual
", - "direct|auto |manual [] [...]\n" - "\t\t\t[exclude [] [...]]", - "yes|no", - "off|dhcp|manual
", - " Remove service", - NULL -}; + if (dbus_message_is_signal(message, "net.connman.vpn.Manager", + "ConnectionRemoved")) { + dbus_message_iter_init(message, &iter); + vpnconnection_removed(&iter); + return handled; + } -static struct option monitor_options[] = { - {"services", no_argument, 0, 's'}, - {"tech", no_argument, 0, 'c'}, - {"manager", no_argument, 0, 'm'}, - { NULL, } -}; + if (dbus_message_is_signal(message, "net.connman.Manager", + "PeersChanged")) { + dbus_message_iter_init(message, &iter); + update_peers(&iter); + return handled; + } -static const char *monitor_desc[] = { - "[off] Monitor only services", - "[off] Monitor only technologies", - "[off] Monitor only manager interface", - NULL -}; + if (dbus_message_is_signal(message, "net.connman.Manager", + "TechnologyAdded")) { + dbus_message_iter_init(message, &iter); + add_technology(&iter); + return handled; + } -static const struct { - const char *cmd; - const char *argument; - struct option *options; - const char **options_desc; - int (*func) (char *args[], int num, struct option *options); - const char *desc; -} cmd_table[] = { - { "enable", "|offline", NULL, NULL, - cmd_enable, "Enables given technology or offline mode" }, - { "disable", "|offline", NULL, NULL, - cmd_disable, "Disables given technology or offline mode"}, - { "state", NULL, NULL, NULL, - cmd_state, "Shows if the system is online or offline" }, - { "services", "[]", service_options, &service_desc[0], - cmd_services, "Display services" }, - { "technologies", NULL, NULL, NULL, - cmd_technologies, "Display technologies" }, - { "scan", "", NULL, NULL, - cmd_scan, "Scans for new services for given technology" }, - { "connect", "", NULL, NULL, - cmd_connect, "Connect a given service" }, - { "disconnect", "", NULL, NULL, - cmd_disconnect, "Disconnect a given service" }, - { "config", "", config_options, &config_desc[0], - cmd_config, "Set service configuration options" }, - { "monitor", "[off]", monitor_options, &monitor_desc[0], - cmd_monitor, "Monitor signals from interfaces" }, - { "help", NULL, NULL, NULL, - cmd_help, "Show help" }, - { "exit", NULL, NULL, NULL, - cmd_exit, "Exit" }, - { "quit", NULL, NULL, NULL, - cmd_exit, "Quit" }, - { NULL, }, -}; + if (dbus_message_is_signal(message, "net.connman.Manager", + "TechnologyRemoved")) { + dbus_message_iter_init(message, &iter); + remove_technology(&iter); + return handled; + } + + if (!g_strcmp0(dbus_message_get_interface(message), + "net.connman.Manager")) + return handled; -static int cmd_help(char *args[], int num, struct option *options) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +void __connmanctl_monitor_completions(DBusConnection *dbus_conn) { - bool interactive = __connmanctl_is_interactive(); - int i, j; + bool *manager_enabled = NULL; + DBusError err; + int i; - if (interactive == false) - fprintf(stdout, "Usage: connmanctl [[command] [args]]\n"); + for (i = 0; monitor[i].interface; i++) { + if (!strcmp(monitor[i].interface, "Manager")) { + manager_enabled = &monitor[i].enabled; + break; + } + } - for (i = 0; cmd_table[i].cmd != NULL; i++) { - const char *cmd = cmd_table[i].cmd; - const char *argument = cmd_table[i].argument; - const char *desc = cmd_table[i].desc; + if (!dbus_conn) { + g_hash_table_destroy(service_hash); + g_hash_table_destroy(vpnconnection_hash); + g_hash_table_destroy(technology_hash); + + dbus_bus_remove_match(connection, + "type='signal',interface='net.connman.Manager'", NULL); + dbus_bus_remove_match(connection, + "type='signal',interface='net.connman.vpn.Manager'", + NULL); + dbus_connection_remove_filter(connection, + monitor_completions_changed, + manager_enabled); + return; + } - printf("%-12s%-22s%s\n", cmd != NULL? cmd: "", - argument != NULL? argument: "", - desc != NULL? desc: ""); + connection = dbus_conn; - if (cmd_table[i].options != NULL) { - for (j = 0; cmd_table[i].options[j].name != NULL; - j++) { - const char *options_desc = - cmd_table[i].options_desc != NULL ? - cmd_table[i].options_desc[j]: ""; + service_hash = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, NULL); - printf(" --%-12s%s\n", - cmd_table[i].options[j].name, - options_desc); - } - } - } + vpnconnection_hash = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, NULL); - if (interactive == false) - fprintf(stdout, "\nNote: arguments and output are considered " - "EXPERIMENTAL for now.\n"); + peer_hash = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, NULL); - return 0; -} + technology_hash = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, NULL); -int commands(DBusConnection *dbus_conn, char *argv[], int argc) -{ - int i, result; + __connmanctl_dbus_method_call(connection, + CONNMAN_SERVICE, CONNMAN_PATH, + "net.connman.Manager", "GetServices", + populate_service_hash, NULL, NULL, NULL); - connection = dbus_conn; + __connmanctl_dbus_method_call(connection, + VPN_SERVICE, CONNMAN_PATH, + "net.connman.vpn.Manager", "GetConnections", + populate_vpnconnection_hash, NULL, NULL, NULL); - for (i = 0; cmd_table[i].cmd != NULL; i++) { - if (g_strcmp0(cmd_table[i].cmd, argv[0]) == 0 && - cmd_table[i].func != NULL) { - result = cmd_table[i].func(argv, argc, - cmd_table[i].options); - if (result < 0 && result != -EINPROGRESS) - fprintf(stderr, "Error '%s': %s\n", argv[0], - strerror(-result)); - return result; - } + __connmanctl_dbus_method_call(connection, + CONNMAN_SERVICE, CONNMAN_PATH, + "net.connman.Manager", "GetPeers", + populate_peer_hash, NULL, NULL, NULL); + + __connmanctl_dbus_method_call(connection, + CONNMAN_SERVICE, CONNMAN_PATH, + "net.connman.Manager", "GetTechnologies", + populate_technology_hash, NULL, NULL, NULL); + + dbus_connection_add_filter(connection, + monitor_completions_changed, manager_enabled, + NULL); + + dbus_error_init(&err); + dbus_bus_add_match(connection, + "type='signal',interface='net.connman.Manager'", &err); + + if (dbus_error_is_set(&err)) { + fprintf(stderr, "Error: %s\n", err.message); + return; } - fprintf(stderr, "Error '%s': Unknown command\n", argv[0]); - return -EINVAL; + dbus_bus_add_match(connection, + "type='signal',interface='net.connman.vpn.Manager'", + &err); + + if (dbus_error_is_set(&err)) + fprintf(stderr, "Error: %s\n", err.message); }