1 Commits

11 changed files with 184 additions and 608 deletions

View File

@@ -36,6 +36,7 @@ jobs:
libgtk-3-bin libglib2.0-bin shared-mime-info gsettings-desktop-schemas \
libluajit-5.1-dev libpci-dev libperl-dev libssl-dev libayatana-appindicator3-dev \
perl python3 python3-minimal python3-dev python3-cffi mono-devel desktop-file-utils \
fonts-noto-color-emoji breeze-gtk-theme \
patchelf file curl
- name: Configure
@@ -104,6 +105,26 @@ jobs:
cp -a /usr/lib/x86_64-linux-gnu/libpython3*.so* AppDir/usr/lib/x86_64-linux-gnu/
fi
if [ -d "/usr/share/gtk-3.0/emoji" ]; then
install -d AppDir/usr/share/gtk-3.0
cp -a /usr/share/gtk-3.0/emoji AppDir/usr/share/gtk-3.0/
fi
if [ -f "/usr/share/fonts/truetype/noto/NotoColorEmoji.ttf" ]; then
install -d AppDir/usr/share/fonts/truetype/noto
cp -a /usr/share/fonts/truetype/noto/NotoColorEmoji.ttf AppDir/usr/share/fonts/truetype/noto/
fi
if [ -d "/usr/lib/x86_64-linux-gnu/gtk-3.0/modules" ]; then
install -d AppDir/usr/lib/x86_64-linux-gnu/gtk-3.0
cp -a /usr/lib/x86_64-linux-gnu/gtk-3.0/modules AppDir/usr/lib/x86_64-linux-gnu/gtk-3.0/
fi
if [ -d "/usr/lib/gtk-3.0/modules" ]; then
install -d AppDir/usr/lib/gtk-3.0
cp -a /usr/lib/gtk-3.0/modules AppDir/usr/lib/gtk-3.0/
fi
- name: Verify bundled plugins
run: |
set -eux
@@ -144,6 +165,26 @@ jobs:
export PATH="${PATH:-/usr/bin:/bin}:$APPDIR/usr/bin"
export LD_LIBRARY_PATH="$APPDIR/usr/lib:$APPDIR/usr/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH:-}"
export XDG_DATA_DIRS="$APPDIR/usr/share:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
export GTK_EXE_PREFIX="$APPDIR/usr"
export GTK_DATA_PREFIX="$APPDIR/usr"
gtk_path_entries=""
if [ -d "$APPDIR/usr/lib/x86_64-linux-gnu/gtk-3.0" ]; then
gtk_path_entries="$APPDIR/usr/lib/x86_64-linux-gnu/gtk-3.0"
fi
if [ -d "$APPDIR/usr/lib/gtk-3.0" ]; then
gtk_path_entries="${gtk_path_entries:+$gtk_path_entries:}$APPDIR/usr/lib/gtk-3.0"
fi
if [ -n "$gtk_path_entries" ]; then
export GTK_PATH="$gtk_path_entries${GTK_PATH:+:$GTK_PATH}"
fi
if [ -d "$APPDIR/etc/fonts" ]; then
export FONTCONFIG_SYSROOT="$APPDIR"
export FONTCONFIG_PATH="$APPDIR/etc/fonts${FONTCONFIG_PATH:+:$FONTCONFIG_PATH}"
if [ -f "$APPDIR/etc/fonts/fonts.conf" ]; then
export FONTCONFIG_FILE="$APPDIR/etc/fonts/fonts.conf"
fi
fi
if [ -d "$APPDIR/usr/lib/x86_64-linux-gnu/zoitechat/plugins" ]; then
export ZOITECHAT_LIBDIR="$APPDIR/usr/lib/x86_64-linux-gnu/zoitechat/plugins"
@@ -192,6 +233,16 @@ jobs:
fi
fi
for hook_dir in "$APPDIR/apprun-hooks" "$APPDIR/usr/apprun-hooks"; do
if [ -d "$hook_dir" ]; then
for hook in "$hook_dir"/*.sh; do
if [ -f "$hook" ]; then
. "$hook"
fi
done
fi
done
exec "$APPDIR/usr/bin/zoitechat" "$@"
EOF
chmod +x AppRun
@@ -217,7 +268,7 @@ jobs:
subject-path: Zoitechat-*-x86_64.AppImage
- name: Upload AppImage artifact
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: zoitechat-appimage
path: Zoitechat-*-x86_64.AppImage

View File

@@ -36,7 +36,7 @@ jobs:
- name: Upload Flatpak Bundle
id: upload_flatpak
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: zoitechat.flatpak
path: zoitechat.flatpak

View File

@@ -74,7 +74,7 @@ jobs:
cp -v "$GITHUB_WORKSPACE"/packaging/manjaro/.SRCINFO artifacts/
- name: Upload package artifacts
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: zoitechat-manjaro-package
path: artifacts/*

View File

@@ -143,7 +143,7 @@ jobs:
- name: Upload Installer
id: upload_installer
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: Installer ${{ matrix.arch }}
path: ZoiteChat-*.exe
@@ -157,7 +157,7 @@ jobs:
- name: Upload Build Files
id: upload_buildfiles
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: Build Files ${{ matrix.arch }}
path: zoitechat-build

View File

@@ -16,10 +16,11 @@
"--filesystem=~/.themes:ro",
"--filesystem=~/.icons:ro",
"--filesystem=xdg-run/tray-icon:create",
"--env=GTK_CSD=1",
"--talk-name=org.freedesktop.Notifications",
"--talk-name=org.kde.StatusNotifierWatcher",
"--talk-name=com.canonical.AppMenu.Registrar",
"--talk-name=org.mpris.MediaPlayer2.*"
],
"add-extensions": {
@@ -39,7 +40,6 @@
"shared-modules/libcanberra/libcanberra.json",
"shared-modules/libayatana-appindicator/libayatana-appindicator-gtk3.json",
"python3-cffi.json",
"perl.json",
{
"name": "lgi",
"buildsystem": "meson",
@@ -56,8 +56,8 @@
"buildsystem": "meson",
"config-opts": [
"-Ddbus-service-use-appid=true",
"-Dwith-perl=perl",
"-Dwith-python=python3",
"-Dwith-perl=false",
"-Dwith-python=false",
"-Dwith-lua=lua"
],
"build-options": {

View File

@@ -1,20 +0,0 @@
{
"name": "perl",
"buildsystem": "simple",
"build-commands": [
"./Configure -des -Dprefix=/app -Dvendorprefix=/app -Duseshrplib -Dman1dir=none -Dman3dir=none",
"make -j${FLATPAK_BUILDER_N_JOBS}",
"make install"
],
"cleanup": [
"/share/man",
"/lib/perl5/*/*/CORE/*.a"
],
"sources": [
{
"type": "archive",
"url": "https://www.cpan.org/src/5.0/perl-5.40.1.tar.xz",
"sha256": "dfa20c2eef2b4af133525610bbb65dd13777ecf998c9c5b1ccf0d308e732ee3f"
}
]
}

View File

@@ -7,13 +7,13 @@
"sources": [
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz",
"sha256": "491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"
"url": "https://files.pythonhosted.org/packages/0f/86/e19659527668d70be91d0369aeaa055b4eb396b0f387a4f92293a20035bd/pycparser-2.20.tar.gz",
"sha256": "2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"
},
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz",
"sha256": "1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"
"url": "https://files.pythonhosted.org/packages/a8/20/025f59f929bbcaa579704f443a438135918484fffaacfaddba776b374563/cffi-1.14.5.tar.gz",
"sha256": "fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c"
}
]
}

View File

@@ -32,7 +32,6 @@
#include <stdbool.h>
#else
#include <dirent.h>
#include <strings.h>
#endif
#include <glib.h>

View File

@@ -59,8 +59,11 @@ static const struct defaultserver def[] =
{0, "irc.afternet.org"},
{"Aitvaras", 0},
#ifdef USE_OPENSSL
{0, "irc.data.lt/+6668"},
{0, "irc.omicron.lt/+6668"},
{0, "irc.vub.lt/+6668"},
#endif
{0, "irc.data.lt"},
{0, "irc.omicron.lt"},
{0, "irc.vub.lt"},
@@ -93,6 +96,9 @@ static const struct defaultserver def[] =
{"ChatSpike", 0, 0, 0, LOGIN_SASL},
{0, "irc.chatspike.net"},
{"DaIRC", 0},
{0, "irc.dairc.net"},
{"DALnet", 0, 0, 0, LOGIN_NICKSERV},
/* Self signed */
{0, "us.dal.net"},
@@ -100,18 +106,32 @@ static const struct defaultserver def[] =
{"DarkMyst", 0, 0, 0, LOGIN_SASL, 0, TRUE},
{0, "irc.darkmyst.org"},
#ifdef USE_OPENSSL
{"darkscience", 0, 0, 0, LOGIN_SASL, 0, TRUE},
{0, "irc.darkscience.net"},
{0, "irc.drk.sc"},
{0, "irc.darkscience.ws"},
#endif
{"Dark-Tou-Net", 0},
{0, "irc.d-t-net.de"},
{"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, 0, LOGIN_SASL, 0, TRUE},
{0, "irc.efnet.org"},
{"EFnet", 0},
{0, "irc.choopa.net"},
{0, "efnet.port80.se"},
{0, "irc.underworld.no"},
{0, "efnet.deic.eu"},
{"EnterTheGame", 0},
{0, "irc.enterthegame.com"},
{"EntropyNet", 0, 0, 0, LOGIN_SASL, 0, TRUE},
{0, "irc.entropynet.net"},
@@ -126,6 +146,10 @@ static const struct defaultserver def[] =
/* Self signed */
{0, "irc.europnet.org"},
{"FDFNet", 0},
/* Self signed */
{0, "irc.fdfnet.net"},
{"GameSurge", 0},
{0, "irc.gamesurge.net"},
@@ -138,60 +162,86 @@ static const struct defaultserver def[] =
{"GIMPNet", 0},
/* Invalid hostname in cert */
{0, "irc.gimp.org"},
{0, "irc.gnome.org"},
{"GlobalGamers", 0},
#ifdef USE_OPENSSL
{0, "irc.globalgamers.net/+6660"},
#endif
{0, "irc.globalgamers.net"},
#ifdef USE_OPENSSL
{"hackint", 0, 0, 0, LOGIN_SASL, 0, TRUE},
{0, "irc.hackint.org"},
{0, "irc.eu.hackint.org"},
#endif
{"Hashmark", 0},
{0, "irc.hashmark.net"},
{"ICQ-Chat", 0, 0, 0, LOGIN_SASL, 0, TRUE},
{0, "irc.icq-chat.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"},
{"IRCNet", 0},
{0, "open.ircnet.net"},
{"IRCtoo", 0},
{0, "irc.irctoo.net"},
{"Keyboard-Failure", 0},
/* SSL is self-signed */
{0, "irc.kbfail.net"},
{"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"},
#endif
{"LibraIRC", 0},
/* Self signed */
{0, "irc.librairc.net"},
#ifdef USE_OPENSSL
{"LinkNet", 0},
{0, "irc.link-net.org/+7000"},
#endif
{"MindForge", 0, 0, 0, LOGIN_SASL},
{0, "irc.mindforge.org"},
{"MIXXnet", 0},
{0, "irc.mixxnet.net"},
{"Newnet", 0, 0, 0, LOGIN_SASL, 0, TRUE},
{0, "irc.newnet.net"},
{0, "australia-au.newnet.net"},
{0, "beauharnois-ca.newnet.net"},
{0, "vancouver-ca.newnet.net"},
{0, "gravelines-fr.newnet.net"},
{0, "sao-paulo.newnet.net"},
{0, "australia-au.newnet.net"},
{0, "beauharnois-ca.newnet.net"},
{0, "vancouver-ca.newnet.net"},
{0, "gravelines-fr.newnet.net"},
{0, "sao-paulo.newnet.net"},
{"Oceanius", 0, 0, 0, LOGIN_SASL},
/* Self signed */
{0, "irc.oceanius.com"},
{"OFTC", 0, 0, 0, 0, 0, TRUE},
{0, "irc.oftc.net"},
{"OtherNet", 0},
{0, "irc.othernet.org"},
{"OzOrg", 0},
{0, "irc.oz.org"},
@@ -218,6 +268,8 @@ static const struct defaultserver def[] =
{"RusNet", 0, 0, "KOI8-R (Cyrillic)"},
/* Self signed */
{0, "irc.tomsk.net"},
{0, "irc.run.net"},
{0, "irc.ru"},
{0, "irc.lucky.net"},
{"Serenity-IRC", 0},
@@ -239,7 +291,7 @@ static const struct defaultserver def[] =
{"SorceryNet", 0, 0, 0, LOGIN_SASL},
/* Self signed */
{0, "irc.sorcery.net"},
{"SpotChat", 0, 0, 0, LOGIN_SASL, 0, TRUE},
{0, "irc.spotchat.org"},
@@ -247,6 +299,9 @@ static const struct defaultserver def[] =
/* Self signed */
{0, "irc.station51.net"},
{"StormBit", 0, 0, 0, LOGIN_SASL, 0, TRUE},
{0, "irc.stormbit.net"},
{"SwiftIRC", 0},
/* Expired cert */
{0, "irc.swiftirc.net"},
@@ -257,16 +312,23 @@ static const struct defaultserver def[] =
{"Techtronix", 0, 0, 0, LOGIN_SASL, 0, TRUE},
{0, "irc.techtronix.net"},
{"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"},
{0, "newirc.tripsit.me"},
{0, "coconut.tripsit.me"},
{0, "innsbruck.tripsit.me"},
#endif
{"UnderNet", 0, 0, 0, LOGIN_CUSTOM, "MSG x@channels.undernet.org login %u %p"},
{0, "us.undernet.org"},
@@ -276,8 +338,8 @@ static const struct defaultserver def[] =
{"Zoite", 0, 0, 0, LOGIN_SASL, 0, TRUE},
{0, "irc.zoite.net"},
{0, "penumbra.zoite.net"},
{0, "hedy.zoite.net"},
{0, "penumbra.zoite.net"},
{0, "hedy.zoite.net"},
{0,0}
};

