pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Fix an error caused by misreading purple_strequal()
2019-11-05, John Bailey
e74e34093dfa
Fix an error caused by misreading purple_strequal()
/*
* purple - Jabber Protocol Plugin
*
* 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
"debug.h"
#include
"notify.h"
#include
"smiley-custom.h"
#include
"smiley-parser.h"
#include
"server.h"
#include
"util.h"
#include
"adhoccommands.h"
#include
"buddy.h"
#include
"chat.h"
#include
"data.h"
#include
"google/google.h"
#include
"message.h"
#include
"xmlnode.h"
#include
"pep.h"
#include
"smiley.h"
#include
"iq.h"
#include
<string.h>
typedef
struct
{
PurpleConversation
*
conv
;
gchar
*
shortcut
;
}
JabberMessageRemoteSmileyAddData
;
static
GString
*
jm_body_with_oob
(
JabberMessage
*
jm
)
{
GList
*
etc
;
GString
*
body
=
g_string_new
(
""
);
if
(
jm
->
xhtml
)
g_string_append
(
body
,
jm
->
xhtml
);
else
if
(
jm
->
body
)
g_string_append
(
body
,
jm
->
body
);
for
(
etc
=
jm
->
etc
;
etc
;
etc
=
etc
->
next
)
{
PurpleXmlNode
*
x
=
etc
->
data
;
const
char
*
xmlns
=
purple_xmlnode_get_namespace
(
x
);
if
(
purple_strequal
(
xmlns
,
NS_OOB_X_DATA
))
{
PurpleXmlNode
*
url
,
*
desc
;
char
*
urltxt
,
*
desctxt
;
url
=
purple_xmlnode_get_child
(
x
,
"url"
);
desc
=
purple_xmlnode_get_child
(
x
,
"desc"
);
if
(
!
url
)
continue
;
urltxt
=
purple_xmlnode_get_data
(
url
);
desctxt
=
desc
?
purple_xmlnode_get_data
(
desc
)
:
urltxt
;
if
(
body
->
len
&&
!
purple_strequal
(
body
->
str
,
urltxt
))
g_string_append_printf
(
body
,
"<br/><a href='%s'>%s</a>"
,
urltxt
,
desctxt
);
else
g_string_printf
(
body
,
"<a href='%s'>%s</a>"
,
urltxt
,
desctxt
);
g_free
(
urltxt
);
if
(
desctxt
!=
urltxt
)
g_free
(
desctxt
);
}
}
return
body
;
}
void
jabber_message_free
(
JabberMessage
*
jm
)
{
g_free
(
jm
->
from
);
g_free
(
jm
->
to
);
g_free
(
jm
->
id
);
g_free
(
jm
->
subject
);
g_free
(
jm
->
body
);
g_free
(
jm
->
xhtml
);
g_free
(
jm
->
password
);
g_free
(
jm
->
error
);
g_free
(
jm
->
thread_id
);
g_list_free
(
jm
->
etc
);
g_list_free
(
jm
->
eventitems
);
g_free
(
jm
);
}
static
void
handle_chat
(
JabberMessage
*
jm
)
{
JabberID
*
jid
=
jabber_id_new
(
jm
->
from
);
PurpleConnection
*
gc
;
PurpleAccount
*
account
;
JabberBuddy
*
jb
;
JabberBuddyResource
*
jbr
;
GString
*
body
;
if
(
!
jid
)
return
;
gc
=
jm
->
js
->
gc
;
account
=
purple_connection_get_account
(
gc
);
jb
=
jabber_buddy_find
(
jm
->
js
,
jm
->
from
,
TRUE
);
jbr
=
jabber_buddy_find_resource
(
jb
,
jid
->
resource
);
if
(
jbr
&&
jm
->
chat_state
!=
JM_STATE_NONE
)
jbr
->
chat_states
=
JABBER_CHAT_STATES_SUPPORTED
;
switch
(
jm
->
chat_state
)
{
case
JM_STATE_COMPOSING
:
purple_serv_got_typing
(
gc
,
jm
->
from
,
0
,
PURPLE_IM_TYPING
);
break
;
case
JM_STATE_PAUSED
:
purple_serv_got_typing
(
gc
,
jm
->
from
,
0
,
PURPLE_IM_TYPED
);
break
;
case
JM_STATE_GONE
:
{
PurpleIMConversation
*
im
=
purple_conversations_find_im_with_account
(
jm
->
from
,
account
);
if
(
im
&&
jid
->
node
&&
jid
->
domain
)
{
char
buf
[
256
];
PurpleBuddy
*
buddy
;
g_snprintf
(
buf
,
sizeof
(
buf
),
"%s@%s"
,
jid
->
node
,
jid
->
domain
);
if
((
buddy
=
purple_blist_find_buddy
(
account
,
buf
)))
{
const
char
*
who
;
char
*
escaped
;
who
=
purple_buddy_get_alias
(
buddy
);
escaped
=
g_markup_escape_text
(
who
,
-1
);
g_snprintf
(
buf
,
sizeof
(
buf
),
_
(
"%s has left the conversation."
),
escaped
);
g_free
(
escaped
);
/* At some point when we restructure PurpleConversation,
* this should be able to be implemented by removing the
* user from the conversation like we do with chats now. */
purple_conversation_write_system_message
(
PURPLE_CONVERSATION
(
im
),
buf
,
0
);
}
}
purple_serv_got_typing_stopped
(
gc
,
jm
->
from
);
break
;
}
default
:
purple_serv_got_typing_stopped
(
gc
,
jm
->
from
);
}
if
(
jm
->
js
->
googletalk
&&
jm
->
body
&&
jm
->
xhtml
==
NULL
)
{
char
*
tmp
=
jm
->
body
;
jm
->
body
=
jabber_google_format_to_html
(
jm
->
body
);
g_free
(
tmp
);
}
body
=
jm_body_with_oob
(
jm
);
if
(
body
&&
body
->
len
)
{
if
(
jid
->
resource
)
{
/*
* We received a message from a specific resource, so
* we probably want a reply to go to this specific
* resource (i.e. bind/lock the conversation to this
* resource).
*
* This works because purple_im_conversation_send gets the name
* from purple_conversation_get_name()
*/
PurpleIMConversation
*
im
;
im
=
purple_conversations_find_im_with_account
(
jm
->
from
,
account
);
if
(
im
&&
!
purple_strequal
(
jm
->
from
,
purple_conversation_get_name
(
PURPLE_CONVERSATION
(
im
))))
{
purple_debug_info
(
"jabber"
,
"Binding conversation to %s
\n
"
,
jm
->
from
);
purple_conversation_set_name
(
PURPLE_CONVERSATION
(
im
),
jm
->
from
);
}
}
if
(
jbr
)
{
/* Treat SUPPORTED as a terminal with no escape :) */
if
(
jbr
->
chat_states
!=
JABBER_CHAT_STATES_SUPPORTED
)
{
if
(
jm
->
chat_state
!=
JM_STATE_NONE
)
jbr
->
chat_states
=
JABBER_CHAT_STATES_SUPPORTED
;
else
jbr
->
chat_states
=
JABBER_CHAT_STATES_UNSUPPORTED
;
}
g_free
(
jbr
->
thread_id
);
jbr
->
thread_id
=
g_strdup
(
jm
->
thread_id
);
}
purple_serv_got_im
(
gc
,
jm
->
from
,
body
->
str
,
0
,
jm
->
sent
);
}
jabber_id_free
(
jid
);
if
(
body
)
g_string_free
(
body
,
TRUE
);
}
static
void
handle_headline
(
JabberMessage
*
jm
)
{
char
*
title
;
GString
*
body
;
if
(
!
jm
->
xhtml
&&
!
jm
->
body
)
return
;
/* ignore headlines without any content */
body
=
jm_body_with_oob
(
jm
);
title
=
g_strdup_printf
(
_
(
"Message from %s"
),
jm
->
from
);
purple_notify_formatted
(
jm
->
js
->
gc
,
title
,
jm
->
subject
?
jm
->
subject
:
title
,
NULL
,
body
->
str
,
NULL
,
NULL
);
g_free
(
title
);
g_string_free
(
body
,
TRUE
);
}
static
void
handle_groupchat
(
JabberMessage
*
jm
)
{
JabberID
*
jid
=
jabber_id_new
(
jm
->
from
);
JabberChat
*
chat
;
PurpleMessageFlags
messageFlags
=
0
;
if
(
!
jid
)
return
;
chat
=
jabber_chat_find
(
jm
->
js
,
jid
->
node
,
jid
->
domain
);
if
(
!
chat
)
return
;
if
(
jm
->
subject
)
{
purple_chat_conversation_set_topic
(
chat
->
conv
,
jid
->
resource
,
jm
->
subject
);
messageFlags
|=
PURPLE_MESSAGE_NO_LOG
;
if
(
!
jm
->
xhtml
&&
!
jm
->
body
)
{
char
*
msg
,
*
tmp
,
*
tmp2
;
tmp
=
g_markup_escape_text
(
jm
->
subject
,
-1
);
tmp2
=
purple_markup_linkify
(
tmp
);
if
(
jid
->
resource
)
msg
=
g_strdup_printf
(
_
(
"%s has set the topic to: %s"
),
jid
->
resource
,
tmp2
);
else
msg
=
g_strdup_printf
(
_
(
"The topic is: %s"
),
tmp2
);
purple_conversation_write_system_message
(
PURPLE_CONVERSATION
(
chat
->
conv
),
msg
,
messageFlags
);
g_free
(
tmp
);
g_free
(
tmp2
);
g_free
(
msg
);
}
}
if
(
jm
->
xhtml
||
jm
->
body
)
{
if
(
jid
->
resource
)
purple_serv_got_chat_in
(
jm
->
js
->
gc
,
chat
->
id
,
jid
->
resource
,
messageFlags
|
(
jm
->
delayed
?
PURPLE_MESSAGE_DELAYED
:
0
),
jm
->
xhtml
?
jm
->
xhtml
:
jm
->
body
,
jm
->
sent
);
else
if
(
chat
->
muc
)
purple_conversation_write_system_message
(
PURPLE_CONVERSATION
(
chat
->
conv
),
jm
->
xhtml
?
jm
->
xhtml
:
jm
->
body
,
messageFlags
);
}
jabber_id_free
(
jid
);
}
static
void
handle_groupchat_invite
(
JabberMessage
*
jm
)
{
GHashTable
*
components
;
JabberID
*
jid
=
jabber_id_new
(
jm
->
to
);
if
(
!
jid
)
return
;
components
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
NULL
,
g_free
);
g_hash_table_replace
(
components
,
"room"
,
g_strdup
(
jid
->
node
));
g_hash_table_replace
(
components
,
"server"
,
g_strdup
(
jid
->
domain
));
g_hash_table_replace
(
components
,
"handle"
,
g_strdup
(
jm
->
js
->
user
->
node
));
g_hash_table_replace
(
components
,
"password"
,
g_strdup
(
jm
->
password
));
jabber_id_free
(
jid
);
purple_serv_got_chat_invite
(
jm
->
js
->
gc
,
jm
->
to
,
jm
->
from
,
jm
->
body
,
components
);
}
static
void
handle_error
(
JabberMessage
*
jm
)
{
char
*
buf
;
if
(
!
jm
->
body
)
return
;
buf
=
g_strdup_printf
(
_
(
"Message delivery to %s failed: %s"
),
jm
->
from
,
jm
->
error
?
jm
->
error
:
""
);
purple_notify_formatted
(
jm
->
js
->
gc
,
_
(
"XMPP Message Error"
),
_
(
"XMPP Message Error"
),
buf
,
jm
->
xhtml
?
jm
->
xhtml
:
jm
->
body
,
NULL
,
NULL
);
g_free
(
buf
);
}
static
void
handle_buzz
(
JabberMessage
*
jm
)
{
PurpleAccount
*
account
;
/* Delayed buzz MUST NOT be accepted */
if
(
jm
->
delayed
)
return
;
/* Reject buzz when it's not enabled */
if
(
!
jm
->
js
->
allowBuzz
)
return
;
account
=
purple_connection_get_account
(
jm
->
js
->
gc
);
if
(
purple_blist_find_buddy
(
account
,
jm
->
from
)
==
NULL
)
return
;
/* Do not accept buzzes from unknown people */
/* xmpp only has 1 attention type, so index is 0 */
purple_protocol_got_attention
(
jm
->
js
->
gc
,
jm
->
from
,
0
);
}
/* used internally by the functions below */
typedef
struct
{
gchar
*
cid
;
gchar
*
alt
;
}
JabberSmileyRef
;
static
void
jabber_message_get_refs_from_xmlnode_internal
(
const
PurpleXmlNode
*
message
,
GHashTable
*
table
)
{
PurpleXmlNode
*
child
;
for
(
child
=
purple_xmlnode_get_child
(
message
,
"img"
)
;
child
;
child
=
purple_xmlnode_get_next_twin
(
child
))
{
const
gchar
*
src
=
purple_xmlnode_get_attrib
(
child
,
"src"
);
if
(
g_str_has_prefix
(
src
,
"cid:"
))
{
const
gchar
*
cid
=
src
+
4
;
/* if we haven't "fetched" this yet... */
if
(
!
g_hash_table_lookup
(
table
,
cid
))
{
/* take a copy of the cid and let the SmileyRef own it... */
gchar
*
temp_cid
=
g_strdup
(
cid
);
JabberSmileyRef
*
ref
=
g_new0
(
JabberSmileyRef
,
1
);
const
gchar
*
alt
=
purple_xmlnode_get_attrib
(
child
,
"alt"
);
ref
->
cid
=
temp_cid
;
/* if there is no "alt" string, use the cid...
include the entire src, eg. "cid:.." to avoid linkification */
if
(
alt
&&
alt
[
0
]
!=
'\0'
)
{
/* workaround for when "alt" is set to the value of the
CID (which Jabbim seems to do), to avoid it showing up
as an mailto: link */
if
(
purple_email_is_valid
(
alt
))
{
ref
->
alt
=
g_strdup_printf
(
"smiley:%s"
,
alt
);
}
else
{
ref
->
alt
=
g_strdup
(
alt
);
}
}
else
{
ref
->
alt
=
g_strdup
(
src
);
}
g_hash_table_insert
(
table
,
temp_cid
,
ref
);
}
}
}
for
(
child
=
message
->
child
;
child
;
child
=
child
->
next
)
{
jabber_message_get_refs_from_xmlnode_internal
(
child
,
table
);
}
}
static
gboolean
jabber_message_get_refs_steal
(
gpointer
key
,
gpointer
value
,
gpointer
user_data
)
{
GList
**
refs
=
(
GList
**
)
user_data
;
JabberSmileyRef
*
ref
=
(
JabberSmileyRef
*
)
value
;
*
refs
=
g_list_append
(
*
refs
,
ref
);
return
TRUE
;
}
static
GList
*
jabber_message_get_refs_from_xmlnode
(
const
PurpleXmlNode
*
message
)
{
GList
*
refs
=
NULL
;
GHashTable
*
unique_refs
=
g_hash_table_new
(
g_str_hash
,
g_str_equal
);
jabber_message_get_refs_from_xmlnode_internal
(
message
,
unique_refs
);
(
void
)
g_hash_table_foreach_steal
(
unique_refs
,
jabber_message_get_refs_steal
,
(
gpointer
)
&
refs
);
g_hash_table_destroy
(
unique_refs
);
return
refs
;
}
static
gchar
*
jabber_message_xml_to_string_strip_img_smileys
(
PurpleXmlNode
*
xhtml
)
{
gchar
*
markup
=
purple_xmlnode_to_str
(
xhtml
,
NULL
);
int
len
=
strlen
(
markup
);
int
pos
=
0
;
GString
*
out
=
g_string_new
(
NULL
);
while
(
pos
<
len
)
{
/* this is a bit cludgy, maybe there is a better way to do this...
we need to find all <img> tags within the XHTML and replace those
tags with the value of their "alt" attributes */
if
(
g_str_has_prefix
(
&
(
markup
[
pos
]),
"<img"
))
{
PurpleXmlNode
*
img
=
NULL
;
int
pos2
=
pos
;
const
gchar
*
src
;
for
(;
pos2
<
len
;
pos2
++
)
{
if
(
g_str_has_prefix
(
&
(
markup
[
pos2
]),
"/>"
))
{
pos2
+=
2
;
break
;
}
else
if
(
g_str_has_prefix
(
&
(
markup
[
pos2
]),
"</img>"
))
{
pos2
+=
5
;
break
;
}
}
/* note, if the above loop didn't find the end of the <img> tag,
it the parsed string will be until the end of the input string,
in which case purple_xmlnode_from_str will bail out and return NULL,
in this case the "if" statement below doesn't trigger and the
text is copied unchanged */
img
=
purple_xmlnode_from_str
(
&
(
markup
[
pos
]),
pos2
-
pos
);
src
=
purple_xmlnode_get_attrib
(
img
,
"src"
);
if
(
g_str_has_prefix
(
src
,
"cid:"
))
{
const
gchar
*
alt
=
purple_xmlnode_get_attrib
(
img
,
"alt"
);
/* if the "alt" attribute is empty, put the cid as smiley string */
if
(
alt
&&
alt
[
0
]
!=
'\0'
)
{
/* if the "alt" is the same as the CID, as Jabbim does,
this prevents linkification... */
if
(
purple_email_is_valid
(
alt
))
{
gchar
*
safe_alt
=
g_strdup_printf
(
"smiley:%s"
,
alt
);
out
=
g_string_append
(
out
,
safe_alt
);
g_free
(
safe_alt
);
}
else
{
gchar
*
alt_escaped
=
g_markup_escape_text
(
alt
,
-1
);
out
=
g_string_append
(
out
,
alt_escaped
);
g_free
(
alt_escaped
);
}
}
else
{
out
=
g_string_append
(
out
,
src
);
}
pos
+=
pos2
-
pos
;
}
else
{
out
=
g_string_append_c
(
out
,
markup
[
pos
]);
pos
++
;
}
purple_xmlnode_free
(
img
);
}
else
{
out
=
g_string_append_c
(
out
,
markup
[
pos
]);
pos
++
;
}
}
g_free
(
markup
);
return
g_string_free
(
out
,
FALSE
);
}
static
void
jabber_message_add_remote_smileys
(
JabberStream
*
js
,
const
gchar
*
who
,
const
PurpleXmlNode
*
message
)
{
PurpleXmlNode
*
data_tag
;
for
(
data_tag
=
purple_xmlnode_get_child_with_namespace
(
message
,
"data"
,
NS_BOB
)
;
data_tag
;
data_tag
=
purple_xmlnode_get_next_twin
(
data_tag
))
{
const
gchar
*
cid
=
purple_xmlnode_get_attrib
(
data_tag
,
"cid"
);
const
JabberData
*
data
=
jabber_data_find_remote_by_cid
(
js
,
who
,
cid
);
if
(
!
data
&&
cid
!=
NULL
)
{
/* we haven't cached this already, let's add it */
JabberData
*
new_data
=
jabber_data_create_from_xml
(
data_tag
);
if
(
new_data
)
{
jabber_data_associate_remote
(
js
,
who
,
new_data
);
}
}
}
}
static
void
jabber_message_remote_smiley_got
(
JabberData
*
jdata
,
gchar
*
alt
,
gpointer
d
)
{
JabberMessageRemoteSmileyAddData
*
data
=
(
JabberMessageRemoteSmileyAddData
*
)
d
;
if
(
jdata
)
{
PurpleSmiley
*
smiley
=
NULL
;
purple_debug_info
(
"jabber"
,
"smiley data retrieved successfully"
);
smiley
=
purple_smiley_new_from_data
(
data
->
shortcut
,
jabber_data_get_data
(
jdata
),
jabber_data_get_size
(
jdata
)
);
purple_conversation_add_smiley
(
data
->
conv
,
smiley
);
g_object_unref
(
G_OBJECT
(
smiley
));
}
else
{
purple_debug_error
(
"jabber"
,
"failed retrieving smiley data"
);
}
g_object_unref
(
G_OBJECT
(
data
->
conv
));
g_free
(
data
->
shortcut
);
g_slice_free
(
JabberMessageRemoteSmileyAddData
,
data
);
}
static
void
jabber_message_remote_smiley_add
(
JabberStream
*
js
,
PurpleConversation
*
conv
,
const
gchar
*
from
,
const
gchar
*
shortcut
,
const
gchar
*
cid
)
{
PurpleSmiley
*
smiley
=
NULL
;
const
JabberData
*
jdata
=
NULL
;
purple_debug_misc
(
"jabber"
,
"about to add remote smiley %s to the conv"
,
shortcut
);
smiley
=
purple_conversation_get_smiley
(
conv
,
shortcut
);
if
(
PURPLE_IS_SMILEY
(
smiley
))
{
purple_debug_misc
(
"jabber"
,
"smiley was already present"
);
return
;
}
/* TODO: cache lookup by "cid" */
jdata
=
jabber_data_find_remote_by_cid
(
js
,
from
,
cid
);
if
(
jdata
)
{
purple_debug_info
(
"jabber"
,
"smiley data is already known"
);
smiley
=
purple_smiley_new_from_data
(
shortcut
,
jabber_data_get_data
(
jdata
),
jabber_data_get_size
(
jdata
)
);
purple_conversation_add_smiley
(
conv
,
smiley
);
g_object_unref
(
G_OBJECT
(
smiley
));
}
else
{
JabberMessageRemoteSmileyAddData
*
data
=
NULL
;
data
=
g_slice_new
(
JabberMessageRemoteSmileyAddData
);
data
->
conv
=
g_object_ref
(
conv
);
data
->
shortcut
=
g_strdup
(
shortcut
);
purple_debug_info
(
"jabber"
,
"smiley data is unknown, "
"need to request it"
);
jabber_data_request
(
js
,
cid
,
from
,
NULL
,
FALSE
,
jabber_message_remote_smiley_got
,
data
);
}
}
void
jabber_message_parse
(
JabberStream
*
js
,
PurpleXmlNode
*
packet
)
{
JabberMessage
*
jm
;
const
char
*
id
,
*
from
,
*
to
,
*
type
;
PurpleXmlNode
*
child
;
gboolean
signal_return
;
from
=
purple_xmlnode_get_attrib
(
packet
,
"from"
);
id
=
purple_xmlnode_get_attrib
(
packet
,
"id"
);
to
=
purple_xmlnode_get_attrib
(
packet
,
"to"
);
type
=
purple_xmlnode_get_attrib
(
packet
,
"type"
);
signal_return
=
GPOINTER_TO_INT
(
purple_signal_emit_return_1
(
purple_connection_get_protocol
(
js
->
gc
),
"jabber-receiving-message"
,
js
->
gc
,
type
,
id
,
from
,
to
,
packet
));
if
(
signal_return
)
return
;
jm
=
g_new0
(
JabberMessage
,
1
);
jm
->
js
=
js
;
jm
->
sent
=
time
(
NULL
);
jm
->
delayed
=
FALSE
;
jm
->
chat_state
=
JM_STATE_NONE
;
if
(
type
)
{
if
(
purple_strequal
(
type
,
"normal"
))
jm
->
type
=
JABBER_MESSAGE_NORMAL
;
else
if
(
purple_strequal
(
type
,
"chat"
))
jm
->
type
=
JABBER_MESSAGE_CHAT
;
else
if
(
purple_strequal
(
type
,
"groupchat"
))
jm
->
type
=
JABBER_MESSAGE_GROUPCHAT
;
else
if
(
purple_strequal
(
type
,
"headline"
))
jm
->
type
=
JABBER_MESSAGE_HEADLINE
;
else
if
(
purple_strequal
(
type
,
"error"
))
jm
->
type
=
JABBER_MESSAGE_ERROR
;
else
jm
->
type
=
JABBER_MESSAGE_OTHER
;
}
else
{
jm
->
type
=
JABBER_MESSAGE_NORMAL
;
}
jm
->
from
=
g_strdup
(
from
);
jm
->
to
=
g_strdup
(
to
);
jm
->
id
=
g_strdup
(
id
);
for
(
child
=
packet
->
child
;
child
;
child
=
child
->
next
)
{
const
char
*
xmlns
=
purple_xmlnode_get_namespace
(
child
);
if
(
child
->
type
!=
PURPLE_XMLNODE_TYPE_TAG
)
continue
;
if
(
purple_strequal
(
child
->
name
,
"error"
))
{
const
char
*
code
=
purple_xmlnode_get_attrib
(
child
,
"code"
);
char
*
code_txt
=
NULL
;
char
*
text
=
purple_xmlnode_get_data
(
child
);
if
(
!
text
)
{
PurpleXmlNode
*
enclosed_text_node
;
if
((
enclosed_text_node
=
purple_xmlnode_get_child
(
child
,
"text"
)))
text
=
purple_xmlnode_get_data
(
enclosed_text_node
);
}
if
(
code
)
code_txt
=
g_strdup_printf
(
_
(
"(Code %s)"
),
code
);
if
(
!
jm
->
error
)
jm
->
error
=
g_strdup_printf
(
"%s%s%s"
,
text
?
text
:
""
,
text
&&
code_txt
?
" "
:
""
,
code_txt
?
code_txt
:
""
);
g_free
(
code_txt
);
g_free
(
text
);
}
else
if
(
xmlns
==
NULL
)
{
/* QuLogic: Not certain this is correct, but it would have happened
with the previous code. */
if
(
purple_strequal
(
child
->
name
,
"x"
))
jm
->
etc
=
g_list_append
(
jm
->
etc
,
child
);
/* The following tests expect xmlns != NULL */
continue
;
}
else
if
(
purple_strequal
(
child
->
name
,
"subject"
)
&&
purple_strequal
(
xmlns
,
NS_XMPP_CLIENT
))
{
if
(
!
jm
->
subject
)
{
jm
->
subject
=
purple_xmlnode_get_data
(
child
);
if
(
!
jm
->
subject
)
jm
->
subject
=
g_strdup
(
""
);
}
}
else
if
(
purple_strequal
(
child
->
name
,
"thread"
)
&&
purple_strequal
(
xmlns
,
NS_XMPP_CLIENT
))
{
if
(
!
jm
->
thread_id
)
jm
->
thread_id
=
purple_xmlnode_get_data
(
child
);
}
else
if
(
purple_strequal
(
child
->
name
,
"body"
)
&&
purple_strequal
(
xmlns
,
NS_XMPP_CLIENT
))
{
if
(
!
jm
->
body
)
{
char
*
msg
=
purple_xmlnode_get_data
(
child
);
char
*
escaped
=
purple_markup_escape_text
(
msg
,
-1
);
jm
->
body
=
purple_strdup_withhtml
(
escaped
);
g_free
(
escaped
);
g_free
(
msg
);
}
}
else
if
(
purple_strequal
(
child
->
name
,
"html"
)
&&
purple_strequal
(
xmlns
,
NS_XHTML_IM
))
{
if
(
!
jm
->
xhtml
&&
purple_xmlnode_get_child
(
child
,
"body"
))
{
char
*
c
;
PurpleConnection
*
gc
=
js
->
gc
;
PurpleAccount
*
account
=
purple_connection_get_account
(
gc
);
PurpleConversation
*
conv
=
NULL
;
GList
*
smiley_refs
=
NULL
,
*
it
;
gchar
*
reformatted_xhtml
;
if
(
purple_account_get_bool
(
account
,
"custom_smileys"
,
TRUE
))
{
/* find a list of smileys ("cid" and "alt" text pairs)
occuring in the message */
smiley_refs
=
jabber_message_get_refs_from_xmlnode
(
child
);
purple_debug_info
(
"jabber"
,
"found %d smileys
\n
"
,
g_list_length
(
smiley_refs
));
if
(
smiley_refs
)
{
if
(
jm
->
type
==
JABBER_MESSAGE_GROUPCHAT
)
{
JabberID
*
jid
=
jabber_id_new
(
jm
->
from
);
JabberChat
*
chat
=
NULL
;
if
(
jid
)
{
chat
=
jabber_chat_find
(
js
,
jid
->
node
,
jid
->
domain
);
if
(
chat
)
conv
=
PURPLE_CONVERSATION
(
chat
->
conv
);
jabber_id_free
(
jid
);
}
}
else
if
(
jm
->
type
==
JABBER_MESSAGE_NORMAL
||
jm
->
type
==
JABBER_MESSAGE_CHAT
)
{
conv
=
purple_conversations_find_with_account
(
from
,
account
);
if
(
!
conv
)
{
/* we need to create the conversation here */
conv
=
PURPLE_CONVERSATION
(
purple_im_conversation_new
(
account
,
from
));
}
}
}
/* process any newly provided smileys */
jabber_message_add_remote_smileys
(
js
,
to
,
packet
);
}
purple_xmlnode_strip_prefixes
(
child
);
/* reformat xhtml so that img tags with a "cid:" src gets
translated to the bare text of the emoticon (the "alt" attrib) */
/* this is done also when custom smiley retrieval is turned off,
this way the receiver always sees the shortcut instead */
reformatted_xhtml
=
jabber_message_xml_to_string_strip_img_smileys
(
child
);
jm
->
xhtml
=
reformatted_xhtml
;
/* add known custom emoticons to the conversation */
/* note: if there were no smileys in the incoming message, or
if receiving custom smileys is turned off, smiley_refs will
be NULL */
for
(
it
=
smiley_refs
;
it
;
it
=
g_list_next
(
it
))
{
JabberSmileyRef
*
ref
=
it
->
data
;
if
(
conv
)
{
jabber_message_remote_smiley_add
(
js
,
conv
,
from
,
ref
->
alt
,
ref
->
cid
);
}
g_free
(
ref
->
cid
);
g_free
(
ref
->
alt
);
g_free
(
ref
);
}
g_list_free
(
smiley_refs
);
/* Convert all newlines to whitespace. Technically, even regular, non-XML HTML is supposed to ignore newlines, but Pidgin has, as convention
* treated \n as a newline for compatibility with other protocols
*/
for
(
c
=
jm
->
xhtml
;
*
c
!=
'\0'
;
c
++
)
{
if
(
*
c
==
'\n'
)
*
c
=
' '
;
}
}
}
else
if
(
purple_strequal
(
child
->
name
,
"active"
)
&&
purple_strequal
(
xmlns
,
"http://jabber.org/protocol/chatstates"
))
{
jm
->
chat_state
=
JM_STATE_ACTIVE
;
}
else
if
(
purple_strequal
(
child
->
name
,
"composing"
)
&&
purple_strequal
(
xmlns
,
"http://jabber.org/protocol/chatstates"
))
{
jm
->
chat_state
=
JM_STATE_COMPOSING
;
}
else
if
(
purple_strequal
(
child
->
name
,
"paused"
)
&&
purple_strequal
(
xmlns
,
"http://jabber.org/protocol/chatstates"
))
{
jm
->
chat_state
=
JM_STATE_PAUSED
;
}
else
if
(
purple_strequal
(
child
->
name
,
"inactive"
)
&&
purple_strequal
(
xmlns
,
"http://jabber.org/protocol/chatstates"
))
{
jm
->
chat_state
=
JM_STATE_INACTIVE
;
}
else
if
(
purple_strequal
(
child
->
name
,
"gone"
)
&&
purple_strequal
(
xmlns
,
"http://jabber.org/protocol/chatstates"
))
{
jm
->
chat_state
=
JM_STATE_GONE
;
}
else
if
(
purple_strequal
(
child
->
name
,
"event"
)
&&
purple_strequal
(
xmlns
,
"http://jabber.org/protocol/pubsub#event"
))
{
PurpleXmlNode
*
items
;
jm
->
type
=
JABBER_MESSAGE_EVENT
;
for
(
items
=
purple_xmlnode_get_child
(
child
,
"items"
);
items
;
items
=
items
->
next
)
jm
->
eventitems
=
g_list_append
(
jm
->
eventitems
,
items
);
}
else
if
(
purple_strequal
(
child
->
name
,
"attention"
)
&&
purple_strequal
(
xmlns
,
NS_ATTENTION
))
{
jm
->
hasBuzz
=
TRUE
;
}
else
if
(
purple_strequal
(
child
->
name
,
"delay"
)
&&
purple_strequal
(
xmlns
,
NS_DELAYED_DELIVERY
))
{
const
char
*
timestamp
=
purple_xmlnode_get_attrib
(
child
,
"stamp"
);
jm
->
delayed
=
TRUE
;
if
(
timestamp
)
jm
->
sent
=
purple_str_to_time
(
timestamp
,
TRUE
,
NULL
,
NULL
,
NULL
);
}
else
if
(
purple_strequal
(
child
->
name
,
"x"
))
{
if
(
purple_strequal
(
xmlns
,
NS_DELAYED_DELIVERY_LEGACY
))
{
const
char
*
timestamp
=
purple_xmlnode_get_attrib
(
child
,
"stamp"
);
jm
->
delayed
=
TRUE
;
if
(
timestamp
)
jm
->
sent
=
purple_str_to_time
(
timestamp
,
TRUE
,
NULL
,
NULL
,
NULL
);
}
else
if
(
purple_strequal
(
xmlns
,
"jabber:x:conference"
)
&&
jm
->
type
!=
JABBER_MESSAGE_GROUPCHAT_INVITE
&&
jm
->
type
!=
JABBER_MESSAGE_ERROR
)
{
const
char
*
jid
=
purple_xmlnode_get_attrib
(
child
,
"jid"
);
if
(
jid
)
{
const
char
*
reason
=
purple_xmlnode_get_attrib
(
child
,
"reason"
);
const
char
*
password
=
purple_xmlnode_get_attrib
(
child
,
"password"
);
jm
->
type
=
JABBER_MESSAGE_GROUPCHAT_INVITE
;
g_free
(
jm
->
to
);
jm
->
to
=
g_strdup
(
jid
);
if
(
reason
)
{
g_free
(
jm
->
body
);
jm
->
body
=
g_strdup
(
reason
);
}
if
(
password
)
{
g_free
(
jm
->
password
);
jm
->
password
=
g_strdup
(
password
);
}
}
}
else
if
(
purple_strequal
(
xmlns
,
"http://jabber.org/protocol/muc#user"
)
&&
jm
->
type
!=
JABBER_MESSAGE_ERROR
)
{
PurpleXmlNode
*
invite
=
purple_xmlnode_get_child
(
child
,
"invite"
);
if
(
invite
)
{
PurpleXmlNode
*
reason
,
*
password
;
const
char
*
jid
=
purple_xmlnode_get_attrib
(
invite
,
"from"
);
g_free
(
jm
->
to
);
jm
->
to
=
jm
->
from
;
jm
->
from
=
g_strdup
(
jid
);
if
((
reason
=
purple_xmlnode_get_child
(
invite
,
"reason"
)))
{
g_free
(
jm
->
body
);
jm
->
body
=
purple_xmlnode_get_data
(
reason
);
}
if
((
password
=
purple_xmlnode_get_child
(
child
,
"password"
)))
{
g_free
(
jm
->
password
);
jm
->
password
=
purple_xmlnode_get_data
(
password
);
}
jm
->
type
=
JABBER_MESSAGE_GROUPCHAT_INVITE
;
}
}
else
{
jm
->
etc
=
g_list_append
(
jm
->
etc
,
child
);
}
}
else
if
(
purple_strequal
(
child
->
name
,
"query"
))
{
const
char
*
node
=
purple_xmlnode_get_attrib
(
child
,
"node"
);
if
(
purple_strequal
(
xmlns
,
NS_DISCO_ITEMS
)
&&
purple_strequal
(
node
,
"http://jabber.org/protocol/commands"
))
{
jabber_adhoc_got_list
(
js
,
jm
->
from
,
child
);
}
}
}
if
(
jm
->
hasBuzz
)
handle_buzz
(
jm
);
switch
(
jm
->
type
)
{
case
JABBER_MESSAGE_OTHER
:
purple_debug_info
(
"jabber"
,
"Received message of unknown type: %s
\n
"
,
type
);
/* Fall-through is intentional */
case
JABBER_MESSAGE_NORMAL
:
case
JABBER_MESSAGE_CHAT
:
handle_chat
(
jm
);
break
;
case
JABBER_MESSAGE_HEADLINE
:
handle_headline
(
jm
);
break
;
case
JABBER_MESSAGE_GROUPCHAT
:
handle_groupchat
(
jm
);
break
;
case
JABBER_MESSAGE_GROUPCHAT_INVITE
:
handle_groupchat_invite
(
jm
);
break
;
case
JABBER_MESSAGE_EVENT
:
jabber_handle_event
(
jm
);
break
;
case
JABBER_MESSAGE_ERROR
:
handle_error
(
jm
);
break
;
}
jabber_message_free
(
jm
);
}
static
gboolean
jabber_conv_support_custom_smileys
(
JabberStream
*
js
,
PurpleConversation
*
conv
,
const
gchar
*
who
)
{
JabberBuddy
*
jb
;
JabberChat
*
chat
;
if
(
PURPLE_IS_IM_CONVERSATION
(
conv
))
{
jb
=
jabber_buddy_find
(
js
,
who
,
FALSE
);
if
(
jb
)
{
return
jabber_buddy_has_capability
(
jb
,
NS_BOB
);
}
else
{
return
FALSE
;
}
}
else
if
(
PURPLE_IS_CHAT_CONVERSATION
(
conv
))
{
chat
=
jabber_chat_find_by_conv
(
PURPLE_CHAT_CONVERSATION
(
conv
));
if
(
chat
)
{
/* do not attempt to send custom smileys in a MUC with more than
10 people, to avoid getting too many BoB requests */
return
jabber_chat_get_num_participants
(
chat
)
<=
10
&&
jabber_chat_all_participants_have_capability
(
chat
,
NS_BOB
);
}
else
{
return
FALSE
;
}
}
else
{
return
FALSE
;
}
}
static
gboolean
jabber_message_smileyify_cb
(
GString
*
out
,
PurpleSmiley
*
smiley
,
PurpleConversation
*
_empty
,
gpointer
_unused
)
{
const
gchar
*
shortcut
;
const
JabberData
*
data
;
PurpleXmlNode
*
smiley_node
;
gchar
*
node_xml
;
shortcut
=
purple_smiley_get_shortcut
(
smiley
);
data
=
jabber_data_find_local_by_alt
(
shortcut
);
if
(
!
data
)
return
FALSE
;
smiley_node
=
jabber_data_get_xhtml_im
(
data
,
shortcut
);
node_xml
=
purple_xmlnode_to_str
(
smiley_node
,
NULL
);
g_string_append
(
out
,
node_xml
);
purple_xmlnode_free
(
smiley_node
);
g_free
(
node_xml
);
return
TRUE
;
}
static
char
*
jabber_message_smileyfy_xhtml
(
JabberMessage
*
jm
,
const
char
*
xhtml
)
{
PurpleAccount
*
account
=
purple_connection_get_account
(
jm
->
js
->
gc
);
GList
*
found_smileys
,
*
it
,
*
it_next
;
PurpleConversation
*
conv
;
gboolean
has_too_large_smiley
=
FALSE
;
gchar
*
smileyfied_xhtml
=
NULL
;
conv
=
purple_conversations_find_with_account
(
jm
->
to
,
account
);
if
(
!
jabber_conv_support_custom_smileys
(
jm
->
js
,
conv
,
jm
->
to
))
return
NULL
;
found_smileys
=
purple_smiley_parser_find
(
purple_smiley_custom_get_list
(),
xhtml
,
TRUE
);
if
(
!
found_smileys
)
return
NULL
;
for
(
it
=
found_smileys
;
it
;
it
=
it_next
)
{
PurpleSmiley
*
smiley
=
it
->
data
;
gboolean
valid
=
TRUE
;
it_next
=
g_list_next
(
it
);
if
(
purple_image_get_data_size
(
PURPLE_IMAGE
(
smiley
))
>
JABBER_DATA_MAX_SIZE
)
{
has_too_large_smiley
=
TRUE
;
valid
=
FALSE
;
purple_debug_warning
(
"jabber"
,
"Refusing to send "
"smiley %s (too large, max is %d)"
,
purple_smiley_get_shortcut
(
smiley
),
JABBER_DATA_MAX_SIZE
);
}
if
(
!
valid
)
found_smileys
=
g_list_delete_link
(
found_smileys
,
it
);
}
if
(
has_too_large_smiley
)
{
purple_conversation_write_system_message
(
conv
,
_
(
"A custom smiley in the message is too large to send."
),
PURPLE_MESSAGE_ERROR
);
}
if
(
!
found_smileys
)
return
NULL
;
for
(
it
=
found_smileys
;
it
;
it
=
g_list_next
(
it
))
{
PurpleSmiley
*
smiley
=
it
->
data
;
const
gchar
*
shortcut
=
purple_smiley_get_shortcut
(
smiley
);
const
gchar
*
mimetype
;
JabberData
*
jdata
;
/* the object has been sent before */
if
(
jabber_data_find_local_by_alt
(
shortcut
))
continue
;
mimetype
=
purple_image_get_mimetype
(
PURPLE_IMAGE
(
smiley
));
if
(
!
mimetype
)
{
purple_debug_error
(
"jabber"
,
"unknown mime type for image"
);
continue
;
}
jdata
=
jabber_data_create_from_data
(
purple_image_get_data
(
PURPLE_IMAGE
(
smiley
)),
purple_image_get_data_size
(
PURPLE_IMAGE
(
smiley
)),
mimetype
,
FALSE
,
jm
->
js
);
purple_debug_info
(
"jabber"
,
"cache local smiley alt=%s, cid=%s"
,
shortcut
,
jabber_data_get_cid
(
jdata
));
jabber_data_associate_local
(
jdata
,
shortcut
);
}
g_list_free
(
found_smileys
);
smileyfied_xhtml
=
purple_smiley_parser_replace
(
purple_smiley_custom_get_list
(),
xhtml
,
jabber_message_smileyify_cb
,
NULL
);
return
smileyfied_xhtml
;
}
void
jabber_message_send
(
JabberMessage
*
jm
)
{
PurpleXmlNode
*
message
,
*
child
;
const
char
*
type
=
NULL
;
message
=
purple_xmlnode_new
(
"message"
);
switch
(
jm
->
type
)
{
case
JABBER_MESSAGE_NORMAL
:
type
=
"normal"
;
break
;
case
JABBER_MESSAGE_CHAT
:
case
JABBER_MESSAGE_GROUPCHAT_INVITE
:
type
=
"chat"
;
break
;
case
JABBER_MESSAGE_HEADLINE
:
type
=
"headline"
;
break
;
case
JABBER_MESSAGE_GROUPCHAT
:
type
=
"groupchat"
;
break
;
case
JABBER_MESSAGE_ERROR
:
type
=
"error"
;
break
;
case
JABBER_MESSAGE_OTHER
:
default
:
type
=
NULL
;
break
;
}
if
(
type
)
purple_xmlnode_set_attrib
(
message
,
"type"
,
type
);
if
(
jm
->
id
)
purple_xmlnode_set_attrib
(
message
,
"id"
,
jm
->
id
);
purple_xmlnode_set_attrib
(
message
,
"to"
,
jm
->
to
);
if
(
jm
->
thread_id
)
{
child
=
purple_xmlnode_new_child
(
message
,
"thread"
);
purple_xmlnode_insert_data
(
child
,
jm
->
thread_id
,
-1
);
}
child
=
NULL
;
switch
(
jm
->
chat_state
)
{
case
JM_STATE_ACTIVE
:
child
=
purple_xmlnode_new_child
(
message
,
"active"
);
break
;
case
JM_STATE_COMPOSING
:
child
=
purple_xmlnode_new_child
(
message
,
"composing"
);
break
;
case
JM_STATE_PAUSED
:
child
=
purple_xmlnode_new_child
(
message
,
"paused"
);
break
;
case
JM_STATE_INACTIVE
:
child
=
purple_xmlnode_new_child
(
message
,
"inactive"
);
break
;
case
JM_STATE_GONE
:
child
=
purple_xmlnode_new_child
(
message
,
"gone"
);
break
;
case
JM_STATE_NONE
:
/* yep, nothing */
break
;
}
if
(
child
)
purple_xmlnode_set_namespace
(
child
,
"http://jabber.org/protocol/chatstates"
);
if
(
jm
->
subject
)
{
child
=
purple_xmlnode_new_child
(
message
,
"subject"
);
purple_xmlnode_insert_data
(
child
,
jm
->
subject
,
-1
);
}
if
(
jm
->
body
)
{
child
=
purple_xmlnode_new_child
(
message
,
"body"
);
purple_xmlnode_insert_data
(
child
,
jm
->
body
,
-1
);
}
if
(
jm
->
xhtml
)
{
if
((
child
=
purple_xmlnode_from_str
(
jm
->
xhtml
,
-1
)))
{
purple_xmlnode_insert_child
(
message
,
child
);
}
else
{
purple_debug_error
(
"jabber"
,
"XHTML translation/validation failed, returning: %s
\n
"
,
jm
->
xhtml
);
}
}
jabber_send
(
jm
->
js
,
message
);
purple_xmlnode_free
(
message
);
}
/*
* Compare the XHTML and plain strings passed in for "equality". Any HTML markup
* other than <br/> (matches a newline) in the XHTML will cause this to return
* FALSE.
*/
static
gboolean
jabber_xhtml_plain_equal
(
const
char
*
xhtml_escaped
,
const
char
*
plain
)
{
int
i
=
0
;
int
j
=
0
;
gboolean
ret
;
char
*
xhtml
=
purple_unescape_html
(
xhtml_escaped
);
while
(
xhtml
[
i
]
&&
plain
[
j
])
{
if
(
xhtml
[
i
]
==
plain
[
j
])
{
i
+=
1
;
j
+=
1
;
continue
;
}
if
(
plain
[
j
]
==
'\n'
&&
!
strncmp
(
xhtml
+
i
,
"<br/>"
,
5
))
{
i
+=
5
;
j
+=
1
;
continue
;
}
g_free
(
xhtml
);
return
FALSE
;
}
/* Are we at the end of both strings? */
ret
=
(
xhtml
[
i
]
==
plain
[
j
])
&&
(
xhtml
[
i
]
==
'\0'
);
g_free
(
xhtml
);
return
ret
;
}
int
jabber_message_send_im
(
PurpleConnection
*
gc
,
PurpleMessage
*
msg
)
{
JabberMessage
*
jm
;
JabberBuddy
*
jb
;
JabberBuddyResource
*
jbr
;
char
*
xhtml
;
char
*
tmp
;
char
*
resource
;
const
gchar
*
rcpt
=
purple_message_get_recipient
(
msg
);
if
(
!
rcpt
||
purple_message_is_empty
(
msg
))
return
0
;
resource
=
jabber_get_resource
(
rcpt
);
jb
=
jabber_buddy_find
(
purple_connection_get_protocol_data
(
gc
),
rcpt
,
TRUE
);
jbr
=
jabber_buddy_find_resource
(
jb
,
resource
);
g_free
(
resource
);
jm
=
g_new0
(
JabberMessage
,
1
);
jm
->
js
=
purple_connection_get_protocol_data
(
gc
);
jm
->
type
=
JABBER_MESSAGE_CHAT
;
jm
->
chat_state
=
JM_STATE_ACTIVE
;
jm
->
to
=
g_strdup
(
rcpt
);
jm
->
id
=
jabber_get_next_id
(
jm
->
js
);
if
(
jbr
)
{
if
(
jbr
->
thread_id
)
jm
->
thread_id
=
jbr
->
thread_id
;
if
(
jbr
->
chat_states
==
JABBER_CHAT_STATES_UNSUPPORTED
)
jm
->
chat_state
=
JM_STATE_NONE
;
else
{
/* if(JABBER_CHAT_STATES_UNKNOWN == jbr->chat_states)
jbr->chat_states = JABBER_CHAT_STATES_UNSUPPORTED; */
}
}
tmp
=
purple_utf8_strip_unprintables
(
purple_message_get_contents
(
msg
));
purple_markup_html_to_xhtml
(
tmp
,
&
xhtml
,
&
jm
->
body
);
g_free
(
tmp
);
tmp
=
jabber_message_smileyfy_xhtml
(
jm
,
xhtml
);
if
(
tmp
)
{
g_free
(
xhtml
);
xhtml
=
tmp
;
}
/*
* For backward compatibility with user expectations or for those not on
* the user's roster, allow sending XHTML-IM markup.
*/
if
(
!
jbr
||
!
jbr
->
caps
.
info
||
jabber_resource_has_capability
(
jbr
,
NS_XHTML_IM
))
{
if
(
!
jabber_xhtml_plain_equal
(
xhtml
,
jm
->
body
))
/* Wrap the message in <p/> for great interoperability justice. */
jm
->
xhtml
=
g_strdup_printf
(
"<html xmlns='"
NS_XHTML_IM
"'><body xmlns='"
NS_XHTML
"'><p>%s</p></body></html>"
,
xhtml
);
}
g_free
(
xhtml
);
jabber_message_send
(
jm
);
jabber_message_free
(
jm
);
return
1
;
}
int
jabber_message_send_chat
(
PurpleConnection
*
gc
,
int
id
,
PurpleMessage
*
msg
)
{
JabberChat
*
chat
;
JabberMessage
*
jm
;
JabberStream
*
js
;
char
*
xhtml
;
char
*
tmp
;
if
(
!
gc
||
purple_message_is_empty
(
msg
))
return
0
;
js
=
purple_connection_get_protocol_data
(
gc
);
chat
=
jabber_chat_find_by_id
(
js
,
id
);
if
(
!
chat
)
return
0
;
jm
=
g_new0
(
JabberMessage
,
1
);
jm
->
js
=
purple_connection_get_protocol_data
(
gc
);
jm
->
type
=
JABBER_MESSAGE_GROUPCHAT
;
jm
->
to
=
g_strdup_printf
(
"%s@%s"
,
chat
->
room
,
chat
->
server
);
jm
->
id
=
jabber_get_next_id
(
jm
->
js
);
tmp
=
purple_utf8_strip_unprintables
(
purple_message_get_contents
(
msg
));
purple_markup_html_to_xhtml
(
tmp
,
&
xhtml
,
&
jm
->
body
);
g_free
(
tmp
);
tmp
=
jabber_message_smileyfy_xhtml
(
jm
,
xhtml
);
if
(
tmp
)
{
g_free
(
xhtml
);
xhtml
=
tmp
;
}
if
(
chat
->
xhtml
&&
!
jabber_xhtml_plain_equal
(
xhtml
,
jm
->
body
))
/* Wrap the message in <p/> for greater interoperability justice. */
jm
->
xhtml
=
g_strdup_printf
(
"<html xmlns='"
NS_XHTML_IM
"'><body xmlns='"
NS_XHTML
"'><p>%s</p></body></html>"
,
xhtml
);
g_free
(
xhtml
);
jabber_message_send
(
jm
);
jabber_message_free
(
jm
);
return
1
;
}
unsigned
int
jabber_send_typing
(
PurpleConnection
*
gc
,
const
char
*
who
,
PurpleIMTypingState
state
)
{
JabberStream
*
js
;
JabberMessage
*
jm
;
JabberBuddy
*
jb
;
JabberBuddyResource
*
jbr
;
char
*
resource
;
js
=
purple_connection_get_protocol_data
(
gc
);
jb
=
jabber_buddy_find
(
js
,
who
,
TRUE
);
if
(
!
jb
)
return
0
;
resource
=
jabber_get_resource
(
who
);
jbr
=
jabber_buddy_find_resource
(
jb
,
resource
);
g_free
(
resource
);
/* We know this entity doesn't support chat states */
if
(
jbr
&&
jbr
->
chat_states
==
JABBER_CHAT_STATES_UNSUPPORTED
)
return
0
;
/* *If* we don't have presence /and/ the buddy can't see our
* presence, don't send typing notifications.
*/
if
(
!
jbr
&&
!
(
jb
->
subscription
&
JABBER_SUB_FROM
))
return
0
;
/* TODO: figure out threading */
jm
=
g_new0
(
JabberMessage
,
1
);
jm
->
js
=
js
;
jm
->
type
=
JABBER_MESSAGE_CHAT
;
jm
->
to
=
g_strdup
(
who
);
jm
->
id
=
jabber_get_next_id
(
jm
->
js
);
if
(
PURPLE_IM_TYPING
==
state
)
jm
->
chat_state
=
JM_STATE_COMPOSING
;
else
if
(
PURPLE_IM_TYPED
==
state
)
jm
->
chat_state
=
JM_STATE_PAUSED
;
else
jm
->
chat_state
=
JM_STATE_ACTIVE
;
/* if(JABBER_CHAT_STATES_UNKNOWN == jbr->chat_states)
jbr->chat_states = JABBER_CHAT_STATES_UNSUPPORTED; */
jabber_message_send
(
jm
);
jabber_message_free
(
jm
);
return
0
;
}
gboolean
jabber_buzz_isenabled
(
JabberStream
*
js
,
const
gchar
*
namespace
)
{
return
js
->
allowBuzz
;
}
gboolean
jabber_custom_smileys_isenabled
(
JabberStream
*
js
,
const
gchar
*
namespace
)
{
PurpleAccount
*
account
=
purple_connection_get_account
(
js
->
gc
);
return
purple_account_get_bool
(
account
,
"custom_smileys"
,
TRUE
);
}