Merged pidgin/main into default
[pidgin-git.git] / libpurple / connection.c
blob20b042f81a7823857fa94bb8a40c192afb7be986
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 = g_timeout_add_seconds(KEEPALIVE_INTERVAL, send_keepalive, gc);
151 else if (!on && priv->keepalive > 0)
153 purple_debug_info("connection", "Deactivating keepalive.\n");
154 g_source_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 GDateTime *dt = g_date_time_new_from_unix_local(purple_presence_get_login_time(presence));
210 purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
211 purple_account_get_username(account),
212 dt, msg);
213 g_date_time_unref(dt);
214 g_free(msg);
218 if (ops != NULL && ops->connected != NULL)
219 ops->connected(gc);
221 purple_blist_add_account(account);
223 purple_signal_emit(purple_connections_get_handle(), "signed-on", gc);
224 purple_signal_emit_return_1(purple_connections_get_handle(), "autojoin", gc);
226 purple_serv_set_permit_deny(gc);
228 update_keepalive(gc, TRUE);
230 else if (priv->state == PURPLE_CONNECTION_DISCONNECTED) {
231 PurpleAccount *account = purple_connection_get_account(gc);
233 if (purple_prefs_get_bool("/purple/logging/log_system"))
235 PurpleLog *log = purple_account_get_log(account, FALSE);
237 if (log != NULL)
239 char *msg = g_strdup_printf(_("+++ %s signed off"),
240 purple_account_get_username(account));
241 GDateTime *dt = g_date_time_new_now_utc();
242 purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
243 purple_account_get_username(account),
244 dt, msg);
245 g_date_time_unref(dt);
246 g_free(msg);
250 purple_account_destroy_log(account);
252 if (ops != NULL && ops->disconnected != NULL)
253 ops->disconnected(gc);
256 if (!priv->is_finalizing)
257 g_object_notify_by_pspec(G_OBJECT(gc), properties[PROP_STATE]);
260 void
261 purple_connection_set_flags(PurpleConnection *gc, PurpleConnectionFlags flags)
263 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
265 g_return_if_fail(priv != NULL);
267 priv->flags = flags;
269 if (!priv->is_finalizing)
270 g_object_notify_by_pspec(G_OBJECT(gc), properties[PROP_FLAGS]);
273 void
274 purple_connection_set_display_name(PurpleConnection *gc, const char *name)
276 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
278 g_return_if_fail(priv != NULL);
280 g_free(priv->display_name);
281 priv->display_name = g_strdup(name);
283 g_object_notify_by_pspec(G_OBJECT(gc), properties[PROP_DISPLAY_NAME]);
286 void
287 purple_connection_set_protocol_data(PurpleConnection *gc, void *proto_data)
289 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
291 g_return_if_fail(priv != NULL);
293 priv->proto_data = proto_data;
296 PurpleConnectionState
297 purple_connection_get_state(const PurpleConnection *gc)
299 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
301 g_return_val_if_fail(priv != NULL, PURPLE_CONNECTION_DISCONNECTED);
303 return priv->state;
306 PurpleConnectionFlags
307 purple_connection_get_flags(const PurpleConnection *gc)
309 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
311 g_return_val_if_fail(priv != NULL, 0);
313 return priv->flags;
316 gboolean
317 purple_connection_is_disconnecting(const PurpleConnection *gc)
319 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
321 g_return_val_if_fail(priv != NULL, TRUE);
323 return priv->is_finalizing;
326 PurpleAccount *
327 purple_connection_get_account(const PurpleConnection *gc)
329 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
331 g_return_val_if_fail(priv != NULL, NULL);
333 return priv->account;
336 PurpleProtocol *
337 purple_connection_get_protocol(const PurpleConnection *gc)
339 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
341 g_return_val_if_fail(priv != NULL, NULL);
343 return priv->protocol;
346 const char *
347 purple_connection_get_password(const PurpleConnection *gc)
349 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
351 g_return_val_if_fail(priv != NULL, NULL);
353 return priv->password;
356 GSList *
357 purple_connection_get_active_chats(const PurpleConnection *gc)
359 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
361 g_return_val_if_fail(priv != NULL, NULL);
363 return priv->active_chats;
366 const char *
367 purple_connection_get_display_name(const PurpleConnection *gc)
369 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
371 g_return_val_if_fail(priv != NULL, NULL);
373 return priv->display_name;
376 void *
377 purple_connection_get_protocol_data(const PurpleConnection *gc)
379 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
381 g_return_val_if_fail(priv != NULL, NULL);
383 return priv->proto_data;
386 void
387 _purple_connection_add_active_chat(PurpleConnection *gc, PurpleChatConversation *chat)
389 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
391 g_return_if_fail(priv != NULL);
393 priv->active_chats = g_slist_append(priv->active_chats, chat);
396 void
397 _purple_connection_remove_active_chat(PurpleConnection *gc, PurpleChatConversation *chat)
399 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
401 g_return_if_fail(priv != NULL);
403 priv->active_chats = g_slist_remove(priv->active_chats, chat);
406 gboolean
407 _purple_connection_wants_to_die(const PurpleConnection *gc)
409 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
411 g_return_val_if_fail(priv != NULL, FALSE);
413 return priv->wants_to_die;
416 void
417 purple_connection_update_progress(PurpleConnection *gc, const char *text,
418 size_t step, size_t count)
420 PurpleConnectionUiOps *ops;
422 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
423 g_return_if_fail(text != NULL);
424 g_return_if_fail(step < count);
425 g_return_if_fail(count > 1);
427 ops = purple_connections_get_ui_ops();
429 if (ops != NULL && ops->connect_progress != NULL)
430 ops->connect_progress(gc, text, step, count);
433 void
434 purple_connection_notice(PurpleConnection *gc, const char *text)
436 PurpleConnectionUiOps *ops;
438 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
439 g_return_if_fail(text != NULL);
441 ops = purple_connections_get_ui_ops();
443 if (ops != NULL && ops->notice != NULL)
444 ops->notice(gc, text);
447 static gboolean
448 purple_connection_disconnect_cb(gpointer data)
450 PurpleAccount *account;
451 PurpleConnection *gc;
452 PurpleConnectionPrivate *priv;
454 account = data;
455 gc = purple_account_get_connection(account);
456 priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
458 if (priv != NULL)
459 priv->disconnect_timeout = 0;
461 purple_account_disconnect(account);
462 return FALSE;
465 void
466 purple_connection_error (PurpleConnection *gc,
467 PurpleConnectionError reason,
468 const char *description)
470 PurpleConnectionUiOps *ops;
471 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
473 g_return_if_fail(priv != NULL);
474 /* This sanity check relies on PURPLE_CONNECTION_ERROR_OTHER_ERROR
475 * being the last member of the PurpleConnectionError enum in
476 * connection.h; if other reasons are added after it, this check should
477 * be updated.
479 if (reason > PURPLE_CONNECTION_ERROR_OTHER_ERROR) {
480 purple_debug_error("connection",
481 "purple_connection_error: reason %u isn't a "
482 "valid reason\n", reason);
483 reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
486 if (description == NULL) {
487 purple_debug_error("connection", "purple_connection_error called with NULL description\n");
488 description = _("Unknown error");
491 /* If we've already got one error, we don't need any more */
492 if (purple_connection_get_error_info(gc))
493 return;
495 priv->wants_to_die = purple_connection_error_is_fatal (reason);
497 purple_debug_info("connection", "Connection error on %p (reason: %u description: %s)\n",
498 gc, reason, description);
500 ops = purple_connections_get_ui_ops();
502 if (ops && ops->report_disconnect)
503 ops->report_disconnect(gc, reason, description);
505 priv->error_info = purple_connection_error_info_new(reason, description);
507 purple_signal_emit(purple_connections_get_handle(), "connection-error",
508 gc, reason, description);
510 priv->disconnect_timeout = g_timeout_add(0, purple_connection_disconnect_cb,
511 purple_connection_get_account(gc));
514 PurpleConnectionErrorInfo *
515 purple_connection_get_error_info(const PurpleConnection *gc)
517 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
519 g_return_val_if_fail(priv != NULL, NULL);
521 return priv->error_info;
524 void
525 purple_connection_ssl_error (PurpleConnection *gc,
526 PurpleSslErrorType ssl_error)
528 PurpleConnectionError reason;
530 switch (ssl_error) {
531 case PURPLE_SSL_HANDSHAKE_FAILED:
532 reason = PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR;
533 break;
534 case PURPLE_SSL_CONNECT_FAILED:
535 reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
536 break;
537 case PURPLE_SSL_CERTIFICATE_INVALID:
538 /* TODO: maybe PURPLE_SSL_* should be more specific? */
539 reason = PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR;
540 break;
541 default:
542 g_assert_not_reached ();
543 reason = PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR;
546 purple_connection_error (gc, reason,
547 purple_ssl_strerror(ssl_error));
550 void
551 purple_connection_g_error(PurpleConnection *pc, const GError *error)
553 PurpleConnectionError reason;
555 if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
556 /* Not a connection error. Ignore. */
557 return;
560 if (error->domain == G_TLS_ERROR) {
561 switch (error->code) {
562 case G_TLS_ERROR_UNAVAILABLE:
563 reason = PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT;
564 break;
565 case G_TLS_ERROR_NOT_TLS:
566 case G_TLS_ERROR_HANDSHAKE:
567 reason = PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR;
568 break;
569 case G_TLS_ERROR_BAD_CERTIFICATE:
570 case G_TLS_ERROR_CERTIFICATE_REQUIRED:
571 reason = PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR;
572 break;
573 case G_TLS_ERROR_EOF:
574 case G_TLS_ERROR_MISC:
575 default:
576 reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
578 } else if (error->domain == G_IO_ERROR) {
579 reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
580 } else if (error->domain == PURPLE_CONNECTION_ERROR) {
581 reason = error->code;
582 } else {
583 reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
586 purple_connection_error(pc, reason, error->message);
589 void
590 purple_connection_take_error(PurpleConnection *pc, GError *error)
592 purple_connection_g_error(pc, error);
593 g_error_free(error);
596 gboolean
597 purple_connection_error_is_fatal (PurpleConnectionError reason)
599 switch (reason)
601 case PURPLE_CONNECTION_ERROR_NETWORK_ERROR:
602 case PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR:
603 return FALSE;
604 case PURPLE_CONNECTION_ERROR_INVALID_USERNAME:
605 case PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED:
606 case PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE:
607 case PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT:
608 case PURPLE_CONNECTION_ERROR_NAME_IN_USE:
609 case PURPLE_CONNECTION_ERROR_INVALID_SETTINGS:
610 case PURPLE_CONNECTION_ERROR_OTHER_ERROR:
611 case PURPLE_CONNECTION_ERROR_CERT_NOT_PROVIDED:
612 case PURPLE_CONNECTION_ERROR_CERT_UNTRUSTED:
613 case PURPLE_CONNECTION_ERROR_CERT_EXPIRED:
614 case PURPLE_CONNECTION_ERROR_CERT_NOT_ACTIVATED:
615 case PURPLE_CONNECTION_ERROR_CERT_HOSTNAME_MISMATCH:
616 case PURPLE_CONNECTION_ERROR_CERT_FINGERPRINT_MISMATCH:
617 case PURPLE_CONNECTION_ERROR_CERT_SELF_SIGNED:
618 case PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR:
619 return TRUE;
620 default:
621 g_return_val_if_reached(TRUE);
625 void purple_connection_update_last_received(PurpleConnection *gc)
627 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
629 g_return_if_fail(priv != NULL);
631 priv->last_received = time(NULL);
634 static PurpleConnectionErrorInfo *
635 purple_connection_error_info_new(PurpleConnectionError type,
636 const gchar *description)
638 PurpleConnectionErrorInfo *err;
640 g_return_val_if_fail(description != NULL, NULL);
642 err = g_new(PurpleConnectionErrorInfo, 1);
644 err->type = type;
645 err->description = g_strdup(description);
647 return err;
650 /**************************************************************************
651 * GBoxed code
652 **************************************************************************/
653 static PurpleConnectionUiOps *
654 purple_connection_ui_ops_copy(PurpleConnectionUiOps *ops)
656 PurpleConnectionUiOps *ops_new;
658 g_return_val_if_fail(ops != NULL, NULL);
660 ops_new = g_new(PurpleConnectionUiOps, 1);
661 *ops_new = *ops;
663 return ops_new;
666 GType
667 purple_connection_ui_ops_get_type(void)
669 static GType type = 0;
671 if (type == 0) {
672 type = g_boxed_type_register_static("PurpleConnectionUiOps",
673 (GBoxedCopyFunc)purple_connection_ui_ops_copy,
674 (GBoxedFreeFunc)g_free);
677 return type;
680 static PurpleConnectionErrorInfo *
681 purple_connection_error_info_copy(PurpleConnectionErrorInfo *err)
683 g_return_val_if_fail(err != NULL, NULL);
685 return purple_connection_error_info_new(err->type, err->description);
688 static void
689 purple_connection_error_info_free(PurpleConnectionErrorInfo *err)
691 g_return_if_fail(err != NULL);
693 g_free(err->description);
694 g_free(err);
697 GType
698 purple_connection_error_info_get_type(void)
700 static GType type = 0;
702 if (type == 0) {
703 type = g_boxed_type_register_static("PurpleConnectionErrorInfo",
704 (GBoxedCopyFunc)purple_connection_error_info_copy,
705 (GBoxedFreeFunc)purple_connection_error_info_free);
708 return type;
711 /**************************************************************************
712 * GObject code
713 **************************************************************************/
715 /* Set method for GObject properties */
716 static void
717 purple_connection_set_property(GObject *obj, guint param_id, const GValue *value,
718 GParamSpec *pspec)
720 PurpleConnection *gc = PURPLE_CONNECTION(obj);
721 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
723 switch (param_id) {
724 case PROP_PROTOCOL:
725 priv->protocol = g_value_get_object(value);
726 break;
727 case PROP_FLAGS:
728 purple_connection_set_flags(gc, g_value_get_flags(value));
729 break;
730 case PROP_STATE:
731 purple_connection_set_state(gc, g_value_get_enum(value));
732 break;
733 case PROP_ACCOUNT:
734 priv->account = g_value_get_object(value);
735 break;
736 case PROP_PASSWORD:
737 g_free(priv->password);
738 priv->password = g_strdup(g_value_get_string(value));
739 break;
740 case PROP_DISPLAY_NAME:
741 purple_connection_set_display_name(gc, g_value_get_string(value));
742 break;
743 default:
744 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
745 break;
749 /* Get method for GObject properties */
750 static void
751 purple_connection_get_property(GObject *obj, guint param_id, GValue *value,
752 GParamSpec *pspec)
754 PurpleConnection *gc = PURPLE_CONNECTION(obj);
756 switch (param_id) {
757 case PROP_PROTOCOL:
758 g_value_set_object(value, purple_connection_get_protocol(gc));
759 break;
760 case PROP_FLAGS:
761 g_value_set_flags(value, purple_connection_get_flags(gc));
762 break;
763 case PROP_STATE:
764 g_value_set_enum(value, purple_connection_get_state(gc));
765 break;
766 case PROP_ACCOUNT:
767 g_value_set_object(value, purple_connection_get_account(gc));
768 break;
769 case PROP_PASSWORD:
770 g_value_set_string(value, purple_connection_get_password(gc));
771 break;
772 case PROP_DISPLAY_NAME:
773 g_value_set_string(value, purple_connection_get_display_name(gc));
774 break;
775 default:
776 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
777 break;
781 /* GObject initialization function */
782 static void
783 purple_connection_init(GTypeInstance *instance, gpointer klass)
785 PurpleConnection *gc = PURPLE_CONNECTION(instance);
787 purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTING);
788 connections = g_list_append(connections, gc);
790 PURPLE_DBUS_REGISTER_POINTER(gc, PurpleConnection);
793 /* Called when done constructing */
794 static void
795 purple_connection_constructed(GObject *object)
797 PurpleConnection *gc = PURPLE_CONNECTION(object);
798 PurpleAccount *account;
800 G_OBJECT_CLASS(parent_class)->constructed(object);
802 g_object_get(gc, "account", &account, NULL);
803 purple_account_set_connection(account, gc);
804 g_object_unref(account);
806 purple_signal_emit(purple_connections_get_handle(), "signing-on", gc);
809 /* GObject finalize function */
810 static void
811 purple_connection_finalize(GObject *object)
813 PurpleConnection *gc = PURPLE_CONNECTION(object);
814 PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
815 PurpleAccount *account;
816 GSList *buddies;
817 gboolean remove = FALSE;
819 priv->is_finalizing = TRUE;
821 account = purple_connection_get_account(gc);
823 purple_debug_info("connection", "Disconnecting connection %p\n", gc);
825 if (purple_connection_get_state(gc) != PURPLE_CONNECTION_CONNECTING)
826 remove = TRUE;
828 purple_signal_emit(purple_connections_get_handle(), "signing-off", gc);
830 while (priv->active_chats)
832 PurpleChatConversation *b = priv->active_chats->data;
834 priv->active_chats = g_slist_remove(priv->active_chats, b);
835 purple_chat_conversation_leave(b);
838 update_keepalive(gc, FALSE);
840 purple_protocol_class_close(priv->protocol, gc);
842 /* Clear out the proto data that was freed in the protocol's close method */
843 buddies = purple_blist_find_buddies(account, NULL);
844 while (buddies != NULL) {
845 PurpleBuddy *buddy = buddies->data;
846 purple_buddy_set_protocol_data(buddy, NULL);
847 buddies = g_slist_delete_link(buddies, buddies);
850 purple_http_conn_cancel_all(gc);
851 purple_proxy_connect_cancel_with_handle(gc);
853 connections = g_list_remove(connections, gc);
855 purple_connection_set_state(gc, PURPLE_CONNECTION_DISCONNECTED);
857 if (remove)
858 purple_blist_remove_account(account);
860 purple_signal_emit(purple_connections_get_handle(), "signed-off", gc);
862 purple_account_request_close_with_account(account);
863 purple_request_close_with_handle(gc);
864 purple_notify_close_with_handle(gc);
866 purple_debug_info("connection", "Destroying connection %p\n", gc);
868 purple_account_set_connection(account, NULL);
870 if (priv->error_info)
871 purple_connection_error_info_free(priv->error_info);
873 if (priv->disconnect_timeout > 0)
874 g_source_remove(priv->disconnect_timeout);
876 purple_str_wipe(priv->password);
877 g_free(priv->display_name);
879 PURPLE_DBUS_UNREGISTER_POINTER(gc);
881 G_OBJECT_CLASS(parent_class)->finalize(object);
884 /* Class initializer function */
885 static void purple_connection_class_init(PurpleConnectionClass *klass)
887 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
889 parent_class = g_type_class_peek_parent(klass);
891 obj_class->finalize = purple_connection_finalize;
892 obj_class->constructed = purple_connection_constructed;
894 /* Setup properties */
895 obj_class->get_property = purple_connection_get_property;
896 obj_class->set_property = purple_connection_set_property;
898 g_type_class_add_private(klass, sizeof(PurpleConnectionPrivate));
900 properties[PROP_PROTOCOL] = g_param_spec_object("protocol", "Protocol",
901 "The protocol that the connection is using.",
902 PURPLE_TYPE_PROTOCOL,
903 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
904 G_PARAM_STATIC_STRINGS);
906 properties[PROP_FLAGS] = g_param_spec_flags("flags", "Connection flags",
907 "The flags of the connection.",
908 PURPLE_TYPE_CONNECTION_FLAGS, 0,
909 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
911 properties[PROP_STATE] = g_param_spec_enum("state", "Connection state",
912 "The current state of the connection.",
913 PURPLE_TYPE_CONNECTION_STATE, PURPLE_CONNECTION_DISCONNECTED,
914 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
916 properties[PROP_ACCOUNT] = g_param_spec_object("account", "Account",
917 "The account using the connection.", PURPLE_TYPE_ACCOUNT,
918 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
919 G_PARAM_STATIC_STRINGS);
921 properties[PROP_PASSWORD] = g_param_spec_string("password", "Password",
922 "The password used for connection.", NULL,
923 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
924 G_PARAM_STATIC_STRINGS);
926 properties[PROP_DISPLAY_NAME] = g_param_spec_string("display-name",
927 "Display name",
928 "Your name that appears to other people.", NULL,
929 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
931 g_object_class_install_properties(obj_class, PROP_LAST, properties);
934 GType
935 purple_connection_get_type(void)
937 static GType type = 0;
939 if(type == 0) {
940 static const GTypeInfo info = {
941 sizeof(PurpleConnectionClass),
942 NULL,
943 NULL,
944 (GClassInitFunc)purple_connection_class_init,
945 NULL,
946 NULL,
947 sizeof(PurpleConnection),
949 (GInstanceInitFunc)purple_connection_init,
950 NULL,
953 type = g_type_register_static(G_TYPE_OBJECT, "PurpleConnection",
954 &info, 0);
957 return type;
960 void
961 _purple_connection_new(PurpleAccount *account, gboolean regist, const char *password)
963 PurpleConnection *gc;
964 PurpleProtocol *protocol;
966 g_return_if_fail(PURPLE_IS_ACCOUNT(account));
968 if (!purple_account_is_disconnected(account))
969 return;
971 protocol = purple_protocols_find(purple_account_get_protocol_id(account));
973 if (protocol == NULL) {
974 gchar *message;
976 message = g_strdup_printf(_("Missing protocol for %s"),
977 purple_account_get_username(account));
978 purple_notify_error(NULL, regist ? _("Registration Error") :
979 _("Connection Error"), message, NULL,
980 purple_request_cpar_from_account(account));
981 g_free(message);
982 return;
985 if (regist)
987 if (!PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, register_user))
988 return;
990 else
992 if (((password == NULL) || (*password == '\0')) &&
993 !(purple_protocol_get_options(protocol) & OPT_PROTO_NO_PASSWORD) &&
994 !(purple_protocol_get_options(protocol) & OPT_PROTO_PASSWORD_OPTIONAL))
996 purple_debug_error("connection", "Cannot connect to account %s without "
997 "a password.\n", purple_account_get_username(account));
998 return;
1002 if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY_IFACE, connection_new))
1003 gc = purple_protocol_factory_iface_connection_new(protocol, account,
1004 password);
1005 else
1006 gc = g_object_new(PURPLE_TYPE_CONNECTION,
1007 "protocol", protocol,
1008 "account", account,
1009 "password", password,
1010 NULL);
1012 g_return_if_fail(gc != NULL);
1014 if (regist)
1016 purple_debug_info("connection", "Registering. gc = %p\n", gc);
1018 /* set this so we don't auto-reconnect after registering */
1019 PURPLE_CONNECTION_GET_PRIVATE(gc)->wants_to_die = TRUE;
1021 purple_protocol_server_iface_register_user(protocol, account);
1023 else
1025 purple_debug_info("connection", "Connecting. gc = %p\n", gc);
1027 purple_signal_emit(purple_accounts_get_handle(), "account-connecting", account);
1028 purple_protocol_class_login(protocol, account);
1032 void
1033 _purple_connection_new_unregister(PurpleAccount *account, const char *password,
1034 PurpleAccountUnregistrationCb cb, void *user_data)
1036 /* Lots of copy/pasted code to avoid API changes. You might want to integrate that into the previous function when posssible. */
1037 PurpleConnection *gc;
1038 PurpleProtocol *protocol;
1040 g_return_if_fail(PURPLE_IS_ACCOUNT(account));
1042 protocol = purple_protocols_find(purple_account_get_protocol_id(account));
1044 if (protocol == NULL) {
1045 gchar *message;
1047 message = g_strdup_printf(_("Missing protocol for %s"),
1048 purple_account_get_username(account));
1049 purple_notify_error(NULL, _("Unregistration Error"), message,
1050 NULL, purple_request_cpar_from_account(account));
1051 g_free(message);
1052 return;
1055 if (!purple_account_is_disconnected(account)) {
1056 purple_protocol_server_iface_unregister_user(protocol, account, cb, user_data);
1057 return;
1060 if (((password == NULL) || (*password == '\0')) &&
1061 !(purple_protocol_get_options(protocol) & OPT_PROTO_NO_PASSWORD) &&
1062 !(purple_protocol_get_options(protocol) & OPT_PROTO_PASSWORD_OPTIONAL))
1064 purple_debug_error("connection", "Cannot connect to account %s without "
1065 "a password.\n", purple_account_get_username(account));
1066 return;
1069 if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY_IFACE, connection_new))
1070 gc = purple_protocol_factory_iface_connection_new(protocol, account,
1071 password);
1072 else
1073 gc = g_object_new(PURPLE_TYPE_CONNECTION,
1074 "protocol", protocol,
1075 "account", account,
1076 "password", password,
1077 NULL);
1079 g_return_if_fail(gc != NULL);
1081 purple_debug_info("connection", "Unregistering. gc = %p\n", gc);
1083 purple_protocol_server_iface_unregister_user(protocol, account, cb, user_data);
1086 /**************************************************************************
1087 * Connections API
1088 **************************************************************************/
1090 void
1091 _purple_assert_connection_is_valid(PurpleConnection *gc,
1092 const gchar *file, int line)
1094 if (gc && g_list_find(purple_connections_get_all(), gc))
1095 return;
1097 purple_debug_fatal("connection", "PURPLE_ASSERT_CONNECTION_IS_VALID(%p)"
1098 " failed at %s:%d", gc, file, line);
1099 exit(-1);
1102 void
1103 purple_connections_disconnect_all(void)
1105 GList *l;
1106 PurpleConnection *gc;
1107 PurpleConnectionPrivate *priv;
1109 while ((l = purple_connections_get_all()) != NULL) {
1110 gc = l->data;
1111 priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
1112 priv->wants_to_die = TRUE;
1113 purple_account_disconnect(priv->account);
1117 GList *
1118 purple_connections_get_all(void)
1120 return connections;
1123 GList *
1124 purple_connections_get_connecting(void)
1126 return connections_connecting;
1129 void
1130 purple_connections_set_ui_ops(PurpleConnectionUiOps *ops)
1132 connection_ui_ops = ops;
1135 PurpleConnectionUiOps *
1136 purple_connections_get_ui_ops(void)
1138 return connection_ui_ops;
1141 void
1142 purple_connections_init(void)
1144 void *handle = purple_connections_get_handle();
1146 purple_signal_register(handle, "signing-on",
1147 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
1148 PURPLE_TYPE_CONNECTION);
1150 purple_signal_register(handle, "signed-on",
1151 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
1152 PURPLE_TYPE_CONNECTION);
1154 purple_signal_register(handle, "signing-off",
1155 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
1156 PURPLE_TYPE_CONNECTION);
1158 purple_signal_register(handle, "signed-off",
1159 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
1160 PURPLE_TYPE_CONNECTION);
1162 purple_signal_register(handle, "connection-error",
1163 purple_marshal_VOID__POINTER_INT_POINTER,
1164 G_TYPE_NONE, 3, PURPLE_TYPE_CONNECTION,
1165 PURPLE_TYPE_CONNECTION_ERROR, G_TYPE_STRING);
1167 purple_signal_register(handle, "autojoin",
1168 purple_marshal_BOOLEAN__POINTER, G_TYPE_NONE, 1,
1169 PURPLE_TYPE_CONNECTION);
1173 void
1174 purple_connections_uninit(void)
1176 purple_signals_unregister_by_instance(purple_connections_get_handle());
1179 void *
1180 purple_connections_get_handle(void)
1182 return &connections_handle;