changeset 30385:9d386bf63eab

Stop using custom encodings (and LATIN-1, for that matter) for sending OSCAR messages (ICBM, chat, Direct IM). Now, we use ASCII if a message contains ASCII characters only, and UTF-16 in all other cases. That fixes #10833 (offline messages now will be sent as UTF-16) and also a whole bunch of potential problems we can get with charset 0x3. Different clients tend to interpret this charset differently; for instance, the official client always interprets it as LATIN-1, while alternative clients may decode it as some other user-specified 8-bit encoding. On the other hand, ASCII messages (charset 0x0) and UTF-16 messages (charset 0x2) are understood uniformly by all clients. I also cleaned-up the code a little (got rid of code paths that were never executed, flags that were always set, unused struct members, etc.)
author ivan.komarov@soc.pidgin.im
date Tue, 27 Jul 2010 21:17:01 +0000
parents 42862490dcb7
children ca90b6c27eb8
files libpurple/protocols/oscar/encoding.c libpurple/protocols/oscar/encoding.h libpurple/protocols/oscar/family_icbm.c libpurple/protocols/oscar/oscar.c libpurple/protocols/oscar/oscar.h
diffstat 5 files changed, 61 insertions(+), 416 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/oscar/encoding.c	Mon Jul 26 17:02:32 2010 +0000
+++ b/libpurple/protocols/oscar/encoding.c	Tue Jul 27 21:17:01 2010 +0000
@@ -20,45 +20,17 @@
 
 #include "encoding.h"
 
-guint32
+guint16
 oscar_charset_check(const char *utf8)
 {
-	int i = 0;
-	int charset = AIM_CHARSET_ASCII;
-
-	/*
-	 * Can we get away with using our custom encoding?
-	 */
-	while (utf8[i])
+	while (*utf8++)
 	{
-		if ((unsigned char)utf8[i] > 0x7f) {
+		if ((unsigned char)(*utf8) > 0x7f) {
 			/* not ASCII! */
-			charset = AIM_CHARSET_LATIN_1;
-			break;
+			return AIM_CHARSET_UNICODE;
 		}
-		i++;
 	}
-
-	/*
-	 * Must we send this message as UNICODE (in the UTF-16BE encoding)?
-	 */
-	while (utf8[i])
-	{
-		/* ISO-8859-1 is 0x00-0xbf in the first byte
-		 * followed by 0xc0-0xc3 in the second */
-		if ((unsigned char)utf8[i] < 0x80) {
-			i++;
-			continue;
-		} else if (((unsigned char)utf8[i] & 0xfc) == 0xc0 &&
-				   ((unsigned char)utf8[i + 1] & 0xc0) == 0x80) {
-			i += 2;
-			continue;
-		}
-		charset = AIM_CHARSET_UNICODE;
-		break;
-	}
-
-	return charset;
+	return AIM_CHARSET_ASCII;
 }
 
 gchar *
@@ -259,103 +231,15 @@
 	return ret;
 }
 
