diff --git a/include/client.h b/include/client.h index 6a50f5339..805f8599d 100644 --- a/include/client.h +++ b/include/client.h @@ -234,6 +234,9 @@ struct LocalUser /* nicknames theyre monitoring */ rb_dlink_list monitor_list; + /* collects umodes explicitly changed by this user */ + unsigned int changed_umodes; + /* * Anti-flood stuff. We track how many messages were parsed and how * many we were allowed in the current second, and apply a simple decay diff --git a/include/s_user.h b/include/s_user.h index 8e81f4a28..8ed9c1050 100644 --- a/include/s_user.h +++ b/include/s_user.h @@ -38,6 +38,7 @@ extern bool valid_username(const char *username); extern int user_mode(struct Client *, struct Client *, int, const char **); extern void send_umode(struct Client *, struct Client *, int, char *); extern void send_umode_out(struct Client *, struct Client *, int); +extern void change_default_umodes(struct Client *); extern void show_lusers(struct Client *source_p); extern int register_local_user(struct Client *, struct Client *); @@ -55,6 +56,6 @@ struct PrivilegeSet; extern void report_priv_change(struct Client *, struct PrivilegeSet *, struct PrivilegeSet *); extern void oper_up(struct Client *, struct oper_conf *); -#endif - extern bool has_common_channel(struct Client *source_p, struct Client *target_p); + +#endif diff --git a/ircd/s_conf.c b/ircd/s_conf.c index cb86811ff..2d4ba795c 100644 --- a/ircd/s_conf.c +++ b/ircd/s_conf.c @@ -653,6 +653,7 @@ bool rehash(bool sig) { rb_dlink_node *n; + int old_umodes; hook_data_rehash hdata = { sig }; @@ -664,6 +665,10 @@ rehash(bool sig) privilegeset_prepare_rehash(); + /* capture current set of default umodes; if this changes we'll apply the + * change to all local clients */ + old_umodes = ConfigFileEntry.default_umodes; + /* don't close listeners until we know we can go ahead with the rehash */ read_conf_files(false); @@ -683,6 +688,15 @@ rehash(bool sig) privilegeset_cleanup_rehash(); + if (old_umodes != ConfigFileEntry.default_umodes) + RB_DLINK_FOREACH(n, lclient_list.head) + { + struct Client *client = n->data; + if (!IsPerson(client)) + continue; + change_default_umodes(client); + } + call_hook(h_rehash, &hdata); return false; } diff --git a/ircd/s_user.c b/ircd/s_user.c index d758b537c..223ed23ee 100644 --- a/ircd/s_user.c +++ b/ircd/s_user.c @@ -1089,6 +1089,7 @@ user_mode(struct Client *client_p, struct Client *source_p, int parc, const char const char *pm; struct Client *target_p; int what, setflags; + int changemask = 0; bool badflag = false; /* Only send one bad flag notice */ bool showsnomask = false; unsigned int setsnomask; @@ -1279,6 +1280,7 @@ user_mode(struct Client *client_p, struct Client *source_p, int parc, const char source_p->umodes |= flag; else source_p->umodes &= ~flag; + changemask |= flag; } } else @@ -1336,6 +1338,17 @@ user_mode(struct Client *client_p, struct Client *source_p, int parc, const char ++Count.invisi; if((setflags & UMODE_INVISIBLE) && !IsInvisible(source_p)) --Count.invisi; + + /* mark changed umodes so that if default umodes change, these don't get touched */ + if (MyClient(source_p)) + { + /* capture modes the user listed in the string, even if they were already (un)set, + * e.g. /umode -R when they were already -R */ + source_p->localClient->changed_umodes |= changemask; + /* all changed modes, whether through the user directly or as a result of a hook */ + source_p->localClient->changed_umodes |= source_p->umodes ^ setflags; + } + /* * compare new flags with old flags and send string which * will cause servers to update correctly. @@ -1432,6 +1445,37 @@ send_umode_out(struct Client *client_p, struct Client *source_p, int old) send_umode(client_p, source_p, old, buf); } +/* + * change_default_umodes + * + * inputs - pointer to local client + * outputs - NONE + * side effects - changes the user's umodes to correspond to the new defaults + */ +void +change_default_umodes(struct Client *client_p) +{ + struct ConfItem *a_conf; + unsigned int old, new_default, changed_mask; + + if (!MyClient(client_p)) + return; + + a_conf = client_p->localClient->att_conf; + old = client_p->umodes; + changed_mask = client_p->localClient->changed_umodes; + + new_default = ConfigFileEntry.default_umodes & ~a_conf->umodes_mask; + new_default |= a_conf->umodes; + new_default &= ~orphaned_umodes; + if (!IsOper(client_p)) + new_default &= ~ConfigFileEntry.oper_only_umodes; + + client_p->umodes = (new_default & ~changed_mask) | (old & changed_mask); + if (client_p->umodes != old) + send_umode_out(client_p, client_p, old); +} + /* * user_welcome *