aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libempathy-gtk/empathy-chat-text-view.c144
-rw-r--r--libempathy-gtk/empathy-smiley-manager.c158
-rw-r--r--libempathy-gtk/empathy-smiley-manager.h15
-rw-r--r--libempathy-gtk/empathy-theme-adium.c230
-rw-r--r--libempathy-gtk/empathy-ui-utils.c122
-rw-r--r--libempathy-gtk/empathy-ui-utils.h45
-rw-r--r--libempathy/empathy-debug.c1
-rw-r--r--libempathy/empathy-debug.h1
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/Makefile.am6
-rw-r--r--tests/empathy-parser-test.c145
-rw-r--r--tests/test-helper.c6
12 files changed, 607 insertions, 267 deletions
diff --git a/libempathy-gtk/empathy-chat-text-view.c b/libempathy-gtk/empathy-chat-text-view.c
index de777f2fb..a46f6a4c5 100644
--- a/libempathy-gtk/empathy-chat-text-view.c
+++ b/libempathy-gtk/empathy-chat-text-view.c
@@ -1256,109 +1256,99 @@ empathy_chat_text_view_set_only_if_date (EmpathyChatTextView *view,
}
static void
-chat_text_view_insert_text_with_emoticons (EmpathyChatTextView *view,
- GtkTextIter *iter,
- const gchar *str)
+chat_text_view_replace_link (const gchar *text,
+ gssize len,
+ gpointer match_data,
+ gpointer user_data)
{
- EmpathyChatTextViewPriv *priv = GET_PRIV (view);
- gboolean use_smileys = FALSE;
- GSList *smileys, *l;
+ GtkTextBuffer *buffer = GTK_TEXT_BUFFER (user_data);
+ GtkTextIter iter;
- empathy_conf_get_bool (empathy_conf_get (),
- EMPATHY_PREFS_CHAT_SHOW_SMILEYS,
- &use_smileys);
+ gtk_text_buffer_get_end_iter (buffer, &iter);
+ gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
+ text, len,
+ EMPATHY_CHAT_TEXT_VIEW_TAG_LINK,
+ NULL);
+}
- if (!use_smileys) {
- gtk_text_buffer_insert (priv->buffer, iter, str, -1);
- return;
- }
+static void
+chat_text_view_replace_smiley (const gchar *text,
+ gssize len,
+ gpointer match_data,
+ gpointer user_data)
+{
+ EmpathySmileyHit *hit = match_data;
+ GtkTextBuffer *buffer = GTK_TEXT_BUFFER (user_data);
+ GtkTextIter iter;
- smileys = empathy_smiley_manager_parse (priv->smiley_manager, str);
- for (l = smileys; l; l = l->next) {
- EmpathySmiley *smiley;
+ gtk_text_buffer_get_end_iter (buffer, &iter);
+ gtk_text_buffer_insert_pixbuf (buffer, &iter, hit->pixbuf);
+}
- smiley = l->data;
- if (smiley->pixbuf) {
- gtk_text_buffer_insert_pixbuf (priv->buffer, iter, smiley->pixbuf);
- } else {
- gtk_text_buffer_insert (priv->buffer, iter, smiley->str, -1);
- }
- empathy_smiley_free (smiley);
- }
- g_slist_free (smileys);
+static void
+chat_text_view_replace_verbatim (const gchar *text,
+ gssize len,
+ gpointer match_data,
+ gpointer user_data)
+{
+ GtkTextBuffer *buffer = GTK_TEXT_BUFFER (user_data);
+ GtkTextIter iter;
+
+ gtk_text_buffer_get_end_iter (buffer, &iter);
+ gtk_text_buffer_insert (buffer, &iter, text, len);
}
+static EmpathyStringParser string_parsers[] = {
+ {empathy_string_match_link, chat_text_view_replace_link},
+ {empathy_string_match_all, chat_text_view_replace_verbatim},
+ {NULL, NULL}
+};
+
+static EmpathyStringParser string_parsers_with_smiley[] = {
+ {empathy_string_match_link, chat_text_view_replace_link},
+ {empathy_string_match_smiley, chat_text_view_replace_smiley},
+ {empathy_string_match_all, chat_text_view_replace_verbatim},
+ {NULL, NULL}
+};
+
void
empathy_chat_text_view_append_body (EmpathyChatTextView *view,
const gchar *body,
const gchar *tag)
{
EmpathyChatTextViewPriv *priv = GET_PRIV (view);
- GtkTextIter start_iter, end_iter;
- GtkTextMark *mark;
+ EmpathyStringParser *parsers;
+ gboolean use_smileys;
+ GtkTextIter start_iter;
GtkTextIter iter;
- GRegex *uri_regex;
- GMatchInfo *match_info;
- gboolean match;
- gint last = 0;
- gint s = 0, e = 0;
- gchar *tmp;
+ GtkTextMark *mark;
- priv = GET_PRIV (view);
+ /* Check if we have to parse smileys */
+ empathy_conf_get_bool (empathy_conf_get (),
+ EMPATHY_PREFS_CHAT_SHOW_SMILEYS,
+ &use_smileys);
+ if (use_smileys)
+ parsers = string_parsers_with_smiley;
+ else
+ parsers = string_parsers;
+ /* Create a mark at the place we'll start inserting */
gtk_text_buffer_get_end_iter (priv->buffer, &start_iter);
mark = gtk_text_buffer_create_mark (priv->buffer, NULL, &start_iter, TRUE);
- uri_regex = empathy_uri_regex_dup_singleton ();
- for (match = g_regex_match (uri_regex, body, 0, &match_info); match;
- match = g_match_info_next (match_info, NULL)) {
- if (!g_match_info_fetch_pos (match_info, 0, &s, &e))
- continue;
-
- if (s > last) {
- tmp = empathy_substring (body, last, s);
-
- gtk_text_buffer_get_end_iter (priv->buffer, &iter);
- chat_text_view_insert_text_with_emoticons (view,
- &iter,
- tmp);
- g_free (tmp);
- }
-
- tmp = empathy_substring (body, s, e);
-
- gtk_text_buffer_get_end_iter (priv->buffer, &iter);
- gtk_text_buffer_insert_with_tags_by_name (priv->buffer,
- &iter,
- tmp,
- -1,
- EMPATHY_CHAT_TEXT_VIEW_TAG_LINK,
- NULL);
-
- g_free (tmp);
- last = e;
- }
- g_match_info_free (match_info);
- g_regex_unref (uri_regex);
-
- if (last < (gint) strlen (body)) {
- gtk_text_buffer_get_end_iter (priv->buffer, &iter);
- chat_text_view_insert_text_with_emoticons (view,
- &iter,
- body + last);
- }
+ /* Parse text for links/smileys and insert in the buffer */
+ empathy_string_parser_substr (body, -1, parsers, priv->buffer);
+ /* Insert a newline after the text inserted */
gtk_text_buffer_get_end_iter (priv->buffer, &iter);
gtk_text_buffer_insert (priv->buffer, &iter, "\n", 1);
/* Apply the style to the inserted text. */
gtk_text_buffer_get_iter_at_mark (priv->buffer, &start_iter, mark);
- gtk_text_buffer_get_end_iter (priv->buffer, &end_iter);
-
- gtk_text_buffer_apply_tag_by_name (priv->buffer,
- tag,
+ gtk_text_buffer_get_end_iter (priv->buffer, &iter);
+ gtk_text_buffer_apply_tag_by_name (priv->buffer, tag,
&start_iter,
- &end_iter);
+ &iter);
gtk_text_buffer_delete_mark (priv->buffer, mark);
}
diff --git a/libempathy-gtk/empathy-smiley-manager.c b/libempathy-gtk/empathy-smiley-manager.c
index cf7a70ada..71cb5065f 100644
--- a/libempathy-gtk/empathy-smiley-manager.c
+++ b/libempathy-gtk/empathy-smiley-manager.c
@@ -81,32 +81,22 @@ smiley_manager_tree_free (SmileyManagerTree *tree)
g_slice_free (SmileyManagerTree, tree);
}
-/* Note: This function takes the ownership of str */
static EmpathySmiley *
-smiley_new (GdkPixbuf *pixbuf, gchar *str, const gchar *path)
+smiley_new (GdkPixbuf *pixbuf, const gchar *str)
{
EmpathySmiley *smiley;
smiley = g_slice_new0 (EmpathySmiley);
- if (pixbuf) {
- smiley->pixbuf = g_object_ref (pixbuf);
- }
- smiley->str = str;
- smiley->path = path;
+ smiley->pixbuf = g_object_ref (pixbuf);
+ smiley->str = g_strdup (str);
return smiley;
}
-void
-empathy_smiley_free (EmpathySmiley *smiley)
+static void
+smiley_free (EmpathySmiley *smiley)
{
- if (!smiley) {
- return;
- }
-
- if (smiley->pixbuf) {
- g_object_unref (smiley->pixbuf);
- }
+ g_object_unref (smiley->pixbuf);
g_free (smiley->str);
g_slice_free (EmpathySmiley, smiley);
}
@@ -115,16 +105,9 @@ static void
smiley_manager_finalize (GObject *object)
{
EmpathySmileyManagerPriv *priv = GET_PRIV (object);
- GSList *l;
smiley_manager_tree_free (priv->tree);
- for (l = priv->smileys; l; l = l->next) {
- EmpathySmiley *smiley = l->data;
-
- /* The smiley got the ownership of the path */
- g_free ((gchar *) smiley->path);
- empathy_smiley_free (smiley);
- }
+ g_slist_foreach (priv->smileys, (GFunc) smiley_free, NULL);
g_slist_free (priv->smileys);
}
@@ -247,7 +230,7 @@ smiley_manager_add_valist (EmpathySmileyManager *manager,
/* We give the ownership of path to the smiley */
g_object_set_data_full (G_OBJECT (pixbuf), "smiley_str",
g_strdup (first_str), g_free);
- smiley = smiley_new (pixbuf, g_strdup (first_str), path);
+ smiley = smiley_new (pixbuf, first_str);
priv->smileys = g_slist_prepend (priv->smileys, smiley);
}
@@ -305,70 +288,119 @@ empathy_smiley_manager_load (EmpathySmileyManager *manager)
empathy_smiley_manager_add (manager, "face-worried", ":-S", ":S", ":-s", ":s", NULL);
}
+static EmpathySmileyHit *
+smiley_hit_new (SmileyManagerTree *tree,
+ guint start,
+ guint end)
+{
+ EmpathySmileyHit *hit;
+
+ hit = g_slice_new (EmpathySmileyHit);
+ hit->pixbuf = tree->pixbuf;
+ hit->path = tree->path;
+ hit->start = start;
+ hit->end = end;
+
+ return hit;
+}
+
+void
+empathy_smiley_hit_free (EmpathySmileyHit *hit)
+{
+ g_return_if_fail (hit != NULL);
+
+ g_slice_free (EmpathySmileyHit, hit);
+}
+
GSList *
-empathy_smiley_manager_parse (EmpathySmileyManager *manager,
- const gchar *text)
+empathy_smiley_manager_parse_len (EmpathySmileyManager *manager,
+ const gchar *text,
+ gssize len)
{
EmpathySmileyManagerPriv *priv = GET_PRIV (manager);
- EmpathySmiley *smiley;
+ EmpathySmileyHit *hit;
+ GSList *hits = NULL;
SmileyManagerTree *cur_tree = priv->tree;
- const gchar *t;
- const gchar *cur_str = text;
- GSList *smileys = NULL;
+ const gchar *cur_str;
+ const gchar *start = NULL;
g_return_val_if_fail (EMPATHY_IS_SMILEY_MANAGER (manager), NULL);
g_return_val_if_fail (text != NULL, NULL);
- for (t = text; *t; t = g_utf8_next_char (t)) {
+ /* If len is negative, parse the string until we find '\0' */
+ if (len < 0) {
+ len = G_MAXSSIZE;
+ }
+
+ /* Parse the len first bytes of text to find smileys. Each time a smiley
+ * is detected, append a EmpathySmileyHit struct to the returned list,
+ * containing the smiley pixbuf and the position of the text to be
+ * replaced by it.
+ * cur_str is a pointer in the text showing the current position
+ * of the parsing. It is always at the begining of an UTF-8 character,
+ * because we support unicode smileys! For example we could want to
+ * replace ™ by an image. */
+
+ for (cur_str = text;
+ *cur_str != '\0' && cur_str - text < len;
+ cur_str = g_utf8_next_char (cur_str)) {
SmileyManagerTree *child;
gunichar c;
- c = g_utf8_get_char (t);
+ c = g_utf8_get_char (cur_str);
child = smiley_manager_tree_find_child (cur_tree, c);
- if (cur_tree == priv->tree) {
- if (child) {
- if (t > cur_str) {
- smiley = smiley_new (NULL,
- g_strndup (cur_str, t - cur_str),
- NULL);
- smileys = g_slist_prepend (smileys, smiley);
- }
- cur_str = t;
- cur_tree = child;
- }
-
- continue;
- }
-
+ /* If we have a child it means c is part of a smiley */
if (child) {
+ if (cur_tree == priv->tree) {
+ /* c is the first char of some smileys, keep
+ * the begining position */
+ start = cur_str;
+ }
cur_tree = child;
continue;
}
- smiley = smiley_new (cur_tree->pixbuf,
- g_strndup (cur_str, t - cur_str),
- cur_tree->path);
- smileys = g_slist_prepend (smileys, smiley);
- if (cur_tree->pixbuf) {
- cur_str = t;
- cur_tree = smiley_manager_tree_find_child (priv->tree, c);
+ /* c is not part of a smiley. let's check if we found a smiley
+ * before it. */
+ if (cur_tree->pixbuf != NULL) {
+ /* found! */
+ hit = smiley_hit_new (cur_tree, start - text,
+ cur_str - text);
+ hits = g_slist_prepend (hits, hit);
- if (!cur_tree) {
+ /* c was not part of this smiley, check if a new smiley
+ * start with it. */
+ cur_tree = smiley_manager_tree_find_child (priv->tree, c);
+ if (cur_tree) {
+ start = cur_str;
+ } else {
cur_tree = priv->tree;
}
- } else {
- cur_str = t;
+ } else if (cur_tree != priv->tree) {
+ /* We searched a smiley starting at 'start' but we ended
+ * with no smiley. Look again starting from next char.
+ *
+ * For example ">:)" and ":(" are both valid smileys,
+ * when parsing text ">:(" we first see '>' which could
+ * be the start of a smiley. 'start' variable is set to
+ * that position and we parse next char which is ':' and
+ * is still potential smiley. Then we see '(' which is
+ * NOT part of the smiley, ">:(" does not exist, so we
+ * have to start again from ':' to find ":(" which is
+ * correct smiley. */
+ cur_str = start;
cur_tree = priv->tree;
}
}
- smiley = smiley_new (cur_tree->pixbuf,
- g_strndup (cur_str, t - cur_str),
- cur_tree->path);
- smileys = g_slist_prepend (smileys, smiley);
+ /* Check if last char of the text was the end of a smiley */
+ if (cur_tree->pixbuf != NULL) {
+ hit = smiley_hit_new (cur_tree, start - text, cur_str - text);
+ hits = g_slist_prepend (hits, hit);
+ }
- return g_slist_reverse (smileys);
+ return g_slist_reverse (hits);
}
GSList *
diff --git a/libempathy-gtk/empathy-smiley-manager.h b/libempathy-gtk/empathy-smiley-manager.h
index dc7428c3b..1d6eaac54 100644
--- a/libempathy-gtk/empathy-smiley-manager.h
+++ b/libempathy-gtk/empathy-smiley-manager.h
@@ -50,9 +50,15 @@ struct _EmpathySmileyManagerClass {
typedef struct {
GdkPixbuf *pixbuf;
gchar *str;
- const gchar *path;
} EmpathySmiley;
+typedef struct {
+ GdkPixbuf *pixbuf; /* Pixbuf of the smiley */
+ const gchar *path; /* Filename of the smiley image */
+ guint start; /* text[start:end] should be replaced by pixbuf */
+ guint end;
+} EmpathySmileyHit;
+
typedef void (*EmpathySmileyMenuFunc) (EmpathySmileyManager *manager,
EmpathySmiley *smiley,
gpointer user_data);
@@ -65,12 +71,13 @@ void empathy_smiley_manager_add (EmpathySmileyManag
const gchar *first_str,
...);
GSList * empathy_smiley_manager_get_all (EmpathySmileyManager *manager);
-GSList * empathy_smiley_manager_parse (EmpathySmileyManager *manager,
- const gchar *text);
+GSList * empathy_smiley_manager_parse_len (EmpathySmileyManager *manager,
+ const gchar *text,
+ gssize len);
GtkWidget * empathy_smiley_menu_new (EmpathySmileyManager *manager,
EmpathySmileyMenuFunc func,
gpointer user_data);
-void empathy_smiley_free (EmpathySmiley *smiley);
+void empathy_smiley_hit_free (EmpathySmileyHit *hit);
G_END_DECLS
diff --git a/libempathy-gtk/empathy-theme-adium.c b/libempathy-gtk/empathy-theme-adium.c
index c1365579b..4e70fedb2 100644
--- a/libempathy-gtk/empathy-theme-adium.c
+++ b/libempathy-gtk/empathy-theme-adium.c
@@ -191,122 +191,126 @@ theme_adium_open_address_cb (GtkMenuItem *menuitem,
g_free (uri);
}
-static gchar *
-theme_adium_parse_body (EmpathyThemeAdium *theme,
- const gchar *text)
+static void
+theme_adium_match_newline (const gchar *text,
+ gssize len,
+ EmpathyStringReplace replace_func,
+ EmpathyStringParser *sub_parsers,
+ gpointer user_data)
{
- EmpathyThemeAdiumPriv *priv = GET_PRIV (theme);
- gboolean use_smileys = FALSE;
- GSList *smileys, *l;
- GString *string;
- gint i;
- GRegex *uri_regex;
- GMatchInfo *match_info;
- gboolean match;
- gchar *ret = NULL;
- gint prev;
-
- empathy_conf_get_bool (empathy_conf_get (),
- EMPATHY_PREFS_CHAT_SHOW_SMILEYS,
- &use_smileys);
-
- /* Add <a href></a> arround links */
- uri_regex = empathy_uri_regex_dup_singleton ();
- match = g_regex_match (uri_regex, text, 0, &match_info);
- if (match) {
- gint last = 0;
- gint s = 0, e = 0;
-
- string = g_string_sized_new (strlen (text));
- do {
- gchar *real_url;
-
- g_match_info_fetch_pos (match_info, 0, &s, &e);
-
- if (s > last) {
- /* Append the text between last link (or the
- * start of the message) and this link */
- gchar *str;
- str = g_markup_escape_text (text + last, s - last);
- g_string_append (string, str);
- g_free (str);
- }
-
- /* Append the link inside <a href=""></a> tag */
- real_url = empathy_make_absolute_url_len (text + s, e - s);
-
- g_string_append (string, "<a href=\"");
- g_string_append (string, real_url);
- g_string_append (string, "\">");
- g_string_append_len (string, text + s, e - s);
- g_string_append (string, "</a>");
-
- g_free (real_url);
- last = e;
- } while (g_match_info_next (match_info, NULL));
-
- if (e < (gint) strlen (text)) {
- /* Append the text after the last link */
- gchar *str;
- str = g_markup_escape_text (text + e, strlen (text) - e);
- g_string_append (string, str);
- g_free (str);
- }
-
- g_free (ret);
- text = ret = g_string_free (string, FALSE);
- } else if (use_smileys) {
- /* Replace smileys by a <img/> tag */
- string = g_string_sized_new (strlen (text));
- smileys = empathy_smiley_manager_parse (priv->smiley_manager, text);
- for (l = smileys; l; l = l->next) {
- EmpathySmiley *smiley;
-
- smiley = l->data;
- if (smiley->path) {
- g_string_append_printf (string,
- "<abbr title='%s'><img src=\"%s\"/ alt=\"%s\"/></abbr>",
- smiley->str, smiley->path, smiley->str);
- } else {
- gchar *str;
+ GString *string = user_data;
+ gint i;
+ gint prev = 0;
- str = g_markup_escape_text (smiley->str, -1);
- g_string_append (string, str);
- g_free (str);
- }
- empathy_smiley_free (smiley);
- }
- g_slist_free (smileys);
-
- g_free (ret);
- text = ret = g_string_free (string, FALSE);
- } else {
- text = ret = g_markup_escape_text (text, -1);
+ if (len < 0) {
+ len = G_MAXSSIZE;
}
- g_match_info_free (match_info);
- g_regex_unref (uri_regex);
-
/* Replace \n by <br/> */
- string = NULL;
- prev = 0;
- for (i = 0; text[i] != '\0'; i++) {
+ for (i = 0; i < len && text[i] != '\0'; i++) {
if (text[i] == '\n') {
- if (!string ) {
- string = g_string_sized_new (strlen (text));
- }
- g_string_append_len (string, text + prev, i - prev);
+ empathy_string_parser_substr (text + prev,
+ i - prev, sub_parsers,
+ user_data);
g_string_append (string, "<br/>");
prev = i + 1;
}
}
- if (string) {
- g_string_append (string, text + prev);
- g_free (ret);
- text = ret = g_string_free (string, FALSE);
- }
+ empathy_string_parser_substr (text + prev, i - prev,
+ sub_parsers, user_data);
+}
- return ret;
+static void
+theme_adium_replace_link (const gchar *text,
+ gssize len,
+ gpointer match_data,
+ gpointer user_data)
+{
+ GString *string = user_data;
+ gchar *real_url;
+ gchar *escaped;
+
+ real_url = empathy_make_absolute_url_len (text, len);
+
+ /* The thing we are making a link of may contain
+ * characters which need escaping */
+ escaped = g_markup_escape_text (text, len);
+
+ /* Append the link inside <a href=""></a> tag */
+ g_string_append_printf (string, "<a href=\"%s\">%s</a>",
+ real_url, escaped);
+
+ g_free (real_url);
+ g_free (escaped);
+}
+
+static void
+theme_adium_replace_smiley (const gchar *text,
+ gssize len,
+ gpointer match_data,
+ gpointer user_data)
+{
+ EmpathySmileyHit *hit = match_data;
+ GString *string = user_data;
+
+ /* Replace smiley by a <img/> tag */
+ g_string_append_printf (string,
+ "<img src=\"%s\" alt=\"%.*s\" title=\"%.*s\"/>",
+ hit->path, len, text, len, text);
+}
+
+static void
+theme_adium_replace_escaped (const gchar *text,
+ gssize len,
+ gpointer match_data,
+ gpointer user_data)
+{
+ GString *string = user_data;
+ gchar *escaped;
+
+ escaped = g_markup_escape_text (text, len);
+ g_string_append (string, escaped);
+ g_free (escaped);
+}
+
+static EmpathyStringParser string_parsers[] = {
+ {empathy_string_match_link, theme_adium_replace_link},
+ {theme_adium_match_newline, NULL},
+ {empathy_string_match_all, theme_adium_replace_escaped},
+ {NULL, NULL}
+};
+
+static EmpathyStringParser string_parsers_with_smiley[] = {
+ {empathy_string_match_link, theme_adium_replace_link},
+ {empathy_string_match_smiley, theme_adium_replace_smiley},
+ {theme_adium_match_newline, NULL},
+ {empathy_string_match_all, theme_adium_replace_escaped},
+ {NULL, NULL}
+};
+
+static gchar *
+theme_adium_parse_body (const gchar *text)
+{
+ EmpathyStringParser *parsers;
+ GString *string;
+ gboolean use_smileys;
+
+ /* Check if we have to parse smileys */
+ empathy_conf_get_bool (empathy_conf_get (),
+ EMPATHY_PREFS_CHAT_SHOW_SMILEYS,
+ &use_smileys);
+ if (use_smileys)
+ parsers = string_parsers_with_smiley;
+ else
+ parsers = string_parsers;
+
+ /* Parse text and construct string with links and smileys replaced
+ * by html tags. Also escape text to make sure html code is
+ * displayed verbatim. */
+ string = g_string_sized_new (strlen (text));
+ empathy_string_parser_substr (text, -1, parsers, string);
+
+ return g_string_free (string, FALSE);
}
static void
@@ -464,7 +468,7 @@ theme_adium_append_message (EmpathyChatView *view,
EmpathyThemeAdiumPriv *priv = GET_PRIV (theme);
EmpathyContact *sender;
TpAccount *account;
- gchar *dup_body = NULL;
+ gchar *body_escaped;
const gchar *body;
const gchar *name;
const gchar *contact_id;
@@ -494,10 +498,7 @@ theme_adium_append_message (EmpathyChatView *view,
service_name = tp_account_get_protocol (account);
timestamp = empathy_message_get_timestamp (msg);
body = empathy_message_get_body (msg);
- dup_body = theme_adium_parse_body (theme, body);
- if (dup_body) {
- body = dup_body;
- }
+ body_escaped = theme_adium_parse_body (body);
name = empathy_contact_get_name (sender);
contact_id = empathy_contact_get_id (sender);
@@ -505,10 +506,11 @@ theme_adium_append_message (EmpathyChatView *view,
if (empathy_message_get_tptype (msg) == TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION) {
gchar *str;
- str = g_strdup_printf ("%s %s", name, body);
+ str = g_strdup_printf ("%s %s", name, body_escaped);
theme_adium_append_event_escaped (view, str);
+
g_free (str);
- g_free (dup_body);
+ g_free (body_escaped);
return;
}
@@ -629,7 +631,7 @@ theme_adium_append_message (EmpathyChatView *view,
}
if (html != NULL) {
- theme_adium_append_html (theme, func, html, len, body,
+ theme_adium_append_html (theme, func, html, len, body_escaped,
avatar_filename, name, contact_id,
service_name, message_classes->str,
timestamp);
@@ -645,7 +647,7 @@ theme_adium_append_message (EmpathyChatView *view,
priv->last_timestamp = timestamp;
priv->last_is_backlog = is_backlog;
- g_free (dup_body);
+ g_free (body_escaped);
g_string_free (message_classes, TRUE);
}
diff --git a/libempathy-gtk/empathy-ui-utils.c b/libempathy-gtk/empathy-ui-utils.c
index ce5ec419e..c737873d1 100644
--- a/libempathy-gtk/empathy-ui-utils.c
+++ b/libempathy-gtk/empathy-ui-utils.c
@@ -40,6 +40,7 @@
#include "empathy-ui-utils.h"
#include "empathy-images.h"
+#include "empathy-smiley-manager.h"
#include "empathy-conf.h"
#define DEBUG_FLAG EMPATHY_DEBUG_OTHER
@@ -49,13 +50,15 @@
#include <libempathy/empathy-idle.h>
#include <libempathy/empathy-ft-factory.h>
-#define SCHEMES "(https?|s?ftps?|nntp|news|javascript|about|ghelp|apt|telnet|"\
- "file|webcal|mailto)"
-#define BODY "([^\\ \\n\"]+)"
-#define END_BODY "([^\\ \\n\"]*[^,;\?><()\\ \"\\.\\n])"
-#define URI_REGEX "("SCHEMES"://"END_BODY")" \
- "|((mailto:)?"BODY"@"BODY"\\."END_BODY")"\
- "|((www|ftp)\\."END_BODY")"
+#define SCHEMES "([a-zA-Z\\+]+)"
+#define INVALID_CHARS " \n\"'"
+#define INVALID_CHARS_EXT INVALID_CHARS "\\[\\]<>(){},;:?"
+#define BODY "([^"INVALID_CHARS"]+)"
+#define BODY_END "([^"INVALID_CHARS"]*)[^"INVALID_CHARS_EXT".]"
+#define BODY_STRICT "([^"INVALID_CHARS_EXT"]+)"
+#define URI_REGEX "("SCHEMES"://"BODY_END")" \
+ "|((www|ftp)\\."BODY_END")" \
+ "|((mailto:)?"BODY_STRICT"@"BODY"\\."BODY_END")"
void
empathy_gtk_init (void)
@@ -1569,3 +1572,108 @@ empathy_receive_file_with_file_chooser (EmpathyFTHandler *handler)
gtk_widget_show (widget);
}
+
+void
+empathy_string_parser_substr (const gchar *text,
+ gssize len,
+ EmpathyStringParser *parsers,
+ gpointer user_data)
+{
+ if (parsers != NULL && parsers[0].match_func != NULL) {
+ parsers[0].match_func (text, len,
+ parsers[0].replace_func, parsers + 1,
+ user_data);
+ }
+}
+
+void
+empathy_string_match_link (const gchar *text,
+ gssize len,
+ EmpathyStringReplace replace_func,
+ EmpathyStringParser *sub_parsers,
+ gpointer user_data)
+{
+ GRegex *uri_regex;
+ GMatchInfo *match_info;
+ gboolean match;
+ gint last = 0;
+
+ uri_regex = empathy_uri_regex_dup_singleton ();
+ match = g_regex_match_full (uri_regex, text, len, 0, 0, &match_info, NULL);
+ if (match) {
+ gint s = 0, e = 0;
+
+ do {
+ g_match_info_fetch_pos (match_info, 0, &s, &e);
+
+ if (s > last) {
+ /* Append the text between last link (or the
+ * start of the message) and this link */
+ empathy_string_parser_substr (text + last,
+ s - last,
+ sub_parsers,
+ user_data);
+ }
+
+ replace_func (text + s, e - s, NULL, user_data);
+
+ last = e;
+ } while (g_match_info_next (match_info, NULL));
+ }
+
+ empathy_string_parser_substr (text + last, len - last,
+ sub_parsers, user_data);
+
+ g_match_info_free (match_info);
+ g_regex_unref (uri_regex);
+}
+
+void
+empathy_string_match_smiley (const gchar *text,
+ gssize len,
+ EmpathyStringReplace replace_func,
+ EmpathyStringParser *sub_parsers,
+ gpointer user_data)
+{
+ guint last = 0;
+ EmpathySmileyManager *smiley_manager;
+ GSList *hits, *l;
+
+ smiley_manager = empathy_smiley_manager_dup_singleton ();
+ hits = empathy_smiley_manager_parse_len (smiley_manager, text, len);
+
+ for (l = hits; l; l = l->next) {
+ EmpathySmileyHit *hit = l->data;
+
+ if (hit->start > last) {
+ /* Append the text between last smiley (or the
+ * start of the message) and this smiley */
+ empathy_string_parser_substr (text + last,
+ hit->start - last,
+ sub_parsers, user_data);
+ }
+
+ replace_func (text + hit->start, hit->end - hit->start,
+ hit, user_data);
+
+ last = hit->end;
+
+ empathy_smiley_hit_free (hit);
+ }
+ g_slist_free (hits);
+ g_object_unref (smiley_manager);
+
+ empathy_string_parser_substr (text + last, len - last,
+ sub_parsers, user_data);
+}
+
+void
+empathy_string_match_all (const gchar *text,
+ gssize len,
+ EmpathyStringReplace replace_func,
+ EmpathyStringParser *sub_parsers,
+ gpointer user_data)
+{
+ replace_func (text, len, NULL, user_data);
+}
+
diff --git a/libempathy-gtk/empathy-ui-utils.h b/libempathy-gtk/empathy-ui-utils.h
index 7bec0884e..65fc9ac0e 100644
--- a/libempathy-gtk/empathy-ui-utils.h
+++ b/libempathy-gtk/empathy-ui-utils.h
@@ -117,6 +117,51 @@ gchar * empathy_make_absolute_url (const gchar *url);
gchar * empathy_make_absolute_url_len (const gchar *url,
guint len);
+/* String parser */
+typedef struct _EmpathyStringParser EmpathyStringParser;
+
+typedef void (*EmpathyStringReplace) (const gchar *text,
+ gssize len,
+ gpointer match_data,
+ gpointer user_data);
+typedef void (*EmpathyStringMatch) (const gchar *text,
+ gssize len,
+ EmpathyStringReplace replace_func,
+ EmpathyStringParser *sub_parsers,
+ gpointer user_data);
+
+struct _EmpathyStringParser {
+ EmpathyStringMatch match_func;
+ EmpathyStringReplace replace_func;
+};
+
+void
+empathy_string_parser_substr (const gchar *text,
+ gssize len,
+ EmpathyStringParser *parsers,
+ gpointer user_data);
+
+void
+empathy_string_match_link (const gchar *text,
+ gssize len,
+ EmpathyStringReplace replace_func,
+ EmpathyStringParser *sub_parsers,
+ gpointer user_data);
+
+void
+empathy_string_match_smiley (const gchar *text,
+ gssize len,
+ EmpathyStringReplace replace_func,
+ EmpathyStringParser *sub_parsers,
+ gpointer user_data);
+
+void
+empathy_string_match_all (const gchar *text,
+ gssize len,
+ EmpathyStringReplace replace_func,
+ EmpathyStringParser *sub_parsers,
+ gpointer user_data);
+
G_END_DECLS
#endif /* __EMPATHY_UI_UTILS_H__ */
diff --git a/libempathy/empathy-debug.c b/libempathy/empathy-debug.c
index 141340024..3f22c8969 100644
--- a/libempathy/empathy-debug.c
+++ b/libempathy/empathy-debug.c
@@ -51,6 +51,7 @@ static GDebugKey keys[] = {
{ "Other", EMPATHY_DEBUG_OTHER },
{ "Connectivity", EMPATHY_DEBUG_CONNECTIVITY },
{ "ImportMc4Accounts", EMPATHY_DEBUG_IMPORT_MC4_ACCOUNTS },
+ { "Tests", EMPATHY_DEBUG_TESTS },
{ 0, }
};
diff --git a/libempathy/empathy-debug.h b/libempathy/empathy-debug.h
index cc8eca0a3..fa80d403a 100644
--- a/libempathy/empathy-debug.h
+++ b/libempathy/empathy-debug.h
@@ -44,6 +44,7 @@ typedef enum
EMPATHY_DEBUG_SHARE_DESKTOP = 1 << 10,
EMPATHY_DEBUG_CONNECTIVITY = 1 << 11,
EMPATHY_DEBUG_IMPORT_MC4_ACCOUNTS = 1 << 11,
+ EMPATHY_DEBUG_TESTS = 1 << 12,
} EmpathyDebugFlags;
gboolean empathy_debug_flag_is_set (EmpathyDebugFlags flag);
diff --git a/tests/.gitignore b/tests/.gitignore
index 73a19245d..f3af9c7fe 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -5,4 +5,5 @@ empathy-irc-network-test
empathy-irc-network-manager-test
empathy-chatroom-test
empathy-chatroom-manager-test
+empathy-parser-test
test-report.xml
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 25e399406..936761533 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -27,7 +27,8 @@ TEST_PROGS = \
empathy-irc-network-test \
empathy-irc-network-manager-test \
empathy-chatroom-test \
- empathy-chatroom-manager-test
+ empathy-chatroom-manager-test \
+ empathy-parser-test
empathy_utils_test_SOURCES = empathy-utils-test.c \
test-helper.c test-helper.h
@@ -50,6 +51,9 @@ empathy_chatroom_test_SOURCES = empathy-chatroom-test.c \
empathy_chatroom_manager_test_SOURCES = empathy-chatroom-manager-test.c \
test-helper.c test-helper.h
+empathy_parser_test_SOURCES = empathy-parser-test.c \
+ test-helper.c test-helper.h
+
check_PROGRAMS = $(TEST_PROGS)
TESTS_ENVIRONMENT = EMPATHY_SRCDIR=@abs_top_srcdir@ \
diff --git a/tests/empathy-parser-test.c b/tests/empathy-parser-test.c
new file mode 100644
index 000000000..001882863
--- /dev/null
+++ b/tests/empathy-parser-test.c
@@ -0,0 +1,145 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "test-helper.h"
+
+#include <telepathy-glib/util.h>
+
+#define DEBUG_FLAG EMPATHY_DEBUG_TESTS
+#include <libempathy/empathy-debug.h>
+
+#include <libempathy-gtk/empathy-ui-utils.h>
+
+static void
+test_replace_match (const gchar *text,
+ gssize len,
+ gpointer match_data,
+ gpointer user_data)
+{
+ GString *string = user_data;
+
+ g_string_append_c (string, '[');
+ g_string_append_len (string, text, len);
+ g_string_append_c (string, ']');
+}
+
+static void
+test_replace_verbatim (const gchar *text,
+ gssize len,
+ gpointer match_data,
+ gpointer user_data)
+{
+ GString *string = user_data;
+
+ g_string_append_len (string, text, len);
+}
+
+static void
+test_parsers (void)
+{
+ gchar *tests[] =
+ {
+ /* Basic link matches */
+ "http://foo.com", "[http://foo.com]",
+ "http://foo.com\nhttp://bar.com", "[http://foo.com]\n[http://bar.com]",
+ "http://foo.com/test?id=bar?", "[http://foo.com/test?id=bar]?",
+ "git://foo.com", "[git://foo.com]",
+ "git+ssh://foo.com", "[git+ssh://foo.com]",
+ "mailto:user@server.com", "[mailto:user@server.com]",
+ "www.foo.com", "[www.foo.com]",
+ "ftp.foo.com", "[ftp.foo.com]",
+ "user@server.com", "[user@server.com]",
+ "first.last@server.com", "[first.last@server.com]",
+ "http://foo.com. bar", "[http://foo.com]. bar",
+ "http://foo.com; bar", "[http://foo.com]; bar",
+ "http://foo.com: bar", "[http://foo.com]: bar",
+ "http://foo.com:bar", "[http://foo.com:bar]",
+
+ /* They are not links! */
+ "http://", "http[:/]/", /* Hm... */
+ "www.", "www.",
+ "w.foo.com", "w.foo.com",
+ "@server.com", "@server.com",
+ "mailto:user@", "mailto:user@",
+ "mailto:user@.com", "mailto:user@.com",
+ "user@.com", "user@.com",
+
+ /* Links inside (), {}, [], <> or "" */
+ /* FIXME: How to test if the ending ] is matched or not? */
+ "Foo (www.foo.com)", "Foo ([www.foo.com])",
+ "Foo {www.foo.com}", "Foo {[www.foo.com]}",
+ "Foo [www.foo.com]", "Foo [[www.foo.com]]",
+ "Foo <www.foo.com>", "Foo <[www.foo.com]>",
+ "Foo \"www.foo.com\"", "Foo \"[www.foo.com]\"",
+ "Foo (www.foo.com/bar(123)baz)", "Foo ([www.foo.com/bar(123)baz])",
+ "<a href=\"http://foo.com\">bar</a>", "<a href=\"[http://foo.com]\">bar</a>",
+ "Foo (user@server.com)", "Foo ([user@server.com])",
+ "Foo {user@server.com}", "Foo {[user@server.com]}",
+ "Foo [user@server.com]", "Foo [[user@server.com]]",
+ "Foo <user@server.com>", "Foo <[user@server.com]>",
+ "Foo \"user@server.com\"", "Foo \"[user@server.com]\"",
+
+ /* Basic smileys */
+ "a:)b", "a[:)]b",
+ ">:)", "[>:)]",
+ ">:(", ">[:(]",
+
+ /* Smileys and links mixed */
+ ":)http://foo.com", "[:)][http://foo.com]",
+ "a :) b http://foo.com c :( d www.test.com e", "a [:)] b [http://foo.com] c [:(] d [www.test.com] e",
+
+ /* FIXME: Known issue: Brackets should be counted by the parser */
+ //"Foo www.bar.com/test(123)", "Foo [www.bar.com/test(123)]",
+ //"Foo (www.bar.com/test(123))", "Foo ([www.bar.com/test(123)])",
+ //"Foo www.bar.com/test{123}", "Foo [www.bar.com/test{123}]",
+ //"Foo (:))", "Foo ([:)])",
+ //"Foo <a href=\"http://foo.com\">:)</a>", "Foo <a href=\"[http://foo.com]\">[:)]</a>",
+
+ NULL, NULL
+ };
+ EmpathyStringParser parsers[] =
+ {
+ {empathy_string_match_link, test_replace_match},
+ {empathy_string_match_smiley, test_replace_match},
+ {empathy_string_match_all, test_replace_verbatim},
+ {NULL, NULL}
+ };
+ guint i;
+ gboolean failed = FALSE;
+
+ DEBUG ("Started");
+ for (i = 0; tests[i] != NULL; i += 2)
+ {
+ GString *string;
+ gboolean ok;
+
+ string = g_string_new (NULL);
+ empathy_string_parser_substr (tests[i], -1, parsers, string);
+
+ ok = !tp_strdiff (tests[i + 1], string->str);
+ DEBUG ("'%s' => '%s': %s", tests[i], string->str, ok ? "OK" : "FAILED");
+ if (!ok)
+ failed = TRUE;
+
+ g_string_free (string, TRUE);
+ }
+
+ g_assert (!failed);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ int result;
+
+ test_init (argc, argv);
+
+ g_test_add_func ("/parsers", test_parsers);
+
+ result = g_test_run ();
+ test_deinit ();
+
+ return result;
+}
diff --git a/tests/test-helper.c b/tests/test-helper.c
index 8b6cc2937..d51d22a52 100644
--- a/tests/test-helper.c
+++ b/tests/test-helper.c
@@ -20,6 +20,9 @@
#include <stdlib.h>
#include <glib.h>
#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include <libempathy-gtk/empathy-ui-utils.h>
#include "test-helper.h"
@@ -28,7 +31,8 @@ test_init (int argc,
char **argv)
{
g_test_init (&argc, &argv, NULL);
- g_type_init ();
+ gtk_init (&argc, &argv);
+ empathy_gtk_init ();
}
void