Commit message: irc: fix crash when a new message 005 is received with longer nick prefixes

Thanks to Stuart Nevans Locke for reporting the issue.
CVE: CVE-2020-9760
CWE: CWE-787
Vulnerability description: An issue was discovered in WeeChat before 2.7.1 (0.3.4 to 2.7 are affected). When a new IRC message 005 is received with longer nick prefixes, a buffer overflow and possibly a crash can happen when a new mode is set for a nick.

Project: weechat
File: irc-nick.c

irc_nick_nicklist_remove (struct t_irc_server *server,
                          struct t_irc_channel *channel,
                          struct t_irc_nick *nick)
{
    struct t_gui_nick_group *ptr_group;

    ptr_group = irc_nick_get_nicklist_group (server, channel->buffer, nick);
    weechat_nicklist_remove_nick (channel->buffer,
                                  weechat_nicklist_search_nick (channel->buffer,
                                                                ptr_group,
                                                                nick->name));
}

Project: weechat
File: irc-server.c

irc_server_check_join_smart_filtered_cb (void *data,
                                         struct t_hashtable *hashtable,
                                         const void *key, const void *value)
{
    int unmask_delay;

    /* make C compiler happy */
    (void) data;

    unmask_delay = weechat_config_integer (irc_config_look_smart_filter_join_unmask);
    if ((unmask_delay == 0)
        || (*((time_t *)value) < time (NULL) - (unmask_delay * 60)))
    {
        weechat_hashtable_remove (hashtable, key);
    }
}

Project: weechat
File: irc-server.c

irc_server_timer_sasl_cb (const void *pointer, void *data, int remaining_calls)
{
    struct t_irc_server *server;
    int sasl_fail;

    /* make C compiler happy */
    (void) data;
    (void) remaining_calls;

    server = (struct t_irc_server *)pointer;

    if (!server)
        return WEECHAT_RC_ERROR;

    server->hook_timer_sasl = NULL;

    if (!server->is_connected)
    {
        weechat_printf (server->buffer,
                        _("%s%s: SASL authentication timeout"),
                        weechat_prefix ("error"), IRC_PLUGIN_NAME);
        sasl_fail = IRC_SERVER_OPTION_INTEGER(server,
                                              IRC_SERVER_OPTION_SASL_FAIL);
        if ((sasl_fail == IRC_SERVER_SASL_FAIL_RECONNECT)
            || (sasl_fail == IRC_SERVER_SASL_FAIL_DISCONNECT))
        {
            irc_server_disconnect (
                server, 0,
                (sasl_fail == IRC_SERVER_SASL_FAIL_RECONNECT) ? 1 : 0);
        }
        else
            irc_server_sendf (server, 0, NULL, "CAP END");
    }

    return WEECHAT_RC_OK;
}

Project: weechat
File: irc-server.c

irc_server_msgq_add_unterminated (struct t_irc_server *server,
                                  const char *string)
{
    char *unterminated_message2;

    if (!string[0])
        return;

    if (server->unterminated_message)
    {
        unterminated_message2 =
            realloc (server->unterminated_message,
                     (strlen (server->unterminated_message) +
                      strlen (string) + 1));
        if (!unterminated_message2)
        {
            weechat_printf (server->buffer,
                            _("%s%s: not enough memory for received message"),
                            weechat_prefix ("error"), IRC_PLUGIN_NAME);
            free (server->unterminated_message);
            server->unterminated_message = NULL;
            return;
        }
        server->unterminated_message = unterminated_message2;
        strcat (server->unterminated_message, string);
    }
    else
    {
        server->unterminated_message = strdup (string);
        if (!server->unterminated_message)
        {
            weechat_printf (server->buffer,
                            _("%s%s: not enough memory for received message"),
                            weechat_prefix ("error"), IRC_PLUGIN_NAME);
        }
    }
}

Project: weechat
File: irc-server.c

irc_server_outqueue_add (struct t_irc_server *server, int priority,
                         const char *command, const char *msg1,
                         const char *msg2, int modified, const char *tags,
                         struct t_irc_redirect *redirect)
{
    struct t_irc_outqueue *new_outqueue;

    new_outqueue = malloc (sizeof (*new_outqueue));
    if (new_outqueue)
    {
        new_outqueue->command = (command) ? strdup (command) : strdup ("unknown");
        new_outqueue->message_before_mod = (msg1) ? strdup (msg1) : NULL;
        new_outqueue->message_after_mod = (msg2) ? strdup (msg2) : NULL;
        new_outqueue->modified = modified;
        new_outqueue->tags = (tags) ? strdup (tags) : NULL;
        new_outqueue->redirect = redirect;

        new_outqueue->prev_outqueue = server->last_outqueue[priority];
        new_outqueue->next_outqueue = NULL;
        if (server->last_outqueue[priority])
            server->last_outqueue[priority]->next_outqueue = new_outqueue;
        else
            server->outqueue[priority] = new_outqueue;
        server->last_outqueue[priority] = new_outqueue;
    }
}

Project: weechat
File: irc-server.c

irc_server_get_prefix_char_index (struct t_irc_server *server,
                                  char prefix_char)
{
    const char *prefix_chars;
    char *pos;

    if (server)
    {
        prefix_chars = irc_server_get_prefix_chars (server);
        pos = strchr (prefix_chars, prefix_char);
        if (pos)
            return pos - prefix_chars;
    }

    return -1;
}

