~ubuntu-branches/ubuntu/feisty/irssi/feisty-backports

« back to all changes in this revision

Viewing changes to src/irc/core/modes.c

  • Committer: Bazaar Package Importer
  • Author(s): David Pashley
  • Date: 2005-12-10 21:25:51 UTC
  • Revision ID: james.westby@ubuntu.com-20051210212551-5qwm108g7inyu2f2
Tags: upstream-0.8.10
ImportĀ upstreamĀ versionĀ 0.8.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 modes.c : irssi
 
3
 
 
4
    Copyright (C) 1999-2000 Timo Sirainen
 
5
 
 
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.
 
10
 
 
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.
 
15
 
 
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
 
19
*/
 
20
 
 
21
#include "module.h"
 
22
#include "signals.h"
 
23
#include "settings.h"
 
24
 
 
25
#include "irc-commands.h"
 
26
#include "irc-servers.h"
 
27
#include "irc-channels.h"
 
28
#include "servers-redirect.h"
 
29
#include "modes.h"
 
30
#include "mode-lists.h"
 
31
#include "nicklist.h"
 
32
 
 
33
/* Change nick's mode in channel */
 
34
static void nick_mode_change(IRC_CHANNEL_REC *channel, const char *nick,
 
35
                             char mode, int type, const char *setby)
 
36
{
 
37
        NICK_REC *nickrec;
 
38
        char modestr[2], typestr[2];
 
39
 
 
40
        g_return_if_fail(IS_IRC_CHANNEL(channel));
 
41
        g_return_if_fail(nick != NULL);
 
42
 
 
43
        nickrec = nicklist_find(CHANNEL(channel), nick);
 
44
        if (nickrec == NULL) return; /* No /names list got yet */
 
45
 
 
46
        if (mode == '@') nickrec->op = type == '+';
 
47
        else if (mode == '+') nickrec->voice = type == '+';
 
48
        else if (mode == '%') nickrec->halfop = type == '+';
 
49
        else if (channel->server->prefix[(unsigned char) mode] != '\0')
 
50
                nickrec->other = (type == '+' ? mode : '\0');
 
51
 
 
52
        modestr[0] = mode; modestr[1] = '\0';
 
53
        typestr[0] = type; typestr[1] = '\0';
 
54
        signal_emit("nick mode changed", 5,
 
55
                    channel, nickrec, setby, modestr, typestr);
 
56
}
 
57
 
 
58
static int mode_is_set(const char *str, char mode)
 
59
{
 
60
        char *end, *pos;
 
61
 
 
62
        g_return_val_if_fail(str != NULL, FALSE);
 
63
 
 
64
        end = strchr(str, ' ');
 
65
        pos = strchr(str, mode);
 
66
        return pos != NULL && (end == NULL || pos < end);
 
67
}
 
68
 
 
69
/* add argument to specified position */
 
70
static void mode_add_arg(GString *str, int pos, int updating, const char *arg)
 
71
{
 
72
        char *p;
 
73
 
 
74
        for (p = str->str; *p != '\0'; p++) {
 
75
                if (*p != ' ')
 
76
                        continue;
 
77
 
 
78
                if (pos == 0)
 
79
                        break;
 
80
                pos--;
 
81
        }
 
82
 
 
83
        pos = (int) (p-str->str);
 
84
        if (updating && *p != '\0') {
 
85
                /* remove the old argument */
 
86
                p++;
 
87
                while (*p != '\0' && *p != ' ') p++;
 
88
                g_string_erase(str, pos, (int) (p-str->str)-pos);
 
89
        }
 
90
 
 
91
        /* .. GLib shouldn't fail when inserting at the end of the string */
 
92
        if (pos == str->len) {
 
93
                g_string_append_c(str, ' ');
 
94
                g_string_append(str, arg);
 
95
        } else {
 
96
                g_string_insert_c(str, pos, ' ');
 
97
                g_string_insert(str, pos+1, arg);
 
98
        }
 
99
}
 
100
 
 
101
/* Add mode character to list sorted alphabetically */
 
102
static void mode_add_sorted(IRC_SERVER_REC *server, GString *str,
 
103
                            char mode, const char *arg, int user)
 
