diff --git a/include/h.h b/include/h.h index 3f3599563..2010cfe67 100644 --- a/include/h.h +++ b/include/h.h @@ -94,6 +94,7 @@ extern long set_usermode(const char *umode); extern const char *get_usermode_string(Client *acptr); extern const char *get_usermode_string_r(Client *client, char *buf, size_t buflen); extern const char *get_usermode_string_raw(long umodes); +extern const char *get_usermode_string_raw_r(long umodes, char *buf, size_t buflen); extern ConfigFile *config_parse(const char *filename, char *confdata); extern ConfigFile *config_parse_with_offset(const char *filename, char *confdata, unsigned int line_offset); extern void config_error(FORMAT_STRING(const char *format), ...) __attribute__((format(printf,1,2))); diff --git a/include/modules.h b/include/modules.h index 16bd5591c..d98be18a4 100644 --- a/include/modules.h +++ b/include/modules.h @@ -109,14 +109,16 @@ typedef enum ModuleObjectType { MOBJ_HISTORY_BACKEND = 18, } ModuleObjectType; -typedef struct { - long mode; /**< Mode mask */ - char letter; /**< Mode character */ - int unset_on_deoper; /**< When set to 1 then this user mode will be unset on de-oper */ - int (*allowed)(Client *client, int what); /**< The 'is this user allowed to set this mode?' routine */ - char unloaded; /**< Internal flag to indicate module is being unloaded */ - Module *owner; /**< Module that owns this user mode */ -} Umode; +typedef struct Umode Umode; +struct Umode { + Umode *prev, *next; + long mode; /**< Mode mask */ + char letter; /**< Mode character */ + int unset_on_deoper; /**< When set to 1 then this user mode will be unset on de-oper */ + int (*allowed)(Client *client, int what); /**< The 'is this user allowed to set this mode?' routine */ + char unloaded; /**< Internal flag to indicate module is being unloaded */ + Module *owner; /**< Module that owns this user mode */ +}; typedef enum ModDataType { MODDATATYPE_LOCAL_VARIABLE = 1, diff --git a/include/struct.h b/include/struct.h index 0e2ed6c42..315c66a29 100644 --- a/include/struct.h +++ b/include/struct.h @@ -1191,8 +1191,7 @@ struct CommandOverride { OverrideCmdFunc func; }; -extern MODVAR Umode *Usermode_Table; -extern MODVAR int Usermode_highest; +extern MODVAR Umode *usermodes; extern MODVAR Cmode *channelmodes; extern Umode *UmodeAdd(Module *module, char ch, int options, int unset_on_deoper, int (*allowed)(Client *client, int what), long *mode); diff --git a/src/api-usermode.c b/src/api-usermode.c index 8b2fde4a8..510b82c4a 100644 --- a/src/api-usermode.c +++ b/src/api-usermode.c @@ -24,10 +24,9 @@ char umodestring[UMODETABLESZ+1]; -Umode *Usermode_Table = NULL; -int Usermode_highest = 0; +/** User modes and their handlers */ +Umode *usermodes = NULL; -/* client->umodes (32 bits): 26 used, 6 free */ long UMODE_INVISIBLE = 0L; /* makes user invisible */ long UMODE_OPER = 0L; /* Operator */ long UMODE_REGNICK = 0L; /* Nick set by services as registered */ @@ -59,20 +58,11 @@ long SendUmodes; /* All umodes which are sent to other servers (global umodes) * /* Forward declarations */ int umode_hidle_allow(Client *client, int what); +static void unload_usermode_commit(Umode *m); -void umode_init(void) +void umode_init(void) { - long val = 1; - int i; - Usermode_Table = safe_alloc(sizeof(Umode) * UMODETABLESZ); - for (i = 0; i < UMODETABLESZ; i++) - { - Usermode_Table[i].mode = val; - val *= 2; - } - Usermode_highest = 0; - - /* Set up modes */ + /* Some built-in modes */ UmodeAdd(NULL, 'i', UMODE_GLOBAL, 0, umode_allow_all, &UMODE_INVISIBLE); UmodeAdd(NULL, 'o', UMODE_GLOBAL, 1, umode_allow_opers, &UMODE_OPER); UmodeAdd(NULL, 'r', UMODE_GLOBAL, 0, umode_allow_none, &UMODE_REGNICK); @@ -87,16 +77,13 @@ void umode_init(void) void make_umodestr(void) { - int i; - char *m; + Umode *um; + char *p = umodestring; - m = umodestring; - for (i = 0; i <= Usermode_highest; i++) - { - if (Usermode_Table[i].letter) - *m++ = Usermode_Table[i].letter; - } - *m = '\0'; + for (um=usermodes; um; um = um->next) + if (um->letter) + *p++ = um->letter; + *p = '\0'; } static char previous_umodestring[256]; @@ -125,101 +112,134 @@ void umodes_check_for_changes(void) strlcpy(previous_umodestring, umodestring, sizeof(previous_umodestring)); } +void usermode_add_sorted(Umode *n) +{ + Umode *m; + + if (usermodes == NULL) + { + usermodes = n; + return; + } + + for (m = usermodes; m; m = m->next) + { + if (m->letter == '\0') + abort(); + if (sort_character_lowercase_before_uppercase(n->letter, m->letter)) + { + /* Insert us before */ + if (m->prev) + m->prev->next = n; + else + usermodes = n; /* new head */ + n->prev = m->prev; + + n->next = m; + m->prev = n; + return; + } + if (!m->next) + { + /* Append us at end */ + m->next = n; + n->prev = m; + return; + } + } +} + + /* UmodeAdd: * Add a usermode with character 'ch', if global is set to 1 the usermode is global * (sent to other servers) otherwise it's a local usermode */ Umode *UmodeAdd(Module *module, char ch, int global, int unset_on_deoper, int (*allowed)(Client *client, int what), long *mode) { - short i = 0; - short j = 0; - short save = -1; - while (i < UMODETABLESZ) + Umode *um; + int existing = 0; + + for (um=usermodes; um; um = um->next) { - if (!Usermode_Table[i].letter && save == -1) - save = i; - else if (Usermode_Table[i].letter == ch) + if (um->letter == ch) { - if (Usermode_Table[i].unloaded) + if (um->unloaded) { - save = i; - Usermode_Table[i].unloaded = 0; + um->unloaded = 0; + existing = 1; break; - } - else - { + } else { if (module) module->errorcode = MODERR_EXISTS; return NULL; } } - i++; } - i = save; - if (i != UMODETABLESZ) + + if (!um) { - Usermode_Table[i].letter = ch; - Usermode_Table[i].allowed = allowed; - Usermode_Table[i].unset_on_deoper = unset_on_deoper; - /* Update usermode table highest */ - for (j = 0; j < UMODETABLESZ; j++) - if (Usermode_Table[i].letter) - if (i > Usermode_highest) - Usermode_highest = i; - make_umodestr(); - AllUmodes |= Usermode_Table[i].mode; - if (global) - SendUmodes |= Usermode_Table[i].mode; - *mode = Usermode_Table[i].mode; - Usermode_Table[i].owner = module; - if (module) + /* Not found, create */ + long l, found = 0; + for (l = 1; l < INT_MAX/2; l *= 2) { - ModuleObject *umodeobj = safe_alloc(sizeof(ModuleObject)); - umodeobj->object.umode = &(Usermode_Table[i]); - umodeobj->type = MOBJ_UMODE; - AddListItem(umodeobj, module->objects); - module->errorcode = MODERR_NOERROR; + found = 0; + for (um=usermodes; um; um = um->next) + { + if (um->mode == l) + { + found = 1; + break; + } + } + if (!found) + break; } - return &(Usermode_Table[i]); + /* If 'found' is still true, then we are out of space */ + if (found) + { + unreal_log(ULOG_ERROR, "module", "USER_MODE_OUT_OF_SPACE", NULL, + "UmodeAdd: out of space!!!"); + if (module) + module->errorcode = MODERR_NOSPACE; + return NULL; + } + um = safe_alloc(sizeof(Umode)); + um->letter = ch; + um->mode = l; + usermode_add_sorted(um); } - else + + um->letter = ch; + um->allowed = allowed; + um->unset_on_deoper = unset_on_deoper; + make_umodestr(); + AllUmodes |= um->mode; + if (global) + SendUmodes |= um->mode; + *mode = um->mode; + um->owner = module; + if (module) { - unreal_log(ULOG_ERROR, "module", "USER_MODE_OUT_OF_SPACE", NULL, - "UmodeAdd: out of space!!!"); - if (module) - module->errorcode = MODERR_NOSPACE; - return NULL; + ModuleObject *umodeobj = safe_alloc(sizeof(ModuleObject)); + umodeobj->object.umode = um; + umodeobj->type = MOBJ_UMODE; + AddListItem(umodeobj, module->objects); + module->errorcode = MODERR_NOERROR; } + return um; } void UmodeDel(Umode *umode) { - if (loop.rehashing) - umode->unloaded = 1; - else + /* Always free the module object */ + if (umode->owner) { - Client *client; - list_for_each_entry(client, &client_list, client_node) - { - long oldumode = 0; - if (!IsUser(client)) - continue; - oldumode = client->umodes; - client->umodes &= ~umode->mode; - if (MyUser(client)) - send_umode_out(client, 1, oldumode); - } - umode->letter = '\0'; - AllUmodes &= ~(umode->mode); - SendUmodes &= ~(umode->mode); - make_umodestr(); - } - - if (umode->owner) { ModuleObject *umodeobj; - for (umodeobj = umode->owner->objects; umodeobj; umodeobj = umodeobj->next) { - if (umodeobj->type == MOBJ_UMODE && umodeobj->object.umode == umode) { + for (umodeobj = umode->owner->objects; umodeobj; umodeobj = umodeobj->next) + { + if (umodeobj->type == MOBJ_UMODE && umodeobj->object.umode == umode) + { DelListItem(umodeobj, umode->owner->objects); safe_free(umodeobj); break; @@ -227,7 +247,13 @@ void UmodeDel(Umode *umode) } umode->owner = NULL; } - return; + + /* Whether we can actually (already) free the Umode depends... */ + + if (loop.rehashing) + umode->unloaded = 1; + else + unload_usermode_commit(umode); } int umode_allow_all(Client *client, int what) @@ -270,41 +296,45 @@ int umode_hidle_allow(Client *client, int what) return 0; /* if set::hide-idle-time is 'never' or 'always' then +I makes no sense */ } -void unload_all_unused_umodes(void) +static void unload_usermode_commit(Umode *um) { - long removed_umode = 0; - int i; Client *client; - for (i = 0; i < UMODETABLESZ; i++) - { - if (Usermode_Table[i].unloaded) - removed_umode |= Usermode_Table[i].mode; - } - if (!removed_umode) /* Nothing was unloaded */ + long removed_umode; + + if (!um) return; + + removed_umode = um->mode; + + /* First send the -mode regarding all users */ list_for_each_entry(client, &lclient_list, lclient_node) { - long oldumode = 0; - if (!IsUser(client)) - continue; - oldumode = client->umodes; - client->umodes &= ~(removed_umode); - if (MyUser(client)) - send_umode_out(client, 1, oldumode); - } - for (i = 0; i < UMODETABLESZ; i++) - { - if (Usermode_Table[i].unloaded) + if (MyUser(client) && (client->umodes & removed_umode)) { - AllUmodes &= ~(Usermode_Table[i].mode); - SendUmodes &= ~(Usermode_Table[i].mode); - Usermode_Table[i].letter = '\0'; - Usermode_Table[i].unloaded = 0; + long oldumode = client->umodes; + client->umodes &= ~(removed_umode); + send_umode_out(client, 1, oldumode); } } + + /* Then unload the mode */ + DelListItem(um, usermodes); + safe_free(um); make_umodestr(); } +void unload_all_unused_umodes(void) +{ + Umode *um, *um_next; + + for (um=usermodes; um; um = um_next) + { + um_next = um->next; + if (um->letter && um->unloaded) + unload_usermode_commit(um); + } +} + /** * This function removes any oper-only snomasks when the user is no * longer an IRC Operator. @@ -323,14 +353,12 @@ void remove_all_snomasks(Client *client) */ void remove_oper_modes(Client *client) { - int i; + Umode *um; - for (i = 0; i <= Usermode_highest; i++) + for (um = usermodes; um; um = um->next) { - if (!Usermode_Table[i].letter) - continue; - if (Usermode_Table[i].unset_on_deoper) - client->umodes &= ~Usermode_Table[i].mode; + if (um->unset_on_deoper) + client->umodes &= ~um->mode; } /* Bit of a hack, since this is a dynamic permission umode */ @@ -350,15 +378,14 @@ void remove_oper_privileges(Client *client, int broadcast_mode_change) } /** Return long integer mode for a user mode character (eg: 'x' -> 0x10) */ -long find_user_mode(char flag) +long find_user_mode(char letter) { - int i; + Umode *um; + + for (um = usermodes; um; um = um->next) + if ((um->letter == letter) && !um->unloaded) + return um->mode; - for (i = 0; i < UMODETABLESZ; i++) - { - if ((Usermode_Table[i].letter == flag) && !(Usermode_Table[i].unloaded)) - return Usermode_Table[i].mode; - } return 0; } diff --git a/src/modules/mode.c b/src/modules/mode.c index 075b8b899..a1ba378a6 100644 --- a/src/modules/mode.c +++ b/src/modules/mode.c @@ -1079,7 +1079,7 @@ void _set_mode(Channel *channel, Client *client, int parc, const char *parv[], u */ CMD_FUNC(_cmd_umode) { - int i; + Umode *um; const char *m; Client *acptr; int what; @@ -1120,10 +1120,7 @@ CMD_FUNC(_cmd_umode) userhost_save_current(client); /* save host, in case we do any +x/-x or similar */ - /* find flags already set for user */ - for (i = 0; i <= Usermode_highest; i++) - if ((client->umodes & Usermode_Table[i].mode)) - oldumodes |= Usermode_Table[i].mode; + oldumodes = client->umodes; if (RESTRICT_USERMODES && MyUser(client) && !ValidatePermissionsForPath("immune:restrict-usermodes",client,NULL,NULL,NULL)) chk_restrict = 1; @@ -1238,24 +1235,23 @@ CMD_FUNC(_cmd_umode) break; default: def: - for (i = 0; i <= Usermode_highest; i++) + for (um = usermodes; um; um = um->next) { - if (*m == Usermode_Table[i].letter) + if (um->letter == *m) { - if (Usermode_Table[i].allowed) - if (!Usermode_Table[i].allowed(client,what)) + if (um->allowed && !um->allowed(client,what)) break; if (what == MODE_ADD) - client->umodes |= Usermode_Table[i].mode; + client->umodes |= um->mode; else - client->umodes &= ~Usermode_Table[i].mode; + client->umodes &= ~um->mode; break; } - else if (i == Usermode_highest && MyConnect(client) && !rpterror) - { - sendnumeric(client, ERR_UMODEUNKNOWNFLAG); - rpterror = 1; - } + } + if (!um && MyConnect(client) && !rpterror) + { + sendnumeric(client, ERR_UMODEUNKNOWNFLAG); + rpterror = 1; } break; } /* switch */ @@ -1273,19 +1269,16 @@ CMD_FUNC(_cmd_umode) int i; /* MODES */ - for (i = 0; i <= Usermode_highest; i++) + for (um = usermodes; um; um = um->next) { - if (!Usermode_Table[i].letter) - continue; - if (Usermode_Table[i].unset_on_deoper) + if (um->unset_on_deoper) { /* This is an oper mode. Is it set now and wasn't earlier? * then it needs to be stripped, as setting it is not * permitted. */ - long m = Usermode_Table[i].mode; - if ((client->umodes & m) && !(oldumodes & m)) - client->umodes &= ~Usermode_Table[i].mode; /* remove */ + if ((client->umodes & um->mode) && !(oldumodes & um->mode)) + client->umodes &= ~um->mode; /* remove */ } } diff --git a/src/modules/sendumode.c b/src/modules/sendumode.c index 82c97038a..18c7c4092 100644 --- a/src/modules/sendumode.c +++ b/src/modules/sendumode.c @@ -83,19 +83,7 @@ CMD_FUNC(cmd_sendumode) sendto_server(client, 0, 0, mtags, ":%s SENDUMODE %s :%s", client->id, parv[1], message); - for (p = parv[1]; *p; p++) - { - for(i = 0; i <= Usermode_highest; i++) - { - if (!Usermode_Table[i].letter) - continue; - if (Usermode_Table[i].letter == *p) - { - umode_s |= Usermode_Table[i].mode; - break; - } - } - } + umode_s = set_usermode(parv[1]); list_for_each_entry(acptr, &oper_list, special_node) { diff --git a/src/modules/svsmode.c b/src/modules/svsmode.c index 40505a12a..6d39641cf 100644 --- a/src/modules/svsmode.c +++ b/src/modules/svsmode.c @@ -317,11 +317,11 @@ void channel_svsmode(Client *client, int parc, const char *parv[]) */ void do_svsmode(Client *client, MessageTag *recv_mtags, int parc, const char *parv[], int show_change) { - int i; + Umode *um; const char *m; Client *target; int what; - long setflags = 0; + long oldumodes = 0; if (!IsULine(client)) return; @@ -342,10 +342,7 @@ void do_svsmode(Client *client, MessageTag *recv_mtags, int parc, const char *pa userhost_save_current(target); - /* initialize setflag to be the user's pre-SVSMODE flags */ - for (i = 0; i <= Usermode_highest; i++) - if (Usermode_Table[i].letter && (target->umodes & Usermode_Table[i].mode)) - setflags |= Usermode_Table[i].mode; + oldumodes = target->umodes; /* parse mode change string(s) */ for (m = parv[2]; *m; m++) @@ -505,16 +502,14 @@ void do_svsmode(Client *client, MessageTag *recv_mtags, int parc, const char *pa break; default: setmodex: - for (i = 0; i <= Usermode_highest; i++) + for (um = usermodes; um; um = um->next) { - if (!Usermode_Table[i].letter) - continue; - if (*m == Usermode_Table[i].letter) + if (um->letter == *m) { if (what == MODE_ADD) - target->umodes |= Usermode_Table[i].mode; + target->umodes |= um->mode; else - target->umodes &= ~Usermode_Table[i].mode; + target->umodes &= ~um->mode; break; } } @@ -531,15 +526,15 @@ void do_svsmode(Client *client, MessageTag *recv_mtags, int parc, const char *pa parv[1], parv[2]); /* Here we trigger the same hooks that cmd_mode does and, likewise, - only if the old flags (setflags) are different than the newly- + only if the old flags (oldumodes) are different than the newly- set ones */ - if (setflags != target->umodes) - RunHook(HOOKTYPE_UMODE_CHANGE, target, setflags, target->umodes); + if (oldumodes != target->umodes) + RunHook(HOOKTYPE_UMODE_CHANGE, target, oldumodes, target->umodes); if (show_change) { char buf[BUFSIZE]; - build_umode_string(target, setflags, ALL_UMODES, buf); + build_umode_string(target, oldumodes, ALL_UMODES, buf); if (MyUser(target) && *buf) sendto_one(target, NULL, ":%s MODE %s :%s", client->name, target->name, buf); } diff --git a/src/modules/who_old.c b/src/modules/who_old.c index 10769236c..d2a8572b6 100644 --- a/src/modules/who_old.c +++ b/src/modules/who_old.c @@ -330,17 +330,7 @@ static int parse_who_options(Client *client, int argc, const char **argv) else umodes = &wfl.umodes_dontwant; - while (*s) - { - int i; - for (i = 0; i <= Usermode_highest; i++) - if (*s == Usermode_Table[i].letter) - { - *umodes |= Usermode_Table[i].mode; - break; - } - s++; - } + *umodes = set_usermode(s); if (!IsOper(client)) *umodes = *umodes & UMODE_OPER; /* these are usermodes regular users may search for. just oper now. */ diff --git a/src/modules/whox.c b/src/modules/whox.c index ab9e1faaa..ca4239eea 100644 --- a/src/modules/whox.c +++ b/src/modules/whox.c @@ -295,7 +295,7 @@ CMD_FUNC(cmd_whox) while (*s) { - int i; + Umode *um; switch (*s) { @@ -317,11 +317,11 @@ CMD_FUNC(cmd_whox) else umodes = &fmt.noumodes; - for (i = 0; i <= Usermode_highest; i++) + for (um = usermodes; um; um = um->next) { - if (*s == Usermode_Table[i].letter) + if (um->letter == *s) { - *umodes |= Usermode_Table[i].mode; + *umodes |= um->mode; break; } } diff --git a/src/send.c b/src/send.c index 8286d7bfb..0878ce203 100644 --- a/src/send.c +++ b/src/send.c @@ -736,22 +736,13 @@ void sendto_umode_global(int umodes, FORMAT_STRING(const char *pattern), ...) { va_list vl; Client *acptr; + Umode *um; char nbuf[1024]; - int i; char modestr[128]; char *p; /* Convert 'umodes' (int) to 'modestr' (string) */ - *modestr = '\0'; - p = modestr; - for(i = 0; i <= Usermode_highest; i++) - { - if (!Usermode_Table[i].letter) - continue; - if (umodes & Usermode_Table[i].mode) - *p++ = Usermode_Table[i].letter; - } - *p = '\0'; + get_usermode_string_raw_r(umodes, modestr, sizeof(modestr)); list_for_each_entry(acptr, &lclient_list, lclient_node) { diff --git a/src/user.c b/src/user.c index 114b574c2..23e72200c 100644 --- a/src/user.c +++ b/src/user.c @@ -82,43 +82,43 @@ void iNAH_host(Client *client, const char *host) */ long set_usermode(const char *umode) { - int newumode; - int what; + Umode *um; + int newumode; + int what; const char *m; - int i; newumode = 0; what = MODE_ADD; for (m = umode; *m; m++) + { switch (*m) { - case '+': - what = MODE_ADD; - break; - case '-': - what = MODE_DEL; - break; - case ' ': - case '\n': - case '\r': - case '\t': - break; - default: - for (i = 0; i <= Usermode_highest; i++) - { - if (!Usermode_Table[i].letter) - continue; - if (*m == Usermode_Table[i].letter) - { - if (what == MODE_ADD) - newumode |= Usermode_Table[i].mode; - else - newumode &= ~Usermode_Table[i].mode; - } - } + case '+': + what = MODE_ADD; + break; + case '-': + what = MODE_DEL; + break; + case ' ': + case '\n': + case '\r': + case '\t': + break; + default: + for (um = usermodes; um; um = um->next) + { + if (um->letter == *m) + { + if (what == MODE_ADD) + newumode |= um->mode; + else + newumode &= ~um->mode; + } + } } + } - return (newumode); + return newumode; } /** Convert a target pointer to an 8 bit hash, used for target limiting. */ @@ -257,15 +257,13 @@ char *canonize(const char *buffer) const char *get_usermode_string(Client *client) { static char buf[128]; - int i; - char *m; + Umode *um; + + strlcpy(buf, "+", sizeof(buf)); + for (um = usermodes; um; um = um->next) + if (client->umodes & um->mode) + strlcat_letter(buf, um->letter, sizeof(buf)); - m = buf; - *m++ = '+'; - for (i = 0; (i <= Usermode_highest) && (m - buf < sizeof(buf) - 4); i++) - if (Usermode_Table[i].letter && (client->umodes & Usermode_Table[i].mode)) - *m++ = Usermode_Table[i].letter; - *m = '\0'; return buf; } @@ -273,21 +271,20 @@ const char *get_usermode_string(Client *client) * @param client The client * @param buf The buffer to write to * @param buflen The size of the buffer - * @returns string of user modes (temporary storage) + * @returns string of user modes (buf) */ const char *get_usermode_string_r(Client *client, char *buf, size_t buflen) { - int i; + Umode *um; strlcpy(buf, "+", buflen); - for (i = 0; i <= Usermode_highest; i++) - if (Usermode_Table[i].letter && (client->umodes & Usermode_Table[i].mode)) - strlcat_letter(buf, Usermode_Table[i].letter, buflen); + for (um = usermodes; um; um = um->next) + if (client->umodes & um->mode) + strlcat_letter(buf, um->letter, buflen); return buf; } - /** Get user modes as a string - this one does not work on 'client' but directly on 'umodes'. * @param umodes The user modes that are set * @returns string of user modes (temporary storage) @@ -295,19 +292,35 @@ const char *get_usermode_string_r(Client *client, char *buf, size_t buflen) const char *get_usermode_string_raw(long umodes) { static char buf[128]; - int i; - char *m; + Umode *um; + + strlcpy(buf, "+", sizeof(buf)); + for (um = usermodes; um; um = um->next) + if (umodes & um->mode) + strlcat_letter(buf, um->letter, sizeof(buf)); - m = buf; - *m++ = '+'; - for (i = 0; (i <= Usermode_highest) && (m - buf < sizeof(buf) - 4); i++) - - if (Usermode_Table[i].letter && (umodes & Usermode_Table[i].mode)) - *m++ = Usermode_Table[i].letter; - *m = '\0'; return buf; } +/** Get user modes as a string - this one does not work on 'client' but directly on 'umodes'. + * @param umodes The user modes that are set + * @param buf The buffer to write to + * @param buflen The size of the buffer + * @returns string of user modes (buf) + */ +const char *get_usermode_string_raw_r(long umodes, char *buf, size_t buflen) +{ + Umode *um; + + strlcpy(buf, "+", buflen); + for (um = usermodes; um; um = um->next) + if (umodes & um->mode) + strlcat_letter(buf, um->letter, buflen); + + return buf; +} + + /** Set a new snomask on the user. * The user is not informed of the change by this function. * @param client The client @@ -355,7 +368,7 @@ void set_snomask(Client *client, const char *snomask) */ void build_umode_string(Client *client, long old, long sendmask, char *umode_buf) { - int i; + Umode *um; long flag; char *m; int what = MODE_NULL; @@ -366,33 +379,31 @@ void build_umode_string(Client *client, long old, long sendmask, char *umode_buf */ m = umode_buf; *m = '\0'; - for (i = 0; i <= Usermode_highest; i++) + for (um = usermodes; um; um = um->next) { - if (!Usermode_Table[i].letter) - continue; - flag = Usermode_Table[i].mode; + flag = um->mode; if (MyUser(client) && !(flag & sendmask)) continue; if ((flag & old) && !(client->umodes & flag)) { if (what == MODE_DEL) - *m++ = Usermode_Table[i].letter; + *m++ = um->letter; else { what = MODE_DEL; *m++ = '-'; - *m++ = Usermode_Table[i].letter; + *m++ = um->letter; } } else if (!(flag & old) && (client->umodes & flag)) { if (what == MODE_ADD) - *m++ = Usermode_Table[i].letter; + *m++ = um->letter; else { what = MODE_ADD; *m++ = '+'; - *m++ = Usermode_Table[i].letter; + *m++ = um->letter; } } }