Disable installing plugins that aren't built.
[pidgin-git.git] / libpurple / buddy.c
blobe2dd68d88aba3642843add0a22bc035d6e02aec2
1 /*
2 * purple
4 * Purple is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
6 * source distribution.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
23 #include "internal.h"
24 #include "glibcompat.h"
25 #include "util.h"
27 typedef struct _PurpleBuddyPrivate PurpleBuddyPrivate;
29 struct _PurpleBuddyPrivate {
30 char *name; /* The name of the buddy. */
31 char *local_alias; /* The user-set alias of the buddy */
32 char *server_alias; /* The server-specified alias of the buddy.
33 (i.e. MSN "Friendly Names") */
34 void *proto_data; /* This allows the protocol to associate
35 whatever data it wants with a buddy. */
36 PurpleBuddyIcon *icon; /* The buddy icon. */
37 PurpleAccount *account; /* the account this buddy belongs to */
38 PurplePresence *presence; /* Presense information of the buddy */
39 PurpleMediaCaps media_caps; /* The media capabilities of the buddy. */
41 gboolean is_constructed; /* Indicates if the buddy has finished
42 being constructed. */
45 enum {
46 PROP_0,
47 PROP_NAME,
48 PROP_LOCAL_ALIAS,
49 PROP_SERVER_ALIAS,
50 PROP_ICON,
51 PROP_ACCOUNT,
52 PROP_PRESENCE,
53 PROP_MEDIA_CAPS,
54 PROP_LAST
57 /******************************************************************************
58 * Globals
59 *****************************************************************************/
60 static GParamSpec *properties[PROP_LAST];
62 G_DEFINE_TYPE_WITH_PRIVATE(PurpleBuddy, purple_buddy, PURPLE_TYPE_BLIST_NODE)
64 /******************************************************************************
65 * API
66 *****************************************************************************/
67 void
68 purple_buddy_set_icon(PurpleBuddy *buddy, PurpleBuddyIcon *icon)
70 PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
72 g_return_if_fail(priv != NULL);
74 if (priv->icon != icon)
76 purple_buddy_icon_unref(priv->icon);
77 priv->icon = (icon != NULL ? purple_buddy_icon_ref(icon) : NULL);
79 g_object_notify_by_pspec(G_OBJECT(buddy),
80 properties[PROP_ICON]);
83 purple_signal_emit(purple_blist_get_handle(), "buddy-icon-changed", buddy);
85 purple_blist_update_node(purple_blist_get_default(),
86 PURPLE_BLIST_NODE(buddy));
89 PurpleBuddyIcon *
90 purple_buddy_get_icon(PurpleBuddy *buddy)
92 PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
94 g_return_val_if_fail(priv != NULL, NULL);
96 return priv->icon;
99 PurpleAccount *
100 purple_buddy_get_account(PurpleBuddy *buddy)
102 PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
104 g_return_val_if_fail(priv != NULL, NULL);
106 return priv->account;
109 void
110 purple_buddy_set_name(PurpleBuddy *buddy, const char *name)
112 PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
114 g_return_if_fail(priv != NULL);
116 purple_blist_update_buddies_cache(buddy, name);
118 g_free(priv->name);
119 priv->name = purple_utf8_strip_unprintables(name);
121 g_object_notify_by_pspec(G_OBJECT(buddy), properties[PROP_NAME]);
123 purple_blist_save_node(purple_blist_get_default(),
124 PURPLE_BLIST_NODE(buddy));
125 purple_blist_update_node(purple_blist_get_default(),
126 PURPLE_BLIST_NODE(buddy));
129 const char *
130 purple_buddy_get_name(PurpleBuddy *buddy)
132 PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
134 g_return_val_if_fail(priv != NULL, NULL);
136 return priv->name;
139 gpointer
140 purple_buddy_get_protocol_data(PurpleBuddy *buddy)
142 PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
144 g_return_val_if_fail(priv != NULL, NULL);
146 return priv->proto_data;
149 void
150 purple_buddy_set_protocol_data(PurpleBuddy *buddy, gpointer data)
152 PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
154 g_return_if_fail(priv != NULL);
156 priv->proto_data = data;
159 const char *purple_buddy_get_alias_only(PurpleBuddy *buddy)
161 PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
163 g_return_val_if_fail(priv != NULL, NULL);
165 if ((priv->local_alias != NULL) && (*priv->local_alias != '\0')) {
166 return priv->local_alias;
167 } else if ((priv->server_alias != NULL) &&
168 (*priv->server_alias != '\0')) {
170 return priv->server_alias;
173 return NULL;
176 const char *purple_buddy_get_contact_alias(PurpleBuddy *buddy)
178 PurpleContact *c;
179 PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
181 g_return_val_if_fail(priv != NULL, NULL);
183 /* Search for an alias for the buddy. In order of precedence: */
184 /* The local buddy alias */
185 if (priv->local_alias != NULL)
186 return priv->local_alias;
188 /* The contact alias */
189 c = purple_buddy_get_contact(buddy);
190 if ((c != NULL) && (purple_contact_get_alias(c) != NULL))
191 return purple_contact_get_alias(c);
193 /* The server alias */
194 if ((priv->server_alias) && (*priv->server_alias))
195 return priv->server_alias;
197 /* The buddy's user name (i.e. no alias) */
198 return priv->name;
201 const char *purple_buddy_get_alias(PurpleBuddy *buddy)
203 PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
205 g_return_val_if_fail(priv != NULL, NULL);
207 /* Search for an alias for the buddy. In order of precedence: */
208 /* The buddy alias */
209 if (priv->local_alias != NULL)
210 return priv->local_alias;
212 /* The server alias */
213 if ((priv->server_alias) && (*priv->server_alias))
214 return priv->server_alias;
216 /* The buddy's user name (i.e. no alias) */
217 return priv->name;
220 void
221 purple_buddy_set_local_alias(PurpleBuddy *buddy, const char *alias)
223 PurpleIMConversation *im;
224 char *old_alias;
225 char *new_alias = NULL;
226 PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
228 g_return_if_fail(priv != NULL);
230 if ((alias != NULL) && (*alias != '\0'))
231 new_alias = purple_utf8_strip_unprintables(alias);
233 if (purple_strequal(priv->local_alias, new_alias)) {
234 g_free(new_alias);
235 return;
238 old_alias = priv->local_alias;
240 if ((new_alias != NULL) && (*new_alias != '\0'))
241 priv->local_alias = new_alias;
242 else {
243 priv->local_alias = NULL;
244 g_free(new_alias); /* could be "\0" */
247 g_object_notify_by_pspec(G_OBJECT(buddy),
248 properties[PROP_LOCAL_ALIAS]);
250 purple_blist_save_node(purple_blist_get_default(),
251 PURPLE_BLIST_NODE(buddy));
252 purple_blist_update_node(purple_blist_get_default(),
253 PURPLE_BLIST_NODE(buddy));
255 im = purple_conversations_find_im_with_account(priv->name,
256 priv->account);
257 if (im)
258 purple_conversation_autoset_title(PURPLE_CONVERSATION(im));
260 purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
261 buddy, old_alias);
262 g_free(old_alias);
265 const char *purple_buddy_get_local_alias(PurpleBuddy *buddy)
267 PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
269 g_return_val_if_fail(priv != NULL, NULL);
271 return priv->local_alias;
274 void
275 purple_buddy_set_server_alias(PurpleBuddy *buddy, const char *alias)
277 PurpleIMConversation *im;
278 char *old_alias;
279 char *new_alias = NULL;
280 PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
282 g_return_if_fail(priv != NULL);
284 if ((alias != NULL) && (*alias != '\0') && g_utf8_validate(alias, -1, NULL))
285 new_alias = purple_utf8_strip_unprintables(alias);
287 if (purple_strequal(priv->server_alias, new_alias)) {
288 g_free(new_alias);
289 return;
292 old_alias = priv->server_alias;
294 if ((new_alias != NULL) && (*new_alias != '\0'))
295 priv->server_alias = new_alias;
296 else {
297 priv->server_alias = NULL;
298 g_free(new_alias); /* could be "\0"; */
301 g_object_notify_by_pspec(G_OBJECT(buddy),
302 properties[PROP_SERVER_ALIAS]);
304 purple_blist_save_node(purple_blist_get_default(),
305 PURPLE_BLIST_NODE(buddy));
306 purple_blist_update_node(purple_blist_get_default(),
307 PURPLE_BLIST_NODE(buddy));
309 im = purple_conversations_find_im_with_account(priv->name,
310 priv->account);
311 if (im)
312 purple_conversation_autoset_title(PURPLE_CONVERSATION(im));
314 purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
315 buddy, old_alias);
316 g_free(old_alias);
319 const char *purple_buddy_get_server_alias(PurpleBuddy *buddy)
321 PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
323 g_return_val_if_fail(priv != NULL, NULL);
325 if ((priv->server_alias) && (*priv->server_alias))
326 return priv->server_alias;
328 return NULL;
331 PurpleContact *purple_buddy_get_contact(PurpleBuddy *buddy)
333 g_return_val_if_fail(PURPLE_IS_BUDDY(buddy), NULL);
335 return PURPLE_CONTACT(PURPLE_BLIST_NODE(buddy)->parent);
338 PurplePresence *purple_buddy_get_presence(PurpleBuddy *buddy)
340 PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
342 g_return_val_if_fail(priv != NULL, NULL);
344 return priv->presence;
347 void
348 purple_buddy_update_status(PurpleBuddy *buddy, PurpleStatus *old_status)
350 PurpleStatus *status;
351 PurpleBlistNode *cnode;
352 PurpleContact *contact;
353 PurpleCountingNode *contact_counter, *group_counter;
354 PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
356 g_return_if_fail(priv != NULL);
358 status = purple_presence_get_active_status(priv->presence);
360 purple_debug_info("blistnodetypes", "Updating buddy status for %s (%s)\n",
361 priv->name, purple_account_get_protocol_name(priv->account));
363 if (purple_status_is_online(status) &&
364 !purple_status_is_online(old_status)) {
366 purple_signal_emit(purple_blist_get_handle(), "buddy-signed-on", buddy);
368 cnode = PURPLE_BLIST_NODE(buddy)->parent;
369 contact = PURPLE_CONTACT(cnode);
370 contact_counter = PURPLE_COUNTING_NODE(contact);
371 group_counter = PURPLE_COUNTING_NODE(cnode->parent);
373 purple_counting_node_change_online_count(contact_counter, +1);
374 if (purple_counting_node_get_online_count(contact_counter) == 1)
375 purple_counting_node_change_online_count(group_counter, +1);
376 } else if (!purple_status_is_online(status) &&
377 purple_status_is_online(old_status)) {
379 purple_blist_node_set_int(PURPLE_BLIST_NODE(buddy), "last_seen", time(NULL));
380 purple_signal_emit(purple_blist_get_handle(), "buddy-signed-off", buddy);
382 cnode = PURPLE_BLIST_NODE(buddy)->parent;
383 contact = PURPLE_CONTACT(cnode);
384 contact_counter = PURPLE_COUNTING_NODE(contact);
385 group_counter = PURPLE_COUNTING_NODE(cnode->parent);
387 purple_counting_node_change_online_count(contact_counter, -1);
388 if (purple_counting_node_get_online_count(contact_counter) == 0)
389 purple_counting_node_change_online_count(group_counter, -1);
390 } else {
391 purple_signal_emit(purple_blist_get_handle(),
392 "buddy-status-changed", buddy, old_status,
393 status);
397 * This function used to only call the following two functions if one of
398 * the above signals had been triggered, but that's not good, because
399 * if someone's away message changes and they don't go from away to back
400 * to away then no signal is triggered.
402 * It's a safe assumption that SOMETHING called this function. PROBABLY
403 * because something, somewhere changed. Calling the stuff below
404 * certainly won't hurt anything. Unless you're on a K6-2 300.
406 purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy));
408 purple_blist_update_node(purple_blist_get_default(),
409 PURPLE_BLIST_NODE(buddy));
412 PurpleMediaCaps purple_buddy_get_media_caps(PurpleBuddy *buddy)
414 PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
416 g_return_val_if_fail(priv != NULL, 0);
418 return priv->media_caps;
421 void purple_buddy_set_media_caps(PurpleBuddy *buddy, PurpleMediaCaps media_caps)
423 PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
425 g_return_if_fail(priv != NULL);
427 priv->media_caps = media_caps;
429 g_object_notify_by_pspec(G_OBJECT(buddy),
430 properties[PROP_MEDIA_CAPS]);
433 PurpleGroup *purple_buddy_get_group(PurpleBuddy *buddy)
435 g_return_val_if_fail(PURPLE_IS_BUDDY(buddy), NULL);
437 if (PURPLE_BLIST_NODE(buddy)->parent == NULL)
438 return purple_blist_get_default_group();
440 return PURPLE_GROUP(PURPLE_BLIST_NODE(buddy)->parent->parent);
443 /******************************************************************************
444 * GObject Stuff
445 *****************************************************************************/
446 static void
447 purple_buddy_set_property(GObject *obj, guint param_id, const GValue *value,
448 GParamSpec *pspec)
450 PurpleBuddy *buddy = PURPLE_BUDDY(obj);
451 PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
453 switch (param_id) {
454 case PROP_NAME:
455 if (priv->is_constructed)
456 purple_buddy_set_name(buddy, g_value_get_string(value));
457 else
458 priv->name =
459 purple_utf8_strip_unprintables(g_value_get_string(value));
460 break;
461 case PROP_LOCAL_ALIAS:
462 if (priv->is_constructed)
463 purple_buddy_set_local_alias(buddy, g_value_get_string(value));
464 else
465 priv->local_alias =
466 purple_utf8_strip_unprintables(g_value_get_string(value));
467 break;
468 case PROP_SERVER_ALIAS:
469 purple_buddy_set_server_alias(buddy, g_value_get_string(value));
470 break;
471 case PROP_ICON:
472 purple_buddy_set_icon(buddy, g_value_get_pointer(value));
473 break;
474 case PROP_ACCOUNT:
475 priv->account = g_value_get_object(value);
476 break;
477 case PROP_MEDIA_CAPS:
478 purple_buddy_set_media_caps(buddy, g_value_get_enum(value));
479 break;
480 default:
481 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
482 break;
486 static void
487 purple_buddy_get_property(GObject *obj, guint param_id, GValue *value,
488 GParamSpec *pspec)
490 PurpleBuddy *buddy = PURPLE_BUDDY(obj);
492 switch (param_id) {
493 case PROP_NAME:
494 g_value_set_string(value, purple_buddy_get_name(buddy));
495 break;
496 case PROP_LOCAL_ALIAS:
497 g_value_set_string(value, purple_buddy_get_local_alias(buddy));
498 break;
499 case PROP_SERVER_ALIAS:
500 g_value_set_string(value, purple_buddy_get_server_alias(buddy));
501 break;
502 case PROP_ICON:
503 g_value_set_pointer(value, purple_buddy_get_icon(buddy));
504 break;
505 case PROP_ACCOUNT:
506 g_value_set_object(value, purple_buddy_get_account(buddy));
507 break;
508 case PROP_PRESENCE:
509 g_value_set_object(value, purple_buddy_get_presence(buddy));
510 break;
511 case PROP_MEDIA_CAPS:
512 g_value_set_enum(value, purple_buddy_get_media_caps(buddy));
513 break;
514 default:
515 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
516 break;
520 static void
521 purple_buddy_init(PurpleBuddy *buddy)
525 static void
526 purple_buddy_constructed(GObject *object) {
527 PurpleBuddy *buddy = PURPLE_BUDDY(object);
528 PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
530 G_OBJECT_CLASS(purple_buddy_parent_class)->constructed(object);
532 priv->presence = PURPLE_PRESENCE(purple_buddy_presence_new(buddy));
533 purple_presence_set_status_active(priv->presence, "offline", TRUE);
535 purple_blist_new_node(purple_blist_get_default(),
536 PURPLE_BLIST_NODE(buddy));
538 priv->is_constructed = TRUE;
541 static void
542 purple_buddy_dispose(GObject *object) {
543 PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(PURPLE_BUDDY(object));
545 if (priv->icon) {
546 purple_buddy_icon_unref(priv->icon);
547 priv->icon = NULL;
550 if (priv->presence) {
551 g_object_unref(priv->presence);
552 priv->presence = NULL;
555 G_OBJECT_CLASS(purple_buddy_parent_class)->dispose(object);
558 static void
559 purple_buddy_finalize(GObject *object) {
560 PurpleBuddy *buddy = PURPLE_BUDDY(object);
561 PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
562 PurpleProtocol *protocol;
565 * Tell the owner protocol that we're about to free the buddy so it
566 * can free proto_data
568 protocol = purple_protocols_find(purple_account_get_protocol_id(priv->account));
569 if (protocol)
570 purple_protocol_client_iface_buddy_free(protocol, buddy);
572 g_free(priv->name);
573 g_free(priv->local_alias);
574 g_free(priv->server_alias);
576 G_OBJECT_CLASS(purple_buddy_parent_class)->finalize(object);
579 static void purple_buddy_class_init(PurpleBuddyClass *klass) {
580 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
582 obj_class->dispose = purple_buddy_dispose;
583 obj_class->finalize = purple_buddy_finalize;
585 /* Setup properties */
586 obj_class->get_property = purple_buddy_get_property;
587 obj_class->set_property = purple_buddy_set_property;
588 obj_class->constructed = purple_buddy_constructed;
590 properties[PROP_NAME] = g_param_spec_string(
591 "name",
592 "Name",
593 "The name of the buddy.",
594 NULL,
595 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS
598 properties[PROP_LOCAL_ALIAS] = g_param_spec_string(
599 "local-alias",
600 "Local alias",
601 "Local alias of thee buddy.",
602 NULL,
603 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS
606 properties[PROP_SERVER_ALIAS] = g_param_spec_string(
607 "server-alias",
608 "Server alias",
609 "Server-side alias of the buddy.",
610 NULL,
611 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
614 properties[PROP_ICON] = g_param_spec_pointer(
615 "icon",
616 "Buddy icon",
617 "The icon for the buddy.",
618 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
621 properties[PROP_ACCOUNT] = g_param_spec_object(
622 "account",
623 "Account",
624 "The account for the buddy.",
625 PURPLE_TYPE_ACCOUNT,
626 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS
629 properties[PROP_PRESENCE] = g_param_spec_object(
630 "presence",
631 "Presence",
632 "The status information for the buddy.",
633 PURPLE_TYPE_PRESENCE,
634 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS
637 properties[PROP_MEDIA_CAPS] = g_param_spec_enum(
638 "media-caps",
639 "Media capabilities",
640 "The media capabilities of the buddy.",
641 PURPLE_MEDIA_TYPE_CAPS, PURPLE_MEDIA_CAPS_NONE,
642 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
645 g_object_class_install_properties(obj_class, PROP_LAST, properties);
648 PurpleBuddy *
649 purple_buddy_new(PurpleAccount *account, const char *name, const char *alias)
651 g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
652 g_return_val_if_fail(name != NULL, NULL);
654 return g_object_new(PURPLE_TYPE_BUDDY,
655 "account", account,
656 "name", name,
657 "local-alias", alias,
658 NULL);