changeset 27806:07915e7a4c08

propagate from branch 'im.pidgin.pidgin' (head ccf8998cb268f07fd8ef3f817b6cb5f1bc32cf02) to branch 'im.pidgin.pidgin.yaz' (head 91de8e43f6b89d2bb2ca63b700acf3395cdf0e57)
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Thu, 20 Mar 2008 11:57:02 +0000
parents 80d3f4ff1cae (diff) 49deb72ffdc1 (current diff)
children aceb6c9734a4
files configure.ac
diffstat 40 files changed, 1071 insertions(+), 138 deletions(-) [+]
line wrap: on
line diff
--- a/autogen.sh	Thu Mar 20 08:42:15 2008 +0000
+++ b/autogen.sh	Thu Mar 20 11:57:02 2008 +0000
@@ -69,5 +69,5 @@
 echo;
 echo "Running ./configure ${CONFIGURE_ARGS} $@"
 echo;
-./configure ${CONFIGURE_ARGS} $@
+#./configure ${CONFIGURE_ARGS} $@
 
--- a/configure.ac	Thu Mar 20 08:42:15 2008 +0000
+++ b/configure.ac	Thu Mar 20 11:57:02 2008 +0000
@@ -136,7 +136,7 @@
 	;;
 esac
 
-ALL_LINGUAS="af am ar az be@latin bg bn bs ca ca@valencia cs da de dz el en_AU en_CA en_GB eo es et eu fa fi fr gl gu he hi hu id it ja ka kn ko ku lo lt mk my_MM nb ne nl nn pa pl pt_BR pt ps ro ru si sk sl sq sr sr@latin sv ta te th tr uk ur vi xh zh_CN zh_HK zh_TW"
+ALL_LINGUAS=""
 AM_GLIB_GNU_GETTEXT
 
 dnl If we don't have msgfmt, then po/ is going to fail -- ensure that
@@ -891,8 +891,7 @@
 AC_SUBST(GADU_CFLAGS)
 
 # uncomment the next line to make MSNP14 the available
-# AC_ARG_ENABLE(msnp14,[AC_HELP_STRING([--enable-msnp14], [Disable the newer MSNP14 protocol])],,enable_msnp14=no)
-enable_msnp14=no
+AC_ARG_ENABLE(msnp14,[AC_HELP_STRING([--enable-msnp14], [Enable the newer MSNP14 protocol])],,enable_msnp14=no)
 
 AC_ARG_ENABLE(distrib,,,enable_distrib=no)
 AM_CONDITIONAL(DISTRIB, test "x$enable_distrib" = "xyes")
--- a/libpurple/conversation.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/conversation.c	Thu Mar 20 11:57:02 2008 +0000
@@ -656,6 +656,8 @@
 	PurpleBuddy *b;
 	PurpleChat *chat;
 	const char *text = NULL, *name;
+	gchar *utf8;
+	gsize dummy;
 
 	g_return_if_fail(conv != NULL);
 
@@ -674,7 +676,9 @@
 	if(text == NULL)
 		text = name;
 
-	purple_conversation_set_title(conv, text);
+	utf8 = sanitize_utf(text, strlen(text), &dummy);
+	purple_conversation_set_title(conv, utf8);
+	g_free(utf8);
 }
 
 void
@@ -1149,17 +1153,27 @@
 			  PurpleMessageFlags flags, time_t mtime)
 {
 	PurpleConversation *c;
+	char *tmpmessage = NULL;
 
 	g_return_if_fail(im != NULL);
 	g_return_if_fail(message != NULL);
 
 	c = purple_conv_im_get_conversation(im);
 
+	// yaz
+	if (purple_prefs_get_bool("/purple/conversations/msnstyle")) {
+		tmpmessage = g_strdup_printf("<br>%s", message);
+	} else {
+		tmpmessage = g_strdup_printf("%s", message);
+	}
+
 	/* Pass this on to either the ops structure or the default write func. */
 	if (c->ui_ops != NULL && c->ui_ops->write_im != NULL)
-		c->ui_ops->write_im(c, who, message, flags, mtime);
+		c->ui_ops->write_im(c, who, tmpmessage, flags, mtime);
 	else
-		purple_conversation_write(c, who, message, flags, mtime);
+		purple_conversation_write(c, who, tmpmessage, flags, mtime);
+
+	g_free(tmpmessage);
 }
 
 gboolean purple_conv_present_error(const char *who, PurpleAccount *account, const char *what)
--- a/libpurple/notify.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/notify.c	Thu Mar 20 11:57:02 2008 +0000
@@ -458,6 +458,9 @@
 		purple_signal_emit(purple_notify_get_handle(), "displaying-userinfo",
 						 purple_connection_get_account(gc), who, user_info);
 
+		g_return_val_if_fail(g_utf8_validate(who, -1, NULL), NULL); //yaz
+		g_return_val_if_fail(g_utf8_validate(user_info, -1, NULL), NULL); //yaz
+
 		info->ui_handle = ops->notify_userinfo(gc, who, user_info);
 		info->cb = cb;
 		info->cb_user_data = user_data;
--- a/libpurple/protocols/gg/lib/http.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/gg/lib/http.c	Thu Mar 20 11:57:02 2008 +0000
@@ -44,6 +44,7 @@
 
 #include "compat.h"
 #include "libgadu.h"
+#include <glib.h>
 
 /*
  * gg_http_connect() // funkcja pomocnicza
--- a/libpurple/protocols/gg/lib/pubdir50.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/gg/lib/pubdir50.c	Thu Mar 20 11:57:02 2008 +0000
@@ -22,6 +22,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
+#include <glib.h>
 
 #include "libgadu.h"
 
--- a/libpurple/protocols/irc/irc.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/irc/irc.c	Thu Mar 20 11:57:02 2008 +0000
@@ -971,6 +971,12 @@
 	option = purple_account_option_string_new(_("Encodings"), "encoding", IRC_DEFAULT_CHARSET);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
+	option = purple_account_option_bool_new(_("Use SO/SI to send hankaku kana"), "irc_use_sosi", FALSE);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = purple_account_option_bool_new(_("Use 8bit to send hankaku kana"), "irc_use_8bit", FALSE);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
 	option = purple_account_option_string_new(_("Username"), "username", "");
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
--- a/libpurple/protocols/irc/parse.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/irc/parse.c	Thu Mar 20 11:57:02 2008 +0000
@@ -35,7 +35,7 @@
 #include <ctype.h>
 
 static char *irc_send_convert(struct irc_conn *irc, const char *string);
-static char *irc_recv_convert(struct irc_conn *irc, const char *string);
+static char *irc_recv_convert(struct irc_conn *irc, char *string);
 
 static void irc_parse_error_cb(struct irc_conn *irc, char *input);
 
@@ -157,6 +157,20 @@
 	{ NULL, NULL, NULL, NULL }
 };
 
+/* yaz */
+#define ASCII	0
+#define KANJI	1
+#define KANA	2
+#define ROMAN	3
+char seq_ascii[] = {0x1B,0x28,0x42,0x00}; /* ESC ( B */
+char seq_kanji[] = {0x1B,0x24,0x42,0x00}; /* ESC $ B */
+char seq_kana[]  = {0x1B,0x28,0x49,0x00}; /* ESC ( I */
+char seq_roman[] = {0x1B,0x28,0x4A,0x00}; /* ESC ( J */
+char *seq[4] = {seq_ascii, seq_kanji, seq_kana, seq_roman};
+char *jisstate[5] = {"ASCII", "KANJI", "KANA", "ROMAN"};
+char SO[] = {0x0E,0x00};
+char SI[] = {0x0F,0x00};
+
 static PurpleCmdRet irc_parse_purple_cmd(PurpleConversation *conv, const gchar *cmd,
                                         gchar **args, gchar **error, void *data)
 {
@@ -224,33 +238,110 @@
 	GError *err = NULL;
 	gchar **encodings;
 	const gchar *enclist;
+	char *escpos = NULL;
+	char *temp = NULL;
+	gboolean iskana = FALSE;
+	char *pos = NULL;
+	gboolean irc_use_sosi, irc_use_8bit;
+	char *strtmp;
+	gsize strtmp_len;
 
 	enclist = purple_account_get_string(irc->account, "encoding", IRC_DEFAULT_CHARSET);
 	encodings = g_strsplit(enclist, ",", 2);
 
+	irc_use_sosi = purple_account_get_bool(irc->account, "irc_use_sosi", FALSE);
+	irc_use_8bit = purple_account_get_bool(irc->account, "irc_use_8bit", FALSE);
+
 	if (encodings[0] == NULL || !g_ascii_strcasecmp("UTF-8", encodings[0])) {
 		g_strfreev(encodings);
 		return g_strdup(string);
 	}
 
-	utf8 = g_convert(string, strlen(string), encodings[0], "UTF-8", NULL, NULL, &err);
+	strtmp  = botch_utf(string, strlen(string), &strtmp_len);
+	utf8 = g_convert(strtmp, strlen(strtmp), encodings[0], "UTF-8", NULL, NULL, &err);
+
 	if (err) {
 		purple_debug(PURPLE_DEBUG_ERROR, "irc", "Send conversion error: %s\n", err->message);
 		purple_debug(PURPLE_DEBUG_ERROR, "irc", "Sending as UTF-8 instead of %s\n", encodings[0]);
-		utf8 = g_strdup(string);
+		utf8 = g_strdup(strtmp);
 		g_error_free(err);
 	}
+
+	/* yaz */
+	if (!strncasecmp("iso-2022-jp", encodings[0], strlen("iso-2022-jp"))) {
+		escpos = strrchr(utf8, 0x1B);
+		if(escpos && (!strncmp(seq_kanji, escpos, 3) || !strncmp(seq_kana, escpos, 3))){
+			char *oldutf8 = utf8;
+			utf8 = g_realloc(utf8, strlen(utf8)+1+3);
+			if(utf8)
+				strncat(utf8, seq_ascii, 3);
+			else
+				utf8 = oldutf8;
+		}
+
+		if(irc_use_sosi || irc_use_8bit){
+			/* SO/SI */
+			//find kana escape and replace with roman+SO
+			temp = g_malloc0(strlen(utf8) * 7); //XXX should be reasonable size
+			pos = utf8;
+			while(pos < utf8+strlen(utf8)){
+				escpos = strchr(pos, 0x1B);
+				if(escpos){
+					if(!strncmp(seq_kana, escpos, 3)){ /* kana found */
+						iskana = TRUE;
+						strncat(temp, pos, escpos-pos);
+						strcat(temp, seq_roman);
+						if(irc_use_sosi)
+							strcat(temp, SO);
+						pos = escpos+3;
+					} else {
+						if(iskana){
+							char *ptr;
+							ptr = temp + strlen(temp);
+							while(pos<escpos){
+								if(irc_use_8bit)
+									*ptr = *pos + 128; // convert to 8bit
+								else
+									*ptr = *pos;
+								ptr++; pos++;
+							}
+							if(irc_use_sosi)
+								strcat(temp, SI);
+							strncat(temp, escpos, 3);
+							pos = escpos+3;
+							iskana = FALSE;
+						} else {
+							strncat(temp, pos, escpos-pos+3); //include esc
+							pos = escpos+3;
+						}
+					}
+				} else { /* escpos == NULL */
+					strcat(temp, pos);
+					break;
+				}
+			}
+			g_free(utf8);
+			utf8 = temp;
+		}
+	}
+
 	g_strfreev(encodings);
-
+	g_free(strtmp);
 	return utf8;
 }
 
