1 |
/* Routines to maintain a list of online users. |
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 |
/* Maximum number of tries to randomly select a new guest nick when the |
16 |
* first one chosen is in use before giving up. |
17 |
*/ |
18 |
#define MAKEGUESTNICK_TRIES 1000 |
19 |
|
20 |
/*************************************************************************/ |
21 |
|
22 |
#define add_user static add_user |
23 |
#define del_user static del_user |
24 |
#include "hash.h" |
25 |
DEFINE_HASH(user, User, nick) |
26 |
#undef add_user |
27 |
#undef del_user |
28 |
int32 usercnt = 0, opcnt = 0; |
29 |
|
30 |
static int cb_check = -1; |
31 |
static int cb_create = -1; |
32 |
static int cb_servicestamp_change = -1; |
33 |
static int cb_nickchange1 = -1; |
34 |
static int cb_nickchange2 = -1; |
35 |
static int cb_delete = -1; |
36 |
static int cb_mode = -1; |
37 |
static int cb_chan_part = -1; |
38 |
static int cb_chan_kick = -1; |
39 |
|
40 |
/*************************************************************************/ |
41 |
|
42 |
int |
43 |
user_init(int ac, char **av) |
44 |
{ |
45 |
cb_check = register_callback("user check"); |
46 |
cb_create = register_callback("user create"); |
47 |
cb_servicestamp_change = register_callback("user servicestamp change"); |
48 |
cb_nickchange1 = register_callback("user nickchange (before)"); |
49 |
cb_nickchange2 = register_callback("user nickchange (after)"); |
50 |
cb_delete = register_callback("user delete"); |
51 |
cb_mode = register_callback("user MODE"); |
52 |
cb_chan_part = register_callback("channel PART"); |
53 |
cb_chan_kick = register_callback("channel KICK"); |
54 |
|
55 |
if (cb_check < 0 || cb_create < 0 || cb_servicestamp_change < 0 || |
56 |
cb_nickchange1 < 0 || cb_nickchange2 < 0 || cb_delete < 0 || |
57 |
cb_mode < 0 || cb_chan_part < 0 || cb_chan_kick < 0) |
58 |
{ |
59 |
log("user_init: register_callback() failed\n"); |
60 |
return 0; |
61 |
} |
62 |
|
63 |
return 1; |
64 |
} |
65 |
|
66 |
/*************************************************************************/ |
67 |
|
68 |
void |
69 |
user_cleanup(void) |
70 |
{ |
71 |
User *u; |
72 |
|
73 |
for (u = first_user(); u; u = next_user()) |
74 |
del_user(u); |
75 |
|
76 |
unregister_callback(cb_chan_kick); |
77 |
unregister_callback(cb_chan_part); |
78 |
unregister_callback(cb_mode); |
79 |
unregister_callback(cb_delete); |
80 |
unregister_callback(cb_nickchange2); |
81 |
unregister_callback(cb_nickchange1); |
82 |
unregister_callback(cb_servicestamp_change); |
83 |
unregister_callback(cb_create); |
84 |
unregister_callback(cb_check); |
85 |
} |
86 |
|
87 |
/*************************************************************************/ |
88 |
/************************* User list management **************************/ |
89 |
/*************************************************************************/ |
90 |
|
91 |
/* Allocate a new User structure, fill in basic values, link it to the |
92 |
* overall list, and return it. Always successful. |
93 |
*/ |
94 |
|
95 |
static User * |
96 |
new_user(const char *nick) |
97 |
{ |
98 |
User *user = scalloc(sizeof(User), 1); |
99 |
|
100 |
if (!nick) |
101 |
nick = ""; |
102 |
|
103 |
strbcpy(user->nick, nick); |
104 |
add_user(user); |
105 |
usercnt++; |
106 |
|
107 |
return user; |
108 |
} |
109 |
|
110 |
/*************************************************************************/ |
111 |
|
112 |
/* Change the nickname of a user, and move pointers as necessary. */ |
113 |
|
114 |
static void |
115 |
change_user_nick(User *user, const char *nick) |
116 |
{ |
117 |
del_user(user); |
118 |
strbcpy(user->nick, nick); |
119 |
add_user(user); |
120 |
} |
121 |
|
122 |
/*************************************************************************/ |
123 |
|
124 |
/* Remove and free a User structure. */ |
125 |
|
126 |
static void |
127 |
delete_user(User *user) |
128 |
{ |
129 |
struct u_chanlist *c, *c2; |
130 |
struct u_chaninfolist *ci, *ci2; |
131 |
|
132 |
usercnt--; |
133 |
|
134 |
if (is_oper(user)) |
135 |
opcnt--; |
136 |
|
137 |
free(user->username); |
138 |
free(user->host); |
139 |
free(user->ipaddr); |
140 |
free(user->realname); |
141 |
free(user->fakehost); |
142 |
free(user->id_nicks); |
143 |
|
144 |
LIST_FOREACH_SAFE(c, user->chans, c2) |
145 |
{ |
146 |
chan_deluser(user, c->chan); |
147 |
free(c); |
148 |
} |
149 |
|
150 |
LIST_FOREACH_SAFE(ci, user->id_chans, ci2) |
151 |
free(ci); |
152 |
#define next snext |
153 |
#define prev sprev |
154 |
if (user->server) |
155 |
LIST_REMOVE(user, user->server->userlist); |
156 |
#undef next |
157 |
#undef prev |
158 |
del_user(user); |
159 |
free(user); |
160 |
} |
161 |
|
162 |
/*************************************************************************/ |
163 |
/*************************************************************************/ |
164 |
|
165 |
/* Remove a user on QUIT/KILL. Calls the user delete callback and then |
166 |
* deletes the User structure. |
167 |
*/ |
168 |
|
169 |
void |
170 |
quit_user(User *user, const char *quitmsg, int is_kill) |
171 |
{ |
172 |
call_callback_3(cb_delete, user, quitmsg, is_kill); |
173 |
delete_user(user); |
174 |
} |
175 |
|
176 |
/*************************************************************************/ |
177 |
|
178 |
/* Return statistics. Pointers are assumed to be valid. */ |
179 |
|
180 |
void |
181 |
get_user_stats(long *nusers, long *memuse) |
182 |
{ |
183 |
long count = 0, mem = 0; |
184 |
User *user; |
185 |
struct u_chanlist *uc; |
186 |
struct u_chaninfolist *uci; |
187 |
|
188 |
for (user = first_user(); user; user = next_user()) |
189 |
{ |
190 |
count++; |
191 |
mem += sizeof(*user); |
192 |
|
193 |
if (user->username) |
194 |
mem += strlen(user->username) + 1; |
195 |
if (user->host) |
196 |
mem += strlen(user->host) + 1; |
197 |
if (user->realname) |
198 |
mem += strlen(user->realname) + 1; |
199 |
|
200 |
LIST_FOREACH(uc, user->chans) |
201 |
mem += sizeof(*uc); |
202 |
LIST_FOREACH(uci, user->id_chans) |
203 |
mem += sizeof(*uci); |
204 |
} |
205 |
|
206 |
*nusers = count; |
207 |
*memuse = mem; |
208 |
} |
209 |
|
210 |
/*************************************************************************/ |
211 |
/************************* Internal routines *****************************/ |
212 |
/*************************************************************************/ |
213 |
|
214 |
/* Part a user from a channel given the user's u_chanlist entry for the |
215 |
* channel. */ |
216 |
|
217 |
static void |
218 |
part_channel_uc(User *user, struct u_chanlist *uc, int callback, |
219 |
const char *param, const char *source) |
220 |
{ |
221 |
call_callback_4(callback, uc->chan, user, param, source); |
222 |
chan_deluser(user, uc->chan); |
223 |
LIST_REMOVE(uc, user->chans); |
224 |
free(uc); |
225 |
} |
226 |
|
227 |
/*************************************************************************/ |
228 |
/************************* Message handlers ******************************/ |
229 |
/*************************************************************************/ |
230 |
|
231 |
/* Handle a server NICK command. Parameters must be in the following order. |
232 |
* av[0] = nick |
233 |
* If a new user: |
234 |
* av[1] = hop count |
235 |
* av[2] = signon time |
236 |
* av[3] = username |
237 |
* av[4] = hostname |
238 |
* av[5] = server |
239 |
* av[6] = real name |
240 |
* av[7] = services stamp (if ac >= 8; NULL if none) |
241 |
* av[8] = IP address (if ac >= 9; NULL if unknown) |
242 |
* av[9] = user modes (lf ac >= 10; NULL if unknown. |
243 |
* Leading + optional) |
244 |
* av[10..] available for protocol module use |
245 |
* Else: |
246 |
* av[1] = time of change |
247 |
* Return 1 if message was accepted, 0 if rejected (AKILL/session limit). |
248 |
*/ |
249 |
|
250 |
int |
251 |
do_nick(const char *source, int ac, char **av) |
252 |
{ |
253 |
User *user; |
254 |
|
255 |
if (!*source) |
256 |
{ |
257 |
/* This is a new user; create a User structure for it. */ |
258 |
|
259 |
int reconnect = 0; /* Is user reconnecting after a split? */ |
260 |
|
261 |
log_debug(1, "new user: %s", av[0]); |
262 |
|
263 |
/* We used to ignore the ~ which a lot of ircd's use to indicate no |
264 |
* identd response. That caused channel bans to break, so now we |
265 |
* just take what the server gives us. People are still encouraged |
266 |
* to read the RFCs and stop doing anything to usernames depending |
267 |
* on the result of an identd lookup. */ |
268 |
|
269 |
/* First check whether the user should be allowed on. */ |
270 |
if (call_callback_2(cb_check, ac, av)) |
271 |
return 0; |
272 |
|
273 |
/* User was accepted; allocate User structure and fill it in. */ |
274 |
user = new_user(av[0]); |
275 |
user->my_signon = time(NULL); |
276 |
user->signon = strtotime(av[2], NULL); |
277 |
user->username = sstrdup(av[3]); |
278 |
user->host = sstrdup(av[4]); |
279 |
user->server = get_server(av[5]); |
280 |
user->realname = sstrdup(av[6]); |
281 |
|
282 |
if (ac >= 8 && av[7]) |
283 |
{ |
284 |
user->servicestamp = strtoul(av[7], NULL, 10); |
285 |
reconnect = (user->servicestamp != 0); |
286 |
} |
287 |
else |
288 |
{ |
289 |
user->servicestamp = (uint32) user->signon; |
290 |
/* Unfortunately, we have no way to tell whether the user is |
291 |
* new or not */ |
292 |
} |
293 |
|
294 |
if (ac >= 9 && av[8]) |
295 |
user->ipaddr = sstrdup(av[8]); |
296 |
else |
297 |
user->ipaddr = NULL; |
298 |
#define next snext |
299 |
#define prev sprev |
300 |
if (user->server) |
301 |
LIST_INSERT(user, user->server->userlist); |
302 |
#undef next |
303 |
#undef prev |
304 |
ignore_init(user); |
305 |
|
306 |
call_callback_4(cb_create, user, ac, av, reconnect); |
307 |
|
308 |
if (ac >= 8 && av[7] && !user->servicestamp) |
309 |
{ |
310 |
/* A servicestamp was provided, but it was zero, so assign one. |
311 |
* Note that we use a random value for the initial Services |
312 |
* stamp instead of the current time for the following reason: |
313 |
* |
314 |
* Suppose you have a network with an average of more than one |
315 |
* new user per second; for the sake of argument, assume there |
316 |
* are an average of 1.3 new users per second. If the initial |
317 |
* Services stamp is T, the current time, then in 100 seconds |
318 |
* (i.e. at T+100) the Services stamp will have gone to T+130. |
319 |
* (In reality, it would jump much higher on the initial net |
320 |
* burst when no users have Services stamps, but that does not |
321 |
* affect this argument.) |
322 |
* |
323 |
* If Services is now restarted, clearing the last used stamp |
324 |
* value, then assuming 5 seconds for restart, Services will |
325 |
* receive a network burst at T+105. However! While most of |
326 |
* the users will already have Services stamps, any new users |
327 |
* (as well as any users which connect after the network burst) |
328 |
* will be assigned new Services stamps starting with the |
329 |
* default value of the current time, in this case T+105. But |
330 |
* other users _already_ have Services stamp values in the |
331 |
* range T+105 to T+130 from the previous run--thus you have |
332 |
* Services stamp collisions, and all the security problems |
333 |
* that go with them. |
334 |
* |
335 |
* Obviously, this possibility does not disappear entirely by |
336 |
* using a random initial value, but it becomes much more |
337 |
* unlikely. |
338 |
* |
339 |
* Note that Unreal 3.1.1 (at least) upper-bounds values at |
340 |
* 2^31-1, so we limit ourselves to 31 bits here, even though |
341 |
* our field is unsigned. |
342 |
*/ |
343 |
|
344 |
static int32 servstamp = 0; |
345 |
|
346 |
if (servstamp == 0) |
347 |
servstamp = (rand() & 0x7FFFFFFF) | 1; |
348 |
|
349 |
user->servicestamp = servstamp++; |
350 |
|
351 |
if (servstamp <= 0) |
352 |
servstamp = 1; |
353 |
|
354 |
call_callback_1(cb_servicestamp_change, user); |
355 |
} |
356 |
|
357 |
if (ac >= 10 && av[9] && *av[9]) |
358 |
{ |
359 |
/* Apply modes supplied in av[9]. Current protocol modules all |
360 |
* include a '+' before the mode letters, but allow strings |
361 |
* without the '+' for robustness. */ |
362 |
char buf[BUFSIZE]; |
363 |
char *newav[2]; |
364 |
newav[0] = user->nick; |
365 |
|
366 |
if (*av[9] == '+') |
367 |
{ |
368 |
newav[1] = av[9]; |
369 |
} |
370 |
else |
371 |
{ |
372 |
snprintf(buf, sizeof(buf), "+%s", av[9]); |
373 |
newav[1] = buf; |
374 |
} |
375 |
|
376 |
do_umode(user->nick, 2, newav); |
377 |
} |
378 |
|
379 |
} |
380 |
else |
381 |
{ |
382 |
/* An old user changing nicks. */ |
383 |
char oldnick[NICKMAX]; |
384 |
|
385 |
user = get_user(source); |
386 |
|
387 |
if (!user) |
388 |
{ |
389 |
log_debug(1, "user: NICK from nonexistent nick %s: %s", |
390 |
source, merge_args(ac, av)); |
391 |
return 0; |
392 |
} |
393 |
|
394 |
log_debug(1, "%s changes nick to %s", source, av[0]); |
395 |
|
396 |
strbcpy(oldnick, user->nick); |
397 |
call_callback_2(cb_nickchange1, user, av[0]); |
398 |
|
399 |
/* Flush out all mode changes; necessary to avoid desynch (otherwise |
400 |
* we can't find the user when the mode goes out later). The IRC |
401 |
* servers will take care of translating the old nick to the new one */ |
402 |
set_cmode(NULL, NULL); |
403 |
change_user_nick(user, av[0]); |
404 |
call_callback_2(cb_nickchange2, user, oldnick); |
405 |
} |
406 |
|
407 |
return 1; |
408 |
} |
409 |
|
410 |
/*************************************************************************/ |
411 |
|
412 |
/* Handle a JOIN command. |
413 |
* av[0] = channels to join |
414 |
*/ |
415 |
|
416 |
void |
417 |
do_join(const char *source, int ac, char **av) |
418 |
{ |
419 |
User *user; |
420 |
char *s, *t; |
421 |
|
422 |
user = get_user(source); |
423 |
|
424 |
if (!user) |
425 |
{ |
426 |
log_debug(1, "user: JOIN from nonexistent user %s: %s", |
427 |
source, merge_args(ac, av)); |
428 |
return; |
429 |
} |
430 |
|
431 |
t = av[0]; |
432 |
|
433 |
while (*(s = t)) |
434 |
{ |
435 |
t = s + strcspn(s, ","); |
436 |
|
437 |
if (*t) |
438 |
*t++ = 0; |
439 |
log_debug(1, "%s joins %s", source, s); |
440 |
|
441 |
if (*s == '0') |
442 |
part_all_channels(user); |
443 |
else |
444 |
join_channel(user, s, 0); |
445 |
} |
446 |
} |
447 |
|
448 |
/*************************************************************************/ |
449 |
|
450 |
/* Handle a PART command. |
451 |
* av[0] = channels to leave |
452 |
* av[1] = reason (optional) |
453 |
*/ |
454 |
|
455 |
void |
456 |
do_part(const char *source, int ac, char **av) |
457 |
{ |
458 |
User *user; |
459 |
char *s, *t; |
460 |
|
461 |
user = get_user(source); |
462 |
|
463 |
if (!user) |
464 |
{ |
465 |
log_debug(1, "user: PART from nonexistent user %s: %s", |
466 |
source, merge_args(ac, av)); |
467 |
return; |
468 |
} |
469 |
|
470 |
t = av[0]; |
471 |
|
472 |
while (*(s = t)) |
473 |
{ |
474 |
t = s + strcspn(s, ","); |
475 |
|
476 |
if (*t) |
477 |
*t++ = 0; |
478 |
|
479 |
log_debug(1, "%s leaves %s", source, s); |
480 |
|
481 |
if (!part_channel(user, s, cb_chan_part, av[1], source)) |
482 |
{ |
483 |
log("user: do_part: no channel record for %s on %s (bug?)", |
484 |
user->nick, av[0]); |
485 |
} |
486 |
} |
487 |
} |
488 |
|
489 |
/*************************************************************************/ |
490 |
|
491 |
/* Handle a KICK command. |
492 |
* av[0] = channel |
493 |
* av[1] = nick(s) being kicked |
494 |
* av[2] = reason |
495 |
* When called internally to remove a single user (no "," in av[1]) from a |
496 |
* channel, callers may assume that the contents of the argument strings |
497 |
* will not be modified. |
498 |
*/ |
499 |
|
500 |
void |
501 |
do_kick(const char *source, int ac, char **av) |
502 |
{ |
503 |
User *user; |
504 |
char *s, *t; |
505 |
|
506 |
t = av[1]; |
507 |
|
508 |
while (*(s = t)) |
509 |
{ |
510 |
t = s + strcspn(s, ","); |
511 |
|
512 |
if (*t) |
513 |
*t++ = 0; |
514 |
|
515 |
user = get_user(s); |
516 |
|
517 |
if (!user) |
518 |
{ |
519 |
log_debug(1, "user: KICK for nonexistent user %s on %s: %s", |
520 |
s, av[0], merge_args(ac - 2, av + 2)); |
521 |
continue; |
522 |
} |
523 |
|
524 |
log_debug(1, "kicking %s from %s", s, av[0]); |
525 |
|
526 |
if (!part_channel(user, av[0], cb_chan_kick, av[2], source)) |
527 |
{ |
528 |
log("user: do_kick: no channel record for %s on %s (bug?)", |
529 |
user->nick, av[0]); |
530 |
} |
531 |
} |
532 |
} |
533 |
|
534 |
/*************************************************************************/ |
535 |
|
536 |
/* Handle a MODE command for a user. |
537 |
* av[0] = nick to change mode for |
538 |
* av[1] = modes |
539 |
*/ |
540 |
|
541 |
void |
542 |
do_umode(const char *source, int ac, char **av) |
543 |
{ |
544 |
User *user; |
545 |
char *modestr, *s; |
546 |
int add = 1; /* 1 if adding modes, 0 if deleting */ |
547 |
|
548 |
user = get_user(av[0]); |
549 |
|
550 |
if (!user) |
551 |
{ |
552 |
log_debug(1, "user: MODE %s for nonexistent nick %s from %s: %s", |
553 |
av[1], av[0], source, merge_args(ac, av)); |
554 |
return; |
555 |
} |
556 |
|
557 |
log_debug(1, "Changing mode for %s to %s", av[0], av[1]); |
558 |
modestr = s = av[1]; |
559 |
av += 2; |
560 |
ac -= 2; |
561 |
|
562 |
while (*s) |
563 |
{ |
564 |
char modechar = *s++; |
565 |
int32 flag; |
566 |
int params; |
567 |
|
568 |
if (modechar == '+') |
569 |
{ |
570 |
add = 1; |
571 |
continue; |
572 |
} |
573 |
else if (modechar == '-') |
574 |
{ |
575 |
add = 0; |
576 |
continue; |
577 |
} |
578 |
else if (add < 0) |
579 |
{ |
580 |
continue; |
581 |
} |
582 |
|
583 |
flag = mode_char_to_flag(modechar, MODE_USER); |
584 |
|
585 |
if (!flag) |
586 |
continue; |
587 |
|
588 |
if (flag == MODE_INVALID) |
589 |
flag = 0; |
590 |
|
591 |
params = mode_char_to_params(modechar, MODE_USER); |
592 |
params = (params >> (add * 8)) & 0xFF; |
593 |
|
594 |
if (ac < params) |
595 |
{ |
596 |
log("user: MODE %s %s: missing parameter(s) for %c%c", |
597 |
user->nick, modestr, add ? '+' : '-', modechar); |
598 |
break; |
599 |
} |
600 |
|
601 |
if (call_callback_4(cb_mode, user, modechar, add, av) <= 0) |
602 |
{ |
603 |
if (modechar == 'o') |
604 |
{ |
605 |
if (add) |
606 |
opcnt++; |
607 |
else |
608 |
opcnt--; |
609 |
} |
610 |
|
611 |
if (add) |
612 |
user->mode |= flag; |
613 |
else |
614 |
user->mode &= ~flag; |
615 |
} |
616 |
|
617 |
av += params; |
618 |
ac -= params; |
619 |
} |
620 |
} |
621 |
|
622 |
/*************************************************************************/ |
623 |
|
624 |
/* Handle a QUIT command. |
625 |
* av[0] = reason |
626 |
* When called internally, callers may assume that the contents of the |
627 |
* argument string will not be modified. |
628 |
*/ |
629 |
|
630 |
void |
631 |
do_quit(const char *source, int ac, char **av) |
632 |
{ |
633 |
User *user = get_user(source); |
634 |
|
635 |
if (!user) |
636 |
{ |
637 |
log_debug(1, "user: QUIT from nonexistent user %s: %s", |
638 |
source, merge_args(ac, av)); |
639 |
return; |
640 |
} |
641 |
|
642 |
log_debug(1, "%s quits", source); |
643 |
quit_user(user, av[0], 0); |
644 |
} |
645 |
|
646 |
/*************************************************************************/ |
647 |
|
648 |
/* Handle a KILL command. |
649 |
* av[0] = nick being killed |
650 |
* av[1] = reason |
651 |
* When called internally, callers may assume that the contents of the |
652 |
* argument strings will not be modified. |
653 |
*/ |
654 |
|
655 |
void |
656 |
do_kill(const char *source, int ac, char **av) |
657 |
{ |
658 |
User *user = get_user(av[0]); |
659 |
|
660 |
if (!user) |
661 |
return; |
662 |
|
663 |
log_debug(1, "%s killed", av[0]); |
664 |
quit_user(user, av[1], 1); |
665 |
} |
666 |
|
667 |
/*************************************************************************/ |
668 |
/*************************************************************************/ |
669 |
|
670 |
/* Join a user to a channel. Return a pointer to the channel record if the |
671 |
* join succeeded, NULL otherwise. |
672 |
*/ |
673 |
|
674 |
Channel * |
675 |
join_channel(User *user, const char *channel, int32 modes) |
676 |
{ |
677 |
Channel *c = chan_adduser(user, channel, modes); |
678 |
struct u_chanlist *uc; |
679 |
|
680 |
if (!c) |
681 |
return NULL; |
682 |
|
683 |
uc = smalloc(sizeof(*uc)); |
684 |
LIST_INSERT(uc, user->chans); |
685 |
uc->chan = c; |
686 |
|
687 |
return c; |
688 |
} |
689 |
|
690 |
/*************************************************************************/ |
691 |
|
692 |
/* Part a user from a channel. */ |
693 |
|
694 |
int |
695 |
part_channel(User *user, const char *channel, int callback, |
696 |
const char *param, const char *source) |
697 |
{ |
698 |
struct u_chanlist *uc; |
699 |
|
700 |
LIST_SEARCH(user->chans, chan->name, channel, irc_stricmp, uc); |
701 |
|
702 |
if (uc) |
703 |
part_channel_uc(user, uc, callback, param, source); |
704 |
|
705 |
return uc != NULL; |
706 |
} |
707 |
|
708 |
/*************************************************************************/ |
709 |
|
710 |
/* Part a user from all channels s/he is in. Assumes cb_chan_part, an |
711 |
* empty `param' string, and the user as source. */ |
712 |
|
713 |
void |
714 |
part_all_channels(User *user) |
715 |
{ |
716 |
struct u_chanlist *uc, *nextuc; |
717 |
|
718 |
LIST_FOREACH_SAFE(uc, user->chans, nextuc) |
719 |
part_channel_uc(user, uc, cb_chan_part, "", user->nick); |
720 |
} |
721 |
|
722 |
/*************************************************************************/ |
723 |
/*************************************************************************/ |
724 |
|
725 |
/* Various check functions. All of these return false/NULL if any |
726 |
* parameter is NULL. */ |
727 |
|
728 |
/*************************************************************************/ |
729 |
|
730 |
/* Is the given user an oper? */ |
731 |
|
732 |
int |
733 |
is_oper(const User *user) |
734 |
{ |
735 |
return user != NULL && (user->mode & UMODE_o); |
736 |
} |
737 |
|
738 |
/*************************************************************************/ |
739 |
|
740 |
/* Is the given user on the given channel? Return the Channel * for the |
741 |
* channel if so, NULL if not. |
742 |
*/ |
743 |
|
744 |
Channel * |
745 |
is_on_chan(const User *user, const char *chan) |
746 |
{ |
747 |
struct u_chanlist *c; |
748 |
|
749 |
if (!user || !chan) |
750 |
return NULL; |
751 |
|
752 |
LIST_SEARCH(user->chans, chan->name, chan, irc_stricmp, c); |
753 |
|
754 |
return c ? c->chan : NULL; |
755 |
} |
756 |
|
757 |
/*************************************************************************/ |
758 |
|
759 |
/* Is the given user a channel operator on the given channel? */ |
760 |
|
761 |
int |
762 |
is_chanop(const User *user, const char *chan) |
763 |
{ |
764 |
Channel *c = chan ? get_channel(chan) : NULL; |
765 |
struct c_userlist *cu; |
766 |
|
767 |
if (!user || !chan || !c) |
768 |
return 0; |
769 |
|
770 |
LIST_SEARCH(c->users, user->nick, user->nick, irc_stricmp, cu); |
771 |
|
772 |
return cu != NULL && (cu->mode & CUMODE_o) != 0; |
773 |
} |
774 |
|
775 |
/*************************************************************************/ |
776 |
|
777 |
/* Is the given user voiced (channel mode +v) on the given channel? */ |
778 |
|
779 |
int |
780 |
is_voiced(const User *user, const char *chan) |
781 |
{ |
782 |
Channel *c = chan ? get_channel(chan) : NULL; |
783 |
struct c_userlist *cu; |
784 |
|
785 |
if (!user || !chan || !c) |
786 |
return 0; |
787 |
|
788 |
LIST_SEARCH(c->users, user->nick, user->nick, irc_stricmp, cu); |
789 |
|
790 |
return cu != NULL && (cu->mode & CUMODE_v) != 0; |
791 |
} |
792 |
|
793 |
/*************************************************************************/ |
794 |
/*************************************************************************/ |
795 |
|
796 |
/* Does the user's usermask match the given mask? The mask may be in |
797 |
* either nick!user@host or just user@host form. When "fakehosts" or IP |
798 |
* addresses are available in the user record, they are also checked |
799 |
* against the "host" part of the mask, and a match by any field (real |
800 |
* host, fakehost, or IP address) is treated as a match for the host part. |
801 |
* Note that CIDR matching on IP addresses is _not_ performed. |
802 |
*/ |
803 |
|
804 |
int |
805 |
match_usermask(const char *mask, const User * user) |
806 |
{ |
807 |
char *mask2; |
808 |
char *nick, *username, *host; |
809 |
int match_user, match_host, result; |
810 |
|
811 |
if (!mask || !user) |
812 |
{ |
813 |
log_debug(1, "match_usermask: NULL %s!", !mask ? "mask" : "user"); |
814 |
return 0; |
815 |
} |
816 |
|
817 |
mask2 = sstrdup(mask); |
818 |
|
819 |
if (strchr(mask2, '!')) |
820 |
{ |
821 |
nick = strtok(mask2, "!"); |
822 |
username = strtok(NULL, "@"); |
823 |
} |
824 |
else |
825 |
{ |
826 |
nick = NULL; |
827 |
username = strtok(mask2, "@"); |
828 |
} |
829 |
|
830 |
host = strtok(NULL, ""); |
831 |
|
832 |
if (!host) |
833 |
{ |
834 |
free(mask2); |
835 |
return 0; |
836 |
} |
837 |
|
838 |
match_user = match_wild_nocase(username, user->username); |
839 |
match_host = match_wild_nocase(host, user->host); |
840 |
|
841 |
if (user->fakehost) |
842 |
match_host |= match_wild_nocase(host, user->fakehost); |
843 |
if (user->ipaddr) |
844 |
match_host |= match_wild_nocase(host, user->ipaddr); |
845 |
|
846 |
if (nick) |
847 |
{ |
848 |
result = match_wild_nocase(nick, user->nick) && match_user && match_host; |
849 |
} |
850 |
else |
851 |
{ |
852 |
result = match_user && match_host; |
853 |
} |
854 |
|
855 |
free(mask2); |
856 |
|
857 |
return result; |
858 |
} |
859 |
|
860 |
/*************************************************************************/ |
861 |
|
862 |
/* Split a usermask up into its constitutent parts. Returned strings are |
863 |
* malloc()'d, and should be free()'d when done with. Returns "*" for |
864 |
* missing parts. Assumes `mask' is a non-empty string. |
865 |
*/ |
866 |
|
867 |
void |
868 |
split_usermask(const char *mask, char **nick, char **user, char **host) |
869 |
{ |
870 |
char *mask2 = sstrdup(mask); |
871 |
char *mynick, *myuser, *myhost; |
872 |
|
873 |
mynick = mask2; |
874 |
myuser = strchr(mask2, '!'); |
875 |
myhost = myuser ? strchr(myuser, '@') : NULL; |
876 |
|
877 |
if (myuser) |
878 |
*myuser++ = 0; |
879 |
if (myhost) |
880 |
*myhost++ = 0; |
881 |
|
882 |
/* Handle special case: mask == user@host */ |
883 |
if (mynick && !myuser && strchr(mynick, '@')) |
884 |
{ |
885 |
mynick = NULL; |
886 |
myuser = mask2; |
887 |
myhost = strchr(mask2, '@'); |
888 |
|
889 |
if (myhost) /* Paranoia */ |
890 |
*myhost++ = 0; |
891 |
} |
892 |
|
893 |
if (!mynick || !*mynick) |
894 |
mynick = (char *) "*"; |
895 |
if (!myuser || !*myuser) |
896 |
myuser = (char *) "*"; |
897 |
if (!myhost || !*myhost) |
898 |
myhost = (char *) "*"; |
899 |
|
900 |
*nick = sstrdup(mynick); |
901 |
*user = sstrdup(myuser); |
902 |
*host = sstrdup(myhost); |
903 |
free(mask2); |
904 |
} |
905 |
|
906 |
/*************************************************************************/ |
907 |
|
908 |
/* Given a user, return a mask that will most likely match any address the |
909 |
* user will have from that location. For IP addresses, wildcards the last |
910 |
* octet of the address (e.g. 10.1.1.1 -> 10.1.1.*); for named addresses, |
911 |
* wildcards the leftmost part of the name unless the name only contains |
912 |
* two parts. The returned character string is malloc'd and should be |
913 |
* free'd when done with. |
914 |
* |
915 |
* Where supported, uses the fake host instead of the real one if |
916 |
* use_fakehost is nonzero. |
917 |
*/ |
918 |
|
919 |
char * |
920 |
create_mask(const User *user, int use_fakehost) |
921 |
{ |
922 |
char *mask, *s, *end, *host; |
923 |
|
924 |
host = user->host; |
925 |
|
926 |
if (use_fakehost && user->fakehost) |
927 |
host = user->fakehost; |
928 |
|
929 |
/* Get us a buffer the size of the username plus hostname. The result |
930 |
* will never be longer than this (and will often be shorter), thus we |
931 |
* can use strcpy() and sprintf() safely. |
932 |
*/ |
933 |
end = mask = smalloc(strlen(user->username) + strlen(host) + 2); |
934 |
end += sprintf(end, "%s@", user->username); |
935 |
|
936 |
if (strspn(host, "0123456789.") == strlen(host) && |
937 |
(s = strchr(host, '.')) && |
938 |
(s = strchr(s + 1, '.')) && |
939 |
(s = strchr(s + 1, '.')) && (!strchr(s + 1, '.'))) |
940 |
{ /* IP addr */ |
941 |
s = sstrdup(host); |
942 |
*strrchr(s, '.') = 0; |
943 |
sprintf(end, "%s.*", s); |
944 |
free(s); |
945 |
} |
946 |
else |
947 |
{ |
948 |
if ((s = strchr(host + 1, '.')) && strchr(s + 1, '.')) |
949 |
{ |
950 |
s = sstrdup(s - 1); |
951 |
*s = '*'; |
952 |
} |
953 |
else |
954 |
{ |
955 |
s = sstrdup(host); |
956 |
} |
957 |
|
958 |
strcpy(end, s); /* safe: see above */ |
959 |
free(s); |
960 |
} |
961 |
|
962 |
return mask; |
963 |
} |
964 |
|
965 |
/*************************************************************************/ |
966 |
/*************************************************************************/ |
967 |
|
968 |
/* Create a new guest nick using GuestNickPrefix and a unique series of |
969 |
* digits, and return it. The returned nick is stored in a static buffer |
970 |
* and will be overwritten at the next call. |
971 |
* |
972 |
* At present, we simply use a rollover counter attached to the nick, |
973 |
* initialized to a random value. This provides for uniqueness as long as |
974 |
* Services is not restarted and a reasonable chance of uniqueness even |
975 |
* when Services is restarted (unless you have a long prefix and a short |
976 |
* maximum nick length; however, this routine will make sure at least 4 |
977 |
* digits are available, possibly by shortening the prefix). |
978 |
* |
979 |
* Note that initializing the counter based on the current time would be a |
980 |
* bad idea, since if more than one user per second (on average) connected |
981 |
* to the network, duplicate nicks would be almost guaranteed if Services |
982 |
* restarted. |
983 |
*/ |
984 |
|
985 |
char * |
986 |
make_guest_nick(void) |
987 |
{ |
988 |
static char nickbuf[NICKMAX + 1]; /* +1 to check for overrun */ |
989 |
static uint32 counter = 0; /* Unique suffix counter */ |
990 |
int tries; /* Tries to find an unused nick */ |
991 |
int prefixlen; /* Length of nick prefix */ |
992 |
uint32 suffixmod; /* Modulo for suffix counter */ |
993 |
int i; |
994 |
|
995 |
/* Sanity checks on nick prefix length */ |
996 |
prefixlen = strlen(GuestNickPrefix); |
997 |
|
998 |
if (protocol_nickmax <= 4) |
999 |
{ |
1000 |
/* This violates RFC1459 as well as common sense, so just blow |
1001 |
* ourselves out of the water. */ |
1002 |
fatal("make_guest_nick(): protocol_nickmax too small (%d)", |
1003 |
protocol_nickmax); |
1004 |
} |
1005 |
else if (prefixlen + 4 > protocol_nickmax) |
1006 |
{ |
1007 |
/* Reserve at least 4 digits for the suffix */ |
1008 |
prefixlen = protocol_nickmax - 4; |
1009 |
log("warning: make_guest_nick(): GuestNickPrefix too long," |
1010 |
" shortening to %d characters", prefixlen); |
1011 |
GuestNickPrefix[prefixlen] = 0; |
1012 |
} |
1013 |
|
1014 |
/* Calculate number of digits available for suffix -> suffix modulo */ |
1015 |
i = protocol_nickmax - prefixlen; |
1016 |
|
1017 |
if (i < 10) |
1018 |
{ |
1019 |
suffixmod = 1; |
1020 |
while (i-- > 0) |
1021 |
suffixmod *= 10; |
1022 |
} |
1023 |
else |
1024 |
{ |
1025 |
suffixmod = 0; /* no modulo */ |
1026 |
} |
1027 |
|
1028 |
/* |
1029 |
* Actually generate the nick. If the nick already exists, generate a |
1030 |
* new one, and repeat until finding an unused nick or trying too many |
1031 |
* times. If we try too many times, we just kill the last one we end |
1032 |
* up with. |
1033 |
*/ |
1034 |
tries = 0; |
1035 |
|
1036 |
for (; ;) |
1037 |
{ |
1038 |
if (counter == 0) /* initialize to random the first time */ |
1039 |
counter = rand(); |
1040 |
if (suffixmod) /* strip down to right number of digits */ |
1041 |
counter %= suffixmod; |
1042 |
if (counter == 0) /* if rand() gave us 0 or N*suffixmod... */ |
1043 |
counter = 1; /* ... use 1 instead */ |
1044 |
|
1045 |
i = snprintf(nickbuf, sizeof(nickbuf), "%s%u", GuestNickPrefix, counter); |
1046 |
|
1047 |
if (i > protocol_nickmax) |
1048 |
{ |
1049 |
log("BUG: make_guest_nick() generated %s but nickmax == %d!", |
1050 |
nickbuf, protocol_nickmax); |
1051 |
nickbuf[protocol_nickmax] = 0; |
1052 |
} |
1053 |
|
1054 |
if (!get_user(nickbuf)) /* done if user not found */ |
1055 |
break; |
1056 |
|
1057 |
/* Increment try count, and break out if it's too much */ |
1058 |
if (++tries >= MAKEGUESTNICK_TRIES) |
1059 |
{ |
1060 |
kill_user(NULL, nickbuf, "Guest nicks may not be used"); |
1061 |
break; |
1062 |
} |
1063 |
|
1064 |
/* Reset counter to 0 to use a new random number */ |
1065 |
counter = 0; |
1066 |
} |
1067 |
|
1068 |
/* |
1069 |
* Increment the unique suffix counter modulo suffixmod, and avoid 0 |
1070 |
* (which would cause a reset to a random value) |
1071 |
*/ |
1072 |
counter++; |
1073 |
if (counter == suffixmod) /* because suffixmod==0 if no modulo */ |
1074 |
counter = 1; |
1075 |
|
1076 |
/* Return the nick */ |
1077 |
return nickbuf; |
1078 |
}; |
1079 |
|
1080 |
/*************************************************************************/ |
1081 |
|
1082 |
/* Return nonzero if the given nickname can potentially be a guest nickname |
1083 |
* (i.e. a nickname that could be generated by make_guest_nick()), zero if |
1084 |
* not. Currently this means a nickname consisting of GuestNickPrefix |
1085 |
* followed by between 1 and 10 digits inclusive. |
1086 |
*/ |
1087 |
|
1088 |
int |
1089 |
is_guest_nick(const char *nick) |
1090 |
{ |
1091 |
int prefixlen = strlen(GuestNickPrefix); |
1092 |
int nicklen = strlen(nick); |
1093 |
|
1094 |
return nicklen >= prefixlen + 1 && nicklen <= prefixlen + 10 && |
1095 |
irc_strnicmp(nick, GuestNickPrefix, prefixlen) == 0 && |
1096 |
strspn(nick + prefixlen, "1234567890") == nicklen - prefixlen; |
1097 |
} |
1098 |
|
1099 |
/*************************************************************************/ |
1100 |
|
1101 |
/* |
1102 |
* Local variables: |
1103 |
* c-file-style: "stroustrup" |
1104 |
* c-file-offsets: ((case-label . *) (statement-case-intro . *)) |
1105 |
* indent-tabs-mode: nil |
1106 |
* End: |
1107 |
* |
1108 |
* vim: expandtab shiftwidth=4: |
1109 |
*/ |