104
{
 
105
        char *p;
 
106
        int updating, argpos = 0;
 
107
 
 
108
        /* check that mode isn't already set */
 
109
        if ((!user && !HAS_MODE_ARG_SET(server, mode)) &&
 
110
            mode_is_set(str->str, mode))
 
111
                return;
 
112
 
 
113
        updating = FALSE;
 
114
        for (p = str->str; *p != '\0' && *p != ' '; p++) {
 
115
                if (mode < *p)
 
116
                        break;
 
117
                if (mode == *p) {
 
118
                        updating = TRUE;
 
119
                        break;
 
120
                }
 
121
                if (!user && HAS_MODE_ARG_SET(server, *p))
 
122
                        argpos++;
 
123
        }
 
124
 
 
125
        /* .. GLib shouldn't fail when inserting at the end of the string */
 
126
        if (!updating) {
 
127
                if (*p == '\0')
 
128
                        g_string_append_c(str, mode);
 
129
                else
 
130
                        g_string_insert_c(str, (int) (p-str->str), mode);
 
131
        }
 
132
        if (arg != NULL)
 
133
                mode_add_arg(str, argpos, updating, arg);
 
134
}
 
135
 
 
136
/* remove the n'th argument */
 
137
static void node_remove_arg(GString *str, int pos)
 
138
{
 
139
        char *p;
 
140
        int startpos;
 
141
 
 
142
        startpos = -1;
 
143
        for (p = str->str; *p != '\0'; p++) {
 
144
                if (*p != ' ')
 
145
                        continue;
 
146
 
 
147
                if (pos < 0)
 
148
                        break;
 
149
                if (pos == 0)
 
150
                        startpos = (int) (p-str->str);
 
151
                pos--;
 
152
        }
 
153
 
 
154
        if (startpos == -1)
 
155
                return; /* not found */
 
156
 
 
157
        g_string_erase(str, startpos, (int) (p-str->str)-startpos);
 
158
}
 
159
 
 
160
/* remove mode (and it's argument) from string */
 
161
static void mode_remove(IRC_SERVER_REC *server, GString *str, char mode, int user)
 
162
{
 
163
        char *p;
 
164
        int argpos = 0;
 
165
 
 
166
        for (p = str->str; *p != '\0' && *p != ' '; p++) {
 
167
                if (mode == *p) {
 
168
                        g_string_erase(str, (int) (p-str->str), 1);
 
169
                        if (!user && HAS_MODE_ARG_SET(server, mode))
 
170
                                node_remove_arg(str, argpos);
 
171
                        break;
 
172
                }
 
173
                if (!user && HAS_MODE_ARG_SET(server, *p))
 
174
                        argpos++;
 
175
        }
 
176
}
 
177
 
 
178
static void mode_set(IRC_SERVER_REC *server, GString *str,
 
179
                     char type, char mode, int user)
 
180
{
 
181
        g_return_if_fail(str != NULL);
 
182
 
 
183
        if (type == '-')
 
184
                mode_remove(server, str, mode, user);
 
185
        else
 
186
                mode_add_sorted(server, str, mode, NULL, user);
 
187
}
 
188
 
 
189
static void mode_set_arg(IRC_SERVER_REC *server, GString *str,
 
190
                         char type, char mode, const char *arg, int user)
 
191
{
 
192
        g_return_if_fail(str != NULL);
 
193
        g_return_if_fail(type == '-' || arg != NULL);
 
194
 
 
195
        if (type == '-')
 
196
                mode_remove(server, str, mode, user);
 
197
        else
 
198
                mode_add_sorted(server, str, mode, arg, user);
 
199
}
 
200
 
 
201
/* Mode that needs a parameter of a mask for both setting and removing
 
202
   (eg: bans) */
 
203
void modes_type_a(IRC_CHANNEL_REC *channel, const char *setby, char type,
 
204
                  char mode, char *arg, GString *newmode)
 
205
{
 
206
        if (mode == 'b') {
 
207
                if (type == '+')
 
208
                        banlist_add(channel, arg, setby, time(NULL));
 
209
                else
 
210
                        banlist_remove(channel, arg, setby);
 
211
        }
 
212
}
 
213
 
 
214
/* Mode that needs parameter for both setting and removing (eg: +k) */
 
215
void modes_type_b(IRC_CHANNEL_REC *channel, const char *setby, char type,
 
216
                  char mode, char *arg, GString *newmode)
 
217
{
 
218
        if (mode == 'k') {
 
219
                if (*arg == '\0' && type == '+')
 
220
                        arg = channel->key != NULL ? channel->key : "???";
 
221
 
 
222
                if (arg != channel->key) {
 
223
                        g_free_and_null(channel->key);
 
224
                        if (type == '+')
 
225
                                channel->key = g_strdup(arg);
 
226
                }
 
227
        }
 
228
        
 
229
        mode_set_arg(channel->server, newmode, type, mode, arg, FALSE);
 
230
}
 
231
 
 
232
/* Mode that needs parameter only for adding */
 
233
void modes_type_c(IRC_CHANNEL_REC *channel, const char *setby,
 
234
                  char type, char mode, char *arg, GString *newmode)
 