-static char *irc_recv_convert(struct irc_conn *irc, const char *string)
+static char *irc_recv_convert(struct irc_conn *irc, char *string)
 {
 	char *utf8 = NULL;
 	const gchar *charset, *enclist;
 	gchar **encodings;
 	int i;
+	GError *err;
+	gboolean retry;
+	gsize in_len, out_len;
+	int conv_len;
+	char *strtmp;
+	gsize strtmp_len;
 
 	enclist = purple_account_get_string(irc->account, "encoding", IRC_DEFAULT_CHARSET);
 	encodings = g_strsplit(enclist, ",", -1);
@@ -268,13 +359,123 @@
 		if (!g_ascii_strcasecmp("UTF-8", charset)) {
 			if (g_utf8_validate(string, -1, NULL))
 				utf8 = g_strdup(string);
+		} else if (!strncasecmp("iso-2022-jp-2", charset, strlen("iso-2022-jp-2"))){
+			/* pre-process quirky jis */
+			unsigned char *jisstr;
+			unsigned char *ptr, *ptr2;
+			int state = ASCII;
+			int is8bit = FALSE;
+
+			jisstr = (unsigned char *)calloc(1, strlen(string)*7); /* enough? */
+			ptr = (unsigned char *)string; ptr2 = jisstr;
+
+			while(*ptr){
+				if(*ptr == 0x1B){
+					/* escape sequence. */
+					if(*(ptr+1) == 0x28 && *(ptr+2) == 0x42){
+						state = ASCII;
+
+					} else if(*(ptr+1) == 0x24 && *(ptr+2) == 0x42){
+						state = KANJI;
+
+					} else if(*(ptr+1) == 0x28 && *(ptr+2) == 0x49){
+						state = KANA;
+
+					} else if(*(ptr+1) == 0x28 && *(ptr+2) == 0x4a){
+						state = ROMAN;
+
+					}
+					purple_debug(PURPLE_DEBUG_INFO, "irc", "state %s\n", jisstate[state]);
+				}
+				if(*ptr >= 0xA1 && *ptr <= 0xDF){
+					/* raw 8bit */
+					if(!is8bit){
+						strcat((char *)jisstr, seq[KANA]);
+						ptr2 += 3;
+						is8bit = TRUE;
+						purple_debug(PURPLE_DEBUG_INFO, "irc", "8bit = TRUE\n");
+					}
+					*ptr2 = *ptr - 0x80;
+					ptr++ ; ptr2++;
+				} else {
+					/* 7bit */
+					if(*ptr == 0x0E){
+						/* SO */
+						strcat((char *)jisstr, seq[KANA]);
+						ptr++; ptr2 += 3;
+						purple_debug(PURPLE_DEBUG_INFO, "irc", "SO\n");
+						continue;
+					} else if(*ptr == 0x0F){
+						/* SI */
+						strcat((char *)jisstr, seq[state]);
+						purple_debug(PURPLE_DEBUG_INFO, "irc", "SI to %s\n", jisstate[state]);
+						ptr++; ptr2 += 3;
+						purple_debug(PURPLE_DEBUG_INFO, "irc", "SI\n");
+						continue;
+					}
+					if(is8bit){ /* the edge of 8bit -> 7bit */
+						purple_debug(PURPLE_DEBUG_INFO, "irc", "8bit to %s\n", jisstate[state]);
+						strcat((char *)jisstr, seq[state]);
+						ptr2 += 3;
+						is8bit=FALSE;
+						purple_debug(PURPLE_DEBUG_INFO, "irc", "8bit = FALSE\n");
+					}
+					/* copy str */
+					*ptr2 = *ptr;
+					ptr++; ptr2++;
+				}
+			}
+
+			/* convert & error recovery */
+			do {
+				err = NULL;
+				retry = FALSE;
+
+				conv_len = strlen((char *)jisstr);
+				utf8 = g_convert_with_fallback((char *)jisstr, conv_len, "UTF-8", charset,
+							       "?", &in_len, &out_len, &err);
+				if(err != NULL){
+					if(err->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE){
+						memmove(jisstr + in_len, jisstr + in_len + 1,
+							conv_len - in_len -1);
+						conv_len--;
+						*(jisstr + conv_len) = '\0';
+						retry = TRUE;
+					}
+					g_error_free(err);
+				}
+			} while(retry);
+
+			if(jisstr)
+				free(jisstr);
+
 		} else {
-			utf8 = g_convert(string, -1, "UTF-8", charset, NULL, NULL, NULL);
+			do {
+				err = NULL;
+				retry = FALSE;
+
+				conv_len = strlen(string);
+				utf8 = g_convert_with_fallback(string, conv_len, "UTF-8", charset,
+							       "?", &in_len, &out_len, &err);
+				if(err != NULL){
+					if(err->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE){
+						memmove(string + in_len, string + in_len + 1,
+							 conv_len - in_len -1);
+						conv_len--;
+						*(string + conv_len) = '\0';
+						retry = TRUE;
+					}
+					g_error_free(err);
+				}
+			} while(retry);
 		}
 
-		if (utf8) {
-			g_strfreev(encodings);
-			return utf8;
+
+		if(utf8){
+			strtmp = sanitize_utf(utf8, strlen(utf8), &strtmp_len);
+ 			g_strfreev(encodings);
+			g_free(utf8);
+			return strtmp;
 		}
 	}
 	g_strfreev(encodings);
--- a/libpurple/protocols/jabber/jabber.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Thu Mar 20 11:57:02 2008 +0000
@@ -374,8 +374,9 @@
 
 void jabber_send(JabberStream *js, xmlnode *packet)
 {
-	char *txt;
+	char *txt, *utf;
 	int len;
+	gsize utflen;
 
 	purple_signal_emit(my_protocol, "jabber-sending-xmlnode", js->gc, &packet);
 
@@ -384,8 +385,9 @@
 		return;
 
 	txt = xmlnode_to_str(packet, &len);
-	jabber_send_raw(js, txt, len);
-	g_free(txt);
+	utf = botch_utf(txt, len, &utflen); //yaz
+	jabber_send_raw(js, utf, utflen);
+	g_free(txt); g_free(utf);
 }
 
 static void jabber_pong_cb(JabberStream *js, xmlnode *packet, gpointer timeout) 
--- a/libpurple/protocols/jabber/message.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/jabber/message.c	Thu Mar 20 11:57:02 2008 +0000
@@ -361,14 +361,19 @@
 				jm->thread_id = xmlnode_get_data(child);
 		} else if(!strcmp(child->name, "body") && !strcmp(xmlns,"jabber:client")) {
 			if(!jm->body) {
-				char *msg = xmlnode_to_str(child, NULL);
+				char *tmp, *msg;
+				gsize len;
+				tmp = xmlnode_to_str(child, NULL);
+				msg = sanitize_utf(tmp, strlen(tmp), &len);
 				jm->body = purple_strdup_withhtml(msg);
-				g_free(msg);
+				g_free(msg); g_free(tmp);
 			}
 		} else if(!strcmp(child->name, "html") && !strcmp(xmlns,"http://jabber.org/protocol/xhtml-im")) {
-			if(!jm->xhtml && xmlnode_get_child(child, "body")) {
-				char *c;
-				jm->xhtml = xmlnode_to_str(child, NULL);
+			if(!jm->xhtml && xmlnode_get_child(child, "body")){
+				char *tmp, *c;
+				gsize len;
+				tmp = xmlnode_to_str(child, NULL);
+				jm->xhtml = sanitize_utf(tmp, strlen(tmp), &len);
 			        /* 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
 				 */
@@ -376,6 +381,7 @@
 					if (*c == '\n') 
 						*c = ' ';
 				}
+				g_free(tmp);
 			}
 		} else if(!strcmp(child->name, "active") && !strcmp(xmlns,"http://jabber.org/protocol/chatstates")) {
 			jm->chat_state = JM_STATE_ACTIVE;
--- a/libpurple/protocols/jabber/si.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/jabber/si.c	Thu Mar 20 11:57:02 2008 +0000
@@ -38,6 +38,8 @@
 
 #define STREAMHOST_CONNECT_TIMEOUT 15
 
+#include "util.h"
+
 typedef struct _JabberSIXfer {
 	JabberStream *js;
 
@@ -895,8 +897,17 @@
 	JabberIq *iq;
 	xmlnode *si, *file, *feature, *x, *field, *option, *value;
 	char buf[32];
+	gchar *f1 = NULL, *f2 = NULL;
+	gsize dummy;
 
-	xfer->filename = g_path_get_basename(xfer->local_filename);
+	/* yaz */
+	f1 = g_filename_display_basename(xfer->local_filename);
+	f2 = botch_utf(f1, strlen(f1), &dummy);
+	if(f2){
+		purple_xfer_set_filename(xfer, (char *)f2);
+	}
+	g_free(f1); f1 = NULL;
+	g_free(f2); f2 = NULL;
 
 	iq = jabber_iq_new(jsx->js, JABBER_IQ_SET);
 	xmlnode_set_attrib(iq->node, "to", xfer->who);
--- a/libpurple/protocols/msn/msg.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/msn/msg.c	Thu Mar 20 11:57:02 2008 +0000
@@ -522,9 +522,7 @@
 
 	if (data != NULL && len > 0)
 	{
-		msg->body = g_malloc0(len + 1);
-		memcpy(msg->body, data, len);
-		msg->body_len = len;
+		msg->body = botch_utf((gchar *)data, len, &msg->body_len); /* yaz */
 	}
 	else
 	{
--- a/libpurple/protocols/msn/msn.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/msn/msn.c	Thu Mar 20 11:57:02 2008 +0000
@@ -154,13 +154,18 @@
 	MsnSession *session;
 	PurpleAccount *account;
 	const char *alias;
+	gchar *tmp;
+	gsize dummy;
 
 	session = gc->proto_data;
 	cmdproc = session->notification->cmdproc;
 	account = purple_connection_get_account(gc);
 
-	if(entry && strlen(entry))
-		alias = purple_url_encode(entry);
+	if(entry && strlen(entry)) {
+		tmp = botch_utf(entry, strlen(entry), &dummy);
+		alias = purple_url_encode(tmp);
+		g_free(tmp);
+	}
 	else
 		alias = "";
 
@@ -176,6 +181,8 @@
 	}
 
 	msn_cmdproc_send(cmdproc, "PRP", "MFN %s", alias);
+	// propagate change on server side to local --yaz
+	purple_account_set_alias(account, entry); //oct16 <-- XXX questionable. Sep19.
 
 }
 
@@ -290,6 +297,23 @@
 }
 
 static void
+msn_ipc_init(PurplePlugin *plugin)
+{
+	purple_plugin_ipc_register(plugin, "msn_set_friendly_name",
+				   PURPLE_CALLBACK(msn_act_id),
+				   purple_marshal_VOID__POINTER_POINTER,
+				   purple_value_new(PURPLE_TYPE_UNKNOWN),2,
+				   purple_value_new(PURPLE_TYPE_POINTER),
+				   purple_value_new(PURPLE_TYPE_POINTER));
+}
+
+static void
+msn_ipc_end(PurplePlugin *plugin)
+{
+	purple_plugin_ipc_unregister_all(plugin);
+}
+
+static void
 msn_show_set_home_phone(PurplePluginAction *action)
 {
 	PurpleConnection *gc;
@@ -2148,6 +2172,9 @@
 	msn_switchboard_init();
 	msn_sync_init();
 
+	// yaz
+	msn_ipc_init(plugin);
+
 	return TRUE;
 }
 
@@ -2157,6 +2184,9 @@
 	msn_switchboard_end();
 	msn_sync_end();
 
+	// yaz
+	msn_ipc_end(plugin);
+
 	return TRUE;
 }
 
--- a/libpurple/protocols/msn/switchboard.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/msn/switchboard.c	Thu Mar 20 11:57:02 2008 +0000
@@ -842,7 +842,7 @@
 	char *body_str;
 	char *body_enc;
 	char *body_final;
-	size_t body_len;
+	size_t body_len, new_len;
 	const char *passport;
 	const char *value;
 
@@ -850,7 +850,7 @@
 	swboard = cmdproc->data;
 
 	body = msn_message_get_bin_data(msg, &body_len);
-	body_str = g_strndup(body, body_len);
+	body_str = sanitize_utf(body, body_len, &new_len);
 	body_enc = g_markup_escape_text(body_str, -1);
 	g_free(body_str);
 
