diff options
Diffstat (limited to 'src/common')
30 files changed, 1027 insertions, 343 deletions
diff --git a/src/common/cfgfiles.c b/src/common/cfgfiles.c index fdee9f2c..f0799de9 100644 --- a/src/common/cfgfiles.c +++ b/src/common/cfgfiles.c @@ -468,6 +468,7 @@ const struct prefs vars[] = {"gui_win_fullscreen", P_OFFINT (hex_gui_win_fullscreen), TYPE_INT}, {"gui_win_left", P_OFFINT (hex_gui_win_left), TYPE_INT}, {"gui_win_modes", P_OFFINT (hex_gui_win_modes), TYPE_BOOL}, + {"gui_win_nick", P_OFFINT (hex_gui_win_nick), TYPE_BOOL}, {"gui_win_save", P_OFFINT (hex_gui_win_save), TYPE_BOOL}, {"gui_win_state", P_OFFINT (hex_gui_win_state), TYPE_INT}, {"gui_win_swap", P_OFFINT (hex_gui_win_swap), TYPE_BOOL}, @@ -772,6 +773,7 @@ load_default_config(void) prefs.hex_gui_ulist_count = 1; prefs.hex_gui_ulist_icons = 1; prefs.hex_gui_ulist_style = 1; + prefs.hex_gui_win_nick = 1; prefs.hex_gui_win_save = 1; prefs.hex_input_filter_beep = 1; prefs.hex_input_flash_hilight = 1; diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj index bc191f43..c91d8cbb 100644 --- a/src/common/common.vcxproj +++ b/src/common/common.vcxproj @@ -36,6 +36,7 @@ <ClInclude Include="server.h" /> <ClInclude Include="servlist.h" /> <ClInclude Include="ssl.h" /> + <ClInclude Include="scram.h" /> <ClInclude Include="sysinfo\sysinfo.h" /> <ClInclude Include="text.h" /> <ClInclude Include="$(HexChatLib)textenums.h" /> @@ -69,6 +70,7 @@ <ClCompile Include="server.c" /> <ClCompile Include="servlist.c" /> <ClCompile Include="ssl.c" /> + <ClCompile Include="scram.c" /> <ClCompile Include="sysinfo\win32\backend.c" /> <ClCompile Include="text.c" /> <ClCompile Include="tree.c" /> diff --git a/src/common/ctcp.c b/src/common/ctcp.c index a8e1ea8d..f9c05440 100644 --- a/src/common/ctcp.c +++ b/src/common/ctcp.c @@ -94,9 +94,6 @@ ctcp_handle (session *sess, char *to, char *nick, char *ip, char outbuf[1024]; int ctcp_offset = 2; - if (serv->have_idmsg && (word[4][1] == '+' || word[4][1] == '-') ) - ctcp_offset = 3; - /* consider DCC to be different from other CTCPs */ if (!g_ascii_strncasecmp (msg, "DCC", 3)) { @@ -129,7 +126,7 @@ ctcp_handle (session *sess, char *to, char *nick, char *ip, if (ctcp_check (sess, nick, word, word_eol, word[4] + ctcp_offset)) goto generic; - inbound_action (sess, to, nick, ip, msg + 7, FALSE, id, tags_data); + inbound_action (sess, to, nick, ip, msg + 7, FALSE, tags_data->identified, tags_data); return; } diff --git a/src/common/dbus/dbus-client.c b/src/common/dbus/dbus-client.c index 8b40dd24..e70a49a9 100644 --- a/src/common/dbus/dbus-client.c +++ b/src/common/dbus/dbus-client.c @@ -67,7 +67,7 @@ hexchat_remote (void) gboolean hexchat_running; GError *error = NULL; char *command = NULL; - int i; + guint i; /* if there is nothing to do, return now. */ if (!arg_existing || !(arg_url || arg_urls || arg_command)) { diff --git a/src/common/dbus/meson.build b/src/common/dbus/meson.build index 69066be0..856bbe55 100644 --- a/src/common/dbus/meson.build +++ b/src/common/dbus/meson.build @@ -1,5 +1,5 @@ dbus_deps = [ - dependency('dbus-glib-1') + dbus_glib_dep ] dbus_sources = [ diff --git a/src/common/fe.h b/src/common/fe.h index 6614055b..b8a6279e 100644 --- a/src/common/fe.h +++ b/src/common/fe.h @@ -69,7 +69,16 @@ int fe_input_add (int sok, int flags, void *func, void *data); void fe_input_remove (int tag); void fe_idle_add (void *func, void *data); void fe_set_topic (struct session *sess, char *topic, char *stripped_topic); -void fe_set_tab_color (struct session *sess, int col); +typedef enum +{ + FE_COLOR_NONE = 0, + FE_COLOR_NEW_DATA = 1, + FE_COLOR_NEW_MSG = 2, + FE_COLOR_NEW_HILIGHT = 3, + FE_COLOR_FLAG_NOOVERRIDE = 8, +} tabcolor; +#define FE_COLOR_ALLFLAGS (FE_COLOR_FLAG_NOOVERRIDE) +void fe_set_tab_color (struct session *sess, tabcolor col); void fe_flash_window (struct session *sess); void fe_update_mode_buttons (struct session *sess, char mode, char sign); void fe_update_channel_key (struct session *sess); @@ -132,6 +141,7 @@ void fe_get_int (char *prompt, int def, void *callback, void *ud); #define FRF_NOASKOVERWRITE 32 /* don't ask to overwrite existing files */ #define FRF_EXTENSIONS 64 /* specify file extensions to be displayed */ #define FRF_MIMETYPES 128 /* specify file mimetypes to be displayed */ +#define FRF_MODAL 256 /* dialog should be modal to parent */ void fe_get_file (const char *title, char *initial, void (*callback) (void *userdata, char *file), void *userdata, int flags); diff --git a/src/common/hexchat.c b/src/common/hexchat.c index 9be2e56d..f74fe489 100644 --- a/src/common/hexchat.c +++ b/src/common/hexchat.c @@ -57,10 +57,6 @@ #include <glib-object.h> /* for g_type_init() */ #endif -#ifdef USE_LIBPROXY -#include <proxy.h> -#endif - GSList *popup_list = 0; GSList *button_list = 0; GSList *dlgbutton_list = 0; @@ -111,10 +107,6 @@ struct session *current_tab; struct session *current_sess = 0; struct hexchatprefs prefs; -#ifdef USE_LIBPROXY -pxProxyFactory *libproxy_factory; -#endif - /* * Update the priority queue of the "interesting sessions" * (sess_list_by_lastact). @@ -560,7 +552,7 @@ new_ircwindow (server *serv, char *name, int type, int focus) if (user && user->hostname) set_topic (sess, user->hostname, user->hostname); } - plugin_emit_dummy_print (sess, "Open Context"); + plugin_emit_dummy_print (sess, "Open Context", -1); return sess; } @@ -637,7 +629,7 @@ session_free (session *killsess) GSList *list; int oldidx; - plugin_emit_dummy_print (killsess, "Close Context"); + plugin_emit_dummy_print (killsess, "Close Context", 0); if (current_tab == killsess) current_tab = NULL; @@ -1102,10 +1094,6 @@ main (int argc, char *argv[]) hexchat_remote (); #endif -#ifdef USE_LIBPROXY - libproxy_factory = px_proxy_factory_new (); -#endif - #ifdef WIN32 coinit_result = CoInitializeEx (NULL, COINIT_APARTMENTTHREADED); if (SUCCEEDED (coinit_result)) @@ -1148,10 +1136,6 @@ main (int argc, char *argv[]) } #endif -#ifdef USE_LIBPROXY - px_proxy_factory_free (libproxy_factory); -#endif - #ifdef WIN32 WSACleanup (); #endif diff --git a/src/common/hexchat.h b/src/common/hexchat.h index d8effa1f..5ead96d1 100644 --- a/src/common/hexchat.h +++ b/src/common/hexchat.h @@ -41,6 +41,7 @@ #ifdef USE_OPENSSL #include <openssl/ssl.h> /* SSL_() */ +#include "scram.h" #endif #ifdef __EMX__ /* for o/s 2 */ @@ -150,6 +151,7 @@ struct hexchatprefs unsigned int hex_gui_ulist_style; unsigned int hex_gui_usermenu; unsigned int hex_gui_win_modes; + unsigned int hex_gui_win_nick; unsigned int hex_gui_win_save; unsigned int hex_gui_win_swap; unsigned int hex_gui_win_ucount; @@ -429,6 +431,9 @@ typedef struct session /* SASL Mechanisms */ #define MECH_PLAIN 0 #define MECH_EXTERNAL 1 +#define MECH_SCRAM_SHA_1 2 +#define MECH_SCRAM_SHA_256 3 +#define MECH_SCRAM_SHA_512 4 typedef struct server { @@ -501,9 +506,9 @@ typedef struct server int joindelay_tag; /* waiting before we send JOIN */ char hostname[128]; /* real ip number */ char servername[128]; /* what the server says is its name */ - char password[86]; + char password[1024]; char nick[NICKLEN]; - char linebuf[2048]; /* RFC says 512 chars including \r\n */ + char linebuf[8704]; /* RFC says 512 chars including \r\n, IRCv3 message tags add 8191, plus the NUL byte */ char *last_away_reason; int pos; /* current position in linebuf */ int nickcount; @@ -567,7 +572,7 @@ typedef struct server unsigned int have_awaynotify:1; unsigned int have_uhnames:1; unsigned int have_whox:1; /* have undernet's WHOX features */ - unsigned int have_idmsg:1; /* freenode's IDENTIFY-MSG */ + unsigned int have_idmsg:1; /* cap solanum.chat/identify-msg */ unsigned int have_accnotify:1; /* cap account-notify */ unsigned int have_extjoin:1; /* cap extended-join */ unsigned int have_account_tag:1; /* cap account-tag */ @@ -584,6 +589,7 @@ typedef struct server #ifdef USE_OPENSSL unsigned int use_ssl:1; /* is server SSL capable? */ unsigned int accept_invalid_cert:1;/* ignore result of server's cert. verify */ + scram_session *scram_session; /* session for SASL SCRAM authentication */ #endif } server; diff --git a/src/common/inbound.c b/src/common/inbound.c index 7175b2ae..fdee2ecc 100644 --- a/src/common/inbound.c +++ b/src/common/inbound.c @@ -107,7 +107,8 @@ find_session_from_nick (char *nick, server *serv) if (serv->front_session) { - if (userlist_find (serv->front_session, nick)) + // If we are here for ChanServ, then it is usually a reply for the user + if (!g_ascii_strcasecmp(nick, "ChanServ") || userlist_find (serv->front_session, nick)) return serv->front_session; } @@ -189,7 +190,7 @@ inbound_privmsg (server *serv, char *from, char *ip, char *text, int id, if (ip && ip[0]) set_topic (sess, ip, ip); - inbound_chanmsg (serv, NULL, NULL, from, text, FALSE, id, tags_data); + inbound_chanmsg (serv, NULL, NULL, from, text, FALSE, tags_data->identified, tags_data); return; } @@ -1473,10 +1474,17 @@ inbound_user_info (session *sess, char *chan, char *user, char *host, for (list = sess_list; list; list = list->next) { sess = list->data; - if (sess->type == SESS_CHANNEL && sess->server == serv) + if (sess->server != serv) + continue; + + if (sess->type == SESS_CHANNEL) { userlist_add_hostname (sess, nick, uhost, realname, servname, account, away); } + else if (sess->type == SESS_DIALOG && uhost && !serv->p_cmp (sess->channel, nick)) + { + set_topic (sess, uhost, uhost); + } } } @@ -1640,7 +1648,10 @@ inbound_identified (server *serv) /* 'MODE +e MYSELF' on freenode */ static const char *sasl_mechanisms[] = { "PLAIN", - "EXTERNAL" + "EXTERNAL", + "SCRAM-SHA-1", + "SCRAM-SHA-256", + "SCRAM-SHA-512" }; static void @@ -1655,7 +1666,7 @@ inbound_toggle_caps (server *serv, const char *extensions_str, gboolean enable) { const char *extension = extensions[i]; - if (!strcmp (extension, "identify-msg")) + if (!strcmp (extension, "solanum.chat/identify-msg")) serv->have_idmsg = enable; else if (!strcmp (extension, "multi-prefix")) serv->have_namesx = enable; @@ -1681,6 +1692,12 @@ inbound_toggle_caps (server *serv, const char *extensions_str, gboolean enable) #ifdef USE_OPENSSL if (serv->loginmethod == LOGIN_SASLEXTERNAL) serv->sasl_mech = MECH_EXTERNAL; + else if (serv->loginmethod == LOGIN_SASL_SCRAM_SHA_1) + serv->sasl_mech = MECH_SCRAM_SHA_1; + else if (serv->loginmethod == LOGIN_SASL_SCRAM_SHA_256) + serv->sasl_mech = MECH_SCRAM_SHA_256; + else if (serv->loginmethod == LOGIN_SASL_SCRAM_SHA_512) + serv->sasl_mech = MECH_SCRAM_SHA_512; #endif /* Mechanism either defaulted to PLAIN or server gave us list */ tcp_sendf (serv, "AUTHENTICATE %s\r\n", sasl_mechanisms[serv->sasl_mech]); @@ -1712,8 +1729,6 @@ inbound_cap_del (server *serv, char *nick, char *extensions, } static const char * const supported_caps[] = { - "identify-msg", - /* IRCv3.1 */ "multi-prefix", "away-notify", @@ -1729,6 +1744,7 @@ static const char * const supported_caps[] = { "setname", "invite-notify", "account-tag", + "extended-monitor", /* ZNC */ "znc.in/server-time-iso", @@ -1736,6 +1752,9 @@ static const char * const supported_caps[] = { /* Twitch */ "twitch.tv/membership", + + /* Solanum */ + "solanum.chat/identify-msg", }; static int @@ -1756,6 +1775,30 @@ get_supported_mech (server *serv, const char *list) break; } } + else if (serv->loginmethod == LOGIN_SASL_SCRAM_SHA_1) + { + if (!strcmp(mechs[i], "SCRAM-SHA-1")) + { + ret = MECH_SCRAM_SHA_1; + break; + } + } + else if (serv->loginmethod == LOGIN_SASL_SCRAM_SHA_256) + { + if (!strcmp(mechs[i], "SCRAM-SHA-256")) + { + ret = MECH_SCRAM_SHA_256; + break; + } + } + else if (serv->loginmethod == LOGIN_SASL_SCRAM_SHA_512) + { + if (!strcmp(mechs[i], "SCRAM-SHA-512")) + { + ret = MECH_SCRAM_SHA_512; + break; + } + } else #endif if (!strcmp (mechs[i], "PLAIN")) @@ -1811,7 +1854,11 @@ inbound_cap_ls (server *serv, char *nick, char *extensions_str, /* if the SASL password is set AND auth mode is set to SASL, request SASL auth */ if (!g_strcmp0 (extension, "sasl") && - ((serv->loginmethod == LOGIN_SASL && strlen (serv->password) != 0) + (((serv->loginmethod == LOGIN_SASL + || serv->loginmethod == LOGIN_SASL_SCRAM_SHA_1 + || serv->loginmethod == LOGIN_SASL_SCRAM_SHA_256 + || serv->loginmethod == LOGIN_SASL_SCRAM_SHA_512) + && strlen (serv->password) != 0) || serv->loginmethod == LOGIN_SASLEXTERNAL)) { if (value) @@ -1891,11 +1938,103 @@ inbound_cap_list (server *serv, char *nick, char *extensions, NULL, NULL, 0, tags_data->timestamp); } +static void +plain_authenticate (server *serv, char *user, char *password) +{ + char *pass = encode_sasl_pass_plain (user, password); + + if (pass == NULL) + { + /* something went wrong abort */ + tcp_sendf (serv, "AUTHENTICATE *\r\n"); + return; + } + + /* long SASL passwords must be split into 400-byte chunks + https://ircv3.net/specs/extensions/sasl-3.1#the-authenticate-command */ + size_t pass_len = strlen (pass); + if (pass_len <= 400) + tcp_sendf (serv, "AUTHENTICATE %s\r\n", pass); + else + { + size_t sent = 0; + while (sent < pass_len) + { + char *pass_chunk = g_strndup (pass + sent, 400); + tcp_sendf (serv, "AUTHENTICATE %s\r\n", pass_chunk); + sent += 400; + g_free (pass_chunk); + } + } + if (pass_len % 400 == 0) + tcp_sendf (serv, "AUTHENTICATE +\r\n"); +} + +#ifdef USE_OPENSSL +/* + * Sends AUTHENTICATE messages to log in via SCRAM. + */ +static void +scram_authenticate (server *serv, const char *data, const char *digest, + const char *user, const char *password) +{ + char *encoded, *decoded, *output; + scram_status status; + size_t output_len; + gsize decoded_len; + + if (serv->scram_session == NULL) + { + serv->scram_session = scram_session_create (digest, user, password); + + if (serv->scram_session == NULL) + { + PrintTextf (serv->server_session, _("Could not create SCRAM session with digest %s"), digest); + g_warning ("Could not create SCRAM session with digest %s", digest); + tcp_sendf (serv, "AUTHENTICATE *\r\n"); + return; + } + } + + decoded = g_base64_decode (data, &decoded_len); + status = scram_process (serv->scram_session, decoded, &output, &output_len); + g_free (decoded); + + if (status == SCRAM_IN_PROGRESS) + { + // Authentication is still in progress + encoded = g_base64_encode ((guchar *) output, output_len); + tcp_sendf (serv, "AUTHENTICATE %s\r\n", encoded); + g_free (encoded); + g_free (output); + } + else if (status == SCRAM_SUCCESS) + { + // Authentication succeeded + tcp_sendf (serv, "AUTHENTICATE +\r\n"); + g_clear_pointer (&serv->scram_session, scram_session_free); + } + else if (status == SCRAM_ERROR) + { + // Authentication failed + tcp_sendf (serv, "AUTHENTICATE *\r\n"); + + if (serv->scram_session->error != NULL) + { + PrintTextf (serv->server_session, _("SASL SCRAM authentication failed: %s"), serv->scram_session->error); + g_info ("SASL SCRAM authentication failed: %s", serv->scram_session->error); + } + + g_clear_pointer (&serv->scram_session, scram_session_free); + } +} +#endif + void inbound_sasl_authenticate (server *serv, char *data) { ircnet *net = (ircnet*)serv->network; - char *user, *pass = NULL; + char *user; const char *mech = sasl_mechanisms[serv->sasl_mech]; /* Got a list of supported mechanisms from outdated inspircd @@ -1911,26 +2050,24 @@ inbound_sasl_authenticate (server *serv, char *data) switch (serv->sasl_mech) { case MECH_PLAIN: - pass = encode_sasl_pass_plain (user, serv->password); + plain_authenticate(serv, user, serv->password); break; #ifdef USE_OPENSSL case MECH_EXTERNAL: - pass = g_strdup ("+"); + tcp_sendf (serv, "AUTHENTICATE +\r\n"); break; -#endif - } - - if (pass == NULL) - { - /* something went wrong abort */ - tcp_sendf (serv, "AUTHENTICATE *\r\n"); + case MECH_SCRAM_SHA_1: + scram_authenticate(serv, data, "SHA1", user, serv->password); + return; + case MECH_SCRAM_SHA_256: + scram_authenticate(serv, data, "SHA256", user, serv->password); return; + case MECH_SCRAM_SHA_512: + scram_authenticate(serv, data, "SHA512", user, serv->password); + return; +#endif } - tcp_sendf (serv, "AUTHENTICATE %s\r\n", pass); - g_free (pass); - - EMIT_SIGNAL_TIMESTAMP (XP_TE_SASLAUTH, serv->server_session, user, (char*)mech, NULL, NULL, 0, 0); } @@ -1938,6 +2075,9 @@ inbound_sasl_authenticate (server *serv, char *data) void inbound_sasl_error (server *serv) { +#ifdef USE_OPENSSL + g_clear_pointer (&serv->scram_session, scram_session_free); +#endif /* Just abort, not much we can do */ tcp_sendf (serv, "AUTHENTICATE *\r\n"); } diff --git a/src/common/meson.build b/src/common/meson.build index 492227b2..35db2c27 100644 --- a/src/common/meson.build +++ b/src/common/meson.build @@ -15,6 +15,7 @@ common_sources = [ 'plugin-identd.c', 'plugin-timer.c', 'proto-irc.c', + 'scram.c', 'server.c', 'servlist.c', 'text.c', @@ -28,6 +29,7 @@ common_sysinfo_deps = [] common_deps = [ libgio_dep, + libcanberra_dep, ] + global_deps common_includes = [ @@ -46,7 +48,6 @@ if host_machine.system() == 'windows' ] common_sysinfo_deps += [ cc.find_library('wbemuuid'), # sysinfo - cc.find_library('wbemcore'), ] common_sources += 'sysinfo/win32/backend.c' @@ -72,26 +73,18 @@ textevents = custom_target('textevents', # SIGACTION # HAVE_GTK_MAC -if get_option('with-ssl') +if libssl_dep.found() common_sources += 'ssl.c' common_deps += libssl_dep endif -if get_option('with-libproxy') - common_deps += dependency('libproxy-1.0') -endif - -if get_option('with-libcanberra') - common_deps += dependency('libcanberra', version: '>= 0.22') -endif - -if get_option('with-dbus') +if dbus_glib_dep.found() subdir('dbus') common_deps += hexchat_dbus_dep common_includes += include_directories('dbus') endif -if get_option('with-plugin') +if get_option('plugin') common_deps += libgmodule_dep install_headers('hexchat-plugin.h') endif diff --git a/src/common/modes.c b/src/common/modes.c index 3c0ac8ab..1ff309bd 100644 --- a/src/common/modes.c +++ b/src/common/modes.c @@ -67,8 +67,8 @@ send_channel_modes (session *sess, char *tbuf, char *word[], int wpos, int usable_modes, orig_len, len, wlen, i, max; server *serv = sess->server; - /* sanity check. IRC RFC says three per line. */ - if (serv->modes_per_line < 3) + /* sanity check. IRC RFC says three per line but some servers may support less. */ + if (serv->modes_per_line < 1) serv->modes_per_line = 3; if (modes_per_line < 1) modes_per_line = serv->modes_per_line; @@ -680,7 +680,7 @@ handle_mode (server * serv, char *word[], char *word_eol[], int len; size_t arg; size_t i, num_args; - int num_modes; + size_t num_modes; size_t offset = 3; int all_modes_have_args = FALSE; int using_front_tab = FALSE; @@ -785,6 +785,64 @@ handle_mode (server * serv, char *word[], char *word_eol[], mode_print_grouped (sess, nick, &mr, tags_data); } +static char +hex_to_chr(char chr) +{ + return g_ascii_isdigit (chr) ? chr - '0' : g_ascii_tolower (chr) - 'a' + 10; +} + +static void +parse_005_token (const char *token, char **name, char **value, gboolean *adding) +{ + char *toksplit, *valuecurr; + size_t idx; + + if (token[0] == '-') + { + *adding = FALSE; + token++; + } else + { + *adding = TRUE; + } + + toksplit = strchr (token, '='); + if (toksplit && *toksplit++) + { + /* The token has a value; parse any escape codes. */ + *name = g_strndup (token, toksplit - token - 1); + *value = g_malloc (strlen (toksplit) + 1); + valuecurr = *value; + + while (*toksplit) + { + if (toksplit[0] == '\\') + { + /** If it's a malformed escape then just skip it. */ + if (toksplit[1] == 'x' && g_ascii_isxdigit (toksplit[2]) && g_ascii_isxdigit (toksplit[3])) + *valuecurr++ = hex_to_chr (toksplit[2]) << 4 | hex_to_chr (toksplit[3]); + + for (idx = 0; idx < 4; ++idx) + { + /* We need to do this to avoid jumping past the end of the array. */ + if (*toksplit) + toksplit++; + } + } else + { + /** Non-escape characters can be copied as is. */ + *valuecurr++ = *toksplit++; + } + } + *valuecurr++ = 0; + } else + { + /* The token has no value; store a dummy value instead. */ + *name = g_strdup (token); + *value = g_strdup (""); + } +} + /* handle the 005 numeric */ void @@ -792,84 +850,99 @@ inbound_005 (server * serv, char *word[], const message_tags_data *tags_data) { int w; char *pre; + char *tokname, *tokvalue; + gboolean tokadding; w = 4; /* start at the 4th word */ while (w < PDIWORDS && *word[w]) { - if (strncmp (word[w], "MODES=", 6) == 0) + if (word[w][0] == ':') + break; // :are supported by this server + + parse_005_token(word[w], &tokname, &tokvalue, &tokadding); + if (g_strcmp0 (tokname, "MODES") == 0) { - serv->modes_per_line = atoi (word[w] + 6); - } else if (strncmp (word[w], "CHANTYPES=", 10) == 0) + serv->modes_per_line = atoi (tokvalue); + } else if (g_strcmp0 (tokname, "CHANTYPES") == 0) { g_free (serv->chantypes); - serv->chantypes = g_strdup (word[w] + 10); - } else if (strncmp (word[w], "CHANMODES=", 10) == 0) + serv->chantypes = g_strdup (tokvalue); + } else if (g_strcmp0 (tokname, "CHANMODES") == 0) { g_free (serv->chanmodes); - serv->chanmodes = g_strdup (word[w] + 10); - } else if (strncmp (word[w], "PREFIX=", 7) == 0) + serv->chanmodes = g_strdup (tokvalue); + } else if (g_strcmp0 (tokname, "PREFIX") == 0) { - pre = strchr (word[w] + 7, ')'); + pre = strchr (tokvalue, ')'); if (pre) { pre[0] = 0; /* NULL out the ')' */ g_free (serv->nick_prefixes); g_free (serv->nick_modes); serv->nick_prefixes = g_strdup (pre + 1); - serv->nick_modes = g_strdup (word[w] + 8); + serv->nick_modes = g_strdup (tokvalue + 1); } else { /* bad! some ircds don't give us the modes. */ /* in this case, we use it only to strip /NAMES */ serv->bad_prefix = TRUE; g_free (serv->bad_nick_prefixes); - serv->bad_nick_prefixes = g_strdup (word[w] + 7); + serv->bad_nick_prefixes = g_strdup (tokvalue); } - } else if (strncmp (word[w], "WATCH=", 6) == 0) + } else if (g_strcmp0 (tokname, "WATCH") == 0) { - serv->supports_watch = TRUE; - } else if (strncmp (word[w], "MONITOR=", 8) == 0) + serv->supports_watch = tokadding; + } else if (g_strcmp0 (tokname, "MONITOR") == 0) { - serv->supports_monitor = TRUE; - } else if (strncmp (word[w], "NETWORK=", 8) == 0) + serv->supports_monitor = tokadding; + } else if (g_strcmp0 (tokname, "NETWORK") == 0) { - if (serv->server_session->type == SESS_SERVER) + if (serv->server_session->type == SESS_SERVER && strlen (tokvalue)) { - safe_strcpy (serv->server_session->channel, word[w] + 8, CHANLEN); + safe_strcpy (serv->server_session->channel, tokvalue, CHANLEN); fe_set_channel (serv->server_session); } - } else if (strncmp (word[w], "CASEMAPPING=", 12) == 0) + } else if (g_strcmp0 (tokname, "CASEMAPPING") == 0) { - if (strcmp (word[w] + 12, "ascii") == 0) /* bahamut */ + if (g_strcmp0 (tokvalue, "ascii") == 0) serv->p_cmp = (void *)g_ascii_strcasecmp; - } else if (strncmp (word[w], "CHARSET=", 8) == 0) + } else if (g_strcmp0 (tokname, "CHARSET") == 0) { - if (g_ascii_strncasecmp (word[w] + 8, "UTF-8", 5) == 0) + if (g_ascii_strcasecmp (tokvalue, "UTF-8") == 0) { server_set_encoding (serv, "UTF-8"); } - } else if (strcmp (word[w], "NAMESX") == 0) + } else if (g_strcmp0 (tokname, "UTF8ONLY") == 0) { - /* 12345678901234567 */ - tcp_send_len (serv, "PROTOCTL NAMESX\r\n", 17); - } else if (strcmp (word[w], "WHOX") == 0) + server_set_encoding (serv, "UTF-8"); + } else if (g_strcmp0 (tokname, "NAMESX") == 0) + { + if (tokadding && !serv->have_namesx) + { + /* only use protoctl if the server doesn't have the equivalent cap */ + tcp_send_len (serv, "PROTOCTL NAMESX\r\n", 17); + serv->have_namesx = TRUE; + } + } else if (g_strcmp0 (tokname, "WHOX") == 0) { - serv->have_whox = TRUE; - } else if (strcmp (word[w], "EXCEPTS") == 0) + serv->have_whox = tokadding; + } else if (g_strcmp0 (tokname, "EXCEPTS") == 0) { - serv->have_except = TRUE; - } else if (strcmp (word[w], "INVEX") == 0) + serv->have_except = tokadding; + } else if (g_strcmp0 (tokname, "INVEX") == 0) { /* supports mode letter +I, default channel invite */ - serv->have_invite = TRUE; - } else if (strncmp (word[w], "ELIST=", 6) == 0) + serv->have_invite = tokadding; + } else if (g_strcmp0 (tokname, "ELIST") == 0) { /* supports LIST >< min/max user counts? */ - if (strchr (word[w] + 6, 'U') || strchr (word[w] + 6, 'u')) + if (strchr (tokvalue, 'U') || strchr (tokvalue, 'u')) serv->use_listargs = TRUE; } + g_free (tokname); + g_free (tokvalue); w++; } } diff --git a/src/common/notify.c b/src/common/notify.c index b5316c36..ef52889b 100644 --- a/src/common/notify.c +++ b/src/common/notify.c @@ -123,7 +123,11 @@ notify_save (void) { int fh; struct notify *notify; - GSList *list = notify_list; + // while reading the notify.conf file, elements are added by prepending to the + // list. reverse the list before writing to disk to keep the original + // order of the list + GSList *list = g_slist_copy(notify_list); + list = g_slist_reverse(list); fh = hexchat_open_file ("notify.conf", O_TRUNC | O_WRONLY | O_CREAT, 0600, XOF_DOMODE); if (fh != -1) @@ -142,6 +146,7 @@ notify_save (void) } close (fh); } + g_slist_free(list); } void diff --git a/src/common/outbound.c b/src/common/outbound.c index 614aad38..b8153502 100644 --- a/src/common/outbound.c +++ b/src/common/outbound.c @@ -468,7 +468,7 @@ create_mask (session * sess, char *mask, char *mode, char *typestr, int deop) type = prefs.hex_irc_ban_type; buf[0] = 0; - if (inet_addr (fullhost) != -1) /* "fullhost" is really a IP number */ + if (inet_addr (fullhost) != (guint32) -1) /* "fullhost" is really a IP number */ { lastdot = strrchr (fullhost, '.'); if (!lastdot) @@ -1579,9 +1579,26 @@ cmd_execw (struct session *sess, char *tbuf, char *word[], char *word_eol[]) EMIT_SIGNAL (XP_TE_NOCHILD, sess, NULL, NULL, NULL, NULL, 0); return FALSE; } - len = strlen(word_eol[2]); - temp = g_strconcat (word_eol[2], "\n", NULL); - PrintText(sess, temp); + if (strcmp (word[2], "--") == 0) + { + len = strlen(word_eol[3]); + temp = g_strconcat (word_eol[3], "\n", NULL); + PrintText(sess, temp); + } + else + { + if (strcmp (word[2], "-q") == 0) + { + len = strlen(word_eol[3]); + temp = g_strconcat (word_eol[3], "\n", NULL); + } + else + { + len = strlen(word_eol[2]); + temp = g_strconcat (word_eol[2], "\n", NULL); + PrintText(sess, temp); + } + } write(sess->running_exec->myfd, temp, len + 1); g_free(temp); @@ -2152,7 +2169,6 @@ cmd_gui (struct session *sess, char *tbuf, char *word[], char *word_eol[]) { case 0x058b836e: fe_ctrl_gui (sess, 8, 0); break; /* APPLY */ case 0xac1eee45: fe_ctrl_gui (sess, 7, 2); break; /* ATTACH */ - case 0x05a72f63: fe_ctrl_gui (sess, 4, atoi (word[3])); break; /* COLOR */ case 0xb06a1793: fe_ctrl_gui (sess, 7, 1); break; /* DETACH */ case 0x05cfeff0: fe_ctrl_gui (sess, 3, 0); break; /* FLASH */ case 0x05d154d8: fe_ctrl_gui (sess, 2, 0); break; /* FOCUS */ @@ -2166,6 +2182,12 @@ cmd_gui (struct session *sess, char *tbuf, char *word[], char *word_eol[]) else return FALSE; break; + case 0x05a72f63: /* COLOR */ + if (!g_ascii_strcasecmp (word[4], "-NOOVERRIDE")) + fe_ctrl_gui (sess, 4, FE_COLOR_FLAG_NOOVERRIDE | atoi (word[3])); + else + fe_ctrl_gui (sess, 4, atoi (word[3])); + break; default: return FALSE; } @@ -3225,16 +3247,28 @@ cmd_reconnect (struct session *sess, char *tbuf, char *word[], char *word_eol[]) else if (*word[2]) { int offset = 0; -#ifdef USE_OPENSSL - int use_ssl = FALSE; - if (strcmp (word[2], "-ssl") == 0) +#ifdef USE_OPENSSL + int use_ssl = TRUE; + int use_ssl_noverify = FALSE; + if (g_strcmp0 (word[2], "-ssl") == 0) { use_ssl = TRUE; + use_ssl_noverify = FALSE; + offset++; /* args move up by 1 word */ + } else if (g_strcmp0 (word[2], "-ssl-noverify") == 0) + { + use_ssl = TRUE; + use_ssl_noverify = TRUE; + offset++; /* args move up by 1 word */ + } else if (g_strcmp0 (word[2], "-insecure") == 0) + { + use_ssl = FALSE; + use_ssl_noverify = FALSE; offset++; /* args move up by 1 word */ } serv->use_ssl = use_ssl; - serv->accept_invalid_cert = TRUE; + serv->accept_invalid_cert = use_ssl_noverify; #endif if (*word[4+offset]) @@ -3421,18 +3455,34 @@ cmd_server (struct session *sess, char *tbuf, char *word[], char *word_eol[]) char *pass = NULL; char *channel = NULL; char *key = NULL; +#ifdef USE_OPENSSL + int use_ssl = TRUE; + int use_ssl_noverify = FALSE; +#else int use_ssl = FALSE; +#endif int is_url = TRUE; server *serv = sess->server; ircnet *net = NULL; #ifdef USE_OPENSSL /* BitchX uses -ssl, mIRC uses -e, let's support both */ - if (strcmp (word[2], "-ssl") == 0 || strcmp (word[2], "-e") == 0) + if (g_strcmp0 (word[2], "-ssl") == 0 || g_strcmp0 (word[2], "-e") == 0) { use_ssl = TRUE; offset++; /* args move up by 1 word */ } + else if (g_strcmp0 (word[2], "-ssl-noverify") == 0) + { + use_ssl = TRUE; + use_ssl_noverify = TRUE; + offset++; /* args move up by 1 word */ + } + else if (g_strcmp0 (word[2], "-insecure") == 0) + { + use_ssl = FALSE; + offset++; /* args move up by 1 word */ + } #endif if (!parse_irc_url (word[2 + offset], &server_name, &port, &channel, &key, &use_ssl)) @@ -3473,6 +3523,13 @@ cmd_server (struct session *sess, char *tbuf, char *word[], char *word_eol[]) use_ssl = TRUE; #endif } + else if (port[0] == '-') + { + port++; +#ifdef USE_OPENSSL + use_ssl = FALSE; +#endif + } if (*pass) { @@ -3497,7 +3554,7 @@ cmd_server (struct session *sess, char *tbuf, char *word[], char *word_eol[]) #ifdef USE_OPENSSL serv->use_ssl = use_ssl; - serv->accept_invalid_cert = TRUE; + serv->accept_invalid_cert = use_ssl_noverify; #endif /* try to connect by Network name */ @@ -3528,7 +3585,7 @@ cmd_servchan (struct session *sess, char *tbuf, char *word[], int offset = 0; #ifdef USE_OPENSSL - if (strcmp (word[2], "-ssl") == 0) + if (g_strcmp0 (word[2], "-ssl") == 0 || g_strcmp0 (word[2], "-ssl-noverify") == 0 || g_strcmp0 (word[2], "-insecure") == 0) offset++; #endif @@ -3863,34 +3920,6 @@ cmd_wallchop (struct session *sess, char *tbuf, char *word[], } static int -cmd_wallchan (struct session *sess, char *tbuf, char *word[], - char *word_eol[]) -{ - GSList *list; - - if (*word_eol[2]) - { - list = sess_list; - while (list) - { - sess = list->data; - if (sess->type == SESS_CHANNEL) - { - message_tags_data no_tags = MESSAGE_TAGS_DATA_INIT; - - inbound_chanmsg (sess->server, NULL, sess->channel, - sess->server->nick, word_eol[2], TRUE, FALSE, - &no_tags); - sess->server->p_message (sess->server, sess->channel, word_eol[2]); - } - list = list->next; - } - return TRUE; - } - return FALSE; -} - -static int cmd_hop (struct session *sess, char *tbuf, char *word[], char *word_eol[]) { int i = 2; @@ -3930,7 +3959,7 @@ cmd_voice (struct session *sess, char *tbuf, char *word[], char *word_eol[]) const struct commands xc_cmds[] = { {"ADDBUTTON", cmd_addbutton, 0, 0, 1, N_("ADDBUTTON <name> <action>, adds a button under the user-list")}, - {"ADDSERVER", cmd_addserver, 0, 0, 1, N_("ADDSERVER <NewNetwork> <newserver/6667>, adds a new network with a new server to the network list")}, + {"ADDSERVER", cmd_addserver, 0, 0, 1, N_("ADDSERVER <NewNetwork> <hostname/port>, adds a new network with a new server to the network list")}, {"ALLCHAN", cmd_allchannels, 0, 0, 1, N_("ALLCHAN <cmd>, sends a command to all channels you're in")}, {"ALLCHANL", cmd_allchannelslocal, 0, 0, 1, @@ -3986,7 +4015,7 @@ const struct commands xc_cmds[] = { N_("EXECKILL [-9], kills a running exec in the current session. If -9 is given the process is SIGKILL'ed")}, #ifndef __EMX__ {"EXECSTOP", cmd_execs, 0, 0, 1, N_("EXECSTOP, sends the process SIGSTOP")}, - {"EXECWRITE", cmd_execw, 0, 0, 1, N_("EXECWRITE, sends data to the processes stdin")}, + {"EXECWRITE", cmd_execw, 0, 0, 1, N_("EXECWRITE [-q|--], sends data to the processes stdin; use -q flag to quiet/suppress output at text box; use -- flag to stop interpreting arguments as flags, needed if -q itself would occur as data")}, #endif #endif #if 0 @@ -4001,8 +4030,9 @@ const struct commands xc_cmds[] = { {"GETINT", cmd_getint, 0, 0, 1, "GETINT <default> <command> <prompt>"}, {"GETSTR", cmd_getstr, 0, 0, 1, "GETSTR <default> <command> <prompt>"}, {"GHOST", cmd_ghost, 1, 0, 1, N_("GHOST <nick> [password], Kills a ghosted nickname")}, - {"GUI", cmd_gui, 0, 0, 1, "GUI [APPLY|ATTACH|DETACH|SHOW|HIDE|FOCUS|FLASH|ICONIFY|COLOR <n>]\n" - " GUI [MSGBOX <text>|MENU TOGGLE]"}, + {"GUI", cmd_gui, 0, 0, 1, "GUI [APPLY|ATTACH|DETACH|SHOW|HIDE|FOCUS|FLASH|ICONIFY]\n" + " GUI [MSGBOX <text>|MENU TOGGLE]\n" + " GUI COLOR <n> [-NOOVERRIDE]"}, {"HELP", cmd_help, 0, 0, 1, 0}, {"HOP", cmd_hop, 1, 1, 1, N_("HOP <nick>, gives chanhalf-op status to the nick (needs chanop)")}, @@ -4077,7 +4107,7 @@ const struct commands xc_cmds[] = { N_("QUOTE <text>, sends the text in raw form to the server")}, #ifdef USE_OPENSSL {"RECONNECT", cmd_reconnect, 0, 0, 1, - N_("RECONNECT [-ssl] [<host>] [<port>] [<password>], Can be called just as /RECONNECT to reconnect to the current server or with /RECONNECT ALL to reconnect to all the open servers")}, + N_("RECONNECT [-ssl|-ssl-noverify] [<host>] [<port>] [<password>], Can be called just as /RECONNECT to reconnect to the current server or with /RECONNECT ALL to reconnect to all the open servers")}, #else {"RECONNECT", cmd_reconnect, 0, 0, 1, N_("RECONNECT [<host>] [<port>] [<password>], Can be called just as /RECONNECT to reconnect to the current server or with /RECONNECT ALL to reconnect to all the open servers")}, @@ -4089,14 +4119,14 @@ const struct commands xc_cmds[] = { {"SEND", cmd_send, 0, 0, 1, N_("SEND <nick> [<file>]")}, #ifdef USE_OPENSSL {"SERVCHAN", cmd_servchan, 0, 0, 1, - N_("SERVCHAN [-ssl] <host> <port> <channel>, connects and joins a channel")}, + N_("SERVCHAN [-insecure|-ssl|-ssl-noverify] <host> <port> <channel>, connects and joins a channel using ssl unless otherwise specified")}, #else {"SERVCHAN", cmd_servchan, 0, 0, 1, N_("SERVCHAN <host> <port> <channel>, connects and joins a channel")}, #endif #ifdef USE_OPENSSL {"SERVER", cmd_server, 0, 0, 1, - N_("SERVER [-ssl] <host> [<port>] [<password>], connects to a server, the default port is 6667 for normal connections, and 6697 for ssl connections")}, + N_("SERVER [-insecure|-ssl|-ssl-noverify] <host> [<port>] [<password>], connects to a server using ssl unless otherwise specified, the default port is 6697 for ssl connections and 6667 for insecure connections")}, #else {"SERVER", cmd_server, 0, 0, 1, N_("SERVER <host> [<port>] [<password>], connects to a server, the default port is 6667")}, @@ -4127,8 +4157,6 @@ const struct commands xc_cmds[] = { {"USERLIST", cmd_userlist, 1, 1, 1, 0}, {"VOICE", cmd_voice, 1, 1, 1, N_("VOICE <nick>, gives voice status to someone (needs chanop)")}, - {"WALLCHAN", cmd_wallchan, 1, 1, 1, - N_("WALLCHAN <message>, writes the message to all channels")}, {"WALLCHOP", cmd_wallchop, 1, 1, 1, N_("WALLCHOP <message>, sends the message to all chanops on the current channel")}, {0, 0, 0, 0, 0, 0} @@ -4420,6 +4448,9 @@ check_special_chars (char *cmd, int do_ascii) /* check for %X */ case 'I': buf[i] = '\035'; break; + case 'S': + buf[i] = '\036'; + break; case 'C': buf[i] = '\003'; break; diff --git a/src/common/plugin-timer.c b/src/common/plugin-timer.c index d0c82c28..17f11ce3 100644 --- a/src/common/plugin-timer.c +++ b/src/common/plugin-timer.c @@ -198,7 +198,7 @@ timer_cb (char *word[], char *word_eol[], void *userdata) offset += 2; } - timeout = atof (word[2 + offset]); + timeout = g_ascii_strtod (word[2 + offset], NULL); command = word_eol[3 + offset]; if (timeout < 0.1 || timeout * 1000 > INT_MAX || !command[0]) diff --git a/src/common/plugin.c b/src/common/plugin.c index d3f3b7ca..f4691d73 100644 --- a/src/common/plugin.c +++ b/src/common/plugin.c @@ -491,7 +491,6 @@ plugin_auto_load (session *sess) for_files (lib_dir, "hcfishlim.dll", plugin_auto_load_cb); for_files(lib_dir, "hclua.dll", plugin_auto_load_cb); for_files (lib_dir, "hcperl.dll", plugin_auto_load_cb); - for_files (lib_dir, "hcpython2.dll", plugin_auto_load_cb); for_files (lib_dir, "hcpython3.dll", plugin_auto_load_cb); for_files (lib_dir, "hcupd.dll", plugin_auto_load_cb); for_files (lib_dir, "hcwinamp.dll", plugin_auto_load_cb); @@ -572,7 +571,7 @@ plugin_hook_find (GSList *list, int type, char *name) static int plugin_hook_run (session *sess, char *name, char *word[], char *word_eol[], - hexchat_event_attrs *attrs, int type) + hexchat_event_attrs *attrs, int type, int mask) { /* fix segfault https://github.com/hexchat/hexchat/issues/2265 */ static int depth = 0; @@ -612,6 +611,11 @@ plugin_hook_run (session *sess, char *name, char *word[], char *word_eol[], break; } + if ((ret & mask) != ret) { + g_critical("plugin tried to eat cleanup hooks"); + } + ret &= mask; + if ((ret & HEXCHAT_EAT_HEXCHAT) && (ret & HEXCHAT_EAT_PLUGIN)) { eat = 1; @@ -652,7 +656,7 @@ xit: int plugin_emit_command (session *sess, char *name, char *word[], char *word_eol[]) { - return plugin_hook_run (sess, name, word, word_eol, NULL, HOOK_COMMAND); + return plugin_hook_run (sess, name, word, word_eol, NULL, HOOK_COMMAND, -1); } hexchat_event_attrs * @@ -678,7 +682,7 @@ plugin_emit_server (session *sess, char *name, char *word[], char *word_eol[], attrs.server_time_utc = server_time; return plugin_hook_run (sess, name, word, word_eol, &attrs, - HOOK_SERVER | HOOK_SERVER_ATTRS); + HOOK_SERVER | HOOK_SERVER_ATTRS, -1); } /* see if any plugins are interested in this print event */ @@ -691,7 +695,7 @@ plugin_emit_print (session *sess, char *word[], time_t server_time) attrs.server_time_utc = server_time; return plugin_hook_run (sess, word[0], word, NULL, &attrs, - HOOK_PRINT | HOOK_PRINT_ATTRS); + HOOK_PRINT | HOOK_PRINT_ATTRS, -1); } /* used by plugin_emit_dummy_print to fix some UB */ @@ -707,7 +711,7 @@ check_and_invalidate(void *plug_, void *killsess_) } int -plugin_emit_dummy_print (session *sess, char *name) +plugin_emit_dummy_print (session *sess, char *name, int mask) { char *word[PDIWORDS]; int i; @@ -716,7 +720,7 @@ plugin_emit_dummy_print (session *sess, char *name) for (i = 1; i < PDIWORDS; i++) word[i] = "\000"; - i = plugin_hook_run (sess, name, word, NULL, NULL, HOOK_PRINT); + i = plugin_hook_run (sess, name, word, NULL, NULL, HOOK_PRINT, mask); /* shoehorned fix for Undefined Behaviour */ /* see https://stackoverflow.com/q/52628773/3691554 */ @@ -758,7 +762,7 @@ plugin_emit_keypress (session *sess, unsigned int state, unsigned int keyval, gu for (i = 5; i < PDIWORDS; i++) word[i] = "\000"; - return plugin_hook_run (sess, word[0], word, NULL, NULL, HOOK_PRINT); + return plugin_hook_run (sess, word[0], word, NULL, NULL, HOOK_PRINT, -1); } static int @@ -1149,12 +1153,16 @@ hexchat_get_context (hexchat_plugin *ph) int hexchat_set_context (hexchat_plugin *ph, hexchat_context *context) { - if (is_session (context)) + if (context == NULL) { + return 0; + } + if (!is_session (context)) { - ph->context = context; - return 1; + g_critical("plugin tried to set an invalid context"); + return 0; } - return 0; + ph->context = context; + return 1; } hexchat_context * diff --git a/src/common/plugin.h b/src/common/plugin.h index fb7da831..051d1f5a 100644 --- a/src/common/plugin.h +++ b/src/common/plugin.h @@ -174,7 +174,7 @@ int plugin_emit_command (session *sess, char *name, char *word[], char *word_eol int plugin_emit_server (session *sess, char *name, char *word[], char *word_eol[], time_t server_time); int plugin_emit_print (session *sess, char *word[], time_t server_time); -int plugin_emit_dummy_print (session *sess, char *name); +int plugin_emit_dummy_print (session *sess, char *name, int mask); int plugin_emit_keypress (session *sess, unsigned int state, unsigned int keyval, gunichar key); GList* plugin_command_list(GList *tmp_list); int plugin_show_help (session *sess, char *cmd); diff --git a/src/common/proto-irc.c b/src/common/proto-irc.c index c8e44b62..5b8e02c4 100644 --- a/src/common/proto-irc.c +++ b/src/common/proto-irc.c @@ -460,6 +460,18 @@ channel_date (session *sess, char *chan, char *timestr, tags_data->timestamp); } +static int +trailing_index(char *word_eol[]) +{ + int param_index; + for (param_index = 3; param_index < PDIWORDS; ++param_index) + { + if (word_eol[param_index][0] == ':') + break; + } + return param_index; +} + static void process_numeric (session * sess, int n, char *word[], char *word_eol[], char *text, @@ -491,22 +503,6 @@ process_numeric (session * sess, int n, goto def; - case 4: /* check the ircd type */ - serv->use_listargs = FALSE; - serv->modes_per_line = 3; /* default to IRC RFC */ - if (strncmp (word[5], "bahamut", 7) == 0) /* DALNet */ - { - serv->use_listargs = TRUE; /* use the /list args */ - } else if (strncmp (word[5], "u2.10.", 6) == 0) /* Undernet */ - { - serv->use_listargs = TRUE; /* use the /list args */ - serv->modes_per_line = 6; /* allow 6 modes per line */ - } else if (strncmp (word[5], "glx2", 4) == 0) - { - serv->use_listargs = TRUE; /* use the /list args */ - } - goto def; - case 5: inbound_005 (serv, word, tags_data); goto def; @@ -631,7 +627,7 @@ process_numeric (session * sess, int n, case 320: /* :is an identified user */ if (!serv->skip_next_whois) EMIT_SIGNAL_TIMESTAMP (XP_TE_WHOIS_ID, whois_sess, word[4], - word_eol[5] + 1, NULL, NULL, 0, + word_eol[5][0] == ':' ? word_eol[5] + 1 : word_eol[5], NULL, NULL, 0, tags_data->timestamp); break; @@ -801,7 +797,7 @@ process_numeric (session * sess, int n, break; case 346: /* +I-list entry */ - if (!inbound_banlist (sess, atol (word[7]), word[4], word[5], word[6], 346, + if (!inbound_banlist (sess, atol (STRIP_COLON (word, word_eol, 7)), word[4], word[5], word[6], 346, tags_data)) goto def; break; @@ -812,7 +808,7 @@ process_numeric (session * sess, int n, break; case 348: /* +e-list entry */ - if (!inbound_banlist (sess, atol (word[7]), word[4], word[5], word[6], 348, + if (!inbound_banlist (sess, atol (STRIP_COLON (word, word_eol, 7)), word[4], word[5], word[6], 348, tags_data)) goto def; break; @@ -837,7 +833,7 @@ process_numeric (session * sess, int n, break; case 367: /* banlist entry */ - if (!inbound_banlist (sess, atol (word[7]), word[4], word[5], word[6], 367, + if (!inbound_banlist (sess, atol (STRIP_COLON (word, word_eol, 7)), word[4], word[5], word[6], 367, tags_data)) goto def; break; @@ -924,6 +920,14 @@ process_numeric (session * sess, int n, notify_set_online (serv, word[4], tags_data); break; + case 524: // ERR_HELPNOTFOUND + case 704: // RPL_HELPSTART + case 705: // RPL_HELPTXT + case 706: // RPL_ENDOFHELP + EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVTEXT, sess, STRIP_COLON(word, word_eol, 5), NULL, NULL, NULL, + 0, tags_data->timestamp); + break; + case 728: /* +q-list entry */ /* NOTE: FREENODE returns these results inconsistent with e.g. +b */ /* Who else has imlemented MODE_QUIET, I wonder? */ @@ -1139,6 +1143,39 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[], (word_eol[3][0] == ':') ? word_eol[3] + 1 : NULL, tags_data); return; + + case WORDL('F','A','I','L'): + text = STRIP_COLON(word, word_eol, trailing_index(word_eol)); + if (g_strcmp0(word[3], "*") == 0) + { + EMIT_SIGNAL_TIMESTAMP (XP_TE_FAIL, sess, word[4], text, NULL, NULL, NULL, tags_data->timestamp); + } else + { + EMIT_SIGNAL_TIMESTAMP (XP_TE_FAILCMD, sess, word[3], word[4], text, NULL, NULL, tags_data->timestamp); + } + return; + + case WORDL('W','A','R','N'): + text = STRIP_COLON(word, word_eol, trailing_index(word_eol)); + if (g_strcmp0(word[3], "*") == 0) + { + EMIT_SIGNAL_TIMESTAMP (XP_TE_WARN, sess, word[4], text, NULL, NULL, NULL, tags_data->timestamp); + } else + { + EMIT_SIGNAL_TIMESTAMP (XP_TE_WARNCMD, sess, word[3], word[4], text, NULL, NULL, tags_data->timestamp); + } + return; + + case WORDL('N','O','T','E'): + text = STRIP_COLON(word, word_eol, trailing_index(word_eol)); + if (g_strcmp0(word[3], "*") == 0) + { + EMIT_SIGNAL_TIMESTAMP (XP_TE_NOTE, sess, word[4], text, NULL, NULL, NULL, tags_data->timestamp); + } else + { + EMIT_SIGNAL_TIMESTAMP (XP_TE_NOTECMD, sess, word[3], word[4], text, NULL, NULL, tags_data->timestamp); + } + return; } goto garbage; @@ -1189,8 +1226,6 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[], case WORDL('N','O','T','I'): { - int id = FALSE; /* identified */ - text = word_eol[4]; if (*text == ':') { @@ -1219,18 +1254,8 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[], } #endif - if (serv->have_idmsg) - { - if (*text == '+') - { - id = TRUE; - text++; - } else if (*text == '-') - text++; - } - if (!ignore_check (word[1], IG_NOTI)) - inbound_notice (serv, word[3], nick, text, ip, id, tags_data); + inbound_notice (serv, word[3], nick, text, ip, tags_data->identified, tags_data); } return; @@ -1238,7 +1263,6 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[], { char *to = word[3]; int len; - int id = FALSE; /* identified */ if (*to) { /* Handle limited channel messages, for now no special event */ @@ -1249,15 +1273,7 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[], text = word_eol[4]; if (*text == ':') text++; - if (serv->have_idmsg) - { - if (*text == '+') - { - id = TRUE; - text++; - } else if (*text == '-') - text++; - } + len = strlen (text); if (text[0] == 1) /* ctcp */ { @@ -1289,7 +1305,7 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[], } } - ctcp_handle (sess, to, nick, ip, text, word, word_eol, id, + ctcp_handle (sess, to, nick, ip, text, word, word_eol, tags_data->identified, tags_data); /* Note word will be invalid beyond this scope */ @@ -1300,13 +1316,13 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[], { if (ignore_check (word[1], IG_CHAN)) return; - inbound_chanmsg (serv, NULL, to, nick, text, FALSE, id, + inbound_chanmsg (serv, NULL, to, nick, text, FALSE, tags_data->identified, tags_data); } else { if (ignore_check (word[1], IG_PRIV)) return; - inbound_privmsg (serv, nick, ip, text, id, tags_data); + inbound_privmsg (serv, nick, ip, text, tags_data->identified, tags_data); } } } @@ -1537,6 +1553,9 @@ handle_message_tags (server *serv, const char *tags_str, if (serv->have_account_tag && !strcmp (key, "account")) tags_data->account = g_strdup (value); + if (serv->have_idmsg && strcmp (key, "solanum.chat/identified")) + tags_data->identified = TRUE; + if (serv->have_server_time && !strcmp (key, "time")) handle_message_tag_time (value, tags_data); } diff --git a/src/common/proto-irc.h b/src/common/proto-irc.h index 0f72c644..6f52f1bc 100644 --- a/src/common/proto-irc.h +++ b/src/common/proto-irc.h @@ -26,6 +26,7 @@ #define MESSAGE_TAGS_DATA_INIT \ { \ NULL, /* account name */ \ + FALSE, /* identified to nick */ \ (time_t)0, /* timestamp */ \ } @@ -38,6 +39,7 @@ typedef struct { char *account; + gboolean identified; time_t timestamp; } message_tags_data; diff --git a/src/common/scram.c b/src/common/scram.c new file mode 100644 index 00000000..b39199de --- /dev/null +++ b/src/common/scram.c @@ -0,0 +1,333 @@ +/* HexChat + * Copyright (C) 2023 Patrick Okraku + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "hexchat.h" + +#ifdef USE_OPENSSL + +#include "scram.h" +#include <openssl/hmac.h> +#include <openssl/rand.h> + +#define NONCE_LENGTH 18 +#define CLIENT_KEY "Client Key" +#define SERVER_KEY "Server Key" + +// EVP_MD_CTX_create() and EVP_MD_CTX_destroy() were renamed in OpenSSL 1.1.0 +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) +#define EVP_MD_CTX_new(ctx) EVP_MD_CTX_create(ctx) +#define EVP_MD_CTX_free(ctx) EVP_MD_CTX_destroy(ctx) +#endif + +scram_session +*scram_session_create (const char *digest, const char *username, const char *password) +{ + scram_session *session; + const EVP_MD *md; +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + OpenSSL_add_all_algorithms (); +#endif + md = EVP_get_digestbyname (digest); + + if (md == NULL) + { + // Unknown message digest + return NULL; + } + + session = g_new0 (scram_session, 1); + session->digest = md; + session->digest_size = EVP_MD_size (md); + session->username = g_strdup (username); + session->password = g_strdup (password); + return session; +} + +void +scram_session_free (scram_session *session) +{ + if (session == NULL) + { + return; + } + + g_free (session->username); + g_free (session->password); + g_free (session->client_nonce_b64); + g_free (session->client_first_message_bare); + g_free (session->salted_password); + g_free (session->auth_message); + g_free (session->error); + + g_free (session); +} + +static int +create_nonce (void *buffer, size_t length) +{ + return RAND_bytes (buffer, length); +} + +static int +create_SHA (scram_session *session, const unsigned char *input, size_t input_len, + unsigned char *output, unsigned int *output_len) +{ + EVP_MD_CTX *md_ctx = EVP_MD_CTX_new (); + + if (!EVP_DigestInit_ex (md_ctx, session->digest, NULL)) + { + session->error = g_strdup ("Message digest initialization failed"); + EVP_MD_CTX_free (md_ctx); + return SCRAM_ERROR; + } + + if (!EVP_DigestUpdate (md_ctx, input, input_len)) + { + session->error = g_strdup ("Message digest update failed"); + EVP_MD_CTX_free (md_ctx); + return SCRAM_ERROR; + } + + if (!EVP_DigestFinal_ex (md_ctx, output, output_len)) + { + session->error = g_strdup ("Message digest finalization failed"); + EVP_MD_CTX_free (md_ctx); + return SCRAM_ERROR; + } + + EVP_MD_CTX_free (md_ctx); + return SCRAM_IN_PROGRESS; +} + +static scram_status +process_client_first (scram_session *session, char **output, size_t *output_len) +{ + char nonce[NONCE_LENGTH]; + + if (!create_nonce (nonce, NONCE_LENGTH)) + { + session->error = g_strdup ("Could not create client nonce"); + return SCRAM_ERROR; + } + + session->client_nonce_b64 = g_base64_encode ((guchar *) nonce, NONCE_LENGTH); + *output = g_strdup_printf ("n,,n=%s,r=%s", session->username, session->client_nonce_b64); + *output_len = strlen (*output); + session->client_first_message_bare = g_strdup (*output + 3); + session->step++; + return SCRAM_IN_PROGRESS; +} + +static scram_status +process_server_first (scram_session *session, const char *data, char **output, + size_t *output_len) +{ + char **params, *client_final_message_without_proof, *salt, *server_nonce_b64, + *client_proof_b64; + unsigned char *client_key, stored_key[EVP_MAX_MD_SIZE], *client_signature, *client_proof; + unsigned int i, param_count, iteration_count, client_key_len, stored_key_len; + gsize salt_len = 0; + size_t client_nonce_len; + + params = g_strsplit (data, ",", -1); + param_count = g_strv_length (params); + + if (param_count < 3) + { + session->error = g_strdup_printf ("Invalid server-first-message: %s", data); + g_strfreev (params); + return SCRAM_ERROR; + } + + server_nonce_b64 = NULL; + salt = NULL; + iteration_count = 0; + + for (i = 0; i < param_count; i++) + { + if (!strncmp (params[i], "r=", 2)) + { + g_free (server_nonce_b64); + server_nonce_b64 = g_strdup (params[i] + 2); + } + else if (!strncmp (params[i], "s=", 2)) + { + g_free (salt); + salt = g_strdup (params[i] + 2); + } + else if (!strncmp (params[i], "i=", 2)) + { + iteration_count = strtoul (params[i] + 2, NULL, 10); + } + } + + g_strfreev (params); + + if (server_nonce_b64 == NULL || *server_nonce_b64 == '\0' || salt == NULL || + *salt == '\0' || iteration_count == 0) + { + session->error = g_strdup_printf ("Invalid server-first-message: %s", data); + g_free (server_nonce_b64); + g_free (salt); + return SCRAM_ERROR; + } + + client_nonce_len = strlen (session->client_nonce_b64); + + // The server can append his nonce to the client's nonce + if (strlen (server_nonce_b64) < client_nonce_len || + strncmp (server_nonce_b64, session->client_nonce_b64, client_nonce_len)) + { + session->error = g_strdup_printf ("Invalid server nonce: %s", server_nonce_b64); + return SCRAM_ERROR; + } + + g_base64_decode_inplace ((gchar *) salt, &salt_len); + + // SaltedPassword := Hi(Normalize(password), salt, i) + session->salted_password = g_malloc (session->digest_size); + + PKCS5_PBKDF2_HMAC (session->password, strlen (session->password), (unsigned char *) salt, + salt_len, iteration_count, session->digest, session->digest_size, + session->salted_password); + + // AuthMessage := client-first-message-bare + "," + + // server-first-message + "," + + // client-final-message-without-proof + client_final_message_without_proof = g_strdup_printf ("c=biws,r=%s", server_nonce_b64); + + session->auth_message = g_strdup_printf ("%s,%s,%s", session->client_first_message_bare, + data, client_final_message_without_proof); + + // ClientKey := HMAC(SaltedPassword, "Client Key") + client_key = g_malloc0 (session->digest_size); + + HMAC (session->digest, session->salted_password, session->digest_size, + (unsigned char *) CLIENT_KEY, strlen (CLIENT_KEY), client_key, &client_key_len); + + // StoredKey := H(ClientKey) + if (!create_SHA (session, client_key, session->digest_size, stored_key, &stored_key_len)) + { + g_free (client_final_message_without_proof); + g_free (server_nonce_b64); + g_free (salt); + g_free (client_key); + return SCRAM_ERROR; + } + + // ClientSignature := HMAC(StoredKey, AuthMessage) + client_signature = g_malloc0 (session->digest_size); + HMAC (session->digest, stored_key, stored_key_len, (unsigned char *) session->auth_message, + strlen ((char *) session->auth_message), client_signature, NULL); + + // ClientProof := ClientKey XOR ClientSignature + client_proof = g_malloc0 (client_key_len); + + for (i = 0; i < client_key_len; i++) + { + client_proof[i] = client_key[i] ^ client_signature[i]; + } + + client_proof_b64 = g_base64_encode ((guchar *) client_proof, client_key_len); + + *output = g_strdup_printf ("%s,p=%s", client_final_message_without_proof, client_proof_b64); + *output_len = strlen (*output); + + g_free (server_nonce_b64); + g_free (salt); + g_free (client_final_message_without_proof); + g_free (client_key); + g_free (client_signature); + g_free (client_proof); + g_free (client_proof_b64); + + session->step++; + return SCRAM_IN_PROGRESS; +} + +static scram_status +process_server_final (scram_session *session, const char *data) +{ + char *verifier; + unsigned char *server_key, *server_signature; + unsigned int server_key_len = 0, server_signature_len = 0; + gsize verifier_len = 0; + + if (strlen (data) < 3 || (data[0] != 'v' && data[1] != '=')) + { + return SCRAM_ERROR; + } + + verifier = g_strdup (data + 2); + g_base64_decode_inplace (verifier, &verifier_len); + + // ServerKey := HMAC(SaltedPassword, "Server Key") + server_key = g_malloc0 (session->digest_size); + HMAC (session->digest, session->salted_password, session->digest_size, + (unsigned char *) SERVER_KEY, strlen (SERVER_KEY), server_key, &server_key_len); + + // ServerSignature := HMAC(ServerKey, AuthMessage) + server_signature = g_malloc0 (session->digest_size); + HMAC (session->digest, server_key, session->digest_size, + (unsigned char *) session->auth_message, strlen ((char *) session->auth_message), + server_signature, &server_signature_len); + + if (verifier_len == server_signature_len && + memcmp (verifier, server_signature, verifier_len) == 0) + { + g_free (verifier); + g_free (server_key); + g_free (server_signature); + return SCRAM_SUCCESS; + } + else + { + g_free (verifier); + g_free (server_key); + g_free (server_signature); + return SCRAM_ERROR; + } +} + +scram_status +scram_process (scram_session *session, const char *input, char **output, size_t *output_len) +{ + scram_status status; + + switch (session->step) + { + case 0: + status = process_client_first (session, output, output_len); + break; + case 1: + status = process_server_first (session, input, output, output_len); + break; + case 2: + status = process_server_final (session, input); + break; + default: + *output = NULL; + *output_len = 0; + status = SCRAM_ERROR; + break; + } + + return status; +} + +#endif \ No newline at end of file diff --git a/src/common/scram.h b/src/common/scram.h new file mode 100644 index 00000000..ffe22037 --- /dev/null +++ b/src/common/scram.h @@ -0,0 +1,51 @@ +/* HexChat + * Copyright (C) 2023 Patrick Okraku + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ +#ifndef HEXCHAT_SCRAM_H +#define HEXCHAT_SCRAM_H + +#include "config.h" +#ifdef USE_OPENSSL +#include <openssl/evp.h> + +typedef struct +{ + const EVP_MD *digest; + size_t digest_size; + char *username; + char *password; + char *client_nonce_b64; + char *client_first_message_bare; + unsigned char *salted_password; + char *auth_message; + char *error; + int step; +} scram_session; + +typedef enum +{ + SCRAM_ERROR = 0, + SCRAM_IN_PROGRESS, + SCRAM_SUCCESS +} scram_status; + +scram_session *scram_session_create (const char *digset, const char *username, const char *password); +void scram_session_free (scram_session *session); +scram_status scram_process (scram_session *session, const char *input, char **output, size_t *output_len); + +#endif +#endif \ No newline at end of file diff --git a/src/common/server.c b/src/common/server.c index 5c645eb5..c78ce900 100644 --- a/src/common/server.c +++ b/src/common/server.c @@ -61,10 +61,6 @@ #include "ssl.h" #endif -#ifdef USE_LIBPROXY -#include <proxy.h> -#endif - #ifdef USE_OPENSSL /* local variables */ static struct session *g_sess = NULL; @@ -78,9 +74,15 @@ static void server_disconnect (session * sess, int sendquit, int err); static int server_cleanup (server * serv); static void server_connect (server *serv, char *hostname, int port, int no_login); -#ifdef USE_LIBPROXY -extern pxProxyFactory *libproxy_factory; -#endif +static void +write_error (char *message, GError **error) +{ + if (error == NULL || *error == NULL) { + return; + } + g_printerr ("%s: %s\n", message, (*error)->message); + g_clear_error (error); +} /* actually send to the socket. This might do a character translation or send via SSL. server/dcc both use this function. */ @@ -360,7 +362,7 @@ server_read (GIOChannel *source, GIOCondition condition, server *serv) serv->linebuf[serv->pos] = lbuf[i]; if (serv->pos >= (sizeof (serv->linebuf) - 1)) fprintf (stderr, - "*** HEXCHAT WARNING: Buffer overflow - shit server!\n"); + "*** HEXCHAT WARNING: Buffer overflow - non-compliant server!\n"); else serv->pos++; } @@ -770,7 +772,7 @@ server_connect_success (server *serv) /* it'll be a memory leak, if connection isn't terminated by server_cleanup() */ - if ((err = _SSL_set_verify (serv->ctx, ssl_cb_verify, NULL))) + if ((err = _SSL_set_verify (serv->ctx, ssl_cb_verify))) { EMIT_SIGNAL (XP_TE_CONNFAIL, serv->server_session, err, NULL, NULL, NULL, 0); @@ -1392,14 +1394,16 @@ server_child (server * serv) if (!serv->dont_use_proxy) /* blocked in serverlist? */ { -#ifdef USE_LIBPROXY if (prefs.hex_net_proxy_type == 5) { char **proxy_list; char *url, *proxy; + GProxyResolver *resolver; + GError *error = NULL; + resolver = g_proxy_resolver_get_default (); url = g_strdup_printf ("irc://%s:%d", hostname, port); - proxy_list = px_proxy_factory_get_proxies (libproxy_factory, url); + proxy_list = g_proxy_resolver_lookup (resolver, url, NULL, &error); if (proxy_list) { /* can use only one */ @@ -1412,6 +1416,8 @@ server_child (server * serv) proxy_type = 3; else if (!strncmp (proxy, "socks", 5)) proxy_type = 2; + } else { + write_error ("Failed to lookup proxy", &error); } if (proxy_type) { @@ -1426,7 +1432,7 @@ server_child (server * serv) g_strfreev (proxy_list); g_free (url); } -#endif + if (prefs.hex_net_proxy_host[0] && prefs.hex_net_proxy_type > 0 && prefs.hex_net_proxy_use != 2) /* proxy is NOT dcc-only */ @@ -1553,7 +1559,7 @@ server_connect (server *serv, char *hostname, int port, int no_login) if (!hostname[0]) return; - if (port < 0) + if (port < 1 || port > 65535) { /* use default port for this server type */ port = 6667; @@ -1561,8 +1567,8 @@ server_connect (server *serv, char *hostname, int port, int no_login) if (serv->use_ssl) port = 6697; #endif + g_debug ("Attempted to connect to invalid port, assuming default port %d", port); } - port &= 0xffff; /* wrap around */ if (serv->connected || serv->connecting || serv->recondelay_tag) server_disconnect (sess, TRUE, -1); @@ -1759,11 +1765,14 @@ server_set_defaults (server *serv) g_free (serv->chanmodes); g_free (serv->nick_prefixes); g_free (serv->nick_modes); - +#ifdef USE_OPENSSL + g_clear_pointer (&serv->scram_session, scram_session_free); +#endif serv->chantypes = g_strdup ("#&!+"); serv->chanmodes = g_strdup ("beI,k,l"); serv->nick_prefixes = g_strdup ("@%+"); serv->nick_modes = g_strdup ("ohv"); + serv->modes_per_line = 3; /* https://datatracker.ietf.org/doc/html/rfc1459#section-4.2.3.1 */ serv->sasl_mech = MECH_PLAIN; if (!serv->encoding) @@ -1772,6 +1781,7 @@ server_set_defaults (server *serv) serv->nickcount = 1; serv->end_of_motd = FALSE; serv->sent_capend = FALSE; + serv->use_listargs = FALSE; serv->is_away = FALSE; serv->supports_watch = FALSE; serv->supports_monitor = FALSE; @@ -1929,6 +1939,8 @@ server_free (server *serv) #ifdef USE_OPENSSL if (serv->ctx) _SSL_context_free (serv->ctx); + + g_clear_pointer (&serv->scram_session, scram_session_free); #endif fe_server_callback (serv); diff --git a/src/common/servlist.c b/src/common/servlist.c index 79a5694b..1f29bb32 100644 --- a/src/common/servlist.c +++ b/src/common/servlist.c @@ -54,12 +54,6 @@ static const struct defaultserver def[] = /* Invalid hostname in cert */ {0, "irc.2600.net"}, - {"AccessIRC", 0}, - /* Self signed */ - {0, "irc.accessirc.net"}, - - {"ACN", 0, 0, 0, LOGIN_SASL, 0, TRUE}, - {0, "global.acn.gr"}, {"AfterNET", 0, 0, 0, LOGIN_SASL, 0, TRUE}, {0, "irc.afternet.org"}, @@ -67,15 +61,11 @@ static const struct defaultserver def[] = {"Aitvaras", 0}, #ifdef USE_OPENSSL {0, "irc.data.lt/+6668"}, - {0, "irc.omnitel.net/+6668"}, - {0, "irc.ktu.lt/+6668"}, - {0, "irc.kis.lt/+6668"}, + {0, "irc.omicron.lt/+6668"}, {0, "irc.vub.lt/+6668"}, #endif {0, "irc.data.lt"}, - {0, "irc.omnitel.net"}, - {0, "irc.ktu.lt"}, - {0, "irc.kis.lt"}, + {0, "irc.omicron.lt"}, {0, "irc.vub.lt"}, {"Anthrochat", 0, 0, 0, 0, 0, TRUE}, @@ -90,10 +80,6 @@ static const struct defaultserver def[] = {"AzzurraNet", 0}, {0, "irc.azzurra.org"}, - {"BetaChat", 0, 0, 0, LOGIN_SASL}, - {0, "irc.betachat.net"}, - {"BuddyIM", 0, 0, 0, LOGIN_SASL, 0, TRUE}, - {0, "irc.buddy.im"}, {"Canternet", 0, 0, 0, LOGIN_SASL, 0, TRUE}, {0, "irc.canternet.org"}, @@ -103,19 +89,16 @@ static const struct defaultserver def[] = {"ChatJunkies", 0}, {0, "irc.chatjunkies.org"}, - {"ChatNet", 0}, - {0, "irc.chatnet.org"}, + {"chatpat", 0, 0, "CP1251", LOGIN_CUSTOM, "MSG NS IDENTIFY %p"}, + {0, "irc.unibg.net"}, + {0, "irc.chatpat.bg"}, {"ChatSpike", 0, 0, 0, LOGIN_SASL}, {0, "irc.chatspike.net"}, - {"ChattingAway", 0}, - {0, "irc.chattingaway.com"}, - - {"Criten", 0}, - /* Self signed */ - {0, "irc.criten.net"}, - + {"DaIRC", 0}, + {0, "irc.dairc.net"}, + {"DALnet", 0, 0, 0, LOGIN_NICKSERV}, /* Self signed */ {0, "us.dal.net"}, @@ -132,19 +115,20 @@ static const struct defaultserver def[] = {"Dark-Tou-Net", 0}, {0, "irc.d-t-net.de"}, - - {"DeltaAnime", 0}, - {0, "irc.deltaanime.net"}, + + {"DigitalIRC", 0, 0, 0, LOGIN_SASL, 0, TRUE}, + {0, "irc.digitalirc.org"}, + +#ifdef USE_OPENSSL + {"DosersNET", 0, 0, 0, LOGIN_SASL, 0, TRUE}, + {0, "irc.dosers.net/+6697"}, +#endif {"EFnet", 0}, {0, "irc.choopa.net"}, - {0, "irc.paraphysics.net"}, {0, "efnet.port80.se"}, {0, "irc.underworld.no"}, - {0, "irc.inet.tele.dk"}, - - {"ElectroCode", 0, 0, 0, LOGIN_SASL, 0, TRUE}, - {0, "irc.electrocode.net"}, + {0, "efnet.deic.eu"}, {"EnterTheGame", 0}, {0, "irc.enterthegame.com"}, @@ -166,20 +150,8 @@ static const struct defaultserver def[] = /* Self signed */ {0, "irc.fdfnet.net"}, - {"freenode", 0, 0, 0, LOGIN_SASL, 0, TRUE}, - {0, "chat.freenode.net"}, - /* irc. points to chat. but many users and urls still reference it */ - {0, "irc.freenode.net"}, - - {"GalaxyNet", 0}, - {0, "irc.galaxynet.org"}, - {"GameSurge", 0}, {0, "irc.gamesurge.net"}, - - {"GeeksIRC", 0, 0, 0, LOGIN_SASL}, - /* Self signed */ - {0, "irc.geeksirc.net"}, {"GeekShed", 0, 0, 0, 0, 0, TRUE}, {0, "irc.geekshed.net"}, @@ -207,38 +179,31 @@ static const struct defaultserver def[] = {"Hashmark", 0}, {0, "irc.hashmark.net"}, - {"IdleMonkeys", 0}, - {0, "irc.idlemonkeys.net"}, + {"ICQ-Chat", 0, 0, 0, LOGIN_SASL, 0, TRUE}, + {0, "irc.icq-chat.com"}, - {"IndirectIRC", 0, 0, 0, LOGIN_SASL}, - /* Self signed */ - {0, "irc.indirectirc.com"}, - {"Interlinked", 0, 0, 0, LOGIN_SASL, 0, TRUE}, {0, "irc.interlinked.me"}, + {"Irc-Nerds", 0, 0, 0, LOGIN_SASL, 0, TRUE}, + {0, "irc.irc-nerds.net"}, + {"IRC4Fun", 0, 0, 0, LOGIN_SASL, 0, TRUE}, {0, "irc.irc4fun.net"}, - {"IRCHighWay", 0, 0, 0, 0, 0, TRUE}, - {0, "irc.irchighway.net"}, - {"IRCNet", 0}, {0, "open.ircnet.net"}, - {"Irctoo.net", 0}, + {"IRCtoo", 0}, {0, "irc.irctoo.net"}, - {"iZ-smart.net", 0, 0, "CP1252"}, - {0, "irc.iz-smart.net"}, - - {"KBFail", 0}, + {"Keyboard-Failure", 0}, /* SSL is self-signed */ {0, "irc.kbfail.net"}, - {"Krstarica", 0}, - {0, "irc.krstarica.com"}, - + {"Libera.Chat", 0, 0, 0, LOGIN_SASL, 0, TRUE}, + {0, "irc.libera.chat"}, + #ifdef USE_OPENSSL {"LibertaCasa", 0, 0, 0, LOGIN_SASL, 0, TRUE}, {0, "irc.liberta.casa"}, @@ -248,9 +213,6 @@ static const struct defaultserver def[] = /* Self signed */ {0, "irc.librairc.net"}, - {"Libera.Chat", 0, 0, 0, LOGIN_SASL, 0, TRUE}, - {0, "irc.libera.chat"}, - #ifdef USE_OPENSSL {"LinkNet", 0}, {0, "irc.link-net.org/+7000"}, @@ -262,10 +224,6 @@ static const struct defaultserver def[] = {"MIXXnet", 0}, {0, "irc.mixxnet.net"}, - {"ObsidianIRC", 0}, - /* Self signed */ - {0, "irc.obsidianirc.net"}, - {"Oceanius", 0, 0, 0, LOGIN_SASL}, /* Self signed */ {0, "irc.oceanius.com"}, @@ -276,16 +234,16 @@ static const struct defaultserver def[] = {"OtherNet", 0}, {0, "irc.othernet.org"}, - {"OzNet", 0}, + {"OzOrg", 0}, {0, "irc.oz.org"}, - {"PIRC.PL", 0, 0, 0, 0, 0, TRUE}, + {"PIK", 0}, + {0, "irc.krstarica.com"}, + + {"pirc.pl", 0, 0, 0, 0, 0, TRUE}, {0, "irc.pirc.pl"}, - - {"PonyChat", 0, 0, 0, LOGIN_SASL, 0, TRUE}, - {0, "irc.ponychat.net"}, - {"PTNet.org", 0}, + {"PTNet", 0}, {0, "irc.ptnet.org"}, {0, "uevora.ptnet.org"}, {0, "claranet.ptnet.org"}, @@ -306,12 +264,6 @@ static const struct defaultserver def[] = {0, "irc.ru"}, {0, "irc.lucky.net"}, - {"SceneNet", 0}, - {0, "irc.scene.org"}, - - {"SeilEn.de", 0, 0, "CP1252"}, - {0, "irc.seilen.de"}, - {"Serenity-IRC", 0}, {0, "irc.serenity-irc.net"}, @@ -328,10 +280,6 @@ static const struct defaultserver def[] = {"Sohbet.Net", 0, 0, "CP1254"}, {0, "irc.sohbet.net"}, - {"SolidIRC", 0}, - /* Self signed */ - {0, "irc.solidirc.com"}, - {"SorceryNet", 0, 0, 0, LOGIN_SASL}, /* Self signed */ {0, "irc.sorcery.net"}, @@ -339,9 +287,6 @@ static const struct defaultserver def[] = {"SpotChat", 0, 0, 0, LOGIN_SASL, 0, TRUE}, {0, "irc.spotchat.org"}, - {"StarChat", 0}, - {0, "irc.starchat.net"}, - {"Station51", 0}, /* Self signed */ {0, "irc.station51.net"}, @@ -360,9 +305,18 @@ static const struct defaultserver def[] = {"Techtronix", 0, 0, 0, LOGIN_SASL, 0, TRUE}, {0, "irc.techtronix.net"}, + {"TechNet", 0, 0, 0, LOGIN_SASL, 0, TRUE}, + {0, "irc.technet.chat"}, + {"tilde.chat", 0, 0, 0, LOGIN_SASL, 0, TRUE}, {0, "irc.tilde.chat"}, + {"TURLINet", 0, 0, 0, 0, 0, TRUE}, + /* all servers use UTF-8 and valid certs */ + {0, "irc.servx.org"}, + {0, "i.valware.uk"}, + + #ifdef USE_OPENSSL {"TripSit", 0, 0, 0, LOGIN_SASL, 0, TRUE}, {0, "irc.tripsit.me"}, @@ -370,27 +324,12 @@ static const struct defaultserver def[] = {0, "coconut.tripsit.me"}, {0, "innsbruck.tripsit.me"}, #endif - - {"TURLINet", 0, 0, 0, 0, 0, TRUE}, - /* Other servers use CP1251 and invalid certs */ - {0, "irc.servx.ru"}, {"UnderNet", 0, 0, 0, LOGIN_CUSTOM, "MSG x@channels.undernet.org login %u %p"}, - {0, "us.undernet.org"}, - - {"UniBG", 0, 0, "CP1251", LOGIN_CUSTOM, "MSG NS IDENTIFY %p"}, - {0, "irc.lirex.com"}, - {0, "irc.naturella.com"}, - {0, "irc.techno-link.com"}, - - {"Worldnet", 0}, - {0, "irc.worldnet.net"}, + {0, "irc.undernet.org"}, {"Xertion", 0, 0, 0, LOGIN_SASL, 0, TRUE}, {0, "irc.xertion.org"}, - - {"DeltaPool", 0, 0, 0, LOGIN_SASL, 0, TRUE}, - {0, "irc.deltapool.net"}, {0,0} }; @@ -947,6 +886,9 @@ servlist_net_add (char *name, char *comment, int prepend) net = g_new0 (ircnet, 1); net->name = g_strdup (name); net->flags = FLAG_CYCLE | FLAG_USE_GLOBAL | FLAG_USE_PROXY; +#ifdef USE_OPENSSL + net->flags |= FLAG_USE_SSL; +#endif if (prepend) network_list = g_slist_prepend (network_list, net); diff --git a/src/common/servlist.h b/src/common/servlist.h index ec885fef..c3d158b2 100644 --- a/src/common/servlist.h +++ b/src/common/servlist.h @@ -79,6 +79,9 @@ extern GSList *network_list; #define LOGIN_CHALLENGEAUTH 8 #define LOGIN_CUSTOM 9 #define LOGIN_SASLEXTERNAL 10 +#define LOGIN_SASL_SCRAM_SHA_1 11 +#define LOGIN_SASL_SCRAM_SHA_256 12 +#define LOGIN_SASL_SCRAM_SHA_512 13 #define CHALLENGEAUTH_ALGO "HMAC-SHA-256" #define CHALLENGEAUTH_NICK "Q@CServe.quakenet.org" diff --git a/src/common/ssl.c b/src/common/ssl.c index 0eb78bd7..e7f7e0a8 100644 --- a/src/common/ssl.c +++ b/src/common/ssl.c @@ -321,23 +321,22 @@ _SSL_socket (SSL_CTX *ctx, int sd) char * -_SSL_set_verify (SSL_CTX *ctx, void *verify_callback, char *cacert) +_SSL_set_verify (SSL_CTX *ctx, void *verify_callback) { - if (!SSL_CTX_set_default_verify_paths (ctx)) +#ifdef DEFAULT_CERT_FILE + if (!SSL_CTX_load_verify_locations (ctx, DEFAULT_CERT_FILE, NULL)) { - __SSL_fill_err_buf ("SSL_CTX_set_default_verify_paths"); + __SSL_fill_err_buf ("SSL_CTX_load_verify_locations"); return (err_buf); } -/* - if (cacert) +#else + if (!SSL_CTX_set_default_verify_paths (ctx)) { - if (!SSL_CTX_load_verify_locations (ctx, cacert, NULL)) - { - __SSL_fill_err_buf ("SSL_CTX_load_verify_locations"); - return (err_buf); - } + __SSL_fill_err_buf ("SSL_CTX_set_default_verify_paths"); + return (err_buf); } -*/ +#endif + SSL_CTX_set_verify (ctx, SSL_VERIFY_PEER, verify_callback); return (NULL); diff --git a/src/common/ssl.h b/src/common/ssl.h index e722f831..bea2f440 100644 --- a/src/common/ssl.h +++ b/src/common/ssl.h @@ -45,7 +45,7 @@ SSL_CTX *_SSL_context_init (void (*info_cb_func)); #define _SSL_context_free(a) SSL_CTX_free(a); SSL *_SSL_socket (SSL_CTX *ctx, int sd); -char *_SSL_set_verify (SSL_CTX *ctx, void *(verify_callback), char *cacert); +char *_SSL_set_verify (SSL_CTX *ctx, void *(verify_callback)); /* int SSL_connect(SSL *); int SSL_accept(SSL *); diff --git a/src/common/sysinfo/win32/backend.c b/src/common/sysinfo/win32/backend.c index 1d88b139..e2ae83ed 100644 --- a/src/common/sysinfo/win32/backend.c +++ b/src/common/sysinfo/win32/backend.c @@ -84,7 +84,8 @@ sysinfo_get_cpu_arch (void) GetNativeSystemInfo (&si); - if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) + if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 || + si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM64) { return cpu_arch = 64; } @@ -106,7 +107,8 @@ sysinfo_get_build_arch (void) GetSystemInfo (&si); - if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) + if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 || + si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM64) { return build_arch = 64; } @@ -354,6 +356,8 @@ static char *read_cpu_info (IWbemClassObject *object) VariantClear (&max_clock_speed_variant); + g_strchomp (name_utf8); + if (cpu_freq_mhz > 1000) { result = g_strdup_printf ("%s (%.2fGHz)", name_utf8, cpu_freq_mhz / 1000.f); diff --git a/src/common/text.c b/src/common/text.c index b0a90e03..a77700fa 100644 --- a/src/common/text.c +++ b/src/common/text.c @@ -1512,6 +1512,17 @@ static char * const pevt_discon_help[] = { N_("Error"), }; +static char * const pevt_stdrpl_help[] = { + N_("Error Code"), + N_("Error Message"), +}; + +static char * const pevt_stdrplcmd_help[] = { + N_("Command"), + N_("Error Code"), + N_("Error Message"), +}; + #include "textevents.h" static void diff --git a/src/common/textevents.in b/src/common/textevents.in index 14bd4b06..19b0d497 100644 --- a/src/common/textevents.in +++ b/src/common/textevents.in @@ -436,6 +436,18 @@ pevt_discon_help %C20*%O$tDisconnected (%C20$1%O) 1 +Fail +XP_TE_FAIL +pevt_stdrpl_help +%C20*%O$t$2%O +2 + +Fail Command +XP_TE_FAILCMD +pevt_stdrplcmd_help +%C20*%O$t$1: $3%O +3 + Found IP XP_TE_FOUNDIP pevt_foundip_help @@ -574,6 +586,18 @@ pevt_generic_none_help %C23*%O$tNo process is currently running 0 +Note +XP_TE_NOTE +pevt_stdrpl_help +%C22*%O$t$2%O +2 + +Note Command +XP_TE_NOTECMD +pevt_stdrplcmd_help +%C22*%O$t$1: $3%O +3 + Notice XP_TE_NOTICE pevt_notice_help @@ -802,6 +826,18 @@ pevt_usersonchan_help %C22*%O$tUsers on %C22$1%C: %C24$2%O 2 +Warn +XP_TE_WARN +pevt_stdrpl_help +%C23*%O$t$2%O +2 + +Warn Command +XP_TE_WARNCMD +pevt_stdrplcmd_help +%C23*%O$t$1: $3%O +3 + WhoIs Authenticated XP_TE_WHOIS_AUTH pevt_whoisauth_help diff --git a/src/common/url.c b/src/common/url.c index 6a1d09e8..162c5706 100644 --- a/src/common/url.c +++ b/src/common/url.c @@ -331,7 +331,7 @@ url_check_line (char *buf) GRegex *re(void); GMatchInfo *gmi; char *po = buf; - int i; + size_t i; /* Skip over message prefix */ if (*po == ':') @@ -536,6 +536,7 @@ struct { "lastfm", "/", URI_PATH }, { "xfire", "", URI_PATH }, { "ts3server", "", URI_PATH }, + { "web+*", "", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH }, { NULL, "", 0} }; @@ -569,7 +570,21 @@ re_url (void) g_string_append (grist_gstr, "|"); g_string_append (grist_gstr, "("); - g_string_append_printf (grist_gstr, "%s:", uri[i].scheme); + + if (!strcmp(uri[i].scheme, "web+*")) + { + g_string_append(grist_gstr, "web\\+[a-z]+"); + } + else + { + char *scheme_escaped = g_regex_escape_string (uri[i].scheme, strlen(uri[i].scheme)); + + g_string_append_printf (grist_gstr, "%s", scheme_escaped); + + g_free (scheme_escaped); + } + + g_string_append(grist_gstr, ":"); if (uri[i].flags & URI_AUTHORITY) g_string_append (grist_gstr, "//"); diff --git a/src/common/util.c b/src/common/util.c index 5b5fb23f..bd920cae 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -329,6 +329,7 @@ strip_color2 (const char *src, int len, char *dst, int flags) case '\026': /*ATTR_REVERSE: */ case '\002': /*ATTR_BOLD: */ case '\037': /*ATTR_UNDERLINE: */ + case '\036': /*ATTR_STRIKETHROUGH: */ case '\035': /*ATTR_ITALICS: */ if (!(flags & STRIP_ATTRIB)) goto pass_char; break; @@ -987,7 +988,7 @@ void country_search (char *pattern, void *ud, void (*print)(void *, char *, ...)) { const domain_t *dom; - int i; + size_t i; for (i = 0; i < sizeof (domain) / sizeof (domain_t); i++) { @@ -1374,11 +1375,16 @@ str_sha256hash (char *string) int i; unsigned char hash[SHA256_DIGEST_LENGTH]; char buf[SHA256_DIGEST_LENGTH * 2 + 1]; /* 64 digit hash + '\0' */ + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + SHA256 (string, strlen (string), hash); +#else SHA256_CTX sha256; SHA256_Init (&sha256); SHA256_Update (&sha256, string, strlen (string)); SHA256_Final (hash, &sha256); +#endif for (i = 0; i < SHA256_DIGEST_LENGTH; i++) { |