Project: weechat
File: irc-server.c

irc_server_strcasecmp (struct t_irc_server *server,
                       const char *string1, const char *string2)
{
    int casemapping, rc;

    casemapping = (server) ? server->casemapping : IRC_SERVER_CASEMAPPING_RFC1459;
    switch (casemapping)
    {
        case IRC_SERVER_CASEMAPPING_RFC1459:
            rc = weechat_strcasecmp_range (string1, string2, 30);
            break;
        case IRC_SERVER_CASEMAPPING_STRICT_RFC1459:
            rc = weechat_strcasecmp_range (string1, string2, 29);
            break;
        case IRC_SERVER_CASEMAPPING_ASCII:
            rc = weechat_strcasecmp (string1, string2);
            break;
        default:
            rc = weechat_strcasecmp_range (string1, string2, 30);
            break;
    }
    return rc;
}

Project: weechat
File: irc-server.c

irc_server_connect (struct t_irc_server *server)
{
    int length;
    char *option_name;
    struct t_config_option *proxy_type, *proxy_ipv6, *proxy_address;
    struct t_config_option *proxy_port;
    const char *proxy, *str_proxy_type, *str_proxy_address;

    server->disconnected = 0;

    if (!server->buffer)
    {
        if (!irc_server_create_buffer (server))
            return 0;
        weechat_buffer_set (server->buffer, "display", "auto");
    }

    irc_bar_item_update_channel ();

    irc_server_set_index_current_address (server,
                                          server->index_current_address);

    if (!server->current_address)
    {
        weechat_printf (
            server->buffer,
            _("%s%s: unknown address for server \"%s\", cannot connect"),
            weechat_prefix ("error"), IRC_PLUGIN_NAME, server->name);
        return 0;
    }

    /* free some old values (from a previous connection to server) */
    if (server->isupport)
    {
        free (server->isupport);
        server->isupport = NULL;
    }
    if (server->prefix_modes)
    {
        free (server->prefix_modes);
        server->prefix_modes = NULL;
    }
    if (server->prefix_chars)
    {
        free (server->prefix_chars);
        server->prefix_chars = NULL;
    }

    proxy_type = NULL;
    proxy_ipv6 = NULL;
    proxy_address = NULL;
    proxy_port = NULL;
    str_proxy_type = NULL;
    str_proxy_address = NULL;

    proxy = IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_PROXY);
    if (proxy && proxy[0])
    {
        length = 32 + strlen (proxy) + 1;
        option_name = malloc (length);
        if (!option_name)
        {
            weechat_printf (
                server->buffer,
                _("%s%s: not enough memory (%s)"),
                weechat_prefix ("error"), IRC_PLUGIN_NAME,
                "proxy");
            return 0;
        }
        snprintf (option_name, length, "weechat.proxy.%s.type", proxy);
        proxy_type = weechat_config_get (option_name);
        snprintf (option_name, length, "weechat.proxy.%s.ipv6", proxy);
        proxy_ipv6 = weechat_config_get (option_name);
        snprintf (option_name, length, "weechat.proxy.%s.address", proxy);
        proxy_address = weechat_config_get (option_name);
        snprintf (option_name, length, "weechat.proxy.%s.port", proxy);
        proxy_port = weechat_config_get (option_name);
        free (option_name);
        if (!proxy_type || !proxy_address)
        {
            weechat_printf (
                server->buffer,
                _("%s%s: proxy \"%s\" not found for server \"%s\", cannot "
                  "connect"),
                weechat_prefix ("error"), IRC_PLUGIN_NAME, proxy, server->name);
            return 0;
        }
        str_proxy_type = weechat_config_string (proxy_type);
        str_proxy_address = weechat_config_string (proxy_address);
        if (!str_proxy_type[0] || !proxy_ipv6 || !str_proxy_address[0]
            || !proxy_port)
        {
            weechat_printf (
                server->buffer,
                _("%s%s: missing proxy settings, check options for proxy "
                  "\"%s\""),
                weechat_prefix ("error"), IRC_PLUGIN_NAME, proxy);
            return 0;
        }
    }

    if (!server->nicks_array)
    {
        weechat_printf (
            server->buffer,
            _("%s%s: nicks not defined for server \"%s\", cannot connect"),
            weechat_prefix ("error"), IRC_PLUGIN_NAME, server->name);
        return 0;
    }

#ifndef HAVE_GNUTLS
    if (IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_SSL))
    {
        weechat_printf (
            server->buffer,
            _("%s%s: cannot connect with SSL because WeeChat was not built "
              "with GnuTLS support"),
            weechat_prefix ("error"), IRC_PLUGIN_NAME);
        return 0;
    }