-void
-oscar_convert_to_best_encoding(PurpleConnection *gc,
-				const char *destbn, const gchar *from,
-				gchar **msg, int *msglen_int,
-				guint16 *charset, guint16 *charsubset)
+gchar *
+oscar_convert_to_best_encoding(const gchar *msg, gsize *result_len, guint16 *charset, gchar **charsetstr)
 {
-	OscarData *od = purple_connection_get_protocol_data(gc);
-	PurpleAccount *account = purple_connection_get_account(gc);
-	GError *err = NULL;
-	aim_userinfo_t *userinfo = NULL;
-	const gchar *charsetstr;
-	gsize msglen;
-
-	/* Attempt to send as ASCII */
-	if (oscar_charset_check(from) == AIM_CHARSET_ASCII) {
-		*msg = g_convert(from, -1, "ASCII", "UTF-8", NULL, &msglen, NULL);
-		*charset = AIM_CHARSET_ASCII;
-		*charsubset = 0x0000;
-		*msglen_int = msglen;
-		return;
+	guint16 msg_charset = oscar_charset_check(msg);
+	if (charset != NULL) {
+		*charset = msg_charset;
 	}
-
-	/*
-	 * If we're sending to an ICQ user, and they are in our
-	 * buddy list, and they are advertising the Unicode
-	 * capability, and they are online, then attempt to send
-	 * as UTF-16BE.
-	 */
-	if ((destbn != NULL) && oscar_util_valid_name_icq(destbn))
-		userinfo = aim_locate_finduserinfo(od, destbn);
-
-	if ((userinfo != NULL) && (userinfo->capabilities & OSCAR_CAPABILITY_UNICODE))
-	{
-		PurpleBuddy *b;
-		b = purple_find_buddy(account, destbn);
-		if ((b != NULL) && (PURPLE_BUDDY_IS_ONLINE(b)))
-		{
-			*msg = g_convert(from, -1, "UTF-16BE", "UTF-8", NULL, &msglen, &err);
-			if (*msg != NULL)
-			{
-				*charset = AIM_CHARSET_UNICODE;
-				*charsubset = 0x0000;
-				*msglen_int = msglen;
-				return;
-			}
-
-			purple_debug_error("oscar", "Conversion from UTF-8 to UTF-16BE failed: %s.\n",
-							   err->message);
-			g_error_free(err);
-			err = NULL;
-		}
+	if (charsetstr != NULL) {
+		*charsetstr = msg_charset == AIM_CHARSET_ASCII ? "us-ascii" : "unicode-2-0";
 	}
-
-	/*
-	 * If this is AIM then attempt to send as ISO-8859-1.  If this is
-	 * ICQ then attempt to send as the user specified character encoding.
-	 */
-	charsetstr = "ISO-8859-1";
-	if ((destbn != NULL) && oscar_util_valid_name_icq(destbn))
-		charsetstr = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
-
-	/*
-	 * XXX - We need a way to only attempt to convert if we KNOW "from"
-	 * can be converted to "charsetstr"
-	 */
-	*msg = g_convert(from, -1, charsetstr, "UTF-8", NULL, &msglen, &err);
-	if (*msg != NULL) {
-		*charset = AIM_CHARSET_LATIN_1;
-		*charsubset = 0x0000;
-		*msglen_int = msglen;
-		return;
-	}
-
-	purple_debug_info("oscar", "Conversion from UTF-8 to %s failed (%s). Falling back to unicode.\n",
-					  charsetstr, err->message);
-	g_error_free(err);
-	err = NULL;
-
-	/*
-	 * Nothing else worked, so send as UTF-16BE.
-	 */
-	*msg = g_convert(from, -1, "UTF-16BE", "UTF-8", NULL, &msglen, &err);
-	if (*msg != NULL) {
-		*charset = AIM_CHARSET_UNICODE;
-		*charsubset = 0x0000;
-		*msglen_int = msglen;
-		return;
-	}
-
-	purple_debug_error("oscar", "Error converting a Unicode message: %s\n", err->message);
-	g_error_free(err);
-	err = NULL;
-
-	purple_debug_error("oscar", "This should NEVER happen!  Sending UTF-8 text flagged as ASCII.\n");
-	*msg = g_strdup(from);
-	*msglen_int = strlen(*msg);
-	*charset = AIM_CHARSET_ASCII;
-	*charsubset = 0x0000;
-	return;
+	return g_convert(msg, -1, msg_charset == AIM_CHARSET_ASCII ? "ASCII" : "UTF-16BE", "UTF-8", NULL, result_len, NULL);
 }
--- a/libpurple/protocols/oscar/encoding.h	Mon Jul 26 17:02:32 2010 +0000
+++ b/libpurple/protocols/oscar/encoding.h	Tue Jul 27 21:17:01 2010 +0000
@@ -27,7 +27,7 @@
 /**
  * Determine the simplest encoding we can send this message in.
  */
-guint32 oscar_charset_check(const char *utf8);
+guint16 oscar_charset_check(const char *utf8);
 
 /**
  * Take a string of the form charset="bleh" where bleh is
@@ -56,9 +56,6 @@
 /**
  * Figure out what encoding to use when sending a given outgoing message.
  */
-void oscar_convert_to_best_encoding(PurpleConnection *gc,
-				const char *destbn, const gchar *from,
-				gchar **msg, int *msglen_int,
-				guint16 *charset, guint16 *charsubset);
+gchar * oscar_convert_to_best_encoding(const gchar *msg, gsize *result_len, guint16 *charset, gchar **charsetstr);
 
 #endif
\ No newline at end of file
--- a/libpurple/protocols/oscar/family_icbm.c	Mon Jul 26 17:02:32 2010 +0000
+++ b/libpurple/protocols/oscar/family_icbm.c	Tue Jul 27 21:17:01 2010 +0000
@@ -348,31 +348,9 @@
  *
  * Possible flags:
  *   AIM_IMFLAGS_AWAY  -- Marks the message as an autoresponse
- *   AIM_IMFLAGS_ACK   -- Requests that the server send an ack
- *                        when the message is received (of type SNAC_FAMILY_ICBM/0x000c)
  *   AIM_IMFLAGS_OFFLINE--If destination is offline, store it until they are
  *                        online (probably ICQ only).
  *
- * Generally, you should use the lowest encoding possible to send
- * your message.  If you only use basic punctuation and the generic
- * Latin alphabet, use ASCII7 (no flags).  If you happen to use non-ASCII7
- * characters, but they are all clearly defined in ISO-8859-1, then
- * use that.  Keep in mind that not all characters in the PC ASCII8
- * character set are defined in the ISO standard. For those cases (most
- * notably when the (r) symbol is used), you must use the full UNICODE
- * encoding for your message.  In UNICODE mode, _all_ characters must
- * occupy 16bits, including ones that are not special.  (Remember that
- * the first 128 UNICODE symbols are equivalent to ASCII7, however they
- * must be prefixed with a zero high order byte.)
- *
- * I strongly discourage the use of UNICODE mode, mainly because none
- * of the clients I use can parse those messages (and besides that,
- * wchars are difficult and non-portable to handle in most UNIX environments).
- * If you really need to include special characters, use the HTML UNICODE
- * entities.  These are of the form &#2026; where 2026 is the hex
- * representation of the UNICODE index (in this case, UNICODE
- * "Horizontal Ellipsis", or 133 in in ASCII8).
- *
  * Implementation note:  Since this is one of the most-used functions
  * in all of libfaim, it is written with performance in mind.  As such,
  * it is not as clear as it could be in respect to how this message is
@@ -390,7 +368,6 @@
 	ByteStream data;
 	guchar cookie[8];
 	int msgtlvlen;
-	static const guint8 deffeatures[] = { 0x01, 0x01, 0x01, 0x02 };
 
 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
 		return -EINVAL;
@@ -398,37 +375,17 @@
 	if (!args)
 		return -EINVAL;
 
-	if (args->flags & AIM_IMFLAGS_MULTIPART) {
-		if (args->mpmsg->numparts == 0)
-			return -EINVAL;
-	} else {
-		if (!args->msg || (args->msglen <= 0))
-			return -EINVAL;
-
-		if (args->msglen > MAXMSGLEN)
-			return -E2BIG;
-	}
+	if (!args->msg || (args->msglen <= 0))
+		return -EINVAL;
+
+	if (args->msglen > MAXMSGLEN)
+		return -E2BIG;
 
 	/* Painfully calculate the size of the message TLV */
 	msgtlvlen = 1 + 1; /* 0501 */
-
-	if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES)
-		msgtlvlen += 2 + args->featureslen;
-	else
-		msgtlvlen += 2 + sizeof(deffeatures);
-
-	if (args->flags & AIM_IMFLAGS_MULTIPART) {
-		aim_mpmsg_section_t *sec;
-
-		for (sec = args->mpmsg->parts; sec; sec = sec->next) {
-			msgtlvlen += 2 /* 0101 */ + 2 /* block len */;
-			msgtlvlen += 4 /* charset */ + sec->datalen;
-		}
-
-	} else {
-		msgtlvlen += 2 /* 0101 */ + 2 /* block len */;
-		msgtlvlen += 4 /* charset */ + args->msglen;
-	}
+	msgtlvlen += 2 + args->featureslen;
+	msgtlvlen += 2 /* 0101 */ + 2 /* block len */;
+	msgtlvlen += 4 /* charset */ + args->msglen;
 
 	byte_stream_new(&data, msgtlvlen + 128);
 
@@ -444,52 +401,31 @@
 
 	/* Features TLV (type 0x0501) */
 	byte_stream_put16(&data, 0x0501);
-	if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES) {
-		byte_stream_put16(&data, args->featureslen);
-		byte_stream_putraw(&data, args->features, args->featureslen);
-	} else {
-		byte_stream_put16(&data, sizeof(deffeatures));
-		byte_stream_putraw(&data, deffeatures, sizeof(deffeatures));
-	}
-
-	if (args->flags & AIM_IMFLAGS_MULTIPART) {
-		aim_mpmsg_section_t *sec;
-
-		/* Insert each message part in a TLV (type 0x0101) */
-		for (sec = args->mpmsg->parts; sec; sec = sec->next) {
-			byte_stream_put16(&data, 0x0101);
-			byte_stream_put16(&data, sec->datalen + 4);
-			byte_stream_put16(&data, sec->charset);
-			byte_stream_put16(&data, sec->charsubset);
-			byte_stream_putraw(&data, (guchar *)sec->data, sec->datalen);
-		}
-
-	} else {
-
-		/* Insert message text in a TLV (type 0x0101) */
-		byte_stream_put16(&data, 0x0101);
-
-		/* Message block length */
-		byte_stream_put16(&data, args->msglen + 0x04);
-
-		/* Character set */
-		byte_stream_put16(&data, args->charset);
-		byte_stream_put16(&data, args->charsubset);
-
-		/* Message.  Not terminated */
-		byte_stream_putraw(&data, (guchar *)args->msg, args->msglen);
-	}
+	byte_stream_put16(&data, args->featureslen);
+	byte_stream_putraw(&data, args->features, args->featureslen);
+
+	/* Insert message text in a TLV (type 0x0101) */
+	byte_stream_put16(&data, 0x0101);
+
+	/* Message block length */
+	byte_stream_put16(&data, args->msglen + 0x04);
+
+	/* Character set */
+	byte_stream_put16(&data, args->charset);
+	/* Character subset -- we always use 0 here */
+	byte_stream_put16(&data, 0x0);
+
+	/* Message.  Not terminated */
+	byte_stream_putraw(&data, (guchar *)args->msg, args->msglen);
 
 	/* Set the Autoresponse flag */
 	if (args->flags & AIM_IMFLAGS_AWAY) {
 		byte_stream_put16(&data, 0x0004);
 		byte_stream_put16(&data, 0x0000);
 	} else {
-		if (args->flags & AIM_IMFLAGS_ACK) {
-			/* Set the Request Acknowledge flag */
-			byte_stream_put16(&data, 0x0003);
-			byte_stream_put16(&data, 0x0000);
-		}
+		/* Set the Request Acknowledge flag */
+		byte_stream_put16(&data, 0x0003);
+		byte_stream_put16(&data, 0x0000);
 
 		if (args->flags & AIM_IMFLAGS_OFFLINE) {
 			/* Allow this message to be queued as an offline message */
@@ -534,33 +470,6 @@
 }
 
 /*
- * Simple wrapper for aim_im_sendch1_ext()
- *
- * You cannot use aim_send_im if you need the HASICON flag.  You must
- * use aim_im_sendch1_ext directly for that.
- *
- * aim_send_im also cannot be used if you require UNICODE messages, because
- * that requires an explicit message length.  Use aim_im_sendch1_ext().
- *
- */
-int aim_im_sendch1(OscarData *od, const char *bn, guint16 flags, const char *msg)
-{
-	struct aim_sendimext_args args;
-
-	args.destbn = bn;
-	args.flags = flags;
-	args.msg = msg;
-	args.msglen = strlen(msg);
-	args.charset = 0x0000;
-	args.charsubset = 0x0000;
-
-	/* Make these don't get set by accident -- they need aim_im_sendch1_ext */
-	args.flags &= ~(AIM_IMFLAGS_CUSTOMFEATURES | AIM_IMFLAGS_HASICON | AIM_IMFLAGS_MULTIPART);
-
-	return aim_im_sendch1_ext(od, &args);
-}
-
-/*
  * Subtype 0x0006 - Send a chat invitation.
  */
 int aim_im_sendch2_chatinvite(OscarData *od, const char *bn, const char *msg, guint16 exchange, const char *roomname, guint16 instance)
@@ -1352,75 +1261,6 @@
 }
 
 /*
- * XXX - I don't see when this would ever get called...
- */
-static int outgoingim(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	guchar cookie[8];
-	guint16 channel;
-	GSList *tlvlist;
-	char *bn;
-	int bnlen;
-	guint16 icbmflags = 0;
-	guint8 flag1 = 0, flag2 = 0;
-	gchar *msg = NULL;
-	aim_tlv_t *msgblock;
-
-	/* ICBM Cookie. */
-	aim_icbm_makecookie(cookie);
-
-	/* Channel ID */
-	channel = byte_stream_get16(bs);
-
-	if (channel != 0x01) {
-		purple_debug_misc("oscar", "icbm: ICBM received on unsupported channel.  Ignoring. (chan = %04x)\n", channel);
-		return 0;
-	}
-
-	bnlen = byte_stream_get8(bs);
-	bn = byte_stream_getstr(bs, bnlen);
-
-	tlvlist = aim_tlvlist_read(bs);
-
-	if (aim_tlv_gettlv(tlvlist, 0x0003, 1))
-		icbmflags |= AIM_IMFLAGS_ACK;
-	if (aim_tlv_gettlv(tlvlist, 0x0004, 1))
-		icbmflags |= AIM_IMFLAGS_AWAY;
-
-	if ((msgblock = aim_tlv_gettlv(tlvlist, 0x0002, 1))) {
-		ByteStream mbs;
-		int featurelen, msglen;
-
-		byte_stream_init(&mbs, msgblock->value, msgblock->length);
-
-		byte_stream_get8(&mbs);
-		byte_stream_get8(&mbs);
-		for (featurelen = byte_stream_get16(&mbs); featurelen; featurelen--)
-			byte_stream_get8(&mbs);
-		byte_stream_get8(&mbs);
-		byte_stream_get8(&mbs);
-
-		msglen = byte_stream_get16(&mbs) - 4; /* final block length */
-
-		flag1 = byte_stream_get16(&mbs);
-		flag2 = byte_stream_get16(&mbs);
-
-		msg = byte_stream_getstr(&mbs, msglen);
-	}
-
-	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
-		ret = userfunc(od, conn, frame, channel, bn, msg, icbmflags, flag1, flag2);
-
-	g_free(bn);
-	g_free(msg);
-	aim_tlvlist_free(tlvlist);
-
-	return ret;
-}
-
-/*
  * Ahh, the joys of nearly ridiculous over-engineering.
  *
  * Not only do AIM ICBM's support multiple channels.  Not only do they
@@ -1624,8 +1464,6 @@
 
 	} /* while */
 
