changeset 27732:73ef026191e7

propagate from branch 'im.pidgin.pidgin' (head 8d61a119c53ac77e595d5ec300d30482b914bdf7) to branch 'im.pidgin.pidgin.yaz' (head 456e82d794038f69ba0113eecb77dc8ce0f3d605)
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Tue, 16 Oct 2007 07:48:36 +0000
parents 271d154bbb91 (diff) 17ee8c81c99b (current diff)
children bc47a0388c66
files configure.ac libpurple/notify.c libpurple/protocols/jabber/jabber.c libpurple/protocols/msn/msg.c libpurple/protocols/msn/msn.c libpurple/protocols/msn/switchboard.c libpurple/protocols/oscar/oscar.c libpurple/util.c libpurple/util.h pidgin/gtkconv.c pidgin/gtkutils.c pidgin/gtkutils.h
diffstat 32 files changed, 858 insertions(+), 80 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ChangeLog.yaz	Tue Oct 16 07:48:36 2007 +0000
@@ -0,0 +1,3 @@
+yaz patched version:
+	* just branched.
+	
--- a/autogen.sh	Tue Oct 16 07:31:41 2007 +0000
+++ b/autogen.sh	Tue Oct 16 07:48:36 2007 +0000
@@ -69,5 +69,5 @@
 echo;
 echo "Running ./configure ${CONFIGURE_ARGS} $@"
 echo;
-./configure ${CONFIGURE_ARGS} $@
+#./configure ${CONFIGURE_ARGS} $@
 
--- a/configure.ac	Tue Oct 16 07:31:41 2007 +0000
+++ b/configure.ac	Tue Oct 16 07:48:36 2007 +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 sk sl sq sr sr@latin sv ta te th tr uk 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
--- a/libpurple/conversation.c	Tue Oct 16 07:31:41 2007 +0000
+++ b/libpurple/conversation.c	Tue Oct 16 07:48:36 2007 +0000
@@ -1142,17 +1142,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	Tue Oct 16 07:31:41 2007 +0000
+++ b/libpurple/notify.c	Tue Oct 16 07:48:36 2007 +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	Tue Oct 16 07:31:41 2007 +0000
+++ b/libpurple/protocols/gg/lib/http.c	Tue Oct 16 07:48:36 2007 +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	Tue Oct 16 07:31:41 2007 +0000
+++ b/libpurple/protocols/gg/lib/pubdir50.c	Tue Oct 16 07:48:36 2007 +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	Tue Oct 16 07:31:41 2007 +0000
+++ b/libpurple/protocols/irc/irc.c	Tue Oct 16 07:48:36 2007 +0000
@@ -946,6 +946,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	Tue Oct 16 07:31:41 2007 +0000
+++ b/libpurple/protocols/irc/parse.c	Tue Oct 16 07:48:36 2007 +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);
 
@@ -156,6 +156,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)
 {
@@ -223,33 +237,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;
+	size_t 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  = (char *)sanitize_utf((unsigned char *)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;
+	size_t strtmp_len;
 
 	enclist = purple_account_get_string(irc->account, "encoding", IRC_DEFAULT_CHARSET);
 	encodings = g_strsplit(enclist, ",", -1);
@@ -267,13 +358,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  = (char *)botch_utf((unsigned char *)utf8, strlen(utf8), &strtmp_len);
+ 			g_strfreev(encodings);
+			g_free(utf8);
+			return strtmp;
 		}
 	}
 	g_strfreev(encodings);
--- a/libpurple/protocols/jabber/jabber.c	Tue Oct 16 07:31:41 2007 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Tue Oct 16 07:48:36 2007 +0000
@@ -360,8 +360,8 @@
 
 void jabber_send(JabberStream *js, xmlnode *packet)
 {
-	char *txt;
-	int len;
+	char *txt, *utf;
+	int len, utflen;
 
 	purple_signal_emit(my_protocol, "jabber-sending-xmlnode", js->gc, &packet);
 
@@ -370,8 +370,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);
 }
 
 void jabber_keepalive(PurpleConnection *gc)
