209 changes: 209 additions & 0 deletions plugins/fishlim/dh1080.c
@@ -0,0 +1,209 @@
/* HexChat
*
* 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
*/

/*
* For Diffie-Hellman key-exchange a 1080bit germain prime is used, the
* generator g=2 renders a field Fp from 1 to p-1. Therefore breaking it
* means to solve a discrete logarithm problem with no less than 1080bit.
*
* Base64 format is used to send the public keys over IRC.
*
* The calculated secret key is hashed with SHA-256, the result is converted
* to base64 for final use with blowfish.
*/

#include "dh1080.h"

#include <openssl/bn.h>
#include <openssl/dh.h>
#include <openssl/sha.h>

#include <string.h>
#include <glib.h>

#define DH1080_PRIME_BITS 1080
#define DH1080_PRIME_BYTES 135
#define SHA256_DIGEST_LENGTH 32
#define B64ABC "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
#define MEMZERO(x) memset(x, 0x00, (sizeof(x)/sizeof(*x)) )

/* All clients must use the same prime number to be able to keyx */
static const guchar prime1080[DH1080_PRIME_BYTES] =
{
0xFB, 0xE1, 0x02, 0x2E, 0x23, 0xD2, 0x13, 0xE8, 0xAC, 0xFA, 0x9A, 0xE8, 0xB9, 0xDF, 0xAD, 0xA3, 0xEA,
0x6B, 0x7A, 0xC7, 0xA7, 0xB7, 0xE9, 0x5A, 0xB5, 0xEB, 0x2D, 0xF8, 0x58, 0x92, 0x1F, 0xEA, 0xDE, 0x95,
0xE6, 0xAC, 0x7B, 0xE7, 0xDE, 0x6A, 0xDB, 0xAB, 0x8A, 0x78, 0x3E, 0x7A, 0xF7, 0xA7, 0xFA, 0x6A, 0x2B,
0x7B, 0xEB, 0x1E, 0x72, 0xEA, 0xE2, 0xB7, 0x2F, 0x9F, 0xA2, 0xBF, 0xB2, 0xA2, 0xEF, 0xBE, 0xFA, 0xC8,
0x68, 0xBA, 0xDB, 0x3E, 0x82, 0x8F, 0xA8, 0xBA, 0xDF, 0xAD, 0xA3, 0xE4, 0xCC, 0x1B, 0xE7, 0xE8, 0xAF,
0xE8, 0x5E, 0x96, 0x98, 0xA7, 0x83, 0xEB, 0x68, 0xFA, 0x07, 0xA7, 0x7A, 0xB6, 0xAD, 0x7B, 0xEB, 0x61,
0x8A, 0xCF, 0x9C, 0xA2, 0x89, 0x7E, 0xB2, 0x8A, 0x61, 0x89, 0xEF, 0xA0, 0x7A, 0xB9, 0x9A, 0x8A, 0x7F,
0xA9, 0xAE, 0x29, 0x9E, 0xFA, 0x7B, 0xA6, 0x6D, 0xEA, 0xFE, 0xFB, 0xEF, 0xBF, 0x0B, 0x7D, 0x8B
};

static DH *g_dh;

int
dh1080_init (void)
{
g_return_val_if_fail (g_dh == NULL, 0);

if ((g_dh = DH_new()))
{
int codes;

g_dh->p = BN_bin2bn (prime1080, DH1080_PRIME_BYTES, NULL);
g_dh->g = BN_new ();

g_assert (g_dh->p != NULL && g_dh->g != NULL);
BN_set_word(g_dh->g, 2);

if (DH_check (g_dh, &codes))
return codes == 0;
}

return 0;
}

void
dh1080_deinit (void)
{
g_clear_pointer (&g_dh, DH_free);
}

static inline int
DH_verifyPubKey (BIGNUM *pk)
{
int codes;
return DH_check_pub_key (g_dh, pk, &codes) && codes == 0;
}

static guchar *
dh1080_decode_b64 (const char *data, gsize *out_len)
{
GString *str = g_string_new (data);
guchar *ret;

if (str->len % 4 == 1 && str->str[str->len - 1] == 'A')
g_string_truncate (str, str->len - 1);

while (str->len % 4 != 0)
g_string_append_c (str, '=');

ret = g_base64_decode_inplace (str->str, out_len);
g_string_free (str, FALSE);
return ret;
}

static char *
dh1080_encode_b64 (const guchar *data, gsize data_len)
{
char *ret = g_base64_encode (data, data_len);
char *p;

if (!(p = strchr (ret, '=')))
{
char *new_ret = g_new(char, strlen(ret) + 2);
strcpy (new_ret, ret);
strcat (new_ret, "A");
g_free (ret);
ret = new_ret;
}
else
{
*p = '\0';
}

return ret;
}

int
dh1080_generate_key (char **priv_key, char **pub_key)
{
guchar buf[DH1080_PRIME_BYTES];
int len;
DH *dh;

g_assert (priv_key != NULL);
g_assert (pub_key != NULL);

dh = DHparams_dup (g_dh);
if (!dh)
return 0;

if (!DH_generate_key (dh))
{
DH_free (dh);
return 0;
}

MEMZERO (buf);
len = BN_bn2bin (dh->priv_key, buf);
*priv_key = dh1080_encode_b64 (buf, len);

MEMZERO (buf);
len = BN_bn2bin(dh->pub_key, buf);
*pub_key = dh1080_encode_b64 (buf, len);

OPENSSL_cleanse (buf, sizeof (buf));
DH_free (dh);
return 1;
}

int
dh1080_compute_key (const char *priv_key, const char *pub_key, char **secret_key)
{
char *pub_key_data;
gsize pub_key_len;
BIGNUM *pk;
DH *dh;

g_assert (secret_key != NULL);

/* Verify base64 strings */
if (strspn (priv_key, B64ABC) != strlen (priv_key)
|| strspn (pub_key, B64ABC) != strlen (pub_key))
return 0;

dh = DHparams_dup (g_dh);

pub_key_data = dh1080_decode_b64 (pub_key, &pub_key_len);
pk = BN_bin2bn (pub_key_data, pub_key_len, NULL);

if (DH_verifyPubKey (pk))
{
guchar shared_key[DH1080_PRIME_BYTES] = { 0 };
guchar sha256[SHA256_DIGEST_LENGTH] = { 0 };
char *priv_key_data;
gsize priv_key_len;
int shared_len;

priv_key_data = dh1080_decode_b64 (priv_key, &priv_key_len);
dh->priv_key = BN_bin2bn(priv_key_data, priv_key_len, NULL);

shared_len = DH_compute_key (shared_key, pk, dh);
SHA256(shared_key, shared_len, sha256);
*secret_key = dh1080_encode_b64 (sha256, sizeof(sha256));

OPENSSL_cleanse (priv_key_data, priv_key_len);
g_free (priv_key_data);
}

BN_free (pk);
DH_free (dh);
g_free (pub_key_data);
return 1;
}
24 changes: 24 additions & 0 deletions plugins/fishlim/dh1080.h
@@ -0,0 +1,24 @@
/* HexChat
*
* 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
*/

#pragma once

int dh1080_generate_key (char **, char **);
int dh1080_compute_key (const char *, const char *, char **);
int dh1080_init (void);
void dh1080_deinit (void);

3 changes: 2 additions & 1 deletion plugins/fishlim/fishlim.vcxproj
Expand Up @@ -53,13 +53,14 @@
<None Include="fishlim.def" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="bool.h" />
<ClInclude Include="dh1080.h" />
<ClInclude Include="fish.h" />
<ClInclude Include="irc.h" />
<ClInclude Include="keystore.h" />
<ClInclude Include="plugin_hexchat.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dh1080.c" />
<ClCompile Include="fish.c" />
<ClCompile Include="irc.c" />
<ClCompile Include="keystore.c" />
Expand Down
4 changes: 3 additions & 1 deletion plugins/fishlim/irc.c
Expand Up @@ -50,7 +50,9 @@ gboolean irc_parse_message(const char *words[],
if (command) *command = words[w];
w++;

*parameters_offset = w;
if (parameters_offset)
*parameters_offset = w;

return TRUE;
}

Expand Down
38 changes: 31 additions & 7 deletions plugins/fishlim/keystore.c
Expand Up @@ -63,6 +63,22 @@ static const char *get_keystore_password(void) {
}


static char *escape_nickname(const char *nick) {
char *escaped = g_strdup(nick);
char *p = escaped;

while (*p) {
if (*p == '[')
*p = '~';
else if (*p == ']')
*p = '!';

++p;
}

return escaped;
}

/**
* Gets a value for a nick/channel from addon_fishlim.conf. Unlike
* g_key_file_get_string, this function is case insensitive.
Expand Down Expand Up @@ -90,9 +106,13 @@ static gchar *get_nick_value(GKeyFile *keyfile, const char *nick, const char *it
char *keystore_get_key(const char *nick) {
/* Get the key */
GKeyFile *keyfile = getConfigFile();
gchar *value = get_nick_value(keyfile, nick, "key");
char *escaped_nick = escape_nickname(nick);
gchar *value = get_nick_value(keyfile, escaped_nick, "key");
g_key_file_free(keyfile);
if (!value) return NULL;
g_free(escaped_nick);

if (!value)
return NULL;