@@ -909,13 +909,17 @@
 	}
 	else
 	{
-		serv_got_im(gc, passport, body_final, 0, time(NULL));
+		char *yaz_body_final;
+		yaz_body_final = purple_strreplace(body_final, "\r\n", "<br>"); // replace 0D 0A with <br>
+		purple_debug_info("yaz msn", "yaz_body_final=%s\n", yaz_body_final);
+		serv_got_im(gc, passport, yaz_body_final, 0, time(NULL));
 		if (swboard->conv == NULL)
 		{
 			swboard->conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
 									passport, purple_connection_get_account(gc));
 			swboard->flag |= MSN_SB_FLAG_IM;
 		}
+		g_free(yaz_body_final);
 	}
 
 	g_free(body_final);
--- a/libpurple/protocols/msnp9/msg.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/msnp9/msg.c	Thu Mar 20 11:57:02 2008 +0000
@@ -525,9 +525,7 @@
 
 	if (data != NULL && len > 0)
 	{
-		msg->body = g_malloc0(len + 1);
-		memcpy(msg->body, data, len);
-		msg->body_len = len;
+		msg->body = botch_utf((gchar *)data, len, &msg->body_len); /* yaz */
 	}
 	else
 	{
--- a/libpurple/protocols/msnp9/msn.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/msnp9/msn.c	Thu Mar 20 11:57:02 2008 +0000
@@ -155,13 +155,18 @@
 	MsnSession *session;
 	PurpleAccount *account;
 	const char *alias;
+	gchar *tmp;
+	gsize dummy;
 
 	session = gc->proto_data;
 	cmdproc = session->notification->cmdproc;
 	account = purple_connection_get_account(gc);
 
-	if(entry && strlen(entry))
-		alias = purple_url_encode(entry);
+	if(entry && strlen(entry)) {
+		tmp = botch_utf(entry, strlen(entry), &dummy);
+		alias = purple_url_encode(tmp);
+		g_free(tmp);
+	}
 	else
 		alias = "";
 
--- a/libpurple/protocols/msnp9/switchboard.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/msnp9/switchboard.c	Thu Mar 20 11:57:02 2008 +0000
@@ -834,7 +834,7 @@
 	char *body_str;
 	char *body_enc;
 	char *body_final;
-	size_t body_len;
+	size_t body_len, new_len;
 	const char *passport;
 	const char *value;
 
@@ -842,7 +842,7 @@
 	swboard = cmdproc->data;
 
 	body = msn_message_get_bin_data(msg, &body_len);
-	body_str = g_strndup(body, body_len);
+	body_str = sanitize_utf(body, body_len, &new_len);
 	body_enc = g_markup_escape_text(body_str, -1);
 	g_free(body_str);
 
--- a/libpurple/protocols/oscar/family_icbm.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/oscar/family_icbm.c	Thu Mar 20 11:57:02 2008 +0000
@@ -52,8 +52,8 @@
 #endif
 
 #include "util.h"
-
-
+/* yaz */
+#include "debug.h"
 /**
  * Add a standard ICBM header to the given bstream with the given
  * information.
@@ -461,6 +461,9 @@
 	guchar cookie[8];
 	GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
 	ByteStream hdrbs;
+	// yaz
+	char *ucs = NULL;
+	gsize bytes;
 
 	if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)))
 		return -EINVAL;
@@ -500,15 +503,51 @@
 	 * raw data, followed by a series of TLVs.
 	 *
 	 */
+#if 0
 	byte_stream_new(&hdrbs, 2+8+16+6+4+4+strlen(msg)+4+2+1+strlen(roomname)+2);
 
 	byte_stream_put16(&hdrbs, 0x0000); /* Unknown! */
 	byte_stream_putraw(&hdrbs, cookie, sizeof(cookie)); /* I think... */
 	byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_CHAT);
-
+#endif
+	//yaz
+	// convert msg to ascii first. if it succeed, send as plain ascii.
+	// if it fails, convert msg into UTF-16BE, and send it. 
+	ucs = g_convert(msg, strlen(msg), "ASCII", "UTF-8", NULL, &bytes, NULL);
+	if(ucs){
+		byte_stream_new(&hdrbs, 2+8+16+6+4+4+strlen(msg)+4+2+1+strlen(roomname)+2);
+
+		byte_stream_put16(&hdrbs, 0x0000); /* Unknown! */
+		byte_stream_putraw(&hdrbs, cookie, sizeof(cookie)); /* I think... */
+		byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_CHAT);
+
+		aim_tlvlist_add_16(&inner_tlvlist, 0x000a, 0x0001);
+		aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
+		aim_tlvlist_add_raw(&inner_tlvlist, 0x000c, strlen(msg), (guint8 *)msg);
+		free(ucs);
+	} else {
+		byte_stream_new(&hdrbs, 2+8+16+6+4+4+strlen(msg)+4+2+1+strlen(roomname)+2+4+11);
+
+		byte_stream_put16(&hdrbs, 0x0000); /* Unknown! */
+		byte_stream_putraw(&hdrbs, cookie, sizeof(cookie)); /* I think... */
+		byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_CHAT);
+
+		aim_tlvlist_add_16(&inner_tlvlist, 0x000a, 0x0001);
+		aim_tlvlist_add_raw(&inner_tlvlist, 0x000d, 11, (guint8 *)"unicode-2-0");
+		aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
+		//yaz
+		ucs = g_convert(msg, strlen(msg), "UTF-16BE", "UTF-8", NULL, &bytes, NULL);
+		if(ucs){
+			botch_ucs(ucs, bytes);
+			aim_tlvlist_add_raw(&inner_tlvlist, 0x000c, bytes, (guint8 *)ucs);
+			free(ucs);
+		}
+	}
+#if 0
 	aim_tlvlist_add_16(&inner_tlvlist, 0x000a, 0x0001);
 	aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
 	aim_tlvlist_add_str(&inner_tlvlist, 0x000c, msg);
+#endif
 	aim_tlvlist_add_chatroom(&inner_tlvlist, 0x2711, exchange, roomname, instance);
 	aim_tlvlist_write(&hdrbs, &inner_tlvlist);
 
--- a/libpurple/protocols/oscar/odc.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/oscar/odc.c	Thu Mar 20 11:57:02 2008 +0000
@@ -101,22 +101,28 @@
 	byte_stream_new(&bs, length + frame->payload.len);
 	byte_stream_putraw(&bs, conn->magic, 4);
 	byte_stream_put16(&bs, length);
+
 	byte_stream_put16(&bs, frame->type);
 	byte_stream_put16(&bs, frame->subtype);
 	byte_stream_put16(&bs, 0x0000);
+
 	byte_stream_putraw(&bs, frame->cookie, 8);
 	byte_stream_put16(&bs, 0x0000);
 	byte_stream_put16(&bs, 0x0000);
 	byte_stream_put16(&bs, 0x0000);
 	byte_stream_put16(&bs, 0x0000);
+
 	byte_stream_put32(&bs, frame->payload.len);
-	byte_stream_put16(&bs, 0x0000);
 	byte_stream_put16(&bs, frame->encoding);
 	byte_stream_put16(&bs, 0x0000);
+	byte_stream_put16(&bs, 0x0000);
+
 	byte_stream_put16(&bs, frame->flags);
 	byte_stream_put16(&bs, 0x0000);
 	byte_stream_put16(&bs, 0x0000);
+
 	byte_stream_putraw(&bs, frame->sn, 32);
+
 	byte_stream_putraw(&bs, frame->payload.data, frame->payload.len);
 
 	peer_connection_send(conn, &bs);
@@ -496,13 +502,17 @@
 	frame->type = byte_stream_get16(bs);
 	frame->subtype = byte_stream_get16(bs);
 	byte_stream_advance(bs, 2);
+
 	byte_stream_getrawbuf(bs, frame->cookie, 8);
 	byte_stream_advance(bs, 8);
+
 	frame->payload.len = byte_stream_get32(bs);
 	frame->encoding = byte_stream_get16(bs);
 	byte_stream_advance(bs, 4);
+
 	frame->flags = byte_stream_get16(bs);
 	byte_stream_advance(bs, 4);
