Adapt migration for files
[pidgin-git.git] / libpurple / connection.c
bloba0e29da1e816db7e3ff1441206a0aa8bc4609c3d
1 /* purple
3 * Purple is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
5 * source distribution.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
21 #define _PURPLE_CONNECTION_C_
23 #include "internal.h"
24 #include "glibcompat.h"
26 #include "account.h"
27 #include "buddylist.h"
28 #include "connection.h"
29 #include "dbus-maybe.h"
30 #include "debug.h"
31 #include "enums.h"
32 #include "http.h"
33 #include "log.h"
34 #include "notify.h"
35 #include "prefs.h"
36 #include "proxy.h"
37 #include "request.h"
38 #include "server.h"
39 #include "signals.h"
40 #include "util.h"
42 G_DEFINE_QUARK(purple-connection-error-quark, purple_connection_error);
44 #define KEEPALIVE_INTERVAL 30
46 #define PURPLE_CONNECTION_GET_PRIVATE(obj) \
47 (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_CONNECTION, PurpleConnectionPrivate))
49 typedef struct _PurpleConnectionPrivate PurpleConnectionPrivate;
51 /* Private data for a connection */
52 struct _PurpleConnectionPrivate
54 PurpleProtocol *protocol; /* The protocol. */
55 PurpleConnectionFlags flags; /* Connection flags. */
57 PurpleConnectionState state; /* The connection state. */
59 PurpleAccount *account; /* The account being connected to. */
60 char *password; /* The password used. */
62 GSList *active_chats; /* A list of active chats
63 (#PurpleChatConversation structs). */
65 /* TODO Remove this and use protocol-specific subclasses. */
66 void *proto_data; /* Protocol-specific data. */
68 char *display_name; /* How you appear to other people. */
69 guint keepalive; /* Keep-alive. */
71 /* Wants to Die state. This is set when the user chooses to log out, or
72 * when the protocol is disconnected and should not be automatically
73 * reconnected (incorrect password, etc.). Protocols should rely on
74 * purple_connection_error() to set this for them rather than
75 * setting it themselves.
76 * See purple_connection_error_is_fatal()
78 gboolean wants_to_die;
80 gboolean is_finalizing; /* The object is being destroyed. */
82 /* The connection error and its description if an error occured */
83 PurpleConnectionErrorInfo *error_info;
85 guint disconnect_timeout; /* Timer used for nasty stack tricks */
86 time_t last_received; /* When we last received a packet. Set by the
87 protocols to avoid sending unneeded keepalives */
90 /* GObject property enums */
91 enum
93 PROP_0,
94 PROP_PROTOCOL,
95 PROP_FLAGS,
96 PROP_STATE,
97 PROP_ACCOUNT,
98 PROP_PASSWORD,
99 PROP_DISPLAY_NAME,
100 PROP_LAST
103 static GObjectClass *parent_class;
104 static GParamSpec *properties[PROP_LAST];
106 static GList *connections = NULL;
107 static GList *connections_connecting = NULL;
108 static PurpleConnectionUiOps *connection_ui_ops = NULL;
110 static int connections_handle;
112 static PurpleConnectionErrorInfo *
113 purple_connection_error_info_new(PurpleConnectionError type,
114 const gchar *description);
116 /**************************************************************************
117 * Connection API
118 **************************************************************************/
119 static gboolean
120 send_keepalive(gpointer data)
122 PurpleConnection *gc = data;
123 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
125 /* Only send keep-alives if we haven't heard from the
126 * server in a while.
128 if ((time(NULL) - priv->last_received) < KEEPALIVE_INTERVAL)
129 return TRUE;
131 purple_protocol_server_iface_keepalive(priv->protocol, gc);
133 return TRUE;
136 static void
137 update_keepalive(PurpleConnection *gc, gboolean on)
139 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
141 g_return_if_fail(priv != NULL);
143 if (!PURPLE_PROTOCOL_IMPLEMENTS(priv->protocol, SERVER_IFACE, keepalive))
144 return;
146 if (on && !priv->keepalive)
148 purple_debug_info("connection", "Activating keepalive.\n");
149 priv->keepalive = purple_timeout_add_seconds(KEEPALIVE_INTERVAL, send_keepalive, gc);
151 else if (!on && priv->keepalive > 0)
153 purple_debug_info("connection", "Deactivating keepalive.\n");
154 purple_timeout_remove(priv->keepalive);
155 priv->keepalive = 0;
160 * d:)->-<
162 * d:O-\-<
164 * d:D-/-<
166 * d8D->-< DANCE!
169 void
170 purple_connection_set_state(PurpleConnection *gc, PurpleConnectionState state)
172 PurpleConnectionUiOps *ops;
173 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
175 g_return_if_fail(priv != NULL);
177 if (priv->state == state)
178 return;
180 priv->state = state;
182 ops = purple_connections_get_ui_ops();
184 if (priv->state == PURPLE_CONNECTION_CONNECTING) {
185 connections_connecting = g_list_append(connections_connecting, gc);
187 else {
188 connections_connecting = g_list_remove(connections_connecting, gc);
191 if (priv->state == PURPLE_CONNECTION_CONNECTED) {
192 PurpleAccount *account;
193 PurplePresence *presence;
195 account = purple_connection_get_account(gc);
196 presence = purple_account_get_presence(account);
198 /* Set the time the account came online */
199 purple_presence_set_login_time(presence, time(NULL));
201 if (purple_prefs_get_bool("/purple/logging/log_system"))
203 PurpleLog *log = purple_account_get_log(account, TRUE);
205 if (log != NULL)
207 char *msg = g_strdup_printf(_("+++ %s signed on"),
208 purple_account_get_username(account));
209 purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
210 purple_account_get_username(account),
211 purple_presence_get_login_time(presence),
212 msg);
213 g_free(msg);
217 if (ops != NULL && ops->connected != NULL)
218 ops->connected(gc);
220 purple_blist_add_account(account);
222 purple_signal_emit(purple_connections_get_handle(), "signed-on", gc);
223 purple_signal_emit_return_1(purple_connections_get_handle(), "autojoin", gc);
225 purple_serv_set_permit_deny(gc);
227 update_keepalive(gc, TRUE);
229 else if (priv->state == PURPLE_CONNECTION_DISCONNECTED) {
230 PurpleAccount *account = purple_connection_get_account(gc);
232 if (purple_prefs_get_bool("/purple/logging/log_system"))
234 PurpleLog *log = purple_account_get_log(account, FALSE);
236 if (log != NULL)
238 char *msg = g_strdup_printf(_("+++ %s signed off"),
239 purple_account_get_username(account));
240 purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
241 purple_account_get_username(account), time(NULL),
242 msg);
243 g_free(msg);
247 purple_account_destroy_log(account);
249 if (ops != NULL && ops->disconnected != NULL)
250 ops->disconnected(gc);
253 if (!priv->is_finalizing)
254 g_object_notify_by_pspec(G_OBJECT(gc), properties[PROP_STATE]);
257 void
258 purple_connection_set_flags(PurpleConnection *gc, PurpleConnectionFlags flags)
260 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
262 g_return_if_fail(priv != NULL);
264 priv->flags = flags;
266 if (!priv->is_finalizing)
267 g_object_notify_by_pspec(G_OBJECT(gc), properties[PROP_FLAGS]);
270 void
271 purple_connection_set_display_name(PurpleConnection *gc, const char *name)
273 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
275 g_return_if_fail(priv != NULL);
277 g_free(priv->display_name);
278 priv->display_name = g_strdup(name);
280 g_object_notify_by_pspec(G_OBJECT(gc), properties[PROP_DISPLAY_NAME]);
283 void
284 purple_connection_set_protocol_data(PurpleConnection *gc, void *proto_data)
286 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
288 g_return_if_fail(priv != NULL);
290 priv->proto_data = proto_data;
293 PurpleConnectionState
294 purple_connection_get_state(const PurpleConnection *gc)
296 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
298 g_return_val_if_fail(priv != NULL, PURPLE_CONNECTION_DISCONNECTED);
300 return priv->state;
303 PurpleConnectionFlags
304 purple_connection_get_flags(const PurpleConnection *gc)
306 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
308 g_return_val_if_fail(priv != NULL, 0);
310 return priv->flags;
313 gboolean
314 purple_connection_is_disconnecting(const PurpleConnection *gc)
316 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
318 g_return_val_if_fail(priv != NULL, TRUE);
320 return priv->is_finalizing;
323 PurpleAccount *
324 purple_connection_get_account(const PurpleConnection *gc)
326 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
328 g_return_val_if_fail(priv != NULL, NULL);
330 return priv->account;
333 PurpleProtocol *
334 purple_connection_get_protocol(const PurpleConnection *gc)
336 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
338 g_return_val_if_fail(priv != NULL, NULL);
340 return priv->protocol;
343 const char *
344 purple_connection_get_password(const PurpleConnection *gc)
346 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
348 g_return_val_if_fail(priv != NULL, NULL);
350 return priv->password;
353 GSList *
354 purple_connection_get_active_chats(const PurpleConnection *gc)
356 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
358 g_return_val_if_fail(priv != NULL, NULL);
360 return priv->active_chats;
363 const char *
364 purple_connection_get_display_name(const PurpleConnection *gc)
366 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
368 g_return_val_if_fail(priv != NULL, NULL);
370 return priv->display_name;
373 void *
374 purple_connection_get_protocol_data(const PurpleConnection *gc)
376 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
378 g_return_val_if_fail(priv != NULL, NULL);
380 return priv->proto_data;
383 void
384 _purple_connection_add_active_chat(PurpleConnection *gc, PurpleChatConversation *chat)
386 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
388 g_return_if_fail(priv != NULL);
390 priv->active_chats = g_slist_append(priv->active_chats, chat);
393 void
394 _purple_connection_remove_active_chat(PurpleConnection *gc, PurpleChatConversation *chat)
396 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
398 g_return_if_fail(priv != NULL);
400 priv->active_chats = g_slist_remove(priv->active_chats, chat);
403 gboolean
404 _purple_connection_wants_to_die(const PurpleConnection *gc)
406 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
408 g_return_val_if_fail(priv != NULL, FALSE);
410 return priv->wants_to_die;
413 void
414 purple_connection_update_progress(PurpleConnection *gc, const char *text,
415 size_t step, size_t count)
417 PurpleConnectionUiOps *ops;
419 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
420 g_return_if_fail(text != NULL);
421 g_return_if_fail(step < count);
422 g_return_if_fail(count > 1);
424 ops = purple_connections_get_ui_ops();
426 if (ops != NULL && ops->connect_progress != NULL)
427 ops->connect_progress(gc, text, step, count);
430 void
431 purple_connection_notice(PurpleConnection *gc, const char *text)
433 PurpleConnectionUiOps *ops;
435 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
436 g_return_if_fail(text != NULL);
438 ops = purple_connections_get_ui_ops();
440 if (ops != NULL && ops->notice != NULL)
441 ops->notice(gc, text);
444 static gboolean
445 purple_connection_disconnect_cb(gpointer data)
447 PurpleAccount *account;
448 PurpleConnection *gc;
449 PurpleConnectionPrivate *priv;
451 account = data;
452 gc = purple_account_get_connection(account);
453 priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
455 if (priv != NULL)
456 priv->disconnect_timeout = 0;
458 purple_account_disconnect(account);
459 return FALSE;
462 void
463 purple_connection_error (PurpleConnection *gc,
464 PurpleConnectionError reason,
465 const char *description)
467 PurpleConnectionUiOps *ops;
468 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
470 g_return_if_fail(priv != NULL);
471 /* This sanity check relies on PURPLE_CONNECTION_ERROR_OTHER_ERROR
472 * being the last member of the PurpleConnectionError enum in
473 * connection.h; if other reasons are added after it, this check should
474 * be updated.
476 if (reason > PURPLE_CONNECTION_ERROR_OTHER_ERROR) {
477 purple_debug_error("connection",
478 "purple_connection_error: reason %u isn't a "
479 "valid reason\n", reason);
480 reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
483 if (description == NULL) {
484 purple_debug_error("connection", "purple_connection_error called with NULL description\n");
485 description = _("Unknown error");
488 /* If we've already got one error, we don't need any more */
489 if (purple_connection_get_error_info(gc))
490 return;
492 priv->wants_to_die = purple_connection_error_is_fatal (reason);
494 purple_debug_info("connection", "Connection error on %p (reason: %u description: %s)\n",
495 gc, reason, description);
497 ops = purple_connections_get_ui_ops();
499 if (ops && ops->report_disconnect)
500 ops->report_disconnect(gc, reason, description);
502 priv->error_info = purple_connection_error_info_new(reason, description);
504 purple_signal_emit(purple_connections_get_handle(), "connection-error",
505 gc, reason, description);
507 priv->disconnect_timeout = purple_timeout_add(0, purple_connection_disconnect_cb,
508 purple_connection_get_account(gc));
511 PurpleConnectionErrorInfo *
512 purple_connection_get_error_info(const PurpleConnection *gc)
514 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
516 g_return_val_if_fail(priv != NULL, NULL);
518 return priv->error_info;
521 void
522 purple_connection_ssl_error (PurpleConnection *gc,
523 PurpleSslErrorType ssl_error)
525 PurpleConnectionError reason;
527 switch (ssl_error) {
528 case PURPLE_SSL_HANDSHAKE_FAILED:
529 reason = PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR;
530 break;
531 case PURPLE_SSL_CONNECT_FAILED:
532 reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
533 break;
534 case PURPLE_SSL_CERTIFICATE_INVALID:
535 /* TODO: maybe PURPLE_SSL_* should be more specific? */
536 reason = PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR;
537 break;
538 default:
539 g_assert_not_reached ();
540 reason = PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR;
543 purple_connection_error (gc, reason,
544 purple_ssl_strerror(ssl_error));
547 void
548 purple_connection_g_error(PurpleConnection *pc, const GError *error)
550 PurpleConnectionError reason;
552 if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
553 /* Not a connection error. Ignore. */
554 return;
557 if (error->domain == G_TLS_ERROR) {
558 switch (error->code) {
559 case G_TLS_ERROR_UNAVAILABLE:
560 reason = PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT;
561 break;
562 case G_TLS_ERROR_NOT_TLS:
563 case G_TLS_ERROR_HANDSHAKE:
564 reason = PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR;
565 break;
566 case G_TLS_ERROR_BAD_CERTIFICATE:
567 case G_TLS_ERROR_CERTIFICATE_REQUIRED:
568 reason = PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR;
569 break;
570 case G_TLS_ERROR_EOF:
571 case G_TLS_ERROR_MISC:
572 default:
573 reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
575 } else if (error->domain == G_IO_ERROR) {
576 reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
577 } else if (error->domain == PURPLE_CONNECTION_ERROR) {
578 reason = error->code;
579 } else {
580 reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
583 purple_connection_error(pc, reason, error->message);
586 void
587 purple_connection_take_error(PurpleConnection *pc, GError *error)
589 purple_connection_g_error(pc, error);
590 g_error_free(error);
593 gboolean
594 purple_connection_error_is_fatal (PurpleConnectionError reason)
596 switch (reason)
598 case PURPLE_CONNECTION_ERROR_NETWORK_ERROR:
599 case PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR:
600 return FALSE;
601 case PURPLE_CONNECTION_ERROR_INVALID_USERNAME:
602 case PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED:
603 case PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE:
604 case PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT:
605 case PURPLE_CONNECTION_ERROR_NAME_IN_USE:
606 case PURPLE_CONNECTION_ERROR_INVALID_SETTINGS:
607 case PURPLE_CONNECTION_ERROR_OTHER_ERROR:
608 case PURPLE_CONNECTION_ERROR_CERT_NOT_PROVIDED:
609 case PURPLE_CONNECTION_ERROR_CERT_UNTRUSTED:
610 case PURPLE_CONNECTION_ERROR_CERT_EXPIRED:
611 case PURPLE_CONNECTION_ERROR_CERT_NOT_ACTIVATED:
612 case PURPLE_CONNECTION_ERROR_CERT_HOSTNAME_MISMATCH:
613 case PURPLE_CONNECTION_ERROR_CERT_FINGERPRINT_MISMATCH:
614 case PURPLE_CONNECTION_ERROR_CERT_SELF_SIGNED:
615 case PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR:
616 return TRUE;
617 default:
618 g_return_val_if_reached(TRUE);
622 void purple_connection_update_last_received(PurpleConnection *gc)
624 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
626 g_return_if_fail(priv != NULL);
628 priv->last_received = time(NULL);
631 static PurpleConnectionErrorInfo *
632 purple_connection_error_info_new(PurpleConnectionError type,
633 const gchar *description)
635 PurpleConnectionErrorInfo *err;
637 g_return_val_if_fail(description != NULL, NULL);
639 err = g_new(PurpleConnectionErrorInfo, 1);
641 err->type = type;
642 err->description = g_strdup(description);
644 return err;
647 /**************************************************************************
648 * GBoxed code
649 **************************************************************************/
650 static PurpleConnectionUiOps *
651 purple_connection_ui_ops_copy(PurpleConnectionUiOps *ops)
653 PurpleConnectionUiOps *ops_new;
655 g_return_val_if_fail(ops != NULL, NULL);
657 ops_new = g_new(PurpleConnectionUiOps, 1);
658 *ops_new = *ops;
660 return ops_new;
663 GType
664 purple_connection_ui_ops_get_type(void)
666 static GType type = 0;
668 if (type == 0) {
669 type = g_boxed_type_register_static("PurpleConnectionUiOps",
670 (GBoxedCopyFunc)purple_connection_ui_ops_copy,
671 (GBoxedFreeFunc)g_free);
674 return type;
677 static PurpleConnectionErrorInfo *
678 purple_connection_error_info_copy(PurpleConnectionErrorInfo *err)
680 g_return_val_if_fail(err != NULL, NULL);
682 return purple_connection_error_info_new(err->type, err->description);
685 static void
686 purple_connection_error_info_free(PurpleConnectionErrorInfo *err)
688 g_return_if_fail(err != NULL);
690 g_free(err->description);
691 g_free(err);
694 GType
695 purple_connection_error_info_get_type(void)
697 static GType type = 0;
699 if (type == 0) {
700 type = g_boxed_type_register_static("PurpleConnectionErrorInfo",
701 (GBoxedCopyFunc)purple_connection_error_info_copy,
702 (GBoxedFreeFunc)purple_connection_error_info_free);
705 return type;
708 /**************************************************************************
709 * GObject code
710 **************************************************************************/
712 /* Set method for GObject properties */
713 static void
714 purple_connection_set_property(GObject *obj, guint param_id, const GValue *value,
715 GParamSpec *pspec)
717 PurpleConnection *gc = PURPLE_CONNECTION(obj);
718 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
720 switch (param_id) {
721 case PROP_PROTOCOL:
722 priv->protocol = g_value_get_object(value);
723 break;
724 case PROP_FLAGS:
725 purple_connection_set_flags(gc, g_value_get_flags(value));
726 break;
727 case PROP_STATE:
728 purple_connection_set_state(gc, g_value_get_enum(value));
729 break;
730 case PROP_ACCOUNT:
731 priv->account = g_value_get_object(value);
732 break;
733 case PROP_PASSWORD:
734 g_free(priv->password);
735 priv->password = g_strdup(g_value_get_string(value));
736 break;
737 case PROP_DISPLAY_NAME:
738 purple_connection_set_display_name(gc, g_value_get_string(value));
739 break;
740 default:
741 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
742 break;
746 /* Get method for GObject properties */
747 static void
748 purple_connection_get_property(GObject *obj, guint param_id, GValue *value,
749 GParamSpec *pspec)
751 PurpleConnection *gc = PURPLE_CONNECTION(obj);
753 switch (param_id) {
754 case PROP_PROTOCOL:
755 g_value_set_object(value, purple_connection_get_protocol(gc));
756 break;
757 case PROP_FLAGS:
758 g_value_set_flags(value, purple_connection_get_flags(gc));
759 break;
760 case PROP_STATE:
761 g_value_set_enum(value, purple_connection_get_state(gc));
762 break;
763 case PROP_ACCOUNT:
764 g_value_set_object(value, purple_connection_get_account(gc));
765 break;
766 case PROP_PASSWORD:
767 g_value_set_string(value, purple_connection_get_password(gc));
768 break;
769 case PROP_DISPLAY_NAME:
770 g_value_set_string(value, purple_connection_get_display_name(gc));
771 break;
772 default:
773 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
774 break;
778 /* GObject initialization function */
779 static void
780 purple_connection_init(GTypeInstance *instance, gpointer klass)
782 PurpleConnection *gc = PURPLE_CONNECTION(instance);
784 purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTING);
785 connections = g_list_append(connections, gc);
787 PURPLE_DBUS_REGISTER_POINTER(gc, PurpleConnection);
790 /* Called when done constructing */
791 static void
792 purple_connection_constructed(GObject *object)
794 PurpleConnection *gc = PURPLE_CONNECTION(object);
795 PurpleAccount *account;
797 G_OBJECT_CLASS(parent_class)->constructed(object);
799 g_object_get(gc, "account", &account, NULL);
800 purple_account_set_connection(account, gc);
801 g_object_unref(account);
803 purple_signal_emit(purple_connections_get_handle(), "signing-on", gc);
806 /* GObject finalize function */
807 static void
808 purple_connection_finalize(GObject *object)
810 PurpleConnection *gc = PURPLE_CONNECTION(object);
811 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
812 PurpleAccount *account;
813 GSList *buddies;
814 gboolean remove = FALSE;
816 priv->is_finalizing = TRUE;
818 account = purple_connection_get_account(gc);
820 purple_debug_info("connection", "Disconnecting connection %p\n", gc);
822 if (purple_connection_get_state(gc) != PURPLE_CONNECTION_CONNECTING)
823 remove = TRUE;
825 purple_signal_emit(purple_connections_get_handle(), "signing-off", gc);
827 while (priv->active_chats)
829 PurpleChatConversation *b = priv->active_chats->data;
831 priv->active_chats = g_slist_remove(priv->active_chats, b);
832 purple_chat_conversation_leave(b);
835 update_keepalive(gc, FALSE);
837 purple_protocol_class_close(priv->protocol, gc);
839 /* Clear out the proto data that was freed in the protocol's close method */
840 buddies = purple_blist_find_buddies(account, NULL);
841 while (buddies != NULL) {
842 PurpleBuddy *buddy = buddies->data;
843 purple_buddy_set_protocol_data(buddy, NULL);
844 buddies = g_slist_delete_link(buddies, buddies);
847 purple_http_conn_cancel_all(gc);
848 _purple_socket_cancel_with_connection(gc);
849 purple_proxy_connect_cancel_with_handle(gc);
851 connections = g_list_remove(connections, gc);
853 purple_connection_set_state(gc, PURPLE_CONNECTION_DISCONNECTED);
855 if (remove)
856 purple_blist_remove_account(account);
858 purple_signal_emit(purple_connections_get_handle(), "signed-off", gc);
860 purple_account_request_close_with_account(account);
861 purple_request_close_with_handle(gc);
862 purple_notify_close_with_handle(gc);
864 purple_debug_info("connection", "Destroying connection %p\n", gc);
866 purple_account_set_connection(account, NULL);
868 if (priv->error_info)
869 purple_connection_error_info_free(priv->error_info);
871 if (priv->disconnect_timeout > 0)
872 purple_timeout_remove(priv->disconnect_timeout);
874 purple_str_wipe(priv->password);
875 g_free(priv->display_name);
877 PURPLE_DBUS_UNREGISTER_POINTER(gc);
879 G_OBJECT_CLASS(parent_class)->finalize(object);
882 /* Class initializer function */
883 static void purple_connection_class_init(PurpleConnectionClass *klass)
885 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
887 parent_class = g_type_class_peek_parent(klass);
889 obj_class->finalize = purple_connection_finalize;
890 obj_class->constructed = purple_connection_constructed;
892 /* Setup properties */
893 obj_class->get_property = purple_connection_get_property;
894 obj_class->set_property = purple_connection_set_property;
896 g_type_class_add_private(klass, sizeof(PurpleConnectionPrivate));
898 properties[PROP_PROTOCOL] = g_param_spec_object("protocol", "Protocol",
899 "The protocol that the connection is using.",
900 PURPLE_TYPE_PROTOCOL,
901 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
902 G_PARAM_STATIC_STRINGS);
904 properties[PROP_FLAGS] = g_param_spec_flags("flags", "Connection flags",
905 "The flags of the connection.",
906 PURPLE_TYPE_CONNECTION_FLAGS, 0,
907 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
909 properties[PROP_STATE] = g_param_spec_enum("state", "Connection state",
910 "The current state of the connection.",
911 PURPLE_TYPE_CONNECTION_STATE, PURPLE_CONNECTION_DISCONNECTED,
912 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
914 properties[PROP_ACCOUNT] = g_param_spec_object("account", "Account",
915 "The account using the connection.", PURPLE_TYPE_ACCOUNT,
916 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
917 G_PARAM_STATIC_STRINGS);
919 properties[PROP_PASSWORD] = g_param_spec_string("password", "Password",
920 "The password used for connection.", NULL,
921 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
922 G_PARAM_STATIC_STRINGS);
924 properties[PROP_DISPLAY_NAME] = g_param_spec_string("display-name",
925 "Display name",
926 "Your name that appears to other people.", NULL,
927 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
929 g_object_class_install_properties(obj_class, PROP_LAST, properties);
932 GType
933 purple_connection_get_type(void)
935 static GType type = 0;
937 if(type == 0) {
938 static const GTypeInfo info = {
939 sizeof(PurpleConnectionClass),
940 NULL,
941 NULL,
942 (GClassInitFunc)purple_connection_class_init,
943 NULL,
944 NULL,
945 sizeof(PurpleConnection),
947 (GInstanceInitFunc)purple_connection_init,
948 NULL,
951 type = g_type_register_static(G_TYPE_OBJECT, "PurpleConnection",
952 &info, 0);
955 return type;
958 void
959 _purple_connection_new(PurpleAccount *account, gboolean regist, const char *password)
961 PurpleConnection *gc;
962 PurpleProtocol *protocol;
964 g_return_if_fail(PURPLE_IS_ACCOUNT(account));
966 if (!purple_account_is_disconnected(account))
967 return;
969 protocol = purple_protocols_find(purple_account_get_protocol_id(account));
971 if (protocol == NULL) {
972 gchar *message;
974 message = g_strdup_printf(_("Missing protocol for %s"),
975 purple_account_get_username(account));
976 purple_notify_error(NULL, regist ? _("Registration Error") :
977 _("Connection Error"), message, NULL,
978 purple_request_cpar_from_account(account));
979 g_free(message);
980 return;
983 if (regist)
985 if (!PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, register_user))
986 return;
988 else
990 if (((password == NULL) || (*password == '\0')) &&
991 !(purple_protocol_get_options(protocol) & OPT_PROTO_NO_PASSWORD) &&
992 !(purple_protocol_get_options(protocol) & OPT_PROTO_PASSWORD_OPTIONAL))
994 purple_debug_error("connection", "Cannot connect to account %s without "
995 "a password.\n", purple_account_get_username(account));
996 return;
1000 if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY_IFACE, connection_new))
1001 gc = purple_protocol_factory_iface_connection_new(protocol, account,
1002 password);
1003 else
1004 gc = g_object_new(PURPLE_TYPE_CONNECTION,
1005 "protocol", protocol,
1006 "account", account,
1007 "password", password,
1008 NULL);
1010 g_return_if_fail(gc != NULL);
1012 if (regist)
1014 purple_debug_info("connection", "Registering. gc = %p\n", gc);
1016 /* set this so we don't auto-reconnect after registering */
1017 PURPLE_CONNECTION_GET_PRIVATE(gc)->wants_to_die = TRUE;
1019 purple_protocol_server_iface_register_user(protocol, account);
1021 else
1023 purple_debug_info("connection", "Connecting. gc = %p\n", gc);
1025 purple_signal_emit(purple_accounts_get_handle(), "account-connecting", account);
1026 purple_protocol_class_login(protocol, account);
1030 void
1031 _purple_connection_new_unregister(PurpleAccount *account, const char *password,
1032 PurpleAccountUnregistrationCb cb, void *user_data)
1034 /* Lots of copy/pasted code to avoid API changes. You might want to integrate that into the previous function when posssible. */
1035 PurpleConnection *gc;
1036 PurpleProtocol *protocol;
1038 g_return_if_fail(PURPLE_IS_ACCOUNT(account));
1040 protocol = purple_protocols_find(purple_account_get_protocol_id(account));
1042 if (protocol == NULL) {
1043 gchar *message;
1045 message = g_strdup_printf(_("Missing protocol for %s"),
1046 purple_account_get_username(account));
1047 purple_notify_error(NULL, _("Unregistration Error"), message,
1048 NULL, purple_request_cpar_from_account(account));
1049 g_free(message);
1050 return;
1053 if (!purple_account_is_disconnected(account)) {
1054 purple_protocol_server_iface_unregister_user(protocol, account, cb, user_data);
1055 return;
1058 if (((password == NULL) || (*password == '\0')) &&
1059 !(purple_protocol_get_options(protocol) & OPT_PROTO_NO_PASSWORD) &&
1060 !(purple_protocol_get_options(protocol) & OPT_PROTO_PASSWORD_OPTIONAL))
1062 purple_debug_error("connection", "Cannot connect to account %s without "
1063 "a password.\n", purple_account_get_username(account));
1064 return;
1067 if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY_IFACE, connection_new))
1068 gc = purple_protocol_factory_iface_connection_new(protocol, account,
1069 password);
1070 else
1071 gc = g_object_new(PURPLE_TYPE_CONNECTION,
1072 "protocol", protocol,
1073 "account", account,
1074 "password", password,
1075 NULL);
1077 g_return_if_fail(gc != NULL);
1079 purple_debug_info("connection", "Unregistering. gc = %p\n", gc);
1081 purple_protocol_server_iface_unregister_user(protocol, account, cb, user_data);
1084 /**************************************************************************
1085 * Connections API
1086 **************************************************************************/
1088 void
1089 _purple_assert_connection_is_valid(PurpleConnection *gc,
1090 const gchar *file, int line)
1092 if (gc && g_list_find(purple_connections_get_all(), gc))
1093 return;
1095 purple_debug_fatal("connection", "PURPLE_ASSERT_CONNECTION_IS_VALID(%p)"
1096 " failed at %s:%d", gc, file, line);
1097 exit(-1);
1100 void
1101 purple_connections_disconnect_all(void)
1103 GList *l;
1104 PurpleConnection *gc;
1105 PurpleConnectionPrivate *priv;
1107 while ((l = purple_connections_get_all()) != NULL) {
1108 gc = l->data;
1109 priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
1110 priv->wants_to_die = TRUE;
1111 purple_account_disconnect(priv->account);
1115 GList *
1116 purple_connections_get_all(void)
1118 return connections;
1121 GList *
1122 purple_connections_get_connecting(void)
1124 return connections_connecting;
1127 void
1128 purple_connections_set_ui_ops(PurpleConnectionUiOps *ops)
1130 connection_ui_ops = ops;
1133 PurpleConnectionUiOps *
1134 purple_connections_get_ui_ops(void)
1136 return connection_ui_ops;
1139 void
1140 purple_connections_init(void)
1142 void *handle = purple_connections_get_handle();
1144 purple_signal_register(handle, "signing-on",
1145 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
1146 PURPLE_TYPE_CONNECTION);
1148 purple_signal_register(handle, "signed-on",
1149 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
1150 PURPLE_TYPE_CONNECTION);
1152 purple_signal_register(handle, "signing-off",
1153 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
1154 PURPLE_TYPE_CONNECTION);
1156 purple_signal_register(handle, "signed-off",
1157 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
1158 PURPLE_TYPE_CONNECTION);
1160 purple_signal_register(handle, "connection-error",
1161 purple_marshal_VOID__POINTER_INT_POINTER,
1162 G_TYPE_NONE, 3, PURPLE_TYPE_CONNECTION,
1163 PURPLE_TYPE_CONNECTION_ERROR, G_TYPE_STRING);
1165 purple_signal_register(handle, "autojoin",
1166 purple_marshal_BOOLEAN__POINTER, G_TYPE_NONE, 1,
1167 PURPLE_TYPE_CONNECTION);
1171 void
1172 purple_connections_uninit(void)
1174 purple_signals_unregister_by_instance(purple_connections_get_handle());
1177 void *
1178 purple_connections_get_handle(void)
1180 return &connections_handle;