* 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
* 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 "smiley-parser.h"
#include "smiley-custom.h"
#include "smiley-theme.h"
#define DISPLAY_OUR_CUSTOM_SMILEYS_FOR_INCOMING_MESSAGES 1
PurpleConversation *conv;
GHashTable *found_smileys;
static PurpleTrie *html_sentry;
purple_smiley_parse_check_html(const gchar *word,
PurpleSmileyParseData *parse_data)
if (G_LIKELY(word[0] == '<'))
parse_data->in_html_tag = TRUE;
else if (G_LIKELY(word[0] == '>'))
parse_data->in_html_tag = FALSE;
g_warn_if_fail(word[1] == '\0');
purple_smiley_parse_cb(GString *out, const gchar *word, gpointer _smiley,
PurpleSmileyParseData *parse_data = _parse_data;
PurpleSmiley *smiley = _smiley;
/* a special-case for html_sentry */
purple_smiley_parse_check_html(word, parse_data);
if (parse_data->in_html_tag)
return parse_data->job.replace.cb(out, smiley,
parse_data->job.replace.conv, parse_data->job.replace.ui_data);
/* XXX: this shouldn't be a conv for incoming messages - see
* PurpleConversationPrivate.remote_smileys.
* For outgoing messages, we could pass conv in ui_data (or something).
* Or maybe we should use two distinct functions?
* To be reconsidered when we had PurpleDude-like objects.
purple_smiley_parser_smileify(PurpleConversation *conv, const gchar *html_message,
gboolean use_remote_smileys, PurpleSmileyParseCb cb, gpointer ui_data)
PurpleSmileyTheme *theme;
PurpleSmileyList *theme_smileys = NULL, *remote_smileys = NULL;
PurpleTrie *theme_trie = NULL, *custom_trie = NULL, *remote_trie = NULL;
GSList tries_sentry, tries_theme, tries_custom, tries_remote;
PurpleSmileyParseData parse_data;
if (html_message == NULL || html_message[0] == '\0')
return g_strdup(html_message);
remote_smileys = purple_conversation_get_remote_smileys(conv);
remote_trie = purple_smiley_list_get_trie(remote_smileys);
if (remote_trie && purple_trie_get_size(remote_trie) == 0)
if (purple_conversation_get_features(conv) &
PURPLE_CONNECTION_FLAG_ALLOW_CUSTOM_SMILEY)
custom_trie = purple_smiley_list_get_trie(
purple_smiley_custom_get_list());
#if !DISPLAY_OUR_CUSTOM_SMILEYS_FOR_INCOMING_MESSAGES
if (custom_trie && purple_trie_get_size(custom_trie) == 0)
theme = purple_smiley_theme_get_current();
theme_smileys = purple_smiley_theme_get_smileys(theme, ui_data);
if (theme_smileys != NULL)
theme_trie = purple_smiley_list_get_trie(theme_smileys);
/* we have absolutely no smileys */
if (theme_trie == NULL && custom_trie == NULL && remote_trie == NULL)
return g_strdup(html_message);
/* Create a tries list on the stack. */
tries_sentry.data = html_sentry;
tries_theme.data = theme_trie;
tries_custom.data = custom_trie;
tries_remote.data = remote_trie;
tries_sentry.next = NULL;
tries_theme.next = tries_custom.next = tries_remote.next = NULL;
g_slist_last(tries)->next = &tries_remote;
g_slist_last(tries)->next = &tries_custom;
g_slist_last(tries)->next = &tries_theme;
parse_data.job.replace.conv = conv;
parse_data.job.replace.cb = cb;
parse_data.job.replace.ui_data = ui_data;
parse_data.in_html_tag = FALSE;
/* TODO: parse greedily (as much as possible) when PurpleTrie
* provides support for it. */
return purple_trie_multi_replace(tries, html_message,
purple_smiley_parse_cb, &parse_data);
purple_smiley_parser_replace(PurpleSmileyList *smileys,
const gchar *html_message, PurpleSmileyParseCb cb, gpointer ui_data)
PurpleTrie *smileys_trie = NULL;
PurpleSmileyParseData parse_data;
if (html_message == NULL || html_message[0] == '\0')
return g_strdup(html_message);
if (smileys == NULL || purple_smiley_list_is_empty(smileys))
return g_strdup(html_message);
smileys_trie = purple_smiley_list_get_trie(smileys);
g_return_val_if_fail(smileys_trie != NULL, NULL);
trie_1.data = html_sentry;
trie_2.data = smileys_trie;
parse_data.job.replace.conv = NULL;
parse_data.job.replace.cb = cb;
parse_data.job.replace.ui_data = ui_data;
parse_data.in_html_tag = FALSE;
return purple_trie_multi_replace(&trie_1, html_message,
purple_smiley_parse_cb, &parse_data);
smiley_find_cb(const gchar *word, gpointer _smiley, gpointer _parse_data)
PurpleSmiley *smiley = _smiley;
PurpleSmileyParseData *parse_data = _parse_data;
/* a special-case for html_sentry */
purple_smiley_parse_check_html(word, parse_data);
if (parse_data->in_html_tag)
g_hash_table_insert(parse_data->job.find.found_smileys, smiley, smiley);
purple_smiley_parser_find(PurpleSmileyList *smileys, const gchar *message,
PurpleTrie *smileys_trie;
gchar *escaped_message = NULL;
PurpleSmileyParseData parse_data;
if (message == NULL || message[0] == '\0')
if (smileys == NULL || purple_smiley_list_is_empty(smileys))
smileys_trie = purple_smiley_list_get_trie(smileys);
g_return_val_if_fail(smileys_trie != NULL, NULL);
message = escaped_message = g_markup_escape_text(message, -1);
parse_data.job.find.found_smileys =
g_hash_table_new(g_direct_hash, g_direct_equal);
parse_data.in_html_tag = FALSE;
trie_1.data = html_sentry;
trie_2.data = smileys_trie;
purple_trie_multi_find(&trie_1, message, smiley_find_cb, &parse_data);
found_list = g_hash_table_get_values(parse_data.job.find.found_smileys);
g_hash_table_destroy(parse_data.job.find.found_smileys);
_purple_smiley_parser_init(void)
html_sentry = purple_trie_new();
purple_trie_add(html_sentry, "<", NULL);
purple_trie_add(html_sentry, ">", NULL);
_purple_smiley_parser_uninit(void)
g_object_unref(html_sentry);