#endif /* HAVE_GNUTLS */
    if (proxy_type)
    {
        weechat_printf (
            server->buffer,
            _("%s%s: connecting to server %s/%d%s via %s proxy %s/%d%s..."),
            weechat_prefix ("network"),
            IRC_PLUGIN_NAME,
            server->current_address,
            server->current_port,
            (IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_SSL)) ?
            " (SSL)" : "",
            str_proxy_type,
            str_proxy_address,
            weechat_config_integer (proxy_port),
            (weechat_config_boolean (proxy_ipv6)) ? " (IPv6)" : "");
        weechat_log_printf (
            _("Connecting to server %s/%d%s via %s proxy %s/%d%s..."),
            server->current_address,
            server->current_port,
            (IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_SSL)) ?
            " (SSL)" : "",
            str_proxy_type,
            str_proxy_address,
            weechat_config_integer (proxy_port),
            (weechat_config_boolean (proxy_ipv6)) ? " (IPv6)" : "");
    }
    else
    {
        weechat_printf (
            server->buffer,
            _("%s%s: connecting to server %s/%d%s..."),
            weechat_prefix ("network"),
            IRC_PLUGIN_NAME,
            server->current_address,
            server->current_port,
            (IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_SSL)) ?
            " (SSL)" : "");
        weechat_log_printf (
            _("%s%s: connecting to server %s/%d%s..."),
            "",
            IRC_PLUGIN_NAME,
            server->current_address,
            server->current_port,
            (IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_SSL)) ?
            " (SSL)" : "");
    }

    /* close connection if opened */
    irc_server_close_connection (server);

    /* open auto-joined channels now (if needed) */
    if (weechat_config_boolean (irc_config_look_buffer_open_before_autojoin)
        && !server->disable_autojoin)
    {
        irc_server_autojoin_create_buffers (server);
    }

    /* init SSL if asked and connect */
    server->ssl_connected = 0;
#ifdef HAVE_GNUTLS
    if (IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_SSL))
        server->ssl_connected = 1;
    server->hook_connect = weechat_hook_connect (
        proxy,
        server->current_address,
        server->current_port,
        proxy_type ? weechat_config_integer (proxy_ipv6) : IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_IPV6),
        server->current_retry,
        (server->ssl_connected) ? &server->gnutls_sess : NULL,
        (server->ssl_connected) ? &irc_server_gnutls_callback : NULL,
        IRC_SERVER_OPTION_INTEGER(server, IRC_SERVER_OPTION_SSL_DHKEY_SIZE),
        IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_SSL_PRIORITIES),
        IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_LOCAL_HOSTNAME),
        &irc_server_connect_cb,
        server,
        NULL);
#else
    server->hook_connect = weechat_hook_connect (
        proxy,
        server->current_address,
        server->current_port,
        proxy_type ? weechat_config_integer (proxy_ipv6) : IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_IPV6),
        server->current_retry,
        NULL, NULL, 0, NULL,
        IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_LOCAL_HOSTNAME),
        &irc_server_connect_cb,
        server,
        NULL);
#endif /* HAVE_GNUTLS */

    /* send signal "irc_server_connecting" with server name */
    (void) weechat_hook_signal_send ("irc_server_connecting",
                                     WEECHAT_HOOK_SIGNAL_STRING, server->name);

    return 1;
}

Project: weechat
File: irc-nick.c

irc_nick_free (struct t_irc_server *server, struct t_irc_channel *channel,
               struct t_irc_nick *nick)
{
    struct t_irc_nick *new_nicks;

    if (!channel || !nick)
        return;

    /* remove nick from nicklist */
    irc_nick_nicklist_remove (server, channel, nick);

    /* remove nick */
    if (channel->last_nick == nick)
        channel->last_nick = nick->prev_nick;
    if (nick->prev_nick)
    {
        (nick->prev_nick)->next_nick = nick->next_nick;
        new_nicks = channel->nicks;
    }
    else
        new_nicks = nick->next_nick;

    if (nick->next_nick)
        (nick->next_nick)->prev_nick = nick->prev_nick;

    channel->nicks_count--;

    /* free data */
    if (nick->name)
        free (nick->name);
    if (nick->host)
        free (nick->host);
    if (nick->prefixes)
        free (nick->prefixes);
    if (nick->prefix)
        free (nick->prefix);
    if (nick->account)
        free (nick->account);
    if (nick->realname)
        free (nick->realname);
    if (nick->color)
        free (nick->color);

    free (nick);

    channel->nicks = new_nicks;
    channel->nick_completion_reset = 1;
}

Project: weechat
File: irc-server.c

