Index: Makefile.am
===================================================================
--- Makefile.am	(revision b373d4433450dc226e49ae0d4eda3542c4a42904)
+++ Makefile.am	(revision b373d4433450dc226e49ae0d4eda3542c4a42904)
@@ -42,4 +42,6 @@
 GEN_H = owl_prototypes.h
 
+TYPEMAP = typemap
+
 BUILT_SOURCES = $(GEN_C) $(GEN_H)
 
@@ -53,6 +55,6 @@
 proto: owl_prototypes.h
 
-perlglue.c: perlglue.xs
-	$(AM_V_GEN)perl $(XSUBPPDIR)/xsubpp $(XSUBPPFLAGS) -prototypes perlglue.xs > perlglue.c
+perlglue.c: perlglue.xs $(TYPEMAP)
+	$(AM_V_GEN)perl $(XSUBPPDIR)/xsubpp $(XSUBPPFLAGS) -prototypes perlglue.xs -typemap $(TYPEMAP) > perlglue.c
 
 varstubs.c: stubgen.pl variable.c
Index: aim.c
===================================================================
--- aim.c	(revision c79a04782cf599c7fdc305dbe33acb4b8842c771)
+++ aim.c	(revision 1fdca1ba004d56eb347146421fd728b822931f1d)
@@ -249,22 +249,62 @@
 }
 
+/*
+ * I got these constants by skimming libfaim/im.c
+ *
+ * "UNICODE" actually means "UCS-2BE".
+ */
+#define AIM_CHARSET_ISO_8859_1         0x0003
+#define AIM_CHARSET_UNICODE            0x0002
+
+int owl_aim_do_send(const char *to, const char *msg, int flags) /* noproto */
+{
+  int ret;
+  char *encoded;
+  struct aim_sendimext_args args;
+    gsize len;
+
+  encoded = g_convert(msg, -1, "ISO-8859-1", "UTF-8", NULL, &len, NULL);
+  if (encoded) {
+    owl_function_debugmsg("Encoded outgoing AIM as ISO-8859-1");
+    args.charset = AIM_CHARSET_ISO_8859_1;
+    args.charsubset = 0;
+    args.flags = AIM_IMFLAGS_ISO_8859_1;
+  } else {
+    owl_function_debugmsg("Encoding outgoing IM as UCS-2BE");
+    encoded = g_convert(msg, -1, "UCS-2BE", "UTF-8", NULL, &len, NULL);
+    if (!encoded) {
+      /*
+       * TODO: Strip or HTML-encode characters, or figure out how to
+       * send in a differen charset.
+       */
+      owl_function_error("Unable to encode outgoing AIM message in UCS-2");
+      return 1;
+    }
+
+    args.charset = AIM_CHARSET_UNICODE;
+    args.charsubset = 0;
+    args.flags = AIM_IMFLAGS_UNICODE;
+  }
+
+  args.destsn = to;
+  args.msg = encoded;
+  args.msglen = len;
+  args.flags |= flags;
+
+  ret=aim_im_sendch1_ext(owl_global_get_aimsess(&g), &args);
+
+  owl_free(encoded);
+
+  return(ret);
+}
+
 int owl_aim_send_im(const char *to, const char *msg)
 {
-  int ret;
-
-  ret=aim_im_sendch1(owl_global_get_aimsess(&g), to, 0, msg);
-    
-  /* I don't know how to check for an error yet */
-  return(ret);
+  return owl_aim_do_send(to, msg, 0);
 }
 
 int owl_aim_send_awaymsg(const char *to, const char *msg)
 {
-  int ret;
-
-  ret=aim_im_sendch1(owl_global_get_aimsess(&g), to, AIM_IMFLAGS_AWAY, msg);
-
-  /* I don't know how to check for an error yet */
-  return(ret);
+  return owl_aim_do_send(to, msg, AIM_IMFLAGS_AWAY);
 }
 
@@ -1149,80 +1189,20 @@
   owl_message *m;
   char *stripmsg, *nz_screenname, *wrapmsg;