--- a/libpurple/protocols/jabber/message.c	Tue Oct 16 07:31:41 2007 +0000
+++ b/libpurple/protocols/jabber/message.c	Tue Oct 16 07:48:36 2007 +0000
@@ -360,14 +360,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;
+				int 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;
+				int 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
 				 */
@@ -375,6 +380,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	Tue Oct 16 07:31:41 2007 +0000
+++ b/libpurple/protocols/jabber/si.c	Tue Oct 16 07:48:36 2007 +0000
@@ -38,6 +38,8 @@
 
 #include "si.h"
 
+#include "util.h"
+
 struct bytestreams_streamhost {
 	char *jid;
 	char *host;
@@ -668,7 +670,16 @@
 	xmlnode *si, *file, *feature, *x, *field, *option, *value;
 	char buf[32];
 
-	xfer->filename = g_path_get_basename(xfer->local_filename);
+//	xfer->filename = g_path_get_basename(xfer->local_filename);
+	{ /* yaz */
+		guchar *tmp;
+		size_t dummy;
+		tmp = botch_utf(xfer->filename, strlen(xfer->filename), &dummy);
+		if(tmp){
+			purple_xfer_set_filename(xfer, (char *)tmp);
+			g_free(tmp);
+		}
+	}
 
 	iq = jabber_iq_new(jsx->js, JABBER_IQ_SET);
 	xmlnode_set_attrib(iq->node, "to", xfer->who);
--- a/libpurple/protocols/msn/msg.c	Tue Oct 16 07:31:41 2007 +0000
+++ b/libpurple/protocols/msn/msg.c	Tue Oct 16 07:48:36 2007 +0000
@@ -527,9 +527,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((void *)data, len, &msg->body_len); /* yaz */
 	}
 	else
 	{
--- a/libpurple/protocols/msn/msn.c	Tue Oct 16 07:31:41 2007 +0000
+++ b/libpurple/protocols/msn/msn.c	Tue Oct 16 07:48:36 2007 +0000
@@ -180,6 +180,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.
 
 }
 
@@ -293,6 +295,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;
@@ -2096,6 +2115,9 @@
 	msn_switchboard_init();
 	msn_sync_init();
 
+	// yaz
+	msn_ipc_init(plugin);
+
 	return TRUE;
 }
 
@@ -2105,6 +2127,9 @@
 	msn_switchboard_end();
 	msn_sync_end();
 
+	// yaz
+	msn_ipc_end(plugin);
+
 	return TRUE;
 }
 
@@ -2216,7 +2241,7 @@
 	NULL,					/* register_user */
 	NULL,					/* get_cb_info */
 	NULL,					/* get_cb_away */
-	NULL,					/* alias_buddy */
+	NULL,				/* alias_buddy */
 	msn_group_buddy,		/* group_buddy */
 	msn_rename_group,		/* rename_group */
 	NULL,					/* buddy_free */
--- a/libpurple/protocols/msn/switchboard.c	Tue Oct 16 07:31:41 2007 +0000
+++ b/libpurple/protocols/msn/switchboard.c	Tue Oct 16 07:48:36 2007 +0000
@@ -904,13 +904,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/oscar/family_icbm.c	Tue Oct 16 07:31:41 2007 +0000
+++ b/libpurple/protocols/oscar/family_icbm.c	Tue Oct 16 07:48:36 2007 +0000
@@ -51,6 +51,10 @@
 #include "win32dep.h"
 #endif
 
+/* yaz */
+#include "debug.h"
+#include "../../util.h"
+
 /**
  * Add a standard ICBM header to the given bstream with the given
  * information.
@@ -453,6 +457,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;
@@ -492,15 +499,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 ucs-2be, 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), 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, "unicode-2-0");
+		aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
+		//yaz
+		ucs = g_convert(msg, strlen(msg), "UCS-2BE", "UTF-8", NULL, &bytes, NULL);
+		if(ucs){
+			botch_ucs(ucs, bytes);
+			aim_tlvlist_add_raw(&inner_tlvlist, 0x000c, bytes, 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/oft.c	Tue Oct 16 07:31:41 2007 +0000
+++ b/libpurple/protocols/oscar/oft.c	Tue Oct 16 07:48:36 2007 +0000
@@ -679,7 +679,15 @@
 	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 */