irc_server_sendf (struct t_irc_server *server, int flags, const char *tags,
                  const char *format, ...)
{
    char **items, hash_key[32], value[32], *nick, *command, *channel, *new_msg;
    char str_modifier[128];
    const char *str_message, *str_args;
    int i, items_count, number, ret_number, rc;
    struct t_hashtable *hashtable, *ret_hashtable;

    if (!server)
        return NULL;

    weechat_va_format (format);
    if (!vbuffer)
        return NULL;

    ret_hashtable = NULL;
    ret_number = 1;
    if (flags & IRC_SERVER_SEND_RETURN_HASHTABLE)
    {
        ret_hashtable = weechat_hashtable_new (32,
                                               WEECHAT_HASHTABLE_STRING,
                                               WEECHAT_HASHTABLE_STRING,
                                               NULL, NULL);
    }

    rc = 1;
    items = weechat_string_split (vbuffer, "\n", NULL,
                                  WEECHAT_STRING_SPLIT_STRIP_LEFT
                                  | WEECHAT_STRING_SPLIT_STRIP_RIGHT
                                  | WEECHAT_STRING_SPLIT_COLLAPSE_SEPS,
                                  0, &items_count);
    for (i = 0; i < items_count; i++)
    {
        /* run modifier "irc_out1_xxx" (like "irc_out_xxx", but before split) */
        irc_message_parse (server, items[i], NULL, NULL,
                           &nick, NULL, NULL, &command, &channel, NULL, NULL,
                           NULL, NULL, NULL, NULL);
        snprintf (str_modifier, sizeof (str_modifier),
                  "irc_out1_%s",
                  (command) ? command : "unknown");
        new_msg = weechat_hook_modifier_exec (str_modifier,
                                              server->name,
                                              items[i]);

        /* no changes in new message */
        if (new_msg && (strcmp (items[i], new_msg) == 0))
        {
            free (new_msg);
            new_msg = NULL;
        }

        /* message not dropped? */
        if (!new_msg || new_msg[0])
        {
            /* send signal with command that will be sent to server (before split) */
            irc_server_send_signal (server, "irc_out1",
                                    (command) ? command : "unknown",
                                    (new_msg) ? new_msg : items[i],
                                    NULL);

            /*
             * split message if needed (max is 512 bytes by default,
             * including the final "\r\n")
             */
            hashtable = irc_message_split (server,
                                           (new_msg) ? new_msg : items[i]);
            if (hashtable)
            {
                number = 1;
                while (1)
                {
                    snprintf (hash_key, sizeof (hash_key), "msg%d", number);
                    str_message = weechat_hashtable_get (hashtable, hash_key);
                    if (!str_message)
                        break;
                    snprintf (hash_key, sizeof (hash_key), "args%d", number);
                    str_args = weechat_hashtable_get (hashtable, hash_key);

                    rc = irc_server_send_one_msg (server, flags, str_message,
                                                  nick, command, channel, tags);
                    if (!rc)
                        break;

                    if (ret_hashtable)
                    {
                        snprintf (hash_key, sizeof (hash_key),
                                  "msg%d", ret_number);
                        weechat_hashtable_set (ret_hashtable,
                                               hash_key, str_message);
                        if (str_args)
                        {
                            snprintf (hash_key, sizeof (hash_key),
                                      "args%d", ret_number);
                            weechat_hashtable_set (ret_hashtable,
                                                   hash_key, str_args);
                        }
                        ret_number++;
                    }
                    number++;
                }
                if (ret_hashtable)
                {
                    snprintf (value, sizeof (value), "%d", ret_number - 1);
                    weechat_hashtable_set (ret_hashtable, "count", value);
                }
                weechat_hashtable_free (hashtable);
                if (!rc)
                    break;
            }
        }
        if (nick)
            free (nick);
        if (command)
            free (command);
        if (channel)
            free (channel);
        if (new_msg)
            free (new_msg);
    }
    if (items)
        weechat_string_free_split (items);

    free (vbuffer);

    return ret_hashtable;
}

Project: weechat
File: irc-server.c

irc_server_check_certificate_fingerprint (struct t_irc_server *server,
                                          gnutls_x509_crt_t certificate,
                                          const char *good_fingerprints)
{
    unsigned char *fingerprint_server[IRC_FINGERPRINT_NUM_ALGOS];
    char **fingerprints;
    int i, rc, algo;
    size_t size_bits, size_bytes;

    for (i = 0; i < IRC_FINGERPRINT_NUM_ALGOS; i++)
    {
        fingerprint_server[i] = NULL;
    }

    /* split good_fingerprints */
    fingerprints = weechat_string_split (good_fingerprints, ",", NULL,
                                         WEECHAT_STRING_SPLIT_STRIP_LEFT
                                         | WEECHAT_STRING_SPLIT_STRIP_RIGHT
                                         | WEECHAT_STRING_SPLIT_COLLAPSE_SEPS,
                                         0, NULL);
    if (!fingerprints)
        return 0;

    rc = 0;

    for (i = 0; fingerprints[i]; i++)
    {
        size_bits = strlen (fingerprints[i]) * 4;
        size_bytes = size_bits / 8;

        algo = irc_server_fingerprint_search_algo_with_size (size_bits);
        if (algo < 0)
            continue;

        if (!fingerprint_server[algo])
        {
            fingerprint_server[algo] = malloc (size_bytes);
            if (fingerprint_server[algo])
            {
                /* calculate the fingerprint for the certificate */
                if (gnutls_x509_crt_get_fingerprint (
                        certificate,
                        irc_fingerprint_digest_algos[algo],
                        fingerprint_server[algo],
                        &size_bytes) != GNUTLS_E_SUCCESS)
                {
                    weechat_printf (
                        server->buffer,
                        _("%sgnutls: failed to calculate certificate "
                          "fingerprint (%s)"),
                        weechat_prefix ("error"),
                        irc_fingerprint_digest_algos_name[algo]);
                    free (fingerprint_server[algo]);
                    fingerprint_server[algo] = NULL;
                }
            }
            else
            {
                weechat_printf (
                    server->buffer,
                    _("%s%s: not enough memory (%s)"),
                    weechat_prefix ("error"), IRC_PLUGIN_NAME,
                    "fingerprint");
            }
        }

        if (fingerprint_server[algo])
        {
            /* check if the fingerprint matches */
            if (irc_server_compare_fingerprints (fingerprints[i],
                                                 fingerprint_server[algo],
                                                 size_bytes) == 0)
            {
                rc = 1;
                break;
            }
        }
    }

    weechat_string_free_split (fingerprints);

    for (i = 0; i < IRC_FINGERPRINT_NUM_ALGOS; i++)
    {
        if (fingerprint_server[i])
            free (fingerprint_server[i]);
    }

    return rc;
}