+
 	byte_stream_getrawbuf(bs, frame->sn, 32);
 
 	purple_debug_info("oscar", "Incoming ODC frame from %s with "
--- a/libpurple/protocols/oscar/oft.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/oscar/oft.c	Thu Mar 20 11:57:02 2008 +0000
@@ -642,6 +642,8 @@
 {
 	PeerConnection *conn;
 	size_t size;
+	gchar *f1 = NULL, *f2 = NULL;
+	gsize dummy;
 
 	conn = xfer->data;
 	conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
@@ -679,7 +681,16 @@
 	strncpy((gchar *)conn->xferdata.idstring, "Cool FileXfer", 31);
 	conn->xferdata.modtime = 0;
 	conn->xferdata.cretime = 0;
-	xfer->filename = g_path_get_basename(xfer->local_filename);
+
+	/* yaz */
+	f1 = g_filename_display_basename(xfer->local_filename);
+	f2 = botch_utf(f1, strlen(f1), &dummy);
+	if(f2){
+		purple_xfer_set_filename(xfer, (char *)f2);
+	}
+	g_free(f1); f1 = NULL;
+	g_free(f2); f2 = NULL;
+
 	conn->xferdata.name_length = MAX(64, strlen(xfer->filename) + 1);
 	conn->xferdata.name = (guchar *)g_strndup(xfer->filename, conn->xferdata.name_length - 1);
 
--- a/libpurple/protocols/oscar/oscar.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Thu Mar 20 11:57:02 2008 +0000
@@ -304,7 +304,25 @@
 	gchar *utf8 = NULL;
 
 	if ((encoding == NULL) || encoding[0] == '\0') {
-		purple_debug_info("oscar", "Empty encoding, assuming UTF-8\n");
+		purple_debug_info("yaz oscar", "Empty encoding, validate as UTF-8\n");
+		if(g_utf8_validate(text, textlen, NULL)){
+			gsize newlen;
+			utf8 = sanitize_utf(text, textlen, &newlen);
+			goto done;
+		}
+		// not UTF-8
+		purple_debug_info("yaz oscar", "Empty encoding, assuming UTF-16BE\n");
+		sanitize_ucs((gchar *)text, textlen);
+		utf8 = g_convert(text, textlen, "UTF-8", "UTF-16BE", NULL, NULL, NULL);
+		if(utf8){
+			if(!g_utf8_validate(utf8, strlen(utf8), NULL)){
+				purple_debug_info("yaz oscar", "Invalid conversion\n");
+				g_free(utf8);
+				utf8 = NULL;
+			}
+		} else {
+			purple_debug_info("yaz oscar", "Conversion failed\n");
+		}
 	} else if (!g_ascii_strcasecmp(encoding, "iso-8859-1")) {
 		utf8 = g_convert(text, textlen, "UTF-8", "iso-8859-1", NULL, NULL, NULL);
 	} else if (!g_ascii_strcasecmp(encoding, "ISO-8859-1-Windows-3.1-Latin-1") ||
@@ -319,6 +337,7 @@
 		 * and do something (un)reasonable with that, and not
 		 * mess up too much else. */
 		const gchar *charset = purple_account_get_string(account, "encoding", NULL);
+		sanitize_ucs((gchar *)text, textlen);
 		if (charset) {
 			gsize len;
 			utf8 = g_convert(text, textlen, charset, "UTF-16BE", &len, NULL, NULL);
@@ -350,7 +369,7 @@
 		else
 			utf8 = g_strndup(text, textlen);
 	}
-
+done:
 	return utf8;
 }
 
@@ -375,7 +394,7 @@
 static gchar *
 purple_plugin_oscar_convert_to_utf8(const gchar *data, gsize datalen, const char *charsetstr, gboolean fallback)
 {
-	gchar *ret = NULL;
+	gchar *ret = NULL, *ret2 = NULL;
 	GError *err = NULL;
 
 	if ((charsetstr == NULL) || (*charsetstr == '\0'))
@@ -398,7 +417,9 @@
 			purple_debug_warning("oscar", "String is not valid UTF-8.\n");
 	}
 
-	return ret;
+	ret2 = sanitize_utf(ret, -1, NULL);
+	g_free(ret);
+	return ret2;
 }
 
 /**
@@ -434,11 +455,11 @@
 	} else if (charset == AIM_CHARSET_ASCII) {
 		/* Should just be "ASCII" */
 		charsetstr1 = "ASCII";
-		charsetstr2 = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
-	} else if (charset == 0x000d) {
+		charsetstr2 = "UTF-8";
+	} else if (charset == AIM_CHARSET_QUIRKUTF8) {
 		/* Mobile AIM client on a Nokia 3100 and an LG VX6000 */
-		charsetstr1 = "ISO-8859-1";
-		charsetstr2 = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
+		charsetstr1 = "UTF-8";  //iChat use 0x000d when it sends UTF-8. --yaz
+		charsetstr2 = "ISO-8859-1";
 	} else {
 		/* Unknown, hope for valid UTF-8... */
 		charsetstr1 = "UTF-8";
@@ -506,7 +527,8 @@
 		b = purple_find_buddy(account, destsn);
 		if ((b != NULL) && (PURPLE_BUDDY_IS_ONLINE(b)))
 		{
-			*msg = g_convert(from, -1, "UTF-16BE", "UTF-8", NULL, &msglen, NULL);
+			*msg = g_convert(from, strlen(from), "UTF-16BE", "UTF-8", NULL, &msglen, NULL);
+			botch_ucs(*msg, msglen);
 			if (*msg != NULL)
 			{
 				*charset = AIM_CHARSET_UNICODE;
@@ -540,7 +562,8 @@
 	/*
 	 * Nothing else worked, so send as UTF-16BE.
 	 */
-	*msg = g_convert(from, -1, "UTF-16BE", "UTF-8", NULL, &msglen, &err);
+	*msg = g_convert(from, strlen(from), "UTF-16BE", "UTF-8", NULL, &msglen, &err);
+	botch_ucs(*msg, msglen);
 	if (*msg != NULL) {
 		*charset = AIM_CHARSET_UNICODE;
 		*charsubset = 0x0000;
@@ -2028,6 +2051,8 @@
 		tmp = purple_plugin_oscar_decode_im_part(account, userinfo->sn, curpart->charset,
 				curpart->charsubset, curpart->data, curpart->datalen);
 		if (tmp != NULL) {
+			purple_str_strip_char(tmp, 0x0d); // yaz: strip CR
+//			purple_debug_info("yaz oscar", "tmp=%s",tmp);
 			g_string_append(message, tmp);
 			g_free(tmp);
 		}
@@ -2130,6 +2155,7 @@
 		char *encoding, *utf8name, *tmp;
 		GHashTable *components;
 
+//		purple_debug_info("yaz oscar", "chat request %s\n", args->msg);
 		if (!args->info.chat.roominfo.name || !args->info.chat.roominfo.exchange) {
 			g_free(message);
 			return 1;
@@ -2152,6 +2178,8 @@
 		g_hash_table_replace(components, g_strdup("room"), utf8name);
 		g_hash_table_replace(components, g_strdup("exchange"),
 				g_strdup_printf("%d", args->info.chat.roominfo.exchange));
+		purple_debug_info("yaz oscar", "about to call serv_got_chat_invite\n");
+//		purple_debug_info("yaz oscar", "name=%s message=%s\n", name ? name : args->info.chat.roominfo.name, message);
 		serv_got_chat_invite(gc,
 				     utf8name,
 				     userinfo->sn,
@@ -2387,7 +2415,7 @@
 	 * for this suck-ass part of the protocol by splitting the string into at
 	 * most 1 baby string.
 	 */
-	msg1 = g_strsplit(args->msg, "\376", (args->type == 0x01 ? 1 : 0));
+	msg1 = g_strsplit(args->msg, "\376", (args->type == 0x01 ? 1 : 0)); // \376 is 0xfe
 	for (numtoks=0; msg1[numtoks]; numtoks++);
 	msg2 = (gchar **)g_malloc((numtoks+1)*sizeof(gchar *));
 	for (i=0; msg1[i]; i++) {
@@ -4127,9 +4155,7 @@
 {
 	GString *msg;
 	GString *data;
-	gchar *tmp;
-	int tmplen;
-	guint16 charset, charsubset;
+	guint16 charset;
 	GData *attribs;
 	const char *start, *end, *last;
 	int oscar_id = 0;
@@ -4189,12 +4215,8 @@
 
 	g_string_append(msg, "</BODY></HTML>");
 
-	/* Convert the message to a good encoding */
-	purple_plugin_oscar_convert_to_best_encoding(conn->od->gc,
-			conn->sn, msg->str, &tmp, &tmplen, &charset, &charsubset);
-	g_string_free(msg, TRUE);
-	msg = g_string_new_len(tmp, tmplen);
-	g_free(tmp);
+	/* iChat and AIM6 use 0x000d to send UTF8. moreover, AIM6 persists only to UTF8! --yaz */
+	charset = AIM_CHARSET_QUIRKUTF8;
 
 	/* Append any binary data that we may have */
 	if (oscar_id) {
@@ -4436,7 +4458,8 @@
 
 	charset = oscar_charset_check(str);
 	if (charset == AIM_CHARSET_UNICODE) {
-		encoded = g_convert(str, -1, "UTF-16BE", "UTF-8", NULL, ret_len, NULL);
+		encoded = g_convert(str, strlen(str), "UTF-16BE", "UTF-8", NULL, ret_len, NULL);
+		botch_ucs(encoded, *ret_len);
 		*encoding = "unicode-2-0";
 	} else if (charset == AIM_CHARSET_CUSTOM) {
 		encoded = g_convert(str, -1, "ISO-8859-1", "UTF-8", NULL, ret_len, NULL);
@@ -5573,7 +5596,7 @@
 		charsetstr = "unicode-2-0";
 	else if (charset == AIM_CHARSET_CUSTOM)
 		charsetstr = "iso-8859-1";
-	aim_chat_send_im(od, c->conn, 0, buf2, len, charsetstr, "en");
+	aim_chat_send_im(od, c->conn, 0, buf2, len, charsetstr, "JA");
 	g_free(buf2);
 	g_free(buf);
 
--- a/libpurple/protocols/oscar/oscar.h	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Thu Mar 20 11:57:02 2008 +0000
@@ -756,6 +756,7 @@
 #define AIM_CHARSET_ASCII		0x0000
 #define AIM_CHARSET_UNICODE	0x0002 /* UTF-16BE */
 #define AIM_CHARSET_CUSTOM	0x0003
+#define AIM_CHARSET_QUIRKUTF8	0x000d /* iChat and AIM6 use this in the meaning of UTF-8 in ODC. --yaz */
 
 /*
  * Multipart message structures.
--- a/libpurple/protocols/yahoo/util.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/yahoo/util.c	Thu Mar 20 11:57:02 2008 +0000
@@ -29,6 +29,7 @@
 #include "prpl.h"
 
 #include "yahoo.h"
+#include "util.h"
 
 #include <string.h>
 /*
@@ -111,21 +112,21 @@
 char *yahoo_string_encode(PurpleConnection *gc, const char *str, gboolean *utf8)
 {
 	struct yahoo_data *yd = gc->proto_data;
-	char *ret;
+	char *ret = NULL;
+	gsize newlen;
 	const char *to_codeset;
 
-	if (yd->jp && utf8 && *utf8)
-		*utf8 = FALSE;
-
-	if (utf8 && *utf8) /* FIXME: maybe don't use utf8 if it'll fit in latin1 */
-		return g_strdup(str);
+	if (utf8 && *utf8) {
+		return botch_utf((gchar *)str, strlen((gchar *)str), &newlen);
+	}
 
 	if (yd->jp)
 		to_codeset = "SHIFT_JIS";
 	else
 		to_codeset = purple_account_get_string(purple_connection_get_account(gc), "local_charset",  "ISO-8859-1");
 
-	ret = g_convert_with_fallback(str, -1, to_codeset, "UTF-8", "?", NULL, NULL, NULL);
+	ret = g_convert_with_fallback(str, strlen(str), to_codeset, "UTF-8", "?", NULL, NULL, NULL);
+
 	if (ret)
 		return ret;
 	else
@@ -143,23 +144,29 @@
 char *yahoo_string_decode(PurpleConnection *gc, const char *str, gboolean utf8)
 {
 	struct yahoo_data *yd = gc->proto_data;
-	char *ret;
+	char *ret, *tmp;
 	const char *from_codeset;
+	gsize newlen;
 
 	if (utf8) {
-		if (g_utf8_validate(str, -1, NULL))
-			return g_strdup(str);
+		ret = sanitize_utf((gchar *)str, strlen((gchar *)str), &newlen);
+		if (g_utf8_validate(ret, -1, NULL))
+			return ret;
 	}
 
-	if (yd->jp)
+	if (yd->jp && !utf8)
 		from_codeset = "SHIFT_JIS";
 	else
 		from_codeset = purple_account_get_string(purple_connection_get_account(gc), "local_charset",  "ISO-8859-1");
 
-	ret = g_convert_with_fallback(str, -1, "UTF-8", from_codeset, NULL, NULL, NULL, NULL);
+	ret = g_convert_with_fallback(str, strlen(str), "UTF-8", from_codeset, NULL, NULL, NULL, NULL);
 
-	if (ret)
+	if (ret){
+		tmp = ret;
+		ret = sanitize_utf((gchar *)tmp, strlen((gchar *)tmp), &newlen);
+		g_free(tmp);
 		return ret;
+	}
 	else
 		return g_strdup("");
 }
--- a/libpurple/protocols/yahoo/yahoo.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Thu Mar 20 11:57:02 2008 +0000
@@ -777,6 +777,7 @@
 				list = g_slist_append(list, im);
 				im->from = pair->value;
 				im->time = time(NULL);
+				im->utf8 = 1;
 			}
 			if (pair->key == 97)
 				if (im)
@@ -791,6 +792,12 @@
 				if (im)
 					im->msg = pair->value;
 			}
+#if 0
+			if (pair->key == 244) { /* Web client? */
+				if (im)
+					im->utf8 = 1;
+			}
+#endif
 			/* IMV key */
 			if (pair->key == 63)
 			{
@@ -1236,8 +1243,9 @@
 
 #define OUT_CHARSET "utf-8"
 
-static char *yahoo_decode(const char *text)
+static char *yahoo_decode(PurpleConnection *gc, const char *text)
 {
+	struct yahoo_data *yd = gc->proto_data;
 	char *converted = NULL;
 	char *n, *new;
 	const char *end, *p;
@@ -1278,11 +1286,16 @@
 
 	*n = '\0';
 
-	if (strstr(text, "\033$B"))
-		converted = g_convert(new, n - new, OUT_CHARSET, "iso-2022-jp", NULL, NULL, NULL);
-	if (!converted)
-		converted = g_convert(new, n - new, OUT_CHARSET, "iso-8859-1", NULL, NULL, NULL);
-	g_free(new);
+	if (yd->jp) {
+		converted = g_convert(new, n - new, OUT_CHARSET, "UTF-8", NULL, NULL, NULL);
+	}
+	if (!yd->jp || !converted) {
+		if (strstr(text, "\033$B"))
+			converted = g_convert(new, n - new, OUT_CHARSET, "iso-2022-jp", NULL, NULL, NULL);
+		if (!converted)
+			converted = g_convert(new, n - new, OUT_CHARSET, "iso-8859-1", NULL, NULL, NULL);
+		g_free(new);
+	}
 
 	return converted;
 }
@@ -1315,8 +1328,8 @@
 	}
 
 	if (who && subj && email && *email) {
-		char *dec_who = yahoo_decode(who);
-		char *dec_subj = yahoo_decode(subj);
+		char *dec_who = yahoo_decode(gc, who);
+		char *dec_subj = yahoo_decode(gc, subj);
 		char *from = g_strdup_printf("%s (%s)", dec_who, email);
 
 		purple_notify_email(gc, dec_subj, from, purple_account_get_username(account),
@@ -1473,11 +1486,12 @@
 	pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP,	YAHOO_STATUS_AVAILABLE, 0);
 
 	if(yd->jp) {
-		yahoo_packet_hash(pack, "sssss",
+		yahoo_packet_hash(pack, "ssssss",
 						  0, name,
 						  6, result6,
 						  96, result96,
 						  1, name,
+						  244, YAHOOJP_CLIENT_VERSION_ID,
 						  135, YAHOOJP_CLIENT_VERSION);
 	} else {
 		yahoo_packet_hash(pack, "ssssss",
@@ -1936,11 +1950,12 @@
 	pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP,	yd->current_status, 0);
 
 	if(yd->jp) {
-		yahoo_packet_hash(pack, "sssss",
+		yahoo_packet_hash(pack, "ssssss",
 						  0, name,
 						  6, resp_6,
 						  96, resp_96,
 						  1, name,
+						  244, YAHOOJP_CLIENT_VERSION_ID,
 						  135, YAHOOJP_CLIENT_VERSION);
 	} else {
 		yahoo_packet_hash(pack, "ssssss",
@@ -3535,7 +3550,10 @@
 	if ((gc->account->proxy_info) && (gc->account->proxy_info->type == PURPLE_PROXY_HTTP))
 	    use_whole_url = TRUE;
 
-	url_data = purple_util_fetch_url_request(base_url, use_whole_url,
+	if (yd->jp)
+		url_data = NULL;
+	else
+		url_data = purple_util_fetch_url_request(base_url, use_whole_url,
 			"Mozilla/4.0 (compatible; MSIE 5.5)", TRUE, request, FALSE,
 			yahoo_get_inbox_token_cb, gc);
 
@@ -3728,6 +3746,10 @@
 	}
 
 	pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE, YAHOO_STATUS_AVAILABLE, 0);
+    if(!pkt) { // yaz
+        purple_debug_info("yahoo", "yahoo_set_idle: pkt == NULL\n");
+        return;
+    }
 	yahoo_packet_hash_int(pkt, 10, yd->current_status);
 
 	if (yd->current_status == YAHOO_STATUS_CUSTOM) {
@@ -3772,6 +3794,10 @@
 	}
 
 	pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE, YAHOO_STATUS_AVAILABLE, 0);
+	if(!pkt) {
+		purple_debug_info("yahoo", "yahoo_set_idle: pkt == NULL\n");
+		return;
+	}
 
 	yahoo_packet_hash_int(pkt, 10, yd->current_status);
 	if (yd->current_status == YAHOO_STATUS_CUSTOM) {
@@ -3782,7 +3808,7 @@
 		if (tmp != NULL) {
 			msg = yahoo_string_encode(gc, tmp, NULL);
 			msg2 = purple_markup_strip_html(msg);
-			yahoo_packet_hash_str(pkt, 19, msg2);
+			yahoo_packet_hash_str(pkt, 19, msg2); // yaz: pkt may be NULL.
 		} else {
 			/* get_yahoo_status_from_purple_status() returns YAHOO_STATUS_CUSTOM for
 			 * the generic away state (YAHOO_STATUS_TYPE_AWAY) with no message */
@@ -4449,11 +4475,11 @@
 	option = purple_account_option_bool_new(_("Ignore conference and chatroom invitations"), "ignore_invites", FALSE);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
-	option = purple_account_option_string_new(_("Encoding"), "local_charset", "ISO-8859-1");
+	option = purple_account_option_string_new(_("Encoding"), "local_charset", "UTF-8");
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
 
-#if 0
+#if 1
 	option = purple_account_option_string_new(_("Chat room list URL"), "room_list", YAHOO_ROOMLIST_URL);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
--- a/libpurple/protocols/yahoo/yahoo.h	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo.h	Thu Mar 20 11:57:02 2008 +0000
@@ -76,13 +76,14 @@
 
 #define YAHOO_CLIENT_VERSION_ID "2097087"
 #define YAHOO_CLIENT_VERSION "8.1.0.421"
-#define YAHOOJP_CLIENT_VERSION "6,0,0,1710"
 
 #if 0
+#define YAHOOJP_CLIENT_VERSION "6,0,0,1710"
+#else
 /* The following were observed with the Yahoo Japan client current as of January
  * 2008, but appear not to work correctly for file transfer.  Here as reference */
-# define YAHOOJP_CLIENT_VERSION_ID "524223"
-# define YAHOOJP_CLIENT_VERSION "7,0,1,1"
+#define YAHOOJP_CLIENT_VERSION_ID "524223"
+#define YAHOOJP_CLIENT_VERSION "7,0,1,3"
 #endif
 
 
--- a/libpurple/protocols/yahoo/yahoo_filexfer.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo_filexfer.c	Thu Mar 20 11:57:02 2008 +0000
@@ -217,7 +217,7 @@
 	PurpleXfer *xfer;
 	struct yahoo_xfer_data *xd;
 	struct yahoo_packet *pkt;
-	gchar *size, *filename, *encoded_filename, *header;
+	gchar *size, *filename, *encoded_filename, *header, *tmp = NULL;
 	guchar *pkt_buf;
 	const char *host;
 	int port;
@@ -225,6 +225,7 @@
 	PurpleConnection *gc;
 	PurpleAccount *account;
 	struct yahoo_data *yd;
+	gsize dummy;
 
 	purple_debug(PURPLE_DEBUG_INFO, "yahoo",
 			   "AAA - in yahoo_sendfile_connected\n");
@@ -251,8 +252,12 @@
 		YAHOO_STATUS_AVAILABLE, yd->session_id);
 
 	size = g_strdup_printf("%" G_GSIZE_FORMAT, purple_xfer_get_size(xfer));
-	filename = g_path_get_basename(purple_xfer_get_local_filename(xfer));
-	encoded_filename = yahoo_string_encode(gc, filename, NULL);
+
+	/* yaz */
+	tmp = g_filename_display_basename(purple_xfer_get_local_filename(xfer));
+	filename = botch_utf(tmp, strlen(tmp), &dummy);
+	g_free(tmp);
+	encoded_filename = yahoo_string_encode(gc, filename, NULL); // this takes utf8 as input. --yaz
 
 	yahoo_packet_hash(pkt, "sssss", 0, purple_connection_get_display_name(gc),
 	  5, xfer->who, 14, "", 27, encoded_filename, 28, size);
@@ -948,8 +953,8 @@
 	struct yahoo_xfer_data *xd;
 	struct sockaddr_in *addr;
 	struct yahoo_packet *pkt;
-	long actaddr;
-	long a,b,c,d;
+	unsigned long actaddr;
+	unsigned long a,b,c,d;
 	PurpleConnection *gc;
 	PurpleAccount *account;
 	struct yahoo_data *yd;
@@ -1043,6 +1048,10 @@
 	if(yf && yf->version_id > 500000 && !yd->jp)
 		ver = 15; 
 
+	/* recent yahoo japan --yaz */
+	if(yd->jp && yf && yf->version_id > 500000)
+		ver = 15;
+
 	g_return_if_fail(xfer != NULL);
 
 	if(ver == 15) {
--- a/libpurple/protocols/yahoo/yahoo_packet.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo_packet.c	Thu Mar 20 11:57:02 2008 +0000
@@ -42,6 +42,7 @@
 {
 	struct yahoo_pair *pair;
 
+	g_return_if_fail(pkt != NULL);
 	g_return_if_fail(value != NULL);
 
 	pair = g_new0(struct yahoo_pair, 1);
@@ -54,6 +55,8 @@
 {
 	struct yahoo_pair *pair;
 
+	g_return_if_fail(pkt != NULL);
+
 	pair = g_new0(struct yahoo_pair, 1);
 	pair->key = key;
 	pair->value = g_strdup_printf("%d", value);
@@ -387,6 +390,9 @@
 {
 	int ret;
 
+	g_return_val_if_fail(pkt != NULL, 0);
+	g_return_val_if_fail(yd != NULL, 0);
+
 	ret = yahoo_packet_send(pkt, yd);
 	yahoo_packet_free(pkt);
 	return ret;
--- a/libpurple/protocols/yahoo/yahoo_packet.h	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo_packet.h	Thu Mar 20 11:57:02 2008 +0000
@@ -123,7 +123,7 @@
 
 #define YAHOO_WEBMESSENGER_PROTO_VER 0x0065
 #define YAHOO_PROTO_VER 0x000f
-#define YAHOO_PROTO_VER_JAPAN 0x000c
+#define YAHOO_PROTO_VER_JAPAN 0x000d
 
 #define YAHOO_PACKET_HDRLEN (4 + 2 + 2 + 2 + 2 + 4 + 4)
 
--- a/libpurple/protocols/yahoo/yahoo_profile.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo_profile.c	Thu Mar 20 11:57:02 2008 +0000
@@ -22,6 +22,7 @@
  */
 
 #define PHOTO_SUPPORT 1
+//original is 1 --yaz
 
 #include "internal.h"
 #include "debug.h"
@@ -670,6 +671,7 @@
 	},
 };
 
+#if 0
 static char *yahoo_info_date_reformat(const char *field, size_t len)
 {
 	char *tmp = g_strndup(field, len);
@@ -678,6 +680,7 @@
 	g_free(tmp);
 	return g_strdup(purple_date_format_short(localtime(&t)));
 }
+#endif
 
 static char *yahoo_remove_nonbreaking_spaces(char *str)
 {
@@ -761,6 +764,7 @@
 
 #endif /* PHOTO_SUPPORT */
 
+#define PROF_LEN (1024 * 10)
 static void yahoo_got_info(PurpleUtilFetchUrlData *url_data, gpointer user_data,
 		const gchar *url_text, size_t len, const gchar *error_message)
 {
@@ -1017,6 +1021,9 @@
 			g_free(stripped);
 			stripped = purple_utf8_ncr_decode(p);
 			stripped_len = strlen(stripped);
+
+			purple_debug_misc("yahoo", "after utf8 conversion: stripped@1 = (%s)\n",
+                              stripped); //payload --yaz
 			g_free(p);
 		}
 	}
@@ -1028,7 +1035,7 @@
 				strings->charset, NULL, NULL, NULL);
 		yahoo_remove_nonbreaking_spaces(last_updated_utf8_string);
 
-		purple_debug_misc("yahoo", "after utf8 conversion: stripped = (%s)\n", stripped);
+		purple_debug_misc("yahoo", "after utf8 conversion: stripped = (%s)\n", stripped); //payload --yaz
 	}
 
 	if (profile_state == PROFILE_STATE_DEFAULT) {
@@ -1061,16 +1068,17 @@
 		}
 	}
 #endif /* PHOTO_SUPPORT */
+	purple_debug_info("yahoo", "email = %s\n", strings->my_email_string);
 
 	/* extract their Email address and put it in */
 	found |= purple_markup_extract_info_field(stripped, stripped_len, user_info,
 			strings->my_email_string, (yd->jp ? 4 : 1), " ", 0,
 			strings->private_string, _("E-Mail"), 0, NULL, NULL);
-
+#if 0
 	/* extract the Nickname if it exists */
 	found |= purple_markup_extract_info_field(stripped, stripped_len, user_info,
 			"Nickname:", 1, "\n", '\n',
-			NULL, _("Nickname"), 0, NULL, NULL);
+			 NULL, _("Nickname"), 0, NULL, NULL);
 
 	/* extract their RealName and put it in */
 	found |= purple_markup_extract_info_field(stripped, stripped_len, user_info,
@@ -1101,7 +1109,7 @@
 	found |= purple_markup_extract_info_field(stripped, stripped_len, user_info,
 			strings->occupation_string, 2, "\n", '\n',
 			NULL, _("Occupation"), 0, NULL, NULL);
-
+#endif
 	/* Hobbies, Latest News, and Favorite Quote are a bit different, since
 	 * the values can contain embedded newlines... but any or all of them
 	 * can also not appear.  The way we delimit them is to successively
@@ -1110,7 +1118,7 @@
 	 * next thing to follow this bunch.  (For Yahoo Japan, we check for
 	 * the "Description" ("Self PR") heading instead of "Links".)
 	 */
-
+#if 0
 	if (!purple_markup_extract_info_field(stripped, stripped_len, user_info,
 			strings->hobbies_string, (yd->jp ? 3 : 1), strings->latest_news_string,
 			'\n', "\n", _("Hobbies"), 0, NULL, NULL))
@@ -1196,6 +1204,7 @@
 				last_updated_utf8_string, (yd->jp ? 2 : 1), (yd->jp ? "\n" : " "), (yd->jp ? 0 : '\n'), NULL,
 				_("Last Update"), 0, NULL, (yd->jp ? NULL : yahoo_info_date_reformat));
 	}
+#endif
 	} /* if (profile_state == PROFILE_STATE_DEFAULT) */
 
 	if(!found)
--- a/libpurple/server.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/server.c	Thu Mar 20 11:57:02 2008 +0000
@@ -242,6 +242,8 @@
 	GSList *buddies;
 	PurpleBuddy *b;
 	PurpleConversation *conv;
+	gsize dummy;
+	gchar *alias2 = NULL;
 
 	account = purple_connection_get_account(gc);
 	buddies = purple_find_buddies(account, who);
@@ -251,19 +253,23 @@
 		b = buddies->data;
 		buddies = g_slist_delete_link(buddies, buddies);
 
+		/* alias may be NULL */
 		if((b->server_alias == NULL && alias == NULL) ||
 		    (b->server_alias && alias && !strcmp(b->server_alias, alias)))
 		{
 			continue;
 		}
 
-		purple_blist_server_alias_buddy(b, alias);
+		/* alias must be non-NULL here */
+		alias2 = sanitize_utf(alias, strlen(alias), &dummy);
+
+		purple_blist_server_alias_buddy(b, alias2);
 
 		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, b->name, account);
-		if(conv != NULL && alias != NULL && strcmp(alias, who))
+		if(conv != NULL && alias2 != NULL && strcmp(alias2, who))
 		{
 			char *tmp = g_strdup_printf(_("%s is now known as %s.\n"),
-										who, alias);
+										who, alias2);
 
 			purple_conversation_write(conv, NULL, tmp,
 					PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LINKIFY,
@@ -271,6 +277,8 @@
 
 			g_free(tmp);
 		}
+		g_free(alias2);
+		alias2 = NULL;
 	}
 }
 
@@ -280,6 +288,8 @@
 	PurpleAccount *account = NULL;
 	GSList *buddies = NULL;
 	PurpleBuddy *b = NULL;
+	gsize dummy;
+	gchar *alias2 = NULL;
 
 	account = purple_connection_get_account(gc);
 	buddies = purple_find_buddies(account, who);
@@ -292,11 +302,13 @@
 		if((!b->alias && !alias) || (b->alias && alias && !strcmp(b->alias, alias)))
 			continue;
 
-		purple_blist_alias_buddy(b, alias);
+		alias2 = sanitize_utf(alias, strlen(alias), &dummy);
+		purple_blist_alias_buddy(b, alias2);
+		g_free(alias2);
+		alias2 = NULL;
 	}
 }
 
-
 PurpleAttentionType *purple_get_attention_type_from_code(PurpleAccount *account, guint type_code)
 {
 	PurplePlugin *prpl;
--- a/libpurple/util.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/util.c	Thu Mar 20 11:57:02 2008 +0000
@@ -1209,6 +1209,8 @@
 		return FALSE;
 
 	q = strstr(p, end_token);
+	if(q == NULL) //yaz
+		return FALSE;
 
 	/* Trim leading blanks */
 	while (*p != '\n' && g_ascii_isspace(*p)) {
@@ -1221,7 +1223,7 @@
 	}
 
 	/* Don't bother with null strings */
-	if (p == q)
+	if (p >= q)
 		return FALSE;
 
 	if (q != NULL && (!no_value_token ||
@@ -4707,3 +4709,296 @@
 	return g_string_free(string, FALSE);
 }
 
+
+#ifdef _WIN32
+void botch_ucs(gchar *ucs_src, gsize len)
+{
+	/* no operation */
+}
+#else
+void botch_ucs(gchar *ucs_src, gsize len)
+{
+	gint i;
+	guchar *ucs = (guchar *)ucs_src;
+
+	g_return_if_fail(ucs_src != NULL);
+	g_return_if_fail(len > 0);
+
+ 	for(i=0;i<len;i+=2){
+ 		switch(*(ucs+i)){
+ 		case 0x00:
+ 			switch(*(ucs+i+1)){
+ 			case 0xa2:	// ¢
+ 				*(ucs+i) = 0xff;
+ 				*(ucs+i+1) = 0xe0;
+ 				break;
+ 			case 0xa3:	// £
+ 				*(ucs+i) = 0xff;
+				*(ucs+i+1) = 0xe1;
+ 				break;
+ 			case 0xac:	// ¬
+ 				*(ucs+i) = 0xff;
+ 				*(ucs+i+1) = 0xe2;
+ 				break;
+ 			}
+ 			break;
+ 		case 0x20:	// ‖
+ 			if(*(ucs+i+1) == 0x16){
+ 				*(ucs+i) = 0x22;
+				*(ucs+i+1) = 0x25;
+ 			}
+ 			break;
+ 		case 0x22:	// −
+ 			if(*(ucs+i+1) == 0x12){
+ 				*(ucs+i) = 0xff;
+ 				*(ucs+i+1) = 0x0d;
+ 			}
+ 			break;
+ 		case 0x30:	// 〜
+ 			if(*(ucs+i+1) == 0x1c){
+ 				*(ucs+i) = 0xff;
+ 				*(ucs+i+1) = 0x5e;
+ 			}
+ 			break;
+ 		}
+ 	}
+
+}
+#endif
+
+#ifdef _WIN32
+void sanitize_ucs(gchar *ucs, gsize len)
+{
+	/* no operation */
+}
+#else
+void sanitize_ucs(gchar *ucs_src, gsize len)
+{
+	gint i;
+	guchar *ucs = (guchar *)ucs_src;
+
+	g_return_if_fail(ucs_src != NULL);
+	g_return_if_fail(len > 0);
+
+	for(i=0;i<len;i+=2){
+		switch(*(ucs+i)){
+		case 0x22:
+			switch(*(ucs+i+1)){
+			case 0x25:	// ‖
+				*(ucs+i) = 0x20;
+				*(ucs+i+1) = 0x16;
+				break;
+			}
+			break;
+		case 0xff:
+			switch(*(ucs+i+1)){
+			case 0x0d:	// −
+				*(ucs+i) = 0x22;
+				*(ucs+i+1) = 0x12;
+				break;
+			case 0x5e:	// 〜
+				*(ucs+i) = 0x30;
+				*(ucs+i+1) = 0x1c;
+				break;
+			case 0xe0:	// ¢
+				*(ucs+i) = 0x00;
+				*(ucs+i+1) = 0xa2;
+				break;
+			case 0xe1:	// £
+				*(ucs+i) = 0x00;
+				*(ucs+i+1) = 0xa3;
+				break;
+			case 0xe2:	// ¬
+				*(ucs+i) = 0x00;
+				*(ucs+i+1) = 0xac;
+				break;
+			}
+			break;
+		}
+	}
+}
+#endif
+
+#ifdef _WIN32
+gchar *sanitize_utf(const gchar *msg, gsize len, gsize *newlen)
+{
+	g_return_val_if_fail(msg != NULL, NULL);
+	if(len == -1)
+		len = strlen(msg);
+	g_return_val_if_fail(len > 0, NULL);
+
+	if(newlen)
+		*newlen = len;
+
+	return g_strndup(msg, len);
+}
+#else
+gchar *sanitize_utf(const gchar *msg, gsize len, gsize *newlen)
+{
+	gint i;
+	size_t bytes;
+	guchar *utf;
+
+	g_return_val_if_fail(msg != NULL, NULL);
+	if(len == -1)
+		len = strlen(msg);
+	g_return_val_if_fail(len > 0, NULL);
+
+	utf = (guchar *)g_strndup(msg, len);
+
+	bytes = len;
+
+	for(i=0;i<len;i++){
+		switch(*(utf+i)){
+		case 0xe2:
+			if(*(utf+i+1) == 0x88) {
+				if(*(utf+i+2) == 0xa5) {	// ‖
+					*(utf+i) = 0xe2;
+					*(utf+i+1) = 0x80;
+					*(utf+i+2) = 0x96;
+				}
+			}
+			break;
+		case 0xef:
+			switch(*(utf+i+1)){
+			case 0xbc:
+				if(*(utf+i+2) == 0x8d) {	// −
+					*(utf+i) = 0xe2;
+					*(utf+i+1) = 0x88;
+					*(utf+i+2) = 0x92;
+				}
+				break;
+			case 0xbd:
+				if(*(utf+i+2) == 0x9e) {	// 〜
+					*(utf+i) = 0xe3;
+					*(utf+i+1) = 0x80;
+					*(utf+i+2) = 0x9c;
+				}
+				break;
+			case 0xbf:
+				switch(*(utf+i+2)){
+			       case 0xa0:	// ¢
+				       *(utf+i) = 0xc2;
+				       *(utf+i+1) = 0xa2;
+				       memmove(utf+i+2, utf+i+3,
+					       len-i-3); //shorten by 1byte
+				       bytes--;
+				       break;
+			       case 0xa1:	// £
+				       *(utf+i) = 0xc2;
+				       *(utf+i+1) = 0xa3;
+				       memmove(utf+i+2, utf+i+3,
+					       len-i-3); //shorten by 1byte
+				       bytes--;
+				       break;
+			       case 0xa2:	// ¬
+				       *(utf+i) = 0xc2;
+				       *(utf+i+1) = 0xac;
+				       memmove(utf+i+2, utf+i+3,
+					       len-i-3); //shorten by 1byte
+				       bytes--;
+				       break;
+			       }
+			       break;
+			}
+			break;
+		}
+	}
+	*(utf+bytes)= 0x00; //terminate
+	if(newlen)
+		*newlen = bytes;
+	return (gchar *)utf;
+}
+#endif
+
+#ifdef _WIN32
+gchar *botch_utf(const gchar *msg, gsize len, gsize *newlen)
+{
+	g_return_val_if_fail(msg != NULL, NULL);
+	if(len == -1)
+		len = strlen(msg);
+	g_return_val_if_fail(len > 0, NULL);
+
+	if(newlen)
+		*newlen = len;
+
+	return g_strndup(msg, len);
+}
+#else
+gchar *botch_utf(const gchar *msg, gsize len, gsize *newlen)
+{
+ 	int i,bytes;
+	unsigned char *utf;
+
+	g_return_val_if_fail(msg != NULL, NULL);
+	if(len == -1)
+		len = strlen(msg);
+	g_return_val_if_fail(len > 0, NULL);
+	
+	bytes = len;
+
+	utf = g_malloc0(bytes*3/2+1); /* new length might be 3/2 in the worst case */
+	memcpy(utf, msg, bytes);
+	
+ 	for(i=0;i<bytes;i++){
+ 		switch(*(utf+i)){
+ 		case 0xc2:
+ 			switch(*(utf+i+1)){
+ 			case 0xa2:	// ¢
+ 				*(utf+i) = 0xef;
+ 				*(utf+i+1) = 0xbf;
+				memmove(utf+i+3, utf+i+2, bytes-i-2);
+				*(utf+i+2) = 0xa0;
+				bytes++;
+ 				break;
+ 			case 0xa3:	// £
+ 				*(utf+i) = 0xef;
+ 				*(utf+i+1) = 0xbf;
+				memmove(utf+i+3, utf+i+2, bytes-i-2);
+				*(utf+i+2) = 0xa1;
+				bytes++;
+ 				break;
+ 			case 0xac:	// ¬
+ 				*(utf+i) = 0xef;
+ 				*(utf+i+1) = 0xbf;
+				memmove(utf+i+3, utf+i+2, bytes-i-2);
+				*(utf+i+2) = 0xa2;
+				bytes++;
+ 				break;
+ 			}
+ 			break;
+ 		case 0xe2:
+			switch(*(utf+i+1)){
+			case 0x80:	// ‖
+				if(*(utf+i+2) == 0x96){
+					*(utf+i) = 0xe2;
+					*(utf+i+1) = 0x88;
+					*(utf+i+2) = 0xa5;
+				}
+				break;
+			case 0x88:	// −
+				if(*(utf+i+1) == 0x92){
+					*(utf+i) = 0xef;
+					*(utf+i+1) = 0xbc;
+					*(utf+i+2) = 0x8d;
+				}
+				break;
+			}
+			break;
+ 		case 0xe3:	// 〜
+ 			if(*(utf+i+1) == 0x80){
+				if(*(utf+i+2) == 0x9c){
+					*(utf+i) = 0xef;
+					*(utf+i+1) = 0xbd;
+					*(utf+i+2) = 0x9e;
+				}
+ 			}
+ 			break;
+ 		} //switch
+ 	}
+	*(utf+bytes) = 0x00; //terminate
+	if(newlen)
+		*newlen = bytes;
+	return (gchar *)utf;
+}
+#endif
--- a/libpurple/util.h	Thu Mar 20 08:42:15 2008 +0000
+++ b/libpurple/util.h	Thu Mar 20 11:57:02 2008 +0000
@@ -1263,4 +1263,10 @@
 }
 #endif
 
+/* to address incompatibility with cp932. */
+void botch_ucs(gchar *ucs, gsize len);
+void sanitize_ucs(gchar *ucs, gsize len);
+gchar *botch_utf(const gchar *utf, gsize len, gsize *newlen);
+gchar *sanitize_utf(const gchar *msg, gsize len, gsize *newlen);
+
 #endif /* _PURPLE_UTIL_H_ */
--- a/pidgin/gtkconv.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/pidgin/gtkconv.c	Thu Mar 20 11:57:02 2008 +0000
@@ -1,3 +1,4 @@
+/* -*- coding: utf-8 -*- */
 /**
  * @file gtkconv.c GTK+ Conversation API
  * @ingroup pidgin
@@ -114,6 +115,10 @@
 static GdkColor *nick_colors = NULL;
 static guint nbr_nick_colors;
 
+/* yaz. If you want to use shortcut keys that may conflict with
+   inputmethods, change this to 1. */
+#define ENABLE_SHORTCUT 0
+
 typedef struct {
 	GtkWidget *window;
 
@@ -269,6 +274,43 @@
 	return FALSE;
 }
 
+static gboolean
+size_allocate_cb(GtkWidget *w, GtkAllocation *allocation, PidginConversation *gtkconv)
+{
+	PurpleConversation *conv = gtkconv->active_conv;
+
+	if (!GTK_WIDGET_VISIBLE(w))
+		return FALSE;
+
+	if (!PIDGIN_IS_PIDGIN_CONVERSATION(conv))
+		return FALSE;
+	if (gtkconv->auto_resize) {
+		return FALSE;
+	}
+
+	if (gdk_window_get_state(gtkconv->win->window->window) & GDK_WINDOW_STATE_MAXIMIZED) {
+		return FALSE;
+	}
+
+	/* I find that I resize the window when it has a bunch of conversations in it, mostly so that the
+	 * tab bar will fit, but then I don't want new windows taking up the entire screen.  I check to see
+	 * if there is only one conversation in the window.  This way we'll be setting new windows to the
+	 * size of the last resized new window. */
+	/* I think that the above justification is not the majority, and that the new tab resizing should
+	 * negate it anyway.  --luke */
+	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
+	{
+		if (w == gtkconv->lower_hbox)
+			purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/entry_height", allocation->height);
+	}
+	else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
+	{
+		if (w == gtkconv->lower_hbox)
+			purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/chat/entry_height", allocation->height);
+	}
+	return FALSE;
+}
+
 static void
 default_formatize(PidginConversation *c)
 {
@@ -418,6 +460,20 @@
 	gtkconv->send_history = g_list_prepend(first, NULL);
 }
 
+#if 0
+static void
+reset_default_size(PidginConversation *gtkconv)
+{
+	PurpleConversation *conv = gtkconv->active_conv;
+	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
+		gtk_widget_set_size_request(gtkconv->lower_hbox, -1,
+					    purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/entry_height"));
+	else
+		gtk_widget_set_size_request(gtkconv->lower_hbox, -1,
+					    purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/entry_height"));
+}
+#endif
+
 static gboolean
 check_for_and_do_command(PurpleConversation *conv)
 {
@@ -2936,20 +2992,33 @@
 
 	{ "/Conversation/sep0", NULL, NULL, 0, "<Separator>", NULL },
 
+#if ENABLE_SHORTCUT
 	{ N_("/Conversation/_Find..."), NULL, menu_find_cb, 0,
 			"<StockItem>", GTK_STOCK_FIND },
+#else
+	{ N_("/Conversation/_Find..."), NULL, menu_find_cb, 0,
+      "<Item>", NULL },
+#endif
 	{ N_("/Conversation/View _Log"), NULL, menu_view_log_cb, 0, "<Item>", NULL },
 	{ N_("/Conversation/_Save As..."), NULL, menu_save_as_cb, 0,
 			"<StockItem>", GTK_STOCK_SAVE_AS },
+#if ENABLE_SHORTCUT
 	{ N_("/Conversation/Clea_r Scrollback"), "<CTL>L", menu_clear_cb, 0, "<StockItem>", GTK_STOCK_CLEAR },
-
+#else
+	{ N_("/Conversation/Clea_r Scrollback"), NULL, menu_clear_cb, 0, "<Item>", NULL },
+#endif
 	{ "/Conversation/sep1", NULL, NULL, 0, "<Separator>", NULL },
 
 	{ N_("/Conversation/Se_nd File..."), NULL, menu_send_file_cb, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_SEND_FILE },
 	{ N_("/Conversation/Add Buddy _Pounce..."), NULL, menu_add_pounce_cb,
 			0, "<Item>", NULL },
+#if ENABLE_SHORTCUT
 	{ N_("/Conversation/_Get Info"), "<CTL>O", menu_get_info_cb, 0,
 			"<StockItem>", PIDGIN_STOCK_TOOLBAR_USER_INFO },
+#else
+	{ N_("/Conversation/_Get Info"), NULL, menu_get_info_cb, 0,
+			"<StockItem>", PIDGIN_STOCK_TOOLBAR_USER_INFO },
+#endif
 	{ N_("/Conversation/In_vite..."), NULL, menu_invite_cb, 0,
 			"<Item>", NULL },
 	{ N_("/Conversation/M_ore"), NULL, NULL, 0, "<Branch>", NULL },
@@ -3635,7 +3704,11 @@
 		gtk_widget_destroy(win->menu.send_to);
 
 	/* Build the Send To menu */
+#if ENABLE_SHORTCUT
 	win->menu.send_to = gtk_menu_item_new_with_mnemonic(_("S_end To"));
+#else
+	win->menu.send_to = gtk_menu_item_new_with_mnemonic(_("Send To")); //to free Alt-s. intentional. --yaz
+#endif
 	gtk_widget_show(win->menu.send_to);
 
 	menu = gtk_menu_new();
@@ -4109,6 +4182,7 @@
 	PidginChatPane *gtkchat;
 	char *new_topic;
 	const char *current_topic;
+	char dummy[] = "No Topic";
 
 	gc      = purple_conversation_get_gc(conv);
 
@@ -4128,7 +4202,7 @@
 		return;
 	}
 
-	gtk_entry_set_text(GTK_ENTRY(gtkchat->topic_text), current_topic);
+	gtk_entry_set_text(GTK_ENTRY(gtkchat->topic_text), current_topic ? current_topic : dummy);
 	prpl_info->set_chat_topic(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)),
 			new_topic);
 
@@ -4394,12 +4468,10 @@
 	height = MIN(height, max_height);
 
 	diff = height - gtkconv->entry->allocation.height;
-	if (diff == 0 || (diff < 0 && -diff < oneline.height / 2))
+	if (diff <= 0)
 		return FALSE;
-
 	gtk_widget_set_size_request(gtkconv->lower_hbox, -1,
-		diff + gtkconv->lower_hbox->allocation.height);
-
+				    diff + gtkconv->lower_hbox->allocation.height);
 	return FALSE;
 }
 
@@ -4590,15 +4662,19 @@
 static GtkWidget *
 setup_common_pane(PidginConversation *gtkconv)
 {
-	GtkWidget *vbox, *frame, *imhtml_sw, *event_box;
+	GtkWidget *paned, *vbox, *frame, *imhtml_sw, *event_box;
 	GtkCellRenderer *rend;
 	GtkTreePath *path;
 	PurpleConversation *conv = gtkconv->active_conv;
 	gboolean chat = (conv->type == PURPLE_CONV_TYPE_CHAT);
 	GtkPolicyType imhtml_sw_hscroll;
 
+	paned = gtk_vpaned_new();
+	gtk_widget_show(paned);
+
 	/* Setup the top part of the pane */
 	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+	gtk_paned_pack1(GTK_PANED(paned), vbox, TRUE, TRUE);
 	gtk_widget_show(vbox);
 
 	/* Setup the info pane */
@@ -4691,18 +4767,30 @@
 	g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_release_event",
 	                 G_CALLBACK(refocus_entry_cb), gtkconv);
 
+	/* Setup the bottom half of the conversation window */
+	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+	gtk_paned_pack2(GTK_PANED(paned), vbox, FALSE, TRUE);
+	gtk_widget_show(vbox);
+
 	gtkconv->lower_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_start(GTK_BOX(vbox), gtkconv->lower_hbox, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), gtkconv->lower_hbox, TRUE, TRUE, 0);
 	gtk_widget_show(gtkconv->lower_hbox);
 
+	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+	gtk_box_pack_end(GTK_BOX(gtkconv->lower_hbox), vbox, TRUE, TRUE, 0);
+	gtk_widget_show(vbox);
+
 	/* Setup the toolbar, entry widget and all signals */
 	frame = pidgin_create_imhtml(TRUE, &gtkconv->entry, &gtkconv->toolbar, NULL);
-	gtk_box_pack_start(GTK_BOX(gtkconv->lower_hbox), frame, TRUE, TRUE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
 	gtk_widget_show(frame);
 
 	gtk_widget_set_name(gtkconv->entry, "pidgin_conv_entry");
 	gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->entry),
 			purple_account_get_protocol_name(conv->account));
+	gtk_widget_set_size_request(gtkconv->lower_hbox, -1,
+			chat ? purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/entry_height") :
+			purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/entry_height"));
 
 	g_signal_connect(G_OBJECT(gtkconv->entry), "populate-popup",
 	                 G_CALLBACK(entry_popup_menu_cb), gtkconv);
@@ -4712,6 +4800,8 @@
 	                       G_CALLBACK(send_cb), gtkconv);
 	g_signal_connect_after(G_OBJECT(gtkconv->entry), "button_press_event",
 	                       G_CALLBACK(entry_stop_rclick_cb), NULL);
+	g_signal_connect(G_OBJECT(gtkconv->lower_hbox), "size-allocate",
+	                 G_CALLBACK(size_allocate_cb), gtkconv);
 
 	gtkconv->entry_buffer =
 		gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry));