+		guchar *tmp = NULL;
+		size_t dummy;
+		tmp = botch_utf(xfer->filename, strlen(xfer->filename), &dummy);
+		if(tmp){
+			purple_xfer_set_filename(xfer, (char *)tmp);
+			g_free(tmp);
+		}
+	}
 	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	Tue Oct 16 07:31:41 2007 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Tue Oct 16 07:48:36 2007 +0000
@@ -303,7 +303,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)){
+			size_t newlen;
+			utf8 = sanitize_utf(text, textlen, &newlen);
+			goto done;
+		}
+		// not UTF-8
+		purple_debug_info("yaz oscar", "Empty encoding, assuming UCS-2BE\n");
+		sanitize_ucs(text, textlen);
+		utf8 = g_convert(text, textlen, "UTF-8", "UCS-2BE", 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") ||
@@ -311,6 +329,7 @@
 	{
 		utf8 = g_convert(text, textlen, "UTF-8", "Windows-1252", NULL, NULL, NULL);
 	} else if (!g_ascii_strcasecmp(encoding, "unicode-2-0")) {
+		sanitize_ucs(text, textlen);
 		/* Some official ICQ clients are apparently total crack,
 		 * and have been known to save a UTF-8 string converted
 		 * from the locale character set to UCS-2 (not from UTF-8
@@ -349,7 +368,7 @@
 		else
 			utf8 = g_strndup(text, textlen);
 	}
-
+done:
 	return utf8;
 }
 
@@ -505,7 +524,8 @@
 		b = purple_find_buddy(account, destsn);
 		if ((b != NULL) && (PURPLE_BUDDY_IS_ONLINE(b)))
 		{
-			*msg = g_convert(from, -1, "UCS-2BE", "UTF-8", NULL, &msglen, NULL);
+			*msg = g_convert(from, strlen(from), "UCS-2BE", "UTF-8", NULL, &msglen, NULL);
+			botch_ucs(*msg, msglen);
 			if (*msg != NULL)
 			{
 				*charset = AIM_CHARSET_UNICODE;
@@ -539,7 +559,8 @@
 	/*
 	 * Nothing else worked, so send as UCS-2BE.
 	 */
-	*msg = g_convert(from, -1, "UCS-2BE", "UTF-8", NULL, &msglen, &err);
+	*msg = g_convert(from, strlen(from), "UCS-2BE", "UTF-8", NULL, &msglen, &err);
+	botch_ucs(*msg, msglen);
 	if (*msg != NULL) {
 		*charset = AIM_CHARSET_UNICODE;
 		*charsubset = 0x0000;
@@ -1977,6 +1998,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);
 		}
@@ -2078,6 +2101,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;
@@ -2100,6 +2124,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,
@@ -2333,7 +2359,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++) {
@@ -4405,7 +4431,8 @@
 
 	charset = oscar_charset_check(str);
 	if (charset == AIM_CHARSET_UNICODE) {
-		encoded = g_convert(str, -1, "UCS-2BE", "UTF-8", NULL, ret_len, NULL);
+		encoded = g_convert(str, strlen(str), "UCS-2BE", "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);
@@ -5497,7 +5524,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/yahoo/util.c	Tue Oct 16 07:31:41 2007 +0000
+++ b/libpurple/protocols/yahoo/util.c	Tue Oct 16 07:48:36 2007 +0000
@@ -29,6 +29,7 @@
 #include "prpl.h"
 
 #include "yahoo.h"
+#include "util.h"
 
 #include <string.h>
 
@@ -47,7 +48,8 @@
 char *yahoo_string_encode(PurpleConnection *gc, const char *str, gboolean *utf8)
 {
 	struct yahoo_data *yd = gc->proto_data;
-	char *ret;
+	char *ret, *strtmp;
+	int newlen;
 	const char *to_codeset;
 
 	if (yd->jp && utf8 && *utf8)
@@ -61,7 +63,12 @@
 	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);
+	strtmp = sanitize_utf((char *)str, strlen((char *)str), &newlen);
+
+	ret = g_convert_with_fallback(strtmp, strlen(strtmp), to_codeset, "UTF-8", "?", NULL, NULL, NULL);
+
+	g_free(strtmp);
+
 	if (ret)
 		return ret;
 	else
@@ -79,8 +86,9 @@
 char *yahoo_string_decode(PurpleConnection *gc, const char *str, gboolean utf8)
 {
 	struct yahoo_data *yd = gc->proto_data;
-	char *ret;
-	const char *from_codeset;
+	char *ret, *tmp;
+	char *from_codeset;
+	int newlen;
 
 	if (utf8) {
 		if (g_utf8_validate(str, -1, NULL))
@@ -92,10 +100,21 @@
 	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);
+	/* yaz: it's a kind of voodoo to avoid malconversion of "~". */
+	tmp = g_convert(str, strlen(str), "EUC-JP", from_codeset, NULL, NULL, NULL);
+	if(tmp) { 
+		ret = g_convert(tmp, strlen(tmp), "UTF-8", "EUC-JP", NULL, NULL, NULL);
+		g_free(tmp);
+	} else {
+		ret = g_convert_with_fallback(str, strlen(str), "UTF-8", from_codeset, NULL, NULL, NULL, NULL);
+	}
 
-	if (ret)
+	if (ret){
+		tmp = ret;
+		ret = botch_utf(ret, strlen(ret), &newlen);
+		g_free(tmp);
 		return ret;
+	}
 	else
 		return g_strdup("");
 }
--- a/libpurple/protocols/yahoo/yahoo.c	Tue Oct 16 07:31:41 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Tue Oct 16 07:48:36 2007 +0000
@@ -3668,6 +3668,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) {
@@ -3712,6 +3716,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) {
@@ -3722,7 +3730,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 */
@@ -4413,7 +4421,7 @@
 	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_packet.c	Tue Oct 16 07:31:41 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo_packet.c	Tue Oct 16 07:48:36 2007 +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,9 @@
 {
 	struct yahoo_pair *pair;
 
+	g_return_if_fail(pkt != NULL);
+	g_return_if_fail(value != NULL);
+
 	pair = g_new0(struct yahoo_pair, 1);
 	pair->key = key;
 	pair->value = g_strdup_printf("%d", value);
@@ -386,6 +390,9 @@
 {
 	int ret;
 
+	g_return_if_fail(pkt != NULL);
+	g_return_if_fail(yd != NULL);
+
 	ret = yahoo_packet_send(pkt, yd);
 	yahoo_packet_free(pkt);
 	return ret;
--- a/libpurple/protocols/yahoo/yahoo_profile.c	Tue Oct 16 07:31:41 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo_profile.c	Tue Oct 16 07:48:36 2007 +0000
@@ -22,6 +22,7 @@
  */
 
 #define PHOTO_SUPPORT 1
+//original is 1 --yaz
 
 #include "internal.h"
 #include "debug.h"
@@ -761,6 +762,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)
 {
@@ -1011,6 +1013,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);
 		}
 	}
@@ -1022,7 +1027,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) {
@@ -1054,47 +1059,48 @@
 		}
 	}
 #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);
