diff options
| author | Sjoerd Simons <sjoerd@luon.net> | 2013-10-25 21:20:58 +0200 |
|---|---|---|
| committer | Sjoerd Simons <sjoerd@luon.net> | 2013-10-25 21:20:58 +0200 |
| commit | a4256940da049f91bbbea5e53e469a59ea9c10f7 (patch) | |
| tree | 2211c102aea3a33f34f960f48c8b8a55daccd97b /src/nm-connectivity.c | |
| parent | 8d275584fc94f398f0a7c990dcd057533d9a5856 (diff) | |
Imported Upstream version 0.9.8.8 upstream/0.9.8.8
Diffstat (limited to 'src/nm-connectivity.c')
| -rw-r--r-- | src/nm-connectivity.c | 298 |
1 files changed, 171 insertions, 127 deletions
diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c index 7e0de2c1..7e3bed99 100644 --- a/src/nm-connectivity.c +++ b/src/nm-connectivity.c @@ -22,7 +22,9 @@ #include <config.h> #include <string.h> +#if WITH_CONCHECK #include <libsoup/soup.h> +#endif #include "nm-connectivity.h" #include "nm-logging.h" @@ -36,159 +38,205 @@ G_DEFINE_TYPE (NMConnectivity, nm_connectivity, G_TYPE_OBJECT) #define DEFAULT_RESPONSE "NetworkManager is online" /* NOT LOCALIZED */ typedef struct { - /* used for http requests */ - SoupSession *soup_session; - /* indicates if a connectivity check is currently running */ - gboolean running; - /* the uri to check */ char *uri; - /* seconds when a check will be repeated */ - guint interval; - /* the expected response for the connectivity check */ char *response; - /* indicates if the last connection check was successful */ - gboolean connected; - /* the source id for the periodic check */ + guint interval; + +#if WITH_CONCHECK + SoupSession *soup_session; + gboolean running; guint check_id; +#endif + + NMConnectivityState state; } NMConnectivityPrivate; enum { PROP_0, - PROP_RUNNING, PROP_URI, PROP_INTERVAL, PROP_RESPONSE, - PROP_CONNECTED, + PROP_STATE, LAST_PROP }; -gboolean -nm_connectivity_get_connected (NMConnectivity *connectivity) +NMConnectivityState +nm_connectivity_get_state (NMConnectivity *connectivity) { - g_return_val_if_fail (NM_IS_CONNECTIVITY (connectivity), FALSE); + g_return_val_if_fail (NM_IS_CONNECTIVITY (connectivity), NM_CONNECTIVITY_UNKNOWN); - return NM_CONNECTIVITY_GET_PRIVATE (connectivity)->connected; + return NM_CONNECTIVITY_GET_PRIVATE (connectivity)->state; } static void -update_connected (NMConnectivity *self, gboolean connected) +update_state (NMConnectivity *self, NMConnectivityState state) { NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); - gboolean old_connected = priv->connected; - if (priv->uri == NULL || priv->interval == 0) { - /* Default to connected if no checks are to be run */ - priv->connected = TRUE; - } else - priv->connected = connected; - - if (priv->connected != old_connected) - g_object_notify (G_OBJECT (self), NM_CONNECTIVITY_CONNECTED); + if (priv->state != state) { + priv->state = state; + g_object_notify (G_OBJECT (self), NM_CONNECTIVITY_STATE); + } } +#if WITH_CONCHECK static void nm_connectivity_check_cb (SoupSession *session, SoupMessage *msg, gpointer user_data) { - NMConnectivity *self = NM_CONNECTIVITY (user_data); - NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); - SoupURI *soup_uri; - gboolean connected_new = FALSE; + GSimpleAsyncResult *simple = user_data; + NMConnectivity *self; + NMConnectivityPrivate *priv; + NMConnectivityState new_state; const char *nm_header; - char *uri_string; - soup_uri = soup_message_get_uri (msg); - uri_string = soup_uri_to_string (soup_uri, FALSE); + self = NM_CONNECTIVITY (g_async_result_get_source_object (G_ASYNC_RESULT (simple))); + g_object_unref (self); + priv = NM_CONNECTIVITY_GET_PRIVATE (self); + + if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) { + nm_log_info (LOGD_CONCHECK, "Connectivity check for uri '%s' failed with '%s'.", + priv->uri, msg->reason_phrase); + new_state = NM_CONNECTIVITY_LIMITED; + goto done; + } /* Check headers; if we find the NM-specific one we're done */ nm_header = soup_message_headers_get_one (msg->response_headers, "X-NetworkManager-Status"); if (g_strcmp0 (nm_header, "online") == 0) { - nm_log_dbg (LOGD_CORE, "Connectivity check for uri '%s' with Status header successful.", uri_string); - connected_new = TRUE; - } else { + nm_log_dbg (LOGD_CONCHECK, "Connectivity check for uri '%s' with Status header successful.", priv->uri); + new_state = NM_CONNECTIVITY_FULL; + } else if (msg->status_code == SOUP_STATUS_OK) { /* check response */ if (msg->response_body->data && (g_str_has_prefix (msg->response_body->data, priv->response))) { - nm_log_dbg (LOGD_CORE, "Connectivity check for uri '%s' with expected response '%s' successful.", - uri_string, priv->response); - connected_new = TRUE; + nm_log_dbg (LOGD_CONCHECK, "Connectivity check for uri '%s' successful.", + priv->uri); + new_state = NM_CONNECTIVITY_FULL; } else { - nm_log_dbg (LOGD_CORE, "Connectivity check for uri '%s' with expected response '%s' failed (status %d).", - uri_string, priv->response, msg->status_code); + nm_log_info (LOGD_CONCHECK, "Connectivity check for uri '%s' did not match expected response '%s'; assuming captive portal.", + priv->uri, priv->response); + new_state = NM_CONNECTIVITY_PORTAL; } + } else { + nm_log_info (LOGD_CONCHECK, "Connectivity check for uri '%s' returned status '%d %s'; assuming captive portal.", + priv->uri, msg->status_code, msg->reason_phrase); + new_state = NM_CONNECTIVITY_PORTAL; } - g_free (uri_string); - /* update connectivity and emit signal */ - update_connected (self, connected_new); + done: + g_simple_async_result_set_op_res_gssize (simple, new_state); + g_simple_async_result_complete (simple); + update_state (self, new_state); +} + +static void +run_check_complete (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + NMConnectivity *self = NM_CONNECTIVITY (object); + NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); + GError *error = NULL; + + nm_connectivity_check_finish (self, result, &error); priv->running = FALSE; - g_object_notify (G_OBJECT (self), NM_CONNECTIVITY_RUNNING); + if (error) { + nm_log_err (LOGD_CONCHECK, "Connectivity check failed: %s", error->message); + g_error_free (error); + } } static gboolean run_check (gpointer user_data) { NMConnectivity *self = NM_CONNECTIVITY (user_data); - NMConnectivityPrivate *priv; - SoupURI *soup_uri; - SoupMessage *msg; - - g_return_val_if_fail (NM_IS_CONNECTIVITY (self), FALSE); - priv = NM_CONNECTIVITY_GET_PRIVATE (self); - - /* check given url async */ - soup_uri = soup_uri_new (priv->uri); - if (soup_uri && SOUP_URI_VALID_FOR_HTTP (soup_uri)) { - msg = soup_message_new_from_uri ("GET", soup_uri); - soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT); - soup_session_queue_message (priv->soup_session, - msg, - nm_connectivity_check_cb, - self); - - priv->running = TRUE; - g_object_notify (G_OBJECT (self), NM_CONNECTIVITY_RUNNING); - nm_log_dbg (LOGD_CORE, "Connectivity check with uri '%s' started.", priv->uri); - } else - nm_log_err (LOGD_CORE, "Invalid uri '%s' for connectivity check.", priv->uri); + NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); - if (soup_uri) - soup_uri_free (soup_uri); + nm_connectivity_check_async (self, run_check_complete, NULL); + priv->running = TRUE; + nm_log_dbg (LOGD_CONCHECK, "Connectivity check with uri '%s' started.", priv->uri); - return TRUE; /* keep firing */ + return TRUE; } +#endif void -nm_connectivity_start_check (NMConnectivity *self) +nm_connectivity_set_online (NMConnectivity *self, + gboolean online) { +#if WITH_CONCHECK NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); - if (!priv->uri || !priv->interval) { - nm_connectivity_stop_check (self); + if (online && priv->uri && priv->interval) { + if (!priv->check_id) + priv->check_id = g_timeout_add_seconds (priv->interval, run_check, self); + if (!priv->running) + run_check (self); + return; + } else if (priv->check_id) { + g_source_remove (priv->check_id); + priv->check_id = 0; } +#endif - if (priv->check_id == 0) - priv->check_id = g_timeout_add_seconds (priv->interval, run_check, self); - - /* Start an immediate check */ - if (priv->running == FALSE) - run_check (self); + /* Either @online is %TRUE but we aren't checking connectivity, or + * @online is %FALSE. Either way we can update our status immediately. + */ + update_state (self, online ? NM_CONNECTIVITY_FULL : NM_CONNECTIVITY_NONE); } void -nm_connectivity_stop_check (NMConnectivity *self) +nm_connectivity_check_async (NMConnectivity *self, + GAsyncReadyCallback callback, + gpointer user_data) { - NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); + NMConnectivityPrivate *priv; +#if WITH_CONCHECK + SoupMessage *msg; +#endif + GSimpleAsyncResult *simple; - if (priv->check_id) { - g_source_remove (priv->check_id); - priv->check_id = 0; + g_return_if_fail (NM_IS_CONNECTIVITY (self)); + priv = NM_CONNECTIVITY_GET_PRIVATE (self); + + simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, + nm_connectivity_check_async); + +#if WITH_CONCHECK + if (priv->uri && priv->interval) { + msg = soup_message_new ("GET", priv->uri); + soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT); + soup_session_queue_message (priv->soup_session, + msg, + nm_connectivity_check_cb, + simple); + + return; } +#endif + + g_simple_async_result_set_op_res_gssize (simple, priv->state); + g_simple_async_result_complete_in_idle (simple); +} - update_connected (self, FALSE); +NMConnectivityState +nm_connectivity_check_finish (NMConnectivity *self, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), nm_connectivity_check_async), NM_CONNECTIVITY_UNKNOWN); + + simple = G_SIMPLE_ASYNC_RESULT (result); + if (g_simple_async_result_propagate_error (simple, error)) + return NM_CONNECTIVITY_UNKNOWN; + return (NMConnectivityState) g_simple_async_result_get_op_res_gssize (simple); } + NMConnectivity * nm_connectivity_new (const gchar *check_uri, guint check_interval, @@ -202,23 +250,21 @@ nm_connectivity_new (const gchar *check_uri, NM_CONNECTIVITY_RESPONSE, check_response ? check_response : DEFAULT_RESPONSE, NULL); g_return_val_if_fail (self != NULL, NULL); - update_connected (self, FALSE); + update_state (self, NM_CONNECTIVITY_NONE); return self; } static char * -sanitize_string_val (const GValue *val) +get_non_empty_string_value (const GValue *val) { - char *s; + const char *s; - /* Return NULL if string is NULL or zero-length */ - s = g_value_dup_string (val); - if (!s || !s[0]) { - g_free (s); + s = g_value_get_string (val); + if (s && s[0]) + return g_strdup (s); + else return NULL; - } - return s; } static void @@ -229,22 +275,28 @@ set_property (GObject *object, guint property_id, NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); switch (property_id) { - case PROP_RUNNING: - priv->running = g_value_get_boolean (value); - break; case PROP_URI: g_free (priv->uri); - priv->uri = sanitize_string_val (value); + priv->uri = get_non_empty_string_value (value); + +#if WITH_CONCHECK + if (priv->uri) { + SoupURI *uri = soup_uri_new (priv->uri); + + if (!uri || !SOUP_URI_VALID_FOR_HTTP (uri)) { + nm_log_err (LOGD_CONCHECK, "Invalid uri '%s' for connectivity check.", priv->uri); + g_free (priv->uri); + priv->uri = NULL; + } + } +#endif break; case PROP_INTERVAL: priv->interval = g_value_get_uint (value); break; case PROP_RESPONSE: g_free (priv->response); - priv->response = sanitize_string_val (value); - break; - case PROP_CONNECTED: - priv->connected = g_value_get_boolean (value); + priv->response = get_non_empty_string_value (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -260,9 +312,6 @@ get_property (GObject *object, guint property_id, NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); switch (property_id) { - case PROP_RUNNING: - g_value_set_boolean (value, priv->running); - break; case PROP_URI: g_value_set_string (value, priv->uri); break; @@ -272,8 +321,8 @@ get_property (GObject *object, guint property_id, case PROP_RESPONSE: g_value_set_string (value, priv->response); break; - case PROP_CONNECTED: - g_value_set_boolean (value, priv->connected); + case PROP_STATE: + g_value_set_uint (value, priv->state); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -285,9 +334,11 @@ get_property (GObject *object, guint property_id, static void nm_connectivity_init (NMConnectivity *self) { +#if WITH_CONCHECK NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); priv->soup_session = soup_session_async_new_with_options (SOUP_SESSION_TIMEOUT, 15, NULL); +#endif } @@ -297,19 +348,20 @@ dispose (GObject *object) NMConnectivity *self = NM_CONNECTIVITY (object); NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); + g_free (priv->uri); + g_free (priv->response); + +#if WITH_CONCHECK if (priv->soup_session) { soup_session_abort (priv->soup_session); - g_object_unref (priv->soup_session); - priv->soup_session = NULL; + g_clear_object (&priv->soup_session); } - g_free (priv->uri); - g_free (priv->response); - if (priv->check_id > 0) { g_source_remove (priv->check_id); priv->check_id = 0; } +#endif } @@ -326,14 +378,6 @@ nm_connectivity_class_init (NMConnectivityClass *klass) /* properties */ g_object_class_install_property - (object_class, PROP_RUNNING, - g_param_spec_boolean (NM_CONNECTIVITY_RUNNING, - "Running", - "Connectivity check is running", - FALSE, - G_PARAM_READABLE)); - - g_object_class_install_property (object_class, PROP_URI, g_param_spec_string (NM_CONNECTIVITY_URI, "URI", @@ -358,11 +402,11 @@ nm_connectivity_class_init (NMConnectivityClass *klass) G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property - (object_class, PROP_CONNECTED, - g_param_spec_boolean (NM_CONNECTIVITY_CONNECTED, - "Connected", - "Is connected", - FALSE, - G_PARAM_READABLE)); + (object_class, PROP_STATE, + g_param_spec_uint (NM_CONNECTIVITY_STATE, + "State", + "Connectivity state", + NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN, + G_PARAM_READABLE)); } |