ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/vendor/ircservices-5.1.24/channels.c
Revision: 1171
Committed: Fri Aug 12 20:00:46 2011 UTC (12 years, 7 months ago) by michael
Content type: text/x-csrc
File size: 14707 byte(s)
Log Message:
- Import ircservices-5.1.24. Don't ever think about modifying anything in this
  folder!
  Since Andrew Church has discontinued his services project in April 2011, the
  ircd-hybrid team has been given permissions to officially continue and
  maintain the already mentioned project.
  The name of this project will be changed for the reason being that the current
  name "IRC Services" is way too generic these days.

  Remember: Don't ever modify anything in here. This folder is kept for reference.

File Contents

# Content
1 /* Channel-handling routines.
2 *
3 * IRC Services is copyright (c) 1996-2009 Andrew Church.
4 * E-mail: <achurch@achurch.org>
5 * Parts written by Andrew Kempe and others.
6 * This program is free but copyrighted software; see the file GPL.txt for
7 * details.
8 */
9
10 #include "services.h"
11 #include "modules.h"
12
13 /*************************************************************************/
14
15 #define HASHFUNC(key) DEFAULT_HASHFUNC(key+1)
16 #define add_channel static add_channel
17 #define del_channel static del_channel
18 #include "hash.h"
19 DEFINE_HASH(channel, Channel, name)
20 #undef add_channel
21 #undef del_channel
22
23 static int cb_create = -1;
24 static int cb_delete = -1;
25 static int cb_join = -1;
26 static int cb_join_check = -1;
27 static int cb_mode = -1;
28 static int cb_mode_change = -1;
29 static int cb_umode_change = -1;
30 static int cb_topic = -1;
31
32 /*************************************************************************/
33 /*************************************************************************/
34
35 int channel_init(int ac, char **av)
36 {
37 cb_create = register_callback("channel create");
38 cb_delete = register_callback("channel delete");
39 cb_join = register_callback("channel JOIN");
40 cb_join_check = register_callback("channel JOIN check");
41 cb_mode = register_callback("channel MODE");
42 cb_mode_change = register_callback("channel mode change");
43 cb_umode_change = register_callback("channel umode change");
44 cb_topic = register_callback("channel TOPIC");
45 if (cb_create < 0 || cb_delete < 0 || cb_join < 0 || cb_join_check < 0
46 || cb_mode < 0 || cb_mode_change < 0 || cb_umode_change < 0
47 || cb_topic < 0
48 ) {
49 log("channel_init: register_callback() failed\n");
50 return 0;
51 }
52 return 1;
53 }
54
55 /*************************************************************************/
56
57 void channel_cleanup(void)
58 {
59 Channel *c;
60
61 for (c = first_channel(); c; c = next_channel())
62 del_channel(c);
63 unregister_callback(cb_topic);
64 unregister_callback(cb_umode_change);
65 unregister_callback(cb_mode_change);
66 unregister_callback(cb_mode);
67 unregister_callback(cb_join_check);
68 unregister_callback(cb_join);
69 unregister_callback(cb_delete);
70 unregister_callback(cb_create);
71 }
72
73 /*************************************************************************/
74
75 /* Return statistics. Pointers are assumed to be valid. */
76
77 void get_channel_stats(long *nrec, long *memuse)
78 {
79 long count = 0, mem = 0;
80 Channel *chan;
81 struct c_userlist *cu;
82 int i;
83
84 for (chan = first_channel(); chan; chan = next_channel()) {
85 count++;
86 mem += sizeof(*chan);
87 if (chan->topic)
88 mem += strlen(chan->topic)+1;
89 if (chan->key)
90 mem += strlen(chan->key)+1;
91 ARRAY_FOREACH (i, chan->bans) {
92 mem += sizeof(char *);
93 if (chan->bans[i])
94 mem += strlen(chan->bans[i])+1;
95 }
96 ARRAY_FOREACH (i, chan->excepts) {
97 mem += sizeof(char *);
98 if (chan->excepts[i])
99 mem += strlen(chan->excepts[i])+1;
100 }
101 LIST_FOREACH (cu, chan->users)
102 mem += sizeof(*cu);
103 }
104 *nrec = count;
105 *memuse = mem;
106 }
107
108 /*************************************************************************/
109 /*************************************************************************/
110
111 /* Add/remove a user to/from a channel, creating or deleting the channel as
112 * necessary. If creating the channel, restore mode lock and topic as
113 * necessary. Also check for auto-opping and auto-voicing. If a mode is
114 * given, it is assumed to have been set by the remote server.
115 * Returns the Channel structure for the given channel, or NULL if the user
116 * was refused access to the channel (by the join check callback).
117 */
118
119 Channel *chan_adduser(User *user, const char *chan, int32 modes)
120 {
121 Channel *c = get_channel(chan);
122 int newchan = !c;
123 struct c_userlist *u;
124
125 if (call_callback_2(cb_join_check, chan, user) > 0)
126 return NULL;
127 if (newchan) {
128 log_debug(1, "Creating channel %s", chan);
129 /* Allocate pre-cleared memory */
130 c = scalloc(sizeof(Channel), 1);
131 strbcpy(c->name, chan);
132 c->creation_time = time(NULL);
133 add_channel(c);
134 call_callback_3(cb_create, c, user, modes);
135 }
136 u = smalloc(sizeof(struct c_userlist));
137 LIST_INSERT(u, c->users);
138 u->user = user;
139 u->mode = modes;
140 u->flags = 0;
141 call_callback_2(cb_join, c, u);
142 return c;
143 }
144
145
146 void chan_deluser(User *user, Channel *c)
147 {
148 struct c_userlist *u;
149 int i;
150
151 LIST_SEARCH_SCALAR(c->users, user, user, u);
152 if (!u) {
153 log("channel: BUG: chan_deluser() called for %s in %s but they "
154 "were not found on the channel's userlist.",
155 user->nick, c->name);
156 return;
157 }
158 LIST_REMOVE(u, c->users);
159 free(u);
160
161 if (!c->users) {
162 log_debug(1, "Deleting channel %s", c->name);
163 call_callback_1(cb_delete, c);
164 set_cmode(NULL, c); /* make sure nothing's left buffered */
165 free(c->topic);
166 free(c->key);
167 free(c->link);
168 free(c->flood);
169 for (i = 0; i < c->bans_count; i++)
170 free(c->bans[i]);
171 free(c->bans);
172 for (i = 0; i < c->excepts_count; i++)
173 free(c->excepts[i]);
174 free(c->excepts);
175 del_channel(c);
176 free(c);
177 }
178 }
179
180 /*************************************************************************/
181
182 /* Search for the given ban on the given channel, and return the index into
183 * chan->bans[] if found, -1 otherwise. Nicknames are compared using
184 * irc_stricmp(), usernames and hostnames using stricmp().
185 */
186 static int find_ban(const Channel *chan, const char *ban)
187 {
188 char *s, *t;
189 int i;
190
191 t = strchr(ban, '!');
192 i = 0;
193 ARRAY_FOREACH (i, chan->bans) {
194 s = strchr(chan->bans[i], '!');
195 if (s && t) {
196 if (s-(chan->bans[i]) == t-ban
197 && irc_strnicmp(chan->bans[i], ban, s-(chan->bans[i])) == 0
198 && stricmp(s+1, t+1) == 0
199 ) {
200 return i;
201 }
202 } else if (!s && !t) {
203 /* Bans without '!' should be impossible; just
204 * do a case-insensitive compare */
205 if (stricmp(chan->bans[i], ban) == 0)
206 return i;
207 }
208 }
209 return -1;
210 }
211
212 /*************************************************************************/
213
214 /* Search for the given ban (case-insensitive) on the channel; return 1 if
215 * it exists, 0 if not.
216 */
217
218 int chan_has_ban(const char *chan, const char *ban)
219 {
220 Channel *c = get_channel(chan);
221 if (!c)
222 return 0;
223 return find_ban(c, ban) >= 0;
224 }
225
226 /*************************************************************************/
227
228 /* Handle a channel MODE command.
229 * When called internally to modify channel modes, callers may assume that
230 * the contents of the argument strings will not be modified.
231 */
232
233 /* Hack to allow -o+v to work without having to search the whole channel
234 * user list for changed modes. */
235 #define MAX_CUMODES 16
236 static struct {
237 struct c_userlist *user;
238 int32 add, remove;
239 } cumode_changes[MAX_CUMODES];
240 static int cumode_count = 0;
241
242 static void do_cumode(const char *source, Channel *chan, int32 flag, int add,
243 const char *nick);
244 static void finish_cumode(const char *source, Channel *chan);
245
246 void do_cmode(const char *source, int ac, char **av)
247 {
248 Channel *chan;
249 char *s;
250 int add = 1; /* 1 if adding modes, 0 if deleting */
251 char *modestr;
252
253 chan = get_channel(av[0]);
254 if (!chan) {
255 log_debug(1, "channel: MODE %s for nonexistent channel %s",
256 merge_args(ac-1, av+1), av[0]);
257 return;
258 }
259 if ((protocol_features & PF_MODETS_FIRST) && isdigit(av[1][0])) {
260 ac--;
261 av++;
262 }
263 modestr = av[1];
264
265 if (!NoBouncyModes) {
266 /* Count identical server mode changes per second (mode bounce check)*/
267 /* Doesn't trigger on +/-[bov] or other multiply-settable modes */
268 static char multimodes[BUFSIZE];
269 if (!*multimodes) {
270 int i = 0;
271 i = snprintf(multimodes, sizeof(multimodes), "%s",
272 chanmode_multiple);
273 snprintf(multimodes+i, sizeof(multimodes)-i, "%s",
274 mode_flags_to_string(MODE_ALL,MODE_CHANUSER));
275 }
276 if (strchr(source, '.') && strcmp(source, ServerName) != 0
277 && !modestr[strcspn(modestr, multimodes)]
278 ) {
279 static char lastmodes[BUFSIZE];
280 if (time(NULL) != chan->server_modetime
281 || strcmp(modestr, lastmodes) != 0
282 ) {
283 chan->server_modecount = 0;
284 chan->server_modetime = time(NULL);
285 strbcpy(lastmodes, modestr);
286 }
287 chan->server_modecount++;
288 }
289 }
290
291 s = modestr;
292 ac -= 2;
293 av += 2;
294 cumode_count = 0;
295
296 while (*s) {
297 char modechar = *s++;
298 int32 flag;
299 int params;
300
301 if (modechar == '+') {
302 add = 1;
303 continue;
304 } else if (modechar == '-') {
305 add = 0;
306 continue;
307 } else if (add < 0) {
308 continue;
309 }
310
311 /* Check for it as a channel user mode */
312
313 flag = mode_char_to_flag(modechar, MODE_CHANUSER);
314 if (flag) {
315 if (--ac < 0) {
316 log("channel: MODE %s %s: missing parameter for %c%c",
317 chan->name, modestr, add ? '+' : '-', modechar);
318 break;
319 }
320 do_cumode(source, chan, flag, add, *av++);
321 continue;
322 }
323
324 /* Nope, must be a regular channel mode */
325
326 flag = mode_char_to_flag(modechar, MODE_CHANNEL);
327 if (!flag)
328 continue;
329 if (flag == MODE_INVALID)
330 flag = 0;
331 params = mode_char_to_params(modechar, MODE_CHANNEL);
332 params = (params >> (add*8)) & 0xFF;
333 if (ac < params) {
334 log("channel: MODE %s %s: missing parameter(s) for %c%c",
335 chan->name, modestr, add ? '+' : '-', modechar);
336 break;
337 }
338
339 if (call_callback_5(cb_mode, source, chan, modechar, add, av) <= 0) {
340
341 if (add)
342 chan->mode |= flag;
343 else
344 chan->mode &= ~flag;
345
346 switch (modechar) {
347 case 'k':
348 free(chan->key);
349 if (add)
350 chan->key = sstrdup(av[0]);
351 else
352 chan->key = NULL;
353 break;
354
355 case 'l':
356 if (add)
357 chan->limit = atoi(av[0]);
358 else
359 chan->limit = 0;
360 break;
361
362 case 'b': {
363 int i = find_ban(chan, av[0]);
364 if (add) {
365 if (i < 0) { /* Don't add if it already exists */
366 ARRAY_EXTEND(chan->bans);
367 chan->bans[chan->bans_count-1] = sstrdup(av[0]);
368 }
369 } else {
370 if (i >= 0) {
371 free(chan->bans[i]);
372 ARRAY_REMOVE(chan->bans, i);
373 } else {
374 log("channel: MODE %s -b %s: ban not found",
375 chan->name, *av);
376 }
377 }
378 break;
379 } /* case 'b' */
380
381 } /* switch (modechar) */
382
383 } /* if (callback() <= 0) */
384
385 ac -= params;
386 av += params;
387
388 } /* while (*s) */
389
390 call_callback_2(cb_mode_change, source, chan);
391 finish_cumode(source, chan);
392 }
393
394 /* Modify a user's CUMODE. */
395 static void do_cumode(const char *source, Channel *chan, int32 flag, int add,
396 const char *nick)
397 {
398 struct c_userlist *u;
399 User *user;
400 int i;
401
402 user = get_user(nick);
403 if (!user) {
404 log_debug(1, "channel: MODE %s %c%c for nonexistent user %s",
405 chan->name, add ? '+' : '-',
406 mode_flag_to_char(flag, MODE_CHANUSER), nick);
407 return;
408 }
409 LIST_SEARCH_SCALAR(chan->users, user, user, u);
410 if (!u) {
411 log("channel: MODE %s %c%c for user %s not on channel",
412 chan->name, add ? '+' : '-',
413 mode_flag_to_char(flag, MODE_CHANUSER), nick);
414 return;
415 }
416
417 if (flag == MODE_INVALID)
418 return;
419 for (i = 0; i < cumode_count; i++) {
420 if (cumode_changes[i].user == u)
421 break;
422 }
423 if (i >= MAX_CUMODES) {
424 finish_cumode(source, chan);
425 i = cumode_count = 0;
426 }
427 cumode_changes[i].user = u;
428 if (i >= cumode_count) {
429 cumode_changes[i].add = cumode_changes[i].remove = 0;
430 }
431 if (add) {
432 cumode_changes[i].add |= flag;
433 cumode_changes[i].remove &= ~flag;
434 } else {
435 cumode_changes[i].remove |= flag;
436 cumode_changes[i].add &= ~flag;
437 }
438 if (i >= cumode_count)
439 cumode_count = i+1;
440 }
441
442 static void finish_cumode(const char *source, Channel *chan)
443 {
444 int i;
445
446 for (i = 0; i < cumode_count; i++) {
447 struct c_userlist *u = cumode_changes[i].user;
448 int32 oldmode = u->mode;
449 u->mode |= cumode_changes[i].add;
450 u->mode &= ~cumode_changes[i].remove;
451 if (u->mode != oldmode)
452 call_callback_4(cb_umode_change, source, chan, u, oldmode);
453 }
454 cumode_count = 0;
455 }
456
457 /*************************************************************************/
458
459 /* Handle a TOPIC command. */
460
461 void do_topic(const char *source, int ac, char **av)
462 {
463 Channel *c = get_channel(av[0]);
464 const char *topic;
465 char *s;
466
467 if (!c) {
468 log_debug(1, "channel: TOPIC %s for nonexistent channel %s",
469 merge_args(ac-1, av+1), av[0]);
470 return;
471 }
472 s = strchr(av[1], '!');
473 if (s)
474 *s = 0;
475 if (ac > 3)
476 topic = av[3];
477 else
478 topic = "";
479 if (call_callback_4(cb_topic, c, topic, av[1], strtotime(av[2],NULL)) > 0)
480 return;
481 strbcpy(c->topic_setter, av[1]);
482 if (c->topic) {
483 free(c->topic);
484 c->topic = NULL;
485 }
486 if (ac > 3 && *av[3])
487 c->topic = sstrdup(av[3]);
488 }
489
490 /*************************************************************************/
491
492 /*
493 * Local variables:
494 * c-file-style: "stroustrup"
495 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
496 * indent-tabs-mode: nil
497 * End:
498 *
499 * vim: expandtab shiftwidth=4:
500 */