changeset 2541:8229710b343b

[gaim-migrate @ 2554] fun stuff. committer: Tailor Script <tailor@pidgin.im>
author Eric Warmenhoven <eric@warmenhoven.org>
date Thu, 18 Oct 2001 20:56:59 +0000
parents 42e8c40247f5
children 221e1c8e2410
files ChangeLog TODO configure.ac src/about.c src/conversation.c src/core.c src/gtkimhtml.c src/html.c src/protocols/gg/.cvsignore src/protocols/gg/Makefile.am src/protocols/gg/gg.c src/protocols/gg/iconv_string.c src/protocols/gg/iconv_string.h src/protocols/msn/msn.c src/protocols/yahoo/yay.c src/prpl.c src/prpl.h
diffstat 17 files changed, 367 insertions(+), 131 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Thu Oct 18 19:55:19 2001 +0000
+++ b/ChangeLog	Thu Oct 18 20:56:59 2001 +0000
@@ -1,6 +1,10 @@
 Gaim: The Pimpin' Penguin IM Clone thats good for the soul! 
 
 version 0.47:
+	* Better font loading (pays attention to charset now)
+	  (thanks Arkadiusz Miskiewicz)
+	* Better recoding in Gadu-Gadu (thanks Arkadiusz Miskiewicz)
+	* Open Mail button for when you get new mail (Yahoo and MSN)
 
 version 0.46 (10/18/2001):
 	* New applet icons (courtesy David Raeman)
--- a/TODO	Thu Oct 18 19:55:19 2001 +0000
+++ b/TODO	Thu Oct 18 20:56:59 2001 +0000
@@ -50,6 +50,7 @@
 		that you can "send" more than one message before you're
 		actually connected. This is actually a bug that it doesn't do
 		this already.
+	Permit/Deny
 	Need some way of indicating "invite" in IM window
 	File Transfer
 
--- a/configure.ac	Thu Oct 18 19:55:19 2001 +0000
+++ b/configure.ac	Thu Oct 18 20:56:59 2001 +0000
@@ -100,7 +100,7 @@
 AC_ARG_WITH(krb4,      [  --with-krb4=PREFIX      Compile Zephyr plugin with Kerberos 4 support],kerberos="$withval",kerberos="no")
 
 if test "$enable_debug" = yes ; then
-	CFLAGS="$CFLAGS -Wall -g"
+	CFLAGS="$CFLAGS -Wall -g3"
 	AC_DEFINE(DEBUG)
 fi
 
--- a/src/about.c	Thu Oct 18 19:55:19 2001 +0000
+++ b/src/about.c	Thu Oct 18 20:56:59 2001 +0000
@@ -134,8 +134,8 @@
 
 		text = gtk_text_new(NULL, NULL);
 		gtk_text_insert(GTK_TEXT(text), NULL, NULL, NULL,
-				_("Rob Flynn (maintainer)               rob@marko.net\nEric Warmenhoven (lead coder)  warmenhoven@yahoo.com\n\nBenjamin Miller\nDecklin Foster\nJim Duchek\nMark Spencer (original author)   markster@marko.net"),
-				198);
+				_("Rob Flynn (maintainer)               rob@marko.net\nEric Warmenhoven (lead coder)  warmenhoven@yahoo.com\n\nBenjamin Miller\nDecklin Foster\nSean Egan\nJim Duchek\nMark Spencer (original author)   markster@marko.net"),
+				208);
 		gtk_box_pack_start(GTK_BOX(fbox), text, TRUE, TRUE, 0);
 		gtk_widget_show(text);
 
--- a/src/conversation.c	Thu Oct 18 19:55:19 2001 +0000
+++ b/src/conversation.c	Thu Oct 18 20:56:59 2001 +0000
@@ -2118,7 +2118,6 @@
 	GtkWidget *toolbar;
 	GtkWidget *hbox;
 	GtkWidget *label;
-	GtkStyle *style;
 	int dispstyle = set_dispstyle(0);
 
 	c->font_dialog = NULL;
@@ -2323,10 +2322,6 @@
 
 	update_buttons_by_protocol(c);
 
-	style = gtk_widget_get_style(GTK_WIDGET(entry));
-	gtk_imhtml_set_defaults(GTK_IMHTML(text), 0, &style->fg[GTK_STATE_NORMAL],
-				&style->base[GTK_STATE_NORMAL]);
-
 	gtk_widget_show(win);
 }
 
--- a/src/core.c	Thu Oct 18 19:55:19 2001 +0000
+++ b/src/core.c	Thu Oct 18 20:56:59 2001 +0000
@@ -277,7 +277,7 @@
 	}
 }
 
-static gint gaim_recv(GIOChannel *source, void *buf, gint len)
+static gint gaim_recv(GIOChannel *source, guchar *buf, gint len)
 {
 	gint total = 0;
 	gint cur;
@@ -322,7 +322,7 @@
 		return FALSE;
 	}
 