235
{
 
236
        if (mode == 'l') {
 
237
                channel->limit = type == '-' ? 0 : atoi(arg);
 
238
        }
 
239
        
 
240
        mode_set_arg(channel->server, newmode, type, mode, arg, FALSE);
 
241
}
 
242
 
 
243
/* Mode that takes no parameter */
 
244
void modes_type_d(IRC_CHANNEL_REC *channel, const char *setby,
 
245
                  char type, char mode, char *arg, GString *newmode)
 
246
{
 
247
        mode_set(channel->server, newmode, type, mode, FALSE);
 
248
}
 
249
 
 
250
void modes_type_prefix(IRC_CHANNEL_REC *channel, const char *setby,
 
251
                       char type, char mode, char *arg, GString *newmode)
 
252
{
 
253
        int umode = (unsigned char) mode;
 
254
        nick_mode_change(channel, arg, channel->server->modes[umode].prefix,
 
255
                         type, setby);
 
256
 
 
257
        if (g_strcasecmp(channel->server->nick, arg) == 0) {
 
258
                /* see if we need to update channel->chanop */
 
259
                const char *prefix =
 
260
                        g_hash_table_lookup(channel->server->isupport, "PREFIX");
 
261
                if (prefix != NULL && *prefix == '(') {
 
262
                        prefix++;
 
263
                        while (*prefix != ')' && *prefix != '\0') {
 
264
                                if (*prefix == mode) {
 
265
                                        channel->chanop = type == '+';
 
266
                                        break;
 
267
                                }
 
268
                                if (*prefix == 'o')
 
269
                                        break;
 
270
                                prefix++;
 
271
                        }
 
272
                } else {
 
273
                        if (mode == 'o' || mode == 'O')
 
274
                                channel->chanop = type == '+';
 
275
                }
 
276
        }
 
277
}
 
278
 
 
279
int channel_mode_is_set(IRC_CHANNEL_REC *channel, char mode)
 
280
{
 
281
        g_return_val_if_fail(IS_IRC_CHANNEL(channel), FALSE);
 
282
 
 
283
        return channel->mode == NULL ? FALSE :
 
284
                mode_is_set(channel->mode, mode);
 
285
}
 
286
 
 
287
/* Parse channel mode string */
 
288
void parse_channel_modes(IRC_CHANNEL_REC *channel, const char *setby,
 
289
                         const char *mode, int update_key)
 
290
{
 
291
        IRC_SERVER_REC *server = channel->server;
 
292
        GString *newmode;
 
293
        char *dup, *modestr, *arg, *curmode, type, *old_key;
 
294
        int umode;
 
295
 
 
296
        g_return_if_fail(IS_IRC_CHANNEL(channel));
 
297
        g_return_if_fail(mode != NULL);
 
298
 
 
299
        type = '+';
 
300
        newmode = g_string_new(channel->mode);
 
301
        old_key = update_key ? NULL : g_strdup(channel->key);
 
302
 
 
303
        dup = modestr = g_strdup(mode);
 
304
        curmode = cmd_get_param(&modestr);
 
305
        while (*curmode != '\0') {
 
306
                if (HAS_MODE_ARG(server, type, *curmode)) {
 
307
                        /* get the argument for the mode. NOTE: We don't
 
308
                           get the +k's argument when joining to channel. */
 
309
                        arg = cmd_get_param(&modestr);
 
310
                } else {
 
311
                        arg = NULL;
 
312
                }
 
313
 
 
314
                switch (*curmode) {
 
315
                case '+':
 
316
                case '-':
 
317
                        type = *curmode;
 
318
                        break;
 
319
                default:
 
320
                        umode = (unsigned char) *curmode;
 
321
                        if (server->modes[umode].func != NULL) {
 
322
                                server->modes[umode].func(channel, setby,
 
323
                                                          type, *curmode, arg,
 
324
                                                          newmode);
 
325
                        } else {
 
326
                                /* Treat unknown modes as ones without params */
 
327
                                modes_type_d(channel, setby, type, *curmode,
 
328
                                             arg, newmode);
 
329
                        }
 
330
                }
 
331
 
 
332
                curmode++;
 
333
        }
 
334
        g_free(dup);
 
335
 
 
336
        if (channel->key != NULL &&
 
337
            strchr(channel->mode, 'k') == NULL &&
 
338
            strchr(newmode->str, 'k') == NULL) {
 
339
                /* join was used with key but there's no key set
 
340
                   in channel modes.. */
 
341
                g_free(channel->key);
 
342
                channel->key = NULL;
 
343
        } else if (!update_key && old_key != NULL) {
 
344
                /* get the old one back, just in case it was replaced */
 
345
                g_free(channel->key);
 
346
                channel->key = old_key;
 
347
                mode_set_arg(channel->server, newmode, '+', 'k', old_key, FALSE);
 
348
                old_key = NULL;
 
349
        }
 
350
 
 
351
        if (strcmp(newmode->str, channel->mode) != 0) {
 
352
                g_free(channel->mode);
 
353
                channel->mode = g_strdup(newmode->str);
 
354
 
 
355
                signal_emit("channel mode changed", 2, channel, setby);
 
356
        }
 
357
 
 
358
        g_string_free(newmode, TRUE);
 
359
        g_free(old_key);
 
360
}
 