-  char realmsg[8192+1] = "";
-  /* int clienttype = AIM_CLIENTTYPE_UNKNOWN; */
-
-  /* clienttype = aim_fingerprintclient(args->features, args->featureslen); */
-
-  /*
-  printf("icbm: sn = \"%s\"\n", userinfo->sn);
-  printf("icbm: probable client type: %d\n", clienttype);
-  printf("icbm: warnlevel = %f\n", aim_userinfo_warnlevel(userinfo));
-  printf("icbm: flags = 0x%04x = ", userinfo->flags);
-  printuserflags(userinfo->flags);
-  printf("\n");
-  */
-
-  /*
-  printf("icbm: membersince = %lu\n", userinfo->membersince);
-  printf("icbm: onlinesince = %lu\n", userinfo->onlinesince);
-  printf("icbm: idletime = 0x%04x\n", userinfo->idletime);
-  printf("icbm: capabilities = %s = 0x%08lx\n", (userinfo->present & AIM_USERINFO_PRESENT_CAPABILITIES) ? "present" : "not present", userinfo->capabilities);
-  */
-
-  /*
-  printf("icbm: icbmflags = ");
-  if (args->icbmflags & AIM_IMFLAGS_AWAY) printf("away ");
-  if (args->icbmflags & AIM_IMFLAGS_ACK) printf("ackrequest ");
-  if (args->icbmflags & AIM_IMFLAGS_OFFLINE) printf("offline ");
-  if (args->icbmflags & AIM_IMFLAGS_BUDDYREQ) printf("buddyreq ");
-  if (args->icbmflags & AIM_IMFLAGS_HASICON) printf("hasicon ");
-  printf("\n");
-  */
-
-  /*
-  if (args->icbmflags & AIM_IMFLAGS_CUSTOMCHARSET) {
-  printf("icbm: encoding flags = {%04x, %04x}\n", args->charset, args->charsubset);
-  }
-  */
-  
-  /*
-   * Quickly convert it to eight bit format, replacing non-ASCII UNICODE 
-   * characters with their equivelent HTML entity.
-   */
-  if (args->icbmflags & AIM_IMFLAGS_UNICODE) {
-    int i;
-    
-    for (i=0; i<args->msglen; i+=2) {
-      fu16_t uni;
-
-      uni = ((args->msg[i] & 0xff) << 8) | (args->msg[i+1] & 0xff);
-      if ((uni < 128) || ((uni >= 160) && (uni <= 255))) { /* ISO 8859-1 */
-	snprintf(realmsg+strlen(realmsg), sizeof(realmsg)-strlen(realmsg), "%c", uni);
-      } else { /* something else, do UNICODE entity */
-	snprintf(realmsg+strlen(realmsg), sizeof(realmsg)-strlen(realmsg), "&#%04x;", uni);
-      }
-    }
+  char *realmsg = NULL;
+
+  if (!args->msg) {
+    realmsg = owl_strdup("");
+  } else if (args->icbmflags & AIM_IMFLAGS_UNICODE) {
+    realmsg = g_convert(args->msg, args->msglen, "UTF-8", "UCS-2BE",
+                        NULL, NULL, NULL);
+  } else if(args->icbmflags & AIM_IMFLAGS_ISO_8859_1) {
+    realmsg = g_convert(args->msg, args->msglen, "UTF-8", "ISO-8859-1",
+                        NULL, NULL, NULL);
   } else {
-    /*
-     * For non-UNICODE encodings (ASCII and ISO 8859-1), there is 
-     * no need to do anything special here.  Most 
-     * terminals/whatever will be able to display such characters 
-     * unmodified.
-     *
-     * Beware that PC-ASCII 128 through 159 are _not_ actually 
-     * defined in ASCII or ISO 8859-1, and you should send them as 
-     * UNICODE.  WinAIM will send these characters in a UNICODE 
-     * message, so you need to do so as well.
-     *
-     * You may not think it necessary to handle UNICODE messages.  
-     * You're probably wrong.  For one thing, Microsoft "Smart
-     * Quotes" will be sent by WinAIM as UNICODE (not HTML UNICODE,
-     * but real UNICODE). If you don't parse UNICODE at all, your 
-     * users will get a blank message instead of the message 
-     * containing Smart Quotes.
-     *
-     */
-    if (args->msg && args->msglen)
-      strncpy(realmsg, args->msg, sizeof(realmsg));
+    realmsg = owl_strdup(args->msg);
+  }
+
+  if (!realmsg) {
+    realmsg = owl_strdup("[Error decoding incoming IM]");
   }
 
@@ -1269,19 +1249,6 @@
   }
 
-  /*
-  if (realmsg) {
-    int i = 0;
-    while (realmsg[i] == '<') {
-      if (realmsg[i] == '<') {
-	while (realmsg[i] != '>')
-	  i++;
-	i++;
-      }
-    }
-    tmpstr = realmsg+i;
-    faimtest_handlecmd(sess, conn, userinfo, tmpstr);
-  }
-  */
-  
+  owl_free(realmsg);
+
   return(1);
 }