-
+            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,
 			strings->realname_string, (yd->jp ? 3 : 1), "\n", '\n',
-			NULL, _("Real Name"), 0, NULL, NULL);
+            NULL, _("Real Name"), 0, NULL, NULL);
 
 	/* extract their Location and put it in */
 	found |= purple_markup_extract_info_field(stripped, stripped_len, user_info,
 			strings->location_string, (yd->jp ? 4 : 2), "\n", '\n',
-			NULL, _("Location"), 0, NULL, NULL);
+            NULL, _("Location"), 0, NULL, NULL);
 
 	/* extract their Age and put it in */
 	found |= purple_markup_extract_info_field(stripped, stripped_len, user_info,
 			strings->age_string, (yd->jp ? 2 : 3), "\n", '\n',
-			NULL, _("Age"), 0, NULL, NULL);
+            NULL, _("Age"), 0, NULL, NULL);
 
 	/* extract their MaritalStatus and put it in */
 	found |= purple_markup_extract_info_field(stripped, stripped_len, user_info,
 			strings->maritalstatus_string, (yd->jp ? 2 : 3), "\n", '\n',
-			strings->no_answer_string, _("Marital Status"), 0, NULL, NULL);
+            strings->no_answer_string, _("Marital Status"), 0, NULL, NULL);
 
 	/* extract their Gender and put it in */
 	found |= purple_markup_extract_info_field(stripped, stripped_len, user_info,
 			strings->gender_string, (yd->jp ? 2 : 3), "\n", '\n',
-			strings->no_answer_string, _("Gender"), 0, NULL, NULL);
+            strings->no_answer_string, _("Gender"), 0, NULL, NULL);
 
 	/* extract their Occupation and put it in */
 	found |= purple_markup_extract_info_field(stripped, stripped_len, user_info,
 			strings->occupation_string, 2, "\n", '\n',
-			NULL, _("Occupation"), 0, NULL, NULL);
-
+            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
@@ -1103,18 +1109,18 @@
 	 * 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))
+            '\n', "\n", _("Hobbies"), 0, NULL, NULL))
 	{
 		if (!purple_markup_extract_info_field(stripped, stripped_len, user_info,
 				strings->hobbies_string, 1, strings->favorite_quote_string,
-				'\n', "\n", _("Hobbies"), 0, NULL, NULL))
+                '\n', "\n", _("Hobbies"), 0, NULL, NULL))
 		{
 			found |= purple_markup_extract_info_field(stripped, stripped_len, user_info,
 					strings->hobbies_string, 1, strings->links_string,
-					'\n', "\n", _("Hobbies"), 0, NULL, NULL);
+                    '\n', "\n", _("Hobbies"), 0, NULL, NULL);
 		}
 		else
 			found = TRUE;
@@ -1124,7 +1130,7 @@
 
 	if (!purple_markup_extract_info_field(stripped, stripped_len, user_info,
 			strings->latest_news_string, 1, strings->favorite_quote_string,
-			'\n', "\n", _("Latest News"), 0, NULL, NULL))
+            '\n', "\n", _("Latest News"), 0, NULL, NULL))
 	{
 		found |= purple_markup_extract_info_field(stripped, stripped_len, user_info,
 				strings->latest_news_string, (yd->jp ? 2 : 1), strings->links_string,
@@ -1135,7 +1141,7 @@
 
 	found |= purple_markup_extract_info_field(stripped, stripped_len, user_info,
 			strings->favorite_quote_string, 1, strings->links_string,
-			'\n', "\n", _("Favorite Quote"), 0, NULL, NULL);
+            '\n', "\n", _("Favorite Quote"), 0, NULL, NULL);
 
 	/* Home Page will either be "No home page specified",
 	 * or "Home Page: " and a link.
@@ -1149,7 +1155,7 @@
 		{
 			found |= purple_markup_extract_info_field(stripped, stripped_len, user_info,
 					strings->home_page_string, 1, "\n", 0, NULL,
-					_("Home Page"), 1, NULL, NULL);
+                    _("Home Page"), 1, NULL, NULL);
 		}
 	}
 
@@ -1164,7 +1170,7 @@
 	{
 		if (purple_markup_extract_info_field(stripped, stripped_len, user_info,
 				strings->cool_link_1_string, 1, "\n", 0, NULL,
-				_("Cool Link 1"), 1, NULL, NULL))
+                _("Cool Link 1"), 1, NULL, NULL))
 		{
 			found = TRUE;
 			if (purple_markup_extract_info_field(stripped, stripped_len, user_info,
@@ -1182,13 +1188,14 @@
 		/* see if Member Since is there, and if so, extract it. */
 		found |= purple_markup_extract_info_field(stripped, stripped_len, user_info,
 				"Member Since:", 1, last_updated_utf8_string,
-				'\n', NULL, _("Member Since"), 0, NULL, yahoo_info_date_reformat);
+                '\n', NULL, _("Member Since"), 0, NULL, yahoo_info_date_reformat);
 
 		/* extract the Last Updated date and put it in */
 		found |= purple_markup_extract_info_field(stripped, stripped_len, user_info,
 				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));
