| /* |
| * |
| * Connection Manager |
| * |
| * Copyright (C) 2007-2009 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 version 2 as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <string.h> |
| |
| #include <glib.h> |
| #include <gdbus.h> |
| |
| #include <connman/assert.h> |
| |
| #include "connman.h" |
| |
| #define PROFILE_DEFAULT_IDENT "default" |
| #define PROFILE_MAX 3 /* 2 is probably sufficient */ |
| |
| #define PROFILE_DEFAULT_PORTAL_URL \ |
| "http://clients3.google.com/generate_204" |
| |
| #define _DBG_PROFILE(fmt, arg...) DBG(DBG_PROFILE, fmt, ## arg) |
| |
| struct connman_profile { |
| struct connman_storage_ident ident; |
| guint changed_timeout; |
| char *path; |
| char *name; |
| connman_bool_t offlinemode; |
| connman_bool_t arpgateway; |
| char *country; |
| uint32_t checkportal; |
| char *portal_url; |
| }; |
| |
| static GHashTable *profile_hash = NULL; |
| |
| static struct connman_profile *profile_stack[PROFILE_MAX]; |
| static int cur_profile = -1; |
| |
| static struct connman_storage_ident default_ident = { |
| .ident = PROFILE_DEFAULT_IDENT |
| }; |
| static struct connman_profile *default_profile = NULL; |
| |
| static guint changed_timeout = 0; /* for NULL profile handling */ |
| |
| static DBusConnection *connection = NULL; |
| |
| /* NB: everything but UNKONWN and VPN */ |
| static uint32_t profile_default_checkportal_mask = |
| (0xffffffff &~ |
| ((1<<CONNMAN_SERVICE_TYPE_UNKNOWN) | (1<<CONNMAN_SERVICE_TYPE_VPN))); |
| |
| static const char *__profile_name(const struct connman_profile *profile) |
| { |
| return (profile == NULL) ? "(null)" : profile->path; |
| } |
| |
| /* |
| * Loading/Saving objects. |
| * |
| * Service objects go to the profile they are pinned to (typically |
| * the active profile at the time they were created but this can be |
| * changed, e.g. from private -> global). |
| * |
| * Device and ipconfig objects go in the global profile (if any). |
| * This ensures that enable/disable state is maintained between |
| * users (and reboots); or possibly discarded (e.g. for testing). |
| * |
| * Likewise global state like offline mode is stored in the global |
| * profile (see above). |
| */ |
| |
| /* |
| * Return the active profile; it's on the top of the stack. |
| */ |
| static inline struct connman_profile *active_profile(void) |
| { |
| return cur_profile >= 0 ? profile_stack[cur_profile] : NULL; |
| } |
| |
| /* |
| * Return the global profile; it's top-most non-user profile. |
| */ |
| static struct connman_profile *global_profile(void) |
| { |
| /* TODO(sleffler) cheat for now */ |
| return default_profile != NULL ? default_profile : NULL; |
| } |
| |
| static int ident_equal(const struct connman_storage_ident *a, |
| const struct connman_storage_ident *b) |
| { |
| return (g_strcmp0(a->user, b->user) == 0 && |
| g_strcmp0(a->ident, b->ident) == 0); |
| } |
| |
| static void append_path(gpointer key, gpointer value, gpointer user_data) |
| { |
| struct connman_profile *profile = value; |
| DBusMessageIter *iter = user_data; |
| |
| dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, |
| &profile->path); |
| } |
| |
| void __connman_profile_list(DBusMessageIter *iter, void *arg) |
| { |
| g_hash_table_foreach(profile_hash, append_path, iter); |
| } |
| |
| static void profiles_changed(void) |
| { |
| connman_dbus_send_property_changed_array(CONNMAN_MANAGER_PATH, |
| CONNMAN_MANAGER_INTERFACE, "Profiles", |
| DBUS_TYPE_OBJECT_PATH, __connman_profile_list, NULL); |
| } |
| |
| connman_bool_t __connman_profile_get_offlinemode(void) |
| { |
| struct connman_profile *profile = global_profile(); |
| return (profile == NULL) ? FALSE : profile->offlinemode; |
| } |
| |
| int __connman_profile_set_offlinemode(connman_bool_t offlinemode) |
| { |
| struct connman_profile *profile = global_profile(); |
| int ret; |
| |
| _DBG_PROFILE("offlinemode %d profile %s", offlinemode, |
| __profile_name(profile)); |
| |
| /* NB: always succeeds (ATM) */ |
| ret = __connman_device_set_offlinemode(offlinemode); |
| if (ret != 0) |
| return ret; |
| |
| /* TODO(sleffler) sallow even if no global profile? */ |
| if (profile != NULL) { |
| /* |
| * OfflineMode is only saved to the default profile; |
| * this ensures it is preserved across user changes. |
| */ |
| if (profile->offlinemode == offlinemode) |
| return -EALREADY; |
| |
| profile->offlinemode = offlinemode; |
| |
| connman_dbus_send_property_changed_variant( |
| profile->path, |
| CONNMAN_PROFILE_INTERFACE, "OfflineMode", |
| DBUS_TYPE_BOOLEAN, &offlinemode); |
| |
| __connman_storage_save_profile(profile); |
| } |
| return 0; |
| } |
| |
| connman_bool_t connman_profile_get_arpgateway(void) |
| { |
| struct connman_profile *profile = global_profile(); |
| return (profile == NULL) ? FALSE : profile->arpgateway; |
| } |
| |
| int connman_profile_set_arpgateway(connman_bool_t arpgateway) |
| { |
| struct connman_profile *profile = global_profile(); |
| |
| _DBG_PROFILE("arpgateway %d profile %s", arpgateway, |
| __profile_name(profile)); |
| |
| if (profile != NULL) { |
| if (profile->arpgateway == arpgateway) |
| return -EALREADY; |
| |
| profile->arpgateway = arpgateway; |
| |
| connman_dbus_send_property_changed_variant( |
| profile->path, |
| CONNMAN_PROFILE_INTERFACE, "ArpGateway", |
| DBUS_TYPE_BOOLEAN, &arpgateway); |
| |
| __connman_storage_save_profile(profile); |
| } |
| return 0; |
| } |
| |
| static void country_changed(struct connman_profile *profile) |
| { |
| connman_dbus_send_property_changed_variant(profile->path, |
| CONNMAN_PROFILE_INTERFACE, "Country", |
| DBUS_TYPE_STRING, &profile->country); |
| } |
| |
| const char *connman_profile_get_country(void) |
| { |
| struct connman_profile *profile = global_profile(); |
| return (profile == NULL) ? NULL : profile->country; |
| } |
| |
| int connman_profile_set_country(const char *country) |
| { |
| struct connman_profile *profile = global_profile(); |
| |
| _DBG_PROFILE("country %s profile %s", country, __profile_name(profile)); |
| |
| if (profile != NULL) { |
| if (g_strcmp0(profile->country, country) == 0) |
| return -EALREADY; |
| |
| g_free(profile->country); |
| profile->country = g_strdup(country); |
| |
| country_changed(profile); |
| |
| __connman_storage_save_profile(profile); |
| } |
| __connman_notifier_country_changed(country); |
| return 0; |
| } |
| |
| uint32_t __connman_profile_get_checkportal(void) |
| { |
| struct connman_profile *profile = global_profile(); |
| return (profile == NULL) ? |
| profile_default_checkportal_mask : profile->checkportal; |
| } |
| |
| int __connman_profile_set_checkportal(uint32_t checkportal) |
| { |
| struct connman_profile *profile = global_profile(); |
| gchar *str; |
| |
| _DBG_PROFILE("checkportal 0x%x profile %s", checkportal, |
| __profile_name(profile)); |
| |
| if (profile != NULL) { |
| /* |
| * CheckPortal is only saved to the global profile; |
| * this ensures it is preserved across user changes. |
| */ |
| if (profile->checkportal == checkportal) |
| return -EALREADY; |
| |
| profile->checkportal = checkportal; |
| |
| str = __connman_service_mask_to_list(checkportal); |
| if (str != NULL) { |
| connman_dbus_send_property_changed_variant( |
| profile->path, |
| CONNMAN_PROFILE_INTERFACE, "CheckPortalList", |
| DBUS_TYPE_STRING, &str); |
| g_free(str); |
| } |
| |
| __connman_storage_save_profile(profile); |
| } |
| return 0; |
| } |
| |
| const char *__connman_profile_get_portal_url(void) |
| { |
| struct connman_profile *profile = global_profile(); |
| /* NB: always return something not NULL */ |
| return (profile == NULL || profile->portal_url == NULL) ? |
| PROFILE_DEFAULT_PORTAL_URL : profile->portal_url; |
| } |
| |
| int __connman_profile_set_portal_url(const char *url) |
| { |
| struct connman_profile *profile = global_profile(); |
| |
| _DBG_PROFILE("url %s profile %s", url, __profile_name(profile)); |
| |
| /* NB: require "http" prefix (allows https too) */ |
| if (url == NULL || g_str_has_prefix(url, "http") == FALSE) |
| return -EINVAL; |
| |
| if (profile != NULL) { |
| if (g_strcmp0(profile->portal_url, url) == 0) |
| return -EALREADY; |
| |
| g_free(profile->portal_url); |
| profile->portal_url = g_strdup(url); |
| |
| connman_dbus_send_property_changed_variant(profile->path, |
| CONNMAN_PROFILE_INTERFACE, "PortalURL", |
| DBUS_TYPE_STRING, &url); |
| |
| __connman_storage_save_profile(profile); |
| } |
| return 0; |
| } |
| |
| static inline int load_continue(int err) |
| { |
| /* NB: ENXIO for no file, ESRCH for no entry */ |
| return (err == -ENXIO || err == -ESRCH); |
| } |
| |
| int __connman_profile_load_service(struct connman_service *service) |
| { |
| struct connman_profile *profile = |
| __connman_service_get_profile(service); |
| int err, i; |
| |
| _DBG_PROFILE("service %p profile %s", service, __profile_name(profile)); |
| |
| if (profile != NULL) |
| return __connman_storage_load_service(service, &profile->ident); |
| /* |
| * Not bound to a profile yet, search the stack for an |
| * entry and if found bind the profile to the service. |
| */ |
| err = 0; |
| for (i = cur_profile; i >= 0; i--) { |
| profile = profile_stack[i]; |
| err = __connman_storage_load_service(service, &profile->ident); |
| if (err == 0) { |
| _DBG_PROFILE("bind to profile %s", profile->path); |
| __connman_service_set_profile(service, profile); |
| return 0; |
| } |
| if (!load_continue(err)) |
| break; |
| } |
| return err; |
| } |
| |
| /* |
| * Search for the specified GUID in the profile stack and return |
| * the associated object path suitable for instantiating an instance |
| * of the associated object (currently only a connman_service). |
| */ |
| char *__connman_profile_find_guid(const char *guid) |
| { |
| char *ident = NULL; |
| int i; |
| |
| _DBG_PROFILE("guid %s", guid); |
| |
| for (i = cur_profile; i >= 0; i--) { |
| struct connman_profile *profile = profile_stack[i]; |
| |
| ident = __connman_storage_find_guid(guid, &profile->ident); |
| if (ident != NULL) { |
| _DBG_PROFILE("found %s in profile %s", ident, |
| profile->path); |
| break; |
| } |
| } |
| return ident; |
| } |
| |
| /* |
| * NB: This is like __connman_profile_load_service but defaults |
| * the service to use the global profile. This is used for |
| * "device services" (see __connman_service_create_from_device). |
| */ |
| int __connman_profile_load_device_service(struct connman_service *service) |
| { |
| struct connman_profile *profile = |
| __connman_service_get_profile(service); |
| |
| _DBG_PROFILE("service %p profile %s", service, __profile_name(profile)); |
| |
| if (profile == NULL) { |
| /* |
| * Not bound to a profile yet, bind to the global profile. |
| */ |
| profile = global_profile(); |
| if (profile == NULL) { |
| _DBG_PROFILE("no global profile; cannot bind"); |
| return -ESRCH; |
| } |
| _DBG_PROFILE("bind to profile %s", profile->path); |
| __connman_service_set_profile(service, profile); |
| } |
| return __connman_storage_load_service(service, &profile->ident); |
| } |
| |
| int __connman_profile_save_service(struct connman_service *service) |
| { |
| struct connman_profile *profile = |
| __connman_service_get_profile(service); |
| |
| _DBG_PROFILE("service %p profile %s", service, __profile_name(profile)); |
| |
| if (profile == NULL) { |
| /* not bound yet, bind to the active profile */ |
| profile = active_profile(); |
| _DBG_PROFILE("bind to profile %s", __profile_name(profile)); |
| __connman_service_set_profile(service, profile); |
| } |
| return (profile == NULL) ? 0 : |
| __connman_storage_save_service(service, &profile->ident); |
| } |
| |
| int __connman_profile_load_provider(struct connman_provider *provider) |
| { |
| struct connman_profile *profile = |
| __connman_provider_get_profile(provider); |
| int err, i; |
| |
| _DBG_PROFILE("provider %p profile %s", provider, |
| __profile_name(profile)); |
| |
| if (profile != NULL) |
| return __connman_storage_load_provider(provider, |
| &profile->ident); |
| /* |
| * Not bound to a profile yet, search the stack for an |
| * entry and if found bind the profile to the provider. |
| */ |
| err = 0; |
| for (i = cur_profile; i >= 0; i--) { |
| profile = profile_stack[i]; |
| err = __connman_storage_load_provider(provider, &profile->ident); |
| if (err == 0) { |
| _DBG_PROFILE("bind to profile %s", profile->path); |
| __connman_provider_set_profile(provider, profile); |
| return 0; |
| } |
| if (!load_continue(err)) |
| break; |
| } |
| return err; |
| } |
| |
| int __connman_profile_save_provider(struct connman_provider *provider) |
| { |
| struct connman_profile *profile = |
| __connman_provider_get_profile(provider); |
| |
| _DBG_PROFILE("provider %p profile %s", provider, |
| __profile_name(profile)); |
| |
| if (profile == NULL) { |
| /* not bound yet, bind to the active profile */ |
| profile = active_profile(); |
| _DBG_PROFILE("bind to profile %s", profile->path); |
| __connman_provider_set_profile(provider, profile); |
| } |
| return (profile == NULL) ? 0 : |
| __connman_storage_save_provider(provider, &profile->ident); |
| } |
| |
| int __connman_profile_load_device(struct connman_device *device) |
| { |
| struct connman_profile *profile = global_profile(); |
| |
| _DBG_PROFILE("device %p profile %s", device, __profile_name(profile)); |
| |
| return (profile == NULL) ? 0 : |
| __connman_storage_load_device(device, &profile->ident); |
| } |
| |
| int __connman_profile_save_device(struct connman_device *device) |
| { |
| struct connman_profile *profile = global_profile(); |
| |
| _DBG_PROFILE("device %p profile %s", device, __profile_name(profile)); |
| |
| return (profile == NULL) ? 0: |
| __connman_storage_save_device(device, &profile->ident); |
| } |
| |
| int __connman_profile_load_ipconfig(struct connman_ipconfig *ipconfig) |
| { |
| struct connman_profile *profile = global_profile(); |
| |
| _DBG_PROFILE("ipconfig %p profile %s", ipconfig, |
| __profile_name(profile)); |
| |
| return (profile == NULL) ? 0: |
| __connman_storage_load_ipconfig(ipconfig, &profile->ident); |
| } |
| |
| int __connman_profile_save_ipconfig(const struct connman_ipconfig *ipconfig) |
| { |
| struct connman_profile *profile = global_profile(); |
| |
| _DBG_PROFILE("ipconfig %p profile %s", ipconfig, |
| __profile_name(profile)); |
| |
| return (profile == NULL) ? 0 : |
| __connman_storage_save_ipconfig(ipconfig, &profile->ident); |
| } |
| |
| int __connman_profile_append_hidden_ssids(GSList **hidden_ssids, |
| void (*append_hidden_ssids)(GKeyFile *keyfile, GSList **hidden_ssids)) |
| { |
| int i; |
| |
| _DBG_PROFILE(""); |
| |
| for (i = cur_profile; i >= 0; i--) { |
| const struct connman_profile *profile = profile_stack[i]; |
| GKeyFile *keyfile; |
| |
| keyfile = __connman_storage_open(&profile->ident); |
| if (keyfile != NULL) { |
| append_hidden_ssids(keyfile, hidden_ssids); |
| __connman_storage_close(&profile->ident, keyfile, |
| FALSE); |
| } |
| } |
| return 0; |
| } |
| |
| /* |
| * Return the object path for the specified profile. |
| */ |
| const char *__connman_profile_get_path(const struct connman_profile *profile) |
| { |
| return profile != NULL ? profile->path : NULL; |
| } |
| |
| /* |
| * Return the profile given an object path. |
| */ |
| struct connman_profile *__connman_profile_lookup_profile(const char *path) |
| { |
| return g_hash_table_lookup(profile_hash, path); |
| } |
| |
| /* |
| * Return the active profile or NULL |
| */ |
| struct connman_profile *__connman_profile_active_profile(void) |
| { |
| return active_profile(); |
| } |
| |
| const struct connman_storage_ident *__connman_profile_active_ident(void) |
| { |
| struct connman_profile *profile = active_profile(); |
| return profile != NULL ? &profile->ident : NULL; |
| } |
| |
| /* |
| * Return the object path for the active profile or NULL |
| * if there is none. |
| */ |
| const char *__connman_profile_active_path(void) |
| { |
| struct connman_profile *profile = active_profile(); |
| return profile != NULL ? profile->path : NULL; |
| } |
| |
| /* |
| * Delete an entry in the specified profile. |
| */ |
| int __connman_profile_delete_entry(struct connman_profile *profile, |
| const char *group) |
| { |
| GKeyFile *keyfile; |
| gboolean status; |
| |
| _DBG_PROFILE("profile %s group %s", __profile_name(profile), group); |
| |
| keyfile = __connman_storage_open(&profile->ident); |
| if (keyfile == NULL) { |
| _DBG_PROFILE("cannot open key file"); |
| return -EINVAL; |
| } |
| |
| status = g_key_file_remove_group(keyfile, group, NULL); |
| __connman_storage_close(&profile->ident, keyfile, status); |
| |
| if (status == FALSE) { |
| _DBG_PROFILE("cannot remove %s", group); |
| return -ENXIO; |
| } |
| return 0; |
| } |
| |
| static void __clear_timeout(guint *pchanged_timeout) |
| { |
| if (*pchanged_timeout > 0) { |
| g_source_remove(*pchanged_timeout); |
| *pchanged_timeout = 0; |
| } |
| } |
| |
| static void clear_timeout(struct connman_profile *profile) |
| { |
| if (profile != NULL) |
| __clear_timeout(&profile->changed_timeout); |
| else |
| __clear_timeout(&changed_timeout); |
| } |
| |
| static void append_services(DBusMessageIter *iter, void *arg) |
| { |
| __connman_service_list(iter, arg); |
| } |
| static gboolean services_changed(gpointer user_data) |
| { |
| struct connman_profile *profile = user_data; |
| |
| if (profile != NULL) { |
| profile->changed_timeout = 0; |
| |
| connman_dbus_send_property_changed_array(profile->path, |
| CONNMAN_PROFILE_INTERFACE, "Services", |
| DBUS_TYPE_OBJECT_PATH, append_services, NULL); |
| } else |
| changed_timeout = 0; |
| |
| connman_dbus_send_property_changed_array(CONNMAN_MANAGER_PATH, |
| CONNMAN_MANAGER_INTERFACE, "Services", |
| DBUS_TYPE_OBJECT_PATH, append_services, NULL); |
| return FALSE; |
| } |
| |
| /* |
| * Handle changes to a profile. Generate PropertyChanged signals |
| * on Manager.Services and Profile.Services for the currently active |
| * profile. To minimize overhead requests may be coalesced using a |
| * 1 second delay on the signals. |
| */ |
| void __connman_profile_changed(struct connman_profile *profile, |
| gboolean delayed) |
| { |
| _DBG_PROFILE("profile %s%s changed_timeout %d", |
| __profile_name(profile), delayed ? " delayed" : "", |
| profile != NULL ? profile->changed_timeout : changed_timeout); |
| |
| clear_timeout(profile); |
| |
| if (delayed == TRUE) { |
| guint timeout = g_timeout_add_seconds(1, |
| services_changed, profile); |
| if (profile != NULL) |
| profile->changed_timeout = timeout; |
| else |
| changed_timeout = timeout; |
| } else |
| services_changed(profile); |
| } |
| |
| int __connman_profile_add_device(struct connman_device *device) |
| { |
| _DBG_PROFILE("device %p", device); |
| |
| return __connman_service_create_from_device(device); |
| } |
| |
| int __connman_profile_remove_device(struct connman_device *device) |
| { |
| _DBG_PROFILE("device %p", device); |
| |
| __connman_service_remove_from_device(device); |
| |
| return 0; |
| } |
| |
| int __connman_profile_add_network(struct connman_network *network) |
| { |
| _DBG_PROFILE("network %p", network); |
| |
| return __connman_service_create_from_network(network); |
| } |
| |
| int __connman_profile_update_network(struct connman_network *network) |
| { |
| _DBG_PROFILE("network %p", network); |
| |
| __connman_service_update_from_network(network); |
| |
| return 0; |
| } |
| |
| int __connman_profile_remove_network(struct connman_network *network) |
| { |
| _DBG_PROFILE("network %p", network); |
| |
| __connman_service_remove_from_network(network); |
| |
| return 0; |
| } |
| |
| /* |
| * Extract the service type from a profile group name |
| * and return the enumerated type value (if any). Return |
| * CONNMAN_SERVICE_TYPE_UNKNOWN for group names we don't |
| * care about. |
| */ |
| static int get_service_type(const char *gname) |
| { |
| char type[32]; /* NB: known large enough */ |
| int i; |
| |
| /* extract type from group name */ |
| for (i = 0; gname[i] != '_'; i++) { |
| if (gname[i] == '\0') |
| return CONNMAN_SERVICE_TYPE_UNKNOWN; |
| if (i >= sizeof(type)-1) /* -1 for \0 */ |
| return CONNMAN_SERVICE_TYPE_UNKNOWN; |
| type[i] = gname[i]; |
| } |
| type[i] = '\0'; |
| return __connman_service_string2type(type); |
| } |
| |
| static void __profile_entry_list(DBusMessageIter *iter, void *arg) |
| { |
| struct connman_profile *profile = arg; |
| GKeyFile *keyfile; |
| gchar **groups; |
| int i; |
| |
| _DBG_PROFILE("profile %s", __profile_name(profile)); |
| |
| keyfile = __connman_storage_open(&profile->ident); |
| if (keyfile == NULL) |
| return; |
| |
| groups = g_key_file_get_groups(keyfile, NULL); |
| for (i = 0; groups[i] != NULL; i++) { |
| const char *gname = groups[i]; |
| switch (get_service_type(gname)) { |
| case CONNMAN_SERVICE_TYPE_UNKNOWN: |
| case CONNMAN_SERVICE_TYPE_ETHERNET: |
| break; |
| case CONNMAN_SERVICE_TYPE_BLUETOOTH: |
| case CONNMAN_SERVICE_TYPE_WIMAX: |
| /* NB: don't care about these right now */ |
| break; |
| case CONNMAN_SERVICE_TYPE_CELLULAR: |
| case CONNMAN_SERVICE_TYPE_WIFI: |
| case CONNMAN_SERVICE_TYPE_VPN: |
| dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, |
| &gname); |
| break; |
| } |
| } |
| g_strfreev(groups); |
| __connman_storage_close(&profile->ident, keyfile, FALSE); |
| } |
| |
| static DBusMessage *get_properties(DBusConnection *conn, |
| DBusMessage *msg, void *data) |
| { |
| struct connman_profile *profile = data; |
| DBusMessage *reply; |
| DBusMessageIter array, dict; |
| |
| _DBG_PROFILE("conn %p", conn); |
| |
| reply = dbus_message_new_method_return(msg); |
| if (reply == NULL) |
| return NULL; |
| |
| dbus_message_iter_init_append(reply, &array); |
| |
| dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY, |
| DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING |
| DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING |
| DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); |
| |
| if (profile->name != NULL) |
| connman_dbus_dict_append_variant(&dict, "Name", |
| DBUS_TYPE_STRING, &profile->name); |
| |
| if (profile == global_profile()) { |
| gchar *str; |
| |
| connman_dbus_dict_append_variant(&dict, "OfflineMode", |
| DBUS_TYPE_BOOLEAN, &profile->offlinemode); |
| if (profile->country != NULL) |
| connman_dbus_dict_append_variant(&dict, "Country", |
| DBUS_TYPE_STRING, &profile->country); |
| str = __connman_service_mask_to_list(profile->checkportal); |
| if (str != NULL) { |
| connman_dbus_dict_append_variant(&dict, |
| "CheckPortalList", DBUS_TYPE_STRING, &str); |
| g_free(str); |
| } |
| if (profile->portal_url != NULL) |
| connman_dbus_dict_append_variant(&dict, "PortalURL", |
| DBUS_TYPE_STRING, &profile->portal_url); |
| connman_dbus_dict_append_variant(&dict, "ArpGateway", |
| DBUS_TYPE_BOOLEAN, &profile->arpgateway); |
| } |
| |
| if (profile == active_profile()) |
| connman_dbus_dict_append_variant_array(&dict, "Services", |
| DBUS_TYPE_OBJECT_PATH, __connman_service_list, NULL); |
| |
| connman_dbus_dict_append_variant_array(&dict, "Entries", |
| DBUS_TYPE_STRING, __profile_entry_list, profile); |
| |
| dbus_message_iter_close_container(&array, &dict); |
| |
| return reply; |
| } |
| |
| static DBusMessage *set_property(DBusConnection *conn, |
| DBusMessage *msg, void *data) |
| { |
| struct connman_profile *profile = data; |
| DBusMessageIter iter, value; |
| const char *name; |
| int type; |
| |
| _DBG_PROFILE("conn %p", conn); |
| |
| if (dbus_message_iter_init(msg, &iter) == FALSE) |
| return __connman_error_invalid_arguments(msg); |
| |
| dbus_message_iter_get_basic(&iter, &name); |
| dbus_message_iter_next(&iter); |
| dbus_message_iter_recurse(&iter, &value); |
| |
| if (__connman_security_check_privilege(msg, |
| CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0) |
| return __connman_error_permission_denied(msg); |
| |
| type = dbus_message_iter_get_arg_type(&value); |
| |
| if (g_str_equal(name, "Name") == TRUE) { |
| const char *name; |
| |
| if (type != DBUS_TYPE_STRING) |
| return __connman_error_invalid_arguments(msg); |
| |
| dbus_message_iter_get_basic(&value, &name); |
| |
| g_free(profile->name); |
| profile->name = g_strdup(name); |
| |
| if (profile->name != NULL) { |
| connman_dbus_send_property_changed_variant( |
| profile->path, CONNMAN_PROFILE_INTERFACE, "Name", |
| DBUS_TYPE_STRING, &profile->name); |
| } |
| |
| __connman_storage_save_profile(profile); |
| } else |
| return __connman_error_invalid_property(msg); |
| |
| return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); |
| } |
| |
| static void __append_str(DBusMessageIter *dict, const char *name, |
| GKeyFile *keyfile, const char *ident) |
| { |
| char *val = g_key_file_get_string(keyfile, ident, name, NULL); |
| if (val != NULL) { |
| connman_dbus_dict_append_variant(dict, name, |
| DBUS_TYPE_STRING, &val); |
| g_free(val); |
| } |
| } |
| |
| static void __append_bool(DBusMessageIter *dict, const char *name, |
| GKeyFile *keyfile, const char *ident) |
| { |
| char *val = g_key_file_get_string(keyfile, ident, name, NULL); |
| if (val != NULL) { |
| connman_bool_t b = (g_strcmp0(val, "true") == 0 ? TRUE : FALSE); |
| connman_dbus_dict_append_variant(dict, name, |
| DBUS_TYPE_BOOLEAN, &b); |
| g_free(val); |
| } |
| } |
| |
| static DBusMessage *get_entry(DBusConnection *conn, |
| DBusMessage *msg, void *data) |
| { |
| struct connman_profile *profile = data; |
| GKeyFile *keyfile; |
| const char *ident, *val; |
| gchar **tokens; |
| DBusMessageIter iter, array, dict; |
| DBusMessage *reply; |
| int len; |
| |
| if (dbus_message_iter_init(msg, &iter) == FALSE) |
| return __connman_error_invalid_arguments(msg); |
| |
| dbus_message_iter_get_basic(&iter, &ident); |
| |
| keyfile = __connman_storage_open(&profile->ident); |
| if (keyfile == NULL) { |
| _DBG_PROFILE("cannot open keyfile %s", __profile_name(profile)); |
| return __connman_error_not_found(msg); /* XXX */ |
| } |
| |
| if (g_key_file_has_group(keyfile, ident) == FALSE) { |
| _DBG_PROFILE("keyfile %s ident %s not found", |
| __profile_name(profile), ident); |
| __connman_storage_close(&profile->ident, keyfile, FALSE); |
| return __connman_error_not_found(msg); |
| } |
| |
| _DBG_PROFILE("profile %s ident %s", __profile_name(profile), ident); |
| |
| /* |
| * Split group name into components; e.g. |
| * <type>_<device>_<ssid>_<mode>_<security> for wifi |
| */ |
| tokens = g_strsplit(ident, "_", 0); |
| len = g_strv_length(tokens); |
| if (len < 2) { |
| _DBG_PROFILE("profile %s ident %s malformed, len %d", |
| __profile_name(profile), ident, len); |
| g_strfreev(tokens); |
| __connman_storage_close(&profile->ident, keyfile, FALSE); |
| return __connman_error_invalid_arguments(msg); |
| } |
| |
| reply = dbus_message_new_method_return(msg); |
| if (reply == NULL) { |
| _DBG_PROFILE("cannot allocate reply"); |
| g_strfreev(tokens); |
| __connman_storage_close(&profile->ident, keyfile, FALSE); |
| return NULL; |
| } |
| |
| dbus_message_iter_init_append(reply, &array); |
| |
| dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY, |
| DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING |
| DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING |
| DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); |
| |
| val = tokens[0]; |
| connman_dbus_dict_append_variant(&dict, "Type", DBUS_TYPE_STRING, &val); |
| |
| __append_str(&dict, "Name", keyfile, ident); |
| __append_bool(&dict, "AutoConnect", keyfile, ident); |
| __append_str(&dict, "Failure", keyfile, ident); |
| __append_str(&dict, "Modified", keyfile, ident); |
| __append_str(&dict, "Passphrase", keyfile, ident); |
| __append_str(&dict, "UIData", keyfile, ident); |
| __append_str(&dict, "GUID", keyfile, ident); |
| |
| switch (__connman_service_string2type(tokens[0])) { |
| case CONNMAN_SERVICE_TYPE_WIFI: |
| if (len != 5 && len != 6) { |
| _DBG_PROFILE("profile %s ident %s bad token cnt %d", |
| __profile_name(profile), ident, len); |
| break; |
| } |
| val = tokens[3]; |
| connman_dbus_dict_append_variant(&dict, "Mode", |
| DBUS_TYPE_STRING, &val); |
| val = tokens[4]; |
| /* NB: g_strsplit breaks 802_1x into 802+1x; restore */ |
| if (g_strcmp0(val, "802") == 0) |
| val = "802_1x"; |
| connman_dbus_dict_append_variant(&dict, "Security", |
| DBUS_TYPE_STRING, &val); |
| |
| __append_bool(&dict, "WiFi.HiddenSSID", keyfile, ident); |
| break; |
| case CONNMAN_SERVICE_TYPE_VPN: { |
| struct __connman_provider_append_profile_properties_args args; |
| |
| args.keyfile = keyfile; |
| args.ident = ident; |
| connman_dbus_dict_append_dict(&dict, "Provider", |
| __connman_provider_append_profile_properties, &args); |
| break; |
| } |
| case CONNMAN_SERVICE_TYPE_CELLULAR: |
| /* TODO(sleffler) extract cellular attributes */ |
| break; |
| default: |
| break; |
| } |
| |
| dbus_message_iter_close_container(&array, &dict); |
| |
| g_strfreev(tokens); |
| __connman_storage_close(&profile->ident, keyfile, FALSE); |
| |
| return reply; |
| } |
| |
| static DBusMessage *delete_entry(DBusConnection *conn, |
| DBusMessage *msg, void *data) |
| { |
| struct connman_profile *profile = data; |
| DBusMessageIter iter; |
| const char *identifier; |
| struct connman_service *service; |
| int err; |
| |
| if (dbus_message_iter_init(msg, &iter) == FALSE) |
| return __connman_error_invalid_arguments(msg); |
| |
| dbus_message_iter_get_basic(&iter, &identifier); |
| |
| _DBG_PROFILE("profile %s:%s %s", profile->ident.user, |
| profile->ident.ident, identifier); |
| |
| if (__connman_security_check_privilege(msg, |
| CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0) |
| return __connman_error_permission_denied(msg); |
| |
| service = __connman_service_lookup(identifier); |
| if (service != NULL) { |
| __connman_service_disconnect(service); |
| |
| /* NB: this does not remove the service */ |
| __connman_service_reset_in_memory(service); |
| __connman_service_set_profile(service, NULL); |
| } |
| |
| /* Remove directly from profile */ |
| err = __connman_profile_delete_entry(profile, identifier); |
| if (err) |
| return __connman_error_failed(msg, -err); |
| |
| return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); |
| } |
| |
| static GDBusMethodTable profile_methods[] = { |
| { "GetProperties", "", "a{sv}", get_properties }, |
| { "SetProperty", "sv", "", set_property }, |
| { "GetEntry", "s", "a{sv}", get_entry }, |
| { "DeleteEntry", "s", "", delete_entry }, |
| { }, |
| }; |
| |
| static GDBusSignalTable profile_signals[] = { |
| { "PropertyChanged", "sv" }, |
| { }, |
| }; |
| |
| static void free_ident(struct connman_storage_ident *ident) |
| { |
| /* NB: blech, don't reclaim, it's statically allocated */ |
| if (!ident_equal(ident, &default_ident)) { |
| g_free(ident->user); |
| g_free(ident->ident); |
| } |
| } |
| |
| static void free_profile(struct connman_profile *profile) |
| { |
| free_ident(&profile->ident); |
| g_free(profile->path); |
| g_free(profile->name); |
| g_free(profile); |
| } |
| |
| static void unregister_profile(gpointer data) |
| { |
| struct connman_profile *profile = data; |
| |
| connman_info("Remove profile %s:%s", profile->ident.user, |
| profile->ident.ident); |
| |
| g_dbus_unregister_interface(connection, profile->path, |
| CONNMAN_PROFILE_INTERFACE); |
| |
| clear_timeout(profile); |
| |
| if (profile == default_profile) |
| default_profile = NULL; |
| |
| free_profile(profile); |
| } |
| |
| static char *getpath(const struct connman_storage_ident *ident) |
| { |
| if (ident->user != NULL) { |
| /* NB: check for two tokens done in validate_ident */ |
| return g_strdup_printf("/profile/%s/%s", |
| ident->user, ident->ident); |
| } else |
| return g_strdup_printf("/profile/%s", ident->ident); |
| } |
| |
| /* |
| * Create an in-memory profile using the specified identifier |
| * and name. The object path and a reference to the profile |
| * data structure are returned on success. |
| */ |
| static int create_profile(struct connman_storage_ident *ident, |
| const char *name, const char **path, struct connman_profile **pprofile) |
| { |
| struct connman_profile *profile; |
| |
| _DBG_PROFILE("ident %s:%s name %s", ident->user, ident->ident, name); |
| |
| profile = g_try_new0(struct connman_profile, 1); |
| if (profile == NULL) { |
| free_ident(ident); |
| return -ENOMEM; |
| } |
| |
| profile->ident = *ident; |
| profile->path = getpath(ident); |
| /* TODO(sleffler) check ident.user */ |
| if (profile->ident.ident == NULL || profile->path == NULL) { |
| free_profile(profile); |
| return -ENOMEM; |
| } |
| |
| if (g_hash_table_lookup(profile_hash, profile->path) != NULL) { |
| free_profile(profile); |
| return -EEXIST; |
| } |
| |
| profile->checkportal = profile_default_checkportal_mask; |
| profile->name = g_strdup(name); |
| |
| __connman_storage_load_profile(profile); |
| |
| g_hash_table_insert(profile_hash, g_strdup(profile->path), profile); |
| |
| connman_info("Add profile %s:%s", ident->user, ident->ident); |
| |
| if (ident_equal(ident, &default_ident)) |
| default_profile = profile; |
| |
| g_dbus_register_interface(connection, profile->path, |
| CONNMAN_PROFILE_INTERFACE, |
| profile_methods, profile_signals, |
| NULL, profile, NULL); |
| |
| if (path != NULL) |
| *path = profile->path; |
| |
| _DBG_PROFILE("profile %p path %s", profile, profile->path); |
| |
| *pprofile = profile; |
| return 0; |
| } |
| |
| /* |
| * Check a profile identifier token. This must be non-null and |
| * made up of alpha-numeric chars suitable for use in a D-bus |
| * object path. |
| */ |
| static gboolean validate_token(const char *ident) |
| { |
| unsigned int i; |
| unsigned int len = strlen(ident); |
| |
| if (len < 1) |
| return FALSE; |
| |
| for (i = 0; i < len; i++) { |
| if (ident[i] >= '0' && ident[i] <= '9') |
| continue; |
| if (ident[i] >= 'a' && ident[i] <= 'z') |
| continue; |
| if (ident[i] >= 'A' && ident[i] <= 'Z') |
| continue; |
| return FALSE; |
| } |
| |
| return TRUE; |
| } |
| |
| /* |
| * Validate the profile identifier. It must be suitable |
| * for use in a D-Bus object path and, optionally, be of |
| * the form ~user/ident to signify a per-user profile. |
| */ |
| static gboolean parse_ident(const char *str, |
| struct connman_storage_ident *ident) |
| { |
| gboolean is_valid; |
| |
| if (str[0] == '~') { |
| /* syntax is ~user/name for profile in cryptohome */ |
| gchar **tokens = g_strsplit_set(str, "~/", 4); |
| if (g_strv_length(tokens) == 3) { |
| is_valid = (validate_token(tokens[1]) == TRUE && |
| validate_token(tokens[2]) == TRUE); |
| if (is_valid) { |
| ident->user = g_strdup(tokens[1]); |
| ident->ident = g_strdup(tokens[2]); |
| } |
| } else |
| is_valid = FALSE; |
| g_strfreev(tokens); |
| } else { |
| is_valid = validate_token(str); |
| if (is_valid) { |
| ident->user = NULL; |
| ident->ident = g_strdup(str); |
| } |
| } |
| return is_valid; |
| } |
| |
| /* |
| * Lookup a profile on the stack by object path. |
| */ |
| static struct connman_profile *lookup_stack_by_path(const char *path) |
| { |
| int i; |
| |
| for (i = cur_profile; i >= 0; i--) |
| if (g_strcmp0(profile_stack[i]->path, path) == 0) |
| return profile_stack[i]; |
| return NULL; |
| } |
| |
| /* |
| * Push a profile on the stack and make it the ``active profile''. |
| * The profile may be currently registered in memory or previously |
| * created by CreateProfile. The profile may not already be on |
| * the stack. |
| */ |
| int __connman_profile_push(const char *ident, const char *name, |
| const char **path) |
| { |
| struct connman_profile *profile; |
| struct connman_storage_ident sid; |
| char *tmp_path; |
| int err; |
| |
| _DBG_PROFILE("ident %s name %s", ident, name); |
| |
| if (parse_ident(ident, &sid) == FALSE) { |
| connman_error("%s: invalid profile name %s", __func__, ident); |
| return -EINVAL; |
| } |
| |
| if (cur_profile+1 >= PROFILE_MAX) { |
| connman_error("%s: too many profiles (max %d)", __func__, |
| PROFILE_MAX); |
| free_ident(&sid); |
| return -EMFILE; |
| } |
| |
| /* |
| * Check for in-memory profile by way of a CreateProfile request. |
| */ |
| tmp_path = getpath(&sid); |
| if (tmp_path == NULL) { |
| connman_error("%s: no memory for %s", __func__, ident); |
| free_ident(&sid); |
| return -ENOMEM; |
| } |
| |
| profile = g_hash_table_lookup(profile_hash, tmp_path); |
| g_free(tmp_path); |
| if (profile == NULL) { |
| /* |
| * Not in memory; accept an existing file. |
| */ |
| if (__connman_storage_exists(&sid) == FALSE) { |
| connman_error("%s: profile %s does not exist", |
| __func__, ident); |
| free_ident(&sid); |
| return -ENOENT; |
| } |
| |
| err = create_profile(&sid, name, path, &profile); |
| if (err < 0) { |
| connman_error("%s: cannot open, error %d", |
| __func__, err); |
| /* NB: create_profile reclaims sid */ |
| return err; |
| } |
| } else { |
| free_ident(&sid); /* NB: not needed below */ |
| /* |
| * Check this profile is not already on the stack. |
| */ |
| if (lookup_stack_by_path(profile->path) != NULL) { |
| connman_error("%s: already pushed", __func__); |
| return -EEXIST; |
| } |
| if (path != NULL) |
| *path = profile->path; |
| } |
| |
| profile_stack[++cur_profile] = profile; |
| |
| profiles_changed(); |
| |
| __connman_notifier_profile_push(profile); |
| |
| return 0; |
| } |
| |
| /* |
| * Pop the profile from the top of the stack and remove it from |
| * the in-memory table. Any associated services are invalidated |
| * (credentials revoked and connections closed). After a pop we |
| * generate a PropertyChanged signal for Manager.Profiles. |
| * |
| * An optional identifier may be specified. If specified it is |
| * checked against the active profile and if not the same then |
| * the request is rejected. This is useful to ensure the right |
| * profile is pop'd (as might happen on logout if an associated |
| * push failed for some reason). |
| */ |
| int __connman_profile_pop(const char *ident) |
| { |
| struct connman_profile *profile; |
| |
| _DBG_PROFILE("ident %s", ident); |
| |
| if (cur_profile < 0) { |
| connman_error("%s: profile stack empty", __func__); |
| return -EINVAL; |
| } |
| |
| profile = profile_stack[cur_profile]; |
| |
| if (ident != NULL) { |
| struct connman_storage_ident sid; |
| |
| if (parse_ident(ident, &sid) == FALSE) { |
| connman_error("%s: invalid profile name %s", |
| __func__, ident); |
| return -EINVAL; |
| } |
| if (ident_equal(&profile->ident, &sid) == FALSE) { |
| connman_error("%s: %s is not the active profile", |
| __func__, ident); |
| free_ident(&sid); |
| return -ENXIO; |
| } |
| free_ident(&sid); |
| } |
| |
| /* |
| * Pop the stack, invalidate all objects holding references |
| * to the profile, then remove it from the in-memory table. |
| * We do the reclaim after notification in case anyone holding |
| * a reference tries to use it. |
| */ |
| cur_profile--; |
| |
| __connman_notifier_profile_pop(profile); |
| |
| g_hash_table_remove(profile_hash, profile->path); |
| |
| profiles_changed(); |
| |
| return 0; |
| } |
| |
| /* |
| * Create a profile file and register it in memory. The |
| * file is created with minimal contents. |
| * TODO(sleffler) disallow overwriting an existing file? |
| */ |
| int __connman_profile_create(const char *name, const char **path) |
| { |
| struct connman_profile *profile; |
| struct connman_storage_ident sid; |
| int err; |
| |
| _DBG_PROFILE("name %s", name); |
| |
| if (parse_ident(name, &sid) == FALSE) { |
| connman_error("%s: invalid profile name %s)", __func__, name); |
| return -EINVAL; |
| } |
| |
| err = create_profile(&sid, NULL, path, &profile); |
| if (err < 0) { |
| connman_error("%s: cannot open, error %d", __func__, err); |
| /* NB: create_profile reclaims sid */ |
| return err; |
| } |
| |
| __connman_storage_save_profile(profile); |
| |
| profiles_changed(); |
| |
| return 0; |
| } |
| |
| /* |
| * Delete a profile and remove from memory. The default |
| * profile may not be removed/deleted. Likewise, one |
| * cannot remove a profile pushed on the stack. |
| */ |
| int __connman_profile_remove(const char *ident) |
| { |
| struct connman_profile *profile; |
| struct connman_storage_ident sid; |
| char *tmp_path; |
| int err = 0; |
| |
| _DBG_PROFILE("ident %s", ident); |
| |
| if (parse_ident(ident, &sid) == FALSE) { |
| connman_error("%s: invalid profile name %s)", __func__, ident); |
| return -EINVAL; |
| } |
| |
| if (ident_equal(&sid, &default_ident)) { |
| /* TODO(sleffler) should this be permitted? */ |
| connman_error("%s: cannot delete default profile", __func__); |
| err = -EINVAL; |
| goto done; |
| } |
| |
| /* |
| * Check for in-memory profile by way of a CreateProfile request. |
| */ |
| tmp_path = getpath(&sid); |
| if (tmp_path == NULL) { |
| connman_error("%s: no memory for %s", __func__, ident); |
| err = -ENOMEM; |
| goto done; |
| } |
| profile = g_hash_table_lookup(profile_hash, tmp_path); |
| g_free(tmp_path); |
| |
| if (profile != NULL) { |
| /* |
| * Check this profile is not on the stack. |
| */ |
| if (lookup_stack_by_path(profile->path) != NULL) { |
| connman_error("%s: cannot delete (on the stack)", |
| __func__); |
| err = -EEXIST; |
| goto done; |
| } |
| g_hash_table_remove(profile_hash, profile->path); |
| |
| profiles_changed(); |
| } |
| |
| __connman_storage_delete(&sid); |
| done: |
| free_ident(&sid); |
| return err; |
| } |
| |
| /* |
| * Initialize the profile stack by pushing the default |
| * (global) profile. Unlike connman we do not read in |
| * all available .profile's in the global dir. |
| */ |
| static int profile_init(void) |
| { |
| struct connman_profile *profile; |
| int err; |
| |
| err = create_profile(&default_ident, "Default", NULL, &profile); |
| if (err == 0) |
| profile_stack[++cur_profile] = profile; |
| return err; |
| } |
| |
| static int profile_load(struct connman_profile *profile) |
| { |
| GKeyFile *keyfile; |
| GError *error = NULL; |
| connman_bool_t offlinemode; |
| connman_bool_t arpgateway; |
| char *name, *country, *str; |
| |
| _DBG_PROFILE("profile %s", __profile_name(profile)); |
| |
| keyfile = __connman_storage_open(&profile->ident); |
| if (keyfile == NULL) |
| return -EIO; |
| |
| name = g_key_file_get_string(keyfile, "global", "Name", NULL); |
| if (name != NULL) { |
| g_free(profile->name); |
| profile->name = name; |
| } |
| |
| country = g_key_file_get_string(keyfile, "global", "Country", NULL); |
| if (country != NULL) { |
| g_free(profile->country); |
| profile->country = country; |
| } |
| |
| str = g_key_file_get_string(keyfile, "global", "CheckPortalList", NULL); |
| if (str != NULL) { |
| profile->checkportal = __connman_service_list_to_mask(str); |
| g_free(str); |
| } else |
| profile->checkportal = profile_default_checkportal_mask; |
| |
| str = g_key_file_get_string(keyfile, "global", "PortalURL", NULL); |
| if (str != NULL) { |
| g_free(profile->portal_url); |
| profile->portal_url = str; |
| } |
| |
| offlinemode = g_key_file_get_boolean(keyfile, "global", |
| "OfflineMode", &error); |
| if (error == NULL) |
| profile->offlinemode = offlinemode; |
| g_clear_error(&error); |
| |
| |
| arpgateway = g_key_file_get_boolean(keyfile, "global", |
| "ArpGateway", &error); |
| if (error == NULL) |
| profile->arpgateway = arpgateway; |
| else |
| profile->arpgateway = TRUE; |
| |
| g_clear_error(&error); |
| |
| __connman_storage_close(&profile->ident, keyfile, FALSE); |
| |
| return 0; |
| } |
| |
| static int profile_save(struct connman_profile *profile) |
| { |
| GKeyFile *keyfile; |
| gchar *str; |
| |
| _DBG_PROFILE("profile %s", __profile_name(profile)); |
| |
| keyfile = __connman_storage_open(&profile->ident); |
| if (keyfile == NULL) |
| return -EIO; |
| |
| if (profile->name != NULL) |
| g_key_file_set_string(keyfile, "global", |
| "Name", profile->name); |
| |
| if (profile->country != NULL) |
| g_key_file_set_string(keyfile, "global", |
| "Country", profile->country); |
| |
| str = (profile->checkportal != profile_default_checkportal_mask)? |
| __connman_service_mask_to_list(profile->checkportal):NULL; |
| if (str != NULL) { |
| g_key_file_set_string(keyfile, "global", |
| "CheckPortalList", str); |
| g_free(str); |
| } |
| |
| if (profile->portal_url != NULL) |
| g_key_file_set_string(keyfile, "global", |
| "PortalURL", profile->portal_url); |
| |
| g_key_file_set_boolean(keyfile, "global", |
| "OfflineMode", profile->offlinemode); |
| |
| g_key_file_set_boolean(keyfile, "global", |
| "ArpGateway", profile->arpgateway); |
| |
| __connman_storage_close(&profile->ident, keyfile, TRUE); |
| |
| return 0; |
| } |
| |
| static struct connman_storage profile_storage = { |
| .name = "profile", |
| .priority = CONNMAN_STORAGE_PRIORITY_LOW, |
| .profile_init = profile_init, |
| .profile_load = profile_load, |
| .profile_save = profile_save, |
| }; |
| |
| int __connman_profile_init(void) |
| { |
| const char *portal_list; |
| |
| _DBG_PROFILE(""); |
| |
| connection = connman_dbus_get_connection(); |
| if (connection == NULL) |
| return -1; |
| |
| if (connman_storage_register(&profile_storage) < 0) |
| connman_error("Failed to register profile storage"); |
| |
| profile_hash = g_hash_table_new_full(g_str_hash, g_str_equal, |
| g_free, unregister_profile); |
| |
| portal_list = connman_option_get_string("portal-list"); |
| if (portal_list != NULL) |
| profile_default_checkportal_mask = |
| __connman_service_list_to_mask(portal_list); |
| |
| return 0; |
| } |
| |
| int __connman_profile_push_batch(char **profiles) |
| { |
| int i; |
| |
| if (profiles == NULL) |
| return 0; |
| |
| for (i = 0; profiles[i] != NULL; i++) { |
| int err = __connman_profile_push(profiles[i], NULL, NULL); |
| if (err != 0) |
| return err; |
| } |
| return 0; |
| } |
| |
| void __connman_profile_cleanup(void) |
| { |
| _DBG_PROFILE(""); |
| |
| if (connection == NULL) |
| return; |
| |
| while (cur_profile >= 0) |
| __connman_profile_pop(NULL); |
| |
| clear_timeout(NULL); /* NB: clear global timer */ |
| |
| g_hash_table_destroy(profile_hash); |
| profile_hash = NULL; |
| |
| connman_storage_unregister(&profile_storage); |
| |
| dbus_connection_unref(connection); |
| } |