/* * gibber-xmpp-error.c - Source for Gibber's XMPP 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 "gibber-xmpp-error.h" #include #include #include "gibber-namespaces.h" #define MAX_LEGACY_ERRORS 3 typedef struct { const gchar *name; const gchar *description; const gchar *type; guint specialises; const gchar *namespace; const guint16 legacy_errors[MAX_LEGACY_ERRORS]; } XmppErrorSpec; static const XmppErrorSpec xmpp_errors[NUM_XMPP_ERRORS] = { { "undefined-condition", "application-specific condition", NULL, 0, GIBBER_XMPP_NS_STANZAS, { 500, 0, }, }, { "redirect", "the recipient or server is redirecting requests for this information " "to another entity", "modify", 0, GIBBER_XMPP_NS_STANZAS, { 302, 0, }, }, { "gone", "the recipient or server can no longer be contacted at this address", "modify", 0, GIBBER_XMPP_NS_STANZAS, { 302, 0, }, }, { "bad-request", "the sender has sent XML that is malformed or that cannot be processed", "modify", 0, GIBBER_XMPP_NS_STANZAS, { 400, 0, }, }, { "unexpected-request", "the recipient or server understood the request but was not expecting " "it at this time", "wait", 0, GIBBER_XMPP_NS_STANZAS, { 400, 0, }, }, { "jid-malformed", "the sending entity has provided or communicated an XMPP address or " "aspect thereof (e.g., a resource identifier) that does not adhere " "to the syntax defined in Addressing Scheme (Section 3)", "modify", 0, GIBBER_XMPP_NS_STANZAS, { 400, 0, }, }, { "not-authorized", "the sender must provide proper credentials before being allowed to " "perform the action, or has provided improper credentials", "auth", 0, GIBBER_XMPP_NS_STANZAS, { 401, 0, }, }, { "payment-required", "the requesting entity is not authorized to access the requested " "service because payment is required", "auth", 0, GIBBER_XMPP_NS_STANZAS, { 402, 0, }, }, { "forbidden", "the requesting entity does not possess the required permissions to " "perform the action", "auth", 0, GIBBER_XMPP_NS_STANZAS, { 403, 0, }, }, { "item-not-found", "the addressed JID or item requested cannot be found", "cancel", 0, GIBBER_XMPP_NS_STANZAS, { 404, 0, }, }, { "recipient-unavailable", "the intended recipient is temporarily unavailable", "wait", 0, GIBBER_XMPP_NS_STANZAS, { 404, 0, }, }, { "remote-server-not-found", "a remote server or service specified as part or all of the JID of the " "intended recipient (or required to fulfill a request) could not be " "contacted within a reasonable amount of time", "cancel", 0, GIBBER_XMPP_NS_STANZAS, { 404, 0, }, }, { "not-allowed", "the recipient or server does not allow any entity to perform the action", "cancel", 0, GIBBER_XMPP_NS_STANZAS, { 405, 0, }, }, { "not-acceptable", "the recipient or server understands the request but is refusing to " "process it because it does not meet criteria defined by the recipient " "or server (e.g., a local policy regarding acceptable words in messages)", "modify", 0, GIBBER_XMPP_NS_STANZAS, { 406, 0, }, }, { "registration-required", "the requesting entity is not authorized to access the requested service " "because registration is required", "auth", 0, GIBBER_XMPP_NS_STANZAS, { 407, 0, }, }, { "subscription-required", "the requesting entity is not authorized to access the requested service " "because a subscription is required", "auth", 0, GIBBER_XMPP_NS_STANZAS, { 407, 0, }, }, { "remote-server-timeout", "a remote server or service specified as part or all of the JID of the " "intended recipient (or required to fulfill a request) could not be " "contacted within a reasonable amount of time", "wait", 0, GIBBER_XMPP_NS_STANZAS, { 408, 504, 0, }, }, { "conflict", "access cannot be granted because an existing resource or session exists " "with the same name or address", "cancel", 0, GIBBER_XMPP_NS_STANZAS, { 409, 0, }, }, { "internal-server-error", "the server could not process the stanza because of a misconfiguration " "or an otherwise-undefined internal server error", "wait", 0, GIBBER_XMPP_NS_STANZAS, { 500, 0, }, }, { "resource-constraint", "the server or recipient lacks the system resources necessary to service " "the request", "wait", 0, GIBBER_XMPP_NS_STANZAS, { 500, 0, }, }, { "feature-not-implemented", "the feature requested is not implemented by the recipient or server and " "therefore cannot be processed", "cancel", 0, GIBBER_XMPP_NS_STANZAS, { 501, 0, }, }, { "service-unavailable", "the server or recipient does not currently provide the requested " "service", "cancel", 0, GIBBER_XMPP_NS_STANZAS, { 502, 503, 510, }, }, { "out-of-order", "the request cannot occur at this point in the state machine", "cancel", XMPP_ERROR_UNEXPECTED_REQUEST, GIBBER_XMPP_NS_JINGLE_ERRORS, { 0, }, }, { "unknown-session", "the 'sid' attribute specifies a session that is unknown to the " "recipient", "cancel", XMPP_ERROR_BAD_REQUEST, GIBBER_XMPP_NS_JINGLE_ERRORS, { 0, }, }, { "unsupported-transports", "the recipient does not support any of the desired content transport " "methods", "cancel", XMPP_ERROR_FEATURE_NOT_IMPLEMENTED, GIBBER_XMPP_NS_JINGLE_ERRORS, { 0, }, }, { "unsupported-content", "the recipient does not support any of the desired content description" "formats", "cancel", XMPP_ERROR_FEATURE_NOT_IMPLEMENTED, GIBBER_XMPP_NS_JINGLE_ERRORS, { 0, }, }, { "no-valid-streams", "None of the available streams are acceptable.", "cancel", XMPP_ERROR_BAD_REQUEST, GIBBER_XMPP_NS_SI, { 400, 0 }, }, { "bad-profile", "The profile is not understood or invalid.", "modify", XMPP_ERROR_BAD_REQUEST, GIBBER_XMPP_NS_SI, { 400, 0 }, }, }; GQuark gibber_xmpp_error_quark (void) { static GQuark quark = 0; if (!quark) quark = g_quark_from_static_string ("gibber-xmpp-error"); return quark; } GibberXmppError gibber_xmpp_error_from_node (GibberXmppNode *error_node) { gint i, j; const gchar *error_code_str; g_return_val_if_fail (error_node != NULL, XMPP_ERROR_UNDEFINED_CONDITION); /* First, try to look it up the modern way */ if (error_node->children) { /* we loop backwards because the most specific errors are the larger * numbers; the >= 0 test is OK because i is signed */ for (i = NUM_XMPP_ERRORS - 1; i >= 0; i--) { if (gibber_xmpp_node_get_child_ns (error_node, xmpp_errors[i].name, xmpp_errors[i].namespace)) { return i; } } } /* Ok, do it the legacy way */ error_code_str = gibber_xmpp_node_get_attribute (error_node, "code"); if (error_code_str) { gint error_code; error_code = atoi (error_code_str); /* skip UNDEFINED_CONDITION, we want code 500 to be translated * to INTERNAL_SERVER_ERROR */ for (i = 1; i < NUM_XMPP_ERRORS; i++) { const XmppErrorSpec *spec = &xmpp_errors[i]; for (j = 0; j < MAX_LEGACY_ERRORS; j++) { gint cur_code = spec->legacy_errors[j]; if (cur_code == 0) break; if (cur_code == error_code) return i; } } } return XMPP_ERROR_UNDEFINED_CONDITION; } static GError * gibber_xmpp_error_to_g_error (GibberXmppError error) { if (error >= NUM_XMPP_ERRORS) return g_error_new (GIBBER_XMPP_ERROR, XMPP_ERROR_UNDEFINED_CONDITION, "Unknown or invalid XMPP error"); return g_error_new_literal (GIBBER_XMPP_ERROR, error, xmpp_errors[error].description); } /* * See RFC 3920: 4.7 Stream Errors, 9.3 Stanza Errors. */ GibberXmppNode * gibber_xmpp_error_to_node (GibberXmppError error, GibberXmppNode *parent_node, const gchar *errmsg) { const XmppErrorSpec *spec, *extra; GibberXmppNode *error_node, *node; gchar str[6]; g_return_val_if_fail (error != XMPP_ERROR_UNDEFINED_CONDITION && error < NUM_XMPP_ERRORS, NULL); if (xmpp_errors[error].specialises) { extra = &xmpp_errors[error]; spec = &xmpp_errors[extra->specialises]; } else { extra = NULL; spec = &xmpp_errors[error]; } error_node = gibber_xmpp_node_add_child (parent_node, "error"); sprintf (str, "%d", spec->legacy_errors[0]); gibber_xmpp_node_set_attribute (error_node, "code", str); if (spec->type) { gibber_xmpp_node_set_attribute (error_node, "type", spec->type); } node = gibber_xmpp_node_add_child_ns (error_node, spec->name, GIBBER_XMPP_NS_STANZAS); if (extra != NULL) { node = gibber_xmpp_node_add_child_ns (error_node, extra->name, extra->namespace); } if (NULL != errmsg) { node = gibber_xmpp_node_add_child (error_node, "text"); gibber_xmpp_node_set_content (node, errmsg); } return error_node; } const gchar * gibber_xmpp_error_string (GibberXmppError error) { if (error < NUM_XMPP_ERRORS) return xmpp_errors[error].name; else return NULL; } const gchar * gibber_xmpp_error_description (GibberXmppError error) { if (error < NUM_XMPP_ERRORS) return xmpp_errors[error].description; else return NULL; } GError * gibber_message_get_xmpp_error (GibberXmppStanza *msg) { GibberStanzaSubType sub_type; g_return_val_if_fail (msg != NULL, NULL); gibber_xmpp_stanza_get_type_info (msg, NULL, &sub_type); if (sub_type == GIBBER_STANZA_SUB_TYPE_ERROR) { GibberXmppNode *error_node = gibber_xmpp_node_get_child ( wocky_stanza_get_top_node (msg), "error"); if (error_node != NULL) { return gibber_xmpp_error_to_g_error (gibber_xmpp_error_from_node (error_node)); } else { return g_error_new (GIBBER_XMPP_ERROR, XMPP_ERROR_UNDEFINED_CONDITION, "Unknown or invalid XMPP error"); } } /* no error */ return NULL; }