361
 
 
362
/* add `mode' to `old' - return newly allocated mode.
 
363
   `channel' specifies if we're parsing channel mode and we should try
 
364
   to join mode arguments too. */
 
365
char *modes_join(IRC_SERVER_REC *server, const char *old,
 
366
                 const char *mode, int channel)
 
367
{
 
368
        GString *newmode;
 
369
        char *dup, *modestr, *curmode, type;
 
370
 
 
371
        g_return_val_if_fail(mode != NULL, NULL);
 
372
 
 
373
        type = '+';
 
374
        newmode = g_string_new(old);
 
375
 
 
376
        dup = modestr = g_strdup(mode);
 
377
        curmode = cmd_get_param(&modestr);
 
378
        while (*curmode != '\0' && *curmode != ' ') {
 
379
                if (*curmode == '+' || *curmode == '-') {
 
380
                        type = *curmode;
 
381
                        curmode++;
 
382
                        continue;
 
383
                }
 
384
 
 
385
                if (!channel || !HAS_MODE_ARG(server, type, *curmode))
 
386
                        mode_set(server, newmode, type, *curmode, !channel);
 
387
                else {
 
388
                        mode_set_arg(server, newmode, type, *curmode,
 
389
                                     cmd_get_param(&modestr), !channel);
 
390
                }
 
391
 
 
392
                curmode++;
 
393
        }
 
394
        g_free(dup);
 
395
 
 
396
        modestr = newmode->str;
 
397
        g_string_free(newmode, FALSE);
 
398
        return modestr;
 
399
}
 
400
 
 
401
/* Parse user mode string */
 
402
static void parse_user_mode(IRC_SERVER_REC *server, const char *modestr)
 
403
{
 
404
        char *newmode, *oldmode;
 
405
 
 
406
        g_return_if_fail(IS_IRC_SERVER(server));
 
407
        g_return_if_fail(modestr != NULL);
 
408
 
 
409
        newmode = modes_join(NULL, server->usermode, modestr, FALSE);
 
410
        oldmode = server->usermode;
 
411
        server->usermode = newmode;
 
412
        server->server_operator = (strchr(newmode, 'o') != NULL);
 
413
 
 
414
        signal_emit("user mode changed", 2, server, oldmode);
 
415
        g_free_not_null(oldmode);
 
416
}
 
417
 
 
418
static void event_user_mode(IRC_SERVER_REC *server, const char *data)
 
419
{
 
420
        char *params, *nick, *mode;
 
421
 
 
422
        g_return_if_fail(data != NULL);
 
423
 
 
424
        params = event_get_params(data, 3, NULL, &nick, &mode);
 
425
        parse_user_mode(server, mode);
 
426
 
 
427
        g_free(params);
 
428
}
 
429
 
 
430
static void event_mode(IRC_SERVER_REC *server, const char *data,
 
431
                       const char *nick)
 
432
{
 
433
        IRC_CHANNEL_REC *chanrec;
 
434
        char *params, *channel, *mode;
 
435
 
 
436
        g_return_if_fail(data != NULL);
 
437
 
 
438
        params = event_get_params(data, 2 | PARAM_FLAG_GETREST,
 
439
                                  &channel, &mode);
 
440
 
 
441
        if (!ischannel(*channel)) {
 
442
                /* user mode change */
 
443
                parse_user_mode(server, mode);
 
444
        } else {
 
445
                /* channel mode change */
 
446
                chanrec = irc_channel_find(server, channel);
 
447
                if (chanrec != NULL)
 
448
                        parse_channel_modes(chanrec, nick, mode, TRUE);
 
449
        }
 
450
 
 
451
        g_free(params);
 
452
}
 
453
 
 
454
static void event_oper(IRC_SERVER_REC *server, const char *data)
 
455
{
 
456
        const char *opermode;
 
457
 
 
458
        opermode = settings_get_str("opermode");
 
459
        if (*opermode != '\0')
 
460
                irc_send_cmdv(server, "MODE %s %s", server->nick, opermode);
 
461
}
 
