summaryrefslogtreecommitdiff
path: root/src/error.c
blob: ad3bf7b14623f96a54ae3aa6004d5dc758b67d78 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
/*
 * 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 <stdio.h>
#include <stdlib.h>

#include "namespaces.h"
#include "util.h"

#include <wocky/wocky.h>

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 <conflict/> 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 <resource-constraint/> */
      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);
}