Project: weechat
File: irc-server.c

irc_server_xfer_resume_ready_cb (const void *pointer, void *data,
                                 const char *signal,
                                 const char *type_data, void *signal_data)
{
    struct t_infolist *infolist;
    struct t_irc_server *ptr_server;
    const char *plugin_name, *plugin_id, *filename;
    int spaces_in_name;

    /* make C compiler happy */
    (void) pointer;
    (void) data;
    (void) signal;
    (void) type_data;

    infolist = (struct t_infolist *)signal_data;

    if (weechat_infolist_next (infolist))
    {
        plugin_name = weechat_infolist_string (infolist, "plugin_name");
        plugin_id = weechat_infolist_string (infolist, "plugin_id");
        if (plugin_name && (strcmp (plugin_name, IRC_PLUGIN_NAME) == 0) && plugin_id)
        {
            ptr_server = irc_server_search (plugin_id);
            if (ptr_server)
            {
                filename = weechat_infolist_string (infolist, "filename");
                spaces_in_name = (strchr (filename, ' ') != NULL);
                irc_server_sendf (
                    ptr_server,
                    IRC_SERVER_SEND_OUTQ_PRIO_HIGH, NULL,
                    "PRIVMSG %s :\01DCC RESUME %s%s%s %d %s\01",
                    weechat_infolist_string (infolist, "remote_nick"),
                    (spaces_in_name) ? "\"" : "",
                    filename,
                    (spaces_in_name) ? "\"" : "",
                    weechat_infolist_integer (infolist, "port"),
                    weechat_infolist_string (infolist, "start_resume"));
            }
        }
    }

    weechat_infolist_reset_item_cursor (infolist);

    return WEECHAT_RC_OK;
}

Project: weechat
File: irc-nick.c

irc_nick_nicklist_set_prefix_color_all ()
{
    struct t_irc_server *ptr_server;
    struct t_irc_channel *ptr_channel;
    struct t_irc_nick *ptr_nick;

    for (ptr_server = irc_servers; ptr_server;
         ptr_server = ptr_server->next_server)
    {
        for (ptr_channel = ptr_server->channels; ptr_channel;
             ptr_channel = ptr_channel->next_channel)
        {
            for (ptr_nick = ptr_channel->nicks; ptr_nick;
                 ptr_nick = ptr_nick->next_nick)
            {
                irc_nick_nicklist_set (ptr_channel, ptr_nick, "prefix_color",
                                       irc_nick_get_prefix_color_name (ptr_server,
                                                                       ptr_nick->prefix[0]));
            }
        }
    }
}

Project: weechat
File: irc-server.c

irc_server_strncasecmp (struct t_irc_server *server,
                        const char *string1, const char *string2, int max)
{
    int casemapping, rc;

    casemapping = (server) ? server->casemapping : IRC_SERVER_CASEMAPPING_RFC1459;
    switch (casemapping)
    {
        case IRC_SERVER_CASEMAPPING_RFC1459:
            rc = weechat_strncasecmp_range (string1, string2, max, 30);
            break;
        case IRC_SERVER_CASEMAPPING_STRICT_RFC1459:
            rc = weechat_strncasecmp_range (string1, string2, max, 29);
            break;
        case IRC_SERVER_CASEMAPPING_ASCII:
            rc = weechat_strncasecmp (string1, string2, max);
            break;
        default:
            rc = weechat_strncasecmp_range (string1, string2, max, 30);
            break;
    }
    return rc;
}

Project: weechat
File: irc-server.c