+                _("Last Update"), 0, NULL, (yd->jp ? NULL : yahoo_info_date_reformat));
 	}
+#endif
 	} /* if (profile_state == PROFILE_STATE_DEFAULT) */
 
 	if(!found)
--- a/libpurple/util.c	Tue Oct 16 07:31:41 2007 +0000
+++ b/libpurple/util.c	Tue Oct 16 07:48:36 2007 +0000
@@ -1172,6 +1172,8 @@
 		return FALSE;
 
 	q = strstr(p, end_token);
+	if(q == NULL) //yaz
+		return FALSE;
 
 	/* Trim leading blanks */
 	while (*p != '\n' && g_ascii_isspace(*p)) {
@@ -1184,7 +1186,7 @@
 	}
 
 	/* Don't bother with null strings */
-	if (p == q)
+	if (p >= q)
 		return FALSE;
 
 	if (q != NULL && (!no_value_token ||
@@ -4488,3 +4490,234 @@
 #endif /* HAVE_SIGNAL_H */
 #endif /* !_WIN32 */
 }
+
+
+void botch_ucs(gchar *ucs, gssize len)
+{
+	gint i;
+
+ 	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;
+ 		}
+ 	}
+
+}
+
+void sanitize_ucs(gchar *ucs, gssize len)
+{
+	gint i;
+
+	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;
+		}
+	}
+}
+
+guchar *sanitize_utf(unsigned char *msg, size_t len, size_t *newlen)
+{
+	gint i;
+	size_t bytes;
+	unsigned char *utf;
+
+	utf = 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); //1byte詰める
+				       bytes--;
+				       break;
+			       case 0xa1:	// £
+				       *(utf+i) = 0xc2;
+				       *(utf+i+1) = 0xa3;
+				       memmove(utf+i+2, utf+i+3,
+					       len-i-3); //1byte詰める
+				       bytes--;
+				       break;
+			       case 0xa2:	// ¬
+				       *(utf+i) = 0xc2;
+				       *(utf+i+1) = 0xac;
+				       memmove(utf+i+2, utf+i+3,
+					       len-i-3); //1byte詰める
+				       bytes--;
+				       break;
+			       }
+			       break;
+			}
+			break;
+		}
+	}
+	*(utf+bytes)= 0x00; //terminate
+	*newlen = bytes;
+	return utf;
+}
+
+
+guchar *botch_utf(const void *msg, size_t len, size_t *newlen)
+{
+ 	int i,bytes;
+	unsigned char *utf;
+
+	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
+	*newlen = bytes;
+	return utf;
+}
--- a/libpurple/util.h	Tue Oct 16 07:31:41 2007 +0000
+++ b/libpurple/util.h	Tue Oct 16 07:48:36 2007 +0000
@@ -1196,4 +1196,10 @@
 }
 #endif
 
+/* to address incompatibility with cp932. */
+void botch_ucs(gchar *ucs, gssize len);
+void sanitize_ucs(gchar *ucs, gssize len);
+guchar *botch_utf(const void *utf, size_t len, size_t *newlen);
+guchar *sanitize_utf(unsigned char *msg, size_t len, size_t *newlen);
+
 #endif /* _PURPLE_UTIL_H_ */
--- a/pidgin/gtkconv.c	Tue Oct 16 07:31:41 2007 +0000
+++ b/pidgin/gtkconv.c	Tue Oct 16 07:48:36 2007 +0000
@@ -112,6 +112,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;
 
@@ -2954,20 +2958,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>" },
+#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>" },
+#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 },
@@ -3642,7 +3659,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();
@@ -4115,7 +4136,9 @@
 	PurpleConversation *conv = gtkconv->active_conv;
 	PidginChatPane *gtkchat;
 	char *new_topic;
