pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
HTTP: fix use-after-free. Fixes #15905
2014-02-01, Tomasz Wasilczyk
aa0328600eb6
HTTP: fix use-after-free. Fixes #15905
/*
* purple
*
* Purple is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
*/
#include
"internal.h"
#include
"glibcompat.h"
#include
"dbus-maybe.h"
#include
"debug.h"
#define PURPLE_BUDDY_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_BUDDY, PurpleBuddyPrivate))
/** @copydoc _PurpleBuddyPrivate */
typedef
struct
_PurpleBuddyPrivate
PurpleBuddyPrivate
;
#define PURPLE_CONTACT_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_CONTACT, PurpleContactPrivate))
/** @copydoc _PurpleContactPrivate */
typedef
struct
_PurpleContactPrivate
PurpleContactPrivate
;
#define PURPLE_GROUP_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_GROUP, PurpleGroupPrivate))
/** @copydoc _PurpleGroupPrivate */
typedef
struct
_PurpleGroupPrivate
PurpleGroupPrivate
;
#define PURPLE_CHAT_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_CHAT, PurpleChatPrivate))
/** @copydoc _PurpleChatPrivate */
typedef
struct
_PurpleChatPrivate
PurpleChatPrivate
;
/**************************************************************************/
/* Private data */
/**************************************************************************/
/** Private data for a buddy. */
struct
_PurpleBuddyPrivate
{
char
*
name
;
/**< The name of the buddy. */
char
*
local_alias
;
/**< The user-set alias of the buddy */
char
*
server_alias
;
/**< The server-specified alias of the buddy.
(i.e. MSN "Friendly Names") */
void
*
proto_data
;
/**< This allows the prpl to associate
whatever data it wants with a buddy. */
PurpleBuddyIcon
*
icon
;
/**< The buddy icon. */
PurpleAccount
*
account
;
/**< the account this buddy belongs to */
PurplePresence
*
presence
;
/**< Presense information of the buddy */
PurpleMediaCaps
media_caps
;
/**< The media capabilities of the buddy. */
gboolean
is_constructed
;
/**< Indicates if the buddy has finished
being constructed. */
};
/* Buddy property enums */
enum
{
BUDDY_PROP_0
,
BUDDY_PROP_NAME
,
BUDDY_PROP_LOCAL_ALIAS
,
BUDDY_PROP_SERVER_ALIAS
,
BUDDY_PROP_ICON
,
BUDDY_PROP_ACCOUNT
,
BUDDY_PROP_PRESENCE
,
BUDDY_PROP_MEDIA_CAPS
,
BUDDY_PROP_LAST
};
/** Private data for a contact */
struct
_PurpleContactPrivate
{
char
*
alias
;
/**< The user-set alias of the contact */
PurpleBuddy
*
priority_buddy
;
/**< The "top" buddy for this contact */
gboolean
priority_valid
;
/**< Is priority valid? */
};
/* Contact property enums */
enum
{
CONTACT_PROP_0
,
CONTACT_PROP_ALIAS
,
CONTACT_PROP_PRIORITY_BUDDY
,
CONTACT_PROP_LAST
};
/** Private data for a group */
struct
_PurpleGroupPrivate
{
char
*
name
;
/**< The name of this group. */
gboolean
is_constructed
;
/**< Indicates if the group has finished being
constructed. */
};
/* Group property enums */
enum
{
GROUP_PROP_0
,
GROUP_PROP_NAME
,
GROUP_PROP_LAST
};
/** Private data for a chat node */
struct
_PurpleChatPrivate
{
char
*
alias
;
/**< The display name of this chat. */
PurpleAccount
*
account
;
/**< The account this chat is attached to */
GHashTable
*
components
;
/**< the stuff the protocol needs to know to
join the chat */
gboolean
is_constructed
;
/**< Indicates if the chat has finished being
constructed. */
};
/* Chat property enums */
enum
{
CHAT_PROP_0
,
CHAT_PROP_ALIAS
,
CHAT_PROP_ACCOUNT
,
CHAT_PROP_COMPONENTS
,
CHAT_PROP_LAST
};
static
PurpleBlistNode
*
blistnode_parent_class
;
static
PurpleCountingNode
*
counting_parent_class
;
static
GParamSpec
*
bd_properties
[
BUDDY_PROP_LAST
];
static
GParamSpec
*
co_properties
[
CONTACT_PROP_LAST
];
static
GParamSpec
*
gr_properties
[
GROUP_PROP_LAST
];
static
GParamSpec
*
ch_properties
[
CHAT_PROP_LAST
];
static
gboolean
purple_strings_are_different
(
const
char
*
one
,
const
char
*
two
)
{
return
!
((
one
&&
two
&&
g_utf8_collate
(
one
,
two
)
==
0
)
||
((
one
==
NULL
||
*
one
==
'\0'
)
&&
(
two
==
NULL
||
*
two
==
'\0'
)));
}
/**************************************************************************/
/* Buddy API */
/**************************************************************************/
void
purple_buddy_set_icon
(
PurpleBuddy
*
buddy
,
PurpleBuddyIcon
*
icon
)
{
PurpleBlistUiOps
*
ops
=
purple_blist_get_ui_ops
();
PurpleBuddyPrivate
*
priv
=
PURPLE_BUDDY_GET_PRIVATE
(
buddy
);
g_return_if_fail
(
priv
!=
NULL
);
if
(
priv
->
icon
!=
icon
)
{
purple_buddy_icon_unref
(
priv
->
icon
);
priv
->
icon
=
(
icon
!=
NULL
?
purple_buddy_icon_ref
(
icon
)
:
NULL
);
g_object_notify_by_pspec
(
G_OBJECT
(
buddy
),
bd_properties
[
BUDDY_PROP_ICON
]);
}
purple_signal_emit
(
purple_blist_get_handle
(),
"buddy-icon-changed"
,
buddy
);
if
(
ops
&&
ops
->
update
)
ops
->
update
(
purple_blist_get_buddy_list
(),
PURPLE_BLIST_NODE
(
buddy
));
}
PurpleBuddyIcon
*
purple_buddy_get_icon
(
const
PurpleBuddy
*
buddy
)
{
PurpleBuddyPrivate
*
priv
=
PURPLE_BUDDY_GET_PRIVATE
(
buddy
);
g_return_val_if_fail
(
priv
!=
NULL
,
NULL
);
return
priv
->
icon
;
}
PurpleAccount
*
purple_buddy_get_account
(
const
PurpleBuddy
*
buddy
)
{
PurpleBuddyPrivate
*
priv
=
PURPLE_BUDDY_GET_PRIVATE
(
buddy
);
g_return_val_if_fail
(
priv
!=
NULL
,
NULL
);
return
priv
->
account
;
}
void
purple_buddy_set_name
(
PurpleBuddy
*
buddy
,
const
char
*
name
)
{
PurpleBuddyPrivate
*
priv
=
PURPLE_BUDDY_GET_PRIVATE
(
buddy
);
PurpleBlistUiOps
*
ops
=
purple_blist_get_ui_ops
();
g_return_if_fail
(
priv
!=
NULL
);
purple_blist_update_buddies_cache
(
buddy
,
name
);
g_free
(
priv
->
name
);
priv
->
name
=
purple_utf8_strip_unprintables
(
name
);
g_object_notify_by_pspec
(
G_OBJECT
(
buddy
),
bd_properties
[
BUDDY_PROP_NAME
]);
if
(
ops
)
{
if
(
ops
->
save_node
)
ops
->
save_node
(
PURPLE_BLIST_NODE
(
buddy
));
if
(
ops
->
update
)
ops
->
update
(
purple_blist_get_buddy_list
(),
PURPLE_BLIST_NODE
(
buddy
));
}
}
const
char
*
purple_buddy_get_name
(
const
PurpleBuddy
*
buddy
)
{
PurpleBuddyPrivate
*
priv
=
PURPLE_BUDDY_GET_PRIVATE
(
buddy
);
g_return_val_if_fail
(
priv
!=
NULL
,
NULL
);
return
priv
->
name
;
}
gpointer
purple_buddy_get_protocol_data
(
const
PurpleBuddy
*
buddy
)
{
PurpleBuddyPrivate
*
priv
=
PURPLE_BUDDY_GET_PRIVATE
(
buddy
);
g_return_val_if_fail
(
priv
!=
NULL
,
NULL
);
return
priv
->
proto_data
;
}
void
purple_buddy_set_protocol_data
(
PurpleBuddy
*
buddy
,
gpointer
data
)
{
PurpleBuddyPrivate
*
priv
=
PURPLE_BUDDY_GET_PRIVATE
(
buddy
);
g_return_if_fail
(
priv
!=
NULL
);
priv
->
proto_data
=
data
;
}
const
char
*
purple_buddy_get_alias_only
(
PurpleBuddy
*
buddy
)
{
PurpleBuddyPrivate
*
priv
=
PURPLE_BUDDY_GET_PRIVATE
(
buddy
);
g_return_val_if_fail
(
priv
!=
NULL
,
NULL
);
if
((
priv
->
local_alias
!=
NULL
)
&&
(
*
priv
->
local_alias
!=
'\0'
))
{
return
priv
->
local_alias
;
}
else
if
((
priv
->
server_alias
!=
NULL
)
&&
(
*
priv
->
server_alias
!=
'\0'
))
{
return
priv
->
server_alias
;
}
return
NULL
;
}
const
char
*
purple_buddy_get_contact_alias
(
PurpleBuddy
*
buddy
)
{
PurpleContact
*
c
;
PurpleBuddyPrivate
*
priv
=
PURPLE_BUDDY_GET_PRIVATE
(
buddy
);
g_return_val_if_fail
(
priv
!=
NULL
,
NULL
);
/* Search for an alias for the buddy. In order of precedence: */
/* The local buddy alias */
if
(
priv
->
local_alias
!=
NULL
)
return
priv
->
local_alias
;
/* The contact alias */
c
=
purple_buddy_get_contact
(
buddy
);
if
((
c
!=
NULL
)
&&
(
purple_contact_get_alias
(
c
)
!=
NULL
))
return
purple_contact_get_alias
(
c
);
/* The server alias */
if
((
priv
->
server_alias
)
&&
(
*
priv
->
server_alias
))
return
priv
->
server_alias
;
/* The buddy's user name (i.e. no alias) */
return
priv
->
name
;
}
const
char
*
purple_buddy_get_alias
(
PurpleBuddy
*
buddy
)
{
PurpleBuddyPrivate
*
priv
=
PURPLE_BUDDY_GET_PRIVATE
(
buddy
);
g_return_val_if_fail
(
priv
!=
NULL
,
NULL
);
/* Search for an alias for the buddy. In order of precedence: */
/* The buddy alias */
if
(
priv
->
local_alias
!=
NULL
)
return
priv
->
local_alias
;
/* The server alias */
if
((
priv
->
server_alias
)
&&
(
*
priv
->
server_alias
))
return
priv
->
server_alias
;
/* The buddy's user name (i.e. no alias) */
return
priv
->
name
;
}
void
purple_buddy_set_local_alias
(
PurpleBuddy
*
buddy
,
const
char
*
alias
)
{
PurpleBlistUiOps
*
ops
=
purple_blist_get_ui_ops
();
PurpleIMConversation
*
im
;
char
*
old_alias
;
char
*
new_alias
=
NULL
;
PurpleBuddyPrivate
*
priv
=
PURPLE_BUDDY_GET_PRIVATE
(
buddy
);
g_return_if_fail
(
priv
!=
NULL
);
if
((
alias
!=
NULL
)
&&
(
*
alias
!=
'\0'
))
new_alias
=
purple_utf8_strip_unprintables
(
alias
);
if
(
!
purple_strings_are_different
(
priv
->
local_alias
,
new_alias
))
{
g_free
(
new_alias
);
return
;
}
old_alias
=
priv
->
local_alias
;
if
((
new_alias
!=
NULL
)
&&
(
*
new_alias
!=
'\0'
))
priv
->
local_alias
=
new_alias
;
else
{
priv
->
local_alias
=
NULL
;
g_free
(
new_alias
);
/* could be "\0" */
}
g_object_notify_by_pspec
(
G_OBJECT
(
buddy
),
bd_properties
[
BUDDY_PROP_LOCAL_ALIAS
]);
if
(
ops
&&
ops
->
save_node
)
ops
->
save_node
(
PURPLE_BLIST_NODE
(
buddy
));
if
(
ops
&&
ops
->
update
)
ops
->
update
(
purple_blist_get_buddy_list
(),
PURPLE_BLIST_NODE
(
buddy
));
im
=
purple_conversations_find_im_with_account
(
priv
->
name
,
priv
->
account
);
if
(
im
)
purple_conversation_autoset_title
(
PURPLE_CONVERSATION
(
im
));
purple_signal_emit
(
purple_blist_get_handle
(),
"blist-node-aliased"
,
buddy
,
old_alias
);
g_free
(
old_alias
);
}
const
char
*
purple_buddy_get_local_alias
(
PurpleBuddy
*
buddy
)
{
PurpleBuddyPrivate
*
priv
=
PURPLE_BUDDY_GET_PRIVATE
(
buddy
);
g_return_val_if_fail
(
priv
!=
NULL
,
NULL
);
return
priv
->
local_alias
;
}
void
purple_buddy_set_server_alias
(
PurpleBuddy
*
buddy
,
const
char
*
alias
)
{
PurpleBlistUiOps
*
ops
=
purple_blist_get_ui_ops
();
PurpleIMConversation
*
im
;
char
*
old_alias
;
char
*
new_alias
=
NULL
;
PurpleBuddyPrivate
*
priv
=
PURPLE_BUDDY_GET_PRIVATE
(
buddy
);
g_return_if_fail
(
priv
!=
NULL
);
if
((
alias
!=
NULL
)
&&
(
*
alias
!=
'\0'
)
&&
g_utf8_validate
(
alias
,
-1
,
NULL
))
new_alias
=
purple_utf8_strip_unprintables
(
alias
);
if
(
!
purple_strings_are_different
(
priv
->
server_alias
,
new_alias
))
{
g_free
(
new_alias
);
return
;
}
old_alias
=
priv
->
server_alias
;
if
((
new_alias
!=
NULL
)
&&
(
*
new_alias
!=
'\0'
))
priv
->
server_alias
=
new_alias
;
else
{
priv
->
server_alias
=
NULL
;
g_free
(
new_alias
);
/* could be "\0"; */
}
g_object_notify_by_pspec
(
G_OBJECT
(
buddy
),
bd_properties
[
BUDDY_PROP_SERVER_ALIAS
]);
if
(
ops
)
{
if
(
ops
->
save_node
)
ops
->
save_node
(
PURPLE_BLIST_NODE
(
buddy
));
if
(
ops
->
update
)
ops
->
update
(
purple_blist_get_buddy_list
(),
PURPLE_BLIST_NODE
(
buddy
));
}
im
=
purple_conversations_find_im_with_account
(
priv
->
name
,
priv
->
account
);
if
(
im
)
purple_conversation_autoset_title
(
PURPLE_CONVERSATION
(
im
));
purple_signal_emit
(
purple_blist_get_handle
(),
"blist-node-aliased"
,
buddy
,
old_alias
);
g_free
(
old_alias
);
}
const
char
*
purple_buddy_get_server_alias
(
PurpleBuddy
*
buddy
)
{
PurpleBuddyPrivate
*
priv
=
PURPLE_BUDDY_GET_PRIVATE
(
buddy
);
g_return_val_if_fail
(
priv
!=
NULL
,
NULL
);
if
((
priv
->
server_alias
)
&&
(
*
priv
->
server_alias
))
return
priv
->
server_alias
;
return
NULL
;
}
PurpleContact
*
purple_buddy_get_contact
(
PurpleBuddy
*
buddy
)
{
g_return_val_if_fail
(
PURPLE_IS_BUDDY
(
buddy
),
NULL
);
return
PURPLE_CONTACT
(
PURPLE_BLIST_NODE
(
buddy
)
->
parent
);
}
PurplePresence
*
purple_buddy_get_presence
(
const
PurpleBuddy
*
buddy
)
{
PurpleBuddyPrivate
*
priv
=
PURPLE_BUDDY_GET_PRIVATE
(
buddy
);
g_return_val_if_fail
(
priv
!=
NULL
,
NULL
);
return
priv
->
presence
;
}
void
purple_buddy_update_status
(
PurpleBuddy
*
buddy
,
PurpleStatus
*
old_status
)
{
PurpleStatus
*
status
;
PurpleBlistNode
*
cnode
;
PurpleContact
*
contact
;
PurpleCountingNode
*
contact_counter
,
*
group_counter
;
PurpleBlistUiOps
*
ops
=
purple_blist_get_ui_ops
();
PurpleBuddyPrivate
*
priv
=
PURPLE_BUDDY_GET_PRIVATE
(
buddy
);
g_return_if_fail
(
priv
!=
NULL
);
status
=
purple_presence_get_active_status
(
priv
->
presence
);
purple_debug_info
(
"blistnodetypes"
,
"Updating buddy status for %s (%s)
\n
"
,
priv
->
name
,
purple_account_get_protocol_name
(
priv
->
account
));
if
(
purple_status_is_online
(
status
)
&&
!
purple_status_is_online
(
old_status
))
{
purple_signal_emit
(
purple_blist_get_handle
(),
"buddy-signed-on"
,
buddy
);
cnode
=
PURPLE_BLIST_NODE
(
buddy
)
->
parent
;
contact
=
PURPLE_CONTACT
(
cnode
);
contact_counter
=
PURPLE_COUNTING_NODE
(
contact
);
group_counter
=
PURPLE_COUNTING_NODE
(
cnode
->
parent
);
purple_counting_node_change_online_count
(
contact_counter
,
+
1
);
if
(
purple_counting_node_get_online_count
(
contact_counter
)
==
1
)
purple_counting_node_change_online_count
(
group_counter
,
+
1
);
}
else
if
(
!
purple_status_is_online
(
status
)
&&
purple_status_is_online
(
old_status
))
{
purple_blist_node_set_int
(
PURPLE_BLIST_NODE
(
buddy
),
"last_seen"
,
time
(
NULL
));
purple_signal_emit
(
purple_blist_get_handle
(),
"buddy-signed-off"
,
buddy
);
cnode
=
PURPLE_BLIST_NODE
(
buddy
)
->
parent
;
contact
=
PURPLE_CONTACT
(
cnode
);
contact_counter
=
PURPLE_COUNTING_NODE
(
contact
);
group_counter
=
PURPLE_COUNTING_NODE
(
cnode
->
parent
);
purple_counting_node_change_online_count
(
contact_counter
,
-1
);
if
(
purple_counting_node_get_online_count
(
contact_counter
)
==
0
)
purple_counting_node_change_online_count
(
group_counter
,
-1
);
}
else
{
purple_signal_emit
(
purple_blist_get_handle
(),
"buddy-status-changed"
,
buddy
,
old_status
,
status
);
}
/*
* This function used to only call the following two functions if one of
* the above signals had been triggered, but that's not good, because
* if someone's away message changes and they don't go from away to back
* to away then no signal is triggered.
*
* It's a safe assumption that SOMETHING called this function. PROBABLY
* because something, somewhere changed. Calling the stuff below
* certainly won't hurt anything. Unless you're on a K6-2 300.
*/
purple_contact_invalidate_priority_buddy
(
purple_buddy_get_contact
(
buddy
));
if
(
ops
&&
ops
->
update
)
ops
->
update
(
purple_blist_get_buddy_list
(),
PURPLE_BLIST_NODE
(
buddy
));
}
PurpleMediaCaps
purple_buddy_get_media_caps
(
const
PurpleBuddy
*
buddy
)
{
PurpleBuddyPrivate
*
priv
=
PURPLE_BUDDY_GET_PRIVATE
(
buddy
);
g_return_val_if_fail
(
priv
!=
NULL
,
0
);
return
priv
->
media_caps
;
}
void
purple_buddy_set_media_caps
(
PurpleBuddy
*
buddy
,
PurpleMediaCaps
media_caps
)
{
PurpleBuddyPrivate
*
priv
=
PURPLE_BUDDY_GET_PRIVATE
(
buddy
);
g_return_if_fail
(
priv
!=
NULL
);
priv
->
media_caps
=
media_caps
;
g_object_notify_by_pspec
(
G_OBJECT
(
buddy
),
bd_properties
[
BUDDY_PROP_MEDIA_CAPS
]);
}
PurpleGroup
*
purple_buddy_get_group
(
PurpleBuddy
*
buddy
)
{
g_return_val_if_fail
(
PURPLE_IS_BUDDY
(
buddy
),
NULL
);
if
(
PURPLE_BLIST_NODE
(
buddy
)
->
parent
==
NULL
)
return
NULL
;
return
PURPLE_GROUP
(
PURPLE_BLIST_NODE
(
buddy
)
->
parent
->
parent
);
}
/**************************************************************************
* GObject code for PurpleBuddy
**************************************************************************/
/* Set method for GObject properties */
static
void
purple_buddy_set_property
(
GObject
*
obj
,
guint
param_id
,
const
GValue
*
value
,
GParamSpec
*
pspec
)
{
PurpleBuddy
*
buddy
=
PURPLE_BUDDY
(
obj
);
PurpleBuddyPrivate
*
priv
=
PURPLE_BUDDY_GET_PRIVATE
(
buddy
);
switch
(
param_id
)
{
case
BUDDY_PROP_NAME
:
if
(
priv
->
is_constructed
)
purple_buddy_set_name
(
buddy
,
g_value_get_string
(
value
));
else
priv
->
name
=
purple_utf8_strip_unprintables
(
g_value_get_string
(
value
));
break
;
case
BUDDY_PROP_LOCAL_ALIAS
:
if
(
priv
->
is_constructed
)
purple_buddy_set_local_alias
(
buddy
,
g_value_get_string
(
value
));
else
priv
->
local_alias
=
purple_utf8_strip_unprintables
(
g_value_get_string
(
value
));
break
;
case
BUDDY_PROP_SERVER_ALIAS
:
purple_buddy_set_server_alias
(
buddy
,
g_value_get_string
(
value
));
break
;
case
BUDDY_PROP_ICON
:
purple_buddy_set_icon
(
buddy
,
g_value_get_pointer
(
value
));
break
;
case
BUDDY_PROP_ACCOUNT
:
priv
->
account
=
g_value_get_object
(
value
);
break
;
case
BUDDY_PROP_MEDIA_CAPS
:
purple_buddy_set_media_caps
(
buddy
,
g_value_get_enum
(
value
));
break
;
default
:
G_OBJECT_WARN_INVALID_PROPERTY_ID
(
obj
,
param_id
,
pspec
);
break
;
}
}
/* Get method for GObject properties */
static
void
purple_buddy_get_property
(
GObject
*
obj
,
guint
param_id
,
GValue
*
value
,
GParamSpec
*
pspec
)
{
PurpleBuddy
*
buddy
=
PURPLE_BUDDY
(
obj
);
switch
(
param_id
)
{
case
BUDDY_PROP_NAME
:
g_value_set_string
(
value
,
purple_buddy_get_name
(
buddy
));
break
;
case
BUDDY_PROP_LOCAL_ALIAS
:
g_value_set_string
(
value
,
purple_buddy_get_local_alias
(
buddy
));
break
;
case
BUDDY_PROP_SERVER_ALIAS
:
g_value_set_string
(
value
,
purple_buddy_get_server_alias
(
buddy
));
break
;
case
BUDDY_PROP_ICON
:
g_value_set_pointer
(
value
,
purple_buddy_get_icon
(
buddy
));
break
;
case
BUDDY_PROP_ACCOUNT
:
g_value_set_object
(
value
,
purple_buddy_get_account
(
buddy
));
break
;
case
BUDDY_PROP_PRESENCE
:
g_value_set_object
(
value
,
purple_buddy_get_presence
(
buddy
));
break
;
case
BUDDY_PROP_MEDIA_CAPS
:
g_value_set_enum
(
value
,
purple_buddy_get_media_caps
(
buddy
));
break
;
default
:
G_OBJECT_WARN_INVALID_PROPERTY_ID
(
obj
,
param_id
,
pspec
);
break
;
}
}
/* GObject initialization function */
static
void
purple_buddy_init
(
GTypeInstance
*
instance
,
gpointer
klass
)
{
PURPLE_DBUS_REGISTER_POINTER
(
PURPLE_BUDDY
(
instance
),
PurpleBuddy
);
}
/* Called when done constructing */
static
void
purple_buddy_constructed
(
GObject
*
object
)
{
PurpleBuddy
*
buddy
=
PURPLE_BUDDY
(
object
);
PurpleBuddyPrivate
*
priv
=
PURPLE_BUDDY_GET_PRIVATE
(
buddy
);
PurpleBlistUiOps
*
ops
=
purple_blist_get_ui_ops
();
G_OBJECT_CLASS
(
blistnode_parent_class
)
->
constructed
(
object
);
priv
->
presence
=
PURPLE_PRESENCE
(
purple_buddy_presence_new
(
buddy
));
purple_presence_set_status_active
(
priv
->
presence
,
"offline"
,
TRUE
);
if
(
ops
&&
ops
->
new_node
)
ops
->
new_node
((
PurpleBlistNode
*
)
buddy
);
priv
->
is_constructed
=
TRUE
;
}
/* GObject dispose function */
static
void
purple_buddy_dispose
(
GObject
*
object
)
{
PurpleBuddyPrivate
*
priv
=
PURPLE_BUDDY_GET_PRIVATE
(
object
);
if
(
priv
->
icon
)
{
purple_buddy_icon_unref
(
priv
->
icon
);
priv
->
icon
=
NULL
;
}
if
(
priv
->
presence
)
{
g_object_unref
(
priv
->
presence
);
priv
->
presence
=
NULL
;
}
G_OBJECT_CLASS
(
blistnode_parent_class
)
->
dispose
(
object
);
}
/* GObject finalize function */
static
void
purple_buddy_finalize
(
GObject
*
object
)
{
PurpleBuddy
*
buddy
=
PURPLE_BUDDY
(
object
);
PurpleBuddyPrivate
*
priv
=
PURPLE_BUDDY_GET_PRIVATE
(
buddy
);
PurplePlugin
*
prpl
;
PurplePluginProtocolInfo
*
prpl_info
;
/*
* Tell the owner PRPL that we're about to free the buddy so it
* can free proto_data
*/
prpl
=
purple_find_prpl
(
purple_account_get_protocol_id
(
priv
->
account
));
if
(
prpl
)
{
prpl_info
=
PURPLE_PLUGIN_PROTOCOL_INFO
(
prpl
);
if
(
prpl_info
&&
prpl_info
->
buddy_free
)
prpl_info
->
buddy_free
(
buddy
);
}
g_free
(
priv
->
name
);
g_free
(
priv
->
local_alias
);
g_free
(
priv
->
server_alias
);
PURPLE_DBUS_UNREGISTER_POINTER
(
buddy
);
G_OBJECT_CLASS
(
blistnode_parent_class
)
->
finalize
(
object
);
}
/* Class initializer function */
static
void
purple_buddy_class_init
(
PurpleBuddyClass
*
klass
)
{
GObjectClass
*
obj_class
=
G_OBJECT_CLASS
(
klass
);
blistnode_parent_class
=
g_type_class_peek_parent
(
klass
);
obj_class
->
dispose
=
purple_buddy_dispose
;
obj_class
->
finalize
=
purple_buddy_finalize
;
/* Setup properties */
obj_class
->
get_property
=
purple_buddy_get_property
;
obj_class
->
set_property
=
purple_buddy_set_property
;
obj_class
->
constructed
=
purple_buddy_constructed
;
g_type_class_add_private
(
klass
,
sizeof
(
PurpleBuddyPrivate
));
bd_properties
[
BUDDY_PROP_NAME
]
=
g_param_spec_string
(
"name"
,
"Name"
,
"The name of the buddy."
,
NULL
,
G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT
|
G_PARAM_STATIC_STRINGS
);
bd_properties
[
BUDDY_PROP_LOCAL_ALIAS
]
=
g_param_spec_string
(
"local-alias"
,
"Local alias"
,
"Local alias of thee buddy."
,
NULL
,
G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT
|
G_PARAM_STATIC_STRINGS
);
bd_properties
[
BUDDY_PROP_SERVER_ALIAS
]
=
g_param_spec_string
(
"server-alias"
,
"Server alias"
,
"Server-side alias of the buddy."
,
NULL
,
G_PARAM_READWRITE
|
G_PARAM_STATIC_STRINGS
);
bd_properties
[
BUDDY_PROP_ICON
]
=
g_param_spec_pointer
(
"icon"
,
"Buddy icon"
,
"The icon for the buddy."
,
G_PARAM_READWRITE
|
G_PARAM_STATIC_STRINGS
);
bd_properties
[
BUDDY_PROP_ACCOUNT
]
=
g_param_spec_object
(
"account"
,
"Account"
,
"The account for the buddy."
,
PURPLE_TYPE_ACCOUNT
,
G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT_ONLY
|
G_PARAM_STATIC_STRINGS
);
bd_properties
[
BUDDY_PROP_PRESENCE
]
=
g_param_spec_object
(
"presence"
,
"Presence"
,
"The status information for the buddy."
,
PURPLE_TYPE_PRESENCE
,
G_PARAM_READABLE
|
G_PARAM_STATIC_STRINGS
);
bd_properties
[
BUDDY_PROP_MEDIA_CAPS
]
=
g_param_spec_enum
(
"media-caps"
,
"Media capabilities"
,
"The media capabilities of the buddy."
,
PURPLE_MEDIA_TYPE_CAPS
,
PURPLE_MEDIA_CAPS_NONE
,
G_PARAM_READWRITE
|
G_PARAM_STATIC_STRINGS
);
g_object_class_install_properties
(
obj_class
,
BUDDY_PROP_LAST
,
bd_properties
);
}
GType
purple_buddy_get_type
(
void
)
{
static
GType
type
=
0
;
if
(
type
==
0
)
{
static
const
GTypeInfo
info
=
{
sizeof
(
PurpleBuddyClass
),
NULL
,
NULL
,
(
GClassInitFunc
)
purple_buddy_class_init
,
NULL
,
NULL
,
sizeof
(
PurpleBuddy
),
0
,
(
GInstanceInitFunc
)
purple_buddy_init
,
NULL
,
};
type
=
g_type_register_static
(
PURPLE_TYPE_BLIST_NODE
,
"PurpleBuddy"
,
&
info
,
0
);
}
return
type
;
}
PurpleBuddy
*
purple_buddy_new
(
PurpleAccount
*
account
,
const
char
*
name
,
const
char
*
alias
)
{
g_return_val_if_fail
(
PURPLE_IS_ACCOUNT
(
account
),
NULL
);
g_return_val_if_fail
(
name
!=
NULL
,
NULL
);
return
g_object_new
(
PURPLE_TYPE_BUDDY
,
"account"
,
account
,
"name"
,
name
,
"local-alias"
,
alias
,
NULL
);
}
/**************************************************************************/
/* Contact API */
/**************************************************************************/
static
void
purple_contact_compute_priority_buddy
(
PurpleContact
*
contact
)
{
PurpleBlistNode
*
bnode
;
PurpleBuddy
*
new_priority
=
NULL
;
PurpleContactPrivate
*
priv
=
PURPLE_CONTACT_GET_PRIVATE
(
contact
);
g_return_if_fail
(
priv
!=
NULL
);
priv
->
priority_buddy
=
NULL
;
for
(
bnode
=
PURPLE_BLIST_NODE
(
contact
)
->
child
;
bnode
!=
NULL
;
bnode
=
bnode
->
next
)
{
PurpleBuddy
*
buddy
;
if
(
!
PURPLE_IS_BUDDY
(
bnode
))
continue
;
buddy
=
PURPLE_BUDDY
(
bnode
);
if
(
new_priority
==
NULL
)
{
new_priority
=
buddy
;
continue
;
}
if
(
purple_account_is_connected
(
purple_buddy_get_account
(
buddy
)))
{
int
cmp
=
1
;
if
(
purple_account_is_connected
(
purple_buddy_get_account
(
new_priority
)))
cmp
=
purple_buddy_presence_compare
(
PURPLE_BUDDY_PRESENCE
(
purple_buddy_get_presence
(
new_priority
)),
PURPLE_BUDDY_PRESENCE
(
purple_buddy_get_presence
(
buddy
)));
if
(
cmp
>
0
||
(
cmp
==
0
&&
purple_prefs_get_bool
(
"/purple/contact/last_match"
)))
{
new_priority
=
buddy
;
}
}
}
priv
->
priority_buddy
=
new_priority
;
priv
->
priority_valid
=
TRUE
;
g_object_notify_by_pspec
(
G_OBJECT
(
contact
),
co_properties
[
CONTACT_PROP_PRIORITY_BUDDY
]);
}
PurpleGroup
*
purple_contact_get_group
(
const
PurpleContact
*
contact
)
{
g_return_val_if_fail
(
PURPLE_IS_CONTACT
(
contact
),
NULL
);
return
PURPLE_GROUP
(
PURPLE_BLIST_NODE
(
contact
)
->
parent
);
}
void
purple_contact_set_alias
(
PurpleContact
*
contact
,
const
char
*
alias
)
{
PurpleBlistUiOps
*
ops
=
purple_blist_get_ui_ops
();
PurpleIMConversation
*
im
;
PurpleBlistNode
*
bnode
;
char
*
old_alias
;
char
*
new_alias
=
NULL
;
PurpleContactPrivate
*
priv
=
PURPLE_CONTACT_GET_PRIVATE
(
contact
);
g_return_if_fail
(
priv
!=
NULL
);
if
((
alias
!=
NULL
)
&&
(
*
alias
!=
'\0'
))
new_alias
=
purple_utf8_strip_unprintables
(
alias
);
if
(
!
purple_strings_are_different
(
priv
->
alias
,
new_alias
))
{
g_free
(
new_alias
);
return
;
}
old_alias
=
priv
->
alias
;
if
((
new_alias
!=
NULL
)
&&
(
*
new_alias
!=
'\0'
))
priv
->
alias
=
new_alias
;
else
{
priv
->
alias
=
NULL
;
g_free
(
new_alias
);
/* could be "\0" */
}
g_object_notify_by_pspec
(
G_OBJECT
(
contact
),
co_properties
[
CONTACT_PROP_ALIAS
]);
if
(
ops
)
{
if
(
ops
->
save_node
)
ops
->
save_node
(
PURPLE_BLIST_NODE
(
contact
));
if
(
ops
->
update
)
ops
->
update
(
purple_blist_get_buddy_list
(),
PURPLE_BLIST_NODE
(
contact
));
}
for
(
bnode
=
PURPLE_BLIST_NODE
(
contact
)
->
child
;
bnode
!=
NULL
;
bnode
=
bnode
->
next
)
{
PurpleBuddy
*
buddy
=
PURPLE_BUDDY
(
bnode
);
im
=
purple_conversations_find_im_with_account
(
purple_buddy_get_name
(
buddy
),
purple_buddy_get_account
(
buddy
));
if
(
im
)
purple_conversation_autoset_title
(
PURPLE_CONVERSATION
(
im
));
}
purple_signal_emit
(
purple_blist_get_handle
(),
"blist-node-aliased"
,
contact
,
old_alias
);
g_free
(
old_alias
);
}
const
char
*
purple_contact_get_alias
(
PurpleContact
*
contact
)
{
PurpleContactPrivate
*
priv
=
PURPLE_CONTACT_GET_PRIVATE
(
contact
);
g_return_val_if_fail
(
priv
!=
NULL
,
NULL
);
if
(
priv
->
alias
)
return
priv
->
alias
;
return
purple_buddy_get_alias
(
purple_contact_get_priority_buddy
(
contact
));
}
gboolean
purple_contact_on_account
(
PurpleContact
*
c
,
PurpleAccount
*
account
)
{
PurpleBlistNode
*
bnode
,
*
cnode
=
(
PurpleBlistNode
*
)
c
;
g_return_val_if_fail
(
PURPLE_IS_CONTACT
(
c
),
FALSE
);
g_return_val_if_fail
(
PURPLE_IS_ACCOUNT
(
account
),
FALSE
);
for
(
bnode
=
cnode
->
child
;
bnode
;
bnode
=
bnode
->
next
)
{
PurpleBuddy
*
buddy
;
if
(
!
PURPLE_IS_BUDDY
(
bnode
))
continue
;
buddy
=
(
PurpleBuddy
*
)
bnode
;
if
(
purple_buddy_get_account
(
buddy
)
==
account
)
return
TRUE
;
}
return
FALSE
;
}
void
purple_contact_invalidate_priority_buddy
(
PurpleContact
*
contact
)
{
PurpleContactPrivate
*
priv
=
PURPLE_CONTACT_GET_PRIVATE
(
contact
);
g_return_if_fail
(
priv
!=
NULL
);
priv
->
priority_valid
=
FALSE
;
}
PurpleBuddy
*
purple_contact_get_priority_buddy
(
PurpleContact
*
contact
)
{
PurpleContactPrivate
*
priv
=
PURPLE_CONTACT_GET_PRIVATE
(
contact
);
g_return_val_if_fail
(
priv
!=
NULL
,
NULL
);
if
(
!
priv
->
priority_valid
)
purple_contact_compute_priority_buddy
(
contact
);
return
priv
->
priority_buddy
;
}
void
purple_contact_merge
(
PurpleContact
*
source
,
PurpleBlistNode
*
node
)
{
PurpleBlistNode
*
sourcenode
=
(
PurpleBlistNode
*
)
source
;
PurpleBlistNode
*
prev
,
*
cur
,
*
next
;
PurpleContact
*
target
;
g_return_if_fail
(
PURPLE_IS_CONTACT
(
source
));
g_return_if_fail
(
PURPLE_IS_BLIST_NODE
(
node
));
if
(
PURPLE_IS_CONTACT
(
node
))
{
target
=
(
PurpleContact
*
)
node
;
prev
=
_purple_blist_get_last_child
(
node
);
}
else
if
(
PURPLE_IS_BUDDY
(
node
))
{
target
=
(
PurpleContact
*
)
node
->
parent
;
prev
=
node
;
}
else
{
return
;
}
if
(
source
==
target
||
!
target
)
return
;
next
=
sourcenode
->
child
;
while
(
next
)
{
cur
=
next
;
next
=
cur
->
next
;
if
(
PURPLE_IS_BUDDY
(
cur
))
{
purple_blist_add_buddy
((
PurpleBuddy
*
)
cur
,
target
,
NULL
,
prev
);
prev
=
cur
;
}
}
}
/**************************************************************************
* GObject code for PurpleContact
**************************************************************************/
/* Set method for GObject properties */
static
void
purple_contact_set_property
(
GObject
*
obj
,
guint
param_id
,
const
GValue
*
value
,
GParamSpec
*
pspec
)
{
PurpleContact
*
contact
=
PURPLE_CONTACT
(
obj
);
switch
(
param_id
)
{
case
CONTACT_PROP_ALIAS
:
purple_contact_set_alias
(
contact
,
g_value_get_string
(
value
));
break
;
default
:
G_OBJECT_WARN_INVALID_PROPERTY_ID
(
obj
,
param_id
,
pspec
);
break
;
}
}
/* Get method for GObject properties */
static
void
purple_contact_get_property
(
GObject
*
obj
,
guint
param_id
,
GValue
*
value
,
GParamSpec
*
pspec
)
{
PurpleContact
*
contact
=
PURPLE_CONTACT
(
obj
);
PurpleContactPrivate
*
priv
=
PURPLE_CONTACT_GET_PRIVATE
(
contact
);
switch
(
param_id
)
{
case
CONTACT_PROP_ALIAS
:
g_value_set_string
(
value
,
priv
->
alias
);
break
;
case
CONTACT_PROP_PRIORITY_BUDDY
:
g_value_set_object
(
value
,
purple_contact_get_priority_buddy
(
contact
));
break
;
default
:
G_OBJECT_WARN_INVALID_PROPERTY_ID
(
obj
,
param_id
,
pspec
);
break
;
}
}
/* GObject initialization function */
static
void
purple_contact_init
(
GTypeInstance
*
instance
,
gpointer
klass
)
{
PurpleContact
*
contact
=
PURPLE_CONTACT
(
instance
);
PurpleBlistUiOps
*
ops
=
purple_blist_get_ui_ops
();
if
(
ops
&&
ops
->
new_node
)
ops
->
new_node
(
PURPLE_BLIST_NODE
(
contact
));
PURPLE_DBUS_REGISTER_POINTER
(
contact
,
PurpleContact
);
}
/* GObject finalize function */
static
void
purple_contact_finalize
(
GObject
*
object
)
{
g_free
(
PURPLE_CONTACT_GET_PRIVATE
(
object
)
->
alias
);
PURPLE_DBUS_UNREGISTER_POINTER
(
object
);
G_OBJECT_CLASS
(
counting_parent_class
)
->
finalize
(
object
);
}
/* Class initializer function */
static
void
purple_contact_class_init
(
PurpleContactClass
*
klass
)
{
GObjectClass
*
obj_class
=
G_OBJECT_CLASS
(
klass
);
counting_parent_class
=
g_type_class_peek_parent
(
klass
);
obj_class
->
finalize
=
purple_contact_finalize
;
/* Setup properties */
obj_class
->
get_property
=
purple_contact_get_property
;
obj_class
->
set_property
=
purple_contact_set_property
;
g_type_class_add_private
(
klass
,
sizeof
(
PurpleContactPrivate
));
co_properties
[
CONTACT_PROP_ALIAS
]
=
g_param_spec_string
(
"alias"
,
"Alias"
,
"The alias for the contact."
,
NULL
,
G_PARAM_READWRITE
|
G_PARAM_STATIC_STRINGS
);
co_properties
[
CONTACT_PROP_PRIORITY_BUDDY
]
=
g_param_spec_object
(
"priority-buddy"
,
"Priority buddy"
,
"The priority buddy of the contact."
,
PURPLE_TYPE_BUDDY
,
G_PARAM_READABLE
|
G_PARAM_STATIC_STRINGS
);
g_object_class_install_properties
(
obj_class
,
CONTACT_PROP_LAST
,
co_properties
);
}
GType
purple_contact_get_type
(
void
)
{
static
GType
type
=
0
;
if
(
type
==
0
)
{
static
const
GTypeInfo
info
=
{
sizeof
(
PurpleContactClass
),
NULL
,
NULL
,
(
GClassInitFunc
)
purple_contact_class_init
,
NULL
,
NULL
,
sizeof
(
PurpleContact
),
0
,
(
GInstanceInitFunc
)
purple_contact_init
,
NULL
,
};
type
=
g_type_register_static
(
PURPLE_TYPE_COUNTING_NODE
,
"PurpleContact"
,
&
info
,
0
);
}
return
type
;
}
PurpleContact
*
purple_contact_new
(
void
)
{
return
g_object_new
(
PURPLE_TYPE_CONTACT
,
NULL
);
}
/**************************************************************************/
/* Chat API */
/**************************************************************************/
const
char
*
purple_chat_get_name
(
PurpleChat
*
chat
)
{
PurpleChatPrivate
*
priv
=
PURPLE_CHAT_GET_PRIVATE
(
chat
);
g_return_val_if_fail
(
priv
!=
NULL
,
NULL
);
if
((
priv
->
alias
!=
NULL
)
&&
(
*
priv
->
alias
!=
'\0'
))
return
priv
->
alias
;
return
purple_chat_get_name_only
(
chat
);
}
const
char
*
purple_chat_get_name_only
(
PurpleChat
*
chat
)
{
char
*
ret
=
NULL
;
PurplePlugin
*
prpl
;
PurplePluginProtocolInfo
*
prpl_info
=
NULL
;
PurpleChatPrivate
*
priv
=
PURPLE_CHAT_GET_PRIVATE
(
chat
);
g_return_val_if_fail
(
priv
!=
NULL
,
NULL
);
prpl
=
purple_find_prpl
(
purple_account_get_protocol_id
(
priv
->
account
));
prpl_info
=
PURPLE_PLUGIN_PROTOCOL_INFO
(
prpl
);
if
(
prpl_info
->
chat_info
)
{
struct
proto_chat_entry
*
pce
;
GList
*
parts
=
prpl_info
->
chat_info
(
purple_account_get_connection
(
priv
->
account
));
pce
=
parts
->
data
;
ret
=
g_hash_table_lookup
(
priv
->
components
,
pce
->
identifier
);
g_list_foreach
(
parts
,
(
GFunc
)
g_free
,
NULL
);
g_list_free
(
parts
);
}
return
ret
;
}
void
purple_chat_set_alias
(
PurpleChat
*
chat
,
const
char
*
alias
)
{
PurpleBlistUiOps
*
ops
=
purple_blist_get_ui_ops
();
char
*
old_alias
;
char
*
new_alias
=
NULL
;
PurpleChatPrivate
*
priv
=
PURPLE_CHAT_GET_PRIVATE
(
chat
);
g_return_if_fail
(
priv
!=
NULL
);
if
((
alias
!=
NULL
)
&&
(
*
alias
!=
'\0'
))
new_alias
=
purple_utf8_strip_unprintables
(
alias
);
if
(
!
purple_strings_are_different
(
priv
->
alias
,
new_alias
))
{
g_free
(
new_alias
);
return
;
}
old_alias
=
priv
->
alias
;
if
((
new_alias
!=
NULL
)
&&
(
*
new_alias
!=
'\0'
))
priv
->
alias
=
new_alias
;
else
{
priv
->
alias
=
NULL
;
g_free
(
new_alias
);
/* could be "\0" */
}
g_object_notify_by_pspec
(
G_OBJECT
(
chat
),
ch_properties
[
CHAT_PROP_ALIAS
]);
if
(
ops
)
{
if
(
ops
->
save_node
)
ops
->
save_node
(
PURPLE_BLIST_NODE
(
chat
));
if
(
ops
->
update
)
ops
->
update
(
purple_blist_get_buddy_list
(),
PURPLE_BLIST_NODE
(
chat
));
}
purple_signal_emit
(
purple_blist_get_handle
(),
"blist-node-aliased"
,
chat
,
old_alias
);
g_free
(
old_alias
);
}
PurpleGroup
*
purple_chat_get_group
(
PurpleChat
*
chat
)
{
g_return_val_if_fail
(
PURPLE_IS_CHAT
(
chat
),
NULL
);
return
PURPLE_GROUP
(
PURPLE_BLIST_NODE
(
chat
)
->
parent
);
}
PurpleAccount
*
purple_chat_get_account
(
PurpleChat
*
chat
)
{
PurpleChatPrivate
*
priv
=
PURPLE_CHAT_GET_PRIVATE
(
chat
);
g_return_val_if_fail
(
priv
!=
NULL
,
NULL
);
return
priv
->
account
;
}
GHashTable
*
purple_chat_get_components
(
PurpleChat
*
chat
)
{
PurpleChatPrivate
*
priv
=
PURPLE_CHAT_GET_PRIVATE
(
chat
);
g_return_val_if_fail
(
priv
!=
NULL
,
NULL
);
return
priv
->
components
;
}
/**************************************************************************
* GObject code for PurpleChat
**************************************************************************/
/* Set method for GObject properties */
static
void
purple_chat_set_property
(
GObject
*
obj
,
guint
param_id
,
const
GValue
*
value
,
GParamSpec
*
pspec
)
{
PurpleChat
*
chat
=
PURPLE_CHAT
(
obj
);
PurpleChatPrivate
*
priv
=
PURPLE_CHAT_GET_PRIVATE
(
chat
);
switch
(
param_id
)
{
case
CHAT_PROP_ALIAS
:
if
(
priv
->
is_constructed
)
purple_chat_set_alias
(
chat
,
g_value_get_string
(
value
));
else
priv
->
alias
=
purple_utf8_strip_unprintables
(
g_value_get_string
(
value
));
break
;
case
CHAT_PROP_ACCOUNT
:
priv
->
account
=
g_value_get_object
(
value
);
break
;
case
CHAT_PROP_COMPONENTS
:
priv
->
components
=
g_value_get_pointer
(
value
);
break
;
default
:
G_OBJECT_WARN_INVALID_PROPERTY_ID
(
obj
,
param_id
,
pspec
);
break
;
}
}
/* Get method for GObject properties */
static
void
purple_chat_get_property
(
GObject
*
obj
,
guint
param_id
,
GValue
*
value
,
GParamSpec
*
pspec
)
{
PurpleChat
*
chat
=
PURPLE_CHAT
(
obj
);
PurpleChatPrivate
*
priv
=
PURPLE_CHAT_GET_PRIVATE
(
chat
);
switch
(
param_id
)
{
case
CHAT_PROP_ALIAS
:
g_value_set_string
(
value
,
priv
->
alias
);
break
;
case
CHAT_PROP_ACCOUNT
:
g_value_set_object
(
value
,
purple_chat_get_account
(
chat
));
break
;
case
CHAT_PROP_COMPONENTS
:
g_value_set_pointer
(
value
,
purple_chat_get_components
(
chat
));
break
;
default
:
G_OBJECT_WARN_INVALID_PROPERTY_ID
(
obj
,
param_id
,
pspec
);
break
;
}
}
/* GObject initialization function */
static
void
purple_chat_init
(
GTypeInstance
*
instance
,
gpointer
klass
)
{
PURPLE_DBUS_REGISTER_POINTER
(
PURPLE_CHAT
(
instance
),
PurpleChat
);
}
/* Called when done constructing */
static
void
purple_chat_constructed
(
GObject
*
object
)
{
PurpleChat
*
chat
=
PURPLE_CHAT
(
object
);
PurpleChatPrivate
*
priv
=
PURPLE_CHAT_GET_PRIVATE
(
chat
);
PurpleBlistUiOps
*
ops
=
purple_blist_get_ui_ops
();
G_OBJECT_CLASS
(
blistnode_parent_class
)
->
constructed
(
object
);
if
(
ops
!=
NULL
&&
ops
->
new_node
!=
NULL
)
ops
->
new_node
(
PURPLE_BLIST_NODE
(
chat
));
priv
->
is_constructed
=
TRUE
;
}
/* GObject finalize function */
static
void
purple_chat_finalize
(
GObject
*
object
)
{
PurpleChatPrivate
*
priv
=
PURPLE_CHAT_GET_PRIVATE
(
object
);
g_free
(
priv
->
alias
);
g_hash_table_destroy
(
priv
->
components
);
PURPLE_DBUS_UNREGISTER_POINTER
(
object
);
G_OBJECT_CLASS
(
blistnode_parent_class
)
->
finalize
(
object
);
}
/* Class initializer function */
static
void
purple_chat_class_init
(
PurpleChatClass
*
klass
)
{
GObjectClass
*
obj_class
=
G_OBJECT_CLASS
(
klass
);
blistnode_parent_class
=
g_type_class_peek_parent
(
klass
);
obj_class
->
finalize
=
purple_chat_finalize
;
/* Setup properties */
obj_class
->
get_property
=
purple_chat_get_property
;
obj_class
->
set_property
=
purple_chat_set_property
;
obj_class
->
constructed
=
purple_chat_constructed
;
g_type_class_add_private
(
klass
,
sizeof
(
PurpleChatPrivate
));
ch_properties
[
CHAT_PROP_ALIAS
]
=
g_param_spec_string
(
"alias"
,
"Alias"
,
"The alias for the chat."
,
NULL
,
G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT
|
G_PARAM_STATIC_STRINGS
);
ch_properties
[
CHAT_PROP_ACCOUNT
]
=
g_param_spec_object
(
"account"
,
"Account"
,
"The account that the chat belongs to."
,
PURPLE_TYPE_ACCOUNT
,
G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT_ONLY
|
G_PARAM_STATIC_STRINGS
);
ch_properties
[
CHAT_PROP_COMPONENTS
]
=
g_param_spec_pointer
(
"components"
,
"Components"
,
"The protocol components of the chat."
,
G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT_ONLY
|
G_PARAM_STATIC_STRINGS
);
g_object_class_install_properties
(
obj_class
,
CHAT_PROP_LAST
,
ch_properties
);
}
GType
purple_chat_get_type
(
void
)
{
static
GType
type
=
0
;
if
(
type
==
0
)
{
static
const
GTypeInfo
info
=
{
sizeof
(
PurpleChatClass
),
NULL
,
NULL
,
(
GClassInitFunc
)
purple_chat_class_init
,
NULL
,
NULL
,
sizeof
(
PurpleChat
),
0
,
(
GInstanceInitFunc
)
purple_chat_init
,
NULL
,
};
type
=
g_type_register_static
(
PURPLE_TYPE_BLIST_NODE
,
"PurpleChat"
,
&
info
,
0
);
}
return
type
;
}
PurpleChat
*
purple_chat_new
(
PurpleAccount
*
account
,
const
char
*
alias
,
GHashTable
*
components
)
{
g_return_val_if_fail
(
PURPLE_IS_ACCOUNT
(
account
),
NULL
);
g_return_val_if_fail
(
components
!=
NULL
,
NULL
);
return
g_object_new
(
PURPLE_TYPE_CHAT
,
"account"
,
account
,
"alias"
,
alias
,
"components"
,
components
,
NULL
);
}
/**************************************************************************/
/* Group API */
/**************************************************************************/
GSList
*
purple_group_get_accounts
(
PurpleGroup
*
group
)
{
GSList
*
l
=
NULL
;
PurpleBlistNode
*
gnode
,
*
cnode
,
*
bnode
;
gnode
=
(
PurpleBlistNode
*
)
group
;
for
(
cnode
=
gnode
->
child
;
cnode
;
cnode
=
cnode
->
next
)
{
if
(
PURPLE_IS_CHAT
(
cnode
))
{
if
(
!
g_slist_find
(
l
,
purple_chat_get_account
(
PURPLE_CHAT
(
cnode
))))
l
=
g_slist_append
(
l
,
purple_chat_get_account
(
PURPLE_CHAT
(
cnode
)));
}
else
if
(
PURPLE_IS_CONTACT
(
cnode
))
{
for
(
bnode
=
cnode
->
child
;
bnode
;
bnode
=
bnode
->
next
)
{
if
(
PURPLE_IS_BUDDY
(
bnode
))
{
if
(
!
g_slist_find
(
l
,
purple_buddy_get_account
(
PURPLE_BUDDY
(
bnode
))))
l
=
g_slist_append
(
l
,
purple_buddy_get_account
(
PURPLE_BUDDY
(
bnode
)));
}
}
}
}
return
l
;
}
gboolean
purple_group_on_account
(
PurpleGroup
*
g
,
PurpleAccount
*
account
)
{
PurpleBlistNode
*
cnode
;
for
(
cnode
=
((
PurpleBlistNode
*
)
g
)
->
child
;
cnode
;
cnode
=
cnode
->
next
)
{
if
(
PURPLE_IS_CONTACT
(
cnode
))
{
if
(
purple_contact_on_account
((
PurpleContact
*
)
cnode
,
account
))
return
TRUE
;
}
else
if
(
PURPLE_IS_CHAT
(
cnode
))
{
PurpleChat
*
chat
=
(
PurpleChat
*
)
cnode
;
if
((
!
account
&&
purple_account_is_connected
(
purple_chat_get_account
(
chat
)))
||
purple_chat_get_account
(
chat
)
==
account
)
return
TRUE
;
}
}
return
FALSE
;
}
/*
* TODO: If merging, prompt the user if they want to merge.
*/
void
purple_group_set_name
(
PurpleGroup
*
source
,
const
char
*
name
)
{
PurpleBlistUiOps
*
ops
=
purple_blist_get_ui_ops
();
PurpleGroup
*
dest
;
gchar
*
old_name
;
gchar
*
new_name
;
GList
*
moved_buddies
=
NULL
;
GSList
*
accts
;
PurpleGroupPrivate
*
priv
=
PURPLE_GROUP_GET_PRIVATE
(
source
);
g_return_if_fail
(
priv
!=
NULL
);
g_return_if_fail
(
name
!=
NULL
);
new_name
=
purple_utf8_strip_unprintables
(
name
);
if
(
*
new_name
==
'\0'
||
purple_strequal
(
new_name
,
priv
->
name
))
{
g_free
(
new_name
);
return
;
}
dest
=
purple_blist_find_group
(
new_name
);
if
(
dest
!=
NULL
&&
purple_utf8_strcasecmp
(
priv
->
name
,
PURPLE_GROUP_GET_PRIVATE
(
dest
)
->
name
)
!=
0
)
{
/* We're merging two groups */
PurpleBlistNode
*
prev
,
*
child
,
*
next
;
prev
=
_purple_blist_get_last_child
((
PurpleBlistNode
*
)
dest
);
child
=
PURPLE_BLIST_NODE
(
source
)
->
child
;
/*
* TODO: This seems like a dumb way to do this... why not just
* append all children from the old group to the end of the new
* one? PRPLs might be expecting to receive an add_buddy() for
* each moved buddy...
*/
while
(
child
)
{
next
=
child
->
next
;
if
(
PURPLE_IS_CONTACT
(
child
))
{
PurpleBlistNode
*
bnode
;
purple_blist_add_contact
((
PurpleContact
*
)
child
,
dest
,
prev
);
for
(
bnode
=
child
->
child
;
bnode
!=
NULL
;
bnode
=
bnode
->
next
)
{
purple_blist_add_buddy
((
PurpleBuddy
*
)
bnode
,
(
PurpleContact
*
)
child
,
NULL
,
bnode
->
prev
);
moved_buddies
=
g_list_append
(
moved_buddies
,
bnode
);
}
prev
=
child
;
}
else
if
(
PURPLE_IS_CHAT
(
child
))
{
purple_blist_add_chat
((
PurpleChat
*
)
child
,
dest
,
prev
);
prev
=
child
;
}
else
{
purple_debug
(
PURPLE_DEBUG_ERROR
,
"blistnodetypes"
,
"Unknown child type in group %s
\n
"
,
priv
->
name
);
}
child
=
next
;
}
/* Make a copy of the old group name and then delete the old group */
old_name
=
g_strdup
(
priv
->
name
);
purple_blist_remove_group
(
source
);
source
=
dest
;
g_free
(
new_name
);
}
else
{
/* A simple rename */
PurpleBlistNode
*
cnode
,
*
bnode
;
/* Build a GList of all buddies in this group */
for
(
cnode
=
PURPLE_BLIST_NODE
(
source
)
->
child
;
cnode
!=
NULL
;
cnode
=
cnode
->
next
)
{
if
(
PURPLE_IS_CONTACT
(
cnode
))
for
(
bnode
=
cnode
->
child
;
bnode
!=
NULL
;
bnode
=
bnode
->
next
)
moved_buddies
=
g_list_append
(
moved_buddies
,
bnode
);
}
purple_blist_update_groups_cache
(
source
,
new_name
);
old_name
=
priv
->
name
;
priv
->
name
=
new_name
;
g_object_notify_by_pspec
(
G_OBJECT
(
source
),
gr_properties
[
GROUP_PROP_NAME
]);
}
/* Save our changes */
if
(
ops
&&
ops
->
save_node
)
ops
->
save_node
(
PURPLE_BLIST_NODE
(
source
));
/* Update the UI */
if
(
ops
&&
ops
->
update
)
ops
->
update
(
purple_blist_get_buddy_list
(),
PURPLE_BLIST_NODE
(
source
));
/* Notify all PRPLs */
/* TODO: Is this condition needed? Seems like it would always be TRUE */
if
(
old_name
&&
!
purple_strequal
(
priv
->
name
,
old_name
))
{
for
(
accts
=
purple_group_get_accounts
(
source
);
accts
;
accts
=
g_slist_remove
(
accts
,
accts
->
data
))
{
PurpleAccount
*
account
=
accts
->
data
;
PurpleConnection
*
gc
=
NULL
;
PurplePlugin
*
prpl
=
NULL
;
PurplePluginProtocolInfo
*
prpl_info
=
NULL
;
GList
*
l
=
NULL
,
*
buddies
=
NULL
;
gc
=
purple_account_get_connection
(
account
);
if
(
gc
)
prpl
=
purple_connection_get_prpl
(
gc
);
if
(
gc
&&
prpl
)
prpl_info
=
PURPLE_PLUGIN_PROTOCOL_INFO
(
prpl
);
if
(
!
prpl_info
)
continue
;
for
(
l
=
moved_buddies
;
l
;
l
=
l
->
next
)
{
PurpleBuddy
*
buddy
=
PURPLE_BUDDY
(
l
->
data
);
if
(
buddy
&&
purple_buddy_get_account
(
buddy
)
==
account
)
buddies
=
g_list_append
(
buddies
,
(
PurpleBlistNode
*
)
buddy
);
}
if
(
prpl_info
->
rename_group
)
{
prpl_info
->
rename_group
(
gc
,
old_name
,
source
,
buddies
);
}
else
{
GList
*
cur
,
*
groups
=
NULL
;
/* Make a list of what the groups each buddy is in */
for
(
cur
=
buddies
;
cur
;
cur
=
cur
->
next
)
{
PurpleBlistNode
*
node
=
(
PurpleBlistNode
*
)
cur
->
data
;
groups
=
g_list_prepend
(
groups
,
node
->
parent
->
parent
);
}
purple_account_remove_buddies
(
account
,
buddies
,
groups
);
g_list_free
(
groups
);
purple_account_add_buddies
(
account
,
buddies
,
NULL
);
}
g_list_free
(
buddies
);
}
}
g_list_free
(
moved_buddies
);
g_free
(
old_name
);
g_object_notify_by_pspec
(
G_OBJECT
(
source
),
gr_properties
[
GROUP_PROP_NAME
]);
}
const
char
*
purple_group_get_name
(
PurpleGroup
*
group
)
{
PurpleGroupPrivate
*
priv
=
PURPLE_GROUP_GET_PRIVATE
(
group
);
g_return_val_if_fail
(
priv
!=
NULL
,
NULL
);
return
priv
->
name
;
}
/**************************************************************************
* GObject code for PurpleGroup
**************************************************************************/
/* Set method for GObject properties */
static
void
purple_group_set_property
(
GObject
*
obj
,
guint
param_id
,
const
GValue
*
value
,
GParamSpec
*
pspec
)
{
PurpleGroup
*
group
=
PURPLE_GROUP
(
obj
);
PurpleGroupPrivate
*
priv
=
PURPLE_GROUP_GET_PRIVATE
(
group
);
switch
(
param_id
)
{
case
GROUP_PROP_NAME
:
if
(
priv
->
is_constructed
)
purple_group_set_name
(
group
,
g_value_get_string
(
value
));
else
priv
->
name
=
purple_utf8_strip_unprintables
(
g_value_get_string
(
value
));
break
;
default
:
G_OBJECT_WARN_INVALID_PROPERTY_ID
(
obj
,
param_id
,
pspec
);
break
;
}
}
/* Get method for GObject properties */
static
void
purple_group_get_property
(
GObject
*
obj
,
guint
param_id
,
GValue
*
value
,
GParamSpec
*
pspec
)
{
PurpleGroup
*
group
=
PURPLE_GROUP
(
obj
);
switch
(
param_id
)
{
case
GROUP_PROP_NAME
:
g_value_set_string
(
value
,
purple_group_get_name
(
group
));
break
;
default
:
G_OBJECT_WARN_INVALID_PROPERTY_ID
(
obj
,
param_id
,
pspec
);
break
;
}
}
/* GObject initialization function */
static
void
purple_group_init
(
GTypeInstance
*
instance
,
gpointer
klass
)
{
PURPLE_DBUS_REGISTER_POINTER
(
PURPLE_GROUP
(
instance
),
PurpleGroup
);
}
/* Called when done constructing */
static
void
purple_group_constructed
(
GObject
*
object
)
{
PurpleGroup
*
group
=
PURPLE_GROUP
(
object
);
PurpleGroupPrivate
*
priv
=
PURPLE_GROUP_GET_PRIVATE
(
group
);
PurpleBlistUiOps
*
ops
=
purple_blist_get_ui_ops
();
G_OBJECT_CLASS
(
counting_parent_class
)
->
constructed
(
object
);
if
(
ops
&&
ops
->
new_node
)
ops
->
new_node
(
PURPLE_BLIST_NODE
(
group
));
priv
->
is_constructed
=
TRUE
;
}
/* GObject finalize function */
static
void
purple_group_finalize
(
GObject
*
object
)
{
g_free
(
PURPLE_GROUP_GET_PRIVATE
(
object
)
->
name
);
PURPLE_DBUS_UNREGISTER_POINTER
(
object
);
G_OBJECT_CLASS
(
counting_parent_class
)
->
finalize
(
object
);
}
/* Class initializer function */
static
void
purple_group_class_init
(
PurpleGroupClass
*
klass
)
{
GObjectClass
*
obj_class
=
G_OBJECT_CLASS
(
klass
);
counting_parent_class
=
g_type_class_peek_parent
(
klass
);
obj_class
->
finalize
=
purple_group_finalize
;
obj_class
->
constructed
=
purple_group_constructed
;
/* Setup properties */
obj_class
->
get_property
=
purple_group_get_property
;
obj_class
->
set_property
=
purple_group_set_property
;
g_type_class_add_private
(
klass
,
sizeof
(
PurpleGroupPrivate
));
gr_properties
[
GROUP_PROP_NAME
]
=
g_param_spec_string
(
"name"
,
"Name"
,
"Name of the group."
,
NULL
,
G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT
|
G_PARAM_STATIC_STRINGS
);
g_object_class_install_properties
(
obj_class
,
GROUP_PROP_LAST
,
gr_properties
);
}
GType
purple_group_get_type
(
void
)
{
static
GType
type
=
0
;
if
(
type
==
0
)
{
static
const
GTypeInfo
info
=
{
sizeof
(
PurpleGroupClass
),
NULL
,
NULL
,
(
GClassInitFunc
)
purple_group_class_init
,
NULL
,
NULL
,
sizeof
(
PurpleGroup
),
0
,
(
GInstanceInitFunc
)
purple_group_init
,
NULL
,
};
type
=
g_type_register_static
(
PURPLE_TYPE_COUNTING_NODE
,
"PurpleGroup"
,
&
info
,
0
);
}
return
type
;
}
PurpleGroup
*
purple_group_new
(
const
char
*
name
)
{
PurpleGroup
*
group
;
g_return_val_if_fail
(
name
!=
NULL
,
NULL
);
g_return_val_if_fail
(
*
name
!=
'\0'
,
NULL
);
group
=
purple_blist_find_group
(
name
);
if
(
group
!=
NULL
)
return
group
;
return
g_object_new
(
PURPLE_TYPE_GROUP
,
"name"
,
name
,
NULL
);
}