irc_server_timer_cb (const void *pointer, void *data, int remaining_calls)
{
    struct t_irc_server *ptr_server;
    struct t_irc_channel *ptr_channel;
    struct t_irc_redirect *ptr_redirect, *ptr_next_redirect;
    time_t current_time;
    static struct timeval tv;
    int away_check, refresh_lag;

    /* make C compiler happy */
    (void) pointer;
    (void) data;
    (void) remaining_calls;

    current_time = time (NULL);

    for (ptr_server = irc_servers; ptr_server;
         ptr_server = ptr_server->next_server)
    {
        /* check if reconnection is pending */
        if ((!ptr_server->is_connected)
            && (ptr_server->reconnect_start > 0)
            && (current_time >= (ptr_server->reconnect_start + ptr_server->reconnect_delay)))
        {
            irc_server_reconnect (ptr_server);
        }
        else
        {
            if (!ptr_server->is_connected)
                continue;

            /* send queued messages */
            irc_server_outqueue_send (ptr_server);

            /* check for lag */
            if ((weechat_config_integer (irc_config_network_lag_check) > 0)
                && (ptr_server->lag_check_time.tv_sec == 0)
                && (current_time >= ptr_server->lag_next_check))
            {
                irc_server_sendf (ptr_server, 0, NULL, "PING %s",
                                  (ptr_server->current_address) ?
                                  ptr_server->current_address : "weechat");
                gettimeofday (&(ptr_server->lag_check_time), NULL);
                ptr_server->lag = 0;
                ptr_server->lag_last_refresh = 0;
            }
            else
            {
                /* check away (only if lag check was not done) */
                away_check = IRC_SERVER_OPTION_INTEGER(
                    ptr_server, IRC_SERVER_OPTION_AWAY_CHECK);
                if (!weechat_hashtable_has_key (ptr_server->cap_list,
                                                "away-notify")
                    && (away_check > 0)
                    && ((ptr_server->last_away_check == 0)
                        || (current_time >= ptr_server->last_away_check + (away_check * 60))))
                {
                    irc_server_check_away (ptr_server);
                }
            }

            /* check if it's time to autojoin channels (after command delay) */
            if ((ptr_server->command_time != 0)
                && (current_time >= ptr_server->command_time +
                    IRC_SERVER_OPTION_INTEGER(ptr_server, IRC_SERVER_OPTION_COMMAND_DELAY)))
            {
                irc_server_autojoin_channels (ptr_server);
                ptr_server->command_time = 0;
            }

            /* check if it's time to send MONITOR command */
            if ((ptr_server->monitor_time != 0)
                && (current_time >= ptr_server->monitor_time))
            {
                if (ptr_server->monitor > 0)
                    irc_notify_send_monitor (ptr_server);
                ptr_server->monitor_time = 0;
            }

            /* compute lag */
            if (ptr_server->lag_check_time.tv_sec != 0)
            {
                refresh_lag = 0;
                gettimeofday (&tv, NULL);
                ptr_server->lag = (int)(weechat_util_timeval_diff (&(ptr_server->lag_check_time),
                                                                   &tv) / 1000);
                /* refresh lag item if needed */
                if (((ptr_server->lag_last_refresh == 0)
                     || (current_time >= ptr_server->lag_last_refresh + weechat_config_integer (irc_config_network_lag_refresh_interval)))
                    && (ptr_server->lag >= weechat_config_integer (irc_config_network_lag_min_show)))
                {
                    ptr_server->lag_last_refresh = current_time;
                    if (ptr_server->lag != ptr_server->lag_displayed)
                    {
                        ptr_server->lag_displayed = ptr_server->lag;
                        refresh_lag = 1;
                    }
                }
                /* lag timeout? => disconnect */
                if ((weechat_config_integer (irc_config_network_lag_reconnect) > 0)
                    && (ptr_server->lag >= weechat_config_integer (irc_config_network_lag_reconnect) * 1000))
                {
                    weechat_printf (
                        ptr_server->buffer,
                        _("%s%s: lag is high, reconnecting to server %s%s%s"),
                        weechat_prefix ("network"),
                        IRC_PLUGIN_NAME,
                        IRC_COLOR_CHAT_SERVER,
                        ptr_server->name,
                        IRC_COLOR_RESET);
                    irc_server_disconnect (ptr_server, 0, 1);
                }
                else
                {
                    /* stop lag counting if max lag is reached */
                    if ((weechat_config_integer (irc_config_network_lag_max) > 0)
                        && (ptr_server->lag >= (weechat_config_integer (irc_config_network_lag_max) * 1000)))
                    {
                        /* refresh lag item */
                        ptr_server->lag_last_refresh = current_time;
                        if (ptr_server->lag != ptr_server->lag_displayed)
                        {
                            ptr_server->lag_displayed = ptr_server->lag;
                            refresh_lag = 1;
                        }

                        /* schedule next lag check in 5 seconds */
                        ptr_server->lag_check_time.tv_sec = 0;
                        ptr_server->lag_check_time.tv_usec = 0;
                        ptr_server->lag_next_check = time (NULL) +
                            weechat_config_integer (irc_config_network_lag_check);
                    }
                }
                if (refresh_lag)
                    irc_server_set_lag (ptr_server);
            }

            /* remove redirects if timeout occurs */
            ptr_redirect = ptr_server->redirects;
            while (ptr_redirect)
            {
                ptr_next_redirect = ptr_redirect->next_redirect;

                if ((ptr_redirect->start_time > 0)
                    && (ptr_redirect->start_time + ptr_redirect->timeout < current_time))
                {
                    irc_redirect_stop (ptr_redirect, "timeout");
                }

                ptr_redirect = ptr_next_redirect;
            }

            /* purge some data (every 10 minutes) */
            if (current_time > ptr_server->last_data_purge + (60 * 10))
            {
                weechat_hashtable_map (ptr_server->join_manual,
                                       &irc_server_check_join_manual_cb,
                                       NULL);
                weechat_hashtable_map (ptr_server->join_noswitch,
                                       &irc_server_check_join_noswitch_cb,
                                       NULL);
                for (ptr_channel = ptr_server->channels; ptr_channel;
                     ptr_channel = ptr_channel->next_channel)
                {
                    if (ptr_channel->join_smart_filtered)
                    {
                        weechat_hashtable_map (ptr_channel->join_smart_filtered,
                                               &irc_server_check_join_smart_filtered_cb,
                                               NULL);
                    }
                }
                ptr_server->last_data_purge = current_time;
            }
        }
    }

    return WEECHAT_RC_OK;
}

Project: weechat
File: irc-server.c

irc_server_search_option (const char *option_name)
{
    int i;

    if (!option_name)
        return -1;

    for (i = 0; i < IRC_SERVER_NUM_OPTIONS; i++)
    {
        if (weechat_strcasecmp (irc_server_options[i][0], option_name) == 0)
            return i;
    }

    /* server option not found */
    return -1;
}

