ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/hybrid-ircservices-1/users.c
Revision: 1210
Committed: Thu Aug 25 19:39:01 2011 UTC (12 years, 7 months ago) by michael
Content type: text/x-csrc
File size: 28798 byte(s)
Log Message:
- style corrections

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 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 */