-	if (gaim_recv(source, &len, sizeof(len)) != sizeof(len)) {
+	if (gaim_recv(source, (guchar *)&len, sizeof(len)) != sizeof(len)) {
 		debug_printf("UI has abandoned us!\n");
 		uis = g_slist_remove(uis, ui);
 		g_io_channel_close(ui->channel);
--- a/src/gtkimhtml.c	Thu Oct 18 19:55:19 2001 +0000
+++ b/src/gtkimhtml.c	Thu Oct 18 20:56:59 2001 +0000
@@ -19,6 +19,9 @@
  *
  */
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include "gtkimhtml.h"
 #include <X11/Xlib.h>
 #include <gdk/gdkx.h>
@@ -27,6 +30,10 @@
 #include <ctype.h>
 #include <stdio.h>
 #include <math.h>
+#ifdef HAVE_LANGINFO_CODESET
+#include <langinfo.h>
+#include <locale.h>
+#endif
 
 #include "pixmaps/angel.xpm"
 #include "pixmaps/bigsmile.xpm"
@@ -79,6 +86,21 @@
 	gchar **image;
 };
 
+static gchar* getcharset()
+{
+	static gchar charset[64];
+#ifdef HAVE_LANGINFO_CODESET
+	gchar *ch = nl_langinfo(CODESET);
+	if (strncasecmp(ch, "iso-", 4) == 0)
+		g_snprintf(charset, sizeof(charset), "iso%s", ch + 4);
+	else
+		g_snprintf(charset, sizeof(charset), ch);
+#else
+	g_snprintf(charset, sizeof(charset), "iso8859-*");
+#endif
+	return charset;
+}
+
 static GtkSmileyTree*
 gtk_smiley_tree_new ()
 {
@@ -348,9 +370,12 @@
 	}
 	imhtml->tip_bit = NULL;
 
-	gdk_font_unref (imhtml->default_font);
-	gdk_color_free (imhtml->default_fg_color);
-	gdk_color_free (imhtml->default_bg_color);
+	if (imhtml->default_font)
+		gdk_font_unref (imhtml->default_font);
+	if (imhtml->default_fg_color)
+		gdk_color_free (imhtml->default_fg_color);
+	if (imhtml->default_bg_color)
+		gdk_color_free (imhtml->default_bg_color);
 
 	gdk_cursor_destroy (imhtml->hand_cursor);
 	gdk_cursor_destroy (imhtml->arrow_cursor);
@@ -406,10 +431,15 @@
 
 	gdk_window_set_cursor (widget->window, imhtml->arrow_cursor);
 
+	imhtml->default_font = gdk_font_ref (GTK_WIDGET (imhtml)->style->font);
+
 	gdk_window_set_background (widget->window, &widget->style->base [GTK_STATE_NORMAL]);
 	gdk_window_set_background (GTK_LAYOUT (imhtml)->bin_window,
 				   &widget->style->base [GTK_STATE_NORMAL]);
 
+	imhtml->default_fg_color = gdk_color_copy (&GTK_WIDGET (imhtml)->style->fg [GTK_STATE_NORMAL]);
+	imhtml->default_bg_color = gdk_color_copy (&GTK_WIDGET (imhtml)->style->base [GTK_STATE_NORMAL]);
+
 	gdk_window_show (GTK_LAYOUT (imhtml)->bin_window);
 }
 
@@ -1781,53 +1811,60 @@
 		return gdk_fontset_load ("-*-*-*-*-*-*-*-*-*-*-*-*-*-*,*");
 	}
 
