3 ctrlproxy: A modular IRC proxy
4 (c) 2002-2008 Jelmer Vernooij <jelmer@nl.linux.org>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "transport.h"
26 static gboolean transport_send_queue(GIOChannel *c, GIOCondition cond,
29 static gboolean handle_transport_receive(GIOChannel *c, GIOCondition cond,
32 struct irc_transport *transport = _transport;
37 if (cond & G_IO_ERR) {
38 char *tmp = g_strdup_printf("Error reading from client: %s",
39 g_io_channel_unix_get_sock_error(c));
40 transport->callbacks->error(transport, tmp);
45 if (cond & G_IO_HUP) {
46 transport->callbacks->hangup(transport);
55 while ((status = irc_recv_line(c, transport->incoming_iconv, &error,
56 &l)) == G_IO_STATUS_NORMAL) {
58 ret &= transport->callbacks->recv(transport, l);
65 if (status == G_IO_STATUS_EOF) {
66 transport->callbacks->hangup(transport);
70 if (status != G_IO_STATUS_AGAIN) {
71 if (error->domain == G_CONVERT_ERROR &&
72 error->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE) {
73 transport->callbacks->charset_error(transport,
76 return transport->callbacks->error(transport, error?error->message:NULL);
85 void irc_transport_set_callbacks(struct irc_transport *transport,
86 const struct irc_transport_callbacks *callbacks, void *userdata)
88 transport->userdata = userdata;
89 transport->callbacks = callbacks;
91 transport->incoming_id = g_io_add_watch(
92 transport->incoming, G_IO_IN | G_IO_HUP,
93 handle_transport_receive, transport);
96 /* GIOChannels passed into this function
98 * - have no encoding set
99 * - work asynchronously
101 * @param iochannel Channel to talk over
103 struct irc_transport *irc_transport_new_iochannel(GIOChannel *iochannel)
105 struct irc_transport *ret = g_new0(struct irc_transport, 1);
107 ret->incoming = iochannel;
108 ret->pending_lines = g_queue_new();
109 ret->outgoing_iconv = ret->incoming_iconv = (GIConv)-1;
110 g_io_channel_ref(ret->incoming);
115 void irc_transport_disconnect(struct irc_transport *transport)
117 if (transport->incoming == NULL)
118 return; /* We're already disconnected */
120 g_io_channel_unref(transport->incoming);
122 g_source_remove(transport->incoming_id);
123 if (transport->outgoing_id)
124 g_source_remove(transport->outgoing_id);
126 transport->incoming = NULL;
128 transport->callbacks->disconnect(transport);
131 static void free_pending_line(void *_line, void *userdata)
133 free_line((struct irc_line *)_line);
136 void free_irc_transport(struct irc_transport *transport)
138 /* Should already be disconnected */
139 g_assert(transport->incoming == NULL);
140 g_free(transport->charset);
142 if (transport->outgoing_iconv != (GIConv)-1)
143 g_iconv_close(transport->outgoing_iconv);
144 if (transport->incoming_iconv != (GIConv)-1)
145 g_iconv_close(transport->incoming_iconv);
147 g_assert(transport->pending_lines != NULL);
148 g_queue_foreach(transport->pending_lines, free_pending_line, NULL);
149 g_queue_free(transport->pending_lines);
155 * Change the character set used to send data to a client
156 * @param c client to change the character set for
157 * @param name name of the character set to change to
158 * @return whether changing the character set succeeded
160 gboolean transport_set_charset(struct irc_transport *transport, const char *name)
165 tmp = g_iconv_open(name, "UTF-8");
167 if (tmp == (GIConv)-1) {
174 if (transport->outgoing_iconv != (GIConv)-1)
175 g_iconv_close(transport->outgoing_iconv);
177 transport->outgoing_iconv = tmp;
180 tmp = g_iconv_open("UTF-8", name);
182 if (tmp == (GIConv)-1) {
189 if (transport->incoming_iconv != (GIConv)-1)
190 g_iconv_close(transport->incoming_iconv);
192 transport->incoming_iconv = tmp;
194 g_free(transport->charset);
195 transport->charset = g_strdup(name);
200 static gboolean transport_send_queue(GIOChannel *ioc, GIOCondition cond,
203 gboolean ret = FALSE;
204 struct irc_transport *transport = _transport;
207 g_assert(ioc == transport->incoming);
209 status = g_io_channel_flush(transport->incoming, NULL);
210 if (status == G_IO_STATUS_AGAIN)
213 g_assert(transport->pending_lines != NULL);
215 while (!g_queue_is_empty(transport->pending_lines)) {
216 GError *error = NULL;
217 struct irc_line *l = g_queue_pop_head(transport->pending_lines);
219 g_assert(transport->incoming != NULL);
220 status = irc_send_line(transport->incoming,
221 transport->outgoing_iconv, l, &error);
224 case G_IO_STATUS_AGAIN:
225 g_queue_push_head(transport->pending_lines, l);
227 case G_IO_STATUS_ERROR:
228 transport->callbacks->log(transport, l, error);
230 case G_IO_STATUS_EOF:
231 transport->outgoing_id = 0;
233 transport->callbacks->hangup(transport);
238 case G_IO_STATUS_NORMAL:
239 transport->last_line_sent = time(NULL);
243 status = g_io_channel_flush(transport->incoming, &error);
245 case G_IO_STATUS_EOF:
246 g_assert_not_reached();
247 case G_IO_STATUS_AGAIN:
250 case G_IO_STATUS_NORMAL:
252 case G_IO_STATUS_ERROR:
253 transport->callbacks->log(transport, l, error);
260 transport->outgoing_id = 0;
264 gboolean transport_send_line(struct irc_transport *transport,
265 const struct irc_line *l)
267 GError *error = NULL;
270 if (transport->incoming == NULL)
273 if (transport->outgoing_id != 0) {
274 g_queue_push_tail(transport->pending_lines, linedup(l));
278 status = irc_send_line(transport->incoming, transport->outgoing_iconv, l, &error);
281 case G_IO_STATUS_AGAIN:
282 transport->outgoing_id = g_io_add_watch(transport->incoming, G_IO_OUT,
283 transport_send_queue, transport);
284 g_queue_push_tail(transport->pending_lines, linedup(l));
286 case G_IO_STATUS_EOF:
287 transport->callbacks->hangup(transport);
289 case G_IO_STATUS_ERROR:
290 transport->callbacks->log(transport, l, error);
292 case G_IO_STATUS_NORMAL:
293 transport->last_line_sent = time(NULL);
297 status = g_io_channel_flush(transport->incoming, &error);
300 case G_IO_STATUS_EOF:
301 g_assert_not_reached();
302 case G_IO_STATUS_NORMAL:
304 case G_IO_STATUS_AGAIN:
305 transport->outgoing_id = g_io_add_watch(transport->incoming, G_IO_OUT,
306 transport_send_queue, transport);
308 case G_IO_STATUS_ERROR:
309 transport->callbacks->log(transport, l, error);
316 gboolean transport_send_args(struct irc_transport *transport, ...)
322 va_start(ap, transport);
323 l = virc_parse_line(NULL, ap);
326 ret = transport_send_line(transport, l);
333 void transport_parse_buffer(struct irc_transport *transport)
335 handle_transport_receive(transport->incoming,
336 g_io_channel_get_buffer_condition(transport->incoming),