about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ngircd/Makefile.am4
-rw-r--r--src/ngircd/conn-encoding.c192
-rw-r--r--src/ngircd/conn-encoding.h30
-rw-r--r--src/ngircd/conn.c19
-rw-r--r--src/ngircd/conn.h8
-rw-r--r--src/ngircd/irc-encoding.c68
-rw-r--r--src/ngircd/irc-encoding.h24
-rw-r--r--src/ngircd/irc.c15
-rw-r--r--src/ngircd/messages.h13
-rw-r--r--src/ngircd/parse.c4
10 files changed, 371 insertions, 6 deletions
diff --git a/src/ngircd/Makefile.am b/src/ngircd/Makefile.am
index 3a411a96..c1fd4240 100644
--- a/src/ngircd/Makefile.am
+++ b/src/ngircd/Makefile.am
@@ -27,6 +27,7 @@ ngircd_SOURCES = \
 	client-cap.c \
 	conf.c \
 	conn.c \
+	conn-encoding.c \
 	conn-func.c \
 	conn-ssl.c \
 	conn-zip.c \
@@ -35,6 +36,7 @@ ngircd_SOURCES = \
 	irc.c \
 	irc-cap.c \
 	irc-channel.c \
+	irc-encoding.c \
 	irc-info.c \
 	irc-login.c \
 	irc-mode.c \
@@ -68,6 +70,7 @@ noinst_HEADERS = \
 	conf.h \
 	conf-ssl.h \
 	conn.h \
+	conn-encoding.h \
 	conn-func.h \
 	conn-ssl.h \
 	conn-zip.h \
@@ -77,6 +80,7 @@ noinst_HEADERS = \
 	irc.h \
 	irc-cap.h \
 	irc-channel.h \
+	irc-encoding.h \
 	irc-info.h \
 	irc-login.h \
 	irc-mode.h \