if (strncmp(value, "+OK ", 4) != 0) {
/* Key is stored in plaintext */
Expand Down Expand Up @@ -173,9 +193,10 @@ gboolean keystore_store_key(const char *nick, const char *key) {
char *wrapped;
gboolean ok = FALSE;
GKeyFile *keyfile = getConfigFile();

char *escaped_nick = escape_nickname(nick);

/* Remove old key */
delete_nick(keyfile, nick);
delete_nick(keyfile, escaped_nick);

/* Add new key */
password = get_keystore_password();
Expand All @@ -189,18 +210,19 @@ gboolean keystore_store_key(const char *nick, const char *key) {
g_free(encrypted);

/* Store encrypted in file */
g_key_file_set_string(keyfile, nick, "key", wrapped);
g_key_file_set_string(keyfile, escaped_nick, "key", wrapped);
g_free(wrapped);
} else {
/* Store unencrypted in file */
g_key_file_set_string(keyfile, nick, "key", key);
g_key_file_set_string(keyfile, escaped_nick, "key", key);
}

/* Save key store file */
ok = save_keystore(keyfile);

end:
g_key_file_free(keyfile);
g_free(escaped_nick);
return ok;
}

Expand All @@ -209,13 +231,15 @@ gboolean keystore_store_key(const char *nick, const char *key) {
*/
gboolean keystore_delete_nick(const char *nick) {
GKeyFile *keyfile = getConfigFile();
char *escaped_nick = escape_nickname(nick);

/* Delete entry */
gboolean ok = delete_nick(keyfile, nick);
gboolean ok = delete_nick(keyfile, escaped_nick);

/* Save */
if (ok) save_keystore(keyfile);

g_key_file_free(keyfile);
g_free(escaped_nick);
return ok;
}
314 changes: 297 additions & 17 deletions plugins/fishlim/plugin_hexchat.c

Large diffs are not rendered by default.

34 changes: 27 additions & 7 deletions plugins/lua/lua.c
Expand Up @@ -831,7 +831,7 @@ static inline int list_marshal(lua_State *L, const char *key, hexchat_list *list
}
if (list != NULL)
{
time_t tm = hexchat_list_time(ph, list, key);
time_t tm = hexchat_list_time(ph, list, key);
if(tm != -1)
{
lua_pushinteger(L, tm);
Expand All @@ -845,7 +845,7 @@ static inline int list_marshal(lua_State *L, const char *key, hexchat_list *list

static int api_hexchat_props_meta_index(lua_State *L)
{
char const *key = luaL_checkstring(L, 2);
char const *key = luaL_checkstring(L, 2);
return list_marshal(L, key, NULL);
}

Expand Down Expand Up @@ -875,7 +875,11 @@ static int api_hexchat_pluginprefs_meta_index(lua_State *L)
}
if(hexchat_pluginpref_get_str(h, key, str))
{
lua_pushstring(L, str);
/* Wasn't actually a failure */
if (!strcmp(str, "-1"))
lua_pushinteger(L, r);
else
lua_pushstring(L, str);
return 1;
}
lua_pushnil(L);
Expand Down Expand Up @@ -1481,15 +1485,31 @@ static void inject_string(script_info *info, char const *line)
{
lua_State *L = info->state;
int base, top;
char *ret_line;
gboolean force_ret = FALSE;

if(line[0] == '=')
{
line++;
force_ret = TRUE;
}
ret_line = g_strconcat("return ", line, NULL);

lua_rawgeti(L, LUA_REGISTRYINDEX, info->traceback);
base = lua_gettop(L);
if(luaL_loadbuffer(L, line, strlen(line), "@interpreter"))
if(luaL_loadbuffer(L, ret_line, strlen(ret_line), "@interpreter"))
{
hexchat_printf(ph, "Lua syntax error: %s", luaL_optstring(L, -1, ""));
lua_pop(L, 2);
return;
if(!force_ret)
lua_pop(L, 1);
if(force_ret || luaL_loadbuffer(L, line, strlen(line), "@interpreter"))
{
hexchat_printf(ph, "Lua syntax error: %s", luaL_optstring(L, -1, ""));
lua_pop(L, 2);
g_free(ret_line);
return;
}
}
g_free(ret_line);
info->status |= STATUS_ACTIVE;
if(lua_pcall(L, 0, LUA_MULTRET, base))
{
Expand Down
3 changes: 2 additions & 1 deletion plugins/perl/Makefile.am
Expand Up @@ -8,7 +8,8 @@ lib_LTLIBRARIES = perl.la
perl_la_SOURCES = perl.c
perl_la_LDFLAGS = $(PERL_LDFLAGS) $(PLUGIN_LDFLAGS) -module
perl_la_LIBADD = $(GLIB_LIBS)
perl_la_CFLAGS = $(PERL_CFLAGS) $(GLIB_CFLAGS) -I$(top_srcdir)/src/common
perl_la_CPPFLAGS = -I$(top_srcdir)/src/common
perl_la_CFLAGS = $(PERL_CFLAGS) $(GLIB_CFLAGS)

BUILT_SOURCES = hexchat.pm.h irc.pm.h
CLEANFILES = $(BUILT_SOURCES)
Expand Down
1 change: 1 addition & 0 deletions plugins/perl/perl.c
Expand Up @@ -29,6 +29,7 @@
#endif
#ifdef WIN32
#include <windows.h>
#include <stdbool.h>
#else
#include <dirent.h>
#endif
Expand Down
4 changes: 2 additions & 2 deletions plugins/python/Makefile.am
Expand Up @@ -4,6 +4,6 @@ lib_LTLIBRARIES = python.la
python_la_SOURCES = python.c
python_la_LDFLAGS = $(PLUGIN_LDFLAGS) -module
python_la_LIBADD = $(PYTHON_LIBS) $(GLIB_LIBS)
python_la_CPPFLAGS = $(PYTHON_CPPFLAGS)
python_la_CFLAGS = $(GLIB_CFLAGS) -I$(top_srcdir)/src/common
python_la_CPPFLAGS = -I$(top_srcdir)/src/common $(PYTHON_CPPFLAGS)
python_la_CFLAGS = $(GLIB_CFLAGS)

2 changes: 1 addition & 1 deletion plugins/python/python.c
Expand Up @@ -1849,7 +1849,7 @@ Module_hexchat_pluginpref_get(PyObject *self, PyObject *args)
BEGIN_XCHAT_CALLS(NONE);
retint = hexchat_pluginpref_get_int(prefph, var);
END_XCHAT_CALLS();
if ((retint == 0) && (strcmp(retstr, "0") != 0))
if ((retint == -1) && (strcmp(retstr, "-1") != 0))
ret = PyUnicode_FromString(retstr);
else
ret = PyLong_FromLong(retint);
Expand Down
2 changes: 1 addition & 1 deletion plugins/sysinfo/Makefile.am
Expand Up @@ -14,4 +14,4 @@ lib_LTLIBRARIES = sysinfo.la
sysinfo_la_SOURCES = $(sources)
sysinfo_la_LDFLAGS = $(PLUGIN_LDFLAGS) -module
sysinfo_la_LIBADD = $(LIBPCI_LIBS) $(GLIB_LIBS)
AM_CPPFLAGS = $(LIBPCI_CFLAGS) $(GLIB_CFLAGS) -I$(top_srcdir)/src/common -I$(srcdir)/shared
AM_CPPFLAGS = -I$(top_srcdir)/src/common -I$(srcdir)/shared $(LIBPCI_CFLAGS) $(GLIB_CFLAGS)
12 changes: 12 additions & 0 deletions plugins/sysinfo/osx/backend.m
Expand Up @@ -78,10 +78,22 @@
static char *
get_os_fallback (void)
{
#if !defined (MAC_OS_X_VERSION_10_9) || MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_9
SInt32 ver_major = 0,
ver_minor = 0,
ver_patch = 0;

Gestalt (gestaltSystemVersionMajor, &ver_major);
Gestalt (gestaltSystemVersionMinor, &ver_minor);
Gestalt (gestaltSystemVersionBugFix, &ver_patch);

return g_strdup_printf ("OS X %d.%d.%d", ver_major, ver_minor, ver_patch);
#else
NSProcessInfo *info = [NSProcessInfo processInfo];
NSOperatingSystemVersion version = [info operatingSystemVersion];

return g_strdup_printf ("OS X %ld.%ld.%ld", version.majorVersion, version.minorVersion, version.patchVersion);
#endif
}
char *
sysinfo_backend_get_os(void)
Expand Down
2 changes: 1 addition & 1 deletion plugins/sysinfo/unix/backend.c
Expand Up @@ -60,7 +60,7 @@ char *sysinfo_backend_get_memory(void)
{
return NULL;
}
if (xs_parse_meminfo (&swap_total, &swap_free, 1) != 1)
if (xs_parse_meminfo (&swap_total, &swap_free, 1) != 1 && swap_total != 0)
{
swap_fmt = sysinfo_format_memory (swap_total, swap_free);
}
Expand Down
2,355 changes: 1,190 additions & 1,165 deletions po/af.po

Large diffs are not rendered by default.

2,355 changes: 1,190 additions & 1,165 deletions po/am.po

Large diffs are not rendered by default.

2,357 changes: 1,191 additions & 1,166 deletions po/ast.po

Large diffs are not rendered by default.

2,355 changes: 1,190 additions & 1,165 deletions po/az.po

Large diffs are not rendered by default.

2,357 changes: 1,191 additions & 1,166 deletions po/be.po

Large diffs are not rendered by default.

2,355 changes: 1,190 additions & 1,165 deletions po/bg.po

Large diffs are not rendered by default.

2,370 changes: 1,198 additions & 1,172 deletions po/ca.po

Large diffs are not rendered by default.

2,357 changes: 1,191 additions & 1,166 deletions po/cs.po

Large diffs are not rendered by default.

2,359 changes: 1,192 additions & 1,167 deletions po/da.po

Large diffs are not rendered by default.

2,433 changes: 1,229 additions & 1,204 deletions po/de.po

Large diffs are not rendered by default.

2,355 changes: 1,190 additions & 1,165 deletions po/el.po

Large diffs are not rendered by default.

2,435 changes: 1,230 additions & 1,205 deletions po/en_GB.po

Large diffs are not rendered by default.

2,387 changes: 1,207 additions & 1,180 deletions po/es.po

Large diffs are not rendered by default.

2,357 changes: 1,191 additions & 1,166 deletions po/et.po

Large diffs are not rendered by default.

2,355 changes: 1,190 additions & 1,165 deletions po/eu.po

Large diffs are not rendered by default.

2,369 changes: 1,197 additions & 1,172 deletions po/fi.po

Large diffs are not rendered by default.

2,385 changes: 1,205 additions & 1,180 deletions po/fr.po

Large diffs are not rendered by default.

2,357 changes: 1,191 additions & 1,166 deletions po/gl.po

Large diffs are not rendered by default.

2,355 changes: 1,190 additions & 1,165 deletions po/gu.po

Large diffs are not rendered by default.

2,357 changes: 1,191 additions & 1,166 deletions po/hi.po

Large diffs are not rendered by default.

2,357 changes: 1,191 additions & 1,166 deletions po/hu.po

Large diffs are not rendered by default.

2,359 changes: 1,192 additions & 1,167 deletions po/id.po

Large diffs are not rendered by default.

2,421 changes: 1,223 additions & 1,198 deletions po/it.po

Large diffs are not rendered by default.

2,363 changes: 1,194 additions & 1,169 deletions po/ja_JP.po

Large diffs are not rendered by default.

2,357 changes: 1,191 additions & 1,166 deletions po/kn.po

Large diffs are not rendered by default.

2,467 changes: 1,246 additions & 1,221 deletions po/ko.po

Large diffs are not rendered by default.

2,672 changes: 1,349 additions & 1,323 deletions po/lt.po

Large diffs are not rendered by default.

2,803 changes: 1,414 additions & 1,389 deletions po/lv.po

Large diffs are not rendered by default.

2,357 changes: 1,191 additions & 1,166 deletions po/mk.po

Large diffs are not rendered by default.

2,355 changes: 1,190 additions & 1,165 deletions po/ms.po

Large diffs are not rendered by default.

2,362 changes: 1,194 additions & 1,168 deletions po/nb.po

Large diffs are not rendered by default.

2,355 changes: 1,190 additions & 1,165 deletions po/nl.po

Large diffs are not rendered by default.

2,357 changes: 1,191 additions & 1,166 deletions po/pa.po

Large diffs are not rendered by default.

2,359 changes: 1,192 additions & 1,167 deletions po/pl.po

Large diffs are not rendered by default.

2,359 changes: 1,192 additions & 1,167 deletions po/pt.po

Large diffs are not rendered by default.

2,442 changes: 1,234 additions & 1,208 deletions po/pt_BR.po

Large diffs are not rendered by default.

2,544 changes: 1,286 additions & 1,258 deletions po/ru.po

Large diffs are not rendered by default.

2,355 changes: 1,190 additions & 1,165 deletions po/rw.po

Large diffs are not rendered by default.

2,355 changes: 1,190 additions & 1,165 deletions po/sk.po

Large diffs are not rendered by default.

2,355 changes: 1,190 additions & 1,165 deletions po/sl.po

Large diffs are not rendered by default.

2,362 changes: 1,194 additions & 1,168 deletions po/sq.po

Large diffs are not rendered by default.

2,361 changes: 1,193 additions & 1,168 deletions po/sr.po

Large diffs are not rendered by default.

2,361 changes: 1,193 additions & 1,168 deletions po/sr@latin.po

Large diffs are not rendered by default.

2,507 changes: 1,267 additions & 1,240 deletions po/sv.po

Large diffs are not rendered by default.

2,357 changes: 1,191 additions & 1,166 deletions po/th.po

Large diffs are not rendered by default.

3,112 changes: 1,636 additions & 1,476 deletions po/tr.po

Large diffs are not rendered by default.

2,357 changes: 1,191 additions & 1,166 deletions po/uk.po

Large diffs are not rendered by default.

2,357 changes: 1,191 additions & 1,166 deletions po/vi.po

Large diffs are not rendered by default.

2,355 changes: 1,190 additions & 1,165 deletions po/wa.po

Large diffs are not rendered by default.

2,363 changes: 1,194 additions & 1,169 deletions po/zh_CN.po

Large diffs are not rendered by default.

2,355 changes: 1,190 additions & 1,165 deletions po/zh_TW.po

Large diffs are not rendered by default.

14 changes: 11 additions & 3 deletions src/common/cfgfiles.c
Expand Up @@ -459,7 +459,6 @@ const struct prefs vars[] =
{"gui_ulist_hide", P_OFFINT (hex_gui_ulist_hide), TYPE_BOOL},
{"gui_ulist_icons", P_OFFINT (hex_gui_ulist_icons), TYPE_BOOL},
{"gui_ulist_pos", P_OFFINT (hex_gui_ulist_pos), TYPE_INT},
{"gui_ulist_resizable", P_OFFINT (hex_gui_ulist_resizable), TYPE_BOOL},
{"gui_ulist_show_hosts", P_OFFINT(hex_gui_ulist_show_hosts), TYPE_BOOL},
{"gui_ulist_sort", P_OFFINT (hex_gui_ulist_sort), TYPE_INT},
{"gui_ulist_style", P_OFFINT (hex_gui_ulist_style), TYPE_BOOL},
Expand Down Expand Up @@ -532,7 +531,7 @@ const struct prefs vars[] =
{"net_auto_reconnectonfail", P_OFFINT (hex_net_auto_reconnectonfail), TYPE_BOOL},
#endif
{"net_bind_host", P_OFFSET (hex_net_bind_host), TYPE_STR},
{"net_ping_timeout", P_OFFINT (hex_net_ping_timeout), TYPE_INT},
{"net_ping_timeout", P_OFFINT (hex_net_ping_timeout), TYPE_INT, hexchat_reinit_timers},
{"net_proxy_auth", P_OFFINT (hex_net_proxy_auth), TYPE_BOOL},
{"net_proxy_host", P_OFFSET (hex_net_proxy_host), TYPE_STR},
{"net_proxy_pass", P_OFFSET (hex_net_proxy_pass), TYPE_STR},
Expand Down Expand Up @@ -771,7 +770,6 @@ load_default_config(void)
prefs.hex_gui_tray_blink = 1;
prefs.hex_gui_ulist_count = 1;
prefs.hex_gui_ulist_icons = 1;
prefs.hex_gui_ulist_resizable = 1;
prefs.hex_gui_ulist_style = 1;
prefs.hex_gui_win_save = 1;
prefs.hex_input_flash_hilight = 1;
Expand Down Expand Up @@ -1046,6 +1044,11 @@ save_config (void)
return 0;
}
}

if (vars[i].after_update != NULL)
{
vars[i].after_update();
}
i++;
}
while (vars[i].name);
Expand Down Expand Up @@ -1293,6 +1296,11 @@ cmd_set (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
set_showval (sess, &vars[i], tbuf);
}

if (vars[i].after_update != NULL)
{
vars[i].after_update();
}
break;
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/common/cfgfiles.h
Expand Up @@ -71,6 +71,11 @@ struct prefs
unsigned short offset;
unsigned short len;
unsigned short type;
/*
* an optional function which will be called after the preference value has
* been updated.
*/
void (*after_update)(void);
};

#define TYPE_STR 0
Expand Down
2 changes: 1 addition & 1 deletion src/common/dbus/Makefile.am
Expand Up @@ -15,7 +15,7 @@ BUILT_SOURCES = \

CLEANFILES = $(BUILT_SOURCES)

AM_CPPFLAGS = $(COMMON_CFLAGS) $(DBUS_CFLAGS) -I$(top_srcdir)/src/common
AM_CPPFLAGS = -I$(top_srcdir)/src/common $(COMMON_CFLAGS) $(DBUS_CFLAGS)

noinst_PROGRAMS = example
example_SOURCES = example.c
Expand Down
130 changes: 86 additions & 44 deletions src/common/dbus/dbus-client.c
Expand Up @@ -21,19 +21,22 @@

#include "config.h"

#define GLIB_DISABLE_DEPRECATION_WARNINGS
#include <dbus/dbus-glib.h>
#include "dbus-client.h"
#include <stdlib.h>
#include <gio/gio.h>
#include "hexchat.h"
#include "hexchatc.h"

#define DBUS_SERVICE "org.hexchat.service"
#define DBUS_REMOTE "/org/hexchat/Remote"
#define DBUS_REMOTE_PATH "/org/hexchat/Remote"
#define DBUS_REMOTE_INTERFACE "org.hexchat.plugin"

#define DBUS_SERVICE_DBUS "org.freedesktop.DBus"
#define DBUS_PATH_DBUS "/org/freedesktop/DBus"
#define DBUS_INTERFACE_DBUS "org.freedesktop.DBus"

static void
write_error (char *message,
GError **error)
write_error (char *message, GError **error)
{
if (error == NULL || *error == NULL) {
return;
Expand All @@ -42,97 +45,136 @@ write_error (char *message,
g_clear_error (error);
}

static inline GVariant *
new_param_variant (const char *arg)
{
GVariant * const args[1] = {
g_variant_new_string (arg)
};
return g_variant_new_tuple (args, 1);
}

void
hexchat_remote (void)
/* TODO: dbus_g_connection_unref (connection) are commented because it makes
* dbus to crash. Fixed in dbus >=0.70 ?!?
* https://launchpad.net/distros/ubuntu/+source/dbus/+bug/54375
*/
{
DBusGConnection *connection;
DBusGProxy *dbus = NULL;
DBusGProxy *remote_object = NULL;
GDBusConnection *connection;
GDBusProxy *dbus = NULL;
GVariant *ret;
GDBusProxy *remote_object = NULL;
gboolean hexchat_running;
GError *error = NULL;
char *command = NULL;
int i;

/* GnomeVFS >=2.15 uses D-Bus and threads, so threads should be
* initialised before opening for the first time a D-Bus connection */
if (!g_thread_supported ()) {
g_thread_init (NULL);
}
dbus_g_thread_init ();

/* if there is nothing to do, return now. */
if (!arg_existing || !(arg_url || arg_urls || arg_command)) {
return;
}

arg_dont_autoconnect = TRUE;

connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
if (!connection) {
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
if (!connection)
{
write_error (_("Couldn't connect to session bus"), &error);
return;
}

/* Checks if HexChat is already running */
dbus = dbus_g_proxy_new_for_name (connection,
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS);
if (!dbus_g_proxy_call (dbus, "NameHasOwner", &error,
G_TYPE_STRING, DBUS_SERVICE,
G_TYPE_INVALID,
G_TYPE_BOOLEAN, &hexchat_running,
G_TYPE_INVALID)) {
dbus = g_dbus_proxy_new_sync (connection,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
NULL,
&error);

ret = g_dbus_proxy_call_sync (dbus, "NameHasOwner",
new_param_variant (DBUS_SERVICE),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if (!ret)
{
write_error (_("Failed to complete NameHasOwner"), &error);
hexchat_running = FALSE;
}
else
{
GVariant *child = g_variant_get_child_value (ret, 0);
hexchat_running = g_variant_get_boolean (child);
g_variant_unref (ret);
g_variant_unref (child);
}
g_object_unref (dbus);

if (!hexchat_running) {
/* dbus_g_connection_unref (connection); */
g_object_unref (connection);
return;
}

remote_object = dbus_g_proxy_new_for_name (connection,
DBUS_SERVICE,
DBUS_REMOTE,
DBUS_REMOTE_INTERFACE);
remote_object = g_dbus_proxy_new_sync (connection,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
DBUS_SERVICE,
DBUS_REMOTE_PATH,
DBUS_REMOTE_INTERFACE,
NULL,
&error);

if (!remote_object)
{
write_error("Failed to connect to HexChat", &error);
g_object_unref (connection);
exit (0);
}

if (arg_url) {
command = g_strdup_printf ("url %s", arg_url);
} else if (arg_command) {
command = g_strdup (arg_command);
}

if (command) {
if (!dbus_g_proxy_call (remote_object, "Command",
&error,
G_TYPE_STRING, command,
G_TYPE_INVALID,G_TYPE_INVALID)) {
if (command)
{
g_dbus_proxy_call_sync (remote_object, "Command",
new_param_variant (command),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);

if (error)
write_error (_("Failed to complete Command"), &error);
}
g_free (command);
}

if (arg_urls)
{
for (i = 0; i < g_strv_length(arg_urls); i++)
{
command = g_strdup_printf ("url %s", arg_urls[i]);
if (!dbus_g_proxy_call (remote_object, "Command",
&error,
G_TYPE_STRING, command,
G_TYPE_INVALID, G_TYPE_INVALID)) {

g_dbus_proxy_call_sync (remote_object, "Command",
new_param_variant (command),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if (error)
write_error (_("Failed to complete Command"), &error);
}
g_free (command);
}
g_strfreev (arg_urls);
}
}

g_object_unref (remote_object);
g_object_unref (connection);
exit (0);
}
51 changes: 0 additions & 51 deletions src/common/dbus/example-gdbus.py

This file was deleted.

50 changes: 27 additions & 23 deletions src/common/dbus/example.py
@@ -1,4 +1,4 @@
#! /usr/bin/python
#!/usr/bin/python

# HexChat
# Copyright (C) 1998-2010 Peter Zelezny.
Expand All @@ -19,29 +19,33 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#

import dbus
from gi.repository import Gio

bus = dbus.SessionBus()
proxy = bus.get_object('org.hexchat.service', '/org/hexchat/Remote')
remote = dbus.Interface(proxy, 'org.hexchat.connection')
path = remote.Connect ("example.py",
"Python example",
"Example of a D-Bus client written in python",
"1.0")
proxy = bus.get_object('org.hexchat.service', path)
hexchat = dbus.Interface(proxy, 'org.hexchat.plugin')
bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
connection = Gio.DBusProxy.new_sync(bus, Gio.DBusProxyFlags.NONE, None,
'org.hexchat.service', '/org/hexchat/Remote', 'org.hexchat.connection', None)
path = connection.Connect('(ssss)',
'example.py',
'Python example',
'Example of a D-Bus client written in python',
'1.0')
hexchat = Gio.DBusProxy.new_sync(bus, Gio.DBusProxyFlags.NONE, None,
'org.hexchat.service', path, 'org.hexchat.plugin', None)

# Note the type before every arguement, this must be done.
# Type requirements are listed in our docs and characters are listed in the dbus docs.
# s = string, u = uint, i = int, etc.

channels = hexchat.ListGet ("channels")
while hexchat.ListNext (channels):
name = hexchat.ListStr (channels, "channel")
channels = hexchat.ListGet ('(s)', "channels")
while hexchat.ListNext ('(u)', channels):
name = hexchat.ListStr ('(us)', channels, "channel")
print("------- " + name + " -------")
hexchat.SetContext (hexchat.ListInt (channels, "context"))
hexchat.EmitPrint ("Channel Message", ["John", "Hi there", "@"])
users = hexchat.ListGet ("users")
while hexchat.ListNext (users):
print("Nick: " + hexchat.ListStr (users, "nick"))
hexchat.ListFree (users)
hexchat.ListFree (channels)

print(hexchat.Strip ("\00312Blue\003 \002Bold!\002", -1, 1|2))
hexchat.SetContext ('(u)', hexchat.ListInt ('(us)', channels, "context"))
hexchat.EmitPrint ('(sas)', "Channel Message", ["John", "Hi there", "@"])
users = hexchat.ListGet ('(s)', "users")
while hexchat.ListNext ('(u)', users):
print("Nick: " + hexchat.ListStr ('(us)', users, "nick"))
hexchat.ListFree ('(u)', users)
hexchat.ListFree ('(u)', channels)

print(hexchat.Strip ('(sii)', "\00312Blue\003 \002Bold!\002", -1, 1|2))
54 changes: 45 additions & 9 deletions src/common/dcc.c
Expand Up @@ -63,6 +63,9 @@
#define lseek _lseeki64
#endif

/* interval timer to detect timeouts */
static int timeout_timer = 0;

static char *dcctypes[] = { "SEND", "RECV", "CHAT", "CHAT" };

struct dccstat_info dccstat[] = {
Expand All @@ -78,10 +81,11 @@ static int dcc_global_throttle; /* 0x1 = sends, 0x2 = gets */
static gint64 dcc_sendcpssum, dcc_getcpssum;

static struct DCC *new_dcc (void);
static void dcc_close (struct DCC *dcc, int dccstat, int destroy);
static void dcc_close (struct DCC *dcc, enum dcc_state dccstat, int destroy);
static gboolean dcc_send_data (GIOChannel *, GIOCondition, struct DCC *);
static gboolean dcc_read (GIOChannel *, GIOCondition, struct DCC *);
static gboolean dcc_read_ack (GIOChannel *source, GIOCondition condition, struct DCC *dcc);
static int dcc_check_timeouts (void);

static int new_id(void)
{
Expand Down Expand Up @@ -233,9 +237,9 @@ is_dcc_completed (struct DCC *dcc)
return FALSE;
}

/* this is called from hexchat.c:hexchat_misc_checks() every 1 second. */
/* this is called by timeout_timer every 1 second. */

void
int
dcc_check_timeouts (void)
{
struct DCC *dcc;
Expand Down Expand Up @@ -289,9 +293,12 @@ dcc_check_timeouts (void)
if (prefs.hex_dcc_remove)
dcc_close (dcc, 0, TRUE);
break;
default:
break;
}
list = next;
}
return 1;
}

static int
Expand Down Expand Up @@ -362,7 +369,7 @@ dcc_connect_sok (struct DCC *dcc)
}

static void
dcc_close (struct DCC *dcc, int dccstat, int destroy)
dcc_close (struct DCC *dcc, enum dcc_state dccstat, int destroy)
{
if (dcc->wiotag)
{
Expand Down Expand Up @@ -419,6 +426,11 @@ dcc_close (struct DCC *dcc, int dccstat, int destroy)
g_free (dcc->destfile);
g_free (dcc->nick);
g_free (dcc);
if (dcc_list == NULL && timeout_timer != 0)
{
fe_timeout_remove (timeout_timer);
timeout_timer = 0;
}
return;
}

Expand Down Expand Up @@ -1596,6 +1608,8 @@ dcc_accept (GIOChannel *source, GIOCondition condition, struct DCC *dcc)
EMIT_SIGNAL (XP_TE_DCCCONCHAT, dcc->serv->front_session,
dcc->nick, host, NULL, NULL, 0);
break;
default:
break;
}

fe_dcc_update (dcc);
Expand Down Expand Up @@ -2133,7 +2147,8 @@ update_is_resumable (struct DCC *dcc)
{
d = list->data;
if (d->type == TYPE_RECV && d->dccstat != STAT_ABORTED &&
d->dccstat != STAT_DONE && d->dccstat != STAT_FAILED)
d->dccstat != STAT_DONE && d->dccstat != STAT_FAILED &&
d->dccstat != STAT_QUEUED)
{
if (d != dcc && is_same_file (d, dcc))
{
Expand Down Expand Up @@ -2173,6 +2188,8 @@ dcc_get (struct DCC *dcc)
case STAT_ABORTED:
dcc_close (dcc, 0, TRUE);
break;
default:
break;
}
}

Expand Down Expand Up @@ -2202,10 +2219,17 @@ dcc_get_nick (struct session *sess, char *nick)
{
if (dcc->dccstat == STAT_QUEUED && dcc->type == TYPE_RECV)
{
dcc->resumable = 0;
dcc->pos = 0;
dcc->ack = 0;
dcc_connect (dcc);
update_is_resumable (dcc);
if (prefs.hex_dcc_auto_resume && dcc->resumable)
{
dcc_resume (dcc);
}
else
{
dcc->pos = 0;
dcc->ack = 0;
dcc_connect (dcc);
}
return;
}
}
Expand All @@ -2222,6 +2246,10 @@ new_dcc (void)
dcc->sok = -1;
dcc->fp = -1;
dcc_list = g_slist_prepend (dcc_list, dcc);
if (timeout_timer == 0)
{
timeout_timer = fe_timeout_add_seconds (1, dcc_check_timeouts, NULL);
}
return dcc;
}

Expand All @@ -2244,6 +2272,9 @@ dcc_chat (struct session *sess, char *nick, int passive)
case STAT_ABORTED:
case STAT_FAILED:
dcc_close (dcc, 0, TRUE);
break;
case STAT_DONE:
break;
}
}
dcc = find_dcc (nick, "", TYPE_CHATRECV);
Expand All @@ -2257,6 +2288,9 @@ dcc_chat (struct session *sess, char *nick, int passive)
case STAT_FAILED:
case STAT_ABORTED:
dcc_close (dcc, 0, TRUE);
break;
default:
break;
}
return;
}
Expand Down Expand Up @@ -2308,6 +2342,8 @@ dcc_resume (struct DCC *dcc)
{
char tbuf[500];

update_is_resumable (dcc);

if (dcc->dccstat == STAT_QUEUED && dcc->resumable)
{
dcc->resume_sent = 1;
Expand Down
31 changes: 17 additions & 14 deletions src/common/dcc.h
Expand Up @@ -25,17 +25,21 @@
#ifndef HEXCHAT_DCC_H
#define HEXCHAT_DCC_H

#define STAT_QUEUED 0
#define STAT_ACTIVE 1
#define STAT_FAILED 2
#define STAT_DONE 3
#define STAT_CONNECTING 4
#define STAT_ABORTED 5

#define TYPE_SEND 0
#define TYPE_RECV 1
#define TYPE_CHATRECV 2
#define TYPE_CHATSEND 3
enum dcc_state {
STAT_QUEUED = 0,
STAT_ACTIVE,
STAT_FAILED,
STAT_DONE,
STAT_CONNECTING,
STAT_ABORTED
};

enum dcc_type {
TYPE_SEND = 0,
TYPE_RECV,
TYPE_CHATRECV,
TYPE_CHATSEND
};

#define CPS_AVG_WINDOW 10

Expand Down Expand Up @@ -72,8 +76,8 @@ struct DCC
char *file; /* utf8 */
char *destfile; /* utf8 */
char *nick;
unsigned char type; /* 0 = SEND 1 = RECV 2 = CHAT */
unsigned char dccstat; /* 0 = QUEUED 1 = ACTIVE 2 = FAILED 3 = DONE */
enum dcc_type type;
enum dcc_state dccstat;
unsigned int resume_sent:1; /* resume request sent */
unsigned int fastsend:1;
unsigned int ackoffset:1; /* is receiver sending acks as an offset from */
Expand Down Expand Up @@ -110,7 +114,6 @@ gboolean is_dcc_completed (struct DCC *dcc);
void dcc_abort (session *sess, struct DCC *dcc);
void dcc_get (struct DCC *dcc);
int dcc_resume (struct DCC *dcc);
void dcc_check_timeouts (void);
void dcc_change_nick (server *serv, char *oldnick, char *newnick);
void dcc_notify_kill (struct server *serv);
struct DCC *dcc_write_chat (char *nick, char *text);
Expand Down
2 changes: 1 addition & 1 deletion src/common/fe.h
Expand Up @@ -50,6 +50,7 @@ void fe_main (void);
void fe_cleanup (void);
void fe_exit (void);
int fe_timeout_add (int interval, void *callback, void *userdata);
int fe_timeout_add_seconds (int interval, void *callback, void *userdata);
void fe_timeout_remove (int tag);
void fe_new_window (struct session *sess, int focus);
void fe_new_server (struct server *serv);
Expand All @@ -68,7 +69,6 @@ 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_hilight (struct session *sess);
void fe_set_tab_color (struct session *sess, int col);
void fe_flash_window (struct session *sess);
void fe_update_mode_buttons (struct session *sess, char mode, char sign);
Expand Down
77 changes: 60 additions & 17 deletions src/common/hexchat.c
Expand Up @@ -354,26 +354,74 @@ away_check (void)
return 1;
}

/* these are only run if the lagometer is enabled */
static int
hexchat_misc_checks (void) /* this gets called every 1/2 second */
hexchat_lag_check (void) /* this gets called every 30 seconds */
{
static int count = 0;
lag_check ();
return 1;
}

count++;
static int
hexchat_lag_check_update (void) /* this gets called every 0.5 seconds */
{
lagcheck_update ();
return 1;
}

lagcheck_update (); /* every 500ms */
/* call whenever timeout intervals change */
void
hexchat_reinit_timers (void)
{
static int lag_check_update_tag = 0;
static int lag_check_tag = 0;
static int away_tag = 0;

if (count % 2)
dcc_check_timeouts (); /* every 1 second */
/* notify timeout */
if (prefs.hex_notify_timeout && notify_tag == 0)
{
notify_tag = fe_timeout_add_seconds (prefs.hex_notify_timeout,
notify_checklist, NULL);
}
else if (notify_tag != 0)
{
fe_timeout_remove (notify_tag);
notify_tag = 0;
}

if (count >= 60) /* every 30 seconds */
/* away status tracking */
if (prefs.hex_away_track && away_tag == 0)
{
if (prefs.hex_gui_lagometer)
lag_check ();
count = 0;
away_tag = fe_timeout_add_seconds (prefs.hex_away_timeout, away_check, NULL);
}
else if (away_tag != 0)
{
fe_timeout_remove (away_tag);
away_tag = 0;
}

return 1;
/* lag-o-meter */
if (prefs.hex_gui_lagometer && lag_check_update_tag == 0)
{
lag_check_update_tag = fe_timeout_add (500, hexchat_lag_check_update, NULL);
}
else if (lag_check_update_tag != 0)
{
fe_timeout_remove (lag_check_update_tag);
lag_check_update_tag = 0;
}

/* network timeouts and lag-o-meter */
if ((prefs.hex_net_ping_timeout != 0 || prefs.hex_gui_lagometer)
&& lag_check_tag == 0)
{
lag_check_tag = fe_timeout_add_seconds (30, hexchat_lag_check, NULL);
}
else if (lag_check_tag != 0)
{
fe_timeout_remove (lag_check_tag);
lag_check_tag = 0;
}
}

/* executed when the first irc window opens */
Expand Down Expand Up @@ -401,12 +449,7 @@ irc_init (session *sess)
plugin_add (sess, NULL, NULL, dbus_plugin_init, NULL, NULL, FALSE);
#endif

if (prefs.hex_notify_timeout)
notify_tag = fe_timeout_add (prefs.hex_notify_timeout * 1000,
notify_checklist, 0);

fe_timeout_add (prefs.hex_away_timeout * 1000, away_check, 0);
fe_timeout_add (500, hexchat_misc_checks, 0);
hexchat_reinit_timers ();

if (arg_url != NULL)
{
Expand Down
1 change: 0 additions & 1 deletion src/common/hexchat.h
Expand Up @@ -146,7 +146,6 @@ struct hexchatprefs
unsigned int hex_gui_ulist_count;
unsigned int hex_gui_ulist_hide;
unsigned int hex_gui_ulist_icons;
unsigned int hex_gui_ulist_resizable;
unsigned int hex_gui_ulist_show_hosts;
unsigned int hex_gui_ulist_style;
unsigned int hex_gui_usermenu;
Expand Down
1 change: 1 addition & 0 deletions src/common/hexchatc.h
Expand Up @@ -50,6 +50,7 @@ extern GList *sess_list_by_lastact[];
session * find_channel (server *serv, char *chan);
session * find_dialog (server *serv, char *nick);
session * new_ircwindow (server *serv, char *name, int type, int focus);
void hexchat_reinit_timers (void);
void lastact_update (session * sess);
session * lastact_getfirst (int (*filter) (session *sess));
int is_session (session * sess);
Expand Down
2 changes: 1 addition & 1 deletion src/common/ignore.c
Expand Up @@ -410,7 +410,7 @@ flood_check (char *nick, char *ip, server *serv, session *sess, int what) /*0=ct
{
prefs.hex_gui_autoopen_dialog = 0;
/* turn it back on in 30 secs */
fe_timeout_add (30000, flood_autodialog_timeout, NULL);
fe_timeout_add_seconds (30, flood_autodialog_timeout, NULL);
}
return 0;
}
Expand Down
5 changes: 3 additions & 2 deletions src/common/inbound.c
Expand Up @@ -319,7 +319,6 @@ is_hilight (char *from, char *text, session *sess, server *serv)
sess->nick_said = TRUE;
lastact_update (sess);
}
fe_set_hilight (sess);
return 1;
}

