/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 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 #endif #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #ifndef DBUS_TYPE_UNIX_FD #define DBUS_TYPE_UNIX_FD -1 #endif #define CONNMAN_SERVICE "net.connman" #define CONNMAN_PATH "/net/connman" #define CONNMAN_MANAGER_INTERFACE CONNMAN_SERVICE ".Manager" #define CONNMAN_MANAGER_PATH "/" static DBusConnection *connection; static GHashTable *requests; static unsigned int id; struct connman_req { int uid; DBusPendingCall *pending; ofono_private_network_cb_t cb; void *data; gboolean redundant; char *path; }; static void send_release(const char *path) { DBusMessage *message; message = dbus_message_new_method_call(CONNMAN_SERVICE, CONNMAN_MANAGER_PATH, CONNMAN_MANAGER_INTERFACE, "ReleasePrivateNetwork"); if (message == NULL) return; dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); dbus_message_set_no_reply(message, TRUE); dbus_connection_send(connection, message, NULL); dbus_message_unref(message); } static void connman_release(int uid) { struct connman_req *req; DBG(""); req = g_hash_table_lookup(requests, &uid); if (req == NULL) return; if (req->pending) { /* * We want to cancel the request but we have to wait * the response of ConnMan. So we mark request as * redundant until we get the response, then we remove * it from hash table. */ req->redundant = TRUE; return; } send_release(req->path); g_hash_table_remove(requests, &req->uid); } static gboolean parse_reply(DBusMessage *reply, const char **path, struct ofono_private_network_settings *pns) { DBusMessageIter array, dict, entry; if (!reply) return FALSE; if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) return FALSE; if (dbus_message_iter_init(reply, &array) == FALSE) return FALSE; if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_OBJECT_PATH) return FALSE; dbus_message_iter_get_basic(&array, path); dbus_message_iter_next(&array); if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY) return FALSE; dbus_message_iter_recurse(&array, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter iter; const char *key; int type; dbus_message_iter_recurse(&dict, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &iter); type = dbus_message_iter_get_arg_type(&iter); if (type != DBUS_TYPE_STRING) break; if (g_str_equal(key, "ServerIPv4") && type == DBUS_TYPE_STRING) dbus_message_iter_get_basic(&iter, &pns->server_ip); else if (g_str_equal(key, "PeerIPv4") && type == DBUS_TYPE_STRING) dbus_message_iter_get_basic(&iter, &pns->peer_ip); else if (g_str_equal(key, "PrimaryDNS") && type == DBUS_TYPE_STRING) dbus_message_iter_get_basic(&iter, &pns->primary_dns); else if (g_str_equal(key, "SecondaryDNS") && type == DBUS_TYPE_STRING) dbus_message_iter_get_basic(&iter, &pns->secondary_dns); dbus_message_iter_next(&dict); } dbus_message_iter_next(&array); if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_UNIX_FD) return FALSE; dbus_message_iter_get_basic(&array, &pns->fd); return TRUE; } static void request_reply(DBusPendingCall *call, void *user_data) { struct connman_req *req = user_data; DBusMessage *reply; const char *path = NULL; struct ofono_private_network_settings pns; DBG(""); req->pending = NULL; memset(&pns, 0, sizeof(pns)); pns.fd = -1; reply = dbus_pending_call_steal_reply(call); if (reply == NULL) goto badreply; if (parse_reply(reply, &path, &pns) == FALSE) goto error; DBG("fd: %d, path: %s", pns.fd, path); if (req->redundant == TRUE) goto redundant; if (pns.server_ip == NULL || pns.peer_ip == NULL || pns.primary_dns == NULL || pns.secondary_dns == NULL || pns.fd < 0) { ofono_error("Error while reading dictionary...\n"); goto error; } req->path = g_strdup(path); req->cb(&pns, req->data); dbus_message_unref(reply); dbus_pending_call_unref(call); return; error: redundant: if (pns.fd != -1) close(pns.fd); if (path != NULL) send_release(path); dbus_message_unref(reply); badreply: if (req->redundant == FALSE) req->cb(NULL, req->data); g_hash_table_remove(requests, &req->uid); dbus_pending_call_unref(call); } static int connman_request(ofono_private_network_cb_t cb, void *data) { DBusMessage *message; DBusPendingCall *call; struct connman_req *req; DBG(""); if (DBUS_TYPE_UNIX_FD < 0) return -EBADF; req = g_try_new(struct connman_req, 1); if (req == NULL) return -ENOMEM; message = dbus_message_new_method_call(CONNMAN_SERVICE, CONNMAN_MANAGER_PATH, CONNMAN_MANAGER_INTERFACE, "RequestPrivateNetwork"); if (message == NULL) { g_free(req); return -ENOMEM; } if (dbus_connection_send_with_reply(connection, message, &call, 5000) == FALSE) { g_free(req); dbus_message_unref(message); return -EIO; } id++; req->pending = call; req->cb = cb; req->data = data; req->uid = id; req->redundant = FALSE; req->path = NULL; dbus_pending_call_set_notify(call, request_reply, req, NULL); g_hash_table_insert(requests, &req->uid, req); dbus_message_unref(message); return req->uid; } static struct ofono_private_network_driver pn_driver = { .name = "ConnMan Private Network", .request = connman_request, .release = connman_release, }; static void request_free(gpointer user_data) { struct connman_req *req = user_data; g_free(req->path); g_free(req); } static int connman_init(void) { DBG(""); connection = ofono_dbus_get_connection(); requests = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, request_free); return ofono_private_network_driver_register(&pn_driver); } static void connman_exit(void) { g_hash_table_destroy(requests); ofono_private_network_driver_unregister(&pn_driver); } OFONO_PLUGIN_DEFINE(connman, "ConnMan plugin", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, connman_init, connman_exit)