Mercurial > pidgin
changeset 29020:64bee7ae306f
merge of '18b85ae6ad8091af43d91826cf054e4569ba74aa'
and '5a7c739baddaf14efccf2017d05e0f9bb5b7278a'
author | Sadrul Habib Chowdhury <imadil@gmail.com> |
---|---|
date | Wed, 10 Feb 2010 16:46:08 +0000 |
parents | 6288bbd53f44 (diff) a8614b855c39 (current diff) |
children | 7cab6dc6b79c |
files | |
diffstat | 15 files changed, 187 insertions(+), 116 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Tue Feb 09 16:54:32 2010 +0000 +++ b/ChangeLog Wed Feb 10 16:46:08 2010 +0000 @@ -10,6 +10,8 @@ * When looking up DNS records, use the type of record returned by the server (instead of the type we asked for) to determine how to process the record. + * Fix an issue with parsing XML attributes that contain "<br>". + See ChangeLog.API for more details. General: * Correctly disable all missing dependencies when using the @@ -37,6 +39,11 @@ * Fix a leak and crash when retrieving buddy icons. XMPP: + * Upon receipt of a presence change from a contact, send further messages + to that contact's bare JID [username]. A conversation "locks" to a + specific resource upon receipt of a message from that resource. + Previously, it would only "unlock" when that resource went offline, or + upon receipt of a message from a different resource. * Added support for the SCRAM-SHA-1 SASL mechanism. This is only available when built without Cyrus SASL support. * When getting info on a domain-only (server) JID, show uptime @@ -49,6 +56,8 @@ * Don't send custom smileys larger than the recommended maximum object size specified in the BoB XEP. This prevents a client from being disconnected by servers that dislike overly-large stanzas. + * Fix receiving messages without markup over an Openfire BOSH connection + (forcibly put the stanzas in the jabber:client namespace). Yahoo: * Don't send <span> and </span> tags. (Fartash Faghri)
--- a/ChangeLog.API Tue Feb 09 16:54:32 2010 +0000 +++ b/ChangeLog.API Wed Feb 10 16:46:08 2010 +0000 @@ -7,6 +7,12 @@ purple_xfer_request_denied if an error is found when selecting a file to send. Request denied is still used when a receive request is not allowed. + * xmlnode_from_str now properly handles paring an attribute which + contain "<br>", which were previously transformed into a + newline character (libxml2 unescapes all entities except + representations of '&', and libpurple's purple_unescape_html + converts "<br>" to a newline). + Perl: Changed: * Corrected the package names for the PurpleProxyType and
--- a/configure.ac Tue Feb 09 16:54:32 2010 +0000 +++ b/configure.ac Wed Feb 10 16:46:08 2010 +0000 @@ -2393,30 +2393,6 @@ AC_DEFINE(HAVE_TM_GMTOFF, 1, [Define if you have a tm_gmtoff member in struct tm]) fi -AC_CACHE_CHECK([whether va_lists can be copied by value], ac_cv_va_val_copy,[ - AC_TRY_RUN([#include <stdarg.h> -#include <stdlib.h> - void f (int i, ...) { - va_list args1, args2; - va_start (args1, i); - args2 = args1; - if (va_arg (args2, int) != 42 || va_arg (args1, int) != 42) - exit (1); - va_end (args1); va_end (args2); - } - int main() { - f (0, 42); - return 0; - }], - [ac_cv_va_val_copy=yes], - [ac_cv_va_val_copy=no], - [ac_cv_va_val_copy=yes]) -]) - -if test "x$ac_cv_va_val_copy" = "xno"; then - AC_DEFINE(VA_COPY_AS_ARRAY, 1, ['va_lists' cannot be copied as values]) -fi - dnl ####################################################################### dnl # Check for check dnl #######################################################################
--- a/libpurple/plugins/perl/perl-common.c Tue Feb 09 16:54:32 2010 +0000 +++ b/libpurple/plugins/perl/perl-common.c Wed Feb 10 16:46:08 2010 +0000 @@ -472,74 +472,74 @@ } SV * -purple_perl_sv_from_vargs(const PurpleValue *value, va_list *args, void ***copy_arg) +purple_perl_sv_from_vargs(const PurpleValue *value, va_list args, void ***copy_arg) { if (purple_value_is_outgoing(value)) { switch (purple_value_get_type(value)) { case PURPLE_TYPE_SUBTYPE: - if ((*copy_arg = va_arg(*args, void **)) == NULL) + if ((*copy_arg = va_arg(args, void **)) == NULL) return &PL_sv_undef; return purple_perl_sv_from_subtype(value, *(void **)*copy_arg); case PURPLE_TYPE_BOOLEAN: - if ((*copy_arg = (void *)va_arg(*args, gboolean *)) == NULL) + if ((*copy_arg = (void *)va_arg(args, gboolean *)) == NULL) return &PL_sv_undef; return newSViv(*(gboolean *)*copy_arg); case PURPLE_TYPE_INT: - if ((*copy_arg = (void *)va_arg(*args, int *)) == NULL) + if ((*copy_arg = (void *)va_arg(args, int *)) == NULL) return &PL_sv_undef; return newSViv(*(int *)*copy_arg); case PURPLE_TYPE_UINT: - if ((*copy_arg = (void *)va_arg(*args, unsigned int *)) == NULL) + if ((*copy_arg = (void *)va_arg(args, unsigned int *)) == NULL) return &PL_sv_undef; return newSVuv(*(unsigned int *)*copy_arg); case PURPLE_TYPE_LONG: - if ((*copy_arg = (void *)va_arg(*args, long *)) == NULL) + if ((*copy_arg = (void *)va_arg(args, long *)) == NULL) return &PL_sv_undef; return newSViv(*(long *)*copy_arg); case PURPLE_TYPE_ULONG: - if ((*copy_arg = (void *)va_arg(*args, + if ((*copy_arg = (void *)va_arg(args, unsigned long *)) == NULL) return &PL_sv_undef; return newSVuv(*(unsigned long *)*copy_arg); case PURPLE_TYPE_INT64: - if ((*copy_arg = (void *)va_arg(*args, gint64 *)) == NULL) + if ((*copy_arg = (void *)va_arg(args, gint64 *)) == NULL) return &PL_sv_undef; return newSViv(*(gint64 *)*copy_arg); case PURPLE_TYPE_UINT64: - if ((*copy_arg = (void *)va_arg(*args, guint64 *)) == NULL) + if ((*copy_arg = (void *)va_arg(args, guint64 *)) == NULL) return &PL_sv_undef; return newSVuv(*(guint64 *)*copy_arg); case PURPLE_TYPE_STRING: - if ((*copy_arg = (void *)va_arg(*args, char **)) == NULL) + if ((*copy_arg = (void *)va_arg(args, char **)) == NULL) return &PL_sv_undef; return newSVGChar(*(char **)*copy_arg); case PURPLE_TYPE_POINTER: - if ((*copy_arg = va_arg(*args, void **)) == NULL) + if ((*copy_arg = va_arg(args, void **)) == NULL) return &PL_sv_undef; return newSViv((IV)*(void **)*copy_arg); case PURPLE_TYPE_BOXED: /* Uh.. I dunno. Try this? */ - if ((*copy_arg = va_arg(*args, void **)) == NULL) + if ((*copy_arg = va_arg(args, void **)) == NULL) return &PL_sv_undef; return sv_2mortal(purple_perl_bless_object( @@ -553,40 +553,40 @@ } else { switch (purple_value_get_type(value)) { case PURPLE_TYPE_SUBTYPE: - if ((*copy_arg = va_arg(*args, void *)) == NULL) + if ((*copy_arg = va_arg(args, void *)) == NULL) return &PL_sv_undef; return purple_perl_sv_from_subtype(value, *copy_arg); case PURPLE_TYPE_BOOLEAN: - *copy_arg = GINT_TO_POINTER( va_arg(*args, gboolean) ); + *copy_arg = GINT_TO_POINTER( va_arg(args, gboolean) ); return newSViv((gboolean)GPOINTER_TO_INT(*copy_arg)); case PURPLE_TYPE_INT: - *copy_arg = GINT_TO_POINTER( va_arg(*args, int) ); + *copy_arg = GINT_TO_POINTER( va_arg(args, int) ); return newSViv(GPOINTER_TO_INT(*copy_arg)); case PURPLE_TYPE_UINT: - *copy_arg = GUINT_TO_POINTER(va_arg(*args, unsigned int)); + *copy_arg = GUINT_TO_POINTER(va_arg(args, unsigned int)); return newSVuv(GPOINTER_TO_UINT(*copy_arg)); case PURPLE_TYPE_LONG: - *copy_arg = (void *)va_arg(*args, long); + *copy_arg = (void *)va_arg(args, long); return newSViv((long)*copy_arg); case PURPLE_TYPE_ULONG: - *copy_arg = (void *)va_arg(*args, unsigned long); + *copy_arg = (void *)va_arg(args, unsigned long); return newSVuv((unsigned long)*copy_arg); case PURPLE_TYPE_INT64: #if 0 /* XXX This yells and complains. */ - *copy_arg = va_arg(*args, gint64); + *copy_arg = va_arg(args, gint64); return newSViv(*copy_arg); #endif @@ -595,27 +595,27 @@ case PURPLE_TYPE_UINT64: /* XXX This also yells and complains. */ #if 0 - *copy_arg = (void *)va_arg(*args, guint64); + *copy_arg = (void *)va_arg(args, guint64); return newSVuv(*copy_arg); #endif break; case PURPLE_TYPE_STRING: - if ((*copy_arg = (void *)va_arg(*args, char *)) == NULL) + if ((*copy_arg = (void *)va_arg(args, char *)) == NULL) return &PL_sv_undef; return newSVGChar((char *)*copy_arg); case PURPLE_TYPE_POINTER: - if ((*copy_arg = (void *)va_arg(*args, void *)) == NULL) + if ((*copy_arg = (void *)va_arg(args, void *)) == NULL) return &PL_sv_undef; return newSViv((IV)*copy_arg); case PURPLE_TYPE_BOXED: /* Uh.. I dunno. Try this? */ - if ((*copy_arg = (void *)va_arg(*args, void *)) == NULL) + if ((*copy_arg = (void *)va_arg(args, void *)) == NULL) return &PL_sv_undef; return sv_2mortal(purple_perl_bless_object(*copy_arg,
--- a/libpurple/plugins/perl/perl-common.h Tue Feb 09 16:54:32 2010 +0000 +++ b/libpurple/plugins/perl/perl-common.h Wed Feb 10 16:46:08 2010 +0000 @@ -65,7 +65,7 @@ #endif void *purple_perl_data_from_sv(PurpleValue *value, SV *sv); -SV *purple_perl_sv_from_vargs(const PurpleValue *value, va_list *args, +SV *purple_perl_sv_from_vargs(const PurpleValue *value, va_list args, void ***copy_arg); SV *purple_perl_sv_from_fun(PurplePlugin *plugin, SV *callback); #endif /* _PURPLE_PERL_COMMON_H_ */
--- a/libpurple/plugins/perl/perl-handlers.c Tue Feb 09 16:54:32 2010 +0000 +++ b/libpurple/plugins/perl/perl-handlers.c Wed Feb 10 16:46:08 2010 +0000 @@ -298,11 +298,7 @@ for (i = 0; i < value_count; i++) { sv_args[i] = purple_perl_sv_from_vargs(values[i], -#ifdef VA_COPY_AS_ARRAY args, -#else - (va_list*)&args, -#endif ©_args[i]); XPUSHs(sv_args[i]);
--- a/libpurple/protocols/bonjour/parser.c Tue Feb 09 16:54:32 2010 +0000 +++ b/libpurple/protocols/bonjour/parser.c Wed Feb 10 16:46:08 2010 +0000 @@ -49,6 +49,31 @@ return FALSE; } +static char *purple_unescape_text(const char *in) +{ + GString *ret; + const char *c = in; + + if (in == NULL) + return NULL; + + ret = g_string_new(""); + while (*c) { + int len; + const char *ent; + + if ((ent = purple_markup_unescape_entity(c, &len)) != NULL) { + g_string_append(ret, ent); + c += len; + } else { + g_string_append_c(ret, *c); + c++; + } + } + + return g_string_free(ret, FALSE); +} + static void bonjour_parser_element_start_libxml(void *user_data, const xmlChar *element_name, const xmlChar *prefix, const xmlChar *namespace, @@ -102,7 +127,7 @@ attrib[attrib_len] = '\0'; txt = attrib; - attrib = purple_unescape_html(txt); + attrib = purple_unescape_text(txt); g_free(txt); xmlnode_set_attrib_full(node, name, attrib_ns, prefix, attrib); g_free(attrib);
--- a/libpurple/protocols/jabber/bosh.c Tue Feb 09 16:54:32 2010 +0000 +++ b/libpurple/protocols/jabber/bosh.c Wed Feb 10 16:46:08 2010 +0000 @@ -476,6 +476,18 @@ /* jabber_process_packet might free child */ xmlnode *next = child->next; if (child->type == XMLNODE_TYPE_TAG) { + const char *xmlns = xmlnode_get_namespace(child); + /* + * Workaround for non-compliant servers that don't stamp + * the right xmlns on these packets. See #11315. + */ + if ((xmlns == NULL /* shouldn't happen, but is equally wrong */ || + g_str_equal(xmlns, NS_BOSH)) && + (g_str_equal(child->name, "iq") || + g_str_equal(child->name, "message") || + g_str_equal(child->name, "presence"))) { + xmlnode_set_namespace(child, NS_XMPP_CLIENT); + } jabber_process_packet(js, &child); }
--- a/libpurple/protocols/jabber/jutil.c Tue Feb 09 16:54:32 2010 +0000 +++ b/libpurple/protocols/jabber/jutil.c Wed Feb 10 16:46:08 2010 +0000 @@ -651,25 +651,6 @@ return equal; } -PurpleConversation * -jabber_find_unnormalized_conv(const char *name, PurpleAccount *account) -{ - PurpleConversation *c = NULL; - GList *cnv; - - g_return_val_if_fail(name != NULL, NULL); - - for(cnv = purple_get_conversations(); cnv; cnv = cnv->next) { - c = (PurpleConversation*)cnv->data; - if(purple_conversation_get_type(c) == PURPLE_CONV_TYPE_IM && - !purple_utf8_strcasecmp(name, purple_conversation_get_name(c)) && - account == purple_conversation_get_account(c)) - return c; - } - - return NULL; -} - /* The same as purple_util_get_image_checksum, but guaranteed to remain SHA1 */ char * jabber_calculate_data_sha1sum(gconstpointer data, size_t len)
--- a/libpurple/protocols/jabber/jutil.h Tue Feb 09 16:54:32 2010 +0000 +++ b/libpurple/protocols/jabber/jutil.h Wed Feb 10 16:46:08 2010 +0000 @@ -63,7 +63,5 @@ */ char *jabber_saslprep(const char *); -PurpleConversation *jabber_find_unnormalized_conv(const char *name, PurpleAccount *account); - char *jabber_calculate_data_sha1sum(gconstpointer data, size_t len); #endif /* PURPLE_JABBER_JUTIL_H_ */
--- a/libpurple/protocols/jabber/message.c Tue Feb 09 16:54:32 2010 +0000 +++ b/libpurple/protocols/jabber/message.c Wed Feb 10 16:46:08 2010 +0000 @@ -59,33 +59,37 @@ static void handle_chat(JabberMessage *jm) { JabberID *jid = jabber_id_new(jm->from); - char *from; + PurpleConnection *gc; + PurpleAccount *account; JabberBuddy *jb; JabberBuddyResource *jbr; 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(jabber_find_unnormalized_conv(jm->from, jm->js->gc->account)) { - from = g_strdup(jm->from); - } else if(jid->node) { - if(jid->resource) { - PurpleConversation *conv; + 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_conv_im_send gets the name from + * purple_conversation_get_name() + */ + PurpleConversation *conv; - from = g_strdup_printf("%s@%s", jid->node, jid->domain); - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, from, jm->js->gc->account); - if(conv) { - purple_conversation_set_name(conv, jm->from); - } - g_free(from); + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, jm->from, account); + if (conv && !g_str_equal(jm->from, purple_conversation_get_name(conv))) { + purple_debug_info("jabber", "Binding conversation to %s\n", jm->from); + purple_conversation_set_name(conv, jm->from); } - from = g_strdup(jm->from); - } else { - from = g_strdup(jid->domain); } if(!jm->xhtml && !jm->body) { @@ -97,19 +101,19 @@ } if(JM_STATE_COMPOSING == jm->chat_state) { - serv_got_typing(jm->js->gc, from, 0, PURPLE_TYPING); + serv_got_typing(gc, jm->from, 0, PURPLE_TYPING); } else if(JM_STATE_PAUSED == jm->chat_state) { - serv_got_typing(jm->js->gc, from, 0, PURPLE_TYPED); + serv_got_typing(gc, jm->from, 0, PURPLE_TYPED); } else if(JM_STATE_GONE == jm->chat_state) { PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, - from, jm->js->gc->account); + jm->from, account); if (conv && jid->node && jid->domain) { char buf[256]; PurpleBuddy *buddy; g_snprintf(buf, sizeof(buf), "%s@%s", jid->node, jid->domain); - if ((buddy = purple_find_buddy(jm->js->gc->account, buf))) { + if ((buddy = purple_find_buddy(account, buf))) { const char *who; char *escaped; @@ -127,10 +131,10 @@ PURPLE_MESSAGE_SYSTEM, time(NULL)); } } - serv_got_typing_stopped(jm->js->gc, from); + serv_got_typing_stopped(gc, jm->from); } else { - serv_got_typing_stopped(jm->js->gc, from); + serv_got_typing_stopped(gc, jm->from); } } else { if(jbr) { @@ -149,12 +153,9 @@ jm->body = jabber_google_format_to_html(jm->body); g_free(tmp); } - serv_got_im(jm->js->gc, from, jm->xhtml ? jm->xhtml : jm->body, 0, - jm->sent); + serv_got_im(gc, jm->from, jm->xhtml ? jm->xhtml : jm->body, 0, jm->sent); } - - g_free(from); jabber_id_free(jid); }
--- a/libpurple/protocols/jabber/parser.c Tue Feb 09 16:54:32 2010 +0000 +++ b/libpurple/protocols/jabber/parser.c Wed Feb 10 16:46:08 2010 +0000 @@ -31,6 +31,31 @@ #include "util.h" #include "xmlnode.h" +static char *purple_unescape_text(const char *in) +{ + GString *ret; + const char *c = in; + + if (in == NULL) + return NULL; + + ret = g_string_new(""); + while (*c) { + int len; + const char *ent; + + if ((ent = purple_markup_unescape_entity(c, &len)) != NULL) { + g_string_append(ret, ent); + c += len; + } else { + g_string_append_c(ret, *c); + c++; + } + } + + return g_string_free(ret, FALSE); +} + static void jabber_parser_element_start_libxml(void *user_data, const xmlChar *element_name, const xmlChar *prefix, const xmlChar *namespace, @@ -89,7 +114,7 @@ char *attrib = g_strndup((gchar *)attributes[i+3], attrib_len); txt = attrib; - attrib = purple_unescape_html(txt); + attrib = purple_unescape_text(txt); g_free(txt); xmlnode_set_attrib_full(node, name, attrib_ns, prefix, attrib); g_free(attrib);
--- a/libpurple/protocols/jabber/presence.c Tue Feb 09 16:54:32 2010 +0000 +++ b/libpurple/protocols/jabber/presence.c Wed Feb 10 16:46:08 2010 +0000 @@ -502,6 +502,7 @@ PurpleConvChatBuddyFlags flags = PURPLE_CBFLAGS_NONE; gboolean delayed = FALSE; const gchar *stamp = NULL; /* from <delayed/> element */ + PurpleAccount *account; PurpleBuddy *b = NULL; char *buddy_name; JabberBuddyState state = JABBER_BUDDY_STATE_UNKNOWN; @@ -523,6 +524,8 @@ if (signal_return) return; + account = purple_connection_get_account(js->gc); + jid = jabber_id_new(from); if (jid == NULL) { purple_debug_error("jabber", "Ignoring presence with malformed 'from' " @@ -559,12 +562,10 @@ } else if (g_str_equal(type, "subscribe")) { struct _jabber_add_permit *jap = g_new0(struct _jabber_add_permit, 1); gboolean onlist = FALSE; - PurpleAccount *account; PurpleBuddy *buddy; JabberBuddy *jb = NULL; xmlnode *nick; - account = purple_connection_get_account(js->gc); buddy = purple_find_buddy(account, from); nick = xmlnode_get_child_with_namespace(packet, "nick", "http://jabber.org/protocol/nick"); if (nick) @@ -675,6 +676,7 @@ idle += offset; } + /* DEALING WITH CHATS */ if(jid->node && (chat = jabber_chat_find(js, jid->node, jid->domain))) { static int i = 1; @@ -748,7 +750,7 @@ " you like to configure it, or" " accept the default settings?"), /* Default Action */ 1, - purple_connection_get_account(js->gc), NULL, chat->conv, + account, NULL, chat->conv, chat, 2, _("_Configure Room"), G_CALLBACK(jabber_chat_request_room_configure), _("_Accept Defaults"), G_CALLBACK(jabber_chat_create_instant_room)); @@ -954,13 +956,31 @@ g_free(nickname); g_return_if_reached(); } + /* End of DEALING WITH CHATS...about 5000 lines ago */ } else { + /* DEALING WITH CONTACT (i.e. not a chat) */ + PurpleConversation *conv; + buddy_name = g_strdup_printf("%s%s%s", jid->node ? jid->node : "", jid->node ? "@" : "", jid->domain); - if((b = purple_find_buddy(js->gc->account, buddy_name)) == NULL) { + + /* + * Unbind/unlock from sending messages to a specific resource on + * presence changes. This is locked to a specific resource when + * receiving a message (in message.c). + */ + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, + buddy_name, account); + if (conv) { + purple_debug_info("jabber", "Changed conversation binding from %s to %s\n", + purple_conversation_get_name(conv), buddy_name); + purple_conversation_set_name(conv, buddy_name); + } + + if((b = purple_find_buddy(account, buddy_name)) == NULL) { if (jb != js->user_jb) { purple_debug_warning("jabber", "Got presence for unknown buddy %s on account %s (%p)\n", - buddy_name, purple_account_get_username(js->gc->account), js->gc->account); + buddy_name, purple_account_get_username(account), account); jabber_id_free(jid); g_free(avatar_hash); g_free(buddy_name); @@ -1001,12 +1021,7 @@ if(state == JABBER_BUDDY_STATE_ERROR || (type && (g_str_equal(type, "unavailable") || g_str_equal(type, "unsubscribed")))) { - PurpleConversation *conv; - jabber_buddy_remove_resource(jb, jid->resource); - if((conv = jabber_find_unnormalized_conv(from, js->gc->account))) - purple_conversation_set_name(conv, buddy_name); - } else { jbr = jabber_buddy_track_resource(jb, jid->resource, priority, state, status); @@ -1019,12 +1034,12 @@ if((found_jbr = jabber_buddy_find_resource(jb, NULL))) { jabber_google_presence_incoming(js, buddy_name, found_jbr); - purple_prpl_got_user_status(js->gc->account, buddy_name, jabber_buddy_state_get_status_id(found_jbr->state), "priority", found_jbr->priority, "message", found_jbr->status, NULL); - purple_prpl_got_user_idle(js->gc->account, buddy_name, found_jbr->idle, found_jbr->idle); + purple_prpl_got_user_status(account, buddy_name, jabber_buddy_state_get_status_id(found_jbr->state), "priority", found_jbr->priority, "message", found_jbr->status, NULL); + purple_prpl_got_user_idle(account, buddy_name, found_jbr->idle, found_jbr->idle); if (nickname) serv_got_alias(js->gc, buddy_name, nickname); } else { - purple_prpl_got_user_status(js->gc->account, buddy_name, "offline", status ? "message" : NULL, status, NULL); + purple_prpl_got_user_status(account, buddy_name, "offline", status ? "message" : NULL, status, NULL); } g_free(buddy_name); }
--- a/libpurple/util.h Tue Feb 09 16:54:32 2010 +0000 +++ b/libpurple/util.h Wed Feb 10 16:46:08 2010 +0000 @@ -518,8 +518,10 @@ * Unescapes HTML entities to their literal characters. Also translates * "<br>" to "\n". * For example "&" is replaced by '&' and so on. - * Actually only "&", """, "<" and ">" are currently - * supported. + * + * The following named entities are supported (in addition to numerical + * entities): + * "&", "<", ">", "©", """, "®", "'" * * @param html The string in which to unescape any HTML entities *
--- a/libpurple/xmlnode.c Tue Feb 09 16:54:32 2010 +0000 +++ b/libpurple/xmlnode.c Wed Feb 10 16:46:08 2010 +0000 @@ -545,6 +545,31 @@ return xml_with_declaration; } +static char *purple_unescape_text(const char *in) +{ + GString *ret; + const char *c = in; + + if (in == NULL) + return NULL; + + ret = g_string_new(""); + while (*c) { + int len; + const char *ent; + + if ((ent = purple_markup_unescape_entity(c, &len)) != NULL) { + g_string_append(ret, ent); + c += len; + } else { + g_string_append_c(ret, *c); + c++; + } + } + + return g_string_free(ret, FALSE); +} + struct _xmlnode_parser_data { xmlnode *current; gboolean error; @@ -590,7 +615,7 @@ int attrib_len = attributes[i+4] - attributes[i+3]; char *attrib = g_strndup((const char *)attributes[i+3], attrib_len); txt = attrib; - attrib = purple_unescape_html(txt); + attrib = purple_unescape_text(txt); g_free(txt); xmlnode_set_attrib_full(node, name, NULL, prefix, attrib); g_free(attrib);