-	args->icbmflags |= AIM_IMFLAGS_MULTIPART; /* always set */
-
 	/*
 	 * Clients that support multiparts should never use args->msg, as it
 	 * will point to an arbitrary section.
@@ -2639,16 +2477,10 @@
 }
 
 /*
- * Subtype 0x000c - Receive an ack after sending an ICBM.
- *
- * You have to have send the message with the AIM_IMFLAGS_ACK flag set
- * (TLV t(0003)).  The ack contains the ICBM header of the message you
- * sent.
- *
+ * Subtype 0x000c - Receive an ack after sending an ICBM. The ack contains the ICBM header of the message you sent.
  */
 static int msgack(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 {
-	aim_rxcallback_t userfunc;
 	guint16 ch;
 	guchar *cookie;
 	char *bn;
@@ -2658,8 +2490,7 @@
 	ch = byte_stream_get16(bs);
 	bn = byte_stream_getstr(bs, byte_stream_get8(bs));
 
-	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
-		ret = userfunc(od, conn, frame, ch, bn);
+	purple_debug_info("oscar", "Sent message to %s.\n", bn);
 
 	g_free(bn);
 	g_free(cookie);
@@ -2952,8 +2783,6 @@
 		return error(od, conn, mod, frame, snac, bs);
 	else if (snac->subtype == 0x0005)
 		return aim_im_paraminfo(od, conn, mod, frame, snac, bs);
-	else if (snac->subtype == 0x0006)
-		return outgoingim(od, conn, mod, frame, snac, bs);
 	else if (snac->subtype == 0x0007)
 		return incomingim(od, conn, mod, frame, snac, bs);
 	else if (snac->subtype == 0x000a)
--- a/libpurple/protocols/oscar/oscar.c	Mon Jul 26 17:02:32 2010 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Tue Jul 27 21:17:01 2010 +0000
@@ -108,7 +108,6 @@
 static int purple_conv_chat_incoming_msg(OscarData *, FlapConnection *, FlapFrame *, ...);
 static int purple_email_parseupdate(OscarData *, FlapConnection *, FlapFrame *, ...);
 static int purple_icon_parseicon   (OscarData *, FlapConnection *, FlapFrame *, ...);
-static int purple_parse_msgack     (OscarData *, FlapConnection *, FlapFrame *, ...);
 static int purple_parse_evilnotify (OscarData *, FlapConnection *, FlapFrame *, ...);
 static int purple_parse_searcherror(OscarData *, FlapConnection *, FlapFrame *, ...);
 static int purple_parse_searchreply(OscarData *, FlapConnection *, FlapFrame *, ...);
@@ -666,7 +665,6 @@
 	oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_MISSEDCALL, purple_parse_misses, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_CLIENTAUTORESP, purple_parse_clientauto, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_MTN, purple_parse_mtn, 0);
-	oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_ACK, purple_parse_msgack, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_LOCATE, SNAC_SUBTYPE_LOCATE_RIGHTSINFO, purple_parse_locaterights, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, 0x0001, purple_parse_genericerr, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, 0x000f, purple_selfinfo, 0);
@@ -2724,24 +2722,6 @@
 	purple_debug_misc("oscar", "no more icons to request\n");
 }
 