Project: weechat
File: irc-server.c

irc_server_prefix_char_statusmsg (struct t_irc_server *server,
                                  char prefix_char)
{
    const char *support_statusmsg;

    support_statusmsg = irc_server_get_isupport_value (server, "STATUSMSG");
    if (support_statusmsg)
        return (strchr (support_statusmsg, prefix_char)) ? 1 : 0;

    return (irc_server_get_prefix_char_index (server, prefix_char) >= 0) ?
        1 : 0;
}

Project: weechat
File: irc-nick.c

irc_nick_nicklist_set (struct t_irc_channel *channel,
                       struct t_irc_nick *nick,
                       const char *property, const char *value)
{
    struct t_gui_nick *ptr_nick;

    ptr_nick = weechat_nicklist_search_nick (channel->buffer, NULL, nick->name);
    if (ptr_nick)
    {
        weechat_nicklist_nick_set (channel->buffer, ptr_nick, property, value);
    }
}

Project: weechat
File: irc-server.c

irc_server_get_prefix_chars (struct t_irc_server *server)
{
    return (server && server->prefix_chars) ?
        server->prefix_chars : irc_server_prefix_chars_default;
}

Project: weechat
File: irc-server.c

irc_server_create_buffer (struct t_irc_server *server)
{
    char buffer_name[256], charset_modifier[256];
    struct t_gui_buffer *ptr_buffer_for_merge;

    ptr_buffer_for_merge = NULL;
    switch (weechat_config_integer (irc_config_look_server_buffer))
    {
        case IRC_CONFIG_LOOK_SERVER_BUFFER_MERGE_WITH_CORE:
            /* merge with WeeChat core buffer */
            ptr_buffer_for_merge = weechat_buffer_search_main ();
            break;
        case IRC_CONFIG_LOOK_SERVER_BUFFER_MERGE_WITHOUT_CORE:
            /* find buffer used to merge all IRC server buffers */
            ptr_buffer_for_merge = irc_buffer_search_server_lowest_number ();
            break;
    }

    snprintf (buffer_name, sizeof (buffer_name),
              "server.%s", server->name);
    server->buffer = weechat_buffer_new (buffer_name,
                                         &irc_input_data_cb, NULL, NULL,
                                         &irc_buffer_close_cb, NULL, NULL);
    if (!server->buffer)
        return NULL;

    if (!weechat_buffer_get_integer (server->buffer, "short_name_is_set"))
        weechat_buffer_set (server->buffer, "short_name", server->name);
    weechat_buffer_set (server->buffer, "localvar_set_type", "server");
    weechat_buffer_set (server->buffer, "localvar_set_server", server->name);
    weechat_buffer_set (server->buffer, "localvar_set_channel", server->name);
    snprintf (charset_modifier, sizeof (charset_modifier),
              "irc.%s", server->name);
    weechat_buffer_set (server->buffer, "localvar_set_charset_modifier",
                        charset_modifier);

    (void) weechat_hook_signal_send ("logger_backlog",
                                     WEECHAT_HOOK_SIGNAL_POINTER,
                                     server->buffer);

    if (weechat_config_boolean (irc_config_network_send_unknown_commands))
        weechat_buffer_set (server->buffer, "input_get_unknown_commands", "1");

    /* set highlights settings on server buffer */
    weechat_buffer_set (server->buffer, "highlight_words_add",
                        weechat_config_string (irc_config_look_highlight_server));
    if (weechat_config_string (irc_config_look_highlight_tags_restrict)
        && weechat_config_string (irc_config_look_highlight_tags_restrict)[0])
    {
        weechat_buffer_set (
            server->buffer, "highlight_tags_restrict",
            weechat_config_string (irc_config_look_highlight_tags_restrict));
    }

    irc_server_set_buffer_title (server);

    /*
     * merge buffer if needed: if merge with(out) core set, and if no layout
     * number is assigned for this buffer (if layout number is assigned, then
     * buffer was already moved/merged by WeeChat core)
     */
    if (ptr_buffer_for_merge
        && (weechat_buffer_get_integer (server->buffer, "layout_number") < 1))
    {
        weechat_buffer_merge (server->buffer, ptr_buffer_for_merge);
    }

    (void) weechat_hook_signal_send ("irc_server_opened",
                                     WEECHAT_HOOK_SIGNAL_POINTER,
                                     server->buffer);

    return server->buffer;
}

Project: weechat
File: irc-nick.c

irc_nick_print_log (struct t_irc_nick *nick)
{
    weechat_log_printf ("");
    weechat_log_printf ("    => nick %s (addr:0x%lx):",    nick->name, nick);
    weechat_log_printf ("         host . . . . . : '%s'",  nick->host);
    weechat_log_printf ("         prefixes . . . : '%s'",  nick->prefixes);
    weechat_log_printf ("         prefix . . . . : '%s'",  nick->prefix);
    weechat_log_printf ("         away . . . . . : %d",    nick->away);
    weechat_log_printf ("         account. . . . : '%s'",  nick->account);
    weechat_log_printf ("         realname . . . : '%s'",  nick->realname);
    weechat_log_printf ("         color. . . . . : '%s'",  nick->color);
    weechat_log_printf ("         prev_nick. . . : 0x%lx", nick->prev_nick);
    weechat_log_printf ("         next_nick. . . : 0x%lx", nick->next_nick);
}

