mirror of
https://github.com/weechat/weechat.git
synced 2026-06-12 14:14:48 +02:00
irc: revert compute of nick colors to case sensitive way, deprecate again IRC nick color infos (issue #194, issue #2032)
This commit is contained in:
@@ -18,6 +18,10 @@ New features::
|
||||
* core, alias, trigger: allow wildcard in commands `/bar`, `/item`, `/proxy`, `/alias` and `/trigger` (issue #1956)
|
||||
* irc: add option irc.look.ignore_tag_messages (issue #989)
|
||||
|
||||
Bug fixes::
|
||||
|
||||
* irc: revert compute of nick colors to case sensitive way, deprecate again infos "irc_nick_color" and "irc_nick_color_name" (issue #194, issue #2032)
|
||||
|
||||
Build::
|
||||
|
||||
* core: make libintl.h required if CMake option ENABLE_NLS is enabled (issue #2031)
|
||||
|
||||
@@ -11,6 +11,16 @@ It is recommended to read it when upgrading to a new stable version. +
|
||||
For a complete list of changes, please look at ChangeLog.
|
||||
|
||||
|
||||
[[v4.2.0]]
|
||||
== Version 4.2.0 (under dev)
|
||||
|
||||
[[v4.2.0_nick_color_infos]]
|
||||
=== Nick color infos
|
||||
|
||||
The infos irc_nick_color and irc_nick_color_name are deprecated again, and the
|
||||
algorithm to compute IRC nick colors has been reverted to case sensitive. +
|
||||
The server name has been removed from arguments.
|
||||
|
||||
[[v4.1.0]]
|
||||
== Version 4.1.0 (2023-10-15)
|
||||
|
||||
|
||||
@@ -248,8 +248,7 @@ irc_config_compute_nick_colors ()
|
||||
{
|
||||
if (ptr_nick->color)
|
||||
free (ptr_nick->color);
|
||||
ptr_nick->color = irc_nick_find_color (ptr_server,
|
||||
ptr_nick->name);
|
||||
ptr_nick->color = irc_nick_find_color (ptr_nick->name);
|
||||
}
|
||||
}
|
||||
if (ptr_channel->pv_remote_nick_color)
|
||||
|
||||
@@ -1384,7 +1384,7 @@ irc_ctcp_recv (struct t_irc_protocol_ctxt *ctxt,
|
||||
if (ptr_nick)
|
||||
nick_color = strdup (ptr_nick->color);
|
||||
else if (ctxt->nick)
|
||||
nick_color = irc_nick_find_color (ctxt->server, ctxt->nick);
|
||||
nick_color = irc_nick_find_color (ctxt->nick);
|
||||
else
|
||||
nick_color = strdup (IRC_COLOR_CHAT_NICK);
|
||||
if ((ctxt->num_params > 0)
|
||||
@@ -1477,7 +1477,7 @@ irc_ctcp_recv (struct t_irc_protocol_ctxt *ctxt,
|
||||
weechat_prefix ("action"),
|
||||
(ctxt->nick_is_me) ?
|
||||
IRC_COLOR_CHAT_NICK_SELF : irc_nick_color_for_pv (
|
||||
ctxt->server, ptr_channel, ctxt->nick),
|
||||
ptr_channel, ctxt->nick),
|
||||
ctxt->nick,
|
||||
(pos_args) ? IRC_COLOR_RESET : "",
|
||||
(pos_args) ? " " : "",
|
||||
|
||||
@@ -197,10 +197,6 @@ irc_info_info_irc_nick_color_cb (const void *pointer, void *data,
|
||||
const char *info_name,
|
||||
const char *arguments)
|
||||
{
|
||||
char *pos_comma, *server;
|
||||
const char *pos_nick;
|
||||
struct t_irc_server *ptr_server;
|
||||
|
||||
/* make C compiler happy */
|
||||
(void) pointer;
|
||||
(void) data;
|
||||
@@ -209,21 +205,7 @@ irc_info_info_irc_nick_color_cb (const void *pointer, void *data,
|
||||
if (!arguments || !arguments[0])
|
||||
return NULL;
|
||||
|
||||
ptr_server = NULL;
|
||||
pos_nick = arguments;
|
||||
pos_comma = strchr (arguments, ',');
|
||||
if (pos_comma)
|
||||
{
|
||||
pos_nick = pos_comma + 1;
|
||||
server = weechat_strndup (arguments, pos_comma - arguments);
|
||||
if (server)
|
||||
{
|
||||
ptr_server = irc_server_search (server);
|
||||
free (server);
|
||||
}
|
||||
}
|
||||
|
||||
return irc_nick_find_color (ptr_server, pos_nick);
|
||||
return irc_nick_find_color (arguments);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -235,10 +217,6 @@ irc_info_info_irc_nick_color_name_cb (const void *pointer, void *data,
|
||||
const char *info_name,
|
||||
const char *arguments)
|
||||
{
|
||||
char *pos_comma, *server;
|
||||
const char *pos_nick;
|
||||
struct t_irc_server *ptr_server;
|
||||
|
||||
/* make C compiler happy */
|
||||
(void) pointer;
|
||||
(void) data;
|
||||
@@ -247,21 +225,7 @@ irc_info_info_irc_nick_color_name_cb (const void *pointer, void *data,
|
||||
if (!arguments || !arguments[0])
|
||||
return NULL;
|
||||
|
||||
ptr_server = NULL;
|
||||
pos_nick = arguments;
|
||||
pos_comma = strchr (arguments, ',');
|
||||
if (pos_comma)
|
||||
{
|
||||
pos_nick = pos_comma + 1;
|
||||
server = weechat_strndup (arguments, pos_comma - arguments);
|
||||
if (server)
|
||||
{
|
||||
ptr_server = irc_server_search (server);
|
||||
free (server);
|
||||
}
|
||||
}
|
||||
|
||||
return irc_nick_find_color_name (ptr_server, pos_nick);
|
||||
return irc_nick_find_color_name (arguments);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1259,19 +1223,15 @@ irc_info_init ()
|
||||
&irc_info_info_irc_nick_from_host_cb, NULL, NULL);
|
||||
weechat_hook_info (
|
||||
"irc_nick_color",
|
||||
N_("get nick color code, ignoring case (this calls the info "
|
||||
"\"nick_color_ignore_case\" with appropriate range, according "
|
||||
"to the value of CASEMAPPING on the server, "
|
||||
"defaulting to \"rfc1459\" if the server is not given)"),
|
||||
N_("server,nickname (server is optional)"),
|
||||
N_("get nick color code "
|
||||
"(*deprecated* since version 1.5, replaced by \"nick_color\")"),
|
||||
N_("nickname"),
|
||||
&irc_info_info_irc_nick_color_cb, NULL, NULL);
|
||||
weechat_hook_info (
|
||||
"irc_nick_color_name",
|
||||
N_("get nick color name, ignoring case (this calls the info "
|
||||
"\"nick_color_name_ignore_case\" with appropriate range, according "
|
||||
"to the value of CASEMAPPING on the server, "
|
||||
"defaulting to \"rfc1459\" if the server is not given)"),
|
||||
N_("server,nickname (server is optional)"),
|
||||
N_("get nick color name "
|
||||
"(*deprecated* since version 1.5, replaced by \"nick_color_name\")"),
|
||||
N_("nickname"),
|
||||
&irc_info_info_irc_nick_color_name_cb, NULL, NULL);
|
||||
weechat_hook_info (
|
||||
"irc_buffer",
|
||||
|
||||
+11
-38
@@ -139,22 +139,9 @@ irc_nick_is_nick (struct t_irc_server *server, const char *string)
|
||||
*/
|
||||
|
||||
char *
|
||||
irc_nick_find_color (struct t_irc_server *server, const char *nickname)
|
||||
irc_nick_find_color (const char *nickname)
|
||||
{
|
||||
char str_args[4096];
|
||||
int casemapping, range;
|
||||
|
||||
casemapping = (server) ? server->casemapping : -1;
|
||||
if ((casemapping < 0) || (casemapping >= IRC_SERVER_NUM_CASEMAPPING))
|
||||
casemapping = IRC_SERVER_CASEMAPPING_RFC1459;
|
||||
range = irc_server_casemapping_range[casemapping];
|
||||
|
||||
snprintf (str_args, sizeof (str_args),
|
||||
"%s;%d",
|
||||
(nickname) ? nickname : "",
|
||||
range);
|
||||
|
||||
return weechat_info_get ("nick_color_ignore_case", str_args);
|
||||
return weechat_info_get ("nick_color", nickname);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -164,22 +151,9 @@ irc_nick_find_color (struct t_irc_server *server, const char *nickname)
|
||||
*/
|
||||
|
||||
char *
|
||||
irc_nick_find_color_name (struct t_irc_server *server, const char *nickname)
|
||||
irc_nick_find_color_name (const char *nickname)
|
||||
{
|
||||
char str_args[4096];
|
||||
int casemapping, range;
|
||||
|
||||
casemapping = (server) ? server->casemapping : -1;
|
||||
if ((casemapping < 0) || (casemapping >= IRC_SERVER_NUM_CASEMAPPING))
|
||||
casemapping = IRC_SERVER_CASEMAPPING_RFC1459;
|
||||
range = irc_server_casemapping_range[casemapping];
|
||||
|
||||
snprintf (str_args, sizeof (str_args),
|
||||
"%s;%d",
|
||||
(nickname) ? nickname: "",
|
||||
range);
|
||||
|
||||
return weechat_info_get ("nick_color_name_ignore_case", str_args);
|
||||
return weechat_info_get ("nick_color_name", nickname);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -422,7 +396,7 @@ irc_nick_get_color_for_nicklist (struct t_irc_server *server,
|
||||
if (irc_server_strcasecmp (server, nick->name, server->nick) == 0)
|
||||
return strdup (nick_color_self);
|
||||
else
|
||||
return irc_nick_find_color_name (server, nick->name);
|
||||
return irc_nick_find_color_name (nick->name);
|
||||
}
|
||||
|
||||
return strdup (nick_color_bar_fg);
|
||||
@@ -620,7 +594,7 @@ irc_nick_new (struct t_irc_server *server, struct t_irc_channel *channel,
|
||||
if (irc_server_strcasecmp (server, new_nick->name, server->nick) == 0)
|
||||
new_nick->color = strdup (IRC_COLOR_CHAT_NICK_SELF);
|
||||
else
|
||||
new_nick->color = irc_nick_find_color (server, new_nick->name);
|
||||
new_nick->color = irc_nick_find_color (new_nick->name);
|
||||
|
||||
/* add nick to end of list */
|
||||
new_nick->prev_nick = channel->last_nick;
|
||||
@@ -669,7 +643,7 @@ irc_nick_change (struct t_irc_server *server, struct t_irc_channel *channel,
|
||||
if (nick_is_me)
|
||||
nick->color = strdup (IRC_COLOR_CHAT_NICK_SELF);
|
||||
else
|
||||
nick->color = irc_nick_find_color (server, nick->name);
|
||||
nick->color = irc_nick_find_color (nick->name);
|
||||
|
||||
/* add nick in nicklist */
|
||||
irc_nick_nicklist_add (server, channel, nick);
|
||||
@@ -1011,7 +985,7 @@ irc_nick_as_prefix (struct t_irc_server *server, struct t_irc_nick *nick,
|
||||
else if (nick)
|
||||
color = strdup (nick->color);
|
||||
else if (nickname)
|
||||
color = irc_nick_find_color (server, nickname);
|
||||
color = irc_nick_find_color (nickname);
|
||||
else
|
||||
color = strdup (IRC_COLOR_CHAT_NICK);
|
||||
|
||||
@@ -1054,7 +1028,7 @@ irc_nick_color_for_msg (struct t_irc_server *server, int server_message,
|
||||
{
|
||||
return IRC_COLOR_CHAT_NICK_SELF;
|
||||
}
|
||||
color_found = irc_nick_find_color (server, nickname);
|
||||
color_found = irc_nick_find_color (nickname);
|
||||
index_color = (index_color + 1) % 16;
|
||||
snprintf (color[index_color], sizeof (color[index_color]),
|
||||
"%s",
|
||||
@@ -1072,13 +1046,12 @@ irc_nick_color_for_msg (struct t_irc_server *server, int server_message,
|
||||
*/
|
||||
|
||||
const char *
|
||||
irc_nick_color_for_pv (struct t_irc_server *server,
|
||||
struct t_irc_channel *channel, const char *nickname)
|
||||
irc_nick_color_for_pv (struct t_irc_channel *channel, const char *nickname)
|
||||
{
|
||||
if (weechat_config_boolean (irc_config_look_color_pv_nick_like_channel))
|
||||
{
|
||||
if (!channel->pv_remote_nick_color)
|
||||
channel->pv_remote_nick_color = irc_nick_find_color (server, nickname);
|
||||
channel->pv_remote_nick_color = irc_nick_find_color (nickname);
|
||||
if (channel->pv_remote_nick_color)
|
||||
return channel->pv_remote_nick_color;
|
||||
}
|
||||
|
||||
@@ -49,10 +49,8 @@ struct t_irc_nick
|
||||
extern int irc_nick_valid (struct t_irc_channel *channel,
|
||||
struct t_irc_nick *nick);
|
||||
extern int irc_nick_is_nick (struct t_irc_server *server, const char *string);
|
||||
extern char *irc_nick_find_color (struct t_irc_server *server,
|
||||
const char *nickname);
|
||||
extern char *irc_nick_find_color_name (struct t_irc_server *server,
|
||||
const char *nickname);
|
||||
extern char *irc_nick_find_color (const char *nickname);
|
||||
extern char *irc_nick_find_color_name (const char *nickname);
|
||||
extern void irc_nick_set_host (struct t_irc_nick *nick, const char *host);
|
||||
extern int irc_nick_is_op_or_higher (struct t_irc_server *server,
|
||||
struct t_irc_nick *nick);
|
||||
@@ -103,8 +101,7 @@ extern const char *irc_nick_color_for_msg (struct t_irc_server *server,
|
||||
int server_message,
|
||||
struct t_irc_nick *nick,
|
||||
const char *nickname);
|
||||
extern const char * irc_nick_color_for_pv (struct t_irc_server *server,
|
||||
struct t_irc_channel *channel,
|
||||
extern const char * irc_nick_color_for_pv (struct t_irc_channel *channel,
|
||||
const char *nickname);
|
||||
extern char *irc_nick_default_ban_mask (struct t_irc_nick *nick);
|
||||
extern struct t_hdata *irc_nick_hdata_nick_cb (const void *pointer,
|
||||
|
||||
@@ -2281,8 +2281,8 @@ IRC_PROTOCOL_CALLBACK(nick)
|
||||
{
|
||||
if (weechat_config_boolean (irc_config_look_color_pv_nick_like_channel))
|
||||
{
|
||||
old_color = irc_nick_find_color (ctxt->server, ctxt->nick);
|
||||
new_color = irc_nick_find_color (ctxt->server, ctxt->params[0]);
|
||||
old_color = irc_nick_find_color (ctxt->nick);
|
||||
new_color = irc_nick_find_color (ctxt->params[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3144,7 +3144,6 @@ IRC_PROTOCOL_CALLBACK(privmsg)
|
||||
else
|
||||
{
|
||||
color = irc_nick_find_color_name (
|
||||
ctxt->server,
|
||||
(ptr_nick) ? ptr_nick->name : ctxt->nick);
|
||||
str_color = irc_color_for_tags (color);
|
||||
if (color)
|
||||
@@ -3257,7 +3256,7 @@ IRC_PROTOCOL_CALLBACK(privmsg)
|
||||
{
|
||||
if (weechat_config_boolean (irc_config_look_color_pv_nick_like_channel))
|
||||
{
|
||||
color = irc_nick_find_color_name (ctxt->server, ctxt->nick);
|
||||
color = irc_nick_find_color_name (ctxt->nick);
|
||||
str_color = irc_color_for_tags (color);
|
||||
if (color)
|
||||
free (color);
|
||||
@@ -3310,8 +3309,7 @@ IRC_PROTOCOL_CALLBACK(privmsg)
|
||||
irc_nick_as_prefix (
|
||||
ctxt->server, NULL, ctxt->nick,
|
||||
(ctxt->nick_is_me) ?
|
||||
IRC_COLOR_CHAT_NICK_SELF : irc_nick_color_for_pv (ctxt->server,
|
||||
ptr_channel,
|
||||
IRC_COLOR_CHAT_NICK_SELF : irc_nick_color_for_pv (ptr_channel,
|
||||
ctxt->nick)),
|
||||
(msg_args2) ? msg_args2 : msg_args);
|
||||
}
|
||||
@@ -3404,7 +3402,7 @@ IRC_PROTOCOL_CALLBACK(quit)
|
||||
_("%s%s%s%s%s%s%s%s%s%s has quit %s(%s%s%s)"),
|
||||
weechat_prefix ("quit"),
|
||||
(ptr_channel->type == IRC_CHANNEL_TYPE_PRIVATE) ?
|
||||
irc_nick_color_for_pv (ctxt->server, ptr_channel, ctxt->nick) :
|
||||
irc_nick_color_for_pv (ptr_channel, ctxt->nick) :
|
||||
irc_nick_color_for_msg (ctxt->server, 1, ptr_nick, ctxt->nick),
|
||||
ctxt->nick,
|
||||
IRC_COLOR_CHAT_DELIMITERS,
|
||||
@@ -3438,7 +3436,7 @@ IRC_PROTOCOL_CALLBACK(quit)
|
||||
_("%s%s%s%s%s%s%s%s%s%s has quit"),
|
||||
weechat_prefix ("quit"),
|
||||
(ptr_channel->type == IRC_CHANNEL_TYPE_PRIVATE) ?
|
||||
irc_nick_color_for_pv (ctxt->server, ptr_channel, ctxt->nick) :
|
||||
irc_nick_color_for_pv (ptr_channel, ctxt->nick) :
|
||||
irc_nick_color_for_msg (ctxt->server, 1, ptr_nick, ctxt->nick),
|
||||
ctxt->nick,
|
||||
IRC_COLOR_CHAT_DELIMITERS,
|
||||
@@ -6255,7 +6253,7 @@ IRC_PROTOCOL_CALLBACK(353)
|
||||
}
|
||||
else
|
||||
{
|
||||
color = irc_nick_find_color (ctxt->server, nickname);
|
||||
color = irc_nick_find_color (nickname);
|
||||
weechat_string_dyn_concat (str_nicks, color, -1);
|
||||
if (color)
|
||||
free (color);
|
||||
@@ -6543,7 +6541,7 @@ irc_protocol_get_string_channel_nicks (struct t_irc_server *server,
|
||||
}
|
||||
else
|
||||
{
|
||||
color = irc_nick_find_color (server, nickname);
|
||||
color = irc_nick_find_color (nickname);
|
||||
weechat_string_dyn_concat (str_nicks, color, -1);
|
||||
if (color)
|
||||
free (color);
|
||||
|
||||
@@ -184,76 +184,7 @@ TEST(IrcNick, IsNick)
|
||||
|
||||
TEST(IrcNick, IrcNickFindColor)
|
||||
{
|
||||
struct t_irc_server *server;
|
||||
char *str, str_color[128];
|
||||
|
||||
server = irc_server_alloc ("my_ircd");
|
||||
CHECK(server);
|
||||
|
||||
snprintf (str_color, sizeof (str_color), "%s", gui_color_get_custom ("default"));
|
||||
|
||||
WEE_TEST_STR(str_color, irc_nick_find_color (NULL, NULL));
|
||||
WEE_TEST_STR("default", irc_nick_find_color_name (NULL, NULL));
|
||||
|
||||
WEE_TEST_STR(str_color, irc_nick_find_color (server, NULL));
|
||||
WEE_TEST_STR("default", irc_nick_find_color_name (server, NULL));
|
||||
|
||||
snprintf (str_color, sizeof (str_color), "%s", gui_color_get_custom ("142"));
|
||||
WEE_TEST_STR(str_color, irc_nick_find_color (NULL, "NICK]"));
|
||||
WEE_TEST_STR(str_color, irc_nick_find_color (NULL, "nick}"));
|
||||
WEE_TEST_STR("142", irc_nick_find_color_name (NULL, "NICK]"));
|
||||
WEE_TEST_STR("142", irc_nick_find_color_name (NULL, "nick}"));
|
||||
|
||||
snprintf (str_color, sizeof (str_color), "%s", gui_color_get_custom ("lightcyan"));
|
||||
WEE_TEST_STR(str_color, irc_nick_find_color (NULL, "NICK^A"));
|
||||
WEE_TEST_STR(str_color, irc_nick_find_color (NULL, "nick~a"));
|
||||
WEE_TEST_STR("lightcyan", irc_nick_find_color_name (NULL, "NICK^A"));
|
||||
WEE_TEST_STR("lightcyan", irc_nick_find_color_name (NULL, "nick~a"));
|
||||
|
||||
snprintf (str_color, sizeof (str_color), "%s", gui_color_get_custom ("142"));
|
||||
WEE_TEST_STR(str_color, irc_nick_find_color (server, "NICK]"));
|
||||
WEE_TEST_STR(str_color, irc_nick_find_color (server, "nick}"));
|
||||
WEE_TEST_STR("142", irc_nick_find_color_name (server, "NICK]"));
|
||||
WEE_TEST_STR("142", irc_nick_find_color_name (server, "nick}"));
|
||||
|
||||
snprintf (str_color, sizeof (str_color), "%s", gui_color_get_custom ("lightcyan"));
|
||||
WEE_TEST_STR(str_color, irc_nick_find_color (server, "NICK^A"));
|
||||
WEE_TEST_STR(str_color, irc_nick_find_color (server, "nick~a"));
|
||||
WEE_TEST_STR("lightcyan", irc_nick_find_color_name (server, "NICK^A"));
|
||||
WEE_TEST_STR("lightcyan", irc_nick_find_color_name (server, "nick~a"));
|
||||
|
||||
server->casemapping = IRC_SERVER_CASEMAPPING_STRICT_RFC1459;
|
||||
|
||||
snprintf (str_color, sizeof (str_color), "%s", gui_color_get_custom ("142"));
|
||||
WEE_TEST_STR(str_color, irc_nick_find_color (server, "NICK]"));
|
||||
WEE_TEST_STR(str_color, irc_nick_find_color (server, "nick}"));
|
||||
WEE_TEST_STR("142", irc_nick_find_color_name (server, "NICK]"));
|
||||
WEE_TEST_STR("142", irc_nick_find_color_name (server, "nick}"));
|
||||
|
||||
snprintf (str_color, sizeof (str_color), "%s", gui_color_get_custom ("176"));
|
||||
WEE_TEST_STR(str_color, irc_nick_find_color (server, "NICK^A"));
|
||||
snprintf (str_color, sizeof (str_color), "%s", gui_color_get_custom ("lightcyan"));
|
||||
WEE_TEST_STR(str_color, irc_nick_find_color (server, "nick~a"));
|
||||
WEE_TEST_STR("176", irc_nick_find_color_name (server, "NICK^A"));
|
||||
WEE_TEST_STR("lightcyan", irc_nick_find_color_name (server, "nick~a"));
|
||||
|
||||
server->casemapping = IRC_SERVER_CASEMAPPING_ASCII;
|
||||
|
||||
snprintf (str_color, sizeof (str_color), "%s", gui_color_get_custom ("169"));
|
||||
WEE_TEST_STR(str_color, irc_nick_find_color (server, "NICK]"));
|
||||
snprintf (str_color, sizeof (str_color), "%s", gui_color_get_custom ("142"));
|
||||
WEE_TEST_STR(str_color, irc_nick_find_color (server, "nick}"));
|
||||
WEE_TEST_STR("169", irc_nick_find_color_name (server, "NICK]"));
|
||||
WEE_TEST_STR("142", irc_nick_find_color_name (server, "nick}"));
|
||||
|
||||
snprintf (str_color, sizeof (str_color), "%s", gui_color_get_custom ("176"));
|
||||
WEE_TEST_STR(str_color, irc_nick_find_color (server, "NICK^A"));
|
||||
snprintf (str_color, sizeof (str_color), "%s", gui_color_get_custom ("lightcyan"));
|
||||
WEE_TEST_STR(str_color, irc_nick_find_color (server, "nick~a"));
|
||||
WEE_TEST_STR("176", irc_nick_find_color_name (server, "NICK^A"));
|
||||
WEE_TEST_STR("lightcyan", irc_nick_find_color_name (server, "nick~a"));
|
||||
|
||||
irc_server_free (server);
|
||||
/* TODO: write tests */
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user