View File

@@ -1572,12 +1572,6 @@ menu_away (GtkCheckMenuItem *item, gpointer none)
handle_command (current_sess, gtk_check_menu_item_get_active (item) ? "away" : "back", FALSE);
}
static void
menu_away_toggle (GtkWidget *item, gpointer none)
{
handle_command (current_sess, current_sess->server->is_away ? "back" : "away", FALSE);
}
static void
menu_chanlist (GtkWidget * wid, gpointer none)
{
@@ -1894,12 +1888,30 @@ menu_about (GtkWidget *wid, gpointer sess)
gtk_widget_show_all (GTK_WIDGET(dialog));
}
#define ICON_NEW "zc-menu-new"
#define ICON_NETWORK_LIST "zc-menu-network-list"
#define ICON_LOAD_PLUGIN "zc-menu-load-plugin"
#define ICON_DETACH "zc-menu-detach"
#define ICON_CLOSE "zc-menu-close"
#define ICON_QUIT "zc-menu-quit"
#define ICON_DISCONNECT "zc-menu-disconnect"
#define ICON_CONNECT "zc-menu-connect"
#define ICON_JOIN "zc-menu-join"
#define ICON_CHANLIST "zc-menu-chanlist"
#define ICON_PREFERENCES "zc-menu-preferences"
#define ICON_CLEAR "zc-menu-clear"
#define ICON_SAVE "zc-menu-save"
#define ICON_SEARCH "zc-menu-search"
#define ICON_FIND "zc-menu-find"
#define ICON_HELP "zc-menu-help"
#define ICON_ABOUT "zc-menu-about"
static struct mymenu mymenu[] = {
{N_("_ZoiteChat"), 0, 0, M_NEWMENU, MENU_ID_ZOITECHAT, 0, 1},
{N_("Network Li_st"), menu_open_server_list, 0, M_MENUITEM, 0, 0, 1, GDK_KEY_s},
{N_("Network Li_st"), menu_open_server_list, ICON_NETWORK_LIST, M_MENUSTOCK, 0, 0, 1, GDK_KEY_s},
{0, 0, 0, M_SEP, 0, 0, 0},
{N_("_New"), 0, 0, M_MENUSUB, 0, 0, 1},
{N_("_New"), 0, ICON_NEW, M_MENUSUB, 0, 0, 1},
{N_("Server Tab"), menu_newserver_tab, 0, M_MENUITEM, 0, 0, 1, GDK_KEY_t},
{N_("Channel Tab"), menu_newchannel_tab, 0, M_MENUITEM, 0, 0, 1},
{N_("Server Window"), menu_newserver_window, 0, M_MENUITEM, 0, 0, 1, GDK_KEY_n},
@@ -1907,14 +1919,14 @@ static struct mymenu mymenu[] = {
{0, 0, 0, M_END, 0, 0, 0},
{0, 0, 0, M_SEP, 0, 0, 0},
{N_("_Load Plugin or Script" ELLIPSIS), menu_loadplugin, 0, M_MENUITEM, 0, 0, 1},
{N_("_Load Plugin or Script" ELLIPSIS), menu_loadplugin, ICON_LOAD_PLUGIN, M_MENUSTOCK, 0, 0, 1},
{0, 0, 0, M_SEP, 0, 0, 0}, /* 11 */
#define DETACH_OFFSET (12)
{0, menu_detach, 0, M_MENUITEM, 0, 0, 1}, /* 12 */
{0, menu_detach, ICON_DETACH, M_MENUSTOCK, 0, 0, 1}, /* 12 */
#define CLOSE_OFFSET (13)
{0, menu_close, 0, M_MENUITEM, 0, 0, 1},
{0, menu_close, ICON_CLOSE, M_MENUSTOCK, 0, 0, 1},
{0, 0, 0, M_SEP, 0, 0, 0},
{N_("_Quit"), menu_quit, 0, M_MENUITEM, 0, 0, 1, GDK_KEY_q}, /* 15 */
{N_("_Quit"), menu_quit, ICON_QUIT, M_MENUSTOCK, 0, 0, 1, GDK_KEY_q}, /* 15 */
{N_("_View"), 0, 0, M_NEWMENU, 0, 0, 1},
#define MENUBAR_OFFSET (17)
@@ -1940,18 +1952,18 @@ static struct mymenu mymenu[] = {
{N_ ("_Fullscreen"), menu_fullscreen_toggle, 0, M_MENUTOG, MENU_ID_FULLSCREEN, 0, 1, GDK_KEY_F11},
{N_("_Server"), 0, 0, M_NEWMENU, 0, 0, 1},
{N_("_Disconnect"), menu_disconnect, 0, M_MENUITEM, MENU_ID_DISCONNECT, 0, 1},
{N_("_Reconnect"), menu_reconnect, 0, M_MENUITEM, MENU_ID_RECONNECT, 0, 1},
{N_("_Join a Channel" ELLIPSIS), menu_join, 0, M_MENUITEM, MENU_ID_JOIN, 0, 1},
{N_("Channel _List"), menu_chanlist, 0, M_MENUITEM, 0, 0, 1},
{N_("_Disconnect"), menu_disconnect, ICON_DISCONNECT, M_MENUSTOCK, MENU_ID_DISCONNECT, 0, 1},
{N_("_Reconnect"), menu_reconnect, ICON_CONNECT, M_MENUSTOCK, MENU_ID_RECONNECT, 0, 1},
{N_("_Join a Channel" ELLIPSIS), menu_join, ICON_JOIN, M_MENUSTOCK, MENU_ID_JOIN, 0, 1},
{N_("Channel _List"), menu_chanlist, ICON_CHANLIST, M_MENUSTOCK, 0, 0, 1},
{0, 0, 0, M_SEP, 0, 0, 0},
#define AWAY_OFFSET (41)
{N_("Marked _Away"), menu_away_toggle, 0, M_MENUITEM, MENU_ID_AWAY, 0, 1, GDK_KEY_a},
{N_("Marked _Away"), menu_away, 0, M_MENUTOG, MENU_ID_AWAY, 0, 1, GDK_KEY_a},
{N_("_Usermenu"), 0, 0, M_NEWMENU, MENU_ID_USERMENU, 0, 1}, /* 40 */
{N_("S_ettings"), 0, 0, M_NEWMENU, 0, 0, 1},
{N_("_Preferences"), menu_settings, 0, M_MENUITEM, 0, 0, 1},
{N_("_Preferences"), menu_settings, ICON_PREFERENCES, M_MENUSTOCK, 0, 0, 1},
{0, 0, 0, M_SEP, 0, 0, 0},
{N_("Auto Replace"), menu_rpopup, 0, M_MENUITEM, 0, 0, 1},
{N_("CTCP Replies"), menu_ctcpguiopen, 0, M_MENUITEM, 0, 0, 1},
@@ -1977,18 +1989,18 @@ static struct mymenu mymenu[] = {
{N_("Reset Marker Line"), menu_resetmarker, 0, M_MENUITEM, 0, 0, 1, GDK_KEY_m},
{N_("Move to Marker Line"), menu_movetomarker, 0, M_MENUITEM, 0, 0, 1, GDK_KEY_M},
{N_("_Copy Selection"), menu_copy_selection, 0, M_MENUITEM, 0, 0, 1, GDK_KEY_C},
{N_("C_lear Text"), menu_flushbuffer, 0, M_MENUITEM, 0, 0, 1},
{N_("Save Text" ELLIPSIS), menu_savebuffer, 0, M_MENUITEM, 0, 0, 1},
{N_("C_lear Text"), menu_flushbuffer, ICON_CLEAR, M_MENUSTOCK, 0, 0, 1},
{N_("Save Text" ELLIPSIS), menu_savebuffer, ICON_SAVE, M_MENUSTOCK, 0, 0, 1},
#define SEARCH_OFFSET (70)
{N_("Search"), 0, 0, M_MENUSUB, 0, 0, 1},
{N_("Search Text" ELLIPSIS), menu_search, 0, M_MENUITEM, 0, 0, 1, GDK_KEY_f},
{N_("Search Next" ), menu_search_next, 0, M_MENUITEM, 0, 0, 1, GDK_KEY_g},
{N_("Search Previous" ), menu_search_prev, 0, M_MENUITEM, 0, 0, 1, GDK_KEY_G},
{N_("Search"), 0, ICON_SEARCH, M_MENUSUB, 0, 0, 1},
{N_("Search Text" ELLIPSIS), menu_search, ICON_FIND, M_MENUSTOCK, 0, 0, 1, GDK_KEY_f},
{N_("Search Next" ), menu_search_next, ICON_FIND, M_MENUSTOCK, 0, 0, 1, GDK_KEY_g},
{N_("Search Previous" ), menu_search_prev, ICON_FIND, M_MENUSTOCK, 0, 0, 1, GDK_KEY_G},
{0, 0, 0, M_END, 0, 0, 0},
{N_("_Help"), 0, 0, M_NEWMENU, 0, 0, 1}, /* 74 */
{N_("_Contents"), menu_docs, 0, M_MENUITEM, 0, 0, 1, GDK_KEY_F1},
{N_("_About"), menu_about, 0, M_MENUITEM, 0, 0, 1},
{N_("_Contents"), menu_docs, ICON_HELP, M_MENUSTOCK, 0, 0, 1, GDK_KEY_F1},
{N_("_About"), menu_about, ICON_ABOUT, M_MENUSTOCK, 0, 0, 1},
{0, 0, 0, M_END, 0, 0, 0},
};
@@ -1996,14 +2008,11 @@ static struct mymenu mymenu[] = {
void
menu_set_away (session_gui *gui, int away)
{
if (GTK_IS_CHECK_MENU_ITEM (gui->menu_item[MENU_ID_AWAY]))
{
GtkCheckMenuItem *item = GTK_CHECK_MENU_ITEM (gui->menu_item[MENU_ID_AWAY]);
GtkCheckMenuItem *item = GTK_CHECK_MENU_ITEM (gui->menu_item[MENU_ID_AWAY]);
g_signal_handlers_block_by_func (G_OBJECT (item), menu_away, NULL);
gtk_check_menu_item_set_active (item, away);
g_signal_handlers_unblock_by_func (G_OBJECT (item), menu_away, NULL);
}
g_signal_handlers_block_by_func (G_OBJECT (item), menu_away, NULL);
gtk_check_menu_item_set_active (item, away);
g_signal_handlers_unblock_by_func (G_OBJECT (item), menu_away, NULL);
}
void

View File

@@ -1,525 +0,0 @@
#!/usr/bin/env bash
set -Eeuo pipefail
IFS=$'\n\t'
SCRIPT_NAME=$(basename "$0")
VERSION="1.0.0"
REPO_URL="${REPO_URL:-}"
BRANCH="${BRANCH:-main}"
CHECKOUT_DIR="${CHECKOUT_DIR:-/tmp/zoitechat-servscan}"
SERVLIST_PATH="${SERVLIST_PATH:-}"
EMAIL_TO="${EMAIL_TO:-}"
EMAIL_FROM="${EMAIL_FROM:-servscan@$(hostname -f 2>/dev/null || hostname)}"
SUBJECT_PREFIX="${SUBJECT_PREFIX:-ZoiteChat servlist scan}"
CONNECT_TIMEOUT="${CONNECT_TIMEOUT:-8}"
SSL_TIMEOUT="${SSL_TIMEOUT:-15}"
PLAIN_PORT="${PLAIN_PORT:-6667}"
SSL_PORT="${SSL_PORT:-6697}"
DO_CLONE=1
DO_EMAIL=1
TMP_DIR=""
REPORT_FILE=""
ROWS_FILE=""
COMMIT_REF="local-file"
usage() {
cat <<USAGE
Usage:
${SCRIPT_NAME} --repo <git-url> --branch <branch> --email-to <address> [options]
${SCRIPT_NAME} --servlist <path/to/src/common/servlist.c> --email-to <address> [options]
Options:
--repo <url> Git repository to clone or update.
--branch <name> Branch to scan. Default: ${BRANCH}
--checkout-dir <path> Dedicated checkout dir. Default: ${CHECKOUT_DIR}
--servlist <path> Scan a local servlist.c instead of cloning.
--email-to <address> Recipient email address.
--email-from <address> From address for the email.
--subject-prefix <text> Subject prefix. Default: ${SUBJECT_PREFIX}
--connect-timeout <sec> TCP timeout. Default: ${CONNECT_TIMEOUT}
--ssl-timeout <sec> TLS timeout. Default: ${SSL_TIMEOUT}
--plain-port <port> Default non-SSL IRC port. Default: ${PLAIN_PORT}
--ssl-port <port> Default SSL IRC port. Default: ${SSL_PORT}
--no-clone Do not clone. Requires --servlist.
--no-email Print report only.
-h, --help Show this help.
Notes:
- Existing checkouts in --checkout-dir are hard-reset to origin/<branch>.
- SSL is tested when the network defaults to SSL or the host uses /+port.
- Email delivery uses sendmail, mailx, or mail.
USAGE
}
die() {
printf 'Error: %s\n' "$*" >&2
exit 1
}
cleanup() {
if [[ -n "${TMP_DIR}" && -d "${TMP_DIR}" ]]; then
rm -rf "${TMP_DIR}"
fi
}
trap cleanup EXIT
sanitize_detail() {
local text=${1:-}
text=$(printf '%s' "$text" | tr '\n' ' ' | sed -E 's/[[:space:]]+/ /g; s/^ +//; s/ +$//')
printf '%.220s' "$text"
}
extract_ssl_error() {
local raw=${1:-}
local line
line=$(printf '%s\n' "$raw" | grep -E 'Verify return code:|verify error:|unable to get local issuer certificate|self-signed certificate|certificate has expired|hostname mismatch|wrong version number|no peer certificate available|tlsv1 alert|sslv3 alert|Connection refused|connect:' | head -n1 || true)
if [[ -z "$line" ]]; then
line=$(printf '%s\n' "$raw" | tail -n5 | head -n1 || true)
fi
sanitize_detail "$line"
}
require_cmd() {
command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1"
}
parse_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
--repo)
REPO_URL=${2:?missing value for --repo}
shift 2
;;
--branch)
BRANCH=${2:?missing value for --branch}
shift 2
;;
--checkout-dir)
CHECKOUT_DIR=${2:?missing value for --checkout-dir}
shift 2
;;
--servlist)
SERVLIST_PATH=${2:?missing value for --servlist}
shift 2
;;
--email-to)
EMAIL_TO=${2:?missing value for --email-to}
shift 2
;;
--email-from)
EMAIL_FROM=${2:?missing value for --email-from}
shift 2
;;
--subject-prefix)
SUBJECT_PREFIX=${2:?missing value for --subject-prefix}
shift 2
;;
--connect-timeout)
CONNECT_TIMEOUT=${2:?missing value for --connect-timeout}
shift 2
;;
--ssl-timeout)
SSL_TIMEOUT=${2:?missing value for --ssl-timeout}
shift 2
;;
--plain-port)
PLAIN_PORT=${2:?missing value for --plain-port}
shift 2
;;
--ssl-port)
SSL_PORT=${2:?missing value for --ssl-port}
shift 2
;;
--no-clone)
DO_CLONE=0
shift
;;
--no-email)
DO_EMAIL=0
shift
;;
-h|--help)
usage
exit 0
;;
*)
die "Unknown argument: $1"
;;
esac
done
if [[ -n "${SERVLIST_PATH}" && -z "${REPO_URL}" ]]; then
DO_CLONE=0
fi
if [[ ${DO_CLONE} -eq 1 && -z "${REPO_URL}" && -z "${SERVLIST_PATH}" ]]; then
die "Provide --repo or --servlist"
fi
if [[ ${DO_CLONE} -eq 0 && -z "${SERVLIST_PATH}" ]]; then
die "--no-clone requires --servlist"
fi
if [[ ${DO_EMAIL} -eq 1 && -z "${EMAIL_TO}" ]]; then
die "Provide --email-to, or use --no-email for testing"
fi
}
prepare_workspace() {
TMP_DIR=$(mktemp -d)
REPORT_FILE="${TMP_DIR}/report.txt"
ROWS_FILE="${TMP_DIR}/rows.tsv"
}
clone_or_update_repo() {
[[ ${DO_CLONE} -eq 1 ]] || return 0
require_cmd git
if [[ -d "${CHECKOUT_DIR}/.git" ]]; then
git -C "${CHECKOUT_DIR}" fetch --depth 1 origin "${BRANCH}"
git -C "${CHECKOUT_DIR}" checkout -q "${BRANCH}" 2>/dev/null || git -C "${CHECKOUT_DIR}" checkout -q -B "${BRANCH}" "origin/${BRANCH}"
git -C "${CHECKOUT_DIR}" reset --hard "origin/${BRANCH}" >/dev/null
else
rm -rf "${CHECKOUT_DIR}"
git clone --depth 1 --branch "${BRANCH}" "${REPO_URL}" "${CHECKOUT_DIR}" >/dev/null
fi
COMMIT_REF=$(git -C "${CHECKOUT_DIR}" rev-parse --short HEAD)
SERVLIST_PATH="${CHECKOUT_DIR}/src/common/servlist.c"
}
validate_inputs() {
require_cmd awk
require_cmd grep
require_cmd sed
require_cmd openssl
require_cmd timeout
[[ -f "${SERVLIST_PATH}" ]] || die "servlist.c not found: ${SERVLIST_PATH}"
}
parse_servlist() {
awk '
function clean_comment(s) {
gsub(/^[[:space:]]*\/\*[[:space:]]*/, "", s)
gsub(/[[:space:]]*\*\/[[:space:]]*$/, "", s)
gsub(/[[:space:]]+/, " ", s)
return s
}
/static const struct defaultserver def\[\]/ {
in_def = 1
next
}
!in_def { next }
/^[[:space:]]*#(if|ifdef|ifndef|else|elif|endif)/ { next }
/^[[:space:]]*\};/ { exit }
/^[[:space:]]*\/\*/ {
last_comment = clean_comment($0)
next
}
/^[[:space:]]*\{[[:space:]]*0[[:space:]]*,[[:space:]]*0[[:space:]]*\}[[:space:]]*,?[[:space:]]*$/ {
exit
}
/^[[:space:]]*\{[[:space:]]*"/ {
line = $0
sub(/^[[:space:]]*\{[[:space:]]*"/, "", line)
split(line, parts, /"/)
current_network = parts[1]
current_ssl = ($0 ~ /,[[:space:]]*TRUE[[:space:]]*\}[[:space:]]*,?[[:space:]]*$/) ? 1 : 0
last_comment = ""
next
}
/^[[:space:]]*\{[[:space:]]*0[[:space:]]*,[[:space:]]*"/ {
line = $0
sub(/^[[:space:]]*\{[[:space:]]*0[[:space:]]*,[[:space:]]*"/, "", line)
split(line, parts, /"/)
gsub(/\t/, " ", last_comment)
printf "%s\t%s\t%s\t%s\n", current_network, current_ssl, parts[1], last_comment
last_comment = ""
next
}
{
last_comment = ""
}
' "${SERVLIST_PATH}"
}
scan_server() {
local network=$1
local ssl_default=$2
local hostspec=$3
local note=${4:-}
local host="$hostspec"
local port=""
local port_part=""
local explicit_ssl=0
local ssl_expected=$ssl_default
local tcp_status="FAIL"
local tcp_detail=""
local ssl_status="N/A"
local ssl_detail="-"
local ssl_raw=""
local tcp_raw=""
local tcp_rc=0
local ssl_rc=0
local verify_line=""
local cert_block=""
local cert_meta=""
local enddate=""
local expires_note=""
if [[ "$hostspec" == */* ]]; then
host=${hostspec%/*}
port_part=${hostspec##*/}
if [[ "$port_part" == +* ]]; then
explicit_ssl=1
port=${port_part#+}
else
port=$port_part
fi
fi
if [[ ${explicit_ssl} -eq 1 ]]; then
ssl_expected=1
fi
if [[ -z "$port" ]]; then
if [[ ${ssl_expected} -eq 1 ]]; then
port=$SSL_PORT
else
port=$PLAIN_PORT
fi
fi
set +e
tcp_raw=$(timeout "${CONNECT_TIMEOUT}" bash -c "exec 3<>/dev/tcp/${host}/${port}" 2>&1)
tcp_rc=$?
set -e
if [[ ${tcp_rc} -eq 0 ]]; then
tcp_status="OK"
tcp_detail="connected"
elif [[ ${tcp_rc} -eq 124 ]]; then
tcp_detail="timeout"
else
tcp_detail=$(sanitize_detail "$tcp_raw")
fi
if [[ ${ssl_expected} -eq 1 ]]; then
if [[ "$tcp_status" != "OK" ]]; then
ssl_status="SKIPPED"
ssl_detail="tcp failed"
else
set +e
ssl_raw=$(timeout "${SSL_TIMEOUT}" openssl s_client \
-connect "${host}:${port}" \
-servername "${host}" \
-verify_return_error \
-verify_hostname "${host}" \
< /dev/null 2>&1)
ssl_rc=$?
set -e
if [[ ${ssl_rc} -eq 124 ]]; then
ssl_status="INVALID"
ssl_detail="tls timeout"
else
verify_line=$(printf '%s\n' "$ssl_raw" | grep -E 'Verify return code:' | tail -n1 || true)
cert_block=$(printf '%s\n' "$ssl_raw" | awk '
/-----BEGIN CERTIFICATE-----/ { found = 1 }
found { print }
/-----END CERTIFICATE-----/ { exit }
')
if [[ -n "$cert_block" ]]; then
cert_meta=$(printf '%s\n' "$cert_block" | openssl x509 -noout -enddate 2>/dev/null || true)
enddate=$(printf '%s\n' "$cert_meta" | sed -n 's/^notAfter=//p' | head -n1)
if [[ -n "$enddate" ]]; then
expires_note="expires ${enddate}"
fi
fi
if printf '%s\n' "$ssl_raw" | grep -q 'Verify return code: 0 (ok)'; then
ssl_status="VALID"
ssl_detail="ok"
else
ssl_status="INVALID"
ssl_detail=$(extract_ssl_error "$ssl_raw")
fi
if [[ -n "$verify_line" && "$ssl_status" != "VALID" ]]; then
ssl_detail=$(sanitize_detail "$verify_line")
fi
if [[ -n "$expires_note" ]]; then
ssl_detail="${ssl_detail}; ${expires_note}"
fi
fi
fi
fi
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
"$network" \
"$host" \
"$port" \
"$ssl_expected" \
"$tcp_status" \
"$ssl_status" \
"$ssl_detail" \
"$note"
}
build_report() {
local total=0
local tcp_ok=0
local tcp_fail=0
local ssl_expected_count=0
local ssl_valid=0
local ssl_invalid=0
local issue_count=0
local scan_status="CLEAN"
local repo_display="${REPO_URL:-local file}"
local generated_at
local host_name
generated_at=$(date '+%Y-%m-%d %H:%M:%S %Z')
host_name=$(hostname -f 2>/dev/null || hostname)
while IFS=$'\t' read -r network ssl_default hostspec note; do
[[ -n "$network" ]] || continue
scan_server "$network" "$ssl_default" "$hostspec" "$note" >> "${ROWS_FILE}"
done < <(parse_servlist)
while IFS=$'\t' read -r network host port ssl_expected tcp_status ssl_status ssl_detail note; do
((total += 1))
if [[ "$tcp_status" == "OK" ]]; then
((tcp_ok += 1))
else
((tcp_fail += 1))
((issue_count += 1))
fi
if [[ "$ssl_expected" == "1" ]]; then
((ssl_expected_count += 1))
if [[ "$ssl_status" == "VALID" ]]; then
((ssl_valid += 1))
else
((ssl_invalid += 1))
((issue_count += 1))
fi
fi
done < "${ROWS_FILE}"
if [[ ${issue_count} -gt 0 ]]; then
scan_status="ISSUES"
fi
{
printf 'ZoiteChat servlist scan report\n'
printf 'Status: %s\n' "$scan_status"
printf 'Generated: %s\n' "$generated_at"
printf 'Runner: %s\n' "$host_name"
printf 'Repo: %s\n' "$repo_display"
printf 'Branch: %s\n' "$BRANCH"
printf 'Commit: %s\n' "$COMMIT_REF"
printf 'servlist: %s\n' "$SERVLIST_PATH"
printf '\n'
printf 'Summary\n'
printf ' Total servers scanned : %d\n' "$total"
printf ' TCP reachable : %d\n' "$tcp_ok"
printf ' TCP failed : %d\n' "$tcp_fail"
printf ' SSL expected : %d\n' "$ssl_expected_count"
printf ' SSL valid : %d\n' "$ssl_valid"
printf ' SSL invalid/skipped : %d\n' "$ssl_invalid"
printf '\n'
printf '%-18s %-34s %-6s %-4s %-5s %-8s %s\n' 'Network' 'Server' 'Port' 'SSL' 'TCP' 'TLS' 'Details'
printf '%-18s %-34s %-6s %-4s %-5s %-8s %s\n' '------------------' '----------------------------------' '------' '----' '-----' '--------' '-------'
while IFS=$'\t' read -r network host port ssl_expected tcp_status ssl_status ssl_detail note; do
local detail="$ssl_detail"
if [[ "$ssl_expected" != "1" ]]; then
detail="$tcp_status"
fi
if [[ -n "$note" ]]; then
detail="${detail} | comment: ${note}"
fi
printf '%-18.18s %-34.34s %-6s %-4s %-5s %-8s %s\n' \
"$network" \
"$host" \
"$port" \
"$([[ "$ssl_expected" == "1" ]] && printf 'yes' || printf 'no')" \
"$tcp_status" \
"$ssl_status" \
"$detail"
done < "${ROWS_FILE}"
} > "${REPORT_FILE}"
}
send_report() {
[[ ${DO_EMAIL} -eq 1 ]] || return 0
local status_word="CLEAN"
local subject=""
if grep -q '^Status: ISSUES$' "${REPORT_FILE}"; then
status_word="ISSUES"
fi
subject="${SUBJECT_PREFIX} [${status_word}] $(date '+%Y-%m-%d')"
if command -v sendmail >/dev/null 2>&1; then
{
printf 'To: %s\n' "$EMAIL_TO"
printf 'From: %s\n' "$EMAIL_FROM"
printf 'Subject: %s\n' "$subject"
printf 'Date: %s\n' "$(LC_ALL=C date -R)"
printf 'MIME-Version: 1.0\n'
printf 'Content-Type: text/plain; charset=UTF-8\n'
printf '\n'
cat "${REPORT_FILE}"
} | sendmail -t
return 0
fi
if command -v mailx >/dev/null 2>&1; then
mailx -r "$EMAIL_FROM" -s "$subject" "$EMAIL_TO" < "${REPORT_FILE}"
return 0
fi
if command -v mail >/dev/null 2>&1; then
if mail --help 2>&1 | grep -q -- ' -r'; then
mail -r "$EMAIL_FROM" -s "$subject" "$EMAIL_TO" < "${REPORT_FILE}"
else
mail -s "$subject" "$EMAIL_TO" < "${REPORT_FILE}"
fi
return 0
fi
die "No supported mailer found. Install sendmail, mailx, or mail."
}
main() {
parse_args "$@"
prepare_workspace
clone_or_update_repo
validate_inputs
build_report
cat "${REPORT_FILE}"
send_report
}
main "$@"