diff --git a/src/ngircd/conn-encoding.c b/src/ngircd/conn-encoding.c
new file mode 100644
index 00000000..71ab5884
--- /dev/null
+++ b/src/ngircd/conn-encoding.c
@@ -0,0 +1,192 @@
+/*
+ * ngIRCd -- The Next Generation IRC Daemon
+ * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * Please read the file COPYING, README and AUTHORS for more information.
+ */
+
+#define __conn_encoding_c__
+
+#define CONN_MODULE
+
+#include "portab.h"
+
+/**
+ * @file
+ * Functions to deal with character encodings and conversions
+ */
+
+#include "imp.h"
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "defines.h"
+#include "conn.h"
+#include "log.h"
+
+#include "exp.h"
+#include "conn-encoding.h"
+
+#ifdef ICONV
+
+char Encoding_Buffer[COMMAND_LEN];
+
+char *Convert_Message PARAMS((iconv_t Handle, char *Message));
+
+
+/**
+ * Set client character encoding on a connection.
+ *
+ * @param Conn Connection identifier.
+ * @param ClientEnc Client encoding (for example "ASCII", "MacRoman", ...).
+ * @return true on success, false otherwise.
+ */
+GLOBAL bool
+Conn_SetEncoding(CONN_ID Conn, const char *ClientEnc)
+{
+	char client_enc[25], server_enc[25];
+
+	assert(Conn > NONE);
+	assert(ClientEnc != NULL);
+
+	Conn_UnsetEncoding(Conn);
+
+	/* Is the client character set identical to server character set? */
+	if (strcasecmp(ClientEnc, "UTF-8") == 0)
+		return true;
+
+	snprintf(client_enc, sizeof(client_enc), "%s//TRANSLIT", ClientEnc);
+	snprintf(server_enc, sizeof(server_enc), "%s//TRANSLIT", "UTF-8");
+
+	My_Connections[Conn].iconv_from = iconv_open(server_enc, client_enc);
+	if (My_Connections[Conn].iconv_from == (iconv_t)(-1)) {
+		Conn_UnsetEncoding(Conn);
+		return false;
+	}
+	My_Connections[Conn].iconv_to = iconv_open(client_enc, server_enc);
+	if (My_Connections[Conn].iconv_to == (iconv_t)(-1)) {
+		Conn_UnsetEncoding(Conn);
+		return false;
+	}
+
+	LogDebug("Set client character set of connection \"%d\" to \"%s\".",
+		 Conn, client_enc);
+	return true;
+}
+
+/**
+ * Remove client character encoding conversion on a connection.
+ *
+ * @param Conn Connection identifier.
+ */
+GLOBAL void
+Conn_UnsetEncoding(CONN_ID Conn)
+{
+	assert(Conn > NONE);
+
+	if (My_Connections[Conn].iconv_from != (iconv_t)(-1))
+		iconv_close(My_Connections[Conn].iconv_from);
+	if (My_Connections[Conn].iconv_to != (iconv_t)(-1))
+		iconv_close(My_Connections[Conn].iconv_to);
+
+	My_Connections[Conn].iconv_from = (iconv_t)(-1);
+	My_Connections[Conn].iconv_to = (iconv_t)(-1);
+
+	LogDebug("Unset character conversion of connection %d.", Conn);
+}
+
+/**
+ * Convert the encoding of a given message.
+ *
+ * This function uses a static buffer for the result of the encoding
+ * conversion which is overwritten by subsequent calls to this function!
+ *
+ * @param Handle libiconv handle.
+ * @param Message The message to convert.
+ * @return Pointer to the result.
+ */
+char *
+Convert_Message(iconv_t Handle, char *Message)
+{
+	size_t in_left, out_left;
+	char *out = Encoding_Buffer;
+
+	assert (Handle != (iconv_t)(-1));
+	assert (Message != NULL);
+
+	in_left = strlen(Message);
+	out_left = sizeof(Encoding_Buffer) - 1;
+
+	if (iconv(Handle, &Message, &in_left, &out, &out_left) == (size_t)(-1)) {
+		/* An error occured! */
+		LogDebug("Error converting message encoding!");
+		strlcpy(Encoding_Buffer, Message, sizeof(Encoding_Buffer));
+		iconv(Handle, NULL, NULL, NULL, NULL);
+	} else
+		*out = '\0';
+
+	return Encoding_Buffer;
+}
+
+#endif
+
+/**
+ * Convert encoding of a message received from a connection.
+ *
+ * Note 1: If no conversion is required, this function returns the original
+ * pointer to the message.
+ *
+ * Note 2: This function uses Convert_Message(), so subsequent calls to this
+ * function will overwrite the earlier results.
+ *
+ * @param Conn Connection identifier.
+ * @param Message The message to convert.
+ * @return Pointer to the result.
+ * @see Convert_Message
+ */
+GLOBAL char *
+Conn_EncodingFrom(UNUSED CONN_ID Conn, char *Message)
+{
+	assert(Conn > NONE);
+	assert (Message != NULL);
+
+#ifdef ICONV
+	if (My_Connections[Conn].iconv_from != (iconv_t)(-1))
+		return Convert_Message(My_Connections[Conn].iconv_from, Message);
+#endif
+	return Message;
+}
+
+/**
+ * Convert encoding of a message for sending on a connection.
+ *
+ * Note 1: If no conversion is required, this function returns the original
+ * pointer to the message.
+ *
+ * Note 2: This function uses Convert_Message(), so subsequent calls to this
+ * function will overwrite the earlier results.
+ *
+ * @param Conn Connection identifier.
+ * @param Message The message to convert.
+ * @return Pointer to the result.
+ * @see Convert_Message
+ */
+GLOBAL char *
+Conn_EncodingTo(UNUSED CONN_ID Conn, char *Message)
+{
+	assert(Conn > NONE);
+	assert (Message != NULL);
+
+#ifdef ICONV
+	if (My_Connections[Conn].iconv_to != (iconv_t)(-1))
+		return Convert_Message(My_Connections[Conn].iconv_to, Message);
+#endif
+	return Message;
+}
+
+/* -eof- */
diff --git a/src/ngircd/conn-encoding.h b/src/ngircd/conn-encoding.h
new file mode 100644
index 00000000..7b50ed65
--- /dev/null
+++ b/src/ngircd/conn-encoding.h
@@ -0,0 +1,30 @@
+/*
+ * ngIRCd -- The Next Generation IRC Daemon
+ * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * Please read the file COPYING, README and AUTHORS for more information.
+ */
+
+#ifndef __conn_encoding_h__
+#define __conn_encoding_h__
+
+/**
+ * @file
+ * Functions to deal with character encodings and conversions (header)
+ */
+
+#ifdef ICONV
+
+GLOBAL bool Conn_SetEncoding PARAMS((CONN_ID Idx, const char *ClientEnc));
+GLOBAL void Conn_UnsetEncoding PARAMS((CONN_ID Idx));
+
+#endif /* ICONV */
+
+GLOBAL char* Conn_EncodingFrom PARAMS((CONN_ID Idx, char *Message));
+GLOBAL char* Conn_EncodingTo PARAMS((CONN_ID Idx, char *Message));
+
+#endif
diff --git a/src/ngircd/conn.c b/src/ngircd/conn.c
index f4511642..5d086857 100644
--- a/src/ngircd/conn.c
+++ b/src/ngircd/conn.c
@@ -63,6 +63,7 @@
 #include "client.h"
 #include "class.h"
 #include "conf.h"