-	g_snprintf (buf, sizeof (buf), "-*-%s-%s-%c-*-*-*-%d-*-*-*-*-iso8859-*",
+	g_snprintf (buf, sizeof (buf), "-*-%s-%s-%c-*-*-*-%d-*-*-*-*-%s",
 		    choice,
 		    bold ? "bold" : "medium",
 		    italics ? 'i' : 'r',
-		    size);
+		    size,
+		    getcharset());
 	font = gdk_font_load (buf);
 
 	if (!font && italics) {
-		g_snprintf (buf, sizeof (buf), "-*-%s-%s-o-*-*-*-%d-*-*-*-*-iso8859-*",
+		g_snprintf (buf, sizeof (buf), "-*-%s-%s-o-*-*-*-%d-*-*-*-*-%s",
 			    choice,
 			    bold ? "bold" : "medium",
-			    size);
+			    size,
+			    getcharset());
 		font = gdk_font_load (buf);
 	}
 
 	if (!font) {
-		g_snprintf (buf, sizeof (buf), "-*-%s-%s-%c-*-*-*-*-*-*-*-*-iso8859-*",
+		g_snprintf (buf, sizeof (buf), "-*-%s-%s-%c-*-*-*-*-*-*-*-*-%s",
 			    choice,
 			    bold ? "bold" : "medium",
-			    italics ? 'i' : 'r');
+			    italics ? 'i' : 'r',
+			    getcharset());
 		font = gdk_font_load (buf);
 	}
 
 	if (!font && italics) {
-		g_snprintf (buf, sizeof (buf), "-*-%s-%s-o-*-*-*-*-*-*-*-*-iso8859-*",
+		g_snprintf (buf, sizeof (buf), "-*-%s-%s-o-*-*-*-*-*-*-*-*-%s",
 			    choice,
-			    bold ? "bold" : "medium");
+			    bold ? "bold" : "medium",
+			    getcharset());
 		font = gdk_font_load (buf);
 	}
 
 	if (!font) {
-		g_snprintf (buf, sizeof (buf), "-*-%s-*-%c-*-*-*-*-*-*-*-*-iso8859-*",
+		g_snprintf (buf, sizeof (buf), "-*-%s-*-%c-*-*-*-*-*-*-*-*-%s",
 			    choice,
-			    italics ? 'i' : 'r');
+			    italics ? 'i' : 'r',
+			    getcharset());
 		font = gdk_font_load (buf);
 	}
 
 	if (!font) {
-		g_snprintf (buf, sizeof (buf), "-*-%s-*-%c-*-*-*-*-*-*-*-*-iso8859-*",
+		g_snprintf (buf, sizeof (buf), "-*-%s-*-%c-*-*-*-*-*-*-*-*-%s",
 			    choice,
-			    italics ? 'o' : '*');
+			    italics ? 'o' : '*',
+			    getcharset());
 		font = gdk_font_load (buf);
 	}
 
 	if (!font && italics) {
-		g_snprintf (buf, sizeof (buf), "-*-%s-*-*-*-*-*-*-*-*-*-*-iso8859-*",
-			    choice);
+		g_snprintf (buf, sizeof (buf), "-*-%s-*-*-*-*-*-*-*-*-*-*-%s",
+			    choice,
+			    getcharset());
 		font = gdk_font_load (buf);
 	}
 
@@ -1914,9 +1951,7 @@
 		{ "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT }
 	};
 
-	imhtml->default_font = gdk_font_ref (GTK_WIDGET (imhtml)->style->font);
-	imhtml->default_fg_color = gdk_color_copy (&GTK_WIDGET (imhtml)->style->fg [GTK_STATE_NORMAL]);
-	imhtml->default_bg_color = gdk_color_copy (&GTK_WIDGET (imhtml)->style->base [GTK_STATE_NORMAL]);
+	imhtml->default_font = gtk_imhtml_font_load (imhtml, DEFAULT_FONT_NAME, FALSE, FALSE, 0);
 	imhtml->hand_cursor = gdk_cursor_new (GDK_HAND2);
 	imhtml->arrow_cursor = gdk_cursor_new (GDK_LEFT_PTR);
 
--- a/src/html.c	Thu Oct 18 19:55:19 2001 +0000
+++ b/src/html.c	Thu Oct 18 20:56:59 2001 +0000
@@ -74,9 +74,9 @@
 	return text2;
 }
 