-	const char *current_topic;
+//	const char *current_topic;
+	char dummy[] = "No Topic";
+	char *current_topic = NULL;
 
 	gc      = purple_conversation_get_gc(conv);
 
@@ -4128,8 +4151,13 @@
 	gtkconv = PIDGIN_CONVERSATION(conv);
 	gtkchat = gtkconv->u.chat;
 	new_topic = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtkchat->topic_text)));
+//	purple_debug_info("yaz gtkconv", "new_topic=%s\n", new_topic);
 	current_topic = purple_conv_chat_get_topic(PURPLE_CONV_CHAT(conv));
 
+	if(!current_topic)
+ 		current_topic = dummy;
+//	purple_debug_info("yaz gtkconv", "current_topic=%s\n", current_topic);
+
 	if(current_topic && !g_utf8_collate(new_topic, current_topic)){
 		g_free(new_topic);
 		return;
@@ -6515,13 +6543,14 @@
 			(fields & PIDGIN_CONV_SET_TITLE) ||
     			(fields & PIDGIN_CONV_TOPIC))
 	{
-		char *title;
+		char *title, *title_tmp;
 		PurpleConvIm *im = NULL;
 		PurpleAccount *account = purple_conversation_get_account(conv);
 	 	PurpleBuddy *buddy = NULL;
 		PurplePresence *p = NULL;
 		char *markup = NULL;
 		AtkObject *accessibility_obj;
+		gboolean ellipsis = FALSE;
 		/* I think this is a little longer than it needs to be but I'm lazy. */
 		char *style;
 		gboolean bold = FALSE;
@@ -6590,6 +6619,18 @@
 		} else {
 			style = NULL;
 		}
+		
+		// nosuke's tab width patch
+		if ((purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side") == (GTK_POS_LEFT|8)
+			 || purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side") == (GTK_POS_RIGHT|8))
+			&& purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/trim_vertical_tabs"))
+			ellipsis = TRUE;
+
+		if (ellipsis)
+			title_tmp = pidgin_gtk_ellipsis_text(gtkconv->tab_label, title, 60, "...");
+		else
+			title_tmp = title;
+
 
 		if (gtkconv->unseen_state == PIDGIN_UNSEEN_TEXT ||
 				gtkconv->unseen_state == PIDGIN_UNSEEN_NICK ||
@@ -6600,7 +6641,7 @@
 		{
 			char *html_title,*label;
 
-			html_title = g_markup_escape_text(title, -1);
+			html_title = g_markup_escape_text(title_tmp, -1);
 			label = g_strdup_printf("<span %s %s>%s</span>",
 			                        style ? style : "",
 			                        bold ? "weight=\"bold\"" : "",
@@ -6610,7 +6651,7 @@
 			g_free(label);
 		}
 		else
-			gtk_label_set_text(GTK_LABEL(gtkconv->tab_label), title);
+			gtk_label_set_text(GTK_LABEL(gtkconv->tab_label), title_tmp);
 
 		if (pidgin_conv_window_is_active_conversation(conv))
 			update_typing_icon(gtkconv);
@@ -6993,6 +7034,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)
@@ -7062,6 +7121,7 @@
 			pidgin_conv_tab_pack(gtkwin, gtkconvs->data);
 		}
 	}
+	trim_vertical_tabs_pref_cb(name, type, value, data);
 }
 
 static void
@@ -7510,6 +7570,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);
@@ -7563,6 +7624,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	Tue Oct 16 07:31:41 2007 +0000
+++ b/pidgin/gtkimhtml.c	Tue Oct 16 07:48:36 2007 +0000
@@ -89,6 +89,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);
@@ -417,10 +421,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;
 
@@ -461,7 +466,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) +
@@ -575,14 +582,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.h	Tue Oct 16 07:31:41 2007 +0000
+++ b/pidgin/gtkimhtmltoolbar.h	Tue Oct 16 07:48:36 2007 +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/gtkmain.c	Tue Oct 16 07:31:41 2007 +0000
+++ b/pidgin/gtkmain.c	Tue Oct 16 07:48:36 2007 +0000
@@ -203,10 +203,12 @@
 		purple_debug_warning("sighandler", "Caught signal %d\n", sig);
 		purple_connections_disconnect_all();
 		break;
