2 ctrlproxy: A modular IRC proxy
3 (c) 2002-2003 Jelmer Vernooij <jelmer@nl.linux.org>
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.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include "internals.h"
24 #endif /* HAVE_CONFIG_H */
28 static GSList *linestack_backends = NULL;
30 void register_linestack(const struct linestack_ops *b)
32 linestack_backends = g_slist_append(linestack_backends, g_memdup(b, sizeof(*b)));
35 struct linestack_context *create_linestack(const struct linestack_ops *ops,
37 struct ctrlproxy_config *cfg,
38 const struct network_state *state)
40 struct linestack_context *ctx;
46 ctx = g_new0(struct linestack_context, 1);
48 ops->init(ctx, name, cfg, state);
53 void free_linestack_context(struct linestack_context *ctx)
61 static struct linestack_marker *wrap_linestack_marker(struct linestack_context *ctx, void *data)
63 struct linestack_marker *mrk;
67 mrk = g_new0(struct linestack_marker, 1);
68 mrk->free_fn = ctx->ops->free_marker;
73 struct linestack_marker *linestack_get_marker_numlines (struct linestack_context *ctx, int lines)
75 g_assert(ctx != NULL);
79 if (ctx->ops->get_marker_numlines == NULL)
82 return wrap_linestack_marker(ctx, ctx->ops->get_marker_numlines(ctx, lines));
85 struct network_state *linestack_get_state(
86 struct linestack_context *ctx,
87 struct linestack_marker *lm)
89 struct network_state *st;
90 g_assert(ctx != NULL);
92 if (!ctx->ops) return NULL;
93 if (!ctx->ops->get_state) return NULL;
95 st = ctx->ops->get_state(ctx, lm?lm->data:NULL);
99 g_assert(st->me.nick);
100 g_assert(st->me.query);
104 gboolean linestack_traverse(struct linestack_context *ctx,
105 struct linestack_marker *lm_from, struct linestack_marker *lm_to,
106 linestack_traverse_fn handler, void *userdata)
108 g_assert(ctx != NULL);
109 g_assert(ctx->ops != NULL);
110 g_assert(ctx->ops->traverse != NULL);
112 return ctx->ops->traverse(ctx, lm_from?lm_from->data:NULL, lm_to?lm_to->data:NULL, handler, userdata);
115 struct traverse_object_data {
116 linestack_traverse_fn handler;
121 static gboolean traverse_object_handler(struct line *l, time_t t, void *state)
123 struct traverse_object_data *d = state;
129 if (strchr(l->args[1], ',') == NULL) {
130 if (!strcmp(l->args[1], d->object))
131 ret &= d->handler(l, t, d->userdata);
134 char **channels = g_strsplit(l->args[1], ",", 0);
135 for (i = 0; channels[i]; i++) {
136 if (!strcmp(channels[i], d->object)) {
137 gboolean ret = d->handler(l, t, d->userdata);
138 g_strfreev(channels);
142 g_strfreev(channels);
148 gboolean linestack_traverse_object(
149 struct linestack_context *ctx,
151 struct linestack_marker *lm_from,
152 struct linestack_marker *lm_to, linestack_traverse_fn hl,
155 struct traverse_object_data d;
156 g_assert(ctx != NULL);
157 g_assert(ctx->ops != NULL);
160 d.userdata = userdata;
163 return linestack_traverse(ctx, lm_from, lm_to, traverse_object_handler, &d);
166 void linestack_free_marker(struct linestack_marker *lm)
172 if (lm->free_fn != NULL)
173 lm->free_fn(lm->data);
177 struct linestack_marker *linestack_get_marker(struct linestack_context *ctx)
182 g_assert (ctx->ops != NULL);
183 g_assert (ctx->ops->get_marker != NULL);
185 return wrap_linestack_marker(ctx, ctx->ops->get_marker(ctx));
188 static const char *linestack_messages[] = {
189 "NICK", "JOIN", "QUIT", "PART", "PRIVMSG", "NOTICE", "KICK",
191 "353", /* RPL_NAMREPLY */
192 "366", /* RPL_ENDOFNAMES */
193 "331", /* RPL_NOTOPIC */
194 "333", /* RPL_TOPICWHOTIME */
195 "332", /* RPL_TOPIC */
196 "324", /* RPL_CHANNELMODEIS */
197 "329", /* RPL_CREATIONTIME */
200 gboolean linestack_insert_line(struct linestack_context *ctx, const struct line *l, enum data_direction dir, const struct network_state *state)
203 gboolean needed = FALSE;
205 if (ctx == NULL) return FALSE;
207 if (l->argc == 0) return FALSE;
209 if (!ctx->ops) return FALSE;
210 g_assert(ctx->ops->insert_line);
212 /* Only need PRIVMSG and NOTICE messages we send ourselves */
213 if (dir == TO_SERVER &&
214 g_strcasecmp(l->args[0], "PRIVMSG") &&
215 g_strcasecmp(l->args[0], "NOTICE")) return FALSE;
217 /* No CTCP, please */
218 if ((!g_strcasecmp(l->args[0], "PRIVMSG") ||
219 !g_strcasecmp(l->args[0], "NOTICE")) &&
220 l->argc > 2 && l->args[2][0] == '\001')
223 for (i = 0; linestack_messages[i]; i++)
224 if (!g_strcasecmp(linestack_messages[i], l->args[0]))
227 if (!needed) return FALSE;
229 for (i = 0; i < l->argc; i++) {
230 g_assert(strchr(l->args[i], '\n') == NULL);
231 g_assert(strchr(l->args[i], '\r') == NULL);
234 return ctx->ops->insert_line(ctx, l, state);
237 static gboolean send_line(struct line *l, time_t t, void *_client)
239 struct client *c = _client;
240 return client_send_line(c, l);
243 static gboolean send_line_timed(struct line *l, time_t t, void *_client)
245 struct client *c = _client;
247 if ((!g_strcasecmp(l->args[0], "PRIVMSG") ||
248 !g_strcasecmp(l->args[0], "NOTICE")) &&
251 struct line *nl = linedup(l);
255 strftime(stime, sizeof(stime), "%H:%M:%S", localtime(&t));
256 tmp = g_strdup_printf("[%s] %s", stime, nl->args[2]);
259 ret = client_send_line(c, nl);
263 return client_send_line(c, l);
267 gboolean linestack_send(struct linestack_context *ctx, struct linestack_marker *mf, struct linestack_marker *mt, struct client *c)
269 return linestack_traverse(ctx, mf, mt, send_line, c);
272 gboolean linestack_send_timed(struct linestack_context *ctx, struct linestack_marker *mf, struct linestack_marker *mt, struct client *c)
274 return linestack_traverse(ctx, mf, mt, send_line_timed, c);
277 gboolean linestack_send_object(struct linestack_context *ctx, const char *obj, struct linestack_marker *mf, struct linestack_marker *mt, struct client *c)
279 return linestack_traverse_object(ctx, obj, mf, mt, send_line, c);
282 gboolean linestack_send_object_timed(struct linestack_context *ctx, const char *obj, struct linestack_marker *mf, struct linestack_marker *mt, struct client *c)
284 return linestack_traverse_object(ctx, obj, mf, mt, send_line_timed, c);
287 static gboolean replay_line(struct line *l, time_t t, void *state)
289 struct network_state *st = state;
290 state_handle_data(st, l);
294 gboolean linestack_replay(struct linestack_context *ctx, struct linestack_marker *mf, struct linestack_marker *mt, struct network_state *st)
296 return linestack_traverse(ctx, mf, mt, replay_line, st);
299 struct linestack_ops *linestack_find_ops(const char *name)
302 for (gl = linestack_backends; gl ; gl = gl->next) {
303 struct linestack_ops *ops = gl->data;
304 if (!strcmp(ops->name, name))
311 struct linestack_context *new_linestack(struct network *n)
313 const struct linestack_ops *current_backend = NULL;
314 struct ctrlproxy_config *cfg = NULL;
316 if (n->global != NULL)
317 cfg = n->global->config;
319 register_linestack(&linestack_file);
321 if (cfg && cfg->linestack_backend) {
322 current_backend = linestack_find_ops(cfg->linestack_backend);
324 if (!current_backend)
325 log_global(LOG_WARNING, "Unable to find linestack backend %s: falling back to default", cfg->linestack_backend);
328 if (!current_backend) {
329 current_backend = &linestack_file;
332 return create_linestack(current_backend, n->info.name, cfg, n->state);