+#include "conn-encoding.h"
 #include "conn-ssl.h"
 #include "conn-zip.h"
 #include "conn-func.h"
@@ -861,6 +862,9 @@ va_dcl
 #endif
 {
 	char buffer[COMMAND_LEN];
+#ifdef ICONV
+	char *ptr, *message;
+#endif
 	size_t len;
 	bool ok;
 	va_list ap;
@@ -901,6 +905,16 @@ va_dcl
 			CUT_TXTSUFFIX);
 	}
 
+#ifdef ICONV
+	ptr = strchr(buffer + 1, ':');
+	if (ptr) {
+		ptr++;
+		message = Conn_EncodingTo(Idx, ptr);
+		if (message != ptr)
+			strlcpy(ptr, message, sizeof(buffer) - (ptr - buffer));
+	}
+#endif
+
 #ifdef SNIFFER
 	if (NGIRCd_Sniffer)
 		Log(LOG_DEBUG, " -> connection %d: '%s'.", Idx, buffer);
@@ -2105,6 +2119,11 @@ Init_Conn_Struct(CONN_ID Idx)
 	My_Connections[Idx].lastdata = now;
 	My_Connections[Idx].lastprivmsg = now;
 	Proc_InitStruct(&My_Connections[Idx].proc_stat);
+
+#ifdef ICONV
+	My_Connections[Idx].iconv_from = (iconv_t)(-1);
+	My_Connections[Idx].iconv_to = (iconv_t)(-1);
+#endif
 } /* Init_Conn_Struct */
 
 
diff --git a/src/ngircd/conn.h b/src/ngircd/conn.h
index e42a2ae6..9236c58b 100644
--- a/src/ngircd/conn.h
+++ b/src/ngircd/conn.h
@@ -54,6 +54,10 @@ typedef int CONN_ID;
 #include "tool.h"
 #include "ng_ipaddr.h"
 
+#ifdef ICONV
+# include <iconv.h>
+#endif
+
 #ifdef ZLIB
 #include <zlib.h>
 typedef struct _ZipData
@@ -95,6 +99,10 @@ typedef struct _Connection
 #ifndef STRICT_RFC
 	long auth_ping;			/** PING response expected on login */
 #endif
+#ifdef ICONV
+	iconv_t iconv_from;		/** iconv: convert from client to server */
+	iconv_t iconv_to;		/** iconv: convert from server to client */
+#endif
 } CONNECTION;
 
 GLOBAL CONNECTION *My_Connections;