@@ -4736,7 +4826,7 @@
 	default_formatize(gtkconv);
 	g_signal_connect_after(G_OBJECT(gtkconv->entry), "format_function_clear",
 	                       G_CALLBACK(clear_formatting_cb), gtkconv);
-	return vbox;
+	return paned;
 }
 
 static void
@@ -6464,6 +6554,9 @@
 		AtkObject *accessibility_obj;
 		/* I think this is a little longer than it needs to be but I'm lazy. */
 		char *style;
+		gboolean ellipsis;
+		int side;
+		char *tab_title = NULL;
 
 		if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
 			im = PURPLE_CONV_IM(conv);
@@ -6554,8 +6647,26 @@
 			style = NULL;
 		}
 
+		// nosuke's tab width patch
+		side = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side");
+		side &= ~8; 
+		if ( (side == GTK_POS_LEFT || side == GTK_POS_RIGHT) &&
+		     purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/trim_vertical_tabs") )
+			ellipsis = TRUE;
+		else
+			ellipsis = FALSE;
+
+		if (ellipsis) {
+			tab_title = pidgin_gtk_ellipsis_text(gtkconv->tab_label, title, 60, "...");
+		}
+		else {
+			tab_title = g_strdup(title);
+		}
+
 		gtk_widget_set_name(gtkconv->tab_label, style);
-		gtk_label_set_text(GTK_LABEL(gtkconv->tab_label), title);
+		gtk_label_set_text(GTK_LABEL(gtkconv->tab_label), tab_title);
+		g_free(tab_title); tab_title = NULL;
+
 		gtk_widget_set_state(gtkconv->tab_label, GTK_STATE_ACTIVE);
 
 		if (gtkconv->unseen_state == PIDGIN_UNSEEN_TEXT ||
@@ -6961,6 +7072,24 @@
 	return page_num;
 }
 
+//nosuke
+static void
+trim_vertical_tabs_pref_cb(const char *name, PurplePrefType type,
+						   gconstpointer value, gpointer data)
+{
+	GList *l;
+	PurpleConversation *conv;
+
+	for (l = purple_get_conversations(); l != NULL; l = l->next) {
+		conv = (PurpleConversation *)l->data;
+
+		if (!PIDGIN_IS_PIDGIN_CONVERSATION(conv))
+			continue;
+
+		pidgin_conv_update_fields(conv, PIDGIN_CONV_SET_TITLE);
+	}
+}
+
 static void
 close_on_tabs_pref_cb(const char *name, PurplePrefType type,
 					  gconstpointer value, gpointer data)
@@ -7030,6 +7159,7 @@
 			pidgin_conv_tab_pack(gtkwin, gtkconvs->data);
 		}
 	}
