From 222ecbffbb5f0c21e64002c95fe9447b4f6b6320 Mon Sep 17 00:00:00 2001 From: Alexander Barton Date: Mon, 17 Sep 2012 00:36:10 +0200 Subject: Implement new IRC+ "CHARCONV" command See bug 109 and doc/Protocol.txt for details and documentation. --- src/ngircd/Makefile.am | 4 + src/ngircd/conn-encoding.c | 192 +++++++++++++++++++++++++++++++++++++++++++++ src/ngircd/conn-encoding.h | 30 +++++++ src/ngircd/conn.c | 19 +++++ src/ngircd/conn.h | 8 ++ src/ngircd/irc-encoding.c | 68 ++++++++++++++++ src/ngircd/irc-encoding.h | 24 ++++++ src/ngircd/irc.c | 15 +++- src/ngircd/messages.h | 13 ++- src/ngircd/parse.c | 4 + 10 files changed, 371 insertions(+), 6 deletions(-) create mode 100644 src/ngircd/conn-encoding.c create mode 100644 src/ngircd/conn-encoding.h create mode 100644 src/ngircd/irc-encoding.c create mode 100644 src/ngircd/irc-encoding.h (limited to 'src') 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 +#include +#include + +#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 +#endif + #ifdef ZLIB #include 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 +#include + +#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 -- cgit 1.4.1 From e9d0b2f0393d70a07dda4d347604faf2a2058ac9 Mon Sep 17 00:00:00 2001 From: Alexander Barton Date: Mon, 17 Sep 2012 01:00:05 +0200 Subject: Add "CHARCONV" to "feature string" when enabled --- src/ngircd/ngircd.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src') diff --git a/src/ngircd/ngircd.c b/src/ngircd/ngircd.c index 585e2ac0..bbfc6ce1 100644 --- a/src/ngircd/ngircd.c +++ b/src/ngircd/ngircd.c @@ -351,6 +351,13 @@ Fill_Version(void) { NGIRCd_VersionAddition[0] = '\0'; +#ifdef ICONV + if (NGIRCd_VersionAddition[0]) + strlcat(NGIRCd_VersionAddition, "+", + sizeof NGIRCd_VersionAddition); + strlcat(NGIRCd_VersionAddition, "CHARCONV", + sizeof NGIRCd_VersionAddition); +#endif #ifdef DEBUG if (NGIRCd_VersionAddition[0]) strlcat(NGIRCd_VersionAddition, "+", -- cgit 1.4.1