diff --git a/src/ngircd/irc-encoding.c b/src/ngircd/irc-encoding.c
new file mode 100644
index 00000000..b1d3a697
--- /dev/null
+++ b/src/ngircd/irc-encoding.c
@@ -0,0 +1,68 @@
+/*
+ * ngIRCd -- The Next Generation IRC Daemon
+ * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * Please read the file COPYING, README and AUTHORS for more information.
+ */
+
+#include "portab.h"
+
+/**
+ * @file
+ * IRC encoding commands
+ */
+
+#include "imp.h"
+#include <assert.h>
+#include <string.h>
+
+#include "conn-func.h"
+#include "channel.h"
+#include "conn-encoding.h"
+#include "irc-write.h"
+#include "messages.h"
+#include "parse.h"
+#include "tool.h"
+
+#include "exp.h"
+#include "irc-encoding.h"
+
+#ifdef ICONV
+
+/**
+ * Handler for the IRC+ "CHARCONV" command.
+ *
+ * @param Client The client from which this command has been received.
+ * @param Req Request structure with prefix and all parameters.
+ * @returns CONNECTED or DISCONNECTED.
+ */
+GLOBAL bool
+IRC_CHARCONV(CLIENT *Client, REQUEST *Req)
+{
+	char encoding[20];
+
+	assert (Client != NULL);
+	assert (Req != NULL);
+
+	if (Req->argc != 1)
+		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+					  Client_ID(Client), Req->command);
+
+	strlcpy(encoding, Req->argv[0], sizeof(encoding));
+	ngt_UpperStr(encoding);
+
+	if (!Conn_SetEncoding(Client_Conn(Client), encoding))
+		return IRC_WriteStrClient(Client, ERR_IP_CHARCONV_MSG,
+					  Client_ID(Client), encoding);
+
+	return IRC_WriteStrClient(Client, RPL_IP_CHARCONV_MSG,
+				  Client_ID(Client), encoding);
+} /* IRC_CHARCONV */
+
+#endif
+
+/* -eof- */
diff --git a/src/ngircd/irc-encoding.h b/src/ngircd/irc-encoding.h
new file mode 100644
index 00000000..4349c910
--- /dev/null
+++ b/src/ngircd/irc-encoding.h
@@ -0,0 +1,24 @@
+/*
+ * ngIRCd -- The Next Generation IRC Daemon
+ * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * Please read the file COPYING, README and AUTHORS for more information.
+ */
+
+#ifndef __irc_encoding_h__
+#define __irc_encoding_h__
+
+/**
+ * @file
+ * IRC encoding commands (header)
+ */
+
+GLOBAL bool IRC_CHARCONV PARAMS((CLIENT *Client, REQUEST *Req));
+
+#endif
+
+/* -eof- */
diff --git a/src/ngircd/irc.c b/src/ngircd/irc.c
index efc34d4b..ceb649ec 100644
--- a/src/ngircd/irc.c
+++ b/src/ngircd/irc.c
@@ -25,6 +25,7 @@
 #include "conn-func.h"
 #include "conf.h"
 #include "channel.h"
+#include "conn-encoding.h"
 #include "defines.h"
 #include "irc-write.h"
 #include "log.h"
@@ -359,6 +360,7 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
 	CHANNEL *chan;
 	char *currentTarget = Req->argv[0];
 	char *lastCurrentTarget = NULL;
+	char *message = NULL;
 
 	assert(Client != NULL);
 	assert(Req != NULL);
@@ -390,6 +392,13 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
 		return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
 					  Client_ID(Client), Req->prefix);
 
+#ifdef ICONV
+	if (Client_Conn(Client) > NONE)
+		message = Conn_EncodingFrom(Client_Conn(Client), Req->argv[1]);
+	else
+#endif
+		message = Req->argv[1];
+
 	/* handle msgtarget = msgto *("," msgto) */
 	currentTarget = strtok_r(currentTarget, ",", &lastCurrentTarget);
 	ngt_UpperStr(Req->command);