+	trim_vertical_tabs_pref_cb(name, type, value, data);
 }
 
 static void
@@ -7088,7 +7218,7 @@
 		else
 			gtk_widget_hide(gtkconv->toolbar);
 
-		g_idle_add((GSourceFunc)resize_imhtml_cb,gtkconv);
+//		g_idle_add((GSourceFunc)resize_imhtml_cb,gtkconv);
 	}
 }
 
@@ -7591,6 +7721,7 @@
 	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/conversations");
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/use_smooth_scrolling", TRUE);
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/close_on_tabs", TRUE);
+	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/trim_vertical_tabs", FALSE);
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/send_bold", FALSE);
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/send_italic", FALSE);
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/send_underline", FALSE);
@@ -7644,6 +7775,8 @@
 	/* Connect callbacks. */
 	purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/conversations/close_on_tabs",
 								close_on_tabs_pref_cb, NULL);
+	purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/conversations/trim_vertical_tabs",
+								  trim_vertical_tabs_pref_cb, NULL);
 	purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/conversations/show_timestamps",
 								show_timestamps_pref_cb, NULL);
 	purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/conversations/show_formatting_toolbar",
--- a/pidgin/gtkimhtml.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/pidgin/gtkimhtml.c	Thu Mar 20 11:57:02 2008 +0000
@@ -90,6 +90,10 @@
                            gint              y,
                            guint             time);
 
