1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001-2014 Alexander Barton (alex@barton.de) and Contributors.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * Please read the file COPYING, README and AUTHORS for more information.
10  */
11 
12 #define __conn_encoding_c__
13 
14 #define CONN_MODULE
15 
16 #include "portab.h"
17 
18 /**
19  * @file
20  * Functions to deal with character encodings and conversions
21  */
22 
23 #include <assert.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <strings.h>
27 
28 #include "conn.h"
29 
30 #ifdef ICONV
31 
32 #include "log.h"
33 #include "conn-encoding.h"
34 
35 char Encoding_Buffer[COMMAND_LEN];
36 char *Convert_Message PARAMS((iconv_t Handle, char *Message));
37 
38 /**
39  * Set client character encoding on a connection.
40  *
41  * @param Conn Connection identifier.
42  * @param ClientEnc Client encoding (for example "ASCII", "MacRoman", ...).
43  * @return true on success, false otherwise.
44  */
45 GLOBAL bool
Conn_SetEncoding(CONN_ID Conn,const char * ClientEnc)46 Conn_SetEncoding(CONN_ID Conn, const char *ClientEnc)
47 {
48 	char client_enc[25], server_enc[25];
49 
50 	assert(Conn > NONE);
51 	assert(ClientEnc != NULL);
52 
53 	Conn_UnsetEncoding(Conn);
54 
55 	/* Is the client character set identical to server character set? */
56 	if (strcasecmp(ClientEnc, "UTF-8") == 0)
57 		return true;
58 
59 	snprintf(client_enc, sizeof(client_enc), "%s//TRANSLIT", ClientEnc);
60 	snprintf(server_enc, sizeof(server_enc), "%s//TRANSLIT", "UTF-8");
61 
62 	My_Connections[Conn].iconv_from = iconv_open(server_enc, client_enc);
63 	if (My_Connections[Conn].iconv_from == (iconv_t)(-1)) {
64 		Conn_UnsetEncoding(Conn);
65 		return false;
66 	}
67 	My_Connections[Conn].iconv_to = iconv_open(client_enc, server_enc);
68 	if (My_Connections[Conn].iconv_to == (iconv_t)(-1)) {
69 		Conn_UnsetEncoding(Conn);
70 		return false;
71 	}
72 
73 	LogDebug("Set client character set of connection \"%d\" to \"%s\".",
74 		 Conn, client_enc);
75 	return true;
76 }
77 
78 /**
79  * Remove client character encoding conversion on a connection.
80  *
81  * @param Conn Connection identifier.
82  */
83 GLOBAL void
Conn_UnsetEncoding(CONN_ID Conn)84 Conn_UnsetEncoding(CONN_ID Conn)
85 {
86 	assert(Conn > NONE);
87 
88 	if (My_Connections[Conn].iconv_from != (iconv_t)(-1))
89 		iconv_close(My_Connections[Conn].iconv_from);
90 	if (My_Connections[Conn].iconv_to != (iconv_t)(-1))
91 		iconv_close(My_Connections[Conn].iconv_to);
92 
93 	My_Connections[Conn].iconv_from = (iconv_t)(-1);
94 	My_Connections[Conn].iconv_to = (iconv_t)(-1);
95 
96 	LogDebug("Unset character conversion of connection %d.", Conn);
97 }
98 
99 /**
100  * Convert the encoding of a given message.
101  *
102  * This function uses a static buffer for the result of the encoding
103  * conversion which is overwritten by subsequent calls to this function!
104  *
105  * @param Handle libiconv handle.
106  * @param Message The message to convert.
107  * @return Pointer to the result.
108  */
109 char *
Convert_Message(iconv_t Handle,char * Message)110 Convert_Message(iconv_t Handle, char *Message)
111 {
112 	size_t in_left, out_left;
113 	char *out = Encoding_Buffer;
114 
115 	assert (Handle != (iconv_t)(-1));
116 	assert (Message != NULL);
117 
118 	in_left = strlen(Message);
119 	out_left = sizeof(Encoding_Buffer) - 1;
120 
121 	if (iconv(Handle, &Message, &in_left, &out, &out_left) == (size_t)(-1)) {
122 		/* An error occurred! */
123 		LogDebug("Error converting message encoding!");
124 		strlcpy(out, Message, sizeof(Encoding_Buffer));
125 		iconv(Handle, NULL, NULL, NULL, NULL);
126 	} else
127 		*out = '\0';
128 
129 	return Encoding_Buffer;
130 }
131 
132 #endif /* ICONV */
133 
134 /**
135  * Convert encoding of a message received from a connection.
136  *
137  * Note 1: If no conversion is required, this function returns the original
138  * pointer to the message.
139  *
140  * Note 2: This function uses Convert_Message(), so subsequent calls to this
141  * function will overwrite the earlier results.
142  *
143  * @param Conn Connection identifier.
144  * @param Message The message to convert.
145  * @return Pointer to the result.
146  * @see Convert_Message
147  */
148 GLOBAL char *
Conn_EncodingFrom(UNUSED CONN_ID Conn,char * Message)149 Conn_EncodingFrom(UNUSED CONN_ID Conn, char *Message)
150 {
151 	assert(Conn > NONE);
152 	assert (Message != NULL);
153 
154 #ifdef ICONV
155 	if (My_Connections[Conn].iconv_from != (iconv_t)(-1))
156 		return Convert_Message(My_Connections[Conn].iconv_from, Message);
157 #endif
158 	return Message;
159 }
160 
161 /**
162  * Convert encoding of a message for sending on a connection.
163  *
164  * Note 1: If no conversion is required, this function returns the original
165  * pointer to the message.
166  *
167  * Note 2: This function uses Convert_Message(), so subsequent calls to this
168  * function will overwrite the earlier results.
169  *
170  * @param Conn Connection identifier.
171  * @param Message The message to convert.
172  * @return Pointer to the result.
173  * @see Convert_Message
174  */
175 GLOBAL char *
Conn_EncodingTo(UNUSED CONN_ID Conn,char * Message)176 Conn_EncodingTo(UNUSED CONN_ID Conn, char *Message)
177 {
178 	assert(Conn > NONE);
179 	assert (Message != NULL);
180 
181 #ifdef ICONV
182 	if (My_Connections[Conn].iconv_to != (iconv_t)(-1))
183 		return Convert_Message(My_Connections[Conn].iconv_to, Message);
184 #endif
185 	return Message;
186 }
187 
188 /* -eof- */
189