-static struct g_url parse_url(char *url)
+static struct g_url *parse_url(char *url)
 {
-	struct g_url test;
+	struct g_url *test = g_new0(struct g_url, 1);
 	char scan_info[255];
 	char port[5];
 	int f;
@@ -87,7 +87,7 @@
 	else
 		g_snprintf(scan_info, sizeof(scan_info),
 			   "%%[A-Za-z0-9.]:%%[0-9]/%%[A-Za-z0-9.~_-/&%%?=+^]");
-	f = sscanf(url, scan_info, test.address, port, test.page);
+	f = sscanf(url, scan_info, test->address, port, test->page);
 	if (f == 1) {
 		if (strstr(url, "http://"))
 			g_snprintf(scan_info, sizeof(scan_info),
@@ -95,8 +95,8 @@
 		else
 			g_snprintf(scan_info, sizeof(scan_info),
 				   "%%[A-Za-z0-9.]/%%[A-Za-z0-9.~_-/&%%?=+^]");
-		f = sscanf(url, scan_info, test.address, test.page);
-		g_snprintf(port, sizeof(test.port), "80");
+		f = sscanf(url, scan_info, test->address, test->page);
+		g_snprintf(port, sizeof(test->port), "80");
 		port[2] = 0;
 	}
 	if (f == 1) {
@@ -104,18 +104,18 @@
 			g_snprintf(scan_info, sizeof(scan_info), "http://%%[A-Za-z0-9.]");
 		else
 			g_snprintf(scan_info, sizeof(scan_info), "%%[A-Za-z0-9.]");
-		f = sscanf(url, scan_info, test.address);
-		g_snprintf(test.page, sizeof(test.page), "%c", '\0');
+		f = sscanf(url, scan_info, test->address);
+		g_snprintf(test->page, sizeof(test->page), "%c", '\0');
 	}
 
-	sscanf(port, "%d", &test.port);
+	sscanf(port, "%d", &test->port);
 	return test;
 }
 
 struct grab_url_data {
 	void (*callback)(gpointer, char *);
 	gpointer data;
-	struct g_url website;
+	struct g_url *website;
 	char *url;
 
 	int inpa;
@@ -133,6 +133,7 @@
 
 	if (sock == -1) {
 		gunk->callback(gunk->data, NULL);
+		g_free(gunk->website);
 		g_free(gunk->url);
 		g_free(gunk);
 		return;
@@ -140,7 +141,7 @@
 
 	if (!gunk->sentreq) {
 		char buf[256];
-		g_snprintf(buf, sizeof(buf), "GET /%s HTTP/1.0\r\n\r\n", gunk->website.page);
+		g_snprintf(buf, sizeof(buf), "GET /%s HTTP/1.0\r\n\r\n", gunk->website->page);
 		debug_printf("Request: %s\n", buf);
 		write(sock, buf, strlen(buf));
 		fcntl(sock, F_SETFL, O_NONBLOCK);
@@ -178,6 +179,7 @@
 		gunk->callback(gunk->data, gunk->webdata);
 		if (gunk->webdata)
 			g_free(gunk->webdata);
+		g_free(gunk->website);
 		g_free(gunk->url);
 		g_free(gunk);
 	} else {
@@ -186,6 +188,7 @@
 		gunk->callback(gunk->data, NULL);
 		if (gunk->webdata)
 			g_free(gunk->webdata);
+		g_free(gunk->website);
 		g_free(gunk->url);
 		g_free(gunk);
 	}
@@ -201,8 +204,9 @@
 	gunk->url = g_strdup(url);
 	gunk->website = parse_url(url);
 
-	if ((sock = proxy_connect(gunk->website.address, gunk->website.port,
+	if ((sock = proxy_connect(gunk->website->address, gunk->website->port,
 				  grab_url_callback, gunk)) < 0) {
+		g_free(gunk->website);
 		g_free(gunk->url);
 		g_free(gunk);
 		callback(data, g_strdup(_("g003: Error opening connection.\n")));
--- a/src/protocols/gg/.cvsignore	Thu Oct 18 19:55:19 2001 +0000
+++ b/src/protocols/gg/.cvsignore	Thu Oct 18 20:56:59 2001 +0000
@@ -5,3 +5,4 @@
 gg.lo
 libgg.la
 libgg.lo
+iconv_string.lo
--- a/src/protocols/gg/Makefile.am	Thu Oct 18 19:55:19 2001 +0000
+++ b/src/protocols/gg/Makefile.am	Thu Oct 18 20:56:59 2001 +0000
@@ -13,6 +13,8 @@
 
 libgg_a_SOURCES = libgg.c \
 		  libgg.h \
+		  iconv_string.c \
+		  iconv_string.h \
 		  gg.c
 
 else
@@ -23,6 +25,8 @@
 
 libgg_la_SOURCES = libgg.c \
 		   libgg.h \
+		   iconv_string.c \
+		   iconv_string.h \
 		   gg.c
 
 endif
--- a/src/protocols/gg/gg.c	Thu Oct 18 19:55:19 2001 +0000
+++ b/src/protocols/gg/gg.c	Thu Oct 18 20:56:59 2001 +0000
@@ -1,6 +1,6 @@
 /*
  * gaim - Gadu-Gadu Protocol Plugin
- * $Id: gg.c 2531 2001-10-16 23:24:35Z warmenhoven $
+ * $Id: gg.c 2554 2001-10-18 20:56:59Z warmenhoven $
  *
  * Copyright (C) 2001, Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
  * 
@@ -20,7 +20,9 @@
  *
  */
 
+#ifdef HAVE_CONFIG_H
 #include <config.h>
+#endif
 
 #include <netdb.h>
 #include <unistd.h>
@@ -40,6 +42,7 @@
 #endif
 #ifdef HAVE_ICONV
 #include <iconv.h>
+#include "iconv_string.h"
 #endif
 /* Library from EKG (Eksperymentalny Klient Gadu-Gadu) */
 #include "libgg.h"
@@ -89,35 +92,12 @@
 
 static gchar *charset_convert(const gchar *locstr, char *encsrc, char *encdst)
 {
+       gchar *result = NULL;
 #ifdef HAVE_ICONV
-	gchar *dststr;
-	size_t loclen, dstlen;
-	gchar *fsave, *tsave;
-	size_t count;
-	static iconv_t cd = (iconv_t)(-1);
-
-	if (cd == (iconv_t)(-1)) {
-		cd = iconv_open(encdst, encsrc);
-		if (cd == (iconv_t)(-1)) {
-			return g_strdup(locstr);
-		}
-	}
-
-	loclen = strlen(locstr);
-	/* we are ready for multibyte conversions */
-	dstlen = MB_LEN_MAX * loclen;
-	dststr = g_new0(gchar, dstlen + 1);
-	fsave = (gchar *)locstr;
-	tsave = dststr;
-	count = iconv(cd, &fsave, &loclen, &tsave, &dstlen);
-	if (count == -1) {
-		g_free(dststr);
-		return g_strdup(locstr);
-	}
-	return dststr;
-#else
+        if (iconv_string(encdst, encsrc, locstr, locstr+strlen(locstr)+1, &result, NULL) < 0)
+#endif
 	return g_strdup(locstr);
-#endif
+	return result;
 }
 
 static gboolean invalid_uin(char *uin)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/gg/iconv_string.c	Thu Oct 18 20:56:59 2001 +0000
@@ -0,0 +1,161 @@
+/* Copyright (C) 1999-2001 Bruno Haible.
+   This file is not part of the GNU LIBICONV Library.
+   This file is put into the public domain.  */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_ICONV
+#include "iconv_string.h"
+#include <iconv.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define tmpbufsize 4096
+
+int iconv_string (const char* tocode, const char* fromcode,
+                  const char* start, const char* end,
+                  char** resultp, size_t* lengthp)
+{
+  iconv_t cd = iconv_open(tocode,fromcode);
+  size_t length;
+  char* result;
+  if (cd == (iconv_t)(-1)) {
+    if (errno != EINVAL)
+      return -1;
+    /* Unsupported fromcode or tocode. Check whether the caller requested
+       autodetection. */
+    if (!strcmp(fromcode,"autodetect_utf8")) {
+      int ret;
+      /* Try UTF-8 first. There are very few ISO-8859-1 inputs that would
+         be valid UTF-8, but many UTF-8 inputs are valid ISO-8859-1. */
+      ret = iconv_string(tocode,"UTF-8",start,end,resultp,lengthp);
+      if (!(ret < 0 && errno == EILSEQ))
+        return ret;
+      ret = iconv_string(tocode,"ISO-8859-1",start,end,resultp,lengthp);
+      return ret;
+    }
+    if (!strcmp(fromcode,"autodetect_jp")) {
+      int ret;
+      /* Try 7-bit encoding first. If the input contains bytes >= 0x80,
+         it will fail. */
+      ret = iconv_string(tocode,"ISO-2022-JP-2",start,end,resultp,lengthp);
+      if (!(ret < 0 && errno == EILSEQ))
+        return ret;
+      /* Try EUC-JP next. Short SHIFT_JIS inputs may come out wrong. This
+         is unavoidable. People will condemn SHIFT_JIS.
+         If we tried SHIFT_JIS first, then some short EUC-JP inputs would
+         come out wrong, and people would condemn EUC-JP and Unix, which
+         would not be good. */
+      ret = iconv_string(tocode,"EUC-JP",start,end,resultp,lengthp);
+      if (!(ret < 0 && errno == EILSEQ))
+        return ret;
+      /* Finally try SHIFT_JIS. */
+      ret = iconv_string(tocode,"SHIFT_JIS",start,end,resultp,lengthp);
+      return ret;
+    }
+    if (!strcmp(fromcode,"autodetect_kr")) {
+      int ret;
+      /* Try 7-bit encoding first. If the input contains bytes >= 0x80,
+         it will fail. */
+      ret = iconv_string(tocode,"ISO-2022-KR",start,end,resultp,lengthp);
+      if (!(ret < 0 && errno == EILSEQ))
+        return ret;
+      /* Finally try EUC-KR. */
+      ret = iconv_string(tocode,"EUC-KR",start,end,resultp,lengthp);
+      return ret;
+    }
+    errno = EINVAL;
+    return -1;
+  }
+  /* Determine the length we need. */
+  {
+    size_t count = 0;
+    char tmpbuf[tmpbufsize];
+    const char* inptr = start;
+    size_t insize = end-start;
+    while (insize > 0) {
+      char* outptr = tmpbuf;
+      size_t outsize = tmpbufsize;
+      size_t res = iconv(cd,&inptr,&insize,&outptr,&outsize);
+      if (res == (size_t)(-1)) {
+        if (errno == EINVAL)
+          break;
+        else {
+          int saved_errno = errno;
+          iconv_close(cd);
+          errno = saved_errno;
+          return -1;
+        }
+      }
+      count += outptr-tmpbuf;
+    }
+    {
+      char* outptr = tmpbuf;
+      size_t outsize = tmpbufsize;
+      size_t res = iconv(cd,NULL,NULL,&outptr,&outsize);
+      if (res == (size_t)(-1)) {
+        int saved_errno = errno;
+        iconv_close(cd);
+        errno = saved_errno;
+        return -1;
+      }
+      count += outptr-tmpbuf;
+    }
+    length = count;
+  }
+  if (lengthp != NULL)
+    *lengthp = length;
+  if (resultp == NULL) {
+    iconv_close(cd);
+    return 0;
+  }
+  result = (*resultp == NULL ? malloc(length) : realloc(*resultp,length));
+  *resultp = result;
+  if (length == 0) {
+    iconv_close(cd);
+    return 0;
+  }
+  if (result == NULL) {
+    iconv_close(cd);
+    errno = ENOMEM;
+    return -1;
+  }
+  iconv(cd,NULL,NULL,NULL,NULL); /* return to the initial state */
+  /* Do the conversion for real. */
+  {
+    const char* inptr = start;
+    size_t insize = end-start;
+    char* outptr = result;
+    size_t outsize = length;
+    while (insize > 0) {
+      size_t res = iconv(cd,&inptr,&insize,&outptr,&outsize);
+      if (res == (size_t)(-1)) {
+        if (errno == EINVAL)
+          break;
+        else {
+          int saved_errno = errno;
+          iconv_close(cd);
+          errno = saved_errno;
+          return -1;
+        }
+      }
+    }
+    {
+      size_t res = iconv(cd,NULL,NULL,&outptr,&outsize);
+      if (res == (size_t)(-1)) {
+        int saved_errno = errno;
+        iconv_close(cd);
+        errno = saved_errno;
+        return -1;
+      }
+    }
+    if (outsize != 0) abort();
+  }
+  iconv_close(cd);
+  return 0;
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/gg/iconv_string.h	Thu Oct 18 20:56:59 2001 +0000
@@ -0,0 +1,47 @@
+/* Copyright (C) 1999-2001 Bruno Haible.
+   This file is not part of the GNU LIBICONV Library.
+   This file is put into the public domain.  */
+
+/*
+ * This C function converts an entire string from one encoding to another,
+ * using iconv. Easier to use than iconv() itself, and supports autodetect
+ * encodings on input.
+ *
+ *   int iconv_string (const char* tocode, const char* fromcode,
+ *                     const char* start, const char* end,
+ *                     char** resultp, size_t* lengthp)
+ *
+ * Converts a memory region given in encoding FROMCODE to a new memory
+ * region in encoding TOCODE. FROMCODE and TOCODE are as for iconv_open(3),
+ * except that FROMCODE may be one of the values
+ *    "autodetect_utf8"          supports ISO-8859-1 and UTF-8
+ *    "autodetect_jp"            supports EUC-JP, ISO-2022-JP-2 and SHIFT_JIS
+ *    "autodetect_kr"            supports EUC-KR and ISO-2022-KR
+ * The input is in the memory region between start (inclusive) and end
+ * (exclusive). If resultp is not NULL, the output string is stored in
+ * *resultp; malloc/realloc is used to allocate the result.
+ *
+ * This function does not treat zero characters specially.
+ *
+ * Return value: 0 if successful, otherwise -1 and errno set. Particular
+ * errno values: EILSEQ and ENOMEM.
+ *
+ * Example:
+ *   const char* s = ...;
+ *   char* result = NULL;
+ *   if (iconv_string("UCS-4-INTERNAL", "autodetect_utf8",
+ *                    s, s+strlen(s)+1, &result, NULL) < 0)
+ *     perror("iconv_string");
+ *
+ */
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int iconv_string (const char* tocode, const char* fromcode, const char* start, const char* end, char** resultp, size_t* lengthp);
+
+#ifdef __cplusplus
+}
+#endif
--- a/src/protocols/msn/msn.c	Thu Oct 18 19:55:19 2001 +0000
+++ b/src/protocols/msn/msn.c	Thu Oct 18 20:56:59 2001 +0000
@@ -20,6 +20,8 @@
 			"User-Agent: Gaim/" VERSION "\r\n" \
 			"X-MMS-IM-Format: FN=MS%20Sans%20Serif; EF=; CO=0; PF=0\r\n\r\n"
 
+#define HOTMAIL_URL "http://www.hotmail.com/cgi-bin/folders"
+
 #define MSN_ONLINE  1
 #define MSN_BUSY    2
 #define MSN_IDLE    3
@@ -269,20 +271,20 @@
 		char *x = strstr(data, "Inbox-Unread:");
 		if (!x) return;
 		x += strlen("Inbox-Unread: ");
-		connection_has_mail(gc, atoi(x), NULL, NULL);
+		connection_has_mail(gc, atoi(x), NULL, NULL, HOTMAIL_URL);
 	} else if (strstr(data, "Content-Type: text/x-msmsgsemailnotification;")) {
 		char *from = strstr(data, "From:");
 		char *subject = strstr(data, "Subject:");
 		char *x;
 		if (!from || !subject) {
-			connection_has_mail(gc, 1, NULL, NULL);
+			connection_has_mail(gc, 1, NULL, NULL, HOTMAIL_URL);
 			return;
 		}
 		from += strlen("From: ");
 		x = strstr(from, "\r\n"); *x = 0;
 		subject += strlen("Subject: ");
 		x = strstr(subject, "\r\n"); *x = 0;
-		connection_has_mail(gc, -1, from, subject);
+		connection_has_mail(gc, -1, from, subject, HOTMAIL_URL);
 	}
 }
 
@@ -831,6 +833,7 @@
 		serv_got_update(gc, user, 1, 0, 0, 0, status, 0);
 	} else if (!g_strncasecmp(buf, "OUT", 3)) {
 	} else if (!g_strncasecmp(buf, "PRP", 3)) {
+	} else if (!g_strncasecmp(buf, "QNG", 3)) {
 	} else if (!g_strncasecmp(buf, "QRY", 3)) {
 	} else if (!g_strncasecmp(buf, "REA", 3)) {
 		char *friend, *tmp = buf;
@@ -1438,7 +1441,7 @@
 static void msn_do_action(struct gaim_connection *gc, char *act)
 {
 	if (!strcmp(act, "Set Friendly Name")) {
-		do_prompt_dialog("Set Friendly Name:", gc, msn_act_id, NULL);
+		do_prompt_dialog("Set Friendly Name:", gc->displayname, gc, msn_act_id, NULL);
 	}
 }
 
--- a/src/protocols/yahoo/yay.c	Thu Oct 18 19:55:19 2001 +0000
+++ b/src/protocols/yahoo/yay.c	Thu Oct 18 20:56:59 2001 +0000
@@ -199,7 +199,7 @@
 	count = va_arg(ap, int);
 	va_end(ap);
 
-	connection_has_mail(gc, count, NULL, NULL);
+	connection_has_mail(gc, count, NULL, NULL, "http://mail.yahoo.com/");
 
 	return 1;
 }
@@ -644,7 +644,7 @@
 
 static void yahoo_do_action(struct gaim_connection *gc, char *act) {
 	if (!strcmp(act, "Activate ID")) {
-		do_prompt_dialog("Activate which ID:", gc, yahoo_act_id, NULL);
+		do_prompt_dialog("Activate which ID:", gc->displayname, gc, yahoo_act_id, NULL);
 	}
 }
 
--- a/src/prpl.c	Thu Oct 18 19:55:19 2001 +0000
+++ b/src/prpl.c	Thu Oct 18 20:56:59 2001 +0000
@@ -27,6 +27,7 @@
 
 #include "pixmaps/ok.xpm"
 #include "pixmaps/cancel.xpm"
+#include "pixmaps/tb_forward.xpm"
 
 GSList *protocols = NULL;
 
@@ -176,7 +177,7 @@
 	gtk_widget_destroy(p->window);
 }
 
-void do_prompt_dialog(const char *text, void *data, void *doit, void *dont)
+void do_prompt_dialog(const char *text, const char *def, void *data, void *doit, void *dont)
 {
 	GtkWidget *window;
 	GtkWidget *vbox;
@@ -212,6 +213,8 @@
 
 	entry = gtk_entry_new();
 	gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);
+	if (def)
+		gtk_entry_set_text(GTK_ENTRY(entry), text);
 	gtk_signal_connect(GTK_OBJECT(entry), "activate", GTK_SIGNAL_FUNC(act_prompt), p);
 	p->entry = entry;
 
@@ -346,6 +349,7 @@
 	struct gaim_connection *gc;
 	GtkWidget *email_win;
 	GtkWidget *email_label;
+	char *url;
 };
 GSList *mailnots = NULL;
 
@@ -368,11 +372,18 @@
 	}
 	debug_printf("removing mail notification\n");
 	mailnots = g_slist_remove(mailnots, mn);
+	if (mn->url)
+		g_free(mn->url);
 	g_free(mn);
 }
 
