2
channels-query.c : irssi
4
Copyright (C) 1999-2000 Timo Sirainen
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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25
- After channel is joined and NAMES list is got, send "channel joined" signal
26
- "channel joined" : add channel to server->queries lists
29
- Wait for NAMES list from all channels before doing anything else..
30
- After got the last NAMES list, start sending the queries ..
31
- find the query to send, check where server->queries list isn't NULL
32
(mode, who, banlist, ban exceptions, invite list)
33
- if not found anything -> all channels are synced
34
- send "command #chan1,#chan2,#chan3,.." command to server
35
- wait for reply from server, then check if it was last query to be sent to
36
channel. If it was, send "channel sync" signal
37
- check if the reply was for last channel in the command list. If so,
47
#include "mode-lists.h"
49
#include "irc-servers.h"
50
#include "irc-channels.h"
51
#include "servers-redirect.h"
61
#define CHANNEL_IS_MODE_QUERY(a) ((a) != CHANNEL_QUERY_WHO)
64
int current_query_type; /* query type that is currently being asked */
65
GSList *current_queries; /* All channels that are currently being queried */
67
GSList *queries[CHANNEL_QUERIES]; /* All queries that need to be asked from server */
70
static void sig_connected(IRC_SERVER_REC *server)
72
SERVER_QUERY_REC *rec;
74
g_return_if_fail(server != NULL);
75
if (!IS_IRC_SERVER(server))
78
rec = g_new0(SERVER_QUERY_REC, 1);
79
server->chanqueries = rec;
82
static void sig_disconnected(IRC_SERVER_REC *server)
84
SERVER_QUERY_REC *rec;
87
g_return_if_fail(server != NULL);
88
if (!IS_IRC_SERVER(server))
91
rec = server->chanqueries;
92
g_return_if_fail(rec != NULL);
94
for (n = 0; n < CHANNEL_QUERIES; n++)
95
g_slist_free(rec->queries[n]);
96
g_slist_free(rec->current_queries);
99
server->chanqueries = NULL;
102
/* Add channel to query list */
103
static void query_add_channel(IRC_CHANNEL_REC *channel, int query_type)
105
SERVER_QUERY_REC *rec;
107
g_return_if_fail(channel != NULL);
109
rec = channel->server->chanqueries;
110
rec->queries[query_type] =
111
g_slist_append(rec->queries[query_type], channel);
114
static void query_check(IRC_SERVER_REC *server);
116
static void query_remove_all(IRC_CHANNEL_REC *channel)
118
SERVER_QUERY_REC *rec;
121
rec = channel->server->chanqueries;
123
/* remove channel from query lists */
124
for (n = 0; n < CHANNEL_QUERIES; n++)
125
rec->queries[n] = g_slist_remove(rec->queries[n], channel);
126
rec->current_queries = g_slist_remove(rec->current_queries, channel);
128
query_check(channel->server);
131
static void sig_channel_destroyed(IRC_CHANNEL_REC *channel)
133
g_return_if_fail(channel != NULL);
135
if (IS_IRC_CHANNEL(channel) && !channel->server->disconnected &&
137
query_remove_all(channel);
140
static int channels_have_all_names(IRC_SERVER_REC *server)
144
for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
145
IRC_CHANNEL_REC *rec = tmp->data;
147
if (IS_IRC_CHANNEL(rec) && !rec->names_got)
154
static int query_find_next(SERVER_QUERY_REC *server)
158
for (n = 0; n < CHANNEL_QUERIES; n++) {
159
if (server->queries[n] != NULL)
166
static void query_send(IRC_SERVER_REC *server, int query)
168
SERVER_QUERY_REC *rec;
169
IRC_CHANNEL_REC *chanrec;
171
char *cmd, *chanstr_commas, *chanstr;
174
rec = server->chanqueries;
176
/* get the list of channels to query */
177
onlyone = (server->no_multi_who && query == CHANNEL_QUERY_WHO) ||
178
(server->no_multi_mode && CHANNEL_IS_MODE_QUERY(query));
181
chans = rec->queries[query];
182
rec->queries[query] =
183
g_slist_remove_link(rec->queries[query], chans);
185
chanrec = chans->data;
186
chanstr_commas = g_strdup(chanrec->name);
187
chanstr = g_strdup(chanrec->name);
190
char *chanstr_spaces;
192
chans = rec->queries[query];
193
count = g_slist_length(chans);
195
if (count > server->max_query_chans) {
198
lastchan = g_slist_nth(rec->queries[query],
199
server->max_query_chans-1);
200
count = server->max_query_chans;
201
rec->queries[query] = lastchan->next;
202
lastchan->next = NULL;
204
rec->queries[query] = NULL;
207
chanstr_commas = gslistptr_to_string(chans, G_STRUCT_OFFSET(IRC_CHANNEL_REC, name), ",");
208
chanstr_spaces = gslistptr_to_string(chans, G_STRUCT_OFFSET(IRC_CHANNEL_REC, name), " ");
210
chanstr = g_strconcat(chanstr_commas, " ", chanstr_spaces, NULL);
211
g_free(chanstr_spaces);
214
rec->current_query_type = query;
215
rec->current_queries = chans;
218
case CHANNEL_QUERY_MODE:
219
cmd = g_strdup_printf("MODE %s", chanstr_commas);
221
/* the stop-event is received once for each channel,
222
and we want to print 329 event (channel created). */
223
server_redirect_event(server, "mode channel", count,
224
chanstr, -1, "chanquery abort",
225
"event 324", "chanquery mode",
226
"event 329", "event 329",
227
"", "chanquery abort", NULL);
230
case CHANNEL_QUERY_WHO:
231
cmd = g_strdup_printf("WHO %s", chanstr_commas);
233
server_redirect_event(server, "who",
234
server->one_endofwho ? 1 : count,
237
"event 315", "chanquery who end",
238
"event 352", "silent event who",
239
"", "chanquery abort", NULL);
242
case CHANNEL_QUERY_BMODE:
243
cmd = g_strdup_printf("MODE %s b", chanstr_commas);
244
/* check all the multichannel problems with all
245
mode requests - if channels are joined manually
246
irssi could ask modes separately but afterwards
247
join the two b/e/I modes together */
248
server_redirect_event(server, "mode b", count, chanstr, -1,
250
"event 367", "chanquery ban",
251
"event 368", "chanquery ban end",
252
"", "chanquery abort", NULL);
259
irc_send_cmd(server, cmd);
262
g_free(chanstr_commas);
266
static void query_check(IRC_SERVER_REC *server)
268
SERVER_QUERY_REC *rec;
271
g_return_if_fail(server != NULL);
273
rec = server->chanqueries;
274
if (rec->current_queries != NULL)
275
return; /* old queries haven't been answered yet */
277
if (!channels_have_all_names(server)) {
278
/* all channels haven't sent /NAMES list yet */
282
query = query_find_next(rec);
284
/* no queries left */
288
query_send(server, query);
291
/* if there's no more queries in queries in buffer, send the sync signal */
292
static void channel_checksync(IRC_CHANNEL_REC *channel)
294
SERVER_QUERY_REC *rec;
297
g_return_if_fail(channel != NULL);
300
return; /* already synced */
302
rec = channel->server->chanqueries;
303
for (n = 0; n < CHANNEL_QUERIES; n++) {
304
if (g_slist_find(rec->queries[n], channel))
308
channel->synced = TRUE;
309
signal_emit("channel sync", 1, channel);
312
/* Error occured when trying to execute query - abort and try again. */
313
static void query_current_error(IRC_SERVER_REC *server)
315
SERVER_QUERY_REC *rec;
317
int query, abort_query;
319
rec = server->chanqueries;
321
/* fix the thing that went wrong - or if it was already fixed,
322
then all we can do is abort. */
325
query = rec->current_query_type;
326
if (query == CHANNEL_QUERY_WHO) {
327
if (server->no_multi_who)
330
server->no_multi_who = TRUE;
332
if (server->no_multi_mode)
335
server->no_multi_mode = TRUE;
339
/* move all currently queried channels to main query lists */
340
for (tmp = rec->current_queries; tmp != NULL; tmp = tmp->next) {
341
rec->queries[query] =
342
g_slist_append(rec->queries[query], tmp->data);
345
/* check if failed channels are synced after this error */
346
g_slist_foreach(rec->current_queries,
347
(GFunc) channel_checksync, NULL);
350
g_slist_free(rec->current_queries);
351
rec->current_queries = NULL;
356
static void sig_channel_joined(IRC_CHANNEL_REC *channel)
358
if (!IS_IRC_CHANNEL(channel))
361
if (!settings_get_bool("channel_sync"))
364
/* Add channel to query lists */
365
if (!channel->no_modes)
366
query_add_channel(channel, CHANNEL_QUERY_MODE);
367
if (g_hash_table_size(channel->nicks) <
368
settings_get_int("channel_max_who_sync"))
369
query_add_channel(channel, CHANNEL_QUERY_WHO);
370
if (!channel->no_modes)
371
query_add_channel(channel, CHANNEL_QUERY_BMODE);
373
query_check(channel->server);
376
static void channel_got_query(IRC_CHANNEL_REC *chanrec, int query_type)
378
SERVER_QUERY_REC *rec;
380
g_return_if_fail(chanrec != NULL);
382
rec = chanrec->server->chanqueries;
383
if (query_type != rec->current_query_type)
384
return; /* shouldn't happen */
386
/* got the query for channel.. */
387
rec->current_queries =
388
g_slist_remove(rec->current_queries, chanrec);
389
channel_checksync(chanrec);
391
/* check if we need to send another query.. */
392
query_check(chanrec->server);
395
static void event_channel_mode(IRC_SERVER_REC *server, const char *data,
398
IRC_CHANNEL_REC *chanrec;
399
char *params, *channel, *mode;
401
g_return_if_fail(data != NULL);
403
params = event_get_params(data, 3 | PARAM_FLAG_GETREST,
404
NULL, &channel, &mode);
405
chanrec = irc_channel_find(server, channel);
406
if (chanrec != NULL) {
407
if (chanrec->key != NULL && strchr(mode, 'k') == NULL) {
408
/* we joined the channel with a key,
409
but it didn't have +k mode.. */
410
parse_channel_modes(chanrec, NULL, "-k", TRUE);
412
parse_channel_modes(chanrec, nick, mode, FALSE);
413
channel_got_query(chanrec, CHANNEL_QUERY_MODE);
419
static void event_end_of_who(IRC_SERVER_REC *server, const char *data)
421
SERVER_QUERY_REC *rec;
423
char *params, *channel, **channels;
424
int failed, multiple;
426
g_return_if_fail(data != NULL);
428
params = event_get_params(data, 2, NULL, &channel);
429
multiple = strchr(channel, ',') != NULL;
430
channels = g_strsplit(channel, ",", -1);
433
rec = server->chanqueries;
434
for (tmp = rec->current_queries; tmp != NULL; tmp = next) {
435
IRC_CHANNEL_REC *chanrec = tmp->data;
438
if (strarray_find(channels, chanrec->name) == -1)
441
if (chanrec->ownnick->host == NULL && multiple &&
442
!server->one_endofwho) {
443
/* we should receive our own host for each channel.
444
However, some servers really are stupid enough
445
not to reply anything to /WHO requests.. */
448
chanrec->wholist = TRUE;
449
signal_emit("channel wholist", 1, chanrec);
450
channel_got_query(chanrec, CHANNEL_QUERY_WHO);
454
g_strfreev(channels);
456
server->one_endofwho = TRUE;
459
/* server didn't understand multiple WHO replies,
460
send them again separately */
461
query_current_error(server);
467
static void event_end_of_banlist(IRC_SERVER_REC *server, const char *data)
469
IRC_CHANNEL_REC *chanrec;
470
char *params, *channel;
472
g_return_if_fail(data != NULL);
474
params = event_get_params(data, 2, NULL, &channel);
475
chanrec = irc_channel_find(server, channel);
478
channel_got_query(chanrec, CHANNEL_QUERY_BMODE);
483
void channels_query_init(void)
485
settings_add_bool("misc", "channel_sync", TRUE);
486
settings_add_int("misc", "channel_max_who_sync", 1000);
488
signal_add("server connected", (SIGNAL_FUNC) sig_connected);
489
signal_add("server disconnected", (SIGNAL_FUNC) sig_disconnected);
490
signal_add("channel joined", (SIGNAL_FUNC) sig_channel_joined);
491
signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
493
signal_add("chanquery mode", (SIGNAL_FUNC) event_channel_mode);
494
signal_add("chanquery who end", (SIGNAL_FUNC) event_end_of_who);
496
signal_add("chanquery ban end", (SIGNAL_FUNC) event_end_of_banlist);
497
signal_add("chanquery abort", (SIGNAL_FUNC) query_current_error);
500
void channels_query_deinit(void)
502
signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
503
signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
504
signal_remove("channel joined", (SIGNAL_FUNC) sig_channel_joined);
505
signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
507
signal_remove("chanquery mode", (SIGNAL_FUNC) event_channel_mode);
508
signal_remove("chanquery who end", (SIGNAL_FUNC) event_end_of_who);
510
signal_remove("chanquery ban end", (SIGNAL_FUNC) event_end_of_banlist);
511
signal_remove("chanquery abort", (SIGNAL_FUNC) query_current_error);