/* * gabble-error.c - Source for Gabble's error handling API * Copyright (C) 2006-2007 Collabora Ltd. * Copyright (C) 2006 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include "error.h" #include #include #include "namespaces.h" #include "util.h" #include static inline TpError set_conn_reason (TpConnectionStatusReason *p, TpConnectionStatusReason r, TpError e) { if (p != NULL) *p = r; return e; } #define set_easy_conn_reason(p, suffix) \ set_conn_reason (p, TP_CONNECTION_STATUS_REASON_ ## suffix, \ TP_ERROR_ ## suffix) static TpError map_wocky_xmpp_error (const GError *error, TpConnectionStatusReason *conn_reason) { g_return_val_if_fail (error->domain == WOCKY_XMPP_ERROR, TP_ERROR_NOT_AVAILABLE); switch (error->code) { case WOCKY_XMPP_ERROR_REDIRECT: case WOCKY_XMPP_ERROR_GONE: /* FIXME: wild guess at the right error */ return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED, TP_ERROR_DOES_NOT_EXIST); case WOCKY_XMPP_ERROR_BAD_REQUEST: case WOCKY_XMPP_ERROR_UNEXPECTED_REQUEST: /* probably an internal error in Gabble/Wocky */ return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NETWORK_ERROR, TP_ERROR_CONFUSED); case WOCKY_XMPP_ERROR_JID_MALFORMED: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NETWORK_ERROR, TP_ERROR_INVALID_HANDLE); case WOCKY_XMPP_ERROR_NOT_AUTHORIZED: case WOCKY_XMPP_ERROR_PAYMENT_REQUIRED: case WOCKY_XMPP_ERROR_FORBIDDEN: /* FIXME: the closest we've got for these, I think? */ return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED, TP_ERROR_PERMISSION_DENIED); case WOCKY_XMPP_ERROR_ITEM_NOT_FOUND: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED, TP_ERROR_DOES_NOT_EXIST); case WOCKY_XMPP_ERROR_RECIPIENT_UNAVAILABLE: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED, TP_ERROR_OFFLINE); case WOCKY_XMPP_ERROR_REMOTE_SERVER_NOT_FOUND: /* FIXME: or NetworkError? */ return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED, TP_ERROR_DOES_NOT_EXIST); case WOCKY_XMPP_ERROR_NOT_ALLOWED: case WOCKY_XMPP_ERROR_NOT_ACCEPTABLE: case WOCKY_XMPP_ERROR_REGISTRATION_REQUIRED: case WOCKY_XMPP_ERROR_SUBSCRIPTION_REQUIRED: /* FIXME: the closest we've got for all these, I think? */ return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED, TP_ERROR_PERMISSION_DENIED); case WOCKY_XMPP_ERROR_REMOTE_SERVER_TIMEOUT: return set_easy_conn_reason (conn_reason, NETWORK_ERROR); case WOCKY_XMPP_ERROR_CONFLICT: /* this is the best we can do in general - callers should * special-case according to their domain knowledge, * to turn it into RegistrationExists, ConnectionReplaced, etc. */ return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NAME_IN_USE, TP_ERROR_NOT_AVAILABLE); case WOCKY_XMPP_ERROR_INTERNAL_SERVER_ERROR: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED, TP_ERROR_SERVICE_CONFUSED); case WOCKY_XMPP_ERROR_RESOURCE_CONSTRAINT: /* FIXME: Telepathy's ServiceBusy means the server, but the remote * client can also raise */ return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED, TP_ERROR_SERVICE_BUSY); case WOCKY_XMPP_ERROR_FEATURE_NOT_IMPLEMENTED: case WOCKY_XMPP_ERROR_SERVICE_UNAVAILABLE: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED, TP_ERROR_NOT_AVAILABLE); case WOCKY_XMPP_ERROR_UNDEFINED_CONDITION: default: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED, TP_ERROR_NOT_AVAILABLE); } } static TpError map_wocky_auth_error (const GError *error, TpConnectionStatusReason *conn_reason) { g_return_val_if_fail (error->domain == WOCKY_AUTH_ERROR, TP_ERROR_NOT_AVAILABLE); switch (error->code) { case WOCKY_AUTH_ERROR_CONNRESET: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NETWORK_ERROR, TP_ERROR_CONNECTION_LOST); case WOCKY_AUTH_ERROR_NETWORK: case WOCKY_AUTH_ERROR_STREAM: return set_easy_conn_reason (conn_reason, NETWORK_ERROR); case WOCKY_AUTH_ERROR_RESOURCE_CONFLICT: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NAME_IN_USE, TP_ERROR_ALREADY_CONNECTED); case WOCKY_AUTH_ERROR_NOT_SUPPORTED: case WOCKY_AUTH_ERROR_NO_SUPPORTED_MECHANISMS: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED, TP_ERROR_NOT_IMPLEMENTED); case WOCKY_AUTH_ERROR_INVALID_REPLY: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED, TP_ERROR_SERVICE_CONFUSED); case WOCKY_AUTH_ERROR_INIT_FAILED: case WOCKY_AUTH_ERROR_NO_CREDENTIALS: case WOCKY_AUTH_ERROR_NOT_AUTHORIZED: case WOCKY_AUTH_ERROR_FAILURE: default: return set_easy_conn_reason (conn_reason, AUTHENTICATION_FAILED); } } static TpError map_wocky_connector_error (const GError *error, TpConnectionStatusReason *conn_reason) { g_return_val_if_fail (error->domain == WOCKY_CONNECTOR_ERROR, TP_ERROR_NOT_AVAILABLE); switch (error->code) { case WOCKY_CONNECTOR_ERROR_SESSION_DENIED: return set_easy_conn_reason (conn_reason, AUTHENTICATION_FAILED); case WOCKY_CONNECTOR_ERROR_BIND_CONFLICT: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NAME_IN_USE, TP_ERROR_ALREADY_CONNECTED); case WOCKY_CONNECTOR_ERROR_REGISTRATION_CONFLICT: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NAME_IN_USE, TP_ERROR_REGISTRATION_EXISTS); case WOCKY_CONNECTOR_ERROR_REGISTRATION_REJECTED: /* AuthenticationFailed is the closest ConnectionStatusReason to * "I tried but couldn't register you an account." */ return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED, TP_ERROR_PERMISSION_DENIED); case WOCKY_CONNECTOR_ERROR_REGISTRATION_UNSUPPORTED: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED, TP_ERROR_NOT_AVAILABLE); default: return set_easy_conn_reason (conn_reason, NETWORK_ERROR); } } static TpError map_wocky_stream_error (const GError *error, TpConnectionStatus previous_status, TpConnectionStatusReason *conn_reason) { g_return_val_if_fail (error->domain == WOCKY_XMPP_STREAM_ERROR, TP_ERROR_NOT_AVAILABLE); switch (error->code) { case WOCKY_XMPP_STREAM_ERROR_HOST_UNKNOWN: /* If we get this while we're logging in, it's because we're trying * to connect to foo@bar.com but the server doesn't know about * bar.com, probably because the user entered a non-GTalk JID into * a GTalk profile that forces the server. */ return set_easy_conn_reason (conn_reason, AUTHENTICATION_FAILED); case WOCKY_XMPP_STREAM_ERROR_CONFLICT: if (previous_status == TP_CONNECTION_STATUS_CONNECTED) { return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NAME_IN_USE, TP_ERROR_CONNECTION_REPLACED); } else { return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_NAME_IN_USE, TP_ERROR_ALREADY_CONNECTED); } default: return set_easy_conn_reason (conn_reason, NETWORK_ERROR); } } static TpError map_wocky_tls_cert_error (const GError *error, TpConnectionStatusReason *conn_reason) { g_return_val_if_fail (error->domain == WOCKY_TLS_CERT_ERROR, TP_ERROR_NOT_AVAILABLE); switch (error->code) { case WOCKY_TLS_CERT_NO_CERTIFICATE: return set_easy_conn_reason (conn_reason, CERT_NOT_PROVIDED); case WOCKY_TLS_CERT_INSECURE: case WOCKY_TLS_CERT_SIGNER_UNKNOWN: case WOCKY_TLS_CERT_SIGNER_UNAUTHORISED: case WOCKY_TLS_CERT_REVOKED: case WOCKY_TLS_CERT_MAYBE_DOS: return set_easy_conn_reason (conn_reason, CERT_UNTRUSTED); case WOCKY_TLS_CERT_EXPIRED: return set_easy_conn_reason (conn_reason, CERT_EXPIRED); case WOCKY_TLS_CERT_NOT_ACTIVE: return set_easy_conn_reason (conn_reason, CERT_NOT_ACTIVATED); case WOCKY_TLS_CERT_NAME_MISMATCH: return set_easy_conn_reason (conn_reason, CERT_HOSTNAME_MISMATCH); case WOCKY_TLS_CERT_INTERNAL_ERROR: case WOCKY_TLS_CERT_UNKNOWN_ERROR: default: return set_conn_reason (conn_reason, TP_CONNECTION_STATUS_REASON_CERT_OTHER_ERROR, TP_ERROR_ENCRYPTION_ERROR); } } static TpError map_connection_error (const GError *error) { switch (error->code) { case WOCKY_XMPP_CONNECTION_ERROR_EOS: case WOCKY_XMPP_CONNECTION_ERROR_CLOSED: return TP_ERROR_CANCELLED; case WOCKY_XMPP_CONNECTION_ERROR_NOT_OPEN: case WOCKY_XMPP_CONNECTION_ERROR_IS_CLOSED: case WOCKY_XMPP_CONNECTION_ERROR_IS_OPEN: default: return TP_ERROR_DISCONNECTED; } } static const gchar * get_error_prefix (GEnumClass *klass, gint code, const gchar *fallback) { GEnumValue *value; if (klass == NULL) return fallback; value = g_enum_get_value (klass, code); if (value == NULL || value->value_name == NULL) return fallback; return value->value_name; } void gabble_set_tp_conn_error_from_wocky (const GError *wocky_error, TpConnectionStatus previous_status, TpConnectionStatusReason *conn_reason, GError **error) { GEnumClass *klass; const gchar *name; if (conn_reason != NULL) *conn_reason = TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED; g_return_if_fail (wocky_error != NULL); if (wocky_error->domain == WOCKY_XMPP_ERROR) { klass = g_type_class_ref (WOCKY_TYPE_XMPP_ERROR); name = get_error_prefix (klass, wocky_error->code, "unknown WockyXmppError code"); g_set_error (error, TP_ERROR, map_wocky_xmpp_error (wocky_error, conn_reason), "%s (#%d): %s", name, wocky_error->code, wocky_error->message); g_type_class_unref (klass); } else if (wocky_error->domain == G_IO_ERROR) { klass = g_type_class_ref (G_TYPE_IO_ERROR_ENUM); name = get_error_prefix (klass, wocky_error->code, "unknown GIOError code"); /* FIXME: is it safe to assume that every GIOError we encounter from * Wocky is a NetworkError? */ g_set_error (error, TP_ERROR, TP_ERROR_NETWORK_ERROR, "%s (#%d): %s", name, wocky_error->code, wocky_error->message); g_type_class_unref (klass); if (conn_reason != NULL) *conn_reason = TP_CONNECTION_STATUS_REASON_NETWORK_ERROR; } else if (wocky_error->domain == WOCKY_AUTH_ERROR) { klass = g_type_class_ref (WOCKY_TYPE_AUTH_ERROR); name = get_error_prefix (klass, wocky_error->code, "unknown WockyAuthError code"); g_set_error (error, TP_ERROR, map_wocky_auth_error (wocky_error, conn_reason), "%s (#%d): %s", name, wocky_error->code, wocky_error->message); g_type_class_unref (klass); } else if (wocky_error->domain == WOCKY_CONNECTOR_ERROR) { klass = g_type_class_ref (WOCKY_TYPE_CONNECTOR_ERROR); name = get_error_prefix (klass, wocky_error->code, "unknown WockyConnectorError code"); g_set_error (error, TP_ERROR, map_wocky_connector_error (wocky_error, conn_reason), "%s (#%d): %s", name, wocky_error->code, wocky_error->message); g_type_class_unref (klass); } else if (wocky_error->domain == WOCKY_XMPP_STREAM_ERROR) { klass = g_type_class_ref (WOCKY_TYPE_XMPP_STREAM_ERROR); name = get_error_prefix (klass, wocky_error->code, "unknown WockyXmppStreamError code"); g_set_error (error, TP_ERROR, map_wocky_stream_error (wocky_error, previous_status, conn_reason), "%s (#%d): %s", name, wocky_error->code, wocky_error->message); g_type_class_unref (klass); } else if (wocky_error->domain == WOCKY_TLS_CERT_ERROR) { klass = g_type_class_ref (WOCKY_TYPE_TLS_CERT_STATUS); name = get_error_prefix (klass, wocky_error->code, "unknown WockyTLSCertStatus code"); g_set_error (error, TP_ERROR, map_wocky_tls_cert_error (wocky_error, conn_reason), "%s (#%d): %s", name, wocky_error->code, wocky_error->message); g_type_class_unref (klass); } else if (wocky_error->domain == WOCKY_XMPP_CONNECTION_ERROR) { /* FIXME: there's no GEnum for WockyXmppConnectionError. */ g_set_error_literal (error, TP_ERROR, map_connection_error (wocky_error), wocky_error->message); } else { /* best we can do... */ g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "%s (#%d): %s", g_quark_to_string (wocky_error->domain), wocky_error->code, wocky_error->message); } } void gabble_set_tp_error_from_wocky (const GError *wocky_error, GError **error) { gabble_set_tp_conn_error_from_wocky (wocky_error, TP_CONNECTION_STATUS_CONNECTED, NULL, error); }