Expand Down Expand Up @@ -1123,6 +1122,7 @@ inbound_nameslist_end (server *serv, char *chan,
{
sess->end_of_names = TRUE;
sess->ignore_names = FALSE;
fe_userlist_numbers (sess);
}
list = list->next;
}
Expand All @@ -1133,6 +1133,7 @@ inbound_nameslist_end (server *serv, char *chan,
{
sess->end_of_names = TRUE;
sess->ignore_names = FALSE;
fe_userlist_numbers (sess);
return TRUE;
}
return FALSE;
Expand Down Expand Up @@ -1594,7 +1595,7 @@ inbound_login_end (session *sess, char *text, const message_tags_data *tags_data
&& ((((ircnet *)serv->network)->pass && inbound_nickserv_login (serv))
|| ((ircnet *)serv->network)->commandlist))
{
serv->joindelay_tag = fe_timeout_add (prefs.hex_irc_join_delay * 1000, check_autojoin_channels, serv);
serv->joindelay_tag = fe_timeout_add_seconds (prefs.hex_irc_join_delay, check_autojoin_channels, serv);
}
else
{
Expand Down
15 changes: 10 additions & 5 deletions src/common/outbound.c
Expand Up @@ -162,8 +162,8 @@ process_data_init (char *buf, char *cmd, char *word[],

word[0] = "\000\000";
word_eol[0] = "\000\000";
word[1] = (char *)buf;
word_eol[1] = (char *)cmd;
word[1] = buf;
word_eol[1] = cmd;

while (1)
{
Expand Down Expand Up @@ -2805,9 +2805,14 @@ cmd_msg (struct session *sess, char *tbuf, char *word[], char *word_eol[])
else
{
/* mask out passwords */
if (g_ascii_strcasecmp (nick, "nickserv") == 0 &&
g_ascii_strncasecmp (msg, "identify ", 9) == 0)
msg = "identify ****";
if (g_ascii_strcasecmp (nick, "nickserv") == 0)
{
if (g_ascii_strncasecmp (msg, "identify ", 9) == 0)
msg = "identify ****";
else if (g_ascii_strncasecmp (msg, "ghost ", 6) == 0)
msg = "ghost ****";
}

EMIT_SIGNAL (XP_TE_MSGSEND, sess, nick, msg, NULL, NULL, 0);
}

Expand Down
18 changes: 13 additions & 5 deletions src/common/plugin.c
Expand Up @@ -426,22 +426,22 @@ plugin_auto_load_cb (char *filename)
}
}

static char *
static const char *
plugin_get_libdir (void)
{
const char *libdir;

libdir = g_getenv ("HEXCHAT_LIBDIR");
if (libdir && *libdir)
return (char*)libdir;
return libdir;
else
return HEXCHATLIBDIR;
}

void
plugin_auto_load (session *sess)
{
char *lib_dir;
const char *lib_dir;
char *sub_dir;
ps = sess;

Expand Down Expand Up @@ -1340,7 +1340,7 @@ hexchat_list_fields (hexchat_plugin *ph, const char *name)
};
static const char * const channels_fields[] =
{
"schannel", "schannelkey", "schantypes", "pcontext", "iflags", "iid", "ilag", "imaxmodes",
"schannel", "schannelkey", "schanmodes", "schantypes", "pcontext", "iflags", "iid", "ilag", "imaxmodes",
"snetwork", "snickmodes", "snickprefixes", "iqueue", "sserver", "itype", "iusers",
NULL
};
Expand Down Expand Up @@ -1437,6 +1437,8 @@ hexchat_list_str (hexchat_plugin *ph, hexchat_list *xlist, const char *name)
return ((session *)data)->channel;
case 0x8cea5e7c: /* channelkey */
return ((session *)data)->channelkey;
case 0x5716ab1e: /* chanmodes */
return ((session*)data)->server->chanmodes;
case 0x577e0867: /* chantypes */
return ((session *)data)->server->chantypes;
case 0x38b735af: /* context */
Expand Down Expand Up @@ -1958,7 +1960,13 @@ hexchat_pluginpref_get_int (hexchat_plugin *pl, const char *var)

if (hexchat_pluginpref_get_str_real (pl, var, buffer, sizeof(buffer)))
{
return atoi (buffer);
int ret = atoi (buffer);

/* 0 could be success or failure, who knows */
if (ret == 0 && *buffer != '0')
return -1;

return ret;
}
else
{
Expand Down
20 changes: 12 additions & 8 deletions src/common/server.c
Expand Up @@ -91,7 +91,7 @@ tcp_send_real (void *ssl, int sok, GIConv write_converter, char *buf, int len)
int ret;

gsize buf_encoded_len;
gchar *buf_encoded = text_convert_invalid (buf, len, write_converter, "?", &buf_encoded_len);
gchar *buf_encoded = text_convert_invalid (buf, len, write_converter, arbitrary_encoding_fallback_string, &buf_encoded_len);
#ifdef USE_OPENSSL
if (!ssl)
ret = send (sok, buf_encoded, buf_encoded_len, 0);
Expand Down Expand Up @@ -249,7 +249,7 @@ static void
close_socket (int sok)
{
/* close the socket in 5 seconds so the QUIT message is not lost */
fe_timeout_add (5000, close_socket_cb, GINT_TO_POINTER (sok));
fe_timeout_add_seconds (5, close_socket_cb, GINT_TO_POINTER (sok));
}

/* handle 1 line of text received from the server */
Expand Down Expand Up @@ -466,19 +466,22 @@ ssl_cb_verify (int ok, X509_STORE_CTX * ctx)
char subject[256];
char issuer[256];
char buf[512];
X509 *current_cert = X509_STORE_CTX_get_current_cert (ctx);

if (!current_cert)
return TRUE;

X509_NAME_oneline (X509_get_subject_name (ctx->current_cert), subject,
sizeof (subject));
X509_NAME_oneline (X509_get_issuer_name (ctx->current_cert), issuer,
sizeof (issuer));
X509_NAME_oneline (X509_get_subject_name (current_cert),
subject, sizeof (subject));
X509_NAME_oneline (X509_get_issuer_name (current_cert),
issuer, sizeof (issuer));

g_snprintf (buf, sizeof (buf), "* Subject: %s", subject);
EMIT_SIGNAL (XP_TE_SSLMESSAGE, g_sess, buf, NULL, NULL, NULL, 0);
g_snprintf (buf, sizeof (buf), "* Issuer: %s", issuer);
EMIT_SIGNAL (XP_TE_SSLMESSAGE, g_sess, buf, NULL, NULL, NULL, 0);

return (TRUE); /* always ok */
return TRUE;
}

static int
Expand Down Expand Up @@ -638,7 +641,8 @@ ssl_do_connect (server * serv)
return (0); /* remove it (0) */
} else
{
if (serv->ssl->session && serv->ssl->session->time + SSLTMOUT < time (NULL))
SSL_SESSION *session = SSL_get_session (serv->ssl);
if (session && SSL_SESSION_get_time (session) + SSLTMOUT < time (NULL))
{
g_snprintf (buf, sizeof (buf), "SSL handshake timed out");
EMIT_SIGNAL (XP_TE_CONNFAIL, serv->server_session, buf, NULL,
Expand Down
3 changes: 2 additions & 1 deletion src/common/servlist.c
Expand Up @@ -272,7 +272,8 @@ static const struct defaultserver def[] =
{"PonyChat", 0, 0, 0, LOGIN_SASL, 0, TRUE},
{0, "irc.ponychat.net"},

{"PTNet.org", 0, 0, "ISO-8859-1"},
{"PTNet.org", 0},
/* Note that the network suggests ISO-8859-1 but most users ignore this */
{0, "uevora.ptnet.org"},
{0, "vianetworks.ptnet.org"},

Expand Down
16 changes: 15 additions & 1 deletion src/common/text.c
Expand Up @@ -79,6 +79,7 @@ scrollback_get_filename (session *sess)
if (!net)
return NULL;

net = log_create_filename (net);
buf = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "scrollback" G_DIR_SEPARATOR_S "%s" G_DIR_SEPARATOR_S "%s.txt", get_xdir (), net, "");
mkdir_p (buf);
g_free (buf);
Expand All @@ -89,6 +90,7 @@ scrollback_get_filename (session *sess)
else
buf = NULL;
g_free (chan);
g_free (net);

if (buf)
{
Expand Down Expand Up @@ -742,6 +744,8 @@ text_convert_invalid (const gchar* text, gssize len, GIConv converter, const gch

/* Find the first position of an invalid sequence. */
result_part = g_convert_with_iconv (text, len, converter, &invalid_start_pos, &result_part_len, NULL);
g_iconv (converter, NULL, NULL, NULL, NULL);

if (result_part != NULL)
{
/* All text converted successfully on the first try. Return it. */
Expand All @@ -763,8 +767,15 @@ text_convert_invalid (const gchar* text, gssize len, GIConv converter, const gch
{
g_assert (current_start + invalid_start_pos < end);

/* Convert everything before the position of the invalid sequence. It should be successful. */
/* Convert everything before the position of the invalid sequence. It should be successful.
* But iconv may not convert everything till invalid_start_pos since the last few bytes may be part of a shift sequence.
* So get the new bytes_read and use it as the actual invalid_start_pos to handle this.
*
* See https://github.com/hexchat/hexchat/issues/1758
*/
result_part = g_convert_with_iconv (current_start, invalid_start_pos, converter, &invalid_start_pos, &result_part_len, NULL);
g_iconv (converter, NULL, NULL, NULL, NULL);

g_assert (result_part != NULL);
g_string_append_len (result, result_part, result_part_len);
g_free (result_part);
Expand All @@ -776,6 +787,8 @@ text_convert_invalid (const gchar* text, gssize len, GIConv converter, const gch
current_start += invalid_start_pos + 1;

result_part = g_convert_with_iconv (current_start, end - current_start, converter, &invalid_start_pos, &result_part_len, NULL);
g_iconv (converter, NULL, NULL, NULL, NULL);

if (result_part != NULL)
{
/* The rest of the text converted successfully. Append it and return the whole converted text. */
Expand Down Expand Up @@ -2037,6 +2050,7 @@ text_emit (int index, session *sess, char *a, char *b, char *c, char *d,
/* ===Highlighted message=== */
case XP_TE_HCHANACTION:
case XP_TE_HCHANMSG:
fe_set_tab_color (sess, 3);
if (chanopt_is_set (prefs.hex_input_beep_hilight, sess->alert_beep) && (!prefs.hex_away_omit_alerts || !sess->server->is_away))
sound_beep (sess);
if (chanopt_is_set (prefs.hex_input_flash_hilight, sess->alert_taskbar) && (!prefs.hex_away_omit_alerts || !sess->server->is_away))
Expand Down
3 changes: 2 additions & 1 deletion src/common/userlist.c
Expand Up @@ -416,7 +416,8 @@ userlist_add (struct session *sess, char *name, char *hostname,
sess->me = user;

fe_userlist_insert (sess, user, FALSE);
fe_userlist_numbers (sess);
if(sess->end_of_names)
fe_userlist_numbers (sess);
}

static int
Expand Down
2 changes: 1 addition & 1 deletion src/common/util.c
Expand Up @@ -634,7 +634,7 @@ int match(const char *mask, const char *string)
}

void
for_files (char *dirname, char *mask, void callback (char *file))
for_files (const char *dirname, const char *mask, void callback (char *file))
{
GDir *dir;
const gchar *entry_name;
Expand Down
2 changes: 1 addition & 1 deletion src/common/util.h
Expand Up @@ -41,7 +41,7 @@ char *expand_homedir (char *file);
void path_part (char *file, char *path, int pathlen);
int match (const char *mask, const char *string);
char *file_part (char *file);
void for_files (char *dirname, char *mask, void callback (char *file));
void for_files (const char *dirname, const char *mask, void callback (char *file));
int rfc_casecmp (const char *, const char *);
int rfc_ncasecmp (char *, char *, int);
int buf_get_line (char *, char **, int *, int len);
Expand Down
2 changes: 1 addition & 1 deletion src/fe-gtk/Makefile.am
Expand Up @@ -45,7 +45,7 @@ hexchat_SOURCES = ascii.c banlist.c chanlist.c chanview.c custom-list.c \
maingui.c notifygui.c $(notify_c) palette.c pixmaps.c plugin-tray.c $(plugingui_c) \
plugin-notification.c rawlog.c servlistgui.c setup.c $(iso_codes_c) \
sexy-spell-entry.c textgui.c urlgrab.c userlistgui.c xtext.c
hexchat_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_builddir)/src/common
hexchat_CPPFLAGS = -I$(top_builddir)/src/common $(AM_CPPFLAGS)

resources_files = $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(top_srcdir)/data --generate-dependencies $(top_srcdir)/data/hexchat.gresource.xml)

Expand Down
5 changes: 5 additions & 0 deletions src/fe-gtk/dccgui.c
Expand Up @@ -713,6 +713,11 @@ dcc_dclick_cb (GtkTreeView *view, GtkTreePath *path,
case STAT_ABORTED:
case STAT_DONE:
dcc_abort (dcc->serv->front_session, dcc);
break;
case STAT_QUEUED:
case STAT_ACTIVE:
case STAT_CONNECTING:
break;
}
}

Expand Down
42 changes: 32 additions & 10 deletions src/fe-gtk/fe-gtk.c
Expand Up @@ -254,6 +254,22 @@ const char cursor_color_rc[] =
"}"
"widget \"*.hexchat-inputbox\" style : application \"xc-ib-st\"";

static const char adwaita_workaround_rc[] =
"style \"hexchat-input-workaround\""
"{"
"engine \"pixmap\" {"
"image {"
"function = FLAT_BOX\n"
"state = NORMAL\n"
"}"
"image {"
"function = FLAT_BOX\n"
"state = ACTIVE\n"
"}"
"}"
"}"
"widget \"*.hexchat-inputbox\" style \"hexchat-input-workaround\"";

GtkStyle *
create_input_style (GtkStyle *style)
{
Expand All @@ -274,6 +290,16 @@ create_input_style (GtkStyle *style)

if (prefs.hex_gui_input_style && !done_rc)
{
GtkSettings *settings = gtk_settings_get_default ();
char *theme_name;

/* gnome-themes-standard 3.20 relies on images to do theming
* so we have to override that. */
g_object_get (settings, "gtk-theme-name", &theme_name, NULL);
if (!g_strcmp0 (theme_name, "Adwaita"))
gtk_rc_parse_string (adwaita_workaround_rc);
g_free (theme_name);

done_rc = TRUE;
sprintf (buf, cursor_color_rc, (colors[COL_FG].red >> 8),
(colors[COL_FG].green >> 8), (colors[COL_FG].blue >> 8));
Expand Down Expand Up @@ -345,6 +371,12 @@ fe_timeout_add (int interval, void *callback, void *userdata)
return g_timeout_add (interval, (GSourceFunc) callback, userdata);
}

int
fe_timeout_add_seconds (int interval, void *callback, void *userdata)
{
return g_timeout_add_seconds (interval, (GSourceFunc) callback, userdata);
}

void
fe_timeout_remove (int tag)
{
Expand Down Expand Up @@ -525,16 +557,6 @@ fe_set_topic (session *sess, char *topic, char *stripped_topic)
}
}

void
fe_set_hilight (struct session *sess)
{
if (sess->gui->is_tab)
fe_set_tab_color (sess, 3); /* set tab to blue */

if (prefs.hex_input_flash_hilight && (!prefs.hex_away_omit_alerts || !sess->server->is_away))
fe_flash_window (sess); /* taskbar flash */
}

static void
fe_update_mode_entry (session *sess, GtkWidget *entry, char **text, char *new_text)
{
Expand Down
11 changes: 6 additions & 5 deletions src/fe-gtk/fkeys.c
Expand Up @@ -182,11 +182,12 @@ static const struct key_action key_actions[KEY_MAX_ACTIONS + 1] = {
"ACCEL=<Alt>2\nChange Page\nD1:2\nD2!\n\n"\
"ACCEL=<Alt>1\nChange Page\nD1:1\nD2!\n\n"\
"ACCEL=<Alt>grave\nChange Page\nD1:auto\nD2!\n\n"\
"ACCEL=<Primary>o\nInsert in Buffer\nD1:\nD2!\n\n"\
"ACCEL=<Primary>b\nInsert in Buffer\nD1:\nD2!\n\n"\
"ACCEL=<Primary>k\nInsert in Buffer\nD1:\nD2!\n\n"\
"ACCEL=<Primary>i\nInsert in Buffer\nD1:\nD2!\n\n"\
"ACCEL=<Primary>u\nInsert in Buffer\nD1:\nD2!\n\n"\
"ACCEL=<Primary>o\nInsert in Buffer\nD1:\017\nD2!\n\n"\
"ACCEL=<Primary>b\nInsert in Buffer\nD1:\002\nD2!\n\n"\
"ACCEL=<Primary>k\nInsert in Buffer\nD1:\003\nD2!\n\n"\
"ACCEL=<Primary>i\nInsert in Buffer\nD1:\035\nD2!\n\n"\
"ACCEL=<Primary>u\nInsert in Buffer\nD1:\037\nD2!\n\n"\
"ACCEL=<Primary>r\nInsert in Buffer\nD1:\026\nD2!\n\n"\
"ACCEL=<Shift>Page_Down\nChange Selected Nick\nD1!\nD2!\n\n"\
"ACCEL=<Shift>Page_Up\nChange Selected Nick\nD1:Up\nD2!\n\n"\
"ACCEL=Page_Down\nScroll Page\nD1:Down\nD2!\n\n"\
Expand Down
41 changes: 10 additions & 31 deletions src/fe-gtk/maingui.c
Expand Up @@ -173,7 +173,7 @@ void
fe_set_tab_color (struct session *sess, int col)
{
struct session *server_sess = sess->server->server_session;
if (sess->gui->is_tab && (col == 0 || sess != current_tab))
if (sess->res->tab && sess->gui->is_tab && (col == 0 || sess != current_tab))
{
switch (col)
{
Expand Down Expand Up @@ -805,14 +805,6 @@ mg_decide_userlist (session *sess, gboolean switch_to_current)
}
}

static void
mg_userlist_toggle_cb (GtkWidget *button, gpointer userdata)
{
prefs.hex_gui_ulist_hide = !prefs.hex_gui_ulist_hide;
mg_decide_userlist (current_sess, FALSE);
gtk_widget_grab_focus (current_sess->gui->input_box);
}

static int ul_tag = 0;

static gboolean
Expand Down Expand Up @@ -1010,12 +1002,6 @@ static void
mg_topdestroy_cb (GtkWidget *win, session *sess)
{
/* printf("enter mg_topdestroy. sess %p was destroyed\n", sess);*/

/* kill the text buffer */
gtk_xtext_buffer_free (sess->res->buffer);
/* kill the user list */
g_object_unref (G_OBJECT (sess->res->user_model));

session_free (sess); /* tell hexchat.c about it */
}

Expand All @@ -1026,11 +1012,6 @@ mg_ircdestroy (session *sess)
{
GSList *list;

/* kill the text buffer */
gtk_xtext_buffer_free (sess->res->buffer);
/* kill the user list */
g_object_unref (G_OBJECT (sess->res->user_model));

session_free (sess); /* tell hexchat.c about it */

if (mg_gui == NULL)
Expand Down Expand Up @@ -1092,7 +1073,10 @@ mg_tab_close (session *sess)
int i;

if (chan_remove (sess->res->tab, FALSE))
{
sess->res->tab = NULL;
mg_ircdestroy (sess);
}
else
{
for (i = 0, list = sess_list; list; list = list->next)
Expand Down Expand Up @@ -1332,9 +1316,6 @@ mg_chan_remove (chan *ch)
static void
mg_close_gen (chan *ch, GtkWidget *box)
{
char *title = g_object_get_data (G_OBJECT (box), "title");

g_free (title);
if (!ch)
ch = g_object_get_data (G_OBJECT (box), "ch");
if (ch)
Expand Down Expand Up @@ -2200,10 +2181,6 @@ mg_create_topicbar (session *sess, GtkWidget *box)
gui->dialogbutton_box = bbox = gtk_hbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (hbox), bbox, 0, 0, 0);
mg_create_dialogbuttons (bbox);

if (!prefs.hex_gui_ulist_resizable)
gtkutil_button (hbox, GTK_STOCK_GOTO_LAST, _("Show/Hide userlist"),
mg_userlist_toggle_cb, 0, 0);
}

/* check if a word is clickable */
Expand Down Expand Up @@ -3339,8 +3316,8 @@ mg_add_generic_tab (char *name, char *title, void *family, GtkWidget *box)

ch = chanview_add (mg_gui->chanview, name, NULL, box, TRUE, TAG_UTIL, pix_tree_util);
chan_set_color (ch, plain_list);
/* FIXME: memory leak */
g_object_set_data (G_OBJECT (box), "title", g_strdup (title));

g_object_set_data_full (G_OBJECT (box), "title", g_strdup (title), g_free);
g_object_set_data (G_OBJECT (box), "ch", ch);

if (prefs.hex_gui_tab_newtofront)
Expand Down Expand Up @@ -3630,8 +3607,7 @@ mg_set_title (GtkWidget *vbox, char *title) /* for non-irc tab/window only */
old = g_object_get_data (G_OBJECT (vbox), "title");
if (old)
{
g_object_set_data (G_OBJECT (vbox), "title", g_strdup (title));
g_free (old);
g_object_set_data_full (G_OBJECT (vbox), "title", g_strdup (title), g_free);
} else
{
gtk_window_set_title (GTK_WINDOW (vbox), title);
Expand All @@ -3657,6 +3633,9 @@ fe_server_callback (server *serv)
void
fe_session_callback (session *sess)
{
gtk_xtext_buffer_free (sess->res->buffer);
g_object_unref (G_OBJECT (sess->res->user_model));

if (sess->res->banlist && sess->res->banlist->window)
mg_close_gen (NULL, sess->res->banlist->window);

Expand Down
4 changes: 4 additions & 0 deletions src/fe-gtk/menu.c
Expand Up @@ -1646,6 +1646,7 @@ menu_metres_off (GtkWidget *item, gpointer none)
{
prefs.hex_gui_lagometer = 0;
prefs.hex_gui_throttlemeter = 0;
hexchat_reinit_timers ();
menu_setting_foreach (menu_apply_metres_cb, -1, 0);
}
}
Expand All @@ -1657,6 +1658,7 @@ menu_metres_text (GtkWidget *item, gpointer none)
{
prefs.hex_gui_lagometer = 2;
prefs.hex_gui_throttlemeter = 2;
hexchat_reinit_timers ();
menu_setting_foreach (menu_apply_metres_cb, -1, 0);
}
}
Expand All @@ -1668,6 +1670,7 @@ menu_metres_graph (GtkWidget *item, gpointer none)
{
prefs.hex_gui_lagometer = 1;
prefs.hex_gui_throttlemeter = 1;
hexchat_reinit_timers ();
menu_setting_foreach (menu_apply_metres_cb, -1, 0);
}
}
Expand All @@ -1679,6 +1682,7 @@ menu_metres_both (GtkWidget *item, gpointer none)
{
prefs.hex_gui_lagometer = 3;
prefs.hex_gui_throttlemeter = 3;
hexchat_reinit_timers ();
menu_setting_foreach (menu_apply_metres_cb, -1, 0);
}
}
Expand Down
10 changes: 5 additions & 5 deletions src/fe-gtk/notifications/notification-winrt.cpp
Expand Up @@ -46,7 +46,7 @@ extern "C"
try
{
auto toastTemplate = ToastNotificationManager::GetTemplateContent (ToastTemplateType::ToastText02);
auto node_list = toastTemplate->GetElementsByTagName ("text");
auto node_list = toastTemplate->GetElementsByTagName (L"text");
UINT node_count = node_list->Length;

auto wtitle = widen (title);
Expand All @@ -58,9 +58,9 @@ extern "C"
toastTemplate->CreateTextNode (Platform::StringReference (wtext.c_str (), wtext.size ())));

// Mute sound, we already play our own
auto node = toastTemplate->SelectSingleNode ("/toast");
auto audio_elem = toastTemplate->CreateElement ("audio");
audio_elem->SetAttribute ("silent", "true");
auto node = toastTemplate->SelectSingleNode (L"/toast");
auto audio_elem = toastTemplate->CreateElement (L"audio");
audio_elem->SetAttribute (L"silent", L"true");
static_cast<XmlElement^>(node)->AppendChild (audio_elem);

notifier->Show (ref new ToastNotification (toastTemplate));
Expand All @@ -77,7 +77,7 @@ extern "C"
notification_backend_init (void)
{
if (!notifier)
notifier = ToastNotificationManager::CreateToastNotifier ("HexChat.Desktop.Notify");
notifier = ToastNotificationManager::CreateToastNotifier (L"HexChat.Desktop.Notify");

if (FAILED (Windows::Foundation::Initialize (RO_INIT_SINGLETHREADED)))
return 0;
Expand Down
14 changes: 13 additions & 1 deletion src/fe-gtk/servlistgui.c
Expand Up @@ -1570,7 +1570,15 @@ servlist_nick_changed_cb (GtkEntry *entry, gpointer userdata)
const gchar *nick1 = gtk_entry_get_text (GTK_ENTRY (entry_nick1));
const gchar *nick2 = gtk_entry_get_text (GTK_ENTRY (entry_nick2));

if (!rfc_casecmp (nick1, nick2))
if (!nick1[0] || !nick2[0])
{
entry = GTK_ENTRY(!nick1[0] ? entry_nick1 : entry_nick2);
gtk_entry_set_icon_from_stock (entry, GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_DIALOG_ERROR);
gtk_entry_set_icon_tooltip_text (entry, GTK_ENTRY_ICON_SECONDARY,
_("You cannot have an empty nick name."));
gtk_widget_set_sensitive (connect_btn, FALSE);
}
else if (!rfc_casecmp (nick1, nick2))
{
gtk_entry_set_icon_from_stock (entry, GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_DIALOG_ERROR);
gtk_entry_set_icon_tooltip_text (entry, GTK_ENTRY_ICON_SECONDARY,
Expand Down Expand Up @@ -2198,6 +2206,10 @@ servlist_open_networks (void)
g_signal_connect (G_OBJECT (entry_nick2), "changed",
G_CALLBACK(servlist_nick_changed_cb), button_connect);

/* Run validity checks now */
servlist_nick_changed_cb (GTK_ENTRY(entry_nick2), button_connect);
servlist_username_changed_cb (GTK_ENTRY(entry_guser), button_connect);

gtk_label_set_mnemonic_widget (GTK_LABEL (label3), entry1);
gtk_label_set_mnemonic_widget (GTK_LABEL (label6), entry4);
/* gtk_label_set_mnemonic_widget (GTK_LABEL (label7), entry5); */
Expand Down
6 changes: 3 additions & 3 deletions src/fe-gtk/setup.c
Expand Up @@ -257,7 +257,6 @@ static const setting userlist_settings[] =
{ST_TOGGLE, N_("Show icons for user modes"), P_OFFINTNL(hex_gui_ulist_icons), N_("Use graphical icons instead of text symbols in the user list."), 0, 0},
{ST_TOGGLE, N_("Color nicknames in userlist"), P_OFFINTNL(hex_gui_ulist_color), N_("Will color nicknames the same as in chat."), 0, 0},
{ST_TOGGLE, N_("Show user count in channels"), P_OFFINTNL(hex_gui_ulist_count), 0, 0, 0},
/* {ST_TOGGLE, N_("Resizable user list"), P_OFFINTNL(hex_gui_ulist_resizable),0,0,0},*/
{ST_MENU, N_("User list sorted by:"), P_OFFINTNL(hex_gui_ulist_sort), 0, ulmenutext, 0},
{ST_MENU, N_("Show user list at:"), P_OFFINTNL(hex_gui_ulist_pos), 0, ulpos, 1},

Expand Down Expand Up @@ -2132,6 +2131,7 @@ setup_apply_real (int new_pix, int do_ulist, int do_layout, int do_identd)

mg_apply_setup ();
tray_apply_setup ();
hexchat_reinit_timers ();

if (do_layout)
menu_change_layout ();
Expand Down Expand Up @@ -2187,14 +2187,14 @@ setup_apply (struct hexchatprefs *pr)
noapply = TRUE;
if (DIFF (hex_gui_ulist_icons))
noapply = TRUE;
if (DIFF (hex_gui_ulist_resizable))
noapply = TRUE;
if (DIFF (hex_gui_ulist_show_hosts))
noapply = TRUE;
if (DIFF (hex_gui_ulist_style))
noapply = TRUE;
if (DIFF (hex_gui_ulist_sort))
noapply = TRUE;
if (DIFF (hex_gui_input_style) && prefs.hex_gui_input_style == TRUE)
noapply = TRUE; /* Requires restart to *disable* */

if (DIFF (hex_gui_tab_dots))
do_layout = TRUE;
Expand Down
10 changes: 6 additions & 4 deletions src/fe-text/fe-text.c
Expand Up @@ -415,6 +415,12 @@ fe_timeout_add (int interval, void *callback, void *userdata)
return g_timeout_add (interval, (GSourceFunc) callback, userdata);
}

int
fe_timeout_add_seconds (int interval, void *callback, void *userdata)
{
return g_timeout_add_seconds (interval, (GSourceFunc) callback, userdata);
}

void
fe_input_remove (int tag)
{
Expand Down Expand Up @@ -612,10 +618,6 @@ fe_cleanup (void)
{
}
void
fe_set_hilight (struct session *sess)
{
}
void
fe_set_tab_color (struct session *sess, int col)
{
}
Expand Down
25 changes: 20 additions & 5 deletions win32/installer/hexchat.iss.tt
Expand Up @@ -100,6 +100,7 @@ Root: HKCR; Subkey: ".hct\shell\open\command"; ValueType: string; ValueName: "";
Filename: "{app}\hexchat.exe"; Description: "Run HexChat after closing the Wizard"; Flags: nowait postinstall skipifsilent
Filename: "http://docs.hexchat.org/en/latest/changelog.html"; Description: "See what's changed"; Flags: shellexec runasoriginaluser postinstall skipifsilent unchecked
Filename: "{tmp}\vcredist.exe"; Parameters: "/install /quiet /norestart"; StatusMsg: "Installing Visual C++ Redistributable"; Flags: skipifdoesntexist; Tasks: not portable
Filename: "{tmp}\vcredist2013.exe"; Parameters: "/install /quiet /norestart"; StatusMsg: "Installing Visual C++ Redistributable"; Flags: skipifdoesntexist; Tasks: not portable
Filename: "{tmp}\dotnet4.exe"; Parameters: "/q /norestart"; StatusMsg: "Installing .NET"; Components: xtm; Flags: skipifdoesntexist; Tasks: not portable
Filename: "{tmp}\perl.msi"; StatusMsg: "Installing Perl"; Components: langs\perl; Flags: shellexec skipifdoesntexist; Tasks: not portable
Filename: "{tmp}\python.msi"; StatusMsg: "Installing Python"; Components: langs\python; Flags: shellexec skipifdoesntexist; Tasks: not portable
Expand Down Expand Up @@ -240,6 +241,12 @@ begin
Result := FileExists(GetSysDir() + 'vcruntime140.dll');;
end;

function CheckVC2013Install(): Boolean;
begin
Result := FileExists(GetSysDir() + 'msvcr120.dll');;
end;


/////////////////////////////////////////////////////////////////////
function CheckSpellInstall(): Boolean;
var
Expand All @@ -266,6 +273,7 @@ end;
procedure CurPageChanged(CurPageID: Integer);
var
REDIST: String;
REDIST_2013: String;
PERL: String;
PY2: String;
PY3: String;
Expand All @@ -281,14 +289,16 @@ begin

#if APPARCH == "x64"
REDIST := 'https://dl.hexchat.net/misc/vcredist_2015_x64.exe';
REDIST_2013 := 'https://dl.hexchat.net/misc/vcredist_2013_x64.exe';
PERL := 'https://dl.hexchat.net/misc/perl/Perl%205.20.0%20x64.msi';
PY2 := 'https://python.org/ftp/python/2.7.10/python-2.7.10.amd64.msi';
PY3 := 'https://python.org/ftp/python/3.5.1/python-3.5.1-amd64.exe';
PY2 := 'https://www.python.org/ftp/python/2.7.12/python-2.7.12.amd64.msi';
PY3 := 'https://www.python.org/ftp/python/3.5.2/python-3.5.2-amd64.exe';
#else
REDIST := 'https://dl.hexchat.net/misc/vcredist_2015_x86.exe';
REDIST_2013 := 'https://dl.hexchat.net/misc/vcredist_2013_x86.exe';
PERL := 'https://dl.hexchat.net/misc/perl/Perl%205.20.0%20x86.msi';
PY2 := 'https://python.org/ftp/python/2.7.10/python-2.7.10.msi';
PY3 := 'https://python.org/ftp/python/3.5.1/python-3.5.1.exe';
PY2 := 'https://www.python.org/ftp/python/2.7.12/python-2.7.12.msi';
PY3 := 'https://www.python.org/ftp/python/3.5.2/python-3.5.2.exe';
#endif
DOTNET := 'https://dl.hexchat.net/misc/dotnet_40.exe';
SPELL := 'https://dl.hexchat.net/hexchat/HexChat%20Spelling%20Dictionaries%20r2.exe';
Expand All @@ -305,7 +315,12 @@ begin
if not WizardSilent() then
begin
if IsComponentSelected('langs\perl') and not CheckDLL('perl520.dll') then
idpAddFile(PERL, ExpandConstant('{tmp}\perl.msi'));
begin
if not CheckVC2013Install() then
idpAddFile(REDIST_2013, ExpandConstant('{tmp}\vcredist2013.exe'));

idpAddFile(PERL, ExpandConstant('{tmp}\perl.msi'))
end;

if IsComponentSelected('langs\python\python2') and not CheckDLL('python27.dll') then
idpAddFile(PY2, ExpandConstant('{tmp}\python.msi'));
Expand Down
2 changes: 1 addition & 1 deletion win32/version.txt
@@ -1 +1 @@
2.12.0
2.12.1-2