-/*
- * Received in response to an IM sent with the AIM_IMFLAGS_ACK option.
- */
-static int purple_parse_msgack(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
-	va_list ap;
-	guint16 type;
-	char *bn;
-
-	va_start(ap, fr);
-	type = (guint16) va_arg(ap, unsigned int);
-	bn = va_arg(ap, char *);
-	va_end(ap);
-
-	purple_debug_info("oscar", "Sent message to %s.\n", bn);
-
-	return 1;
-}
-
 static int purple_parse_evilnotify(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
 #ifdef CRAZY_WARNING
 	va_list ap;
@@ -3183,8 +3163,8 @@
 	GString *msg;
 	GString *data;
 	gchar *tmp;
-	int tmplen;
-	guint16 charset, charsubset;
+	gsize tmplen;
+	guint16 charset;
 	GData *attribs;
 	const char *start, *end, *last;
 	int oscar_id = 0;
@@ -3245,8 +3225,7 @@
 	g_string_append(msg, "</BODY></HTML>");
 
 	/* Convert the message to a good encoding */
-	oscar_convert_to_best_encoding(conn->od->gc,
-			conn->bn, msg->str, &tmp, &tmplen, &charset, &charsubset);
+	tmp = oscar_convert_to_best_encoding(msg->str, &tmplen, &charset, NULL);
 	g_string_free(msg, TRUE);
 	msg = g_string_new_len(tmp, tmplen);
 	g_free(tmp);
@@ -3326,7 +3305,7 @@
 			g_hash_table_insert(od->buddyinfo, g_strdup(purple_normalize(account, name)), bi);
 		}
 