462
 
 
463
static void event_away(IRC_SERVER_REC *server, const char *data)
 
464
{
 
465
        g_return_if_fail(server != NULL);
 
466
 
 
467
        server->usermode_away = TRUE;
 
468
        signal_emit("away mode changed", 1, server);
 
469
}
 
470
 
 
471
static void event_unaway(IRC_SERVER_REC *server, const char *data)
 
472
{
 
473
        g_return_if_fail(server != NULL);
 
474
 
 
475
        server->usermode_away = FALSE;
 
476
        g_free_and_null(server->away_reason);
 
477
        signal_emit("away mode changed", 1, server);
 
478
}
 
479
 
 
480
static void sig_req_usermode_change(IRC_SERVER_REC *server, const char *data,
 
481
                                    const char *nick, const char *addr)
 
482
{
 
483
        char *params, *target, *mode;
 
484
 
 
485
        g_return_if_fail(data != NULL);
 
486
 
 
487
        params = event_get_params(data, 2 | PARAM_FLAG_GETREST,
 
488
                                  &target, &mode);
 
489
        if (!ischannel(*target)) {
 
490
                /* we requested a user mode change, save this */
 
491
                mode = modes_join(NULL, server->wanted_usermode, mode, FALSE);
 
492
                g_free_not_null(server->wanted_usermode);
 
493
                server->wanted_usermode = mode;
 
494
        }
 
495
 
 
496
        g_free(params);
 
497
 
 
498
        signal_emit("event mode", 4, server, data, nick, addr);
 
499
}
 
500
 
 
501
void channel_set_singlemode(IRC_CHANNEL_REC *channel, const char *nicks,
 
502
                            const char *mode)
 
503
{
 
504
        GString *str;
 
505
        int num, modepos;
 
506
        char **nick, **nicklist;
 
507
 
 
508
        g_return_if_fail(IS_IRC_CHANNEL(channel));
 
509
        g_return_if_fail(nicks != NULL && mode != NULL);
 
510
        if (*nicks == '\0') return;
 
511
 
 
512
        num = modepos = 0;
 
513
        str = g_string_new(NULL);
 
514
 
 
515
        nicklist = g_strsplit(nicks, " ", -1);
 
516
        for (nick = nicklist; *nick != NULL; nick++) {
 
517
                if (**nick == '\0')
 
518
                        continue;
 
519
 
 
520
                if (num == 0)
 
521
                {
 
522
                        g_string_sprintf(str, "MODE %s %s",
 
523
                                         channel->name, mode);
 
524
                        modepos = str->len;
 
525
                } else {
 
526
                        /* insert the mode string */
 
527
                        g_string_insert(str, modepos, mode);
 
528
                }
 
529
 
 
530
                g_string_sprintfa(str, " %s", *nick);
 
531
 
 
532
                if (++num == channel->server->max_modes_in_cmd) {
 
533
                        /* max. modes / command reached, send to server */
 
534
                        irc_send_cmd(channel->server, str->str);
 
535
                        num = 0;
 
536
                }
 
537
        }
 
538
        if (num > 0) irc_send_cmd(channel->server, str->str);
 
539
 
 
540
        g_strfreev(nicklist);
 
541
        g_string_free(str, TRUE);
 
542
}
 
543
 
 
544
void channel_set_mode(IRC_SERVER_REC *server, const char *channel,
 
545
                      const char *mode)
 
