ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/vendor/ircservices-5.1.24/users.c
Revision: 3389
Committed: Fri Apr 25 14:12:15 2014 UTC (9 years, 11 months ago) by michael
Content type: text/x-csrc
File size: 30458 byte(s)
Log Message:
- Imported ircservices-5.1.24

File Contents

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