-		args.flags = AIM_IMFLAGS_ACK | AIM_IMFLAGS_CUSTOMFEATURES;
+		args.flags = 0;
 
 		if (!is_sms && (!buddy || !PURPLE_BUDDY_IS_ONLINE(buddy)))
 			args.flags |= AIM_IMFLAGS_OFFLINE;
@@ -3395,7 +3374,7 @@
 		g_free(tmp1);
 		tmp1 = tmp2;
 
-		oscar_convert_to_best_encoding(gc, name, tmp1, (char **)&args.msg, &args.msglen, &args.charset, &args.charsubset);
+		args.msg = oscar_convert_to_best_encoding(tmp1, &args.msglen, &args.charset, NULL);
 		if (is_html && (args.msglen > MAXMSGLEN)) {
 			/* If the length was too long, try stripping the HTML and then running it back through
 			* purple_strdup_withhtml() and the encoding process. The result may be shorter. */
@@ -3412,14 +3391,12 @@
 			g_free(tmp1);
 			tmp1 = tmp2;
 
-			oscar_convert_to_best_encoding(gc, name, tmp1, (char **)&args.msg, &args.msglen, &args.charset, &args.charsubset);
-
+			args.msg = oscar_convert_to_best_encoding(tmp1, &args.msglen, &args.charset, NULL);
 			purple_debug_info("oscar", "Sending %s as %s because the original was too long.\n",
 								  message, (char *)args.msg);
 		}
 