546
{
 
547
        IRC_CHANNEL_REC *chanrec;
 
548
        GString *tmode, *targs;
 
549
        char *modestr, *curmode, *orig, type, prevtype;
 
550
        int count;
 
551
 
 
552
        g_return_if_fail(IS_IRC_SERVER(server));
 
553
        g_return_if_fail(channel != NULL && mode != NULL);
 
554
 
 
555
        tmode = g_string_new(NULL);
 
556
        targs = g_string_new(NULL);
 
557
        count = 0;
 
558
 
 
559
        chanrec = irc_channel_find(server, channel);
 
560
        if (chanrec != NULL)
 
561
                channel = chanrec->name;
 
562
 
 
563
        orig = modestr = g_strdup(mode);
 
564
 
 
565
        type = '+'; prevtype = '\0';
 
566
        curmode = cmd_get_param(&modestr);
 
567
        for (;; curmode++) {
 
568
                if (*curmode == '\0') {
 
569
                        /* support for +o nick +o nick2 */
 
570
                        curmode = cmd_get_param(&modestr);
 
571
                        if (*curmode == '\0')
 
572
                                break;
 
573
                }
 
574
 
 
575
                if (*curmode == '+' || *curmode == '-') {
 
576
                        type = *curmode;
 
577
                        continue;
 
578
                }
 
579
 
 
580
                if (count == server->max_modes_in_cmd &&
 
581
                    HAS_MODE_ARG(server, type, *curmode)) {
 
582
                        irc_send_cmdv(server, "MODE %s %s%s",
 
583
                                      channel, tmode->str, targs->str);
 
584
 
 
585
                        count = 0; prevtype = '\0';
 
586
                        g_string_truncate(tmode, 0);
 
587
                        g_string_truncate(targs, 0);
 
588
                }
 
589
 
 
590
                if (type != prevtype) {
 
591
                        prevtype = type;
 
592
                        g_string_append_c(tmode, type);
 
593
                }
 
594
                g_string_append_c(tmode, *curmode);
 
595
 
 
596
                if (HAS_MODE_ARG(server, type, *curmode)) {
 
597
                        char *arg;
 
598
 
 
599
                        count++;
 
600
                        arg = cmd_get_param(&modestr);
 
601
                        if (*arg == '\0' && type == '-' && *curmode == 'k') {
 
602
                                /* "/mode #channel -k" - no reason why it
 
603
                                   shouldn't work really, so append the key */
 
604
                                IRC_CHANNEL_REC *chanrec;
 
605
 
 
606
                                chanrec = irc_channel_find(server, channel);
 
607
                                if (chanrec != NULL && chanrec->key != NULL)
 
608
                                        arg = chanrec->key;
 
609
                        }
 
610
 
 
611
                        if (*arg != '\0')
 
612
                                g_string_sprintfa(targs, " %s", arg);
 
613
                }
 
614
        }
 
615
 
 
616
        if (tmode->len > 0) {
 
617
                irc_send_cmdv(server, "MODE %s %s%s",
 
618
                              channel, tmode->str, targs->str);
 
619
        }
 
620
 
 
621
        g_string_free(tmode, TRUE);
 
622
        g_string_free(targs, TRUE);
 
623
        g_free(orig);
 
624
}
 
625
 
 
626
static int get_wildcard_nicks(GString *output, const char *mask,
 
627
                              IRC_CHANNEL_REC *channel, int op, int voice)
 
628
{
 
629
        GSList *nicks, *tmp;
 
630
        int count;
 
631
 
 
632
        g_return_val_if_fail(output != NULL, 0);
 
633
        g_return_val_if_fail(mask != NULL, 0);
 
634
        g_return_val_if_fail(IS_IRC_CHANNEL(channel), 0);
 
635
 
 
636
        count = 0;
 
637
        nicks = nicklist_find_multiple(CHANNEL(channel), mask);
 
638
        for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
 
639
                NICK_REC *rec = tmp->data;
 
640
 
 
641
                if ((op == 1 && !rec->op) || (op == 0 && rec->op) ||
 
642
                    (voice == 1 && !rec->voice) || (voice == 0 && rec->voice))
 
643
                        continue;
 
644
 
 
645
                if (g_strcasecmp(rec->nick, channel->server->nick) == 0)
 
646
                        continue;
 
647
 
 
648
                g_string_sprintfa(output, "%s ", rec->nick);
 
649
                count++;
 
650
        }
 
651
        g_slist_free(nicks);
 
652
 
 
653
        return count;
 
654
}
 
655
 
 
656
static char *get_nicks(IRC_SERVER_REC *server, WI_ITEM_REC *item,
 
657
                       const char *data, int op, int voice,
 
658
                       IRC_CHANNEL_REC **ret_channel)
 
659
{
 
660
        IRC_CHANNEL_REC *channel;
 
661
        GString *str;
 
662
        GHashTable *optlist;
 
663
        char **matches, **match, *ret, *channame, *nicks;
 
664
        void *free_arg;
 
665
        int count, max_modes;
 
666
 
 
667
        if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST |
 
668
                            PARAM_FLAG_OPTIONS | PARAM_FLAG_OPTCHAN_NAME,
 
669
                            item, "op", &optlist, &channame, &nicks))
 
670
                return NULL;
 
671
 
 
672
        if (*nicks == '\0')
 
673
                return NULL;
 
674
 
 
675
        channel = irc_channel_find(server, channame);
 
676
        if (channel == NULL) {
 
677
                cmd_params_free(free_arg);
 
678
                return NULL;
 
679
        }
 
680
 
 
681
        str = g_string_new(NULL);
 
682
        matches = g_strsplit(nicks, " ", -1);
 