Project: weechat
File: irc-server.c

irc_server_xfer_send_accept_resume_cb (const void *pointer, void *data,
                                       const char *signal,
                                       const char *type_data,
                                       void *signal_data)
{
    struct t_infolist *infolist;
    struct t_irc_server *ptr_server;
    const char *plugin_name, *plugin_id, *filename;
    int spaces_in_name;

    /* make C compiler happy */
    (void) pointer;
    (void) data;
    (void) signal;
    (void) type_data;

    infolist = (struct t_infolist *)signal_data;

    if (weechat_infolist_next (infolist))
    {
        plugin_name = weechat_infolist_string (infolist, "plugin_name");
        plugin_id = weechat_infolist_string (infolist, "plugin_id");
        if (plugin_name && (strcmp (plugin_name, IRC_PLUGIN_NAME) == 0) && plugin_id)
        {
            ptr_server = irc_server_search (plugin_id);
            if (ptr_server)
            {
                filename = weechat_infolist_string (infolist, "filename");
                spaces_in_name = (strchr (filename, ' ') != NULL);
                irc_server_sendf (
                    ptr_server,
                    IRC_SERVER_SEND_OUTQ_PRIO_HIGH, NULL,
                    "PRIVMSG %s :\01DCC ACCEPT %s%s%s %d %s\01",
                    weechat_infolist_string (infolist, "remote_nick"),
                    (spaces_in_name) ? "\"" : "",
                    filename,
                    (spaces_in_name) ? "\"" : "",
                    weechat_infolist_integer (infolist, "port"),
                    weechat_infolist_string (infolist, "start_resume"));
            }
        }
    }

    weechat_infolist_reset_item_cursor (infolist);

    return WEECHAT_RC_OK;
}

Project: weechat
File: irc-server.c

irc_server_get_alternate_nick (struct t_irc_server *server)
{
    static char nick[64];
    char str_number[64];
    int nick_index, length_nick, length_number;

    nick[0] = '\0';

    /* we are still trying nicks from option "nicks" */
    if (server->nick_alternate_number < 0)
    {
        nick_index = irc_server_get_nick_index (server);
        if (nick_index < 0)
            nick_index = 0;
        else
        {
            nick_index = (nick_index + 1) % server->nicks_count;
            /* stop loop if first nick tried was not in the list of nicks */
            if ((nick_index == 0) && (server->nick_first_tried < 0))
                server->nick_first_tried = 0;
        }

        if (nick_index != server->nick_first_tried)
        {
            snprintf (nick, sizeof (nick),
                      "%s", server->nicks_array[nick_index]);
            return nick;
        }

        /* now we have tried all nicks in list */

        /* if alternate nicks are disabled, just return NULL */
        if (!IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_NICKS_ALTERNATE))
            return NULL;

        /* use main nick and we will add "_" and then number if needed */
        server->nick_alternate_number = 0;
        snprintf (nick, sizeof (nick), "%s", server->nicks_array[0]);
    }
    else
        snprintf (nick, sizeof (nick), "%s", server->nick);

    /* if length is < 9, just add a "_" */
    if (strlen (nick) < 9)
    {
        strcat (nick, "_");
        return nick;
    }

    server->nick_alternate_number++;

    /* number is max 99 */
    if (server->nick_alternate_number > 99)
        return NULL;

    /* be sure the nick has 9 chars max */
    nick[9] = '\0';

    /* generate number */
    snprintf (str_number, sizeof (str_number),
              "%d", server->nick_alternate_number);

    /* copy number in nick */
    length_nick = strlen (nick);
    length_number = strlen (str_number);
    if (length_number > length_nick)
        return NULL;
    memcpy (nick + length_nick - length_number, str_number, length_number);

    /* return alternate nick */
    return nick;
}

Project: weechat
File: irc-server.c

irc_server_eval_expression (struct t_irc_server *server, const char *string)
{
    struct t_hashtable *pointers, *extra_vars;
    char *value;

    pointers = weechat_hashtable_new (
        32,
        WEECHAT_HASHTABLE_STRING,
        WEECHAT_HASHTABLE_POINTER,
        NULL, NULL);
    extra_vars = weechat_hashtable_new (
        32,
        WEECHAT_HASHTABLE_STRING,
        WEECHAT_HASHTABLE_STRING,
        NULL, NULL);

    if (server)
    {
        if (pointers)
            weechat_hashtable_set (pointers, "irc_server", server);
        if (extra_vars)
            weechat_hashtable_set (extra_vars, "server", server->name);
    }

    value = weechat_string_eval_expression (string,
                                            pointers, extra_vars, NULL);

    if (pointers)
        weechat_hashtable_free (pointers);
    if (extra_vars)
        weechat_hashtable_free (extra_vars);

    return value;
}

Project: weechat
File: irc-server.c

irc_server_valid (struct t_irc_server *server)
{
    struct t_irc_server *ptr_server;

    if (!server)
        return 0;

    for (ptr_server = irc_servers; ptr_server;
         ptr_server = ptr_server->next_server)
    {
        if (ptr_server == server)
            return 1;
    }

    /* server not found */
    return 0;
}