-		purple_debug_info("oscar", "Sending IM, charset=0x%04hx, charsubset=0x%04hx, length=%d\n",
-						args.charset, args.charsubset, args.msglen);
+		purple_debug_info("oscar", "Sending IM, charset=0x%04hx, length=%" G_GSIZE_FORMAT "\n", args.charset, args.msglen);
 		ret = aim_im_sendch1_ext(od, &args);
 		g_free((char *)args.msg);
 	}
@@ -3461,28 +3438,6 @@
 	aim_srv_setidle(od, time);
 }
 
-static
-gchar *purple_prpl_oscar_convert_to_infotext(const gchar *str, gsize *ret_len, char **encoding)
-{
-	int charset = 0;
-	char *encoded = NULL;
-
-	charset = oscar_charset_check(str);
-	if (charset == AIM_CHARSET_UNICODE) {
-		encoded = g_convert(str, -1, "UTF-16BE", "UTF-8", NULL, ret_len, NULL);
-		*encoding = "unicode-2-0";
-	} else if (charset == AIM_CHARSET_LATIN_1) {
-		encoded = g_convert(str, -1, "ISO-8859-1", "UTF-8", NULL, ret_len, NULL);
-		*encoding = "iso-8859-1";
-	} else {
-		encoded = g_strdup(str);
-		*ret_len = strlen(str);
-		*encoding = "us-ascii";
-	}
-
-	return encoded;
-}
-
 void
 oscar_set_info(PurpleConnection *gc, const char *rawinfo)
 {
@@ -3586,7 +3541,7 @@
 	else if (rawinfo != NULL)
 	{
 		char *htmlinfo = purple_strdup_withhtml(rawinfo);
-		info = purple_prpl_oscar_convert_to_infotext(htmlinfo, &infolen, &info_encoding);
+		info = oscar_convert_to_best_encoding(htmlinfo, &infolen, NULL, &info_encoding);
 		g_free(htmlinfo);
 
 		if (infolen > od->rights.maxsiglen)
@@ -3619,7 +3574,7 @@
 
 			/* We do this for icq too so that they work for old third party clients */
 			linkified = purple_markup_linkify(status_html);
-			away = purple_prpl_oscar_convert_to_infotext(linkified, &awaylen, &away_encoding);
+			away = oscar_convert_to_best_encoding(linkified, &awaylen, NULL, &away_encoding);
 			g_free(linkified);
 
 			if (awaylen > od->rights.maxawaymsglen)
@@ -4592,9 +4547,9 @@
 	PurpleConversation *conv = NULL;
 	struct chat_connection *c = NULL;
 	char *buf, *buf2, *buf3;
-	guint16 charset, charsubset;
-	char *charsetstr = NULL;
-	int len;
+	guint16 charset;
+	char *charsetstr;
+	gsize len;
 
 	if (!(conv = purple_find_chat(gc, id)))
 		return -EINVAL;
@@ -4610,7 +4565,7 @@
 			  "You cannot send IM Images in AIM chats."),
 			PURPLE_MESSAGE_ERROR, time(NULL));
 