@@ -523,12 +532,12 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
 			}
 			if (!IRC_WriteStrClientPrefix(cl, from, "%s %s :%s",
 						      Req->command, Client_ID(cl),
-						      Req->argv[1]))
+						      message))
 				return DISCONNECTED;
 		} else if (ForceType != CLIENT_SERVICE
 			   && (chan = Channel_Search(currentTarget))) {
 			if (!Channel_Write(chan, from, Client, Req->command,
-					   SendErrors, Req->argv[1]))
+					   SendErrors, message))
 					return DISCONNECTED;
 		} else if (ForceType != CLIENT_SERVICE
 			/* $#: server/target mask, RFC 2812, sec. 3.3.1 */
@@ -536,7 +545,7 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
 			   && strchr(currentTarget, '.')) {
 			/* targetmask */
 			if (!Send_Message_Mask(from, Req->command, currentTarget,
-					       Req->argv[1], SendErrors))
+					       message, SendErrors))
 				return DISCONNECTED;
 		} else {
 			if (!SendErrors)
diff --git a/src/ngircd/messages.h b/src/ngircd/messages.h
index 4f3a397b..99d25828 100644
--- a/src/ngircd/messages.h
+++ b/src/ngircd/messages.h
@@ -21,7 +21,7 @@
 #define RPL_YOURHOST_MSG		"002 %s :Your host is %s, running version ngircd-%s (%s/%s/%s)"
 #define RPL_CREATED_MSG			"003 %s :This server has been started %s"
 #define RPL_MYINFO_MSG			"004 %s %s ngircd-%s %s %s"
-#define RPL_ISUPPORT1_MSG		"005 %s RFC2812 IRCD=ngIRCd CASEMAPPING=ascii PREFIX=(ov)@+ CHANTYPES=#&+ CHANMODES=beI,k,l,imMnOPRstz CHANLIMIT=#&+:%d :are supported on this server"
+#define RPL_ISUPPORT1_MSG		"005 %s RFC2812 IRCD=ngIRCd CHARSET=UTF-8 CASEMAPPING=ascii PREFIX=(ov)@+ CHANTYPES=#&+ CHANMODES=beI,k,l,imMnOPRstz CHANLIMIT=#&+:%d :are supported on this server"
 #define RPL_ISUPPORT2_MSG		"005 %s CHANNELLEN=%d NICKLEN=%d TOPICLEN=%d AWAYLEN=%d KICKLEN=%d MODES=%d MAXLIST=beI:%d EXCEPTS=e INVEX=I PENALTY :are supported on this server"
 
 #define RPL_TRACELINK_MSG		"200 %s Link %s-%s %s %s V%s %ld %d %d"
@@ -34,7 +34,6 @@
 #define RPL_UMODEIS_MSG			"221 %s +%s"
 #define RPL_SERVLIST_MSG		"234 %s %s %s %s %d %d :%s"
 #define RPL_SERVLISTEND_MSG		"235 %s %s %s :End of service listing"
-
 #define RPL_STATSUPTIME			"242 %s :Server Up %u days %u:%02u:%02u"
 #define RPL_LUSERCLIENT_MSG		"251 %s :There are %ld users and %ld services on %ld servers"
 #define RPL_LUSEROP_MSG			"252 %s %lu :operator(s) online"
@@ -149,9 +148,17 @@
 #define ERR_USERSDONTMATCH_MSG		"502 %s :Can't set/get mode for other users"
 
 #ifdef ZLIB
-#define RPL_STATSLINKINFOZIP_MSG	"211 %s %s %d %ld %ld/%ld %ld %ld/%ld :%ld"
+# define RPL_STATSLINKINFOZIP_MSG	"211 %s %s %d %ld %ld/%ld %ld %ld/%ld :%ld"
 #endif
 
+#ifdef IRCPLUS
+
+# define RPL_IP_CHARCONV_MSG		"801 %s %s :Client encoding set"
+
+# define ERR_IP_CHARCONV_MSG		"851 %s :Can't initialize client encoding"
+
+#endif /* IRCPLUS */
+
 #endif
 
 /* -eof- */
diff --git a/src/ngircd/parse.c b/src/ngircd/parse.c
index e9c5d53a..f3b04d0c 100644
--- a/src/ngircd/parse.c
+++ b/src/ngircd/parse.c
@@ -38,6 +38,7 @@
 #include "irc.h"
 #include "irc-cap.h"
 #include "irc-channel.h"
+#include "irc-encoding.h"
 #include "irc-info.h"
 #include "irc-login.h"
 #include "irc-mode.h"
@@ -114,6 +115,9 @@ static COMMAND My_Commands[] =
 
 #ifdef IRCPLUS
 	{ "CHANINFO", IRC_CHANINFO, CLIENT_SERVER, 0, 0, 0 },
+# ifdef ICONV
+	{ "CHARCONV", IRC_CHARCONV, CLIENT_USER, 0, 0, 0 },
+# endif
 #endif
 
 #ifndef STRICT_RFC