-void connection_has_mail(struct gaim_connection *gc, int count, const char *from, const char *subject)
+void connection_has_mail(struct gaim_connection *gc, int count, const char *from, const char *subject, const char *url)
 {
+	GtkWidget *hbox;
+	GtkWidget *vbox;
+	GtkWidget *urlbut;
+	GtkWidget *close;
+
 	struct mail_notify *mn;
 	char buf[2048];
 
@@ -387,65 +398,55 @@
 
 	if (count < 0 && from && subject) {
 		g_snprintf(buf, sizeof buf, "%s has mail from %s: %s", gc->username, from, subject);
-		if (!mn->email_win) {
-			GtkWidget *close;
-
-			mn->email_win = gtk_dialog_new();
-			gtk_window_set_policy(GTK_WINDOW(mn->email_win), 0, 0, 1);
-			gtk_container_set_border_width(GTK_CONTAINER(mn->email_win), 5);
-			gtk_window_set_title(GTK_WINDOW(mn->email_win), "New Mail");
-			gtk_signal_connect(GTK_OBJECT(mn->email_win), "destroy",
-					   GTK_SIGNAL_FUNC(des_email_win), mn);
-			gtk_widget_realize(mn->email_win);
-			aol_icon(mn->email_win->window);
-
-			mn->email_label = gtk_label_new(buf);
-			gtk_box_pack_start(GTK_BOX(GTK_DIALOG(mn->email_win)->vbox),
-					   mn->email_label, 0, 0, 5);
-			gtk_widget_show(mn->email_label);
-
-			close = picture_button(mn->email_win, _("Close"), cancel_xpm);
-			gtk_window_set_focus(GTK_WINDOW(mn->email_win), close);
-			gtk_box_pack_start(GTK_BOX(GTK_DIALOG(mn->email_win)->action_area),
-					   close, 0, 0, 5);
-			gtk_signal_connect(GTK_OBJECT(close), "clicked",                      
-					   GTK_SIGNAL_FUNC(des_email_win), mn);
-
-			gtk_widget_show(mn->email_win);
-		}
-		gtk_label_set_text(GTK_LABEL(mn->email_label), buf);
 	} else if (count) {
 		g_snprintf(buf, sizeof buf, "%s has %d new message%s.",
 			   gc->username, count, count == 1 ? "" : "s");
-		if (!mn->email_win) {
-			GtkWidget *close;
+	} else if (mn->email_win) {
+		gtk_widget_destroy(mn->email_win);
+		return;
+	}
 
-			mn->email_win = gtk_dialog_new();
-			gtk_window_set_policy(GTK_WINDOW(mn->email_win), 0, 0, 1);
-			gtk_container_set_border_width(GTK_CONTAINER(mn->email_win), 5);
-			gtk_window_set_title(GTK_WINDOW(mn->email_win), "New Mail");
-			gtk_signal_connect(GTK_OBJECT(mn->email_win), "destroy",
-					   GTK_SIGNAL_FUNC(des_email_win), mn);
-			gtk_widget_realize(mn->email_win);
-			aol_icon(mn->email_win->window);
+	if (mn->email_win) {
+		gtk_label_set_text(GTK_LABEL(mn->email_label), buf);
+		return;
+	}
+
+
+	mn->email_win = gtk_window_new(GTK_WINDOW_DIALOG);
+	gtk_window_set_wmclass(GTK_WINDOW(mn->email_win), "mail", "Gaim");
+	gtk_window_set_policy(GTK_WINDOW(mn->email_win), FALSE, TRUE, TRUE);
+	gtk_window_set_title(GTK_WINDOW(mn->email_win), _("Gaim - New Mail"));
+	gtk_signal_connect(GTK_OBJECT(mn->email_win), "destroy", GTK_SIGNAL_FUNC(des_email_win), mn);
+	gtk_widget_realize(mn->email_win);
+	aol_icon(mn->email_win->window);
 
-			mn->email_label = gtk_label_new(buf);
-			gtk_box_pack_start(GTK_BOX(GTK_DIALOG(mn->email_win)->vbox),
-					   mn->email_label, 0, 0, 5);
-			gtk_widget_show(mn->email_label);
+	vbox = gtk_vbox_new(FALSE, 5);
+	gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
+	gtk_container_add(GTK_CONTAINER(mn->email_win), vbox);
+	gtk_widget_show(vbox);
+
+	mn->email_label = gtk_label_new(buf);
+	gtk_label_set_text(GTK_LABEL(mn->email_label), buf);
+	gtk_box_pack_start(GTK_BOX(vbox), mn->email_label, 0, 0, 5);
+	gtk_widget_show(mn->email_label);
+
+	hbox = gtk_hbox_new(FALSE, 5);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+	gtk_widget_show(hbox);
 
-			close = picture_button(mn->email_win, _("Close"), cancel_xpm);
-			gtk_window_set_focus(GTK_WINDOW(mn->email_win), close);
-			gtk_box_pack_start(GTK_BOX(GTK_DIALOG(mn->email_win)->action_area),
-					   close, 0, 0, 5);
-			gtk_signal_connect(GTK_OBJECT(close), "clicked",                      
-					   GTK_SIGNAL_FUNC(des_email_win), mn);
+	close = picture_button(mn->email_win, _("Close"), cancel_xpm);
+	gtk_window_set_focus(GTK_WINDOW(mn->email_win), close);
+	gtk_box_pack_end(GTK_BOX(hbox), close, 0, 0, 5);
+	gtk_signal_connect(GTK_OBJECT(close), "clicked", GTK_SIGNAL_FUNC(des_email_win), mn);
 
-			gtk_widget_show(mn->email_win);
-		}
-		gtk_label_set_text(GTK_LABEL(mn->email_label), buf);
-	} else if (mn->email_win)
-		gtk_widget_destroy(mn->email_win);
+	if (url) {
+		mn->url = g_strdup(url);
+		urlbut = picture_button(mn->email_win, _("Open Mail"), tb_forward_xpm);
+		gtk_box_pack_end(GTK_BOX(hbox), urlbut, 0, 0, 5);
+		gtk_signal_connect(GTK_OBJECT(urlbut), "clicked", GTK_SIGNAL_FUNC(open_url_nw), mn->url);
+	}
+
+	gtk_widget_show(mn->email_win);
 }
 
 struct icon_data {
--- a/src/prpl.h	Thu Oct 18 19:55:19 2001 +0000
+++ b/src/prpl.h	Thu Oct 18 20:56:59 2001 +0000
@@ -161,9 +161,9 @@
 extern void do_proto_menu();
 
 extern void do_ask_dialog(const char *, void *, void *, void *);
-extern void do_prompt_dialog(const char *, void *, void *, void *);
+extern void do_prompt_dialog(const char *, const char *, void *, void *, void *);
 
-extern void connection_has_mail(struct gaim_connection *, int, const char *, const char *);
+extern void connection_has_mail(struct gaim_connection *, int, const char *, const char *, const char *);
 
 extern void set_icon_data(struct gaim_connection *, char *, void *, int);
 extern void *get_icon_data(struct gaim_connection *, char *, int *);