Index: perl/modules/IRC/lib/BarnOwl/Module/IRC.pm
===================================================================
--- perl/modules/IRC/lib/BarnOwl/Module/IRC.pm	(revision 744769ed334f09a710f5230e74a6dfe5cf216b86)
+++ perl/modules/IRC/lib/BarnOwl/Module/IRC.pm	(revision 0c4a190dd56c6e2fcdf3a8fd077cade98620946d)
@@ -399,8 +399,8 @@
     $body =~ tr/\n\r/  /;
     if ($body =~ /^\/me (.*)/) {
-        $conn->conn->me($to, $1);
+        $conn->conn->me($to, Encode::encode('utf-8', $1));
         $body = '* '.$conn->nick.' '.$1;
     } else {
-        $conn->conn->privmsg($to, $body);
+        $conn->conn->privmsg($to, Encode::encode('utf-8', $body));
     }
     my $msg = BarnOwl::Message->new(
Index: perlglue.xs
===================================================================
--- perlglue.xs	(revision 5791bf749e1cf8fcf653cee9840ad05eedd577ad)
+++ perlglue.xs	(revision 5791bf749e1cf8fcf653cee9840ad05eedd577ad)
@@ -9,4 +9,6 @@
 
 #define SV_IS_CODEREF(sv) (SvROK((sv)) && SvTYPE(SvRV((sv))) == SVt_PVCV)
+
+typedef char utf8;
 
 	/*************************************************************
@@ -29,5 +31,5 @@
 MODULE = BarnOwl		PACKAGE = BarnOwl
 
-const char *
+const utf8 *
 command(cmd, ...)
 	const char *cmd
@@ -77,5 +79,5 @@
 		RETVAL
 
-const char *
+const utf8 *
 zephyr_getrealm()
 	CODE:
@@ -84,5 +86,5 @@
 		RETVAL
 
-const char *
+const utf8 *
 zephyr_getsender()
 	CODE:
@@ -100,5 +102,5 @@
 		i = owl_zwrite_create_and_send_from_line(cmd, msg);
 
-const char *
+const utf8 *
 ztext_stylestrip(ztext)
 	const char *ztext
@@ -113,5 +115,5 @@
 		if (rv) owl_free(rv);
 
-const char *
+const utf8 *
 zephyr_smartstrip_user(in)
 	const char *in
@@ -128,5 +130,5 @@
 		owl_free(rv);
 
-const char *
+const utf8 *
 zephyr_getsubs()
 	PREINIT:
@@ -314,5 +316,5 @@
 	}
 
-const char *
+const utf8 *
 wordwrap(in, cols)
 	const char *in
@@ -326,5 +328,6 @@
 		RETVAL
 	CLEANUP:
-		if (rv) owl_free(rv);
+		if (rv)
+			owl_free(rv);
 
 void
@@ -429,5 +432,5 @@
 		RETVAL
 
-const char *
+const utf8 *
 skiptokens(str, n)
 	const char *str;
@@ -569,5 +572,5 @@
 		RETVAL
 
-const char *
+const utf8 *
 get_region()
 	PREINIT:
Index: t/unicode.t
===================================================================
--- t/unicode.t	(revision 09ff1eb7370de312e8c2eff74221cfb974dc1df7)
+++ t/unicode.t	(revision 09ff1eb7370de312e8c2eff74221cfb974dc1df7)
@@ -0,0 +1,16 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use utf8;
+use Encode;
+
+BEGIN { bootstrap BarnOwl; };
+
+use Test::More qw(no_plan);
+
+my $unicode = "“hello”";
+
+ok(Encode::is_utf8($unicode));
+ok(Encode::is_utf8(BarnOwl::wordwrap($unicode, 100)));
+
+is(BarnOwl::wordwrap($unicode, 100), $unicode);
Index: typemap
===================================================================
--- typemap	(revision 39e81f799239d76e4d42870e6aabcb35d6e2c059)
+++ typemap	(revision 39e81f799239d76e4d42870e6aabcb35d6e2c059)
@@ -0,0 +1,8 @@
+TYPEMAP
+utf8 *         T_PV_UTF8
+const utf8 *   T_PV_UTF8
+
+OUTPUT
+T_PV_UTF8
+        sv_setpv((SV*)$arg, $var);
+        SvUTF8_on((SV*)$arg);