+/* yaz. If you want to use shortcut keys that may conflict with
+   inputmethods, change this to 1. */
+#define ENABLE_SHORTCUT 0
+
 static void preinsert_cb(GtkTextBuffer *buffer, GtkTextIter *iter, gchar *text, gint len, GtkIMHtml *imhtml);
 static void insert_cb(GtkTextBuffer *buffer, GtkTextIter *iter, gchar *text, gint len, GtkIMHtml *imhtml);
 static void delete_cb(GtkTextBuffer *buffer, GtkTextIter *iter, GtkTextIter *end, GtkIMHtml *imhtml);
@@ -416,10 +420,11 @@
 static gint
 gtk_imhtml_tip (gpointer data)
 {
-	GtkIMHtml *imhtml = data;
+	GtkIMHtml *imhtml = (GtkIMHtml *)data;
 	PangoFontMetrics *font_metrics;
 	PangoLayout *layout;
 	PangoFont *font;
+	PangoLanguage *lang;
 
 	gint gap, x, y, h, w, scr_w, baseline_skip;
 
@@ -464,7 +469,9 @@
 		return FALSE;
 	}
 
-	font_metrics = pango_font_get_metrics(font, NULL);
+	lang = pango_context_get_language (pango_layout_get_context(layout));
+	font_metrics = pango_font_get_metrics(font, lang); //it's ok.
+//	font_metrics = pango_font_get_metrics(font, NULL); //crash!
 
 	pango_layout_get_pixel_size(layout, &scr_w, NULL);
 	gap = PANGO_PIXELS((pango_font_metrics_get_ascent(font_metrics) +
@@ -579,14 +586,15 @@
 		tip = g_object_get_data(G_OBJECT(anchor), "gtkimhtml_plaintext");
 		hand = FALSE;
 	}
-
+//yaz here bomb explodes
+#if 1
 	if (tip){
 		if (!GTK_IMHTML(imhtml)->editable && hand)
 			gdk_window_set_cursor(win, GTK_IMHTML(imhtml)->hand_cursor);
 		GTK_IMHTML(imhtml)->tip_timer = g_timeout_add (TOOLTIP_TIMEOUT,
 							       gtk_imhtml_tip, imhtml);
 	}
-
+#endif
 	GTK_IMHTML(imhtml)->tip = tip;
 	g_slist_free(tags);
 	return FALSE;
--- a/pidgin/gtkimhtmltoolbar.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/pidgin/gtkimhtmltoolbar.c	Thu Mar 20 11:57:02 2008 +0000
@@ -1304,7 +1304,7 @@
 	gtk_container_add(GTK_CONTAINER(smiley_button), bbox);
 	image = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_SMILEY, gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
 	gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 0);
