pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Merge release-2.x.y branch into default.
2014-02-01, Mark Doliner
0efdb7833dee
Merge release-2.x.y branch into default.
Clean merge, apart from the .tx/config change that we don't want here.
/*
* 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
"buddylist.h"
#include
"cmds.h"
#include
"conversation.h"
#include
"dbus-maybe.h"
#include
"debug.h"
#include
"enums.h"
#include
"imgstore.h"
#include
"notify.h"
#include
"prefs.h"
#include
"prpl.h"
#include
"request.h"
#include
"signals.h"
#include
"util.h"
#define PURPLE_CONVERSATION_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_CONVERSATION, PurpleConversationPrivate))
/** @copydoc _PurpleConversationPrivate */
typedef
struct
_PurpleConversationPrivate
PurpleConversationPrivate
;
/** General private data for a conversation */
struct
_PurpleConversationPrivate
{
PurpleAccount
*
account
;
/**< The user using this conversation. */
char
*
name
;
/**< The name of the conversation. */
char
*
title
;
/**< The window title. */
gboolean
logging
;
/**< The status of logging. */
GList
*
logs
;
/**< This conversation's logs */
PurpleConversationUiOps
*
ui_ops
;
/**< UI-specific operations. */
PurpleConnectionFlags
features
;
/**< The supported features */
GList
*
message_history
;
/**< Message history, as a GList of
PurpleConversationMessage's */
PurpleE2eeState
*
e2ee_state
;
/**< End-to-end encryption state. */
};
/**
* Description of a conversation message
*/
struct
_PurpleConversationMessage
{
char
*
who
;
char
*
what
;
PurpleMessageFlags
flags
;
time_t
when
;
PurpleConversation
*
conv
;
char
*
alias
;
};
/* GObject Property enums */
enum
{
PROP_0
,
PROP_ACCOUNT
,
PROP_NAME
,
PROP_TITLE
,
PROP_LOGGING
,
PROP_FEATURES
,
PROP_LAST
};
static
GObjectClass
*
parent_class
;
static
GParamSpec
*
properties
[
PROP_LAST
];
static
void
common_send
(
PurpleConversation
*
conv
,
const
char
*
message
,
PurpleMessageFlags
msgflags
)
{
PurpleAccount
*
account
;
PurpleConnection
*
gc
;
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
char
*
displayed
=
NULL
,
*
sent
=
NULL
;
int
err
=
0
;
g_return_if_fail
(
priv
!=
NULL
);
if
(
*
message
==
'\0'
)
return
;
account
=
purple_conversation_get_account
(
conv
);
g_return_if_fail
(
PURPLE_IS_ACCOUNT
(
account
));
gc
=
purple_account_get_connection
(
account
);
g_return_if_fail
(
PURPLE_IS_CONNECTION
(
gc
));
/* Always linkfy the text for display, unless we're
* explicitly asked to do otheriwse*/
if
(
!
(
msgflags
&
PURPLE_MESSAGE_INVISIBLE
))
{
if
(
msgflags
&
PURPLE_MESSAGE_NO_LINKIFY
)
displayed
=
g_strdup
(
message
);
else
displayed
=
purple_markup_linkify
(
message
);
}
if
(
displayed
&&
(
priv
->
features
&
PURPLE_CONNECTION_FLAG_HTML
)
&&
!
(
msgflags
&
PURPLE_MESSAGE_RAW
))
{
sent
=
g_strdup
(
displayed
);
}
else
sent
=
g_strdup
(
message
);
msgflags
|=
PURPLE_MESSAGE_SEND
;
if
(
PURPLE_IS_IM_CONVERSATION
(
conv
))
{
purple_signal_emit
(
purple_conversations_get_handle
(),
"sending-im-msg"
,
account
,
purple_conversation_get_name
(
conv
),
&
sent
);
if
(
sent
!=
NULL
&&
sent
[
0
]
!=
'\0'
)
{
err
=
serv_send_im
(
gc
,
purple_conversation_get_name
(
conv
),
sent
,
msgflags
);
if
((
err
>
0
)
&&
(
displayed
!=
NULL
))
purple_conversation_write_message
(
conv
,
NULL
,
displayed
,
msgflags
,
time
(
NULL
));
purple_signal_emit
(
purple_conversations_get_handle
(),
"sent-im-msg"
,
account
,
purple_conversation_get_name
(
conv
),
sent
);
}
}
else
if
(
PURPLE_IS_CHAT_CONVERSATION
(
conv
))
{
int
id
=
purple_chat_conversation_get_id
(
PURPLE_CHAT_CONVERSATION
(
conv
));
purple_signal_emit
(
purple_conversations_get_handle
(),
"sending-chat-msg"
,
account
,
&
sent
,
id
);
if
(
sent
!=
NULL
&&
sent
[
0
]
!=
'\0'
)
{
err
=
serv_chat_send
(
gc
,
id
,
sent
,
msgflags
);
purple_signal_emit
(
purple_conversations_get_handle
(),
"sent-chat-msg"
,
account
,
sent
,
id
);
}
}
if
(
err
<
0
)
{
const
char
*
who
;
const
char
*
msg
;
who
=
purple_conversation_get_name
(
conv
);
if
(
err
==
-
E2BIG
)
{
msg
=
_
(
"Unable to send message: The message is too large."
);
if
(
!
purple_conversation_present_error
(
who
,
account
,
msg
))
{
char
*
msg2
=
g_strdup_printf
(
_
(
"Unable to send message to %s."
),
who
);
purple_notify_error
(
gc
,
NULL
,
msg2
,
_
(
"The message is too large."
),
purple_request_cpar_from_connection
(
gc
));
g_free
(
msg2
);
}
}
else
if
(
err
==
-
ENOTCONN
)
{
purple_debug
(
PURPLE_DEBUG_ERROR
,
"conversation"
,
"Not yet connected.
\n
"
);
}
else
{
msg
=
_
(
"Unable to send message."
);
if
(
!
purple_conversation_present_error
(
who
,
account
,
msg
))
{
char
*
msg2
=
g_strdup_printf
(
_
(
"Unable to send message to %s."
),
who
);
purple_notify_error
(
gc
,
NULL
,
msg2
,
NULL
,
purple_request_cpar_from_connection
(
gc
));
g_free
(
msg2
);
}
}
}
g_free
(
displayed
);
g_free
(
sent
);
}
static
void
open_log
(
PurpleConversation
*
conv
)
{
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
g_return_if_fail
(
priv
!=
NULL
);
priv
->
logs
=
g_list_append
(
NULL
,
purple_log_new
(
PURPLE_IS_CHAT_CONVERSATION
(
conv
)
?
PURPLE_LOG_CHAT
:
PURPLE_LOG_IM
,
priv
->
name
,
priv
->
account
,
conv
,
time
(
NULL
),
NULL
));
}
/* Functions that deal with PurpleConversationMessage */
static
void
add_message_to_history
(
PurpleConversation
*
conv
,
const
char
*
who
,
const
char
*
alias
,
const
char
*
message
,
PurpleMessageFlags
flags
,
time_t
when
)
{
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
PurpleConversationMessage
*
msg
;
PurpleConnection
*
gc
;
g_return_if_fail
(
priv
!=
NULL
);
gc
=
purple_account_get_connection
(
priv
->
account
);
if
(
flags
&
PURPLE_MESSAGE_SEND
)
{
const
char
*
me
=
NULL
;
if
(
gc
)
me
=
purple_connection_get_display_name
(
gc
);
if
(
!
me
)
me
=
purple_account_get_username
(
priv
->
account
);
who
=
me
;
}
msg
=
g_new0
(
PurpleConversationMessage
,
1
);
PURPLE_DBUS_REGISTER_POINTER
(
msg
,
PurpleConversationMessage
);
msg
->
who
=
g_strdup
(
who
);
msg
->
alias
=
g_strdup
(
alias
);
msg
->
flags
=
flags
;
msg
->
what
=
g_strdup
(
message
);
msg
->
when
=
when
;
msg
->
conv
=
conv
;
priv
->
message_history
=
g_list_prepend
(
priv
->
message_history
,
msg
);
}
static
void
free_conv_message
(
PurpleConversationMessage
*
msg
)
{
g_free
(
msg
->
who
);
g_free
(
msg
->
alias
);
g_free
(
msg
->
what
);
PURPLE_DBUS_UNREGISTER_POINTER
(
msg
);
g_free
(
msg
);
}
static
void
message_history_free
(
GList
*
list
)
{
g_list_foreach
(
list
,
(
GFunc
)
free_conv_message
,
NULL
);
g_list_free
(
list
);
}
/**************************************************************************
* Conversation API
**************************************************************************/
void
purple_conversation_present
(
PurpleConversation
*
conv
)
{
PurpleConversationUiOps
*
ops
;
g_return_if_fail
(
PURPLE_IS_CONVERSATION
(
conv
));
ops
=
purple_conversation_get_ui_ops
(
conv
);
if
(
ops
&&
ops
->
present
)
ops
->
present
(
conv
);
}
void
purple_conversation_set_features
(
PurpleConversation
*
conv
,
PurpleConnectionFlags
features
)
{
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
g_return_if_fail
(
priv
!=
NULL
);
priv
->
features
=
features
;
g_object_notify_by_pspec
(
G_OBJECT
(
conv
),
properties
[
PROP_FEATURES
]);
purple_conversation_update
(
conv
,
PURPLE_CONVERSATION_UPDATE_FEATURES
);
}
PurpleConnectionFlags
purple_conversation_get_features
(
PurpleConversation
*
conv
)
{
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
g_return_val_if_fail
(
priv
!=
NULL
,
0
);
return
priv
->
features
;
}
void
purple_conversation_set_ui_ops
(
PurpleConversation
*
conv
,
PurpleConversationUiOps
*
ops
)
{
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
g_return_if_fail
(
priv
!=
NULL
);
if
(
priv
->
ui_ops
==
ops
)
return
;
if
(
priv
->
ui_ops
!=
NULL
&&
priv
->
ui_ops
->
destroy_conversation
!=
NULL
)
priv
->
ui_ops
->
destroy_conversation
(
conv
);
priv
->
ui_ops
=
ops
;
}
PurpleConversationUiOps
*
purple_conversation_get_ui_ops
(
const
PurpleConversation
*
conv
)
{
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
g_return_val_if_fail
(
priv
!=
NULL
,
NULL
);
return
priv
->
ui_ops
;
}
void
purple_conversation_set_account
(
PurpleConversation
*
conv
,
PurpleAccount
*
account
)
{
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
g_return_if_fail
(
priv
!=
NULL
);
if
(
account
==
purple_conversation_get_account
(
conv
))
return
;
_purple_conversations_update_cache
(
conv
,
NULL
,
account
);
priv
->
account
=
account
;
g_object_notify_by_pspec
(
G_OBJECT
(
conv
),
properties
[
PROP_ACCOUNT
]);
purple_conversation_update
(
conv
,
PURPLE_CONVERSATION_UPDATE_ACCOUNT
);
}
PurpleAccount
*
purple_conversation_get_account
(
const
PurpleConversation
*
conv
)
{
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
g_return_val_if_fail
(
priv
!=
NULL
,
NULL
);
return
priv
->
account
;
}
PurpleConnection
*
purple_conversation_get_connection
(
const
PurpleConversation
*
conv
)
{
PurpleAccount
*
account
;
g_return_val_if_fail
(
PURPLE_IS_CONVERSATION
(
conv
),
NULL
);
account
=
purple_conversation_get_account
(
conv
);
if
(
account
==
NULL
)
return
NULL
;
return
purple_account_get_connection
(
account
);
}
void
purple_conversation_set_title
(
PurpleConversation
*
conv
,
const
char
*
title
)
{
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
g_return_if_fail
(
priv
!=
NULL
);
g_return_if_fail
(
title
!=
NULL
);
g_free
(
priv
->
title
);
priv
->
title
=
g_strdup
(
title
);
g_object_notify_by_pspec
(
G_OBJECT
(
conv
),
properties
[
PROP_TITLE
]);
purple_conversation_update
(
conv
,
PURPLE_CONVERSATION_UPDATE_TITLE
);
}
const
char
*
purple_conversation_get_title
(
const
PurpleConversation
*
conv
)
{
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
g_return_val_if_fail
(
priv
!=
NULL
,
NULL
);
return
priv
->
title
;
}
void
purple_conversation_autoset_title
(
PurpleConversation
*
conv
)
{
PurpleAccount
*
account
;
PurpleBuddy
*
b
;
PurpleChat
*
chat
;
const
char
*
text
=
NULL
,
*
name
;
g_return_if_fail
(
PURPLE_IS_CONVERSATION
(
conv
));
account
=
purple_conversation_get_account
(
conv
);
name
=
purple_conversation_get_name
(
conv
);
if
(
PURPLE_IS_IM_CONVERSATION
(
conv
))
{
if
(
account
&&
((
b
=
purple_blist_find_buddy
(
account
,
name
))
!=
NULL
))
text
=
purple_buddy_get_contact_alias
(
b
);
}
else
if
(
PURPLE_IS_CHAT_CONVERSATION
(
conv
))
{
if
(
account
&&
((
chat
=
purple_blist_find_chat
(
account
,
name
))
!=
NULL
))
text
=
purple_chat_get_name
(
chat
);
}
if
(
text
==
NULL
)
text
=
name
;
purple_conversation_set_title
(
conv
,
text
);
}
void
purple_conversation_set_name
(
PurpleConversation
*
conv
,
const
char
*
name
)
{
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
g_return_if_fail
(
priv
!=
NULL
);
_purple_conversations_update_cache
(
conv
,
name
,
NULL
);
g_free
(
priv
->
name
);
priv
->
name
=
g_strdup
(
name
);
g_object_notify_by_pspec
(
G_OBJECT
(
conv
),
properties
[
PROP_NAME
]);
purple_conversation_autoset_title
(
conv
);
purple_conversation_update
(
conv
,
PURPLE_CONVERSATION_UPDATE_NAME
);
}
const
char
*
purple_conversation_get_name
(
const
PurpleConversation
*
conv
)
{
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
g_return_val_if_fail
(
priv
!=
NULL
,
NULL
);
return
priv
->
name
;
}
void
purple_conversation_set_e2ee_state
(
PurpleConversation
*
conv
,
PurpleE2eeState
*
state
)
{
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
g_return_if_fail
(
priv
!=
NULL
);
if
(
state
!=
NULL
&&
purple_e2ee_state_get_provider
(
state
)
!=
purple_e2ee_provider_get_main
())
{
purple_debug_error
(
"conversation"
,
"This is not the main e2ee provider"
);
return
;
}
if
(
state
)
purple_e2ee_state_ref
(
state
);
purple_e2ee_state_unref
(
priv
->
e2ee_state
);
priv
->
e2ee_state
=
state
;
purple_conversation_update
(
conv
,
PURPLE_CONVERSATION_UPDATE_E2EE
);
}
PurpleE2eeState
*
purple_conversation_get_e2ee_state
(
PurpleConversation
*
conv
)
{
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
PurpleE2eeProvider
*
provider
;
g_return_val_if_fail
(
priv
!=
NULL
,
NULL
);
if
(
priv
->
e2ee_state
==
NULL
)
return
NULL
;
provider
=
purple_e2ee_provider_get_main
();
if
(
provider
==
NULL
)
return
NULL
;
if
(
purple_e2ee_state_get_provider
(
priv
->
e2ee_state
)
!=
provider
)
{
purple_debug_warning
(
"conversation"
,
"e2ee state has invalid provider set"
);
return
NULL
;
}
return
priv
->
e2ee_state
;
}
void
purple_conversation_set_logging
(
PurpleConversation
*
conv
,
gboolean
log
)
{
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
g_return_if_fail
(
priv
!=
NULL
);
if
(
priv
->
logging
!=
log
)
{
priv
->
logging
=
log
;
if
(
log
&&
priv
->
logs
==
NULL
)
open_log
(
conv
);
g_object_notify_by_pspec
(
G_OBJECT
(
conv
),
properties
[
PROP_LOGGING
]);
purple_conversation_update
(
conv
,
PURPLE_CONVERSATION_UPDATE_LOGGING
);
}
}
gboolean
purple_conversation_is_logging
(
const
PurpleConversation
*
conv
)
{
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
g_return_val_if_fail
(
priv
!=
NULL
,
FALSE
);
return
priv
->
logging
;
}
void
purple_conversation_close_logs
(
PurpleConversation
*
conv
)
{
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
g_return_if_fail
(
priv
!=
NULL
);
g_list_foreach
(
priv
->
logs
,
(
GFunc
)
purple_log_free
,
NULL
);
g_list_free
(
priv
->
logs
);
priv
->
logs
=
NULL
;
}
void
purple_conversation_write
(
PurpleConversation
*
conv
,
const
char
*
who
,
const
char
*
message
,
PurpleMessageFlags
flags
,
time_t
mtime
)
{
PurplePluginProtocolInfo
*
prpl_info
=
NULL
;
PurpleConnection
*
gc
=
NULL
;
PurpleAccount
*
account
;
PurpleConversationUiOps
*
ops
;
const
char
*
alias
;
char
*
displayed
=
NULL
;
PurpleBuddy
*
b
;
int
plugin_return
;
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
/* int logging_font_options = 0; */
g_return_if_fail
(
priv
!=
NULL
);
g_return_if_fail
(
message
!=
NULL
);
ops
=
purple_conversation_get_ui_ops
(
conv
);
account
=
purple_conversation_get_account
(
conv
);
if
(
account
!=
NULL
)
gc
=
purple_account_get_connection
(
account
);
if
(
PURPLE_IS_CHAT_CONVERSATION
(
conv
)
&&
(
gc
!=
NULL
&&
!
g_slist_find
(
purple_connection_get_active_chats
(
gc
),
conv
)))
return
;
if
(
PURPLE_IS_IM_CONVERSATION
(
conv
)
&&
!
g_list_find
(
purple_conversations_get_all
(),
conv
))
return
;
displayed
=
g_strdup
(
message
);
if
(
who
==
NULL
||
*
who
==
'\0'
)
who
=
purple_conversation_get_name
(
conv
);
alias
=
who
;
plugin_return
=
GPOINTER_TO_INT
(
purple_signal_emit_return_1
(
purple_conversations_get_handle
(),
(
PURPLE_IS_IM_CONVERSATION
(
conv
)
?
"writing-im-msg"
:
"writing-chat-msg"
),
account
,
who
,
&
displayed
,
conv
,
flags
));
if
(
displayed
==
NULL
)
return
;
if
(
plugin_return
)
{
g_free
(
displayed
);
return
;
}
if
(
account
!=
NULL
)
{
prpl_info
=
PURPLE_PLUGIN_PROTOCOL_INFO
(
purple_find_prpl
(
purple_account_get_protocol_id
(
account
)));
if
(
PURPLE_IS_IM_CONVERSATION
(
conv
)
||
!
(
prpl_info
->
options
&
OPT_PROTO_UNIQUE_CHATNAME
))
{
if
(
flags
&
PURPLE_MESSAGE_SEND
)
{
b
=
purple_blist_find_buddy
(
account
,
purple_account_get_username
(
account
));
if
(
purple_account_get_private_alias
(
account
)
!=
NULL
)
alias
=
purple_account_get_private_alias
(
account
);
else
if
(
b
!=
NULL
&&
!
purple_strequal
(
purple_buddy_get_name
(
b
),
purple_buddy_get_contact_alias
(
b
)))
alias
=
purple_buddy_get_contact_alias
(
b
);
else
if
(
purple_connection_get_display_name
(
gc
)
!=
NULL
)
alias
=
purple_connection_get_display_name
(
gc
);
else
alias
=
purple_account_get_username
(
account
);
}
else
{
b
=
purple_blist_find_buddy
(
account
,
who
);
if
(
b
!=
NULL
)
alias
=
purple_buddy_get_contact_alias
(
b
);
}
}
}
if
(
!
(
flags
&
PURPLE_MESSAGE_NO_LOG
)
&&
purple_conversation_is_logging
(
conv
))
{
GList
*
log
;
log
=
priv
->
logs
;
while
(
log
!=
NULL
)
{
purple_log_write
((
PurpleLog
*
)
log
->
data
,
flags
,
alias
,
mtime
,
displayed
);
log
=
log
->
next
;
}
}
if
(
ops
&&
ops
->
write_conv
)
ops
->
write_conv
(
conv
,
who
,
alias
,
displayed
,
flags
,
mtime
);
add_message_to_history
(
conv
,
who
,
alias
,
message
,
flags
,
mtime
);
purple_signal_emit
(
purple_conversations_get_handle
(),
(
PURPLE_IS_IM_CONVERSATION
(
conv
)
?
"wrote-im-msg"
:
"wrote-chat-msg"
),
account
,
who
,
displayed
,
conv
,
flags
);
g_free
(
displayed
);
}
void
purple_conversation_write_message
(
PurpleConversation
*
conv
,
const
char
*
who
,
const
char
*
message
,
PurpleMessageFlags
flags
,
time_t
mtime
)
{
PurpleConversationClass
*
klass
=
NULL
;
g_return_if_fail
(
PURPLE_IS_CONVERSATION
(
conv
));
klass
=
PURPLE_CONVERSATION_GET_CLASS
(
conv
);
if
(
klass
&&
klass
->
write_message
)
klass
->
write_message
(
conv
,
who
,
message
,
flags
,
mtime
);
}
void
purple_conversation_send
(
PurpleConversation
*
conv
,
const
char
*
message
)
{
purple_conversation_send_with_flags
(
conv
,
message
,
0
);
}
void
purple_conversation_send_with_flags
(
PurpleConversation
*
conv
,
const
char
*
message
,
PurpleMessageFlags
flags
)
{
g_return_if_fail
(
PURPLE_IS_CONVERSATION
(
conv
));
g_return_if_fail
(
message
!=
NULL
);
common_send
(
conv
,
message
,
flags
);
}
gboolean
purple_conversation_has_focus
(
PurpleConversation
*
conv
)
{
gboolean
ret
=
FALSE
;
PurpleConversationUiOps
*
ops
;
g_return_val_if_fail
(
PURPLE_IS_CONVERSATION
(
conv
),
FALSE
);
ops
=
purple_conversation_get_ui_ops
(
conv
);
if
(
ops
!=
NULL
&&
ops
->
has_focus
!=
NULL
)
ret
=
ops
->
has_focus
(
conv
);
return
ret
;
}
/*
* TODO: Need to make sure calls to this function happen in the core
* instead of the UI. That way UIs have less work to do, and the
* core/UI split is cleaner. Also need to make sure this is called
* when chats are added/removed from the blist.
*/
void
purple_conversation_update
(
PurpleConversation
*
conv
,
PurpleConversationUpdateType
type
)
{
g_return_if_fail
(
PURPLE_IS_CONVERSATION
(
conv
));
purple_signal_emit
(
purple_conversations_get_handle
(),
"conversation-updated"
,
conv
,
type
);
}
gboolean
purple_conversation_present_error
(
const
char
*
who
,
PurpleAccount
*
account
,
const
char
*
what
)
{
PurpleConversation
*
conv
;
g_return_val_if_fail
(
who
!=
NULL
,
FALSE
);
g_return_val_if_fail
(
PURPLE_IS_ACCOUNT
(
account
),
FALSE
);
g_return_val_if_fail
(
what
!=
NULL
,
FALSE
);
conv
=
purple_conversations_find_with_account
(
who
,
account
);
if
(
conv
!=
NULL
)
purple_conversation_write
(
conv
,
NULL
,
what
,
PURPLE_MESSAGE_ERROR
,
time
(
NULL
));
else
return
FALSE
;
return
TRUE
;
}
static
void
purple_conversation_send_confirm_cb
(
gpointer
*
data
)
{
PurpleConversation
*
conv
=
data
[
0
];
char
*
message
=
data
[
1
];
g_free
(
data
);
common_send
(
conv
,
message
,
0
);
}
void
purple_conversation_send_confirm
(
PurpleConversation
*
conv
,
const
char
*
message
)
{
char
*
text
;
gpointer
*
data
;
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
g_return_if_fail
(
priv
!=
NULL
);
g_return_if_fail
(
message
!=
NULL
);
if
(
priv
->
ui_ops
!=
NULL
&&
priv
->
ui_ops
->
send_confirm
!=
NULL
)
{
priv
->
ui_ops
->
send_confirm
(
conv
,
message
);
return
;
}
text
=
g_strdup_printf
(
"You are about to send the following message:
\n
%s"
,
message
);
data
=
g_new0
(
gpointer
,
2
);
data
[
0
]
=
conv
;
data
[
1
]
=
(
gpointer
)
message
;
purple_request_action
(
conv
,
NULL
,
_
(
"Send Message"
),
text
,
0
,
purple_request_cpar_from_account
(
purple_conversation_get_account
(
conv
)),
data
,
2
,
_
(
"_Send Message"
),
G_CALLBACK
(
purple_conversation_send_confirm_cb
),
_
(
"Cancel"
),
NULL
);
}
gboolean
purple_conversation_custom_smiley_add
(
PurpleConversation
*
conv
,
const
char
*
smile
,
const
char
*
cksum_type
,
const
char
*
chksum
,
gboolean
remote
)
{
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
g_return_val_if_fail
(
priv
!=
NULL
,
FALSE
);
if
(
smile
==
NULL
||
!*
smile
)
{
return
FALSE
;
}
/* TODO: check if the icon is in the cache and return false if so */
/* TODO: add an icon cache (that doesn't suck) */
if
(
priv
->
ui_ops
!=
NULL
&&
priv
->
ui_ops
->
custom_smiley_add
!=
NULL
)
{
return
priv
->
ui_ops
->
custom_smiley_add
(
conv
,
smile
,
remote
);
}
else
{
purple_debug_info
(
"conversation"
,
"Could not find add custom smiley function"
);
return
FALSE
;
}
}
void
purple_conversation_custom_smiley_write
(
PurpleConversation
*
conv
,
const
char
*
smile
,
const
guchar
*
data
,
gsize
size
)
{
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
g_return_if_fail
(
priv
!=
NULL
);
g_return_if_fail
(
smile
!=
NULL
&&
*
smile
);
if
(
priv
->
ui_ops
!=
NULL
&&
priv
->
ui_ops
->
custom_smiley_write
!=
NULL
)
priv
->
ui_ops
->
custom_smiley_write
(
conv
,
smile
,
data
,
size
);
else
purple_debug_info
(
"conversation"
,
"Could not find the smiley write function"
);
}
void
purple_conversation_custom_smiley_close
(
PurpleConversation
*
conv
,
const
char
*
smile
)
{
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
g_return_if_fail
(
priv
!=
NULL
);
g_return_if_fail
(
smile
!=
NULL
&&
*
smile
);
if
(
priv
->
ui_ops
!=
NULL
&&
priv
->
ui_ops
->
custom_smiley_close
!=
NULL
)
priv
->
ui_ops
->
custom_smiley_close
(
conv
,
smile
);
else
purple_debug_info
(
"conversation"
,
"Could not find custom smiley close function"
);
}
GList
*
purple_conversation_get_extended_menu
(
PurpleConversation
*
conv
)
{
GList
*
menu
=
NULL
;
g_return_val_if_fail
(
PURPLE_IS_CONVERSATION
(
conv
),
NULL
);
purple_signal_emit
(
purple_conversations_get_handle
(),
"conversation-extended-menu"
,
conv
,
&
menu
);
return
menu
;
}
void
purple_conversation_clear_message_history
(
PurpleConversation
*
conv
)
{
GList
*
list
;
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
g_return_if_fail
(
priv
!=
NULL
);
list
=
priv
->
message_history
;
message_history_free
(
list
);
priv
->
message_history
=
NULL
;
purple_signal_emit
(
purple_conversations_get_handle
(),
"cleared-message-history"
,
conv
);
}
GList
*
purple_conversation_get_message_history
(
PurpleConversation
*
conv
)
{
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
g_return_val_if_fail
(
priv
!=
NULL
,
NULL
);
return
priv
->
message_history
;
}
const
char
*
purple_conversation_message_get_sender
(
const
PurpleConversationMessage
*
msg
)
{
g_return_val_if_fail
(
msg
,
NULL
);
return
msg
->
who
;
}
const
char
*
purple_conversation_message_get_message
(
const
PurpleConversationMessage
*
msg
)
{
g_return_val_if_fail
(
msg
,
NULL
);
return
msg
->
what
;
}
PurpleMessageFlags
purple_conversation_message_get_flags
(
const
PurpleConversationMessage
*
msg
)
{
g_return_val_if_fail
(
msg
,
0
);
return
msg
->
flags
;
}
time_t
purple_conversation_message_get_timestamp
(
const
PurpleConversationMessage
*
msg
)
{
g_return_val_if_fail
(
msg
,
0
);
return
msg
->
when
;
}
const
char
*
purple_conversation_message_get_alias
(
const
PurpleConversationMessage
*
msg
)
{
g_return_val_if_fail
(
msg
,
NULL
);
return
msg
->
alias
;
}
PurpleConversation
*
purple_conversation_message_get_conversation
(
const
PurpleConversationMessage
*
msg
)
{
g_return_val_if_fail
(
msg
,
NULL
);
return
msg
->
conv
;
}
static
PurpleConversationMessage
*
purple_conversation_message_copy
(
PurpleConversationMessage
*
msg
)
{
PurpleConversationMessage
*
newmsg
;
g_return_val_if_fail
(
msg
!=
NULL
,
NULL
);
newmsg
=
g_new
(
PurpleConversationMessage
,
1
);
*
newmsg
=
*
msg
;
newmsg
->
who
=
g_strdup
(
msg
->
who
);
newmsg
->
what
=
g_strdup
(
msg
->
what
);
newmsg
->
alias
=
g_strdup
(
msg
->
alias
);
return
newmsg
;
}
static
void
purple_conversation_message_free
(
PurpleConversationMessage
*
msg
)
{
g_return_if_fail
(
msg
!=
NULL
);
g_free
(
msg
->
who
);
g_free
(
msg
->
what
);
g_free
(
msg
->
alias
);
g_free
(
msg
);
}
GType
purple_conversation_message_get_type
(
void
)
{
static
GType
type
=
0
;
if
(
type
==
0
)
{
type
=
g_boxed_type_register_static
(
"PurpleConversationMessage"
,
(
GBoxedCopyFunc
)
purple_conversation_message_copy
,
(
GBoxedFreeFunc
)
purple_conversation_message_free
);
}
return
type
;
}
void
purple_conversation_set_ui_data
(
PurpleConversation
*
conv
,
gpointer
ui_data
)
{
g_return_if_fail
(
PURPLE_IS_CONVERSATION
(
conv
));
conv
->
ui_data
=
ui_data
;
}
gpointer
purple_conversation_get_ui_data
(
const
PurpleConversation
*
conv
)
{
g_return_val_if_fail
(
PURPLE_IS_CONVERSATION
(
conv
),
NULL
);
return
conv
->
ui_data
;
}
gboolean
purple_conversation_do_command
(
PurpleConversation
*
conv
,
const
gchar
*
cmdline
,
const
gchar
*
markup
,
gchar
**
error
)
{
char
*
mark
=
(
markup
&&
*
markup
)
?
NULL
:
g_markup_escape_text
(
cmdline
,
-1
),
*
err
=
NULL
;
PurpleCmdStatus
status
=
purple_cmd_do_command
(
conv
,
cmdline
,
mark
?
mark
:
markup
,
error
?
error
:
&
err
);
g_free
(
mark
);
g_free
(
err
);
return
(
status
==
PURPLE_CMD_STATUS_OK
);
}
gssize
purple_conversation_get_max_message_size
(
PurpleConversation
*
conv
)
{
PurplePlugin
*
prpl
;
PurplePluginProtocolInfo
*
prpl_info
;
g_return_val_if_fail
(
PURPLE_IS_CONVERSATION
(
conv
),
0
);
prpl
=
purple_connection_get_prpl
(
purple_conversation_get_connection
(
conv
));
g_return_val_if_fail
(
prpl
!=
NULL
,
0
);
prpl_info
=
PURPLE_PLUGIN_PROTOCOL_INFO
(
prpl
);
g_return_val_if_fail
(
prpl_info
!=
NULL
,
0
);
if
(
!
PURPLE_PROTOCOL_PLUGIN_HAS_FUNC
(
prpl_info
,
get_max_message_size
))
return
0
;
return
prpl_info
->
get_max_message_size
(
conv
);
}
/**************************************************************************
* GObject code
**************************************************************************/
/* Set method for GObject properties */
static
void
purple_conversation_set_property
(
GObject
*
obj
,
guint
param_id
,
const
GValue
*
value
,
GParamSpec
*
pspec
)
{
PurpleConversation
*
conv
=
PURPLE_CONVERSATION
(
obj
);
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
switch
(
param_id
)
{
case
PROP_ACCOUNT
:
priv
->
account
=
g_value_get_object
(
value
);
break
;
case
PROP_NAME
:
g_free
(
priv
->
name
);
priv
->
name
=
g_strdup
(
g_value_get_string
(
value
));
break
;
case
PROP_TITLE
:
g_free
(
priv
->
title
);
priv
->
title
=
g_strdup
(
g_value_get_string
(
value
));
break
;
case
PROP_LOGGING
:
purple_conversation_set_logging
(
conv
,
g_value_get_boolean
(
value
));
break
;
case
PROP_FEATURES
:
purple_conversation_set_features
(
conv
,
g_value_get_flags
(
value
));
break
;
default
:
G_OBJECT_WARN_INVALID_PROPERTY_ID
(
obj
,
param_id
,
pspec
);
break
;
}
}
/* Get method for GObject properties */
static
void
purple_conversation_get_property
(
GObject
*
obj
,
guint
param_id
,
GValue
*
value
,
GParamSpec
*
pspec
)
{
PurpleConversation
*
conv
=
PURPLE_CONVERSATION
(
obj
);
switch
(
param_id
)
{
case
PROP_ACCOUNT
:
g_value_set_object
(
value
,
purple_conversation_get_account
(
conv
));
break
;
case
PROP_NAME
:
g_value_set_string
(
value
,
purple_conversation_get_name
(
conv
));
break
;
case
PROP_TITLE
:
g_value_set_string
(
value
,
purple_conversation_get_title
(
conv
));
break
;
case
PROP_LOGGING
:
g_value_set_boolean
(
value
,
purple_conversation_is_logging
(
conv
));
break
;
case
PROP_FEATURES
:
g_value_set_flags
(
value
,
purple_conversation_get_features
(
conv
));
break
;
default
:
G_OBJECT_WARN_INVALID_PROPERTY_ID
(
obj
,
param_id
,
pspec
);
break
;
}
}
/* Called when done constructing */
static
void
purple_conversation_constructed
(
GObject
*
object
)
{
PurpleConversation
*
conv
=
PURPLE_CONVERSATION
(
object
);
PurpleAccount
*
account
;
PurpleConnection
*
gc
;
PurpleConversationUiOps
*
ops
;
parent_class
->
constructed
(
object
);
g_object_get
(
object
,
"account"
,
&
account
,
NULL
);
gc
=
purple_account_get_connection
(
account
);
/* copy features from the connection. */
purple_conversation_set_features
(
conv
,
purple_connection_get_flags
(
gc
));
/* add the conversation to the appropriate lists */
purple_conversations_add
(
conv
);
/* Auto-set the title. */
purple_conversation_autoset_title
(
conv
);
/* Don't move this.. it needs to be one of the last things done otherwise
* it causes mysterious crashes on my system.
* -- Gary
*/
ops
=
purple_conversations_get_ui_ops
();
purple_conversation_set_ui_ops
(
conv
,
ops
);
if
(
ops
!=
NULL
&&
ops
->
create_conversation
!=
NULL
)
ops
->
create_conversation
(
conv
);
purple_signal_emit
(
purple_conversations_get_handle
(),
"conversation-created"
,
conv
);
g_object_unref
(
account
);
}
/* GObject finalize function */
static
void
purple_conversation_finalize
(
GObject
*
object
)
{
PurpleConversation
*
conv
=
PURPLE_CONVERSATION
(
object
);
PurpleConversationPrivate
*
priv
=
PURPLE_CONVERSATION_GET_PRIVATE
(
conv
);
PurpleConversationUiOps
*
ops
=
purple_conversation_get_ui_ops
(
conv
);
purple_request_close_with_handle
(
conv
);
purple_e2ee_state_unref
(
priv
->
e2ee_state
);
priv
->
e2ee_state
=
NULL
;
/* remove from conversations and im/chats lists prior to emit */
purple_conversations_remove
(
conv
);
purple_signal_emit
(
purple_conversations_get_handle
(),
"deleting-conversation"
,
conv
);
purple_conversation_close_logs
(
conv
);
purple_conversation_clear_message_history
(
conv
);
if
(
ops
!=
NULL
&&
ops
->
destroy_conversation
!=
NULL
)
ops
->
destroy_conversation
(
conv
);
g_free
(
priv
->
name
);
g_free
(
priv
->
title
);
priv
->
name
=
NULL
;
priv
->
title
=
NULL
;
parent_class
->
finalize
(
object
);
}
/* Class initializer function */
static
void
purple_conversation_class_init
(
PurpleConversationClass
*
klass
)
{
GObjectClass
*
obj_class
=
G_OBJECT_CLASS
(
klass
);
parent_class
=
g_type_class_peek_parent
(
klass
);
obj_class
->
finalize
=
purple_conversation_finalize
;
obj_class
->
constructed
=
purple_conversation_constructed
;
/* Setup properties */
obj_class
->
get_property
=
purple_conversation_get_property
;
obj_class
->
set_property
=
purple_conversation_set_property
;
g_type_class_add_private
(
klass
,
sizeof
(
PurpleConversationPrivate
));
properties
[
PROP_ACCOUNT
]
=
g_param_spec_object
(
"account"
,
"Account"
,
"The account for the conversation."
,
PURPLE_TYPE_ACCOUNT
,
G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT
|
G_PARAM_STATIC_STRINGS
);
properties
[
PROP_NAME
]
=
g_param_spec_string
(
"name"
,
"Name"
,
"The name of the conversation."
,
NULL
,
G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT
|
G_PARAM_STATIC_STRINGS
);
properties
[
PROP_TITLE
]
=
g_param_spec_string
(
"title"
,
"Title"
,
"The title of the conversation."
,
NULL
,
G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT
|
G_PARAM_STATIC_STRINGS
);
properties
[
PROP_LOGGING
]
=
g_param_spec_boolean
(
"logging"
,
"Logging status"
,
"Whether logging is enabled or not."
,
FALSE
,
G_PARAM_READWRITE
|
G_PARAM_STATIC_STRINGS
);
properties
[
PROP_FEATURES
]
=
g_param_spec_flags
(
"features"
,
"Connection features"
,
"The connection features of the conversation."
,
PURPLE_TYPE_CONNECTION_FLAGS
,
0
,
G_PARAM_READWRITE
|
G_PARAM_STATIC_STRINGS
);
g_object_class_install_properties
(
obj_class
,
PROP_LAST
,
properties
);
}
GType
purple_conversation_get_type
(
void
)
{
static
GType
type
=
0
;
if
(
type
==
0
)
{
static
const
GTypeInfo
info
=
{
sizeof
(
PurpleConversationClass
),
NULL
,
NULL
,
(
GClassInitFunc
)
purple_conversation_class_init
,
NULL
,
NULL
,
sizeof
(
PurpleConversation
),
0
,
NULL
,
NULL
,
};
type
=
g_type_register_static
(
G_TYPE_OBJECT
,
"PurpleConversation"
,
&
info
,
G_TYPE_FLAG_ABSTRACT
);
}
return
type
;
}