From 5e7f9882dd5edfb8050d8fc487ba1378cbcb27dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Janouch?= Date: Thu, 9 Jul 2015 02:46:31 +0200 Subject: [PATCH] degesch: "nickname" -> "nicks" Now you can specify multiple nicknames to try. --- degesch.c | 72 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 13 deletions(-) diff --git a/degesch.c b/degesch.c index ef77c2a..26a1cbd 100644 --- a/degesch.c +++ b/degesch.c @@ -1138,6 +1138,7 @@ struct server struct str_map irc_buffer_map; ///< Maps IRC identifiers to buffers struct user *irc_user; ///< Our own user + int nick_counter; ///< Iterates "nicks" when registering struct str irc_user_mode; ///< Our current user modes char *irc_user_host; ///< Our current user@host @@ -1488,9 +1489,9 @@ config_validate_nonnegative static struct config_schema g_config_server[] = { - { .name = "nickname", + { .name = "nicks", .comment = "IRC nickname", - .type = CONFIG_ITEM_STRING, + .type = CONFIG_ITEM_STRING_ARRAY, .validate = config_validate_nonjunk_string }, { .name = "username", .comment = "IRC user name", @@ -4003,11 +4004,11 @@ static struct transport g_transport_tls = static bool irc_autofill_user_info (struct server *s, struct error **e) { - const char *nickname = get_config_string (s->config, "nickname"); + const char *nicks = get_config_string (s->config, "nicks"); const char *username = get_config_string (s->config, "username"); const char *realname = get_config_string (s->config, "realname"); - if (nickname && username && realname) + if (nicks && *nicks && username && *username && realname) return true; // Read POSIX user info and fill the configuration if needed @@ -4016,9 +4017,9 @@ irc_autofill_user_info (struct server *s, struct error **e) FAIL ("cannot retrieve user information: %s", strerror (errno)); // FIXME: set_config_strings() writes errors on its own - if (!nickname) - set_config_string (s->config, "nickname", pwd->pw_name); - if (!username) + if (!nicks || !*nicks) + set_config_string (s->config, "nicks", pwd->pw_name); + if (!username || !*username) set_config_string (s->config, "username", pwd->pw_name); // Not all systems have the GECOS field but the vast majority does @@ -4037,16 +4038,33 @@ irc_autofill_user_info (struct server *s, struct error **e) return true; } +static char * +irc_fetch_next_nickname (struct server *s) +{ + struct str_vector v; + str_vector_init (&v); + split_str_ignore_empty (get_config_string (s->config, "nicks"), ',', &v); + + char *result = NULL; + if (s->nick_counter >= 0 && s->nick_counter < v.len) + result = str_vector_steal (&v, s->nick_counter++); + if (s->nick_counter >= v.len) + // Exhausted all nicknames + s->nick_counter = -1; + + str_vector_free (&v); + return result; +} + static void irc_register (struct server *s) { // Fill in user information automatically if needed irc_autofill_user_info (s, NULL); - const char *nickname = get_config_string (s->config, "nickname"); const char *username = get_config_string (s->config, "username"); const char *realname = get_config_string (s->config, "realname"); - hard_assert (nickname && username && realname); + hard_assert (username && realname); // Start IRCv3.1 capability negotiation; // at worst the server will ignore this or send a harmless error message @@ -4056,7 +4074,15 @@ irc_register (struct server *s) if (password) irc_send (s, "PASS :%s", password); - irc_send (s, "NICK %s", nickname); + s->nick_counter = 0; + + char *nickname = irc_fetch_next_nickname (s); + if (nickname) + irc_send (s, "NICK :%s", nickname); + else + log_server_error (s, s->buffer, "No nicks present in configuration"); + free (nickname); + // IRC servers may ignore the last argument if it's empty irc_send (s, "USER %s 8 * :%s", username, *realname ? realname : " "); } @@ -5647,6 +5673,28 @@ irc_handle_rpl_inviting (struct server *s, const struct irc_message *msg) "You have invited #n to #S", nickname, channel_name); } +static void +irc_handle_err_nicknameinuse (struct server *s, const struct irc_message *msg) +{ + if (msg->params.len < 2) + return; + + log_server_error (s, s->buffer, + "Nickname is already in use: #S", msg->params.vector[1]); + + // Only do this while we haven't successfully registered yet + if (s->state != IRC_CONNECTED) + return; + + char *nickname = irc_fetch_next_nickname (s); + if (nickname) + { + log_server_status (s, s->buffer, "Retrying with #s...", nickname); + irc_send (s, "NICK :%s", nickname); + free (nickname); + } +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - static void @@ -5848,9 +5896,7 @@ irc_process_numeric (struct server *s, irc_handle_rpl_inviting (s, msg); buffer = NULL; break; case IRC_ERR_NICKNAMEINUSE: - // TODO: if (state == IRC_CONNECTED), use a different nick; - // either use a number suffix, or accept commas in "nickname" config - break; + irc_handle_err_nicknameinuse (s, msg); buffer = NULL; break; case IRC_RPL_LIST: case IRC_RPL_WHOREPLY: