1 |
/* |
2 |
* ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd). |
3 |
* recode.c: Adds codepage option to listen{} blocks. |
4 |
* |
5 |
* Copyright (C) 2006 by the past and present ircd coders, and others. |
6 |
* |
7 |
* This program is free software; you can redistribute it and/or modify |
8 |
* it under the terms of the GNU General Public License as published by |
9 |
* the Free Software Foundation; either version 2 of the License, or |
10 |
* (at your option) any later version. |
11 |
* |
12 |
* This program is distributed in the hope that it will be useful, |
13 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 |
* GNU General Public License for more details. |
16 |
* |
17 |
* You should have received a copy of the GNU General Public License |
18 |
* along with this program; if not, write to the Free Software |
19 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
20 |
* USA |
21 |
* |
22 |
* $Id: $ |
23 |
*/ |
24 |
|
25 |
#include "stdinc.h" |
26 |
#include "client.h" |
27 |
#include "listener.h" |
28 |
#include "packet.h" |
29 |
#include "send.h" |
30 |
#include "conf/conf.h" |
31 |
|
32 |
struct Conversion |
33 |
{ |
34 |
unsigned short port; |
35 |
char first; |
36 |
void *to_utf8; |
37 |
void *from_utf8; |
38 |
struct Conversion *hnext; |
39 |
}; |
40 |
|
41 |
/* |
42 |
* Hashing doesn't affect performance here, as we are almost guaranteed |
43 |
* to have at most 1 entry per bucket. Using 65536 as CONVSIZE would just |
44 |
* be a waste of 1 MB on 64-bit. |
45 |
*/ |
46 |
|
47 |
#define CONVSIZE 1024 |
48 |
|
49 |
static struct Conversion *convtab[CONVSIZE]; |
50 |
static dlink_node *hreset, *hiorecv, *hiosend; |
51 |
static struct ConfSection *section; |
52 |
static struct ConfField *codepage; |
53 |
static char *last_codepage = NULL; |
54 |
static struct ConfField *port; |
55 |
static CONFF_HANDLER *old_port_handler; |
56 |
|
57 |
extern void *iconv_open(const char *, const char *); |
58 |
extern size_t iconv(void *, const char **, size_t *, char **, size_t *); |
59 |
extern int iconv_close(void *); |
60 |
|
61 |
/* |
62 |
* free_convtab() |
63 |
* |
64 |
* Deletes all used recode entries and clears the TCP port hash table. |
65 |
* |
66 |
* inputs: none |
67 |
* output: none |
68 |
*/ |
69 |
static void |
70 |
free_convtab(void) |
71 |
{ |
72 |
int i; |
73 |
struct Conversion *p; |
74 |
|
75 |
for (i = 0; i < CONVSIZE; i++) |
76 |
while ((p = convtab[i]) != NULL) |
77 |
{ |
78 |
if (p->first) |
79 |
{ |
80 |
iconv_close(p->to_utf8); |
81 |
iconv_close(p->from_utf8); |
82 |
} |
83 |
|
84 |
convtab[i] = p->hnext; |
85 |
MyFree(p); |
86 |
} |
87 |
} |
88 |
|
89 |
/* |
90 |
* reset_convtab() |
91 |
* |
92 |
* Wrapper around free_convtab(), used as reset_conf handler. |
93 |
* |
94 |
* inputs: none |
95 |
* output: none |
96 |
*/ |
97 |
static void * |
98 |
reset_convtab(va_list args) |
99 |
{ |
100 |
free_convtab(); |
101 |
|
102 |
return pass_callback(hreset); |
103 |
} |
104 |
|
105 |
/* |
106 |
* my_port_handler() |
107 |
* |
108 |
* Overrides default port= handler, adding a conversion if requested. |
109 |
* |
110 |
* inputs: |
111 |
* list - pointer to dlink_list of longs |
112 |
* var - where to write it |
113 |
* output: none |
114 |
*/ |
115 |
static void |
116 |
my_port_handler(void *list, void *var) |
117 |
{ |
118 |
if (last_codepage != NULL) |
119 |
{ |
120 |
dlink_node *ptr; |
121 |
void *to_utf8 = iconv_open("UTF-8", last_codepage); |
122 |
void *from_utf8 = iconv_open(last_codepage, "UTF-8"); |
123 |
|
124 |
if (to_utf8 == (void *) -1 || from_utf8 == (void *) -1) |
125 |
{ |
126 |
parse_error("Unable to open character set [%s]: %s", |
127 |
last_codepage, strerror(errno)); |
128 |
|
129 |
if (to_utf8 != (void *) -1) |
130 |
iconv_close(to_utf8); |
131 |
if (from_utf8 != (void *) -1) |
132 |
iconv_close(from_utf8); |
133 |
} |
134 |
else |
135 |
{ |
136 |
char first = YES; |
137 |
|
138 |
DLINK_FOREACH(ptr, ((dlink_list *) list)->head) |
139 |
{ |
140 |
unsigned short port = (unsigned short) (long) ptr->data; |
141 |
struct Conversion *conv = MyMalloc(sizeof(struct Conversion)); |
142 |
|
143 |
conv->port = port; |
144 |
conv->first = first; |
145 |
conv->to_utf8 = to_utf8; |
146 |
conv->from_utf8 = from_utf8; |
147 |
conv->hnext = convtab[port % CONVSIZE]; |
148 |
|
149 |
convtab[port % CONVSIZE] = conv; |
150 |
first = NO; |
151 |
} |
152 |
} |
153 |
|
154 |
MyFree(last_codepage); |
155 |
last_codepage = NULL; |
156 |
} |
157 |
|
158 |
old_port_handler(list, port); |
159 |
} |
160 |
|
161 |
/* |
162 |
* my_iorecv() |
163 |
* |
164 |
* Called when data is received from the network. |
165 |
* |
166 |
* inputs: |
167 |
* client_p - the client we get input from |
168 |
* length - amound of data read |
169 |
* buf - data buffer |
170 |
* output: none |
171 |
*/ |
172 |
static void * |
173 |
my_iorecv(va_list args) |
174 |
{ |
175 |
struct Client *client_p = va_arg(args, struct Client *); |
176 |
size_t inlen = va_arg(args, int); |
177 |
size_t saved_inlen = inlen; |
178 |
const char *inbuf = va_arg(args, char *); |
179 |
struct Listener *li = client_p->localClient->listener; |
180 |
struct Conversion *conv; |
181 |
|
182 |
if (li != NULL) |
183 |
for (conv = convtab[li->port % CONVSIZE]; conv != NULL; conv = conv->hnext) |
184 |
if (conv->port == li->port) |
185 |
{ |
186 |
char transbuf[2 * READBUF_SIZE]; |
187 |
size_t outlen = sizeof(transbuf); |
188 |
char *outbuf = transbuf; |
189 |
|
190 |
iconv(conv->to_utf8, NULL, NULL, NULL, NULL); |
191 |
iconv(conv->to_utf8, &inbuf, &inlen, &outbuf, &outlen); |
192 |
|
193 |
if ((inlen = outbuf - transbuf) > 0) |
194 |
inbuf = transbuf; |
195 |
else |
196 |
inlen = saved_inlen; |
197 |
} |
198 |
|
199 |
return pass_callback(hiorecv, client_p, inlen, inbuf); |
200 |
} |
201 |
|
202 |
/* |
203 |
* my_iosend() |
204 |
* |
205 |
* Called when data is to be sent over the network. |
206 |
* |
207 |
* inputs: |
208 |
* client_p - the client we write to |
209 |
* length - amount of output data |
210 |
* buf - data buffer |
211 |
* output: none |
212 |
*/ |
213 |
static void * |
214 |
my_iosend(va_list args) |
215 |
{ |
216 |
struct Client *client_p = va_arg(args, struct Client *); |
217 |
size_t inlen = va_arg(args, int); |
218 |
size_t saved_inlen = inlen; |
219 |
const char *inbuf = va_arg(args, char *); |
220 |
struct Listener *li = client_p->localClient->listener; |
221 |
struct Conversion *conv; |
222 |
|
223 |
if (li != NULL) |
224 |
for (conv = convtab[li->port % CONVSIZE]; conv != NULL; conv = conv->hnext) |
225 |
if (conv->port == li->port) |
226 |
{ |
227 |
char transbuf[2 * IRCD_BUFSIZE]; |
228 |
size_t outlen = sizeof(transbuf); |
229 |
char *outbuf = transbuf; |
230 |
|
231 |
iconv(conv->from_utf8, NULL, NULL, NULL, NULL); |
232 |
iconv(conv->from_utf8, &inbuf, &inlen, &outbuf, &outlen); |
233 |
|
234 |
if ((inlen = outbuf - transbuf) > 0) |
235 |
inbuf = transbuf; |
236 |
else |
237 |
inlen = saved_inlen; |
238 |
} |
239 |
|
240 |
return pass_callback(hiosend, client_p, inlen, inbuf); |
241 |
} |
242 |
|
243 |
/* |
244 |
* Module entry point. |
245 |
*/ |
246 |
INIT_MODULE(recode, "$Revision: $") |
247 |
{ |
248 |
memset(&convtab, 0, sizeof(convtab)); |
249 |
|
250 |
hreset = install_hook(reset_conf, reset_convtab); |
251 |
|
252 |
section = find_conf_section("listen"); |
253 |
codepage = add_conf_field(section, "codepage", CT_STRING, NULL, |
254 |
&last_codepage); |
255 |
|
256 |
port = find_conf_field(section, "port"); |
257 |
old_port_handler = port->handler; |
258 |
port->handler = my_port_handler; |
259 |
|
260 |
hiorecv = install_hook(iorecv_cb, my_iorecv); |
261 |
hiosend = install_hook(iosend_cb, my_iosend); |
262 |
} |
263 |
|
264 |
/* |
265 |
* Module uninstaller. |
266 |
*/ |
267 |
CLEANUP_MODULE |
268 |
{ |
269 |
uninstall_hook(iorecv_cb, my_iorecv); |
270 |
uninstall_hook(iosend_cb, my_iosend); |
271 |
|
272 |
port->handler = old_port_handler; |
273 |
|
274 |
delete_conf_field(section, codepage); |
275 |
|
276 |
uninstall_hook(reset_conf, reset_convtab); |
277 |
|
278 |
free_convtab(); |
279 |
} |