diff --git a/doc/example.conf b/doc/example.conf index c02b3c7d..ba010723 100755 --- a/doc/example.conf +++ b/doc/example.conf @@ -478,6 +478,7 @@ general { ts_max_delta = 5 minutes; client_exit = yes; collision_fnc = yes; + resv_fnc = yes; global_snotices = yes; dline_with_reason = yes; kline_delay = 0 seconds; diff --git a/doc/reference.conf b/doc/reference.conf index 1e3e8996..38519a97 100755 --- a/doc/reference.conf +++ b/doc/reference.conf @@ -1024,6 +1024,13 @@ general { */ collision_fnc = yes; + /* resv fnc: change a user's nick to a nick they have recently used + * (or their UID, if no such nick can be found) when a resv matching + * them is set by services. Only enable this if all servers on the + * network allow remote nicks to start with a digit. + */ + resv_fnc = yes; + /* global snotices: send out certain snotices (most +b, +f, +y, * some +s) to other servers via ENCAP SNOTE. Received SNOTEs are * displayed unconditionally. diff --git a/include/client.h b/include/client.h index 3856f2f3..d394103a 100644 --- a/include/client.h +++ b/include/client.h @@ -566,6 +566,7 @@ extern void check_klines_event(void *unused); extern void check_klines(void); extern void check_dlines(void); extern void check_xlines(void); +extern void resv_nick_fnc(const char *mask, const char *reason, int temp_time); extern const char *get_client_name(struct Client *client, int show_ip); extern const char *log_client_name(struct Client *, int); diff --git a/include/s_conf.h b/include/s_conf.h index 722e9352..2587a42b 100644 --- a/include/s_conf.h +++ b/include/s_conf.h @@ -220,6 +220,7 @@ struct config_file_entry int throttle_duration; int target_change; int collision_fnc; + int resv_fnc; int default_umodes; int global_snotices; int operspy_dont_care_user_info; diff --git a/modules/m_resv.c b/modules/m_resv.c index 97f9edba..98e61fb4 100644 --- a/modules/m_resv.c +++ b/modules/m_resv.c @@ -361,6 +361,7 @@ parse_resv(struct Client *source_p, const char *name, const char *reason, int te } rb_dlinkAddAlloc(aconf, &resv_conf_list); + resv_nick_fnc(aconf->host, aconf->passwd, temp_time); } else sendto_one_notice(source_p, ":You have specified an invalid resv: [%s]", name); diff --git a/src/client.c b/src/client.c index 513e941d..8e895dfc 100644 --- a/src/client.c +++ b/src/client.c @@ -600,6 +600,86 @@ check_xlines(void) } } +/* resv_nick_fnc + * + * inputs - resv, reason, time + * outputs - NONE + * side effects - all local clients matching resv will be FNC'd + */ +void +resv_nick_fnc(const char *mask, const char *reason, int temp_time) +{ + struct Client *client_p, *target_p; + rb_dlink_node *ptr; + rb_dlink_node *next_ptr; + char *nick; + char note[NICKLEN+10]; + + if (!ConfigFileEntry.resv_fnc) + return; + + RB_DLINK_FOREACH_SAFE(ptr, next_ptr, lclient_list.head) + { + client_p = ptr->data; + + if(IsMe(client_p) || !IsPerson(client_p) || IsExemptResv(client_p)) + continue; + + if(match_esc(mask, client_p->name)) + { + nick = client_p->id; + + /* Tell opers. */ + sendto_realops_snomask(SNO_GENERAL, L_ALL, + "RESV forced nick change for %s!%s@%s to %s; nick matched [%s] (%s)", + client_p->name, client_p->username, client_p->host, nick, mask, reason); + + sendto_realops_snomask(SNO_NCHANGE, L_ALL, + "Nick change: From %s to %s [%s@%s]", + client_p->name, nick, client_p->username, client_p->host); + + /* Tell the user. */ + if (temp_time > 0) + { + sendto_one_notice(client_p, + ":*** Nick %s is temporarily unavailable on this server.", + client_p->name); + } + else + { + sendto_one_notice(client_p, + ":*** Nick %s is no longer available on this server.", + client_p->name); + } + + /* Do all of the nick-changing gymnastics. */ + client_p->tsinfo = rb_current_time(); + add_history(client_p, 1); + + invalidate_bancache_user(client_p); + + sendto_common_channels_local(client_p, NOCAPS, ":%s!%s@%s NICK :%s", + client_p->name, client_p->username, client_p->host, nick); + sendto_server(client_p, NULL, CAP_TS6, NOCAPS, ":%s NICK %s :%ld", + use_id(client_p), nick, (long) client_p->tsinfo); + + del_from_client_hash(client_p->name, client_p); + rb_strlcpy(client_p->name, nick, sizeof(client_p->name)); + add_to_client_hash(nick, client_p); + + RB_DLINK_FOREACH_SAFE(ptr, next_ptr, client_p->on_allow_list.head) + { + target_p = ptr->data; + rb_dlinkFindDestroy(client_p, &target_p->localClient->allow_list); + rb_dlinkDestroy(ptr, &client_p->on_allow_list); + } + + rb_snprintf(note, sizeof(note), "Nick: %s", nick); + rb_note(client_p->localClient->F, note); + } + } +} + /* * update_client_exit_stats * diff --git a/src/newconf.c b/src/newconf.c index ddc9f93b..9260024a 100644 --- a/src/newconf.c +++ b/src/newconf.c @@ -2235,6 +2235,7 @@ static struct ConfEntry conf_general_table[] = { "caller_id_wait", CF_TIME, NULL, 0, &ConfigFileEntry.caller_id_wait }, { "client_exit", CF_YESNO, NULL, 0, &ConfigFileEntry.client_exit }, { "collision_fnc", CF_YESNO, NULL, 0, &ConfigFileEntry.collision_fnc }, + { "resv_fnc", CF_YESNO, NULL, 0, &ConfigFileEntry.resv_fnc }, { "connect_timeout", CF_TIME, NULL, 0, &ConfigFileEntry.connect_timeout }, { "default_floodcount", CF_INT, NULL, 0, &ConfigFileEntry.default_floodcount }, { "default_ident_timeout", CF_INT, NULL, 0, &ConfigFileEntry.default_ident_timeout }, diff --git a/src/s_conf.c b/src/s_conf.c index 93849e02..ca93f05d 100644 --- a/src/s_conf.c +++ b/src/s_conf.c @@ -742,6 +742,7 @@ set_default_conf(void) ConfigFileEntry.use_whois_actually = YES; ConfigFileEntry.burst_away = NO; ConfigFileEntry.collision_fnc = YES; + ConfigFileEntry.resv_fnc = YES; ConfigFileEntry.global_snotices = YES; ConfigFileEntry.operspy_dont_care_user_info = NO; ConfigFileEntry.use_propagated_bans = YES;