683
        for (match = matches; *match != NULL; match++) {
 
684
                if (strchr(*match, '*') == NULL &&
 
685
                    strchr(*match, '?') == NULL) {
 
686
                        /* no wildcards */
 
687
                        g_string_sprintfa(str, "%s ", *match);
 
688
                } else {
 
689
                        count = get_wildcard_nicks(str, *match, channel,
 
690
                                                   op, voice);
 
691
                        max_modes = settings_get_int("max_wildcard_modes");
 
692
                        if (max_modes > 0 && count > max_modes &&
 
693
                            g_hash_table_lookup(optlist, "yes") == NULL) {
 
694
                                /* too many matches */
 
695
                                g_string_free(str, TRUE);
 
696
                                cmd_params_free(free_arg);
 
697
 
 
698
                                signal_emit("error command", 1,
 
699
                                            GINT_TO_POINTER(CMDERR_NOT_GOOD_IDEA));
 
700
                                signal_stop();
 
701
                                return NULL;
 
702
                        }
 
703
                }
 
704
        }
 
705
 
 
706
        if (str->len > 0) g_string_truncate(str, str->len-1);
 
707
        ret = str->str;
 
708
        g_string_free(str, FALSE);
 
709
 
 
710
        cmd_params_free(free_arg);
 
711
 
 
712
        *ret_channel = channel;
 
713
        return ret;
 
714
}
 
715
 
 
716
/* SYNTAX: OP <nicks> */
 
717
static void cmd_op(const char *data, IRC_SERVER_REC *server,
 
718
                   WI_ITEM_REC *item)
 
719
{
 
720
        IRC_CHANNEL_REC *channel;
 
721
        char *nicks;
 
722
 
 
723
        CMD_IRC_SERVER(server);
 
724
 
 
725
        nicks = get_nicks(server, item, data, 0, -1, &channel);
 
726
        if (nicks != NULL && *nicks != '\0')
 
727
                channel_set_singlemode(channel, nicks, "+o");
 
728
        g_free_not_null(nicks);
 
729
}
 
730
 
 
731
/* SYNTAX: DEOP <nicks> */
 
732
static void cmd_deop(const char *data, IRC_SERVER_REC *server,
 
733
                     WI_ITEM_REC *item)
 
734
{
 
735
        IRC_CHANNEL_REC *channel;
 
736
        char *nicks;
 
737
 
 
738
        CMD_IRC_SERVER(server);
 
739
 
 
740
        nicks = get_nicks(server, item, data, 1, -1, &channel);
 
741
        if (nicks != NULL && *nicks != '\0')
 
742
                channel_set_singlemode(channel, nicks, "-o");
 
743
        g_free_not_null(nicks);
 
744
}
 
745
 
 
746
/* SYNTAX: VOICE <nicks> */
 
747
static void cmd_voice(const char *data, IRC_SERVER_REC *server,
 
748
                      WI_ITEM_REC *item)
 
749
{
 
750
        IRC_CHANNEL_REC *channel;
 
751
        char *nicks;
 
752
 
 
753
        CMD_IRC_SERVER(server);
 
754
 
 
755
        nicks = get_nicks(server, item, data, 0, 0, &channel);
 
756
        if (nicks != NULL && *nicks != '\0')
 
757
                channel_set_singlemode(channel, nicks, "+v");
 
758
        g_free_not_null(nicks);
 
759
}
 
760
 
 
761
/* SYNTAX: DEVOICE <nicks> */
 
762
static void cmd_devoice(const char *data, IRC_SERVER_REC *server,
 
763
                        WI_ITEM_REC *item)
 
764
{
 
765
        IRC_CHANNEL_REC *channel;
 
766
        char *nicks;
 
767
 
 
768
        CMD_IRC_SERVER(server);
 
769
 
 
770
        nicks = get_nicks(server, item, data, -1, 1, &channel);
 
771
        if (nicks != NULL && *nicks != '\0')
 
772
                channel_set_singlemode(channel, nicks, "-v");
 
773
        g_free_not_null(nicks);
 
774
}
 
775
 
 
776
/* SYNTAX: MODE <your nick>|<channel> [<mode> [<mode parameters>]] */
 
777
static void cmd_mode(const char *data, IRC_SERVER_REC *server,
 
778
                     IRC_CHANNEL_REC *channel)
 
779
{
 
780
        IRC_CHANNEL_REC *chanrec;
 
781
        char *target, *mode;
 
782
        void *free_arg;
 
783
 
 
784
        CMD_IRC_SERVER(server);
 
785
 
 
786
        if (*data == '+' || *data == '-') {
 
787
                target = "*";
 
788
                if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_GETREST, &mode))
 
789
                        return;
 
790
        } else {
 
791
                if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &target, &mode))
 
792
                        return;
 
