2 ctrlproxy: A modular IRC proxy
3 (c) 2002-2005 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 void string2mode(char *modes, char ar[255])
26 memset(ar, 0, sizeof(ar));
31 g_assert(modes[0] == '+');
33 for (; *modes; modes++) {
34 ar[(int)(*modes)] = 1;
38 char *mode2string(char modes[255])
44 for(i = 0; i < 255; i++) {
45 if (modes[i]) { ret[pos] = (char)i; pos++; }
49 if (strlen(ret) == 0) {
52 return g_strdup_printf("+%s", ret);
56 static void client_send_channel_state(struct client *c,
57 struct channel_state *ch)
59 struct line *l = NULL;
64 g_assert(c->network != NULL);
65 g_assert(c->network->state != NULL);
66 g_assert(ch->name != NULL);
68 client_send_args_ex(c, client_get_own_hostmask(c), "JOIN", ch->name,
71 if (ch->topic != NULL) {
72 client_send_response(c, RPL_TOPIC, ch->name, ch->topic, NULL);
74 if (ch->topic_set_time != 0 && ch->topic_set_by != NULL) {
75 char *tmp = g_strdup_printf("%lu", ch->topic_set_time);
76 client_send_response(c, RPL_TOPICWHOTIME, ch->name, ch->topic_set_by,
81 for (nl = ch->nicks; nl; nl = nl->next) {
82 char mode[2] = { ch->mode, 0 };
84 struct channel_nick *n = (struct channel_nick *)nl->data;
86 if (n->mode != '\0' && n->mode != ' ') {
87 arg = g_strdup_printf("%c%s", n->mode, n->global_nick->nick);
89 arg = g_strdup(n->global_nick->nick);
92 if (l == NULL || !line_add_arg(l, arg)) {
95 client_send_line(c, l);
99 l = irc_parse_line_args(client_get_default_origin(c), "353",
100 client_get_default_target(c), mode,
102 l->has_endcolon = WITHOUT_COLON;
103 tmp = g_strdup_printf(":%s", arg);
104 g_assert(line_add_arg(l, tmp));
112 client_send_line(c, l);
116 client_send_response(c, RPL_ENDOFNAMES, ch->name, "End of /NAMES list",
120 gboolean client_send_channel_state_diff(struct client *client,
121 struct channel_state *old_state,
122 struct channel_state *new_state)
126 /* Send PART for each user that is only in old_state */
127 for (gl = old_state->nicks; gl; gl = gl->next) {
128 struct channel_nick *on = gl->data;
129 struct channel_nick *nn;
131 nn = find_channel_nick_hostmask(new_state, on->global_nick->hostmask);
133 client_send_args_ex(client, on->global_nick->hostmask,
134 "PART", on->global_nick->nick, NULL);
136 client_send_args_ex(client, on->global_nick->hostmask,
137 "NICK", nn->global_nick->nick, NULL);
140 /* Send JOIN for each user that is only in new_state */
141 for (gl = new_state->nicks; gl; gl = gl->next) {
142 struct channel_nick *nn = gl->data;
143 struct channel_nick *on;
145 on = find_channel_nick(old_state, nn->global_nick->nick);
147 client_send_args_ex(client, nn->global_nick->hostmask, "JOIN",
148 on->channel->name, NULL);
151 /* Send TOPIC if the topic is different */
152 if (strcmp(old_state->topic, new_state->topic) != 0)
153 client_send_args_ex(client, new_state->topic_set_by, "TOPIC", new_state->topic, NULL);
155 /* Send MODE if the mode changed */
156 if (memcmp(old_state->modes, new_state->modes,
157 sizeof(old_state->modes)) != 0) {
158 char *mode = mode2string(new_state->modes);
159 client_send_args(client, "MODE", new_state->name, mode, NULL);
169 * Send the diff between the current state to change it to some other state.
170 * @param c Client to send to
171 * @param state State to send
172 * @return Whether the state was sent correctly
174 gboolean client_send_state_diff(struct client *client, struct network_state *new_state)
176 struct network_state *old_state = client->network->state;
179 /* Call client_send_channel_state_diff() for each channel that exists
181 /* Send PART for each channel that is only in old_state */
182 for (gl = old_state->channels; gl; gl = gl->next) {
183 struct channel_state *os = gl->data;
184 struct channel_state *ns;
186 ns = find_channel(new_state, os->name);
189 client_send_channel_state_diff(client, os, ns);
191 client_send_args_ex(client, client_get_own_hostmask(client),
192 "PART", os->name, NULL);
195 /* Call client_send_channel_state() for each channel that is only
197 for (gl = new_state->channels; gl; gl = gl->next) {
198 struct channel_state *ns = gl->data;
199 struct channel_state *os;
201 os = find_channel(old_state, ns->name);
203 client_send_channel_state(client, ns);
210 * Send a particular state to a client.
212 * @param c Client to send to
213 * @param state State to send
215 gboolean client_send_state(struct client *c, struct network_state *state)
218 struct channel_state *ch;
221 if (strcmp(state->me.nick, c->nick) != 0) {
222 client_send_args_ex(c, c->hostmask, "NICK", state->me.nick, NULL);
226 g_assert(state != NULL);
228 log_client(LOG_TRACE, c, "Sending state (%d channels)",
229 g_list_length(state->channels));
231 for (cl = state->channels; cl; cl = cl->next) {
232 ch = (struct channel_state *)cl->data;
234 client_send_channel_state(c, ch);
237 mode = mode2string(state->me.modes);
239 client_send_args_ex(c, state->me.nick, "MODE", mode, NULL);
245 static GList *backends = NULL;
247 void register_replication_backend(const struct replication_backend *backend)
249 backends = g_list_append(backends, g_memdup(backend, sizeof(*backend)));
253 * Replicate the current state and backlog to the client.
255 * @param client Client to send data to.
257 void client_replicate(struct client *client)
259 void (*fn) (struct client *);
260 const char *bn = client->network->global->config->replication;
268 for (gl = backends; gl; gl = gl->next) {
269 struct replication_backend *backend = gl->data;
270 if (!strcmp(backend->name, bn))
271 fn = backend->replication_fn;
275 log_client(LOG_WARNING, client,
276 "Unable to find replication backend '%s'", bn);
278 if (client->network->state)
279 client_send_state(client, client->network->state);