Fix an incorrect call to soup_message_set_request.
[pidgin-git.git] / libpurple / connection.c
blobab08be542ecd4c703c0413c55becfe3f93bc6c2a
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
22 #include "internal.h"
23 #include "glibcompat.h"
25 #include "account.h"
26 #include "buddylist.h"
27 #include "connection.h"
28 #include "debug.h"
29 #include "enums.h"
30 #include "http.h"
31 #include "log.h"
32 #include "notify.h"
33 #include "prefs.h"
34 #include "proxy.h"
35 #include "request.h"
36 #include "server.h"
37 #include "signals.h"
38 #include "util.h"
40 G_DEFINE_QUARK(purple-connection-error-quark, purple_connection_error);
42 /**
43 * PurpleConnection:
45 * Represents an active connection on an account.
47 struct _PurpleConnection
49 GObject gparent;
52 /* Private data for a connection */
53 typedef struct
55 PurpleProtocol *protocol; /* The protocol. */
56 PurpleConnectionFlags flags; /* Connection flags. */
58 PurpleConnectionState state; /* The connection state. */
60 PurpleAccount *account; /* The account being connected to. */
61 char *password; /* The password used. */
63 GSList *active_chats; /* A list of active chats
64 (#PurpleChatConversation structs). */
66 /* TODO Remove this and use protocol-specific subclasses. */
67 void *proto_data; /* Protocol-specific data. */
69 char *display_name; /* How you appear to other people. */
70 GSource *keepalive; /* Keep-alive. */
72 /* Wants to Die state. This is set when the user chooses to log out, or
73 * when the protocol is disconnected and should not be automatically
74 * reconnected (incorrect password, etc.). Protocols should rely on
75 * purple_connection_error() to set this for them rather than
76 * setting it themselves.
77 * See purple_connection_error_is_fatal()
79 gboolean wants_to_die;
81 gboolean is_finalizing; /* The object is being destroyed. */
83 /* The connection error and its description if an error occured */
84 PurpleConnectionErrorInfo *error_info;
86 guint disconnect_timeout; /* Timer used for nasty stack tricks */
87 } PurpleConnectionPrivate;
89 /* GObject property enums */
90 enum
92 PROP_0,
93 PROP_PROTOCOL,
94 PROP_FLAGS,
95 PROP_STATE,
96 PROP_ACCOUNT,
97 PROP_PASSWORD,
98 PROP_DISPLAY_NAME,
99 PROP_LAST
102 static GParamSpec *properties[PROP_LAST];
104 static GList *connections = NULL;
105 static GList *connections_connecting = NULL;
106 static PurpleConnectionUiOps *connection_ui_ops = NULL;
108 static int connections_handle;
110 static PurpleConnectionErrorInfo *
111 purple_connection_error_info_new(PurpleConnectionError type,
112 const gchar *description);
114 G_DEFINE_TYPE_WITH_PRIVATE(PurpleConnection, purple_connection, G_TYPE_OBJECT)
116 /**************************************************************************
117 * Connection API
118 **************************************************************************/
119 static gboolean
120 send_keepalive(gpointer data)
122 PurpleConnection *gc = data;
123 PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
125 purple_protocol_server_iface_keepalive(priv->protocol, gc);
127 return TRUE;
130 static void
131 update_keepalive(PurpleConnection *gc, gboolean on)
133 PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
135 if (!PURPLE_PROTOCOL_IMPLEMENTS(priv->protocol, SERVER, keepalive))
136 return;
138 if (on && !priv->keepalive)
140 int interval = purple_protocol_server_iface_get_keepalive_interval(priv->protocol);
141 purple_debug_info("connection", "Activating keepalive to %d seconds.", interval);
142 priv->keepalive = g_main_context_find_source_by_id(NULL, g_timeout_add_seconds(interval, send_keepalive, gc));
144 else if (!on && priv->keepalive)
146 purple_debug_info("connection", "Deactivating keepalive.\n");
147 g_source_destroy(priv->keepalive);
148 priv->keepalive = NULL;
153 * d:)->-<
155 * d:O-\-<
157 * d:D-/-<
159 * d8D->-< DANCE!
162 void
163 purple_connection_set_state(PurpleConnection *gc, PurpleConnectionState state)
165 PurpleConnectionPrivate *priv = NULL;
166 PurpleConnectionUiOps *ops;
168 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
170 priv = purple_connection_get_instance_private(gc);
172 if (priv->state == state)
173 return;
175 priv->state = state;
177 ops = purple_connections_get_ui_ops();
179 if (priv->state == PURPLE_CONNECTION_CONNECTING) {
180 connections_connecting = g_list_append(connections_connecting, gc);
182 else {
183 connections_connecting = g_list_remove(connections_connecting, gc);
186 if (priv->state == PURPLE_CONNECTION_CONNECTED) {
187 PurpleAccount *account;
188 PurplePresence *presence;
190 account = purple_connection_get_account(gc);
191 presence = purple_account_get_presence(account);
193 /* Set the time the account came online */
194 purple_presence_set_login_time(presence, time(NULL));
196 if (purple_prefs_get_bool("/purple/logging/log_system"))
198 PurpleLog *log = purple_account_get_log(account, TRUE);
200 if (log != NULL)
202 char *msg = g_strdup_printf(_("+++ %s signed on"),
203 purple_account_get_username(account));
204 GDateTime *dt = g_date_time_new_from_unix_local(purple_presence_get_login_time(presence));
205 purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
206 purple_account_get_username(account),
207 dt, msg);
208 g_date_time_unref(dt);
209 g_free(msg);
213 if (ops != NULL && ops->connected != NULL)
214 ops->connected(gc);
216 purple_blist_add_account(account);
218 purple_signal_emit(purple_connections_get_handle(), "signed-on", gc);
219 purple_signal_emit_return_1(purple_connections_get_handle(), "autojoin", gc);
221 purple_serv_set_permit_deny(gc);
223 update_keepalive(gc, TRUE);
225 else if (priv->state == PURPLE_CONNECTION_DISCONNECTED) {
226 PurpleAccount *account = purple_connection_get_account(gc);
228 if (purple_prefs_get_bool("/purple/logging/log_system"))
230 PurpleLog *log = purple_account_get_log(account, FALSE);
232 if (log != NULL)
234 char *msg = g_strdup_printf(_("+++ %s signed off"),
235 purple_account_get_username(account));
236 GDateTime *dt = g_date_time_new_now_utc();
237 purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
238 purple_account_get_username(account),
239 dt, msg);
240 g_date_time_unref(dt);
241 g_free(msg);
245 purple_account_destroy_log(account);
247 if (ops != NULL && ops->disconnected != NULL)
248 ops->disconnected(gc);
251 if (!priv->is_finalizing)
252 g_object_notify_by_pspec(G_OBJECT(gc), properties[PROP_STATE]);
255 void
256 purple_connection_set_flags(PurpleConnection *gc, PurpleConnectionFlags flags)
258 PurpleConnectionPrivate *priv = NULL;
260 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
262 priv = purple_connection_get_instance_private(gc);
263 priv->flags = flags;
265 if (!priv->is_finalizing)
266 g_object_notify_by_pspec(G_OBJECT(gc), properties[PROP_FLAGS]);
269 void
270 purple_connection_set_display_name(PurpleConnection *gc, const char *name)
272 PurpleConnectionPrivate *priv = NULL;
274 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
276 priv = purple_connection_get_instance_private(gc);
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 = NULL;
288 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
290 priv = purple_connection_get_instance_private(gc);
291 priv->proto_data = proto_data;
294 PurpleConnectionState
295 purple_connection_get_state(PurpleConnection *gc)
297 PurpleConnectionPrivate *priv = NULL;
299 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), PURPLE_CONNECTION_DISCONNECTED);
301 priv = purple_connection_get_instance_private(gc);
302 return priv->state;
305 PurpleConnectionFlags
306 purple_connection_get_flags(PurpleConnection *gc)
308 PurpleConnectionPrivate *priv = NULL;
310 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), 0);
312 priv = purple_connection_get_instance_private(gc);
313 return priv->flags;
316 gboolean
317 purple_connection_is_disconnecting(PurpleConnection *gc)
319 PurpleConnectionPrivate *priv = NULL;
321 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), TRUE);
323 priv = purple_connection_get_instance_private(gc);
324 return priv->is_finalizing;
327 PurpleAccount *
328 purple_connection_get_account(PurpleConnection *gc)
330 PurpleConnectionPrivate *priv = NULL;
332 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
334 priv = purple_connection_get_instance_private(gc);
335 return priv->account;
338 PurpleProtocol *
339 purple_connection_get_protocol(PurpleConnection *gc)
341 PurpleConnectionPrivate *priv = NULL;
343 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
345 priv = purple_connection_get_instance_private(gc);
346 return priv->protocol;
349 const char *
350 purple_connection_get_password(PurpleConnection *gc)
352 PurpleConnectionPrivate *priv = NULL;
354 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
356 priv = purple_connection_get_instance_private(gc);
357 return priv->password;
360 GSList *
361 purple_connection_get_active_chats(PurpleConnection *gc)
363 PurpleConnectionPrivate *priv = NULL;
365 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
367 priv = purple_connection_get_instance_private(gc);
368 return priv->active_chats;
371 const char *
372 purple_connection_get_display_name(PurpleConnection *gc)
374 PurpleConnectionPrivate *priv = NULL;
376 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
378 priv = purple_connection_get_instance_private(gc);
379 return priv->display_name;
382 void *
383 purple_connection_get_protocol_data(PurpleConnection *gc)
385 PurpleConnectionPrivate *priv = NULL;
387 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
389 priv = purple_connection_get_instance_private(gc);
390 return priv->proto_data;
393 void
394 _purple_connection_add_active_chat(PurpleConnection *gc, PurpleChatConversation *chat)
396 PurpleConnectionPrivate *priv = NULL;
398 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
400 priv = purple_connection_get_instance_private(gc);
401 priv->active_chats = g_slist_append(priv->active_chats, chat);
404 void
405 _purple_connection_remove_active_chat(PurpleConnection *gc, PurpleChatConversation *chat)
407 PurpleConnectionPrivate *priv = NULL;
409 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
411 priv = purple_connection_get_instance_private(gc);
412 priv->active_chats = g_slist_remove(priv->active_chats, chat);
415 gboolean
416 _purple_connection_wants_to_die(PurpleConnection *gc)
418 PurpleConnectionPrivate *priv = NULL;
420 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), FALSE);
422 priv = purple_connection_get_instance_private(gc);
423 return priv->wants_to_die;
426 void
427 purple_connection_update_progress(PurpleConnection *gc, const char *text,
428 size_t step, size_t count)
430 PurpleConnectionUiOps *ops;
432 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
433 g_return_if_fail(text != NULL);
434 g_return_if_fail(step < count);
435 g_return_if_fail(count > 1);
437 ops = purple_connections_get_ui_ops();
439 if (ops != NULL && ops->connect_progress != NULL)
440 ops->connect_progress(gc, text, step, count);
443 void
444 purple_connection_notice(PurpleConnection *gc, const char *text)
446 PurpleConnectionUiOps *ops;
448 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
449 g_return_if_fail(text != NULL);
451 ops = purple_connections_get_ui_ops();
453 if (ops != NULL && ops->notice != NULL)
454 ops->notice(gc, text);
457 static gboolean
458 purple_connection_disconnect_cb(gpointer data)
460 PurpleAccount *account;
461 PurpleConnection *gc;
463 account = data;
464 gc = purple_account_get_connection(account);
466 if (PURPLE_IS_CONNECTION(gc)) {
467 PurpleConnectionPrivate *priv;
468 priv = purple_connection_get_instance_private(gc);
469 priv->disconnect_timeout = 0;
472 purple_account_disconnect(account);
473 return FALSE;
476 void
477 purple_connection_error (PurpleConnection *gc,
478 PurpleConnectionError reason,
479 const char *description)
481 PurpleConnectionPrivate *priv = NULL;
482 PurpleConnectionUiOps *ops;
484 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
485 priv = purple_connection_get_instance_private(gc);
487 /* This sanity check relies on PURPLE_CONNECTION_ERROR_OTHER_ERROR
488 * being the last member of the PurpleConnectionError enum in
489 * connection.h; if other reasons are added after it, this check should
490 * be updated.
492 if (reason > PURPLE_CONNECTION_ERROR_OTHER_ERROR) {
493 purple_debug_error("connection",
494 "purple_connection_error: reason %u isn't a "
495 "valid reason\n", reason);
496 reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
499 if (description == NULL) {
500 purple_debug_error("connection", "purple_connection_error called with NULL description\n");
501 description = _("Unknown error");
504 /* If we've already got one error, we don't need any more */
505 if (purple_connection_get_error_info(gc))
506 return;
508 priv->wants_to_die = purple_connection_error_is_fatal (reason);
510 purple_debug_info("connection", "Connection error on %p (reason: %u description: %s)\n",
511 gc, reason, description);
513 ops = purple_connections_get_ui_ops();
515 if (ops && ops->report_disconnect)
516 ops->report_disconnect(gc, reason, description);
518 priv->error_info = purple_connection_error_info_new(reason, description);
520 purple_signal_emit(purple_connections_get_handle(), "connection-error",
521 gc, reason, description);
523 priv->disconnect_timeout = g_timeout_add(0, purple_connection_disconnect_cb,
524 purple_connection_get_account(gc));
527 PurpleConnectionErrorInfo *
528 purple_connection_get_error_info(PurpleConnection *gc)
530 PurpleConnectionPrivate *priv = NULL;
532 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
534 priv = purple_connection_get_instance_private(gc);
535 return priv->error_info;
538 void
539 purple_connection_ssl_error (PurpleConnection *gc,
540 PurpleSslErrorType ssl_error)
542 PurpleConnectionError reason;
544 switch (ssl_error) {
545 case PURPLE_SSL_HANDSHAKE_FAILED:
546 reason = PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR;
547 break;
548 case PURPLE_SSL_CONNECT_FAILED:
549 reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
550 break;
551 case PURPLE_SSL_CERTIFICATE_INVALID:
552 /* TODO: maybe PURPLE_SSL_* should be more specific? */
553 reason = PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR;
554 break;
555 default:
556 g_assert_not_reached ();
557 reason = PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR;
560 purple_connection_error (gc, reason,
561 purple_ssl_strerror(ssl_error));
564 void
565 purple_connection_g_error(PurpleConnection *pc, const GError *error)
567 PurpleConnectionError reason;
569 if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
570 /* Not a connection error. Ignore. */
571 return;
574 if (error->domain == G_TLS_ERROR) {
575 switch (error->code) {
576 case G_TLS_ERROR_UNAVAILABLE:
577 reason = PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT;
578 break;
579 case G_TLS_ERROR_NOT_TLS:
580 case G_TLS_ERROR_HANDSHAKE:
581 reason = PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR;
582 break;
583 case G_TLS_ERROR_BAD_CERTIFICATE:
584 case G_TLS_ERROR_CERTIFICATE_REQUIRED:
585 reason = PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR;
586 break;
587 case G_TLS_ERROR_EOF:
588 case G_TLS_ERROR_MISC:
589 default:
590 reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
592 } else if (error->domain == G_IO_ERROR) {
593 reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
594 } else if (error->domain == PURPLE_CONNECTION_ERROR) {
595 reason = error->code;
596 } else {
597 reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
600 purple_connection_error(pc, reason, error->message);
603 void
604 purple_connection_take_error(PurpleConnection *pc, GError *error)
606 purple_connection_g_error(pc, error);
607 g_error_free(error);
610 gboolean
611 purple_connection_error_is_fatal (PurpleConnectionError reason)
613 switch (reason)
615 case PURPLE_CONNECTION_ERROR_NETWORK_ERROR:
616 case PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR:
617 return FALSE;
618 case PURPLE_CONNECTION_ERROR_INVALID_USERNAME:
619 case PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED:
620 case PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE:
621 case PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT:
622 case PURPLE_CONNECTION_ERROR_NAME_IN_USE:
623 case PURPLE_CONNECTION_ERROR_INVALID_SETTINGS:
624 case PURPLE_CONNECTION_ERROR_OTHER_ERROR:
625 case PURPLE_CONNECTION_ERROR_CERT_NOT_PROVIDED:
626 case PURPLE_CONNECTION_ERROR_CERT_UNTRUSTED:
627 case PURPLE_CONNECTION_ERROR_CERT_EXPIRED:
628 case PURPLE_CONNECTION_ERROR_CERT_NOT_ACTIVATED:
629 case PURPLE_CONNECTION_ERROR_CERT_HOSTNAME_MISMATCH:
630 case PURPLE_CONNECTION_ERROR_CERT_FINGERPRINT_MISMATCH:
631 case PURPLE_CONNECTION_ERROR_CERT_SELF_SIGNED:
632 case PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR:
633 return TRUE;
634 default:
635 g_return_val_if_reached(TRUE);
639 void purple_connection_update_last_received(PurpleConnection *gc)
641 PurpleConnectionPrivate *priv = NULL;
643 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
644 priv = purple_connection_get_instance_private(gc);
647 * For safety, actually this function shouldn't be called when the
648 * keepalive mechanism is inactive.
650 if (priv->keepalive) {
651 /* The #GTimeoutSource API doesn't expose a function to reset when a
652 * #GTimeoutSource will dispatch the next time, but because it works to
653 * directly call g_source_set_ready_time() on a #GTimeoutSource, and since
654 * it seems unlikely that the implementation will change, we just do that
655 * for now as a workaround for this API shortcoming.
657 gint64 seconds_from_now = purple_protocol_server_iface_get_keepalive_interval(priv->protocol);
659 g_source_set_ready_time(
660 priv->keepalive,
661 g_get_monotonic_time() + (seconds_from_now * G_USEC_PER_SEC)
666 static PurpleConnectionErrorInfo *
667 purple_connection_error_info_new(PurpleConnectionError type,
668 const gchar *description)
670 PurpleConnectionErrorInfo *err;
672 g_return_val_if_fail(description != NULL, NULL);
674 err = g_new(PurpleConnectionErrorInfo, 1);
676 err->type = type;
677 err->description = g_strdup(description);
679 return err;
682 /**************************************************************************
683 * GBoxed code
684 **************************************************************************/
685 static PurpleConnectionUiOps *
686 purple_connection_ui_ops_copy(PurpleConnectionUiOps *ops)
688 PurpleConnectionUiOps *ops_new;
690 g_return_val_if_fail(ops != NULL, NULL);
692 ops_new = g_new(PurpleConnectionUiOps, 1);
693 *ops_new = *ops;
695 return ops_new;
698 GType
699 purple_connection_ui_ops_get_type(void)
701 static GType type = 0;
703 if (type == 0) {
704 type = g_boxed_type_register_static("PurpleConnectionUiOps",
705 (GBoxedCopyFunc)purple_connection_ui_ops_copy,
706 (GBoxedFreeFunc)g_free);
709 return type;
712 static PurpleConnectionErrorInfo *
713 purple_connection_error_info_copy(PurpleConnectionErrorInfo *err)
715 g_return_val_if_fail(err != NULL, NULL);
717 return purple_connection_error_info_new(err->type, err->description);
720 static void
721 purple_connection_error_info_free(PurpleConnectionErrorInfo *err)
723 g_return_if_fail(err != NULL);
725 g_free(err->description);
726 g_free(err);
729 GType
730 purple_connection_error_info_get_type(void)
732 static GType type = 0;
734 if (type == 0) {
735 type = g_boxed_type_register_static("PurpleConnectionErrorInfo",
736 (GBoxedCopyFunc)purple_connection_error_info_copy,
737 (GBoxedFreeFunc)purple_connection_error_info_free);
740 return type;
743 /**************************************************************************
744 * GObject code
745 **************************************************************************/
747 /* Set method for GObject properties */
748 static void
749 purple_connection_set_property(GObject *obj, guint param_id, const GValue *value,
750 GParamSpec *pspec)
752 PurpleConnection *gc = PURPLE_CONNECTION(obj);
753 PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
755 switch (param_id) {
756 case PROP_PROTOCOL:
757 priv->protocol = g_value_get_object(value);
758 break;
759 case PROP_FLAGS:
760 purple_connection_set_flags(gc, g_value_get_flags(value));
761 break;
762 case PROP_STATE:
763 purple_connection_set_state(gc, g_value_get_enum(value));
764 break;
765 case PROP_ACCOUNT:
766 priv->account = g_value_get_object(value);
767 break;
768 case PROP_PASSWORD:
769 g_free(priv->password);
770 priv->password = g_value_dup_string(value);
771 break;
772 case PROP_DISPLAY_NAME:
773 purple_connection_set_display_name(gc, g_value_get_string(value));
774 break;
775 default:
776 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
777 break;
781 /* Get method for GObject properties */
782 static void
783 purple_connection_get_property(GObject *obj, guint param_id, GValue *value,
784 GParamSpec *pspec)
786 PurpleConnection *gc = PURPLE_CONNECTION(obj);
788 switch (param_id) {
789 case PROP_PROTOCOL:
790 g_value_set_object(value, purple_connection_get_protocol(gc));
791 break;
792 case PROP_FLAGS:
793 g_value_set_flags(value, purple_connection_get_flags(gc));
794 break;
795 case PROP_STATE:
796 g_value_set_enum(value, purple_connection_get_state(gc));
797 break;
798 case PROP_ACCOUNT:
799 g_value_set_object(value, purple_connection_get_account(gc));
800 break;
801 case PROP_PASSWORD:
802 g_value_set_string(value, purple_connection_get_password(gc));
803 break;
804 case PROP_DISPLAY_NAME:
805 g_value_set_string(value, purple_connection_get_display_name(gc));
806 break;
807 default:
808 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
809 break;
813 /* GObject initialization function */
814 static void
815 purple_connection_init(PurpleConnection *gc)
817 purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTING);
818 connections = g_list_append(connections, gc);
821 /* Called when done constructing */
822 static void
823 purple_connection_constructed(GObject *object)
825 PurpleConnection *gc = PURPLE_CONNECTION(object);
826 PurpleAccount *account;
828 G_OBJECT_CLASS(purple_connection_parent_class)->constructed(object);
830 g_object_get(gc, "account", &account, NULL);
831 purple_account_set_connection(account, gc);
832 g_object_unref(account);
834 purple_signal_emit(purple_connections_get_handle(), "signing-on", gc);
837 /* GObject finalize function */
838 static void
839 purple_connection_finalize(GObject *object)
841 PurpleConnection *gc = PURPLE_CONNECTION(object);
842 PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
843 PurpleAccount *account;
844 GSList *buddies;
845 gboolean remove = FALSE;
847 priv->is_finalizing = TRUE;
849 account = purple_connection_get_account(gc);
851 purple_debug_info("connection", "Disconnecting connection %p\n", gc);
853 if (purple_connection_get_state(gc) != PURPLE_CONNECTION_CONNECTING)
854 remove = TRUE;
856 purple_signal_emit(purple_connections_get_handle(), "signing-off", gc);
858 while (priv->active_chats)
860 PurpleChatConversation *b = priv->active_chats->data;
862 priv->active_chats = g_slist_remove(priv->active_chats, b);
863 purple_chat_conversation_leave(b);
866 update_keepalive(gc, FALSE);
868 purple_protocol_class_close(priv->protocol, gc);
870 /* Clear out the proto data that was freed in the protocol's close method */
871 buddies = purple_blist_find_buddies(account, NULL);
872 while (buddies != NULL) {
873 PurpleBuddy *buddy = buddies->data;
874 purple_buddy_set_protocol_data(buddy, NULL);
875 buddies = g_slist_delete_link(buddies, buddies);
878 purple_http_conn_cancel_all(gc);
879 purple_proxy_connect_cancel_with_handle(gc);
881 connections = g_list_remove(connections, gc);
883 purple_connection_set_state(gc, PURPLE_CONNECTION_DISCONNECTED);
885 if (remove)
886 purple_blist_remove_account(account);
888 purple_signal_emit(purple_connections_get_handle(), "signed-off", gc);
890 purple_account_request_close_with_account(account);
891 purple_request_close_with_handle(gc);
892 purple_notify_close_with_handle(gc);
894 purple_debug_info("connection", "Destroying connection %p\n", gc);
896 purple_account_set_connection(account, NULL);
898 if (priv->error_info)
899 purple_connection_error_info_free(priv->error_info);
901 if (priv->disconnect_timeout > 0)
902 g_source_remove(priv->disconnect_timeout);
904 purple_str_wipe(priv->password);
905 g_free(priv->display_name);
907 G_OBJECT_CLASS(purple_connection_parent_class)->finalize(object);
910 /* Class initializer function */
911 static void purple_connection_class_init(PurpleConnectionClass *klass)
913 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
915 obj_class->finalize = purple_connection_finalize;
916 obj_class->constructed = purple_connection_constructed;
918 /* Setup properties */
919 obj_class->get_property = purple_connection_get_property;
920 obj_class->set_property = purple_connection_set_property;
922 properties[PROP_PROTOCOL] = g_param_spec_object("protocol", "Protocol",
923 "The protocol that the connection is using.",
924 PURPLE_TYPE_PROTOCOL,
925 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
926 G_PARAM_STATIC_STRINGS);
928 properties[PROP_FLAGS] = g_param_spec_flags("flags", "Connection flags",
929 "The flags of the connection.",
930 PURPLE_TYPE_CONNECTION_FLAGS, 0,
931 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
933 properties[PROP_STATE] = g_param_spec_enum("state", "Connection state",
934 "The current state of the connection.",
935 PURPLE_TYPE_CONNECTION_STATE, PURPLE_CONNECTION_DISCONNECTED,
936 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
938 properties[PROP_ACCOUNT] = g_param_spec_object("account", "Account",
939 "The account using the connection.", PURPLE_TYPE_ACCOUNT,
940 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
941 G_PARAM_STATIC_STRINGS);
943 properties[PROP_PASSWORD] = g_param_spec_string("password", "Password",
944 "The password used for connection.", NULL,
945 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
946 G_PARAM_STATIC_STRINGS);
948 properties[PROP_DISPLAY_NAME] = g_param_spec_string("display-name",
949 "Display name",
950 "Your name that appears to other people.", NULL,
951 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
953 g_object_class_install_properties(obj_class, PROP_LAST, properties);
956 void
957 _purple_connection_new(PurpleAccount *account, gboolean regist, const char *password)
959 PurpleConnection *gc;
960 PurpleProtocol *protocol;
962 g_return_if_fail(PURPLE_IS_ACCOUNT(account));
964 if (!purple_account_is_disconnected(account))
965 return;
967 protocol = purple_protocols_find(purple_account_get_protocol_id(account));
969 if (protocol == NULL) {
970 gchar *message;
972 message = g_strdup_printf(_("Missing protocol for %s"),
973 purple_account_get_username(account));
974 purple_notify_error(NULL, regist ? _("Registration Error") :
975 _("Connection Error"), message, NULL,
976 purple_request_cpar_from_account(account));
977 g_free(message);
978 return;
981 if (regist)
983 if (!PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, register_user))
984 return;
986 else
988 if (((password == NULL) || (*password == '\0')) &&
989 !(purple_protocol_get_options(protocol) & OPT_PROTO_NO_PASSWORD) &&
990 !(purple_protocol_get_options(protocol) & OPT_PROTO_PASSWORD_OPTIONAL))
992 purple_debug_error("connection", "Cannot connect to account %s without "
993 "a password.\n", purple_account_get_username(account));
994 return;
998 if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY, connection_new))
999 gc = purple_protocol_factory_iface_connection_new(protocol, account,
1000 password);
1001 else
1002 gc = g_object_new(PURPLE_TYPE_CONNECTION,
1003 "protocol", protocol,
1004 "account", account,
1005 "password", password,
1006 NULL);
1008 g_return_if_fail(gc != NULL);
1010 if (regist)
1012 PurpleConnectionPrivate *priv;
1013 purple_debug_info("connection", "Registering. gc = %p\n", gc);
1015 /* set this so we don't auto-reconnect after registering */
1016 priv = purple_connection_get_instance_private(gc);
1017 priv->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, 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_instance_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;