793
        }
 
794
 
 
795
        if (strcmp(target, "*") == 0) {
 
796
                if (!IS_IRC_CHANNEL(channel))
 
797
                        cmd_param_error(CMDERR_NOT_JOINED);
 
798
 
 
799
                target = channel->name;
 
800
        }
 
801
        if (*target == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
 
802
 
 
803
        if (*mode == '\0') {
 
804
                chanrec = irc_channel_find(server, target);
 
805
                if (chanrec != NULL)
 
806
                        target = chanrec->name;
 
807
 
 
808
                irc_send_cmdv(server, "MODE %s", target);
 
809
        } else if (ischannel(*target))
 
810
                channel_set_mode(server, target, mode);
 
811
        else {
 
812
                if (g_strcasecmp(target, server->nick) == 0) {
 
813
                        server_redirect_event(server, "mode user", 1, target, -1, NULL,
 
814
                                              "event mode", "requested usermode change", NULL);
 
815
                }
 
816
 
 
817
                irc_send_cmdv(server, "MODE %s %s", target, mode);
 
818
        }
 
819
 
 
820
        cmd_params_free(free_arg);
 
821
}
 
822
 
 
823
void modes_server_init(IRC_SERVER_REC *server)
 
824
{
 
825
        server->modes['b'].func = modes_type_a;
 
826
        server->modes['e'].func = modes_type_a;
 
827
        server->modes['I'].func = modes_type_a;
 
828
 
 
829
        server->modes['h'].func = modes_type_prefix;
 
830
        server->modes['h'].prefix = '%';
 
831
        server->modes['o'].func = modes_type_prefix;
 
832
        server->modes['o'].prefix = '@';
 
833
        server->modes['O'].func = modes_type_prefix;
 
834
        server->modes['O'].prefix = '@';
 
835
        server->modes['v'].func = modes_type_prefix;
 
836
        server->modes['v'].prefix = '+';
 
837
 
 
838
        server->prefix['%'] = 'h';
 
839
        server->prefix['@'] = 'o';
 
840
        server->prefix['+'] = 'v';
 
841
 
 
842
        server->modes['k'].func = modes_type_b;
 
843
        server->modes['l'].func = modes_type_c;
 
844
}
 
845
 
 
846
void modes_init(void)
 
847
{
 
848
        settings_add_str("misc", "opermode", "");
 
849
        settings_add_int("misc", "max_wildcard_modes", 6);
 
850
 
 
851
        signal_add("event 221", (SIGNAL_FUNC) event_user_mode);
 
852
        signal_add("event 305", (SIGNAL_FUNC) event_unaway);
 
853
        signal_add("event 306", (SIGNAL_FUNC) event_away);
 
854
        signal_add("event 381", (SIGNAL_FUNC) event_oper);
 
855
        signal_add("event mode", (SIGNAL_FUNC) event_mode);
 
856
        signal_add("requested usermode change", (SIGNAL_FUNC) sig_req_usermode_change);
 
857
 
 
858
        command_bind_irc("op", NULL, (SIGNAL_FUNC) cmd_op);
 
859
        command_bind_irc("deop", NULL, (SIGNAL_FUNC) cmd_deop);
 
860
        command_bind_irc("voice", NULL, (SIGNAL_FUNC) cmd_voice);
 
861
        command_bind_irc("devoice", NULL, (SIGNAL_FUNC) cmd_devoice);
 
862
        command_bind_irc("mode", NULL, (SIGNAL_FUNC) cmd_mode);
 
863
 
 
864
        command_set_options("op", "yes");
 
865
}
 
866
 
 
867
void modes_deinit(void)
 
868
{
 
869
        signal_remove("event 221", (SIGNAL_FUNC) event_user_mode);
 
870
        signal_remove("event 305", (SIGNAL_FUNC) event_unaway);
 
871
        signal_remove("event 306", (SIGNAL_FUNC) event_away);
 
872
        signal_remove("event 381", (SIGNAL_FUNC) event_oper);
 
873
        signal_remove("event mode", (SIGNAL_FUNC) event_mode);
 
874
        signal_remove("requested usermode change", (SIGNAL_FUNC) sig_req_usermode_change);
 
875
 
 
876
        command_unbind("op", (SIGNAL_FUNC) cmd_op);
 
877
        command_unbind("deop", (SIGNAL_FUNC) cmd_deop);
 
878
        command_unbind("voice", (SIGNAL_FUNC) cmd_voice);
 
879
        command_unbind("devoice", (SIGNAL_FUNC) cmd_devoice);
 
880
        command_unbind("mode", (SIGNAL_FUNC) cmd_mode);
 
881
}