-	oscar_convert_to_best_encoding(gc, NULL, buf, &buf2, &len, &charset, &charsubset);
+	buf2 = oscar_convert_to_best_encoding(buf, &len, &charset, &charsetstr);
 	/*
 	 * Evan S. suggested that maxvis really does mean "number of
 	 * visible characters" and not "number of bytes"
@@ -4626,10 +4581,11 @@
 		buf = purple_strdup_withhtml(buf3);
 		g_free(buf3);
 
-		oscar_convert_to_best_encoding(gc, NULL, buf, &buf2, &len, &charset, &charsubset);
+		buf2 = oscar_convert_to_best_encoding(buf, &len, &charset, &charsetstr);
 
 		if ((len > c->maxlen) || (len > c->maxvis)) {
-			purple_debug_warning("oscar", "Could not send %s because (%i > maxlen %i) or (%i > maxvis %i)\n",
+			purple_debug_warning("oscar",
+					"Could not send %s because (%" G_GSIZE_FORMAT " > maxlen %i) or (%" G_GSIZE_FORMAT " > maxvis %i)\n",
 					buf2, len, c->maxlen, len, c->maxvis);
 			g_free(buf);
 			g_free(buf2);
@@ -4640,12 +4596,6 @@
 				message, buf2);
 	}
 
-	if (charset == AIM_CHARSET_ASCII)
-		charsetstr = "us-ascii";
-	else if (charset == AIM_CHARSET_UNICODE)
-		charsetstr = "unicode-2-0";
-	else if (charset == AIM_CHARSET_LATIN_1)
-		charsetstr = "iso-8859-1";
 	aim_chat_send_im(od, c->conn, 0, buf2, len, charsetstr, "en");
 	g_free(buf2);
 	g_free(buf);
--- a/libpurple/protocols/oscar/oscar.h	Mon Jul 26 17:02:32 2010 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Tue Jul 27 21:17:01 2010 +0000
@@ -845,7 +845,6 @@
 #define AIM_IMFLAGS_CUSTOMFEATURES		0x0080 /* features field present */
 #define AIM_IMFLAGS_EXTDATA				0x0100
 #define AIM_IMFLAGS_X					0x0200