+#if 1
 	case SIGSEGV:
 		fprintf(stderr, "%s", segfault_message);
 		abort();
 		break;
+#endif
 	case SIGCHLD:
 		/* Restore signal catching */
 		signal(SIGCHLD, sighandler);
--- a/pidgin/gtkprefs.c	Tue Oct 16 07:31:41 2007 +0000
+++ b/pidgin/gtkprefs.c	Tue Oct 16 07:48:36 2007 +0000
@@ -932,6 +932,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,
@@ -1014,7 +1017,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	Tue Oct 16 07:31:41 2007 +0000
+++ b/pidgin/gtkutils.c	Tue Oct 16 07:48:36 2007 +0000
@@ -3273,3 +3273,75 @@
 	gtk_entry_set_text(GTK_ENTRY(GTK_BIN((widget))->child), (text));
 }
 
+gchar *
+pidgin_gtk_ellipsis_text(GtkWidget *widget, const char *text, gint min_width, gchar *ellipsis)
+{
+	PangoLayout *layout;
+	gint width, height;
+	glong len0, len1, len2;
+	static gchar buf[1024], buf_tmp[1024];
+	gint ewidth;
+	gboolean with_ellipsis = FALSE;
+
+	g_strlcpy(buf_tmp, text, sizeof(buf_tmp));
+
+	if (strlen(text) >= sizeof(buf)) {
+		/* cutting off */
+		if (! g_utf8_validate(buf_tmp, -1, NULL)) {
+			buf_tmp[sizeof(buf_tmp) - 1] = '\0';
+			if (! g_utf8_validate(buf_tmp, -1, NULL)) {
+				buf_tmp[sizeof(buf_tmp) - 2] = '\0';
+				if (! g_utf8_validate(buf_tmp, -1, NULL)) {
+					return NULL; /* failed */
+				}
+			}
+		}
+	}
+
+	buf[0] = '\0';
+
+#define ELLIPSIS "..."
+
+	layout = gtk_widget_create_pango_layout(widget, 
+						ellipsis ? ellipsis : ELLIPSIS);
+
+	pango_layout_get_pixel_size(layout, &width, &height);
+	ewidth = width;
+
+	len0 = 0;
+	len1 = g_utf8_strlen(buf_tmp, -1);
+	len2 = len1;
+
+	while (1) {
+
+		if (len2 == len0)
+			break;
+
+		g_utf8_strncpy (buf, buf_tmp, 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_utf8_strncpy (buf, buf_tmp, len2);
+	g_object_unref(layout);
+
+
+	if (with_ellipsis) 
+		g_strlcat (buf, ellipsis ? ellipsis : ELLIPSIS, sizeof(buf));
+
+#undef ELLIPSIS
+
+	return buf;
+}
--- a/pidgin/gtkutils.h	Tue Oct 16 07:31:41 2007 +0000
+++ b/pidgin/gtkutils.h	Tue Oct 16 07:48:36 2007 +0000
@@ -705,5 +705,7 @@
  */
 void pidgin_text_combo_box_entry_set_text(GtkWidget *widget, const char *text);
 
+gchar *pidgin_gtk_ellipsis_text(GtkWidget *widget, const char *text, gint min_width, gchar *ellipsis);
+
 #endif /* _PIDGINUTILS_H_ */
 
--- a/po/ja.po	Tue Oct 16 07:31:41 2007 +0000
+++ b/po/ja.po	Tue Oct 16 07:48:36 2007 +0000
@@ -14498,8 +14498,8 @@
 
 #. Build the Send To menu
 #: ../pidgin/gtkconv.c:3574 ../pidgin/gtkconv.c:8022
-msgid "_Send To"
-msgstr "送信先(_S)"
+msgid "Send To"
+msgstr "送信先"
 
 #: ../pidgin/gtkconv.c:4286
 msgid "_Send"