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
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
24 #include "glibcompat.h"
25 #include "dbus-maybe.h"
28 #define PURPLE_BUDDY_GET_PRIVATE(obj) \
29 (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_BUDDY, PurpleBuddyPrivate))
31 typedef struct _PurpleBuddyPrivate PurpleBuddyPrivate
;
33 struct _PurpleBuddyPrivate
{
34 char *name
; /* The name of the buddy. */
35 char *local_alias
; /* The user-set alias of the buddy */
36 char *server_alias
; /* The server-specified alias of the buddy.
37 (i.e. MSN "Friendly Names") */
38 void *proto_data
; /* This allows the protocol to associate
39 whatever data it wants with a buddy. */
40 PurpleBuddyIcon
*icon
; /* The buddy icon. */
41 PurpleAccount
*account
; /* the account this buddy belongs to */
42 PurplePresence
*presence
; /* Presense information of the buddy */
43 PurpleMediaCaps media_caps
; /* The media capabilities of the buddy. */
45 gboolean is_constructed
; /* Indicates if the buddy has finished
61 /******************************************************************************
63 *****************************************************************************/
64 static PurpleBlistNode
*parent_class
;
65 static GParamSpec
*properties
[PROP_LAST
];
67 /******************************************************************************
69 *****************************************************************************/
71 purple_buddy_set_icon(PurpleBuddy
*buddy
, PurpleBuddyIcon
*icon
)
73 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
74 PurpleBuddyPrivate
*priv
= PURPLE_BUDDY_GET_PRIVATE(buddy
);
76 g_return_if_fail(priv
!= NULL
);
78 if (priv
->icon
!= icon
)
80 purple_buddy_icon_unref(priv
->icon
);
81 priv
->icon
= (icon
!= NULL
? purple_buddy_icon_ref(icon
) : NULL
);
83 g_object_notify_by_pspec(G_OBJECT(buddy
),
84 properties
[PROP_ICON
]);
87 purple_signal_emit(purple_blist_get_handle(), "buddy-icon-changed", buddy
);
89 if (ops
&& ops
->update
)
90 ops
->update(purple_blist_get_buddy_list(), PURPLE_BLIST_NODE(buddy
));
94 purple_buddy_get_icon(const PurpleBuddy
*buddy
)
96 PurpleBuddyPrivate
*priv
= PURPLE_BUDDY_GET_PRIVATE(buddy
);
98 g_return_val_if_fail(priv
!= NULL
, NULL
);
104 purple_buddy_get_account(const PurpleBuddy
*buddy
)
106 PurpleBuddyPrivate
*priv
= PURPLE_BUDDY_GET_PRIVATE(buddy
);
108 g_return_val_if_fail(priv
!= NULL
, NULL
);
110 return priv
->account
;
114 purple_buddy_set_name(PurpleBuddy
*buddy
, const char *name
)
116 PurpleBuddyPrivate
*priv
= PURPLE_BUDDY_GET_PRIVATE(buddy
);
117 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
119 g_return_if_fail(priv
!= NULL
);
121 purple_blist_update_buddies_cache(buddy
, name
);
124 priv
->name
= purple_utf8_strip_unprintables(name
);
126 g_object_notify_by_pspec(G_OBJECT(buddy
), properties
[PROP_NAME
]);
130 ops
->save_node(PURPLE_BLIST_NODE(buddy
));
132 ops
->update(purple_blist_get_buddy_list(), PURPLE_BLIST_NODE(buddy
));
137 purple_buddy_get_name(const PurpleBuddy
*buddy
)
139 PurpleBuddyPrivate
*priv
= PURPLE_BUDDY_GET_PRIVATE(buddy
);
141 g_return_val_if_fail(priv
!= NULL
, NULL
);
147 purple_buddy_get_protocol_data(const PurpleBuddy
*buddy
)
149 PurpleBuddyPrivate
*priv
= PURPLE_BUDDY_GET_PRIVATE(buddy
);
151 g_return_val_if_fail(priv
!= NULL
, NULL
);
153 return priv
->proto_data
;
157 purple_buddy_set_protocol_data(PurpleBuddy
*buddy
, gpointer data
)
159 PurpleBuddyPrivate
*priv
= PURPLE_BUDDY_GET_PRIVATE(buddy
);
161 g_return_if_fail(priv
!= NULL
);
163 priv
->proto_data
= data
;
166 const char *purple_buddy_get_alias_only(PurpleBuddy
*buddy
)
168 PurpleBuddyPrivate
*priv
= PURPLE_BUDDY_GET_PRIVATE(buddy
);
170 g_return_val_if_fail(priv
!= NULL
, NULL
);
172 if ((priv
->local_alias
!= NULL
) && (*priv
->local_alias
!= '\0')) {
173 return priv
->local_alias
;
174 } else if ((priv
->server_alias
!= NULL
) &&
175 (*priv
->server_alias
!= '\0')) {
177 return priv
->server_alias
;
183 const char *purple_buddy_get_contact_alias(PurpleBuddy
*buddy
)
186 PurpleBuddyPrivate
*priv
= PURPLE_BUDDY_GET_PRIVATE(buddy
);
188 g_return_val_if_fail(priv
!= NULL
, NULL
);
190 /* Search for an alias for the buddy. In order of precedence: */
191 /* The local buddy alias */
192 if (priv
->local_alias
!= NULL
)
193 return priv
->local_alias
;
195 /* The contact alias */
196 c
= purple_buddy_get_contact(buddy
);
197 if ((c
!= NULL
) && (purple_contact_get_alias(c
) != NULL
))
198 return purple_contact_get_alias(c
);
200 /* The server alias */
201 if ((priv
->server_alias
) && (*priv
->server_alias
))
202 return priv
->server_alias
;
204 /* The buddy's user name (i.e. no alias) */
208 const char *purple_buddy_get_alias(PurpleBuddy
*buddy
)
210 PurpleBuddyPrivate
*priv
= PURPLE_BUDDY_GET_PRIVATE(buddy
);
212 g_return_val_if_fail(priv
!= NULL
, NULL
);
214 /* Search for an alias for the buddy. In order of precedence: */
215 /* The buddy alias */
216 if (priv
->local_alias
!= NULL
)
217 return priv
->local_alias
;
219 /* The server alias */
220 if ((priv
->server_alias
) && (*priv
->server_alias
))
221 return priv
->server_alias
;
223 /* The buddy's user name (i.e. no alias) */
228 purple_buddy_set_local_alias(PurpleBuddy
*buddy
, const char *alias
)
230 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
231 PurpleIMConversation
*im
;
233 char *new_alias
= NULL
;
234 PurpleBuddyPrivate
*priv
= PURPLE_BUDDY_GET_PRIVATE(buddy
);
236 g_return_if_fail(priv
!= NULL
);
238 if ((alias
!= NULL
) && (*alias
!= '\0'))
239 new_alias
= purple_utf8_strip_unprintables(alias
);
241 if (purple_strequal(priv
->local_alias
, new_alias
)) {
246 old_alias
= priv
->local_alias
;
248 if ((new_alias
!= NULL
) && (*new_alias
!= '\0'))
249 priv
->local_alias
= new_alias
;
251 priv
->local_alias
= NULL
;
252 g_free(new_alias
); /* could be "\0" */
255 g_object_notify_by_pspec(G_OBJECT(buddy
),
256 properties
[PROP_LOCAL_ALIAS
]);
258 if (ops
&& ops
->save_node
)
259 ops
->save_node(PURPLE_BLIST_NODE(buddy
));
261 if (ops
&& ops
->update
)
262 ops
->update(purple_blist_get_buddy_list(), PURPLE_BLIST_NODE(buddy
));
264 im
= purple_conversations_find_im_with_account(priv
->name
,
267 purple_conversation_autoset_title(PURPLE_CONVERSATION(im
));
269 purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
274 const char *purple_buddy_get_local_alias(PurpleBuddy
*buddy
)
276 PurpleBuddyPrivate
*priv
= PURPLE_BUDDY_GET_PRIVATE(buddy
);
278 g_return_val_if_fail(priv
!= NULL
, NULL
);
280 return priv
->local_alias
;
284 purple_buddy_set_server_alias(PurpleBuddy
*buddy
, const char *alias
)
286 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
287 PurpleIMConversation
*im
;
289 char *new_alias
= NULL
;
290 PurpleBuddyPrivate
*priv
= PURPLE_BUDDY_GET_PRIVATE(buddy
);
292 g_return_if_fail(priv
!= NULL
);
294 if ((alias
!= NULL
) && (*alias
!= '\0') && g_utf8_validate(alias
, -1, NULL
))
295 new_alias
= purple_utf8_strip_unprintables(alias
);
297 if (purple_strequal(priv
->server_alias
, new_alias
)) {
302 old_alias
= priv
->server_alias
;
304 if ((new_alias
!= NULL
) && (*new_alias
!= '\0'))
305 priv
->server_alias
= new_alias
;
307 priv
->server_alias
= NULL
;
308 g_free(new_alias
); /* could be "\0"; */
311 g_object_notify_by_pspec(G_OBJECT(buddy
),
312 properties
[PROP_SERVER_ALIAS
]);
316 ops
->save_node(PURPLE_BLIST_NODE(buddy
));
318 ops
->update(purple_blist_get_buddy_list(), PURPLE_BLIST_NODE(buddy
));
321 im
= purple_conversations_find_im_with_account(priv
->name
,
324 purple_conversation_autoset_title(PURPLE_CONVERSATION(im
));
326 purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
331 const char *purple_buddy_get_server_alias(PurpleBuddy
*buddy
)
333 PurpleBuddyPrivate
*priv
= PURPLE_BUDDY_GET_PRIVATE(buddy
);
335 g_return_val_if_fail(priv
!= NULL
, NULL
);
337 if ((priv
->server_alias
) && (*priv
->server_alias
))
338 return priv
->server_alias
;
343 PurpleContact
*purple_buddy_get_contact(PurpleBuddy
*buddy
)
345 g_return_val_if_fail(PURPLE_IS_BUDDY(buddy
), NULL
);
347 return PURPLE_CONTACT(PURPLE_BLIST_NODE(buddy
)->parent
);
350 PurplePresence
*purple_buddy_get_presence(const PurpleBuddy
*buddy
)
352 PurpleBuddyPrivate
*priv
= PURPLE_BUDDY_GET_PRIVATE(buddy
);
354 g_return_val_if_fail(priv
!= NULL
, NULL
);
356 return priv
->presence
;
360 purple_buddy_update_status(PurpleBuddy
*buddy
, PurpleStatus
*old_status
)
362 PurpleStatus
*status
;
363 PurpleBlistNode
*cnode
;
364 PurpleContact
*contact
;
365 PurpleCountingNode
*contact_counter
, *group_counter
;
366 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
367 PurpleBuddyPrivate
*priv
= PURPLE_BUDDY_GET_PRIVATE(buddy
);
369 g_return_if_fail(priv
!= NULL
);
371 status
= purple_presence_get_active_status(priv
->presence
);
373 purple_debug_info("blistnodetypes", "Updating buddy status for %s (%s)\n",
374 priv
->name
, purple_account_get_protocol_name(priv
->account
));
376 if (purple_status_is_online(status
) &&
377 !purple_status_is_online(old_status
)) {
379 purple_signal_emit(purple_blist_get_handle(), "buddy-signed-on", buddy
);
381 cnode
= PURPLE_BLIST_NODE(buddy
)->parent
;
382 contact
= PURPLE_CONTACT(cnode
);
383 contact_counter
= PURPLE_COUNTING_NODE(contact
);
384 group_counter
= PURPLE_COUNTING_NODE(cnode
->parent
);
386 purple_counting_node_change_online_count(contact_counter
, +1);
387 if (purple_counting_node_get_online_count(contact_counter
) == 1)
388 purple_counting_node_change_online_count(group_counter
, +1);
389 } else if (!purple_status_is_online(status
) &&
390 purple_status_is_online(old_status
)) {
392 purple_blist_node_set_int(PURPLE_BLIST_NODE(buddy
), "last_seen", time(NULL
));
393 purple_signal_emit(purple_blist_get_handle(), "buddy-signed-off", buddy
);
395 cnode
= PURPLE_BLIST_NODE(buddy
)->parent
;
396 contact
= PURPLE_CONTACT(cnode
);
397 contact_counter
= PURPLE_COUNTING_NODE(contact
);
398 group_counter
= PURPLE_COUNTING_NODE(cnode
->parent
);
400 purple_counting_node_change_online_count(contact_counter
, -1);
401 if (purple_counting_node_get_online_count(contact_counter
) == 0)
402 purple_counting_node_change_online_count(group_counter
, -1);
404 purple_signal_emit(purple_blist_get_handle(),
405 "buddy-status-changed", buddy
, old_status
,
410 * This function used to only call the following two functions if one of
411 * the above signals had been triggered, but that's not good, because
412 * if someone's away message changes and they don't go from away to back
413 * to away then no signal is triggered.
415 * It's a safe assumption that SOMETHING called this function. PROBABLY
416 * because something, somewhere changed. Calling the stuff below
417 * certainly won't hurt anything. Unless you're on a K6-2 300.
419 purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy
));
421 if (ops
&& ops
->update
)
422 ops
->update(purple_blist_get_buddy_list(), PURPLE_BLIST_NODE(buddy
));
425 PurpleMediaCaps
purple_buddy_get_media_caps(const PurpleBuddy
*buddy
)
427 PurpleBuddyPrivate
*priv
= PURPLE_BUDDY_GET_PRIVATE(buddy
);
429 g_return_val_if_fail(priv
!= NULL
, 0);
431 return priv
->media_caps
;
434 void purple_buddy_set_media_caps(PurpleBuddy
*buddy
, PurpleMediaCaps media_caps
)
436 PurpleBuddyPrivate
*priv
= PURPLE_BUDDY_GET_PRIVATE(buddy
);
438 g_return_if_fail(priv
!= NULL
);
440 priv
->media_caps
= media_caps
;
442 g_object_notify_by_pspec(G_OBJECT(buddy
),
443 properties
[PROP_MEDIA_CAPS
]);
446 PurpleGroup
*purple_buddy_get_group(PurpleBuddy
*buddy
)
448 g_return_val_if_fail(PURPLE_IS_BUDDY(buddy
), NULL
);
450 if (PURPLE_BLIST_NODE(buddy
)->parent
== NULL
)
451 return purple_blist_get_default_group();
453 return PURPLE_GROUP(PURPLE_BLIST_NODE(buddy
)->parent
->parent
);
456 /******************************************************************************
458 *****************************************************************************/
460 purple_buddy_set_property(GObject
*obj
, guint param_id
, const GValue
*value
,
463 PurpleBuddy
*buddy
= PURPLE_BUDDY(obj
);
464 PurpleBuddyPrivate
*priv
= PURPLE_BUDDY_GET_PRIVATE(buddy
);
468 if (priv
->is_constructed
)
469 purple_buddy_set_name(buddy
, g_value_get_string(value
));
472 purple_utf8_strip_unprintables(g_value_get_string(value
));
474 case PROP_LOCAL_ALIAS
:
475 if (priv
->is_constructed
)
476 purple_buddy_set_local_alias(buddy
, g_value_get_string(value
));
479 purple_utf8_strip_unprintables(g_value_get_string(value
));
481 case PROP_SERVER_ALIAS
:
482 purple_buddy_set_server_alias(buddy
, g_value_get_string(value
));
485 purple_buddy_set_icon(buddy
, g_value_get_pointer(value
));
488 priv
->account
= g_value_get_object(value
);
490 case PROP_MEDIA_CAPS
:
491 purple_buddy_set_media_caps(buddy
, g_value_get_enum(value
));
494 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
500 purple_buddy_get_property(GObject
*obj
, guint param_id
, GValue
*value
,
503 PurpleBuddy
*buddy
= PURPLE_BUDDY(obj
);
507 g_value_set_string(value
, purple_buddy_get_name(buddy
));
509 case PROP_LOCAL_ALIAS
:
510 g_value_set_string(value
, purple_buddy_get_local_alias(buddy
));
512 case PROP_SERVER_ALIAS
:
513 g_value_set_string(value
, purple_buddy_get_server_alias(buddy
));
516 g_value_set_pointer(value
, purple_buddy_get_icon(buddy
));
519 g_value_set_object(value
, purple_buddy_get_account(buddy
));
522 g_value_set_object(value
, purple_buddy_get_presence(buddy
));
524 case PROP_MEDIA_CAPS
:
525 g_value_set_enum(value
, purple_buddy_get_media_caps(buddy
));
528 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
534 purple_buddy_init(GTypeInstance
*instance
, gpointer klass
) {
535 PURPLE_DBUS_REGISTER_POINTER(PURPLE_BUDDY(instance
), PurpleBuddy
);
539 purple_buddy_constructed(GObject
*object
) {
540 PurpleBuddy
*buddy
= PURPLE_BUDDY(object
);
541 PurpleBuddyPrivate
*priv
= PURPLE_BUDDY_GET_PRIVATE(buddy
);
542 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
544 G_OBJECT_CLASS(parent_class
)->constructed(object
);
546 priv
->presence
= PURPLE_PRESENCE(purple_buddy_presence_new(buddy
));
547 purple_presence_set_status_active(priv
->presence
, "offline", TRUE
);
549 if (ops
&& ops
->new_node
)
550 ops
->new_node((PurpleBlistNode
*)buddy
);
552 priv
->is_constructed
= TRUE
;
556 purple_buddy_dispose(GObject
*object
) {
557 PurpleBuddyPrivate
*priv
= PURPLE_BUDDY_GET_PRIVATE(object
);
560 purple_buddy_icon_unref(priv
->icon
);
564 if (priv
->presence
) {
565 g_object_unref(priv
->presence
);
566 priv
->presence
= NULL
;
569 G_OBJECT_CLASS(parent_class
)->dispose(object
);
573 purple_buddy_finalize(GObject
*object
) {
574 PurpleBuddy
*buddy
= PURPLE_BUDDY(object
);
575 PurpleBuddyPrivate
*priv
= PURPLE_BUDDY_GET_PRIVATE(buddy
);
576 PurpleProtocol
*protocol
;
579 * Tell the owner protocol that we're about to free the buddy so it
580 * can free proto_data
582 protocol
= purple_protocols_find(purple_account_get_protocol_id(priv
->account
));
584 purple_protocol_client_iface_buddy_free(protocol
, buddy
);
587 g_free(priv
->local_alias
);
588 g_free(priv
->server_alias
);
590 PURPLE_DBUS_UNREGISTER_POINTER(buddy
);
592 G_OBJECT_CLASS(parent_class
)->finalize(object
);
595 static void purple_buddy_class_init(PurpleBuddyClass
*klass
) {
596 GObjectClass
*obj_class
= G_OBJECT_CLASS(klass
);
598 parent_class
= g_type_class_peek_parent(klass
);
600 obj_class
->dispose
= purple_buddy_dispose
;
601 obj_class
->finalize
= purple_buddy_finalize
;
603 /* Setup properties */
604 obj_class
->get_property
= purple_buddy_get_property
;
605 obj_class
->set_property
= purple_buddy_set_property
;
606 obj_class
->constructed
= purple_buddy_constructed
;
608 g_type_class_add_private(klass
, sizeof(PurpleBuddyPrivate
));
610 properties
[PROP_NAME
] = g_param_spec_string(
613 "The name of the buddy.",
615 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT
| G_PARAM_STATIC_STRINGS
618 properties
[PROP_LOCAL_ALIAS
] = g_param_spec_string(
621 "Local alias of thee buddy.",
623 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT
| G_PARAM_STATIC_STRINGS
626 properties
[PROP_SERVER_ALIAS
] = g_param_spec_string(
629 "Server-side alias of the buddy.",
631 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
634 properties
[PROP_ICON
] = g_param_spec_pointer(
637 "The icon for the buddy.",
638 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
641 properties
[PROP_ACCOUNT
] = g_param_spec_object(
644 "The account for the buddy.",
646 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
| G_PARAM_STATIC_STRINGS
649 properties
[PROP_PRESENCE
] = g_param_spec_object(
652 "The status information for the buddy.",
653 PURPLE_TYPE_PRESENCE
,
654 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
657 properties
[PROP_MEDIA_CAPS
] = g_param_spec_enum(
659 "Media capabilities",
660 "The media capabilities of the buddy.",
661 PURPLE_MEDIA_TYPE_CAPS
, PURPLE_MEDIA_CAPS_NONE
,
662 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
665 g_object_class_install_properties(obj_class
, PROP_LAST
, properties
);
669 purple_buddy_get_type(void) {
670 static GType type
= 0;
673 static const GTypeInfo info
= {
674 sizeof(PurpleBuddyClass
),
677 (GClassInitFunc
)purple_buddy_class_init
,
682 (GInstanceInitFunc
)purple_buddy_init
,
686 type
= g_type_register_static(PURPLE_TYPE_BLIST_NODE
,
695 purple_buddy_new(PurpleAccount
*account
, const char *name
, const char *alias
)
697 g_return_val_if_fail(PURPLE_IS_ACCOUNT(account
), NULL
);
698 g_return_val_if_fail(name
!= NULL
, NULL
);
700 return g_object_new(PURPLE_TYPE_BUDDY
,
703 "local-alias", alias
,