-	label = gtk_label_new_with_mnemonic(_("_Smile!"));
+	label = gtk_label_new_with_mnemonic(_("Smile!"));
 	gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 0);
 	gtk_box_pack_start(GTK_BOX(box), smiley_button, FALSE, FALSE, 0);
 	g_signal_connect_swapped(G_OBJECT(smiley_button), "clicked", G_CALLBACK(gtk_button_clicked), toolbar->smiley);
--- a/pidgin/gtkimhtmltoolbar.h	Thu Mar 20 08:42:15 2008 +0000
+++ b/pidgin/gtkimhtmltoolbar.h	Thu Mar 20 11:57:02 2008 +0000
@@ -30,7 +30,7 @@
 extern "C" {
 #endif
 
-#define DEFAULT_FONT_FACE "Helvetica 12"
+#define DEFAULT_FONT_FACE "Sans 12"
 
 #define GTK_TYPE_IMHTMLTOOLBAR            (gtk_imhtmltoolbar_get_type ())
 #define GTK_IMHTMLTOOLBAR(obj)            (GTK_CHECK_CAST ((obj), GTK_TYPE_IMHTMLTOOLBAR, GtkIMHtmlToolbar))
--- a/pidgin/gtkprefs.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/pidgin/gtkprefs.c	Thu Mar 20 11:57:02 2008 +0000
@@ -881,6 +881,9 @@
 	pidgin_prefs_checkbox(_("Show close b_utton on tabs"),
 				PIDGIN_PREFS_ROOT "/conversations/close_on_tabs", vbox2);
 
+	pidgin_prefs_checkbox(_("Trim names on vertical tabs"),
+				PIDGIN_PREFS_ROOT "/conversations/trim_vertical_tabs", vbox2);
+
 	label = pidgin_prefs_dropdown(vbox2, _("_Placement:"), PURPLE_PREF_INT,
 					PIDGIN_PREFS_ROOT "/conversations/tab_side",
 					_("Top"), GTK_POS_TOP,
@@ -963,7 +966,7 @@
 #endif
 
 	pidgin_prefs_checkbox(_("Use smooth-scrolling"), PIDGIN_PREFS_ROOT "/conversations/use_smooth_scrolling", vbox);
-
+	pidgin_prefs_checkbox(_("Use msn messenger style"), "/purple/conversations/msnstyle", vbox);//yaz
 #ifdef _WIN32
 	pidgin_prefs_checkbox(_("F_lash window when IMs are received"), PIDGIN_PREFS_ROOT "/win32/blink_im", vbox);
 
--- a/pidgin/gtkutils.c	Thu Mar 20 08:42:15 2008 +0000
+++ b/pidgin/gtkutils.c	Thu Mar 20 11:57:02 2008 +0000
@@ -3461,3 +3461,61 @@
 #endif
 }
 
+gchar *
+pidgin_gtk_ellipsis_text(GtkWidget *widget, const char *text, gint min_width, gchar *ellipsis)
+{
+	PangoLayout *layout;
+	gint width, height;
+	gint ewidth;
+	glong len0, len1, len2;
+	gchar *buf, *buf_tmp;
+	gboolean with_ellipsis = FALSE;
+	const gchar default_ellipsis[] = "...";
+
+	if(!ellipsis)
+		ellipsis = default_ellipsis;
+
+	/* allocate buf */
+	buf = g_malloc0(strlen(text) * 2);
+
+	/* create layout */
+	layout = gtk_widget_create_pango_layout(widget, ellipsis);
+	pango_layout_get_pixel_size(layout, &width, &height);
+	ewidth = width; /* length of ellipsis text. */
+
+	len0 = 0;
+	len1 = g_utf8_strlen(text, -1);
+	len2 = len1;
+
+	while (1) {
+
+		if (len2 == len0)
+			break;
+
+		g_utf8_strncpy(buf, text, len2);
+		pango_layout_set_text(layout, buf, -1);
+		pango_layout_get_pixel_size(layout, &width, &height);
+
+		if(!with_ellipsis && width <= min_width)
+			break;
+		else
+			with_ellipsis = TRUE;
+
+		if (width + ewidth > min_width)
+			len1 = len2;
+		else
+			len0 = len2;
+
+		len2 = (len0 + len1) / 2;
+	}
+
+	g_object_unref(layout);
+
+	if (with_ellipsis) {
+		buf_tmp = buf;
+		buf = g_strdup_printf("%s%s", buf_tmp, ellipsis);
+		g_free(buf_tmp);
+	}
+
+	return buf;
+}
--- a/pidgin/gtkutils.h	Thu Mar 20 08:42:15 2008 +0000
+++ b/pidgin/gtkutils.h	Thu Mar 20 11:57:02 2008 +0000
@@ -809,5 +809,7 @@
  */
 GtkWidget *pidgin_add_widget_to_vbox(GtkBox *vbox, const char *widget_label, GtkSizeGroup *sg, GtkWidget *widget, gboolean expand, GtkWidget **p_label);
 
+gchar *pidgin_gtk_ellipsis_text(GtkWidget *widget, const char *text, gint min_width, gchar *ellipsis);
+
 #endif /* _PIDGINUTILS_H_ */
 
--- a/po/ja.po	Thu Mar 20 08:42:15 2008 +0000
+++ b/po/ja.po	Thu Mar 20 11:57:02 2008 +0000
@@ -14496,11 +14496,6 @@
 msgid "User has typed something and stopped"
 msgstr "ユーザは何か入力して一時停止中です"
 
-#. Build the Send To menu
-#: ../pidgin/gtkconv.c:3574 ../pidgin/gtkconv.c:8022
-msgid "_Send To"
-msgstr "送信先(_S)"
-
 #: ../pidgin/gtkconv.c:4286
 msgid "_Send"
 msgstr "送信する(_S)"