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"
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
57 /******************************************************************************
59 *****************************************************************************/
60 static GParamSpec
*properties
[PROP_LAST
];
62 G_DEFINE_TYPE_WITH_PRIVATE(PurpleBuddy
, purple_buddy
, PURPLE_TYPE_BLIST_NODE
)
64 /******************************************************************************
66 *****************************************************************************/
68 purple_buddy_set_icon(PurpleBuddy
*buddy
, PurpleBuddyIcon
*icon
)
70 PurpleBuddyPrivate
*priv
= NULL
;
72 g_return_if_fail(PURPLE_IS_BUDDY(buddy
));
74 priv
= purple_buddy_get_instance_private(buddy
);
76 if (priv
->icon
!= icon
)
78 purple_buddy_icon_unref(priv
->icon
);
79 priv
->icon
= (icon
!= NULL
? purple_buddy_icon_ref(icon
) : NULL
);
81 g_object_notify_by_pspec(G_OBJECT(buddy
),
82 properties
[PROP_ICON
]);
85 purple_signal_emit(purple_blist_get_handle(), "buddy-icon-changed", buddy
);
87 purple_blist_update_node(purple_blist_get_default(),
88 PURPLE_BLIST_NODE(buddy
));
92 purple_buddy_get_icon(PurpleBuddy
*buddy
)
94 PurpleBuddyPrivate
*priv
= NULL
;
96 g_return_val_if_fail(PURPLE_IS_BUDDY(buddy
), NULL
);
98 priv
= purple_buddy_get_instance_private(buddy
);
103 purple_buddy_get_account(PurpleBuddy
*buddy
)
105 PurpleBuddyPrivate
*priv
= NULL
;
107 g_return_val_if_fail(PURPLE_IS_BUDDY(buddy
), NULL
);
109 priv
= purple_buddy_get_instance_private(buddy
);
110 return priv
->account
;
114 purple_buddy_set_name(PurpleBuddy
*buddy
, const char *name
)
116 PurpleBuddyPrivate
*priv
= NULL
;
118 g_return_if_fail(PURPLE_IS_BUDDY(buddy
));
120 priv
= purple_buddy_get_instance_private(buddy
);
122 purple_blist_update_buddies_cache(buddy
, name
);
125 priv
->name
= purple_utf8_strip_unprintables(name
);
127 g_object_notify_by_pspec(G_OBJECT(buddy
), properties
[PROP_NAME
]);
129 purple_blist_save_node(purple_blist_get_default(),
130 PURPLE_BLIST_NODE(buddy
));
131 purple_blist_update_node(purple_blist_get_default(),
132 PURPLE_BLIST_NODE(buddy
));
136 purple_buddy_get_name(PurpleBuddy
*buddy
)
138 PurpleBuddyPrivate
*priv
= NULL
;
140 g_return_val_if_fail(PURPLE_IS_BUDDY(buddy
), NULL
);
142 priv
= purple_buddy_get_instance_private(buddy
);
147 purple_buddy_get_protocol_data(PurpleBuddy
*buddy
)
149 PurpleBuddyPrivate
*priv
= NULL
;
151 g_return_val_if_fail(PURPLE_IS_BUDDY(buddy
), NULL
);
153 priv
= purple_buddy_get_instance_private(buddy
);
154 return priv
->proto_data
;
158 purple_buddy_set_protocol_data(PurpleBuddy
*buddy
, gpointer data
)
160 PurpleBuddyPrivate
*priv
= NULL
;
162 g_return_if_fail(PURPLE_IS_BUDDY(buddy
));
164 priv
= purple_buddy_get_instance_private(buddy
);
165 priv
->proto_data
= data
;
168 const char *purple_buddy_get_alias_only(PurpleBuddy
*buddy
)
170 PurpleBuddyPrivate
*priv
= NULL
;
172 g_return_val_if_fail(PURPLE_IS_BUDDY(buddy
), NULL
);
174 priv
= purple_buddy_get_instance_private(buddy
);
176 if ((priv
->local_alias
!= NULL
) && (*priv
->local_alias
!= '\0')) {
177 return priv
->local_alias
;
178 } else if ((priv
->server_alias
!= NULL
) &&
179 (*priv
->server_alias
!= '\0')) {
181 return priv
->server_alias
;
187 const char *purple_buddy_get_contact_alias(PurpleBuddy
*buddy
)
189 PurpleBuddyPrivate
*priv
= NULL
;
192 g_return_val_if_fail(PURPLE_IS_BUDDY(buddy
), NULL
);
194 priv
= purple_buddy_get_instance_private(buddy
);
196 /* Search for an alias for the buddy. In order of precedence: */
197 /* The local buddy alias */
198 if (priv
->local_alias
!= NULL
)
199 return priv
->local_alias
;
201 /* The contact alias */
202 c
= purple_buddy_get_contact(buddy
);
203 if ((c
!= NULL
) && (purple_contact_get_alias(c
) != NULL
))
204 return purple_contact_get_alias(c
);
206 /* The server alias */
207 if ((priv
->server_alias
) && (*priv
->server_alias
))
208 return priv
->server_alias
;
210 /* The buddy's user name (i.e. no alias) */
214 const char *purple_buddy_get_alias(PurpleBuddy
*buddy
)
216 PurpleBuddyPrivate
*priv
= NULL
;
218 g_return_val_if_fail(PURPLE_IS_BUDDY(buddy
), NULL
);
220 priv
= purple_buddy_get_instance_private(buddy
);
222 /* Search for an alias for the buddy. In order of precedence: */
223 /* The buddy alias */
224 if (priv
->local_alias
!= NULL
)
225 return priv
->local_alias
;
227 /* The server alias */
228 if ((priv
->server_alias
) && (*priv
->server_alias
))
229 return priv
->server_alias
;
231 /* The buddy's user name (i.e. no alias) */
236 purple_buddy_set_local_alias(PurpleBuddy
*buddy
, const char *alias
)
238 PurpleBuddyPrivate
*priv
= NULL
;
239 PurpleIMConversation
*im
;
241 char *new_alias
= NULL
;
243 g_return_if_fail(PURPLE_IS_BUDDY(buddy
));
245 priv
= purple_buddy_get_instance_private(buddy
);
247 if ((alias
!= NULL
) && (*alias
!= '\0'))
248 new_alias
= purple_utf8_strip_unprintables(alias
);
250 if (purple_strequal(priv
->local_alias
, new_alias
)) {
255 old_alias
= priv
->local_alias
;
257 if ((new_alias
!= NULL
) && (*new_alias
!= '\0'))
258 priv
->local_alias
= new_alias
;
260 priv
->local_alias
= NULL
;
261 g_free(new_alias
); /* could be "\0" */
264 g_object_notify_by_pspec(G_OBJECT(buddy
),
265 properties
[PROP_LOCAL_ALIAS
]);
267 purple_blist_save_node(purple_blist_get_default(),
268 PURPLE_BLIST_NODE(buddy
));
269 purple_blist_update_node(purple_blist_get_default(),
270 PURPLE_BLIST_NODE(buddy
));
272 im
= purple_conversations_find_im_with_account(priv
->name
,
275 purple_conversation_autoset_title(PURPLE_CONVERSATION(im
));
277 purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
282 const char *purple_buddy_get_local_alias(PurpleBuddy
*buddy
)
284 PurpleBuddyPrivate
*priv
= NULL
;
286 g_return_val_if_fail(PURPLE_IS_BUDDY(buddy
), NULL
);
288 priv
= purple_buddy_get_instance_private(buddy
);
289 return priv
->local_alias
;
293 purple_buddy_set_server_alias(PurpleBuddy
*buddy
, const char *alias
)
295 PurpleBuddyPrivate
*priv
= NULL
;
296 PurpleIMConversation
*im
;
298 char *new_alias
= NULL
;
300 g_return_if_fail(PURPLE_IS_BUDDY(buddy
));
302 priv
= purple_buddy_get_instance_private(buddy
);
304 if ((alias
!= NULL
) && (*alias
!= '\0') && g_utf8_validate(alias
, -1, NULL
))
305 new_alias
= purple_utf8_strip_unprintables(alias
);
307 if (purple_strequal(priv
->server_alias
, new_alias
)) {
312 old_alias
= priv
->server_alias
;
314 if ((new_alias
!= NULL
) && (*new_alias
!= '\0'))
315 priv
->server_alias
= new_alias
;
317 priv
->server_alias
= NULL
;
318 g_free(new_alias
); /* could be "\0"; */
321 g_object_notify_by_pspec(G_OBJECT(buddy
),
322 properties
[PROP_SERVER_ALIAS
]);
324 purple_blist_save_node(purple_blist_get_default(),
325 PURPLE_BLIST_NODE(buddy
));
326 purple_blist_update_node(purple_blist_get_default(),
327 PURPLE_BLIST_NODE(buddy
));
329 im
= purple_conversations_find_im_with_account(priv
->name
,
332 purple_conversation_autoset_title(PURPLE_CONVERSATION(im
));
334 purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
339 const char *purple_buddy_get_server_alias(PurpleBuddy
*buddy
)
341 PurpleBuddyPrivate
*priv
= NULL
;
343 g_return_val_if_fail(PURPLE_IS_BUDDY(buddy
), NULL
);
345 priv
= purple_buddy_get_instance_private(buddy
);
347 if ((priv
->server_alias
) && (*priv
->server_alias
))
348 return priv
->server_alias
;
353 PurpleContact
*purple_buddy_get_contact(PurpleBuddy
*buddy
)
355 g_return_val_if_fail(PURPLE_IS_BUDDY(buddy
), NULL
);
357 return PURPLE_CONTACT(PURPLE_BLIST_NODE(buddy
)->parent
);
360 PurplePresence
*purple_buddy_get_presence(PurpleBuddy
*buddy
)
362 PurpleBuddyPrivate
*priv
= NULL
;
364 g_return_val_if_fail(PURPLE_IS_BUDDY(buddy
), NULL
);
366 priv
= purple_buddy_get_instance_private(buddy
);
367 return priv
->presence
;
371 purple_buddy_update_status(PurpleBuddy
*buddy
, PurpleStatus
*old_status
)
373 PurpleBuddyPrivate
*priv
= NULL
;
374 PurpleStatus
*status
;
375 PurpleBlistNode
*cnode
;
376 PurpleContact
*contact
;
377 PurpleCountingNode
*contact_counter
, *group_counter
;
379 g_return_if_fail(PURPLE_IS_BUDDY(buddy
));
381 priv
= purple_buddy_get_instance_private(buddy
);
383 status
= purple_presence_get_active_status(priv
->presence
);
385 purple_debug_info("blistnodetypes", "Updating buddy status for %s (%s)\n",
386 priv
->name
, purple_account_get_protocol_name(priv
->account
));
388 if (purple_status_is_online(status
) &&
389 !purple_status_is_online(old_status
)) {
391 purple_signal_emit(purple_blist_get_handle(), "buddy-signed-on", buddy
);
393 cnode
= PURPLE_BLIST_NODE(buddy
)->parent
;
394 contact
= PURPLE_CONTACT(cnode
);
395 contact_counter
= PURPLE_COUNTING_NODE(contact
);
396 group_counter
= PURPLE_COUNTING_NODE(cnode
->parent
);
398 purple_counting_node_change_online_count(contact_counter
, +1);
399 if (purple_counting_node_get_online_count(contact_counter
) == 1)
400 purple_counting_node_change_online_count(group_counter
, +1);
401 } else if (!purple_status_is_online(status
) &&
402 purple_status_is_online(old_status
)) {
404 purple_blist_node_set_int(PURPLE_BLIST_NODE(buddy
), "last_seen", time(NULL
));
405 purple_signal_emit(purple_blist_get_handle(), "buddy-signed-off", buddy
);
407 cnode
= PURPLE_BLIST_NODE(buddy
)->parent
;
408 contact
= PURPLE_CONTACT(cnode
);
409 contact_counter
= PURPLE_COUNTING_NODE(contact
);
410 group_counter
= PURPLE_COUNTING_NODE(cnode
->parent
);
412 purple_counting_node_change_online_count(contact_counter
, -1);
413 if (purple_counting_node_get_online_count(contact_counter
) == 0)
414 purple_counting_node_change_online_count(group_counter
, -1);
416 purple_signal_emit(purple_blist_get_handle(),
417 "buddy-status-changed", buddy
, old_status
,
422 * This function used to only call the following two functions if one of
423 * the above signals had been triggered, but that's not good, because
424 * if someone's away message changes and they don't go from away to back
425 * to away then no signal is triggered.
427 * It's a safe assumption that SOMETHING called this function. PROBABLY
428 * because something, somewhere changed. Calling the stuff below
429 * certainly won't hurt anything. Unless you're on a K6-2 300.
431 purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy
));
433 purple_blist_update_node(purple_blist_get_default(),
434 PURPLE_BLIST_NODE(buddy
));
437 PurpleMediaCaps
purple_buddy_get_media_caps(PurpleBuddy
*buddy
)
439 PurpleBuddyPrivate
*priv
= NULL
;
441 g_return_val_if_fail(PURPLE_IS_BUDDY(buddy
), 0);
443 priv
= purple_buddy_get_instance_private(buddy
);
444 return priv
->media_caps
;
447 void purple_buddy_set_media_caps(PurpleBuddy
*buddy
, PurpleMediaCaps media_caps
)
449 PurpleBuddyPrivate
*priv
= NULL
;
451 g_return_if_fail(PURPLE_IS_BUDDY(buddy
));
453 priv
= purple_buddy_get_instance_private(buddy
);
454 priv
->media_caps
= media_caps
;
456 g_object_notify_by_pspec(G_OBJECT(buddy
),
457 properties
[PROP_MEDIA_CAPS
]);
460 PurpleGroup
*purple_buddy_get_group(PurpleBuddy
*buddy
)
462 g_return_val_if_fail(PURPLE_IS_BUDDY(buddy
), NULL
);
464 if (PURPLE_BLIST_NODE(buddy
)->parent
== NULL
)
465 return purple_blist_get_default_group();
467 return PURPLE_GROUP(PURPLE_BLIST_NODE(buddy
)->parent
->parent
);
470 /******************************************************************************
472 *****************************************************************************/
474 purple_buddy_set_property(GObject
*obj
, guint param_id
, const GValue
*value
,
477 PurpleBuddy
*buddy
= PURPLE_BUDDY(obj
);
478 PurpleBuddyPrivate
*priv
= purple_buddy_get_instance_private(buddy
);
482 if (priv
->is_constructed
)
483 purple_buddy_set_name(buddy
, g_value_get_string(value
));
486 purple_utf8_strip_unprintables(g_value_get_string(value
));
488 case PROP_LOCAL_ALIAS
:
489 if (priv
->is_constructed
)
490 purple_buddy_set_local_alias(buddy
, g_value_get_string(value
));
493 purple_utf8_strip_unprintables(g_value_get_string(value
));
495 case PROP_SERVER_ALIAS
:
496 purple_buddy_set_server_alias(buddy
, g_value_get_string(value
));
499 purple_buddy_set_icon(buddy
, g_value_get_pointer(value
));
502 priv
->account
= g_value_get_object(value
);
504 case PROP_MEDIA_CAPS
:
505 purple_buddy_set_media_caps(buddy
, g_value_get_enum(value
));
508 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
514 purple_buddy_get_property(GObject
*obj
, guint param_id
, GValue
*value
,
517 PurpleBuddy
*buddy
= PURPLE_BUDDY(obj
);
521 g_value_set_string(value
, purple_buddy_get_name(buddy
));
523 case PROP_LOCAL_ALIAS
:
524 g_value_set_string(value
, purple_buddy_get_local_alias(buddy
));
526 case PROP_SERVER_ALIAS
:
527 g_value_set_string(value
, purple_buddy_get_server_alias(buddy
));
530 g_value_set_pointer(value
, purple_buddy_get_icon(buddy
));
533 g_value_set_object(value
, purple_buddy_get_account(buddy
));
536 g_value_set_object(value
, purple_buddy_get_presence(buddy
));
538 case PROP_MEDIA_CAPS
:
539 g_value_set_enum(value
, purple_buddy_get_media_caps(buddy
));
542 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
548 purple_buddy_init(PurpleBuddy
*buddy
)
553 purple_buddy_constructed(GObject
*object
) {
554 PurpleBuddy
*buddy
= PURPLE_BUDDY(object
);
555 PurpleBuddyPrivate
*priv
= purple_buddy_get_instance_private(buddy
);
557 G_OBJECT_CLASS(purple_buddy_parent_class
)->constructed(object
);
559 priv
->presence
= PURPLE_PRESENCE(purple_buddy_presence_new(buddy
));
560 purple_presence_set_status_active(priv
->presence
, "offline", TRUE
);
562 purple_blist_new_node(purple_blist_get_default(),
563 PURPLE_BLIST_NODE(buddy
));
565 priv
->is_constructed
= TRUE
;
569 purple_buddy_dispose(GObject
*object
) {
570 PurpleBuddyPrivate
*priv
= purple_buddy_get_instance_private(PURPLE_BUDDY(object
));
573 purple_buddy_icon_unref(priv
->icon
);
577 if (priv
->presence
) {
578 g_object_unref(priv
->presence
);
579 priv
->presence
= NULL
;
582 G_OBJECT_CLASS(purple_buddy_parent_class
)->dispose(object
);
586 purple_buddy_finalize(GObject
*object
) {
587 PurpleBuddy
*buddy
= PURPLE_BUDDY(object
);
588 PurpleBuddyPrivate
*priv
= purple_buddy_get_instance_private(buddy
);
589 PurpleProtocol
*protocol
;
592 * Tell the owner protocol that we're about to free the buddy so it
593 * can free proto_data
595 protocol
= purple_protocols_find(purple_account_get_protocol_id(priv
->account
));
597 purple_protocol_client_iface_buddy_free(protocol
, buddy
);
600 g_free(priv
->local_alias
);
601 g_free(priv
->server_alias
);
603 G_OBJECT_CLASS(purple_buddy_parent_class
)->finalize(object
);
606 static void purple_buddy_class_init(PurpleBuddyClass
*klass
) {
607 GObjectClass
*obj_class
= G_OBJECT_CLASS(klass
);
609 obj_class
->dispose
= purple_buddy_dispose
;
610 obj_class
->finalize
= purple_buddy_finalize
;
612 /* Setup properties */
613 obj_class
->get_property
= purple_buddy_get_property
;
614 obj_class
->set_property
= purple_buddy_set_property
;
615 obj_class
->constructed
= purple_buddy_constructed
;
617 properties
[PROP_NAME
] = g_param_spec_string(
620 "The name of the buddy.",
622 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT
| G_PARAM_STATIC_STRINGS
625 properties
[PROP_LOCAL_ALIAS
] = g_param_spec_string(
628 "Local alias of thee buddy.",
630 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT
| G_PARAM_STATIC_STRINGS
633 properties
[PROP_SERVER_ALIAS
] = g_param_spec_string(
636 "Server-side alias of the buddy.",
638 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
641 properties
[PROP_ICON
] = g_param_spec_pointer(
644 "The icon for the buddy.",
645 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
648 properties
[PROP_ACCOUNT
] = g_param_spec_object(
651 "The account for the buddy.",
653 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
| G_PARAM_STATIC_STRINGS
656 properties
[PROP_PRESENCE
] = g_param_spec_object(
659 "The status information for the buddy.",
660 PURPLE_TYPE_PRESENCE
,
661 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
664 properties
[PROP_MEDIA_CAPS
] = g_param_spec_enum(
666 "Media capabilities",
667 "The media capabilities of the buddy.",
668 PURPLE_MEDIA_TYPE_CAPS
, PURPLE_MEDIA_CAPS_NONE
,
669 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
672 g_object_class_install_properties(obj_class
, PROP_LAST
, properties
);
676 purple_buddy_new(PurpleAccount
*account
, const char *name
, const char *alias
)
678 g_return_val_if_fail(PURPLE_IS_ACCOUNT(account
), NULL
);
679 g_return_val_if_fail(name
!= NULL
, NULL
);
681 return g_object_new(PURPLE_TYPE_BUDDY
,
684 "local-alias", alias
,