-#define AIM_IMFLAGS_MULTIPART			0x0400 /* ->mpmsg section valid */
 #define AIM_IMFLAGS_OFFLINE				0x0800 /* send to offline user */
 #define AIM_IMFLAGS_TYPINGNOT			0x1000 /* typing notification */
 
@@ -885,30 +884,22 @@
  */
 struct aim_sendimext_args
 {
-
 	/* These are _required_ */
 	const char *destbn;
 	guint32 flags; /* often 0 */
 
-	/* Only required if not using multipart messages */
 	const char *msg;
-	int msglen;
-
-	/* Required if ->msg is not provided */
-	aim_mpmsg_t *mpmsg;
+	gsize msglen;
 
 	/* Only used if AIM_IMFLAGS_HASICON is set */
 	guint32 iconlen;
 	time_t iconstamp;
 	guint32 iconsum;
 
-	/* Only used if AIM_IMFLAGS_CUSTOMFEATURES is set */
 	guint16 featureslen;
 	guint8 *features;
 
-	/* Only used if AIM_IMFLAGS_CUSTOMCHARSET is set and mpmsg not used */
 	guint16 charset;
-	guint16 charsubset;
 };
 
 /*
@@ -926,18 +917,12 @@
  * This information is provided in the Incoming ICBM callback for
  * Channel 1 ICBM's.
  *
- * Note that although CUSTOMFEATURES and CUSTOMCHARSET say they
- * are optional, both are always set by the current libfaim code.
- * That may or may not change in the future.  It is mainly for
- * consistency with aim_sendimext_args.
- *
  * Multipart messages require some explanation. If you want to use them,
  * I suggest you read all the comments in family_icbm.c.
  *
  */
 struct aim_incomingim_ch1_args
 {
-
 	/* Always provided */
 	aim_mpmsg_t mpmsg;
 	guint32 icbmflags; /* some flags apply only to ->msg, not all mpmsg */