summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitattributes3
-rw-r--r--.gitignore5
-rw-r--r--.travis.yml65
-rw-r--r--AUTHORS24
-rw-r--r--INSTALL23
-rw-r--r--Makefile.am4
-rw-r--r--NEWS200
-rw-r--r--acconfig.h34
-rwxr-xr-xautogen.sh8
-rw-r--r--configure.ac359
-rw-r--r--default.theme2
-rw-r--r--docs/Makefile.am2
-rw-r--r--docs/faq.html150
-rw-r--r--docs/help/in/away.in4
-rw-r--r--docs/help/in/channel.in2
-rw-r--r--docs/help/in/connect.in33
-rw-r--r--docs/help/in/dcc.in8
-rw-r--r--docs/help/in/disconnect.in7
-rw-r--r--docs/help/in/echo.in4
-rw-r--r--docs/help/in/hilight.in3
-rw-r--r--docs/help/in/ignore.in1
-rw-r--r--docs/help/in/irssiproxy.in14
-rw-r--r--docs/help/in/lastlog.in16
-rw-r--r--docs/help/in/list.in17
-rw-r--r--docs/help/in/msg.in4
-rw-r--r--docs/help/in/network.in8
-rw-r--r--docs/help/in/script.in2
-rw-r--r--docs/help/in/server.in69
-rw-r--r--docs/help/in/servlist.in23
-rw-r--r--docs/help/in/squery.in16
-rw-r--r--docs/help/in/window.in84
-rw-r--r--docs/irssi.165
-rw-r--r--docs/perl.txt18
-rw-r--r--docs/proxy.txt34
-rw-r--r--docs/signals.txt16
-rw-r--r--docs/startup-HOWTO-rus.html846
-rw-r--r--docs/startup-HOWTO.html1132
-rwxr-xr-xirssi-version.sh19
-rw-r--r--irssi.conf12
-rw-r--r--m4/curses.m4298
-rw-r--r--m4/glib-2.0.m44
-rw-r--r--scripts/Makefile.am2
-rw-r--r--scripts/autoop.pl73
-rw-r--r--scripts/autorejoin.pl71
-rw-r--r--scripts/buf.pl55
-rw-r--r--scripts/dns.pl22
-rw-r--r--scripts/kills.pl9
-rw-r--r--scripts/mail.pl29
-rw-r--r--scripts/mlock.pl2
-rw-r--r--scripts/quitmsg.pl4
-rw-r--r--scripts/sb_search.pl142
-rw-r--r--scripts/scriptassist.pl454
-rw-r--r--scripts/splitlong.pl60
-rw-r--r--scripts/usercount.pl3
-rw-r--r--src/Makefile.am6
-rw-r--r--src/common.h18
-rw-r--r--src/core/Makefile.am5
-rw-r--r--src/core/channels-setup.c65
-rw-r--r--src/core/channels-setup.h3
-rw-r--r--src/core/channels.c33
-rw-r--r--src/core/channels.h1
-rw-r--r--src/core/chat-commands.c69
-rw-r--r--src/core/chatnets.c4
-rw-r--r--src/core/commands.c13
-rw-r--r--src/core/commands.h7
-rw-r--r--src/core/core.c14
-rw-r--r--src/core/expandos.c31
-rw-r--r--src/core/ignore.c89
-rw-r--r--src/core/ignore.h16
-rw-r--r--src/core/levels.c4
-rw-r--r--src/core/log-away.c7
-rw-r--r--src/core/log.c38
-rw-r--r--src/core/misc.c230
-rw-r--r--src/core/misc.h23
-rw-r--r--src/core/modules-load.c44
-rw-r--r--src/core/modules.c4
-rw-r--r--src/core/modules.h1
-rw-r--r--src/core/net-disconnect.c2
-rw-r--r--src/core/net-nonblock.c10
-rw-r--r--src/core/net-nonblock.h2
-rw-r--r--src/core/net-sendbuffer.c6
-rw-r--r--src/core/net-sendbuffer.h2
-rw-r--r--src/core/network-openssl.c398
-rw-r--r--src/core/network.c146
-rw-r--r--src/core/network.h14
-rw-r--r--src/core/nicklist.c12
-rw-r--r--src/core/nicklist.h1
-rw-r--r--src/core/query-rec.h2
-rw-r--r--src/core/rawlog.c17
-rw-r--r--src/core/recode.c20
-rw-r--r--src/core/recode.h2
-rw-r--r--src/core/server-connect-rec.h17
-rw-r--r--src/core/server-setup-rec.h20
-rw-r--r--src/core/servers-reconnect.c27
-rw-r--r--src/core/servers-setup.c208
-rw-r--r--src/core/servers-setup.h3
-rw-r--r--src/core/servers.c53
-rw-r--r--src/core/servers.h2
-rw-r--r--src/core/session.c32
-rw-r--r--src/core/settings.c131
-rw-r--r--src/core/settings.h19
-rw-r--r--src/core/special-vars.c23
-rw-r--r--src/core/special-vars.h7
-rw-r--r--src/core/tls.c214
-rw-r--r--src/core/tls.h90
-rw-r--r--src/core/utf8.c135
-rw-r--r--src/core/utf8.h56
-rw-r--r--src/core/wcwidth.c (renamed from src/fe-common/core/wcwidth.c)4
-rw-r--r--src/fe-common/core/Makefile.am7
-rw-r--r--src/fe-common/core/chat-completion.c85
-rw-r--r--src/fe-common/core/command-history.c57
-rw-r--r--src/fe-common/core/command-history.h1
-rw-r--r--src/fe-common/core/completion.c90
-rw-r--r--src/fe-common/core/fe-channels.c129
-rw-r--r--src/fe-common/core/fe-common-core.c39
-rw-r--r--src/fe-common/core/fe-core-commands.c3
-rw-r--r--src/fe-common/core/fe-exec.c17
-rw-r--r--src/fe-common/core/fe-help.c6
-rw-r--r--src/fe-common/core/fe-ignore.c30
-rw-r--r--src/fe-common/core/fe-log.c43
-rw-r--r--src/fe-common/core/fe-messages.c53
-rw-r--r--src/fe-common/core/fe-modules.c4
-rw-r--r--src/fe-common/core/fe-queries.c7
-rw-r--r--src/fe-common/core/fe-recode.c2
-rw-r--r--src/fe-common/core/fe-server.c97
-rw-r--r--src/fe-common/core/fe-settings.c65
-rw-r--r--src/fe-common/core/fe-tls.c94
-rw-r--r--src/fe-common/core/fe-tls.h25
-rw-r--r--src/fe-common/core/fe-windows.c236
-rw-r--r--src/fe-common/core/fe-windows.h1
-rw-r--r--src/fe-common/core/formats.c56
-rw-r--r--src/fe-common/core/formats.h4
-rw-r--r--src/fe-common/core/hilight-text.c226
-rw-r--r--src/fe-common/core/hilight-text.h11
-rw-r--r--src/fe-common/core/keyboard.c61
-rw-r--r--src/fe-common/core/module-formats.c20
-rw-r--r--src/fe-common/core/module-formats.h17
-rw-r--r--src/fe-common/core/module.h2
-rw-r--r--src/fe-common/core/printtext.c18
-rw-r--r--src/fe-common/core/printtext.h1
-rw-r--r--src/fe-common/core/themes.c14
-rw-r--r--src/fe-common/core/utf8.c26
-rw-r--r--src/fe-common/core/utf8.h17
-rw-r--r--src/fe-common/core/window-commands.c27
-rw-r--r--src/fe-common/core/windows-layout.c15
-rw-r--r--src/fe-common/irc/Makefile.am2
-rw-r--r--src/fe-common/irc/dcc/fe-dcc-chat-messages.c2
-rw-r--r--src/fe-common/irc/dcc/fe-dcc-chat.c4
-rw-r--r--src/fe-common/irc/dcc/fe-dcc-get.c5
-rw-r--r--src/fe-common/irc/fe-common-irc.c9
-rw-r--r--src/fe-common/irc/fe-ctcp.c8
-rw-r--r--src/fe-common/irc/fe-events-numeric.c10
-rw-r--r--src/fe-common/irc/fe-events.c17
-rw-r--r--src/fe-common/irc/fe-irc-channels.c44
-rw-r--r--src/fe-common/irc/fe-irc-channels.h10
-rw-r--r--src/fe-common/irc/fe-irc-commands.c16
-rw-r--r--src/fe-common/irc/fe-irc-messages.c114
-rw-r--r--src/fe-common/irc/fe-irc-queries.c9
-rw-r--r--src/fe-common/irc/fe-irc-server.c48
-rw-r--r--src/fe-common/irc/fe-ircnet.c63
-rw-r--r--src/fe-common/irc/fe-modes.c4
-rw-r--r--src/fe-common/irc/fe-netjoin.c47
-rw-r--r--src/fe-common/irc/fe-netsplit.c32
-rw-r--r--src/fe-common/irc/fe-sasl.c75
-rw-r--r--src/fe-common/irc/fe-whois.c12
-rw-r--r--src/fe-common/irc/module-formats.c2
-rw-r--r--src/fe-common/irc/module-formats.h2
-rw-r--r--src/fe-fuzz/Makefile.am25
-rw-r--r--src/fe-fuzz/irssi.c57
-rw-r--r--src/fe-fuzz/tokens.txt143
-rw-r--r--src/fe-text/Makefile.am23
-rw-r--r--src/fe-text/gui-entry.c184
-rw-r--r--src/fe-text/gui-entry.h29
-rw-r--r--src/fe-text/gui-printtext.c20
-rw-r--r--src/fe-text/gui-printtext.h1
-rw-r--r--src/fe-text/gui-readline.c289
-rw-r--r--src/fe-text/gui-windows.c10
-rw-r--r--src/fe-text/irssi.c136
-rw-r--r--src/fe-text/lastlog.c48
-rw-r--r--src/fe-text/mainwindows-layout.c3
-rw-r--r--src/fe-text/module-formats.c1
-rw-r--r--src/fe-text/module-formats.h1
-rw-r--r--src/fe-text/statusbar-config.c26
-rw-r--r--src/fe-text/statusbar-items.c38
-rw-r--r--src/fe-text/statusbar.c8
-rw-r--r--src/fe-text/term-curses.c415
-rw-r--r--src/fe-text/term-dummy.c106
-rw-r--r--src/fe-text/term-terminfo.c44
-rw-r--r--src/fe-text/term.h7
-rw-r--r--src/fe-text/terminfo-core.c189
-rw-r--r--src/fe-text/terminfo-core.h8
-rw-r--r--src/fe-text/textbuffer-view.c14
-rw-r--r--src/fe-text/textbuffer-view.h5
-rw-r--r--src/fe-text/textbuffer.c40
-rw-r--r--src/fe-text/tparm.c740
-rw-r--r--src/irc/core/Makefile.am4
-rw-r--r--src/irc/core/bans.c22
-rw-r--r--src/irc/core/channel-events.c8
-rw-r--r--src/irc/core/channel-rejoin.c4
-rw-r--r--src/irc/core/channels-query.c2
-rw-r--r--src/irc/core/irc-cap.c192
-rw-r--r--src/irc/core/irc-cap.h9
-rw-r--r--src/irc/core/irc-channels-setup.c6
-rw-r--r--src/irc/core/irc-channels.c43
-rw-r--r--src/irc/core/irc-chatnets.c26
-rw-r--r--src/irc/core/irc-chatnets.h9
-rw-r--r--src/irc/core/irc-commands.c274
-rw-r--r--src/irc/core/irc-core.c8
-rw-r--r--src/irc/core/irc-expandos.c38
-rw-r--r--src/irc/core/irc-nicklist.c38
-rw-r--r--src/irc/core/irc-nicklist.h3
-rw-r--r--src/irc/core/irc-queries.c28
-rw-r--r--src/irc/core/irc-servers-reconnect.c5
-rw-r--r--src/irc/core/irc-servers-setup.c36
-rw-r--r--src/irc/core/irc-servers.c136
-rw-r--r--src/irc/core/irc-servers.h13
-rw-r--r--src/irc/core/irc-session.c21
-rw-r--r--src/irc/core/irc.c8
-rw-r--r--src/irc/core/irc.h6
-rw-r--r--src/irc/core/modes.c25
-rw-r--r--src/irc/core/sasl.c329
-rw-r--r--src/irc/core/sasl.h34
-rw-r--r--src/irc/core/servers-redirect.c18
-rw-r--r--src/irc/dcc/dcc-autoget.c4
-rw-r--r--src/irc/dcc/dcc-chat.c16
-rw-r--r--src/irc/dcc/dcc-get.c40
-rw-r--r--src/irc/dcc/dcc-resume.c4
-rw-r--r--src/irc/dcc/dcc-send.c12
-rw-r--r--src/irc/dcc/dcc-server.c2
-rw-r--r--src/irc/dcc/dcc.c16
-rw-r--r--src/irc/dcc/dcc.h2
-rw-r--r--src/irc/flood/autoignore.c4
-rw-r--r--src/irc/flood/flood.c4
-rw-r--r--src/irc/notifylist/notify-commands.c3
-rw-r--r--src/irc/notifylist/notify-setup.c6
-rw-r--r--src/irc/notifylist/notifylist.c2
-rw-r--r--src/irc/proxy/dump.c40
-rw-r--r--src/irc/proxy/listen.c414
-rw-r--r--src/irc/proxy/proxy.c64
-rw-r--r--src/irc/proxy/proxy.h5
-rw-r--r--src/lib-config/get.c62
-rw-r--r--src/lib-config/iconfig.h4
-rw-r--r--src/lib-config/parse.c4
-rw-r--r--src/lib-config/set.c10
-rw-r--r--src/perl/Makefile.am14
-rw-r--r--src/perl/Makefile_silent.pm76
-rw-r--r--src/perl/common/Core.xs2
-rw-r--r--src/perl/common/Expando.xs11
-rw-r--r--src/perl/common/Irssi.pm3
-rw-r--r--src/perl/common/Makefile.PL.in2
-rwxr-xr-xsrc/perl/get-signals.pl1
-rw-r--r--src/perl/irc/Irc.xs148
-rw-r--r--src/perl/irc/Makefile.PL.in2
-rw-r--r--src/perl/irc/Server.xs9
-rw-r--r--src/perl/irc/module.h1
-rw-r--r--src/perl/irssi-core.pl4
-rw-r--r--src/perl/module.h2
-rw-r--r--src/perl/perl-common.c240
-rw-r--r--src/perl/perl-core.c34
-rw-r--r--src/perl/perl-core.h2
-rw-r--r--src/perl/perl-fe.c20
-rw-r--r--src/perl/perl-signals.c45
-rw-r--r--src/perl/perl-signals.h2
-rw-r--r--src/perl/textui/Makefile.PL.in2
-rw-r--r--src/perl/textui/Statusbar.xs9
-rw-r--r--src/perl/textui/TextUI.xs66
-rw-r--r--src/perl/ui/Makefile.PL.in2
-rw-r--r--src/perl/ui/UI.xs81
-rwxr-xr-xsyntax.pl2
269 files changed, 7771 insertions, 6787 deletions
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..e1ae7d9d
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,3 @@
+# On Windows, some Git clients may normalize all text files' line-endings to
+# CRLF, which causes obscure errors when running shell scripts.
+*.sh eol=lf
diff --git a/.gitignore b/.gitignore
index 0b15f43e..9af0c4b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,6 +24,7 @@ ltversion.m4
lt~obsolete.m4
pm_to_blib
stamp-h1
+MYMETA.*
docs/help/Makefile.am
docs/help/[a-z]*
@@ -31,6 +32,7 @@ docs/help/[a-z]*
docs/help/in/Makefile.am
src/fe-text/irssi
+src/fe-fuzz/irssi-fuzz
src/fe-common/irc/irc-modules.c
src/irc/irc.c
@@ -45,6 +47,9 @@ src/perl/ui/*.c
src/perl/*/MYMETA.*
src/perl/*/Makefile.old
+src/fe-fuzz/crash-*
+src/fe-fuzz/oom-*
+
*.a
*.bs
*.la
diff --git a/.travis.yml b/.travis.yml
index 04c19d88..804dc58c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,27 +1,46 @@
-language: c
-compiler:
- - gcc
- - clang
+sudo: false
+language: perl
+perl:
+ - "5.20-shrplib"
+ - "5.18-shrplib"
+ - "system-perl"
+env:
+ - CC=clang
+ - CC=gcc
+
+addons:
+ apt:
+ packages:
+ - libperl-dev
+ - elinks
before_install:
- - sudo apt-get update -qq
- - sudo apt-get build-dep -qq irssi
- - sudo apt-get install -qq lynx
+ - perl -V
+ - ./autogen.sh --with-proxy --with-bot --with-perl=module
+ - make dist
+ - cd ..
+ - tar xaf */irssi-*.tar.*
+ - cd irssi-*
+
+install:
+ - ./configure --with-proxy --with-bot --with-perl=module --prefix=$HOME/irssi-build
+ - make CFLAGS="-Wall -Werror"
+ - make install
-script:
- - ./autogen.sh --with-proxy --with-bot
- - cat config.log
- - make
- - sudo make install
+before_script:
+ - cd
+ - mkdir irssi-test
+ - echo echo automated irssi launch test > irssi-test/startup;
+ echo ^set settings_autosave off >> irssi-test/startup;
+ echo ^set -clear log_close_string >> irssi-test/startup;
+ echo ^set -clear log_day_changed >> irssi-test/startup;
+ echo ^set -clear log_open_string >> irssi-test/startup;
+ echo ^set log_timestamp '* ' >> irssi-test/startup;
+ echo ^window log on >> irssi-test/startup
+ - echo load perl >> irssi-test/startup
+ - echo load proxy >> irssi-test/startup
+ - echo ^quit >> irssi-test/startup
+ - irssi-build/bin/irssi --home irssi-test
+ - cat irc.log.*
-notifications:
- irc:
- channels:
- - "irc.freenode.net#irssi"
- template:
- - "%{repository} (%{commit}: %{author}): %{message}"
- - "Build details : %{build_url}"
- on_success: always
- on_failure: always
- use_notice: true
- skip_join: true
+script: true
diff --git a/AUTHORS b/AUTHORS
index d2cb58c0..c1280552 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -11,6 +11,9 @@ Irssi staff (current maintainers) <staff@irssi.org>:
Jilles Tjoelker (jilles)
Alexander Færøy (ahf)
Jase Thew (bazerka)
+ dequis (dx)
+ Ailin Nemui (Nei)
+ Giuseppe (TheLemonMan, lemonboy)
Former developers:
@@ -74,10 +77,25 @@ Other patches (grep for "patch" in ChangeLog) by:
Jon Mayo
Johan Kiviniemi
JasonX
- Lukas Mai
+ Lukas Mai (mauke)
Ismael Luceno
Thomas Karpiniec
Svante Kvarnström
- Ailin Nemui (Nei)
Tom Feist (shabble)
- Sebastian Thorarensen
+ Sebastian Thorarensen (Sebban)
+ Hans Nielsen
+ Jari Matilainen (vague)
+ Thibault B (isundil)
+ kyak
+ Vesa Pirila (Lohhari)
+ Haw Loeung
+ François Revol (mmuman)
+ blha303
+ Guillaume Brogi (guiniol)
+ Adam-
+ Robert C Jensen
+ Paul Johnson
+ KindOne
+ Fabian Kurz
+ Todd Pratt
+ xavierog
diff --git a/INSTALL b/INSTALL
index 719369de..629a75c6 100644
--- a/INSTALL
+++ b/INSTALL
@@ -8,10 +8,12 @@ To compile irssi you need:
- pkg-config
- openssl (for ssl support)
- perl-5.6 or greater (for perl support)
+- terminfo or ncurses (for text frontend)
For most people, this should work just fine:
- ./configure
+ ./autogen.sh (for people who just cloned the repository)
+ ./configure (if this script already exists, skip ./autogen.sh)
make
su
make install (not _really_ required except for perl support)
@@ -28,10 +30,6 @@ configure options
Build the irssi proxy (see startup-HOWTO).
- --disable-ipv6
-
- Disable IPv6 support.
-
--disable-ssl
Disable SSL support.
@@ -62,17 +60,11 @@ configure options
Build without text frontend
-If ncurses is installed in a non-standard path you can specify it with
---with-ncurses=/path. If anything else is in non-standard path, you can just
-give the paths in CPPFLAGS and LIBS environment variable, eg.:
+If anything is in non-standard path, you can just give the paths in
+CPPFLAGS and LIBS environment variable, eg.:
CPPFLAGS=-I/opt/openssl/include LDFLAGS=-L/opt/openssl/lib ./configure
-Irssi doesn't really need curses anymore, by default it uses
-terminfo/termcap directly. The functions for using terminfo/termcap
-however are usually only in curses library, some systems use libtermcap
-as well. If you want to use only curses calls for some reason, use
---without-terminfo.
Perl problems
@@ -94,6 +86,11 @@ things that can go wrong:
- If configure complains that it doesn't find some perl stuff, you're
probably missing libperl.so or libperl.a. In debian, you'll need to do
apt-get install libperl-dev
+ - For unprivileged home directory installations, you probably do not want
+ to specify --with-perl-lib=(site|vendor). Instead, you can use the
+ default perl installation target (below the irssi prefix). If you are
+ using local::lib you can also choose to install there by specifying
+ --with-perl-lib=$PERL_LOCAL_LIB_ROOT/lib/perl5
You can verify that the perl module is loaded and working with "/LOAD"
command. It should print something like:
diff --git a/Makefile.am b/Makefile.am
index 4cebd112..75d502f1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,6 +2,7 @@ ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
# create default-config.h
BUILT_SOURCES = default-config.h default-theme.h irssi-version.h
+CLEANFILES = default-config.h default-theme.h
@MAINTAINER_MODE_TRUE@.PHONY: irssi-version.h
@@ -12,7 +13,8 @@ default-theme.h: $(srcdir)/default.theme
$(srcdir)/file2header.sh $(srcdir)/default.theme default_theme > default-theme.h
irssi-version.h:
- $(srcdir)/irssi-version.sh $(srcdir) | cmp -s - $@ || $(srcdir)/irssi-version.sh $(srcdir) >$@
+ VERSION="$(VERSION)" $(srcdir)/irssi-version.sh $(srcdir) | \
+ cmp -s - $@ || VERSION="$(VERSION)" $(srcdir)/irssi-version.sh $(srcdir) >$@
SUBDIRS = src docs scripts
diff --git a/NEWS b/NEWS
index 8f9e36a1..267eaa2a 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,193 @@
-v0.8.17-head 2014-XX-XX The Irssi team <staff@irssi.org>
+v1.1-head 2017-xx-xx The Irssi team <staff@irssi.org>
+
+v1.0.0 2017-01-03 The Irssi team <staff@irssi.org>
+ * Removed --disable-ipv6 (#408).
+ * /connect Network now aborts with an error if no servers have been
+ added to that network (#443).
+ * /dcc commands now use quotes around spaces consistently.
+ * bell_beeps was removed (#524, #565).
+ * Switch to GRegex instead of regex.h (#412).
+ + irssiproxy can now forward all tags through a single
+ port. By Lukas Mai (mauke, #425).
+ + irssiproxy can also listen on unix sockets. By Lukas Mai (#427).
+ + send channel -botcmds immediately when no mask is specified (#175, #399).
+ + the kill buffer now remembers consecutive kills.
+ New bindings were added: yank_next_cutbuffer and append_next_kill
+ By Todd A. Pratt (#353, #414, #455)
+ + connections will avoid looking up IPv6 addresses if the machine does
+ not have an IPv6 address assigned (exact behaviour is implementation
+ defined, #410).
+ + Fix potential crash if scripts insert undef values into the completion
+ list (#413).
+ + Paste warning is now also shown on pasting overlong
+ lines. By Manish Goregaokar (#426).
+ + autolog_ignore_targets and activity_hide_targets learn a new syntax
+ tag/* and * to ignore whole networks or everything.
+ By Jari Matilainen (vague666, #437)
+ + /hilight got a -matchcase flag to hilight case
+ sensitively. By Thibault B (isundil, #421, #476).
+ + Always build irssi with TLS support.
+ + Rename SSL to TLS in the code and add -tls_* versions of the -ssl_*
+ options to /CONNECT and /SERVER, but make sure the -ssl_* options continue
+ to work.
+ + Use TLS for Freenode, EFnet, EsperNet, OFTC, Rizon, and IRC6 in the default
+ configuration.
+ + Display TLS connection information upon connect. You can disable this by
+ setting tls_verbose_connect to FALSE.
+ + Add -tls_pinned_cert and -tls_pinned_pubkey for x509 and public key pinning.
+
+ The values needed for -tls_pinned_cert and -tls_pinned_pubkey is shown
+ when connecting to a TLS enabled IRC server, but you can also find the
+ values like this: Start by downloading the certificate from a given IRC
+ server:
+
+ $ openssl s_client -connect chat.freenode.net:6697 < /dev/null 2>/dev/null | \
+ openssl x509 > freenode.cert
+
+ Find the value for -tls_pinned_cert:
+
+ $ openssl x509 -in freenode.cert -fingerprint -sha256 -noout
+
+ Find the value for -tls_pinned_pubkey:
+
+ $ openssl x509 -in freenode.cert -pubkey -noout | \
+ openssl pkey -pubin -outform der | \
+ openssl dgst -sha256 -c | \
+ tr a-z A-Z
+
+ + Remove support for DANE validation of TLS certificates.
+
+ There wasn't enough support in the IRC community to push for this on the
+ majority of bigger IRC networks. If you believe this should be
+ reintroduced into irssi, then please come up with an implementation that
+ does not rely on the libval library. It is causing a lot of troubles for
+ our downstream maintainers.
+
+ + /names and $[...] now uses utf8 string operations. By Xavier
+ G. (#40, #411, #471, #480).
+ + New setting completion_nicks_match_case (#488).
+ + /channel /server /network now support modify subcommand. By
+ Jari Matilainen (#338, #498).
+ + Irssi::signal_remove now works with coderefs. By Tom Feist (shabble, #512).
+ + /script reset got an -autorun switch (#540, #538).
+ + cap_toggle can now be called from Perl, and fields
+ cap_active and cap_supported can be inspected (#542).
+ + Make it possible to disable empty line completion. By Lauri
+ Tirkkonen (lotheac, #574).
+ + New option sasl_disconnect_on_failure to disconnect when
+ SASL log-in failed (#514).
+ - IP addresses are no longer stored when resolve_reverse_lookup is
+ used.
+ - Removed broken support for curses (#521).
+ - Removed broken dummy mode (#526).
+ - Fix terminal state after suspend (#450, #452).
+ - Improve Perl library path detection (#479, #132).
+ - Reconnect now works on unix connections (#493).
+ - Fix completion warnings (#125, #496, FS#124).
+ - Fix a crash in the --more-- item (#501).
+ - Fix a display issue in /unignore (#517, bdo#577202).
+ - Fix a crash in some netsplits (#529, #500).
+ - Fix crashes with some invalid config (#550, #551, #563, #564, #587, #581, #570).
+ - Add support for SASL Fragmentation. By Kenny Root (kruton, #506).
+ - Improve netsplit dumping (#420, #465).
+ - Improve responsibility under DCC I/O strain (#578, #159).
+ - Fix query nick change on open (#580, #586).
+ - Correct a few help texts.
+
+v0.8.21 2017-01-03 The Irssi team <staff@irssi.org>
+ - Correct a NULL pointer dereference in the nickcmp function found by
+ Joseph Bisch (GL#1)
+ - Correct an out of bounds read in certain incomplete control codes
+ found by Joseph Bisch (GL#2)
+ - Correct an out of bounds read in certain incomplete character
+ sequences found by Hanno Böck and independently by J. Bisch (GL#3)
+ - Correct an error when receiving invalid nick message (GL#4, #466)
+
+v0.8.20 2016-09-16 The Irssi team <staff@irssi.org>
+ - Correct the name of an emitted sasl signal (#484)
+ - Correct the prototype for the 'message private' signal (#515)
+ - Corrections in away and hilight help text (#477, #518)
+ - /squery and /servlist commands have been restored (#461).
+ - Where Irssi would previously only report "System error" on connect,
+ it will now try harder to retrieve the system error message (#467).
+ - Fixed issue with +channels not working properly (#533)
+ - Fixed crash in optchan when item has no server (#485, bdo#826525)
+ - Fixed random remote crash in the nicklist handling (#529)
+ - Fixed remote crash due to incorrect bounds checking on
+ formats, reported by Gabriel Campana and Adrien Guinet from
+ Quarkslab.
+
+v0.8.19 2016-03-23 The Irssi team <staff@irssi.org>
+ - Fixed regression when joining and parting channels on IRCnet (#435)
+ - Fixed SASL EXTERNAL. By Mantas MikulÄ—nas (grawity, #432)
+ - Fixed regression when not using SASL (#438)
+ - Fixed incorrect SSL disconnects when using SSL from modules/scripts.
+ By Will Storey (horgh, #439)
+ - Fixed regression where proxy_string could not be configured or
+ certain file transfers could not be accepted (#445, #446)
+ - Fixed storing layout of !channels (#183, #405)
+ - Fixed restoration of bracketed paste mode on quit (#449, #457)
+ - Make the usage of meta-O for cursor keys configurable with
+ /set term_appkey_mode off
+ (#430, #459)
+
+v0.8.18 2016-02-13 The Irssi team <staff@irssi.org>
+ * Modules will now require to define a
+ void MODULENAME ## _abicheck(int *version)
+ method to ensure that they are compiled against the correct Irssi
+ version.
+ * The signature of "message private" has been changed to
+ 5: server, message, nick, address, target
+ in order to support "self messages". Module authors should
+ implement this change if they are using this signal.
+ * Removing networks will now remove all attached servers and channels
+ (#45).
+ * The proxy module now has an /irssiproxy command.
+ * sb_search has been moved to scripts.irssi.org
+ * WIN32 has been completely removed (it had not been working and is
+ lacking a maintainer.)
+ * Garbage Collection support has been removed. This will hardly have any
+ effect for anyone given that it has been unsupported for several years.
+ + CAP SASL PLAIN login is now supported natively.
+ + Paste bracket markers can be requested from terminal with
+ /set paste_use_bracketed_mode on
+ + "Self messages" generated by some bouncers can now be received in the
+ proper window.
+ + Try to split long lines on spaces to avoid words being splitted. Adds
+ a new option: 'split_line_on_space' which defaults to on.
+ + Add setting hilight_nick_matches_everywhere (#56).
+ + The config parser is more robust and prints out better diagnostics on
+ incorrect config files.
+ + Ctrl+^ (FS#721) and Ctrl+J can now be bound.
+ + Command history can be cleared with /window history -clear
+ + /hilight -mask -line is now supported (FS#275).
+ + CHANTYPES are now supported.
+ + Improved reload speed of ignores.
+ + Add -date feature to /lastlog
+ + irssiproxy can be more easily enabled and disabled.
+ + Expando for hostname (FS#829).
+ + UNIX sockets can now also be specified in the config file.
+ + Disable SSLv3 due to the POODLE vulnerability.
+ + SSL ciphers can now be specified per server.
+ + Added SNI support for SSL.
+ - /ignore now respects -pattern on merge (#78).
+ - irssiproxy (BNC) module now uses correct line endings.
+ - Fix missing lines on large pastes (FS#905).
+ - Correctly preserve STATUSMSG prefixes (#291).
+ - Fix infinite recursion in key bindings (FS#817).
+ - Fix incomplete awaylog caused by buffering.
+ - Fix calculation of UTF-8 string length display in some cases.
+ - Fix some Perl warnings related to @ISA.
+ - EXEC windowitems now get proper references on the Perl side.
+ - Incremental help file improvements.
+ - ANSI attributes are now properly reset.
+ - Fixed regression where text would blink when terminal lacks color
+ support.
+ - Permit the usage of Freenode extban syntax in /ban (#150)
+ - Fixed regression in scriptassist on unload of scripts.
+ - Fixed regression in -actcolor %n
+
+v0.8.17 2014-10-11 The Irssi team <staff@irssi.org>
+ Document that SSL connections aren't properly handled during /UPGRADE. See Github PR #39.
+ Synchronize scripts with scripts.irssi.org.
+ Performance enhancement of the nicklist as well as the window_item_find function. See Github PR #24.
@@ -19,6 +208,7 @@ v0.8.17-head 2014-XX-XX The Irssi team <staff@irssi.org>
- Fixed gui_printtext_after and term_refresh_*() visibility. See Github PR #22.
- Fixed issue where UTF-8 characters was corrupted once for every 32k text. See Github PR #12.
- Fixed redrawing issue with right-aligned statusbar.
+ - Fixed use-after-free bug with cached settings values. See Github PR #147.
v0.8.16 2014-05-28 The Irssi team <staff@irssi.org>
+ Add -noautosendcmd to /SERVER and /CONNECT. Passing this option will
@@ -364,7 +554,7 @@ v0.8.10 2005-12-11 The Irssi team <staff@irssi.org>
<charset> can be almost everything listed by 'iconv -l'
+ Added transpose_words, capitalize_word, downcase_word, upcase_word
key bindings
- + Avoid accidentaly /VER in a channel, by requiring parameter
+ + Avoid accidentally /VER in a channel, by requiring parameter
- Pasted lines weren't added to command history. Some other paste
detection fixes
- Fixed /BIND escape_char
@@ -1901,7 +2091,7 @@ v0.7.14 1999-08-22 Timo Sirainen <cras@irccrew.org> [unstable]
- /msg <tab> completion was a bit buggy, if someone sent you multiple
messages, you had to press tab multiple times until the nick changed
to someone else..
- - Defaut format for signon time in whois displayed nick instead of
+ - Default format for signon time in whois displayed nick instead of
the signon time..
- Disconnecting server while it was still trying to connect hung irssi
- If old configuration file wasn't found, irssi (could have?) crashed
@@ -2185,7 +2375,7 @@ v0.7.9 1999-04-22 Timo Sirainen <a@sicom.fi> [unstable]
that matched the parameters
- Commented out all GUI_INPUT_EXCEPTIONs .. I don't even know when
exceptions are sent and why (I thought that only when some error
- occured..), Linux doesn't seem to send them ever? IRIX however sends
+ occurred..), Linux doesn't seem to send them ever? IRIX however sends
them all the time which made irssi eating all cpu.
- Fixed compiling gui-text with systems that had only slang/slang.h
- gui_widget_depends() had some bugs
@@ -2631,7 +2821,7 @@ v0.3.2 1999-01-22 Timo Sirainen <a@sicom.fi> [unstable]
v0.3.1 1999-01-22 Timo Sirainen <a@sicom.fi>
- * 4 days since last relase. too long :) I'm now starting to create
+ * 4 days since last release. too long :) I'm now starting to create
"unstable" versions of irssi. They have the latest and greatest
features while they might not build/work too well. Check
http://www.sicom.fi/~ikioma/irssi-download.html, new versions will
diff --git a/acconfig.h b/acconfig.h
deleted file mode 100644
index cfe62381..00000000
--- a/acconfig.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* misc.. */
-#undef HAVE_IPV6
-#undef HAVE_SOCKS_H
-#undef HAVE_STATIC_PERL
-#undef HAVE_GMODULE
-#undef HAVE_GC_H
-#undef HAVE_GC_GC_H
-#undef USE_GC
-
-/* macros/curses checks */
-#undef HAS_CURSES
-#undef USE_SUNOS_CURSES
-#undef USE_BSD_CURSES
-#undef USE_SYSV_CURSES
-#undef USE_NCURSES
-#undef NO_COLOR_CURSES
-#undef SCO_FLAVOR
-
-/* our own curses checks */
-#undef HAVE_NCURSES_USE_DEFAULT_COLORS
-#undef HAVE_CURSES_IDCOK
-#undef HAVE_CURSES_RESIZETERM
-#undef HAVE_CURSES_WRESIZE
-
-/* terminfo/termcap */
-#undef HAVE_TERMINFO
-
-/* What type should be used for uoff_t */
-#undef UOFF_T_INT
-#undef UOFF_T_LONG
-#undef UOFF_T_LONG_LONG
-
-/* printf()-format for uoff_t, eg. "u" or "lu" or "llu" */
-#undef PRIuUOFF_T
diff --git a/autogen.sh b/autogen.sh
index cf4b9798..946f1ebb 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -18,6 +18,10 @@ perl syntax.pl
echo "Creating ChangeLog..."
git log > $srcdir/ChangeLog
+if test "$?" -ne 0; then
+ echo "**Error**: ${PKG_NAME} Autogen must be run in a git clone, cannot proceed."
+ exit 1
+fi
files=`echo docs/help/in/*.in|sed -e 's,docs/help/in/Makefile.in ,,' -e 's,docs/help/in/,!,g' -e 's/\.in /.in ?/g'`
cat docs/help/in/Makefile.am.gen|sed "s/@HELPFILES@/$files/g"|sed 's/?/\\?/g'|tr '!?' '\t\n' > docs/help/in/Makefile.am
@@ -28,9 +32,11 @@ cat docs/help/Makefile.am.gen|sed "s/@HELPFILES@/$files/g"|sed 's/?/\\?/g'|tr '!
# .html -> .txt with lynx or elinks
echo "Documentation: html -> txt..."
if type lynx >/dev/null 2>&1 ; then
- lynx -dump -nolist docs/faq.html|perl -pe 's/^ *//; if ($_ eq "\n" && $state eq "Q") { $_ = ""; } elsif (/^([QA]):/) { $state = $1 } elsif ($_ ne "\n") { $_ = " $_"; };' > docs/faq.txt
+ LC_ALL=en_IE.utf8 lynx -dump docs/faq.html|perl -pe 's/^ *//; if ($_ eq "\n" && $state eq "Q") { $_ = ""; } elsif (/^([QA]):/) { $state = $1 } elsif ($_ ne "\n") { $_ = " $_"; };' > docs/faq.txt
elif type elinks >/dev/null 2>&1 ; then
elinks -dump docs/faq.html|perl -pe 's/^ *//; if ($_ eq "\n" && $state eq "Q") { $_ = ""; } elsif (/^([QA]):/) { $state = $1 } elsif ($_ ne "\n") { $_ = " $_"; };' > docs/faq.txt
+elif type links >/dev/null 2>&1 ; then
+ links -dump docs/faq.html|perl -pe 's/^ *//; if ($_ eq "\n" && $state eq "Q") { $_ = ""; } elsif (/^([QA]):/) { $state = $1 } elsif ($_ ne "\n") { $_ = " $_"; };' > docs/faq.txt
else
echo "**Error**: No lynx or elinks present"
exit 1
diff --git a/configure.ac b/configure.ac
index 2a7bee0d..8d1ba706 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,4 +1,4 @@
-AC_INIT(irssi, 0.8.17-head)
+AC_INIT(irssi, 1.1-head)
AC_CONFIG_SRCDIR([src])
AC_CONFIG_AUX_DIR(build-aux)
AC_PREREQ(2.50)
@@ -7,9 +7,10 @@ AC_CONFIG_HEADERS([irssi-config.h])
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([1.9 no-define foreign])
+AM_SILENT_RULES([yes])
+
AM_MAINTAINER_MODE
-AC_ISC_POSIX
AC_PROG_CC
AC_PROG_CPP
AM_PROG_LIBTOOL
@@ -20,7 +21,7 @@ AC_PATH_PROG(perlpath, perl)
AC_CHECK_HEADERS(unistd.h dirent.h sys/ioctl.h sys/resource.h)
# check posix headers..
-AC_CHECK_HEADERS(sys/time.h sys/utsname.h regex.h)
+AC_CHECK_HEADERS(sys/socket.h sys/time.h sys/utsname.h)
AC_SYS_LARGEFILE
@@ -51,6 +52,19 @@ AC_ARG_WITH(bot,
fi,
want_irssibot=no)
+AC_ARG_WITH(fuzzer,
+[ --with-fuzzer Build irssi-fuzzer],
+ if test x$withval = xno; then
+ want_irssifuzzer=no
+ else
+ want_irssifuzzer=yes
+ fi,
+ want_irssifuzzer=no)
+
+AC_ARG_WITH(fuzzer-lib,
+[ --with-fuzzer-lib Specify path to fuzzer library],
+ fuzzerlibpath="$withval")
+
AC_ARG_WITH(proxy,
[ --with-proxy Build irssi-proxy],
if test x$withval = xno; then
@@ -60,15 +74,6 @@ AC_ARG_WITH(proxy,
fi,
want_irssiproxy=no)
-AC_ARG_WITH(terminfo,
-[ --without-terminfo Use curses backend instead of terminfo],
- if test x$withval = xno; then
- want_terminfo=no
- else
- want_terminfo=yes
- fi,
- want_terminfo=yes)
-
AC_ARG_WITH(modules,
[ --with-modules Specify what modules to build in binary],
if test x$withval != xyes -a x$withval != xno; then
@@ -78,21 +83,11 @@ AC_ARG_WITH(modules,
if test "x$prefix" != "xNONE"; then
prefix=`eval echo $prefix`
PERL_MM_PARAMS="INSTALLDIRS=perl INSTALL_BASE=$prefix"
- perl_library_dir="PERL_USE_LIB"
perl_set_use_lib=yes
perl_prefix_note=yes
fi
-AC_ARG_WITH(gc,
-[ --with-gc Use garbage collector],
- if test x$withval = xno; then
- want_gc=no
- else
- want_gc=yes
- fi,
- want_gc=no)
-
AC_ARG_WITH(perl-staticlib,
[ --with-perl-staticlib Specify that we want to link perl libraries
statically in irssi, default is no],
@@ -105,30 +100,35 @@ AC_ARG_WITH(perl-staticlib,
AC_ARG_WITH(perl-lib,
-[ --with-perl-lib=[site|vendor|DIR] Specify where to install the
+[ --with-perl-lib=[perl|site|vendor|DIR] Specify where to install the
Perl libraries for irssi, default is site],
if test "x$withval" = xyes; then
want_perl=yes
elif test "x$withval" = xno; then
want_perl=no
+ elif test "x$withval" = xperl; then
+ want_perl=yes
+ perl_prefix_note=no
+ PERL_MM_PARAMS="INSTALLDIRS=perl"
+ perl_set_use_lib=no
elif test "x$withval" = xsite; then
want_perl=yes
perl_prefix_note=no
PERL_MM_PARAMS=""
+ perl_set_use_lib=no
elif test "x$withval" = xvendor; then
want_perl=yes
perl_prefix_note=no
if test -z "`$perlpath -v|grep '5\.0'`"; then
PERL_MM_PARAMS="INSTALLDIRS=vendor"
else
- PERL_MM_PARAMS="INSTALLDIRS=perl PREFIX=`perl -e 'use Config; print $Config{prefix}'`"
+ PERL_MM_PARAMS="INSTALLDIRS=perl PREFIX=`$perlpath -e 'use Config; print $Config{prefix}'`"
fi
- perl_library_dir="(vendor default - `$perlpath -e 'use Config; print $Config{archlib}'`)"
+ perl_set_use_lib=no
else
want_perl=yes
perl_prefix_note=no
PERL_MM_PARAMS="INSTALLDIRS=perl LIB=$withval"
- perl_library_dir="PERL_USE_LIB"
perl_set_use_lib=yes
fi,
want_perl=yes)
@@ -148,24 +148,6 @@ AC_ARG_WITH(perl,
fi,
want_perl=static)
-AC_ARG_ENABLE(ipv6,
-[ --disable-ipv6 Disable IPv6 support],
- if test x$enableval = xno; then
- want_ipv6=no
- else
- want_ipv6=yes
- fi,
- want_ipv6=yes)
-
-AC_ARG_ENABLE(dane,
-[ --enable-dane Enable DANE support],
- if test x$enableval = xno ; then
- want_dane=no
- else
- want_dane=yes
- fi,
- want_dane=no)
-
AC_ARG_ENABLE(true-color,
[ --enable-true-color Build with true color support in terminal],
if test x$enableval = xno ; then
@@ -175,13 +157,14 @@ AC_ARG_ENABLE(true-color,
fi,
want_truecolor=no)
-dnl **
-dnl ** SSL Library checks (OpenSSL)
-dnl **
-
-AC_ARG_ENABLE(ssl,
-[ --disable-ssl Disable Secure Sockets Layer support],,
- enable_ssl=yes)
+AC_ARG_ENABLE(gregex,
+[ --disable-gregex Build without GRegex (fall back to regex.h)],
+ if test x$enableval = xno ; then
+ want_gregex=no
+ else
+ want_gregex=yes
+ fi,
+ want_gregex=yes)
dnl **
dnl ** just some generic stuff...
@@ -197,38 +180,22 @@ case "$host_os" in
esac
-AC_CHECK_FUNC(socket, [], [
- AC_CHECK_LIB(socket, socket, [
- LIBS="$LIBS -lsocket"
- ])
-])
+AC_SEARCH_LIBS([socket], [network socket])
-AC_CHECK_FUNC(inet_addr, [], [
- AC_CHECK_LIB(nsl, inet_addr, [
- LIBS="$LIBS -lnsl"
- ])
-])
+AC_SEARCH_LIBS([inet_addr], [nsl])
dnl * gcc specific options
if test "x$ac_cv_prog_gcc" = "xyes"; then
CFLAGS="$CFLAGS -Wall"
fi
-dnl * socklen_t - AC_CHECK_TYPE() would be _really_ useful if it only would
-dnl * accept header files where to find the typedef..
-AC_MSG_CHECKING([for socklen_t])
-AC_CACHE_VAL(irssi_cv_type_socklen_t,
-[AC_TRY_COMPILE([
-#include <sys/types.h>
-#include <sys/socket.h>],
-[socklen_t t = 0; return((int)t); ],
-irssi_cv_type_socklen_t=yes,
-irssi_cv_type_socklen_t=no,
-)])
-if test $irssi_cv_type_socklen_t = no; then
-AC_DEFINE(socklen_t, int, Define to 'int' if <sys/socket.h> doesn't define.)
-fi
-AC_MSG_RESULT($irssi_cv_type_socklen_t)
+AC_CHECK_TYPE(socklen_t, ,
+ [AC_DEFINE([socklen_t], [int], [Define to 'int' if <sys/socket.h> doesn't define.])], [
+AC_INCLUDES_DEFAULT
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+])
AC_CHECK_SIZEOF(int)
AC_CHECK_SIZEOF(long)
@@ -277,6 +244,11 @@ if test "x$want_socks" = "xyes"; then
fi
dnl **
+dnl ** OpenSSL checks
+dnl **
+AC_CHECK_LIB([ssl], [SSL_library_init])
+
+dnl **
dnl ** fe-text checks
dnl **
@@ -287,7 +259,7 @@ for try in 1 2; do
echo "*** trying without -lgmodule"
glib_modules=
fi
- AM_PATH_GLIB_2_0(2.6.0,,, $glib_modules)
+ AM_PATH_GLIB_2_0(2.28.0,,, $glib_modules)
if test "$GLIB_LIBS"; then
if test $glib_modules = gmodule; then
AC_DEFINE(HAVE_GMODULE)
@@ -317,92 +289,48 @@ if test -z "$GLIB_LIBS"; then
AC_ERROR([GLIB is required to build irssi.])
fi
-LIBS="$LIBS $GLIB_LIBS"
-
-have_openssl=no
-if test "$enable_ssl" = "yes"; then
- PKG_CHECK_MODULES(SSL, openssl, :, :)
- if test "$SSL_LIBS"; then
- CFLAGS="$CFLAGS $SSL_CFLAGS"
- have_openssl=yes
- else
- AC_CHECK_LIB(ssl, SSL_read, [
- AC_CHECK_HEADERS(openssl/ssl.h openssl/err.h, [
- SSL_LIBS="-lssl -lcrypto"
- have_openssl=yes
- ])
- ],, -lcrypto)
- fi
- if test "$have_openssl" = "yes"; then
- AC_DEFINE(HAVE_OPENSSL,, Build with OpenSSL support)
- LIBS="$LIBS $SSL_LIBS"
- fi
-fi
+LIBS="$LIBS $GLIB_LIBS -lssl -lcrypto"
dnl **
-dnl ** Garbage Collector
+dnl ** curses checks
dnl **
-have_gc=no
-if test "x$want_gc" = xyes; then
- AC_CHECK_LIB(gc, GC_malloc, [
- AC_CHECK_HEADER(gc/gc.h, [
- AC_DEFINE(HAVE_GC_GC_H)
- AC_DEFINE(USE_GC)
- LIBS="$LIBS -lgc"
- have_gc=yes
- ], [
- AC_CHECK_HEADER(gc.h, [
- AC_DEFINE(HAVE_GC_H)
- AC_DEFINE(USE_GC)
- LIBS="$LIBS -lgc"
- have_gc=yes
- ])
- ])
- ])
+
+if test "x$want_textui" != "xno"; then
+
+ TEXTUI_NO_LIBS="$LIBS"
+ LIBS=
+ AC_SEARCH_LIBS([setupterm], [tinfo ncursesw ncurses], [want_textui=yes], [
+ AC_ERROR(Terminfo not found - install libncurses-dev or ncurses-devel package)
+ want_textui="no, Terminfo not found"
+ ])
+
+ TEXTUI_LIBS="$LIBS"
+ AC_SUBST(TEXTUI_LIBS)
+ LIBS="$TEXTUI_NO_LIBS"
+
fi
dnl **
-dnl ** curses checks
+dnl ** irssifuzzer checks
dnl **
-if test "x$want_textui" = "xyes"; then
- AC_CHECK_CURSES
-
- TEXTUI_LIBS="$CURSES_LIBS"
- if test "x$has_curses" = "xtrue"; then
- old_libs=$LIBS
- LIBS="$LIBS $CURSES_LIBS"
- if test $want_terminfo = no; then
- AC_CHECK_FUNC(use_default_colors, AC_DEFINE(HAVE_NCURSES_USE_DEFAULT_COLORS))
- AC_CHECK_FUNC(idcok, AC_DEFINE(HAVE_CURSES_IDCOK))
- AC_CHECK_FUNC(resizeterm, AC_DEFINE(HAVE_CURSES_RESIZETERM))
- AC_CHECK_FUNC(wresize, AC_DEFINE(HAVE_CURSES_WRESIZE))
- fi
- AC_CHECK_FUNC(setupterm,, [
- want_termcap=yes
- ])
- LIBS=$old_libs
- else
- AC_CHECK_LIB(tinfo, setupterm, [
- TEXTUI_LIBS="-ltinfo"
- want_terminfo=yes
- ], AC_CHECK_LIB(termlib, tgetent, [
- TEXTUI_LIBS="-ltermlib"
- want_termcap=yes
- ], AC_CHECK_LIB(termcap, tgetent, [
- TEXTUI_LIBS="-ltermcap"
- want_termcap=yes
- ], [
- AC_ERROR(Terminfo/termcap not found - install libncurses-dev or ncurses-devel package)
- want_textui=no
- ])))
+if test "$want_irssifuzzer" != "no"; then
+ dnl * we need to build with -fsanitize-coverage=trace-pc-guard
+ dnl * otherwise fuzzer won't be very successful at finding bugs :)
+ if test -z "$SANFLAGS"; then
+ SANFLAGS="-g -fsanitize=address -fsanitize-coverage=trace-pc-guard"
fi
- AC_SUBST(TEXTUI_LIBS)
+ CFLAGS="$CFLAGS $SANFLAGS"
+ CXXFLAGS="$CXXFLAGS $SANFLAGS"
+
+ AC_MSG_CHECKING(for fuzzer library)
- if test "x$want_termcap" = "xyes"; then
- AC_CHECK_FUNC(tparm,, need_tparm=yes)
+ if test -z "$fuzzerlibpath"; then
+ AC_MSG_RESULT([not found, building without fuzzer front end])
+ want_irssifuzzer=no
else
- AC_DEFINE(HAVE_TERMINFO)
+ FUZZER_LIBS="$fuzzerlibpath"
+ AC_SUBST(FUZZER_LIBS)
fi
fi
@@ -426,7 +354,7 @@ if test "$want_perl" != "no"; then
dnl * complain about them. Normally there's only few options
dnl * that we want to keep:
dnl * -Ddefine -Uundef -I/path -fopt -mopt
- PERL_CFLAGS=`echo $PERL_CFLAGS | $perlpath -pe 's/^(.* )?-[^DUIfm][^ ]+/\1/g; s/^(.* )?\+[^ ]+/\1/g'`
+ PERL_CFLAGS=`echo $PERL_CFLAGS | $perlpath -pe 's/^(.* )?-@<:@^DUIfm@:>@@<:@^ @:>@+/\1/g; s/^(.* )?\+@<:@^ @:>@+/\1/g'`
PERL_EXTRA_OPTS="CCCDLFLAGS=\"-fPIC\""
AC_SUBST(PERL_EXTRA_OPTS)
@@ -510,13 +438,37 @@ if test "$want_perl" != "no"; then
PERL_STATIC_LIBS=0
fi
+ # remove any prefix from PERL_MM_OPT
+ PERL_MM_OPT=`perl -MText::ParseWords -e 'sub qu{$_=shift;s{^(.*?)=(.*)$}{($a,$b)=($1,$2);$b=~s/"/\\\\"/g;qq{$a="$b"}}ge if /@<:@\s"@:>@/;$_} local $,=" "; print map qu($_), grep !/^(INSTALL_BASE|PREFIX)=/, shellwords(@ARGV)' "$PERL_MM_OPT"`
# figure out the correct @INC path - we'll need to do this
# through MakeMaker since it's difficult to get it right
# otherwise.
+ $perlpath -MExtUtils::MakeMaker -e 'WriteMakefile(NAME => "test", MAKEFILE => "Makefile.test", FIRST_MAKEFILE => "/dev/null", NO_META => 1, NO_MYMETA => 1);' $PERL_MM_PARAMS >/dev/null
+ echo 'show-INSTALLDIRS:' >> Makefile.test
+ echo ' @echo $(INSTALLDIRS)' >> Makefile.test
+ perl_INSTALLDIRS=`$am_make -s -f Makefile.test show-INSTALLDIRS`
+ if test "x$perl_INSTALLDIRS" = "xsite"; then
+ perl_library_dir="site default"
+ perl_INSTALL_VAR=INSTALLSITEARCH
+ elif test "x$perl_INSTALLDIRS" = "xvendor"; then
+ perl_library_dir="vendor default"
+ perl_INSTALL_VAR=INSTALLVENDORARCH
+ else
+ perl_library_dir="module default"
+ perl_INSTALL_VAR=INSTALLARCHLIB
+ fi
+ echo 'show-ARCHLIB:' >> Makefile.test
+ echo ' @echo $('"$perl_INSTALL_VAR"')' >> Makefile.test
+ perl_use_lib=`$am_make -s -f Makefile.test show-ARCHLIB`
+ rm -f Makefile.test
if test "x$perl_set_use_lib" = "xyes"; then
- perl -e 'use ExtUtils::MakeMaker; WriteMakefile("NAME" => "test", "MAKEFILE" => "Makefile.test");' $PERL_MM_PARAMS >/dev/null
- PERL_USE_LIB=`perl -e 'open(F, "Makefile.test"); while (<F>) { chomp; if (/^(\w+) = (.*$)/) { $keys{$1} = $2; } }; $key = $keys{INSTALLARCHLIB}; while ($key =~ /\\$\((\w+)\)/) { $value = $keys{$1}; $key =~ s/\\$\($1\)/$value/; }; print $key;'`
- rm -f Makefile.test
+ if $perlpath -e 'exit ! grep $_ eq $ARGV@<:@0@:>@, grep /^\//, @INC' "$perl_use_lib"; then
+ perl_library_dir="other path in @INC"
+ perl_set_use_lib=no
+ else
+ perl_library_dir="prepends to @INC with /set perl_use_lib"
+ PERL_USE_LIB="$perl_use_lib"
+ fi
fi
AC_SUBST(perl_module_lib)
@@ -532,6 +484,7 @@ if test "$want_perl" != "no"; then
AC_SUBST(PERL_CFLAGS)
AC_SUBST(PERL_USE_LIB)
+ AC_SUBST(PERL_MM_OPT)
AC_SUBST(PERL_MM_PARAMS)
AC_SUBST(PERL_STATIC_LIBS)
fi
@@ -540,10 +493,9 @@ fi
dnl ** check what we want to build
AM_CONDITIONAL(BUILD_TEXTUI, test "$want_textui" = "yes")
AM_CONDITIONAL(BUILD_IRSSIBOT, test "$want_irssibot" = "yes")
+AM_CONDITIONAL(BUILD_IRSSIFUZZER, test "$want_irssifuzzer" = "yes")
AM_CONDITIONAL(BUILD_IRSSIPROXY, test "$want_irssiproxy" = "yes")
AM_CONDITIONAL(HAVE_PERL, test "$want_perl" != "no")
-AM_CONDITIONAL(NEED_TPARM, test "$need_tparm" = "yes")
-AM_CONDITIONAL(USE_CURSES, test "$want_terminfo" != "yes" -a "$want_termcap" != "yes")
# move LIBS to PROG_LIBS so they're not tried to be used when linking eg. perl libraries
PROG_LIBS=$LIBS
@@ -623,52 +575,26 @@ COMMON_LIBS="$FE_COMMON_LIBS $COMMON_NOUI_LIBS"
AC_SUBST(COMMON_NOUI_LIBS)
AC_SUBST(COMMON_LIBS)
-dnl **
-dnl ** IPv6 support
-dnl **
-
-have_ipv6=no
-if test "x$want_ipv6" = "xyes"; then
- AC_MSG_CHECKING([for IPv6])
- AC_CACHE_VAL(irssi_cv_type_in6_addr,
- [AC_TRY_COMPILE([
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <netdb.h>
- #include <arpa/inet.h>],
- [struct in6_addr i = in6addr_any; return &i == &i;],
- have_ipv6=yes,
- )])
- if test $have_ipv6 = yes; then
- AC_DEFINE(HAVE_IPV6)
- fi
- AC_MSG_RESULT($have_ipv6)
-fi
-
-have_dane=no
-if test "x$want_dane" = "xyes"; then
- AC_MSG_CHECKING([for DANE])
- AC_CHECK_LIB(val-threads, val_getdaneinfo,
- [
- LIBS="$LIBS -lval-threads -lsres"
- AC_DEFINE([HAVE_DANE], [], [DANE support])
- have_dane=yes
- ], [], [-lssl -lcrypto -lsres -lpthread])
-
- if test x$have_dane = "xyes" ; then
- if test x$have_openssl = "xno" ; then
- AC_ERROR([SSL is required to build Irssi with DANE support enabled.])
- fi
- fi
-fi
-
-if test "x$want_truecolor" = "xyes" -a "x$want_termcap" != "xyes" -a "x$want_terminfo" = "xyes" ; then
+if test "x$want_truecolor" = "xyes"; then
AC_DEFINE([TERM_TRUECOLOR], [], [true color support in terminal])
else
want_truecolor=no
fi
+if test "x$want_gregex" = "xyes"; then
+ AC_DEFINE([USE_GREGEX], [], [use GRegex for regular expressions])
+else
+ want_gregex=no
+fi
+
+AH_TEMPLATE(HAVE_GMODULE)
+AH_TEMPLATE(HAVE_SOCKS_H, [misc..])
+AH_TEMPLATE(HAVE_STATIC_PERL)
+AH_TEMPLATE(PRIuUOFF_T, [printf()-format for uoff_t, eg. "u" or "lu" or "llu"])
+AH_TEMPLATE(UOFF_T_INT, [What type should be used for uoff_t])
+AH_TEMPLATE(UOFF_T_LONG)
+AH_TEMPLATE(UOFF_T_LONG_LONG)
+
AC_CONFIG_FILES([
Makefile
src/Makefile
@@ -684,6 +610,7 @@ src/fe-common/core/Makefile
src/fe-common/irc/Makefile
src/fe-common/irc/dcc/Makefile
src/fe-common/irc/notifylist/Makefile
+src/fe-fuzz/Makefile
src/fe-none/Makefile
src/fe-text/Makefile
src/lib-config/Makefile
@@ -705,6 +632,7 @@ AC_OUTPUT
dnl ** for building from objdir
old_dir=`pwd` && cd $srcdir && whole_dir=`pwd` && cd $old_dir
if test "x$old_dir" != "x$whole_dir"; then
+ $LN_S $srcdir/irssi-version.h irssi-version.h
if test "x$want_perl" != "xno"; then
subdirfiles=""
for i in $whole_dir/src/perl/common $whole_dir/src/perl/irc $whole_dir/src/perl/ui $whole_dir/src/perl/textui; do
@@ -720,16 +648,7 @@ fi
echo
-if test "x$want_textui" = "xno"; then
- text=no
-elif test "x$want_termcap" = "xyes"; then
- text="yes, using termcap"
-elif test "x$want_terminfo" = "xyes"; then
- text="yes, using terminfo"
-else
- text="yes, using curses"
-fi
-echo "Building text frontend ........... : $text"
+echo "Building text frontend ........... : $want_textui"
echo "Building irssi bot ............... : $want_irssibot"
echo "Building irssi proxy ............. : $want_irssiproxy"
if test "x$have_gmodule" = "xyes"; then
@@ -769,13 +688,7 @@ if test "x$want_perl" != "xno" -a "x$perl_mod_error" != "x"; then
fi
if test "x$want_perl" != "xno"; then
- if test "$perl_library_dir" = "PERL_USE_LIB"; then
- perl_library_dir=$PERL_USE_LIB
- fi
- if test -z "$perl_library_dir"; then
- perl_library_dir="(site default - `$perlpath -e 'use Config; print $Config{sitearch}'`)"
- fi
- echo "Perl library directory ........... : $perl_library_dir"
+ echo "Perl library directory ........... : ($perl_library_dir - $perl_use_lib)"
if test "x$perl_prefix_note" = "xyes"; then
echo " - NOTE: This was automatically set to the same directory you gave with"
echo " --prefix. If you want the perl libraries to install to their 'correct'"
@@ -787,21 +700,9 @@ echo "Install prefix ................... : $prefix"
echo
-echo "Building with IPv6 support ....... : $have_ipv6"
-echo "Building with SSL support ........ : $have_openssl"
-if test "x$have_openssl" = "xno" -a "x$enable_ssl" = "xyes"; then
- if test -f /etc/debian_version; then
- echo " - Try: sudo apt-get install libssl-dev"
- elif test -f /etc/redhat-release; then
- echo " - Try installing openssl-devel"
- else
- echo " - Try installing OpenSSL development headers"
- fi
-fi
echo "Building with 64bit DCC support .. : $offt_64bit"
-echo "Building with garbage collector .. : $have_gc"
-echo "Building with DANE support ....... : $have_dane"
echo "Building with true color support.. : $want_truecolor"
+echo "Building with GRegex ............. : $want_gregex"
echo
echo "If there are any problems, read the INSTALL file."
diff --git a/default.theme b/default.theme
index ac99356a..6b6aeab8 100644
--- a/default.theme
+++ b/default.theme
@@ -120,7 +120,7 @@ abstracts = {
# the basic styling of how to print message, $0 = nick mode, $1 = nick
msgnick = "%K<%n$0$1-%K>%n %|";
- # message from you is printed. "msgownnick" specifies the styling of the
+ # message from you is printed. "ownnick" specifies the styling of the
# nick ($0 part in msgnick) and "ownmsgnick" specifies the styling of the
# whole line.
diff --git a/docs/Makefile.am b/docs/Makefile.am
index 5e222564..861a2ca4 100644
--- a/docs/Makefile.am
+++ b/docs/Makefile.am
@@ -1,5 +1,3 @@
-docdir = $(datadir)/doc/irssi
-
man_MANS = \
irssi.1
diff --git a/docs/faq.html b/docs/faq.html
index e2de9799..345060dc 100644
--- a/docs/faq.html
+++ b/docs/faq.html
@@ -1,152 +1,80 @@
-<h2>FAQ</h2>
+<h1>Frequently Asked Questions</h1>
+<h3 id="q-why-doesnt-irssi-display-colors-even-when-ircii-etc-displays-them">Q: Why doesn’t irssi display colors even when ircii etc. displays them?</h3>
-<h3>Q: Why doesn't irssi display colors even when ircii etc. displays them?</h3>
+<p>A: They force ANSI colors even if terminal doesn’t support them. By default, irssi uses colors only if terminfo/termcap so says. The correct way to fix this would be to change your TERM environment to a value where colors work, like xterm-color or color_xterm (eg. <code>TERM=xterm-color irssi</code>). If this doesn’t help, then use the evil way of <code>/SET term_force_colors ON</code>.</p>
-<p>A: They force ANSI colors even if terminal doesn't support them. By
-default, irssi uses colors only if terminfo/termcap so says. The correct
-way to fix this would be to change your TERM environment to a value where
-colors work, like xterm-color or color_xterm (eg. <code>TERM=xterm-color
-irssi</code>). If this doesn't help, then use the evil way of <code>/SET
-term_force_colors ON</code>.</p>
-
-
-<h3>Q: How do I easily write text to channel that starts with '/' character?</h3>
+<h3 id="q-how-do-i-easily-write-text-to-channel-that-starts-with--character">Q: How do I easily write text to channel that starts with ‘/’ character?</h3>
<p>A: <code>/ /text</code></p>
+<h3 id="q-why-doesnt-irssi-update-my-realname-or-whatever-after-i-change-it-with-set-realname-and-reconnect-with-reconnect-or-server">Q: Why doesn’t irssi update my realname (or whatever) after I change it with <code>/SET realname</code> and reconnect with <code>/RECONNECT</code> or <code>/SERVER</code>?</h3>
-<h3>Q: Why doesn't irssi update my realname (or whatever) after I change it
-with <code>/SET realname</code> and reconnect with <code>/RECONNECT</code>
-or <code>/SERVER</code>?</h3>
-
-<p>A: Irssi is trying to be too smart. This will be fixed in future, but for
-now you should use <code>/DISCONNECT</code> and <code>/CONNECT</code>.</p>
-
+<p>A: Irssi is trying to be too smart. This will be fixed in future, but for now you should use <code>/DISCONNECT</code> and <code>/CONNECT</code>.</p>
-<h3>Q: I connected to some server which isn't responding but now irssi tries
-to connect back to it all the time! How can I stop it?</h3>
+<h3 id="q-i-connected-to-some-server-which-isnt-responding-but-now-irssi-tries-to-connect-back-to-it-all-the-time-how-can-i-stop-it">Q: I connected to some server which isn’t responding but now irssi tries to connect back to it all the time! How can I stop it?</h3>
-<p>A: Two ways. The "good way" to do it is with <code>/DISCONNECT</code>.
-Check the server tags first with <code>/SERVER</code> without giving it any
-parameters, reconnections are those that have tag starting with "recon"
-text. So most probably you're going to do <code>/DISCONNECT recon-1</code>.
-The other way is to remove all the reconnections with
-<code>/RMRECONNS</code>, easier but may remove some connections you actually
-wanted to reconnect (if you used multiple servers..).</p>
+<p>A: Two ways. The “good way†to do it is with <code>/DISCONNECT</code>. Check the server tags first with <code>/SERVER</code> without giving it any parameters, reconnections are those that have tag starting with “recon†text. So most probably you’re going to do <code>/DISCONNECT recon-1</code>. The other way is to remove all the reconnections with <code>/RMRECONNS</code>, easier but may remove some connections you actually wanted to reconnect (if you used multiple servers..).</p>
+<h3 id="q-how-do-i-add-seconds-to-timestamp">Q: How do I add seconds to timestamp?</h3>
-<h3>Q: How do I add seconds to timestamp?</h3>
+<p>A: <code>/FORMAT timestamp {timestamp %%H:%%M:%%S}</code> - and remember to add the trailing space :)</p>
-<p>A: <code>/FORMAT timestamp {timestamp %%H:%%M:%%S}</code> - and remember
-to add the trailing space :)</p>
+<h3 id="q-why-does-irssi-say-irssi-channel-not-fully-synchronized-yet-try-again-after-a-while-when-i-try-to-use-ban-etc">Q: Why does irssi say “Irssi: Channel not fully synchronized yet, try again after a while†when I try to use /BAN etc?</h3>
+<p>A: Possibly a bug in irssi, or ircd you’re using does something that irssi didn’t really notice. The new code should make this happen far less often than before, but one known reason for this is when irssi doesn’t notice that you were unable to join some channel. Currently however I don’t know of any such events irssi doesn’t know about.</p>
-<h3>Q: Why does irssi say "Irssi: Channel not fully synchronized yet, try
-again after a while" when I try to use /BAN etc?</h3>
-
-<p>A: Possibly a bug in irssi, or ircd you're using does something that
-irssi didn't really notice. The new code should make this happen far less
-often than before, but one known reason for this is when irssi doesn't
-notice that you were unable to join some channel. Currently however I don't
-know of any such events irssi doesn't know about.</p>
-
-<p>Anyway, if this does happen, do <code>/RAWLOG SAVE ~/rawlog</code> soon
-after joining to channel, and either try to figure out yourself why irssi
-didn't get reply to WHO request, or send the whole log to cras@irssi.org. Note
-that the rawlog is by default only 200 lines and it may not be enough to
-show all needed information, so you might want to do <code>/SET rawlog_lines
-1000</code> or so.</p>
+<p>Anyway, if this does happen, do <code>/RAWLOG SAVE ~/rawlog</code> soon after joining to channel, and either try to figure out yourself why irssi didn’t get reply to WHO request, or open a Github issue with the full log included. Note that the rawlog is by default only 200 lines and it may not be enough to show all needed information, so you might want to do <code>/SET rawlog_lines 1000</code> or so.</p>
<p><code>MODE +b</code> still works fine though.</p>
+<h3 id="q-wheres-the-gui-version">Q: Where’s the GUI version?</h3>
-<h3>Q: Where's the GUI version?</h3>
-
-<p>A: Read
-<a href="http://www.irssi.org/about">http://www.irssi.org/about</a></p>
-
-
-<h3>Q: How do I autorejoin channels after being kicked?</h3>
-
-<p>A: That's evil and you shouldn't do it. If you get kicked, you should stay
-out, at least until the channel forgot you existed :) Most channels I've
-joined just ban you if you autorejoin after kick. If you're joined to
-channels who kick people for fun, try changing channels or something.</p>
-
-<p>Anyway, if you REALLY want to do that, and you understand that you're doing
-evilness, you can use the autorejoin.pl script that comes with irssi. You'll
-still need to specify the channels you wish to rejoin with <code>/SET
-autorejoin_channels #chan1 #chan2 ...</code></p>
-
-
-<h3>Q: How do I announce that I'm away/back in all channels I've joined? Or
-how do I change my nick when setting myself away/back?</h3>
-
-<p>A: That's even worse than autorejoin. Who could possibly care every time
-you come and go? Many channels will kick you for using this, and I for example
-have added several ignores so I'd never need to see these messages. Learn to
-use <code>/AWAY</code> command properly and tell its existence to people
-who don't know about it. <code>/WII yournick</code> shows your away reason
-much better for people who actually want to know if you're there or not.</p>
+<p>A: There was one on <a href="https://github.com/irssi-import/xirssi">irssi-import/xirssi</a> but it has not been maintained for a long time.</p>
+<h3 id="q-how-do-i-autorejoin-channels-after-being-kicked">Q: How do I autorejoin channels after being kicked?</h3>
-<h3>Q: Why does irssi autojoin on invite by default?</h3>
+<p>A: That’s evil and you shouldn’t do it. If you get kicked, you should stay out, at least until the channel forgot you existed :) Most channels I’ve joined just ban you if you autorejoin after kick. If you’re joined to channels who kick people for fun, try changing channels or something.</p>
-<p>A: The setting is /SET join_auto_chans_on_invite - it's not the same
-as regular autojoin-on-invite, which irssi doesn't even have. The only
-channels that are joined on invite, are the ones you've added to config
-with /CHANNEL ADD -auto. This is very useful with +i channels when you
-need to first send an invite request to bot, or if you get accidentally
-kicked from channel, the kicker can invite you back immediately.</p>
+<p>Anyway, if you REALLY want to do that, and you understand that you’re doing evilness, you can use the autorejoin.pl script that comes with irssi. You’ll still need to specify the channels you wish to rejoin with <code>/SET autorejoin_channels #chan1 #chan2 ...</code></p>
-<p>I don't see any bad side effects with this feature, so it's ON by
-default. I guess someone could start kicking/inviting you all the time
-but server connection shouldn't drop because of that, and you shouldn't
-join channels whose operators are that evil.</p>
+<h3 id="q-how-do-i-announce-that-im-awayback-in-all-channels-ive-joined-or-how-do-i-change-my-nick-when-setting-myself-awayback">Q: How do I announce that I’m away/back in all channels I’ve joined? Or how do I change my nick when setting myself away/back?</h3>
+<p>A: That’s even worse than autorejoin. Who could possibly care every time you come and go? Many channels will kick you for using this, and I for example have added several ignores so I’d never need to see these messages. Learn to use <code>/AWAY</code> command properly and tell its existence to people who don’t know about it. <code>/WII yournick</code> shows your away reason much better for people who actually want to know if you’re there or not.</p>
-<h3>Q: How to make UTF-8 support work with irssi?</h3>
+<h3 id="q-why-does-irssi-autojoin-on-invite-by-default">Q: Why does irssi autojoin on invite by default?</h3>
-<p>A: Make sure your terminal supports UTF-8 (for example, <code>xterm -u8</code>).
-If you use screen, you may have to do <code>screen -U</code>. And in Irssi do
-<code>/SET term_charset utf-8</code>. (for 0.8.9 and older: <code>/SET term_type utf-8</code>)</p>
+<p>A: The setting is /SET join_auto_chans_on_invite - it’s not the same as regular autojoin-on-invite, which irssi doesn’t even have. The only channels that are joined on invite, are the ones you’ve added to config with /CHANNEL ADD -auto. This is very useful with +i channels when you need to first send an invite request to bot, or if you get accidentally kicked from channel, the kicker can invite you back immediately.</p>
+<p>I don’t see any bad side effects with this feature, so it’s ON by default. I guess someone could start kicking/inviting you all the time but server connection shouldn’t drop because of that, and you shouldn’t join channels whose operators are that evil.</p>
-<h3>Q: Will there be /DETACH-like feature?</h3>
+<h3 id="q-how-to-make-utf-8-support-work-with-irssi">Q: How to make UTF-8 support work with irssi?</h3>
-<p>A: Maybe. Detach code already is there, attach is just missing :) But I
-don't have much interest in coding it,
-<a href="http://www.gnu.org/software/screen/screen.html">screen</a> and
-<a href="http://dtach.sf.net/">dtach</a> can be used to do it just fine.</p>
+<p>A: Make sure your terminal supports UTF-8 (for example, <code>xterm -u8</code>). If you use screen, you may have to do <code>screen -U</code>. And in Irssi do <code>/SET term_charset utf-8</code>. (for 0.8.9 and older: <code>/SET term_type utf-8</code>)</p>
+<h3 id="q-will-there-be-detach-like-feature">Q: Will there be /DETACH-like feature?</h3>
-<h3>Q: How do I run scripts automatically at startup?</h3>
+<p>A: <a href="http://tmux.github.io/">tmux</a>, <a href="http://www.gnu.org/software/screen/screen.html">screen</a> and <a href="http://dtach.sf.net/">dtach</a> can be used to do it just fine.</p>
-<p>A: Put them into <code>~/.irssi/scripts/autorun/</code> directory. Or
-better would be if you placed them in <code>~/.irssi/scripts/</code> and
-created symlinks to autorun directory (eg. <code>cd
-~/.irssi/scripts/autorun/ ; ln -s ../script.pl .</code>)</p>
+<h3 id="q-how-do-i-run-scripts-automatically-at-startup">Q: How do I run scripts automatically at startup?</h3>
+<p>A: Put them into <code>~/.irssi/scripts/autorun/</code> directory. Or better would be if you placed them in <code>~/.irssi/scripts/</code> and created symlinks to autorun directory (eg. <code>cd ~/.irssi/scripts/autorun/ ; ln -s ../script.pl .</code>)</p>
-<h3>Q: How do I execute commands automatically at startup?</h3>
+<h3 id="q-how-do-i-execute-commands-automatically-at-startup">Q: How do I execute commands automatically at startup?</h3>
-<p>A: Put them into <code>~/.irssi/startup</code> file, each command on its
-own line. The preceding slash (/) is not necessary.</p>
+<p>A: Put them into <code>~/.irssi/startup</code> file, each command on its own line. The preceding slash (/) is not necessary.</p>
+<h3 id="q-how-do-i-easily-edit-existing-topic">Q: How do I easily edit existing topic?</h3>
-<h3>Q: How do I easily edit existing topic?</h3>
+<p>A: <code>/TOPIC &lt;tab&gt;</code></p>
-<p>A: /TOPIC &lt;tab&gt;</p>
+<h3 id="q-how-can-i-have-whois-replies-to-active-window">Q: How can I have /WHOIS replies to active window?</h3>
-<h3>Q: How can I have /WHOIS replies to active window?</h3>
+<p>A: You can disable the status window, or do <code>/WINDOW LEVEL -CRAP</code> in it which would also make several other messages show up in active window. You can also use a <a href="http://dgl.cx/irssi/hack-whois-in-current-window.pl">script</a>.</p>
-<p>A: You can disable the status window, or do <code>/WINDOW LEVEL
--CRAP</code> in it which would also make several other messages show up in
-active window. You can also use a
-<a href="http://dgl.cx/irssi/hack-whois-in-current-window.pl">script</a>.</p>
+<h3 id="q-how-do-i-add-the-active-network-to-the-statusbar">Q: How do I add the active network to the statusbar</h3>
-<h3>Q: How do I add the active network to the statusbar</h3>
+<p>A: Modify the window-line in statusbar section in config file to <code>window = "{sb $winref:$tag/$T{sbmode $M}}";</code></p>
-<p>A: Modify the window-line in statusbar section in config file to
-<code>window = "{sb $winref:$tag/$T{sbmode $M}}";</code></p>
+<h3 id="q-how-to-pronounce-irssi">Q: How to pronounce Irssi?</h3>
+<p>A: Check <a href="https://irssi.org/assets/irssi.wav">here</a></p>
diff --git a/docs/help/in/away.in b/docs/help/in/away.in
index e0cf3685..75bc46c4 100644
--- a/docs/help/in/away.in
+++ b/docs/help/in/away.in
@@ -8,8 +8,8 @@
-one: Marks yourself as away on the active server.
-all: Marks yourself as away on all the servers you are connected to.
- You away message; if no argument is given, your away status will be removed.
-
+ Your away message; if no argument is given, your away status will be removed.
+
%9Description:%9
Marks yourself as 'away'; this method is used to inform people that you
diff --git a/docs/help/in/channel.in b/docs/help/in/channel.in
index 01bea3c1..86e824c2 100644
--- a/docs/help/in/channel.in
+++ b/docs/help/in/channel.in
@@ -7,6 +7,7 @@
LIST: Displays the list of configured channels.
ADD: Adds a channel to your configuration.
+ MODIFY: Modifies a channel in your configuration.
REMOVE: Removes a channel from your configuration.
-auto: Automatically join the channel.
@@ -36,6 +37,7 @@
/CHANNEL ADD -auto #basementcat Quakenet secret_lair
/CHANNEL ADD -auto -bots '*!@*.irssi.org *!bot@irssi.org' -botcmd 'msg $0 op WzerTrzq' #hideout Freenode
/CHANNEL ADD -auto -bots 'Q!TheQBot@CServe.quakenet.org' -botcmd '^MSG Q op #irssi' #irssi Quakenet
+ /CHANNEL MODIFY -noauto #irssi Freenode
/CHANNEL REMOVE #hideout Freenode
%9Special Example:%9
diff --git a/docs/help/in/connect.in b/docs/help/in/connect.in
index 1c1aa2bb..e861ad74 100644
--- a/docs/help/in/connect.in
+++ b/docs/help/in/connect.in
@@ -5,21 +5,24 @@
%9Parameters:%9
- -4: Connects using IPv4.
- -6: Connects using IPv6.
- -ssl: Connects using SSL encryption.
- -ssl_cert: The SSL client certificate file.
- -ssl_pkey: The SSL client private key, if not included in the
- certificate file.
- -ssl_verify: Verifies the SSL certificate of the server.
- -ssl_cafile: The file with the list of CA certificates.
- -ssl_capath: The directory which contains the CA certificates.
- -noproxy: Ignores the global proxy configuration.
- -network: The network this connection belongs to.
- -host: The hostname you would like to connect from.
- -rawlog: Immediately open rawlog after connecting.
- -!: Doesn't autojoin channels.
- -noautosendcmd: Doesn't execute autosendcmd.
+ -4: Connects using IPv4.
+ -6: Connects using IPv6.
+ -tls: Connects using TLS encryption.
+ -tls_cert: The TLS client certificate file.
+ -tls_pkey: The TLS client private key, if not included in the certificate file.
+ -tls_pass: The password for the TLS client private key or certificate.
+ -tls_verify: Verifies the TLS certificate of the server.
+ -tls_cafile: The file with the list of CA certificates.
+ -tls_capath: The directory which contains the CA certificates.
+ -tls_ciphers: TLS cipher suite preference lists.
+ -tls_pinned_cert: Pinned x509 certificate fingerprint.
+ -tls_pinned_pubkey: Pinned public key fingerprint.
+ -noproxy: Ignores the global proxy configuration.
+ -network: The network this connection belongs to.
+ -host: The hostname you would like to connect from.
+ -rawlog: Immediately open rawlog after connecting.
+ -!: Doesn't autojoin channels.
+ -noautosendcmd: Doesn't execute autosendcmd.
A network or server to connect to; you can optionally specify a custom port,
password and nickname.
diff --git a/docs/help/in/dcc.in b/docs/help/in/dcc.in
index c348b1ae..18a77ee9 100644
--- a/docs/help/in/dcc.in
+++ b/docs/help/in/dcc.in
@@ -36,10 +36,10 @@
%9Examples:%9
/DCC CHAT mike
- /DCC GET bob 'summer vacation.mkv'
- /DCC SEND sarah documents/resume.pdf
- /DCC CLOSE mike
- /DCC CLOSE bob 'summer vacation.mkv'
+ /DCC GET bob "summer vacation.mkv"
+ /DCC SEND sarah "summer vacation.mkv"
+ /DCC CLOSE get mike
+ /DCC CLOSE send bob "summer vacation.mkv"
%9See also:%9 CD
diff --git a/docs/help/in/disconnect.in b/docs/help/in/disconnect.in
index ace1a3df..bd4a4e17 100644
--- a/docs/help/in/disconnect.in
+++ b/docs/help/in/disconnect.in
@@ -10,10 +10,11 @@
%9Description:%9
- Disconnects from one or more IRC servers; the list of all the servers you
- are connected to can be retrieved via the SERVER command.
+ Disconnects from an IRC server; the list of all the servers you are
+ connected to can be retrieved via the SERVER command.
- Use the wildcard character '*' if you want to disconnect from all servers.
+ Use the wildcard character '*' if you want to disconnect from the active
+ server.
%9Examples:%9
diff --git a/docs/help/in/echo.in b/docs/help/in/echo.in
index a730e282..4ff23730 100644
--- a/docs/help/in/echo.in
+++ b/docs/help/in/echo.in
@@ -6,10 +6,10 @@
%9Parameters:%9
-current: Displays the output in the active window.
- -window: Displays the ouput in the target window.
+ -window: Displays the output in the target window.
-level: Displays the output with a given message level.
- The text ouput; if no target is given, the active window will be used.
+ The text output; if no target is given, the active window will be used.
%9Description:%9
diff --git a/docs/help/in/hilight.in b/docs/help/in/hilight.in
index fabbc2ea..0726e5e7 100644
--- a/docs/help/in/hilight.in
+++ b/docs/help/in/hilight.in
@@ -10,6 +10,7 @@
-line: Highlights the whole line.
-mask: Highlights all messages from users matching the mask.
-full: The text must match the full word.
+ -matchcase: The text must match case.
-regexp: The text is a regular expression.
-color: The color the display the highlight in.
-actcolor: The color to mark the highlight activity in the statusbar.
@@ -31,7 +32,7 @@
/HILIGHT
/HILIGHT mike
/HILIGHT -regexp mi+ke+
- /HILIGHT -mask bob!*@*.irssi.org -color %%G
+ /HILIGHT -mask -color %%G bob!*@*.irssi.org
/HILIGHT -full -color %%G -actcolor %%Y redbull
%9References:%9
diff --git a/docs/help/in/ignore.in b/docs/help/in/ignore.in
index 8c2b1c90..b0a547c1 100644
--- a/docs/help/in/ignore.in
+++ b/docs/help/in/ignore.in
@@ -41,7 +41,6 @@
/IGNORE #irssi NO_ACT JOINS PARTS QUITS
/IGNORE mike NO_ACT -MSGS
/IGNORE -regexp -pattern
- /IGNORE mike NO_ACT -MSGS
%9See also:%9 ACCEPT, SILENCE, UNIGNORE
diff --git a/docs/help/in/irssiproxy.in b/docs/help/in/irssiproxy.in
new file mode 100644
index 00000000..79d75b91
--- /dev/null
+++ b/docs/help/in/irssiproxy.in
@@ -0,0 +1,14 @@
+
+@SYNTAX:irssiproxy@
+
+%9Description:%9
+
+ Displays the list of clients connected to irssiproxy.
+
+%9Examples:%9
+
+ /IRSSIPROXY
+ /IRSSIPROXY STATUS
+
+%9See also:%9 LOAD PROXY, SET irssiproxy
+
diff --git a/docs/help/in/lastlog.in b/docs/help/in/lastlog.in
index e7b90d42..e96e2ed5 100644
--- a/docs/help/in/lastlog.in
+++ b/docs/help/in/lastlog.in
@@ -10,15 +10,19 @@
-window: Specifies the window to check.
-new: Only displays results since the previous lastlog.
-away: Only displays results since you previous away status.
- -level: Specifies the levels to check.
+ -<level>: Specifies the levels to check (e.g. -joins -quits -hilight)
-clear: Removes the previous results from the active window.
-count: Displays how many lines match.
-case: Performs a case-sensitive matching.
+ -date: Prepends each row with the message's date
-regexp: The given text pattern is a regular expression.
-word: The text must match full words.
-force: Forces to display the lastlog, even if it exceeds 1000 lines.
- -after: Only displays results after the given line number.
- -before: Only displays results before the given line number.
+ -after: Include this many lines of content after the match.
+ -before: Include this many lines of content before the match.
+ -<#>: Include this many lines of content around the match.
+ <count>: Display a maximum number of `count' lines.
+ <start>: Skip the last `start' lines.
The pattern to search for and the maximum of lines to display; if no
parameter is given, the entire window buffer will be displayed.
@@ -30,8 +34,10 @@
%9Examples:%9
/LASTLOG holiday
- /LASTLOG 'is on vacation'
- /LASTLOG -file -force ~/mike.log 'mike'
+ /LASTLOG 'is on vacation' 10
+ /LASTLOG -force -file ~/mike.log 'mike'
+ /LASTLOG -hilight
+ /LASTLOG -5 searchterm
%9See also:%9 HILIGHT, SCROLLBACK
diff --git a/docs/help/in/list.in b/docs/help/in/list.in
index 14858337..b796eed0 100644
--- a/docs/help/in/list.in
+++ b/docs/help/in/list.in
@@ -7,8 +7,8 @@
-yes: Confirms that you want to receive a large amount of data.
- The text a channel must match; if no argument is given, the list of all
- channels will be displayed.
+ If the exact name of a channel is given, the only information about this
+ channel is requested; otherwise, a list of all channels will be displayed.
%9Description:%9
@@ -19,7 +19,16 @@
/LIST
/LIST -yes
- /LIST -yes *ubuntu*
+ /LIST #ubuntu
+ /LIST #*ubuntu*,>1
-%9See also:%9 QUOTE, STATS, WHOIS
+%9Remarks:%9
+
+ Not all networks support server-side filtering and may provide a network
+ service or service bot instead; on IRCnet, you may use the List service:
+
+ /SQUERY List HELP
+ /MSG ALIS HELP
+
+%9See also:%9 STATS, SQUERY, WHOIS
diff --git a/docs/help/in/msg.in b/docs/help/in/msg.in
index dc70f1f3..9e0879e4 100644
--- a/docs/help/in/msg.in
+++ b/docs/help/in/msg.in
@@ -11,6 +11,9 @@
The target nickname or channel and the message to send.
+ Use the wildcard character '*' if you want to use the active nickname or
+ channel.
+
%9Description:%9
Sends a message to a nickname or channel.
@@ -19,6 +22,7 @@
/MSG mike Hi, what's up?
/MSG #irssi I am awesome :)
+ /MSG * Do you want to build a snowman?
%9See also:%9 ACTION, DCC, JOIN
diff --git a/docs/help/in/network.in b/docs/help/in/network.in
index 046d9feb..ba08ef69 100644
--- a/docs/help/in/network.in
+++ b/docs/help/in/network.in
@@ -7,6 +7,7 @@
LIST: Displays the list of configured networks.
ADD: Adds a network to your configuration.
+ MODIFY: Modifies a network in your configuration.
REMOVE: Removes a network from your configuration.
-nick: Specifies the nickname to use.
@@ -32,6 +33,12 @@
additional commands to the server.
-cmdmax: Specifies the maximum number of commands to perform before
starting the internal flood protection.
+ -sasl_mechanism Specifies the mechanism to use for the SASL authentication.
+ At the moment irssi only supports the 'plain' and the
+ 'external' mechanisms.
+ -sasl_username Specifies the username to use during the SASL authentication.
+ -sasl_password Specifies the password to use during the SASL authentication.
+
The name of the network to add, edit or remove; if no parameter is given,
the list of networks will be displayed.
@@ -53,6 +60,7 @@
/NETWORK ADD -usermode +iw -nick mike -realname 'The one and only mike!' -host staff.irssi.org Freenode
/NETWORK ADD -autosendcmd '^MSG NickServ identify WzerT8zq' Freenode
/NETWORK ADD -autosendcmd '^MSG Q@CServe.quakenet.org AUTH mike WzerT8zq; WAIT 2000; OPER mike WzerT8zq; WAIT 2000; MODE mike +kXP' Quakenet
+ /NETWORK MODIFY -usermode +gi EFnet
/NETWORK REMOVE Freenode
%9See also:%9 CHANNEL, CONNECT, SERVER
diff --git a/docs/help/in/script.in b/docs/help/in/script.in
index 6e94ef7f..e90135ad 100644
--- a/docs/help/in/script.in
+++ b/docs/help/in/script.in
@@ -12,6 +12,8 @@
RESET: Unloads all the scripts.
-permanent: In combination with EXEC, the code will be loaded into the
memory.
+ -autorun: When passed to RESET the scripts in the autorun folder are
+ reloaded.
If no argument is given, the list of active scripts will be displayed.
diff --git a/docs/help/in/server.in b/docs/help/in/server.in
index 5628b19f..44de0efc 100644
--- a/docs/help/in/server.in
+++ b/docs/help/in/server.in
@@ -5,43 +5,47 @@
%9Parameters:%9
- LIST: Displays the list of servers you are connected to.
- CONNECT: Connects to the given server.
- ADD: Adds a server to your configuration.
- REMOVE: Removes a server from your configuration.
- PURGE: Purges the commands queued to be sent to the server.
+ LIST: Displays the list of servers you are connected to.
+ CONNECT: Connects to the given server.
+ ADD: Adds a server to your configuration.
+ MODIFY: Modifies a server in your configuration.
+ REMOVE: Removes a server from your configuration.
+ PURGE: Purges the commands queued to be sent to the server.
- -!: Doesn't autojoin the channels.
- -4: Connects using IPv4.
- -6: Connects using IPv6.
- -ssl: Connects using SSL encryption.
- -ssl_cert: The SSL client certificate file.
- -ssl_pkey: The SSL client private key, if not included in the
- certificate file.
- -ssl_pass: Verifies the SSL certificate of the server.
- -ssl_verify: Verifies the SSL certificate of the server.
- -ssl_cafile: The file with the list of CA certificates.
- -ssl_capath: The directory which contains the CA certificates.
- -auto: Automatically connects to the server on startup.
- -noauto: Doesn't connect to the server on startup.
- -network: The network the server belongs to.
- -host: The hostname you would like to connect from.
- -cmdspeed: Specifies the minimum amount of time, expressed in
- milliseconds, that the client must wait before sending
- additional commands to the server.
- -cmdmax: Specifies the maximum number of commands to perform
- before starting the internal flood protection.
- -port: Specifies the port to connect to the server.
- -noproxy: Ignores the global proxy configuration.
- -rawlog: Immediately open rawlog after connecting.
- -noautosendcmd: Doesn't execute autosendcmd.
+ -!: Doesn't autojoin the channels.
+ -4: Connects using IPv4.
+ -6: Connects using IPv6.
+ -tls: Connects using TLS encryption.
+ -tls_cert: The TLS client certificate file.
+ -tls_pkey: The TLS client private key, if not included in the
+ certificate file.
+ -tls_pass: The password for the TLS client private key or certificate.
+ -tls_verify: Verifies the TLS certificate of the server.
+ -tls_cafile: The file with the list of CA certificates.
+ -tls_capath: The directory which contains the CA certificates.
+ -tls_ciphers: TLS cipher suite preference lists.
+ -tls_pinned_cert: Pinned x509 certificate fingerprint.
+ -tls_pinned_pubkey: Pinned public key fingerprint.
+ -auto: Automatically connects to the server on startup.
+ -noauto: Doesn't connect to the server on startup.
+ -network: The network the server belongs to.
+ -host: The hostname you would like to connect from.
+ -cmdspeed: Specifies the minimum amount of time, expressed in
+ milliseconds, that the client must wait before sending
+ additional commands to the server.
+ -cmdmax: Specifies the maximum number of commands to perform
+ before starting the internal flood protection.
+ -port: Specifies the port to connect to the server.
+ -noproxy: Ignores the global proxy configuration.
+ -rawlog: Immediately open rawlog after connecting.
+ -noautosendcmd: Doesn't execute autosendcmd.
The server, port and network to add, modify or remove; if no argument is
given, the list of servers you are connected to will be returned.
%9Description:%9
- Displays, adds, modifies or removes the network configuration of IRC
+ Displays, adds, modifies or removes the network configuration of IRC
servers.
When using the ADD parameter on a server that already exists, the
@@ -60,8 +64,9 @@
/SERVER CONNECT chat.freenode.net
/SERVER CONNECT +chat.freenode.net
/SERVER ADD -network Freenode -noautosendcmd orwell.freenode.net
- /SERVER ADD -! -auto -host staff.irssi.org -port 6667 -4 -network Freenode -noproxy orwell.freenode.net
- /SERVER REMOVE -network Freenode orwell.freenode.net
+ /SERVER ADD -! -auto -host staff.irssi.org -4 -network Freenode -noproxy orwell.freenode.net 6667
+ /SERVER MODIFY -network Freenode -noauto orwell.freenode.net
+ /SERVER REMOVE orwell.freenode.net 6667 Freenode
/SERVER PURGE
/SERVER PURGE orwell.freenode.net
diff --git a/docs/help/in/servlist.in b/docs/help/in/servlist.in
new file mode 100644
index 00000000..0a0d025f
--- /dev/null
+++ b/docs/help/in/servlist.in
@@ -0,0 +1,23 @@
+
+%9Syntax:%9
+
+@SYNTAX:servlist@
+
+%9Parameters:%9
+
+ <mask> limits the output to the services which names matches
+ the mask.
+ <type> limits the output to the services of the specified type.
+
+%9Description:%9
+
+ List the network services currently present on the
+ IRC network.
+
+%9Examples:%9
+
+ /SERVLIST *@javairc.*
+ /SERVLIST * 0xD000
+
+%9See also:%9 SQUERY
+
diff --git a/docs/help/in/squery.in b/docs/help/in/squery.in
new file mode 100644
index 00000000..59ee8008
--- /dev/null
+++ b/docs/help/in/squery.in
@@ -0,0 +1,16 @@
+
+%9Syntax:%9
+
+@SYNTAX:squery@
+
+%9Parameters:%9
+
+ <service> - Service nickname or full hostmask of service to query.
+ <message> - Message to send to the service.
+
+%9Description:%9
+
+ /SQUERY sends a query to the specified service.
+
+%9See also:%9 SERVLIST, LIST, MSG
+
diff --git a/docs/help/in/window.in b/docs/help/in/window.in
index c965d3fb..6dde6c50 100644
--- a/docs/help/in/window.in
+++ b/docs/help/in/window.in
@@ -5,49 +5,49 @@
%9Parameters:%9
- LOG: Modifies the logging status.
- LOGFILE: Modifies the location to the log file.
- NEW: Creates a new window.
- CLOSE: Closes a window.
- REFNUM: Go to the window with the given number.
- GOTO: Go to the window with the given nickname, channel or number.
- NEXT: Go to the next window.
- LAST: Go to the last window.
- PREVIOUS: Go to the previous window.
- LEVEL: Modifies the text levels to display in the window.
- IMMORTAL: Modifies the window mortality status.
- SERVER: Set the active server of the window.
- ITEM PREV: Go to the previous item in the window.
- ITEM NEXT: Go to the next item in the window.
- ITEM GOTO: Go to the specified nickname, channel or window item number.
- ITEM MOVE: Move the active window item to another window.
- NUMBER: Move the active window to another position.
- NAME: Give the window a name.
- HISTORY: Clears the window history buffer.
- MOVE PREV: Move the window down.
- MOVE NEXT: Move the window up.
- MOVE FIRST: Move the window to the first position.
- MOVE LAST: Move the window to the last position.
- MOVE: Move the window.
- LIST: List all the windows.
- THEME: Applies a theme to the windows.
- GROW: Increase the window size when using split windows.
- SHRINK: Decrease the window size when using split windows.
- SIZE: Modify the window size when using split windows.
- BALANCE: Balance the window locations when using split windows.
- HIDE: Hide the window when using split windows.
- SHOW: Show the window when using split windows.
- UP: Go to the window above when using split windows.
- DOWN: Go to the window below when using split windows.
- LEFT: Go to the previous window.
- RIGHT: Go to the next window.
- STICK: Make the window sticky.
- MOVE LEFT: Move the window to the previous location.
- MOVE RIGHT: Move the window to the next location.
- MOVE UP: Move the window up when using split windows.
- MOVE DOWN: Move the window down when using split windows.
+ LOG: %|Turn on or off logging of the active window, optionally specifying the log file to use.
+ LOGFILE: %|Sets the location of the log file to use for window logging without starting to log.
+ NEW: %|Creates a new hidden or split window.
+ CLOSE: %|Closes the current window, the specified one or all windows in the given range.
+ REFNUM: %|Go to the window with the given number.
+ GOTO: %|Go to the window with activity, with the given nickname, channel or with the specified number.
+ NEXT: %|Go to the next window numerically.
+ LAST: %|Go to the previously active window.
+ PREVIOUS: %|Go to the previous window numerically.
+ LEVEL: %|Changes the text levels to display in the window, or query the current level.
+ IMMORTAL: %|Modifies or queries the window mortality status. Immortal windows have an extra protection against WINDOW CLOSE.
+ SERVER: %|Change the active server of the window or the server stickyness. If the server is sticky, it cannot be cycled with next_window_item/previous_window_item
+ ITEM PREV: %|Make the previous item in this window active.
+ ITEM NEXT: %|Make the next item in this window active.
+ ITEM GOTO: %|Change to the query with the specified nickname, channel with the given name or window item number.
+ ITEM MOVE: %|Move the active window item to another window, or move the channel or query item specified by their name to the current window.
+ NUMBER: %|Change the active window number to the specified number, swapping the window already in that place if required. With -sticky, protect the window number from renumbering done by windows_auto_renumber. (To re-set the sticky attribute, use WINDOW NUMBER again without -sticky.)
+ NAME: %|Change or clear the window name. Window names must be unique.
+ HISTORY: %|Set or clear a specific named history to use for this window. All windows with the same named history will share a history.
+ MOVE PREV: %|Move the window to the place of the numerically previous window. At the first position, move the window to the end and renumber the consecutive block that it was part of.
+ MOVE NEXT: %|Move the window to the place of the numerically next window. At the last position, move the window to the first position and renumber the consecutive block at first position (if any)
+ MOVE FIRST: %|Move the window to the first position. Any windows inbetween are moved to their numerically next positions.
+ MOVE LAST: %|Move the window to the last position. Any windows inbetween are moved to their numerically previous positions.
+ MOVE: %|Move the window to the specified number or the first number that is in use when moving the window in the direction of the specified position. Any windows inbetween are shifted towards the old position of the window (unused positions remain empty)
+ LIST: %|List all the windows.
+ THEME: %|Applies or removes a per-window theme.
+ GROW: %|Increase the size of the active split window by the specified number of lines.
+ SHRINK: %|Decrease the size of the active split window by the specified number of lines.
+ SIZE: %|Set the current split window size to the specified numer of lines.
+ BALANCE: %|Balance the heights of all split windows.
+ HIDE: %|Hides the current split window, or the split window specified by number or item name.
+ SHOW: %|Show the window specified by number or item name as a new split windows. It is made sticky when autostick_split_windows is turned on.
+ UP: %|Set the split window above the current one active. At the top, wraps to the bottom.
+ DOWN: %|Set the split window below the current one active. At the bottom, wraps to the top.
+ LEFT: %|Go to the previous window numerically that is part of the current sticky group (or not part of any sticky group).
+ RIGHT: %|Go to the next window numerically that is part of the current sticky group (or not part of any sticky group).
+ STICK: %|Make the currently active window sticky, or stick the window specified by number to the currently visible split window. Or turn off stickyness of the currently active window or the window specified by number.
+ MOVE LEFT: %|Move the window to the numerically previous location inside the current sticky group.
+ MOVE RIGHT: %|Move the window to the numerically next location inside the current sticky group.
+ MOVE UP: %|Move the current window to the sticky group of the split window above. If no sticky group remains, the split window collapses.
+ MOVE DOWN: %|Move the current window to the sticky group of the split window below. If no sticky group remains, the split window collapses.
- Add the required arguments for the given command.
+ %|Add the required arguments for the given command. Without arguments, the details (size, immortality, levels, server, name and sticky group) of the currently active window are displayed. If used with a number as argument, same as WINDOW REFNUM.
%9Description:%9
diff --git a/docs/irssi.1 b/docs/irssi.1
index e86be7ca..e1fab0d8 100644
--- a/docs/irssi.1
+++ b/docs/irssi.1
@@ -1,12 +1,13 @@
-.TH Irssi 1 "September 2002" "Irssi IRC client"
+.TH Irssi 1 "June 2015" "Irssi IRC client"
.SH NAME
Irssi \- a modular IRC client for UNIX
.SH SYNOPSIS
.B irssi
-[-dv!?] [-c server] [-p port] [-n nickname] [-w password] [-h hostname]
+[--config=PATH] [--home=PATH] [-dv!?] [-c server] [-p port] [-n nickname]
+[-w password] [-h hostname]
.SH DESCRIPTION
.B Irssi
-is a modular Internet Relay Chat client. It is highly extensible and
+is a modular Internet Relay Chat client; it is highly extensible and
very secure. Being a fullscreen, termcap based client with many
features,
.B Irssi
@@ -16,16 +17,15 @@ is easily extensible through scripts and modules.
.BI "\-\-config="FILE
use
.I FILE
-instead of ~/.irssi/config.
+instead of ~/.irssi/config
.TP
.BI "\-\-home="PATH
.I PATH
-specifies the home directory of Irssi.
-Default is
+specifies the home directory of Irssi; default is
.BR ~/.irssi
.TP
.BI "\-c, \-\-connect="SERVER
-connects to
+connect to
.I SERVER
.TP
.BI "\-w, \-\-password="PASSWORD
@@ -39,71 +39,50 @@ automatically connect to
on server.
.TP
.BI "\-!, \-\-noconnect"
-disables autoconnecting.
+disable autoconnecting of servers
.TP
.BI "\-n, \-\-nick="NICKNAME
specify
.I NICKNAME
-as your nick.
+as your nick
.TP
.BI "\-h, \-\-hostname="HOSTNAME
use
.I HOSTNAME
-for your irc session.
-.TP
-.BI "\-d, \-\-dummy"
-use dummy terminal mode.
+for your irc session
.TP
.BI "\-v, \-\-version"
-display the version of Irssi.
+display the version of Irssi
.TP
.BI "\-?, \-\-help"
-show a help message.
+show a help message
.SH SEE ALSO
.B Irssi
-has been supplied with a huge amount of documentation. Check /help or look
-at the files contained by /usr/share/doc/irssi*
+has a solid amount of documentation available; check /HELP or look online
+at http://www.irssi.org
.SH FILES
.TP
-.I /etc/irssi.conf
-Global configuration file
-.TP
.I ~/.irssi/config
-Personal configuration file
+personal configuration file
.TP
.I ~/.irssi/config.autosave
-Automatic save of the personal config file when it was changed externally
+automatic save of the personal config file when it was changed externally
.TP
.I ~/.irssi/default.theme
-Default irssi theme
+default irssi theme
.TP
.I ~/.irssi/away.log
-Logged messages in away status
-.TP
-.I /usr/share/irssi/help/
-Directory including many help files
-.TP
-.I /usr/share/irssi/scripts/
-Global scripts directory
-.TP
-.I /usr/share/irssi/themes/
-Global themes directory
+logged messages in away status
.TP
.I ~/.irssi/scripts/
-Default scripts directory
+default scripts directory
.TP
.I ~/.irssi/scripts/autorun/
-Directory containing links to scripts that should be loaded
+directory containing links to scripts that should be loaded
automatically on startup
.TP
.I ~/.irssi/startup
-File containing a list of commands to execute on startup
+file containing a list of commands to execute on startup
.SH AUTHORS/CREDITS
.B Irssi
-was written by Timo Sirainen
-.B <cras@irssi.org>
-.sp
-This manpage was written by Istvan Sebestyen
-.BR <stevee@alphanet.ch>
-and Stefan Tomanek
-.BR <stefan@pico.ruhr.de>
+was written by Timo Sirainen; this manpage was written by Istvan Sebestyen, Stefan Tomanek, Geert Hauwaerts.
diff --git a/docs/perl.txt b/docs/perl.txt
index 732b73af..59f65462 100644
--- a/docs/perl.txt
+++ b/docs/perl.txt
@@ -10,7 +10,7 @@ INSTALL file for information about perl problems.
---------------
Scripts are run with /SCRIPT LOAD command, or the default /RUN alias.
-"/SCRIPT" shows list of running script, and /SCRIPT UNLOAD can unload
+"/SCRIPT" shows list of running scripts, and /SCRIPT UNLOAD can unload
scripts.
Scripts should be placed to ~/.irssi/scripts/ or
@@ -796,7 +796,7 @@ Query->{}
address - Host address of the queries nick
server_tag - Server tag used for this nick (doesn't get erased if
server gets disconnected)
- unwanted - 1 if the other side closed or some error occured (DCC chats)
+ unwanted - 1 if the other side closed or some error occurred (DCC chats)
Query
query_create(chat_type, server_tag, nick, automatic)
@@ -1123,7 +1123,7 @@ Netsplit->{}
Netsplitserver->{}
server - The server nick was in
- destserver - The other server where split occured.
+ destserver - The other server where split occurred.
count - Number of splits in server
Netsplitchannel->{}
@@ -1185,3 +1185,15 @@ Client->{}
connected - whether the client is connected and ready
want_ctcp - whether the client wants to receive CTCPs
ircnet - network tag of the network we proxy
+
+
+
+
+ Bugs and Limitations
+ --------------------
+* Calling die in 'script error' handler causes segfault (#101)
+* Storing and later using any Irssi object may result in use-after-free related crash
+ - Workaround: always acquire fresh objects
+* Calling $dcc->close from the "dcc created" signal will cause unstable behaviour and crashes (#386)
+ - Workaround: use "dcc request" signal instead AND call
+ &Irssi::signal_continue(@_); as the first thing
diff --git a/docs/proxy.txt b/docs/proxy.txt
index 41875aab..224d24e3 100644
--- a/docs/proxy.txt
+++ b/docs/proxy.txt
@@ -12,6 +12,11 @@ In irssi, say:
/LOAD proxy
+If you want the proxy to be loaded automatically at startup, add the
+load command to ~/.irssi/startup:
+
+ echo "load proxy" >> ~/.irssi/startup
+
You really should set some password for the proxy with:
/SET irssiproxy_password secret
@@ -24,3 +29,32 @@ something like:
There we have 3 different irc networks answering in 3 ports. Note that
you'll have to make the correct /IRCNET ADD and /SERVER ADD commands to
make it work properly.
+
+The special network name "?" allows the client to select the network
+dynamically on connect:
+
+ /SET irssiproxy_ports ?=2777
+
+Now the client can send <network>:<password> as the server password, e.g.
+
+ /CONNECT ... 2777 efnet:secret
+
+to connect to efnet. If there is no irssiproxy_password set, you can
+omit the ":" and just send the network name as the password.
+
+By default, the proxy binds to all available interfaces. To make it
+only listen on (for example) the loopback address:
+
+ /SET irssiproxy_bind 127.0.0.1
+
+Note that bind address changes won't take effect until the proxy is
+disabled and then reenabled.
+
+Once everything is set up, you can enable / disable the proxy:
+
+ /TOGGLE irssiproxy
+
+When the proxy is configured and running, the following command will
+show all the currently connected clients:
+
+ /IRSSIPROXY status
diff --git a/docs/signals.txt b/docs/signals.txt
index f0860d3e..7776dad7 100644
--- a/docs/signals.txt
+++ b/docs/signals.txt
@@ -56,9 +56,7 @@ modules.c:
"module error", int error, char *text, char *rootmodule, char *submodule
network-openssl.c:
- "tlsa available", SERVER_REC
- "tlsa verification success", SERVER_REC
- "tlsa verification failed", SERVER_REC
+ "tls handshake finished", SERVER_REC, TLS_REC
nicklist.c:
"nicklist new", CHANNEL_REC, NICK_REC
@@ -131,6 +129,15 @@ irc-nicklist.c:
irc-servers.c:
"event connected", SERVER_REC
+irc-cap.c
+ "server cap ack "<cmd>, SERVER_REC
+ "server cap nak "<cmd>, SERVER_REC
+ "server cap end", SERVER_REC
+
+sasl.c
+ "server sasl failure", SERVER_REC, char *reason
+ "server sasl success", SERVER_REC
+
irc.c:
"server event", SERVER_REC, char *data, char *sender_nick, char *sender_address
@@ -220,6 +227,7 @@ notifylist.c:
proxy/listen.c:
+ "proxy client connecting", CLIENT_REC
"proxy client connected", CLIENT_REC
"proxy client disconnected", CLIENT_REC
"proxy client command", CLIENT_REC, char *args, char *data
@@ -250,7 +258,7 @@ fe-exec.c:
fe-messages.c:
"message public", SERVER_REC, char *msg, char *nick, char *address, char *target
- "message private", SERVER_REC, char *msg, char *nick, char *address
+ "message private", SERVER_REC, char *msg, char *nick, char *address, char *target
"message own_public", SERVER_REC, char *msg, char *target
"message own_private", SERVER_REC, char *msg, char *target, char *orig_target
"message join", SERVER_REC, char *channel, char *nick, char *address
diff --git a/docs/startup-HOWTO-rus.html b/docs/startup-HOWTO-rus.html
deleted file mode 100644
index 595d1df0..00000000
--- a/docs/startup-HOWTO-rus.html
+++ /dev/null
@@ -1,846 +0,0 @@
- <h2>Startup HOWTO</h2>
-
- <h3>îÏ×ÉÞËÁÍ × Irssi (Á ÎÅ IRC ..)</h3>
-
- <p>&copy; 2000-2002 by Timo Sirainen, ÒÁÓÐÒÏÓÔÒÁÎÑÅÔÓÑ ÐÏÄ ÌÉÃÅÎÚÉÅÊ
- <a href="http://www.gnu.org/licenses/fdl.html">GNU FDL</a> 1.1.<br/>
- îÁ ÒÕÓÓËÉÊ ÑÚÙË ÐÅÒÅ×ÅÄÅÎÏ NiXoiD'ÏÍ (#xakep @ irc.wenet.ru)
- </p>
-
-
-<p>ïÇÌÁ×ÌÅÎÉÅ Ó ÎÅËÏÔÏÒÙÍÉ ×ÏÐÒÏÓÁÍÉ ÉÚ FAQ, ÎÁ ËÏÔÏÒÙÅ ÄÁÅÔÓÑ ÏÔ×ÅÔ × ÐÁÒÁÇÒÁÆÁÈ:</p>
-
-<ol>
-<li><a href="#c1">äÌÑ ÌÅÎÉ×ÙÈ</a>
- <ul>
- <li>õÐÒÁ×ÌÅÎÉÅ ÏËÎÁÍÉ, ÁÎÁÌÏÇÉÞÎÏÅ ircII</li>
- </ul></li>
-<li><a href="#c2">ïÓÎÏ×Ù ÐÏÌØÚÏ×ÁÔÅÌØÓËÏÇÏ ÉÎÔÅÒÆÅÊÓÁ</a>
- <ul>
- <li>òÁÂÏÔÁ Ó "ÒÁÚÄÅÌÅÎÎÙÍÉ" ÏËÎÁÍÉ (Ñ ÔÁË ÐÅÒÅ×£Ì "split windows")</li>
- <li>ëÁË Ñ ÍÏÇÕ ÌÅÇËÏ ÐÅÒÅËÌÀÞÁÔØÓÑ ÍÅÖÄÕ ÏËÎÁÍÉ?</li>
- <li>îÏ alt-1 É.Ô.Ä. ÎÅ ÒÁÂÏÔÁÅÔ!</li>
- </ul></li>
-<li><a href="#c3">á×ÔÏÚÁÈÏÄ ÎÁ ËÁÎÁÌÙ É ÓÅÒ×ÅÒÙ</a>
- <ul>
- <li>ëÁË Á×ÔÏÍÁÔÉÞÅÓËÉ ÐÏÄËÌÀÞÁÔØÓÑ Ë ÓÅÒ×ÅÒÁÍ ÐÒÉ ÚÁÐÕÓËÅ?</li>
- <li>ëÁË Á×ÔÏÍÁÔÉÞÅÓËÉ ÚÁÈÏÄÉÔØ ÎÁ ËÁÎÁÌÙ?</li>
- <li>ëÁË Á×ÔÏÍÁÔÉÞÅÓËÉ ×ÙÐÏÌÎÑÔØ ËÏÍÁÎÄÙ ÐÒÉ ÐÏÄËÌÀÞÅÎÉÉ?</li>
- </ul></li>
-<li><a href="#c4">îÁÓÔÒÏÊËÁ ÏËÏÎ É Á×ÔÏÍÁÔÉÞÅÓËÏÅ ×ÏÓÓÔÁÎÏ×ÌÅÎÉÅ ÉÈ ÐÒÉ ÚÁÐÕÓËÅ</a></li>
-<li><a href="#c5">ïËÎÁ status É msgs &amp; ÕÒÏ×ÎÉ ÓÏÏÂÝÅÎÉÊ</a>
- <ul>
- <li>ñ ÈÏÞÕ ÞÔÏÂÙ ÏÔ×ÅÔ ÎÁ /WHOIS ×Ù×ÏÄÉÌÓÑ × ÔÅËÕÝÅÅ ÏËÎÏ</li>
- <li>ñ ÈÏÞÕ ÞÔÏÂÙ ×ÓÅ ÓÏÏÂÝÅÎÉÑ ×Ù×ÏÄÉÌÉÓØ × ÏÄÎÏÍ ÏËÎÅ</li>
- </ul></li>
-<li><a href="#c6">ëÁË × irssi ÒÁÂÏÔÁÅÔ ÍÎÏÇÏÓÅÒ×ÅÒÎÁÑ ÐÏÄÄÅÒÖËÁ</a>
- <ul>
- <li>ñ ÐÏÄËÌÀÞÉÌÓÑ Ë ÓÅÒ×ÅÒÕ, ËÏÔÏÒÙÊ ÎÅ ÏÔ×ÅÞÁÅÔ É ÔÅÐÅÒØ irssi ÐÙÔÁÅÔÓÑ ÐÏÄËÌÀÞÉÔØÓÑ Ë ÎÅÍÕ ÓÎÏ×Á É ÓÎÏ×Á. ëÁË ÍÎÅ ÏÓÔÁÎÏ×ÉÔØ ÜÔÏ??</li>
- <li>ñ ÈÏÞÕ ÏÔÄÅÌØÎÏÅ ÏËÎÏ ÓÔÁÔÕÓÁ É ÓÏÏÂÝÅÎÉÊ ÄÌÑ ËÁÖÄÏÇÏ ÓÅÒ×ÅÒÁ</li>
- </ul></li>
-<li><a href="#c7">ëÏÍÁÎÄÁ /LASTLOG É ÐÒÏËÒÕÔËÁ ÏËÏÎ</a>
- <ul>
- <li>ëÁË ÓÏÈÒÁÎÉÔØ ×ÅÓØ ÔÅËÓÔ ÉÚ ÏËÎÁ × ÆÁÊÌ?</li>
- </ul></li>
-<li><a href="#c8">÷ÅÄÅÎÉÅ ÌÏÇÏ×</a></li>
-<li><a href="#c9">éÚÍÅÎÅÎÉÅ ËÌÁ×ÉÁÔÕÒÎÙÈ óÏÞÅÔÁÎÉÊ</a>
- <ul>
- <li>ëÁË Ñ ÍÏÇÕ ÚÁÓÔÁ×ÉÔØ F1 ÄÅÌÁÔØ ÞÔÏ-ÔÏ?</li>
- </ul></li>
-<li><a href="#c10">ðÒÏËÓÉ É ÂÏÕÎÓÅÒÙ</a>
- <ul>
- <li>þÔÏ ÔÁËÏÅ irssi-proxy?</li>
- </ul></li>
-<li><a href="#c11">îÁÓÔÒÏÊËÉ Irssi</a></li>
-<li><a href="#c12">óÔÁÔÕÓÂÁÒ</a>
- <ul>
- <li>ñ ÚÁÇÒÕÚÉÌ ÓËÒÉÐÔ ÄÌÑ ÓÔÁÔÕÓÂÁÒÁ, ÎÏ ÅÇÏ ÎÉÇÄÅ ÎÅ ×ÉÄÎÏ!</li>
- </ul></li>
-</ol>
-
-<h3><a id="c1">1. äÌÑ ÌÅÎÉ×ÙÈ</a></h3>
-
-<p>îÅÓËÏÌØËÏ ÐÏÌÅÚÎÙÈ ÎÁÓÔÒÏÅË ÐÏ ÕÍÏÌÞÁÎÉÀ:</p>
-
-<p>åÓÌÉ ÎÅ ÒÁÂÏÔÁÀÔ Ã×ÅÔÁ É ×Ù ÎÅ ÓÏÂÉÒÁÅÔÅÓØ ÉÓÐÏÌØÚÏ×ÁÔØ VT-ÎÅÓÏ×ÍÅÓÔÉÍÙÊ ÔÅÒÍÉÎÁÌ, ÔÏ ÐÒÏÓÔÏ ××ÅÄÉÔÅ:</p>
-
-<pre>
-/SET term_force_colors ON
-</pre>
-
-<p>åÓÌÉ ×Ù ÈÏÔÉÔÅ ÞÔÏÂÙ ×ÓÅ ÓÏÏÂÝÅÎÉÑ ×Ù×ÏÄÉÌÉÓØ × ÏÄÎÏÍ ÏËÎÅ:</p>
-
-<pre>
-/SET autocreate_own_query OFF
-/SET autocreate_query_level DCCMSGS
-/SET use_status_window OFF
-/SET use_msgs_window ON
-</pre>
-
-<p>þÔÏÂÙ ÏËÎÁ Á×ÔÏÍÁÔÉÞÅÓËÉ ÎÅ ÚÁËÒÙ×ÁÌÉÓØ ËÏÇÄÁ ×Ù ÐÏËÉÄÁÅÔÅ ËÁÎÁÌ(<code>/PART</code>)ÉÌÉ ÐÒÉ×ÁÔ
-(<code>/UNQUERY</code>):</p>
-
-<pre>
-/SET autoclose_windows OFF
-/SET reuse_unused_windows ON
-</pre>
-
-<p>þÔÏÂÙ ÕÐÒÁ×ÌÅÎÉÅ ÏËÎÁÍÉ × irssi ÂÙÌÏ ÐÏÈÏÖÅ ÎÁ ircII ××ÅÄÉÔÅ ÜÔÉ ËÏÍÁÎÄÙ:</p>
-
-<pre>
-/SET autocreate_own_query OFF
-/SET autocreate_query_level NONE
-/SET use_status_window OFF
-/SET use_msgs_window OFF
-/SET reuse_unused_windows ON
-/SET windows_auto_renumber OFF
-
-/SET autostick_split_windows OFF
-/SET autoclose_windows OFF
-/SET print_active_channel ON
-</pre>
-
-<p>÷ÏÔ ÐÒÉÍÅÒ ÄÏÂÁ×ÌÅÎÉÑ ÓÅÒ×ÅÒÏ×:</p>
-
-<p>(ÓÅÔØ OFTC, ÉÄÅÎÔÉÆÉÃÉÒÏ×ÁÔØÓÑ ÞÅÒÅÚ nickserv É ÖÄÁÔØ 2 ÓÅËÕÎÄÙ ÐÅÒÅÄ ÚÁÈÏÄÏÍ ÎÁ ËÁÎÁÌÙ)</p>
-
-<pre>
-/IRCNET ADD -autosendcmd "/^msg nickserv identify pass;wait 2000" OFTC
-</pre>
-
-<p>ôÅÐÅÒØ ÄÏÂÁ×ÌÅÎÉÅ ÎÅÓËÏÌØËÉÈ ÓÅÒ×ÅÒÏ× Ë ÒÁÚÎÙÍ ÓÅÔÑÍ (IRC-ÓÅÔØ ÄÌÑ ÎÉÈ ÕÖÅ ÕÓÔÁÎÏ×ÌÅÎÁ),
- irc.kpnqwest.fi ÉÓÐÏÌØÚÕÅÔÓÑ ÐÏ ÄÅÆÏÌÔÕ ÄÌÑ IRCNet ÎÏ ÅÓÌÉ ÏÎ ÎÅ ÄÏÓÔÕÐÅÎ, ÔÏ irssi ÂÕÄÅÔ ÐÙÔÁÔØÓÑ ÐÏÄËÌÀÞÉÔØÓÑ Ë
-irc.funet.fi:</p>
-
-<pre>
-/SERVER ADD -auto -ircnet ircnet irc.kpnqwest.fi 6667
-/SERVER ADD -ircnet ircnet irc.funet.fi 6667
-/SERVER ADD -auto -ircnet efnet efnet.cs.hut.fi 6667
-</pre>
-
-<p>á×ÔÏÚÁÈÏÄ ÎÁ ËÁÎÁÌÙ ÐÒÉ ÐÏÄËÌÀÞÅÎÉÉ Ë ÓÅÒ×ÅÒÕ É ÏÐ-ÚÁÐÒÏÓ ÂÏÔÁ ÐÒÉ ÚÁÈÏÄÅ ÎÁ efnet/#irssi:</p>
-
-<pre>
-/CHANNEL ADD -auto #irssi ircnet
-/CHANNEL ADD -auto -bots *!*bot@host.org -botcmd "/^msg $0 op pass" #irssi efnet
-</pre>
-
-þÔÏÂÙ ÓÔÒÏËÉ, ÓÏÄÅÒÖÁÝÉÅ ÷ÁÛ ÎÉË ÐÏÄÓ×ÅÞÉ×ÁÌÉÓØ:
-
-<pre>
-/HILIGHT ×ÁÛ_ÎÉË
-</pre>
-
-<h3><a id="c2">2. ïÓÎÏ×Ù ÐÏÌØÚÏ×ÁÔÅÌØÓËÏÇÏ ÉÎÔÅÒÆÅÊÓÁ</a></h3>
-
-<p>äÌÑ ÓËÒÏÌÌÉÎÇÁ ÓÏÄÅÒÖÉÍÏÇÏ ÏËÏÎ ÉÓÐÏÌØÚÕÊÔÅ PgUp É PgDown. åÓÌÉ ÏÎÉ ÎÅ ÒÁÂÏÔÁÀÔ, ÉÓÐÏÌØÚÕÊÔÅ ËÎÏÐËÉ Meta-p É Meta-n.
- þÔÏÂÙ ÐÅÒÅÓËÏÞÉÔØ × ÎÁÞÁÌÏ ÉÌÉ ËÏÎÅÃ ÂÕÆÅÒÁ ÉÓÐÏÌØÚÕÊÔÅ ËÏÍÁÎÄÙ <code>/SB HOME</code> É <code>/SB END</code>.</p>
-
-<p>ðÏ ÕÍÏÌÞÁÎÉÀ irssi ÉÓÐÏÌØÚÕÅÔ ÄÌÑ ×ÓÅÇÏ "ÓËÒÙÔÙÅ ÏËÎÁ". óËÒÙÔÏÅ ÏËÎÏ ÓÏÚÄÁÅÔÓÑ ËÁÖÄÙÊ ÒÁÚ ËÏÇÄÁ ×Ù ÚÁÈÏÄÉÔÅ(<code>/JOIN</code>) ÎÁ ËÁÎÁÌ ÉÌÉ ÓÏÚÄÁÅÔÅ ÐÒÉ×ÁÔ(<code>/QUERY</code>)
-Ó ËÅÍ-ÔÏ. åÓÔØ ÎÅÓËÏÌØËÏ ÓÐÏÓÏÂÏ× ÐÅÒÅËÌÀÞÅÎÉÑ ÍÅÖÄÕ ÜÔÉÍÉ ÏËÎÁÍÉ:</p>
-
-<pre>
-Meta-1, Meta-2, .. Meta-0 - ðÅÒÅËÌÀÞÅÎÉÅ ÍÅÖÄÕ ÏËÎÁÍÉ 1-10
-Meta-q .. Meta-o - ðÅÒÅËÌÀÞÅÎÉÅ ÍÅÖÄÕ ÏËÎÁÍÉ 11-19
-/WINDOW &lt;ÎÏÍÅÒ&gt; - ðÅÒÅËÌÀÞÅÎÉÅ ÎÁ ÏËÎÏ Ó ÚÁÄÁÎÎÙÍ ÎÏÍÅÒÏÍ
-Ctrl-P, Ctrl-N - ðÅÒÅËÌÀÞÅÎÉÅ Ë ÐÒÅÄÙÄÕÝÅÍÕ/ÓÌÅÄÕÀÝÅÍÕ ÏËÎÕ
-</pre>
-
-<p>ðÒÏÓÔÅÊÛÉÊ ÓÐÏÓÏÂ ÐÅÒÅËÌÀÞÅÎÉÑ - ÜÔÏ Meta-ÎÏÍÅÒ. þÔÏ ÔÁËÏÅ Meta?
-äÌÑ ÎÅËÏÔÏÒÙÈ ÔÅÒÍÉÎÁÌÏ× ÜÔÏ ALT. åÓÌÉ Õ ×ÁÓ windows-ÓÏ×ÍÅÓÔÉÍÁÑ ËÌÁ×ÉÁÔÕÒÁ, ÔÏ ÜÔÏ ÔÁË-ÖÅ ÍÏÖÅÔ ÂÙÔØ ÌÅ×ÁÑ ËÎÏÐËÁ windows. åÓÌÉ ÏÎÉ ÎÅ ÒÁÂÏÔÁÀÔ, ÔÏ ×ÁÍ ÐÒÉÄÅÔÓÑ ÎÁÓÔÒÏÉÔØ ÎÅËÏÔÏÒÙÅ X-ÒÅÓÕÒÓÙ
-(ÜÔÏ ÒÁÂÏÔÁÅÔ ËÁË × xterm ÔÁË É × rxvt):</p>
-
-<pre>
-XTerm*eightBitInput: false
-XTerm*metaSendsEscape: true
-</pre>
-
-<p>÷ rxvt ×Ù ÔÁË-ÖÅ ÍÏÖÅÔÅ ÕËÁÚÁÔØ ËÁËÁÑ ËÎÏÐËÁ ÓÏÏÔ×ÅÔÓÔ×ÕÅÔ ËÎÏÐËÅ meta, ÔÁË ÞÔÏ ÅÓÌÉ ×Ù ÈÏÔÉÔÅ ÉÓÐÏÌØÚÏ×ÁÔØ Alt ×ÍÅÓÔÏ Win ÄÏÐÉÛÉÔÅ ÜÔÏ × ÆÁÊÌ Ó ÒÅÓÕÒÓÁÍÉ:</p>
-
-<pre>
-rxvt*modifier: alt
-</pre>
-
-<p>÷Ù ÔÁË-ÖÅ ÍÏÖÅÔÅ ÓÄÅÌÁÔØ ÜÔÏ ÐÒÉ ÐÏÍÏÝÉ xmodmap:</p>
-
-<pre>
-xmodmap -e "keysym Alt_L = Meta_L Alt_L"
-</pre>
-
-<p>ôÁË ËÁË-ÖÅ ÕÓÔÁÎÏ×ÉÔØ ÜÔÉ X-ÒÅÓÕÒÓÙ? äÌÑ Debian'Á, ÜÔÏ ÆÁÊÌ
-<code>/etc/X11/Xresources/xterm</code>, × ËÏÔÏÒÙÊ ×Ù ÍÏÖÅÔÅ ÉÈ ÚÁÓÕÎÕÔØ É ÏÎÉ ÂÕÄÕÔ Á×ÔÏÍÁÔÉÞÅÓËÉ ÞÉÔÁÔØÓÑ ÐÒÉ ÓÔÁÒÔÅ ÉËÓÏ×. æÁÊÌÙ <code>~/.Xresources</code> É
-<code>~/.Xdefaults</code> ÔÁË-ÖÅ ÄÏÌÖÎÙ ÒÁÂÏÔÁÔØ. åÓÌÉ ÎÉÞÅÇÏ ÉÚ ×ÙÛÅÐÅÒÅÞÉÓÌÅÎÎÏÇÏ ÎÅ ÒÁÂÏÔÁÅÔ, ÔÏ ÐÒÏÓÔÏ ÓËÏÐÉÒÕÊÔÅ ÉÈ × <code>~/.Xresources</code>
-É ÚÁÇÒÕÚÉÔÅ ËÏÍÁÎÄÏÊ <code>xrdb -merge ~/.Xresources</code>.
-éÚÍÅÎÅÎÉÑ ÎÁÞÉÎÁÀÔ ÄÅÊÓÔ×Ï×ÁÔØ ÔÏÌØËÏ × ÚÁÎÏ×Ï ÚÁÐÕÝÅÎÎÏÍ ÔÅÒÍÉÎÁÌÅ.</p>
-
-<p>íÎÏÇÉÅ SSH ËÌÉÅÎÔÙ ÐÏÄ Windows ÔÁË ÖÅ ÎÅ ÒÁÚÒÅÛÁÀÔ ÉÓÐÏÌØÚÏ×ÁÔØ ËÎÏÐËÕ ALT. ðÒÅËÒÁÓÎÙÊ ËÌÉÅÎÔ, ËÏÔÏÒÙÊ ÐÏÚ×ÏÌÑÅÔ ÄÅÌÁÔØ ÜÔÏ - putty, ×Ù ÍÏÖÅÔÅ ÓËÁÞÁÔØ ÅÇÏ Ó
-<a href="http://www.chiark.greenend.org.uk/~sgtatham/putty/">
-http://www.chiark.greenend.org.uk/~sgtatham/putty/</a>.</p>
-
-<p>ôÁË-ÖÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ ÒÁÚÄÅÌÅÎÉÅ ÏËÏÎ. ÷ÏÔ ËÏÍÁÎÄÙ, ËÏÔÏÒÙÅ ÐÏÚ×ÏÌÑÀÔ ÜÔÏ ÓÄÅÌÁÔØ:</p>
-
-<pre>
-/WINDOW NEW - óÏÚÄÁÔØ ÎÏ×ÏÅ ÒÁÚÄÅÌÅÎÎÏÅ ÏËÎÏ
-/WINDOW NEW HIDE - óÏÚÄÁÔØ ÎÏ×ÏÅ ÓËÒÙÔÏÅ ÏËÎÏ
-/WINDOW CLOSE - úÁËÒÙÔØ ÒÁÚÄÅÌÅÎÎÏÅ ÉÌÉ ÓËÒÙÔÏÅ ÏËÎÏ
-
-/WINDOW HIDE [&lt;number&gt;|&lt;name&gt;] - óÄÅÌÁÔØ ÒÁÚÄÅÌÅÎÎÏÅ ÏËÎÏ ÓËÒÙÔÙÍ
-/WINDOW SHOW &lt;number&gt;|&lt;name&gt; - óÄÅÌÁÔØ ÓËÒÙÔÏÅ ÏËÎÏ ÒÁÚÄÅÌÅÎÎÙÍ
-
-/WINDOW SHRINK [&lt;lines&gt;] - õÍÅÎØÛÉÔØ ÁËÔÉ×ÎÏÅ ÏËÎÏ
-/WINDOW GROW [&lt;lines&gt;] - õ×ÅÌÉÞÉÔØ ÁËÔÉ×ÎÏÅ ÏËÎÏ
-/WINDOW BALANCE - óÂÁÌÁÎÓÉÒÏ×ÁÔØ ÒÁÚÍÅÒÙ ×ÓÅÈ ÒÁÚÄÅÌÅÎÎÙÈ ÏËÏÎ
-</pre>
-
-<p>ðÏ ÕÍÏÌÞÁÎÉÀ Irssi ÉÓÐÏÌØÚÕÅÔ "ÐÒÉËÌÅÉ×ÁÎÉÅ ÏËÏÎ". üÔÏ ÐÏÄÒÁÚÕÍÅ×ÁÅÔ, ÞÔÏ ÏËÎÏ, ÓÏÚÄÁÎÎÏÅ ×ÎÕÔÒÉ ÒÁÚÄÅÌÅÎÎÏÇÏ ÏËÎÁ ÎÅ ÍÏÖÅÔ ÂÙÔØ ÐÅÒÅÍÅÝÅÎÏ ÂÅÚ ÎÅËÏÔÏÒÏÇÏ ÇÅÍÏÒÁ :). îÁÐÒÉÍÅÒ Õ ×ÁÓ ÍÏÖÅÔ ÂÙÔØ ÓÌÅÄÕÀÝÅÅ ÒÁÓÐÏÌÏÖÅÎÉÅ ÏËÏÎ:</p>
-
-<pre>
-Split window 1: win#1 - Status window, win#2 - ïËÎÏ ÓÏÏÂÝÅÎÉÊ
-Split window 2: win#3 - ircnet/#channel1, win#4 - ircnet/#channel2
-Split window 3: win#5 - efnet/#channel1, win#6 - efnet/#channel2
-</pre>
-
-<p>ëÏÇÄÁ ×Ù × ÏËÎÅ win#1 ÎÁÖÉÍÁÅÔÅ ALT-6, irssi ÐÅÒÅËÌÀÞÁÅÔÓÑ ÎÁ ÒÁÚÄÅÌÅÎÎÏÅ ÏËÎÏ
-#3 É ÐÅÒÅÍÅÝÁÅÔ ËÁÎÁÌ efnet/#channel2 × ÁËÔÉ×ÎÏÅ ÏËÎÏ.</p>
-
-<p>ðÒÉ "ÎÅÚÁËÒÅÐÌ£ÎÎÏÍ" ×ÁÒÉÁÎÔÅ ÏËÎÁ ÎÅ ÉÍÅÀÔ ÎÉËÁËÏÊ Ó×ÑÚÉ Ó ÒÁÚÄÅÌÅÎÎÙÍÉ ÏËÎÁÍÉ
-É ÎÁÖÁÔÉÅ ALT-6 × ÏËÎÅ win#1 ÐÅÒÅÍÅÝÁÅÔ ÏËÎÏ win#6 × ÒÁÚÄÅÌÅÎÎÏÅ ÏËÎÏ 1
-É ÄÅÌÁÅÔ ÅÇÏ ÁËÔÉ×ÎÙÍ, ÉÓËÌÀÞÅÎÉÅ ÍÏÖÅÔ ÂÙÔØ ËÏÇÄÁ ÏËÎÏ win#6 ÕÖÅ ×ÉÄÉÍÏ × ËÁËÏÍ-ÔÏ ÄÒÕÇÏÍ
-ÒÁÚÄÅÌÅÎÎÏÍ ÏËÎÅ, irssi ÐÒÏÓÔÏ ÐÅÒÅËÌÀÞÁÅÔÓÑ Ë ÜÔÏÍÕ ÒÁÚÄÅÌÅÎÎÏÍÕ ÏËÎÕ. ôÁËÏÊ ÍÅÔÏÄ ÐÅÒÅËÌÀÞÅÎÉÑ ÍÅÖÄÕ ÏËÎÁÍÉ ÐÒÉÍÅÎÑÅÔÓÑ × ircII É ÅÓÌÉ ÏÎ ×ÁÍ ÐÏÎÒÁ×ÉÌÓÑ ÔÏ ×Ù ÍÏÖÅÔÅ ÁËÔÉ×ÉÚÉÒÏ×ÁÔØ ÅÇÏ ÐÒÉ ÐÏÍÏÝÉ ËÏÍÁÎÄÙ</p>
-
-<pre>
-/SET autostick_split_windows OFF
-</pre>
-
-<p>ëÁÖÄÏÅ ÏËÎÏ ×ÎÕÔÒÉ ÓÅÂÑ ÍÏÖÅÔ ÓÏÄÅÒÖÁÔØ ÍÎÏÇÏ ËÁÎÁÌÏ×, ÐÒÉ×ÁÔÏ× É ÄÒÕÇÉÈ "×ÅÝÅÊ". åÓÌÉ ×Ù ×ÏÏÂÝÅ ÎÅ ÌÀÂÉÔÅ ÏËÎÁ, ÔÏ ×Ù ÍÏÖÅÔÅ ÏÔÍÅÎÉÔØ ÉÈ ËÏÍÁÎÄÏÊ</p>
-
-<pre>
-/SET autocreate_windows OFF [format c: ÎÁÄ£ÖÎÅÅ ;) - ÐÒÉÍ. ÐÅÒÅ×.]
-</pre>
-
-<p>é ÅÓÌÉ ×Ù ÄÅÒÖÉÔÅ ×ÓÅ ËÁÎÁÌÙ × ÏÄÎÏÍ ÏËÎÅ, ÔÏ ×ÁÍ ÎÁ×ÅÒÎÏÅ ÚÁÈÏÞÅÔÓÑ ÞÔÏÂÙ ÉÍÑ ËÁÎÁÌÁ ×Ù×ÏÄÉÌÏÓØ × ËÁÖÄÏÍ ÓÏÏÂÝÅÎÉÉ:</p>
-
-<pre>
-/SET print_active_channel ON
-</pre>
-
-<p>åÓÌÉ ×Ù ÈÏÔÉÔÅ ÓÇÒÕÐÐÉÒÏ×ÁÔØ × ËÁËÏÅ-ÔÏ ÏËÎÏ ÔÏÌØËÏ ÎÅËÏÔÏÒÙÅ ËÁÎÁÌÙ ÉÌÉ ÐÒÉ×ÁÔÙ, ÔÏ ÉÓÐÏÌØÚÕÊÔÅ ÜÔÉ ËÏÍÁÎÄÙ:</p>
-
-<pre>
-/JOIN -window #channel
-/QUERY -window nick
-</pre>
-
-<h3><a id="c3">3. á×ÔÏÚÁÈÏÄ ÎÁ ËÁÎÁÌÙ É ÓÅÒ×ÅÒÙ</a></h3>
-
-<p>÷ Irssi ÍÎÏÇÏÓÅÒ×ÅÒÎÁÑ ÐÏÄÄÅÒÖËÁ éíèï ÏÞÅÎØ ÈÏÒÏÛÁÑ :). äÁÖÅ ÅÓÌÉ ×Ù ÈÏÔÉÔÅ ÏÂÝÁÔØÓÑ ÔÏÌØËÏ × ÏÄÎÏÊ ÓÅÔÉ, ÔÏ ÏÞÅÎØ ÕÄÏÂÎÏ ÓÇÒÕÐÐÉÒÏ×ÁÔØ ×ÓÅ ÓÅÒ×ÅÒÙ ÜÔÏÊ ÓÅÔÉ × ÏÄÎÕ ÇÒÕÐÐÕ Ô.Ë. ÜÔÏ ÐÏÍÏÇÁÅÔ × ÓÌÕÞÁÅ ÎÅ×ÏÚÍÏÖÎÏÓÔÉ ÓÏÅÄÉÎÅÎÉÑ Ó ÇÌÁ×ÎÙÍ ÓÅÒ×ÅÒÏÍ É × ÎÅËÏÔÏÒÙÈ ÄÒÕÇÉÈ ÓÌÕÞÁÑÈ :).
-äÏÐÏÌÎÉÔÅÌØÎÕÀ ÉÎÆÏÒÍÁÃÉÀ Ï ÜÆÆÅËÔÉ×ÎÏÍ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÍÎÏÇÏÓÅÒ×ÅÒÎÏÊ ÐÏÄÄÅÒÖËÉ ÓÍÏÔÒÉÔÅ × ÇÌÁ×Å 6.</p>
-
-<p>äÌÑ ÎÁÞÁÌÁ ×ÁÍ ÎÕÖÎÏ ÕÓÔÁÎÏ×ÉÔØ Ó×ÏÀ IRC-ÓÅÔØ, ÄÌÑ ÜÔÏÇÏ ÉÓÐÏÌØÚÕÊÔÅ ËÏÍÁÎÄÕ <code>/IRCNET</code>,
-ÞÔÏÂÙ ÕÂÅÄÉÔÓÑ, ÞÔÏ ÏÎÁ ÅÝ£ ÎÅ ÕÓÔÁÎÏ×ÌÅÎÁ. åÓÌÉ ÏÎÁ ÎÅ ÕÓÔÁÎÏ×ÌÅÎÁ, ÔÏ ××ÅÄÉÔÅ <code>/IRCNET ADD
-ÉÍÑ_ÓÅÔÉ</code>. åÓÌÉ ×Ù ÈÏÔÉÔÅ, ÞÔÏÂÙ ËÁËÉÅ-ÔÏ ËÏÍÁÎÄÙ Á×ÔÏÍÁÔÉÞÅÓËÉ ×ÙÐÏÌÎÑÌÉÓØ ÐÒÉ ÐÏÄËÌÀÞÅÎÉÉ Ë ÜÔÏÊ ÓÅÔÉ, ÔÏ ×ÏÓÐÏÌØÚÕÊÔÅÓØ ÏÐÃÉÅÊ <code>-autosendcmd</code>.
-÷ÏÔ ÎÅËÏÔÏÒÙÅ ÐÒÉÍÅÒÙ:</p>
-
-<pre>
-/IRCNET ADD -autosendcmd '^msg bot invite' ircnet
-/IRCNET ADD -autosendcmd "/^msg nickserv identify pass;wait 2000" OFTC
-</pre>
-
-<p>ðÏÓÌÅ ÜÔÏÇÏ ×Ù ÄÏÌÖÎÙ ÄÏÂÁ×ÉÔØ Ë ÜÔÏÊ ÓÅÔÉ ÓÅÒ×ÅÒÙ. îÁÐÒÉÍÅÒ:</p>
-
-<pre>
-/SERVER ADD -auto -ircnet ircnet irc.kpnqwest.fi 6667
-/SERVER ADD -auto -ircnet worknet irc.mycompany.com 6667 ÐÁÒÏÌØ
-</pre>
-
-<p>ïÐÃÉÑ <code>-auto</code> ÕËÁÚÙ×ÁÅÔ, ÞÔÏ Ë ÜÔÏÍÕ ÓÅÒ×ÅÒÕ ÎÕÖÎÏ Á×ÔÏÍÁÔÉÞÅÓËÉ ÐÏÄËÌÀÞÁÔØÓÑ ÐÒÉ ÚÁÐÕÓËÅ.
-÷Ù ÎÅ ÄÏÌÖÎÙ ÐÏÍÅÞÁÔØ ÄÒÕÇÉÅ ÓÅÒ×ÅÒÙ ÔÏÊ-ÖÅ ÓÅÔÉ ÏÐÃÉÅÊ <code>-auto</code> - Irssi Á×ÔÏÍÁÔÉÞÅÓËÉ Ë ÎÉÍ ÐÏÄËÌÀÞÉÔÓÑ, ÅÓÌÉ ÓÅÒ×ÅÒ ÐÏÍÅÞÅÎÎÙÊ <code>-auto</code> ÎÅÄÏÓÔÕÐÅÎ.</p>
-
-<p>é ÎÁËÏÎÅÃ ËÁÎÁÌÙ:</p>
-
-<pre>
-/CHANNEL ADD -auto -bots *!*bot@host.org -botcmd "/^msg $0 op pass" #irssi efnet
-/CHANNEL ADD -auto #secret ircnet password
-</pre>
-
-<p>ïÐÃÉÉ <code>-bots</code> É <code>-botcmd</code> ÔÒÅÂÕÀÔ ÎÅÂÏÌØÛÏÇÏ ÐÏÑÓÎÅÎÉÑ.
-ïÎÉ ÉÓÐÏÌØÚÕÀÔÓÑ ÄÌÑ ÔÏÇÏ, ÞÔÏÂÙ Á×ÔÏÍÁÔÉÞÅÓËÉ ÄÁ×ÁÔØ ËÏÍÁÎÄÙ ÂÏÔÕ ÐÒÉ ÚÁÈÏÄÅ ÎÁ ËÁÎÁÌ,
-ÏÂÙÞÎÏ ÄÌÑ Á×ÔÏÍÁÔÉÞÅÓËÏÇÏ ÐÏÌÕÞÅÎÉÑ ÏÐÁ. ÷Ù ÍÏÖÅÔÅ ÚÁÄÁÔØ ÍÎÏÇÏ ÍÁÓÏË ÂÏÔÏ× ÐÒÉ ÐÏÍÏÝÉ ÏÐÃÉÉ
-<code>-bots</code>, ÒÁÚÄÅÌÅÎÎÏÊ ÐÒÏÂÅÌÁÍÉ (ÎÅ ÚÁÂÕÄØÔÅ ×ÚÑÔØ ÜÔÕ ÓÔÒÏËÕ × ËÁ×ÙÞÅË). ðÅÒÅÍÅÎÎÁÑ $0 × ÏÐÃÉÉ
-<code>-botcmd</code> ÕËÁÚÙ×ÁÅÔ ÎÁ ÐÅÒ×ÏÇÏ ÂÏÔÁ × ÓÐÉÓËÅ ÎÁÊÄÅÎÎÙÈ. åÓÌÉ ×Ù ÎÅ ÈÏÔÉÔÅ ÉÓÐÏÌØÚÏ×ÁÔØ ÍÁÓËÉ ÄÌÑ ÂÏÔÏ× (ÎÁÐÒÉÍÅÒ ÅÓÌÉ ÂÏÔ ×ÓÅÇÄÁ ÓÉÄÉÔ ÐÏÄ ÏÄÎÉÍ ÎÉËÏÍ)
-×Ù ÍÏÖÅÔÅ ÕËÁÚÁÔØ ÔÏÌØËÏ ÏÐÃÉÀ <code>-botcmd</code> É ËÏÍÁÎÄÕ.</p>
-
-<h3><a id="c4">4. îÁÓÔÒÏÊËÁ ÏËÏÎ É Á×ÔÏÍÁÔÉÞÅÓËÏÅ ×ÏÓÓÔÁÎÏ×ÌÅÎÉÅ ÐÒÉ ÚÁÐÕÓËÅ</a></h3>
-
-<p>äÌÑ ÎÁÞÁÌÁ ÓÏÚÄÁÊÔÅ ÎÕÖÎÙÅ ÏËÎÁ(ÐÏÄËÌÀÞÉÔÅÓØ Ë ÎÕÖÎÙÍ ÓÅÒ×ÅÒÁÍ, ËÁÎÁÌÁÍ É.Ô.Ä.).
-äÌÑ ÐÅÒÅÍÅÝÅÎÉÑ ÏËÏÎ ÉÓÐÏÌØÚÕÊÔÅ ÓÌÅÄÕÀÝÉÅ ËÏÍÁÎÄÙ:</p>
-
-<pre>
-/WINDOW MOVE LEFT/RIGHT/ÎÏÍÅÒ - ÐÅÒÅÍÅÓÔÉÔØ ÏËÎÏ ×ÌÅ×Ï, ×ÐÒÁ×Ï ÉÌÉ ÎÁ ÕËÁÚÁÎÎÙÊ ÎÏÍÅÒ
-/WINDOW ITEM MOVE &lt;ÎÏÍÅÒ&gt;|&lt;ÉÍÑ&gt; - ÐÅÒÅÍÅÓÔÉÔØ ËÁÎÁÌ ÉÌÉ ÐÒÉ×ÁÔ × ÄÒÕÇÏÅ ÏËÎÏ
-</pre>
-
-<p>ëÏÇÄÁ ×Ó£ ×ÙÇÌÑÄÉÔ ÔÁË, ËÁË ×Ù ÈÏÔÉÔÅ, ÉÓÐÏÌØÚÕÊÔÅ ËÏÍÁÎÄÕ <code>/LAYOUT SAVE</code>
- (É <code>/SAVE</code>, ÅÓÌÉ ÎÅ ×ËÌÀÞÅÎÏ Á×ÔÏÓÏÈÒÁÎÅÎÉÅ) É ËÏÇÄÁ ×Ù × ÓÌÅÄÕÀÝÉÊ ÒÁÚ ÚÁÐÕÓÔÉÔÅ irssi, ÔÏ ÏÎ ×ÓÐÏÍÎÉÔ ÐÏÚÉÃÉÉ ÓÏÈÒÁÎÅÎÎÙÈ ÏËÏÎ.
- üÔÏ "ÚÁÐÏÍÉÎÁÎÉÅ" ÎÅ ÏÚÎÁÞÁÅÔ, ÞÔÏ ÉÓÐÏÌØÚÏ×ÁÎÉÅ ËÏÍÁÎÄÙ <code>/LAYOUT SAVE</code> ÂÕÄÅÔ ÐÒÉ×ÏÄÉÔØ Ë Á×ÔÏÍÁÔÉÞÅÓËÏÍÕ ÐÏÄËÌÀÞÅÎÉÀ Ë ÓÅÒ×ÅÒÁÍ É ÚÁÈÏÄÕ ÎÁ ËÁÎÁÌÙ,
- ÄÌÑ ÜÔÏÇÏ ×Ù ÄÏÌÖÎÙ ÉÓÐÏÌØÚÏ×ÁÔØ ËÏÍÁÎÄÙ <code>/SERVER ADD -auto</code> É <code>/CHANNEL ADD -auto</code>.</p>
-
-<p>þÔÏÂÙ ÉÚÍÅÎÉÔØ ÓÏÈÒÁÎÅÎÎÙÅ ÎÁÓÔÒÏÊËÉ ÏËÏÎ, ÒÁÓÓÔÁ×ØÔÅ ÉÈ × ÎÕÖÎÙÅ ÐÏÚÉÃÉÉ É ÚÁÎÏ×Ï ××ÅÄÉÔÅ ËÏÍÁÎÄÕ <code>/LAYOUT SAVE</code>.
-þÔÏÂÙ ÏÂÎÕÌÉÔØ ÎÁÓÔÒÏÊËÉ ÉÓÐÏÌØÚÕÊÔÅ ËÏÍÁÎÄÕ <code>/LAYOUT RESET.</code></p>
-
-
-<h3><a id="c5">5. ïËÎÁ status É msgs &amp; ÕÒÏ×ÎÉ ÓÏÏÂÝÅÎÉÊ</a></h3>
-
-<p>ðÏ ÕÍÏÌÞÁÎÉÀ "ÄÏÐÏÌÎÉÔÅÌØÎÙÅ ÓÏÏÂÝÅÎÉÑ" ×Ù×ÏÄÑÔÓÑ × ÏËÎÏ ÓÔÁÔÕÓÁ. ðÏÄ ÄÏÐÏÌÎÉÔÅÌØÎÙÍÉ ÐÏÄÒÁÚÕÍÅ×ÁÀÔÓÑ ÓÏÏÂÝÅÎÉÑ, ËÏÔÏÒÙÅ ÎÅ ÐÒÉÎÁÄÌÅÖÁÔ ÎÉ Ë ÏÄÎÏÍÕ ËÁÎÁÌÕ ÉÌÉ ÐÒÉ×ÁÔÕ(ÎÁÐÒÉÍÅÒ ctcp-ÚÁÐÒÏÓÙ).
-îÅËÏÔÏÒÙÈ ÌÀÄÅÊ ÏÎÉ ÒÁÚÄÒÁÖÁÀÔ, ÔÁË ÞÔÏ ÅÓÌÉ ×Ù ÈÏÔÉÔÅ ÉÈ ÓËÒÙÔØ, ÔÏ ××ÅÄÉÔÅ</p>
-
-<pre>
-/SET use_status_window OFF
-</pre>
-
-<p>üÔÏÔ ÐÁÒÁÍÅÔÒ ÚÁÒÁÂÏÔÁÅÔ ÔÏÌØËÏ ÐÏÓÌÅ ÐÅÒÅÚÁÐÕÓËÁ irssi. åÓÌÉ ×Ù ÈÏÔÉÔÅ ÕÄÁÌÉÔØ ÉÈ ÎÅÍÅÄÌÅÎÎÏ, ÔÏ ÐÒÏÓÔÏ ÚÁËÒÏÊÔÅ ÏËÎÏ(<code>/WINDOW CLOSE</code>).</p>
-
-<p>äÒÕÇÏÅ ÏÓÎÏ×ÎÏÅ ÏËÎÏ - ÜÔÏ "ÏËÎÏ ÓÏÏÂÝÅÎÉÊ", ËÕÄÁ ÉÄÕÔ ×ÓÅ ÓÏÏÂÝÅÎÉÑ ÐÒÉ×ÁÔÁ.
-ðÏ ÕÍÏÌÞÁÎÉÀ ÏÎÏ ÏÔËÌÀÞÅÎÏ É ×ÍÅÓÔÏ ÜÔÏÇÏ ÄÌÑ ËÁÖÄÏÇÏ ÐÒÉ×ÁÔÁ ÓÏÚÄÁÅÔÓÑ ÎÏ×ÏÅ ÏËÎÏ. þÔÏÂÙ ×ÓÅ ÓÏÏÂÝÅÎÉÑ ÐÒÉ×ÁÔÁ ÛÌÉ × ÏÄÎÏ ÏËÎÏ ÉÓÐÏÌØÚÕÊÔÅ ËÏÍÁÎÄÕ:</p>
-
-<pre>
-/SET use_msgs_window ON
-/SET autocreate_query_level DCCMSGS (ÉÌÉ ÅÓÌÉ ×Ù ÔÁË-ÖÅ ÎÅ ÈÏÔÉÔÅ
- ÓÏÚÄÁ×ÁÔØ ÎÏ×ÙÅ ÏËÎÁ ÄÌÑ DCC-ÞÁÔÁ ÎÁÐÉÛÉÔÅ NONE)
-</pre>
-
-<p>üÔÏÔ ÐÁÒÁÍÅÔÒ ÔÁË-ÖÅ ÎÅ ÂÕÄÅÔ ÚÁÄÅÊÓÔ×Ï×ÁÎ ÄÏ ÐÅÒÅÚÁÐÕÓËÁ irssi. þÔÏÂÙ ÐÒÉÍÅÎÉÔØ ÅÇÏ ÎÅÍÅÄÌÅÎÎÏ ××ÅÄÉÔÅ:</p>
-
-<pre>
-/WINDOW NEW HIDE - ÓÏÚÄÁÔØ ÏËÎÏ
-/WINDOW NAME (msgs) - ÐÅÒÅÉÍÅÎÏ×ÁÔØ ÅÇÏ × "(msgs)"
-/WINDOW LEVEL MSGS - ÐÅÒÅÎÁÐÒÁ×ÉÔØ ×ÓÅ ÐÒÉ×ÁÔÎÙÅ ÓÏÏÂÝÅÎÉÑ × ÜÔÏ ÏËÎÏ
-/WINDOW MOVE 1 - ÓÄÅÌÁÔØ ÜÔÏ ÏËÎÏ ÐÅÒ×ÙÍ × ÓÐÉÓËÅ
-</pre>
-
-<p>õÞÔÉÔÅ, ÞÔÏ ÎÉ use_msgs_window, ÎÉ use_status_window ÎÅ ÂÕÄÕÔ ÒÁÂÏÔÁÔØ ÅÓÌÉ ÉÓÐÏÌØÚÏ×ÁÎÁ ËÏÍÁÎÄÁ <code>/LAYOUT SAVE</code>.</p>
-
-<p>ôÅÐÅÒØ ÍÙ ÐÏÄÏÛÌÉ Ë ÕÒÏ×ÎÑÍ ÓÏÏÂÝÅÎÉÊ.. þÔÏ ÜÔÏ? ÷ÓÅ ÓÏÏÂÝÅÎÉÑ, ËÏÔÏÒÙÅ ×Ù×ÏÄÉÔ irssi ÉÍÅÀÔ ÏÄÉÎ ÉÌÉ ÂÏÌØÛÅ
-"ÕÒÏ×ÅÎØ ÓÏÏÂÝÅÎÉÊ". ÷ÏÔ ÏÓÎÏ×ÎÙÅ ÕÒÏ×ÎÉ: PUBLIC - ÄÌÑ ÓÏÏÂÝÅÎÉÊ ÎÁ ËÁÎÁÌÁÈ,
-MSGS - ÄÌÑ ÐÒÉ×ÁÔÎÙÈ ÓÏÏÂÝÅÎÉÊ É CRAP ÄÌÑ ÏÓÔÁÌØÎÙÈ ÓÏÏÂÝÅÎÉÊ, ËÏÔÏÒÙÅ ÎÅÌØÚÑ ËÌÁÓÓÉÆÉÃÉÒÏ×ÁÔØ. ÷Ù ÍÏÖÅÔÅ ÐÏÌÕÞÉÔØ ÐÏÌÎÙÊ ÓÐÉÓÏË ÕÒÏ×ÎÅÊ ÐÒÉ ÐÏÍÏÝÉ ËÏÍÁÎÄÙ</p>
-
-<pre>
-/HELP levels
-</pre>
-
-<p>ïËÎÕ ÓÔÁÔÕÓÁ ÐÒÉÓ×ÏÅÎ ÕÒÏ×ÅÎØ <code>ALL -MSGS</code>, ËÏÔÏÒÙÊ ÐÏÄÒÁÚÕÍÅ×ÁÅÔ, ÞÔÏ ×ÓÅ ÓÏÏÂÝÅÎÉÑ,
-ÉÓËÌÀÞÁÑ ÐÒÉ×ÁÔÎÙÅ, ÄÌÑ ËÏÔÏÒÙÈ ÎÅ ÎÁÚÎÁÞÅÎÏ ÄÒÕÇÏÅ ÍÅÓÔÏ ÉÄÕÔ × ÜÔÏ ÏËÎÏ. âÌÁÇÏÄÁÒÑ ÏÐÃÉÉ <code>-MSGS</code> ÏÎÏ ÎÅ ËÏÎÆÌÉËÔÕÅÔ Ó ÏËÎÏÍ ÓÏÏÂÝÅÎÉÊ.</p>
-
-
-<h3><a id="c6">6. ëÁË × irssi ÒÁÂÏÔÁÅÔ ÍÎÏÇÏÓÅÒ×ÅÒÎÁÑ ÐÏÄÄÅÒÖËÁ</a></h3>
-
-<p>÷ ircII É ÎÅËÏÔÏÒÙÈ ÄÒÕÇÉÈ IRC-ËÌÉÅÎÔÁÈ ÍÎÏÇÏÓÅÒ×ÅÒÎÁÑ ÐÏÄÄÅÒÖËÁ ÒÅÁÌÉÚÏ×ÁÎÁ × ×ÉÄÅ ÐÏÍÅÝÅÎÉÑ ×ËÌÁÄËÉ Ó ÓÅÒ×ÅÒÏÍ × ÓÐÉÓÏË ÏËÏÎ
-. ÷ IRSSI îåô. îÅÔ ÎÉËÁËÏÊ Ó×ÑÚÉ ÍÅÖÄÕ ÏËÎÏÍ É ÓÅÒ×ÅÒÏÍ. ÷Ù ÍÏÖÅÔÅ ÐÏÄËÌÀÞÉÔØÓÑ Ë ÄÅÓÑÔÉ ÓÅÒ×ÅÒÁÍ ÏÄÎÏ×ÒÅÍÅÎÎÏ É ÕÐÒÁ×ÌÑÔØ ÉÍÉ ×ÓÅÍÉ ÉÚ ÏÄÎÏÇÏ ÏËÎÁ, ÉÌÉ ÚÁÈÏÄÉÔØ ÎÁ ËÁÎÁÌÙ ÎÁ ËÁÖÄÏÍ ÉÚ ÎÉÈ
-× ÏÄÎÏÍ ÏËÎÅ, ÅÓÌÉ ×Ù ÄÅÊÓÔ×ÉÔÅÌØÎÏ ÜÔÏÇÏ ÈÏÔÉÔÅ. ëÁË ÂÙÌÏ ÓËÁÚÁÎÏ ×Ù ÍÏÖÅÔÅ ÐÏÄËÌÀÞÉÔØÓÑ Ë ÎÏ×ÏÍÕ ÓÅÒ×ÅÒÕ, ÎÅ ÚÁËÒÙ×ÁÑ ÔÅËÕÝÅÇÏ ÓÏÅÄÉÎÅÎÉÑ:</p>
-
-<pre>
-/CONNECT irc.server.org
-</pre>
-
-<p>÷ÍÅÓÔÏ ËÏÍÁÎÄÙ <code>/SERVER</code>, ËÏÔÏÒÁÑ ÚÁËÒÙ×ÁÅÔ ÓÕÝÅÓÔ×ÕÀÝÅÅ
-ÓÏÅÄÉÎÅÎÉÅ. þÔÏÂÙ ÐÏÓÍÏÔÒÅÔØ ÓÐÉÓÏË ÏÓÕÝÅÓÔ×ÌÅÎÎÙÈ ÓÏÅÄÉÎÅÎÉÊ ÉÓÐÏÌØÚÕÊÔÅ ËÏÍÁÎÄÕ <code>/SERVER</code>
-ÂÅÚ ÐÁÒÁÍÅÔÒÏ×. ÷Ù Õ×ÉÄÉÔÅ ÐÒÉÍÅÒÎÏ ÓÌÅÄÕÀÝÅÅ:</p>
-
-<pre>
--!- IRCNet: irc.song.fi:6667 (IRCNet)
--!- OFTC: irc.oftc.net:6667 (OFTC)
--!- RECON-1: 192.168.0.1:6667 () (02:59 left before reconnecting)
-</pre>
-
-<p>úÄÅÓØ ×ÉÄÎÏ, ÞÔÏ ÍÙ ÐÏÄËÌÀÞÅÎÙ Ë ÓÅÔÑÍ IRCNet É OFTC.
-îÁÄÐÉÓØ IRCNet × ÎÁÞÁÌÅ Ñ×ÌÑÅÔÓÑ "ÍÅÔËÏÊ ÓÅÒ×ÅÒÁ" Á
-(IRCnet) × ËÏÎÃÅ ÐÏËÁÚÙ×ÁÅÔ ÓÏÏÔ×ÅÔÓÔ×ÕÀÝÕÀ IRC-ÓÅÔØ. íÅÔËÁ ÓÅÒ×ÅÒÁ ÓÏÏÔ×ÅÔÓÔ×ÕÅÔ ÕÎÉËÁÌØÎÏÍÕ ÉÍÅÎÉ, ËÏÔÏÒÏÅ ÏÂÙÞÎÏ ÓÏ×ÐÁÄÁÅÔ Ó ÎÁÚ×ÁÎÉÅÍ ÓÅÔÉ.
-ëÏÇÄÁ IRC-ÓÅÔØ ÎÅ ÉÚ×ÅÓÔÎÁ ÜÔÏ ËÁËÁÑ-ÔÏ ÞÁÓÔØ ÉÍÅÎÉ ÓÅÒ×ÅÒÁ.
-ëÏÇÄÁ ÏÓÕÝÅÓÔ×ÌÅÎÙ ÎÅÓËÏÌØËÏ ÓÏÅÄÉÎÅÎÉÊ Ó ÏÄÎÏÊ ÓÅÔØÀ ÉÌÉ ÓÅÒ×ÅÒÏÍ, irssi
-ÄÏÂÁ×ÌÑÅÔ ÃÉÆÒÕ ÐÏÓÌÅ ÍÅÔËÉ, ÔÁË ÞÔÏ ÜÔÏ ÍÏÖÅÔ ÂÙÔØ ircnet, ircnet2, ircnet3
-É.Ô.Ä.</p>
-
-<p>íÅÔËÁ ÓÅÒ×ÅÒÁ, ÎÁÞÉÎÁÀÝÁÑÓÑ Ó <code>RECON-</code> ÏÂÏÚÎÁÞÁÅÔ ÐÅÒÅÐÏÄËÌÀÞÅÎÉÅ.
-÷ ×ÙÛÅÐÒÉ×ÅÄÅÎÎÏÍ ÐÒÉÍÅÒÅ ÍÙ ×ÉÄÉÍ, ÞÔÏ ÐÏÄËÌÀÞÅÎÉÅ Ë ÓÅÒ×ÅÒÕ 192.168.0.1 ÂÙÌÏ ÎÅÕÄÁÞÎÙÍ É
-irssi ÐÏÐÒÏÂÕÅÔ ÐÏÄËÌÀÞÉÔØÓÑ ÚÁÎÏ×Ï ÞÅÒÅÚ 3 ÍÉÎÕÔÙ.</p>
-
-<p>þÔÏÂÙ ÏÔËÌÀÞÉÔØÓÑ ÏÔ ÓÅÒ×ÅÒÁ ÉÓÐÏÌØÚÕÊÔÅ ÓÌÅÄÕÀÝÉÅ ËÏÍÁÎÄÙ:</p>
-
-<pre>
-/DISCONNECT ircnet - ÏÔËÌÀÞÉÔØÓÑ ÏÔ ÓÅÒ×ÅÒÁ Ó ÍÅÔËÏÊ "ircnet"
-/DISCONNECT recon-1 - ÏÓÔÁÎÏ×ÉÔØ ÐÏÐÙÔËÉ ÐÅÒÅÐÏÄËÌÀÞÅÎÉÑ Ë ÓÅÒ×ÅÒÕ RECON-1
-/RMRECONNS - ÏÓÔÁÎÏ×ÉÔØ ×ÓÅ ÐÏÐÙÔËÉ ÐÅÒÅÐÏÄËÌÀÞÅÎÉÑ
-
-/RECONNECT recon-1 - ÎÅÍÅÄÌÅÎÎÏ ÐÏÐÒÏÂÏ×ÁÔØ ÐÅÒÅÐÏÄËÌÀÞÉÔØÓÑ Ë RECON-1
-/RECONNECT ALL - ÎÅÍÅÄÌÅÎÎÏ ÐÏÐÒÏÂÏ×ÁÔØ ÐÅÒÅÐÏÄËÌÀÞÉÔØÓÑ ËÏ ×ÓÅÍ ÓÅÒ×ÅÒÁÍ
- × ÏÞÅÒÅÄÉ ÎÁ ÐÏÄËÌÀÞÅÎÉÅ
-</pre>
-
-<p>ôÅÐÅÒØ, ËÏÇÄÁ ×Ù ÐÏÄËÌÀÞÅÎÙ ËÏ ×ÓÅÍ ÓÅÒ×ÅÒÁÍ ×Ù ÄÏÌÖÎÙ ÚÎÁÔØ ËÁË ÕËÁÚÁÔØ ËÁËÏÊ ÉÚ ÎÉÈ ×Ù ÈÏÔÉÔÅ ÉÓÐÏÌØÚÏ×ÁÔØ.
-åÄÉÎÓÔ×ÅÎÎÙÊ ÓÐÏÓÏ - ÜÔÏ ÉÍÅÔØ ÐÕÓÔÏÅ ÏËÎÏ ÎÁÐÏÄÏÂÅ ÏËÎÁ ÓÔÁÔÕÓÁ. ÷ ÎÅÍ ×Ù ÍÏÖÅÔÅ ×ÙÂÒÁÔØ ËÁËÏÊ ÓÅÒ×ÅÒ ÈÏÔÉÔÅ ÓÄÅÌÁÔØ ÁËÔÉ×ÎÙÍ</p>
-
-<pre>
-/WINDOW SERVER tag - ÓÄÅÌÁÔØ ÓÅÒ×ÅÒ Ó ÍÅÔËÏÊ "tag" ÁËÔÉ×ÎÙÍ
-Ctrl-X - óÄÅÌÁÔØ ÓÌÅÄÕÀÝÉÊ × ÓÐÉÓËÅ ÓÅÒ×ÅÒ ÁËÔÉ×ÎÙÍ
-</pre>
-
-<p>ëÏÇÄÁ ÓÅÒ×ÅÒ ÁËÔÉ×ÎÙÊ ×Ù ÍÏÖÅÔÅ ÎÏÒÍÁÌØÎÏ ÅÇÏ ÉÓÐÏÌØÚÏ×ÁÔØ. ëÏÇÄÁ ×Ù ÐÏÄËÌÀÞÅÎÙ Ë ÎÅÓËÏÌØËÉÍ ÓÅÒ×ÅÒÁÍ, irssi ÄÏÂÁ×ÌÑÅÔ ÐÒÅÆÉËÓ [ÍÅÔËÁ_ÓÅÒ×ÅÒÁ]
-ËÏ ×ÓÅÍ ÓÏÏÂÝÅÎÉÑÍ, ÎÅ ÏÔÎÏÓÑÝÉÍÓÑ Ë ËÁÎÁÌÕ ÉÌÉ ÐÒÉ×ÁÔÕ ÔÁË ÞÔÏ ×Ù ÍÏÖÅÔÅ ÚÎÁÔØ Ó ËÁËÏÇÏ ÓÅÒ×ÅÒÁ ÏÎÏ ÐÒÉÛÌÏ.</p>
-
-<p>îÅËÏÔÏÒÙÅ ËÏÍÁÎÄÙ ÔÁË-ÖÅ ÐÏÚ×ÏÌÑÀÔ ÉÓÐÏÌØÚÏ×ÁÔØ ÏÐÃÉÀ <code>-ÍÅÔËÁ_ÓÅÒ×ÅÒÁ</code>
-ÞÔÏÂÙ ÕËÁÚÁÔØ ÄÌÑ ËÁËÏÇÏ ÓÅÒ×ÅÒÁ ×Ù ÈÏÔÉÔŠţ ÉÓÐÏÌØÚÏ×ÁÔØ:</p>
-
-<pre>
-/MSG -ÍÅÔËÁ ÎÉË ÓÏÏÂÝÅÎÉÅ
-/JOIN -ÍÅÔËÁ #ËÁÎÁÌ
-/QUERY -ÍÅÔËÁ ÎÉË
-</pre>
-
-<p>á×ÔÏÄÏÐÏÌÎÅÎÉÅ ËÏÍÁÎÄÙ <code>/MSG</code> ÔÁË-ÖÅ Á×ÔÏÍÁÔÉÞÅÓËÉ ÄÏÂÁ×ÌÑÅÔ ÍÅÔËÕ ÓÅÒ×ÅÒÁ
- ËÏÇÄÁ ÎÉË ÎÅ ÎÁ ÁËÔÉ×ÎÏÍ ÓÅÒ×ÅÒÅ.</p>
-
-<p>ïËÎÏ ÓÅÒ×ÅÒÁ ÍÏÖÎÏ ÓÄÅÌÁÔØ ÚÁËÒÅÐÌ£ÎÎÙÍ. ëÏÇÄÁ ÏÎÏ ÚÁËÒÅÐÌÅÎÏ, ÔÏ ÏÎÏ ÎÉËÏÇÄÁ Á×ÔÏÍÁÔÉÞÅÓËÉ ÎÅ ÐÅÒÅËÌÀÞÉÔÓÑ ÎÁ ËÁËÏÅ-ÔÏ ÄÒÕÇÏÅ, É ÅÓÌÉ ÐÒÏÉÚÏÛÌÏ ÏÔËÌÀÞÅÎÉÅ ÏÔ ÓÅÒ×ÅÒÁ,
-ÔÏ ÏÎÏ ÎÅ ÂÕÄÅÔ ÉÍÅÔØ ÁËÔÉ×ÎÏÇÏ ÓÅÒ×ÅÒÁ. ëÏÇÄÁ Ë ÓÅÒ×ÅÒÕ ÓÎÏ×Á ÐÒÏÉÚ×ÅÄÅÎÏ ÐÏÄËÌÀÞÅÎÉÅ,
-ÔÏ ÏÎ Á×ÔÏÍÁÔÉÞÅÓËÉ ÓÔÁÎÏ×ÉÔÓÑ ÁËÔÉ×ÎÙÍ × ÜÔÏÍ ÏËÎÅ. þÔÏÂÙ ÚÁËÒÅÐÉÔØ ÏËÎÏ ÓÅÒ×ÅÒÁ ÉÓÐÏÌØÚÕÊÔÅ ÓÌÅÄÕÀÝÕÀ ËÏÍÁÎÄÕ:</p>
-
-<pre>
-/WINDOW SERVER -sticky tag
-</pre>
-
-<p>üÔÏ ÐÏÌÅÚÎÏ ÅÓÌÉ ×Ù ÈÏÔÉÔÅ ÉÍÅÔØ ÏÔÄÅÌØÎÙÅ ÏËÎÁ ÓÔÁÔÕÓÁ É ÓÏÏÂÝÅÎÉÊ ÄÌÑ ËÁÖÄÏÇÏ ÓÅÒ×ÅÒÁ. ÷ÏÔ ËÁË ÜÔÏ ÍÏÖÎÏ ÓÄÅÌÁÔØ (ÐÏ×ÔÏÒÉÔÅ ÄÌÑ ËÁÖÄÏÇÏ ÓÅÒ×ÅÒÁ):</p>
-
-<pre>
-/WINDOW NEW HIDE
-/WINDOW NAME (status)
-/WINDOW LEVEL ALL -MSGS
-/WINDOW SERVER -sticky ircnet
-
-/WINDOW NEW HIDE
-/WINDOW NAME (msgs)
-/WINDOW LEVEL MSGS
-/WINDOW SERVER -sticky ircnet
-</pre>
-
-<h3><a id="c7">7. ëÏÍÁÎÄÁ /LASTLOG É ÐÒÏËÒÕÔËÁ ÏËÏÎ</a></h3>
-
-<p>ëÏÍÁÎÄÁ <code>/LASTLOG</code> ÍÏÖÅÔ ÂÙÔØ ÉÓÐÏÌØÚÏ×ÁÎÁ ÄÌÑ ÐÏÉÓËÁ ÔÅËÓÔÁ × ÂÕÆÅÒÅ ÏËÎÁ. ÷ÏÔ ÐÒÏÓÔÅÊÛÉÅ ÐÒÉÍÅÒ٠ţ ÉÓÐÏÌØÚÏ×ÁÎÉÑ:</p>
-
-<pre>
-/LASTLOG ÓÌÏ×Ï - ×Ù×ÅÓÔÉ ×ÓÅ ÓÔÒÏËÉ, ÓÏÄÅÒÖÁÝÉÅ "ÓÌÏ×Ï"
-/LASTLOG word 10 - ×Ù×ÅÓÔÉ ÐÏÓÌÅÄÎÉÅ 10 ÓÔÒÏË, ÓÏÄÅÒÖÁÝÉÈ "word"
-/LASTLOG -topics - ×Ù×ÅÓÔÉ ×ÓÅ ÉÚÍÅÎÅÎÉÑ ÔÏÐÉËÁ
-</pre>
-
-<p>åÓÌÉ ÒÅÚÕÌØÔÁÔÏÍ ×Ù×ÏÄÁ ÄÏÌÖÎÙ ÓÔÁÔØ ÂÏÌÅÅ 1000 ÓÔÒÏË, ÔÏ irssi ÐÒÅÄÐÏÌÏÖÉÔ, ÞÔÏ ×Ù ÄÏÐÕÓÔÉÌÉ ÏÛÉÂËÕ É ×Ù×ÅÄÅÔ ÉÈ ÔÏÌØËÏ Ó ÏÐÃÉÅÊ <code>-force</code>.
-þÔÏÂÙ ÓÏÈÒÁÎÉÔØ ÓÏÄÅÒÖÉÍÏÅ ÂÕÆÅÒÁ ÏËÎÁ × ÆÁÊÌ, ÉÓÐÏÌØÚÕÊÔÅ ÓÌÅÄÕÀÝÕÀ ËÏÍÁÎÄÕ:</p>
-
-<pre>
-/LASTLOG -file ~/irc.log
-</pre>
-
-<p>ðÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÏÐÃÉÉ <code>-file</code> ÏÐÃÉÑ <code>-force</code>
-ÎÅ ÔÒÅÂÕÅÔÓÑ. õ ËÏÍÁÎÄÙ <code>/LASTLOG</code> ÅÓÔØ ÍÎÏÇÏ ÄÒÕÇÉÈ ÏÐÃÉÊ. þÔÏÂÙ ÐÏÌÕÞÉÔØ ÂÏÌÅÅ ÐÏÄÒÏÂÎÕÀ ÓÐÒÁ×ËÕ ÐÏ ÎÅÊ ÉÓÐÏÌØÚÕÊÔÅ <code>/HELP lastlog</code>.</p>
-
-<p>ëÏÇÄÁ ×Ù ÎÁÛÌÉ ÉÎÔÅÒÅÓÏ×Á×ÛÉÅ ×ÁÓ ÓÔÒÏËÉ, ×ÁÍ ÓËÏÒÅÅ ×ÓÅÇÏ ÚÁÈÏÞÅÔÓÑ ÐÏÓÍÏÔÒÅÔØ ÄÒÕÇÉÅ ÐÒÉÌÅÇÁÀÝÉÅ Ë ÎÉÍ ÓÏÏÂÝÅÎÉÑ. ÷ Irssi ÅÓÔØ ËÏÍÁÎÄÁ <code>/SCROLLBACK</code> (ÉÌÉ
-Å£ ÓÉÎÏÎÉÍ - <code>/SB</code>) ÄÌÑ ÐÅÒÅÍÅÝÅÎÉÑ ÐÏ ÂÕÆÅÒÕ ÏËÎÁ.
-ëÏÍÁÎÄÁ <code>/LASTLOG</code> ×Ù×ÏÄÉÔ ÏÒÉÇÉÎÁÌØÎÏÅ ×ÒÅÍÑ ÓÏÏÂÝÅÎÉÑ
- É ×Ù ÍÏÖÅÔÅ ÉÓÐÏÌØÚÏ×ÁÔØ ËÏÍÁÎÄÕ <code>/SB GOTO ÞÞ:ÍÍ</code> ÞÔÏÂÙ "ÐÅÒÅÐÒÙÇÎÕÔØ" Ë ÜÔÏÍÕ ÆÒÁÇÍÅÎÔÕ ÄÉÓËÕÓÓÉÉ.
- þÔÏÂÙ ÐÅÒÅÍÅÓÔÉÔØÓÑ ÏÂÒÁÔÎÏ ×ÎÉÚ ÉÓÐÏÌØÚÕÊÔÅ ËÏÍÁÎÄÕ <code>/SB
-END</code>.</p>
-
-
-<h3><a id="c8">8. ÷ÅÄÅÎÉÅ ÌÏÇÏ×</a></h3>
-
-<p>Irssi ÍÏÖÅÔ Á×ÔÏÍÁÔÉÞÅÓËÉ ×ÅÓÔÉ ÌÏÇ ×ÓÅÈ ×ÁÖÎÙÈ ÓÏÏÂÝÅÎÉÊ ËÏÇÄÁ ×Ù × Ü×ÅÅ
-(<code>/AWAY ÐÒÉÞÉÎÁ</code>). ËÏÇÄÁ ×Ù ×ÙÛÌÉ ÉÚ Ü×ÅÑ
-(ÅÝ£ ÒÁÚ ××ÅÄÉÔÅ <code>/AWAY</code>), ÎÏ×ÙÅ ÓÏÏÂÝÅÎÉÑ × Ü×ÅÊ-ÌÏÇÅ ×Ù×ÏÄÑÔÓÑ ÎÁ ÜËÒÁÎ.
-÷Ù ÍÏÖÅÔÅ ÎÁÓÔÒÏÉÔØ ÅÇÏ ÐÒÉ ÐÏÍÏÝÉ ÓÌÅÄÕÀÝÉÈ ËÏÍÁÎÄ:</p>
-
-<pre>
-/SET awaylog_level MSGS HILIGHT - ÷ÙÂÉÒÁÅÔ ËÁËÏÅ ÓÏÏÂÝÅÎÉÑ ÎÁÄÏ ÚÁÐÉÓÙ×ÁÔØ × ÌÏÇ
-/SET awaylog_file ~/.irssi/away.log - ÷ÙÂÉÒÁÅÔ ÆÁÊÌ ÄÌÑ ÌÏÇÁ
-</pre>
-
-<p>ðÒÏÓÔÅÊÛÉÊ ÓÐÏÓÏ ×ÅÄÅÎÉÑ ÌÏÇÏ× ÐÒÉ ÐÏÍÏÝÉ Irssi - ×ËÌÀÞÅÎÉÅ Á×ÔÏÌÏÇÁ.
-Irssi ÂÕÄÅÔ ×ÅÓÔÉ ÌÏÇÉ ×ÓÅÈ ÓÏÏÂÝÅÎÉÊ × ÚÁÄÁÎÎÙÊ ËÁÔÁÌÏÇ.
-÷Ù ÍÏÖÅÔÅ ×ËÌÀÞÉÔØ ÅÇÏ ÐÒÉ ÐÏÍÏÝÉ ÓÌÅÄÕÀÝÅÊ ËÏÍÁÎÄÙ:</p>
-
-<pre>
-/SET autolog ON
-</pre>
-
-<p>ðÏ ÕÍÏÌÞÁÎÉÀ × ÌÏÇÉ ÚÁÐÉÓÙ×ÁÅÔÓÑ ÐÏÞÔÉ ×Ó£ ËÒÏÍÅ ÕÒÏ×ÎÅÊ CTCPS ÉÌÉ CRAP
-(<code>/WHOIS</code>-ÚÁÐÒÏÓÙ É.Ô.Ä.). ÷Ù ÍÏÖÅÔÅ ÚÁÄÁÔØ ÕÒÏ×ÎÉ ÓÏÏÂÝÅÎÉÊ, ËÏÔÏÒÙÅ ÎÁÄÏ ÐÉÓÁÔØ × ÌÏÇÉ ÓÌÅÄÕÀÝÅÊ ËÏÍÁÎÄÏÊ:</p>
-
-<pre>
-/SET autolog_level ALL -CRAP -CLIENTCRAP -CTCPS (this is the default)
-</pre>
-
-<p>ðÏ ÕÍÏÌÞÁÎÉÀ irssi ÐÉÛÅÔ ÌÏÇ × ~/irclogs/&lt;ÍÅÔËÁ_ÓÅÒ×ÅÒÁ&gt;/&lt;ÃÅÌØ&gt;.log.
-üÔÏ ÎÁÓÔÒÁÉ×ÁÅÔÓÑ ÓÌÅÄÕÀÝÅÊ ËÏÍÁÎÄÏÊ:</p>
-
-<pre>
-/SET autolog_path ~/irclogs/$tag/$0.log (×ÁÒÉÁÎÔ "ÐÏ ÕÍÏÌÞÁÎÉÀ")
-</pre>
-
-<p>åÓÌÉ ÚÁÄÁÎÎÙÊ ËÁÔÁÌÏÇ ÎÅ ÓÕÝÅÓÔ×ÕÅÔ, ÔÏ ÏÎ Á×ÔÏÍÁÔÉÞÅÓËÉ ÓÏÚÄÁÅÔÓÑ. ÷ ÐÅÒÅÍÅÎÎÏÊ $0
-ÓÏÄÅÒÖÉÔÓÑ ÃÅÌØ(ËÁÎÁÌ ÉÌÉ ÎÉË). ÷Ù ÍÏÖÅÔÅ ÎÁÓÔÒÏÉÔØ Irssi ÔÁË, ÞÔÏÂÙ ÏÎ Á×ÔÏÍÁÔÉÞÅÓËÉ ÄÏÂÁ×ÌÑÌ ÄÁÔÕ/×ÒÅÍÑ Ë ÉÍÅÎÉ ÆÁÊÌÁ Ó ÌÏÇÏÍ.
-÷ÏÒÍÁÔ ÄÁÔÙ - "man strftime" :). ÷ÏÔ ÐÒÉÍÅÒ:</p>
-
-<pre>
-/SET autolog_path ~/irclogs/%Y/$tag/$0.%m-%d.log
-</pre>
-
-<p>þÔÏÂÙ ×ÅÓÔÉ ÌÏÇÉ ÔÏÌØËÏ ÐÏ ËÁËÉÍ-ÔÏ ÏÔÄÅÌØÎÙÍ ËÁÎÁÌÁÍ ÉÌÉ ÎÉËÁÍ ÓÍÏÔÒÉÔÅ <code>/HELP
-log</code></p>
-
-
-<h3><a id="c9">9. éÚÍÅÎÅÎÉÅ ËÌÁ×ÉÁÔÕÒÎÙÈ ÓÏÞÅÔÁÎÉÊ</a></h3>
-
-<p>÷Ù ÍÏÖÅÔÅ ÉÚÍÅÎÉÔØ ÌÀÂÏÅ ËÌÁ×ÉÁÔÕÒÎÏÅ ÓÏÞÅÔÁÎÉÅ, Ï ËÏÔÏÒÏÍ ÔÅÒÍÉÎÁÌ ÄÁ£Ô ÚÎÁÔØ irssi.
-ôÏ ÅÓÔØ irssi "×ÉÄÉÔ" ÎÅ ×ÓÅ ËÌÁ×ÉÁÔÕÒÎÙÅ ÓÏÞÅÔÁÎÉÑ, ÎÁÐÒÉÍÅÒ ÏÎ ÎÅ ÂÕÄÅÔ ÒÅÁÇÉÒÏ×ÁÔØ ÎÁ
-shift-backspace ÅÓÌÉ ×Ù ËÁË-ÔÏ ÎÅ ÏÔÒÅÄÁËÔÉÒÕÅÔÅ ÓÏÏÔ×ÅÔÓÔ×ÕÀÝÉÅ X-ÒÅÓÕÒÓÙ.</p>
-
-<p>ëÏÍÁÎÄÁ <code>/HELP bind</code> ÄÁ£Ô ÎÁÍÎÏÇÏ ÂÏÌØÛÅ ÉÎÆÏÒÍÁÃÉÉ Ï ËÌÁ×ÉÁÔÕÒÎÙÈ ÓÏÞÅÔÁÎÉÑÈ, ÞÅÍ ÐÒÉ×ÅÄÅÎÏ ÚÄÅÓØ.
-ïÂÙÞÎÏ ÐÒÏÂÌÅÍÍÏÊ Ñ×ÌÑÅÔÓÑ "ÚÁÂÉ×ÁÎÉÅ" ËÁËÉÈ-ÔÏ ÎÅ ÓÔÁÎÄÁÒÔÎÙÈ ËÌÁ×ÉÛ.
-ïÎÉ ÎÅÍÎÏÇÏ ÒÁÚÌÉÞÎÙ ÄÌÑ ËÁÖÄÏÇÏ ÔÅÒÍÉÎÁÌÁ, ÔÁË ÞÔÏ ×Ù ÄÏÌÖÎÙ ÂÕÄÅÔÅ ÕÚÎÁÔØ ÞÔÏ ÉÍÅÎÎÏ ÄÁ£Ô ÎÁÖÁÔÉÅ ÜÔÏÊ ËÌÁ×ÉÛÉ.
-ðÒÏÓÔÅÊÛÉÊ ÐÕÔØ ÕÚÎÁÔØ ÜÔÏ - ×ÙÐÏÌÎÉÔØ × ËÏÎÓÏÌÉ <code>cat</code> É ÐÏÓÍÏÔÒÅÔØ ÞÔÏ ÂÕÄÅÔ ×Ù×ÏÄÉÔÓÑ ÐÒÉ ÎÁÖÁÔÉÉ ÜÔÏÊ ËÌÁ×ÉÛÉ.
-÷ÏÔ ÐÒÉÍÅÒ ÎÁÖÁÔÉÑ ËÌÁ×ÉÛÉ F1:</p>
-
-<pre>
-[cras@hurina] ~% cat
-^[OP
-</pre>
-
-<p>ôÁË ÞÔÏ × irssi ÞÔÏÂÙ "ÚÁÂÉÔØ" ÞÔÏ-ÔÏ ÎÁ F1 ×Ù ÄÏÌÖÎÙ ÂÕÄÅÔÅ ÉÓÐÏÌØÚÏ×ÁÔØ ËÏÍÁÎÄÕ <code>/BIND ^[OP /ECHO ÎÁÖÁÔÁ ËÌÁ×ÉÛÁ F1</code>.
-åÓÌÉ ×Ù ÉÓÐÏÌØÚÕÅÔÅ ÒÁÚÎÙÅ ÔÅÒÍÉÎÁÌÙ, ËÏÔÏÒÙÅ ÐÏ ÒÁÚÎÏÍÕ ÒÁÓÐÏÚÎÁÀÔ ÎÁÖÁÔÉÅ ÏÄÎÏÊ É ÔÏÊ-ÖÅ ËÌÁ×ÉÛÉ, ÔÏ ×ÁÍ ÌÕÞÛÅ ÉÓÐÏÌØÚÏ×ÁÔØ ÞÔÏ-ÔÏ ×ÒÏÄÅ ÜÔÏÇÏ:</p>
-
-<pre>
-/BIND ^[OP key F1
-/BIND ^[11~ key F1
-/BIND F1 /ECHO ÎÁÖÁÔÁ ËÌÁ×ÉÛÁ F1.
-</pre>
-
-<h3><a id="c10">10. ðÒÏËÓÉ É ÂÏÕÎÓÅÒÙ</a></h3>
-
-<p>Irssi ÐÏÄÄÅÒÖÉ×ÁÅÔ ÐÏÄËÌÀÞÅÎÉÅ Ë IRC-ÓÅÒ×ÅÒÁÍ ÞÅÒÅÚ ÐÒÏËÓÉ. åÓÌÉ ×Ù ×Ó£ ÐÒÁ×ÉÌØÎÏ ÓÄÅÌÁÅÔÅ, ÔÏ ×ÓÅ ÐÏÄËÌÀÞÅÎÉÑ ÂÕÄÕÔ ÏÓÕÝÅÓÔ×ÌÑÔØÓÑ ÞÅÒÅÚ ÎÅÇÏ É ×ÁÍ ÎÅ ÎÁÄÏ ÂÕÄÅÔ ××ÏÄÉÔØ ÎÉËÁËÉÈ ÄÏÐÏÌÎÉÔÅÌØÎÙÈ ËÏÍÁÎÄ.</p>
-
-<p>÷ÏÔ ÐÒÉÍÅÒ: õ ×ÁÓ ÅÓÔØ ÂÏÕÎÓÅÒ, ×ÉÓÑÝÉÊ ÎÁ
-irc.bouncer.org 5000. ÷Ù ÈÏÔÉÔÅ ÉÓÐÏÌØÚÏ×ÁÔØ ÅÇÏ ÄÌÑ ÐÏÄËÌÀÞÅÎÉÑ Ë ÓÅÒ×ÅÒÁÍ irc.dal.net É irc.efnet.org. äÌÑ ÎÁÞÁÌÁ ×Ù ÄÏÌÖÎÙ ÂÕÄÅÔÅ ÎÁÓÔÒÏÉÔØ ÂÏÕÎÓÅÒ:</p>
-
-<pre>
-/SET use_proxy ON (×ËÌÀÞÉÔØ ÉÓÐÏÌØÚÏ×ÁÎÉÅ ÐÒÏËÓÉ)
-/SET proxy_address irc.bouncer.org
-/SET proxy_port 5000
-
-/SET proxy_password ÷áû_ðáòïìø
-/SET -clear proxy_string
-/SET proxy_string_after conn %s %d
-</pre>
-
-<p>ðÏÔÏÍ ×ÁÍ ÎÕÖÎÏ ÂÕÄÅÔ ÄÏÂÁ×ÉÔØ ÎÕÖÎÙÅ ÓÅÒ×ÅÒÙ. üÔÏ ÄÅÌÁÅÔÓÑ ÔÏÞÎÏ ÔÁË-ÖÅ, ËÁË ÅÓÌÉ ÂÙ ×Ù ÈÏÔÅÌÉ ÐÏÄËÌÀÞÉÔØÓÑ Ë ÎÉÍ ÎÁÐÒÑÍÕÀ:</p>
-
-<pre>
-/SERVER ADD -auto -ircnet dalnet irc.dal.net
-/SERVER ADD -auto -ircnet efnet irc.efnet.org
-</pre>
-
-<p>ðÏÓÌÅ ÔÏÇÏ, ËÁË ×Ù ÓÄÅÌÁÌÉ ×ÙÛÅÐÅÒÅÞÉÓÌÅÎÎÙÅ ÎÁÓÔÒÏÊËÉ ×ÓÅ ÓÏÅÄÉÎÅÎÉÑ irssi ÂÕÄÅÔ ÐÒÏÉÚ×ÏÄÉÔØ ÞÅÒÅÚ ÐÒÏËÓÀ.</p>
-
-<p>åÓÌÉ ×Ù ÎÅ ÈÏÔÉÔÅ ÉÓÐÏÌØÚÏ×ÁÔØ ÐÒÏËÓÀ ÄÌÑ ËÁËÏÇÏ-ÔÏ ÓÅÒ×ÅÒÁ, ÔÏ ÐÒÉ ÅÇÏ ÄÏÂÁ×ÌÅÎÉÉ ÕËÁÖÉÔÅ ÏÐÃÉÀ
-<code>-noproxy</code>.</p>
-
-<p><strong>óÐÅÃÉÆÉÞÎÙÅ ÎÁÓÔÒÏÊËÉ ÄÌÑ ÒÁÚÎÙÈ ÔÉÐÏ× ÐÒÏËÓÉ:</strong></p>
-
-<p>ïÂÙÞÎÙÅ ÎÁÓÔÒÏÊËÉ:</p>
-
-<pre>
-/SET use_proxy ON
-/SET proxy_address &lt;áÄÒÅÓ ÐÒÏËÓÉ&gt;
-/SET proxy_port &lt;ðÏÒÔ&gt;
-</pre>
-
-<p><strong>HTTP proxy</strong></p>
-
-<p>éÓÐÏÌØÚÕÊÔÅ ÜÔÉ ÎÁÓÔÒÏÊËÉ ÄÌÑ HTTP-ÐÒÏËÓÉ:</p>
-
-<pre>
-/SET -clear proxy_password
-/EVAL SET proxy_string CONNECT %s:%d\n\n
-</pre>
-
-<p><strong>BNC</strong></p>
-
-<pre>
-/SET proxy_password ×ÁÛ_ÐÁÒÏÌØ
-/SET -clear proxy_string
-/SET proxy_string_after conn %s %d
-</pre>
-
-<p><strong>dircproxy</strong></p>
-
-<p>dircproxy ÐÒÏÉÚ×ÏÄÉÔ ÐÏÄËÌÀÞÅÎÉÑ Ë ÓÅÒ×ÅÒÁÍ ÐÏ ÐÁÒÏÌÑÍ. ôÁË ÞÔÏ ÅÓÌÉ ÎÁÐÒÉÍÅÒ ×Ù ÈÏÔÉÔÅ ÐÏÄËÌÀÞÉÔØÓÑ Ë ÓÅÒ×ÅÒÕ ircnet Ó ÐÁÒÏÌÅÍ ircpass
-É Ë OFTC Ó ÐÁÒÏÌÅÍ oftcpass, ×Ù ÄÏÌÖÎÙ ÓÄÅÌÁÔØ ÐÒÉÍÅÒÎÏ ÓÌÅÄÕÀÝÅÅ:</p>
-
-<pre>
-/SET -clear proxy_password
-/SET -clear proxy_string
-
-/SERVER ADD -auto -ircnet ircnet fake.ircnet 6667 ircpass
-/SERVER ADD -auto -ircnet OFTC fake.oftc 6667 oftcpass
-</pre>
-
-<p>éÍÑ ÓÅÒ×ÅÒÁ É ÐÏÒÔ, ËÏÔÏÒÙÅ ×Ù ××ÏÄÉÔÅ ÎÉÇÄÅ ÎÅ ÉÓÐÏÌØÚÕÀÔÓÑ, ÔÁË ÞÔÏ ×Ù ÍÏÖÅÔÅ ÐÉÓÁÔØ ÓÀÄÁ ×Ó£ ÞÔÏ ÕÇÏÄÎÏ.</p>
-
-<p><strong>psyBNC</strong></p>
-
-<p>psyBNC ÉÍÅÅÔ ×ÎÕÔÒÅÎÎÀÀ ÍÎÏÇÏÓÅÒ×ÅÒÎÕÀ ÐÏÄÄÅÒÖËÕ.
-üÔÏ ÍÏÖÅÔ ÄÏÓÔÁ×ÌÑÔØ ÎÅÂÏÌØÛÉÅ ÎÅÕÄÏÂÓÔ×Á É ÎÅËÏÔÏÒÙÅ ÌÀÄÉ ÐÒÏÓÔÏ ÉÓÐÏÌØÚÕÀÔ ÒÁÚÎÙÅ ÌÏÇÉÎÙ ÄÌÑ ÐÏÄËÌÀÞÅÎÉÑ Ë ÎÅÓËÏÌØËÉÍ ÓÅÒ×ÅÒÁÍ.
-÷Ù ÏÞÅÎØ ÐÒÏÓÔÏ ÍÏÖÅÔÅ ÄÅÌÁÔØ ÜÔÏ ÓÒÅÄÓÔ×ÁÍÉ Irssi:</p>
-
-<pre>
-/SET -clear proxy_password
-/SET -clear proxy_string
-
-/IRCNET ADD -user ircnetuser ircnet
-/SERVER ADD -auto -ircnet ircnet fake.ircnet 6667 ircpass
-/IRCNET ADD -user oftcuser OFTC
-/SERVER ADD -auto -ircnet OFTC fake.oftc 6667 oftcpass
-</pre>
-
-<p>úÄÅÓØ ÐÒÉ ÐÏÍÏÝÉ ËÏÍÁÎÄÙ <code>/IRCNET ADD</code> ×Ù ÚÁÄÁÅÔÅ ÉÍÅÎÁ ÐÏÌØÚÏ×ÁÔÅÌÅÊ
-É ÐÁÒÏÌÉ ÐÒÉ ÐÏÍÏÝÉ <code>/SERVER ADD</code>.</p>
-
-<p><strong>Irssi proxy</strong></p>
-
-<p>Irssi ×ËÌÀÞÁÅÔ Ó×ÏÀ ÓÏÂÓÔ×ÅÎÎÕÀ ÐÒÏËÓÀ, ËÏÔÏÒÕÀ ×Ù ÍÏÖÅÔÅ ÓÏÂÒÁÔØ ÐÒÉ ÐÏÍÏÝÉ ÏÐÃÉÉ configure
-<code>--with-proxy</code>. þÔÏÂ٠ţ ÉÓÐÏÌØÚÏ×ÁÔØ ×Ù ÄÏÌÖÎÙ ÏÓÔÁ×ÌÑÔØ irssi ÚÁÐÕÝÅÎÎÙÍ.</p>
-
-<p>Irssi-ÐÒÏËÓÑ ÎÅÍÎÏÇÏ ÏÔÌÉÞÁÅÔÓÑ ÏÔ ÏÓÔÁÌØÎÙÈ ÐÒÏËÓÉ-ÓÅÒ×ÅÒÏ×, ÎÏÒÍÁÌØÎÙÅ ÐÒÏËÓÉ ÓÏÚÄÁÀÔ ÎÏ×ÙÅ ÓÏÅÄÉÎÅÎÉÑ Ó IRC-ÓÅÒ×ÅÒÏÍ ËÏÇÄÁ ×Ù ÈÏÔÉÔÅ Ë ÎÅÍÕ ÐÏÄËÌÀÞÉÔØÓÑ, Á
-<strong>irssi-ÐÒÏËÓÑ ÉÓÐÏÌØÚÕÅÔ ÕÖÅ ÓÕÝÅÓÔ×ÕÀÝÅÅ ÓÏÅÄÉÎÅÎÉÅ(Ñ) ÄÌÑ ×ÓÅÈ ËÌÉÅÎÔÏ×</strong>. éÌÉ ÅÝ£ ÐÏÎÑÔÎÅÅ: <strong>÷Ù ÍÏÖÅÔÅ ÉÓÐÏÌØÚÏ×ÁÔØ ÔÏÌØËÏ ÏÄÎÏ ÓÏÅÄÉÎÅÎÉÅ Ó IRC-ÓÅÒ×ÅÒÏÍ ÄÌÑ ÎÅÏÇÒÁÎÉÞÅÎÎÏÇÏ ÞÉÓÌÁ ËÌÉÅÎÔÏ×</strong>.</p>
-
-<p>Irssi-ÐÒÏËÓÑ ÍÏÖÅÔ ÒÁÓÐÒÅÄÅÌÑÔØ ÎÅÓËÏÌØËÏ ÓÏÅÄÉÎÅÎÉÊ Ó ÓÅÒ×ÅÒÁÍÉ ÎÁ ÒÁÚÎÙÅ ÐÏÒÔÙ, ÎÁÐÒÉÍÅÒ ÎÁ 2777-ÏÍ ÐÏÒÔÕ Õ ×ÁÓ ÍÏÖÅÔ ÂÙÔØ ÓÏÅÄÉÎÅÎÉÅ Ó ircnet, Á ÎÁ 2778 Ó efnet.</p>
-
-<p>éÓÐÏÌØÚÏ×ÁÎÉÅ ÎÁ ÓÔÏÒÏÎÅ ÐÒÏËÓÉ:</p>
-
-<pre>
-/LOAD proxy
-/SET irssiproxy_password &lt;ÐÁÒÏÌØ&gt;
-/SET irssiproxy_ports &lt;IRC_ÓÅÔØ&gt;=&lt;ÐÏÒÔ&gt; ... (ÎÁÐÒÉÍÅÒ ircnet=2777 efnet=2778)
-</pre>
-<p>÷Ù <strong>ÄÏÌÖÎÙ</strong> ÄÏÂÁ×ÉÔØ ×ÓÅ ÓÅÒ×ÅÒÙ, ËÏÔÏÒÙÅ ×Ù ÉÓÐÏÌØÚÕÅÔÅ × ÓÐÉÓËÉ ÓÅÒ×ÅÒÏ× É ÓÅÔÅÊ
-ÐÒÉ ÐÏÍÏÝÉ ËÏÍÁÎÄ <code>/SERVER ADD</code> É
-<code>/IRCNET ADD</code>. ..ÒÁÚ×Å ÞÔÏ ÅÓÌÉ ×Ù ÈÏÔÉÔÅ ÉÓÐÏÌØÚÏ×ÁÔØ ÔÏÌØËÏ ÏÄÎÏ ÓÏÅÄÉÎÅÎÉÅ, ÔÏ ×Ù ÍÏÖÅÔÅ ÕËÁÚÁÔØ:</p>
-
-<pre>
-/SET irssiproxy_ports *=2777
-</pre>
-
-<p>éÓÐÏÌØÚÏ×ÁÎÉÅ ÎÁ ÓÔÏÒÏÎÅ ËÌÉÅÎÔÁ:</p>
-
-<p>ðÒÏÓÔÏ ÐÏÄËÌÀÞÉÔÅÓØ Ë ÐÒÏËÓÅ ËÁË Ë ÎÏÒÍÁÌØÎÏÍÕ ÓÅÒ×ÅÒÕ Ó ÐÁÒÏÌÅÍ, ÚÁÄÁÎÎÙÍ ËÏÍÁÎÄÏÊ <code>/SET irssiproxy_password</code>. ðÒÉÍÅÒ:</p>
-
-<pre>
-/SERVER ADD -ircnet ircnet my.irssi-proxy.org 2777 secret
-/SERVER ADD -ircnet efnet my.irssi-proxy.org 2778 secret
-</pre>
-
-<p>Irssi-ÐÒÏËÓÑ ÔÁË-ÖÅ ÎÏÒÍÁÌØÎÏ ÒÁÂÏÔÁÅÔ Ó ÄÒÕÇÉÍÉ irc-ËÌÉÅÎÔÁÍÉ.</p>
-
-<p><strong>SOCKS</strong></p>
-
-Irssi ÍÏÖÅÔ ÂÙÔØ ÓÏÂÒÁÎ Ó ÐÏÄÄÅÒÖËÏÊ socks-ÐÒÏËÓÉ (ÏÐÃÉÑ configure <code>--with-socks</code>),
-ÎÏ Ñ ÎÁ ÓÁÍÏÍ ÄÅÌÅ ÎÅ ÚÎÁÀ ËÁË ÏÎÏ ÒÁÂÏÔÁÅÔ. îÁÓÔÒÏÊËÉ <code>/SET
-proxy</code> ÎÁ ÜÔÉ ÐÒÏËÓÉ ÎÉËÁË ÎÅ ÄÅÊÓÔ×ÕÀÔ.
-
-<p><strong>äÒÕÇÉÅ ÐÒÏËÓÉ</strong></p>
-
-<p>IRC-ÂÏÕÎÓÅÒÙ ÏÂÙÞÎÏ ÒÁÂÏÔÁÀÔ ÔÏÞÎÏ ÔÁË-ÖÅ ËÁË É IRC-ÓÅÒ×ÅÒÙ, ÎÏ ÐÒÏÓÑÔ ÐÁÒÏÌØ. ÷Ù ÍÏÖÅÔÅ ÄÁÔØ ÉÍ ÅÇÏ ÐÒÉ ÐÏÍÏÝÉ ÓÌÅÄÕÀÝÅÊ ËÏÍÁÎÄÙ:</p>
-
-<pre>
-/SET proxy_password &lt;ÐÁÒÏÌØ&gt;
-</pre>
-
-<p>CONNECT-ÓÔÒÏËÉ ÐÏ ÕÍÏÌÞÁÎÉÀ:</p>
-
-<pre>
-/SET proxy_string CONNECT %s %d
-/SET proxy_string_after
-</pre>
-
-<p>proxy_string ÏÔÐÒÁ×ÌÑÀÔÓÑ ÐÅÒÅÄ ËÏÍÁÎÄÁÍÉ NICK/USER, Á
-proxy_string_after ÏÔÐÒÁ×ÌÑÅÔÓÑ ÐÏÓÌÅ ÎÉÈ. %s and %d can be used with both
-of them.</p>
-
-<h3><a id="c11">11. îÁÓÔÒÏÊËÉ Irssi</a></h3>
-
-<p>÷ÁÍ ÍÏÇÕÔ ÎÅ ÐÏÎÒÁ×ÉÔÓÑ ÎÁÓÔÒÏÊËÉ Irssi ÐÏ ÕÍÏÌÞÁÎÉÀ.
-÷ÏÔ ÎÅËÏÔÏÒÙÅ ÉÚ ÎÉÈ, ËÏÔÏÒÙÅ ×Ù ÓËÏÒÅÅ ×ÓÅÇÏ ÚÁÈÏÔÉÔÅ ÉÚÍÅÎÉÔØ(× ÐÒÉÍÅÒÁÈ ÐÒÉ×ÅÄÅÎÙ "ÕÍÏÌÞÁÌØÎÙÅ" ÚÎÁÞÅÎÉÑ):</p>
-
-<p><strong>ïËÎÁ ÐÒÉ×ÁÔÏ×</strong></p>
-
-<dl>
-<dt>/SET autocreate_own_query ON</dt>
- <dd>á×ÔÏÍÁÔÉÞÅÓËÉ ÓÏÚÄÁ×ÁÔØ ÏËÎÏ ÐÒÉ×ÁÔÁ ËÏÇÄÁ ×Ù ÏÔÐÒÁ×ÌÑÅÔÅ ËÏÍÕ-ÔÏ ÓÏÏÂÝÅÎÉÅ ÐÒÉ ÐÏÍÏÝÉ ËÏÍÁÎÄÙ <code>/MSG</code>.</dd>
-
-<dt>/SET autocreate_query_level MSGS</dt>
- <dd>ðÒÉ ÐÏÌÕÞÅÎÉÉ ÓÏÏÂÝÅÎÉÊ ÏËÎÏ ÐÒÉ×ÁÔÁ ÓÏÚÄÁÅÔÓÑ Ó ÜÔÉÍ ÕÒÏ×ÎÅÍ ÓÏÏÂÝÅÎÉÊ. óÅÊÞÁÓ ÒÁÂÏÔÁÀÔ ÔÏÌØËÏ MSGS, DCCMSGS É NOTICES.
- ÷Ù ÍÏÖÅÔÅ ÏÔÍÅÎÉÔØ ÜÔÏ ÐÒÉ ÐÏÍÏÝÉ ËÏÍÁÎÄÙ <code>/SET -clear autocreate_query_level</code>.</dd>
-
-<dt>/SET autoclose_query 0</dt>
- <dd>ïËÎÁ ÐÒÉ×ÁÔÏ× ÍÏÇÕÔ ÂÙÔØ Á×ÔÏÍÁÔÉÞÅÓËÉ ÚÁËÒÙÔÙ ÐÏÓÌÅ ÚÁÄÁÎÎÏÇÏ "ÐÒÏÓÔÏÑ". áËÔÉ×ÎÏÅ ÏËÎÏ É ÏËÎÁ Ó ÎÅÐÒÏÞÉÔÁÎÎÙÍÉ ÓÏÏÂÝÅÎÉÑÍÉ ÎÅ ÚÁËÒÙ×ÁÀÔÓÑ. úÎÁÞÅÎÉÅ ÚÁÄÁÅÔÓÑ × ÓÅËÕÎÄÁÈ.</dd>
-</dl>
-
-<p><strong>ïËÎÁ</strong></p>
-
-<dl>
-<dt>/SET use_msgs_window OFF</dt>
- <dd>óÏÚÄÁ×ÁÔØ ÏËÎÏ ÓÏÏÂÝÅÎÉÊ ÐÒÉ ÚÁÐÕÓËÅ. ÷ÓÅ ÐÒÉ×ÁÔÎÙÅ ÓÏÏÂÝÅÎÉÑ ÂÕÄÕÔ ÎÁÐÒÁ×ÌÑÔØÓÑ × ÜÔÏ ÏËÎÏ.
- üÔÏ ÉÍÅÅÔ ÓÍÙÓÌ ÔÏÌØËÏ ÅÓÌÉ ×Ù ÏÔÍÅÎÉÌÉ Á×ÔÏÓÏÚÄÁÎÉÅ ÏËÏÎ ÐÒÉ×ÁÔÏ×.
- üÔÏ ÏËÎÏ ÔÁË-ÖÅ ÍÏÖÅÔ ÂÙÔØ ÓÏÚÄÁÎÏ ×ÒÕÞÎÕÀ ÐÒÉ ÐÏÍÏÝÉ ËÏÍÁÎÄÙ /WINDOW LEVEL
- MSGS, /WINDOW NAME (msgs).</dd>
-
-<dt>/SET use_status_window ON</dt>
- <dd>óÏÚÄÁ×ÁÔØ ÏËÎÏ ÓÔÁÔÕÓÁ ÐÒÉ ÚÁÐÕÓËÅ. ÷ÓÅ ÓÏÏÂÝÅÎÉÑ, ËÏÔÏÒÙÅ ÂÏÌØÛÅ ÎÅËÕÄÁ ÏÔÐÒÁ×ÉÔØ ÉÄÕÔ ÓÀÄÁ, ×ËÌÀÞÁÑ /WHOIS É.Ô.Ä.
- ïËÎÏ ÓÔÁÔÕÓÁ ÔÏÖÅ ÍÏÖÅÔ ÂÙÔØ ÓÏÚÄÁÎÏ ×ÒÕÞÎÕÀ ÐÒÉ ÐÏÍÏÝÉ ËÏÍÁÎÄ <code>/WINDOW LEVEL ALL -MSGS</code>,
- <code>/WINDOW NAME (status)</code>.</dd>
-
-<dt>/SET autocreate_windows ON</dt>
- <dd>åÓÌÉ ×Ù ÜÔÏ ÏÔËÌÀÞÉÔÅ, ÔÏ ×ÓÅ ÓÏÏÂÝÅÎÉÑ ÂÕÄÕÔ ÐÏÍÅÝÁÔØÓÑ × ÏÄÎÏ ÏËÎÏ</dd>
-
-<dt>/SET autoclose_windows ON</dt>
- <dd>á×ÔÏÚÁËÒÙÔÉÅ ÏËÏÎ (ÎÁÐÒÉÍÅÒ ÐÒÉ ×ÙÈÏÄÅ Ó ËÁÎÁÌÏ×(<code>/PART</code>)).</dd>
-
-<dt>/SET reuse_unused_windows OFF</dt>
- <dd>ëÏÇÄÁ ÉÝÅÔÓÑ ÍÅÓÔÏ ÄÌÑ ÓÏÚÄÁÎÉÑ ÎÏ×ÏÇÏ ÏËÎÁ (ËÁÎÁÌÁ ÉÌÉ ÐÒÉ×ÁÔÁ) Irssi
- ÓÎÁÞÁÌÁ ÐÙÔÁÅÔÓÑ ÉÓÐÏÌØÚÏ×ÁÔØ ÕÖÅ ÓÕÝÅÓÔ×ÕÀÝÉÅ ÐÕÓÔÙÅ ÏËÎÁ. åÓÌÉ ÜÔÁ ÏÐÃÉÑ ×ËÌÀÞÅÎÁ, ÔÏ ×ÓÅÇÄÁ ÂÕÄÕÔ ÓÏÚÄÁ×ÁÔØÓÑ ÎÏ×ÙÅ ÏËÎÁ.
- üÔÁ ÎÁÓÔÒÏÊËÁ ÉÇÎÏÒÉÒÕÅÔÓÑ ÅÓÌÉ autoclose_windows ×ËÌÀÞÅÎ.</dd>
-
-<dt>/SET window_auto_change OFF</dt>
- <dd>á×ÔÏÍÁÔÉÞÅÓËÉ ÐÅÒÅËÌÀÞÁÔØÓÑ × Á×ÔÏÍÁÔÉÞÅÓËÉ ÓÏÚÄÁÎÎÙÅ ÏËÎÁ.</dd>
-
-<dt>/SET print_active_channel OFF</dt>
- <dd>ëÏÇÄÁ ×Ù ÄÅÒÖÉÔÅ × ÏÄÎÏÍ ÏËÎÅ ÂÏÌØÛÅ ÞÅÍ ÏÄÉÎ ËÁÎÁÌ, Irssi ×Ù×ÏÄÉÔ ÓÏÏÂÝÅÎÉÑ, ÐÒÉÈÏÄÑÝÉÅ ÎÁ ÁËÔÉ×ÎÙÊ ËÁÎÁÌ × ÆÏÒÍÅ <code>&lt;ÎÉË&gt; ÔÅËÓÔ</code>
- Á ÔÅ, ÞÔÏ ÐÒÉÈÏÄÑÔ ÎÁ ÄÒÕÇÉÅ ËÁÎÁÌÙ ÔÁË: <code>&lt;ÎÉË:ËÁÎÁÌ&gt; ÔÅËÓÔ</code>. åÓÌÉ ÜÔÁ ÏÐÃÉÑ ×ËÌÀÞÅÎÁ, ÔÏ ÓÏÏÂÝÅÎÉÑ, ÐÒÉÈÏÄÑÝÉÅ ÎÁ ÁËÔÉ×ÎÙÊ ËÁÎÁÌ ÂÕÄÕÔ ÔÁË-ÖÅ ×Ù×ÏÄÉÔØÓÑ ×Ï ×ÔÏÒÏÍ ×ÁÒÉÁÎÔÅ.</dd>
-
-<dt>/SET window_history OFF</dt>
- <dd>èÒÁÎÉÔØ ÏÔÄÅÌØÎÕÀ ÉÓÔÏÒÉÀ ËÏÍÁÎÄ ÄÌÑ ËÁÖÄÏÇÏ ÏËÎÁ.</dd>
-</dl>
-
-
-<p><strong>éÎÆÏÒÍÁÃÉÑ Ï ÐÏÌØÚÏ×ÁÔÅÌÅ</strong></p>
-
-<dl>
-<dt>/SET nick</dt>
- <dd>÷ÁÛ ÎÉË</dd>
-
-<dt>/SET alternate_nick</dt>
- <dd>÷ÁÛ ÁÌØÔÅÒÎÁÔÉ×ÎÙÊ ÎÉË.</dd>
-
-<dt>/SET user_name</dt>
- <dd>÷ÁÛÅ ÉÍÑ ÐÏÌØÚÏ×ÁÔÅÌÑ. åÓÌÉ Õ ×ÁÓ ×ËÌÀÞÅÎ ident, ÔÏ ÏÎÏ ÎÉÞÅÇÏ ÎÅ ÄÁ£Ô.</dd>
-
-<dt>/SET real_name</dt>
- <dd>÷ÁÛÅ ÎÁÓÔÏÑÝÅÅ ÉÍÑ.</dd>
-</dl>
-
-
-<p><strong>éÎÆÏÒÍÁÃÉÑ Ï ÓÅÒ×ÅÒÅ</strong></p>
-
-<dl>
-<dt>/SET skip_motd OFF</dt>
- <dd>ðÒÏÐÕÓËÁÔØ motd ÐÒÉ ÐÏÄËÌÀÞÅÎÉÉ Ë ÓÅÒ×ÅÒÕ.</dd>
-
-<dt>/SET server_reconnect_time 300</dt>
- <dd>óËÏÌØËÏ ÓÅËÕÎÄ ÎÁÄÏ ÖÄÁÔØ ÐÅÒÅÄ ÐÏ×ÔÏÒÎÏÊ ÐÏÐÙÔËÏÊ ÐÏÄËÌÀÞÅÎÉÑ Ë ÓÅÒ×ÅÒÕ.</dd>
-
-<dt>/SET lag_max_before_disconnect 300</dt>
- <dd>ðÒÉ ËÁËÏÍ ÌÁÇÅ(× ÓÅËÕÎÄÁÈ) ÎÁÄÏ ÏÔËÌÀÞÁÔØÓÑ ÏÔ ÓÅÒ×ÅÒÁ É ÐÒÅÄÐÒÉÎÉÍÁÔØ ÐÏÐÙÔËÕ ÐÅÒÅÐÏÄËÌÀÞÅÎÉÑ.</dd>
-</dl>
-
-
-<p><strong>÷ÎÅÛÎÉÊ ×ÉÄ</strong></p>
-
-<dl>
-<dt>/SET timestamps ON</dt>
- <dd>ðÏËÁÚÙ×ÁÔØ ×ÒÅÍÑ ÐÅÒÅÄ ËÁÖÄÙÍ ÓÏÏÂÝÅÎÉÅÍ.</dd>
-
-<dt>/SET hide_text_style OFF</dt>
- <dd>óËÒÙÔØ ÏÆÏÒÍÌÅÎÉÅ ÔÅËÓÔÁ(ÖÉÒÎÙÊ ÛÒÉÆÔ, Ã×ÅÔÁ É.Ô.Ä.).</dd>
-
-<dt>/SET show_nickmode ON</dt>
- <dd>ðÏËÁÚÙ×ÁÔØ "ÒÅÖÉÍ ÎÉËÁ" ÎÁ ËÁÎÁÌÁÈ, ÎÁÐÒÉÍÅÒ
- <code>&lt;@nick&gt;</code> Õ ÏÐÏ×, <code>&lt;+nick&gt;</code> Õ ×ÏÊÓÏ× É.Ô.Ä.</dd>
-
-<dt>/SET show_nickmode_empty ON</dt>
- <dd>åÓÌÉ Õ ÎÉËÁ ÎÅÔ ÒÅÖÉÍÁ - ×Ù×ÏÄÉÔØ ÐÒÏÂÅÌ ÎÁ ÍÅÓÔÅ "ÓÉÍ×ÏÌÁ ÒÅÖÉÍÁ".</dd>
-
-<dt>/SET show_quit_once OFF</dt>
- <dd>ðÏËÁÚÙ×ÁÔØ quit-ÓÏÏÂÝÅÎÉÅ ÔÏÌØËÏ × ÏÄÎÏÍ ÏËÎÅ, ÅÓÌÉ ÞÅÌÏ×ÅË ×ÙÛÅÌ Ó ÎÅÓËÏÌØËÉÈ ËÁÎÁÌÏ×, ÎÁ ËÏÔÏÒÙÈ ×Ù ÓÉÄÉÔÅ.</dd>
-
-<dt>/SET lag_min_show 100</dt>
- <dd>ðÏËÁÚÙ×ÁÔØ × ÓÔÁÔÕÓ-ÂÁÒÅ ÌÁÇ ÅÓÌÉ ÏÎ ÐÒÅ×ÙÛÁÅÔ ÚÁÄÁÎÎÏÅ ÞÉÓÌÏ ÀÎÉÔÏ×. ÷ ÏÄÎÏÊ ÓÅËÕÎÄÅ 100 ÀÎÉÔÏ×.</dd>
-
-<dt>/SET indent 10</dt>
- <dd>åÓÌÉ ÓÔÒÏËÁ, ËÏÔÏÒÕÀ ÎÁÄÏ ×Ù×ÅÓÔÉ ÎÅ ×ÍÅÝÁÅÔÓÑ × ÏÄÎÕ ÓÔÒÏËÕ, ÔÏ ÏÎÁ ÒÁÚÂÉ×ÁÅÔÓÑ É ×Ù×ÏÄÉÔÓÑ ÎÁ ÓÌÅÄÕÀÝÉÈ ÓÔÒÏËÁÈ. üÔÏÔ ÐÁÒÁÍÅÔÒ ÐÏËÁÚÙ×ÁÅÔ ÓËÏÌØËÏ ÍÅÓÔÁ ÎÁÄÏ ÏÔÓÔÕÐÉÔØ ÐÅÒÅÄ ÎÁÞÁÌÏÍ ×Ù×ÏÄÁ ÔÅËÓÔÁ ÎÁ ÓÌÅÄÕÀÝÉÈ ÓÔÒÏËÁÈ.
- üÔÏ ÍÏÖÅÔ ÂÙÔØ ÐÅÒÅÏÐÒÅÄÅÌÅÎÏ × ÎÁÓÔÒÏÊËÁÈ ÆÏÒÍÁÔÉÒÏ×ÁÎÉÑ ÔÅËÓÔÁ ÐÒÉ ÐÏÍÏÝÉ ÆÏÒÍÁÔÁ <code>%|</code>.</dd>
-
-<dt>/SET activity_hide_targets</dt>
- <dd>åÓÌÉ ×Ù ÎÅ ÈÏÔÉÔÅ ×ÉÄÅÔØ ÁËÔÉ×ÎÏÓÔØ ÎÁ ËÁËÉÈ-ÔÏ ËÁÎÁÌÁÈ ÉÌÉ ÐÒÉ×ÁÔÁÈ, ÔÏ ÐÅÒÅÞÉÓÌÉÔÅ ÉÈ ÚÄÅÓØ. îÁÐÒÉÍÅÒ <code>#boringchannel =bot1
- =bot2</code>. üÔÁ ÎÁÓÔÒÏÊËÁ ÉÇÎÏÒÉÒÕÅÔÓÑ ÅÓÌÉ ×ÓÔÒÅÞÁÅÔÓÑ ÔÅËÓÔ ÉÌÉ ÓÏÏÂÝÅÎÉÅ, ÄÌÑ ËÏÔÏÒÏÇÏ ×Ù ÎÁÓÔÒÏÉÌÉ ÐÏÄÓ×ÅÔËÕ(highlight).</dd>
-</dl>
-
-<p><strong>á×ÔÏÄÏÐÏÌÎÅÎÉÅ ÎÉËÏ×</strong></p>
-
-<dl>
-<dt>/SET completion_auto OFF</dt>
- <dd>á×ÔÏÍÁÔÉÞÅÓËÉ ÄÏÐÏÌÎÑÔØ ÎÉË ÅÓÌÉ ÓÔÒÏËÁ ÎÁÞÉÎÁÅÔÓÑ Ó ÐÅÒ×ÙÈ ÂÕË× ÎÉËÁ É "ÓÉÍ×ÏÌÁ Á×ÔÏÄÏÐÏÌÎÅÎÉÑ".
- ìÕÞÛÅ ×ÓÅÇÏ ÉÓÐÏÌØÚÏ×ÁÔØ Á×ÔÏÄÏÐÏÌÎÅÎÉÅ ÔÁÂÏÍ.</dd>
-
-<dt>/SET completion_char :</dt>
- <dd>"óÉÍ×ÏÌ Á×ÔÏÄÏÐÏÌÎÅÎÉÑ".</dd>
-</dl>
-
-<h3><a id="c12">12. ðÁÎÅÌØ ÓÔÁÔÕÓÁ</a></h3>
-
-<p>ëÏÍÁÎÄÁ <code>/STATUSBAR</code> ×Ù×ÏÄÉÔ ÓÐÉÓÏË ÐÁÎÅÌÅÊ ÓÔÁÔÕÓÁ:</p>
-
-<pre>
-Name Type Placement Position Visible
-window window bottom 0 always
-window_inact window bottom 1 inactive
-prompt root bottom 100 always
-topic root top 1 always
-</pre>
-
-<p><code>/STATUSBAR &lt;ÉÍÑ&gt;</code> ×Ù×ÏÄÉÔ ÎÁÓÔÒÏÊËÉ ÐÁÎÅÌÉ ÓÔÁÔÕÓÁ É Å£ ËÏÍÐÏÎÅÎÔÙ.
-<code>/STATUSBAR &lt;ÉÍÑ&gt; ENABLE|DISABLE</code>
-×ËÌÀÞÁÅÔ ÉÌÉ ÏÔËÌÀÞÁÅÔ ÐÁÎÅÌØ. <code>/STATUSBAR &lt;ÉÍÑ&gt; RESET</code>
-ÕÓÔÁÎÁ×ÌÉ×ÁÅÔ ÄÌÑ ÐÁÎÅÌÉ ÓÔÁÔÕÓÁ ÎÁÓÔÒÏÊËÉ ÐÏ ÕÍÏÌÞÁÎÉÀ, ÉÌÉ ÅÓÌÉ ÏÎÁ ÂÙÌÁ ÓÏÚÄÁÎÁ ×ÁÍÉ, ÔÏ ÕÄÁÌÑÅÔ Å£.</p>
-
-<p>ðÁÎÅÌØ ÍÏÖÅÔ ÉÍÅÔØ Ä×Á ÔÉÐÁ: windows É root - ÜÔÏ ÐÏÄÒÁÚÕÍÅ×ÁÅÔ, ÞÔÏ ÏÎÁ ÍÏÖÅÔ ÂÙÔØ ×ÉÄÎÁ ÄÌÑ ×ÓÅÈ ÏËÏÎ ÉÌÉ ÔÏÌØËÏ ÄÌÑ ÏÄÎÏÇÏ.
-Placement - ÜÔÏ ÒÁÓÐÏÌÏÖÅÎÉÅ ÐÁÎÅÌÉ: top - Ó×ÅÒÈÕ, bottom - ÓÎÉÚÕ.
-Position - ÜÔÏ ÞÉÓÌÏ, ÞÅÍ ÂÏÌØÛÅ ÚÎÁÞÅÎÉÅ ËÏÔÏÒÏÇÏ, ÔÅÍ ÎÉÖÅ ÎÁ ÜËÒÁÎÅ ÒÁÓÐÏÌÁÇÁÅÔÓÑ ÐÁÎÅÌØ.
-ðÁÒÁÍÅÔÒ Visible ÍÏÖÅÔ ÐÒÉÎÉÍÁÔØ 3 ÚÎÁÞÅÎÉÑ: always, active É inactive. òÅÖÉÍÙ active/inactive ÐÏÌÅÚÎÙ ÔÏÌØËÏ ÄÌÑ ÒÁÚÄÅÌÅÎÎÙÈ ÏËÏÎ.
-üÔÉ ÎÁÓÔÒÏÊËÉ ÍÏÇÕÔ ÂÙÔØ ÉÚÍÅÎÅÎÙ ÓÌÅÄÕÀÝÉÍÉ ËÏÍÁÎÄÁÍÉ:</p>
-
-<pre>
-/STATUSBAR &lt;ÉÍÑ&gt; TYPE window|root
-/STATUSBAR &lt;ÉÍÑ&gt; PLACEMENT top|bottom
-/STATUSBAR &lt;ÉÍÑ&gt; POSITION &lt;num&gt;
-/STATUSBAR &lt;ÉÍÑ&gt; VISIBLE always|active|inactive
-</pre>
-
-<p>ëÏÇÄÁ ×Ù ÚÁÇÒÕÖÁÅÔÅ ÎÏ×ÙÅ ÓËÒÉÐÔÙ ÄÌÑ ÐÁÎÅÌÅÊ ÓÔÁÔÕÓÁ ×ÁÍ ÓËÏÒÅÅ ×ÓÅÇÏ ÐÒÉÄÅÔÓÑ ×ÙÂÒÁÔØ ÇÄÅ ×Ù ÈÏÔÉÔÅ ÉÈ ÒÁÓÐÏÌÏÖÉÔØ.
-ëÏÍÐÏÎÅÎÔÙ ÐÁÎÅÌÅÊ ÍÏÇÕÔ ÂÙÔØ ÉÚÍÅÎÅÎÙ ÓÌÅÄÕÀÝÉÍÉ ËÏÍÁÎÄÁÍÉ:</p>
-
-<pre>
-/STATUSBAR &lt;ÉÍÑ&gt; ADD [-before | -after &lt;item&gt;] [-priority #] [-alignment left|right] &lt;ËÏÍÐÏÎÅÎÔÁ(item)&gt;
-/STATUSBAR &lt;ÉÍÑ&gt; REMOVE &lt;ËÏÍÐÏÎÅÎÔÁ(item)&gt;
-</pre>
-
-<p>ïÂÙÞÎÏ ÄÌÑ ÉÍÑ ËÏÍÐÏÎÅÎÔÙ × ÓËÒÉÐÔÅ ÄÌÑ ÐÁÎÅÌÉ ÓÏÏÔ×ÅÔÓÔ×ÕÅÔ ÉÍÅÎÉ ÓËÒÉÐÔÁ.
-ï ÜÔÏÍ ÄÏÌÖÎÏ ÂÙÔØ ÎÁÐÉÓÁÎÏ × ÄÏËÕÍÅÎÔÁÃÉÉ Ë ÓËÒÉÐÔÕ. ôÁË ÞÔÏ ÞÔÏÂÙ ÄÏÂÁ×ÉÔØ ÓËÒÉÐÔ mail.pl
-ÐÅÒÅÄ ÓÐÉÓËÏÍ ÁËÔÉ×ÎÙÈ ÏËÏÎ (ÓÍÏÔÒÉÔÅ
-<code>/STATUSBAR</code>), ××ÅÄÉÔÅ ÜÔÕ ËÏÍÁÎÄÕ: <code>/STATUSBAR window ADD -before
-act mail</code>.</p>
diff --git a/docs/startup-HOWTO.html b/docs/startup-HOWTO.html
index 736f25e9..07a9f47c 100644
--- a/docs/startup-HOWTO.html
+++ b/docs/startup-HOWTO.html
@@ -1,979 +1,663 @@
- <h2>Startup HOWTO</h2>
-
- <h3>To new Irssi users (not to new IRC users ..)</h3>
-
- <p>Copyright (c) 2000-2002 by Timo Sirainen, release under
- <a href="http://www.gnu.org/licenses/fdl.html">GNU FDL</a> 1.1 license.</p>
+ <h1>Startup How-To</h1>
+<h3 id="to-new-irssi-users-not-to-new-irc-users-">To new Irssi users (not to new IRC users ..)</h3>
+<p>Copyright (c) 2000-2002 by Timo Sirainen, release under <a href="http://www.gnu.org/licenses/fdl.html">GNU FDL</a> 1.1 license.</p>
<p>Index with some FAQ questions that are answered in the chapter:</p>
<ol>
-<li><a href="#c1">For all the lazy people</a>
- <ul>
- <li>This window management is just weird, I want it exactly like ircII</li>
- </ul></li>
-<li><a href="#c2">Basic user interface usage</a>
+ <li><a href="#for-all-the-ircii-people">For all the ircII people</a>
<ul>
- <li>Split windows work in weird way</li>
- <li>How can I easily switch between windows?</li>
- <li>But alt-1 etc. don't work!</li>
- </ul></li>
-<li><a href="#c3">Server and channel automation</a>
+ <li>This window management is just weird, I want it exactly like ircII</li>
+ </ul>
+ </li>
+ <li><a href="#basic-user-interface-usage">Basic user interface usage</a>
<ul>
- <li>How do I automatically connect to servers at startup?</li>
- <li>How do I automatically join to channels at startup?</li>
- <li>How do I automatically send commands to server at connect?</li>
- </ul></li>
-<li><a href="#c4">Setting up windows and automatically restoring them
- at startup</a></li>
-<li><a href="#c5">Status and msgs windows &amp; message levels</a>
+ <li>Split windows work in weird way</li>
+ <li>How can I easily switch between windows?</li>
+ <li>But alt-1 etc. don’t work!</li>
+ </ul>
+ </li>
+ <li><a href="#server-and-channel-automation">Server and channel automation</a>
<ul>
- <li>I want /WHOIS to print reply to current window</li>
- <li>I want all messages to go to one window, not create new windows</li>
- </ul></li>
-<li><a href="#c6">How support for multiple servers works in irssi</a>
+ <li>How do I automatically connect to servers at startup?</li>
+ <li>How do I automatically join to channels at startup?</li>
+ <li>How do I automatically send commands to server at connect?</li>
+ </ul>
+ </li>
+ <li><a href="#setting-up-windows-and-automatically-restoring-them-at-startup">Setting up windows and automatically restoring them at startup</a></li>
+ <li><a href="#status-and-msgs-windows--message-levels">Status and msgs windows &amp; message levels</a>
<ul>
- <li>I connected to some server that doesn't respond and now irssi
- keeps trying to reconnect to it again and again, how can I stop
- it??</li>
- <li>I want to have own status and/or msgs window for each servers</li>
- </ul></li>
-<li><a href="#c7">/LASTLOG and jumping around in scrollback</a>
+ <li>I want /WHOIS to print reply to current window</li>
+ <li>I want all messages to go to one window, not create new windows</li>
+ </ul>
+ </li>
+ <li><a href="#how-support-for-multiple-servers-works-in-irssi">How support for multiple servers works in irssi</a>
<ul>
- <li>How can I save all texts in a window to file?</li>
- </ul></li>
-<li><a href="#c8">Logging</a></li>
-<li><a href="#c9">Changing keyboard bindings</a>
+ <li>I connected to some server that doesn’t respond and now irssi keeps trying to reconnect to it again and again, how can I stop it??</li>
+ <li>I want to have own status and/or msgs window for each servers</li>
+ </ul>
+ </li>
+ <li><a href="#lastlog-and-jumping-around-in-scrollback">/LASTLOG and jumping around in scrollback</a>
<ul>
- <li>How do I make F1 key do something?</li>
- </ul></li>
-<li><a href="#c10">Proxies and IRC bouncers</a>
+ <li>How can I save all texts in a window to file?</li>
+ </ul>
+ </li>
+ <li><a href="#logging">Logging</a></li>
+ <li><a href="#changing-keyboard-bindings">Changing keyboard bindings</a>
<ul>
- <li>What's this irssi-proxy?</li>
- </ul></li>
-<li><a href="#c11">Irssi's settings</a></li>
-<li><a href="#c12">Statusbar</a>
+ <li>How do I make F1 key do something?</li>
+ </ul>
+ </li>
+ <li><a href="#proxies-and-irc-bouncers">Proxies and IRC bouncers</a></li>
+ <li><a href="#irssis-settings">Irssi’s settings</a></li>
+ <li><a href="#statusbar">Statusbar</a>
<ul>
- <li>I loaded a statusbar script but it's not visible anywhere!</li>
- </ul></li>
+ <li>I loaded a statusbar script but it’s not visible anywhere!</li>
+ </ul>
+ </li>
</ol>
-<h3><a id="c1">1. For all the lazy people</a></h3>
+<h2 id="for-all-the-ircii-people">1. For all the ircII people</h2>
<p>These settings should give you pretty good defaults (the ones I use):</p>
-<p>If colors don't work, and you know you're not going to use some
-weird non-VT compatible terminal (you most probably aren't), just
-say:</p>
+<p>If colors don’t work, and you know you’re not going to use some weird non-VT compatible terminal (you most probably aren’t), just say:</p>
-<pre>
-/SET term_force_colors ON
-</pre>
+<pre><code> /SET term_force_colors ON
+</code></pre>
-<p>I don't like automatic query windows, I don't like status window, I do
-like msgs window where all messages go:</p>
+<p>I don’t like automatic query windows, I don’t like status window, I do like msgs window where all messages go:</p>
-<pre>
-/SET autocreate_own_query OFF
-/SET autocreate_query_level DCCMSGS
-/SET use_status_window OFF
-/SET use_msgs_window ON
-</pre>
+<pre><code> /SET autocreate_own_query OFF
+ /SET autocreate_query_level DCCMSGS
+ /SET use_status_window OFF
+ /SET use_msgs_window ON
+</code></pre>
-<p>Disable automatic window closing when <code>/PART</code>ing channel or
-<code>/UNQUERY</code>ing query:</p>
+<p>Disable automatic window closing when <code>/PART</code>ing channel or <code>/UNQUERY</code>ing query:</p>
-<pre>
-/SET autoclose_windows OFF
-/SET reuse_unused_windows ON
-</pre>
+<pre><code> /SET autoclose_windows OFF
+ /SET reuse_unused_windows ON
+</code></pre>
-<p>Here's the settings that make irssi work exactly like ircII in window
-management (send me a note if you can think of more):</p>
+<p>Here’s the settings that make irssi work exactly like ircII in window management (send me a note if you can think of more):</p>
-<pre>
-/SET autocreate_own_query OFF
-/SET autocreate_query_level NONE
-/SET use_status_window OFF
-/SET use_msgs_window OFF
-/SET reuse_unused_windows ON
-/SET windows_auto_renumber OFF
-
-/SET autostick_split_windows OFF
-/SET autoclose_windows OFF
-/SET print_active_channel ON
-</pre>
+<pre><code> /SET autocreate_own_query OFF
+ /SET autocreate_query_level NONE
+ /SET use_status_window OFF
+ /SET use_msgs_window OFF
+ /SET reuse_unused_windows ON
+ /SET windows_auto_renumber OFF
+
+ /SET autostick_split_windows OFF
+ /SET autoclose_windows OFF
+ /SET print_active_channel ON
+</code></pre>
<p>And example how to add servers:</p>
-<p>(OFTC network, identify with nickserv and wait for 2 seconds before
-joining channels)</p>
+<p>(OFTC network, identify with nickserv and wait for 2 seconds before joining channels)</p>
-<pre>
-/NETWORK ADD -autosendcmd "/^msg nickserv identify pass;wait 2000" OFTC
-</pre>
+<pre><code> /NETWORK ADD -autosendcmd "/^msg nickserv ident pass;wait 2000" OFTC
+</code></pre>
<p>(NOTE: use /IRCNET with 0.8.9 and older)</p>
-<p>Then add some servers to different networks (network is already set up
-for them), irc.kpnqwest.fi is used by default for IRCNet but if it fails,
-irc.funet.fi is tried next:</p>
+<p>Then add some servers to different networks (network is already set up for them), irc.kpnqwest.fi is used by default for IRCNet but if it fails, irc.funet.fi is tried next:</p>
-<pre>
-/SERVER ADD -auto -network IRCnet irc.kpnqwest.fi 6667
-/SERVER ADD -network IRCnet irc.funet.fi 6667
-/SERVER ADD -auto -network efnet efnet.cs.hut.fi 6667
-</pre>
+<pre><code> /SERVER ADD -auto -network IRCnet irc.kpnqwest.fi 6667
+ /SERVER ADD -network IRCnet irc.funet.fi 6667
+ /SERVER ADD -auto -network efnet efnet.cs.hut.fi 6667
+</code></pre>
-<p>Automatically join to channels after connected to server, send op request
-to bot after joined to efnet/#irssi:</p>
+<p>Automatically join to channels after connected to server, send op request to bot after joined to efnet/#irssi:</p>
-<pre>
-/CHANNEL ADD -auto #irssi IRCnet
-/CHANNEL ADD -auto -bots *!*bot@host.org -botcmd "/^msg $0 op pass" #irssi efnet
-</pre>
+<pre><code> /CHANNEL ADD -auto #irssi IRCnet
+ /CHANNEL ADD -auto -bots *!*bot@host.org -botcmd "/^msg $0 op pass" #irssi efnet
+</code></pre>
-If you want lines containing your nick to hilight:
+<p>If you want lines containing your nick to hilight:</p>
-<pre>
-/HILIGHT nick
-</pre>
+<pre><code> /HILIGHT nick
+</code></pre>
-<h3><a id="c2">2. Basic user interface usage</a></h3>
+<h2 id="basic-user-interface-usage">2. Basic user interface usage</h2>
-<p>Windows can be scrolled up/down with PgUp and PgDown keys. If they don't
-work for you, use Meta-p and Meta-n keys. For jumping to beginning or end of
-the buffer, use <code>/SB HOME</code> and <code>/SB END</code> commands.</p>
+<p>Windows can be scrolled up/down with PgUp and PgDown keys. If they don’t work for you, use Meta-p and Meta-n keys. For jumping to beginning or end of the buffer, use <code>/SB HOME</code> and <code>/SB END</code> commands.</p>
-<p>By default, irssi uses "hidden windows" for everything. Hidden window is
-created every time you <code>/JOIN</code> a channel or <code>/QUERY</code>
-someone. There's several ways you can change between these windows:</p>
+<p>By default, irssi uses “hidden windows†for everything. Hidden window is created every time you <code>/JOIN</code> a channel or <code>/QUERY</code> someone. There’s several ways you can change between these windows:</p>
-<pre>
-Meta-1, Meta-2, .. Meta-0 - Jump directly between windows 1-10
-Meta-q .. Meta-o - Jump directly between windows 11-19
-/WINDOW &lt;number&gt; - Jump to any window with specified number
-Ctrl-P, Ctrl-N - Jump to previous / next window
-</pre>
+<pre><code> Meta-1, Meta-2, .. Meta-0 - Jump directly between windows 1-10
+ Meta-q .. Meta-o - Jump directly between windows 11-19
+ /WINDOW &lt;number&gt; - Jump to any window with specified number
+ Ctrl-P, Ctrl-N - Jump to previous / next window
+</code></pre>
-<p>Clearly the easiest way is to use Meta-number keys. And what is the Meta
-key? ESC key always works as Meta, but there's also easier ways. ALT could
-work as Meta, or if you have Windows keyboard, left Windows key might work
-as Meta. If they don't work directly, you'll need to set a few X resources
-(NOTE: these work with both xterm and rxvt):</p>
+<p>Clearly the easiest way is to use Meta-number keys. And what is the Meta key? ESC key always works as Meta, but there’s also easier ways. ALT could work as Meta, or if you have Windows keyboard, left Windows key might work as Meta. If they don’t work directly, you’ll need to set a few X resources (NOTE: these work with both xterm and rxvt):</p>
-<pre>
-XTerm*eightBitInput: false
-XTerm*metaSendsEscape: true
-</pre>
+<pre><code> XTerm*eightBitInput: false
+ XTerm*metaSendsEscape: true
+</code></pre>
-<p>With rxvt, you can also specify which key acts as Meta key. So if you
-want to use ALT instead of Windows key for it, use:</p>
+<p>With rxvt, you can also specify which key acts as Meta key. So if you want to use ALT instead of Windows key for it, use:</p>
-<pre>
-rxvt*modifier: alt
-</pre>
+<pre><code> rxvt*modifier: alt
+</code></pre>
<p>You could do this by changing the X key mappings:</p>
-<pre>
-xmodmap -e "keysym Alt_L = Meta_L Alt_L"
-</pre>
+<pre><code> xmodmap -e "keysym Alt_L = Meta_L Alt_L"
+</code></pre>
-<p>And how exactly do you set these X resources? For Debian, there's
-<code>/etc/X11/Xresources/xterm</code> file where you can put them and it's
-read automatically when X starts. <code>~/.Xresources</code> and
-<code>~/.Xdefaults</code> files might also work. If you can't get anything
-else to work, just copy and paste those lines to <code>~/.Xresources</code>
-and directly call <code>xrdb -merge ~/.Xresources</code> in some xterm.
-The resources affect only the new xterms you start, not existing ones.</p>
+<p>And how exactly do you set these X resources? For Debian, there’s <code>/etc/X11/Xresources/xterm</code> file where you can put them and it’s read automatically when X starts. <code>~/.Xresources</code> and <code>~/.Xdefaults</code> files might also work. If you can’t get anything else to work, just copy and paste those lines to <code>~/.Xresources</code> and directly call <code>xrdb -merge ~/.Xresources</code> in some xterm. The resources affect only the new xterms you start, not existing ones.</p>
-<p>Many windows SSH clients also don't allow usage of ALT. One excellent
-client that does allow is putty, you can download it from
-<a href="http://www.chiark.greenend.org.uk/~sgtatham/putty/">
-http://www.chiark.greenend.org.uk/~sgtatham/putty/</a>.</p>
+<p>Many windows SSH clients also don’t allow usage of ALT. One excellent client that does allow is putty, you can download it from <a href="http://www.chiark.greenend.org.uk/~sgtatham/putty/"> http://www.chiark.greenend.org.uk/~sgtatham/putty/</a>.</p>
-<p>Irssi also supports split windows, they've had some problems in past
-but I think they should work pretty well now :) Here's some commands
-related to them:</p>
+<p>Irssi also supports split windows, they’ve had some problems in past but I think they should work pretty well now :) Here’s some commands related to them:</p>
-<pre>
-/WINDOW NEW - Create new split window
-/WINDOW NEW HIDE - Create new hidden window
-/WINDOW CLOSE - Close split or hidden window
+<pre><code> /WINDOW NEW - Create new split window
+ /WINDOW NEW HIDE - Create new hidden window
+ /WINDOW CLOSE - Close split or hidden window
-/WINDOW HIDE [&lt;number&gt;|&lt;name&gt;] - Make the split window hidden window
-/WINDOW SHOW &lt;number&gt;|&lt;name&gt; - Make the hidden window a split window
+ /WINDOW HIDE [&lt;number&gt;|&lt;name&gt;] - Make the split window hidden window
+ /WINDOW SHOW &lt;number&gt;|&lt;name&gt; - Make the hidden window a split window
-/WINDOW SHRINK [&lt;lines&gt;] - Shrink the split window
-/WINDOW GROW [&lt;lines&gt;] - Grow the split window
-/WINDOW BALANCE - Balance the sizes of all split windows
-</pre>
+ /WINDOW SHRINK [&lt;lines&gt;] - Shrink the split window
+ /WINDOW GROW [&lt;lines&gt;] - Grow the split window
+ /WINDOW BALANCE - Balance the sizes of all split windows
+</code></pre>
-<p>By default, irssi uses "sticky windowing" for split windows. This means
-that windows created inside one split window cannot be moved to another
-split window without some effort. For example you could have following
-window layout:</p>
+<p>By default, irssi uses “sticky windowing†for split windows. This means that windows created inside one split window cannot be moved to another split window without some effort. For example you could have following window layout:</p>
-<pre>
-Split window 1: win#1 - Status window, win#2 - Messages window
-Split window 2: win#3 - IRCnet/#channel1, win#4 - IRCnet/#channel2
-Split window 3: win#5 - efnet/#channel1, win#6 - efnet/#channel2
-</pre>
+<pre><code> Split window 1: win#1 - Status window, win#2 - Messages window
+ Split window 2: win#3 - IRCnet/#channel1, win#4 - IRCnet/#channel2
+ Split window 3: win#5 - efnet/#channel1, win#6 - efnet/#channel2
+</code></pre>
-<p>When you are in win#1 and press ALT-6, irssi jumps to split window
-#3 and moves the efnet/#channel2 the active window.</p>
+<p>When you are in win#1 and press ALT-6, irssi jumps to split window #3 and moves the efnet/#channel2 the active window.</p>
-<p>With non-sticky windowing the windows don't have any relationship with
-split windows, pressing ALT-6 in win#1 moves win#6 to split window 1
-and sets it active, except if win#6 was already visible in some other
-split window irssi just changes to that split window. This it the way
-windows work with ircii, if you prefer it you can set it with</p>
+<p>With non-sticky windowing the windows don’t have any relationship with split windows, pressing ALT-6 in win#1 moves win#6 to split window 1 and sets it active, except if win#6 was already visible in some other split window irssi just changes to that split window. This it the way windows work with ircii, if you prefer it you can set it with</p>
-<pre>
-/SET autostick_split_windows OFF
-</pre>
+<pre><code> /SET autostick_split_windows OFF
+</code></pre>
-<p>Each window can have multiple channels, queries and other "window
-items" inside them. If you don't like windows at all, you disable
-automatic creating of them with</p>
+<p>Each window can have multiple channels, queries and other “window items†inside them. If you don’t like windows at all, you disable automatic creating of them with</p>
-<pre>
-/SET autocreate_windows OFF
-</pre>
+<pre><code> /SET autocreate_windows OFF
+</code></pre>
-<p>And if you keep all channels in one window, you most probably want
-the channel name printed in each line:</p>
+<p>And if you keep all channels in one window, you most probably want the channel name printed in each line:</p>
-<pre>
-/SET print_active_channel ON
-</pre>
+<pre><code> /SET print_active_channel ON
+</code></pre>
-<p>If you want to group only some channels or queries in one window,
-use</p>
+<p>If you want to group only some channels or queries in one window, use</p>
-<pre>
-/JOIN -window #channel
-/QUERY -window nick
-</pre>
+<pre><code> /JOIN -window #channel
+ /QUERY -window nick
+</code></pre>
-<h3><a id="c3">3. Server and channel automation</a></h3>
+<h2 id="server-and-channel-automation">3. Server and channel automation</h2>
-<p>Irssi's multiple IRC network support is IMHO very good - at least
-compared to other clients :) Even if you're only in one IRC network you
-should group all your servers to be in the same IRC network as this
-helps with reconnecting if your primary server breaks and is probably
-useful in some other ways too :) For information how to actually use
-irssi correctly with multiple servers see the chapter 6.</p>
+<p>Irssi’s multiple IRC network support is IMHO very good - at least compared to other clients :) Even if you’re only in one IRC network you should group all your servers to be in the same IRC network as this helps with reconnecting if your primary server breaks and is probably useful in some other ways too :) For information how to actually use irssi correctly with multiple servers see the chapter 6.</p>
-<p>First you need to have your IRC network set, use <code>/NETWORK</code>
-command to see if it's already there. If it isn't, use <code>/NETWORK ADD
-yournetwork</code>. If you want to execute some commands automatically when
-you're connected to some network, use <code>-autosendcmd</code> option.
-(NOTE: use /IRCNET with 0.8.9 and older.) Here's some examples:</p>
+<p>First you need to have your IRC network set, use <code>/NETWORK</code> command to see if it’s already there. If it isn’t, use <code>/NETWORK ADD yournetwork</code>. If you want to execute some commands automatically when you’re connected to some network, use <code>-autosendcmd</code> option. (NOTE: use /IRCNET with 0.8.9 and older.) Here’s some examples:</p>
-<pre>
-/NETWORK ADD -autosendcmd '^msg bot invite' IRCnet
-/NETWORK ADD -autosendcmd "/^msg nickserv identify pass;wait 2000" OFTC
-</pre>
+<pre><code> /NETWORK ADD -autosendcmd '^msg bot invite' IRCnet
+ /NETWORK ADD -autosendcmd "/^msg nickserv ident pass;wait 2000" OFTC
+</code></pre>
<p>After that you need to add your servers. For example:</p>
-<pre>
-/SERVER ADD -auto -network IRCnet irc.kpnqwest.fi 6667
-/SERVER ADD -auto -network worknet irc.mycompany.com 6667 password
-</pre>
+<pre><code> /SERVER ADD -auto -network IRCnet irc.kpnqwest.fi 6667
+ /SERVER ADD -auto -network worknet irc.mycompany.com 6667 password
+</code></pre>
-<p>The <code>-auto</code> option specifies that this server is
-automatically connected at startup. You don't need to make more than one
-server with <code>-auto</code> option to one IRC network, other servers are
-automatically connected in same network if the <code>-auto</code> server
-fails.</p>
+<p>The <code>-auto</code> option specifies that this server is automatically connected at startup. You don’t need to make more than one server with <code>-auto</code> option to one IRC network, other servers are automatically connected in same network if the <code>-auto</code> server fails.</p>
<p>And finally channels:</p>
-<pre>
-/CHANNEL ADD -auto -bots *!*bot@host.org -botcmd "/^msg $0 op pass" #irssi efnet
-/CHANNEL ADD -auto #secret IRCnet password
-</pre>
+<pre><code> /CHANNEL ADD -auto -bots *!*bot@host.org -botcmd "/^msg $0 op pass" #irssi efnet
+ /CHANNEL ADD -auto #secret IRCnet password
+</code></pre>
-<p><code>-bots</code> and <code>-botcmd</code> should be the only ones
-needing a bit of explaining. They're used to send commands automatically to
-bot when channel is joined, usually to get ops automatically. You can
-specify multiple bot masks with <code>-bots</code> option separated with
-spaces (and remember to quote the string then). The $0 in
-<code>-botcmd</code> specifies the first found bot in the list. If you
-don't need the bot masks (ie. the bot is always with the same nick, like
-chanserv) you can give only the <code>-botcmd</code> option and the command
-is always sent.</p>
+<p><code>-bots</code> and <code>-botcmd</code> should be the only ones needing a bit of explaining. They’re used to send commands automatically to bot when channel is joined, usually to get ops automatically. You can specify multiple bot masks with <code>-bots</code> option separated with spaces (and remember to quote the string then). The $0 in <code>-botcmd</code> specifies the first found bot in the list. If you don’t need the bot masks (ie. the bot is always with the same nick, like chanserv) you can give only the <code>-botcmd</code> option and the command is always sent.</p>
-<h3><a id="c4">4. Setting up windows and automatically restoring them at startup</a></h3>
+<h2 id="setting-up-windows-and-automatically-restoring-them-at-startup">4. Setting up windows and automatically restoring them at startup</h2>
-<p>First connect to all the servers, join the channels and create the
-queries you want. If you want to move the windows or channels around
-use commands:</p>
+<p>First connect to all the servers, join the channels and create the queries you want. If you want to move the windows or channels around use commands:</p>
-<pre>
-/WINDOW MOVE LEFT/RIGHT/number - move window elsewhere
-/WINDOW ITEM MOVE &lt;number&gt;|&lt;name&gt; - move channel/query to another window
-</pre>
+<pre><code> /WINDOW MOVE LEFT/RIGHT/number - move window elsewhere
+ /WINDOW ITEM MOVE &lt;number&gt;|&lt;name&gt; - move channel/query to another window
+</code></pre>
-<p>When everything looks the way you like, use <code>/LAYOUT SAVE</code>
-command (and <code>/SAVE</code>, if you don't have autosaving enabled) and
-when you start irssi next time, irssi remembers the positions of the
-channels, queries and everything. This "remembering" doesn't mean that
-simply using <code>/LAYOUT SAVE</code> would automatically make irssi
-reconnect to all servers and join all channels, you'll need the
-<code>/SERVER ADD -auto</code> and <code>/CHANNEL ADD -auto</code> commands
-to do that.</p>
+<p>When everything looks the way you like, use <code>/LAYOUT SAVE</code> command (and <code>/SAVE</code>, if you don’t have autosaving enabled) and when you start irssi next time, irssi remembers the positions of the channels, queries and everything. This “remembering†doesn’t mean that simply using <code>/LAYOUT SAVE</code> would automatically make irssi reconnect to all servers and join all channels, you’ll need the <code>/SERVER ADD -auto</code> and <code>/CHANNEL ADD -auto</code> commands to do that.</p>
-<p>If you want to change the layout, you just rearrange the layout like
-you want it and use <code>/LAYOUT SAVE</code> again. If you want to remove
-the layout for some reason, use <code>/LAYOUT RESET.</code></p>
+<p>If you want to change the layout, you just rearrange the layout like you want it and use <code>/LAYOUT SAVE</code> again. If you want to remove the layout for some reason, use <code>/LAYOUT RESET.</code></p>
+<h2 id="status-and-msgs-windows--message-levels">5. Status and msgs windows &amp; message levels</h2>
-<h3><a id="c5">5. Status and msgs windows &amp; message levels</a></h3>
+<p>By default, all the “extra messages†go to status window. This means pretty much all messages that don’t clearly belong to some channel or query. Some people like it, some don’t. If you want to remove it, use</p>
-<p>By default, all the "extra messages" go to status window. This means
-pretty much all messages that don't clearly belong to some channel or
-query. Some people like it, some don't. If you want to remove it, use</p>
+<pre><code> /SET use_status_window OFF
+</code></pre>
-<pre>
-/SET use_status_window OFF
-</pre>
+<p>This doesn’t have any effect until you restart irssi. If you want to remove it immediately, just <code>/WINDOW CLOSE</code> it.</p>
-<p>This doesn't have any effect until you restart irssi. If you want to
-remove it immediately, just <code>/WINDOW CLOSE</code> it.</p>
+<p>Another common window is “messages windowâ€, where all private messages go. By default it’s disabled and query windows are created instead. To make all private messages go to msgs window, say:</p>
-<p>Another common window is "messages window", where all private
-messages go. By default it's disabled and query windows are created
-instead. To make all private messages go to msgs window, say:</p>
+<pre><code> /SET use_msgs_window ON
+ /SET autocreate_query_level DCCMSGS (or if you don't want queries to
+ dcc chats either, say NONE)
+</code></pre>
-<pre>
-/SET use_msgs_window ON
-/SET autocreate_query_level DCCMSGS (or if you don't want queries to
- dcc chats either, say NONE)
-</pre>
+<p>use_msgs_window either doesn’t have any effect until restarting irssi. To create it immediately say:</p>
-<p>use_msgs_window either doesn't have any effect until restarting
-irssi. To create it immediately say:</p>
+<pre><code> /WINDOW NEW HIDE - create the window
+ /WINDOW NAME (msgs) - name it to "(msgs)"
+ /WINDOW LEVEL MSGS - make all private messages go to this window
+ /WINDOW MOVE 1 - move it to first window
+</code></pre>
-<pre>
-/WINDOW NEW HIDE - create the window
-/WINDOW NAME (msgs) - name it to "(msgs)"
-/WINDOW LEVEL MSGS - make all private messages go to this window
-/WINDOW MOVE 1 - move it to first window
-</pre>
+<p>Note that neither use_msgs_window nor use_status_window have any effect at all if <code>/LAYOUT SAVE</code> has been used.</p>
-<p>Note that neither use_msgs_window nor use_status_window have any
-effect at all if <code>/LAYOUT SAVE</code> has been used.</p>
+<p>This brings us to message levels.. What are they? All messages that irssi prints have one or more “message levelsâ€. Most common are PUBLIC for public messages in channels, MSGS for private messages and CRAP for all sorts of messages with no real classification. You can get a whole list of levels with</p>
-<p>This brings us to message levels.. What are they? All messages that
-irssi prints have one or more "message levels". Most common are PUBLIC
-for public messages in channels, MSGS for private messages and CRAP for
-all sorts of messages with no real classification. You can get a whole
-list of levels with</p>
+<pre><code> /HELP levels
+</code></pre>
-<pre>
-/HELP levels
-</pre>
+<p>Status window has message level <code>ALL -MSGS</code>, meaning that all messages, except private messages, without more specific place go to status window. The <code>-MSGS</code> is there so it doesn’t conflict with messages window.</p>
-<p>Status window has message level <code>ALL -MSGS</code>, meaning that all
-messages, except private messages, without more specific place go to status
-window. The <code>-MSGS</code> is there so it doesn't conflict with
-messages window.</p>
+<h2 id="how-support-for-multiple-servers-works-in-irssi">6. How support for multiple servers works in irssi</h2>
+<p>ircii and several other clients support multiple servers by placing the connection into some window. IRSSI DOES NOT. There is no required relationship between window and server. You can connect to 10 servers and manage them all in just one window, or join channel in each one of them to one single window if you really want to. That being said, here’s how you do connect to new server without closing the old connection:</p>
-<h3><a id="c6">6. How support for multiple servers works in irssi</a></h3>
+<pre><code> /CONNECT irc.server.org
+</code></pre>
-<p>ircii and several other clients support multiple servers by placing
-the connection into some window. IRSSI DOES NOT. There is no required
-relationship between window and server. You can connect to 10 servers
-and manage them all in just one window, or join channel in each one of
-them to one sigle window if you really want to. That being said, here's
-how you do connect to new server without closing the old connection:</p>
+<p>Instead of the <code>/SERVER</code> which disconnects the existing connection. To see list of all active connections, use <code>/SERVER</code> without any parameters. You should see a list of something like:</p>
-<pre>
-/CONNECT irc.server.org
-</pre>
+<pre><code> -!- IRCNet: irc.song.fi:6667 (IRCNet)
+ -!- OFTC: irc.oftc.net:6667 (OFTC)
+ -!- RECON-1: 192.168.0.1:6667 () (02:59 left before reconnecting)
+</code></pre>
-<p>Instead of the <code>/SERVER</code> which disconnects the existing
-connection. To see list of all active connections, use <code>/SERVER</code>
-without any parameters. You should see a list of something like:</p>
+<p>Here you see that we’re connected to IRCNet and OFTC networks. The IRCNet at the beginning is called the “server tag†while the (IRCnet) at the end shows the IRC network. Server tag specifies unique tag to refer to the server, usually it’s the same as the IRC network. When the IRC network isn’t known it’s some part of the server name. When there’s multiple connections to same IRC network or server, irssi adds a number after the tag so there could be network, network2, network3 etc.</p>
-<pre>
--!- IRCNet: irc.song.fi:6667 (IRCNet)
--!- OFTC: irc.oftc.net:6667 (OFTC)
--!- RECON-1: 192.168.0.1:6667 () (02:59 left before reconnecting)
-</pre>
+<p>Server tags beginning with <code>RECON-</code> mean server reconnections. Above we see that connection to server at 192.168.0.1 wasn’t successful and irssi will try to connect it again in 3 minutes.</p>
-<p>Here you see that we're connected to IRCNet and OFTC networks.
-The IRCNet at the beginning is called the "server tag" while the
-(IRCnet) at the end shows the IRC network. Server tag specifies unique
-tag to refer to the server, usually it's the same as the IRC network.
-When the IRC network isn't known it's some part of the server name.
-When there's multiple connections to same IRC network or server, irssi
-adds a number after the tag so there could be network, network2, network3
-etc.</p>
+<p>To disconnect one of the servers, or to stop irssi from reconnecting, use</p>
-<p>Server tags beginning with <code>RECON-</code> mean server
-reconnections. Above we see that connection to server at 192.168.0.1 wasn't
-successful and irssi will try to connect it again in 3 minutes.</p>
+<pre><code> /DISCONNECT network - disconnect server with tag "network"
+ /DISCONNECT recon-1 - stop trying to reconnect to RECON-1 server
+ /RMRECONNS - stop all server reconnections
-<p>To disconnect one of the servers, or to stop irssi from
-reconnecting, use</p>
+ /RECONNECT recon-1 - immediately try reconnecting back to RECON-1
+ /RECONNECT ALL - immediately try reconnecting back to all
+ servers in reconnection queue
+</code></pre>
-<pre>
-/DISCONNECT network - disconnect server with tag "network"
-/DISCONNECT recon-1 - stop trying to reconnect to RECON-1 server
-/RMRECONNS - stop all server reconnections
+<p>Now that you’re connected to all your servers, you’ll have to know how to specify which one of them you want to use. One way is to have an empty window, like status or msgs window. In it, you can specify which server to set active with</p>
-/RECONNECT recon-1 - immediately try reconnecting back to RECON-1
-/RECONNECT ALL - immediately try reconnecting back to all
- servers in reconnection queue
-</pre>
+<pre><code> /WINDOW SERVER tag - set server "tag" active
+ Ctrl-X - set the next server in list active
+</code></pre>
-<p>Now that you're connected to all your servers, you'll have to know how
-to specify which one of them you want to use. One way is to have an
-empty window, like status or msgs window. In it, you can specify which
-server to set active with</p>
+<p>When the server is active, you can use it normally. When there’s multiple connected servers, irssi adds [servertag] prefix to all messages in non-channel/query messages so you’ll know where it came from.</p>
-<pre>
-/WINDOW SERVER tag - set server "tag" active
-Ctrl-X - set the next server in list active
-</pre>
+<p>Several commands also accept <code>-servertag</code> option to specify which server it should use:</p>
-<p>When the server is active, you can use it normally. When there's
-multiple connected servers, irssi adds [servertag] prefix to all
-messages in non-channel/query messages so you'll know where it came
-from.</p>
+<pre><code> /MSG -tag nick message
+ /JOIN -tag #channel
+ /QUERY -tag nick
+</code></pre>
-<p>Several commands also accept <code>-servertag</code> option to specify
-which server it should use:</p>
+<p><code>/MSG</code> tab completion also automatically adds the <code>-tag</code> option when nick isn’t in active server.</p>
-<pre>
-/MSG -tag nick message
-/JOIN -tag #channel
-/QUERY -tag nick
-</pre>
+<p>Window’s server can be made sticky. When sticky, it will never automatically change to anything else, and if server gets disconnected, the window won’t have any active server. When the server gets connected again, it is automatically set active in the window. To set the window’s server sticky use</p>
-<p><code>/MSG</code> tab completion also automatically adds the
-<code>-tag</code> option when nick isn't in active server.</p>
+<pre><code> /WINDOW SERVER -sticky tag
+</code></pre>
-<p>Window's server can be made sticky. When sticky, it will never
-automatically change to anything else, and if server gets disconnected, the
-window won't have any active server. When the server gets connected again,
-it is automatically set active in the window. To set the window's server
-sticky use</p>
+<p>This is useful if you wish to have multiple status or msgs windows, one for each server. Here’s how to do them (repeat for each server)</p>
-<pre>
-/WINDOW SERVER -sticky tag
-</pre>
+<pre><code> /WINDOW NEW HIDE
+ /WINDOW NAME (status)
+ /WINDOW LEVEL ALL -MSGS
+ /WINDOW SERVER -sticky network
-<p>This is useful if you wish to have multiple status or msgs windows, one
-for each server. Here's how to do them (repeat for each server)</p>
+ /WINDOW NEW HIDE
+ /WINDOW NAME (msgs)
+ /WINDOW LEVEL MSGS
+ /WINDOW SERVER -sticky network
+</code></pre>
-<pre>
-/WINDOW NEW HIDE
-/WINDOW NAME (status)
-/WINDOW LEVEL ALL -MSGS
-/WINDOW SERVER -sticky network
-
-/WINDOW NEW HIDE
-/WINDOW NAME (msgs)
-/WINDOW LEVEL MSGS
-/WINDOW SERVER -sticky network
-</pre>
+<h2 id="lastlog-and-jumping-around-in-scrollback">7. /LASTLOG and jumping around in scrollback</h2>
-<h3><a id="c7">7. /LASTLOG and jumping around in scrollback</a></h3>
+<p><code>/LASTLOG</code> command can be used for searching texts in scrollback buffer. Simplest usages are</p>
-<p><code>/LASTLOG</code> command can be used for searching texts in
-scrollback buffer. Simplest usages are</p>
+<pre><code> /LASTLOG word - print all lines with "word" in them
+ /LASTLOG word 10 - print last 10 occurances of "word"
+ /LASTLOG -topics - print all topic changes
+</code></pre>
-<pre>
-/LASTLOG word - print all lines with "word" in them
-/LASTLOG word 10 - print last 10 occurances of "word"
-/LASTLOG -topics - print all topic changes
-</pre>
+<p>If there’s more than 1000 lines to be printed, irssi thinks that you probably made some mistake and won’t print them without <code>-force</code> option. If you want to save the full lastlog to file, use</p>
-<p>If there's more than 1000 lines to be printed, irssi thinks that you
-probably made some mistake and won't print them without <code>-force</code>
-option. If you want to save the full lastlog to file, use</p>
+<pre><code> /LASTLOG -file ~/irc.log
+</code></pre>
-<pre>
-/LASTLOG -file ~/irc.log
-</pre>
+<p>With <code>-file</code> option you don’t need <code>-force</code> even if there’s more than 1000 lines. <code>/LASTLOG</code> has a lot of other options too, see <code>/HELP lastlog</code> for details.</p>
-<p>With <code>-file</code> option you don't need <code>-force</code> even
-if there's more than 1000 lines. <code>/LASTLOG</code> has a lot of other
-options too, see <code>/HELP lastlog</code> for details.</p>
+<p>Once you’ve found the lines you were interested in, you might want to check the discussion around them. Irssi has <code>/SCROLLBACK</code> (or alias <code>/SB</code>) command for jumping around in scrollback buffer. Since <code>/LASTLOG</code> prints the timestamp when the message was originally printed, you can use <code>/SB GOTO hh:mm</code> to jump directly there. To get back to the bottom of scrollback, use <code>/SB END</code> command.</p>
-<p>Once you've found the lines you were interested in, you might want
-to check the discussion around them. Irssi has <code>/SCROLLBACK</code> (or
-alias <code>/SB</code>) command for jumping around in scrollback buffer.
-Since <code>/LASTLOG</code> prints the timestamp when the message was
-originally printed, you can use <code>/SB GOTO hh:mm</code> to jump
-directly there. To get back to the bottom of scrollback, use <code>/SB
-END</code> command.</p>
+<h2 id="logging">8. Logging</h2>
+<p>Irssi can automatically log important messages when you’re set away (<code>/AWAY reason</code>). When you set yourself unaway (<code>/AWAY</code>), the new messages in away log are printed to screen. You can configure it with:</p>
-<h3><a id="c8">8. Logging</a></h3>
+<pre><code> /SET awaylog_level MSGS HILIGHT - Specifies what messages to log
+ /SET awaylog_file ~/.irssi/away.log - Specifies the file to use
+</code></pre>
-<p>Irssi can automatically log important messages when you're set away
-(<code>/AWAY reason</code>). When you set yourself unaway
-(<code>/AWAY</code>), the new messages in away log are printed to screen.
-You can configure it with:</p>
+<p>Easiest way to start logging with Irssi is to use autologging. With it Irssi logs all channels and private messages to specified directory. You can turn it on with</p>
-<pre>
-/SET awaylog_level MSGS HILIGHT - Specifies what messages to log
-/SET awaylog_file ~/.irssi/away.log - Specifies the file to use
-</pre>
+<pre><code> /SET autolog ON
+</code></pre>
-<p>Easiest way to start logging with Irssi is to use autologging. With it
-Irssi logs all channels and private messages to specified directory.
-You can turn it on with</p>
+<p>By default it logs pretty much everything execept CTCPS or CRAP (<code>/WHOIS</code> requests, etc). You can specify the logging level yourself with</p>
-<pre>
-/SET autolog ON
-</pre>
+<pre><code> /SET autolog_level ALL -CRAP -CLIENTCRAP -CTCPS (this is the default)
+</code></pre>
-<p>By default it logs pretty much everything execept CTCPS or CRAP
-(<code>/WHOIS</code> requests, etc). You can specify the logging level
-yourself with</p>
+<p>By default irssi logs to ~/irclogs/<servertag>/<target>.log. You can change this with</target></servertag></p>
-<pre>
-/SET autolog_level ALL -CRAP -CLIENTCRAP -CTCPS (this is the default)
-</pre>
+<pre><code> /SET autolog_path ~/irclogs/$tag/$0.log (this is the default)
+</code></pre>
-<p>By default irssi logs to ~/irclogs/&lt;servertag&gt;/&lt;target&gt;.log.
-You can change this with</p>
+<p>The path is automatically created if it doesn’t exist. $0 specifies the target (channel/nick). You can make irssi automatically rotate the logs by adding date/time formats to the file name. The formats are in “man strftime†format. For example</p>
-<pre>
-/SET autolog_path ~/irclogs/$tag/$0.log (this is the default)
-</pre>
+<pre><code> /SET autolog_path ~/irclogs/%Y/$tag/$0.%m-%d.log
+</code></pre>
-<p>The path is automatically created if it doesn't exist. $0 specifies
-the target (channel/nick). You can make irssi automatically rotate the
-logs by adding date/time formats to the file name. The formats are in
-"man strftime" format. For example</p>
+<p>For logging only some specific channels or nicks, see <code>/HELP log</code></p>
-<pre>
-/SET autolog_path ~/irclogs/%Y/$tag/$0.%m-%d.log
-</pre>
-
-<p>For logging only some specific channels or nicks, see <code>/HELP
-log</code></p>
-
-
-<h3><a id="c9">9. Changing keyboard bindings</a></h3>
+<h2 id="changing-keyboard-bindings">9. Changing keyboard bindings</h2>
-<p>You can change any keyboard binding that terminal lets irssi know
-about. It doesn't let irssi know everything, so for example
-shift-backspace can't be bound unless you modify xterm resources
-somehow.</p>
+<p>You can change any keyboard binding that terminal lets irssi know about. It doesn’t let irssi know everything, so for example shift-backspace can’t be bound unless you modify xterm resources somehow.</p>
-<p><code>/HELP bind</code> tells pretty much everything there is to know
-about keyboard bindings. However, there's the problem of how to bind some
-non-standard keys. They might differ a bit with each terminal, so you'll
-need to find out what exactly the keypress produces. Easiest way to check
-that would be to see what it prints in <code>cat</code>. Here's an example
-for pressing F1 key:</p>
+<p><code>/HELP bind</code> tells pretty much everything there is to know about keyboard bindings. However, there’s the problem of how to bind some non-standard keys. They might differ a bit with each terminal, so you’ll need to find out what exactly the keypress produces. Easiest way to check that would be to see what it prints in <code>cat</code>. Here’s an example for pressing F1 key:</p>
-<pre>
-[cras@hurina] ~% cat
-^[OP
-</pre>
+<pre><code> [cras@hurina] ~% cat
+ ^[OP
+</code></pre>
-<p>So in irssi you would use <code>/BIND ^[OP /ECHO F1 pressed</code>. If
-you use multiple terminals which have different bindings for the key, it
-would be better to use eg.:</p>
+<p>So in irssi you would use <code>/BIND ^[OP /ECHO F1 pressed</code>. If you use multiple terminals which have different bindings for the key, it would be better to use eg.:</p>
-<pre>
-/BIND ^[OP key F1
-/BIND ^[11~ key F1
-/BIND F1 /ECHO F1 pressed.
-</pre>
+<pre><code> /BIND ^[OP key F1
+ /BIND ^[11~ key F1
+ /BIND F1 /ECHO F1 pressed.
+</code></pre>
-<h3><a id="c10">10. Proxies and IRC bouncers</a></h3>
+<h2 id="proxies-and-irc-bouncers">10. Proxies and IRC bouncers</h2>
-<p>Irssi supports connecting to IRC servers via a proxy. All server
-connections are then made through it, and if you've set up everything
-properly, you don't need to do any <code>/QUOTE SERVER</code> commands
-manually.</p>
+<p>Irssi supports connecting to IRC servers via a proxy. All server connections are then made through it, and if you’ve set up everything properly, you don’t need to do any <code>/QUOTE SERVER</code> commands manually.</p>
-<p>Here's an example: You have your bouncer (lets say, BNC or BNC-like)
-listening in irc.bouncer.org port 5000. You want to use it to connect
-to servers irc.dalnet and irc.efnet.org. First you'd need to setup the
-bouncer:</p>
+<p>Here’s an example: You have your bouncer (lets say, BNC or BNC-like) listening in irc.bouncer.org port 5000. You want to use it to connect to servers irc.dalnet and irc.efnet.org. First you’d need to setup the bouncer:</p>
-<pre>
-/SET use_proxy ON
-/SET proxy_address irc.bouncer.org
-/SET proxy_port 5000
+<pre><code> /SET use_proxy ON
+ /SET proxy_address irc.bouncer.org
+ /SET proxy_port 5000
-/SET proxy_password YOUR_BNC_PASSWORD_HERE
-/SET -clear proxy_string
-/SET proxy_string_after conn %s %d
-</pre>
+ /SET proxy_password YOUR_BNC_PASSWORD_HERE
+ /SET -clear proxy_string
+ /SET proxy_string_after conn %s %d
+</code></pre>
-<p>Then you'll need to add the server connections. These are done
-exactly as if you'd want to connect directly to them. Nothing special
-about them:</p>
+<p>Then you’ll need to add the server connections. These are done exactly as if you’d want to connect directly to them. Nothing special about them:</p>
-<pre>
-/SERVER ADD -auto -network dalnet irc.dal.net
-/SERVER ADD -auto -network efnet irc.efnet.org
-</pre>
+<pre><code> /SERVER ADD -auto -network dalnet irc.dal.net
+ /SERVER ADD -auto -network efnet irc.efnet.org
+</code></pre>
-<p>With the proxy <code>/SET</code>s however, irssi now connects to those
-servers through your BNC. All server connections are made through them so
-you can just forget that your bouncer even exists.</p>
+<p>With the proxy <code>/SET</code>s however, irssi now connects to those servers through your BNC. All server connections are made through them so you can just forget that your bouncer even exists.</p>
-<p>If you don't want to use the proxy for some reason, there's
-<code>-noproxy</code> option which you can give to <code>/SERVER</code> and
-<code>/SERVER ADD</code> commands.</p>
+<p>If you don’t want to use the proxy for some reason, there’s <code>-noproxy</code> option which you can give to <code>/SERVER</code> and <code>/SERVER ADD</code> commands.</p>
<p><strong>Proxy specific settings:</strong></p>
<p>All proxies have these settings in common:</p>
-<pre>
-/SET use_proxy ON
-/SET proxy_address &lt;Proxy host address&gt;
-/SET proxy_port &lt;Proxy port&gt;
-</pre>
+<pre><code> /SET use_proxy ON
+ /SET proxy_address &lt;Proxy host address&gt;
+ /SET proxy_port &lt;Proxy port&gt;
+</code></pre>
<p><strong>HTTP proxy</strong></p>
<p>Use these settings with HTTP proxies:</p>
-<pre>
-/SET -clear proxy_password
-/EVAL SET proxy_string CONNECT %s:%d HTTP/1.0\n\n
-</pre>
+<pre><code> /SET -clear proxy_password
+ /EVAL SET proxy_string CONNECT %s:%d HTTP/1.0\n\n
+</code></pre>
<p><strong>BNC</strong></p>
-<pre>
-/SET proxy_password your_pass
-/SET -clear proxy_string
-/SET proxy_string_after conn %s %d
-</pre>
+<pre><code> /SET proxy_password your_pass
+ /SET -clear proxy_string
+ /SET proxy_string_after conn %s %d
+</code></pre>
<p><strong>dircproxy</strong></p>
-<p>dircproxy separates the server connections by passwords. So, if you
-for example have network connection with password ircpass and
-OFTC connection with oftcpass, you would do something like
-this:</p>
+<p>dircproxy separates the server connections by passwords. So, if you for example have network connection with password ircpass and OFTC connection with oftcpass, you would do something like this:</p>
-<pre>
-/SET -clear proxy_password
-/SET -clear proxy_string
+<pre><code> /SET -clear proxy_password
+ /SET -clear proxy_string
-/SERVER ADD -auto -network IRCnet fake.network 6667 ircpass
-/SERVER ADD -auto -network OFTC fake.oftc 6667 oftcpass
-</pre>
+ /SERVER ADD -auto -network IRCnet fake.network 6667 ircpass
+ /SERVER ADD -auto -network OFTC fake.oftc 6667 oftcpass
+</code></pre>
-<p>The server name and port you give isn't used anywhere, so you can
-put anything you want in there.</p>
+<p>The server name and port you give isn’t used anywhere, so you can put anything you want in there.</p>
<p><strong>psyBNC</strong></p>
-<p>psyBNC has internal support for multiple servers. However, it could
-be a bit annoying to use, and some people just use different users for
-connecting to different servers. You can manage this in a bit same way
-as with dircproxy, by creating fake connections:</p>
+<p>psyBNC has internal support for multiple servers. However, it could be a bit annoying to use, and some people just use different users for connecting to different servers. You can manage this in a bit same way as with dircproxy, by creating fake connections:</p>
-<pre>
-/SET -clear proxy_password
-/SET -clear proxy_string
+<pre><code> /SET -clear proxy_password
+ /SET -clear proxy_string
-/NETWORK ADD -user networkuser IRCnet
-/SERVER ADD -auto -network IRCnet fake.network 6667 ircpass
-/NETWORK ADD -user oftcuser OFTC
-/SERVER ADD -auto -network OFTC fake.oftc 6667 oftcpass
-</pre>
+ /NETWORK ADD -user networkuser IRCnet
+ /SERVER ADD -auto -network IRCnet fake.network 6667 ircpass
+ /NETWORK ADD -user oftcuser OFTC
+ /SERVER ADD -auto -network OFTC fake.oftc 6667 oftcpass
+</code></pre>
-<p>So, you'll specify the usernames with <code>/NETWORK ADD</code> command,
-and the user's password with <code>/SERVER ADD</code>.</p>
+<p>So, you’ll specify the usernames with <code>/NETWORK ADD</code> command, and the user’s password with <code>/SERVER ADD</code>.</p>
<p>(NOTE: use /IRCNET with 0.8.9 and older.)</p>
<p><strong>Irssi proxy</strong></p>
-<p>Irssi contains it's own proxy which you can build giving
-<code>--with-proxy</code> option to configure. You'll still need to run
-irssi in a screen to use it though.</p>
+<p>Irssi contains it’s own proxy which you can build giving <code>\--with-proxy</code> option to configure. You’ll still need to run irssi in a screen to use it though.</p>
-<p>Irssi proxy is a bit different than most proxies, normally proxies
-create a new connection to IRC server when you connect to it, but
-<strong>irssi proxy shares your existing IRC connection(s) to multiple
-clients</strong>. And even more clearly: <strong>You can use only one IRC
-server connection to IRC with as many clients as you want</strong>. Can
-anyone figure out even more easier ways to say this, so I wouldn't need to
-try to explain this thing for minutes every time? :)</p>
+<p>Irssi proxy is a bit different than most proxies, normally proxies create a new connection to IRC server when you connect to it, but <strong>irssi proxy shares your existing IRC connection(s) to multiple clients</strong>. And even more clearly: <strong>You can use only one IRC server connection to IRC with as many clients as you want</strong>. Can anyone figure out even more easier ways to say this, so I wouldn’t need to try to explain this thing for minutes every time? :)</p>
-<p>Irssi proxy supports sharing multiple server connections in different
-ports, like you can share network in port 2777 and efnet in port 2778.</p>
+<p>Irssi proxy supports sharing multiple server connections in different ports, like you can share network in port 2777 and efnet in port 2778.</p>
<p>Usage in proxy side:</p>
-<pre>
-/LOAD proxy
-/SET irssiproxy_password &lt;password&gt;
-/SET irssiproxy_ports &lt;network&gt;=&lt;port&gt; ... (eg. IRCnet=2777 efnet=2778)
-</pre>
+<pre><code> /LOAD proxy
+ /SET irssiproxy_password &lt;password&gt;
+ /SET irssiproxy_ports &lt;network&gt;=&lt;port&gt; ... (eg. IRCnet=2777 efnet=2778)
+</code></pre>
-<p><strong>NOTE</strong>: you <strong>MUST</strong> add all the servers you
-are using to server and network lists with <code>/SERVER ADD</code> and
-<code>/NETWORK ADD</code>. ..Except if you really don't want to for some
-reason, and you only use one server connection, you may simply set:</p>
+<p><strong>NOTE</strong>: you <strong>MUST</strong> add all the servers you are using to server and network lists with <code>/SERVER ADD</code> and <code>/NETWORK ADD</code>. ..Except if you really don’t want to for some reason, and you only use one server connection, you may simply set:</p>
+
+<pre><code> /SET irssiproxy_ports *=2777
+</code></pre>
+
+<p>The special network name <code>?</code> allows the client to select the
+network dynamically on connect (see below):</p>
<pre>
-/SET irssiproxy_ports *=2777
+/SET irssiproxy_ports ?=2777
</pre>
<p>Usage in client side:</p>
-<p>Just connect to the irssi proxy like it is a normal server with password
-specified in <code>/SET irssiproxy_password</code>. For example:</p>
+<p>Just connect to the irssi proxy like it is a normal server with password specified in <code>/SET irssiproxy_password</code>. For example:</p>
+
+<pre><code> /SERVER ADD -network IRCnet my.irssi-proxy.org 2777 secret
+ /SERVER ADD -network efnet my.irssi-proxy.org 2778 secret
+</code></pre>
+
+<p>Or, if you used <code>?</code> in <code>irssiproxy_ports</code>:</p>
<pre>
-/SERVER ADD -network IRCnet my.irssi-proxy.org 2777 secret
-/SERVER ADD -network efnet my.irssi-proxy.org 2778 secret
+/SERVER ADD -network IRCnet my.irssi-proxy.org 2777 IRCnet:secret
+/SERVER ADD -network efnet my.irssi-proxy.org 2777 efnet:secret
</pre>
+<p>I.e. the network to connect to is specified as part of the password,
+separated by <code>:</code> from the actual proxy password.</p>
+
<p>Irssi proxy works fine with other IRC clients as well.</p>
<p><strong>SOCKS</strong></p>
-Irssi can be compiled with socks support (<code>--with-socks</code> option
-to configure), but I don't really know how it works, if at all. <code>/SET
-proxy</code> settings don't have anything to do with socks however.
+<p>Irssi can be compiled with socks support (<code>\--with-socks</code> option to configure), but I don’t really know how it works, if at all. <code>/SET proxy</code> settings don’t have anything to do with socks however.</p>
<p><strong>Others</strong></p>
-<p>IRC bouncers usually work like IRC servers, and want a password. You can
-give it with:</p>
+<p>IRC bouncers usually work like IRC servers, and want a password. You can give it with:</p>
-<pre>
-/SET proxy_password &lt;password&gt;
-</pre>
+<pre><code> /SET proxy_password &lt;password&gt;
+</code></pre>
-<p>Irssi's defaults for connect strings are</p>
+<p>Irssi’s defaults for connect strings are</p>
-<pre>
-/SET proxy_string CONNECT %s %d
-/SET proxy_string_after
-</pre>
+<pre><code> /SET proxy_string CONNECT %s %d
+ /SET proxy_string_after
+</code></pre>
-<p>The proxy_string is sent before NICK/USER commands, the
-proxy_string_after is sent after them. %s and %d can be used with both
-of them.</p>
+<p>The proxy_string is sent before NICK/USER commands, the proxy_string_after is sent after them. %s and %d can be used with both of them.</p>
-<h3><a id="c11">11. Irssi's settings</a></h3>
+<h2 id="irssis-settings">11. Irssi’s settings</h2>
-<p>You probably don't like Irssi's default settings. I don't like them.
-But I'm still convinced that they're pretty good defaults. Here's some
-of them you might want to change (the default value is shown):</p>
+<p>You probably don’t like Irssi’s default settings. I don’t like them. But I’m still convinced that they’re pretty good defaults. Here’s some of them you might want to change (the default value is shown): Also check the <a href="/documentation/settings/">Settings Documentation</a></p>
<p><strong>Queries</strong></p>
<dl>
-<dt>/SET autocreate_own_query ON</dt>
- <dd>Should new query window be created when you send message to someone
- (with <code>/MSG</code>).</dd>
-
-<dt>/SET autocreate_query_level MSGS</dt>
- <dd>New query window should be created when receiving messages with
- this level. MSGS, DCCMSGS and NOTICES levels work currently. You can
- disable this with <code>/SET -clear autocreate_query_level</code>.</dd>
-
-<dt>/SET autoclose_query 0</dt>
- <dd>Query windows can be automatically closed after certain time of
- inactivity. Queries with unread messages aren't closed and active
- window is neither never closed. The value is given in seconds.</dd>
+ <dt>/SET autocreate_own_query ON</dt>
+ <dd>Should new query window be created when you send message to someone (with <code>/MSG</code>).</dd>
+ <dt>/SET autocreate_query_level MSGS</dt>
+ <dd>New query window should be created when receiving messages with this level. MSGS, DCCMSGS and NOTICES levels work currently. You can disable this with <code>/SET -clear autocreate_query_level</code>.</dd>
+ <dt>/SET autoclose_query 0</dt>
+ <dd>Query windows can be automatically closed after certain time of inactivity. Queries with unread messages aren’t closed and active window is neither never closed. The value is given in seconds.</dd>
</dl>
<p><strong>Windows</strong></p>
<dl>
-<dt>/SET use_msgs_window OFF</dt>
- <dd>Create messages window at startup. All private messages go to this
- window. This only makes sense if you've disabled automatic query
- windows. Message window can also be created manually with /WINDOW LEVEL
- MSGS, /WINDOW NAME (msgs).</dd>
-
-<dt>/SET use_status_window ON</dt>
- <dd>Create status window at startup. All messages that don't really
- have better place go here, like all /WHOIS replies etc. Status window
- can also be created manually with <code>/WINDOW LEVEL ALL -MSGS</code>,
- <code>/WINDOW NAME (status)</code>.</dd>
-
-<dt>/SET autocreate_windows ON</dt>
- <dd>Should we create new windows for new window items or just place
- everything in one window</dd>
-
-<dt>/SET autoclose_windows ON</dt>
- <dd>Should window be automatically closed when the last item in them is
- removed (ie. <code>/PART</code>, <code>/UNQUERY</code>).</dd>
-
-<dt>/SET reuse_unused_windows OFF</dt>
- <dd>When finding where to place new window item (channel, query) Irssi
- first tries to use already existing empty windows. If this is set ON,
- new window will always be created for all window items. This setting is
- ignored if autoclose_windows is set ON.</dd>
-
-<dt>/SET window_auto_change OFF</dt>
- <dd>Should Irssi automatically change to automatically created windows -
- usually queries when someone sends you a message. To prevent
- accidentally sending text meant to some other channel/nick, Irssi
- clears the input buffer when changing the window. The text is still in
- scrollback buffer, you can get it back with pressing arrow up key.</dd>
-
-<dt>/SET print_active_channel OFF</dt>
- <dd>When you keep more than one channel in same window, Irssi prints
- the messages coming to active channel as <code>&lt;nick&gt; text</code>
- and other channels as <code>&lt;nick:channel&gt; text</code>. If this
- setting is set ON, the messages to active channels are also printed in
- the latter way.</dd>
-
-<dt>/SET window_history OFF</dt>
+ <dt>/SET use_msgs_window OFF</dt>
+ <dd>Create messages window at startup. All private messages go to this window. This only makes sense if you’ve disabled automatic query windows. Message window can also be created manually with /WINDOW LEVEL MSGS, /WINDOW NAME (msgs).</dd>
+ <dt>/SET use_status_window ON</dt>
+ <dd>Create status window at startup. All messages that don’t really have better place go here, like all /WHOIS replies etc. Status window can also be created manually with <code>/WINDOW LEVEL ALL -MSGS</code>, <code>/WINDOW NAME (status)</code>.</dd>
+ <dt>/SET autocreate_windows ON</dt>
+ <dd>Should we create new windows for new window items or just place everything in one window</dd>
+ <dt>/SET autoclose_windows ON</dt>
+ <dd>Should window be automatically closed when the last item in them is removed (ie. <code>/PART</code>, <code>/UNQUERY</code>).</dd>
+ <dt>/SET reuse_unused_windows OFF</dt>
+ <dd>When finding where to place new window item (channel, query) Irssi first tries to use already existing empty windows. If this is set ON, new window will always be created for all window items. This setting is ignored if autoclose_windows is set ON.</dd>
+ <dt>/SET window_auto_change OFF</dt>
+ <dd>Should Irssi automatically change to automatically created windows - usually queries when someone sends you a message. To prevent accidentally sending text meant to some other channel/nick, Irssi clears the input buffer when changing the window. The text is still in scrollback buffer, you can get it back with pressing arrow up key.</dd>
+ <dt>/SET print_active_channel OFF</dt>
+ <dd>When you keep more than one channel in same window, Irssi prints the messages coming to active channel as <code>&lt;nick&gt; text</code> and other channels as <code>&lt;nick:channel&gt; text</code>. If this setting is set ON, the messages to active channels are also printed in the latter way.</dd>
+ <dt>/SET window_history OFF</dt>
<dd>Should command history be kept separate for each window.</dd>
</dl>
-
<p><strong>User information</strong></p>
<dl>
-<dt>/SET nick</dt>
+ <dt>/SET nick</dt>
<dd>Your nick name</dd>
-
-<dt>/SET alternate_nick</dt>
+ <dt>/SET alternate_nick</dt>
<dd>Your alternate nick.</dd>
-
-<dt>/SET user_name</dt>
- <dd>Your username, if you have ident enabled this doesn't affect
- anything</dd>
-
-<dt>/SET real_name</dt>
+ <dt>/SET user_name</dt>
+ <dd>Your username, if you have ident enabled this doesn’t affect anything</dd>
+ <dt>/SET real_name</dt>
<dd>Your real name.</dd>
</dl>
-
<p><strong>Server information</strong></p>
<dl>
-<dt>/SET skip_motd OFF</dt>
- <dd>Should we hide server's MOTD (Message Of The Day).</dd>
-
-<dt>/SET server_reconnect_time 300</dt>
- <dd>Seconds to wait before connecting to same server again. Don't set
- this too low since it usually doesn't help at all - if the host is
- down, the few extra minutes of waiting won't hurt much.</dd>
-
-<dt>/SET lag_max_before_disconnect 300</dt>
- <dd>Maximum server lag in seconds before disconnecting and trying to
- reconnect. This happens mostly only when network breaks between you and
- IRC server.</dd>
+ <dt>/SET skip_motd OFF</dt>
+ <dd>Should we hide server’s MOTD (Message Of The Day).</dd>
+ <dt>/SET server_reconnect_time 300</dt>
+ <dd>Seconds to wait before connecting to same server again. Don’t set this too low since it usually doesn’t help at all - if the host is down, the few extra minutes of waiting won’t hurt much.</dd>
+ <dt>/SET lag_max_before_disconnect 300</dt>
+ <dd>Maximum server lag in seconds before disconnecting and trying to reconnect. This happens mostly only when network breaks between you and IRC server.</dd>
</dl>
-
<p><strong>Appearance</strong></p>
<dl>
-<dt>/SET timestamps ON</dt>
+ <dt>/SET timestamps ON</dt>
<dd>Show timestamps before each message.</dd>
-
-<dt>/SET hide_text_style OFF</dt>
+ <dt>/SET hide_text_style OFF</dt>
<dd>Hide all bolds, underlines, MIRC colors, etc.</dd>
-
-<dt>/SET show_nickmode ON</dt>
- <dd>Show the nick's mode before nick in channels, ie. ops have
- <code>&lt;@nick&gt;</code>, voices <code>&lt;+nick&gt;</code> and others
- <code>&lt;&nbsp;nick&gt;</code></dd>
-
-<dt>/SET show_nickmode_empty ON</dt>
- <dd>If the nick doesn't have a mode, use one space. ie. ON:
- <code>&lt;&nbsp;nick&gt;</code>, OFF: <code>&lt;nick&gt;</code></dd>
-
-<dt>/SET show_quit_once OFF</dt>
- <dd>Show quit message only once in some of the channel windows the
- nick was in instead of in all windows.</dd>
-
-<dt>/SET lag_min_show 100</dt>
- <dd>Show the server lag in status bar if it's bigger than this, the
- unit is 1/100 of seconds (ie. the default value of 100 = 1 second).</dd>
-
-<dt>/SET indent 10</dt>
- <dd>When lines are longer than screen width they have to be split to
- multiple lines. This specifies how much space to put at the beginning
- of the line before the text begins. This can be overridden in text
- formats with <code>%|</code> format.</dd>
-
-<dt>/SET activity_hide_targets</dt>
- <dd>If you don't want to see window activity in some certain channels
- or queries, list them here. For example <code>#boringchannel =bot1
- =bot2</code>. If any highlighted text or message for you appears in that
- window, this setting is ignored and the activity is shown.</dd>
+ <dt>/SET show_nickmode ON</dt>
+ <dd>Show the nick’s mode before nick in channels, ie. ops have <code>&lt;@nick&gt;</code>, voices <code>&lt;+nick&gt;</code> and others <code>&lt; nick&gt;</code></dd>
+ <dt>/SET show_nickmode_empty ON</dt>
+ <dd>If the nick doesn’t have a mode, use one space. ie. ON: <code>&lt; nick&gt;</code>, OFF: <code>&lt;nick&gt;</code></dd>
+ <dt>/SET show_quit_once OFF</dt>
+ <dd>Show quit message only once in some of the channel windows the nick was in instead of in all windows.</dd>
+ <dt>/SET lag_min_show 100</dt>
+ <dd>Show the server lag in status bar if it’s bigger than this, the unit is 1/100 of seconds (ie. the default value of 100 = 1 second).</dd>
+ <dt>/SET indent 10</dt>
+ <dd>When lines are longer than screen width they have to be split to multiple lines. This specifies how much space to put at the beginning of the line before the text begins. This can be overridden in text formats with <code>%|</code> format.</dd>
+ <dt>/SET activity_hide_targets</dt>
+ <dd>If you don’t want to see window activity in some certain channels or queries, list them here. For example <code>#boringchannel =bot1 =bot2</code>. If any highlighted text or message for you appears in that window, this setting is ignored and the activity is shown.</dd>
</dl>
<p><strong>Nick completion</strong></p>
<dl>
-<dt>/SET completion_auto OFF</dt>
- <dd>Automatically complete the nick if line begins with start of nick
- and the completion character. Learn to use the tab-completion instead,
- it's a lot better ;)</dd>
-
-<dt>/SET completion_char :</dt>
+ <dt>/SET completion_auto OFF</dt>
+ <dd>Automatically complete the nick if line begins with start of nick and the completion character. Learn to use the tab-completion instead, it’s a lot better ;)</dd>
+ <dt>/SET completion_char :</dt>
<dd>Completion character to use.</dd>
</dl>
-<h3><a id="c12">12. Statusbar</a></h3>
+<h2 id="statusbar">12. Statusbar</h2>
<p><code>/STATUSBAR</code> displays a list of statusbars:</p>
-<pre>
-Name Type Placement Position Visible
-window window bottom 0 always
-window_inact window bottom 1 inactive
-prompt root bottom 100 always
-topic root top 1 always
-</pre>
+<pre><code> Name Type Placement Position Visible
+ window window bottom 0 always
+ window_inact window bottom 1 inactive
+ prompt root bottom 100 always
+ topic root top 1 always
+</code></pre>
-<p><code>/STATUSBAR &lt;name&gt;</code> prints the statusbar settings and
-it's items. <code>/STATUSBAR &lt;name&gt; ENABLE|DISABLE</code>
-enables/disables the statusbar. <code>/STATUSBAR &lt;name&gt; RESET</code>
-resets the statusbar to it's default settings, or if the statusbar was
-created by you, it will be removed.</p>
+<p><code>/STATUSBAR &lt;name&gt;</code> prints the statusbar settings and it’s items. <code>/STATUSBAR &lt;name&gt; ENABLE|DISABLE</code> enables/disables the statusbar. <code>/STATUSBAR &lt;name&gt; RESET</code> resets the statusbar to it’s default settings, or if the statusbar was created by you, it will be removed.</p>
-<p>Type can be window or root, meaning if the statusbar should be
-created for each split window, or just once. Placement can be top or
-bottom. Position is a number, the higher the value the lower in screen
-it is. Visible can be always, active or inactive. Active/inactive is
-useful only with split windows, one split window is active and the rest
-are inactive. These settings can be changed with:</p>
+<p>Type can be window or root, meaning if the statusbar should be created for each split window, or just once. Placement can be top or bottom. Position is a number, the higher the value the lower in screen it is. Visible can be always, active or inactive. Active/inactive is useful only with split windows, one split window is active and the rest are inactive. These settings can be changed with:</p>
-<pre>
-/STATUSBAR &lt;name&gt; TYPE window|root
-/STATUSBAR &lt;name&gt; PLACEMENT top|bottom
-/STATUSBAR &lt;name&gt; POSITION &lt;num&gt;
-/STATUSBAR &lt;name&gt; VISIBLE always|active|inactive
-</pre>
+<pre><code> /STATUSBAR &lt;name&gt; TYPE window|root
+ /STATUSBAR &lt;name&gt; PLACEMENT top|bottom
+ /STATUSBAR &lt;name&gt; POSITION &lt;num&gt;
+ /STATUSBAR &lt;name&gt; VISIBLE always|active|inactive
+</code></pre>
-<p>When loading a new statusbar scripts, you'll need to also specify
-where you want to show it. Statusbar items can be modified with:</p>
+<p>When loading a new statusbar scripts, you’ll need to also specify where you want to show it. Statusbar items can be modified with:</p>
-<pre>
-/STATUSBAR &lt;name&gt; ADD [-before | -after &lt;item&gt;] [-priority #] [-alignment left|right] &lt;item&gt;
-/STATUSBAR &lt;name&gt; REMOVE &lt;item&gt;
-</pre>
+<pre><code> /STATUSBAR &lt;name&gt; ADD [-before | -after &lt;item&gt;] [-priority #] [-alignment left|right] &lt;item&gt;
+ /STATUSBAR &lt;name&gt; REMOVE &lt;item&gt;
+</code></pre>
-<p>The item name with statusbar scripts is usually same as the script's
-name. Script's documentation should tell if this isn't the case. So, to
-add mail.pl before the window activity item (see the list with
-<code>/STATUSBAR</code> window), use: <code>/STATUSBAR window ADD -before
-act mail</code>.</p>
+<p>The item name with statusbar scripts is usually same as the script’s name. Script’s documentation should tell if this isn’t the case. So, to add mail.pl before the window activity item (see the list with <code>/STATUSBAR</code> window), use: <code>/STATUSBAR window ADD -before act mail</code>.</p>
diff --git a/irssi-version.sh b/irssi-version.sh
index 49abc55e..1fc6a558 100755
--- a/irssi-version.sh
+++ b/irssi-version.sh
@@ -5,5 +5,24 @@ DATE=`GIT_DIR=$1/.git git log -1 --pretty=format:%ai HEAD`
VERSION_DATE=`echo $DATE | cut -f 1 -d ' ' | tr -d -`
VERSION_TIME=`echo $DATE | cut -f 2 -d ' ' | awk -F: '{printf "%d", $1$2}'`
+if test -z "$VERSION_DATE"; then
+ exec>&2
+ echo "**Error**: `basename "$0"` must be run in a git clone, cannot proceed."
+ exit 1
+fi
+
echo "#define IRSSI_VERSION_DATE $VERSION_DATE"
echo "#define IRSSI_VERSION_TIME $VERSION_TIME"
+
+if echo "${VERSION}" | grep -q -- -head; then
+ # -head version, get extra details from git if we can
+ git_version=$(GIT_DIR=$1/.git git describe --dirty --long --always --tags)
+ if [ $? = 0 ]; then
+ new_version="$(echo "${VERSION}" | sed 's/-head//')"
+ # Because the git tag won't yet include the next release we modify the git
+ # describe output using the version defined from configure.ac.
+ version="${new_version}-$(echo "${git_version}" | sed 's/^.*-[0-9]\+-//')"
+ echo "#undef PACKAGE_VERSION"
+ echo "#define PACKAGE_VERSION \"${version}\""
+ fi
+fi
diff --git a/irssi.conf b/irssi.conf
index ba159f34..0e486807 100644
--- a/irssi.conf
+++ b/irssi.conf
@@ -1,16 +1,16 @@
servers = (
{ address = "irc.dal.net"; chatnet = "DALnet"; port = "6667"; },
- { address = "irc.efnet.org"; chatnet = "EFNet"; port = "6667"; },
- { address = "irc.esper.net"; chatnet = "EsperNet"; port = "6667"; },
- { address = "chat.freenode.net"; chatnet = "Freenode"; port = "6667"; },
+ { address = "ssl.efnet.org"; chatnet = "EFNet"; port = "9999"; use_tls = "yes"; },
+ { address = "irc.esper.net"; chatnet = "EsperNet"; port = "6697"; use_tls = "yes"; tls_verify = "yes"; },
+ { address = "chat.freenode.net"; chatnet = "Freenode"; port = "6697"; use_tls = "yes"; tls_verify = "yes"; },
{ address = "irc.gamesurge.net"; chatnet = "GameSurge"; port = "6667"; },
- { address = "eu.irc6.net"; chatnet = "IRCnet"; port = "6667"; },
+ { address = "eu.irc6.net"; chatnet = "IRCnet"; port = "6667"; use_tls = "yes"; },
{ address = "open.ircnet.net"; chatnet = "IRCnet"; port = "6667"; },
{ address = "irc.ircsource.net"; chatnet = "IRCSource"; port = "6667"; },
{ address = "irc.netfuze.net"; chatnet = "NetFuze"; port = "6667"; },
- { address = "irc.oftc.net"; chatnet = "OFTC"; port = "6667"; },
+ { address = "irc.oftc.net"; chatnet = "OFTC"; port = "6697"; use_tls = "yes"; tls_verify = "yes"; },
{ address = "irc.quakenet.org"; chatnet = "QuakeNet"; port = "6667"; },
- { address = "irc.rizon.net"; chatnet = "Rizon"; port = "6667"; },
+ { address = "irc.rizon.net"; chatnet = "Rizon"; port = "6697"; use_tls = "yes"; tls_verify = "yes"; },
{ address = "silc.silcnet.org"; chatnet = "SILC"; port = "706"; },
{ address = "irc.undernet.org"; chatnet = "Undernet"; port = "6667"; }
);
diff --git a/m4/curses.m4 b/m4/curses.m4
deleted file mode 100644
index 82b110fe..00000000
--- a/m4/curses.m4
+++ /dev/null
@@ -1,298 +0,0 @@
-dnl Curses detection: Munged from Midnight Commander's configure.in
-dnl
-dnl What it does:
-dnl =============
-dnl
-dnl - Determine which version of curses is installed on your system
-dnl and set the -I/-L/-l compiler entries and add a few preprocessor
-dnl symbols
-dnl - Do an AC_SUBST on the CURSES_INCLUDEDIR and CURSES_LIBS so that
-dnl @CURSES_INCLUDEDIR@ and @CURSES_LIBS@ will be available in
-dnl Makefile.in's
-dnl - Modify the following configure variables (these are the only
-dnl curses.m4 variables you can access from within configure.in)
-dnl CURSES_INCLUDEDIR - contains -I's and possibly -DRENAMED_CURSES if
-dnl an ncurses.h that's been renamed to curses.h
-dnl is found.
-dnl CURSES_LIBS - sets -L and -l's appropriately
-dnl CFLAGS - if --with-sco, add -D_SVID3
-dnl has_curses - exports result of tests to rest of configure
-dnl
-dnl Usage:
-dnl ======
-dnl 1) Add lines indicated below to acconfig.h
-dnl 2) call AC_CHECK_CURSES after AC_PROG_CC in your configure.in
-dnl 3) Instead of #include <curses.h> you should use the following to
-dnl properly locate ncurses or curses header file
-dnl
-dnl #if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
-dnl #include <ncurses.h>
-dnl #else
-dnl #include <curses.h>
-dnl #endif
-dnl
-dnl 4) Make sure to add @CURSES_INCLUDEDIR@ to your preprocessor flags
-dnl 5) Make sure to add @CURSES_LIBS@ to your linker flags or LIBS
-dnl
-dnl Notes with automake:
-dnl - call AM_CONDITIONAL(HAS_CURSES, test "$has_curses" = true) from
-dnl configure.in
-dnl - your Makefile.am can look something like this
-dnl -----------------------------------------------
-dnl INCLUDES= blah blah blah $(CURSES_INCLUDEDIR)
-dnl if HAS_CURSES
-dnl CURSES_TARGETS=name_of_curses_prog
-dnl endif
-dnl bin_PROGRAMS = other_programs $(CURSES_TARGETS)
-dnl other_programs_SOURCES = blah blah blah
-dnl name_of_curses_prog_SOURCES = blah blah blah
-dnl other_programs_LDADD = blah
-dnl name_of_curses_prog_LDADD = blah $(CURSES_LIBS)
-dnl -----------------------------------------------
-dnl
-dnl
-dnl The following lines should be added to acconfig.h:
-dnl ==================================================
-dnl
-dnl /*=== Curses version detection defines ===*/
-dnl /* Found some version of curses that we're going to use */
-dnl #undef HAS_CURSES
-dnl
-dnl /* Use SunOS SysV curses? */
-dnl #undef USE_SUNOS_CURSES
-dnl
-dnl /* Use old BSD curses - not used right now */
-dnl #undef USE_BSD_CURSES
-dnl
-dnl /* Use SystemV curses? */
-dnl #undef USE_SYSV_CURSES
-dnl
-dnl /* Use Ncurses? */
-dnl #undef USE_NCURSES
-dnl
-dnl /* If you Curses does not have color define this one */
-dnl #undef NO_COLOR_CURSES
-dnl
-dnl /* Define if you want to turn on SCO-specific code */
-dnl #undef SCO_FLAVOR
-dnl
-dnl /* Set to reflect version of ncurses *
-dnl * 0 = version 1.*
-dnl * 1 = version 1.9.9g
-dnl * 2 = version 4.0/4.1 */
-dnl #undef NCURSES_970530
-dnl
-dnl /*=== End new stuff for acconfig.h ===*/
-dnl
-
-
-AC_DEFUN([AC_CHECK_CURSES],[
- search_ncurses=true
- screen_manager=""
- has_curses=false
-
- CFLAGS=${CFLAGS--O}
-
- AC_SUBST(CURSES_LIBS)
- AC_SUBST(CURSES_INCLUDEDIR)
-
- AC_ARG_WITH(sco,
- [ --with-sco Use this to turn on SCO-specific code],[
- if test x$withval = xyes; then
- AC_DEFINE(SCO_FLAVOR)
- CFLAGS="$CFLAGS -D_SVID3"
- fi
- ])
-
- AC_ARG_WITH(sunos-curses,
- [ --with-sunos-curses Used to force SunOS 4.x curses],[
- if test x$withval = xyes; then
- AC_USE_SUNOS_CURSES
- fi
- ])
-
- AC_ARG_WITH(osf1-curses,
- [ --with-osf1-curses Used to force OSF/1 curses],[
- if test x$withval = xyes; then
- AC_USE_OSF1_CURSES
- fi
- ])
-
- AC_ARG_WITH(vcurses,
- [[ --with-vcurses[=incdir] Used to force SysV curses]],
- if test x$withval != xyes; then
- CURSES_INCLUDEDIR="-I$withval"
- fi
- AC_USE_SYSV_CURSES
- )
-
- AC_ARG_WITH(ncurses,
- [[ --with-ncurses[=dir] Compile with ncurses/locate base dir]],
- if test x$withval = xno ; then
- search_ncurses=false
- elif test x$withval != xyes ; then
- AC_NCURSES($withval/include, ncurses.h, -L$withval/lib -lncurses, -I$withval/include, "ncurses on $withval/include")
- fi
- )
-
- if $search_ncurses
- then
- AC_SEARCH_NCURSES()
- fi
-])
-
-
-AC_DEFUN([AC_USE_SUNOS_CURSES], [
- search_ncurses=false
- screen_manager="SunOS 4.x /usr/5include curses"
- AC_MSG_RESULT(Using SunOS 4.x /usr/5include curses)
- AC_DEFINE(USE_SUNOS_CURSES)
- AC_DEFINE(HAS_CURSES)
- has_curses=true
- AC_DEFINE(NO_COLOR_CURSES)
- AC_DEFINE(USE_SYSV_CURSES)
- CURSES_INCLUDEDIR="-I/usr/5include"
- CURSES_LIBS="/usr/5lib/libcurses.a /usr/5lib/libtermcap.a"
- AC_MSG_RESULT(Please note that some screen refreshs may fail)
-])
-
-AC_DEFUN([AC_USE_OSF1_CURSES], [
- AC_MSG_RESULT(Using OSF1 curses)
- search_ncurses=false
- screen_manager="OSF1 curses"
- AC_DEFINE(HAS_CURSES)
- has_curses=true
- AC_DEFINE(NO_COLOR_CURSES)
- AC_DEFINE(USE_SYSV_CURSES)
- CURSES_LIBS="-lcurses"
-])
-
-AC_DEFUN([AC_USE_SYSV_CURSES], [
- AC_MSG_RESULT(Using SysV curses)
- AC_DEFINE(HAS_CURSES)
- has_curses=true
- AC_DEFINE(USE_SYSV_CURSES)
- search_ncurses=false
- screen_manager="SysV/curses"
- CURSES_LIBS="-lcurses"
-])
-
-dnl AC_ARG_WITH(bsd-curses,
-dnl [--with-bsd-curses Used to compile with bsd curses, not very fancy],
-dnl search_ncurses=false
-dnl screen_manager="Ultrix/cursesX"
-dnl if test $system = ULTRIX
-dnl then
-dnl THIS_CURSES=cursesX
-dnl else
-dnl THIS_CURSES=curses
-dnl fi
-dnl
-dnl CURSES_LIBS="-l$THIS_CURSES -ltermcap"
-dnl AC_DEFINE(HAS_CURSES)
-dnl has_curses=true
-dnl AC_DEFINE(USE_BSD_CURSES)
-dnl AC_MSG_RESULT(Please note that some screen refreshs may fail)
-dnl AC_WARN(Use of the bsdcurses extension has some)
-dnl AC_WARN(display/input problems.)
-dnl AC_WARN(Reconsider using xcurses)
-dnl)
-
-
-dnl
-dnl Parameters: directory filename curses_LIBS curses_INCLUDEDIR nicename
-dnl
-AC_DEFUN([AC_NCURSES], [
- if $search_ncurses
- then
- if test -f $1/$2
- then
- AC_MSG_RESULT(Found ncurses on $1/$2)
-
- CURSES_LIBS="$3"
- AC_CHECK_LIB(ncurses, initscr, [
- true;
- ], [
- CHECKLIBS=`echo "$3"|sed 's/-lncurses/-lcurses/g'`
- AC_CHECK_LIB(curses, initscr, [
- CURSES_LIBS="$CHECKLIBS"
- ],, $CHECKLIBS)
- ], $CURSES_LIBS)
- CURSES_INCLUDEDIR="$4"
- search_ncurses=false
- screen_manager="$5"
- AC_DEFINE(HAS_CURSES)
- has_curses=true
- has_ncurses=true
- AC_DEFINE(USE_NCURSES)
- fi
- fi
-])
-
-AC_DEFUN([AC_SEARCH_NCURSES], [
- AC_CHECKING("location of ncurses.h file")
-
- AC_NCURSES(/usr/include, ncurses.h, -lncurses,,
- [ncurses in /usr/include])
- AC_NCURSES(/usr/include/ncurses, ncurses.h, -lncurses, -I/usr/include/ncurses,
- [ncurses in /usr/include/ncurses])
- AC_NCURSES(/usr/local/include, ncurses.h, -L/usr/local/lib -lncurses, -I/usr/local/include,
- [ncurses in /usr/local/include])
- AC_NCURSES(/usr/local/include/ncurses, ncurses.h, -L/usr/local/lib -lncurses, -I/usr/local/include/ncurses,
- [ncurses in /usr/local/include/ncurses])
- AC_NCURSES(/usr/local/include/ncurses, curses.h, -L/usr/local/lib -lncurses, -I/usr/local/include/ncurses -DRENAMED_NCURSES,
- [renamed ncurses in /usr/local/include/ncurses])
- AC_NCURSES(/usr/include/ncurses, curses.h, -lncurses, -I/usr/include/ncurses -DRENAMED_NCURSES,
- [renamed ncurses in /usr/include/ncurses])
-
- dnl
- dnl We couldn't find ncurses, try SysV curses
- dnl
- if $search_ncurses
- then
- AC_EGREP_HEADER(init_color, /usr/include/curses.h,
- AC_USE_SYSV_CURSES)
- AC_EGREP_CPP(USE_NCURSES,[
-#include <curses.h>
-#ifdef __NCURSES_H
-#undef USE_NCURSES
-USE_NCURSES
-#endif
-],[
- CURSES_INCLUDEDIR="$CURSES_INCLUDEDIR -DRENAMED_NCURSES"
- AC_DEFINE(HAS_CURSES)
- has_curses=true
- has_ncurses=true
- AC_DEFINE(USE_NCURSES)
- search_ncurses=false
- screen_manager="ncurses installed as curses"
-])
- fi
-
- dnl
- dnl Try SunOS 4.x /usr/5{lib,include} ncurses
- dnl The flags USE_SUNOS_CURSES, USE_BSD_CURSES and BUGGY_CURSES
- dnl should be replaced by a more fine grained selection routine
- dnl
- if $search_ncurses
- then
- if test -f /usr/5include/curses.h
- then
- AC_USE_SUNOS_CURSES
- fi
- fi
-
- dnl use whatever curses there happens to be
- if $search_ncurses
- then
- if test -f /usr/include/curses.h
- then
- CURSES_LIBS="-lcurses"
- AC_DEFINE(HAS_CURSES)
- has_curses=true
- search_ncurses=false
- screen_manager="curses"
- fi
- fi
-])
-
diff --git a/m4/glib-2.0.m4 b/m4/glib-2.0.m4
index 2a5afd0c..5b7c84c0 100644
--- a/m4/glib-2.0.m4
+++ b/m4/glib-2.0.m4
@@ -43,7 +43,7 @@ AC_ARG_ENABLE(glibtest, [ --disable-glibtest do not try to compile and run
min_glib_version=ifelse([$1], ,2.0.0,$1)
AC_MSG_CHECKING(for GLIB - version >= $min_glib_version)
- if test x$PKG_CONFIG != xno ; then
+ if test "x$PKG_CONFIG" != xno ; then
## don't try to run the test against uninstalled libtool libs
if $PKG_CONFIG --uninstalled $pkg_config_args; then
echo "Will use uninstalled version of GLib found in PKG_CONFIG_PATH"
@@ -187,7 +187,7 @@ main ()
echo "*** If you have an old version installed, it is best to remove it, although"
echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ],
[ echo "*** The test program failed to compile or link. See the file config.log for the"
- echo "*** exact error that occured. This usually means GLIB is incorrectly installed."])
+ echo "*** exact error that occurred. This usually means GLIB is incorrectly installed."])
CFLAGS="$ac_save_CFLAGS"
LIBS="$ac_save_LIBS"
fi
diff --git a/scripts/Makefile.am b/scripts/Makefile.am
index b3c641b7..cd795153 100644
--- a/scripts/Makefile.am
+++ b/scripts/Makefile.am
@@ -11,9 +11,7 @@ script_DATA = \
mail.pl \
mlock.pl \
quitmsg.pl \
- sb_search.pl \
scriptassist.pl \
- splitlong.pl \
usercount.pl
EXTRA_DIST = $(script_DATA)
diff --git a/scripts/autoop.pl b/scripts/autoop.pl
index f7182999..b72def15 100644
--- a/scripts/autoop.pl
+++ b/scripts/autoop.pl
@@ -5,13 +5,13 @@ use Irssi;
use strict;
use vars qw($VERSION %IRSSI);
-$VERSION = "1.00";
+$VERSION = "1.10";
%IRSSI = (
- authors => 'Timo Sirainen',
+ authors => 'Timo Sirainen & Jostein Kjønigsen',
name => 'autoop',
description => 'Simple auto-op script',
license => 'Public Domain',
- changed => 'Sun Mar 10 23:18 EET 2002'
+ changed => 'Fri Nov 24 12:55 GMT+1 2014'
);
my (%opnicks, %temp_opped);
@@ -64,7 +64,7 @@ sub autoop {
if (!$temp_opped{$nick} &&
$server->masks_match($masks, $nick, $host)) {
- $channel->command("op $nick");
+ $channel->command("/op $nick");
$temp_opped{$nick} = 1;
}
}
@@ -89,3 +89,68 @@ sub event_massjoin {
Irssi::command_bind('autoop', 'cmd_autoop');
Irssi::signal_add_last('massjoin', 'event_massjoin');
+
+sub load_autoops {
+ my($file) = Irssi::get_irssi_dir."/autoop";
+ my($count) = 0;
+ local(*CONF);
+
+ %opnicks = ();
+ open(CONF, "<", "$file") or return;
+ while (my $line = <CONF>) {
+ if ($line !=~ /^\s*$/) {
+ cmd_autoop($line);
+ $count++;
+ }
+ }
+ close(CONF);
+
+ Irssi::print("Loaded $count channels from $file");
+}
+
+# --------[ save_autoops ]------------------------------------------------
+
+sub save_autoops {
+ my($auto) = @_;
+ my($file) = Irssi::get_irssi_dir."/autoop";
+ my($count) = 0;
+ my($channel) = "";
+ local(*CONF);
+
+ return if $auto;
+
+ open(CONF, ">", "$file");
+ foreach $channel (keys %opnicks) {
+ my $masks = $opnicks{$channel};
+ print CONF "$channel\t$masks\n";
+ $count++;
+ }
+ close(CONF);
+
+ Irssi::print("Saved $count channels to $file")
+ unless $auto;
+}
+
+
+# --------[ sig_setup_reread ]------------------------------------------
+
+# main setup is reread, so let us do it too
+sub sig_setup_reread {
+ load_autoops;
+}
+
+# --------[ sig_setup_save ]--------------------------------------------
+
+# main config is saved, and so we should save too
+sub sig_setup_save {
+ my($mainconf,$auto) = @_;
+ save_autoops($auto);
+}
+
+# persistance
+
+Irssi::signal_add('setup saved', 'sig_setup_save');
+Irssi::signal_add('setup reread', 'sig_setup_reread');
+
+# ensure we load persisted values on start
+load_autoops;
diff --git a/scripts/autorejoin.pl b/scripts/autorejoin.pl
index 2d23449f..42c97da7 100644
--- a/scripts/autorejoin.pl
+++ b/scripts/autorejoin.pl
@@ -1,6 +1,5 @@
-# automatically rejoin to channel after kicked
-
-# /SET autorejoin_channels #channel1 #channel2 ...
+# automatically rejoin to channel after kick
+# delayed rejoin: Lam 28.10.2001 (lam@lac.pl)
# NOTE: I personally don't like this feature, in most channels I'm in it
# will just result as ban. You've probably misunderstood the idea of /KICK
@@ -10,43 +9,49 @@ use Irssi;
use Irssi::Irc;
use strict;
use vars qw($VERSION %IRSSI);
-
-$VERSION = "1.00";
+$VERSION = "1.0.0";
%IRSSI = (
- authors => 'Timo Sirainen',
- name => 'autorejoin',
- description => 'Automatically rejoin to channel after kicked',
- license => 'Public Domain',
- changed => 'Sun Mar 10 23:18 EET 2002'
+ authors => "Timo 'cras' Sirainen, Leszek Matok",
+ contact => "lam\@lac.pl",
+ name => "autorejoin",
+ description => "Automatically rejoin to channel after being kick, after a (short) user-defined delay",
+ license => "GPLv2",
+ changed => "10.3.2002 14:00"
);
-sub channel_rejoin {
- my ($server, $channel) = @_;
- # check if channel has password
- my $chanrec = $server->channel_find($channel);
- my $password = $chanrec->{key} if ($chanrec);
+# How many seconds to wait before the rejoin?
+# TODO: make this a /setting
+my $delay = 5;
+
+my @tags;
+my $acttag = 0;
+
+sub rejoin {
+ my ( $data ) = @_;
+ my ( $tag, $servtag, $channel, $pass ) = split( / +/, $data );
- # We have to use send_raw() because the channel record still
- # exists and irssi won't even try to join to it with command()
- $server->send_raw("JOIN $channel $password");
+ my $server = Irssi::server_find_tag( $servtag );
+ $server->send_raw( "JOIN $channel $pass" ) if ( $server );
+ Irssi::timeout_remove( $tags[$tag] );
}
sub event_rejoin_kick {
- my ($server, $data) = @_;
- my ($channel, $nick) = split(/ +/, $data);
-
- return if ($server->{nick} ne $nick);
-
- # check if we want to autorejoin this channel
- my @chans = split(/[ ,]+/, Irssi::settings_get_str('autorejoin_channels'));
- foreach my $chan (@chans) {
- if (lc($chan) eq lc($channel)) {
- channel_rejoin($server, $channel);
- last;
- }
- }
+ my ( $server, $data ) = @_;
+ my ( $channel, $nick ) = split( / +/, $data );
+
+ return if ( $server->{ nick } ne $nick );
+
+ # check if channel has password
+ my $chanrec = $server->channel_find( $channel );
+ my $password = $chanrec->{ key } if ( $chanrec );
+ my $rejoinchan = $chanrec->{ name } if ( $chanrec );
+ my $servtag = $server->{ tag };
+
+ Irssi::print "Rejoining $rejoinchan in $delay seconds.";
+ $tags[$acttag] = Irssi::timeout_add( $delay * 1000, "rejoin", "$acttag $servtag $rejoinchan $password" );
+ $acttag++;
+ $acttag = 0 if ( $acttag > 60 );
}
-Irssi::settings_add_str('misc', 'autorejoin_channels', '');
-Irssi::signal_add('event kick', 'event_rejoin_kick');
+Irssi::signal_add( 'event kick', 'event_rejoin_kick' );
diff --git a/scripts/buf.pl b/scripts/buf.pl
index 43b4b3dd..6d907f12 100644
--- a/scripts/buf.pl
+++ b/scripts/buf.pl
@@ -5,7 +5,7 @@ use Irssi qw(command signal_add signal_add_first active_win
settings_get_str settings_get_bool channels windows
settings_add_str settings_add_bool get_irssi_dir
window_find_refnum signal_stop);
-$VERSION = '2.13';
+$VERSION = '2.20';
%IRSSI = (
authors => 'Juerd',
contact => 'juerd@juerd.nl',
@@ -13,10 +13,8 @@ $VERSION = '2.13';
description => 'Saves the buffer for /upgrade, so that no information is lost',
license => 'Public Domain',
url => 'http://juerd.nl/irssi/',
- changed => 'Mon May 13 19:41 CET 2002',
- changes => 'Severe formatting bug removed * oops, I ' .
- 'exposed Irssi to ircII foolishness * sorry ' .
- '** removed logging stuff (this is a fix)',
+ changed => 'Thu Sep 22 01:37 CEST 2016',
+ changes => 'Fixed file permissions (leaked everything via filesystem)',
note1 => 'This script HAS TO BE in your scripts/autorun!',
note2 => 'Perl support must be static or in startup',
);
@@ -39,9 +37,15 @@ use Data::Dumper;
my %suppress;
+sub _filename { sprintf '%s/scrollbuffer', get_irssi_dir }
+
sub upgrade {
- open BUF, sprintf('>%s/scrollbuffer', get_irssi_dir) or die $!;
- print BUF join("\0", map $_->{server}->{address} . $_->{name}, channels), "\n";
+ my $fn = _filename;
+ my $old_umask = umask 0077;
+ open my $fh, q{>}, $fn or die "open $fn: $!";
+ umask $old_umask;
+
+ print $fh join("\0", map $_->{server}->{address} . $_->{name}, channels), "\n";
for my $window (windows) {
next unless defined $window;
next if $window->{name} eq 'status';
@@ -57,36 +61,39 @@ sub upgrade {
redo if defined $line;
}
}
- printf BUF "%s:%s\n%s", $window->{refnum}, $lines, $buf;
+ printf $fh "%s:%s\n%s", $window->{refnum}, $lines, $buf;
}
- close BUF;
+ close $fh;
unlink sprintf("%s/sessionconfig", get_irssi_dir);
command 'layout save';
command 'save';
}
sub restore {
- open BUF, sprintf('<%s/scrollbuffer', get_irssi_dir) or die $!;
- my @suppress = split /\0/, <BUF>;
+ my $fn = _filename;
+ open my $fh, q{<}, $fn or die "open $fn: $!";
+ unlink $fn or warn "unlink $fn: $!";
+
+ my @suppress = split /\0/, readline $fh;
if (settings_get_bool 'upgrade_suppress_join') {
chomp $suppress[-1];
@suppress{@suppress} = (2) x @suppress;
}
active_win->command('^window scroll off');
- while (my $bla = <BUF>){
+ while (my $bla = readline $fh){
chomp $bla;
my ($refnum, $lines) = split /:/, $bla;
next unless $lines;
my $window = window_find_refnum $refnum;
unless (defined $window){
- <BUF> for 1..$lines;
+ readline $fh for 1..$lines;
next;
}
my $view = $window->view;
$view->remove_all_lines();
$view->redraw();
my $buf = '';
- $buf .= <BUF> for 1..$lines;
+ $buf .= readline $fh for 1..$lines;
my $sep = settings_get_str 'upgrade_separator';
$sep .= "\n" if $sep ne '';
$window->gui_printtext_after(undef, MSGLEVEL_CLIENTNOTICE, "$buf\cO$sep");
@@ -98,14 +105,13 @@ sub restore {
sub suppress {
my ($first, $second) = @_;
- return
- unless scalar keys %suppress
- and settings_get_bool 'upgrade_suppress_join';
- my $key = $first->{address} .
- (grep { (s/^://, /^[#!+&]/) } split ' ', $second)[0];
+ return unless scalar keys %suppress and settings_get_bool 'upgrade_suppress_join';
+ my $key_part = (grep { /^:?[#!+&]/ } split ' ', $second)[0];
+ $key_part =~ s/^://;
+ my $key = $first->{address} . $key_part;
if (exists $suppress{$key} and $suppress{$key}--) {
- signal_stop();
- delete $suppress{$key} unless $suppress{$key};
+ signal_stop();
+ delete $suppress{$key} unless $suppress{$key};
}
}
@@ -120,3 +126,10 @@ signal_add 'event join' => 'suppress';
unless (-f sprintf('%s/scripts/autorun/buf.pl', get_irssi_dir)) {
Irssi::print('PUT THIS SCRIPT IN ~/.irssi/scripts/autorun/ BEFORE /UPGRADING!!');
}
+
+# Remove any left-over file. If 'session' doesn't exist (created by irssi
+# during /UPGRADE), neither should our file.
+unless (-e sprintf('%s/session', get_irssi_dir)) {
+ my $fn = _filename;
+ unlink $fn or warn "unlink $fn: $!" if -e $fn;
+}
diff --git a/scripts/dns.pl b/scripts/dns.pl
index 612fab0e..989cdc3e 100644
--- a/scripts/dns.pl
+++ b/scripts/dns.pl
@@ -1,18 +1,24 @@
# /DNS <nick>|<host>|<ip> ...
+# version 2.1.1
+#
+# updated the script to fix a bug where the script would let
+# a trailing whitespace go through (ex: tab completion)
+# - inch <inch@stmpd.net>
-use Irssi;
use strict;
use Socket;
use POSIX;
use vars qw($VERSION %IRSSI);
-$VERSION = "2.1";
+$VERSION = "2.1.1";
%IRSSI = (
- authors => 'Timo Sirainen',
- name => 'dns',
- description => '/DNS <nick>|<host>|<ip> ...',
- license => 'Public Domain',
- changed => 'Sun Mar 10 23:23 EET 2002'
+ authors => "Timo \'cras\' Sirainen",
+ contact => "tss\@iki.fi",
+ name => "dns",
+ description => "/DNS <nick>|<host>|<ip> ...",
+ license => "Public Domain",
+ url => "http://irssi.org/",
+ changed => "2002-03-04T22:47+0100"
);
my (%resolve_hosts, %resolve_nicks, %resolve_print); # resolve queues
@@ -28,7 +34,7 @@ my $pipe_tag;
sub cmd_dns {
my ($nicks, $server) = @_;
return if !$nicks;
-
+ $nicks =~ s/\s+$//;
# get list of nicks/hosts we want to know
my $tag = !$server ? undef : $server->{tag};
my $ask_nicks = "";
diff --git a/scripts/kills.pl b/scripts/kills.pl
index 7ed2d533..50d9383d 100644
--- a/scripts/kills.pl
+++ b/scripts/kills.pl
@@ -10,6 +10,7 @@
# There's a pretty good explanation of (ircnet) ircd's server kills in
# http://www.irc.org/tech_docs/ircnet/kills.html
+use strict;
use Irssi;
use vars qw($VERSION %IRSSI);
@@ -47,13 +48,13 @@ sub msg_quit {
my @printargs = ();
if ($killmsg =~ /([^ ]*) != (.*)/) {
# 1 != 2
- my $server1 = $1, $server2 = $2;
+ my $server1 = $1, my $server2 = $2;
$server1 =~ s/([^\[]*)\[([^\]]*)\]/\1/;
$msg .= "$2 != $server2";
} elsif ($killmsg =~ /([^ ]*) <- (.*)/) {
# 1 <- 2
- my $server1 = $1, $server2 = $2;
+ my $server1 = $1, my $server2 = $2;
if ($server1 =~ /^\(/) {
# (addr1)server1 <- (add2)server2
@@ -84,9 +85,9 @@ sub msg_quit {
$msg = $killmsg;
}
- @list = $server->nicks_get_same($nick);
+ my @list = $server->nicks_get_same($nick);
while (@list) {
- $channel = $list[0];
+ my $channel = $list[0];
shift @list;
# skip nick record
shift @list;
diff --git a/scripts/mail.pl b/scripts/mail.pl
index 33b3c22e..ded02120 100644
--- a/scripts/mail.pl
+++ b/scripts/mail.pl
@@ -1,3 +1,5 @@
+use strict;
+use vars qw($VERSION %IRSSI);
$VERSION = "2.92";
%IRSSI = (
authors => "Timo Sirainen, Matti Hiljanen, Joost Vunderink, Bart Matthaei",
@@ -28,6 +30,7 @@ $VERSION = "2.92";
# Check /mailbox help for help.
use Irssi::TextUI;
+use Irssi;
my $maildirmode = 0; # maildir=1, file(spools)=0
my $old_is_not_new = 0;
@@ -35,7 +38,7 @@ my $extprog;
my ($last_refresh_time, $refresh_tag);
# for mbox caching
-my $last_size, $last_mtime, $last_mailcount, $last_mode;
+my ($last_size, $last_mtime, $last_mailcount, $last_mode);
# list of mailboxes
my %mailboxes = ();
@@ -99,8 +102,9 @@ sub mbox_count {
my $old_is_not_new=Irssi::settings_get_bool('mail_oldnotnew');
if ($extprog ne "") {
- $total = `$extprog`;
- chomp $unread;
+ my $total = `$extprog`;
+ chomp $total;
+ ($read, $unread) = split ' ', $total, 2;
} else {
if (!$maildirmode) {
if (-f $mailfile) {
@@ -113,8 +117,7 @@ sub mbox_count {
$last_size = $size;
$last_mtime = $mtime;
- my $f = gensym;
- return 0 if (!open($f, $mailfile));
+ return 0 if (!open(my $f, "<", $mailfile));
# count new mails only
my $internal_removed = 0;
@@ -203,7 +206,7 @@ sub mail {
my $total = 0;
# check all mailboxes for new email
- foreach $name (keys(%mailboxes)) {
+ foreach my $name (keys(%mailboxes)) {
my $box = $mailboxes{$name};
# replace "~/" at the beginning by the user's home dir
$box =~ s/^~\//$ENV{'HOME'}\//;
@@ -231,7 +234,7 @@ sub mail {
# Show this only if there are any new, unread messages.
if (Irssi::settings_get_bool('mail_show_message') &&
$unread > $new_mails_in_box{$name}) {
- $new_mails = $unread - $new_mails_in_box{$name};
+ my $new_mails = $unread - $new_mails_in_box{$name};
if ($nummailboxes == 1) {
Irssi::print("You have $new_mails new message" . ($new_mails != 1 ? "s." : "."), MSGLEVEL_CRAP);
} else {
@@ -261,11 +264,9 @@ sub add_mailboxes {
my $boxstring = $_[0];
my @boxes = split(/,/, $boxstring);
- foreach $dbox(@boxes) {
- my $name = $dbox;
- $name = substr($dbox, 0, index($dbox, '='));
- my $box = $dbox;
- $box = substr($dbox, index($dbox, '=') + 1, length($dbox));
+ foreach my $dbox(@boxes) {
+ my $name = substr($dbox, 0, index($dbox, '='));
+ my $box = substr($dbox, index($dbox, '=') + 1, length($dbox));
addmailbox($name, $box);
}
}
@@ -304,7 +305,7 @@ sub delmailbox {
sub update_settings_string {
my $setting;
- foreach $name (keys(%mailboxes)) {
+ foreach my $name (keys(%mailboxes)) {
$setting .= $name . "=" . $mailboxes{$name} . ",";
}
@@ -343,7 +344,7 @@ sub cmd_showmailboxes {
return;
}
Irssi::print("Mailboxes:", MSGLEVEL_CRAP);
- foreach $box (keys(%mailboxes)) {
+ foreach my $box (keys(%mailboxes)) {
Irssi::print("$box: " . $mailboxes{$box}, MSGLEVEL_CRAP);
}
}
diff --git a/scripts/mlock.pl b/scripts/mlock.pl
index ed2fe52b..bf2fd002 100644
--- a/scripts/mlock.pl
+++ b/scripts/mlock.pl
@@ -113,7 +113,7 @@ sub mlock_check_mode {
}
if ($modecmd ne "") {
- $channel->{server}->command("mode $channame $modecmd$extracmd");
+ $channel->{server}->command("/mode $channame $modecmd$extracmd");
}
}
diff --git a/scripts/quitmsg.pl b/scripts/quitmsg.pl
index 41bddaa8..e289468c 100644
--- a/scripts/quitmsg.pl
+++ b/scripts/quitmsg.pl
@@ -21,7 +21,7 @@ sub cmd_quit {
my ($data, $server, $channel) = @_;
return if ($data ne "");
- open (f, $quitfile) || return;
+ open (f, "<", $quitfile) || return;
my $lines = 0; while(<f>) { $lines++; };
my $line = int(rand($lines))+1;
@@ -38,7 +38,7 @@ sub cmd_quit {
close(f);
foreach my $server (Irssi::servers) {
- $server->command("disconnect ".$server->{tag}." $quitmsg");
+ $server->command("/disconnect ".$server->{tag}." $quitmsg");
}
}
diff --git a/scripts/sb_search.pl b/scripts/sb_search.pl
deleted file mode 100644
index 43fc7b55..00000000
--- a/scripts/sb_search.pl
+++ /dev/null
@@ -1,142 +0,0 @@
-# sb_search.pl - search in your scrollback, scroll to a match
-# Do /HELP SCROLLBACK for help
-
-# Copyright (C) 2008 Wouter Coekaerts <wouter@coekaerts.be>, Emanuele Giaquinta <exg@irssi.org>
-#
-# 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
-
-use strict;
-use Irssi;
-use Irssi::TextUI;
-use vars qw($VERSION %IRSSI);
-
-$VERSION = '1.0';
-%IRSSI = (
- authors => 'Wouter Coekaerts, Emanuele Giaquinta',
- contact => 'wouter@coekaerts.be, exg@irssi.org',
- name => 'sb_search',
- description => 'search in your scrollback, scroll to a match',
- license => 'GPLv2 or later',
- url => 'http://wouter.coekaerts.be/irssi/',
- changed => '$LastChangedDate$',
-);
-
-sub cmd_help {
- my ($args, $server, $witem) = @_;
- if ($args =~ /^scrollback( search)? *$/i) {
- Irssi::print ( <<SCRIPTHELP_EOF
-
-SCROLLBACK SEARCH [-level <level>] [-regexp] [-case] [-word] [-forward] [-all] [<pattern>]
-
- -level: only search for lines with the given level. see /help levels
- -regexp: the pattern is a regular expression
- -case: search case sensitive
- -word: pattern must match to full words
- -forward: search forwards (default is backwards)
- -all: search in all windows
- <pattern>: text to search for
-SCRIPTHELP_EOF
- ,MSGLEVEL_CLIENTCRAP);
- }
-}
-
-
-sub cmd_sb_search ($$$) {
- my ($args, $server, $witem) = @_;
-
- ### handle options
-
- my ($options, $pattern) = Irssi::command_parse_options('scrollback search', $args);
-
- my $level;
- if (defined($options->{level})) {
- $level = $options->{level};
- $level =~ y/,/ /;
- $level = Irssi::combine_level(0, $level);
- } else {
- return if (!$pattern);
- $level = MSGLEVEL_ALL;
- }
-
- my $regex;
- if ($pattern) {
- my $flags = defined($options->{case}) ? '' : '(?i)';
- my $b = defined($options->{word}) ? '\b' : '';
- if (defined($options->{regexp})) {
- $regex = qr/$flags$b$pattern$b/;
- } else {
- $regex = qr/$flags$b\Q$pattern\E$b/;
- }
- }
-
- my $forward = defined($options->{forward});
- my $all = defined($options->{all});
-
- ### determine window(s) to search in
-
- my $current_win = ref $witem ? $witem->window() : Irssi::active_win();
-
- my @windows;
- if ($all) {
- # cycle backward or forwards over all windows starting from current
- # for example, searching backward through 5 windows, with window 3 active: search order is 3,2,1,5,4
- # if we're searching forward: 3,4,5,1,2
- my $order = $forward ? 1 : -1;
- @windows = sort {$order * ($a->{refnum} cmp $b->{refnum})} Irssi::windows();
- my @before_windows = grep {($_->{refnum} cmp $current_win->{refnum}) == $order} @windows;
- my @after_windows = grep {($_->{refnum} cmp $current_win->{refnum}) == -$order} @windows;
- @windows = ($current_win, @before_windows, @after_windows);
- } else {
- @windows = ($current_win);
- }
-
- ### do the search
-
- foreach my $win (@windows) {
- my $view = $win->view;
-
- ## determine line to start from
- my $line;
- if ($all && $win != $current_win) {
- if ($forward) { # first line
- $line = $view->get_lines;
- } else { # last line
- $line = $view->{startline};
- while ($line->next) {
- $line = $line->next
- }
- }
- } else { # line after or before first visible line
- $line = $forward ? $view->{startline}->next : $view->{startline}->prev;
- }
-
- ## loop over the lines
- while (defined $line) {
- my $line_level = $line->{info}{level};
- if ($line_level & $level && $line->get_text(0) =~ $regex) {
- $view->scroll_line($line);
- if ($all) {
- Irssi::command('window goto ' . $win->{refnum});
- }
- return;
- }
- $line = $forward ? $line->next : $line->prev;
- }
- }
-}
-
-Irssi::command_bind('scrollback search', \&cmd_sb_search);
-Irssi::command_bind_last('help', \&cmd_help);
-Irssi::command_set_options('scrollback search', '-level regexp case word forward all');
diff --git a/scripts/scriptassist.pl b/scripts/scriptassist.pl
index 12678dbb..459d97f6 100644
--- a/scripts/scriptassist.pl
+++ b/scripts/scriptassist.pl
@@ -5,21 +5,19 @@
use strict;
-use vars qw($VERSION %IRSSI);
-$VERSION = '2003020803';
-%IRSSI = (
+our $VERSION = '2003020804';
+our %IRSSI = (
authors => 'Stefan \'tommie\' Tomanek',
contact => 'stefan@pico.ruhr.de',
name => 'scriptassist',
description => 'keeps your scripts on the cutting edge',
license => 'GPLv2',
url => 'http://irssi.org/scripts/',
- changed => $VERSION,
modules => 'Data::Dumper LWP::UserAgent (GnuPG)',
commands => "scriptassist"
);
-use vars qw($forked %remote_db $have_gpg);
+our ($forked, %remote_db, $have_gpg, @complist);
use Irssi 20020324;
use Data::Dumper;
@@ -27,12 +25,11 @@ use LWP::UserAgent;
use POSIX;
# GnuPG is not always needed
-use vars qw($have_gpg @complist);
$have_gpg = 0;
eval "use GnuPG qw(:algo :trust);";
$have_gpg = 1 if not ($@);
-sub show_help() {
+sub show_help {
my $help = "scriptassist $VERSION
/scriptassist check
Check all loaded scripts for new available versions
@@ -42,15 +39,15 @@ sub show_help() {
Search the script database
/scriptassist info <scripts>
Display information about <scripts>
-/scriptassist ratings <scripts>
- Retrieve the average ratings of the the scripts
-/scriptassist top <num>
- Retrieve the first <num> top rated scripts
-/scriptassist new <num>
+".#/scriptassist ratings <scripts>
+# Retrieve the average ratings of the the scripts
+#/scriptassist top <num>
+# Retrieve the first <num> top rated scripts
+"/scriptassist new <num>
Display the newest <num> scripts
-/scriptassist rate <script> <stars>
- Rate the script with a number of stars ranging from 0-5
-/scriptassist contact <script>
+".#/scriptassist rate <script> <stars>
+# Rate the script with a number of stars ranging from 0-5
+"/scriptassist contact <script>
Write an email to the author of the script
(Requires OpenURL)
/scriptassist cpan <module>
@@ -70,7 +67,7 @@ sub show_help() {
#theme_box("ScriptAssist", $text, "scriptassist help", 1);
}
-sub theme_box ($$$$) {
+sub theme_box {
my ($title, $text, $footer, $colour) = @_;
Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'box_header', $title);
foreach (split(/\n/, $text)) {
@@ -79,31 +76,30 @@ sub theme_box ($$$$) {
Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'box_footer', $footer);
}
-sub draw_box ($$$$) {
+sub draw_box {
my ($title, $text, $footer, $colour) = @_;
my $box = '';
$box .= '%R,--[%n%9%U'.$title.'%U%9%R]%n'."\n";
foreach (split(/\n/, $text)) {
$box .= '%R|%n '.$_."\n";
- } $box .= '%R`--<%n'.$footer.'%R>->%n';
+ }
+ $box .= '%R`--<%n'.$footer.'%R>->%n';
$box =~ s/%.//g unless $colour;
return $box;
}
-sub call_openurl ($) {
+sub call_openurl {
my ($url) = @_;
- no strict "refs";
# check for a loaded openurl
- if ( %{ "Irssi::Script::openurl::" }) {
- &{ "Irssi::Script::openurl::launch_url" }($url);
+ if (my $code = Irssi::Script::openurl::->can('launch_url')) {
+ $code->($url);
} else {
print CLIENTCRAP "%R>>%n Please install openurl.pl";
}
- use strict;
}
-sub bg_do ($) {
- my ($func) = @_;
+sub bg_do {
+ my ($func) = @_;
my ($rh, $wh);
pipe($rh, $wh);
if ($forked) {
@@ -137,7 +133,6 @@ sub bg_do ($) {
$result{data}{update} = update_scripts(\@items, $xml);
} elsif ($items[0] eq 'search') {
shift(@items);
- #$result{data}{search}{-foo} = 0;
foreach (@items) {
$result{data}{search}{$_} = search_scripts($_, $xml);
}
@@ -150,14 +145,12 @@ sub bg_do ($) {
} elsif ($items[0] eq 'ratings') {
shift(@items);
@items = @{ loaded_scripts() } if $items[0] eq "all";
- #$result{data}{rating}{-foo} = 1;
my %ratings = %{ get_ratings(\@items, '') };
foreach (keys %ratings) {
$result{data}{rating}{$_}{rating} = $ratings{$_}->[0];
$result{data}{rating}{$_}{votes} = $ratings{$_}->[1];
}
} elsif ($items[0] eq 'rate') {
- #$result{data}{rate}{-foo} = 1;
$result{data}{rate}{$items[1]} = rate_script($items[1], $items[2]);
} elsif ($items[0] eq 'info') {
shift(@items);
@@ -182,12 +175,16 @@ sub bg_do ($) {
my $data = $dumper->Dump;
print($wh $data);
};
+ if ($@) {
+ print($wh Data::Dumper->new([+{data=>+{error=>$@}}])
+ ->Purity(1)->Deepcopy(1)->Indent(0)->Dump);
+ }
close($wh);
POSIX::_exit(1);
}
}
-sub get_unknown ($$) {
+sub get_unknown {
my ($cmd, $db) = @_;
foreach (keys %$db) {
next unless defined $db->{$_}{commands};
@@ -198,56 +195,90 @@ sub get_unknown ($$) {
return undef;
}
-sub script_info ($) {
+sub get_names {
+ my ($sname, $db) = shift;
+ $sname =~ s/\s+$//;
+ $sname =~ s/\.pl$//;
+ my $plname = "$sname.pl";
+ $sname =~ s/^.*\///;
+ my $xname = $sname;
+ $xname =~ s/\W/_/g;
+ my $pname = "${xname}::";
+ if ($xname ne $sname || $sname =~ /_/) {
+ my $dir = Irssi::get_irssi_dir()."/scripts/";
+ if ($db && exists $db->{"$sname.pl"}) {
+ # $found = 1;
+ } elsif (-e $dir.$plname || -e $dir."$sname.pl" || -e $dir."autorun/$sname.pl") {
+ # $found = 1;
+ } else {
+ # not found
+ my $pat = $xname; $pat =~ y/_/?/;
+ my $re = "\Q$xname"; $re =~ s/\Q_/./g;
+ if ($db) {
+ my ($cand) = grep /^$re\.pl$/, sort keys %$db;
+ if ($cand) {
+ return get_names($cand, $db);
+ }
+ }
+ my ($cand) = glob "'$dir$pat.pl' '${dir}autorun/$pat.pl'";
+ if ($cand) {
+ $cand =~ s/^.*\///;
+ return get_names($cand, $db);
+ }
+ }
+ }
+ ($sname, $plname, $pname, $xname)
+}
+
+sub script_info {
my ($scripts) = @_;
- no strict "refs";
my %result;
my $xml = get_scripts();
foreach (@{$scripts}) {
- next unless (defined $xml->{$_.".pl"} || ( %{ 'Irssi::Script::'.$_.'::' } && %{ 'Irssi::Script::'.$_.'::IRSSI' }));
- $result{$_}{version} = get_remote_version($_, $xml);
+ my ($sname, $plname, $pname) = get_names($_, $xml);
+ next unless (defined $xml->{$plname} || ( exists $Irssi::Script::{$pname} && exists $Irssi::Script::{$pname}{IRSSI} ));
+ $result{$sname}{version} = get_remote_version($sname, $xml);
my @headers = ('authors', 'contact', 'description', 'license', 'source');
foreach my $entry (@headers) {
- $result{$_}{$entry} = ${ 'Irssi::Script::'.$_.'::IRSSI' }{$entry};
- if (defined $xml->{$_.".pl"}{$entry}) {
- $result{$_}{$entry} = $xml->{$_.".pl"}{$entry};
+ $result{$sname}{$entry} = $Irssi::Script::{$pname}{IRSSI}{$entry};
+ if (defined $xml->{$plname}{$entry}) {
+ $result{$sname}{$entry} = $xml->{$plname}{$entry};
}
}
- if ($xml->{$_.".pl"}{signature_available}) {
- $result{$_}{signature_available} = 1;
+ if ($xml->{$plname}{signature_available}) {
+ $result{$sname}{signature_available} = 1;
}
- if (defined $xml->{$_.".pl"}{modules}) {
- my $modules = $xml->{$_.".pl"}{modules};
- #$result{$_}{modules}{-foo} = 1;
+ if (defined $xml->{$plname}{modules}) {
+ my $modules = $xml->{$plname}{modules};
foreach my $mod (split(/ /, $modules)) {
my $opt = ($mod =~ /\((.*)\)/)? 1 : 0;
$mod = $1 if $1;
- $result{$_}{modules}{$mod}{optional} = $opt;
- $result{$_}{modules}{$mod}{installed} = module_exist($mod);
+ $result{$sname}{modules}{$mod}{optional} = $opt;
+ $result{$sname}{modules}{$mod}{installed} = module_exist($mod);
}
- } elsif (defined ${ 'Irssi::Script::'.$_.'::IRSSI' }{modules}) {
- my $modules = ${ 'Irssi::Script::'.$_.'::IRSSI' }{modules};
+ } elsif (defined $Irssi::Script::{$pname}{IRSSI}{modules}) {
+ my $modules = $Irssi::Script::{$pname}{IRSSI}{modules};
foreach my $mod (split(/ /, $modules)) {
my $opt = ($mod =~ /\((.*)\)/)? 1 : 0;
$mod = $1 if $1;
- $result{$_}{modules}{$mod}{optional} = $opt;
- $result{$_}{modules}{$mod}{installed} = module_exist($mod);
+ $result{$sname}{modules}{$mod}{optional} = $opt;
+ $result{$sname}{modules}{$mod}{installed} = module_exist($mod);
}
}
- if (defined $xml->{$_.".pl"}{depends}) {
- my $depends = $xml->{$_.".pl"}{depends};
+ if (defined $xml->{$plname}{depends}) {
+ my $depends = $xml->{$plname}{depends};
foreach my $dep (split(/ /, $depends)) {
- $result{$_}{depends}{$dep}{installed} = 1; #(defined ${ 'Irssi::Script::'.$dep });
+ $result{$sname}{depends}{$dep}{installed} = 1;
}
}
}
return \%result;
}
-sub rate_script ($$) {
+sub rate_script {
my ($script, $stars) = @_;
my $ua = LWP::UserAgent->new(env_proxy=>1, keep_alive=>1, timeout=>30);
- $ua->agent('ScriptAssist/'.$VERSION);
+ $ua->agent('ScriptAssist/'.2003020803);
my $request = HTTP::Request->new('GET', 'http://ratings.irssi.de/irssirate.pl?&stars='.$stars.'&mode=rate&script='.$script);
my $response = $ua->request($request);
unless ($response->is_success() && $response->content() =~ /You already rated this script/) {
@@ -257,10 +288,10 @@ sub rate_script ($$) {
}
}
-sub get_ratings ($$) {
+sub get_ratings {
my ($scripts, $limit) = @_;
my $ua = LWP::UserAgent->new(env_proxy=>1, keep_alive=>1, timeout=>30);
- $ua->agent('ScriptAssist/'.$VERSION);
+ $ua->agent('ScriptAssist/'.2003020803);
my $script = join(',', @{$scripts});
my $request = HTTP::Request->new('GET', 'http://ratings.irssi.de/irssirate.pl?script='.$script.'&sort=rating&limit='.$limit);
my $response = $ua->request($request);
@@ -278,7 +309,7 @@ sub get_ratings ($$) {
return \%result;
}
-sub get_new ($) {
+sub get_new {
my ($num) = @_;
my $result;
my $xml = get_scripts();
@@ -290,7 +321,7 @@ sub get_new ($) {
}
return $result;
}
-sub module_exist ($) {
+sub module_exist {
my ($module) = @_;
$module =~ s/::/\//g;
foreach (@INC) {
@@ -299,63 +330,64 @@ sub module_exist ($) {
return 0;
}
-sub debug_scripts ($) {
+sub debug_scripts {
my ($scripts) = @_;
my %result;
+ my $xml = get_scripts();
foreach (@{$scripts}) {
- my $xml = get_scripts();
- if (defined $xml->{$_.".pl"}{modules}) {
- my $modules = $xml->{$_.".pl"}{modules};
+ my ($sname, $plname) = get_names($_, $xml);
+ if (defined $xml->{$plname}{modules}) {
+ my $modules = $xml->{$plname}{modules};
foreach my $mod (split(/ /, $modules)) {
my $opt = ($mod =~ /\((.*)\)/)? 1 : 0;
$mod = $1 if $1;
- $result{$_}{$mod}{optional} = $opt;
- $result{$_}{$mod}{installed} = module_exist($mod);
+ $result{$sname}{$mod}{optional} = $opt;
+ $result{$sname}{$mod}{installed} = module_exist($mod);
}
}
}
return(\%result);
}
-sub install_scripts ($$) {
+sub install_scripts {
my ($scripts, $xml) = @_;
my %success;
- #$success{-foo} = 1;
my $dir = Irssi::get_irssi_dir()."/scripts/";
foreach (@{$scripts}) {
- if (get_local_version($_) && (-e $dir.$_.".pl")) {
- $success{$_}{installed} = -2;
+ my ($sname, $plname, $pname) = get_names($_, $xml);
+ if (get_local_version($sname) && (-e $dir.$plname)) {
+ $success{$sname}{installed} = -2;
} else {
- $success{$_} = download_script($_, $xml);
+ $success{$sname} = download_script($sname, $xml);
}
}
return \%success;
}
-sub update_scripts ($$) {
+sub update_scripts {
my ($list, $database) = @_;
$list = loaded_scripts() if ($list->[0] eq "all" || scalar(@$list) == 0);
my %status;
- #$status{-foo} = 1;
foreach (@{$list}) {
- my $local = get_local_version($_);
- my $remote = get_remote_version($_, $database);
+ my ($sname) = get_names($_, $database);
+ my $local = get_local_version($sname);
+ my $remote = get_remote_version($sname, $database);
next if $local eq '' || $remote eq '';
if (compare_versions($local, $remote) eq "older") {
- $status{$_} = download_script($_, $database);
+ $status{$sname} = download_script($sname, $database);
} else {
- $status{$_}{installed} = -2;
+ $status{$sname}{installed} = -2;
}
- $status{$_}{remote} = $remote;
- $status{$_}{local} = $local;
+ $status{$sname}{remote} = $remote;
+ $status{$sname}{local} = $local;
}
return \%status;
}
-sub search_scripts ($$) {
+sub search_scripts {
my ($query, $database) = @_;
+ $query =~ s/\.pl\Z//;
my %result;
- #$result{-foo} = " ";
foreach (sort keys %{$database}) {
my %entry = %{$database->{$_}};
my $string = $_." ";
@@ -385,23 +417,22 @@ sub search_scripts ($$) {
sub pipe_input {
my ($rh, $pipetag) = @{$_[0]};
- my @lines = <$rh>;
+ my $text = do { local $/; <$rh>; };
close($rh);
Irssi::input_remove($$pipetag);
$forked = 0;
- my $text = join("", @lines);
unless ($text) {
- print CLIENTCRAP "%R<<%n Something weird happend";
+ print CLIENTCRAP "%R<<%n Something weird happend (no text)";
return();
}
- no strict "vars";
- my $incoming = eval("$text");
+ local our $VAR1;
+ my $incoming = eval($text);
if ($incoming->{db} && $incoming->{timestamp}) {
$remote_db{db} = $incoming->{db};
$remote_db{timestamp} = $incoming->{timestamp};
}
unless (defined $incoming->{data}) {
- print CLIENTCRAP "%R<<%n Something weird happend";
+ print CLIENTCRAP "%R<<%n Something weird happend (no data)";
return;
}
my %result = %{ $incoming->{data} };
@@ -447,10 +478,14 @@ sub pipe_input {
if ($result{unknown}) {
print_unknown($result{unknown});
}
+ if (defined $result{error}) {
+ print CLIENTCRAP "%R<<%n There was an error in background processing:"; chomp($result{error});
+ print CLIENTERROR $result{error};
+ }
}
-sub print_unknown ($) {
+sub print_unknown {
my ($data) = @_;
foreach my $cmd (keys %$data) {
print CLIENTCRAP "%R<<%n No script provides '/$cmd'" unless $data->{$cmd};
@@ -458,7 +493,7 @@ sub print_unknown ($) {
my $text .= "The command '/".$cmd."' is provided by the script '".$data->{$cmd}{$_}{name}."'.\n";
$text .= "This script is currently not installed on your system.\n";
$text .= "If you want to install the script, enter\n";
- my ($name) = /(.*?)\.pl$/;
+ my ($name) = get_names($_);
$text .= " %U/script install ".$name."%U ";
my $output = draw_box("ScriptAssist", $text, "'".$_."' missing", 1);
print CLIENTCRAP $output;
@@ -466,11 +501,12 @@ sub print_unknown ($) {
}
}
-sub check_autorun ($) {
+sub check_autorun {
my ($script) = @_;
+ my (undef, $plname) = get_names($script);
my $dir = Irssi::get_irssi_dir()."/scripts/";
- if (-e $dir."/autorun/".$script.".pl") {
- if (readlink($dir."/autorun/".$script.".pl") eq "../".$script.".pl") {
+ if (-e $dir."/autorun/".$plname) {
+ if (readlink($dir."/autorun/".$plname) eq "../".$plname) {
return 1;
}
}
@@ -487,7 +523,7 @@ sub array2table {
$l =~ s/%%/%/g;
$width[$_] = length($l) if $width[$_]<length($l);
}
- }
+ }
my $text;
foreach my $line (@array) {
for (0..scalar(@$line)-1) {
@@ -503,7 +539,7 @@ sub array2table {
}
-sub print_info (%) {
+sub print_info {
my (%data) = @_;
my $line;
foreach my $script (sort keys(%data)) {
@@ -543,7 +579,6 @@ sub print_info (%) {
$line .= " <optional>" if $data{$script}{modules}{$_}{optional};
$line .= "\n";
}
- #$line .= " Needed Irssi scripts:\n";
$line .= " Needed Irssi Scripts:\n" if $data{$script}{depends};
foreach (sort keys %{$data{$script}{depends}}) {
if ( $data{$script}{depends}{$_}{installed} == 1 ) {
@@ -551,14 +586,13 @@ sub print_info (%) {
} else {
$line .= " %r->%n ".$_." (not loaded)";
}
- #$line .= " <optional>" if $data{$script}{depends}{$_}{optional};
$line .= "\n";
}
}
print CLIENTCRAP draw_box('ScriptAssist', $line, 'info', 1) ;
}
-sub print_rate (%) {
+sub print_rate {
my (%data) = @_;
my $line;
foreach my $script (sort keys(%data)) {
@@ -571,7 +605,7 @@ sub print_rate (%) {
print CLIENTCRAP draw_box('ScriptAssist', $line, 'rating', 1) ;
}
-sub print_ratings (%) {
+sub print_ratings {
my (%data) = @_;
my @table;
foreach my $script (sort {$data{$b}{rating}<=>$data{$a}{rating}} keys(%data)) {
@@ -589,12 +623,12 @@ sub print_ratings (%) {
print CLIENTCRAP draw_box('ScriptAssist', array2table(@table), 'ratings', 1) ;
}
-sub print_new ($) {
+sub print_new {
my ($list) = @_;
my @table;
foreach (sort {$list->{$b}{last_modified} cmp $list->{$a}{last_modified}} keys %$list) {
my @line;
- my ($name) = /^(.*?)\.pl$/;
+ my ($name) = get_names($_);
if (get_local_version($name)) {
push @line, "%go%n";
} else {
@@ -607,7 +641,7 @@ sub print_new ($) {
print CLIENTCRAP draw_box('ScriptAssist', array2table(@table), 'new scripts', 1) ;
}
-sub print_debug (%) {
+sub print_debug {
my (%data) = @_;
my $line;
foreach my $script (sort keys %data) {
@@ -627,12 +661,12 @@ sub print_debug (%) {
}
}
-sub load_script ($) {
+sub load_script {
my ($script) = @_;
Irssi::command('script load '.$script);
}
-sub print_install (%) {
+sub print_install {
my (%data) = @_;
my $text;
my ($crashed, @installed);
@@ -681,17 +715,16 @@ sub print_install (%) {
list_sbitems(\@installed);
}
-sub list_sbitems ($) {
+sub list_sbitems {
my ($scripts) = @_;
my $text;
foreach (@$scripts) {
- no strict 'refs';
- next unless %{ "Irssi::Script::${_}::" };
- next unless %{ "Irssi::Script::${_}::IRSSI" };
- my %header = %{ "Irssi::Script::${_}::IRSSI" };
- next unless $header{sbitems};
+ next unless exists $Irssi::Script::{"${_}::"};
+ next unless exists $Irssi::Script::{"${_}::"}{IRSSI};
+ my $header = $Irssi::Script::{"${_}::"}{IRSSI};
+ next unless $header->{sbitems};
$text .= '%9"'.$_.'"%9 provides the following statusbar item(s):'."\n";
- $text .= ' ->'.$_."\n" foreach (split / /, $header{sbitems});
+ $text .= ' ->'.$_."\n" foreach (split / /, $header->{sbitems});
}
return unless $text;
$text .= "\n";
@@ -699,7 +732,7 @@ sub list_sbitems ($) {
print CLIENTCRAP draw_box('ScriptAssist', $text, 'sbitems', 1);
}
-sub check_sig ($) {
+sub check_sig {
my ($sig) = @_;
my $line;
my %trust = ( -1 => 'undefined',
@@ -722,7 +755,7 @@ sub check_sig ($) {
return $line;
}
-sub print_search ($%) {
+sub print_search {
my ($query, %data) = @_;
my $text;
foreach (sort keys %data) {
@@ -738,7 +771,7 @@ sub print_search ($%) {
print CLIENTCRAP draw_box('ScriptAssist', $text, 'search: '.$query, 1) ;
}
-sub print_update (%) {
+sub print_update {
my (%data) = @_;
my $text;
my @table;
@@ -761,7 +794,7 @@ sub print_update (%) {
push @table, ['%yo%n', '%9'.$_.'%9', 'not upgraded'];
foreach (split /\n/, check_sig($data{$_})) {
push @table, ['', '', $_];
- }
+ }
} elsif ($data{$_}{installed} == -2 && $verbose) {
my $local = $data{$_}{local};
push @table, ['%go%n', '%9'.$_.'%9', 'already at the latest version ('.$local.')'];
@@ -771,35 +804,44 @@ sub print_update (%) {
print CLIENTCRAP draw_box('ScriptAssist', $text, 'update', 1) ;
}
-sub contact_author ($) {
+sub contact_author {
my ($script) = @_;
- no strict 'refs';
- return unless %{ "Irssi::Script::${script}::" };
- my %header = %{ "Irssi::Script::${script}::IRSSI" };
- if (defined $header{contact}) {
- my @ads = split(/ |,/, $header{contact});
+ my ($sname, $plname, $pname) = get_names($script);
+ return unless exists $Irssi::Script::{$pname};
+ my $header = $Irssi::Script::{$pname}{IRSSI};
+ if ($header && defined $header->{contact}) {
+ my @ads = split(/ |,/, $header->{contact});
my $address = $ads[0];
$address .= '?subject='.$script;
$address .= '_'.get_local_version($script) if defined get_local_version($script);
- call_openurl($address);
+ call_openurl($address) if $address =~ /[\@:]/;
}
}
sub get_scripts {
my $ua = LWP::UserAgent->new(env_proxy=>1, keep_alive=>1, timeout=>30);
- $ua->agent('ScriptAssist/'.$VERSION);
+ $ua->agent('ScriptAssist/'.2003020803);
$ua->env_proxy();
my @mirrors = split(/ /, Irssi::settings_get_str('scriptassist_script_sources'));
my %sites_db;
+ my $not_modified = 0;
my $fetched = 0;
my @sources;
+ my $error;
foreach my $site (@mirrors) {
my $request = HTTP::Request->new('GET', $site);
if ($remote_db{timestamp}) {
$request->if_modified_since($remote_db{timestamp});
}
my $response = $ua->request($request);
- next unless $response->is_success;
+ if ($response->code == 304) { # HTTP_NOT_MODIFIED
+ $not_modified = 1;
+ next;
+ }
+ unless ($response->is_success) {
+ $error = join "\n", $response->status_line(), (grep / at .* line \d+/, split "\n", $response->content()), '';
+ next;
+ }
$fetched = 1;
my $data = $response->content();
my ($src, $type);
@@ -826,9 +868,8 @@ sub get_scripts {
$sites_db{$_}{source} = $src;
}
} else {
- ## FIXME Panic?!
+ die("Unknown script database type ($type).\n");
}
-
}
if ($fetched) {
# Clean database
@@ -842,32 +883,40 @@ sub get_scripts {
}
$remote_db{db}{$_} = $sites_db{$_} foreach (keys %sites_db);
$remote_db{timestamp} = time();
+ } elsif ($not_modified) {
+ # nothing to do
+ } else {
+ die("No script database sources defined in /set scriptassist_script_sources\n") unless @mirrors;
+ die("Fetching script database failed: $error") if $error;
+ die("Unknown error while fetching script database\n");
}
return $remote_db{db};
}
-sub get_remote_version ($$) {
+sub get_remote_version {
my ($script, $database) = @_;
- return $database->{$script.".pl"}{version};
+ my $plname = (get_names($script, $database))[1];
+ return $database->{$plname}{version};
}
-sub get_local_version ($) {
+sub get_local_version {
my ($script) = @_;
- no strict 'refs';
- return unless %{ "Irssi::Script::${script}::" };
- my $version = ${ "Irssi::Script::${script}::VERSION" };
- return $version;
+ my $pname = (get_names($script))[2];
+ return unless exists $Irssi::Script::{$pname};
+ my $vref = $Irssi::Script::{$pname}{VERSION};
+ return $vref ? $$vref : undef;
}
-sub compare_versions ($$) {
+sub compare_versions {
my ($ver1, $ver2) = @_;
- my @ver1 = split /\./, $ver1;
- my @ver2 = split /\./, $ver2;
- #if (scalar(@ver2) != scalar(@ver1)) {
- # return 0;
- #}
+ for ($ver1, $ver2) {
+ $_ = "0:$_" unless /:/;
+ }
+ my @ver1 = split /[.:]/, $ver1;
+ my @ver2 = split /[.:]/, $ver2;
my $cmp = 0;
### Special thanks to Clemens Heidinger
+ no warnings 'uninitialized';
$cmp ||= $ver1[$_] <=> $ver2[$_] || $ver1[$_] cmp $ver2[$_] for 0..scalar(@ver2);
return 'newer' if $cmp == 1;
return 'older' if $cmp == -1;
@@ -875,24 +924,20 @@ sub compare_versions ($$) {
}
sub loaded_scripts {
- no strict 'refs';
my @modules;
foreach (sort grep(s/::$//, keys %Irssi::Script::)) {
- #my $name = ${ "Irssi::Script::${_}::IRSSI" }{name};
- #my $version = ${ "Irssi::Script::${_}::VERSION" };
- push @modules, $_;# if $name && $version;
+ push @modules, $_;
}
return \@modules;
-
}
sub check_scripts {
my ($data) = @_;
my %versions;
- #$versions{-foo} = 1;
foreach (@{loaded_scripts()}) {
- my $remote = get_remote_version($_, $data);
- my $local = get_local_version($_);
+ my ($sname) = get_names($_, $data);
+ my $remote = get_remote_version($sname, $data);
+ my $local = get_local_version($sname);
my $state;
if ($local && $remote) {
$state = compare_versions($local, $remote);
@@ -905,51 +950,50 @@ sub check_scripts {
$remote = '/';
}
if ($state) {
- $versions{$_}{state} = $state;
- $versions{$_}{remote} = $remote;
- $versions{$_}{local} = $local;
+ $versions{$sname}{state} = $state;
+ $versions{$sname}{remote} = $remote;
+ $versions{$sname}{local} = $local;
}
}
return \%versions;
}
-sub download_script ($$) {
+sub download_script {
my ($script, $xml) = @_;
+ my ($sname, $plname) = get_names($script, $xml);
my %result;
- my $site = $xml->{$script.".pl"}{source};
+ my $site = $xml->{$plname}{source};
$result{installed} = 0;
$result{signed} = 0;
my $dir = Irssi::get_irssi_dir();
my $ua = LWP::UserAgent->new(env_proxy => 1,keep_alive => 1,timeout => 30);
- $ua->agent('ScriptAssist/'.$VERSION);
+ $ua->agent('ScriptAssist/'.2003020803);
my $request = HTTP::Request->new('GET', $site.'/scripts/'.$script.'.pl');
my $response = $ua->request($request);
if ($response->is_success()) {
my $file = $response->content();
mkdir $dir.'/scripts/' unless (-e $dir.'/scripts/');
- local *F;
- open(F, '>'.$dir.'/scripts/'.$script.'.pl.new');
- print F $file;
- close(F);
+ open(my $F, '>', $dir.'/scripts/'.$plname.'.new');
+ print $F $file;
+ close($F);
if ($have_gpg && Irssi::settings_get_bool('scriptassist_use_gpg')) {
my $ua2 = LWP::UserAgent->new(env_proxy => 1,keep_alive => 1,timeout => 30);
- $ua->agent('ScriptAssist/'.$VERSION);
- my $request2 = HTTP::Request->new('GET', $site.'/signatures/'.$script.'.pl.asc');
+ $ua->agent('ScriptAssist/'.2003020803);
+ my $request2 = HTTP::Request->new('GET', $site.'/signatures/'.$plname.'.asc');
my $response2 = $ua->request($request2);
if ($response2->is_success()) {
- local *S;
my $sig_dir = $dir.'/scripts/signatures/';
mkdir $sig_dir unless (-e $sig_dir);
- open(S, '>'.$sig_dir.$script.'.pl.asc');
+ open(my $S, '>', $sig_dir.$plname.'.asc');
my $file2 = $response2->content();
- print S $file2;
- close(S);
+ print $S $file2;
+ close($S);
my $sig;
foreach (1..2) {
# FIXME gpg needs two rounds to load the key
my $gpg = new GnuPG();
eval {
- $sig = $gpg->verify( file => $dir.'/scripts/'.$script.'.pl.new', signature => $sig_dir.$script.'.pl.asc' );
+ $sig = $gpg->verify( file => $dir.'/scripts/'.$plname.'.new', signature => $sig_dir.$plname.'.asc' );
};
}
if (defined $sig->{user}) {
@@ -975,13 +1019,13 @@ sub download_script ($$) {
if ($result{installed}) {
my $old_dir = "$dir/scripts/old/";
mkdir $old_dir unless (-e $old_dir);
- rename "$dir/scripts/$script.pl", "$old_dir/$script.pl.old" if -e "$dir/scripts/$script.pl";
- rename "$dir/scripts/$script.pl.new", "$dir/scripts/$script.pl";
+ rename "$dir/scripts/$plname", "$old_dir/$plname.old" if -e "$dir/scripts/$plname";
+ rename "$dir/scripts/$plname.new", "$dir/scripts/$plname";
}
return \%result;
}
-sub print_check (%) {
+sub print_check {
my (%data) = @_;
my $text;
my @table;
@@ -1001,28 +1045,29 @@ sub print_check (%) {
print CLIENTCRAP draw_box('ScriptAssist', $text, 'check', 1) ;
}
-sub toggle_autorun ($) {
+sub toggle_autorun {
my ($script) = @_;
+ my ($sname, $plname) = get_names($script);
my $dir = Irssi::get_irssi_dir()."/scripts/";
mkdir $dir."autorun/" unless (-e $dir."autorun/");
- return unless (-e $dir.$script.".pl");
- if (check_autorun($script)) {
- if (readlink($dir."/autorun/".$script.".pl") eq "../".$script.".pl") {
- if (unlink($dir."/autorun/".$script.".pl")) {
- print CLIENTCRAP "%R>>%n Autorun of ".$script." disabled";
+ return unless (-e $dir.$plname);
+ if (check_autorun($sname)) {
+ if (readlink($dir."/autorun/".$plname) eq "../".$plname) {
+ if (unlink($dir."/autorun/".$plname)) {
+ print CLIENTCRAP "%R>>%n Autorun of ".$sname." disabled";
} else {
print CLIENTCRAP "%R>>%n Unable to delete link";
}
} else {
- print CLIENTCRAP "%R>>%n ".$dir."/autorun/".$script.".pl is not a correct link";
+ print CLIENTCRAP "%R>>%n ".$dir."/autorun/".$plname." is not a correct link";
}
} else {
- symlink("../".$script.".pl", $dir."/autorun/".$script.".pl");
- print CLIENTCRAP "%R>>%n Autorun of ".$script." enabled";
+ symlink("../".$plname, $dir."/autorun/".$plname);
+ print CLIENTCRAP "%R>>%n Autorun of ".$sname." enabled";
}
}
-sub sig_script_error ($$) {
+sub sig_script_error {
my ($script, $msg) = @_;
return unless Irssi::settings_get_bool('scriptassist_catch_script_errors');
if ($msg =~ /Can't locate (.*?)\.pm in \@INC \(\@INC contains:(.*?) at/) {
@@ -1032,7 +1077,7 @@ sub sig_script_error ($$) {
}
}
-sub missing_module ($$) {
+sub missing_module {
my ($module) = @_;
my $text;
$text .= "The perl module %9".$module."%9 is missing on your system.\n";
@@ -1041,7 +1086,7 @@ sub missing_module ($$) {
print CLIENTCRAP &draw_box('ScriptAssist', $text, $module, 1);
}
-sub cmd_scripassist ($$$) {
+sub cmd_scripassist {
my ($arg, $server, $witem) = @_;
my @args = split(/ /, $arg);
if ($args[0] eq 'help' || $args[0] eq '-h') {
@@ -1083,27 +1128,34 @@ sub cmd_scripassist ($$$) {
}
}
-sub sig_command_script_load ($$$) {
+sub cmd_help {
+ my ($arg, $server, $witem) = @_;
+ $arg =~ s/\s+$//;
+ if ($arg =~ /^scriptassist/i) {
+ show_help();
+ }
+}
+
+sub sig_command_script_load {
my ($script, $server, $witem) = @_;
- no strict;
- $script = $2 if $script =~ /(.*\/)?(.*?)\.pl$/;
- if ( %{ "Irssi::Script::${script}::" }) {
- if ( &{ "Irssi::Script::${script}::pre_unload" }) {
+ my ($sname, $plname, $pname, $xname) = get_names($script);
+ if ( exists $Irssi::Script::{$pname} ) {
+ if (my $code = "Irssi::Script::${pname}"->can('pre_unload')) {
print CLIENTCRAP "%R>>%n Triggering pre_unload function of $script...";
- &{ "Irssi::Script::${script}::pre_unload" }();
+ $code->();
}
}
}
-sub sig_default_command ($$) {
+sub sig_default_command {
my ($cmd, $server) = @_;
return unless Irssi::settings_get_bool("scriptassist_check_unknown_commands");
bg_do('unknown '.$cmd);
}
-sub sig_complete ($$$$$) {
+sub sig_complete {
my ($list, $window, $word, $linestart, $want_space) = @_;
- return unless $linestart =~ /^.script(assist)? (install|rate|ratings|update|check|contact|info|autorun)/;
+ return unless $linestart =~ /^.script(assist)? (install|rate|ratings|update|check|contact|info|autorun)/i;
my @newlist;
my $str = $word;
foreach (@complist) {
@@ -1114,13 +1166,12 @@ sub sig_complete ($$$$$) {
foreach (@{loaded_scripts()}) {
push @newlist, $_ if /^(\Q$str\E.*)?$/;
}
- $want_space = 0;
push @$list, $_ foreach @newlist;
Irssi::signal_stop();
}
-Irssi::settings_add_str($IRSSI{name}, 'scriptassist_script_sources', 'http://scripts.irssi.org/scripts.dmp');
+Irssi::settings_add_str($IRSSI{name}, 'scriptassist_script_sources', 'https://scripts.irssi.org/scripts.dmp');
Irssi::settings_add_bool($IRSSI{name}, 'scriptassist_cache_sources', 1);
Irssi::settings_add_bool($IRSSI{name}, 'scriptassist_update_verbose', 1);
Irssi::settings_add_bool($IRSSI{name}, 'scriptassist_check_verbose', 1);
@@ -1131,24 +1182,37 @@ Irssi::settings_add_bool($IRSSI{name}, 'scriptassist_use_gpg', 1);
Irssi::settings_add_bool($IRSSI{name}, 'scriptassist_integrate', 1);
Irssi::settings_add_bool($IRSSI{name}, 'scriptassist_check_unknown_commands', 1);
-Irssi::signal_add_first("default command", \&sig_default_command);
-Irssi::signal_add_first('complete word', \&sig_complete);
-Irssi::signal_add_first('command script load', \&sig_command_script_load);
-Irssi::signal_add_first('command script unload', \&sig_command_script_load);
+Irssi::signal_add_first("default command", 'sig_default_command');
+Irssi::signal_add_first('complete word', 'sig_complete');
+Irssi::signal_add_first('command script load', 'sig_command_script_load');
+Irssi::signal_add_first('command script unload', 'sig_command_script_load');
-if (defined &Irssi::signal_register) {
- Irssi::signal_register({ 'script error' => [ 'Irssi::Script', 'string' ] });
- Irssi::signal_add_last('script error', \&sig_script_error);
-}
+Irssi::signal_register({ 'script error' => [ 'Irssi::Script', 'string' ] });
+Irssi::signal_add_last('script error', 'sig_script_error');
-Irssi::command_bind('scriptassist', \&cmd_scripassist);
+Irssi::command_bind('scriptassist', 'cmd_scripassist');
+Irssi::command_bind('help', 'cmd_help');
Irssi::theme_register(['box_header', '%R,--[%n$*%R]%n',
'box_inside', '%R|%n $*',
'box_footer', '%R`--<%n$*%R>->%n',
]);
-foreach my $cmd ( ( 'check', 'install', 'update', 'contact', 'search', '-h', 'help', 'ratings', 'rate', 'info', 'echo', 'top', 'cpan', 'autorun', 'new') ) {
+foreach my $cmd ( ( 'check',
+ 'install',
+ 'update',
+ 'contact',
+ 'search',
+# '-h',
+ 'help',
+# 'ratings',
+# 'rate',
+ 'info',
+# 'echo',
+# 'top',
+ 'cpan',
+ 'autorun',
+ 'new' ) ) {
Irssi::command_bind('scriptassist '.$cmd => sub {
cmd_scripassist("$cmd ".$_[0], $_[1], $_[2]); });
if (Irssi::settings_get_bool('scriptassist_integrate')) {
diff --git a/scripts/splitlong.pl b/scripts/splitlong.pl
deleted file mode 100644
index e88840bc..00000000
--- a/scripts/splitlong.pl
+++ /dev/null
@@ -1,60 +0,0 @@
-# /set splitlong_max_length
-# specifies the maximum length of a msg, automatically chosen when set to "0"
-# default: 0
-#
-# /set splitlong_line_start
-# /set splitlong_line_end
-# self-explanatory
-# defaults: "... ", " ..."
-###
-use strict;
-use vars qw($VERSION %IRSSI);
-
-use Irssi 20011001;
-
-$VERSION = "0.20";
-%IRSSI = (
- authors => "Bjoern \'fuchs\' Krombholz",
- contact => "bjkro\@gmx.de",
- name => "splitlong",
- licence => "Public Domain",
- description => "Split overlong PRIVMSGs to msgs with length allowed by ircd",
- changed => "Wed Jun 25 00:17:00 CET 2003",
- changes => "Actually the real 0.19 (now 0.20), but upload didn't work some month ago, target problem fixed..."
-);
-
-sub sig_command_msg {
- my ($cmd, $server, $winitem) = @_;
- my ( $param, $target,$data) = $cmd =~ /^(-\S*\s)?(\S*)\s(.*)/;
-
- my $maxlength = Irssi::settings_get_int('splitlong_max_length');
- my $lstart = Irssi::settings_get_str('splitlong_line_start');
- my $lend = Irssi::settings_get_str('splitlong_line_end');
-
- if ($maxlength == 0) {
- # 497 = 510 - length(":" . "!" . " PRIVMSG " . " :");
- $maxlength = 497 - length($server->{nick} . $server->{userhost} . $target);
- }
- my $maxlength2 = $maxlength - length($lend);
-
- if (length($data) > ($maxlength)) {
- my @spltarr;
-
- while (length($data) > ($maxlength2)) {
- my $pos = rindex($data, " ", $maxlength2);
- push @spltarr, substr($data, 0, ($pos < ($maxlength/10 + 4)) ? $maxlength2 : $pos) . $lend;
- $data = $lstart . substr($data, ($pos < ($maxlength/10 + 4)) ? $maxlength2 : $pos+1);
- }
-
- push @spltarr, $data;
- foreach (@spltarr) {
- Irssi::signal_emit("command msg", "$target $_", $server, $winitem);
- }
- Irssi::signal_stop();
- }
-}
-
-Irssi::settings_add_int('misc', 'splitlong_max_length', 0);
-Irssi::settings_add_str('misc', 'splitlong_line_start', "... ");
-Irssi::settings_add_str('misc', 'splitlong_line_end', " ...");
-Irssi::command_bind('msg', 'sig_command_msg');
diff --git a/scripts/usercount.pl b/scripts/usercount.pl
index 46dc0b46..613da1de 100644
--- a/scripts/usercount.pl
+++ b/scripts/usercount.pl
@@ -1,4 +1,6 @@
+use strict;
use Irssi 20040119.2359 ();
+use vars qw($VERSION %IRSSI);
$VERSION = "1.19";
%IRSSI = (
authors => 'David Leadbeater, Timo Sirainen, Georg Lukas',
@@ -29,7 +31,6 @@ $VERSION = "1.19";
# sb_uc_space = " ";
-use strict;
use Irssi::TextUI;
my ($ircops, $ops, $halfops, $voices, $normal, $total);
diff --git a/src/Makefile.am b/src/Makefile.am
index 76a4af4f..a7fb2ee2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -6,6 +6,10 @@ if BUILD_IRSSIBOT
BOTUI=fe-none
endif
+if BUILD_IRSSIFUZZER
+FUZZERUI=fe-fuzz
+endif
+
if HAVE_PERL
PERLDIR=perl
endif
@@ -14,4 +18,4 @@ pkginc_srcdir=$(pkgincludedir)/src
pkginc_src_HEADERS = \
common.h
-SUBDIRS = lib-config core irc fe-common $(PERLDIR) $(TEXTUI) $(BOTUI)
+SUBDIRS = lib-config core irc fe-common $(PERLDIR) $(TEXTUI) $(BOTUI) $(FUZZERUI)
diff --git a/src/common.h b/src/common.h
index bb246962..43596580 100644
--- a/src/common.h
+++ b/src/common.h
@@ -6,6 +6,8 @@
#define IRSSI_GLOBAL_CONFIG "irssi.conf" /* config file name in /etc/ */
#define IRSSI_HOME_CONFIG "config" /* config file name in ~/.irssi/ */
+#define IRSSI_ABI_VERSION 7
+
#define DEFAULT_SERVER_ADD_PORT 6667
#ifdef HAVE_CONFIG_H
@@ -34,28 +36,12 @@
# include <dirent.h>
#endif
#include <fcntl.h>
-#ifdef WIN32
-# include <win32-compat.h>
-#endif
#include <glib.h>
#ifdef HAVE_GMODULE
# include <gmodule.h>
#endif
-#if !GLIB_CHECK_VERSION(2,10,0)
-#define g_slice_alloc(size) g_malloc(size)
-#define g_slice_alloc0(size) g_malloc0(size)
-#define g_slice_free1(size, mem) g_free(mem)
-#define g_slice_new(type) g_new(type, 1)
-#define g_slice_new0(type) g_new0(type, 1)
-#define g_slice_free(type, mem) g_free(mem)
-#endif
-
-#ifdef USE_GC
-# define g_free(x) G_STMT_START { (x) = NULL; } G_STMT_END
-#endif
-
#if defined (UOFF_T_INT)
typedef unsigned int uoff_t;
#elif defined (UOFF_T_LONG)
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
index fc32e17e..10bd035a 100644
--- a/src/core/Makefile.am
+++ b/src/core/Makefile.am
@@ -44,6 +44,9 @@ libcore_a_SOURCES = \
settings.c \
signals.c \
special-vars.c \
+ utf8.c \
+ wcwidth.c \
+ tls.c \
write-buffer.c
structure_headers = \
@@ -93,6 +96,8 @@ pkginc_core_HEADERS = \
settings.h \
signals.h \
special-vars.h \
+ utf8.h \
window-item-def.h \
+ tls.h \
write-buffer.h \
$(structure_headers)
diff --git a/src/core/channels-setup.c b/src/core/channels-setup.c
index e1289c23..4966d77d 100644
--- a/src/core/channels-setup.c
+++ b/src/core/channels-setup.c
@@ -30,17 +30,36 @@
GSList *setupchannels;
+static int compare_channel_setup (CONFIG_NODE *node, CHANNEL_SETUP_REC *channel)
+{
+ char *name, *chatnet;
+
+ name = config_node_get_str(node, "name", NULL);
+ chatnet = config_node_get_str(node, "chatnet", NULL);
+
+ if (g_strcmp0(name, channel->name) != 0 ||
+ g_strcmp0(chatnet, channel->chatnet) != 0)
+ return 1;
+
+ return 0;
+}
+
static void channel_setup_save(CHANNEL_SETUP_REC *channel)
{
- CONFIG_NODE *parentnode, *node;
- int index;
+ CONFIG_NODE *parent_node, *node;
+ GSList *config_node;
- index = g_slist_index(setupchannels, channel);
+ parent_node = iconfig_node_traverse("(channels", TRUE);
- parentnode = iconfig_node_traverse("(channels", TRUE);
- node = config_node_nth(parentnode, index);
- if (node == NULL)
- node = config_node_section(parentnode, NULL, NODE_TYPE_BLOCK);
+ /* Try to find this channel in the configuration */
+ config_node = g_slist_find_custom(parent_node->value, channel,
+ (GCompareFunc)compare_channel_setup);
+ if (config_node != NULL)
+ /* Let's update this channel record */
+ node = config_node->data;
+ else
+ /* Create a brand-new channel record */
+ node = iconfig_node_section(parent_node, NULL, NODE_TYPE_BLOCK);
iconfig_node_clear(node);
iconfig_node_set_str(node, "name", channel->name);
@@ -65,10 +84,21 @@ void channel_setup_create(CHANNEL_SETUP_REC *channel)
static void channel_config_remove(CHANNEL_SETUP_REC *channel)
{
- CONFIG_NODE *node;
+ CONFIG_NODE *parent_node;
+ GSList *config_node;
- node = iconfig_node_traverse("channels", FALSE);
- if (node != NULL) iconfig_node_list_remove(node, g_slist_index(setupchannels, channel));
+ parent_node = iconfig_node_traverse("channels", FALSE);
+
+ if (parent_node == NULL)
+ return;
+
+ /* Try to find this channel in the configuration */
+ config_node = g_slist_find_custom(parent_node->value, channel,
+ (GCompareFunc)compare_channel_setup);
+
+ if (config_node != NULL)
+ /* Delete the channel from the configuration */
+ iconfig_node_remove(parent_node, config_node->data);
}
static void channel_setup_destroy(CHANNEL_SETUP_REC *channel)
@@ -86,6 +116,21 @@ static void channel_setup_destroy(CHANNEL_SETUP_REC *channel)
g_free(channel);
}
+void channel_setup_remove_chatnet(const char *chatnet)
+{
+ GSList *tmp, *next;
+
+ g_return_if_fail(chatnet != NULL);
+
+ for (tmp = setupchannels; tmp != NULL; tmp = next) {
+ CHANNEL_SETUP_REC *rec = tmp->data;
+
+ next = tmp->next;
+ if (g_ascii_strcasecmp(rec->chatnet, chatnet) == 0)
+ channel_setup_remove(rec);
+ }
+}
+
void channel_setup_remove(CHANNEL_SETUP_REC *channel)
{
channel_config_remove(channel);
diff --git a/src/core/channels-setup.h b/src/core/channels-setup.h
index 61b828b2..3bb7da7f 100644
--- a/src/core/channels-setup.h
+++ b/src/core/channels-setup.h
@@ -21,6 +21,9 @@ void channels_setup_deinit(void);
void channel_setup_create(CHANNEL_SETUP_REC *channel);
void channel_setup_remove(CHANNEL_SETUP_REC *channel);
+/* Remove channels attached to chatnet */
+void channel_setup_remove_chatnet(const char *chatnet);
+
CHANNEL_SETUP_REC *channel_setup_find(const char *channel,
const char *chatnet);
diff --git a/src/core/channels.c b/src/core/channels.c
index 8235a4c7..9c3b92ba 100644
--- a/src/core/channels.c
+++ b/src/core/channels.c
@@ -167,7 +167,7 @@ static GSList *servers_find_chatnet_except(SERVER_REC *server)
SERVER_REC *rec = tmp->data;
if (server != rec && rec->connrec->chatnet != NULL &&
- strcmp(server->connrec->chatnet,
+ g_strcmp0(server->connrec->chatnet,
rec->connrec->chatnet) == 0) {
/* chatnets match */
list = g_slist_append(list, rec);
@@ -233,23 +233,46 @@ static int match_nick_flags(SERVER_REC *server, NICK_REC *nick, char flag)
void channel_send_autocommands(CHANNEL_REC *channel)
{
CHANNEL_SETUP_REC *rec;
- NICK_REC *nick;
- char **bots, **bot;
g_return_if_fail(IS_CHANNEL(channel));
if (channel->session_rejoin)
- return;
+ return;
rec = channel_setup_find(channel->name, channel->server->connrec->chatnet);
if (rec == NULL || rec->autosendcmd == NULL || !*rec->autosendcmd)
return;
+ /* if the autosendcmd alone (with no -bots parameter) has been
+ * specified then send it right after joining the channel, when
+ * the WHO list hasn't been yet retrieved.
+ * Depending on the value of the 'channel_max_who_sync' option
+ * the WHO list might not be retrieved after the join event. */
+
if (rec->botmasks == NULL || !*rec->botmasks) {
/* just send the command. */
eval_special_string(rec->autosendcmd, "", channel->server, channel);
- return;
}
+}
+
+void channel_send_botcommands(CHANNEL_REC *channel)
+{
+ CHANNEL_SETUP_REC *rec;
+ NICK_REC *nick;
+ char **bots, **bot;
+
+ g_return_if_fail(IS_CHANNEL(channel));
+
+ if (channel->session_rejoin)
+ return;
+
+ rec = channel_setup_find(channel->name, channel->server->connrec->chatnet);
+ if (rec == NULL || rec->autosendcmd == NULL || !*rec->autosendcmd)
+ return;
+
+ /* this case has already been handled by channel_send_autocommands */
+ if (rec->botmasks == NULL || !*rec->botmasks)
+ return;
/* find first available bot.. */
bots = g_strsplit(rec->botmasks, " ", -1);
diff --git a/src/core/channels.h b/src/core/channels.h
index 0839d69b..bd136fe2 100644
--- a/src/core/channels.h
+++ b/src/core/channels.h
@@ -31,6 +31,7 @@ void channel_change_visible_name(CHANNEL_REC *channel, const char *name);
/* Send the auto send command to channel */
void channel_send_autocommands(CHANNEL_REC *channel);
+void channel_send_botcommands(CHANNEL_REC *channel);
void channels_init(void);
void channels_deinit(void);
diff --git a/src/core/chat-commands.c b/src/core/chat-commands.c
index 235a9fc4..c737b810 100644
--- a/src/core/chat-commands.c
+++ b/src/core/chat-commands.c
@@ -58,7 +58,7 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
return NULL;
}
- if (strcmp(password, "-") == 0)
+ if (g_strcmp0(password, "-") == 0)
*password = '\0';
/* check if -<chatnet> option is used to specify chat protocol */
@@ -73,6 +73,13 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
conn = server_create_conn(proto != NULL ? proto->id : -1, addr,
atoi(portstr), chatnet, password, nick);
+ if (conn == NULL) {
+ signal_emit("error command", 1,
+ GINT_TO_POINTER(CMDERR_NO_SERVER_DEFINED));
+ cmd_params_free(free_arg);
+ return NULL;
+ }
+
if (proto == NULL)
proto = chat_protocol_find_id(conn->chat_type);
@@ -92,31 +99,37 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
else if (g_hash_table_lookup(optlist, "4") != NULL)
conn->family = AF_INET;
- if (g_hash_table_lookup(optlist, "ssl") != NULL)
- conn->use_ssl = TRUE;
- if ((tmp = g_hash_table_lookup(optlist, "ssl_cert")) != NULL)
- conn->ssl_cert = g_strdup(tmp);
- if ((tmp = g_hash_table_lookup(optlist, "ssl_pkey")) != NULL)
- conn->ssl_pkey = g_strdup(tmp);
- if ((tmp = g_hash_table_lookup(optlist, "ssl_pass")) != NULL)
- conn->ssl_pass = g_strdup(tmp);
- if (g_hash_table_lookup(optlist, "ssl_verify") != NULL)
- conn->ssl_verify = TRUE;
- if ((tmp = g_hash_table_lookup(optlist, "ssl_cafile")) != NULL)
- conn->ssl_cafile = g_strdup(tmp);
- if ((tmp = g_hash_table_lookup(optlist, "ssl_capath")) != NULL)
- conn->ssl_capath = g_strdup(tmp);
- if ((conn->ssl_capath != NULL && conn->ssl_capath[0] != '\0')
- || (conn->ssl_cafile != NULL && conn->ssl_cafile[0] != '\0'))
- conn->ssl_verify = TRUE;
- if ((conn->ssl_cert != NULL && conn->ssl_cert[0] != '\0') || conn->ssl_verify)
- conn->use_ssl = TRUE;
+ if (g_hash_table_lookup(optlist, "tls") != NULL || g_hash_table_lookup(optlist, "ssl") != NULL)
+ conn->use_tls = TRUE;
+ if ((tmp = g_hash_table_lookup(optlist, "tls_cert")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_cert")) != NULL)
+ conn->tls_cert = g_strdup(tmp);
+ if ((tmp = g_hash_table_lookup(optlist, "tls_pkey")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pkey")) != NULL)
+ conn->tls_pkey = g_strdup(tmp);
+ if ((tmp = g_hash_table_lookup(optlist, "tls_pass")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pass")) != NULL)
+ conn->tls_pass = g_strdup(tmp);
+ if (g_hash_table_lookup(optlist, "tls_verify") != NULL || g_hash_table_lookup(optlist, "ssl_verify") != NULL)
+ conn->tls_verify = TRUE;
+ if ((tmp = g_hash_table_lookup(optlist, "tls_cafile")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_cafile")) != NULL)
+ conn->tls_cafile = g_strdup(tmp);
+ if ((tmp = g_hash_table_lookup(optlist, "tls_capath")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_capath")) != NULL)
+ conn->tls_capath = g_strdup(tmp);
+ if ((tmp = g_hash_table_lookup(optlist, "tls_ciphers")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_ciphers")) != NULL)
+ conn->tls_ciphers = g_strdup(tmp);
+ if ((tmp = g_hash_table_lookup(optlist, "tls_pinned_cert")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pinned_cert")) != NULL)
+ conn->tls_pinned_cert = g_strdup(tmp);
+ if ((tmp = g_hash_table_lookup(optlist, "tls_pinned_pubkey")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pinned_pubkey")) != NULL)
+ conn->tls_pinned_pubkey = g_strdup(tmp);
+ if ((conn->tls_capath != NULL && conn->tls_capath[0] != '\0')
+ || (conn->tls_cafile != NULL && conn->tls_cafile[0] != '\0'))
+ conn->tls_verify = TRUE;
+ if ((conn->tls_cert != NULL && conn->tls_cert[0] != '\0') || conn->tls_verify)
+ conn->use_tls = TRUE;
if (g_hash_table_lookup(optlist, "!") != NULL)
conn->no_autojoin_channels = TRUE;
- if (g_hash_table_lookup(optlist, "noautosendcmd") != NULL)
- conn->no_autosendcmd = TRUE;
+ if (g_hash_table_lookup(optlist, "noautosendcmd") != NULL)
+ conn->no_autosendcmd = TRUE;
if (g_hash_table_lookup(optlist, "noproxy") != NULL)
g_free_and_null(conn->proxy);
@@ -138,6 +151,7 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
/* SYNTAX: CONNECT [-4 | -6] [-ssl] [-ssl_cert <cert>] [-ssl_pkey <pkey>] [-ssl_pass <password>]
[-ssl_verify] [-ssl_cafile <cafile>] [-ssl_capath <capath>]
+ [-ssl_ciphers <list>]
[-!] [-noautosendcmd]
[-noproxy] [-network <network>] [-host <hostname>]
[-rawlog <file>]
@@ -244,6 +258,7 @@ static void sig_default_command_server(const char *data, SERVER_REC *server,
/* SYNTAX: SERVER [-4 | -6] [-ssl] [-ssl_cert <cert>] [-ssl_pkey <pkey>] [-ssl_pass <password>]
[-ssl_verify] [-ssl_cafile <cafile>] [-ssl_capath <capath>]
+ [-ssl_ciphers <list>]
[-!] [-noautosendcmd]
[-noproxy] [-network <network>] [-host <hostname>]
[-rawlog <file>]
@@ -283,7 +298,7 @@ static void cmd_disconnect(const char *data, SERVER_REC *server)
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &tag, &msg))
return;
- if (*tag != '\0' && strcmp(tag, "*") != 0) {
+ if (*tag != '\0' && g_strcmp0(tag, "*") != 0) {
server = server_find_tag(tag);
if (server == NULL)
server = server_find_lookup_tag(tag);
@@ -321,7 +336,7 @@ static void cmd_quit(const char *data)
signal_emit("gui exit", 0);
}
-/* SYNTAX: MSG [-<server tag>] [-channel | -nick] <targets> <message> */
+/* SYNTAX: MSG [-<server tag>] [-channel | -nick] *|<targets> <message> */
static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
{
GHashTable *optlist;
@@ -343,7 +358,7 @@ static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
origtarget = target;
free_ret = FALSE;
- if (strcmp(target, ",") == 0 || strcmp(target, ".") == 0) {
+ if (g_strcmp0(target, ",") == 0 || g_strcmp0(target, ".") == 0) {
target = parse_special(&target, server, item,
NULL, &free_ret, NULL, 0);
if (target != NULL && *target == '\0') {
@@ -355,7 +370,7 @@ static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
}
if (target != NULL) {
- if (strcmp(target, "*") == 0) {
+ if (g_strcmp0(target, "*") == 0) {
/* send to active channel/query */
if (item == NULL)
cmd_param_error(CMDERR_NOT_JOINED);
@@ -483,7 +498,7 @@ void chat_commands_init(void)
signal_add("default command server", (SIGNAL_FUNC) sig_default_command_server);
signal_add("server sendmsg", (SIGNAL_FUNC) sig_server_sendmsg);
- command_set_options("connect", "4 6 !! -network ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +host noproxy -rawlog noautosendcmd");
+ command_set_options("connect", "4 6 !! -network ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +ssl_pinned_cert +ssl_pinned_pubkey tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers +tls_pinned_cert +tls_pinned_pubkey +host noproxy -rawlog noautosendcmd");
command_set_options("msg", "channel nick");
}
diff --git a/src/core/chatnets.c b/src/core/chatnets.c
index 3c794ab4..e0a7a8d9 100644
--- a/src/core/chatnets.c
+++ b/src/core/chatnets.c
@@ -36,7 +36,7 @@ static void chatnet_config_save(CHATNET_REC *chatnet)
CONFIG_NODE *node;
node = iconfig_node_traverse("chatnets", TRUE);
- node = config_node_section(node, chatnet->name, NODE_TYPE_BLOCK);
+ node = iconfig_node_section(node, chatnet->name, NODE_TYPE_BLOCK);
iconfig_node_clear(node);
iconfig_node_set_str(node, "type", chat_protocol_find_id(chatnet->chat_type)->name);
@@ -132,7 +132,7 @@ static void chatnet_read(CONFIG_NODE *node)
CHATNET_REC *rec;
char *type;
- if (node == NULL || node->key == NULL)
+ if (node == NULL || node->key == NULL || !is_node_list(node))
return;
type = config_node_get_str(node, "type", NULL);
diff --git a/src/core/commands.c b/src/core/commands.c
index ed82f44e..607baf77 100644
--- a/src/core/commands.c
+++ b/src/core/commands.c
@@ -666,7 +666,7 @@ get_optional_channel(WI_ITEM_REC *active_item, char **data, int require_name)
const char *ret;
char *tmp, *origtmp, *channel;
- if (active_item == NULL) {
+ if (active_item == NULL || active_item->server == NULL) {
/* no active channel in window, channel required */
return cmd_get_param(data);
}
@@ -674,11 +674,13 @@ get_optional_channel(WI_ITEM_REC *active_item, char **data, int require_name)
origtmp = tmp = g_strdup(*data);
channel = cmd_get_param(&tmp);
- if (strcmp(channel, "*") == 0 && !require_name) {
+ if (g_strcmp0(channel, "*") == 0 && IS_CHANNEL(active_item) &&
+ !require_name) {
/* "*" means active channel */
cmd_get_param(data);
ret = window_item_get_target(active_item);
- } else if (!server_ischannel(active_item->server, channel)) {
+ } else if (IS_CHANNEL(active_item) &&
+ !server_ischannel(active_item->server, channel)) {
/* we don't have channel parameter - use active channel */
ret = window_item_get_target(active_item);
} else {
@@ -748,6 +750,11 @@ int cmd_get_params(const char *data, gpointer *free_me, int count, ...)
if (cnt == 0 && count & PARAM_FLAG_GETREST) {
/* get rest */
arg = datad;
+
+ /* strip the trailing whitespace */
+ if (count & PARAM_FLAG_STRIP_TRAILING_WS) {
+ arg = g_strchomp(arg);
+ }
} else {
arg = (count & PARAM_FLAG_NOQUOTES) ?
cmd_get_param(&datad) :
diff --git a/src/core/commands.h b/src/core/commands.h
index c68c5b24..93d6276b 100644
--- a/src/core/commands.h
+++ b/src/core/commands.h
@@ -41,7 +41,8 @@ enum {
CMDERR_INVALID_TIME, /* invalid time specification */
CMDERR_INVALID_CHARSET, /* invalid charset specification */
CMDERR_EVAL_MAX_RECURSE, /* eval hit recursion limit */
- CMDERR_PROGRAM_NOT_FOUND /* program not found */
+ CMDERR_PROGRAM_NOT_FOUND, /* program not found */
+ CMDERR_NO_SERVER_DEFINED, /* no server has been defined for a given chatnet */
};
/* Return the full command for `alias' */
@@ -152,13 +153,15 @@ int command_have_option(const char *cmd, const char *option);
#define PARAM_FLAG_OPTCHAN 0x00010000
/* optional channel in first argument, but don't treat "*" as current channel */
#define PARAM_FLAG_OPTCHAN_NAME (0x00020000|PARAM_FLAG_OPTCHAN)
+/* strip the trailing whitespace */
+#define PARAM_FLAG_STRIP_TRAILING_WS 0x00040000
char *cmd_get_param(char **data);
char *cmd_get_quoted_param(char **data);
/* get parameters from command - you should point free_me somewhere and
cmd_params_free() it after you don't use any of the parameters anymore.
- Returns TRUE if all ok, FALSE if error occured. */
+ Returns TRUE if all ok, FALSE if error occurred. */
int cmd_get_params(const char *data, gpointer *free_me, int count, ...);
void cmd_params_free(void *free_me);
diff --git a/src/core/core.c b/src/core/core.c
index b9debbb5..bf7cdd6b 100644
--- a/src/core/core.c
+++ b/src/core/core.c
@@ -83,7 +83,6 @@ static void sig_reload_config(int signo)
static void read_settings(void)
{
-#ifndef WIN32
static int signals[] = {
SIGINT, SIGQUIT, SIGTERM,
SIGALRM, SIGUSR1, SIGUSR2
@@ -124,7 +123,6 @@ static void read_settings(void)
settings_set_bool("override_coredump_limit", FALSE);
}
#endif
-#endif
}
static void sig_gui_dialog(const char *type, const char *text)
@@ -156,11 +154,17 @@ static void sig_init_finished(void)
static char *fix_path(const char *str)
{
char *new_str = convert_home(str);
+
if (!g_path_is_absolute(new_str)) {
char *tmp_str = new_str;
- new_str = g_strdup_printf("%s/%s", g_get_current_dir(), tmp_str);
+ char *current_dir = g_get_current_dir();
+
+ new_str = g_build_path(G_DIR_SEPARATOR_S, current_dir, tmp_str, NULL);
+
+ g_free(current_dir);
g_free(tmp_str);
}
+
return new_str;
}
@@ -219,9 +223,7 @@ void core_init(void)
client_start_time = time(NULL);
modules_init();
-#ifndef WIN32
pidwait_init();
-#endif
net_disconnect_init();
signals_init();
@@ -297,9 +299,7 @@ void core_deinit(void)
signals_deinit();
net_disconnect_deinit();
-#ifndef WIN32
pidwait_deinit();
-#endif
modules_deinit();
g_free(irssi_dir);
diff --git a/src/core/expandos.c b/src/core/expandos.c
index bed6c5eb..67aea837 100644
--- a/src/core/expandos.c
+++ b/src/core/expandos.c
@@ -57,7 +57,7 @@ static char *last_sent_msg, *last_sent_msg_body;
static char *last_privmsg_from, *last_public_from;
static char *sysname, *sysrelease, *sysarch;
-static const char *timestamp_format;
+static char *timestamp_format;
static int timestamp_seconds;
static time_t last_timestamp;
@@ -414,6 +414,13 @@ static char *expando_releasetime(SERVER_REC *server, void *item, int *free_ret)
return g_strdup_printf("%04d", IRSSI_VERSION_TIME);
}
+/* client abi */
+static char *expando_abiversion(SERVER_REC *server, void *item, int *free_ret)
+{
+ *free_ret = TRUE;
+ return g_strdup_printf("%d", IRSSI_ABI_VERSION);
+}
+
/* current working directory */
static char *expando_workdir(SERVER_REC *server, void *item, int *free_ret)
{
@@ -567,7 +574,9 @@ static int sig_timer(void)
static void read_settings(void)
{
- timestamp_format = settings_get_str("timestamp_format");
+ g_free_not_null(timestamp_format);
+ timestamp_format = g_strdup(settings_get_str("timestamp_format"));
+
timestamp_seconds =
strstr(timestamp_format, "%r") != NULL ||
strstr(timestamp_format, "%s") != NULL ||
@@ -656,6 +665,8 @@ void expandos_init(void)
"", EXPANDO_NEVER, NULL);
expando_create("versiontime", expando_releasetime,
"", EXPANDO_NEVER, NULL);
+ expando_create("abiversion", expando_abiversion,
+ "", EXPANDO_NEVER, NULL);
expando_create("W", expando_workdir, NULL);
expando_create("Y", expando_realname,
"window changed", EXPANDO_ARG_NONE,
@@ -708,14 +719,18 @@ void expandos_deinit(void)
g_free_not_null(char_expandos[n]);
g_hash_table_foreach_remove(expandos, free_expando, NULL);
- g_hash_table_destroy(expandos);
+ g_hash_table_destroy(expandos);
- g_free_not_null(last_sent_msg); g_free_not_null(last_sent_msg_body);
- g_free_not_null(last_privmsg_from); g_free_not_null(last_public_from);
- g_free_not_null(sysname); g_free_not_null(sysrelease);
- g_free_not_null(sysarch);
+ g_free_not_null(last_sent_msg);
+ g_free_not_null(last_sent_msg_body);
+ g_free_not_null(last_privmsg_from);
+ g_free_not_null(last_public_from);
+ g_free_not_null(sysname);
+ g_free_not_null(sysrelease);
+ g_free_not_null(sysarch);
+ g_free_not_null(timestamp_format);
- g_source_remove(timer_tag);
+ g_source_remove(timer_tag);
signal_remove("message public", (SIGNAL_FUNC) sig_message_public);
signal_remove("message private", (SIGNAL_FUNC) sig_message_private);
signal_remove("message own_private", (SIGNAL_FUNC) sig_message_own_private);
diff --git a/src/core/ignore.c b/src/core/ignore.c
index 3c45967c..d4a92e3c 100644
--- a/src/core/ignore.c
+++ b/src/core/ignore.c
@@ -67,11 +67,12 @@ static int ignore_match_pattern(IGNORE_REC *rec, const char *text)
return FALSE;
if (rec->regexp) {
-#ifdef HAVE_REGEX_H
+#ifdef USE_GREGEX
+ return rec->preg != NULL &&
+ g_regex_match(rec->preg, text, 0, NULL);
+#else
return rec->regexp_compiled &&
regexec(&rec->preg, text, 0, NULL, 0) == 0;
-#else
- return FALSE;
#endif
}
@@ -97,8 +98,8 @@ static int ignore_match_pattern(IGNORE_REC *rec, const char *text)
match_wildcards((rec)->mask, nick)))
#define ignore_match_server(rec, server) \
- ((rec)->servertag == NULL || \
- g_ascii_strcasecmp((server)->tag, (rec)->servertag) == 0)
+ ((rec)->servertag == NULL || ((server) != NULL && \
+ g_ascii_strcasecmp((server)->tag, (rec)->servertag) == 0))
#define ignore_match_channel(rec, channel) \
((rec)->channels == NULL || ((channel) != NULL && \
@@ -135,7 +136,6 @@ int ignore_check(SERVER_REC *server, const char *nick, const char *host,
char *nickmask;
int len, best_mask, best_match, best_patt;
- g_return_val_if_fail(server != NULL, 0);
if (nick == NULL) nick = "";
chanrec = server == NULL || channel == NULL ? NULL :
@@ -187,24 +187,17 @@ int ignore_check(SERVER_REC *server, const char *nick, const char *host,
return ignore_check_replies(chanrec, text, level);
}
-IGNORE_REC *ignore_find(const char *servertag, const char *mask,
- char **channels)
-{
- return ignore_find_noact(servertag, mask, channels, 0);
-}
-
-
-IGNORE_REC *ignore_find_noact(const char *servertag, const char *mask,
- char **channels, int noact)
+IGNORE_REC *ignore_find_full(const char *servertag, const char *mask, const char *pattern,
+ char **channels, const int flags)
{
GSList *tmp;
char **chan;
int ignore_servertag;
- if (mask != NULL && (*mask == '\0' || strcmp(mask, "*") == 0))
+ if (mask != NULL && (*mask == '\0' || g_strcmp0(mask, "*") == 0))
mask = NULL;
- ignore_servertag = servertag != NULL && strcmp(servertag, "*") == 0;
+ ignore_servertag = servertag != NULL && g_strcmp0(servertag, "*") == 0;
for (tmp = ignores; tmp != NULL; tmp = tmp->next) {
IGNORE_REC *rec = tmp->data;
@@ -217,28 +210,39 @@ IGNORE_REC *ignore_find_noact(const char *servertag, const char *mask,
continue;
}
- if (noact && (rec->level & MSGLEVEL_NO_ACT) == 0)
+ if ((flags & IGNORE_FIND_NOACT) && (rec->level & MSGLEVEL_NO_ACT) == 0)
continue;
- if (!noact && (rec->level & MSGLEVEL_NO_ACT) != 0)
+ if (!(flags & IGNORE_FIND_NOACT) && (rec->level & MSGLEVEL_NO_ACT) != 0)
continue;
if ((rec->mask == NULL && mask != NULL) ||
- (rec->mask != NULL && mask == NULL)) continue;
+ (rec->mask != NULL && mask == NULL))
+ continue;
if (rec->mask != NULL && g_ascii_strcasecmp(rec->mask, mask) != 0)
continue;
+ /* match the pattern too if requested */
+ if (flags & IGNORE_FIND_PATTERN) {
+ if ((rec->pattern == NULL && pattern != NULL) ||
+ (rec->pattern != NULL && pattern == NULL))
+ continue;
+
+ if (rec->pattern != NULL && g_ascii_strcasecmp(rec->pattern, pattern) != 0)
+ continue;
+ }
+
if ((channels == NULL && rec->channels == NULL))
return rec; /* no channels - ok */
- if (channels != NULL && strcmp(*channels, "*") == 0)
+ if (channels != NULL && g_strcmp0(*channels, "*") == 0)
return rec; /* ignore channels */
if (channels == NULL || rec->channels == NULL)
continue; /* other doesn't have channels */
- if (strarray_length(channels) != strarray_length(rec->channels))
+ if (g_strv_length(channels) != g_strv_length(rec->channels))
continue; /* different amount of channels */
/* check that channels match */
@@ -254,6 +258,16 @@ IGNORE_REC *ignore_find_noact(const char *servertag, const char *mask,
return NULL;
}
+IGNORE_REC *ignore_find(const char *servertag, const char *mask, char **channels)
+{
+ return ignore_find_full(servertag, mask, NULL, channels, 0);
+}
+
+IGNORE_REC *ignore_find_noact(const char *servertag, const char *mask, char **channels, int noact)
+{
+ return ignore_find_full(servertag, mask, NULL, channels, IGNORE_FIND_NOACT);
+}
+
static void ignore_set_config(IGNORE_REC *rec)
{
CONFIG_NODE *node;
@@ -263,7 +277,7 @@ static void ignore_set_config(IGNORE_REC *rec)
return;
node = iconfig_node_traverse("(ignores", TRUE);
- node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
+ node = iconfig_node_section(node, NULL, NODE_TYPE_BLOCK);
if (rec->mask != NULL) iconfig_node_set_str(node, "mask", rec->mask);
if (rec->level) {
@@ -281,7 +295,7 @@ static void ignore_set_config(IGNORE_REC *rec)
iconfig_node_set_str(node, "servertag", rec->servertag);
if (rec->channels != NULL && *rec->channels != NULL) {
- node = config_node_section(node, "channels", NODE_TYPE_LIST);
+ node = iconfig_node_section(node, "channels", NODE_TYPE_LIST);
iconfig_node_add_list(node, rec->channels);
}
}
@@ -313,12 +327,27 @@ static void ignore_remove_config(IGNORE_REC *rec)
static void ignore_init_rec(IGNORE_REC *rec)
{
-#ifdef HAVE_REGEX_H
+#ifdef USE_GREGEX
+ if (rec->preg != NULL)
+ g_regex_unref(rec->preg);
+
+ if (rec->regexp && rec->pattern != NULL) {
+ GError *re_error = NULL;
+
+ rec->preg = g_regex_new(rec->pattern, G_REGEX_OPTIMIZE | G_REGEX_RAW | G_REGEX_CASELESS, 0, &re_error);
+
+ if (rec->preg == NULL) {
+ g_warning("Failed to compile regexp '%s': %s", rec->pattern, re_error->message);
+ g_error_free(re_error);
+ }
+ }
+#else
char *errbuf;
int errcode, errbuf_len;
if (rec->regexp_compiled) regfree(&rec->preg);
rec->regexp_compiled = FALSE;
+
if (rec->regexp && rec->pattern != NULL) {
errcode = regcomp(&rec->preg, rec->pattern,
REG_EXTENDED|REG_ICASE|REG_NOSUB);
@@ -352,7 +381,9 @@ static void ignore_destroy(IGNORE_REC *rec, int send_signal)
if (send_signal)
signal_emit("ignore destroyed", 1, rec);
-#ifdef HAVE_REGEX_H
+#ifdef USE_GREGEX
+ if (rec->preg != NULL) g_regex_unref(rec->preg);
+#else
if (rec->regexp_compiled) regfree(&rec->preg);
#endif
if (rec->channels != NULL) g_strfreev(rec->channels);
@@ -360,8 +391,6 @@ static void ignore_destroy(IGNORE_REC *rec, int send_signal)
g_free_not_null(rec->servertag);
g_free_not_null(rec->pattern);
g_free(rec);
-
- nickmatch_rebuild(nickmatch);
}
void ignore_update_rec(IGNORE_REC *rec)
@@ -380,8 +409,8 @@ void ignore_update_rec(IGNORE_REC *rec)
ignore_init_rec(rec);
signal_emit("ignore changed", 1, rec);
- nickmatch_rebuild(nickmatch);
}
+ nickmatch_rebuild(nickmatch);
}
static int unignore_timeout(void)
@@ -438,7 +467,7 @@ static void read_ignores(void)
rec->unignore_time = config_node_get_int(node, "unignore_time", 0);
rec->servertag = g_strdup(config_node_get_str(node, "servertag", 0));
- node = config_node_section(node, "channels", -1);
+ node = iconfig_node_section(node, "channels", -1);
if (node != NULL) rec->channels = config_node_get_list(node);
ignore_init_rec(rec);
diff --git a/src/core/ignore.h b/src/core/ignore.h
index 46025d4c..80ae1d12 100644
--- a/src/core/ignore.h
+++ b/src/core/ignore.h
@@ -1,7 +1,7 @@
#ifndef __IGNORE_H
#define __IGNORE_H
-#ifdef HAVE_REGEX_H
+#ifndef USE_GREGEX
# include <regex.h>
#endif
@@ -20,7 +20,9 @@ struct _IGNORE_REC {
unsigned int regexp:1;
unsigned int fullword:1;
unsigned int replies:1; /* ignore replies to nick in channel */
-#ifdef HAVE_REGEX_H
+#ifdef USE_GREGEX
+ GRegex *preg;
+#else
unsigned int regexp_compiled:1; /* should always be TRUE, unless regexp is invalid */
regex_t preg;
#endif
@@ -31,6 +33,16 @@ extern GSList *ignores;
int ignore_check(SERVER_REC *server, const char *nick, const char *host,
const char *channel, const char *text, int level);
+enum {
+ IGNORE_FIND_PATTERN = 0x01, // Match the pattern
+ IGNORE_FIND_NOACT = 0x02, // Exclude the targets with NOACT level
+};
+
+IGNORE_REC *ignore_find_full (const char *servertag, const char *mask, const char *pattern,
+ char **channels, const int flags);
+
+// Convenience wrappers around ignore_find_full, for compatibility purpose
+
IGNORE_REC *ignore_find(const char *servertag, const char *mask, char **channels);
IGNORE_REC *ignore_find_noact(const char *servertag, const char *mask, char **channels, int noact);
diff --git a/src/core/levels.c b/src/core/levels.c
index 7997ba98..e623c4de 100644
--- a/src/core/levels.c
+++ b/src/core/levels.c
@@ -54,7 +54,7 @@ int level_get(const char *level)
{
int n, len, match;
- if (g_ascii_strcasecmp(level, "ALL") == 0 || strcmp(level, "*") == 0)
+ if (g_ascii_strcasecmp(level, "ALL") == 0 || g_strcmp0(level, "*") == 0)
return MSGLEVEL_ALL;
if (g_ascii_strcasecmp(level, "NEVER") == 0)
@@ -177,7 +177,7 @@ int combine_level(int dest, const char *src)
itemname = *item + (**item == '+' || **item == '-' ? 1 : 0);
itemlevel = level_get(itemname);
- if (strcmp(itemname, "NONE") == 0)
+ if (g_strcmp0(itemname, "NONE") == 0)
dest = 0;
else if (**item == '-')
dest &= ~(itemlevel);
diff --git a/src/core/log-away.c b/src/core/log-away.c
index 681edcbf..e2a0120b 100644
--- a/src/core/log-away.c
+++ b/src/core/log-away.c
@@ -24,6 +24,7 @@
#include "log.h"
#include "servers.h"
#include "settings.h"
+#include "write-buffer.h"
static LOG_REC *awaylog;
static int away_filepos;
@@ -62,6 +63,9 @@ static void awaylog_open(void)
return;
}
+ /* Flush the dirty buffers to disk before acquiring the file position */
+ write_buffer_flush();
+
awaylog = log;
away_filepos = lseek(log->handle, 0, SEEK_CUR);
away_msgs = 0;
@@ -83,6 +87,9 @@ static void awaylog_close(void)
if (awaylog == log) awaylog = NULL;
+ /* Flush the dirty buffers to disk before showing the away log */
+ write_buffer_flush();
+
signal_emit("awaylog show", 3, log, GINT_TO_POINTER(away_msgs),
GINT_TO_POINTER(away_filepos));
log_close(log);
diff --git a/src/core/log.c b/src/core/log.c
index 263b3526..6af1effc 100644
--- a/src/core/log.c
+++ b/src/core/log.c
@@ -41,7 +41,7 @@ static const char *log_item_types[] = {
NULL
};
-const char *log_timestamp;
+static char *log_timestamp;
static int log_file_create_mode;
static int log_dir_create_mode;
static int rotate_tag;
@@ -110,11 +110,11 @@ int log_start_logging(LOG_REC *log)
log->real_fname = log_filename(log);
if (log->real_fname != NULL &&
- strcmp(log->real_fname, log->fname) != 0) {
+ g_strcmp0(log->real_fname, log->fname) != 0) {
/* path may contain variables (%time, $vars),
make sure the directory is created */
dir = g_path_get_dirname(log->real_fname);
- mkpath(dir, log_dir_create_mode);
+ g_mkdir_with_parents(dir, log_dir_create_mode);
g_free(dir);
}
@@ -181,7 +181,7 @@ static void log_rotate_check(LOG_REC *log)
return;
new_fname = log_filename(log);
- if (strcmp(new_fname, log->real_fname) != 0) {
+ if (g_strcmp0(new_fname, log->real_fname) != 0) {
/* rotate log */
log_stop_logging(log);
signal_emit("log rotated", 1, log);
@@ -245,7 +245,7 @@ static int itemcmp(const char *patt, const char *item)
{
/* returns 0 on match, nonzero otherwise */
- if (!strcmp(patt, "*"))
+ if (!g_strcmp0(patt, "*"))
return 0;
return item ? g_ascii_strcasecmp(patt, item) : 1;
}
@@ -320,7 +320,7 @@ LOG_REC *log_find(const char *fname)
for (tmp = logs; tmp != NULL; tmp = tmp->next) {
LOG_REC *rec = tmp->data;
- if (strcmp(rec->fname, fname) == 0)
+ if (g_strcmp0(rec->fname, fname) == 0)
return rec;
}
@@ -332,11 +332,11 @@ static void log_items_update_config(LOG_REC *log, CONFIG_NODE *parent)
GSList *tmp;
CONFIG_NODE *node;
- parent = config_node_section(parent, "items", NODE_TYPE_LIST);
+ parent = iconfig_node_section(parent, "items", NODE_TYPE_LIST);
for (tmp = log->items; tmp != NULL; tmp = tmp->next) {
LOG_ITEM_REC *rec = tmp->data;
- node = config_node_section(parent, NULL, NODE_TYPE_BLOCK);
+ node = iconfig_node_section(parent, NULL, NODE_TYPE_BLOCK);
iconfig_node_set_str(node, "type", log_item_types[rec->type]);
iconfig_node_set_str(node, "name", rec->name);
iconfig_node_set_str(node, "server", rec->servertag);
@@ -352,7 +352,7 @@ static void log_update_config(LOG_REC *log)
return;
node = iconfig_node_traverse("logs", TRUE);
- node = config_node_section(node, log->fname, NODE_TYPE_BLOCK);
+ node = iconfig_node_section(node, log->fname, NODE_TYPE_BLOCK);
if (log->autoopen)
iconfig_node_set_bool(node, "auto_open", TRUE);
@@ -544,7 +544,7 @@ static void log_read_config(void)
signal_emit("log config read", 2, log, node);
- node = config_node_section(node, "items", -1);
+ node = iconfig_node_section(node, "items", -1);
if (node != NULL)
log_items_read_config(node, log);
@@ -558,13 +558,15 @@ static void log_read_config(void)
static void read_settings(void)
{
- log_timestamp = settings_get_str("log_timestamp");
+ g_free_not_null(log_timestamp);
+ log_timestamp = g_strdup(settings_get_str("log_timestamp"));
+
log_file_create_mode = octal2dec(settings_get_int("log_create_mode"));
- log_dir_create_mode = log_file_create_mode;
- if (log_file_create_mode & 0400) log_dir_create_mode |= 0100;
- if (log_file_create_mode & 0040) log_dir_create_mode |= 0010;
- if (log_file_create_mode & 0004) log_dir_create_mode |= 0001;
+ log_dir_create_mode = log_file_create_mode;
+ if (log_file_create_mode & 0400) log_dir_create_mode |= 0100;
+ if (log_file_create_mode & 0040) log_dir_create_mode |= 0010;
+ if (log_file_create_mode & 0004) log_dir_create_mode |= 0001;
}
void log_init(void)
@@ -595,7 +597,9 @@ void log_deinit(void)
while (logs != NULL)
log_close(logs->data);
+ g_free_not_null(log_timestamp);
+
signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
- signal_remove("setup reread", (SIGNAL_FUNC) log_read_config);
- signal_remove("irssi init finished", (SIGNAL_FUNC) log_read_config);
+ signal_remove("setup reread", (SIGNAL_FUNC) log_read_config);
+ signal_remove("irssi init finished", (SIGNAL_FUNC) log_read_config);
}
diff --git a/src/core/misc.c b/src/core/misc.c
index 5e6087cb..1cfa15b6 100644
--- a/src/core/misc.c
+++ b/src/core/misc.c
@@ -20,8 +20,9 @@
#include "module.h"
#include "misc.h"
+#include "commands.h"
-#ifdef HAVE_REGEX_H
+#ifndef USE_GREGEX
# include <regex.h>
#endif
@@ -150,27 +151,13 @@ int find_substr(const char *list, const char *item)
return FALSE;
}
-int strarray_length(char **array)
-{
- int len;
-
- g_return_val_if_fail(array != NULL, 0);
-
- len = 0;
- while (*array) {
- len++;
- array++;
- }
- return len;
-}
-
int strarray_find(char **array, const char *item)
{
char **tmp;
int index;
- g_return_val_if_fail(array != NULL, 0);
- g_return_val_if_fail(item != NULL, 0);
+ g_return_val_if_fail(array != NULL, -1);
+ g_return_val_if_fail(item != NULL, -1);
index = 0;
for (tmp = array; *tmp != NULL; tmp++, index++) {
@@ -184,7 +171,7 @@ int strarray_find(char **array, const char *item)
GSList *gslist_find_string(GSList *list, const char *key)
{
for (; list != NULL; list = list->next)
- if (strcmp(list->data, key) == 0) return list;
+ if (g_strcmp0(list->data, key) == 0) return list;
return NULL;
}
@@ -211,6 +198,30 @@ void *gslist_foreach_find(GSList *list, FOREACH_FIND_FUNC func, const void *data
return NULL;
}
+void gslist_free_full (GSList *list, GDestroyNotify free_func)
+{
+ GSList *tmp;
+
+ if (list == NULL)
+ return;
+
+ for (tmp = list; tmp != NULL; tmp = tmp->next)
+ free_func(tmp->data);
+
+ g_slist_free(list);
+}
+
+GSList *gslist_remove_string (GSList *list, const char *str)
+{
+ GSList *l;
+
+ l = g_slist_find_custom(list, str, (GCompareFunc) g_strcmp0);
+ if (l != NULL)
+ return g_slist_remove_link(list, l);
+
+ return list;
+}
+
/* `list' contains pointer to structure with a char* to string. */
char *gslistptr_to_string(GSList *list, int offset, const char *delimiter)
{
@@ -255,21 +266,30 @@ void hash_save_key(char *key, void *value, GSList **list)
*list = g_slist_append(*list, key);
}
-/* save all keys in hash table to linked list - you shouldn't remove any
- items while using this list, use g_slist_free() after you're done with it */
-GSList *hashtable_get_keys(GHashTable *hash)
+/* remove all the options from the optlist hash table that are valid for the
+ * command cmd */
+GList *optlist_remove_known(const char *cmd, GHashTable *optlist)
{
- GSList *list;
+ GList *list, *tmp, *next;
+
+ list = g_hash_table_get_keys(optlist);
+ if (cmd != NULL && list != NULL) {
+ for (tmp = list; tmp != NULL; tmp = next) {
+ char *option = tmp->data;
+ next = tmp->next;
+
+ if (command_have_option(cmd, option))
+ list = g_list_remove(list, option);
+ }
+ }
- list = NULL;
- g_hash_table_foreach(hash, (GHFunc) hash_save_key, &list);
return list;
}
GList *glist_find_string(GList *list, const char *key)
{
for (; list != NULL; list = list->next)
- if (strcmp(list->data, key) == 0) return list;
+ if (g_strcmp0(list->data, key) == 0) return list;
return NULL;
}
@@ -365,66 +385,6 @@ char *stristr_full(const char *data, const char *key)
return strstr_full_case(data, key, TRUE);
}
-int regexp_match(const char *str, const char *regexp)
-{
-#ifdef HAVE_REGEX_H
- regex_t preg;
- int ret;
-
- if (regcomp(&preg, regexp, REG_EXTENDED|REG_ICASE|REG_NOSUB) != 0)
- return 0;
-
- ret = regexec(&preg, str, 0, NULL, 0);
- regfree(&preg);
-
- return ret == 0;
-#else
- return FALSE;
-#endif
-}
-
-/* Create the directory and all it's parent directories */
-int mkpath(const char *path, int mode)
-{
- struct stat statbuf;
- const char *p;
- char *dir;
-
- g_return_val_if_fail(path != NULL, -1);
-
- p = g_path_skip_root((char *) path);
- if (p == NULL) {
- /* not a full path, maybe not what we wanted
- but continue anyway.. */
- p = path;
- }
- for (;;) {
- if (*p != G_DIR_SEPARATOR && *p != '\0') {
- p++;
- continue;
- }
-
- dir = g_strndup(path, (int) (p-path));
- if (stat(dir, &statbuf) != 0) {
-#ifndef WIN32
- if (mkdir(dir, mode) == -1)
-#else
- if (_mkdir(dir) == -1)
-#endif
- {
- g_free(dir);
- return -1;
- }
- }
- g_free(dir);
-
- if (*p++ == '\0')
- break;
- }
-
- return 0;
-}
-
/* convert ~/ to $HOME */
char *convert_home(const char *path)
{
@@ -451,22 +411,15 @@ int g_istr_cmp(gconstpointer v, gconstpointer v2)
return g_ascii_strcasecmp((const char *) v, (const char *) v2);
}
-/* a char* hash function from ASU */
-unsigned int g_istr_hash(gconstpointer v)
+guint g_istr_hash(gconstpointer v)
{
- const char *s = (const char *) v;
- unsigned int h = 0, g;
+ const signed char *p;
+ guint32 h = 5381;
- while (*s != '\0') {
- h = (h << 4) + i_toupper(*s);
- if ((g = h & 0xf0000000UL)) {
- h = h ^ (g >> 24);
- h = h ^ g;
- }
- s++;
- }
+ for (p = v; *p != '\0'; p++)
+ h = (h << 5) + h + g_ascii_toupper(*p);
- return h /* % M */;
+ return h;
}
/* Find `mask' from `data', you can use * and ? wildcards. */
@@ -572,15 +525,11 @@ int dec2octal(int decimal)
/* string -> uoff_t */
uoff_t str_to_uofft(const char *str)
{
- uoff_t ret;
-
- ret = 0;
- while (*str != '\0') {
- ret = ret*10 + (*str - '0');
- str++;
- }
-
- return ret;
+#ifdef UOFF_T_LONG_LONG
+ return (uoff_t)strtoull(str, NULL, 10);
+#else
+ return (uoff_t)strtoul(str, NULL, 10);
+#endif
}
/* convert all low-ascii (<32) to ^<A..> combinations */
@@ -791,20 +740,6 @@ char *escape_string(const char *str)
return ret;
}
-int strocpy(char *dest, const char *src, size_t dstsize)
-{
- if (dstsize == 0)
- return -1;
-
- while (*src != '\0' && dstsize > 1) {
- *dest++ = *src++;
- dstsize--;
- }
-
- *dest++ = '\0';
- return *src == '\0' ? 0 : -1;
-}
-
int nearest_power(int num)
{
int n = 1;
@@ -967,20 +902,51 @@ char *ascii_strdown(char *str)
return str;
}
-char **strsplit_len(const char *str, int len)
+char **strsplit_len(const char *str, int len, gboolean onspace)
+{
+ char **ret = g_new(char *, 1);
+ int n;
+ int offset;
+
+ for (n = 0; *str != '\0'; n++, str += offset) {
+ offset = MIN(len, strlen(str));
+ if (onspace && strlen(str) > len) {
+ /*
+ * Try to find a space to split on and leave
+ * the space on the previous line.
+ */
+ int i;
+ for (i = len - 1; i > 0; i--) {
+ if (str[i] == ' ') {
+ offset = i;
+ break;
+ }
+ }
+ }
+ ret[n] = g_strndup(str, offset);
+ ret = g_renew(char *, ret, n + 2);
+ }
+ ret[n] = NULL;
+
+ return ret;
+}
+
+char *binary_to_hex(unsigned char *buffer, size_t size)
{
- char **ret;
- size_t total_len = strlen(str);
- int n = total_len / len;
+ static const char hex[] = "0123456789ABCDEF";
+ char *result = NULL;
int i;
- if (total_len % len)
- n++;
+ if (buffer == NULL || size == 0)
+ return NULL;
+
+ result = g_malloc(3 * size);
- ret = g_new(char *, n + 1);
- for (i = 0; i < n; i++, str += len)
- ret[i] = g_strndup(str, len);
- ret[n] = NULL;
+ for (i = 0; i < size; i++) {
+ result[i * 3 + 0] = hex[(buffer[i] >> 4) & 0xf];
+ result[i * 3 + 1] = hex[(buffer[i] >> 0) & 0xf];
+ result[i * 3 + 2] = i == size - 1 ? '\0' : ':';
+ }
- return ret;
+ return result;
}
diff --git a/src/core/misc.h b/src/core/misc.h
index 8fb5078f..00637da0 100644
--- a/src/core/misc.h
+++ b/src/core/misc.h
@@ -21,6 +21,9 @@ GSList *gslist_find_string(GSList *list, const char *key);
GSList *gslist_find_icase_string(GSList *list, const char *key);
GList *glist_find_string(GList *list, const char *key);
GList *glist_find_icase_string(GList *list, const char *key);
+GSList *gslist_remove_string (GSList *list, const char *str);
+
+void gslist_free_full (GSList *list, GDestroyNotify free_func);
void *gslist_foreach_find(GSList *list, FOREACH_FIND_FUNC func, const void *data);
@@ -29,15 +32,8 @@ char *gslistptr_to_string(GSList *list, int offset, const char *delimiter);
/* `list' contains char* */
char *gslist_to_string(GSList *list, const char *delimiter);
-/* save all keys in hash table to linked list - you shouldn't remove any
- items while using this list, use g_slist_free() after you're done with it */
-GSList *hashtable_get_keys(GHashTable *hash);
-
-/* easy way to check if regexp matches */
-int regexp_match(const char *str, const char *regexp);
+GList *optlist_remove_known(const char *cmd, GHashTable *optlist);
-/* Create the directory and all it's parent directories */
-int mkpath(const char *path, int mode);
/* convert ~/ to $HOME */
char *convert_home(const char *path);
@@ -82,9 +78,6 @@ int parse_size(const char *size, int *bytes);
Stop when `end_char' is found from string. */
int is_numeric(const char *str, char end_char);
-/* Like strlcpy(), but return -1 if buffer was overflown, 0 if not. */
-int strocpy(char *dest, const char *src, size_t dstsize);
-
/* strstr() with case-ignoring */
char *stristr(const char *data, const char *key);
@@ -104,8 +97,6 @@ char *show_lowascii(const char *str);
/* replace all `from' chars in string to `to' chars. returns `str' */
char *replace_chars(char *str, char from, char to);
-/* return how many items `array' has */
-int strarray_length(char **array);
/* return index of `item' in `array' or -1 if not found */
int strarray_find(char **array, const char *item);
@@ -116,6 +107,10 @@ uoff_t str_to_uofft(const char *str);
int find_substr(const char *list, const char *item);
/* split `str' into `len' sized substrings */
-char **strsplit_len(const char *str, int len);
+char **strsplit_len(const char *str, int len, gboolean onspace);
+
+/* Convert a given buffer to a printable, colon-delimited, hex string and
+ * return a pointer to the newly allocated buffer */
+char *binary_to_hex(unsigned char *buffer, size_t size);
#endif
diff --git a/src/core/modules-load.c b/src/core/modules-load.c
index 49f811de..9baac1d7 100644
--- a/src/core/modules-load.c
+++ b/src/core/modules-load.c
@@ -78,7 +78,7 @@ static char *module_get_root(const char *name, char **prefixes)
/* skip the _core part */
len = strlen(name);
- if (len > 5 && strcmp(name+len-5, "_core") == 0)
+ if (len > 5 && g_strcmp0(name+len-5, "_core") == 0)
return g_strndup(name, len-5);
return g_strdup(name);
@@ -94,11 +94,11 @@ static char *module_get_sub(const char *name, const char *root)
g_return_val_if_fail(namelen >= rootlen, g_strdup(name));
if (strncmp(name, root, rootlen) == 0 &&
- strcmp(name+rootlen, "_core") == 0)
+ g_strcmp0(name+rootlen, "_core") == 0)
return g_strdup("core");
if (namelen > rootlen && name[namelen-rootlen-1] == '_' &&
- strcmp(name+namelen-rootlen, root) == 0)
+ g_strcmp0(name+namelen-rootlen, root) == 0)
return g_strndup(name, namelen-rootlen-1);
return g_strdup(name);
@@ -140,10 +140,10 @@ static GModule *module_open(const char *name, int *found)
static char *module_get_func(const char *rootmodule, const char *submodule,
const char *function)
{
- if (strcmp(submodule, "core") == 0)
+ if (g_strcmp0(submodule, "core") == 0)
return g_strconcat(rootmodule, "_core_", function, NULL);
- if (strcmp(rootmodule, submodule) == 0)
+ if (g_strcmp0(rootmodule, submodule) == 0)
return g_strconcat(rootmodule, "_", function, NULL);
return g_strconcat(submodule, "_", rootmodule, "_", function, NULL);
@@ -160,11 +160,14 @@ static int module_load_name(const char *path, const char *rootmodule,
{
void (*module_init) (void);
void (*module_deinit) (void);
+ void (*module_version) (int *);
GModule *gmodule;
MODULE_REC *module;
MODULE_FILE_REC *rec;
+ gpointer value_version = NULL;
gpointer value1, value2 = NULL;
- char *initfunc, *deinitfunc;
+ char *versionfunc, *initfunc, *deinitfunc;
+ int module_abi_version = 0;
int found;
gmodule = module_open(path, &found);
@@ -176,6 +179,27 @@ static int module_load_name(const char *path, const char *rootmodule,
return found ? 0 : -1;
}
+ /* get the module's irssi abi version and bail out on mismatch */
+ versionfunc = module_get_func(rootmodule, submodule, "abicheck");
+ if (!g_module_symbol(gmodule, versionfunc, &value_version)) {
+ g_free(versionfunc);
+ module_error(MODULE_ERROR_VERSION_MISMATCH, "0",
+ rootmodule, submodule);
+ g_module_close(gmodule);
+ return 0;
+ }
+ g_free(versionfunc);
+ module_version = value_version;
+ module_version(&module_abi_version);
+ if (module_abi_version != IRSSI_ABI_VERSION) {
+ char *module_abi_versionstr = g_strdup_printf("%d", module_abi_version);
+ module_error(MODULE_ERROR_VERSION_MISMATCH, module_abi_versionstr,
+ rootmodule, submodule);
+ g_free(module_abi_versionstr);
+ g_module_close(gmodule);
+ return 0;
+ }
+
/* get the module's init() and deinit() functions */
initfunc = module_get_func(rootmodule, submodule, "init");
deinitfunc = module_get_func(rootmodule, submodule, "deinit");
@@ -200,7 +224,7 @@ static int module_load_name(const char *path, const char *rootmodule,
module = module_find(rootmodule);
rec = module == NULL ? NULL :
- strcmp(rootmodule, submodule) == 0 ?
+ g_strcmp0(rootmodule, submodule) == 0 ?
module_file_find(module, "core") :
module_file_find(module, submodule);
if (rec == NULL) {
@@ -277,7 +301,7 @@ static int module_load_full(const char *path, const char *rootmodule,
return FALSE;
module = module_find(rootmodule);
- if (module != NULL && (strcmp(submodule, rootmodule) == 0 ||
+ if (module != NULL && (g_strcmp0(submodule, rootmodule) == 0 ||
module_file_find(module, submodule) != NULL)) {
/* module is already loaded */
module_error(MODULE_ERROR_ALREADY_LOADED, NULL,
@@ -286,7 +310,7 @@ static int module_load_full(const char *path, const char *rootmodule,
}
/* check if the given module exists.. */
- try_prefixes = strcmp(rootmodule, submodule) == 0;
+ try_prefixes = g_strcmp0(rootmodule, submodule) == 0;
status = module_load_name(path, rootmodule, submodule, try_prefixes);
if (status == -1 && try_prefixes) {
/* nope, try loading the module_core,
@@ -340,7 +364,7 @@ int module_load_sub(const char *path, const char *submodule, char **prefixes)
g_free(name);
full_path = g_string_new(exppath);
- if (strcmp(submodule, "core") == 0)
+ if (g_strcmp0(submodule, "core") == 0)
g_string_insert(full_path, end, "_core");
else {
g_string_insert_c(full_path, start, '_');
diff --git a/src/core/modules.c b/src/core/modules.c
index b002819b..a2542c84 100644
--- a/src/core/modules.c
+++ b/src/core/modules.c
@@ -44,7 +44,7 @@ void *module_check_cast_module(void *object, int type_pos,
str = module_find_id_str(module,
G_STRUCT_MEMBER(int, object, type_pos));
- return str == NULL || strcmp(str, id) != 0 ? NULL : object;
+ return str == NULL || g_strcmp0(str, id) != 0 ? NULL : object;
}
/* return unique number across all modules for `id' */
@@ -251,7 +251,7 @@ MODULE_FILE_REC *module_file_find(MODULE_REC *module, const char *name)
for (tmp = module->files; tmp != NULL; tmp = tmp->next) {
MODULE_FILE_REC *rec = tmp->data;
- if (strcmp(rec->name, name) == 0)
+ if (g_strcmp0(rec->name, name) == 0)
return rec;
}
diff --git a/src/core/modules.h b/src/core/modules.h
index 75a77c77..b2fa2fa4 100644
--- a/src/core/modules.h
+++ b/src/core/modules.h
@@ -27,6 +27,7 @@
enum {
MODULE_ERROR_ALREADY_LOADED,
MODULE_ERROR_LOAD,
+ MODULE_ERROR_VERSION_MISMATCH,
MODULE_ERROR_INVALID
};
diff --git a/src/core/net-disconnect.c b/src/core/net-disconnect.c
index 321f79ac..6476e776 100644
--- a/src/core/net-disconnect.c
+++ b/src/core/net-disconnect.c
@@ -116,7 +116,6 @@ void net_disconnect_init(void)
void net_disconnect_deinit(void)
{
-#ifndef WIN32
NET_DISCONNECT_REC *rec;
time_t now, max;
int first, fd;
@@ -155,5 +154,4 @@ void net_disconnect_deinit(void)
first = 0;
}
}
-#endif
}
diff --git a/src/core/net-nonblock.c b/src/core/net-nonblock.c
index e637e673..d6e767b6 100644
--- a/src/core/net-nonblock.c
+++ b/src/core/net-nonblock.c
@@ -77,14 +77,11 @@ int net_gethostbyname_nonblock(const char *addr, GIOChannel *pipe,
{
RESOLVED_IP_REC rec;
const char *errorstr;
-#ifndef WIN32
int pid;
-#endif
int len;
g_return_val_if_fail(addr != NULL, FALSE);
-#ifndef WIN32
pid = fork();
if (pid > 0) {
/* parent */
@@ -97,7 +94,6 @@ int net_gethostbyname_nonblock(const char *addr, GIOChannel *pipe,
g_warning("net_connect_thread(): fork() failed! "
"Using blocking resolving");
}
-#endif
/* child */
srand(time(NULL));
@@ -138,10 +134,8 @@ int net_gethostbyname_nonblock(const char *addr, GIOChannel *pipe,
}
}
-#ifndef WIN32
if (pid == 0)
_exit(99);
-#endif
/* we used blocking lookup */
return 0;
@@ -157,9 +151,7 @@ int net_gethostbyname_return(GIOChannel *pipe, RESOLVED_IP_REC *rec)
rec->host4 = NULL;
rec->host6 = NULL;
-#ifndef WIN32
fcntl(g_io_channel_unix_get_fd(pipe), F_SETFL, O_NONBLOCK);
-#endif
/* get ip+error */
if (g_io_channel_read_block(pipe, rec, sizeof(*rec)) == -1) {
@@ -201,9 +193,7 @@ void net_disconnect_nonblock(int pid)
{
g_return_if_fail(pid > 0);
-#ifndef WIN32
kill(pid, SIGKILL);
-#endif
}
static void simple_init(SIMPLE_THREAD_REC *rec, GIOChannel *handle)
diff --git a/src/core/net-nonblock.h b/src/core/net-nonblock.h
index 32cfac70..af5968c8 100644
--- a/src/core/net-nonblock.h
+++ b/src/core/net-nonblock.h
@@ -29,7 +29,7 @@ int net_gethostbyname_nonblock(const char *addr, GIOChannel *pipe,
int reverse_lookup);
/* Get host's name, call func when finished */
int net_gethostbyaddr_nonblock(IPADDR *ip, NET_HOST_CALLBACK func, void *data);
-/* get the resolved IP address. returns -1 if some error occured with read() */
+/* get the resolved IP address. returns -1 if some error occurred with read() */
int net_gethostbyname_return(GIOChannel *pipe, RESOLVED_IP_REC *rec);
/* Connect to server, call func when finished */
diff --git a/src/core/net-sendbuffer.c b/src/core/net-sendbuffer.c
index 9d4b0e37..97fb551f 100644
--- a/src/core/net-sendbuffer.c
+++ b/src/core/net-sendbuffer.c
@@ -109,7 +109,7 @@ static int buffer_add(NET_SENDBUF_REC *rec, const void *data, int size)
/* Send data, if all of it couldn't be sent immediately, it will be resent
automatically after a while. Returns -1 if some unrecoverable error
- occured. */
+ occurred. */
int net_sendbuffer_send(NET_SENDBUF_REC *rec, const void *data, int size)
{
int ret;
@@ -160,13 +160,9 @@ void net_sendbuffer_flush(NET_SENDBUF_REC *rec)
/* set the socket blocking while doing this */
handle = g_io_channel_unix_get_fd(rec->handle);
-#ifndef WIN32
fcntl(handle, F_SETFL, 0);
-#endif
while (!buffer_send(rec)) ;
-#ifndef WIN32
fcntl(handle, F_SETFL, O_NONBLOCK);
-#endif
}
/* Returns the socket handle */
diff --git a/src/core/net-sendbuffer.h b/src/core/net-sendbuffer.h
index 785f59ae..bdeb7156 100644
--- a/src/core/net-sendbuffer.h
+++ b/src/core/net-sendbuffer.h
@@ -24,7 +24,7 @@ void net_sendbuffer_destroy(NET_SENDBUF_REC *rec, int close);
/* Send data, if all of it couldn't be sent immediately, it will be resent
automatically after a while. Returns -1 if some unrecoverable error
- occured. */
+ occurred. */
int net_sendbuffer_send(NET_SENDBUF_REC *rec, const void *data, int size);
int net_sendbuffer_receive_line(NET_SENDBUF_REC *rec, char **str, int read_socket);
diff --git a/src/core/network-openssl.c b/src/core/network-openssl.c
index 768fd540..1eb85341 100644
--- a/src/core/network-openssl.c
+++ b/src/core/network-openssl.c
@@ -22,8 +22,8 @@
#include "network.h"
#include "misc.h"
#include "servers.h"
-
-#ifdef HAVE_OPENSSL
+#include "signals.h"
+#include "tls.h"
#include <openssl/crypto.h>
#include <openssl/x509.h>
@@ -32,11 +32,6 @@
#include <openssl/ssl.h>
#include <openssl/err.h>
-#ifdef HAVE_DANE
-#include <validator/validator.h>
-#include <validator/val_dane.h>
-#endif
-
/* ssl i/o channel object */
typedef struct
{
@@ -203,78 +198,13 @@ static gboolean irssi_ssl_verify_hostname(X509 *cert, const char *hostname)
return matched;
}
-static gboolean irssi_ssl_verify(SSL *ssl, SSL_CTX *ctx, const char* hostname, int port, X509 *cert, SERVER_REC *server)
+static gboolean irssi_ssl_verify(SSL *ssl, SSL_CTX *ctx, const char* hostname, int port, X509 *cert, SERVER_REC *server, TLS_REC *tls_rec)
{
long result;
-#ifdef HAVE_DANE
- int dane_ret;
- struct val_daneparams daneparams;
- struct val_danestatus *danestatus = NULL;
-
- // Check if a TLSA record is available.
- daneparams.port = port;
- daneparams.proto = DANE_PARAM_PROTO_TCP;
-
- dane_ret = val_getdaneinfo(NULL, hostname, &daneparams, &danestatus);
-
- if (dane_ret == VAL_DANE_NOERROR) {
- signal_emit("tlsa available", 1, server);
- }
-
- if (danestatus != NULL) {
- int do_certificate_check = 1;
-
- if (val_dane_check(NULL, ssl, danestatus, &do_certificate_check) != VAL_DANE_NOERROR) {
- g_warning("DANE: TLSA record for hostname %s port %d could not be verified", hostname, port);
- signal_emit("tlsa verification failed", 1, server);
- val_free_dane(danestatus);
- return FALSE;
- }
-
- signal_emit("tlsa verification success", 1, server);
- val_free_dane(danestatus);
-
- if (do_certificate_check == 0) {
- return TRUE;
- }
- }
-#endif
result = SSL_get_verify_result(ssl);
if (result != X509_V_OK) {
- unsigned char md[EVP_MAX_MD_SIZE];
- unsigned int n;
- char *str;
-
- g_warning("Could not verify SSL servers certificate: %s",
- X509_verify_cert_error_string(result));
- if ((str = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0)) == NULL)
- g_warning(" Could not get subject-name from peer certificate");
- else {
- g_warning(" Subject : %s", str);
- free(str);
- }
- if ((str = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0)) == NULL)
- g_warning(" Could not get issuer-name from peer certificate");
- else {
- g_warning(" Issuer : %s", str);
- free(str);
- }
- if (! X509_digest(cert, EVP_md5(), md, &n))
- g_warning(" Could not get fingerprint from peer certificate");
- else {
- char hex[] = "0123456789ABCDEF";
- char fp[EVP_MAX_MD_SIZE*3];
- if (n < sizeof(fp)) {
- unsigned int i;
- for (i = 0; i < n; i++) {
- fp[i*3+0] = hex[(md[i] >> 4) & 0xF];
- fp[i*3+1] = hex[(md[i] >> 0) & 0xF];
- fp[i*3+2] = i == n - 1 ? '\0' : ':';
- }
- g_warning(" MD5 Fingerprint : %s", fp);
- }
- }
+ g_warning("Could not verify TLS servers certificate: %s", X509_verify_cert_error_string(result));
return FALSE;
} else if (! irssi_ssl_verify_hostname(cert, hostname)){
return FALSE;
@@ -289,6 +219,7 @@ static GIOStatus irssi_ssl_read(GIOChannel *handle, gchar *buf, gsize len, gsize
const char *errstr;
gchar *errmsg;
+ ERR_clear_error();
ret1 = SSL_read(chan->ssl, buf, len);
if(ret1 <= 0)
{
@@ -334,6 +265,7 @@ static GIOStatus irssi_ssl_write(GIOChannel *handle, const gchar *buf, gsize len
const char *errstr;
gchar *errmsg;
+ ERR_clear_error();
ret1 = SSL_write(chan->ssl, (const char *)buf, len);
if(ret1 <= 0)
{
@@ -455,12 +387,13 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, int port, SERVER_
SSL *ssl;
SSL_CTX *ctx = NULL;
- const char *mycert = server->connrec->ssl_cert;
- const char *mypkey = server->connrec->ssl_pkey;
- const char *mypass = server->connrec->ssl_pass;
- const char *cafile = server->connrec->ssl_cafile;
- const char *capath = server->connrec->ssl_capath;
- gboolean verify = server->connrec->ssl_verify;
+ const char *mycert = server->connrec->tls_cert;
+ const char *mypkey = server->connrec->tls_pkey;
+ const char *mypass = server->connrec->tls_pass;
+ const char *cafile = server->connrec->tls_cafile;
+ const char *capath = server->connrec->tls_capath;
+ const char *ciphers = server->connrec->tls_ciphers;
+ gboolean verify = server->connrec->tls_verify;
g_return_val_if_fail(handle != NULL, NULL);
@@ -470,20 +403,27 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, int port, SERVER_
if(!(fd = g_io_channel_unix_get_fd(handle)))
return NULL;
+ ERR_clear_error();
ctx = SSL_CTX_new(SSLv23_client_method());
if (ctx == NULL) {
g_error("Could not allocate memory for SSL context");
return NULL;
}
- SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
+ SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
SSL_CTX_set_default_passwd_cb(ctx, get_pem_password_callback);
SSL_CTX_set_default_passwd_cb_userdata(ctx, (void *)mypass);
+ if (ciphers != NULL && ciphers[0] != '\0') {
+ if (SSL_CTX_set_cipher_list(ctx, ciphers) != 1)
+ g_warning("No valid SSL cipher suite could be selected");
+ }
+
if (mycert && *mycert) {
char *scert = NULL, *spkey = NULL;
scert = convert_home(mycert);
if (mypkey && *mypkey)
spkey = convert_home(mypkey);
+ ERR_clear_error();
if (! SSL_CTX_use_certificate_file(ctx, scert, SSL_FILETYPE_PEM))
g_warning("Loading of client certificate '%s' failed: %s", mycert, ERR_reason_error_string(ERR_get_error()));
else if (! SSL_CTX_use_PrivateKey_file(ctx, spkey ? spkey : scert, SSL_FILETYPE_PEM))
@@ -502,7 +442,7 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, int port, SERVER_
if (capath && *capath)
scapath = convert_home(capath);
if (! SSL_CTX_load_verify_locations(ctx, scafile, scapath)) {
- g_warning("Could not load CA list for verifying SSL server certificate");
+ g_warning("Could not load CA list for verifying TLS server certificate");
g_free(scafile);
g_free(scapath);
SSL_CTX_free(ctx);
@@ -531,6 +471,10 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, int port, SERVER_
return NULL;
}
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+ SSL_set_tlsext_host_name(ssl, server->connrec->address);
+#endif
+
SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE |
SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
@@ -552,6 +496,198 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, int port, SERVER_
return gchan;
}
+static void set_cipher_info(TLS_REC *tls, SSL *ssl)
+{
+ g_return_if_fail(tls != NULL);
+ g_return_if_fail(ssl != NULL);
+
+ tls_rec_set_protocol_version(tls, SSL_get_version(ssl));
+
+ tls_rec_set_cipher(tls, SSL_CIPHER_get_name(SSL_get_current_cipher(ssl)));
+ tls_rec_set_cipher_size(tls, SSL_get_cipher_bits(ssl, NULL));
+}
+
+static void set_pubkey_info(TLS_REC *tls, X509 *cert, unsigned char *cert_fingerprint, size_t cert_fingerprint_size, unsigned char *public_key_fingerprint, size_t public_key_fingerprint_size)
+{
+ g_return_if_fail(tls != NULL);
+ g_return_if_fail(cert != NULL);
+
+ EVP_PKEY *pubkey = NULL;
+ char *cert_fingerprint_hex = NULL;
+ char *public_key_fingerprint_hex = NULL;
+
+ BIO *bio = NULL;
+ char buffer[128];
+ size_t length;
+
+ pubkey = X509_get_pubkey(cert);
+
+ cert_fingerprint_hex = binary_to_hex(cert_fingerprint, cert_fingerprint_size);
+ tls_rec_set_certificate_fingerprint(tls, cert_fingerprint_hex);
+ tls_rec_set_certificate_fingerprint_algorithm(tls, "SHA256");
+
+ // Show algorithm.
+ switch (EVP_PKEY_id(pubkey)) {
+ case EVP_PKEY_RSA:
+ tls_rec_set_public_key_algorithm(tls, "RSA");
+ break;
+
+ case EVP_PKEY_DSA:
+ tls_rec_set_public_key_algorithm(tls, "DSA");
+ break;
+
+ case EVP_PKEY_EC:
+ tls_rec_set_public_key_algorithm(tls, "EC");
+ break;
+
+ default:
+ tls_rec_set_public_key_algorithm(tls, "Unknown");
+ break;
+ }
+
+ public_key_fingerprint_hex = binary_to_hex(public_key_fingerprint, public_key_fingerprint_size);
+ tls_rec_set_public_key_fingerprint(tls, public_key_fingerprint_hex);
+ tls_rec_set_public_key_size(tls, EVP_PKEY_bits(pubkey));
+ tls_rec_set_public_key_fingerprint_algorithm(tls, "SHA256");
+
+ // Read the NotBefore timestamp.
+ bio = BIO_new(BIO_s_mem());
+ ASN1_TIME_print(bio, X509_get_notBefore(cert));
+ length = BIO_read(bio, buffer, sizeof(buffer));
+ buffer[length] = '\0';
+ BIO_free(bio);
+ tls_rec_set_not_before(tls, buffer);
+
+ // Read the NotAfter timestamp.
+ bio = BIO_new(BIO_s_mem());
+ ASN1_TIME_print(bio, X509_get_notAfter(cert));
+ length = BIO_read(bio, buffer, sizeof(buffer));
+ buffer[length] = '\0';
+ BIO_free(bio);
+ tls_rec_set_not_after(tls, buffer);
+
+ g_free(cert_fingerprint_hex);
+ g_free(public_key_fingerprint_hex);
+ EVP_PKEY_free(pubkey);
+}
+
+static void set_peer_cert_chain_info(TLS_REC *tls, SSL *ssl)
+{
+ g_return_if_fail(tls != NULL);
+ g_return_if_fail(ssl != NULL);
+
+ int nid;
+ char *key = NULL;
+ char *value = NULL;
+ STACK_OF(X509) *chain = NULL;
+ int i;
+ int j;
+ TLS_CERT_REC *cert_rec = NULL;
+ X509_NAME *name = NULL;
+ X509_NAME_ENTRY *entry = NULL;
+ TLS_CERT_ENTRY_REC *tls_cert_entry_rec = NULL;
+ ASN1_STRING *data = NULL;
+
+ chain = SSL_get_peer_cert_chain(ssl);
+
+ if (chain == NULL)
+ return;
+
+ for (i = 0; i < sk_X509_num(chain); i++) {
+ cert_rec = tls_cert_create_rec();
+
+ // Subject.
+ name = X509_get_subject_name(sk_X509_value(chain, i));
+
+ for (j = 0; j < X509_NAME_entry_count(name); j++) {
+ entry = X509_NAME_get_entry(name, j);
+
+ nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(entry));
+ key = (char *)OBJ_nid2sn(nid);
+
+ if (key == NULL)
+ key = (char *)OBJ_nid2ln(nid);
+
+ data = X509_NAME_ENTRY_get_data(entry);
+ value = (char *)ASN1_STRING_data(data);
+
+ tls_cert_entry_rec = tls_cert_entry_create_rec(key, value);
+ tls_cert_rec_append_subject_entry(cert_rec, tls_cert_entry_rec);
+ }
+
+ // Issuer.
+ name = X509_get_issuer_name(sk_X509_value(chain, i));
+
+ for (j = 0; j < X509_NAME_entry_count(name); j++) {
+ entry = X509_NAME_get_entry(name, j);
+
+ nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(entry));
+ key = (char *)OBJ_nid2sn(nid);
+
+ if (key == NULL)
+ key = (char *)OBJ_nid2ln(nid);
+
+ data = X509_NAME_ENTRY_get_data(entry);
+ value = (char *)ASN1_STRING_data(data);
+
+ tls_cert_entry_rec = tls_cert_entry_create_rec(key, value);
+ tls_cert_rec_append_issuer_entry(cert_rec, tls_cert_entry_rec);
+ }
+
+ tls_rec_append_cert(tls, cert_rec);
+ }
+}
+
+static void set_server_temporary_key_info(TLS_REC *tls, SSL *ssl)
+{
+ g_return_if_fail(tls != NULL);
+ g_return_if_fail(ssl != NULL);
+
+#ifdef SSL_get_server_tmp_key
+ // Show ephemeral key information.
+ EVP_PKEY *ephemeral_key = NULL;
+
+ // OPENSSL_NO_EC is for solaris 11.3 (2016), github ticket #598
+#ifndef OPENSSL_NO_EC
+ EC_KEY *ec_key = NULL;
+#endif
+ char *ephemeral_key_algorithm = NULL;
+ char *cname = NULL;
+ int nid;
+
+ if (SSL_get_server_tmp_key(ssl, &ephemeral_key)) {
+ switch (EVP_PKEY_id(ephemeral_key)) {
+ case EVP_PKEY_DH:
+ tls_rec_set_ephemeral_key_algorithm(tls, "DH");
+ tls_rec_set_ephemeral_key_size(tls, EVP_PKEY_bits(ephemeral_key));
+ break;
+
+#ifndef OPENSSL_NO_EC
+ case EVP_PKEY_EC:
+ ec_key = EVP_PKEY_get1_EC_KEY(ephemeral_key);
+ nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key));
+ EC_KEY_free(ec_key);
+ cname = (char *)OBJ_nid2sn(nid);
+ ephemeral_key_algorithm = g_strdup_printf("ECDH: %s", cname);
+
+ tls_rec_set_ephemeral_key_algorithm(tls, ephemeral_key_algorithm);
+ tls_rec_set_ephemeral_key_size(tls, EVP_PKEY_bits(ephemeral_key));
+
+ g_free_and_null(ephemeral_key_algorithm);
+ break;
+#endif
+
+ default:
+ tls_rec_set_ephemeral_key_algorithm(tls, "Unknown");
+ tls_rec_set_ephemeral_key_size(tls, EVP_PKEY_bits(ephemeral_key));
+ break;
+ }
+
+ EVP_PKEY_free(ephemeral_key);
+ }
+#endif // SSL_get_server_tmp_key.
+}
+
GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip, SERVER_REC *server)
{
GIOChannel *handle, *ssl_handle;
@@ -569,9 +705,21 @@ int irssi_ssl_handshake(GIOChannel *handle)
{
GIOSSLChannel *chan = (GIOSSLChannel *)handle;
int ret, err;
- X509 *cert;
- const char *errstr;
-
+ const char *errstr = NULL;
+ X509 *cert = NULL;
+ X509_PUBKEY *pubkey = NULL;
+ int pubkey_size = 0;
+ unsigned char *pubkey_der = NULL;
+ unsigned char *pubkey_der_tmp = NULL;
+ unsigned char pubkey_fingerprint[EVP_MAX_MD_SIZE];
+ unsigned int pubkey_fingerprint_size;
+ unsigned char cert_fingerprint[EVP_MAX_MD_SIZE];
+ unsigned int cert_fingerprint_size;
+ const char *pinned_cert_fingerprint = chan->server->connrec->tls_pinned_cert;
+ const char *pinned_pubkey_fingerprint = chan->server->connrec->tls_pinned_pubkey;
+ TLS_REC *tls = NULL;
+
+ ERR_clear_error();
ret = SSL_connect(chan->ssl);
if (ret <= 0) {
err = SSL_get_error(chan->ssl, ret);
@@ -597,22 +745,74 @@ int irssi_ssl_handshake(GIOChannel *handle)
}
cert = SSL_get_peer_certificate(chan->ssl);
+ pubkey = X509_get_X509_PUBKEY(cert);
+
if (cert == NULL) {
- g_warning("SSL server supplied no certificate");
- return -1;
+ g_warning("TLS server supplied no certificate");
+ ret = 0;
+ goto done;
}
- ret = !chan->verify || irssi_ssl_verify(chan->ssl, chan->ctx, chan->server->connrec->address, chan->port, cert, chan->server);
- X509_free(cert);
- return ret ? 0 : -1;
-}
-#else /* HAVE_OPENSSL */
+ if (pubkey == NULL) {
+ g_warning("TLS server supplied no certificate public key");
+ ret = 0;
+ goto done;
+ }
-GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip, SERVER_REC *server)
-{
- g_warning("Connection failed: SSL support not enabled in this build.");
- errno = ENOSYS;
- return NULL;
-}
+ if (! X509_digest(cert, EVP_sha256(), cert_fingerprint, &cert_fingerprint_size)) {
+ g_warning("Unable to generate certificate fingerprint");
+ ret = 0;
+ goto done;
+ }
+
+ pubkey_size = i2d_X509_PUBKEY(pubkey, NULL);
+ pubkey_der = pubkey_der_tmp = g_new(unsigned char, pubkey_size);
+ i2d_X509_PUBKEY(pubkey, &pubkey_der_tmp);
-#endif /* ! HAVE_OPENSSL */
+ EVP_Digest(pubkey_der, pubkey_size, pubkey_fingerprint, &pubkey_fingerprint_size, EVP_sha256(), 0);
+
+ tls = tls_create_rec();
+ set_cipher_info(tls, chan->ssl);
+ set_pubkey_info(tls, cert, cert_fingerprint, cert_fingerprint_size, pubkey_fingerprint, pubkey_fingerprint_size);
+ set_peer_cert_chain_info(tls, chan->ssl);
+ set_server_temporary_key_info(tls, chan->ssl);
+
+ // Emit the TLS rec.
+ signal_emit("tls handshake finished", 2, chan->server, tls);
+
+ ret = 1;
+
+ if (pinned_cert_fingerprint != NULL && pinned_cert_fingerprint[0] != '\0') {
+ ret = g_ascii_strcasecmp(pinned_cert_fingerprint, tls->certificate_fingerprint) == 0;
+
+ if (! ret) {
+ g_warning(" Pinned certificate mismatch");
+ goto done;
+ }
+ }
+
+ if (pinned_pubkey_fingerprint != NULL && pinned_pubkey_fingerprint[0] != '\0') {
+ ret = g_ascii_strcasecmp(pinned_pubkey_fingerprint, tls->public_key_fingerprint) == 0;
+
+ if (! ret) {
+ g_warning(" Pinned public key mismatch");
+ goto done;
+ }
+ }
+
+ if (chan->verify) {
+ ret = irssi_ssl_verify(chan->ssl, chan->ctx, chan->server->connrec->address, chan->port, cert, chan->server, tls);
+
+ if (! ret) {
+ // irssi_ssl_verify emits a warning itself.
+ goto done;
+ }
+ }
+
+done:
+ tls_rec_free(tls);
+ X509_free(cert);
+ g_free(pubkey_der);
+
+ return ret ? 0 : -1;
+}
diff --git a/src/core/network.c b/src/core/network.c
index 3659ab36..3e1b7c70 100644
--- a/src/core/network.c
+++ b/src/core/network.c
@@ -30,26 +30,16 @@
union sockaddr_union {
struct sockaddr sa;
struct sockaddr_in sin;
-#ifdef HAVE_IPV6
struct sockaddr_in6 sin6;
-#endif
};
-#ifdef HAVE_IPV6
-# define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ? \
+#define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ? \
sizeof(so.sin6) : sizeof(so.sin))
-#else
-# define SIZEOF_SOCKADDR(so) (sizeof(so.sin))
-#endif
GIOChannel *g_io_channel_new(int handle)
{
GIOChannel *chan;
-#ifdef WIN32
- chan = g_io_channel_win32_new_socket(handle);
-#else
chan = g_io_channel_unix_new(handle);
-#endif
g_io_channel_set_encoding(chan, NULL, NULL);
g_io_channel_set_buffered(chan, FALSE);
return chan;
@@ -60,7 +50,7 @@ GIOChannel *g_io_channel_new(int handle)
IPADDR ip4_any = {
AF_INET,
-#if defined(HAVE_IPV6) && defined(IN6ADDR_ANY_INIT)
+#if defined(IN6ADDR_ANY_INIT)
IN6ADDR_ANY_INIT
#else
{ INADDR_ANY }
@@ -72,10 +62,8 @@ int net_ip_compare(IPADDR *ip1, IPADDR *ip2)
if (ip1->family != ip2->family)
return 0;
-#ifdef HAVE_IPV6
if (ip1->family == AF_INET6)
return memcmp(&ip1->ip, &ip2->ip, sizeof(ip1->ip)) == 0;
-#endif
return memcmp(&ip1->ip, &ip2->ip, 4) == 0;
}
@@ -84,22 +72,16 @@ int net_ip_compare(IPADDR *ip1, IPADDR *ip2)
static void sin_set_ip(union sockaddr_union *so, const IPADDR *ip)
{
if (ip == NULL) {
-#ifdef HAVE_IPV6
so->sin6.sin6_family = AF_INET6;
so->sin6.sin6_addr = in6addr_any;
-#else
- so->sin.sin_family = AF_INET;
- so->sin.sin_addr.s_addr = INADDR_ANY;
-#endif
return;
}
so->sin.sin_family = ip->family;
-#ifdef HAVE_IPV6
+
if (ip->family == AF_INET6)
memcpy(&so->sin6.sin6_addr, &ip->ip, sizeof(ip->ip));
else
-#endif
memcpy(&so->sin.sin_addr, &ip->ip, 4);
}
@@ -107,31 +89,25 @@ void sin_get_ip(const union sockaddr_union *so, IPADDR *ip)
{
ip->family = so->sin.sin_family;
-#ifdef HAVE_IPV6
if (ip->family == AF_INET6)
memcpy(&ip->ip, &so->sin6.sin6_addr, sizeof(ip->ip));
else
-#endif
memcpy(&ip->ip, &so->sin.sin_addr, 4);
}
static void sin_set_port(union sockaddr_union *so, int port)
{
-#ifdef HAVE_IPV6
if (so->sin.sin_family == AF_INET6)
so->sin6.sin6_port = htons((unsigned short)port);
else
-#endif
so->sin.sin_port = htons((unsigned short)port);
}
static int sin_get_port(union sockaddr_union *so)
{
-#ifdef HAVE_IPV6
- if (so->sin.sin_family == AF_INET6)
- return ntohs(so->sin6.sin6_port);
-#endif
- return ntohs(so->sin.sin_port);
+ return ntohs((so->sin.sin_family == AF_INET6) ?
+ so->sin6.sin6_port :
+ so->sin.sin_port);
}
/* Connect to socket */
@@ -188,9 +164,7 @@ GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip)
return NULL;
/* set socket options */
-#ifndef WIN32
fcntl(handle, F_SETFL, O_NONBLOCK);
-#endif
setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
setsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
@@ -211,11 +185,7 @@ GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip)
sin_set_port(&so, port);
ret = connect(handle, &so.sa, SIZEOF_SOCKADDR(so));
-#ifndef WIN32
if (ret < 0 && errno != EINPROGRESS)
-#else
- if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK)
-#endif
{
int old_errno = errno;
close(handle);
@@ -238,9 +208,7 @@ GIOChannel *net_connect_unix(const char *path)
return NULL;
/* set socket options */
-#ifndef WIN32
fcntl(handle, F_SETFL, O_NONBLOCK);
-#endif
/* connect */
memset(&sa, 0, sizeof(sa));
@@ -284,7 +252,7 @@ GIOChannel *net_listen(IPADDR *my_ip, int *port)
/* create the socket */
handle = socket(so.sin.sin_family, SOCK_STREAM, 0);
-#ifdef HAVE_IPV6
+
if (handle == -1 && (errno == EINVAL || errno == EAFNOSUPPORT)) {
/* IPv6 is not supported by OS */
so.sin.sin_family = AF_INET;
@@ -292,14 +260,12 @@ GIOChannel *net_listen(IPADDR *my_ip, int *port)
handle = socket(AF_INET, SOCK_STREAM, 0);
}
-#endif
+
if (handle == -1)
return NULL;
/* set socket options */
-#ifndef WIN32
fcntl(handle, F_SETFL, O_NONBLOCK);
-#endif
setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
setsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
@@ -342,9 +308,7 @@ GIOChannel *net_accept(GIOChannel *handle, IPADDR *addr, int *port)
if (addr != NULL) sin_get_ip(&so, addr);
if (port != NULL) *port = sin_get_port(&so);
-#ifndef WIN32
fcntl(ret, F_SETFL, O_NONBLOCK);
-#endif
return g_io_channel_new(ret);
}
@@ -415,23 +379,18 @@ int net_getsockname(GIOChannel *handle, IPADDR *addr, int *port)
Returns 0 = ok, others = error code for net_gethosterror() */
int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6)
{
-#ifdef HAVE_IPV6
union sockaddr_union *so;
struct addrinfo hints, *ai, *ailist;
int ret, count_v4, count_v6, use_v4, use_v6;
-#else
- struct hostent *hp;
- int count;
-#endif
g_return_val_if_fail(addr != NULL, -1);
memset(ip4, 0, sizeof(IPADDR));
memset(ip6, 0, sizeof(IPADDR));
-#ifdef HAVE_IPV6
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_ADDRCONFIG;
/* save error to host_error for later use */
ret = getaddrinfo(addr, NULL, &hints, &ailist);
@@ -470,85 +429,40 @@ int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6)
}
freeaddrinfo(ailist);
return 0;
-#else
- hp = gethostbyname(addr);
- if (hp == NULL)
- return h_errno;
-
- /* count IPs */
- count = 0;
- while (hp->h_addr_list[count] != NULL)
- count++;
-
- if (count == 0)
- return HOST_NOT_FOUND; /* shouldn't happen? */
-
- /* if there are multiple addresses, return random one */
- ip4->family = AF_INET;
- memcpy(&ip4->ip, hp->h_addr_list[rand() % count], 4);
-
- return 0;
-#endif
}
/* Get name for host, *name should be g_free()'d unless it's NULL.
Return values are the same as with net_gethostbyname() */
int net_gethostbyaddr(IPADDR *ip, char **name)
{
-#ifdef HAVE_IPV6
union sockaddr_union so;
int host_error;
char hostname[NI_MAXHOST];
-#else
- struct hostent *hp;
-#endif
g_return_val_if_fail(ip != NULL, -1);
g_return_val_if_fail(name != NULL, -1);
*name = NULL;
-#ifdef HAVE_IPV6
+
memset(&so, 0, sizeof(so));
sin_set_ip(&so, ip);
/* save error to host_error for later use */
- host_error = getnameinfo((struct sockaddr *) &so, sizeof(so),
- hostname, sizeof(hostname), NULL, 0, 0);
+ host_error = getnameinfo((struct sockaddr *)&so, sizeof(so),
+ hostname, sizeof(hostname),
+ NULL, 0,
+ NI_NAMEREQD);
if (host_error != 0)
return host_error;
*name = g_strdup(hostname);
-#else
- if (ip->family != AF_INET) return -1;
- hp = gethostbyaddr((const char *) &ip->ip, 4, AF_INET);
- if (hp == NULL) return -1;
-
- *name = g_strdup(hp->h_name);
-#endif
return 0;
}
int net_ip2host(IPADDR *ip, char *host)
{
-#ifdef HAVE_IPV6
- if (!inet_ntop(ip->family, &ip->ip, host, MAX_IP_LEN))
- return -1;
-#else
- unsigned long ip4;
-
- if (ip->family != AF_INET) {
- strcpy(host, "0.0.0.0");
- } else {
- ip4 = ntohl(ip->ip.s_addr);
- g_snprintf(host, MAX_IP_LEN, "%lu.%lu.%lu.%lu",
- (ip4 & 0xff000000UL) >> 24,
- (ip4 & 0x00ff0000) >> 16,
- (ip4 & 0x0000ff00) >> 8,
- (ip4 & 0x000000ff));
- }
-#endif
- return 0;
+ return inet_ntop(ip->family, &ip->ip, host, MAX_IP_LEN) ? 0 : -1;
}
int net_host2ip(const char *host, IPADDR *ip)
@@ -558,12 +472,8 @@ int net_host2ip(const char *host, IPADDR *ip)
if (strchr(host, ':') != NULL) {
/* IPv6 */
ip->family = AF_INET6;
-#ifdef HAVE_IPV6
if (inet_pton(AF_INET6, host, &ip->ip) == 0)
return -1;
-#else
- ip->ip.s_addr = 0;
-#endif
} else {
/* IPv4 */
ip->family = AF_INET;
@@ -598,40 +508,24 @@ int net_geterror(GIOChannel *handle)
/* get error of net_gethostname() */
const char *net_gethosterror(int error)
{
-#ifdef HAVE_IPV6
g_return_val_if_fail(error != 0, NULL);
- return gai_strerror(error);
-#else
- switch (error) {
- case HOST_NOT_FOUND:
- return "Host not found";
- case NO_ADDRESS:
- return "No IP address found for name";
- case NO_RECOVERY:
- return "A non-recovable name server error occurred";
- case TRY_AGAIN:
- return "A temporary error on an authoritative name server";
+ if (error == EAI_SYSTEM) {
+ return strerror(errno);
+ } else {
+ return gai_strerror(error);
}
-
- /* unknown error */
- return NULL;
-#endif
}
/* return TRUE if host lookup failed because it didn't exist (ie. not
some error with name server) */
int net_hosterror_notfound(int error)
{
-#ifdef HAVE_IPV6
-#ifdef EAI_NODATA /* NODATA is depricated */
+#ifdef EAI_NODATA /* NODATA is deprecated */
return error != 1 && (error == EAI_NONAME || error == EAI_NODATA);
#else
return error != 1 && (error == EAI_NONAME);
#endif
-#else
- return error == HOST_NOT_FOUND || error == NO_ADDRESS;
-#endif
}
/* Get name of TCP service */
diff --git a/src/core/network.h b/src/core/network.h
index fa7e9675..03f3b813 100644
--- a/src/core/network.h
+++ b/src/core/network.h
@@ -6,12 +6,10 @@
#endif
#include <sys/types.h>
-#ifndef WIN32
# include <sys/socket.h>
# include <netinet/in.h>
# include <netdb.h>
# include <arpa/inet.h>
-#endif
#ifndef AF_INET6
# ifdef PF_INET6
@@ -23,19 +21,11 @@
struct _IPADDR {
unsigned short family;
-#ifdef HAVE_IPV6
struct in6_addr ip;
-#else
- struct in_addr ip;
-#endif
};
/* maxmimum string length of IP address */
-#ifdef HAVE_IPV6
-# define MAX_IP_LEN INET6_ADDRSTRLEN
-#else
-# define MAX_IP_LEN 20
-#endif
+#define MAX_IP_LEN INET6_ADDRSTRLEN
#define IPADDR_IS_V6(ip) ((ip)->family != AF_INET)
@@ -47,7 +37,7 @@ GIOChannel *g_io_channel_new(int handle);
int net_ip_compare(IPADDR *ip1, IPADDR *ip2);
/* Connect to socket */
-GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip);
+GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip) G_GNUC_DEPRECATED;
/* Connect to socket with ip address and SSL*/
GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip, SERVER_REC *server);
int irssi_ssl_handshake(GIOChannel *handle);
diff --git a/src/core/nicklist.c b/src/core/nicklist.c
index a5f25f34..770b0afc 100644
--- a/src/core/nicklist.c
+++ b/src/core/nicklist.c
@@ -342,7 +342,7 @@ GSList *nicklist_get_same_unique(SERVER_REC *server, void *id)
return rec.list;
}
-/* nick record comparision for sort functions */
+/* nick record comparison for sort functions */
int nicklist_compare(NICK_REC *p1, NICK_REC *p2, const char *nick_prefix)
{
int i;
@@ -478,7 +478,7 @@ static NICK_REC *nick_nfind(CHANNEL_REC *channel, const char *nick, int len)
if (rec != NULL) {
/* if there's multiple, get the one with identical case */
while (rec->next != NULL) {
- if (strcmp(rec->nick, tmpnick) == 0)
+ if (g_strcmp0(rec->nick, tmpnick) == 0)
break;
rec = rec->next;
}
@@ -571,6 +571,14 @@ int nick_match_msg(CHANNEL_REC *channel, const char *msg, const char *nick)
}
}
+int nick_match_msg_everywhere(CHANNEL_REC *channel, const char *msg, const char *nick)
+{
+ g_return_val_if_fail(nick != NULL, FALSE);
+ g_return_val_if_fail(msg != NULL, FALSE);
+
+ return stristr_full(msg, nick) != NULL;
+}
+
void nicklist_init(void)
{
signal_add_first("channel created", (SIGNAL_FUNC) sig_channel_created);
diff --git a/src/core/nicklist.h b/src/core/nicklist.h
index 55dfd5ef..5e0f4f75 100644
--- a/src/core/nicklist.h
+++ b/src/core/nicklist.h
@@ -55,6 +55,7 @@ int nicklist_compare(NICK_REC *p1, NICK_REC *p2, const char *nick_prefix);
/* Check is `msg' is meant for `nick'. */
int nick_match_msg(CHANNEL_REC *channel, const char *msg, const char *nick);
+int nick_match_msg_everywhere(CHANNEL_REC *channel, const char *msg, const char *nick);
void nicklist_init(void);
void nicklist_deinit(void);
diff --git a/src/core/query-rec.h b/src/core/query-rec.h
index fc08d2ef..59519ad4 100644
--- a/src/core/query-rec.h
+++ b/src/core/query-rec.h
@@ -7,5 +7,5 @@ char *server_tag;
time_t last_unread_msg;
unsigned int unwanted:1; /* TRUE if the other side closed or
- some error occured (DCC chats!) */
+ some error occurred (DCC chats!) */
unsigned int destroying:1;
diff --git a/src/core/rawlog.c b/src/core/rawlog.c
index 2fa6b850..5927e730 100644
--- a/src/core/rawlog.c
+++ b/src/core/rawlog.c
@@ -32,6 +32,7 @@
static int rawlog_lines;
static int signal_rawlog;
static int log_file_create_mode;
+static int log_dir_create_mode;
RAWLOG_REC *rawlog_create(void)
{
@@ -145,13 +146,22 @@ void rawlog_close(RAWLOG_REC *rawlog)
void rawlog_save(RAWLOG_REC *rawlog, const char *fname)
{
- char *path;
+ char *path, *dir;
int f;
+ dir = g_path_get_dirname(fname);
+ g_mkdir_with_parents(dir, log_dir_create_mode);
+ g_free(dir);
+
path = convert_home(fname);
f = open(path, O_WRONLY | O_APPEND | O_CREAT, log_file_create_mode);
g_free(path);
+ if (f < 0) {
+ g_warning("rawlog open() failed: %s", strerror(errno));
+ return;
+ }
+
rawlog_dump(rawlog, f);
close(f);
}
@@ -165,6 +175,11 @@ static void read_settings(void)
{
rawlog_set_size(settings_get_int("rawlog_lines"));
log_file_create_mode = octal2dec(settings_get_int("log_create_mode"));
+ log_dir_create_mode = log_file_create_mode;
+ if (log_file_create_mode & 0400) log_dir_create_mode |= 0100;
+ if (log_file_create_mode & 0040) log_dir_create_mode |= 0010;
+ if (log_file_create_mode & 0004) log_dir_create_mode |= 0001;
+
}
static void cmd_rawlog(const char *data, SERVER_REC *server, void *item)
diff --git a/src/core/recode.c b/src/core/recode.c
index 029d7ff1..d001a46a 100644
--- a/src/core/recode.c
+++ b/src/core/recode.c
@@ -183,7 +183,7 @@ char *recode_out(const SERVER_REC *server, const char *str, const char *target)
}
char **recode_split(const SERVER_REC *server, const char *str,
- const char *target, int len)
+ const char *target, int len, gboolean onspace)
{
GIConv cd = (GIConv)-1;
const char *from = translit_charset;
@@ -219,7 +219,7 @@ char **recode_split(const SERVER_REC *server, const char *str,
cd = g_iconv_open(to, from);
if (cd == (GIConv)-1) {
/* Fall back to splitting by byte. */
- ret = strsplit_len(str, len);
+ ret = strsplit_len(str, len, onspace);
goto out;
}
@@ -235,11 +235,25 @@ char **recode_split(const SERVER_REC *server, const char *str,
*/
ret[n] = NULL;
g_strfreev(ret);
- ret = strsplit_len(str, len);
+ ret = strsplit_len(str, len, onspace);
goto out;
}
/* Outbuf overflowed, split the input string. */
+ if (onspace) {
+ /*
+ * Try to find a space to split on and leave
+ * the space on the previous line.
+ */
+ int i;
+ for (i = 0; i < inbuf - previnbuf; i++) {
+ if (previnbuf[inbuf-previnbuf-1-i] == ' ') {
+ inbuf -= i;
+ inbytesleft += i;
+ break;
+ }
+ }
+ }
ret[n++] = g_strndup(previnbuf, inbuf - previnbuf);
ret = g_renew(char *, ret, n + 1);
previnbuf = inbuf;
diff --git a/src/core/recode.h b/src/core/recode.h
index b70ec630..719e8f54 100644
--- a/src/core/recode.h
+++ b/src/core/recode.h
@@ -4,7 +4,7 @@
char *recode_in (const SERVER_REC *server, const char *str, const char *target);
char *recode_out (const SERVER_REC *server, const char *str, const char *target);
char **recode_split(const SERVER_REC *server, const char *str,
- const char *target, int len);
+ const char *target, int len, gboolean onspace);
gboolean is_valid_charset(const char *charset);
gboolean is_utf8(void);
void recode_update_charset(void);
diff --git a/src/core/server-connect-rec.h b/src/core/server-connect-rec.h
index 17537508..fa348769 100644
--- a/src/core/server-connect-rec.h
+++ b/src/core/server-connect-rec.h
@@ -23,11 +23,14 @@ char *nick;
char *username;
char *realname;
-char *ssl_cert;
-char *ssl_pkey;
-char *ssl_pass;
-char *ssl_cafile;
-char *ssl_capath;
+char *tls_cert;
+char *tls_pkey;
+char *tls_pass;
+char *tls_cafile;
+char *tls_capath;
+char *tls_ciphers;
+char *tls_pinned_cert;
+char *tls_pinned_pubkey;
GIOChannel *connect_handle; /* connect using this handle */
@@ -37,8 +40,8 @@ unsigned int reconnecting:1; /* we're trying to reconnect any connection */
unsigned int no_autojoin_channels:1; /* don't autojoin any channels */
unsigned int no_autosendcmd:1; /* don't execute autosendcmd */
unsigned int unix_socket:1; /* Connect using named unix socket */
-unsigned int use_ssl:1; /* this connection uses SSL */
-unsigned int ssl_verify:1;
+unsigned int use_tls:1; /* this connection uses TLS */
+unsigned int tls_verify:1;
unsigned int no_connect:1; /* don't connect() at all, it's done by plugin */
char *channels;
char *away_reason;
diff --git a/src/core/server-setup-rec.h b/src/core/server-setup-rec.h
index ae797559..e6b0431c 100644
--- a/src/core/server-setup-rec.h
+++ b/src/core/server-setup-rec.h
@@ -8,11 +8,17 @@ char *address;
int port;
char *password;
-char *ssl_cert;
-char *ssl_pkey;
-char *ssl_pass;
-char *ssl_cafile;
-char *ssl_capath;
+int sasl_mechanism;
+char *sasl_password;
+
+char *tls_cert;
+char *tls_pkey;
+char *tls_pass;
+char *tls_cafile;
+char *tls_capath;
+char *tls_ciphers;
+char *tls_pinned_cert;
+char *tls_pinned_pubkey;
char *own_host; /* address to use when connecting this server */
IPADDR *own_ip4, *own_ip6; /* resolved own_address if not NULL */
@@ -24,7 +30,7 @@ unsigned int no_proxy:1;
unsigned int last_failed:1; /* if last connection attempt failed */
unsigned int banned:1; /* if we're banned from this server */
unsigned int dns_error:1; /* DNS said the host doesn't exist */
-unsigned int use_ssl:1; /* this connection uses SSL */
-unsigned int ssl_verify:1;
+unsigned int use_tls:1; /* this connection uses TLS */
+unsigned int tls_verify:1;
GHashTable *module_data;
diff --git a/src/core/servers-reconnect.c b/src/core/servers-reconnect.c
index 0a08b461..1727704c 100644
--- a/src/core/servers-reconnect.c
+++ b/src/core/servers-reconnect.c
@@ -190,13 +190,17 @@ server_connect_copy_skeleton(SERVER_CONNECT_REC *src, int connect_info)
dest->away_reason = g_strdup(src->away_reason);
dest->no_autojoin_channels = src->no_autojoin_channels;
dest->no_autosendcmd = src->no_autosendcmd;
-
- dest->use_ssl = src->use_ssl;
- dest->ssl_cert = g_strdup(src->ssl_cert);
- dest->ssl_pkey = g_strdup(src->ssl_pkey);
- dest->ssl_verify = src->ssl_verify;
- dest->ssl_cafile = g_strdup(src->ssl_cafile);
- dest->ssl_capath = g_strdup(src->ssl_capath);
+ dest->unix_socket = src->unix_socket;
+
+ dest->use_tls = src->use_tls;
+ dest->tls_cert = g_strdup(src->tls_cert);
+ dest->tls_pkey = g_strdup(src->tls_pkey);
+ dest->tls_verify = src->tls_verify;
+ dest->tls_cafile = g_strdup(src->tls_cafile);
+ dest->tls_capath = g_strdup(src->tls_capath);
+ dest->tls_ciphers = g_strdup(src->tls_ciphers);
+ dest->tls_pinned_cert = g_strdup(src->tls_pinned_cert);
+ dest->tls_pinned_pubkey = g_strdup(src->tls_pinned_pubkey);
return dest;
}
@@ -253,6 +257,9 @@ static void sig_reconnect(SERVER_REC *server)
conn->port = server->connrec->port;
conn->password = g_strdup(server->connrec->password);
+ if (strchr(conn->address, '/') != NULL)
+ conn->unix_socket = TRUE;
+
server_reconnect_add(conn, (server->connect_time == 0 ? time(NULL) :
server->connect_time) + reconnect_time);
server_connect_unref(conn);
@@ -381,7 +388,7 @@ static void cmd_reconnect(const char *data, SERVER_REC *server)
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &tag, &msg))
return;
- if (*tag != '\0' && strcmp(tag, "*") != 0)
+ if (*tag != '\0' && g_strcmp0(tag, "*") != 0)
server = server_find_tag(tag);
if (server != NULL) {
@@ -416,8 +423,8 @@ static void cmd_reconnect(const char *data, SERVER_REC *server)
cmd_param_error(CMDERR_NOT_CONNECTED);
rec = reconnects->data;
} else {
- if (g_ascii_strncasecmp(data, "RECON-", 6) == 0)
- data += 6;
+ if (g_ascii_strncasecmp(tag, "RECON-", 6) == 0)
+ tag += 6;
tagnum = atoi(tag);
rec = tagnum <= 0 ? NULL : reconnect_find_tag(tagnum);
diff --git a/src/core/servers-setup.c b/src/core/servers-setup.c
index 0819ff1a..9492c58c 100644
--- a/src/core/servers-setup.c
+++ b/src/core/servers-setup.c
@@ -122,6 +122,9 @@ static void server_setup_fill(SERVER_CONNECT_REC *conn,
conn->address = g_strdup(address);
if (port > 0) conn->port = port;
+ if (strchr(address, '/') != NULL)
+ conn->unix_socket = TRUE;
+
if (!conn->nick) conn->nick = g_strdup(settings_get_str("nick"));
conn->username = g_strdup(settings_get_str("user_name"));
conn->realname = g_strdup(settings_get_str("real_name"));
@@ -164,18 +167,24 @@ static void server_setup_fill_server(SERVER_CONNECT_REC *conn,
if (sserver->port > 0 && conn->port <= 0)
conn->port = sserver->port;
- conn->use_ssl = sserver->use_ssl;
- if (conn->ssl_cert == NULL && sserver->ssl_cert != NULL && sserver->ssl_cert[0] != '\0')
- conn->ssl_cert = g_strdup(sserver->ssl_cert);
- if (conn->ssl_pkey == NULL && sserver->ssl_pkey != NULL && sserver->ssl_pkey[0] != '\0')
- conn->ssl_pkey = g_strdup(sserver->ssl_pkey);
- if (conn->ssl_pass == NULL && sserver->ssl_pass != NULL && sserver->ssl_pass[0] != '\0')
- conn->ssl_pass = g_strdup(sserver->ssl_pass);
- conn->ssl_verify = sserver->ssl_verify;
- if (conn->ssl_cafile == NULL && sserver->ssl_cafile != NULL && sserver->ssl_cafile[0] != '\0')
- conn->ssl_cafile = g_strdup(sserver->ssl_cafile);
- if (conn->ssl_capath == NULL && sserver->ssl_capath != NULL && sserver->ssl_capath[0] != '\0')
- conn->ssl_capath = g_strdup(sserver->ssl_capath);
+ conn->use_tls = sserver->use_tls;
+ if (conn->tls_cert == NULL && sserver->tls_cert != NULL && sserver->tls_cert[0] != '\0')
+ conn->tls_cert = g_strdup(sserver->tls_cert);
+ if (conn->tls_pkey == NULL && sserver->tls_pkey != NULL && sserver->tls_pkey[0] != '\0')
+ conn->tls_pkey = g_strdup(sserver->tls_pkey);
+ if (conn->tls_pass == NULL && sserver->tls_pass != NULL && sserver->tls_pass[0] != '\0')
+ conn->tls_pass = g_strdup(sserver->tls_pass);
+ conn->tls_verify = sserver->tls_verify;
+ if (conn->tls_cafile == NULL && sserver->tls_cafile != NULL && sserver->tls_cafile[0] != '\0')
+ conn->tls_cafile = g_strdup(sserver->tls_cafile);
+ if (conn->tls_capath == NULL && sserver->tls_capath != NULL && sserver->tls_capath[0] != '\0')
+ conn->tls_capath = g_strdup(sserver->tls_capath);
+ if (conn->tls_ciphers == NULL && sserver->tls_ciphers != NULL && sserver->tls_ciphers[0] != '\0')
+ conn->tls_ciphers = g_strdup(sserver->tls_ciphers);
+ if (conn->tls_pinned_cert == NULL && sserver->tls_pinned_cert != NULL && sserver->tls_pinned_cert[0] != '\0')
+ conn->tls_pinned_cert = g_strdup(sserver->tls_pinned_cert);
+ if (conn->tls_pinned_pubkey == NULL && sserver->tls_pinned_pubkey != NULL && sserver->tls_pinned_pubkey[0] != '\0')
+ conn->tls_pinned_pubkey = g_strdup(sserver->tls_pinned_pubkey);
server_setup_fill_reconn(conn, sserver);
@@ -316,8 +325,8 @@ server_create_conn(int chat_type, const char *dest, int port,
chatrec = chatnet_find(dest);
if (chatrec != NULL) {
rec = create_chatnet_conn(chatrec->name, port, password, nick);
- if (rec != NULL)
- return rec;
+ /* If rec is NULL the chatnet has no url to connect to */
+ return rec;
}
chatrec = chatnet == NULL ? NULL : chatnet_find(chatnet);
@@ -357,9 +366,10 @@ SERVER_SETUP_REC *server_setup_find(const char *address, int port,
static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node)
{
SERVER_SETUP_REC *rec;
- CHATNET_REC *chatnetrec;
+ CHATNET_REC *chatnetrec;
char *server, *chatnet, *family;
int port;
+ char *value = NULL;
g_return_val_if_fail(node != NULL, NULL);
@@ -385,7 +395,7 @@ static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node)
chatnet_create(chatnetrec);
}
- family = config_node_get_str(node, "family", "");
+ family = config_node_get_str(node, "family", "");
rec = CHAT_PROTOCOL(chatnetrec)->create_server_setup();
rec->type = module_get_uniq_id("SERVER SETUP", 0);
@@ -395,17 +405,55 @@ static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node)
(g_ascii_strcasecmp(family, "inet") == 0 ? AF_INET : 0);
rec->address = g_strdup(server);
rec->password = g_strdup(config_node_get_str(node, "password", NULL));
- rec->use_ssl = config_node_get_bool(node, "use_ssl", FALSE);
- rec->ssl_cert = g_strdup(config_node_get_str(node, "ssl_cert", NULL));
- rec->ssl_pkey = g_strdup(config_node_get_str(node, "ssl_pkey", NULL));
- rec->ssl_pass = g_strdup(config_node_get_str(node, "ssl_pass", NULL));
- rec->ssl_verify = config_node_get_bool(node, "ssl_verify", FALSE);
- rec->ssl_cafile = g_strdup(config_node_get_str(node, "ssl_cafile", NULL));
- rec->ssl_capath = g_strdup(config_node_get_str(node, "ssl_capath", NULL));
- if (rec->ssl_cafile || rec->ssl_capath)
- rec->ssl_verify = TRUE;
- if (rec->ssl_cert != NULL || rec->ssl_verify)
- rec->use_ssl = TRUE;
+
+ rec->use_tls = config_node_get_bool(node, "use_tls", FALSE) || config_node_get_bool(node, "use_ssl", FALSE);
+ rec->tls_verify = config_node_get_bool(node, "tls_verify", FALSE) || config_node_get_bool(node, "ssl_verify", FALSE);
+
+ value = config_node_get_str(node, "tls_cert", NULL);
+ if (value == NULL)
+ value = config_node_get_str(node, "ssl_cert", NULL);
+ rec->tls_cert = g_strdup(value);
+
+ value = config_node_get_str(node, "tls_pkey", NULL);
+ if (value == NULL)
+ value = config_node_get_str(node, "ssl_pkey", NULL);
+ rec->tls_pkey = g_strdup(value);
+
+ value = config_node_get_str(node, "tls_pass", NULL);
+ if (value == NULL)
+ value = config_node_get_str(node, "ssl_pass", NULL);
+ rec->tls_pass = g_strdup(value);
+
+ value = config_node_get_str(node, "tls_cafile", NULL);
+ if (value == NULL)
+ value = config_node_get_str(node, "ssl_cafile", NULL);
+ rec->tls_cafile = g_strdup(value);
+
+ value = config_node_get_str(node, "tls_capath", NULL);
+ if (value == NULL)
+ value = config_node_get_str(node, "ssl_capath", NULL);
+ rec->tls_capath = g_strdup(value);
+
+ value = config_node_get_str(node, "tls_ciphers", NULL);
+ if (value == NULL)
+ value = config_node_get_str(node, "ssl_ciphers", NULL);
+ rec->tls_ciphers = g_strdup(value);
+
+ value = config_node_get_str(node, "tls_pinned_cert", NULL);
+ if (value == NULL)
+ value = config_node_get_str(node, "ssl_pinned_cert", NULL);
+ rec->tls_pinned_cert = g_strdup(value);
+
+ value = config_node_get_str(node, "tls_pinned_pubkey", NULL);
+ if (value == NULL)
+ value = config_node_get_str(node, "ssl_pinned_pubkey", NULL);
+ rec->tls_pinned_pubkey = g_strdup(value);
+
+ if (rec->tls_cafile || rec->tls_capath)
+ rec->tls_verify = TRUE;
+ if (rec->tls_cert != NULL || rec->tls_verify)
+ rec->use_tls = TRUE;
+
rec->port = port;
rec->autoconnect = config_node_get_bool(node, "autoconnect", FALSE);
rec->no_proxy = config_node_get_bool(node, "no_proxy", FALSE);
@@ -417,17 +465,39 @@ static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node)
return rec;
}
+static int compare_server_setup (CONFIG_NODE *node, SERVER_SETUP_REC *server)
+{
+ char *address, *chatnet;
+ int port;
+
+ address = config_node_get_str(node, "address", NULL);
+ chatnet = config_node_get_str(node, "chatnet", NULL);
+ port = config_node_get_int(node, "port", 0);
+
+ if (g_strcmp0(address, server->address) != 0 ||
+ g_strcmp0(chatnet, server->chatnet) != 0 ||
+ port != server->port)
+ return 1;
+
+ return 0;
+}
+
static void server_setup_save(SERVER_SETUP_REC *rec)
{
- CONFIG_NODE *parentnode, *node;
- int index;
+ CONFIG_NODE *parent_node, *node;
+ GSList *config_node;
- index = g_slist_index(setupservers, rec);
+ parent_node = iconfig_node_traverse("(servers", TRUE);
- parentnode = iconfig_node_traverse("(servers", TRUE);
- node = config_node_nth(parentnode, index);
- if (node == NULL)
- node = config_node_section(parentnode, NULL, NODE_TYPE_BLOCK);
+ /* Try to find this channel in the configuration */
+ config_node = g_slist_find_custom(parent_node->value, rec,
+ (GCompareFunc)compare_server_setup);
+ if (config_node != NULL)
+ /* Let's update this server record */
+ node = config_node->data;
+ else
+ /* Create a brand-new server record */
+ node = iconfig_node_section(parent_node, NULL, NODE_TYPE_BLOCK);
iconfig_node_clear(node);
iconfig_node_set_str(node, "address", rec->address);
@@ -435,13 +505,18 @@ static void server_setup_save(SERVER_SETUP_REC *rec)
iconfig_node_set_int(node, "port", rec->port);
iconfig_node_set_str(node, "password", rec->password);
- iconfig_node_set_bool(node, "use_ssl", rec->use_ssl);
- iconfig_node_set_str(node, "ssl_cert", rec->ssl_cert);
- iconfig_node_set_str(node, "ssl_pkey", rec->ssl_pkey);
- iconfig_node_set_str(node, "ssl_pass", rec->ssl_pass);
- iconfig_node_set_bool(node, "ssl_verify", rec->ssl_verify);
- iconfig_node_set_str(node, "ssl_cafile", rec->ssl_cafile);
- iconfig_node_set_str(node, "ssl_capath", rec->ssl_capath);
+
+ iconfig_node_set_bool(node, "use_tls", rec->use_tls);
+ iconfig_node_set_str(node, "tls_cert", rec->tls_cert);
+ iconfig_node_set_str(node, "tls_pkey", rec->tls_pkey);
+ iconfig_node_set_str(node, "tls_pass", rec->tls_pass);
+ iconfig_node_set_bool(node, "tls_verify", rec->tls_verify);
+ iconfig_node_set_str(node, "tls_cafile", rec->tls_cafile);
+ iconfig_node_set_str(node, "tls_capath", rec->tls_capath);
+ iconfig_node_set_str(node, "tls_ciphers", rec->tls_ciphers);
+ iconfig_node_set_str(node, "tls_pinned_cert", rec->tls_pinned_cert);
+ iconfig_node_set_str(node, "tls_pinned_pubkey", rec->tls_pinned_pubkey);
+
iconfig_node_set_str(node, "own_host", rec->own_host);
iconfig_node_set_str(node, "family",
@@ -458,14 +533,21 @@ static void server_setup_save(SERVER_SETUP_REC *rec)
static void server_setup_remove_config(SERVER_SETUP_REC *rec)
{
- CONFIG_NODE *node;
- int index;
+ CONFIG_NODE *parent_node;
+ GSList *config_node;
- node = iconfig_node_traverse("servers", FALSE);
- if (node != NULL) {
- index = g_slist_index(setupservers, rec);
- iconfig_node_list_remove(node, index);
- }
+ parent_node = iconfig_node_traverse("servers", FALSE);
+
+ if (parent_node == NULL)
+ return;
+
+ /* Try to find this server in the configuration */
+ config_node = g_slist_find_custom(parent_node->value, rec,
+ (GCompareFunc)compare_server_setup);
+
+ if (config_node != NULL)
+ /* Delete the server from the configuration */
+ iconfig_node_remove(parent_node, config_node->data);
}
static void server_setup_destroy(SERVER_SETUP_REC *rec)
@@ -478,11 +560,14 @@ static void server_setup_destroy(SERVER_SETUP_REC *rec)
g_free_not_null(rec->own_ip6);
g_free_not_null(rec->chatnet);
g_free_not_null(rec->password);
- g_free_not_null(rec->ssl_cert);
- g_free_not_null(rec->ssl_pkey);
- g_free_not_null(rec->ssl_pass);
- g_free_not_null(rec->ssl_cafile);
- g_free_not_null(rec->ssl_capath);
+ g_free_not_null(rec->tls_cert);
+ g_free_not_null(rec->tls_pkey);
+ g_free_not_null(rec->tls_pass);
+ g_free_not_null(rec->tls_cafile);
+ g_free_not_null(rec->tls_capath);
+ g_free_not_null(rec->tls_ciphers);
+ g_free_not_null(rec->tls_pinned_cert);
+ g_free_not_null(rec->tls_pinned_pubkey);
g_free(rec->address);
g_free(rec);
}
@@ -497,6 +582,21 @@ void server_setup_add(SERVER_SETUP_REC *rec)
signal_emit("server setup updated", 1, rec);
}
+void server_setup_remove_chatnet(const char *chatnet)
+{
+ GSList *tmp, *next;
+
+ g_return_if_fail(chatnet != NULL);
+
+ for (tmp = setupservers; tmp != NULL; tmp = next) {
+ SERVER_SETUP_REC *rec = tmp->data;
+
+ next = tmp->next;
+ if (g_ascii_strcasecmp(rec->chatnet, chatnet) == 0)
+ server_setup_remove(rec);
+ }
+}
+
void server_setup_remove(SERVER_SETUP_REC *rec)
{
server_setup_remove_config(rec);
@@ -523,7 +623,7 @@ static void read_servers(void)
static void read_settings(void)
{
if (old_source_host == NULL ||
- strcmp(old_source_host, settings_get_str("hostname")) != 0) {
+ g_strcmp0(old_source_host, settings_get_str("hostname")) != 0) {
g_free_not_null(old_source_host);
old_source_host = g_strdup(settings_get_str("hostname"));
diff --git a/src/core/servers-setup.h b/src/core/servers-setup.h
index f7601a68..e7ff7abf 100644
--- a/src/core/servers-setup.h
+++ b/src/core/servers-setup.h
@@ -39,6 +39,9 @@ SERVER_SETUP_REC *server_setup_find(const char *address, int port,
void server_setup_add(SERVER_SETUP_REC *rec);
void server_setup_remove(SERVER_SETUP_REC *rec);
+/* Remove servers attached to chatne */
+void server_setup_remove_chatnet(const char *chatnet);
+
void servers_setup_init(void);
void servers_setup_deinit(void);
diff --git a/src/core/servers.c b/src/core/servers.c
index 06f82d4d..b9faab81 100644
--- a/src/core/servers.c
+++ b/src/core/servers.c
@@ -167,7 +167,6 @@ static void server_connect_callback_init(SERVER_REC *server, GIOChannel *handle)
server_connect_finished(server);
}
-#ifdef HAVE_OPENSSL
static void server_connect_callback_init_ssl(SERVER_REC *server, GIOChannel *handle)
{
int error;
@@ -198,7 +197,6 @@ static void server_connect_callback_init_ssl(SERVER_REC *server, GIOChannel *han
server_connect_finished(server);
}
-#endif
static void server_real_connect(SERVER_REC *server, IPADDR *ip,
const char *unix_socket)
@@ -218,12 +216,10 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip,
return;
if (ip != NULL) {
- own_ip = ip == NULL ? NULL :
- (IPADDR_IS_V6(ip) ? server->connrec->own_ip6 :
- server->connrec->own_ip4);
+ own_ip = IPADDR_IS_V6(ip) ? server->connrec->own_ip6 : server->connrec->own_ip4;
port = server->connrec->proxy != NULL ?
server->connrec->proxy_port : server->connrec->port;
- handle = server->connrec->use_ssl ?
+ handle = server->connrec->use_tls ?
net_connect_ip_ssl(ip, port, own_ip, server) : net_connect_ip(ip, port, own_ip);
} else {
handle = net_connect_unix(unix_socket);
@@ -241,7 +237,7 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip,
}
server->no_reconnect = TRUE;
}
- if (server->connrec->use_ssl && errno == ENOSYS)
+ if (server->connrec->use_tls && errno == ENOSYS)
server->no_reconnect = TRUE;
server->connection_lost = TRUE;
@@ -249,11 +245,9 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip,
g_free(errmsg2);
} else {
server->handle = net_sendbuffer_create(handle, 0);
-#ifdef HAVE_OPENSSL
- if (server->connrec->use_ssl)
+ if (server->connrec->use_tls)
server_connect_callback_init_ssl(server, handle);
else
-#endif
server->connect_tag =
g_input_add(handle, G_INPUT_WRITE | G_INPUT_READ,
(GInputFunction)
@@ -628,21 +622,24 @@ void server_connect_unref(SERVER_CONNECT_REC *conn)
g_free_not_null(conn->own_ip4);
g_free_not_null(conn->own_ip6);
- g_free_not_null(conn->password);
- g_free_not_null(conn->nick);
- g_free_not_null(conn->username);
+ g_free_not_null(conn->password);
+ g_free_not_null(conn->nick);
+ g_free_not_null(conn->username);
g_free_not_null(conn->realname);
- g_free_not_null(conn->ssl_cert);
- g_free_not_null(conn->ssl_pkey);
- g_free_not_null(conn->ssl_pass);
- g_free_not_null(conn->ssl_cafile);
- g_free_not_null(conn->ssl_capath);
+ g_free_not_null(conn->tls_cert);
+ g_free_not_null(conn->tls_pkey);
+ g_free_not_null(conn->tls_pass);
+ g_free_not_null(conn->tls_cafile);
+ g_free_not_null(conn->tls_capath);
+ g_free_not_null(conn->tls_ciphers);
+ g_free_not_null(conn->tls_pinned_cert);
+ g_free_not_null(conn->tls_pinned_pubkey);
g_free_not_null(conn->channels);
- g_free_not_null(conn->away_reason);
+ g_free_not_null(conn->away_reason);
- conn->type = 0;
+ conn->type = 0;
g_free(conn);
}
@@ -685,21 +682,11 @@ SERVER_REC *cmd_options_get_server(const char *cmd,
SERVER_REC *defserver)
{
SERVER_REC *server;
- GSList *list, *tmp, *next;
+ GList *list;
/* get all the options, then remove the known ones. there should
be only one left - the server tag. */
- list = hashtable_get_keys(optlist);
- if (cmd != NULL) {
- for (tmp = list; tmp != NULL; tmp = next) {
- char *option = tmp->data;
- next = tmp->next;
-
- if (command_have_option(cmd, option))
- list = g_slist_remove(list, option);
- }
- }
-
+ list = optlist_remove_known(cmd, optlist);
if (list == NULL)
return defserver;
@@ -714,7 +701,7 @@ SERVER_REC *cmd_options_get_server(const char *cmd,
server = NULL;
}
- g_slist_free(list);
+ g_list_free(list);
return server;
}
diff --git a/src/core/servers.h b/src/core/servers.h
index d6afbdf5..f39c650b 100644
--- a/src/core/servers.h
+++ b/src/core/servers.h
@@ -18,7 +18,7 @@
(SERVER_CONNECT(conn) ? TRUE : FALSE)
#define server_ischannel(server, channel) \
- (server)->ischannel(server, channel)
+ ((server)->ischannel(server, channel))
/* all strings should be either NULL or dynamically allocated */
/* address and nick are mandatory, rest are optional */
diff --git a/src/core/session.c b/src/core/session.c
index b3002632..34190c52 100644
--- a/src/core/session.c
+++ b/src/core/session.c
@@ -92,7 +92,7 @@ static void cmd_upgrade(const char *data)
static void session_save_nick(CHANNEL_REC *channel, NICK_REC *nick,
CONFIG_REC *config, CONFIG_NODE *node)
{
- node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
+ node = config_node_section(config, node, NULL, NODE_TYPE_BLOCK);
config_node_set_str(config, node, "nick", nick->nick);
config_node_set_bool(config, node, "op", nick->op);
@@ -109,7 +109,7 @@ static void session_save_channel_nicks(CHANNEL_REC *channel, CONFIG_REC *config,
{
GSList *tmp, *nicks;
- node = config_node_section(node, "nicks", NODE_TYPE_LIST);
+ node = config_node_section(config, node, "nicks", NODE_TYPE_LIST);
nicks = nicklist_getnicks(channel);
for (tmp = nicks; tmp != NULL; tmp = tmp->next)
session_save_nick(channel, tmp->data, config, node);
@@ -119,7 +119,7 @@ static void session_save_channel_nicks(CHANNEL_REC *channel, CONFIG_REC *config,
static void session_save_channel(CHANNEL_REC *channel, CONFIG_REC *config,
CONFIG_NODE *node)
{
- node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
+ node = config_node_section(config, node, NULL, NODE_TYPE_BLOCK);
config_node_set_str(config, node, "name", channel->name);
config_node_set_str(config, node, "visible_name", channel->visible_name);
@@ -138,7 +138,7 @@ static void session_save_server_channels(SERVER_REC *server,
GSList *tmp;
/* save channels */
- node = config_node_section(node, "channels", NODE_TYPE_LIST);
+ node = config_node_section(config, node, "channels", NODE_TYPE_LIST);
for (tmp = server->channels; tmp != NULL; tmp = tmp->next)
session_save_channel(tmp->data, config, node);
}
@@ -148,10 +148,9 @@ static void session_save_server(SERVER_REC *server, CONFIG_REC *config,
{
int handle;
- node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
+ node = config_node_section(config, node, NULL, NODE_TYPE_BLOCK);
- config_node_set_str(config, node, "chat_type",
- chat_protocol_find_id(server->chat_type)->name);
+ config_node_set_str(config, node, "chat_type", chat_protocol_find_id(server->chat_type)->name);
config_node_set_str(config, node, "address", server->connrec->address);
config_node_set_int(config, node, "port", server->connrec->port);
config_node_set_str(config, node, "chatnet", server->connrec->chatnet);
@@ -159,12 +158,15 @@ static void session_save_server(SERVER_REC *server, CONFIG_REC *config,
config_node_set_str(config, node, "nick", server->nick);
config_node_set_str(config, node, "version", server->version);
- config_node_set_bool(config, node, "use_ssl", server->connrec->use_ssl);
- config_node_set_str(config, node, "ssl_cert", server->connrec->ssl_cert);
- config_node_set_str(config, node, "ssl_pkey", server->connrec->ssl_pkey);
- config_node_set_bool(config, node, "ssl_verify", server->connrec->ssl_verify);
- config_node_set_str(config, node, "ssl_cafile", server->connrec->ssl_cafile);
- config_node_set_str(config, node, "ssl_capath", server->connrec->ssl_capath);
+ config_node_set_bool(config, node, "use_tls", server->connrec->use_tls);
+ config_node_set_str(config, node, "tls_cert", server->connrec->tls_cert);
+ config_node_set_str(config, node, "tls_pkey", server->connrec->tls_pkey);
+ config_node_set_bool(config, node, "tls_verify", server->connrec->tls_verify);
+ config_node_set_str(config, node, "tls_cafile", server->connrec->tls_cafile);
+ config_node_set_str(config, node, "tls_capath", server->connrec->tls_capath);
+ config_node_set_str(config, node, "tls_ciphers", server->connrec->tls_ciphers);
+ config_node_set_str(config, node, "tls_pinned_cert", server->connrec->tls_pinned_cert);
+ config_node_set_str(config, node, "tls_pinned_pubkey", server->connrec->tls_pinned_pubkey);
handle = g_io_channel_unix_get_fd(net_sendbuffer_handle(server->handle));
config_node_set_int(config, node, "handle", handle);
@@ -187,7 +189,7 @@ static void session_restore_channel_nicks(CHANNEL_REC *channel,
GSList *tmp;
/* restore nicks */
- node = config_node_section(node, "nicks", -1);
+ node = config_node_section(NULL, node, "nicks", -1);
if (node != NULL && node->type == NODE_TYPE_LIST) {
tmp = config_node_first(node->value);
for (; tmp != NULL; tmp = config_node_next(tmp)) {
@@ -223,7 +225,7 @@ static void session_restore_server_channels(SERVER_REC *server,
GSList *tmp;
/* restore channels */
- node = config_node_section(node, "channels", -1);
+ node = config_node_section(NULL, node, "channels", -1);
if (node != NULL && node->type == NODE_TYPE_LIST) {
tmp = config_node_first(node->value);
for (; tmp != NULL; tmp = config_node_next(tmp))
diff --git a/src/core/settings.c b/src/core/settings.c
index 2296909e..4e0717cd 100644
--- a/src/core/settings.c
+++ b/src/core/settings.c
@@ -59,7 +59,7 @@ static SETTINGS_REC *settings_get(const char *key, SettingType type)
g_warning("settings_get(%s) : not found", key);
return NULL;
}
- if (type != -1 && rec->type != type) {
+ if (type != SETTING_TYPE_ANY && rec->type != type) {
g_warning("settings_get(%s) : invalid type", key);
return NULL;
}
@@ -77,7 +77,7 @@ settings_get_str_type(const char *key, SettingType type)
if (rec == NULL) return NULL;
node = iconfig_node_traverse("settings", FALSE);
- node = node == NULL ? NULL : config_node_section(node, rec->module, -1);
+ node = node == NULL ? NULL : iconfig_node_section(node, rec->module, -1);
return node == NULL ? rec->default_value.v_string :
config_node_get_str(node, key, rec->default_value.v_string);
@@ -85,7 +85,7 @@ settings_get_str_type(const char *key, SettingType type)
const char *settings_get_str(const char *key)
{
- return settings_get_str_type(key, -1);
+ return settings_get_str_type(key, SETTING_TYPE_ANY);
}
int settings_get_int(const char *key)
@@ -97,7 +97,7 @@ int settings_get_int(const char *key)
if (rec == NULL) return 0;
node = iconfig_node_traverse("settings", FALSE);
- node = node == NULL ? NULL : config_node_section(node, rec->module, -1);
+ node = node == NULL ? NULL : iconfig_node_section(node, rec->module, -1);
return node == NULL ? rec->default_value.v_int :
config_node_get_int(node, key, rec->default_value.v_int);
@@ -112,7 +112,7 @@ int settings_get_bool(const char *key)
if (rec == NULL) return FALSE;
node = iconfig_node_traverse("settings", FALSE);
- node = node == NULL ? NULL : config_node_section(node, rec->module, -1);
+ node = node == NULL ? NULL : iconfig_node_section(node, rec->module, -1);
return node == NULL ? rec->default_value.v_bool :
config_node_get_bool(node, key, rec->default_value.v_bool);
@@ -148,11 +148,36 @@ int settings_get_size(const char *key)
return str == NULL ? 0 : bytes;
}
+int settings_get_choice(const char *key)
+{
+ SETTINGS_REC *rec;
+ CONFIG_NODE *node;
+ char *str;
+ int index;
+
+ rec = settings_get(key, SETTING_TYPE_CHOICE);
+ if (rec == NULL) return -1;
+
+ node = iconfig_node_traverse("settings", FALSE);
+ node = node == NULL ? NULL : iconfig_node_section(node, rec->module, -1);
+
+ str = node == NULL ? rec->default_value.v_string :
+ config_node_get_str(node, key, rec->default_value.v_string);
+
+ if (str == NULL || (index = strarray_find(rec->choices, str)) < 0)
+ return rec->default_value.v_int;
+
+ return index;
+}
+
char *settings_get_print(SETTINGS_REC *rec)
{
char *value = NULL;
switch(rec->type) {
+ case SETTING_TYPE_CHOICE:
+ value = g_strdup(rec->choices[settings_get_choice(rec->key)]);
+ break;
case SETTING_TYPE_BOOLEAN:
value = g_strdup(settings_get_bool(rec->key) ? "ON" : "OFF");
break;
@@ -163,6 +188,7 @@ char *settings_get_print(SETTINGS_REC *rec)
case SETTING_TYPE_TIME:
case SETTING_TYPE_LEVEL:
case SETTING_TYPE_SIZE:
+ case SETTING_TYPE_ANY:
value = g_strdup(settings_get_str(rec->key));
break;
}
@@ -171,13 +197,31 @@ char *settings_get_print(SETTINGS_REC *rec)
static void settings_add(const char *module, const char *section,
const char *key, SettingType type,
- const SettingValue *default_value)
+ const SettingValue *default_value,
+ const char *choices)
{
SETTINGS_REC *rec;
+ char **choices_vec = NULL;
g_return_if_fail(key != NULL);
g_return_if_fail(section != NULL);
+ if (type == SETTING_TYPE_CHOICE) {
+ if (choices == NULL) {
+ g_warning("Trying to add setting '%s' with no choices.", key);
+ return;
+ }
+
+ choices_vec = g_strsplit(choices, ";", -1);
+
+ /* validate the default value */
+ if (default_value->v_int < 0 || default_value->v_int >= g_strv_length(choices_vec)) {
+ g_warning("Trying to add setting '%s' with an invalid default value.", key);
+ g_strfreev(choices_vec);
+ return;
+ }
+ }
+
rec = g_hash_table_lookup(settings, key);
if (rec != NULL) {
/* Already exists, make sure it's correct type */
@@ -196,6 +240,7 @@ static void settings_add(const char *module, const char *section,
rec->type = type;
rec->default_value = *default_value;
+ rec->choices = choices_vec;
g_hash_table_insert(settings, rec->key, rec);
}
}
@@ -207,7 +252,17 @@ void settings_add_str_module(const char *module, const char *section,
memset(&default_value, 0, sizeof(default_value));
default_value.v_string = g_strdup(def);
- settings_add(module, section, key, SETTING_TYPE_STRING, &default_value);
+ settings_add(module, section, key, SETTING_TYPE_STRING, &default_value, NULL);
+}
+
+void settings_add_choice_module(const char *module, const char *section,
+ const char *key, int def, const char *choices)
+{
+ SettingValue default_value;
+
+ memset(&default_value, 0, sizeof(default_value));
+ default_value.v_int = def;
+ settings_add(module, section, key, SETTING_TYPE_CHOICE, &default_value, choices);
}
void settings_add_int_module(const char *module, const char *section,
@@ -217,7 +272,7 @@ void settings_add_int_module(const char *module, const char *section,
memset(&default_value, 0, sizeof(default_value));
default_value.v_int = def;
- settings_add(module, section, key, SETTING_TYPE_INT, &default_value);
+ settings_add(module, section, key, SETTING_TYPE_INT, &default_value, NULL);
}
void settings_add_bool_module(const char *module, const char *section,
@@ -227,8 +282,7 @@ void settings_add_bool_module(const char *module, const char *section,
memset(&default_value, 0, sizeof(default_value));
default_value.v_bool = def;
- settings_add(module, section, key, SETTING_TYPE_BOOLEAN,
- &default_value);
+ settings_add(module, section, key, SETTING_TYPE_BOOLEAN, &default_value, NULL);
}
void settings_add_time_module(const char *module, const char *section,
@@ -238,7 +292,7 @@ void settings_add_time_module(const char *module, const char *section,
memset(&default_value, 0, sizeof(default_value));
default_value.v_string = g_strdup(def);
- settings_add(module, section, key, SETTING_TYPE_TIME, &default_value);
+ settings_add(module, section, key, SETTING_TYPE_TIME, &default_value, NULL);
}
void settings_add_level_module(const char *module, const char *section,
@@ -248,7 +302,7 @@ void settings_add_level_module(const char *module, const char *section,
memset(&default_value, 0, sizeof(default_value));
default_value.v_string = g_strdup(def);
- settings_add(module, section, key, SETTING_TYPE_LEVEL, &default_value);
+ settings_add(module, section, key, SETTING_TYPE_LEVEL, &default_value, NULL);
}
void settings_add_size_module(const char *module, const char *section,
@@ -258,14 +312,16 @@ void settings_add_size_module(const char *module, const char *section,
memset(&default_value, 0, sizeof(default_value));
default_value.v_string = g_strdup(def);
- settings_add(module, section, key, SETTING_TYPE_SIZE, &default_value);
+ settings_add(module, section, key, SETTING_TYPE_SIZE, &default_value, NULL);
}
static void settings_destroy(SETTINGS_REC *rec)
{
if (rec->type != SETTING_TYPE_INT &&
- rec->type != SETTING_TYPE_BOOLEAN)
+ rec->type != SETTING_TYPE_BOOLEAN &&
+ rec->type != SETTING_TYPE_CHOICE)
g_free(rec->default_value.v_string);
+ g_strfreev(rec->choices);
g_free(rec->module);
g_free(rec->section);
g_free(rec->key);
@@ -295,7 +351,7 @@ void settings_remove(const char *key)
static int settings_remove_hash(const char *key, SETTINGS_REC *rec,
const char *module)
{
- if (strcmp(rec->module, module) == 0) {
+ if (g_strcmp0(rec->module, module) == 0) {
settings_unref(rec, FALSE);
return TRUE;
}
@@ -324,7 +380,21 @@ static CONFIG_NODE *settings_get_node(const char *key)
}
node = iconfig_node_traverse("settings", TRUE);
- return config_node_section(node, rec->module, NODE_TYPE_BLOCK);
+ return iconfig_node_section(node, rec->module, NODE_TYPE_BLOCK);
+}
+
+gboolean settings_set_choice(const char *key, const char *value)
+{
+ SETTINGS_REC *rec;
+
+ rec = settings_get_record(key);
+
+ if (rec != NULL && strarray_find(rec->choices, value) < 0)
+ return FALSE;
+
+ settings_set_str(key, value);
+
+ return TRUE;
}
void settings_set_str(const char *key, const char *value)
@@ -342,7 +412,7 @@ void settings_set_bool(const char *key, int value)
iconfig_node_set_bool(settings_get_node(key), key, value);
}
-int settings_set_time(const char *key, const char *value)
+gboolean settings_set_time(const char *key, const char *value)
{
int msecs;
@@ -353,7 +423,7 @@ int settings_set_time(const char *key, const char *value)
return TRUE;
}
-int settings_set_level(const char *key, const char *value)
+gboolean settings_set_level(const char *key, const char *value)
{
int iserror;
@@ -365,7 +435,7 @@ int settings_set_level(const char *key, const char *value)
return TRUE;
}
-int settings_set_size(const char *key, const char *value)
+gboolean settings_set_size(const char *key, const char *value)
{
int size;
@@ -380,10 +450,10 @@ SettingType settings_get_type(const char *key)
{
SETTINGS_REC *rec;
- g_return_val_if_fail(key != NULL, -1);
+ g_return_val_if_fail(key != NULL, SETTING_TYPE_ANY);
rec = g_hash_table_lookup(settings, key);
- return rec == NULL ? -1 : rec->type;
+ return rec == NULL ? SETTING_TYPE_ANY : rec->type;
}
/* Get the record of the setting */
@@ -420,7 +490,7 @@ static void settings_clean_invalid_module(const char *module)
node = iconfig_node_traverse("settings", FALSE);
if (node == NULL) return;
- node = config_node_section(node, module, -1);
+ node = iconfig_node_section(node, module, -1);
if (node == NULL) return;
for (tmp = config_node_first(node->value); tmp != NULL; tmp = next) {
@@ -428,7 +498,7 @@ static void settings_clean_invalid_module(const char *module)
next = config_node_next(tmp);
set = g_hash_table_lookup(settings, subnode->key);
- if (set == NULL || strcmp(set->module, module) != 0)
+ if (set == NULL || g_strcmp0(set->module, module) != 0)
iconfig_node_remove(node, subnode);
}
}
@@ -458,7 +528,7 @@ static int backwards_compatibility(const char *module, CONFIG_NODE *node,
new_value = NULL; new_key = NULL; new_module = NULL;
/* fe-text term_type -> fe-common/core term_charset - for 0.8.10-> */
- if (strcmp(module, "fe-text") == 0) {
+ if (g_strcmp0(module, "fe-text") == 0) {
if (g_ascii_strcasecmp(node->key, "term_type") == 0 ||
/* kludge for cvs-version where term_charset was in fe-text */
g_ascii_strcasecmp(node->key, "term_charset") == 0) {
@@ -468,7 +538,7 @@ static int backwards_compatibility(const char *module, CONFIG_NODE *node,
g_strdup(node->value);
new_node = iconfig_node_traverse("settings", FALSE);
new_node = new_node == NULL ? NULL :
- config_node_section(new_node, new_module, -1);
+ iconfig_node_section(new_node, new_module, -1);
config_node_set_str(mainconfig, new_node,
new_key, new_value);
@@ -502,7 +572,7 @@ void settings_check_module(const char *module)
g_return_if_fail(module != NULL);
node = iconfig_node_traverse("settings", FALSE);
- node = node == NULL ? NULL : config_node_section(node, module, -1);
+ node = node == NULL ? NULL : iconfig_node_section(node, module, -1);
if (node == NULL) return;
errors = g_string_new(NULL);
@@ -515,12 +585,13 @@ void settings_check_module(const char *module)
for (; tmp != NULL; tmp = next) {
node = tmp->data;
next = config_node_next(tmp);
+ if (node->key == NULL) continue;
set = g_hash_table_lookup(settings, node->key);
if (backwards_compatibility(module, node, parent))
continue;
- if (set == NULL || strcmp(set->module, module) != 0) {
+ if (set == NULL || g_strcmp0(set->module, module) != 0) {
g_string_append_printf(errors, " %s", node->key);
count++;
}
@@ -548,9 +619,9 @@ void settings_check_module(const char *module)
static int settings_compare(SETTINGS_REC *v1, SETTINGS_REC *v2)
{
- int cmp = strcmp(v1->section, v2->section);
+ int cmp = g_strcmp0(v1->section, v2->section);
if (!cmp)
- cmp = strcmp(v1->key, v2->key);
+ cmp = g_strcmp0(v1->key, v2->key);
return cmp;
}
@@ -682,7 +753,7 @@ static void init_configfile(void)
if (stat(get_irssi_dir(), &statbuf) != 0) {
/* ~/.irssi not found, create it. */
- if (mkpath(get_irssi_dir(), 0700) != 0) {
+ if (g_mkdir_with_parents(get_irssi_dir(), 0700) != 0) {
g_error("Couldn't create %s directory", get_irssi_dir());
}
} else if (!S_ISDIR(statbuf.st_mode)) {
diff --git a/src/core/settings.h b/src/core/settings.h
index f8d9f68f..d174f250 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -7,7 +7,9 @@ typedef enum {
SETTING_TYPE_BOOLEAN,
SETTING_TYPE_TIME,
SETTING_TYPE_LEVEL,
- SETTING_TYPE_SIZE
+ SETTING_TYPE_SIZE,
+ SETTING_TYPE_CHOICE,
+ SETTING_TYPE_ANY
} SettingType;
typedef struct {
@@ -25,6 +27,7 @@ typedef struct {
SettingType type;
SettingValue default_value;
+ char **choices;
} SETTINGS_REC;
/* macros for handling the default Irssi configuration */
@@ -36,6 +39,8 @@ typedef struct {
#define iconfig_set_int(a, b, c) config_set_int(mainconfig, a, b, c)
#define iconfig_set_bool(a, b, c) config_set_bool(mainconfig, a, b, c)
+#define iconfig_node_section(a, b, c) config_node_section(mainconfig, a, b, c)
+#define iconfig_node_section_index(a, b, c, d) config_node_section_index(mainconfig, a, b, c, d)
#define iconfig_node_traverse(a, b) config_node_traverse(mainconfig, a, b)
#define iconfig_node_set_str(a, b, c) config_node_set_str(mainconfig, a, b, c)
#define iconfig_node_set_int(a, b, c) config_node_set_int(mainconfig, a, b, c)
@@ -55,6 +60,7 @@ int settings_get_bool(const char *key);
int settings_get_time(const char *key); /* as milliseconds */
int settings_get_level(const char *key);
int settings_get_size(const char *key); /* as bytes */
+int settings_get_choice(const char *key);
char *settings_get_print(SETTINGS_REC *rec);
/* Functions to add/remove settings */
@@ -70,6 +76,8 @@ void settings_add_level_module(const char *module, const char *section,
const char *key, const char *def);
void settings_add_size_module(const char *module, const char *section,
const char *key, const char *def);
+void settings_add_choice_module(const char *module, const char *section,
+ const char *key, int def, const char *choices);
void settings_remove(const char *key);
void settings_remove_module(const char *module);
@@ -85,13 +93,16 @@ void settings_remove_module(const char *module);
settings_add_level_module(MODULE_NAME, section, key, def)
#define settings_add_size(section, key, def) \
settings_add_size_module(MODULE_NAME, section, key, def)
+#define settings_add_choice(section, key, def, choices) \
+ settings_add_choice_module(MODULE_NAME, section, key, def, choices)
void settings_set_str(const char *key, const char *value);
void settings_set_int(const char *key, int value);
void settings_set_bool(const char *key, int value);
-int settings_set_time(const char *key, const char *value);
-int settings_set_level(const char *key, const char *value);
-int settings_set_size(const char *key, const char *value);
+gboolean settings_set_time(const char *key, const char *value);
+gboolean settings_set_level(const char *key, const char *value);
+gboolean settings_set_size(const char *key, const char *value);
+gboolean settings_set_choice(const char *key, const char *value);
/* Get the type (SETTING_TYPE_xxx) of `key' */
SettingType settings_get_type(const char *key);
diff --git a/src/core/special-vars.c b/src/core/special-vars.c
index 4dcc3d2f..6ca080fc 100644
--- a/src/core/special-vars.c
+++ b/src/core/special-vars.c
@@ -25,10 +25,7 @@
#include "settings.h"
#include "servers.h"
#include "misc.h"
-
-#define ALIGN_RIGHT 0x01
-#define ALIGN_CUT 0x02
-#define ALIGN_PAD 0x04
+#include "utf8.h"
#define isvarchar(c) \
(i_isalnum(c) || (c) == '_')
@@ -47,7 +44,7 @@ static char *get_argument(char **cmd, char **arglist)
arg = 0;
max = -1;
- argcount = arglist == NULL ? 0 : strarray_length(arglist);
+ argcount = arglist == NULL ? 0 : g_strv_length(arglist);
if (**cmd == '*') {
/* get all arguments */
@@ -316,22 +313,28 @@ static int get_alignment_args(char **data, int *align, int *flags, char *pad)
}
/* return the aligned text */
-static char *get_alignment(const char *text, int align, int flags, char pad)
+char *get_alignment(const char *text, int align, int flags, char pad)
{
GString *str;
char *ret;
+ int policy;
+ unsigned int cut_bytes;
g_return_val_if_fail(text != NULL, NULL);
+ policy = string_policy(text);
+
str = g_string_new(text);
/* cut */
- if ((flags & ALIGN_CUT) && align > 0 && str->len > align)
- g_string_truncate(str, align);
+ if ((flags & ALIGN_CUT) && align > 0 && string_width(text, policy) > align) {
+ string_chars_for_width(text, policy, align, &cut_bytes);
+ g_string_truncate(str, cut_bytes);
+ }
/* add pad characters */
if (flags & ALIGN_PAD) {
- while (str->len < align) {
+ while (string_width(str->str, policy) < align) {
if (flags & ALIGN_RIGHT)
g_string_prepend_c(str, pad);
else
@@ -340,7 +343,7 @@ static char *get_alignment(const char *text, int align, int flags, char pad)
}
ret = str->str;
- g_string_free(str, FALSE);
+ g_string_free(str, FALSE);
return ret;
}
diff --git a/src/core/special-vars.h b/src/core/special-vars.h
index 11262dad..300dae0e 100644
--- a/src/core/special-vars.h
+++ b/src/core/special-vars.h
@@ -9,9 +9,16 @@
#define PARSE_FLAG_ESCAPE_THEME 0x08 /* if any arguments/variables contain { or } chars, escape them with % */
#define PARSE_FLAG_ONLY_ARGS 0x10 /* expand only arguments ($0 $1 etc.) but no other $variables */
+#define ALIGN_RIGHT 0x01
+#define ALIGN_CUT 0x02
+#define ALIGN_PAD 0x04
+
typedef char* (*SPECIAL_HISTORY_FUNC)
(const char *text, void *item, int *free_ret);
+/* Cut and/or pad text so it takes exactly "align" characters on the screen */
+char *get_alignment(const char *text, int align, int flags, char pad);
+
/* Parse and expand text after '$' character. return value has to be
g_free()'d if `free_ret' is TRUE. */
char *parse_special(char **cmd, SERVER_REC *server, void *item,
diff --git a/src/core/tls.c b/src/core/tls.c
new file mode 100644
index 00000000..3bddd773
--- /dev/null
+++ b/src/core/tls.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2015 Alexander Færøy <ahf@irssi.org>
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301,USA
+ */
+
+#include "module.h"
+
+#include "tls.h"
+
+TLS_REC *tls_create_rec()
+{
+ TLS_REC *rec = g_new0(TLS_REC, 1);
+ g_return_val_if_fail(rec != NULL, NULL);
+
+ return rec;
+}
+
+void tls_rec_free(TLS_REC *tls_rec)
+{
+ if (tls_rec == NULL)
+ return;
+
+ g_free_and_null(tls_rec->protocol_version);
+ g_free_and_null(tls_rec->cipher);
+ g_free_and_null(tls_rec->public_key_algorithm);
+ g_free_and_null(tls_rec->public_key_fingerprint);
+ g_free_and_null(tls_rec->public_key_fingerprint_algorithm);
+ g_free_and_null(tls_rec->certificate_fingerprint);
+ g_free_and_null(tls_rec->certificate_fingerprint_algorithm);
+ g_free_and_null(tls_rec->not_after);
+ g_free_and_null(tls_rec->not_before);
+ g_free_and_null(tls_rec->ephemeral_key_algorithm);
+
+ if (tls_rec->certs != NULL) {
+ g_slist_foreach(tls_rec->certs, (GFunc)tls_cert_rec_free, NULL);
+ g_slist_free(tls_rec->certs);
+ tls_rec->certs = NULL;
+ }
+
+ g_free(tls_rec);
+}
+
+void tls_rec_set_protocol_version(TLS_REC *tls_rec, const char *protocol_version)
+{
+ g_return_if_fail(tls_rec != NULL);
+
+ tls_rec->protocol_version = g_strdup(protocol_version);
+}
+
+void tls_rec_set_cipher(TLS_REC *tls_rec, const char *cipher)
+{
+ g_return_if_fail(tls_rec != NULL);
+
+ tls_rec->cipher = g_strdup(cipher);
+}
+
+void tls_rec_set_cipher_size(TLS_REC *tls_rec, size_t size)
+{
+ g_return_if_fail(tls_rec != NULL);
+
+ tls_rec->cipher_size = size;
+}
+
+void tls_rec_set_public_key_algorithm(TLS_REC *tls_rec, const char *algorithm)
+{
+ g_return_if_fail(tls_rec != NULL);
+
+ tls_rec->public_key_algorithm = g_strdup(algorithm);
+}
+
+void tls_rec_set_public_key_fingerprint(TLS_REC *tls_rec, const char *fingerprint)
+{
+ g_return_if_fail(tls_rec != NULL);
+
+ tls_rec->public_key_fingerprint = g_strdup(fingerprint);
+}
+
+void tls_rec_set_public_key_fingerprint_algorithm(TLS_REC *tls_rec, const char *algorithm)
+{
+ g_return_if_fail(tls_rec != NULL);
+
+ tls_rec->public_key_fingerprint_algorithm = g_strdup(algorithm);
+}
+
+void tls_rec_set_public_key_size(TLS_REC *tls_rec, size_t size)
+{
+ g_return_if_fail(tls_rec != NULL);
+ tls_rec->public_key_size = size;
+}
+
+void tls_rec_set_certificate_fingerprint(TLS_REC *tls_rec, const char *fingerprint)
+{
+ g_return_if_fail(tls_rec != NULL);
+
+ tls_rec->certificate_fingerprint = g_strdup(fingerprint);
+}
+
+void tls_rec_set_certificate_fingerprint_algorithm(TLS_REC *tls_rec, const char *algorithm)
+{
+ g_return_if_fail(tls_rec != NULL);
+
+ tls_rec->certificate_fingerprint_algorithm = g_strdup(algorithm);
+}
+
+void tls_rec_set_not_after(TLS_REC *tls_rec, const char *not_after)
+{
+ g_return_if_fail(tls_rec != NULL);
+ tls_rec->not_after = g_strdup(not_after);
+}
+
+void tls_rec_set_not_before(TLS_REC *tls_rec, const char *not_before)
+{
+ g_return_if_fail(tls_rec != NULL);
+ tls_rec->not_before = g_strdup(not_before);
+}
+
+void tls_rec_set_ephemeral_key_algorithm(TLS_REC *tls_rec, const char *algorithm)
+{
+ g_return_if_fail(tls_rec != NULL);
+ tls_rec->ephemeral_key_algorithm = g_strdup(algorithm);
+}
+
+void tls_rec_set_ephemeral_key_size(TLS_REC *tls_rec, size_t size)
+{
+ g_return_if_fail(tls_rec != NULL);
+ tls_rec->ephemeral_key_size = size;
+}
+
+void tls_rec_append_cert(TLS_REC *tls_rec, TLS_CERT_REC *tls_cert_rec)
+{
+ g_return_if_fail(tls_rec != NULL);
+ g_return_if_fail(tls_cert_rec != NULL);
+
+ tls_rec->certs = g_slist_append(tls_rec->certs, tls_cert_rec);
+}
+
+TLS_CERT_REC *tls_cert_create_rec()
+{
+ TLS_CERT_REC *rec = g_new0(TLS_CERT_REC, 1);
+ g_return_val_if_fail(rec != NULL, NULL);
+
+ return rec;
+}
+
+void tls_cert_rec_append_subject_entry(TLS_CERT_REC *tls_cert_rec, TLS_CERT_ENTRY_REC *tls_cert_entry_rec)
+{
+ g_return_if_fail(tls_cert_rec != NULL);
+ g_return_if_fail(tls_cert_entry_rec != NULL);
+
+ tls_cert_rec->subject = g_slist_append(tls_cert_rec->subject, tls_cert_entry_rec);
+}
+
+void tls_cert_rec_append_issuer_entry(TLS_CERT_REC *tls_cert_rec, TLS_CERT_ENTRY_REC *tls_cert_entry_rec)
+{
+ g_return_if_fail(tls_cert_rec != NULL);
+ g_return_if_fail(tls_cert_entry_rec != NULL);
+
+ tls_cert_rec->issuer = g_slist_append(tls_cert_rec->issuer, tls_cert_entry_rec);
+}
+
+void tls_cert_rec_free(TLS_CERT_REC *tls_cert_rec)
+{
+ if (tls_cert_rec == NULL)
+ return;
+
+ if (tls_cert_rec->subject != NULL) {
+ g_slist_foreach(tls_cert_rec->subject, (GFunc)tls_cert_entry_rec_free, NULL);
+ g_slist_free(tls_cert_rec->subject);
+ tls_cert_rec->subject = NULL;
+ }
+
+ if (tls_cert_rec->issuer != NULL) {
+ g_slist_foreach(tls_cert_rec->issuer, (GFunc)tls_cert_entry_rec_free, NULL);
+ g_slist_free(tls_cert_rec->issuer);
+ tls_cert_rec->issuer = NULL;
+ }
+
+ g_free(tls_cert_rec);
+}
+
+TLS_CERT_ENTRY_REC *tls_cert_entry_create_rec(const char *name, const char *value)
+{
+ TLS_CERT_ENTRY_REC *rec = g_new0(TLS_CERT_ENTRY_REC, 1);
+ g_return_val_if_fail(rec != NULL, NULL);
+
+ rec->name = g_strdup(name);
+ rec->value = g_strdup(value);
+
+ return rec;
+}
+
+void tls_cert_entry_rec_free(TLS_CERT_ENTRY_REC *tls_cert_entry)
+{
+ if (tls_cert_entry == NULL)
+ return;
+
+ g_free_and_null(tls_cert_entry->name);
+ g_free_and_null(tls_cert_entry->value);
+
+ g_free(tls_cert_entry);
+}
diff --git a/src/core/tls.h b/src/core/tls.h
new file mode 100644
index 00000000..9ba4ac47
--- /dev/null
+++ b/src/core/tls.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2015 Alexander Færøy <ahf@irssi.org>
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301,USA
+ */
+
+#ifndef __TLS_H
+#define __TLS_H
+
+#include <openssl/ssl.h>
+
+#include <stdbool.h>
+
+typedef struct _TLS_REC TLS_REC;
+typedef struct _TLS_CERT_REC TLS_CERT_REC;
+typedef struct _TLS_CERT_ENTRY_REC TLS_CERT_ENTRY_REC;
+
+struct _TLS_REC {
+ char *protocol_version;
+ char *cipher;
+ size_t cipher_size;
+
+ char *public_key_algorithm;
+ char *public_key_fingerprint;
+ char *public_key_fingerprint_algorithm;
+ size_t public_key_size;
+
+ char *certificate_fingerprint;
+ char *certificate_fingerprint_algorithm;
+
+ char *not_after;
+ char *not_before;
+
+ char *ephemeral_key_algorithm;
+ size_t ephemeral_key_size;
+
+ GSList *certs;
+};
+
+struct _TLS_CERT_REC {
+ GSList *subject;
+ GSList *issuer;
+};
+
+struct _TLS_CERT_ENTRY_REC {
+ char *name;
+ char *value;
+};
+
+TLS_REC *tls_create_rec();
+void tls_rec_free(TLS_REC *tls_rec);
+
+void tls_rec_set_protocol_version(TLS_REC *tls_rec, const char *protocol_version);
+void tls_rec_set_cipher(TLS_REC *tls_rec, const char *cipher);
+void tls_rec_set_cipher_size(TLS_REC *tls_rec, size_t size);
+void tls_rec_set_public_key_algorithm(TLS_REC *tls_rec, const char *algorithm);
+void tls_rec_set_public_key_fingerprint(TLS_REC *tls_rec, const char *fingerprint);
+void tls_rec_set_public_key_fingerprint_algorithm(TLS_REC *tls_rec, const char *algorithm);
+void tls_rec_set_public_key_size(TLS_REC *tls_rec, size_t size);
+void tls_rec_set_certificate_fingerprint(TLS_REC *tls_rec, const char *fingerprint);
+void tls_rec_set_certificate_fingerprint_algorithm(TLS_REC *tls_rec, const char *algorithm);
+void tls_rec_set_not_after(TLS_REC *tls_rec, const char *not_after);
+void tls_rec_set_not_before(TLS_REC *tls_rec, const char *not_before);
+void tls_rec_set_ephemeral_key_algorithm(TLS_REC *tls_rec, const char *algorithm);
+void tls_rec_set_ephemeral_key_size(TLS_REC *tls_rec, size_t size);
+
+void tls_rec_append_cert(TLS_REC *tls_rec, TLS_CERT_REC *tls_cert_rec);
+
+TLS_CERT_REC *tls_cert_create_rec();
+void tls_cert_rec_free(TLS_CERT_REC *tls_cert_rec);
+
+void tls_cert_rec_append_subject_entry(TLS_CERT_REC *tls_cert_rec, TLS_CERT_ENTRY_REC *tls_cert_entry_rec);
+void tls_cert_rec_append_issuer_entry(TLS_CERT_REC *tls_cert_rec, TLS_CERT_ENTRY_REC *tls_cert_entry_rec);
+
+TLS_CERT_ENTRY_REC *tls_cert_entry_create_rec(const char *name, const char *value);
+void tls_cert_entry_rec_free(TLS_CERT_ENTRY_REC *tls_cert_entry);
+
+#endif
diff --git a/src/core/utf8.c b/src/core/utf8.c
new file mode 100644
index 00000000..c53d8816
--- /dev/null
+++ b/src/core/utf8.c
@@ -0,0 +1,135 @@
+/* utf8.c - Operations on UTF-8 strings.
+ *
+ * Copyright (C) 2002 Timo Sirainen
+ *
+ * Based on GLib code by
+ *
+ * Copyright (C) 1999 Tom Tromey
+ * Copyright (C) 2000 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "utf8.h"
+#include "module.h"
+
+/* Provide is_utf8(): */
+#include "recode.h"
+
+int string_advance(char const **str, int policy)
+{
+ if (policy == TREAT_STRING_AS_UTF8) {
+ gunichar c;
+
+ c = g_utf8_get_char(*str);
+ *str = g_utf8_next_char(*str);
+
+ return unichar_isprint(c) ? mk_wcwidth(c) : 1;
+ } else {
+ /* Assume TREAT_STRING_AS_BYTES: */
+ *str += 1;
+
+ return 1;
+ }
+}
+
+int string_policy(const char *str)
+{
+ if (is_utf8()) {
+ if (str == NULL || g_utf8_validate(str, -1, NULL)) {
+ /* No string provided or valid UTF-8 string: treat as UTF-8: */
+ return TREAT_STRING_AS_UTF8;
+ }
+ }
+ return TREAT_STRING_AS_BYTES;
+}
+
+int string_length(const char *str, int policy)
+{
+ g_return_val_if_fail(str != NULL, 0);
+
+ if (policy == -1) {
+ policy = string_policy(str);
+ }
+
+ if (policy == TREAT_STRING_AS_UTF8) {
+ return g_utf8_strlen(str, -1);
+ }
+ else {
+ /* Assume TREAT_STRING_AS_BYTES: */
+ return strlen(str);
+ }
+}
+
+int string_width(const char *str, int policy)
+{
+ int len;
+
+ g_return_val_if_fail(str != NULL, 0);
+
+ if (policy == -1) {
+ policy = string_policy(str);
+ }
+
+ len = 0;
+ while (*str != '\0') {
+ len += string_advance(&str, policy);
+ }
+ return len;
+}
+
+int string_chars_for_width(const char *str, int policy, unsigned int n, unsigned int *bytes)
+{
+ const char *c, *previous_c;
+ int str_width, char_width, char_count;
+
+ g_return_val_if_fail(str != NULL, -1);
+
+ /* Handle the dummy case where n is 0: */
+ if (n == 0) {
+ if (bytes != NULL) {
+ *bytes = 0;
+ }
+ return 0;
+ }
+
+ if (policy == -1) {
+ policy = string_policy(str);
+ }
+
+ /* Iterate over characters until we reach n: */
+ char_count = 0;
+ str_width = 0;
+ c = str;
+ while (*c != '\0') {
+ previous_c = c;
+ char_width = string_advance(&c, policy);
+ if (str_width + char_width > n) {
+ /* We stepped beyond n, get one step back and stop there: */
+ c = previous_c;
+ break;
+ }
+ ++ char_count;
+ str_width += char_width;
+ }
+ /* At this point, we know that char_count characters reach str_width
+ * columns, which is less than or equal to n. */
+
+ /* Optionally provide the equivalent amount of bytes: */
+ if (bytes != NULL) {
+ *bytes = c - str;
+ }
+ return char_count;
+}
diff --git a/src/core/utf8.h b/src/core/utf8.h
new file mode 100644
index 00000000..5bb53193
--- /dev/null
+++ b/src/core/utf8.h
@@ -0,0 +1,56 @@
+#ifndef __UTF8_H
+#define __UTF8_H
+
+/* XXX I didn't check the encoding range of big5+. This is standard big5. */
+#define is_big5_los(lo) (0x40 <= (lo) && (lo) <= 0x7E) /* standard */
+#define is_big5_lox(lo) (0x80 <= (lo) && (lo) <= 0xFE) /* extended */
+#define is_big5_lo(lo) ((is_big5_los(lo) || is_big5_lox(lo)))
+#define is_big5_hi(hi) (0x81 <= (hi) && (hi) <= 0xFE)
+#define is_big5(hi,lo) (is_big5_hi(hi) && is_big5_lo(lo))
+
+#include <glib.h>
+typedef guint32 unichar;
+
+/* Returns width for character (0-2). */
+int mk_wcwidth(unichar c);
+
+/* Advance the str pointer one character further; return the number of columns
+ * occupied by the skipped character.
+ */
+int string_advance(char const **str, int policy);
+
+/* TREAT_STRING_AS_BYTES means strings are to be treated using strncpy,
+ * strnlen, etc.
+ * TREAT_STRING_AS_UTF8 means strings are to be treated using g_utf8_*
+ * functions.
+ */
+enum str_policy {
+ TREAT_STRING_AS_BYTES,
+ TREAT_STRING_AS_UTF8
+};
+
+/* Return how the str string ought to be treated: TREAT_STRING_AS_UTF8 if the
+ * terminal handles UTF-8 and if the string appears to be a valid UTF-8 string;
+ * TREAT_STRING_AS_BYTES otherwise.
+ */
+int string_policy(const char *str);
+
+/* Return the length of the str string according to the given policy; if policy
+ * is -1, this function will call string_policy().
+ */
+int string_length(const char *str, int policy);
+/* Return the screen width of the str string according to the given policy; if
+ * policy is -1, this function will call string_policy().
+ */
+int string_width(const char *str, int policy);
+
+/* Return the amount of characters from str it takes to reach n columns, or -1 if
+ * str is NULL. Optionally return the equivalent amount of bytes.
+ * If policy is -1, this function will call string_policy().
+ */
+int string_chars_for_width(const char *str, int policy, unsigned int n, unsigned int *bytes);
+
+#define unichar_isprint(c) (((c) & ~0x80) >= 32)
+#define is_utf8_leading(c) (((c) & 0xc0) != 0x80)
+
+#endif
diff --git a/src/fe-common/core/wcwidth.c b/src/core/wcwidth.c
index 4ebfaca9..711c4646 100644
--- a/src/fe-common/core/wcwidth.c
+++ b/src/core/wcwidth.c
@@ -59,7 +59,7 @@
* Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
*/
-#include "module.h"
+#include "utf8.h"
struct interval {
int first;
@@ -187,7 +187,7 @@ int mk_wcwidth(unichar ucs)
/* if we arrive here, ucs is not a combining or C0/C1 control character */
- return 1 +
+ return 1 +
(ucs >= 0x1100 &&
(ucs <= 0x115f || /* Hangul Jamo init. consonants */
ucs == 0x2329 || ucs == 0x232a ||
diff --git a/src/fe-common/core/Makefile.am b/src/fe-common/core/Makefile.am
index e755b510..6efff411 100644
--- a/src/fe-common/core/Makefile.am
+++ b/src/fe-common/core/Makefile.am
@@ -24,8 +24,7 @@ libfe_common_core_a_SOURCES = \
fe-queries.c \
fe-server.c \
fe-settings.c \
- utf8.c \
- wcwidth.c \
+ fe-tls.c \
formats.c \
hilight-text.c \
keyboard.c \
@@ -50,6 +49,7 @@ pkginc_fe_common_core_HEADERS = \
fe-exec.h \
fe-messages.h \
fe-queries.h \
+ fe-tls.h \
formats.h \
hilight-text.h \
keyboard.h \
@@ -62,6 +62,3 @@ pkginc_fe_common_core_HEADERS = \
window-items.h \
windows-layout.h \
fe-windows.h
-
-noinst_HEADERS = \
- utf8.h
diff --git a/src/fe-common/core/chat-completion.c b/src/fe-common/core/chat-completion.c
index d3e018ad..1f00feaf 100644
--- a/src/fe-common/core/chat-completion.c
+++ b/src/fe-common/core/chat-completion.c
@@ -38,11 +38,18 @@
#include "chat-completion.h"
#include "window-items.h"
+enum {
+ COMPLETE_MCASE_NEVER = 0,
+ COMPLETE_MCASE_ALWAYS,
+ COMPLETE_MCASE_AUTO,
+};
+
static int keep_privates_count, keep_publics_count;
static int completion_lowercase;
-static const char *completion_char, *cmdchars;
+static char *completion_char, *cmdchars;
static GSList *global_lastmsgs;
-static int completion_auto, completion_strict;
+static int completion_auto, completion_strict, completion_empty_line;
+static int completion_match_case;
#define SERVER_LAST_MSG_ADD(server, nick) \
last_msg_add(&((MODULE_SERVER_REC *) MODULE_DATA(server))->lastmsgs, \
@@ -52,6 +59,18 @@ static int completion_auto, completion_strict;
last_msg_add(&((MODULE_CHANNEL_REC *) MODULE_DATA(channel))->lastmsgs, \
nick, own, keep_publics_count)
+static gboolean contains_uppercase(const char *s1)
+{
+ const char *ch;
+
+ for (ch = s1; *ch != '\0'; ch++) {
+ if (g_ascii_isupper(*ch))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static LAST_MSG_REC *last_msg_find(GSList *list, const char *nick)
{
while (list != NULL) {
@@ -336,7 +355,8 @@ GList *completion_msg(SERVER_REC *win_server,
}
static void complete_from_nicklist(GList **outlist, CHANNEL_REC *channel,
- const char *nick, const char *suffix)
+ const char *nick, const char *suffix,
+ const int match_case)
{
MODULE_CHANNEL_REC *mchannel;
GSList *tmp;
@@ -352,8 +372,10 @@ static void complete_from_nicklist(GList **outlist, CHANNEL_REC *channel,
for (tmp = mchannel->lastmsgs; tmp != NULL; tmp = tmp->next) {
LAST_MSG_REC *rec = tmp->data;
- if (g_ascii_strncasecmp(rec->nick, nick, len) == 0 &&
- glist_find_icase_string(*outlist, rec->nick) == NULL) {
+ if ((match_case? strncmp(rec->nick, nick, len)
+ : g_ascii_strncasecmp(rec->nick, nick, len)) == 0 &&
+ (match_case? glist_find_string(*outlist, rec->nick)
+ : glist_find_icase_string(*outlist, rec->nick)) == NULL) {
str = g_strconcat(rec->nick, suffix, NULL);
if (completion_lowercase) ascii_strdown(str);
if (rec->own)
@@ -368,7 +390,8 @@ static void complete_from_nicklist(GList **outlist, CHANNEL_REC *channel,
static GList *completion_nicks_nonstrict(CHANNEL_REC *channel,
const char *nick,
- const char *suffix)
+ const char *suffix,
+ const int match_case)
{
GSList *nicks, *tmp;
GList *list;
@@ -404,7 +427,8 @@ static GList *completion_nicks_nonstrict(CHANNEL_REC *channel,
*out = '\0';
/* add to list if 'cleaned' nick matches */
- if (g_ascii_strncasecmp(str, nick, len) == 0) {
+ if ((match_case? strncmp(str, nick, len)
+ : g_ascii_strncasecmp(str, nick, len)) == 0) {
tnick = g_strconcat(rec->nick, suffix, NULL);
if (completion_lowercase)
ascii_strdown(tnick);
@@ -428,7 +452,7 @@ static GList *completion_channel_nicks(CHANNEL_REC *channel, const char *nick,
GSList *nicks, *tmp;
GList *list;
char *str;
- int len;
+ int len, match_case;
g_return_val_if_fail(channel != NULL, NULL);
g_return_val_if_fail(nick != NULL, NULL);
@@ -437,9 +461,12 @@ static GList *completion_channel_nicks(CHANNEL_REC *channel, const char *nick,
if (suffix != NULL && *suffix == '\0')
suffix = NULL;
+ match_case = completion_match_case == COMPLETE_MCASE_ALWAYS ||
+ (completion_match_case == COMPLETE_MCASE_AUTO && contains_uppercase(nick));
+
/* put first the nicks who have recently said something */
list = NULL;
- complete_from_nicklist(&list, channel, nick, suffix);
+ complete_from_nicklist(&list, channel, nick, suffix, match_case);
/* and add the rest of the nicks too */
len = strlen(nick);
@@ -447,7 +474,8 @@ static GList *completion_channel_nicks(CHANNEL_REC *channel, const char *nick,
for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
NICK_REC *rec = tmp->data;
- if (g_ascii_strncasecmp(rec->nick, nick, len) == 0 &&
+ if ((match_case? strncmp(rec->nick, nick, len)
+ : g_ascii_strncasecmp(rec->nick, nick, len)) == 0 &&
rec != channel->ownnick) {
str = g_strconcat(rec->nick, suffix, NULL);
if (completion_lowercase)
@@ -463,7 +491,7 @@ static GList *completion_channel_nicks(CHANNEL_REC *channel, const char *nick,
/* remove non alphanum chars from nick and search again in case
list is still NULL ("foo<tab>" would match "_foo_" f.e.) */
if (!completion_strict)
- list = g_list_concat(list, completion_nicks_nonstrict(channel, nick, suffix));
+ list = g_list_concat(list, completion_nicks_nonstrict(channel, nick, suffix, match_case));
return list;
}
@@ -574,7 +602,7 @@ GList *completion_get_aliases(const char *word)
list = g_list_append(list, g_strdup(node->key));
}
-
+
return list;
}
@@ -641,6 +669,8 @@ static void sig_complete_word(GList **list, WINDOW_REC *window,
return;
if (*linestart == '\0' && *word == '\0') {
+ if (!completion_empty_line)
+ return;
/* pressed TAB at the start of line - add /MSG */
prefix = g_strdup_printf("%cmsg", *cmdchars);
*list = completion_msg(server, NULL, "", prefix);
@@ -781,7 +811,7 @@ GList *completion_get_servers(const char *word)
for (tmp = setupservers; tmp != NULL; tmp = tmp->next) {
SERVER_SETUP_REC *rec = tmp->data;
- if (g_ascii_strncasecmp(rec->address, word, len) == 0)
+ if (g_ascii_strncasecmp(rec->address, word, len) == 0)
list = g_list_append(list, g_strdup(rec->address));
}
@@ -814,12 +844,12 @@ GList *completion_get_targets(const char *word)
list = g_list_append(list, g_strdup(node->key));
}
-
+
return list;
}
static void sig_complete_connect(GList **list, WINDOW_REC *window,
- const char *word, const char *line,
+ const char *word, const char *line,
int *want_space)
{
g_return_if_fail(list != NULL);
@@ -895,7 +925,7 @@ static void sig_complete_alias(GList **list, WINDOW_REC *window,
int *want_space)
{
const char *definition;
-
+
g_return_if_fail(list != NULL);
g_return_if_fail(word != NULL);
g_return_if_fail(line != NULL);
@@ -905,7 +935,7 @@ static void sig_complete_alias(GList **list, WINDOW_REC *window,
*list = g_list_append(NULL, g_strdup(definition));
signal_stop();
}
- } else {
+ } else {
*list = completion_get_aliases(word);
if (*list != NULL) signal_stop();
}
@@ -965,7 +995,7 @@ static void sig_complete_target(GList **list, WINDOW_REC *window,
int *want_space)
{
const char *definition;
-
+
g_return_if_fail(list != NULL);
g_return_if_fail(word != NULL);
g_return_if_fail(line != NULL);
@@ -975,7 +1005,7 @@ static void sig_complete_target(GList **list, WINDOW_REC *window,
*list = g_list_append(NULL, g_strdup(definition));
signal_stop();
}
- } else {
+ } else {
*list = completion_get_targets(word);
if (*list != NULL) signal_stop();
}
@@ -1126,10 +1156,18 @@ static void read_settings(void)
keep_privates_count = settings_get_int("completion_keep_privates");
keep_publics_count = settings_get_int("completion_keep_publics");
completion_lowercase = settings_get_bool("completion_nicks_lowercase");
- completion_char = settings_get_str("completion_char");
- cmdchars = settings_get_str("cmdchars");
+
completion_auto = settings_get_bool("completion_auto");
completion_strict = settings_get_bool("completion_strict");
+ completion_empty_line = settings_get_bool("completion_empty_line");
+
+ completion_match_case = settings_get_choice("completion_nicks_match_case");
+
+ g_free_not_null(completion_char);
+ completion_char = g_strdup(settings_get_str("completion_char"));
+
+ g_free_not_null(cmdchars);
+ cmdchars = g_strdup(settings_get_str("cmdchars"));
if (*completion_char == '\0') {
/* this would break.. */
@@ -1145,6 +1183,8 @@ void chat_completion_init(void)
settings_add_int("completion", "completion_keep_privates", 10);
settings_add_bool("completion", "completion_nicks_lowercase", FALSE);
settings_add_bool("completion", "completion_strict", FALSE);
+ settings_add_bool("completion", "completion_empty_line", TRUE);
+ settings_add_choice("completion", "completion_nicks_match_case", COMPLETE_MCASE_AUTO, "never;always;auto");
settings_add_bool("lookandfeel", "expand_escapes", FALSE);
@@ -1220,4 +1260,7 @@ void chat_completion_deinit(void)
signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+
+ g_free_not_null(completion_char);
+ g_free_not_null(cmdchars);
}
diff --git a/src/fe-common/core/command-history.c b/src/fe-common/core/command-history.c
index afe19aa7..1060744e 100644
--- a/src/fe-common/core/command-history.c
+++ b/src/fe-common/core/command-history.c
@@ -42,10 +42,10 @@ void command_history_add(HISTORY_REC *history, const char *text)
g_return_if_fail(text != NULL);
link = g_list_last(history->list);
- if (link != NULL && strcmp(link->data, text) == 0)
+ if (link != NULL && g_strcmp0(link->data, text) == 0)
return; /* same as previous entry */
- if (settings_get_int("max_command_history") < 1 ||
+ if (settings_get_int("max_command_history") < 1 ||
history->lines < settings_get_int("max_command_history"))
history->lines++;
else {
@@ -78,12 +78,12 @@ HISTORY_REC *command_history_find_name(const char *name)
for (tmp = histories; tmp != NULL; tmp = tmp->next) {
HISTORY_REC *rec = tmp->data;
-
+
if (rec->name != NULL &&
g_ascii_strcasecmp(rec->name, name) == 0)
return rec;
}
-
+
return NULL;
}
@@ -94,13 +94,13 @@ HISTORY_REC *command_history_current(WINDOW_REC *window)
if (window == NULL)
return global_history;
- if (window_history)
- return window->history;
-
rec = command_history_find_name(window->history_name);
if (rec != NULL)
return rec;
+ if (window_history)
+ return window->history;
+
return global_history;
}
@@ -121,7 +121,7 @@ const char *command_history_prev(WINDOW_REC *window, const char *text)
}
if (*text != '\0' &&
- (pos == NULL || strcmp(pos->data, text) != 0)) {
+ (pos == NULL || g_strcmp0(pos->data, text) != 0)) {
/* save the old entry to history */
command_history_add(history, text);
}
@@ -135,7 +135,7 @@ const char *command_history_next(WINDOW_REC *window, const char *text)
GList *pos;
history = command_history_current(window);
- pos = history->pos;
+ pos = history->pos;
if (pos != NULL)
history->pos = history->pos->next;
@@ -145,7 +145,7 @@ const char *command_history_next(WINDOW_REC *window, const char *text)
}
if (*text != '\0' &&
- (pos == NULL || strcmp(pos->data, text) != 0)) {
+ (pos == NULL || g_strcmp0(pos->data, text) != 0)) {
/* save the old entry to history */
command_history_add(history, text);
}
@@ -160,24 +160,35 @@ void command_history_clear_pos_func(HISTORY_REC *history, gpointer user_data)
void command_history_clear_pos(WINDOW_REC *window)
{
- g_slist_foreach(histories,
+ g_slist_foreach(histories,
(GFunc) command_history_clear_pos_func, NULL);
}
HISTORY_REC *command_history_create(const char *name)
{
HISTORY_REC *rec;
-
+
rec = g_new0(HISTORY_REC, 1);
-
+
if (name != NULL)
rec->name = g_strdup(name);
histories = g_slist_append(histories, rec);
-
+
return rec;
}
+void command_history_clear(HISTORY_REC *history)
+{
+ g_return_if_fail(history != NULL);
+
+ command_history_clear_pos_func(history, NULL);
+ g_list_foreach(history->list, (GFunc) g_free, NULL);
+ g_list_free(history->list);
+ history->list = NULL;
+ history->lines = 0;
+}
+
void command_history_destroy(HISTORY_REC *history)
{
g_return_if_fail(history != NULL);
@@ -186,9 +197,7 @@ void command_history_destroy(HISTORY_REC *history)
g_return_if_fail(history->refcount == 0);
histories = g_slist_remove(histories, history);
-
- g_list_foreach(history->list, (GFunc) g_free, NULL);
- g_list_free(history->list);
+ command_history_clear(history);
g_free_not_null(history->name);
g_free(history);
@@ -229,6 +238,18 @@ static void sig_window_destroyed(WINDOW_REC *window)
g_free_not_null(window->history_name);
}
+static void sig_window_history_cleared(WINDOW_REC *window, const char *name) {
+ HISTORY_REC *history;
+
+ if (name == NULL || *name == '\0') {
+ history = command_history_current(window);
+ } else {
+ history = command_history_find_name(name);
+ }
+
+ command_history_clear(history);
+}
+
static void sig_window_history_changed(WINDOW_REC *window, const char *oldname)
{
command_history_link(window->history_name);
@@ -279,6 +300,7 @@ void command_history_init(void)
signal_add("window created", (SIGNAL_FUNC) sig_window_created);
signal_add("window destroyed", (SIGNAL_FUNC) sig_window_destroyed);
signal_add("window history changed", (SIGNAL_FUNC) sig_window_history_changed);
+ signal_add_last("window history cleared", (SIGNAL_FUNC) sig_window_history_cleared);
signal_add("setup changed", (SIGNAL_FUNC) read_settings);
}
@@ -287,6 +309,7 @@ void command_history_deinit(void)
signal_remove("window created", (SIGNAL_FUNC) sig_window_created);
signal_remove("window destroyed", (SIGNAL_FUNC) sig_window_destroyed);
signal_remove("window history changed", (SIGNAL_FUNC) sig_window_history_changed);
+ signal_remove("window history cleared", (SIGNAL_FUNC) sig_window_history_cleared);
signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
command_history_destroy(global_history);
diff --git a/src/fe-common/core/command-history.h b/src/fe-common/core/command-history.h
index 7b76246b..a572216b 100644
--- a/src/fe-common/core/command-history.h
+++ b/src/fe-common/core/command-history.h
@@ -28,6 +28,7 @@ const char *command_history_next(WINDOW_REC *window, const char *text);
void command_history_clear_pos(WINDOW_REC *window);
HISTORY_REC *command_history_create(const char *name);
+void command_history_clear(HISTORY_REC *history);
void command_history_destroy(HISTORY_REC *history);
void command_history_link(const char *name);
void command_history_unlink(const char *name);
diff --git a/src/fe-common/core/completion.c b/src/fe-common/core/completion.c
index 31d62e10..914ba80b 100644
--- a/src/fe-common/core/completion.c
+++ b/src/fe-common/core/completion.c
@@ -37,8 +37,11 @@ static int last_want_space, last_line_pos;
#define isseparator_notspace(c) \
((c) == ',')
+#define isseparator_space(c) \
+ ((c) == ' ')
+
#define isseparator(c) \
- ((c) == ' ' || isseparator_notspace(c))
+ (isseparator_space(c) || isseparator_notspace(c))
void chat_completion_init(void);
void chat_completion_deinit(void);
@@ -51,7 +54,7 @@ static const char *completion_find(const char *key, int automatic)
if (node == NULL || node->type != NODE_TYPE_BLOCK)
return NULL;
- node = config_node_section(node, key, -1);
+ node = iconfig_node_section(node, key, -1);
if (node == NULL)
return NULL;
@@ -141,7 +144,7 @@ char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase, i
g_return_val_if_fail(pos != NULL, NULL);
continue_complete = complist != NULL && *pos == last_line_pos &&
- strcmp(line, last_line) == 0;
+ g_strcmp0(line, last_line) == 0;
if (erase && !continue_complete)
return NULL;
@@ -153,20 +156,23 @@ char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase, i
word = NULL;
linestart = NULL;
} else {
+ char* old_wordstart;
+
/* get the word we want to complete */
word = get_word_at(line, *pos, &wordstart);
+ old_wordstart = wordstart;
+
startpos = (int) (wordstart-line);
wordlen = strlen(word);
- /* get the start of line until the word we're completing */
- if (isseparator(*line)) {
- /* empty space at the start of line */
- if (wordstart == line)
- wordstart += strlen(wordstart);
- } else {
- while (wordstart > line && isseparator(wordstart[-1]))
- wordstart--;
- }
+ /* remove trailing spaces from linestart */
+ while (wordstart > line && isseparator_space(wordstart[-1]))
+ wordstart--;
+
+ /* unless everything was spaces */
+ if (old_wordstart > line && wordstart == line)
+ wordstart = old_wordstart - 1;
+
linestart = g_strndup(line, (int) (wordstart-line));
/* completions usually add space after the word, that makes
@@ -175,19 +181,24 @@ char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase, i
BUT if we start completion with "/msg "<tab>, we don't
want to complete the /msg word, but instead complete empty
word with /msg being in linestart. */
- if (!erase && *pos > 0 && line[*pos-1] == ' ' &&
- (*linestart == '\0' || wordstart[-1] != ' ')) {
+ if (!erase && *pos > 0 && isseparator_space(line[*pos-1]) &&
+ (*linestart == '\0' || !isseparator_space(wordstart[-1]))) {
char *old;
- old = linestart;
+ old = linestart;
linestart = *linestart == '\0' ?
g_strdup(word) :
- g_strconcat(linestart, " ", word, NULL);
+ g_strdup_printf("%s%c%s",
+ /* do not accidentally duplicate the word separator */
+ line == wordstart - 1 ? "" : linestart,
+ old_wordstart[-1], word);
g_free(old);
g_free(word);
word = g_strdup("");
- startpos = strlen(linestart)+1;
+
+ startpos = *linestart == '\0' ? 0 :
+ strlen(linestart)+1;
wordlen = 0;
}
@@ -217,6 +228,11 @@ char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase, i
want_space = TRUE;
signal_emit("complete word", 5, &complist, window, word, linestart, &want_space);
last_want_space = want_space;
+
+ if (complist != NULL) {
+ /* Remove all nulls (from the signal) before doing further processing */
+ complist = g_list_remove_all(g_list_first(complist), NULL);
+ }
}
g_free(linestart);
@@ -329,7 +345,9 @@ GList *filename_complete(const char *path, const char *default_path)
(dp->d_name[1] == '.' && dp->d_name[2] == '\0'))
continue; /* skip . and .. */
- if (basename[0] != '.')
+ /* Skip the dotfiles unless the user explicitly asked us
+ * to do so. Basename might be './', beware of that */
+ if (basename[0] != '.' || basename[1] == '\0')
continue;
}
@@ -362,8 +380,7 @@ static GList *completion_get_settings(const char *key, SettingType type)
for (tmp = sets; tmp != NULL; tmp = tmp->next) {
SETTINGS_REC *rec = tmp->data;
- if ((type == -1 || rec->type == type) &&
- g_ascii_strncasecmp(rec->key, key, len) == 0)
+ if ((type == SETTING_TYPE_ANY || rec->type == type) && g_ascii_strncasecmp(rec->key, key, len) == 0)
complist = g_list_insert_sorted(complist, g_strdup(rec->key), (GCompareFunc) g_istr_cmp);
}
g_slist_free(sets);
@@ -393,7 +410,8 @@ static GList *completion_get_aliases(const char *alias, char cmdchar)
continue;
if (g_ascii_strncasecmp(node->key, alias, len) == 0) {
- word = g_strdup_printf("%c%s", cmdchar, node->key);
+ word = cmdchar == '\0' ? g_strdup(node->key) :
+ g_strdup_printf("%c%s", cmdchar, node->key);
/* add matching alias to completion list, aliases will
be appended after command completions and kept in
uppercase to show it's an alias */
@@ -585,13 +603,19 @@ static void sig_complete_word(GList **list, WINDOW_REC *window,
/* command completion? */
cmdchars = settings_get_str("cmdchars");
- if (*word != '\0' && *linestart == '\0' && strchr(cmdchars, *word)) {
+ if (*word != '\0' && ((*linestart == '\0' && strchr(cmdchars, *word)) ||
+ (*linestart != '\0' && linestart[1] == '\0' &&
+ strchr(cmdchars, *linestart)))) {
+ gboolean skip = *linestart == '\0' ? TRUE : FALSE;
+
/* complete /command */
- *list = completion_get_commands(word+1, *word);
+ *list = completion_get_commands(word + (skip ? 1 : 0),
+ skip ? *word : '\0');
/* complete aliases, too */
*list = g_list_concat(*list,
- completion_get_aliases(word+1, *word));
+ completion_get_aliases(word + (skip ? 1 : 0),
+ skip ? *word : '\0'));
if (*list != NULL) signal_stop();
return;
@@ -681,14 +705,26 @@ static void sig_complete_set(GList **list, WINDOW_REC *window,
g_return_if_fail(line != NULL);
if (*line == '\0' ||
- !strcmp("-clear", line) || !strcmp("-default", line))
- *list = completion_get_settings(word, -1);
+ !g_strcmp0("-clear", line) || !g_strcmp0("-default", line))
+ *list = completion_get_settings(word, SETTING_TYPE_ANY);
else if (*line != '\0' && *word == '\0') {
SETTINGS_REC *rec = settings_get_record(line);
if (rec != NULL) {
char *value = settings_get_print(rec);
+
+ /* show the current option first */
if (value != NULL)
*list = g_list_append(*list, value);
+
+ /* show the whole list of valid options */
+ if (rec->type == SETTING_TYPE_CHOICE) {
+ char **tmp;
+
+ for (tmp = rec->choices; *tmp; tmp++) {
+ if (g_ascii_strcasecmp(*tmp, value) != 0)
+ *list = g_list_append(*list, g_strdup(*tmp));
+ }
+ }
}
}
@@ -785,7 +821,7 @@ static void cmd_completion(const char *data)
} else if (*key != '\0' && *value != '\0') {
int automatic = g_hash_table_lookup(optlist, "auto") != NULL;
- node = config_node_section(node, key, NODE_TYPE_BLOCK);
+ node = iconfig_node_section(node, key, NODE_TYPE_BLOCK);
iconfig_node_set_str(node, "value", value);
if (automatic)
iconfig_node_set_bool(node, "auto", TRUE);
diff --git a/src/fe-common/core/fe-channels.c b/src/fe-common/core/fe-channels.c
index 75147fdb..00aac885 100644
--- a/src/fe-common/core/fe-channels.c
+++ b/src/fe-common/core/fe-channels.c
@@ -26,6 +26,8 @@
#include "levels.h"
#include "misc.h"
#include "settings.h"
+#include "special-vars.h"
+#include "utf8.h"
#include "chat-protocols.h"
#include "chatnets.h"
@@ -122,7 +124,8 @@ static void cmd_join(const char *data, SERVER_REC *server)
void *free_arg;
if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
- PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
+ PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST |
+ PARAM_FLAG_STRIP_TRAILING_WS,
"join", &optlist, &pdata))
return;
@@ -245,9 +248,7 @@ static void cmd_channel(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
}
}
-/* SYNTAX: CHANNEL ADD [-auto | -noauto] [-bots <masks>] [-botcmd <command>]
- <channel> <network> [<password>] */
-static void cmd_channel_add(const char *data)
+static void cmd_channel_add_modify(const char *data, gboolean add)
{
GHashTable *optlist;
CHATNET_REC *chatnetrec;
@@ -256,18 +257,19 @@ static void cmd_channel_add(const char *data)
void *free_arg;
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTIONS,
- "channel add", &optlist, &channel, &chatnet, &password))
+ "channel add", &optlist, &channel, &chatnet, &password))
return;
- if (*chatnet == '\0' || *channel == '\0')
+ if (*chatnet == '\0' || *channel == '\0') {
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+ }
chatnetrec = chatnet_find(chatnet);
if (chatnetrec == NULL) {
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
- TXT_UNKNOWN_CHATNET, chatnet);
+ TXT_UNKNOWN_CHATNET, chatnet);
cmd_params_free(free_arg);
- return;
+ return;
}
botarg = g_hash_table_lookup(optlist, "bots");
@@ -275,6 +277,13 @@ static void cmd_channel_add(const char *data)
rec = channel_setup_find(channel, chatnet);
if (rec == NULL) {
+ if (add == FALSE) {
+ cmd_params_free(free_arg);
+ printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+ TXT_CHANSETUP_NOT_FOUND, channel, chatnet);
+ return;
+ }
+
rec = CHAT_PROTOCOL(chatnetrec)->create_channel_setup();
rec->name = g_strdup(channel);
rec->chatnet = g_strdup(chatnet);
@@ -287,7 +296,7 @@ static void cmd_channel_add(const char *data)
if (g_hash_table_lookup(optlist, "noauto")) rec->autojoin = FALSE;
if (botarg != NULL && *botarg != '\0') rec->botmasks = g_strdup(botarg);
if (botcmdarg != NULL && *botcmdarg != '\0') rec->autosendcmd = g_strdup(botcmdarg);
- if (*password != '\0' && strcmp(password, "-") != 0) rec->password = g_strdup(password);
+ if (*password != '\0' && g_strcmp0(password, "-") != 0) rec->password = g_strdup(password);
signal_emit("channel add fill", 2, rec, optlist);
@@ -298,6 +307,18 @@ static void cmd_channel_add(const char *data)
cmd_params_free(free_arg);
}
+/* SYNTAX: CHANNEL ADD|MODIFY [-auto | -noauto] [-bots <masks>] [-botcmd <command>]
+ <channel> <network> [<password>] */
+static void cmd_channel_add(const char *data)
+{
+ cmd_channel_add_modify(data, TRUE);
+}
+
+static void cmd_channel_modify(const char *data)
+{
+ cmd_channel_add_modify(data, FALSE);
+}
+
/* SYNTAX: CHANNEL REMOVE <channel> <network> */
static void cmd_channel_remove(const char *data)
{
@@ -322,40 +343,40 @@ static void cmd_channel_remove(const char *data)
static int get_nick_length(void *data)
{
- return strlen(((NICK_REC *) data)->nick);
+ return string_width(((NICK_REC *) data)->nick, -1);
}
static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
{
- WINDOW_REC *window;
+ WINDOW_REC *window;
TEXT_DEST_REC dest;
GString *str;
GSList *tmp;
- char *format, *stripped, *prefix_format;
- char *linebuf, nickmode[2] = { 0, 0 };
+ char *format, *stripped, *prefix_format;
+ char *aligned_nick, nickmode[2] = { 0, 0 };
int *columns, cols, rows, last_col_rows, col, row, max_width;
- int item_extra, linebuf_size, formatnum;
+ int item_extra, formatnum;
window = window_find_closest(channel->server, channel->visible_name,
- MSGLEVEL_CLIENTCRAP);
- max_width = window->width;
+ MSGLEVEL_CLIENTCRAP);
+ max_width = window->width;
- /* get the length of item extra stuff ("[ ] ") */
+ /* get the length of item extra stuff ("[ ] ") */
format = format_get_text(MODULE_NAME, NULL,
- channel->server, channel->visible_name,
- TXT_NAMES_NICK, " ", "");
+ channel->server, channel->visible_name,
+ TXT_NAMES_NICK, " ", "");
stripped = strip_codes(format);
item_extra = strlen(stripped);
- g_free(stripped);
+ g_free(stripped);
g_free(format);
if (settings_get_int("names_max_width") > 0 &&
settings_get_int("names_max_width") < max_width)
max_width = settings_get_int("names_max_width");
- /* remove width of the timestamp from max_width */
+ /* remove width of the timestamp from max_width */
format_create_dest(&dest, channel->server, channel->visible_name,
- MSGLEVEL_CLIENTCRAP, NULL);
+ MSGLEVEL_CLIENTCRAP, NULL);
format = format_get_line_start(current_theme, &dest, time(NULL));
if (format != NULL) {
stripped = strip_codes(format);
@@ -364,11 +385,11 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
g_free(format);
}
- /* remove width of the prefix from max_width */
+ /* remove width of the prefix from max_width */
prefix_format = format_get_text(MODULE_NAME, NULL,
- channel->server, channel->visible_name,
- TXT_NAMES_PREFIX,
- channel->visible_name);
+ channel->server, channel->visible_name,
+ TXT_NAMES_PREFIX,
+ channel->visible_name);
if (prefix_format != NULL) {
stripped = strip_codes(prefix_format);
max_width -= strlen(stripped);
@@ -383,19 +404,18 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
/* calculate columns */
cols = get_max_column_count(nicklist, get_nick_length, max_width,
- settings_get_int("names_max_columns"),
- item_extra, 3, &columns, &rows);
+ settings_get_int("names_max_columns"),
+ item_extra, 3, &columns, &rows);
nicklist = columns_sort_list(nicklist, rows);
- /* rows in last column */
+ /* rows in last column */
last_col_rows = rows-(cols*rows-g_slist_length(nicklist));
if (last_col_rows == 0)
- last_col_rows = rows;
+ last_col_rows = rows;
str = g_string_new(prefix_format);
- linebuf_size = max_width+1; linebuf = g_malloc(linebuf_size);
- col = 0; row = 0;
+ col = 0; row = 0;
for (tmp = nicklist; tmp != NULL; tmp = tmp->next) {
NICK_REC *rec = tmp->data;
@@ -403,49 +423,45 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
nickmode[0] = rec->prefixes[0];
else
nickmode[0] = ' ';
-
- if (linebuf_size < columns[col]-item_extra+1) {
- linebuf_size = (columns[col]-item_extra+1)*2;
- linebuf = g_realloc(linebuf, linebuf_size);
- }
- memset(linebuf, ' ', columns[col]-item_extra);
- linebuf[columns[col]-item_extra] = '\0';
- memcpy(linebuf, rec->nick, strlen(rec->nick));
-
- formatnum = rec->op ? TXT_NAMES_NICK_OP :
- rec->halfop ? TXT_NAMES_NICK_HALFOP :
- rec->voice ? TXT_NAMES_NICK_VOICE :
- TXT_NAMES_NICK;
+
+ aligned_nick = get_alignment(rec->nick,
+ columns[col]-item_extra,
+ ALIGN_PAD, ' ');
+
+ formatnum = rec->op ? TXT_NAMES_NICK_OP :
+ rec->halfop ? TXT_NAMES_NICK_HALFOP :
+ rec->voice ? TXT_NAMES_NICK_VOICE :
+ TXT_NAMES_NICK;
format = format_get_text(MODULE_NAME, NULL,
- channel->server,
- channel->visible_name,
- formatnum, nickmode, linebuf);
+ channel->server,
+ channel->visible_name,
+ formatnum, nickmode, aligned_nick);
g_string_append(str, format);
+ g_free(aligned_nick);
g_free(format);
if (++col == cols) {
printtext(channel->server, channel->visible_name,
- MSGLEVEL_CLIENTCRAP, "%s", str->str);
+ MSGLEVEL_CLIENTCRAP, "%s", str->str);
g_string_truncate(str, 0);
if (prefix_format != NULL)
- g_string_assign(str, prefix_format);
+ g_string_assign(str, prefix_format);
col = 0; row++;
if (row == last_col_rows)
- cols--;
+ cols--;
}
}
if (str->len > strlen(prefix_format)) {
printtext(channel->server, channel->visible_name,
- MSGLEVEL_CLIENTCRAP, "%s", str->str);
+ MSGLEVEL_CLIENTCRAP, "%s", str->str);
}
g_slist_free(nicklist);
g_string_free(str, TRUE);
g_free_not_null(columns);
g_free_not_null(prefix_format);
- g_free(linebuf);
}
void fe_channels_nicklist(CHANNEL_REC *channel, int flags)
@@ -523,7 +539,7 @@ static void cmd_names(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
"names", &optlist, &channel))
return;
- if (strcmp(channel, "*") == 0 || *channel == '\0') {
+ if (g_strcmp0(channel, "*") == 0 || *channel == '\0') {
if (!IS_CHANNEL(item))
cmd_param_error(CMDERR_NOT_JOINED);
@@ -561,7 +577,7 @@ static void cmd_names(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
if (unknowns->len > 1)
g_string_truncate(unknowns, unknowns->len-1);
- if (unknowns->len > 0 && strcmp(channel, unknowns->str) != 0)
+ if (unknowns->len > 0 && g_strcmp0(channel, unknowns->str) != 0)
signal_emit("command names", 3, unknowns->str, server, item);
g_string_free(unknowns, TRUE);
@@ -621,12 +637,14 @@ void fe_channels_init(void)
command_bind("join", NULL, (SIGNAL_FUNC) cmd_join);
command_bind("channel", NULL, (SIGNAL_FUNC) cmd_channel);
command_bind("channel add", NULL, (SIGNAL_FUNC) cmd_channel_add);
+ command_bind("channel modify", NULL, (SIGNAL_FUNC) cmd_channel_modify);
command_bind("channel remove", NULL, (SIGNAL_FUNC) cmd_channel_remove);
command_bind("channel list", NULL, (SIGNAL_FUNC) cmd_channel_list);
command_bind("names", NULL, (SIGNAL_FUNC) cmd_names);
command_bind("cycle", NULL, (SIGNAL_FUNC) cmd_cycle);
command_set_options("channel add", "auto noauto -bots -botcmd");
+ command_set_options("channel modify", "auto noauto -bots -botcmd");
command_set_options("names", "count ops halfops voices normal");
command_set_options("join", "invite window");
}
@@ -642,6 +660,7 @@ void fe_channels_deinit(void)
command_unbind("join", (SIGNAL_FUNC) cmd_join);
command_unbind("channel", (SIGNAL_FUNC) cmd_channel);
command_unbind("channel add", (SIGNAL_FUNC) cmd_channel_add);
+ command_unbind("channel modify", (SIGNAL_FUNC) cmd_channel_modify);
command_unbind("channel remove", (SIGNAL_FUNC) cmd_channel_remove);
command_unbind("channel list", (SIGNAL_FUNC) cmd_channel_list);
command_unbind("names", (SIGNAL_FUNC) cmd_names);
diff --git a/src/fe-common/core/fe-common-core.c b/src/fe-common/core/fe-common-core.c
index a475f056..512fc84c 100644
--- a/src/fe-common/core/fe-common-core.c
+++ b/src/fe-common/core/fe-common-core.c
@@ -88,6 +88,9 @@ void fe_server_deinit(void);
void fe_settings_init(void);
void fe_settings_deinit(void);
+void fe_tls_init(void);
+void fe_tls_deinit(void);
+
void window_commands_init(void);
void window_commands_deinit(void);
@@ -147,7 +150,6 @@ void fe_common_core_init(void)
settings_add_level("lookandfeel", "timestamp_level", "ALL");
settings_add_time("lookandfeel", "timestamp_timeout", "0");
- settings_add_bool("lookandfeel", "bell_beeps", FALSE);
settings_add_level("lookandfeel", "beep_msg_level", "");
settings_add_bool("lookandfeel", "beep_when_window_active", TRUE);
settings_add_bool("lookandfeel", "beep_when_away", TRUE);
@@ -168,9 +170,7 @@ void fe_common_core_init(void)
keyboard_init();
printtext_init();
formats_init();
-#ifndef WIN32
fe_exec_init();
-#endif
fe_expandos_init();
fe_help_init();
fe_ignore_init();
@@ -178,6 +178,7 @@ void fe_common_core_init(void)
fe_modules_init();
fe_server_init();
fe_settings_init();
+ fe_tls_init();
windows_init();
window_activity_init();
window_commands_init();
@@ -211,9 +212,7 @@ void fe_common_core_deinit(void)
keyboard_deinit();
printtext_deinit();
formats_deinit();
-#ifndef WIN32
fe_exec_deinit();
-#endif
fe_expandos_deinit();
fe_help_deinit();
fe_ignore_deinit();
@@ -221,6 +220,7 @@ void fe_common_core_deinit(void)
fe_modules_deinit();
fe_server_deinit();
fe_settings_deinit();
+ fe_tls_deinit();
windows_deinit();
window_activity_deinit();
window_commands_deinit();
@@ -326,8 +326,11 @@ static void autoconnect_servers(void)
if (autocon_server != NULL) {
/* connect to specified server */
- str = g_strdup_printf(autocon_password == NULL ? "%s %d" : "%s %d %s",
- autocon_server, autocon_port, autocon_password);
+ if (autocon_password == NULL)
+ str = g_strdup_printf("%s %d", autocon_server, autocon_port);
+ else
+ str = g_strdup_printf("%s %d %s", autocon_server, autocon_port, autocon_password);
+
signal_emit("command connect", 1, str);
g_free(str);
return;
@@ -447,18 +450,7 @@ void fe_common_core_finish_init(void)
signal_add_first("setup changed", (SIGNAL_FUNC) sig_setup_changed);
/* _after_ windows are created.. */
-#if GLIB_CHECK_VERSION(2,6,0)
g_log_set_default_handler((GLogFunc) glog_func, NULL);
-#else
- g_log_set_handler(G_LOG_DOMAIN,
- (GLogLevelFlags) (G_LOG_LEVEL_CRITICAL |
- G_LOG_LEVEL_WARNING),
- (GLogFunc) glog_func, NULL);
- g_log_set_handler("GLib",
- (GLogLevelFlags) (G_LOG_LEVEL_CRITICAL |
- G_LOG_LEVEL_WARNING),
- (GLogFunc) glog_func, NULL); /* send glib errors to the same place */
-#endif
if (setup_changed)
signal_emit("setup changed", 0);
@@ -471,15 +463,24 @@ gboolean strarray_find_dest(char **array, const TEXT_DEST_REC *dest)
{
g_return_val_if_fail(array != NULL, FALSE);
+ if (strarray_find(array, "*") != -1)
+ return TRUE;
+
if (strarray_find(array, dest->target) != -1)
return TRUE;
if (dest->server_tag != NULL) {
- char *tagtarget = g_strdup_printf("%s/%s", dest->server_tag, dest->target);
+ char *tagtarget = g_strdup_printf("%s/%s", dest->server_tag, "*");
int ret = strarray_find(array, tagtarget);
g_free(tagtarget);
if (ret != -1)
return TRUE;
+
+ tagtarget = g_strdup_printf("%s/%s", dest->server_tag, dest->target);
+ ret = strarray_find(array, tagtarget);
+ g_free(tagtarget);
+ if (ret != -1)
+ return TRUE;
}
return FALSE;
}
diff --git a/src/fe-common/core/fe-core-commands.c b/src/fe-common/core/fe-core-commands.c
index 7b1d7b94..97a246ec 100644
--- a/src/fe-common/core/fe-core-commands.c
+++ b/src/fe-common/core/fe-core-commands.c
@@ -51,7 +51,8 @@ static int ret_texts[] = {
TXT_INVALID_TIME,
TXT_INVALID_CHARSET,
TXT_EVAL_MAX_RECURSE,
- TXT_PROGRAM_NOT_FOUND
+ TXT_PROGRAM_NOT_FOUND,
+ TXT_NO_SERVER_DEFINED,
};
int command_hide_output;
diff --git a/src/fe-common/core/fe-exec.c b/src/fe-common/core/fe-exec.c
index 9249f432..36990866 100644
--- a/src/fe-common/core/fe-exec.c
+++ b/src/fe-common/core/fe-exec.c
@@ -161,7 +161,7 @@ static PROCESS_REC *process_find(const char *name, int verbose)
for (tmp = processes; tmp != NULL; tmp = tmp->next) {
PROCESS_REC *rec = tmp->data;
- if (rec->name != NULL && strcmp(rec->name, name) == 0)
+ if (rec->name != NULL && g_strcmp0(rec->name, name) == 0)
return rec;
}
@@ -237,22 +237,13 @@ static int signal_name_to_id(const char *name)
static int cmd_options_get_signal(const char *cmd,
GHashTable *optlist)
{
- GSList *list, *tmp, *next;
+ GList *list;
char *signame;
int signum;
/* get all the options, then remove the known ones. there should
be only one left - the signal */
- list = hashtable_get_keys(optlist);
- if (cmd != NULL) {
- for (tmp = list; tmp != NULL; tmp = next) {
- char *option = tmp->data;
- next = tmp->next;
-
- if (command_have_option(cmd, option))
- list = g_slist_remove(list, option);
- }
- }
+ list = optlist_remove_known(cmd, optlist);
if (list == NULL)
return -1;
@@ -272,7 +263,7 @@ static int cmd_options_get_signal(const char *cmd,
return -2;
}
- g_slist_free(list);
+ g_list_free(list);
return signum;
}
diff --git a/src/fe-common/core/fe-help.c b/src/fe-common/core/fe-help.c
index 4ea7c89f..23a6e701 100644
--- a/src/fe-common/core/fe-help.c
+++ b/src/fe-common/core/fe-help.c
@@ -37,12 +37,12 @@ static int commands_equal(COMMAND_REC *rec, COMMAND_REC *rec2)
if (rec2->category == NULL && rec->category != NULL)
return 1;
if (rec->category != NULL && rec2->category != NULL) {
- i = strcmp(rec->category, rec2->category);
+ i = g_strcmp0(rec->category, rec2->category);
if (i != 0)
return i;
}
- return strcmp(rec->cmd, rec2->cmd);
+ return g_strcmp0(rec->cmd, rec2->cmd);
}
static int get_cmd_length(void *data)
@@ -176,7 +176,7 @@ static void show_help(const char *data)
if (last != NULL && rec->category != NULL &&
(last->category == NULL ||
- strcmp(rec->category, last->category) != 0)) {
+ g_strcmp0(rec->category, last->category) != 0)) {
/* category changed */
if (items > 0) {
if (!header) {
diff --git a/src/fe-common/core/fe-ignore.c b/src/fe-common/core/fe-ignore.c
index 96242f9e..800e881d 100644
--- a/src/fe-common/core/fe-ignore.c
+++ b/src/fe-common/core/fe-ignore.c
@@ -58,14 +58,17 @@ static void ignore_print(int index, IGNORE_REC *rec)
g_string_append(options, "-regexp ");
if (rec->pattern == NULL)
g_string_append(options, "[INVALID! -pattern missing] ");
-#ifdef HAVE_REGEX_H
+#ifdef USE_GREGEX
+ else if (rec->preg == NULL)
+ g_string_append(options, "[INVALID!] ");
+#else
else if (!rec->regexp_compiled)
g_string_append(options, "[INVALID!] ");
#endif
}
if (rec->fullword) g_string_append(options, "-full ");
if (rec->replies) g_string_append(options, "-replies ");
- if (rec->servertag != NULL)
+ if (rec->servertag != NULL)
g_string_append_printf(options, "-network %s ", rec->servertag);
if (rec->pattern != NULL)
g_string_append_printf(options, "-pattern %s ", rec->pattern);
@@ -127,7 +130,8 @@ static void cmd_ignore(const char *data)
return;
}
- if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS | PARAM_FLAG_GETREST,
+ if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS |
+ PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
"ignore", &optlist, &mask, &levels))
return;
@@ -137,7 +141,7 @@ static void cmd_ignore(const char *data)
/* Allow -ircnet for backwards compatibility */
if (!servertag)
servertag = g_hash_table_lookup(optlist, "ircnet");
-
+
if (*mask == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
if (*levels == '\0') levels = "ALL";
level = level2bits(levels, NULL);
@@ -157,15 +161,15 @@ static void cmd_ignore(const char *data)
channels = (chanarg == NULL || *chanarg == '\0') ? NULL :
g_strsplit(chanarg, ",", -1);
- rec = patternarg != NULL ? NULL: ignore_find_noact(servertag, mask, channels,
- (level & MSGLEVEL_NO_ACT));
+ rec = ignore_find_full(servertag, mask, patternarg, channels,
+ IGNORE_FIND_PATTERN | ((level & MSGLEVEL_NO_ACT) ? IGNORE_FIND_NOACT : 0));
new_ignore = rec == NULL;
if (rec == NULL) {
rec = g_new0(IGNORE_REC, 1);
rec->mask = mask == NULL || *mask == '\0' ||
- strcmp(mask, "*") == 0 ? NULL : g_strdup(mask);
+ g_strcmp0(mask, "*") == 0 ? NULL : g_strdup(mask);
rec->channels = channels;
} else {
g_free_and_null(rec->pattern);
@@ -214,7 +218,7 @@ static void cmd_unignore(const char *data)
{
IGNORE_REC *rec;
GSList *tmp;
- char *mask;
+ char *mask, *mask_orig;
void *free_arg;
if (!cmd_get_params(data, &free_arg, 1, &mask))
@@ -223,6 +227,10 @@ static void cmd_unignore(const char *data)
if (*mask == '\0')
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+ /* Save the mask string here since it might be modified in the code
+ * below and we need it to print meaningful error messages. */
+ mask_orig = mask;
+
if (is_numeric(mask, ' ')) {
/* with index number */
tmp = g_slist_nth(ignores, atoi(mask)-1);
@@ -236,9 +244,9 @@ static void cmd_unignore(const char *data)
chans[0] = mask;
mask = NULL;
}
- rec = ignore_find_noact("*", mask, (char **) chans, 0);
+ rec = ignore_find_full("*", mask, NULL, (char **) chans, 0);
if (rec == NULL) {
- rec = ignore_find_noact("*", mask, (char **) chans, 1);
+ rec = ignore_find_full("*", mask, NULL, (char **) chans, IGNORE_FIND_NOACT);
}
}
@@ -247,7 +255,7 @@ static void cmd_unignore(const char *data)
ignore_update_rec(rec);
} else {
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
- TXT_IGNORE_NOT_FOUND, mask);
+ TXT_IGNORE_NOT_FOUND, mask_orig);
}
cmd_params_free(free_arg);
}
diff --git a/src/fe-common/core/fe-log.c b/src/fe-common/core/fe-log.c
index 5ee72d8b..5bc5c4e1 100644
--- a/src/fe-common/core/fe-log.c
+++ b/src/fe-common/core/fe-log.c
@@ -43,11 +43,11 @@
static int autolog_level;
static int autoremove_tag;
-static const char *autolog_path;
+static char *autolog_path;
static THEME_REC *log_theme;
static int skip_next_printtext;
-static const char *log_theme_name;
+static char *log_theme_name;
static int log_dir_create_mode;
@@ -87,8 +87,9 @@ static void cmd_log_open(const char *data)
int level;
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST |
- PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_OPTIONS,
- "log open", &optlist, &fname, &levels))
+ PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_OPTIONS |
+ PARAM_FLAG_STRIP_TRAILING_WS, "log open", &optlist,
+ &fname, &levels))
return;
if (*fname == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
@@ -452,7 +453,7 @@ static void autolog_open(SERVER_REC *server, const char *server_tag,
log_item_add(log, LOG_ITEM_TARGET, target, server_tag);
dir = g_path_get_dirname(log->real_fname);
- mkpath(dir, log_dir_create_mode);
+ g_mkdir_with_parents(dir, log_dir_create_mode);
g_free(dir);
log->temp = TRUE;
@@ -485,7 +486,7 @@ static void autolog_open_check(TEXT_DEST_REC *dest)
return;
if (target != NULL)
- autolog_open(server, server_tag, strcmp(target, "*") ? target : deftarget);
+ autolog_open(server, server_tag, g_strcmp0(target, "*") ? target : deftarget);
}
static void log_single_line(WINDOW_REC *window, const char *server_tag,
@@ -616,7 +617,7 @@ static void sig_window_item_remove(WINDOW_REC *window, WI_ITEM_REC *item)
static void sig_log_locked(LOG_REC *log)
{
printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
- TXT_LOG_LOCKED, log->fname);
+ TXT_LOG_LOCKED, log->real_fname);
}
static void sig_log_create_failed(LOG_REC *log)
@@ -629,7 +630,7 @@ static void sig_log_create_failed(LOG_REC *log)
static void sig_log_new(LOG_REC *log)
{
if (!settings_get_bool("awaylog_colors") &&
- strcmp(log->fname, settings_get_str("awaylog_file")) == 0)
+ g_strcmp0(log->fname, settings_get_str("awaylog_file")) == 0)
log->colorizer = log_colorizer_strip;
}
@@ -656,11 +657,11 @@ static void sig_awaylog_show(LOG_REC *log, gpointer pmsgs, gpointer pfilepos)
filepos = GPOINTER_TO_INT(pfilepos);
if (msgs == 0)
- printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_NO_AWAY_MSGS, log->fname);
+ printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_NO_AWAY_MSGS, log->real_fname);
else {
- printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_AWAY_MSGS, log->fname, msgs);
+ printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_AWAY_MSGS, log->real_fname, msgs);
- str = g_strdup_printf("\"%s\" %d", log->fname, filepos);
+ str = g_strdup_printf("\"%s\" %d", log->real_fname, filepos);
signal_emit("command cat", 1, str);
g_free(str);
}
@@ -675,9 +676,11 @@ static void sig_theme_destroyed(THEME_REC *theme)
static void read_settings(void)
{
int old_autolog = autolog_level;
- int log_file_create_mode;
+ int log_file_create_mode;
+
+ g_free_not_null(autolog_path);
+ autolog_path = g_strdup(settings_get_str("autolog_path"));
- autolog_path = settings_get_str("autolog_path");
autolog_level = !settings_get_bool("autolog") ? 0 :
settings_get_level("autolog_level");
@@ -687,9 +690,14 @@ static void read_settings(void)
/* write to log files with different theme? */
if (log_theme_name != NULL)
signal_remove("print format", (SIGNAL_FUNC) sig_print_format);
- log_theme_name = settings_get_str("log_theme");
- if (*log_theme_name == '\0')
+
+ g_free_not_null(log_theme_name);
+ log_theme_name = g_strdup(settings_get_str("log_theme"));
+
+ if (*log_theme_name == '\0') {
+ g_free(log_theme_name);
log_theme_name = NULL;
+ }
else
signal_add("print format", (SIGNAL_FUNC) sig_print_format);
@@ -752,7 +760,7 @@ void fe_log_deinit(void)
{
g_source_remove(autoremove_tag);
if (log_theme_name != NULL)
- signal_remove("print format", (SIGNAL_FUNC) sig_print_format);
+ signal_remove("print format", (SIGNAL_FUNC) sig_print_format);
command_unbind("log", (SIGNAL_FUNC) cmd_log);
command_unbind("log open", (SIGNAL_FUNC) cmd_log_open);
@@ -776,4 +784,7 @@ void fe_log_deinit(void)
if (autolog_ignore_targets != NULL)
g_strfreev(autolog_ignore_targets);
+
+ g_free_not_null(autolog_path);
+ g_free_not_null(log_theme_name);
}
diff --git a/src/fe-common/core/fe-messages.c b/src/fe-common/core/fe-messages.c
index 95d9a9d8..487a5754 100644
--- a/src/fe-common/core/fe-messages.c
+++ b/src/fe-common/core/fe-messages.c
@@ -92,7 +92,7 @@ char *expand_emphasis(WI_ITEM_REC *item, const char *text)
use emphasis on them. */
int found;
char c;
- char *end2;
+ char *end2;
/* check if _foo_ is a nick */
c = end[1];
@@ -175,6 +175,7 @@ static void sig_message_public(SERVER_REC *server, const char *msg,
int for_me, print_channel, level;
char *nickmode, *color, *freemsg = NULL;
HILIGHT_REC *hilight;
+ TEXT_DEST_REC dest;
/* NOTE: this may return NULL if some channel is just closed with
/WINDOW CLOSE and server still sends the few last messages */
@@ -183,7 +184,9 @@ static void sig_message_public(SERVER_REC *server, const char *msg,
nickrec = nicklist_find(chanrec, nick);
for_me = !settings_get_bool("hilight_nick_matches") ? FALSE :
- nick_match_msg(chanrec, msg, server->nick);
+ !settings_get_bool("hilight_nick_matches_everywhere") ?
+ nick_match_msg(chanrec, msg, server->nick) :
+ nick_match_msg_everywhere(chanrec, msg, server->nick);
hilight = for_me ? NULL :
hilight_match_nick(server, target, nick, address, MSGLEVEL_PUBLIC, msg);
color = (hilight == NULL) ? NULL : hilight_get_color(hilight);
@@ -212,10 +215,11 @@ static void sig_message_public(SERVER_REC *server, const char *msg,
if (printnick == NULL)
printnick = nick;
+ format_create_dest(&dest, server, target, level, NULL);
+ dest.address = address;
+ dest.nick = nick;
if (color != NULL) {
/* highlighted nick */
- TEXT_DEST_REC dest;
- format_create_dest(&dest, server, target, level, NULL);
hilight_update_text_dest(&dest,hilight);
if (!print_channel) /* message to active channel in window */
printformat_dest(&dest, TXT_PUBMSG_HILIGHT, color,
@@ -226,15 +230,15 @@ static void sig_message_public(SERVER_REC *server, const char *msg,
nickmode);
} else {
if (!print_channel)
- printformat(server, target, level,
+ printformat_dest(&dest,
for_me ? TXT_PUBMSG_ME : TXT_PUBMSG,
printnick, msg, nickmode);
else
- printformat(server, target, level,
+ printformat_dest(&dest,
for_me ? TXT_PUBMSG_ME_CHANNEL :
TXT_PUBMSG_CHANNEL,
printnick, target, msg, nickmode);
- }
+ }
g_free_not_null(nickmode);
g_free_not_null(freemsg);
@@ -242,13 +246,16 @@ static void sig_message_public(SERVER_REC *server, const char *msg,
}
static void sig_message_private(SERVER_REC *server, const char *msg,
- const char *nick, const char *address)
+ const char *nick, const char *address, const char *target)
{
QUERY_REC *query;
char *freemsg = NULL;
int level = MSGLEVEL_MSGS;
- query = query_find(server, nick);
+ /* own message returned by bouncer? */
+ int own = (!g_strcmp0(nick, server->nick));
+
+ query = query_find(server, own ? target : nick);
if (settings_get_bool("emphasis"))
msg = freemsg = expand_emphasis((WI_ITEM_REC *) query, msg);
@@ -256,9 +263,15 @@ static void sig_message_private(SERVER_REC *server, const char *msg,
if (ignore_check(server, nick, address, NULL, msg, level | MSGLEVEL_NO_ACT))
level |= MSGLEVEL_NO_ACT;
- printformat(server, nick, level,
- query == NULL ? TXT_MSG_PRIVATE :
- TXT_MSG_PRIVATE_QUERY, nick, address, msg);
+ if (own) {
+ printformat(server, target, level,
+ query == NULL ? TXT_OWN_MSG_PRIVATE :
+ TXT_OWN_MSG_PRIVATE_QUERY, target, msg, server->nick);
+ } else {
+ printformat(server, nick, level,
+ query == NULL ? TXT_MSG_PRIVATE :
+ TXT_MSG_PRIVATE_QUERY, nick, address, msg);
+ }
g_free_not_null(freemsg);
}
@@ -314,8 +327,8 @@ static void sig_message_own_private(SERVER_REC *server, const char *msg,
/* this should only happen if some special target failed and
we should display some error message. currently the special
targets are only ',' and '.'. */
- g_return_if_fail(strcmp(origtarget, ",") == 0 ||
- strcmp(origtarget, ".") == 0);
+ g_return_if_fail(g_strcmp0(origtarget, ",") == 0 ||
+ g_strcmp0(origtarget, ".") == 0);
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
*origtarget == ',' ? TXT_NO_MSGS_GOT :
@@ -383,8 +396,9 @@ static void sig_message_quit(SERVER_REC *server, const char *nick,
count = 0; windows = NULL;
chans = g_string_new(NULL);
for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
+ CHANNEL_REC *rec;
level = MSGLEVEL_QUITS;
- CHANNEL_REC *rec = tmp->data;
+ rec = tmp->data;
if (!nicklist_find(rec, nick))
continue;
@@ -560,7 +574,7 @@ static int printnick_exists(NICK_REC *first, NICK_REC *ignore,
while (first != NULL) {
if (first != ignore) {
printnick = g_hash_table_lookup(printnicks, first);
- if (printnick != NULL && strcmp(printnick, nick) == 0)
+ if (printnick != NULL && g_strcmp0(printnick, nick) == 0)
return TRUE;
}
@@ -589,9 +603,6 @@ static void sig_nicklist_new(CHANNEL_REC *channel, NICK_REC *nick)
char *nickhost, *p;
int n;
- if (nick->host == NULL)
- return;
-
firstnick = g_hash_table_lookup(channel->nicks, nick->nick);
if (firstnick->next == NULL)
return;
@@ -604,6 +615,9 @@ static void sig_nicklist_new(CHANNEL_REC *channel, NICK_REC *nick)
return; /* nope, we have it */
}
+ if (nick->host == NULL)
+ return;
+
/* identical nick already exists, have to change it somehow.. */
p = strchr(nick->host, '@');
if (p == NULL) p = nick->host; else p++;
@@ -685,6 +699,7 @@ void fe_messages_init(void)
(GCompareFunc) g_direct_equal);
settings_add_bool("lookandfeel", "hilight_nick_matches", TRUE);
+ settings_add_bool("lookandfeel", "hilight_nick_matches_everywhere", FALSE);
settings_add_bool("lookandfeel", "emphasis", TRUE);
settings_add_bool("lookandfeel", "emphasis_replace", FALSE);
settings_add_bool("lookandfeel", "emphasis_multiword", FALSE);
diff --git a/src/fe-common/core/fe-modules.c b/src/fe-common/core/fe-modules.c
index df97ceb1..27b6b4c1 100644
--- a/src/fe-common/core/fe-modules.c
+++ b/src/fe-common/core/fe-modules.c
@@ -43,6 +43,10 @@ static void sig_module_error(void *number, const char *data,
printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
TXT_MODULE_LOAD_ERROR, rootmodule, submodule, data);
break;
+ case MODULE_ERROR_VERSION_MISMATCH:
+ printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+ TXT_MODULE_VERSION_MISMATCH, rootmodule, submodule, data);
+ break;
case MODULE_ERROR_INVALID:
printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
TXT_MODULE_INVALID, rootmodule, submodule);
diff --git a/src/fe-common/core/fe-queries.c b/src/fe-common/core/fe-queries.c
index 7599fb23..121417e4 100644
--- a/src/fe-common/core/fe-queries.c
+++ b/src/fe-common/core/fe-queries.c
@@ -326,12 +326,15 @@ static int sig_query_autoclose(void)
}
static void sig_message_private(SERVER_REC *server, const char *msg,
- const char *nick, const char *address)
+ const char *nick, const char *address, const char *target)
{
QUERY_REC *query;
+ /* own message returned by bouncer? */
+ int own = (!g_strcmp0(nick, server->nick));
+
/* create query window if needed */
- query = privmsg_get_query(server, nick, FALSE, MSGLEVEL_MSGS);
+ query = privmsg_get_query(server, own ? target : nick, FALSE, MSGLEVEL_MSGS);
/* reset the query's last_unread_msg timestamp */
if (query != NULL)
diff --git a/src/fe-common/core/fe-recode.c b/src/fe-common/core/fe-recode.c
index dbc43574..829c89e7 100644
--- a/src/fe-common/core/fe-recode.c
+++ b/src/fe-common/core/fe-recode.c
@@ -45,7 +45,7 @@ static const char *fe_recode_get_target (WI_ITEM_REC *witem)
static int fe_recode_compare_func (CONFIG_NODE *node1, CONFIG_NODE *node2)
{
- return strcmp(node1->key, node2->key);
+ return g_strcmp0(node1->key, node2->key);
}
/* SYNTAX: RECODE */
diff --git a/src/fe-common/core/fe-server.c b/src/fe-common/core/fe-server.c
index 2dec1d8a..f4c1d3ee 100644
--- a/src/fe-common/core/fe-server.c
+++ b/src/fe-common/core/fe-server.c
@@ -104,7 +104,7 @@ static SERVER_SETUP_REC *create_server_setup(GHashTable *optlist)
return server;
}
-static void cmd_server_add(const char *data)
+static void cmd_server_add_modify(const char *data, gboolean add)
{
GHashTable *optlist;
SERVER_SETUP_REC *rec;
@@ -113,7 +113,7 @@ static void cmd_server_add(const char *data)
int port;
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTIONS,
- "server add", &optlist, &addr, &portstr, &password))
+ "server add", &optlist, &addr, &portstr, &password))
return;
if (*addr == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
@@ -124,6 +124,13 @@ static void cmd_server_add(const char *data)
rec = server_setup_find(addr, port, chatnet);
if (rec == NULL) {
+ if (add == FALSE) {
+ cmd_params_free(free_arg);
+ printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+ TXT_SETUPSERVER_NOT_FOUND, addr, port);
+ return;
+ }
+
rec = create_server_setup(optlist);
if (rec == NULL) {
cmd_params_free(free_arg);
@@ -147,45 +154,73 @@ static void cmd_server_add(const char *data)
else if (g_hash_table_lookup(optlist, "4"))
rec->family = AF_INET;
- if (g_hash_table_lookup(optlist, "ssl"))
- rec->use_ssl = TRUE;
+ if (g_hash_table_lookup(optlist, "tls") || g_hash_table_lookup(optlist, "ssl"))
+ rec->use_tls = TRUE;
+
+ value = g_hash_table_lookup(optlist, "tls_cert");
+ if (value == NULL)
+ value = g_hash_table_lookup(optlist, "ssl_cert");
+ if (value != NULL && *value != '\0')
+ rec->tls_cert = g_strdup(value);
+
+ value = g_hash_table_lookup(optlist, "tls_pkey");
+ if (value == NULL)
+ value = g_hash_table_lookup(optlist, "ssl_pkey");
+ if (value != NULL && *value != '\0')
+ rec->tls_pkey = g_strdup(value);
- value = g_hash_table_lookup(optlist, "ssl_cert");
+ value = g_hash_table_lookup(optlist, "tls_pass");
+ if (value == NULL)
+ value = g_hash_table_lookup(optlist, "ssl_pass");
if (value != NULL && *value != '\0')
- rec->ssl_cert = g_strdup(value);
+ rec->tls_pass = g_strdup(value);
- value = g_hash_table_lookup(optlist, "ssl_pkey");
+ if (g_hash_table_lookup(optlist, "tls_verify") || g_hash_table_lookup(optlist, "ssl_verify"))
+ rec->tls_verify = TRUE;
+
+ value = g_hash_table_lookup(optlist, "tls_cafile");
+ if (value == NULL)
+ value = g_hash_table_lookup(optlist, "ssl_cafile");
if (value != NULL && *value != '\0')
- rec->ssl_pkey = g_strdup(value);
+ rec->tls_cafile = g_strdup(value);
- value = g_hash_table_lookup(optlist, "ssl_pass");
+ value = g_hash_table_lookup(optlist, "tls_capath");
+ if (value == NULL)
+ value = g_hash_table_lookup(optlist, "ssl_capath");
if (value != NULL && *value != '\0')
- rec->ssl_pass = g_strdup(value);
+ rec->tls_capath = g_strdup(value);
- if (g_hash_table_lookup(optlist, "ssl_verify"))
- rec->ssl_verify = TRUE;
+ value = g_hash_table_lookup(optlist, "tls_ciphers");
+ if (value == NULL)
+ value = g_hash_table_lookup(optlist, "ssl_ciphers");
+ if (value != NULL && *value != '\0')
+ rec->tls_ciphers = g_strdup(value);
- value = g_hash_table_lookup(optlist, "ssl_cafile");
+ value = g_hash_table_lookup(optlist, "tls_pinned_cert");
+ if (value == NULL)
+ value = g_hash_table_lookup(optlist, "ssl_pinned_cert");
if (value != NULL && *value != '\0')
- rec->ssl_cafile = g_strdup(value);
+ rec->tls_pinned_cert = g_strdup(value);
- value = g_hash_table_lookup(optlist, "ssl_capath");
+ value = g_hash_table_lookup(optlist, "tls_pinned_pubkey");
+ if (value == NULL)
+ value = g_hash_table_lookup(optlist, "ssl_pinned_pubkey");
if (value != NULL && *value != '\0')
- rec->ssl_capath = g_strdup(value);
+ rec->tls_pinned_pubkey = g_strdup(value);
- if ((rec->ssl_cafile != NULL && rec->ssl_cafile[0] != '\0')
- || (rec->ssl_capath != NULL && rec->ssl_capath[0] != '\0'))
- rec->ssl_verify = TRUE;
+ if ((rec->tls_cafile != NULL && rec->tls_cafile[0] != '\0')
+ || (rec->tls_capath != NULL && rec->tls_capath[0] != '\0'))
+ rec->tls_verify = TRUE;
- if ((rec->ssl_cert != NULL && rec->ssl_cert[0] != '\0') || rec->ssl_verify == TRUE)
- rec->use_ssl = TRUE;
+ if ((rec->tls_cert != NULL && rec->tls_cert[0] != '\0') || rec->tls_verify == TRUE)
+ rec->use_tls = TRUE;
if (g_hash_table_lookup(optlist, "auto")) rec->autoconnect = TRUE;
if (g_hash_table_lookup(optlist, "noauto")) rec->autoconnect = FALSE;
if (g_hash_table_lookup(optlist, "proxy")) rec->no_proxy = FALSE;
if (g_hash_table_lookup(optlist, "noproxy")) rec->no_proxy = TRUE;
- if (*password != '\0' && strcmp(password, "-") != 0) rec->password = g_strdup(password);
+ if (*password != '\0' && g_strcmp0(password, "-") != 0) rec->password = g_strdup(password);
value = g_hash_table_lookup(optlist, "host");
if (value != NULL && *value != '\0') {
rec->own_host = g_strdup(value);
@@ -201,6 +236,16 @@ static void cmd_server_add(const char *data)
cmd_params_free(free_arg);
}
+static void cmd_server_add(const char *data)
+{
+ cmd_server_add_modify(data, TRUE);
+}
+
+static void cmd_server_modify(const char *data)
+{
+ cmd_server_add_modify(data, FALSE);
+}
+
/* SYNTAX: SERVER REMOVE <address> [<port>] [<network>] */
static void cmd_server_remove(const char *data)
{
@@ -264,7 +309,7 @@ static void cmd_server_connect(const char *data)
"connect", &optlist, &addr))
return;
- if (*addr == '\0' || strcmp(addr, "+") == 0)
+ if (*addr == '\0' || g_strcmp0(addr, "+") == 0)
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
if (*addr == '+') window_create(NULL, FALSE);
@@ -384,10 +429,13 @@ void fe_server_init(void)
command_bind("server", NULL, (SIGNAL_FUNC) cmd_server);
command_bind("server connect", NULL, (SIGNAL_FUNC) cmd_server_connect);
command_bind("server add", NULL, (SIGNAL_FUNC) cmd_server_add);
+ command_bind("server modify", NULL, (SIGNAL_FUNC) cmd_server_modify);
command_bind("server remove", NULL, (SIGNAL_FUNC) cmd_server_remove);
command_bind_first("server", NULL, (SIGNAL_FUNC) server_command);
command_bind_first("disconnect", NULL, (SIGNAL_FUNC) server_command);
- command_set_options("server add", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath auto noauto proxy noproxy -host -port noautosendcmd");
+
+ command_set_options("server add", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +ssl_fingerprint tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers +tls_pinned_cert +tls_pinned_pubkey auto noauto proxy noproxy -host -port noautosendcmd");
+ command_set_options("server modify", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +ssl_fingerprint tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers +tls_pinned_cert +tls_pinned_pubkey auto noauto proxy noproxy -host -port noautosendcmd");
signal_add("server looking", (SIGNAL_FUNC) sig_server_looking);
signal_add("server connecting", (SIGNAL_FUNC) sig_server_connecting);
@@ -408,6 +456,7 @@ void fe_server_deinit(void)
command_unbind("server", (SIGNAL_FUNC) cmd_server);
command_unbind("server connect", (SIGNAL_FUNC) cmd_server_connect);
command_unbind("server add", (SIGNAL_FUNC) cmd_server_add);
+ command_unbind("server modify", (SIGNAL_FUNC) cmd_server_modify);
command_unbind("server remove", (SIGNAL_FUNC) cmd_server_remove);
command_unbind("server", (SIGNAL_FUNC) server_command);
command_unbind("disconnect", (SIGNAL_FUNC) server_command);
diff --git a/src/fe-common/core/fe-settings.c b/src/fe-common/core/fe-settings.c
index 96e03ceb..abbd45a8 100644
--- a/src/fe-common/core/fe-settings.c
+++ b/src/fe-common/core/fe-settings.c
@@ -53,7 +53,7 @@ static void set_print_pattern(const char *pattern)
if (stristr(rec->key, pattern) == NULL)
continue;
- if (strcmp(last_section, rec->section) != 0) {
+ if (g_strcmp0(last_section, rec->section) != 0) {
/* print section */
printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
TXT_SET_TITLE, rec->section);
@@ -67,6 +67,7 @@ static void set_print_pattern(const char *pattern)
static void set_boolean(const char *key, const char *value)
{
char *stripped_value;
+
stripped_value = g_strdup(value);
g_strstrip(stripped_value);
@@ -79,7 +80,7 @@ static void set_boolean(const char *key, const char *value)
else
printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_NOT_TOGGLE);
- g_free(stripped_value);
+ g_free(stripped_value);
}
static void set_int(const char *key, const char *value)
@@ -99,6 +100,24 @@ static void set_int(const char *key, const char *value)
settings_set_int(key, (int)longval);
}
+static void set_choice(const char *key, const char *value)
+{
+ char *stripped_value;
+
+ stripped_value = g_strdup(value);
+ g_strstrip(stripped_value);
+
+ if (settings_set_choice(key, stripped_value) == FALSE) {
+ SETTINGS_REC *rec = settings_get_record(key);
+ char *msg = g_strjoinv(", ", rec->choices);
+
+ printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_INVALID_CHOICE, msg);
+ g_free(msg);
+ }
+
+ g_free(stripped_value);
+}
+
/* SYNTAX: SET [-clear | -default] [<key> [<value>]] */
static void cmd_set(char *data)
{
@@ -108,7 +127,8 @@ static void cmd_set(char *data)
int clear, set_default;
SETTINGS_REC *rec;
- if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST | PARAM_FLAG_OPTIONS,
+ if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST |
+ PARAM_FLAG_OPTIONS,
"set", &optlist, &key, &value))
return;
@@ -126,7 +146,7 @@ static void cmd_set(char *data)
/* change the setting */
switch (rec->type) {
case SETTING_TYPE_BOOLEAN:
- if (clear)
+ if (clear)
settings_set_bool(key, FALSE);
else if (set_default)
settings_set_bool(key, rec->default_value.v_bool);
@@ -141,6 +161,12 @@ static void cmd_set(char *data)
else
set_int(key, value);
break;
+ case SETTING_TYPE_CHOICE:
+ if (clear || set_default)
+ settings_set_choice(key, rec->choices[rec->default_value.v_int]);
+ else
+ set_choice(key, value);
+ break;
case SETTING_TYPE_STRING:
settings_set_str(key, clear ? "" :
set_default ? rec->default_value.v_string :
@@ -149,32 +175,30 @@ static void cmd_set(char *data)
case SETTING_TYPE_TIME:
if (!settings_set_time(key, clear ? "0" :
set_default ? rec->default_value.v_string : value))
- printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
- TXT_INVALID_TIME);
+ printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_INVALID_TIME);
break;
case SETTING_TYPE_LEVEL:
if (!settings_set_level(key, clear ? "" :
set_default ? rec->default_value.v_string : value))
- printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
- TXT_INVALID_LEVEL);
+ printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_INVALID_LEVEL);
break;
case SETTING_TYPE_SIZE:
if (!settings_set_size(key, clear ? "0" :
set_default ? rec->default_value.v_string : value))
- printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
- TXT_INVALID_SIZE);
+ printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_INVALID_SIZE);
+ break;
+ case SETTING_TYPE_ANY:
+ /* Unpossible! */
break;
}
signal_emit("setup changed", 0);
- printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
- TXT_SET_TITLE, rec->section);
+ printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_SET_TITLE, rec->section);
set_print(rec);
} else
- printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
- TXT_SET_UNKNOWN, key);
+ printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_SET_UNKNOWN, key);
}
- cmd_params_free(free_arg);
+ cmd_params_free(free_arg);
}
/* SYNTAX: TOGGLE <key> [on|off|toggle] */
@@ -184,23 +208,24 @@ static void cmd_toggle(const char *data)
void *free_arg;
int type;
- if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &key, &value))
+ if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS, &key, &value))
return;
- if (*key == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+ if (*key == '\0')
+ cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
type = settings_get_type(key);
- if (type == -1)
+ if (type == SETTING_TYPE_ANY)
printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_SET_UNKNOWN, key);
else if (type != SETTING_TYPE_BOOLEAN)
printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_SET_NOT_BOOLEAN, key);
else {
set_boolean(key, *value != '\0' ? value : "TOGGLE");
- set_print(settings_get_record(key));
+ set_print(settings_get_record(key));
signal_emit("setup changed", 0);
}
- cmd_params_free(free_arg);
+ cmd_params_free(free_arg);
}
static int config_key_compare(CONFIG_NODE *node1, CONFIG_NODE *node2)
diff --git a/src/fe-common/core/fe-tls.c b/src/fe-common/core/fe-tls.c
new file mode 100644
index 00000000..ed206d18
--- /dev/null
+++ b/src/fe-common/core/fe-tls.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2015 Alexander Færøy <ahf@irssi.org>
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301,USA
+ */
+
+#include "module.h"
+#include "signals.h"
+#include "settings.h"
+#include "levels.h"
+#include "tls.h"
+
+#include "module-formats.h"
+#include "printtext.h"
+
+#include "fe-tls.h"
+
+static void tls_handshake_finished(SERVER_REC *server, TLS_REC *tls)
+{
+ GSList *certs = NULL;
+ GSList *subject = NULL;
+ GSList *issuer = NULL;
+ GString *str = NULL;
+ TLS_CERT_ENTRY_REC *data = NULL;
+
+ if (! settings_get_bool("tls_verbose_connect"))
+ return;
+
+ printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_HEADER);
+
+ for (certs = tls->certs; certs != NULL; certs = certs->next) {
+ TLS_CERT_REC *tls_cert_rec = certs->data;
+ str = g_string_new(NULL);
+
+ for (subject = tls_cert_rec->subject; subject != NULL; subject = subject->next) {
+ data = subject->data;
+ g_string_append_printf(str, "%s: %s, ", data->name, data->value);
+ }
+
+ if (str->len > 1)
+ g_string_truncate(str, str->len - 2);
+
+ printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_SUBJECT, str->str);
+ g_string_free(str, TRUE);
+
+ str = g_string_new(NULL);
+
+ for (issuer = tls_cert_rec->issuer; issuer != NULL; issuer = issuer->next) {
+ data = issuer->data;
+ g_string_append_printf(str, "%s: %s, ", data->name, data->value);
+ }
+
+ if (str->len > 1)
+ g_string_truncate(str, str->len - 2);
+
+ printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_ISSUER, str->str);
+ g_string_free(str, TRUE);
+ }
+
+ printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_PROTOCOL_VERSION, tls->protocol_version, tls->cipher_size, tls->cipher);
+
+ if (tls->ephemeral_key_algorithm != NULL)
+ printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_EPHEMERAL_KEY, tls->ephemeral_key_size, tls->ephemeral_key_algorithm);
+ else
+ printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_EPHEMERAL_KEY_UNAVAILBLE);
+
+ printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_PUBKEY, tls->public_key_size, tls->public_key_algorithm, tls->not_before, tls->not_after);
+ printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_PUBKEY_FINGERPRINT, tls->public_key_fingerprint, tls->public_key_fingerprint_algorithm);
+ printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_FINGERPRINT, tls->certificate_fingerprint, tls->certificate_fingerprint_algorithm);
+}
+
+void fe_tls_init(void)
+{
+ settings_add_bool("lookandfeel", "tls_verbose_connect", TRUE);
+
+ signal_add("tls handshake finished", (SIGNAL_FUNC)tls_handshake_finished);
+}
+
+void fe_tls_deinit(void)
+{
+ signal_remove("tls handshake finished", (SIGNAL_FUNC)tls_handshake_finished);
+}
diff --git a/src/fe-common/core/fe-tls.h b/src/fe-common/core/fe-tls.h
new file mode 100644
index 00000000..f0082477
--- /dev/null
+++ b/src/fe-common/core/fe-tls.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015 Alexander Færøy <ahf@irssi.org>
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301,USA
+ */
+
+#ifndef __FE_TLS_H
+#define __FE_TLS_H
+
+void fe_tls_init(void);
+void fe_tls_deinit(void);
+
+#endif
diff --git a/src/fe-common/core/fe-windows.c b/src/fe-common/core/fe-windows.c
index bf9d7154..0afa2914 100644
--- a/src/fe-common/core/fe-windows.c
+++ b/src/fe-common/core/fe-windows.c
@@ -35,30 +35,83 @@
GSList *windows; /* first in the list is the active window,
next is the last active, etc. */
+GSequence *windows_seq;
WINDOW_REC *active_win;
static int daytag;
static int daycheck; /* 0 = don't check, 1 = time is 00:00, check,
2 = time is 00:00, already checked */
+static int window_refnum_lookup(WINDOW_REC *window, void *refnum_p)
+{
+ int refnum = GPOINTER_TO_INT(refnum_p);
+ return window->refnum == refnum ? 0 : window->refnum < refnum ? -1 : 1;
+}
+
+static GSequenceIter *windows_seq_begin(void)
+{
+ return g_sequence_get_begin_iter(windows_seq);
+}
+
+static GSequenceIter *windows_seq_end(void)
+{
+ return g_sequence_get_end_iter(windows_seq);
+}
+
+static GSequenceIter *windows_seq_insert(WINDOW_REC *rec)
+{
+ return g_sequence_insert_sorted(windows_seq, rec, (GCompareDataFunc)window_refnum_cmp, NULL);
+}
+
+static GSequenceIter *windows_seq_refnum_lookup(int refnum)
+{
+ return g_sequence_lookup(windows_seq, GINT_TO_POINTER(refnum), (GCompareDataFunc)window_refnum_lookup, NULL);
+}
+
+static void windows_seq_changed(GSequenceIter *iter)
+{
+ g_sequence_sort_changed(iter, (GCompareDataFunc)window_refnum_cmp, NULL);
+}
+
+static GSequenceIter *windows_seq_window_lookup(WINDOW_REC *rec)
+{
+ return g_sequence_lookup(windows_seq, rec, (GCompareDataFunc)window_refnum_cmp, NULL);
+}
+
+/* search to the numerically right iterator of refnum */
+static GSequenceIter *windows_seq_refnum_search_right(int refnum)
+{
+ return g_sequence_search(windows_seq, GINT_TO_POINTER(refnum), (GCompareDataFunc)window_refnum_lookup, NULL);
+}
+
+/* we want to find the numerically left iterator of refnum, so we
+ search the right of the previous refnum. but we need to figure out
+ the case where the iterator is already at the beginning, i.e
+ iter->refnum >= refnum */
+static GSequenceIter *windows_seq_refnum_search_left(int refnum)
+{
+ GSequenceIter *iter = windows_seq_refnum_search_right(refnum - 1);
+ return iter == windows_seq_begin() ? NULL : g_sequence_iter_prev(iter);
+}
+
static int window_get_new_refnum(void)
{
WINDOW_REC *win;
- GSList *tmp;
+ GSequenceIter *iter, *end;
int refnum;
refnum = 1;
- tmp = windows;
- while (tmp != NULL) {
- win = tmp->data;
+ iter = windows_seq_begin();
+ end = windows_seq_end();
- if (refnum != win->refnum) {
- tmp = tmp->next;
- continue;
- }
+ while (iter != end) {
+ win = g_sequence_get(iter);
+
+ if (refnum != win->refnum)
+ return refnum;
refnum++;
- tmp = windows;
+ iter = g_sequence_iter_next(iter);
}
return refnum;
@@ -73,6 +126,7 @@ WINDOW_REC *window_create(WI_ITEM_REC *item, int automatic)
rec->level = settings_get_level("window_default_level");
windows = g_slist_prepend(windows, rec);
+ windows_seq_insert(rec);
signal_emit("window created", 2, rec, GINT_TO_POINTER(automatic));
if (item != NULL) window_item_add(rec, item, automatic);
@@ -84,6 +138,19 @@ WINDOW_REC *window_create(WI_ITEM_REC *item, int automatic)
return rec;
}
+static void window_set_refnum0(WINDOW_REC *window, int refnum)
+{
+ int old_refnum;
+
+ g_return_if_fail(window != NULL);
+ g_return_if_fail(refnum >= 1);
+ if (window->refnum == refnum) return;
+
+ old_refnum = window->refnum;
+ window->refnum = refnum;
+ signal_emit("window refnum changed", 2, window, GINT_TO_POINTER(old_refnum));
+}
+
/* removed_refnum was removed from the windows list, pack the windows so
there won't be any holes. If there is any holes after removed_refnum,
leave the windows behind it alone. */
@@ -91,23 +158,37 @@ static void windows_pack(int removed_refnum)
{
WINDOW_REC *window;
int refnum;
+ GSequenceIter *iter, *end;
+
+ refnum = removed_refnum + 1;
+ end = windows_seq_end();
+ iter = windows_seq_refnum_lookup(refnum);
+ if (iter == NULL) return;
- for (refnum = removed_refnum+1;; refnum++) {
- window = window_find_refnum(refnum);
- if (window == NULL || window->sticky_refnum)
+ while (iter != end) {
+ window = g_sequence_get(iter);
+
+ if (window == NULL || window->sticky_refnum || window->refnum != refnum)
break;
- window_set_refnum(window, refnum-1);
+ window_set_refnum0(window, refnum - 1);
+ windows_seq_changed(iter);
+
+ refnum++;
+ iter = g_sequence_iter_next(iter);
}
}
void window_destroy(WINDOW_REC *window)
{
+ GSequenceIter *iter;
g_return_if_fail(window != NULL);
if (window->destroying) return;
window->destroying = TRUE;
windows = g_slist_remove(windows, window);
+ iter = windows_seq_window_lookup(window);
+ if (iter != NULL) g_sequence_remove(iter);
if (active_win == window) {
active_win = NULL; /* it's corrupted */
@@ -184,31 +265,37 @@ void window_change_server(WINDOW_REC *window, void *server)
if (window->active_server != active) {
window->active_server = active;
signal_emit("window server changed", 2, window, active);
- }
+ }
}
void window_set_refnum(WINDOW_REC *window, int refnum)
{
- GSList *tmp;
+ GSequenceIter *other_iter, *window_iter;
int old_refnum;
g_return_if_fail(window != NULL);
g_return_if_fail(refnum >= 1);
if (window->refnum == refnum) return;
- for (tmp = windows; tmp != NULL; tmp = tmp->next) {
- WINDOW_REC *rec = tmp->data;
+ other_iter = windows_seq_refnum_lookup(refnum);
+ window_iter = windows_seq_refnum_lookup(window->refnum);
- if (rec->refnum == refnum) {
- rec->refnum = window->refnum;
- signal_emit("window refnum changed", 2, rec, GINT_TO_POINTER(refnum));
- break;
- }
+ if (other_iter != NULL) {
+ WINDOW_REC *rec = g_sequence_get(other_iter);
+
+ rec->refnum = window->refnum;
+ signal_emit("window refnum changed", 2, rec, GINT_TO_POINTER(refnum));
}
old_refnum = window->refnum;
window->refnum = refnum;
signal_emit("window refnum changed", 2, window, GINT_TO_POINTER(old_refnum));
+
+ if (window_iter != NULL && other_iter != NULL) {
+ g_sequence_swap(other_iter, window_iter);
+ } else {
+ windows_seq_changed(window_iter);
+ }
}
void window_set_name(WINDOW_REC *window, const char *name)
@@ -229,11 +316,16 @@ void window_set_history(WINDOW_REC *window, const char *name)
else
window->history_name = g_strdup(name);
- signal_emit("window history changed", 1, window, oldname);
+ signal_emit("window history changed", 2, window, oldname);
g_free_not_null(oldname);
}
+void window_clear_history(WINDOW_REC *window, const char *name)
+{
+ signal_emit("window history cleared", 2, window, name);
+}
+
void window_set_level(WINDOW_REC *window, int level)
{
g_return_if_fail(window != NULL);
@@ -341,13 +433,13 @@ WINDOW_REC *window_find_closest(void *server, const char *name, int level)
WINDOW_REC *window_find_refnum(int refnum)
{
- GSList *tmp;
+ GSequenceIter *iter;
- for (tmp = windows; tmp != NULL; tmp = tmp->next) {
- WINDOW_REC *rec = tmp->data;
+ iter = windows_seq_refnum_lookup(refnum);
+ if (iter != NULL) {
+ WINDOW_REC *rec = g_sequence_get(iter);
- if (rec->refnum == refnum)
- return rec;
+ return rec;
}
return NULL;
@@ -395,71 +487,86 @@ WINDOW_REC *window_find_item(SERVER_REC *server, const char *name)
int window_refnum_prev(int refnum, int wrap)
{
- GSList *tmp;
- int prev, max;
+ WINDOW_REC *rec;
+ GSequenceIter *iter, *end;
- max = prev = -1;
- for (tmp = windows; tmp != NULL; tmp = tmp->next) {
- WINDOW_REC *rec = tmp->data;
+ iter = windows_seq_refnum_search_left(refnum);
+ end = windows_seq_end();
- if (rec->refnum < refnum && (prev == -1 || rec->refnum > prev))
- prev = rec->refnum;
- if (wrap && (max == -1 || rec->refnum > max))
- max = rec->refnum;
+ if (iter != NULL) {
+ rec = g_sequence_get(iter);
+ return rec->refnum;
+ }
+
+ if (wrap) {
+ iter = g_sequence_iter_prev(end);
+ if (iter != end) {
+ rec = g_sequence_get(iter);
+ return rec->refnum;
+ }
}
- return prev != -1 ? prev : max;
+ return -1;
}
int window_refnum_next(int refnum, int wrap)
{
- GSList *tmp;
- int min, next;
+ WINDOW_REC *rec;
+ GSequenceIter *iter, *end;
- min = next = -1;
- for (tmp = windows; tmp != NULL; tmp = tmp->next) {
- WINDOW_REC *rec = tmp->data;
+ iter = windows_seq_refnum_search_right(refnum);
+ end = windows_seq_end();
- if (rec->refnum > refnum && (next == -1 || rec->refnum < next))
- next = rec->refnum;
- if (wrap && (min == -1 || rec->refnum < min))
- min = rec->refnum;
+ if (iter != end) {
+ rec = g_sequence_get(iter);
+ return rec->refnum;
}
- return next != -1 ? next : min;
+ if (wrap) {
+ iter = windows_seq_begin();
+ if (iter != end) {
+ rec = g_sequence_get(iter);
+ return rec->refnum;
+ }
+ }
+
+ return -1;
}
int windows_refnum_last(void)
{
- GSList *tmp;
- int max;
-
- max = -1;
- for (tmp = windows; tmp != NULL; tmp = tmp->next) {
- WINDOW_REC *rec = tmp->data;
+ WINDOW_REC *rec;
+ GSequenceIter *end, *iter;
- if (rec->refnum > max)
- max = rec->refnum;
+ end = windows_seq_end();
+ iter = g_sequence_iter_prev(end);
+ if (iter != end) {
+ rec = g_sequence_get(iter);
+ return rec->refnum;
}
- return max;
+ return -1;
}
int window_refnum_cmp(WINDOW_REC *w1, WINDOW_REC *w2)
{
- return w1->refnum < w2->refnum ? -1 : 1;
+ return w1 == w2 ? 0 : w1->refnum < w2->refnum ? -1 : 1;
}
GSList *windows_get_sorted(void)
{
- GSList *tmp, *sorted;
+ GSequenceIter *iter, *begin;
+ GSList *sorted;
sorted = NULL;
- for (tmp = windows; tmp != NULL; tmp = tmp->next) {
- WINDOW_REC *rec = tmp->data;
+ iter = windows_seq_end();
+ begin = windows_seq_begin();
+
+ while (iter != begin) {
+ iter = g_sequence_iter_prev(iter);
+ WINDOW_REC *rec = g_sequence_get(iter);
- sorted = g_slist_insert_sorted(sorted, rec, (GCompareFunc)
- window_refnum_cmp);
+ sorted = g_slist_prepend(sorted, rec);
}
return sorted;
@@ -704,6 +811,7 @@ static void read_settings(void)
void windows_init(void)
{
active_win = NULL;
+ windows_seq = g_sequence_new(NULL);
daycheck = 0; daytag = -1;
settings_add_bool("lookandfeel", "window_auto_change", FALSE);
settings_add_bool("lookandfeel", "windows_auto_renumber", TRUE);
@@ -728,4 +836,6 @@ void windows_deinit(void)
signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
signal_remove("server connect failed", (SIGNAL_FUNC) sig_server_disconnected);
signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+ g_sequence_free(windows_seq);
+ windows_seq = NULL;
}
diff --git a/src/fe-common/core/fe-windows.h b/src/fe-common/core/fe-windows.h
index 613f15f8..32d6cfcd 100644
--- a/src/fe-common/core/fe-windows.h
+++ b/src/fe-common/core/fe-windows.h
@@ -66,6 +66,7 @@ void window_change_server(WINDOW_REC *window, void *server);
void window_set_refnum(WINDOW_REC *window, int refnum);
void window_set_name(WINDOW_REC *window, const char *name);
void window_set_history(WINDOW_REC *window, const char *name);
+void window_clear_history(WINDOW_REC *window, const char *name);
void window_set_level(WINDOW_REC *window, int level);
void window_set_immortal(WINDOW_REC *window, int immortal);
diff --git a/src/fe-common/core/formats.c b/src/fe-common/core/formats.c
index 375b00eb..17c13a97 100644
--- a/src/fe-common/core/formats.c
+++ b/src/fe-common/core/formats.c
@@ -68,7 +68,7 @@ static void format_expand_code(const char **format, GString *out, int *flags)
if (flags == NULL) {
/* flags are being ignored - skip the code */
- while (**format != ']')
+ while (**format != ']' && **format != '\0')
(*format)++;
return;
}
@@ -131,6 +131,8 @@ void unformat_24bit_color(char **ptr, int off, int *fgcolor, int *bgcolor, int *
unsigned char rgbx[4];
unsigned int i;
for (i = 0; i < 4; ++i) {
+ if ((*ptr)[i + off] == '\0')
+ return;
rgbx[i] = (*ptr)[i + off];
}
rgbx[3] -= 0x20;
@@ -244,6 +246,10 @@ int format_expand_styles(GString *out, const char **format, int *flags)
case '[':
/* code */
format_expand_code(format, out, flags);
+ if ((*format)[0] == '\0')
+ /* oops, reached end prematurely */
+ (*format)--;
+
break;
case 'x':
case 'X':
@@ -420,33 +426,17 @@ void format_create_dest_tag(TEXT_DEST_REC *dest, void *server,
window_find_closest(server, target, level);
}
-static int advance (char const **str, gboolean utf8)
-{
- if (utf8) {
- gunichar c;
-
- c = g_utf8_get_char(*str);
- *str = g_utf8_next_char(*str);
-
- return unichar_isprint(c) ? mk_wcwidth(c) : 1;
- } else {
- *str += 1;
-
- return 1;
- }
-}
-
/* Return length of text part in string (ie. without % codes) */
int format_get_length(const char *str)
{
GString *tmp;
int len;
- gboolean utf8;
+ int utf8;
int adv = 0;
g_return_val_if_fail(str != NULL, 0);
- utf8 = is_utf8() && g_utf8_validate(str, -1, NULL);
+ utf8 = string_policy(str);
tmp = g_string_new(NULL);
len = 0;
@@ -465,7 +455,7 @@ int format_get_length(const char *str)
len++;
}
- len += advance(&str, utf8);
+ len += string_advance(&str, utf8);
}
g_string_free(tmp, TRUE);
@@ -480,12 +470,12 @@ int format_real_length(const char *str, int len)
GString *tmp;
const char *start;
const char *oldstr;
- gboolean utf8;
+ int utf8;
int adv = 0;
g_return_val_if_fail(str != NULL, 0);
g_return_val_if_fail(len >= 0, 0);
- utf8 = is_utf8() && g_utf8_validate(str, -1, NULL);
+ utf8 = string_policy(str);
start = str;
tmp = g_string_new(NULL);
@@ -507,7 +497,7 @@ int format_real_length(const char *str, int len)
}
oldstr = str;
- len -= advance(&str, utf8);
+ len -= string_advance(&str, utf8);
if (len < 0)
str = oldstr;
}
@@ -905,10 +895,13 @@ static const char *get_ansi_color(THEME_REC *theme, const char *str,
switch (num) {
case 0:
- /* reset colors back to default */
+ /* reset colors and attributes back to default */
fg = theme->default_color;
bg = -1;
- flags &= ~(GUI_PRINT_FLAG_COLOR_24_FG | GUI_PRINT_FLAG_COLOR_24_BG | GUI_PRINT_FLAG_INDENT);
+ flags &= ~(GUI_PRINT_FLAG_INDENT |
+ GUI_PRINT_FLAG_BOLD | GUI_PRINT_FLAG_ITALIC | GUI_PRINT_FLAG_UNDERLINE |
+ GUI_PRINT_FLAG_BLINK | GUI_PRINT_FLAG_REVERSE |
+ GUI_PRINT_FLAG_COLOR_24_FG | GUI_PRINT_FLAG_COLOR_24_BG);
break;
case 1:
/* hilight */
@@ -967,6 +960,7 @@ static const char *get_ansi_color(THEME_REC *theme, const char *str,
str++;
for (num2 = 0; i_isdigit(*str); str++)
num2 = num2*10 + (*str-'0');
+ if (*str == '\0') return start;
switch (num2) {
case 2:
@@ -984,6 +978,8 @@ static const char *get_ansi_color(THEME_REC *theme, const char *str,
for (; i_isdigit(*str); str++)
num2 = (num2&~0xff) |
(((num2&0xff) * 10 + (*str-'0'))&0xff);
+
+ if (*str == '\0') return start;
}
if (i == -1) break;
@@ -1012,6 +1008,7 @@ static const char *get_ansi_color(THEME_REC *theme, const char *str,
str++;
for (num2 = 0; i_isdigit(*str); str++)
num2 = num2*10 + (*str-'0');
+ if (*str == '\0') return start;
if (num == 38) {
flags &= ~GUI_PRINT_FLAG_COLOR_24_FG;
@@ -1235,11 +1232,7 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text)
}
}
- if (type == 7) {
- /* bell */
- if (settings_get_bool("bell_beeps"))
- signal_emit("beep", 0);
- } else if (type == 4 && *ptr == FORMAT_STYLE_CLRTOEOL) {
+ if (type == 4 && *ptr == FORMAT_STYLE_CLRTOEOL) {
/* clear to end of line */
flags |= GUI_PRINT_FLAG_CLRTOEOL;
}
@@ -1354,6 +1347,9 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text)
bgcolor = *ptr==(char)0xff ? -1 : *ptr-'0';
}
}
+ if (*ptr == '\0')
+ break;
+
ptr++;
break;
case 6:
diff --git a/src/fe-common/core/formats.h b/src/fe-common/core/formats.h
index 07e1832c..8efd204c 100644
--- a/src/fe-common/core/formats.h
+++ b/src/fe-common/core/formats.h
@@ -45,11 +45,15 @@ struct _FORMAT_REC {
#define PRINT_FLAG_SET_SERVERTAG 0x0010
#define PRINT_FLAG_UNSET_SERVERTAG 0x0020
+typedef struct _HILIGHT_REC HILIGHT_REC;
+
typedef struct _TEXT_DEST_REC {
WINDOW_REC *window;
SERVER_REC *server;
const char *server_tag; /* if server is non-NULL, must be server->tag */
const char *target;
+ const char *nick;
+ const char *address;
int level;
int hilight_priority;
diff --git a/src/fe-common/core/hilight-text.c b/src/fe-common/core/hilight-text.c
index 4b914517..037cde5c 100644
--- a/src/fe-common/core/hilight-text.c
+++ b/src/fe-common/core/hilight-text.c
@@ -49,7 +49,7 @@ static void reset_level_cache(void)
HILIGHT_REC *rec = tmp->data;
if (never_hilight_level & rec->level)
- never_hilight_level &= ~rec->level;
+ never_hilight_level &= ~rec->level;
}
}
@@ -66,22 +66,23 @@ static void hilight_add_config(HILIGHT_REC *rec)
g_return_if_fail(rec != NULL);
node = iconfig_node_traverse("(hilights", TRUE);
- node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
-
- iconfig_node_set_str(node, "text", rec->text);
- if (rec->level > 0) iconfig_node_set_int(node, "level", rec->level);
- if (rec->color) iconfig_node_set_str(node, "color", rec->color);
- if (rec->act_color) iconfig_node_set_str(node, "act_color", rec->act_color);
- if (rec->priority > 0) iconfig_node_set_int(node, "priority", rec->priority);
- iconfig_node_set_bool(node, "nick", rec->nick);
- iconfig_node_set_bool(node, "word", rec->word);
- if (rec->nickmask) iconfig_node_set_bool(node, "mask", TRUE);
- if (rec->fullword) iconfig_node_set_bool(node, "fullword", TRUE);
- if (rec->regexp) iconfig_node_set_bool(node, "regexp", TRUE);
- if (rec->servertag) iconfig_node_set_str(node, "servertag", rec->servertag);
+ node = iconfig_node_section(node, NULL, NODE_TYPE_BLOCK);
+
+ iconfig_node_set_str(node, "text", rec->text);
+ if (rec->level > 0) iconfig_node_set_int(node, "level", rec->level);
+ if (rec->color) iconfig_node_set_str(node, "color", rec->color);
+ if (rec->act_color) iconfig_node_set_str(node, "act_color", rec->act_color);
+ if (rec->priority > 0) iconfig_node_set_int(node, "priority", rec->priority);
+ iconfig_node_set_bool(node, "nick", rec->nick);
+ iconfig_node_set_bool(node, "word", rec->word);
+ if (rec->nickmask) iconfig_node_set_bool(node, "mask", TRUE);
+ if (rec->fullword) iconfig_node_set_bool(node, "fullword", TRUE);
+ if (rec->regexp) iconfig_node_set_bool(node, "regexp", TRUE);
+ if (rec->case_sensitive) iconfig_node_set_bool(node, "matchcase", TRUE);
+ if (rec->servertag) iconfig_node_set_str(node, "servertag", rec->servertag);
if (rec->channels != NULL && *rec->channels != NULL) {
- node = config_node_section(node, "channels", NODE_TYPE_LIST);
+ node = iconfig_node_section(node, "channels", NODE_TYPE_LIST);
iconfig_node_add_list(node, rec->channels);
}
}
@@ -100,7 +101,9 @@ static void hilight_destroy(HILIGHT_REC *rec)
{
g_return_if_fail(rec != NULL);
-#ifdef HAVE_REGEX_H
+#ifdef USE_GREGEX
+ if (rec->preg != NULL) g_regex_unref(rec->preg);
+#else
if (rec->regexp_compiled) regfree(&rec->preg);
#endif
if (rec->channels != NULL) g_strfreev(rec->channels);
@@ -119,10 +122,18 @@ static void hilights_destroy_all(void)
static void hilight_init_rec(HILIGHT_REC *rec)
{
-#ifdef HAVE_REGEX_H
+#ifdef USE_GREGEX
+ if (rec->preg != NULL)
+ g_regex_unref(rec->preg);
+
+ rec->preg = g_regex_new(rec->text, G_REGEX_OPTIMIZE | G_REGEX_RAW | G_REGEX_CASELESS, 0, NULL);
+#else
if (rec->regexp_compiled) regfree(&rec->preg);
- rec->regexp_compiled = !rec->regexp ? FALSE :
- regcomp(&rec->preg, rec->text, REG_EXTENDED|REG_ICASE) == 0;
+ if (!rec->regexp)
+ rec->regexp_compiled = FALSE;
+ else
+ rec->regexp_compiled = regcomp(&rec->preg, rec->text,
+ rec->case_sensitive ? REG_EXTENDED : (REG_EXTENDED|REG_ICASE)) == 0;
#endif
}
@@ -168,13 +179,13 @@ static HILIGHT_REC *hilight_find(const char *text, char **channels)
if ((channels == NULL && rec->channels == NULL))
return rec; /* no channels - ok */
- if (channels != NULL && strcmp(*channels, "*") == 0)
+ if (channels != NULL && g_strcmp0(*channels, "*") == 0)
return rec; /* ignore channels */
if (channels == NULL || rec->channels == NULL)
continue; /* other doesn't have channels */
- if (strarray_length(channels) != strarray_length(rec->channels))
+ if (g_strv_length(channels) != g_strv_length(rec->channels))
continue; /* different amount of channels */
/* check that channels match */
@@ -190,39 +201,58 @@ static HILIGHT_REC *hilight_find(const char *text, char **channels)
return NULL;
}
-static int hilight_match_text(HILIGHT_REC *rec, const char *text,
- int *match_beg, int *match_end)
+static gboolean hilight_match_text(HILIGHT_REC *rec, const char *text,
+ int *match_beg, int *match_end)
{
- char *match;
+ gboolean ret = FALSE;
if (rec->regexp) {
-#ifdef HAVE_REGEX_H
+#ifdef USE_GREGEX
+ if (rec->preg != NULL) {
+ GMatchInfo *match;
+
+ g_regex_match (rec->preg, text, 0, &match);
+
+ if (g_match_info_matches(match))
+ ret = g_match_info_fetch_pos(match, 0, match_beg, match_end);
+
+ g_match_info_free(match);
+ }
+#else
regmatch_t rmatch[1];
if (rec->regexp_compiled &&
- regexec(&rec->preg, text, 1, rmatch, 0) == 0) {
+ regexec(&rec->preg, text, 1, rmatch, 0) == 0) {
if (rmatch[0].rm_so > 0 &&
- match_beg != NULL && match_end != NULL) {
+ match_beg != NULL && match_end != NULL) {
*match_beg = rmatch[0].rm_so;
*match_end = rmatch[0].rm_eo;
}
- return TRUE;
+ ret = TRUE;
}
#endif
} else {
- match = rec->fullword ?
- stristr_full(text, rec->text) :
- stristr(text, rec->text);
+ char *match;
+
+ if (rec->case_sensitive) {
+ match = rec->fullword ?
+ strstr_full(text, rec->text) :
+ strstr(text, rec->text);
+ } else {
+ match = rec->fullword ?
+ stristr_full(text, rec->text) :
+ stristr(text, rec->text);
+ }
if (match != NULL) {
if (match_beg != NULL && match_end != NULL) {
*match_beg = (int) (match-text);
*match_end = *match_beg + strlen(rec->text);
}
- return TRUE;
+ ret = TRUE;
}
}
- return FALSE;
+ return ret;
}
#define hilight_match_level(rec, level) \
@@ -267,14 +297,14 @@ HILIGHT_REC *hilight_match(SERVER_REC *server, const char *channel,
HILIGHT_REC *rec = tmp->data;
if (!rec->nickmask && hilight_match_level(rec, level) &&
- hilight_match_channel(rec, channel) &&
- (rec->servertag == NULL ||
- (server != NULL && g_ascii_strcasecmp(rec->servertag, server->tag) == 0)) &&
- hilight_match_text(rec, str, match_beg, match_end))
+ hilight_match_channel(rec, channel) &&
+ (rec->servertag == NULL ||
+ (server != NULL && g_ascii_strcasecmp(rec->servertag, server->tag) == 0)) &&
+ hilight_match_text(rec, str, match_beg, match_end))
return rec;
}
- return NULL;
+ return NULL;
}
static char *hilight_get_act_color(HILIGHT_REC *rec)
@@ -306,9 +336,9 @@ void hilight_update_text_dest(TEXT_DEST_REC *dest, HILIGHT_REC *rec)
dest->hilight_priority = rec->priority;
g_free_and_null(dest->hilight_color);
- if (rec->act_color != NULL && strcmp(rec->act_color, "%n") == 0)
+ if (rec->act_color != NULL && g_strcmp0(rec->act_color, "%n") == 0)
dest->level |= MSGLEVEL_NO_ACT;
- else
+ else
dest->hilight_color = hilight_get_act_color(rec);
}
@@ -325,11 +355,11 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *text,
if (dest->level & MSGLEVEL_NOHILIGHT)
return;
- hilight_start = hilight_end = 0;
- hilight = hilight_match(dest->server, dest->target,
- NULL, NULL, dest->level, stripped,
- &hilight_start,
- &hilight_end);
+ hilight_start = hilight_end = 0;
+ hilight = hilight_match(dest->server, dest->target, dest->nick,
+ dest->address, dest->level, stripped,
+ &hilight_start, &hilight_end);
+
if (hilight == NULL)
return;
@@ -337,14 +367,14 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *text,
old_level = dest->level;
if (!nick_match || (dest->level & MSGLEVEL_HILIGHT)) {
- /* update the level / hilight info */
- hilight_update_text_dest(dest, hilight);
/* Remove NO_ACT, this means explicitly defined hilights will bypass
* /IGNORE ... NO_ACT.
* (It's still possible to use /hilight -actcolor %n to hide
* hilight/beep).
*/
dest->level &= ~MSGLEVEL_NO_ACT;
+ /* update the level / hilight info */
+ hilight_update_text_dest(dest, hilight);
}
if (nick_match)
@@ -362,29 +392,29 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *text,
/* hilight whole line */
char *tmp = strip_codes(text);
newstr = g_strconcat(color, tmp, NULL);
- g_free(tmp);
+ g_free(tmp);
} else {
/* hilight part of the line */
- GString *tmp;
- char *middle;
+ GString *tmp;
+ char *middle;
int pos, color_pos, color_len;
- tmp = g_string_new(NULL);
+ tmp = g_string_new(NULL);
- /* start of the line */
+ /* start of the line */
pos = strip_real_length(text, hilight_start, NULL, NULL);
g_string_append(tmp, text);
- g_string_truncate(tmp, pos);
+ g_string_truncate(tmp, pos);
/* color */
- g_string_append(tmp, color);
+ g_string_append(tmp, color);
/* middle of the line, stripped */
middle = strip_codes(text+pos);
- pos = tmp->len;
+ pos = tmp->len;
g_string_append(tmp, middle);
- g_string_truncate(tmp, pos+hilight_len);
- g_free(middle);
+ g_string_truncate(tmp, pos+hilight_len);
+ g_free(middle);
/* end of the line */
pos = strip_real_length(text, hilight_end,
@@ -398,8 +428,8 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *text,
}
g_string_append(tmp, text+pos);
- newstr = tmp->str;
- g_string_free(tmp, FALSE);
+ newstr = tmp->str;
+ g_string_free(tmp, FALSE);
}
signal_emit("print text", 3, dest, newstr, stripped);
@@ -417,7 +447,7 @@ HILIGHT_REC *hilight_match_nick(SERVER_REC *server, const char *channel,
HILIGHT_REC *rec;
rec = hilight_match(server, channel, nick, address,
- level, msg, NULL, NULL);
+ level, msg, NULL, NULL);
return (rec == NULL || !rec->nick) ? NULL : rec;
}
@@ -464,6 +494,7 @@ static void read_hilight_config(void)
rec->priority = config_node_get_int(node, "priority", 0);
rec->nick = config_node_get_bool(node, "nick", TRUE);
rec->word = config_node_get_bool(node, "word", TRUE);
+ rec->case_sensitive = config_node_get_bool(node, "matchcase", FALSE);
rec->nickmask = config_node_get_bool(node, "mask", FALSE);
rec->fullword = config_node_get_bool(node, "fullword", FALSE);
@@ -471,11 +502,11 @@ static void read_hilight_config(void)
rec->servertag = config_node_get_str(node, "servertag", NULL);
hilight_init_rec(rec);
- node = config_node_section(node, "channels", -1);
+ node = iconfig_node_section(node, "channels", -1);
if (node != NULL) rec->channels = config_node_get_list(node);
}
- reset_cache();
+ reset_cache();
}
static void hilight_print(int index, HILIGHT_REC *rec)
@@ -484,16 +515,24 @@ static void hilight_print(int index, HILIGHT_REC *rec)
GString *options;
options = g_string_new(NULL);
- if (!rec->nick || !rec->word) {
- if (rec->nick) g_string_append(options, "-nick ");
- if (rec->word) g_string_append(options, "-word ");
- }
+
+ if (rec->nick && rec->word) { /* default case, no option */ }
+ else if (rec->nick)
+ g_string_append(options, "-nick ");
+ else if (rec->word)
+ g_string_append(options, "-word ");
+ else
+ g_string_append(options, "-line ");
if (rec->nickmask) g_string_append(options, "-mask ");
if (rec->fullword) g_string_append(options, "-full ");
+ if (rec->case_sensitive) g_string_append(options, "-matchcase ");
if (rec->regexp) {
g_string_append(options, "-regexp ");
-#ifdef HAVE_REGEX_H
+#ifdef USE_GREGEX
+ if (rec->preg == NULL)
+ g_string_append(options, "[INVALID!] ");
+#else
if (!rec->regexp_compiled)
g_string_append(options, "[INVALID!] ");
#endif
@@ -515,10 +554,10 @@ static void hilight_print(int index, HILIGHT_REC *rec)
if (levelstr != NULL)
levelstr = g_strconcat(levelstr, " ", NULL);
printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
- TXT_HILIGHT_LINE, index, rec->text,
- chans != NULL ? chans : "",
- levelstr != NULL ? levelstr : "",
- options->str);
+ TXT_HILIGHT_LINE, index, rec->text,
+ chans != NULL ? chans : "",
+ levelstr != NULL ? levelstr : "",
+ options->str);
g_free_not_null(chans);
g_free_not_null(levelstr);
g_string_free(options, TRUE);
@@ -539,12 +578,12 @@ static void cmd_hilight_show(void)
printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_HILIGHT_FOOTER);
}
-/* SYNTAX: HILIGHT [-nick | -word | -line] [-mask | -full | -regexp]
+/* SYNTAX: HILIGHT [-nick | -word | -line] [-mask | -full | -matchcase | -regexp]
[-color <color>] [-actcolor <color>] [-level <level>]
[-network <network>] [-channels <channels>] <text> */
static void cmd_hilight(const char *data)
{
- GHashTable *optlist;
+ GHashTable *optlist;
HILIGHT_REC *rec;
char *colorarg, *actcolorarg, *levelarg, *priorityarg, *chanarg, *text, *servertag;
char **channels;
@@ -558,7 +597,7 @@ static void cmd_hilight(const char *data)
}
if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
- PARAM_FLAG_GETREST, "hilight", &optlist, &text))
+ PARAM_FLAG_GETREST, "hilight", &optlist, &text))
return;
chanarg = g_hash_table_lookup(optlist, "channels");
@@ -578,7 +617,7 @@ static void cmd_hilight(const char *data)
rec = g_new0(HILIGHT_REC, 1);
/* default to nick/word hilighting */
- rec->nick = TRUE;
+ rec->nick = TRUE;
rec->word = TRUE;
rec->text = g_strdup(text);
@@ -598,15 +637,16 @@ static void cmd_hilight(const char *data)
if (g_hash_table_lookup(optlist, "word") != NULL) {
rec->word = TRUE;
- rec->nick = FALSE;
+ rec->nick = FALSE;
}
if (g_hash_table_lookup(optlist, "nick") != NULL)
- rec->nick = TRUE;
+ rec->nick = TRUE;
rec->nickmask = g_hash_table_lookup(optlist, "mask") != NULL;
rec->fullword = g_hash_table_lookup(optlist, "full") != NULL;
rec->regexp = g_hash_table_lookup(optlist, "regexp") != NULL;
+ rec->case_sensitive = g_hash_table_lookup(optlist, "matchcase") != NULL;
if (colorarg != NULL) {
g_free_and_null(rec->color);
@@ -627,7 +667,7 @@ static void cmd_hilight(const char *data)
hilight_create(rec);
hilight_print(g_slist_index(hilights, rec)+1, rec);
- cmd_params_free(free_arg);
+ cmd_params_free(free_arg);
reset_cache();
}
@@ -645,7 +685,7 @@ static void cmd_dehilight(const char *data)
} else {
/* with mask */
char *chans[2] = { "*", NULL };
- rec = hilight_find(data, chans);
+ rec = hilight_find(data, chans);
}
if (rec == NULL)
@@ -653,20 +693,20 @@ static void cmd_dehilight(const char *data)
else {
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_HILIGHT_REMOVED, rec->text);
hilight_remove(rec);
- reset_cache();
+ reset_cache();
}
}
static void hilight_nick_cache(GHashTable *list, CHANNEL_REC *channel,
- NICK_REC *nick)
+ NICK_REC *nick)
{
GSList *tmp;
HILIGHT_REC *match;
- char *nickmask;
+ char *nickmask;
int len, best_match;
if (nick->host == NULL)
- return; /* don't check until host is known */
+ return; /* don't check until host is known */
nickmask = g_strconcat(nick->nick, "!", nick->host, NULL);
@@ -675,8 +715,8 @@ static void hilight_nick_cache(GHashTable *list, CHANNEL_REC *channel,
HILIGHT_REC *rec = tmp->data;
if (rec->nickmask &&
- hilight_match_channel(rec, channel->name) &&
- match_wildcards(rec->text, nickmask)) {
+ hilight_match_channel(rec, channel->name) &&
+ match_wildcards(rec->text, nickmask)) {
len = strlen(rec->text);
if (best_match < len) {
best_match = len;
@@ -687,7 +727,7 @@ static void hilight_nick_cache(GHashTable *list, CHANNEL_REC *channel,
g_free_not_null(nickmask);
if (match != NULL)
- g_hash_table_insert(list, nick, match);
+ g_hash_table_insert(list, nick, match);
}
static void read_settings(void)
@@ -702,28 +742,28 @@ void hilight_text_init(void)
settings_add_str("lookandfeel", "hilight_act_color", "%M");
settings_add_level("lookandfeel", "hilight_level", "PUBLIC DCCMSGS");
- read_settings();
+ read_settings();
nickmatch = nickmatch_init(hilight_nick_cache);
read_hilight_config();
signal_add_first("print text", (SIGNAL_FUNC) sig_print_text);
- signal_add("setup reread", (SIGNAL_FUNC) read_hilight_config);
- signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+ signal_add("setup reread", (SIGNAL_FUNC) read_hilight_config);
+ signal_add("setup changed", (SIGNAL_FUNC) read_settings);
command_bind("hilight", NULL, (SIGNAL_FUNC) cmd_hilight);
command_bind("dehilight", NULL, (SIGNAL_FUNC) cmd_dehilight);
- command_set_options("hilight", "-color -actcolor -level -priority -network -channels nick word line mask full regexp");
+ command_set_options("hilight", "-color -actcolor -level -priority -network -channels nick word line mask full regexp matchcase");
}
void hilight_text_deinit(void)
{
hilights_destroy_all();
- nickmatch_deinit(nickmatch);
+ nickmatch_deinit(nickmatch);
signal_remove("print text", (SIGNAL_FUNC) sig_print_text);
- signal_remove("setup reread", (SIGNAL_FUNC) read_hilight_config);
- signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+ signal_remove("setup reread", (SIGNAL_FUNC) read_hilight_config);
+ signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
command_unbind("hilight", (SIGNAL_FUNC) cmd_hilight);
command_unbind("dehilight", (SIGNAL_FUNC) cmd_dehilight);
diff --git a/src/fe-common/core/hilight-text.h b/src/fe-common/core/hilight-text.h
index d54ec4b5..76beec1f 100644
--- a/src/fe-common/core/hilight-text.h
+++ b/src/fe-common/core/hilight-text.h
@@ -1,14 +1,12 @@
#ifndef __HILIGHT_TEXT_H
#define __HILIGHT_TEXT_H
-#ifdef HAVE_REGEX_H
+#ifndef USE_GREGEX
# include <regex.h>
#endif
#include "formats.h"
-typedef struct _HILIGHT_REC HILIGHT_REC;
-
struct _HILIGHT_REC {
char *text;
@@ -25,7 +23,10 @@ struct _HILIGHT_REC {
unsigned int nickmask:1; /* `text' is a nick mask */
unsigned int fullword:1; /* match `text' only for full words */
unsigned int regexp:1; /* `text' is a regular expression */
-#ifdef HAVE_REGEX_H
+ unsigned int case_sensitive:1;/* `text' must match case */
+#ifdef USE_GREGEX
+ GRegex *preg;
+#else
unsigned int regexp_compiled:1; /* should always be TRUE, unless regexp is invalid */
regex_t preg;
#endif
@@ -42,7 +43,7 @@ HILIGHT_REC *hilight_match(SERVER_REC *server, const char *channel,
HILIGHT_REC *hilight_match_nick(SERVER_REC *server, const char *channel,
const char *nick, const char *address,
int level, const char *msg);
-
+
char *hilight_get_color(HILIGHT_REC *rec);
void hilight_update_text_dest(TEXT_DEST_REC *dest, HILIGHT_REC *rec);
diff --git a/src/fe-common/core/keyboard.c b/src/fe-common/core/keyboard.c
index 00454401..fed7f9cb 100644
--- a/src/fe-common/core/keyboard.c
+++ b/src/fe-common/core/keyboard.c
@@ -31,6 +31,8 @@
#include "fe-windows.h"
#include "printtext.h"
+#define MAX_EXPAND_RECURSION 100
+
GSList *keyinfos;
static GHashTable *keys, *default_keys;
@@ -118,7 +120,7 @@ static CONFIG_NODE *key_config_find(const char *key)
for (; tmp != NULL; tmp = config_node_next(tmp)) {
node = tmp->data;
- if (strcmp(config_node_get_str(node, "key", ""), key) == 0)
+ if (g_strcmp0(config_node_get_str(node, "key", ""), key) == 0)
return node;
}
@@ -135,7 +137,7 @@ static void keyconfig_save(const char *id, const char *key, const char *data)
node = key_config_find(key);
if (node == NULL) {
node = iconfig_node_traverse("(keyboard", TRUE);
- node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
+ node = iconfig_node_section(node, NULL, NODE_TYPE_BLOCK);
}
iconfig_node_set_str(node, "key", key);
@@ -171,7 +173,7 @@ KEYINFO_REC *key_info_find(const char *id)
return NULL;
}
-static int expand_key(const char *key, GSList **out);
+static int expand_key(const char *key, GSList **out, int *limit);
#define expand_out_char(out, c) \
{ \
@@ -188,13 +190,17 @@ static int expand_key(const char *key, GSList **out);
g_slist_free(out); out = NULL; \
}
-static int expand_combo(const char *start, const char *end, GSList **out)
+static int expand_combo(const char *start, const char *end, GSList **out, int *limit)
{
KEY_REC *rec;
KEYINFO_REC *info;
GSList *tmp, *tmp2, *list, *copy, *newout;
char *str, *p;
+ if ((*limit)-- < 0) {
+ return FALSE;
+ }
+
if (start == end) {
/* single key */
expand_out_char(*out, *start);
@@ -211,7 +217,7 @@ static int expand_combo(const char *start, const char *end, GSList **out)
for (tmp = info->keys; tmp != NULL; tmp = tmp->next) {
KEY_REC *rec = tmp->data;
- if (strcmp(rec->data, str) == 0)
+ if (g_strcmp0(rec->data, str) == 0)
list = g_slist_append(list, rec);
}
@@ -229,7 +235,7 @@ static int expand_combo(const char *start, const char *end, GSList **out)
/* only one way to generate the combo, good */
rec = list->data;
g_slist_free(list);
- return expand_key(rec->key, out);
+ return expand_key(rec->key, out, limit);
}
/* multiple ways to generate the combo -
@@ -244,7 +250,11 @@ static int expand_combo(const char *start, const char *end, GSList **out)
copy = g_slist_append(copy, g_string_new(str->str));
}
- if (!expand_key(rec->key, &copy)) {
+ if (!expand_key(rec->key, &copy, limit)) {
+ if (*limit < 0) {
+ return FALSE;
+ }
+
/* illegal key combo, remove from list */
expand_out_free(copy);
} else {
@@ -254,7 +264,11 @@ static int expand_combo(const char *start, const char *end, GSList **out)
rec = list->data;
g_slist_free(list);
- if (!expand_key(rec->key, out)) {
+ if (!expand_key(rec->key, out, limit)) {
+ if (*limit < 0) {
+ return FALSE;
+ }
+
/* illegal key combo, remove from list */
expand_out_free(*out);
}
@@ -264,12 +278,16 @@ static int expand_combo(const char *start, const char *end, GSList **out)
}
/* Expand key code - returns TRUE if successful. */
-static int expand_key(const char *key, GSList **out)
+static int expand_key(const char *key, GSList **out, int *limit)
{
GSList *tmp;
const char *start;
int last_hyphen;
+ if ((*limit)-- < 0) {
+ return FALSE;
+ }
+
/* meta-^W^Gf -> ^[-^W-^G-f */
start = NULL; last_hyphen = TRUE;
for (; *key != '\0'; key++) {
@@ -279,7 +297,7 @@ static int expand_key(const char *key, GSList **out)
continue;
}
- if (!expand_combo(start, key-1, out))
+ if (!expand_combo(start, key-1, out, limit))
return FALSE;
expand_out_char(*out, '-');
start = NULL;
@@ -292,12 +310,18 @@ static int expand_key(const char *key, GSList **out)
}
last_hyphen = !last_hyphen;
} else if (*key == '^') {
+ expand_out_char(*out, '^');
+
/* ctrl-code */
- if (key[1] != '\0')
+ if (key[1] != '\0' && key[1] != '-') {
key++;
+ expand_out_char(*out, *key);
+ }
+ else {
+ /* escaped syntax for ^, see gui-readline.c */
+ expand_out_char(*out, '-');
+ }
- expand_out_char(*out, '^');
- expand_out_char(*out, *key);
expand_out_char(*out, '-');
last_hyphen = FALSE; /* optional */
} else if (last_hyphen && i_isalpha(*key)) {
@@ -326,7 +350,7 @@ static int expand_key(const char *key, GSList **out)
}
if (start != NULL)
- return expand_combo(start, key-1, out);
+ return expand_combo(start, key-1, out, limit);
for (tmp = *out; tmp != NULL; tmp = tmp->next) {
GString *str = tmp->data;
@@ -340,12 +364,13 @@ static int expand_key(const char *key, GSList **out)
static void key_states_scan_key(const char *key, KEY_REC *rec)
{
GSList *tmp, *out;
+ int limit = MAX_EXPAND_RECURSION;
- if (strcmp(rec->info->id, "key") == 0)
+ if (g_strcmp0(rec->info->id, "key") == 0)
return;
out = g_slist_append(NULL, g_string_new(NULL));
- if (expand_key(key, &out)) {
+ if (expand_key(key, &out, &limit)) {
for (tmp = out; tmp != NULL; tmp = tmp->next) {
GString *str = tmp->data;
@@ -377,7 +402,7 @@ static void key_states_rescan(void)
g_tree_foreach(key_states, (GTraverseFunc) key_state_destroy,
NULL);
g_tree_destroy(key_states);
- key_states = g_tree_new((GCompareFunc) strcmp);
+ key_states = g_tree_new((GCompareFunc) g_strcmp0);
temp = g_string_new(NULL);
g_hash_table_foreach(keys, (GHFunc) key_states_scan_key, temp);
@@ -854,7 +879,7 @@ void keyboard_init(void)
default_keys = g_hash_table_new((GHashFunc) g_str_hash,
(GCompareFunc) g_str_equal);
keyinfos = NULL;
- key_states = g_tree_new((GCompareFunc) strcmp);
+ key_states = g_tree_new((GCompareFunc) g_strcmp0);
key_config_frozen = 0;
memset(used_keys, 0, sizeof(used_keys));
diff --git a/src/fe-common/core/module-formats.c b/src/fe-common/core/module-formats.c
index db11fae1..da9705be 100644
--- a/src/fe-common/core/module-formats.c
+++ b/src/fe-common/core/module-formats.c
@@ -42,8 +42,8 @@ FORMAT_REC fecommon_core_formats[] = {
{ "window_set_immortal", "Window is now immortal", 0 },
{ "window_unset_immortal", "Window isn't immortal anymore", 0 },
{ "window_immortal_error", "Window is immortal, if you really want to close it, say /WINDOW IMMORTAL OFF", 0 },
- { "windowlist_header", "%#Ref Name Active item Server Level", 0 },
- { "windowlist_line", "%#$[3]0 %|$[20]1 $[15]2 $[15]3 $4", 5, { 1, 0, 0, 0, 0 } },
+ { "windowlist_header", "%#Ref Name Active item Server Level", 0 },
+ { "windowlist_line", "%#$[4]0 %|$[20]1 $[15]2 $[15]3 $4", 5, { 1, 0, 0, 0, 0 } },
{ "windowlist_footer", "", 0 },
{ "windows_layout_saved", "Layout of windows is now remembered", 0 },
{ "windows_layout_reset", "Layout of windows reset to defaults", 0 },
@@ -196,6 +196,7 @@ FORMAT_REC fecommon_core_formats[] = {
{ "module_already_loaded", "Module {hilight $0/$1} already loaded", 2, { 0, 0 } },
{ "module_not_loaded", "Module {hilight $0/$1} is not loaded", 2, { 0, 0 } },
{ "module_load_error", "Error loading module {hilight $0/$1}: $2", 3, { 0, 0, 0 } },
+ { "module_version_mismatch", "{hilight $0/$1} is ABI version $2 but Irssi is version $abiversion, cannot load", 3, { 0, 0, 0 } },
{ "module_invalid", "{hilight $0/$1} isn't Irssi module", 2, { 0, 0 } },
{ "module_loaded", "Loaded module {hilight $0/$1}", 2, { 0, 0 } },
{ "module_unloaded", "Unloaded module {hilight $0/$1}", 2, { 0, 0 } },
@@ -220,8 +221,10 @@ FORMAT_REC fecommon_core_formats[] = {
{ "invalid_level", "Invalid message level", 0 },
{ "invalid_size", "Invalid size", 0 },
{ "invalid_charset", "Invalid charset: $0", 1, { 0 } },
+ { "invalid_choice", "Invalid choice, must be one of $0", 1, { 0 } },
{ "eval_max_recurse", "/eval hit maximum recursion limit", 0 },
{ "program_not_found", "Could not find file or file is not executable", 0 },
+ { "no_server_defined", "No servers defined for this network, see /help server for how to add one", 0 },
/* ---- */
{ NULL, "Themes", 0 },
@@ -288,5 +291,18 @@ FORMAT_REC fecommon_core_formats[] = {
{ "completion_line", "%#$[10]0 $[!40]1 $2", 3, { 0, 0, 0 } },
{ "completion_footer", "", 0 },
+ /* ---- */
+ { NULL, "TLS", 0 },
+
+ { "tls_ephemeral_key", "EDH Key: {hilight $0} bit {hilight $1}", 2, { 1, 0 } },
+ { "tls_ephemeral_key_unavailable", "EDH Key: {error N/A}", 0 },
+ { "tls_pubkey", "Public Key: {hilight $0} bit {hilight $1}, valid from {hilight $2} to {hilight $3}", 4, { 1, 0, 0, 0 } },
+ { "tls_cert_header", "Certificate Chain:", 0 },
+ { "tls_cert_subject", " Subject: {hilight $0}", 1, { 0 } },
+ { "tls_cert_issuer", " Issuer: {hilight $0}", 1, { 0 } },
+ { "tls_pubkey_fingerprint", "Public Key Fingerprint: {hilight $0} ({hilight $1})", 2, { 0, 0 } },
+ { "tls_cert_fingerprint", "Certificate Fingerprint: {hilight $0} ({hilight $1})", 2, { 0, 0 } },
+ { "tls_protocol_version", "Protocol: {hilight $0} ({hilight $1} bit, {hilight $2})", 3, { 0, 1, 0 } },
+
{ NULL, NULL, 0 }
};
diff --git a/src/fe-common/core/module-formats.h b/src/fe-common/core/module-formats.h
index 18bf91f5..a9ed28c5 100644
--- a/src/fe-common/core/module-formats.h
+++ b/src/fe-common/core/module-formats.h
@@ -166,6 +166,7 @@ enum {
TXT_MODULE_ALREADY_LOADED,
TXT_MODULE_NOT_LOADED,
TXT_MODULE_LOAD_ERROR,
+ TXT_MODULE_VERSION_MISMATCH,
TXT_MODULE_INVALID,
TXT_MODULE_LOADED,
TXT_MODULE_UNLOADED,
@@ -189,8 +190,10 @@ enum {
TXT_INVALID_LEVEL,
TXT_INVALID_SIZE,
TXT_INVALID_CHARSET,
+ TXT_INVALID_CHOICE,
TXT_EVAL_MAX_RECURSE,
TXT_PROGRAM_NOT_FOUND,
+ TXT_NO_SERVER_DEFINED,
TXT_FILL_11,
@@ -251,7 +254,19 @@ enum {
TXT_COMPLETION_REMOVED,
TXT_COMPLETION_HEADER,
TXT_COMPLETION_LINE,
- TXT_COMPLETION_FOOTER
+ TXT_COMPLETION_FOOTER,
+
+ TLS_FILL_15,
+
+ TXT_TLS_EPHEMERAL_KEY,
+ TXT_TLS_EPHEMERAL_KEY_UNAVAILBLE,
+ TXT_TLS_PUBKEY,
+ TXT_TLS_CERT_HEADER,
+ TXT_TLS_CERT_SUBJECT,
+ TXT_TLS_CERT_ISSUER,
+ TXT_TLS_PUBKEY_FINGERPRINT,
+ TXT_TLS_CERT_FINGERPRINT,
+ TXT_TLS_PROTOCOL_VERSION
};
extern FORMAT_REC fecommon_core_formats[];
diff --git a/src/fe-common/core/module.h b/src/fe-common/core/module.h
index 51b61b3e..db712ec7 100644
--- a/src/fe-common/core/module.h
+++ b/src/fe-common/core/module.h
@@ -2,7 +2,7 @@
#define MODULE_NAME "fe-common/core"
-typedef guint32 unichar;
+#include "utf8.h"
typedef struct {
time_t time;
char *nick;
diff --git a/src/fe-common/core/printtext.c b/src/fe-common/core/printtext.c
index a5eaa38f..01ef2dcd 100644
--- a/src/fe-common/core/printtext.c
+++ b/src/fe-common/core/printtext.c
@@ -170,13 +170,13 @@ static void print_line(TEXT_DEST_REC *dest, const char *text)
g_return_if_fail(dest != NULL);
g_return_if_fail(text != NULL);
-
+
theme = window_get_theme(dest->window);
tmp = format_get_level_tag(theme, dest);
str = !theme->info_eol ? format_add_linestart(text, tmp) :
format_add_lineend(text, tmp);
g_free_not_null(tmp);
-
+
/* send both the formatted + stripped (for logging etc.) */
stripped = strip_codes(str);
signal_emit_id(signal_print_text, 3, dest, str, stripped);
@@ -413,6 +413,18 @@ void printtext_gui(const char *text)
g_free(str);
}
+/* Like printtext_gui(), but don't expand % codes. */
+void printtext_gui_internal(const char *str)
+{
+ TEXT_DEST_REC dest;
+
+ g_return_if_fail(str != NULL);
+
+ memset(&dest, 0, sizeof(dest));
+
+ format_send_to_gui(&dest, str);
+}
+
static void msg_beep_check(TEXT_DEST_REC *dest)
{
if (dest->level != 0 && (dest->level & MSGLEVEL_NO_ACT) == 0 &&
@@ -434,7 +446,9 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *text)
if (dest->window == NULL) {
str = strip_codes(text);
+#ifndef SUPPRESS_PRINTF_FALLBACK
printf("NO WINDOWS: %s\n", str);
+#endif
g_free(str);
return;
}
diff --git a/src/fe-common/core/printtext.h b/src/fe-common/core/printtext.h
index 66ba28f4..ab8dfac1 100644
--- a/src/fe-common/core/printtext.h
+++ b/src/fe-common/core/printtext.h
@@ -23,6 +23,7 @@ void printtext_dest(TEXT_DEST_REC *dest, const char *text, ...);
/* only GUI should call these - used for printing text to somewhere else
than windows */
void printtext_gui(const char *text);
+void printtext_gui_internal(const char *str);
void printformat_module_gui(const char *module, int formatnum, ...);
void printformat_module_gui_args(const char *module, int formatnum, va_list va);
diff --git a/src/fe-common/core/themes.c b/src/fe-common/core/themes.c
index d92d23fe..2b1459be 100644
--- a/src/fe-common/core/themes.c
+++ b/src/fe-common/core/themes.c
@@ -365,9 +365,9 @@ char *theme_format_expand_get(THEME_REC *theme, const char **format)
GString *str;
char *ret;
theme_rm_col dummy, reset;
+ int braces = 1; /* we start with one brace opened */
dummy.m[0] = '\0';
strcpy(reset.m, "n");
- int braces = 1; /* we start with one brace opened */
str = g_string_new(NULL);
while (**format != '\0' && braces != 0) {
@@ -385,7 +385,7 @@ char *theme_format_expand_get(THEME_REC *theme, const char **format)
&dummy, &dummy, 0);
continue;
}
-
+
if (braces == 0) {
(*format)++;
break;
@@ -739,7 +739,7 @@ static void theme_read_formats(THEME_REC *theme, const char *module,
node = config_node_traverse(config, "formats", FALSE);
if (node == NULL) return;
- node = config_node_section(node, module, -1);
+ node = config_node_section(config, node, module, -1);
if (node == NULL) return;
for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
@@ -895,7 +895,7 @@ THEME_REC *theme_load(const char *setname)
name = g_strdup(setname);
p = strrchr(name, '.');
- if (p != NULL && strcmp(p, ".theme") == 0) {
+ if (p != NULL && g_strcmp0(p, ".theme") == 0) {
/* remove the trailing .theme */
*p = '\0';
}
@@ -1177,7 +1177,7 @@ static void module_save(const char *module, MODULE_THEME_REC *rec,
fnode = config_node_traverse(data->config, "formats", TRUE);
- node = config_node_section(fnode, rec->name, NODE_TYPE_BLOCK);
+ node = config_node_section(data->config, fnode, rec->name, NODE_TYPE_BLOCK);
for (n = 1; formats[n].def != NULL; n++) {
if (rec->formats[n] != NULL) {
config_node_set_str(data->config, node, formats[n].tag,
@@ -1358,9 +1358,9 @@ static void read_settings(void)
theme = settings_get_str("theme");
len = strlen(current_theme->name);
- if (strcmp(current_theme->name, theme) != 0 &&
+ if (g_strcmp0(current_theme->name, theme) != 0 &&
(strncmp(current_theme->name, theme, len) != 0 ||
- strcmp(theme+len, ".theme") != 0))
+ g_strcmp0(theme+len, ".theme") != 0))
change_theme(theme, TRUE);
}
diff --git a/src/fe-common/core/utf8.c b/src/fe-common/core/utf8.c
deleted file mode 100644
index 2d07ea8e..00000000
--- a/src/fe-common/core/utf8.c
+++ /dev/null
@@ -1,26 +0,0 @@
-/* utf8.c - Operations on UTF-8 strings.
- *
- * Copyright (C) 2002 Timo Sirainen
- *
- * Based on GLib code by
- *
- * Copyright (C) 1999 Tom Tromey
- * Copyright (C) 2000 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "module.h"
-
diff --git a/src/fe-common/core/utf8.h b/src/fe-common/core/utf8.h
deleted file mode 100644
index 3c15dc7d..00000000
--- a/src/fe-common/core/utf8.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef __UTF8_H
-#define __UTF8_H
-
-/* XXX I didn't check the encoding range of big5+. This is standard big5. */
-#define is_big5_los(lo) (0x40 <= (lo) && (lo) <= 0x7E) /* standard */
-#define is_big5_lox(lo) (0x80 <= (lo) && (lo) <= 0xFE) /* extended */
-#define is_big5_lo(lo) ((is_big5_los(lo) || is_big5_lox(lo)))
-#define is_big5_hi(hi) (0x81 <= (hi) && (hi) <= 0xFE)
-#define is_big5(hi,lo) (is_big5_hi(hi) && is_big5_lo(lo))
-
-/* Returns width for character (0-2). */
-int mk_wcwidth(unichar c);
-
-#define unichar_isprint(c) (((c) & ~0x80) >= 32)
-#define is_utf8_leading(c) (((c) & 0xc0) != 0x80)
-
-#endif
diff --git a/src/fe-common/core/window-commands.c b/src/fe-common/core/window-commands.c
index a975fe5c..9e4aab3a 100644
--- a/src/fe-common/core/window-commands.c
+++ b/src/fe-common/core/window-commands.c
@@ -33,6 +33,7 @@
#include "window-items.h"
#include "windows-layout.h"
#include "printtext.h"
+#include "command-history.h"
static void window_print_binds(WINDOW_REC *win)
{
@@ -127,7 +128,7 @@ static void cmd_window_info(WINDOW_REC *win)
win->active_server->tag : "NONE");
} else {
if (win->active_server != NULL &&
- strcmp(win->active_server->tag, win->servertag) != 0)
+ g_strcmp0(win->active_server->tag, win->servertag) != 0)
g_warning("Active server isn't the sticky server!");
printformat_window(win, MSGLEVEL_CLIENTCRAP,
@@ -529,7 +530,7 @@ static void cmd_window_item_goto(const char *data, SERVER_REC *server)
GSList *tmp;
void *free_arg;
char *target;
-
+
if (!cmd_get_params(data, &free_arg, 1, &target))
return;
@@ -609,16 +610,31 @@ static void cmd_window_name(const char *data)
if (win == NULL || win == active_win)
window_set_name(active_win, data);
else if (active_win->name == NULL ||
- strcmp(active_win->name, data) != 0) {
+ g_strcmp0(active_win->name, data) != 0) {
printformat_window(active_win, MSGLEVEL_CLIENTERROR,
TXT_WINDOW_NAME_NOT_UNIQUE, data);
}
}
-/* SYNTAX: WINDOW HISTORY <name> */
+/* SYNTAX: WINDOW HISTORY [-clear] <name> */
void cmd_window_history(const char *data)
{
- window_set_history(active_win, data);
+ GHashTable *optlist;
+ char *name;
+ void *free_arg;
+
+ if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS | PARAM_FLAG_STRIP_TRAILING_WS,
+ "window history", &optlist, &name))
+ return;
+
+ if (g_hash_table_lookup(optlist, "clear") != NULL) {
+ signal_continue(1, data);
+ window_clear_history(active_win, name);
+ } else {
+ window_set_history(active_win, name);
+ }
+
+ cmd_params_free(free_arg);
}
/* we're moving the first window to last - move the first contiguous block
@@ -883,6 +899,7 @@ void window_commands_init(void)
command_set_options("window number", "sticky");
command_set_options("window server", "sticky unsticky");
command_set_options("window theme", "delete");
+ command_set_options("window history", "clear");
}
void window_commands_deinit(void)
diff --git a/src/fe-common/core/windows-layout.c b/src/fe-common/core/windows-layout.c
index 65741a73..ce6b9910 100644
--- a/src/fe-common/core/windows-layout.c
+++ b/src/fe-common/core/windows-layout.c
@@ -68,7 +68,7 @@ static void sig_layout_restore_item(WINDOW_REC *window, const char *type,
(SIGNAL_FUNC) signal_query_created_curwin);
restore_win = window;
-
+
protocol = chat_protocol_find(chat_type);
if (protocol == NULL)
window_bind_add(window, tag, name);
@@ -127,6 +127,7 @@ static void sig_layout_restore(void)
for (; tmp != NULL; tmp = config_node_next(tmp)) {
CONFIG_NODE *node = tmp->data;
+ if (node->key == NULL) continue;
window = window_find_refnum(atoi(node->key));
if (window == NULL)
window = window_create(NULL, TRUE);
@@ -143,7 +144,7 @@ static void sig_layout_restore(void)
if (window->theme_name != NULL)
window->theme = theme_load(window->theme_name);
- window_add_items(window, config_node_section(node, "items", -1));
+ window_add_items(window, iconfig_node_section(node, "items", -1));
signal_emit("layout restore window", 2, window, node);
}
}
@@ -160,19 +161,19 @@ static void sig_layout_save_item(WINDOW_REC *window, WI_ITEM_REC *item,
if (type == NULL)
return;
- subnode = config_node_section(node, NULL, NODE_TYPE_BLOCK);
+ subnode = iconfig_node_section(node, NULL, NODE_TYPE_BLOCK);
iconfig_node_set_str(subnode, "type", type);
proto = item->chat_type == 0 ? NULL :
chat_protocol_find_id(item->chat_type);
if (proto != NULL)
iconfig_node_set_str(subnode, "chat_type", proto->name);
- iconfig_node_set_str(subnode, "name", item->visible_name);
+ iconfig_node_set_str(subnode, "name", item->name);
if (item->server != NULL) {
iconfig_node_set_str(subnode, "tag", item->server->tag);
if (IS_CHANNEL(item)) {
- rec = window_bind_add(window, item->server->tag, item->visible_name);
+ rec = window_bind_add(window, item->server->tag, item->name);
if (rec != NULL)
rec->sticky = TRUE;
}
@@ -185,7 +186,7 @@ static void window_save_items(WINDOW_REC *window, CONFIG_NODE *node)
{
GSList *tmp;
- node = config_node_section(node, "items", NODE_TYPE_LIST);
+ node = iconfig_node_section(node, "items", NODE_TYPE_LIST);
for (tmp = window->items; tmp != NULL; tmp = tmp->next)
signal_emit("layout save item", 3, window, tmp->data, node);
}
@@ -195,7 +196,7 @@ static void window_save(WINDOW_REC *window, CONFIG_NODE *node)
char refnum[MAX_INT_STRLEN];
ltoa(refnum, window->refnum);
- node = config_node_section(node, refnum, NODE_TYPE_BLOCK);
+ node = iconfig_node_section(node, refnum, NODE_TYPE_BLOCK);
if (window->sticky_refnum)
iconfig_node_set_bool(node, "sticky_refnum", TRUE);
diff --git a/src/fe-common/irc/Makefile.am b/src/fe-common/irc/Makefile.am
index 463f145c..bf88f5cd 100644
--- a/src/fe-common/irc/Makefile.am
+++ b/src/fe-common/irc/Makefile.am
@@ -26,6 +26,7 @@ real_sources = \
fe-netsplit.c \
fe-common-irc.c \
fe-whois.c \
+ fe-sasl.c \
irc-completion.c \
module-formats.c
@@ -36,6 +37,7 @@ libfe_common_irc_a_SOURCES = \
pkginc_fe_common_ircdir=$(pkgincludedir)/src/fe-common/irc
pkginc_fe_common_irc_HEADERS = \
fe-irc-server.h \
+ fe-irc-channels.h \
module.h \
module-formats.h
diff --git a/src/fe-common/irc/dcc/fe-dcc-chat-messages.c b/src/fe-common/irc/dcc/fe-dcc-chat-messages.c
index 93e10943..45791f56 100644
--- a/src/fe-common/irc/dcc/fe-dcc-chat-messages.c
+++ b/src/fe-common/irc/dcc/fe-dcc-chat-messages.c
@@ -60,7 +60,7 @@ static void sig_message_dcc_own_action(CHAT_DCC_REC *dcc, const char *msg)
format_create_dest_tag(&dest, dcc->server, dcc->servertag, tag,
MSGLEVEL_DCCMSGS | MSGLEVEL_ACTIONS |
MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT, NULL);
-
+
printformat_dest(&dest, query != NULL ? IRCTXT_OWN_DCC_ACTION_QUERY :
IRCTXT_OWN_DCC_ACTION, dcc->mynick, dcc->id, msg);
g_free(tag);
diff --git a/src/fe-common/irc/dcc/fe-dcc-chat.c b/src/fe-common/irc/dcc/fe-dcc-chat.c
index 101aeb31..5bcb3475 100644
--- a/src/fe-common/irc/dcc/fe-dcc-chat.c
+++ b/src/fe-common/irc/dcc/fe-dcc-chat.c
@@ -44,7 +44,7 @@ static void dcc_request(CHAT_DCC_REC *dcc)
if (!IS_DCC_CHAT(dcc)) return;
printformat(dcc->server, NULL, MSGLEVEL_DCC,
- ischannel(*dcc->target) ? IRCTXT_DCC_CHAT_CHANNEL :
+ server_ischannel(SERVER(dcc->server), dcc->target) ? IRCTXT_DCC_CHAT_CHANNEL :
IRCTXT_DCC_CHAT, dcc->id, dcc->addrstr,
dcc->port, dcc->target);
}
@@ -243,7 +243,7 @@ static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
return;
/* handle only DCC messages */
- if (strcmp(target, "*") == 0)
+ if (g_strcmp0(target, "*") == 0)
dcc = item_get_dcc(item);
else if (*target == '=')
dcc = dcc_chat_find_id(target+1);
diff --git a/src/fe-common/irc/dcc/fe-dcc-get.c b/src/fe-common/irc/dcc/fe-dcc-get.c
index 451463f9..675cab65 100644
--- a/src/fe-common/irc/dcc/fe-dcc-get.c
+++ b/src/fe-common/irc/dcc/fe-dcc-get.c
@@ -21,6 +21,7 @@
#include "module.h"
#include "signals.h"
#include "levels.h"
+#include "servers.h"
#include "irc.h"
#include "dcc-file.h"
@@ -35,12 +36,12 @@ static void dcc_request(GET_DCC_REC *dcc)
{
char *sizestr;
- if (!IS_DCC_GET(dcc)) return;
+ if (!IS_DCC_GET(dcc)) return;
sizestr = dcc_get_size_str(dcc->size);
printformat(dcc->server, NULL, MSGLEVEL_DCC,
- ischannel(*dcc->target) ? IRCTXT_DCC_SEND_CHANNEL :
+ server_ischannel(SERVER(dcc->server), dcc->target) ? IRCTXT_DCC_SEND_CHANNEL :
IRCTXT_DCC_SEND, dcc->nick, dcc->addrstr,
dcc->port, dcc->arg, sizestr, dcc->target);
diff --git a/src/fe-common/irc/fe-common-irc.c b/src/fe-common/irc/fe-common-irc.c
index 72606263..4a3ef1d3 100644
--- a/src/fe-common/irc/fe-common-irc.c
+++ b/src/fe-common/irc/fe-common-irc.c
@@ -28,13 +28,11 @@
#include "themes.h"
#include "fe-irc-server.h"
+#include "fe-irc-channels.h"
void fe_irc_modules_init(void);
void fe_irc_modules_deinit(void);
-void fe_irc_channels_init(void);
-void fe_irc_channels_deinit(void);
-
void fe_irc_queries_init(void);
void fe_irc_queries_deinit(void);
@@ -71,6 +69,9 @@ void fe_netjoin_deinit(void);
void fe_whois_init(void);
void fe_whois_deinit(void);
+void fe_sasl_init(void);
+void fe_sasl_deinit(void);
+
void irc_completion_init(void);
void irc_completion_deinit(void);
@@ -93,6 +94,7 @@ void fe_common_irc_init(void)
fe_netsplit_init();
fe_netjoin_init();
fe_whois_init();
+ fe_sasl_init();
irc_completion_init();
settings_check();
@@ -118,6 +120,7 @@ void fe_common_irc_deinit(void)
fe_netsplit_deinit();
fe_netjoin_deinit();
fe_whois_deinit();
+ fe_sasl_deinit();
irc_completion_deinit();
theme_unregister();
diff --git a/src/fe-common/irc/fe-ctcp.c b/src/fe-common/irc/fe-ctcp.c
index 2321bb7a..cbb591ba 100644
--- a/src/fe-common/irc/fe-ctcp.c
+++ b/src/fe-common/irc/fe-ctcp.c
@@ -49,7 +49,7 @@ static void ctcp_default_msg(IRC_SERVER_REC *server, const char *data,
data = p+1;
}
- printformat(server, ischannel(*target) ? target : nick, MSGLEVEL_CTCPS,
+ printformat(server, server_ischannel(SERVER(server), target) ? target : nick, MSGLEVEL_CTCPS,
IRCTXT_CTCP_REQUESTED_UNKNOWN,
nick, addr, cmd, data, target);
g_free(cmd);
@@ -113,8 +113,8 @@ static void ctcp_default_reply(IRC_SERVER_REC *server, const char *data,
ctcpdata = ptr+1;
}
- printformat(server, ischannel(*target) ? target : nick, MSGLEVEL_CTCPS,
- ischannel(*target) ? IRCTXT_CTCP_REPLY_CHANNEL :
+ printformat(server, server_ischannel(SERVER(server), target) ? target : nick, MSGLEVEL_CTCPS,
+ server_ischannel(SERVER(server), target) ? IRCTXT_CTCP_REPLY_CHANNEL :
IRCTXT_CTCP_REPLY, ctcp, nick, ctcpdata, target);
g_free(ctcp);
}
@@ -137,7 +137,7 @@ static void ctcp_ping_reply(IRC_SERVER_REC *server, const char *data,
g_get_current_time(&tv);
usecs = get_timeval_diff(&tv, &tv2);
- printformat(server, ischannel(*target) ? target : nick, MSGLEVEL_CTCPS,
+ printformat(server, server_ischannel(SERVER(server), target) ? target : nick, MSGLEVEL_CTCPS,
IRCTXT_CTCP_PING_REPLY, nick, usecs/1000, usecs%1000);
}
diff --git a/src/fe-common/irc/fe-events-numeric.c b/src/fe-common/irc/fe-events-numeric.c
index d1cd12d4..3eb124fc 100644
--- a/src/fe-common/irc/fe-events-numeric.c
+++ b/src/fe-common/irc/fe-events-numeric.c
@@ -118,7 +118,7 @@ static void event_who(IRC_SERVER_REC *server, const char *data)
while (*realname != '\0' && *realname != ' ') realname++;
if (*realname == ' ')
*realname++ = '\0';
-
+
recoded = recode_in(SERVER(server), realname, nick);
printformat(server, NULL, MSGLEVEL_CRAP, IRCTXT_WHO,
channel, nick, stat, hops, user, host, recoded, serv);
@@ -400,7 +400,7 @@ static void event_target_unavailable(IRC_SERVER_REC *server, const char *data,
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &target);
- if (!ischannel(*target)) {
+ if (!server_ischannel(SERVER(server), target)) {
/* nick unavailable */
printformat(server, NULL, MSGLEVEL_CRAP,
IRCTXT_NICK_UNAVAILABLE, target);
@@ -427,7 +427,7 @@ static void event_no_such_nick(IRC_SERVER_REC *server, const char *data,
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &unick);
- if (!strcmp(unick, "*"))
+ if (!g_strcmp0(unick, "*"))
/* more information will be in the description,
* e.g. * :Target left IRC. Failed to deliver: [hi] */
print_event_received(server, data, nick, FALSE);
@@ -583,7 +583,7 @@ static void print_event_received(IRC_SERVER_REC *server, const char *data,
return;
ptr++;
- if (ischannel(*data)) /* directed at channel */
+ if (server_ischannel(SERVER(server), data)) /* directed at channel */
target = g_strndup(data, (int)(ptr - data - 1));
else if (!target_param || *ptr == ':' || (ptr2 = strchr(ptr, ' ')) == NULL)
target = NULL;
@@ -605,7 +605,7 @@ static void print_event_received(IRC_SERVER_REC *server, const char *data,
recoded = recode_in(SERVER(server), args, NULL);
format = nick == NULL || server->real_address == NULL ||
- strcmp(nick, server->real_address) == 0 ?
+ g_strcmp0(nick, server->real_address) == 0 ?
IRCTXT_DEFAULT_EVENT : IRCTXT_DEFAULT_EVENT_SERVER;
printformat(server, target, MSGLEVEL_CRAP, format,
nick, recoded, current_server_event);
diff --git a/src/fe-common/irc/fe-events.c b/src/fe-common/irc/fe-events.c
index 5cde9e4b..850174c5 100644
--- a/src/fe-common/irc/fe-events.c
+++ b/src/fe-common/irc/fe-events.c
@@ -41,6 +41,7 @@
#include "fe-queries.h"
#include "fe-windows.h"
#include "fe-irc-server.h"
+#include "fe-irc-channels.h"
static void event_privmsg(IRC_SERVER_REC *server, const char *data,
const char *nick, const char *addr)
@@ -52,15 +53,19 @@ static void event_privmsg(IRC_SERVER_REC *server, const char *data,
params = event_get_params(data, 2 | PARAM_FLAG_GETREST, &target, &msg);
if (nick == NULL) nick = server->real_address;
if (addr == NULL) addr = "";
- if (*target == '@' && ischannel(target[1])) {
+
+ if (fe_channel_is_opchannel(server, target)) {
/* Hybrid 6 feature, send msg to all ops in channel */
- recoded = recode_in(SERVER(server), msg, target+1);
+ const char *cleantarget = fe_channel_skip_prefix(server, target);
+ recoded = recode_in(SERVER(server), msg, cleantarget);
+
+ /* pass the original target to the signal, with the @+ here
+ * the other one is only needed for recode_in*/
signal_emit("message irc op_public", 5,
- server, recoded, nick, addr,
- get_visible_target(server, target+1));
+ server, recoded, nick, addr, target);
} else {
- recoded = recode_in(SERVER(server), msg, ischannel(*target) ? target : nick);
- signal_emit(ischannel(*target) ?
+ recoded = recode_in(SERVER(server), msg, server_ischannel(SERVER(server), target) ? target : nick);
+ signal_emit(server_ischannel(SERVER(server), target) ?
"message public" : "message private", 5,
server, recoded, nick, addr,
get_visible_target(server, target));
diff --git a/src/fe-common/irc/fe-irc-channels.c b/src/fe-common/irc/fe-irc-channels.c
index e884aef4..0ec30003 100644
--- a/src/fe-common/irc/fe-irc-channels.c
+++ b/src/fe-common/irc/fe-irc-channels.c
@@ -31,6 +31,48 @@
#include "fe-windows.h"
#include "window-items.h"
+int fe_channel_is_opchannel(IRC_SERVER_REC *server, const char *target)
+{
+ const char *statusmsg;
+
+ /* Quick check */
+ if (server == NULL || server->prefix[(int)(unsigned char)*target] == 0)
+ return FALSE;
+
+ statusmsg = g_hash_table_lookup(server->isupport, "statusmsg");
+ if (statusmsg == NULL)
+ statusmsg = "@";
+
+ return strchr(statusmsg, *target) != NULL;
+}
+
+const char *fe_channel_skip_prefix(IRC_SERVER_REC *server, const char *target)
+{
+ const char *statusmsg;
+
+ /* Quick check */
+ if (server == NULL || server->prefix[(int)(unsigned char)*target] == 0)
+ return target;
+
+ /* Exit early if target doesn't name a channel */
+ if (server_ischannel(SERVER(server), target) == FALSE)
+ return target;
+
+ statusmsg = g_hash_table_lookup(server->isupport, "statusmsg");
+
+ /* Hack: for bahamut 1.4 which sends neither STATUSMSG nor
+ * WALLCHOPS in 005 */
+ if (statusmsg == NULL)
+ statusmsg = "@";
+
+ /* Strip the leading statusmsg prefixes */
+ while (strchr(statusmsg, *target) != NULL) {
+ target++;
+ }
+
+ return target;
+}
+
static void sig_channel_rejoin(SERVER_REC *server, REJOIN_REC *rec)
{
g_return_if_fail(rec != NULL);
@@ -46,7 +88,7 @@ static void sig_event_forward(SERVER_REC *server, const char *data,
char *params, *from, *to;
params = event_get_params(data, 3, NULL, &from, &to);
- if (from != NULL && to != NULL && ischannel(*from) && ischannel(*to)) {
+ if (from != NULL && to != NULL && server_ischannel(server, from) && server_ischannel(server, to)) {
channel = irc_channel_find(server, from);
if (channel != NULL && irc_channel_find(server, to) == NULL) {
window_bind_add(window_item_window(channel),
diff --git a/src/fe-common/irc/fe-irc-channels.h b/src/fe-common/irc/fe-irc-channels.h
new file mode 100644
index 00000000..d05c91a5
--- /dev/null
+++ b/src/fe-common/irc/fe-irc-channels.h
@@ -0,0 +1,10 @@
+#ifndef __FE_IRC_CHANNELS_H
+#define __FE_IRC_CHANNELS_H
+
+int fe_channel_is_opchannel(IRC_SERVER_REC *server, const char *target);
+const char *fe_channel_skip_prefix(IRC_SERVER_REC *server, const char *target);
+
+void fe_irc_channels_init(void);
+void fe_irc_channels_deinit(void);
+
+#endif
diff --git a/src/fe-common/irc/fe-irc-commands.c b/src/fe-common/irc/fe-irc-commands.c
index 765b5340..11a911d2 100644
--- a/src/fe-common/irc/fe-irc-commands.c
+++ b/src/fe-common/irc/fe-irc-commands.c
@@ -110,14 +110,14 @@ static void cmd_notice(const char *data, IRC_SERVER_REC *server,
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST,
&target, &msg))
return;
- if (strcmp(target, "*") == 0)
+ if (g_strcmp0(target, "*") == 0)
target = item == NULL ? "" : window_item_get_target(item);
if (*target == '\0' || *msg == '\0')
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
-
+
signal_emit("message irc own_notice", 3, server, msg, target);
-
+
cmd_params_free(free_arg);
}
@@ -133,7 +133,7 @@ static void cmd_ctcp(const char *data, IRC_SERVER_REC *server,
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST,
&target, &ctcpcmd, &ctcpdata))
return;
- if (strcmp(target, "*") == 0)
+ if (g_strcmp0(target, "*") == 0)
target = item == NULL ? "" : window_item_get_target(item);
if (*target == '\0' || *ctcpcmd == '\0')
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
@@ -162,7 +162,7 @@ static void cmd_nctcp(const char *data, IRC_SERVER_REC *server,
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST,
&target, &text))
return;
- if (strcmp(target, "*") == 0)
+ if (g_strcmp0(target, "*") == 0)
target = item == NULL ? "" : window_item_get_target(item);
if (*target == '\0' || *text == '\0')
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
@@ -246,8 +246,8 @@ static void cmd_ban(const char *data, IRC_SERVER_REC *server,
CMD_IRC_SERVER(server);
- if (!cmd_get_params(data, &free_arg, 2 |
- PARAM_FLAG_OPTCHAN | PARAM_FLAG_GETREST,
+ if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTCHAN |
+ PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
item, &channel, &nicks))
return;
@@ -262,7 +262,7 @@ static void cmd_ban(const char *data, IRC_SERVER_REC *server,
if (chanrec == NULL && *channel == '\0')
cmd_param_error(CMDERR_NOT_JOINED);
- if (*channel != '\0' && strcmp(channel, "*") != 0)
+ if (*channel != '\0' && g_strcmp0(channel, "*") != 0)
chanrec = irc_channel_find(server, channel);
if (chanrec == NULL || !chanrec->synced) {
diff --git a/src/fe-common/irc/fe-irc-messages.c b/src/fe-common/irc/fe-irc-messages.c
index a8d52745..40ca306d 100644
--- a/src/fe-common/irc/fe-irc-messages.c
+++ b/src/fe-common/irc/fe-irc-messages.c
@@ -36,32 +36,8 @@
#include "fe-queries.h"
#include "window-items.h"
-
-static const char *skip_target(IRC_SERVER_REC *server, const char *target)
-{
- int i = 0;
- const char *val, *chars;
-
- /* Quick check */
- if (server == NULL || server->prefix[(int)(unsigned char)*target] == 0)
- return target;
-
- /* Hack: for bahamut 1.4 which sends neither STATUSMSG nor
- * WALLCHOPS in 005, accept @#chan and @+#chan (but not +#chan) */
- val = g_hash_table_lookup(server->isupport, "STATUSMSG");
- if (val == NULL && *target != '@')
- return target;
- chars = val ? val : "@+";
- for(i = 0; target[i] != '\0'; i++) {
- if (strchr(chars, target[i]) == NULL)
- break;
- };
-
- if(ischannel(target[i]))
- target += i;
-
- return target;
-}
+#include "fe-irc-channels.h"
+#include "fe-irc-server.h"
static void sig_message_own_public(SERVER_REC *server, const char *msg,
const char *target, const char *origtarget)
@@ -72,7 +48,7 @@ static void sig_message_own_public(SERVER_REC *server, const char *msg,
if (!IS_IRC_SERVER(server))
return;
oldtarget = target;
- target = skip_target(IRC_SERVER(server), target);
+ target = fe_channel_skip_prefix(IRC_SERVER(server), target);
if (target != oldtarget) {
/* Hybrid 6 / Bahamut feature, send msg to all
ops / ops+voices in channel */
@@ -87,7 +63,7 @@ static void sig_message_own_public(SERVER_REC *server, const char *msg,
g_free(nickmode);
signal_stop();
}
-
+
}
/* received msg to all ops in channel */
@@ -95,18 +71,28 @@ static void sig_message_irc_op_public(SERVER_REC *server, const char *msg,
const char *nick, const char *address,
const char *target)
{
- char *nickmode, *optarget;
+ char *nickmode, *optarget, *prefix;
+ const char *cleantarget;
- nickmode = channel_get_nickmode(channel_find(server, target),
+ /* only skip here so the difference can be stored in prefix */
+ cleantarget = fe_channel_skip_prefix(IRC_SERVER(server), target);
+ prefix = g_strndup(target, cleantarget - target);
+
+ /* and clean the rest here */
+ cleantarget = get_visible_target(IRC_SERVER(server), cleantarget);
+
+ nickmode = channel_get_nickmode(channel_find(server, cleantarget),
nick);
- optarget = g_strconcat("@", target, NULL);
- printformat_module("fe-common/core", server, target,
+ optarget = g_strconcat(prefix, cleantarget, NULL);
+
+ printformat_module("fe-common/core", server, cleantarget,
MSGLEVEL_PUBLIC,
TXT_PUBMSG_CHANNEL,
nick, optarget, msg, nickmode);
g_free(nickmode);
- g_free(optarget);
+ g_free(optarget);
+ g_free(prefix);
}
static void sig_message_own_wall(SERVER_REC *server, const char *msg,
@@ -117,7 +103,8 @@ static void sig_message_own_wall(SERVER_REC *server, const char *msg,
nickmode = channel_get_nickmode(channel_find(server, target),
server->nick);
- optarget = g_strconcat("@", target, NULL);
+ /* this is always @, skip_prefix is not needed here */
+ optarget = g_strconcat("@", target, NULL);
printformat_module("fe-common/core", server, target,
MSGLEVEL_PUBLIC | MSGLEVEL_NOHILIGHT |
MSGLEVEL_NO_ACT,
@@ -135,8 +122,8 @@ static void sig_message_own_action(IRC_SERVER_REC *server, const char *msg,
char *freemsg = NULL;
oldtarget = target;
- target = skip_target(IRC_SERVER(server), target);
- if (ischannel(*target))
+ target = fe_channel_skip_prefix(IRC_SERVER(server), target);
+ if (server_ischannel(SERVER(server), target))
item = irc_channel_find(server, target);
else
item = irc_query_find(server, target);
@@ -146,7 +133,7 @@ static void sig_message_own_action(IRC_SERVER_REC *server, const char *msg,
printformat(server, target,
MSGLEVEL_ACTIONS | MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT |
- (ischannel(*target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS),
+ (server_ischannel(SERVER(server), target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS),
item != NULL && oldtarget == target ? IRCTXT_OWN_ACTION : IRCTXT_OWN_ACTION_TARGET,
server->nick, msg, oldtarget);
g_free_not_null(freemsg);
@@ -160,12 +147,13 @@ static void sig_message_irc_action(IRC_SERVER_REC *server, const char *msg,
const char *oldtarget;
char *freemsg = NULL;
int level;
+ int own = FALSE;
oldtarget = target;
- target = skip_target(IRC_SERVER(server), target);
+ target = fe_channel_skip_prefix(IRC_SERVER(server), target);
level = MSGLEVEL_ACTIONS |
- (ischannel(*target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS);
+ (server_ischannel(SERVER(server), target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS);
if (ignore_check(SERVER(server), nick, address, target, msg, level))
return;
@@ -174,15 +162,17 @@ static void sig_message_irc_action(IRC_SERVER_REC *server, const char *msg,
level | MSGLEVEL_NO_ACT))
level |= MSGLEVEL_NO_ACT;
- if (ischannel(*target))
+ if (server_ischannel(SERVER(server), target)) {
item = irc_channel_find(server, target);
- else
- item = privmsg_get_query(SERVER(server), nick, FALSE, level);
+ } else {
+ own = (!g_strcmp0(nick, server->nick));
+ item = privmsg_get_query(SERVER(server), own ? target : nick, FALSE, level);
+ }
if (settings_get_bool("emphasis"))
msg = freemsg = expand_emphasis(item, msg);
- if (ischannel(*target)) {
+ if (server_ischannel(SERVER(server), target)) {
/* channel action */
if (window_item_is_active(item) && target == oldtarget) {
/* message to active channel in window */
@@ -195,20 +185,28 @@ static void sig_message_irc_action(IRC_SERVER_REC *server, const char *msg,
nick, oldtarget, msg);
}
} else {
- /* private action */
- printformat(server, nick, MSGLEVEL_ACTIONS | MSGLEVEL_MSGS,
- item == NULL ? IRCTXT_ACTION_PRIVATE :
- IRCTXT_ACTION_PRIVATE_QUERY,
- nick, address == NULL ? "" : address, msg);
+ if (own) {
+ /* own action bounced */
+ printformat(server, target,
+ MSGLEVEL_ACTIONS | MSGLEVEL_MSGS,
+ item != NULL && oldtarget == target ? IRCTXT_OWN_ACTION : IRCTXT_OWN_ACTION_TARGET,
+ server->nick, msg, oldtarget);
+ } else {
+ /* private action */
+ printformat(server, nick, MSGLEVEL_ACTIONS | MSGLEVEL_MSGS,
+ item == NULL ? IRCTXT_ACTION_PRIVATE :
+ IRCTXT_ACTION_PRIVATE_QUERY,
+ nick, address == NULL ? "" : address, msg);
+ }
}
-
+
g_free_not_null(freemsg);
}
static void sig_message_own_notice(IRC_SERVER_REC *server, const char *msg,
const char *target)
{
- printformat(server, skip_target(server, target), MSGLEVEL_NOTICES |
+ printformat(server, fe_channel_skip_prefix(server, target), MSGLEVEL_NOTICES |
MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT,
IRCTXT_OWN_NOTICE, target, msg);
}
@@ -219,9 +217,9 @@ static void sig_message_irc_notice(SERVER_REC *server, const char *msg,
{
const char *oldtarget;
int level = MSGLEVEL_NOTICES;
-
+
oldtarget = target;
- target = skip_target(IRC_SERVER(server), target);
+ target = fe_channel_skip_prefix(IRC_SERVER(server), target);
if (address == NULL || *address == '\0') {
/* notice from server */
@@ -234,16 +232,16 @@ static void sig_message_irc_notice(SERVER_REC *server, const char *msg,
}
if (ignore_check(server, nick, address,
- ischannel(*target) ? target : NULL,
+ server_ischannel(SERVER(server), target) ? target : NULL,
msg, level))
return;
if (ignore_check(server, nick, address,
- ischannel(*target) ? target : NULL,
+ server_ischannel(SERVER(server), target) ? target : NULL,
msg, level | MSGLEVEL_NO_ACT))
level |= MSGLEVEL_NO_ACT;
- if (ischannel(*target)) {
+ if (server_ischannel(SERVER(server), target)) {
/* notice in some channel */
printformat(server, target, level,
IRCTXT_NOTICE_PUBLIC, nick, oldtarget, msg);
@@ -259,7 +257,7 @@ static void sig_message_irc_notice(SERVER_REC *server, const char *msg,
static void sig_message_own_ctcp(IRC_SERVER_REC *server, const char *cmd,
const char *data, const char *target)
{
- printformat(server, skip_target(server, target), MSGLEVEL_CTCPS |
+ printformat(server, fe_channel_skip_prefix(server, target), MSGLEVEL_CTCPS |
MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT,
IRCTXT_OWN_CTCP, target, cmd, data);
}
@@ -271,8 +269,8 @@ static void sig_message_irc_ctcp(IRC_SERVER_REC *server, const char *cmd,
const char *oldtarget;
oldtarget = target;
- target = skip_target(server, target);
- printformat(server, ischannel(*target) ? target : nick, MSGLEVEL_CTCPS,
+ target = fe_channel_skip_prefix(server, target);
+ printformat(server, server_ischannel(SERVER(server), target) ? target : nick, MSGLEVEL_CTCPS,
IRCTXT_CTCP_REQUESTED, nick, addr, cmd, data, oldtarget);
}
diff --git a/src/fe-common/irc/fe-irc-queries.c b/src/fe-common/irc/fe-irc-queries.c
index 0861c9e4..c928a94a 100644
--- a/src/fe-common/irc/fe-irc-queries.c
+++ b/src/fe-common/irc/fe-irc-queries.c
@@ -63,7 +63,7 @@ static void event_privmsg(SERVER_REC *server, const char *data,
g_return_if_fail(data != NULL);
- if (nick == NULL || address == NULL || ischannel(*data) ||
+ if (nick == NULL || address == NULL || server_ischannel(server, data) ||
!settings_get_bool("query_track_nick_changes"))
return;
@@ -78,6 +78,13 @@ static void event_privmsg(SERVER_REC *server, const char *data,
if (!server_has_nick(server, query->name))
query_change_nick(query, nick);
}
+ } else {
+ /* process the changes to the query structure now, before the
+ * privmsg is dispatched. */
+ if (g_strcmp0(query->name, nick) != 0)
+ query_change_nick(query, nick);
+ if (address != NULL && g_strcmp0(query->address, address) != 0)
+ query_change_address(query, address);
}
}
diff --git a/src/fe-common/irc/fe-irc-server.c b/src/fe-common/irc/fe-irc-server.c
index fbfe4d9d..c4435d8f 100644
--- a/src/fe-common/irc/fe-irc-server.c
+++ b/src/fe-common/irc/fe-irc-server.c
@@ -50,11 +50,13 @@ const char *get_visible_target(IRC_SERVER_REC *server, const char *target)
return target;
}
-/* SYNTAX: SERVER ADD [-4 | -6] [-ssl] [-ssl_cert <cert>] [-ssl_pkey <pkey>] [-ssl_pass <password>]
- [-ssl_verify] [-ssl_cafile <cafile>] [-ssl_capath <capath>]
- [-auto | -noauto] [-network <network>] [-host <hostname>]
- [-cmdspeed <ms>] [-cmdmax <count>] [-port <port>]
- <address> [<port> [<password>]] */
+
+/* SYNTAX: SERVER ADD|MODIFY [-4 | -6] [-ssl] [-ssl_cert <cert>] [-ssl_pkey <pkey>] [-ssl_pass <password>]
+ [-ssl_verify] [-ssl_cafile <cafile>] [-ssl_capath <capath>]
+ [-ssl_ciphers <list>]
+ [-auto | -noauto] [-network <network>] [-host <hostname>]
+ [-cmdspeed <ms>] [-cmdmax <count>] [-port <port>]
+ <address> [<port> [<password>]] */
/* NOTE: -network replaces the old -ircnet flag. */
static void sig_server_add_fill(IRC_SERVER_SETUP_REC *rec,
GHashTable *optlist)
@@ -63,7 +65,7 @@ static void sig_server_add_fill(IRC_SERVER_SETUP_REC *rec,
char *value;
value = g_hash_table_lookup(optlist, "network");
- /* For backwards compatibility, also allow the old name 'ircnet'.
+ /* For backwards compatibility, also allow the old name 'ircnet'.
But of course only if -network was not given. */
if (!value)
value = g_hash_table_lookup(optlist, "ircnet");
@@ -106,22 +108,28 @@ static void cmd_server_list(const char *data)
g_string_append(str, "autoconnect, ");
if (rec->no_proxy)
g_string_append(str, "noproxy, ");
- if (rec->use_ssl) {
- g_string_append(str, "ssl, ");
- if (rec->ssl_cert) {
- g_string_append_printf(str, "ssl_cert: %s, ", rec->ssl_cert);
- if (rec->ssl_pkey)
- g_string_append_printf(str, "ssl_pkey: %s, ", rec->ssl_pkey);
- if (rec->ssl_pass)
+ if (rec->use_tls) {
+ g_string_append(str, "tls, ");
+ if (rec->tls_cert) {
+ g_string_append_printf(str, "tls_cert: %s, ", rec->tls_cert);
+ if (rec->tls_pkey)
+ g_string_append_printf(str, "tls_pkey: %s, ", rec->tls_pkey);
+ if (rec->tls_pass)
g_string_append_printf(str, "(pass), ");
}
- if (rec->ssl_verify)
- g_string_append(str, "ssl_verify, ");
- if (rec->ssl_cafile)
- g_string_append_printf(str, "ssl_cafile: %s, ", rec->ssl_cafile);
- if (rec->ssl_capath)
- g_string_append_printf(str, "ssl_capath: %s, ", rec->ssl_capath);
-
+ if (rec->tls_verify)
+ g_string_append(str, "tls_verify, ");
+ if (rec->tls_cafile)
+ g_string_append_printf(str, "tls_cafile: %s, ", rec->tls_cafile);
+ if (rec->tls_capath)
+ g_string_append_printf(str, "tls_capath: %s, ", rec->tls_capath);
+ if (rec->tls_ciphers)
+ g_string_append_printf(str, "tls_ciphers: %s, ", rec->tls_ciphers);
+ if (rec->tls_pinned_cert)
+ g_string_append_printf(str, "tls_pinned_cert: %s, ", rec->tls_pinned_cert);
+ if (rec->tls_pinned_pubkey)
+ g_string_append_printf(str, "tls_pinned_pubkey: %s, ", rec->tls_pinned_pubkey);
+
}
if (rec->max_cmds_at_once > 0)
g_string_append_printf(str, "cmdmax: %d, ", rec->max_cmds_at_once);
diff --git a/src/fe-common/irc/fe-ircnet.c b/src/fe-common/irc/fe-ircnet.c
index 6618edd7..b70a9ea7 100644
--- a/src/fe-common/irc/fe-ircnet.c
+++ b/src/fe-common/irc/fe-ircnet.c
@@ -29,6 +29,8 @@
#include "irc-servers.h"
#include "irc-chatnets.h"
#include "printtext.h"
+#include "servers-setup.h"
+#include "channels-setup.h"
static void cmd_network_list(void)
{
@@ -56,7 +58,12 @@ static void cmd_network_list(void)
g_string_append_printf(str, "autosendcmd: %s, ", rec->autosendcmd);
if (rec->usermode != NULL)
g_string_append_printf(str, "usermode: %s, ", rec->usermode);
-
+ if (rec->sasl_mechanism != NULL)
+ g_string_append_printf(str, "sasl_mechanism: %s, ", rec->sasl_mechanism);
+ if (rec->sasl_username != NULL)
+ g_string_append_printf(str, "sasl_username: %s, ", rec->sasl_username);
+ if (rec->sasl_password != NULL)
+ g_string_append_printf(str, "sasl_password: (pass), ");
if (rec->cmd_queue_speed > 0)
g_string_append_printf(str, "cmdspeed: %d, ", rec->cmd_queue_speed);
if (rec->max_cmds_at_once > 0)
@@ -81,12 +88,7 @@ static void cmd_network_list(void)
printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_NETWORK_FOOTER);
}
-/* SYNTAX: NETWORK ADD [-nick <nick>] [-user <user>] [-realname <name>]
- [-host <host>] [-usermode <mode>] [-autosendcmd <cmd>]
- [-querychans <count>] [-whois <count>] [-msgs <count>]
- [-kicks <count>] [-modes <count>]
- [-cmdspeed <ms>] [-cmdmax <count>] <name> */
-static void cmd_network_add(const char *data)
+static void cmd_network_add_modify(const char *data, gboolean add)
{
GHashTable *optlist;
char *name, *value;
@@ -94,12 +96,20 @@ static void cmd_network_add(const char *data)
IRC_CHATNET_REC *rec;
if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS,
- "network add", &optlist, &name))
+ "network add", &optlist, &name))
return;
+
if (*name == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
rec = ircnet_find(name);
if (rec == NULL) {
+ if (add == FALSE) {
+ cmd_params_free(free_arg);
+ printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+ IRCTXT_NETWORK_NOT_FOUND, name);
+ return;
+ }
+
rec = g_new0(IRC_CHATNET_REC, 1);
rec->name = g_strdup(name);
} else {
@@ -112,6 +122,9 @@ static void cmd_network_add(const char *data)
}
if (g_hash_table_lookup(optlist, "usermode")) g_free_and_null(rec->usermode);
if (g_hash_table_lookup(optlist, "autosendcmd")) g_free_and_null(rec->autosendcmd);
+ if (g_hash_table_lookup(optlist, "sasl_mechanism")) g_free_and_null(rec->sasl_mechanism);
+ if (g_hash_table_lookup(optlist, "sasl_username")) g_free_and_null(rec->sasl_username);
+ if (g_hash_table_lookup(optlist, "sasl_password")) g_free_and_null(rec->sasl_password);
}
value = g_hash_table_lookup(optlist, "kicks");
@@ -148,12 +161,37 @@ static void cmd_network_add(const char *data)
value = g_hash_table_lookup(optlist, "autosendcmd");
if (value != NULL && *value != '\0') rec->autosendcmd = g_strdup(value);
+ /* the validity of the parameters is checked in sig_server_setup_fill_chatnet */
+ value = g_hash_table_lookup(optlist, "sasl_mechanism");
+ if (value != NULL && *value != '\0') rec->sasl_mechanism = g_strdup(value);
+ value = g_hash_table_lookup(optlist, "sasl_username");
+ if (value != NULL && *value != '\0') rec->sasl_username = g_strdup(value);
+ value = g_hash_table_lookup(optlist, "sasl_password");
+ if (value != NULL && *value != '\0') rec->sasl_password = g_strdup(value);
+
ircnet_create(rec);
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_NETWORK_ADDED, name);
cmd_params_free(free_arg);
}
+/* SYNTAX: NETWORK ADD|MODIFY [-nick <nick>] [-user <user>] [-realname <name>]
+ [-host <host>] [-usermode <mode>] [-autosendcmd <cmd>]
+ [-querychans <count>] [-whois <count>] [-msgs <count>]
+ [-kicks <count>] [-modes <count>] [-cmdspeed <ms>]
+ [-cmdmax <count>] [-sasl_mechanism <mechanism>]
+ [-sasl_username <username>] [-sasl_password <password>]
+ <name> */
+static void cmd_network_add(const char *data)
+{
+ cmd_network_add_modify(data, TRUE);
+}
+
+static void cmd_network_modify(const char *data)
+{
+ cmd_network_add_modify(data, FALSE);
+}
+
/* SYNTAX: NETWORK REMOVE <network> */
static void cmd_network_remove(const char *data)
{
@@ -165,6 +203,8 @@ static void cmd_network_remove(const char *data)
if (rec == NULL)
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_NETWORK_NOT_FOUND, data);
else {
+ server_setup_remove_chatnet(data);
+ channel_setup_remove_chatnet(data);
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_NETWORK_REMOVED, data);
chatnet_remove(CHATNET(rec));
}
@@ -184,9 +224,13 @@ void fe_ircnet_init(void)
command_bind("network", NULL, (SIGNAL_FUNC) cmd_network);
command_bind("network list", NULL, (SIGNAL_FUNC) cmd_network_list);
command_bind("network add", NULL, (SIGNAL_FUNC) cmd_network_add);
+ command_bind("network modify", NULL, (SIGNAL_FUNC) cmd_network_modify);
command_bind("network remove", NULL, (SIGNAL_FUNC) cmd_network_remove);
- command_set_options("network add", "-kicks -msgs -modes -whois -cmdspeed -cmdmax -nick -user -realname -host -autosendcmd -querychans -usermode");
+ command_set_options("network add", "-kicks -msgs -modes -whois -cmdspeed "
+ "-cmdmax -nick -user -realname -host -autosendcmd -querychans -usermode -sasl_mechanism -sasl_username -sasl_password");
+ command_set_options("network modify", "-kicks -msgs -modes -whois -cmdspeed "
+ "-cmdmax -nick -user -realname -host -autosendcmd -querychans -usermode -sasl_mechanism -sasl_username -sasl_password");
}
void fe_ircnet_deinit(void)
@@ -195,5 +239,6 @@ void fe_ircnet_deinit(void)
command_unbind("network", (SIGNAL_FUNC) cmd_network);
command_unbind("network list", (SIGNAL_FUNC) cmd_network_list);
command_unbind("network add", (SIGNAL_FUNC) cmd_network_add);
+ command_unbind("network modify", (SIGNAL_FUNC) cmd_network_modify);
command_unbind("network remove", (SIGNAL_FUNC) cmd_network_remove);
}
diff --git a/src/fe-common/irc/fe-modes.c b/src/fe-common/irc/fe-modes.c
index 027d7a76..53e56c97 100644
--- a/src/fe-common/irc/fe-modes.c
+++ b/src/fe-common/irc/fe-modes.c
@@ -135,7 +135,7 @@ static void msg_multi_mode(IRC_CHANNEL_REC *channel, const char *sender,
signal_add("print starting", (SIGNAL_FUNC) sig_print_starting);
rec = mode_find_channel(channel);
- if (rec != NULL && strcmp(rec->mode, mode) != 0) {
+ if (rec != NULL && g_strcmp0(rec->mode, mode) != 0) {
/* different mode than last time, show and remove the old */
print_mode(rec);
mode_destroy(rec);
@@ -168,7 +168,7 @@ static void sig_message_mode(IRC_SERVER_REC *server, const char *channel,
mode, MSGLEVEL_MODES))
return;
- if (!ischannel(*channel)) {
+ if (!server_ischannel(SERVER(server), channel)) {
/* user mode change */
printformat(server, NULL, MSGLEVEL_MODES,
IRCTXT_USERMODE_CHANGE, mode, channel);
diff --git a/src/fe-common/irc/fe-netjoin.c b/src/fe-common/irc/fe-netjoin.c
index 35c463e4..4eb388c0 100644
--- a/src/fe-common/irc/fe-netjoin.c
+++ b/src/fe-common/irc/fe-netjoin.c
@@ -164,11 +164,11 @@ static void print_channel_netjoins(char *channel, TEMP_PRINT_REC *rec,
g_free(channel);
}
-static void print_netjoins(NETJOIN_SERVER_REC *server)
+static void print_netjoins(NETJOIN_SERVER_REC *server, const char *filter_channel)
{
TEMP_PRINT_REC *temp;
GHashTable *channels;
- GSList *tmp, *next, *old;
+ GSList *tmp, *tmp2, *next, *next2, *old;
g_return_if_fail(server != NULL);
@@ -181,11 +181,19 @@ static void print_netjoins(NETJOIN_SERVER_REC *server)
for (tmp = server->netjoins; tmp != NULL; tmp = next) {
NETJOIN_REC *rec = tmp->data;
- next = tmp->next;
- while (rec->now_channels != NULL) {
- char *channel = rec->now_channels->data;
+ next = g_slist_next(tmp);
+
+ for (tmp2 = rec->now_channels; tmp2 != NULL; tmp2 = next2) {
+ char *channel = tmp2->data;
char *realchannel = channel + 1;
+ next2 = g_slist_next(tmp2);
+
+ /* Filter the results by channel if asked to do so */
+ if (filter_channel != NULL &&
+ strcasecmp(realchannel, filter_channel) != 0)
+ continue;
+
temp = g_hash_table_lookup(channels, realchannel);
if (temp == NULL) {
temp = g_new0(TEMP_PRINT_REC, 1);
@@ -214,8 +222,8 @@ static void print_netjoins(NETJOIN_SERVER_REC *server)
g_free(data);
}
- rec->now_channels =
- g_slist_remove(rec->now_channels, channel);
+ /* drop tmp2 from the list */
+ rec->now_channels = g_slist_delete_link(rec->now_channels, tmp2);
g_free(channel);
}
@@ -235,20 +243,25 @@ static void print_netjoins(NETJOIN_SERVER_REC *server)
/* something is going to be printed to screen, print our current netsplit
message before it. */
-static void sig_print_starting(void)
+static void sig_print_starting(TEXT_DEST_REC *dest)
{
- GSList *tmp, *next;
+ NETJOIN_SERVER_REC *rec;
if (printing_joins)
return;
- for (tmp = joinservers; tmp != NULL; tmp = next) {
- NETJOIN_SERVER_REC *server = tmp->data;
+ if (!IS_IRC_SERVER(dest->server))
+ return;
- next = tmp->next;
- if (server->netjoins != NULL)
- print_netjoins(server);
- }
+ if (!(dest->level & MSGLEVEL_PUBLIC))
+ return;
+
+ if (!server_ischannel(dest->server, dest->target))
+ return;
+
+ rec = netjoin_find_server(IRC_SERVER(dest->server));
+ if (rec != NULL && rec->netjoins != NULL)
+ print_netjoins(rec, dest->target);
}
static int sig_check_netjoins(void)
@@ -272,7 +285,7 @@ static int sig_check_netjoins(void)
}
if (server->netjoins != NULL)
- print_netjoins(server);
+ print_netjoins(server, NULL);
}
/* now remove all netjoins which haven't had any new joins
@@ -400,7 +413,7 @@ static void msg_mode(IRC_SERVER_REC *server, const char *channel,
int show;
g_return_if_fail(data != NULL);
- if (!ischannel(*channel) || addr != NULL)
+ if (!server_ischannel(SERVER(server), channel) || addr != NULL)
return;
params = event_get_params(data, 2 | PARAM_FLAG_GETREST,
diff --git a/src/fe-common/irc/fe-netsplit.c b/src/fe-common/irc/fe-netsplit.c
index 3eb30796..17fb1994 100644
--- a/src/fe-common/irc/fe-netsplit.c
+++ b/src/fe-common/irc/fe-netsplit.c
@@ -142,7 +142,7 @@ static void get_server_splits(void *key, NETSPLIT_REC *split,
}
}
-static void print_server_splits(IRC_SERVER_REC *server, TEMP_SPLIT_REC *rec)
+static void print_server_splits(IRC_SERVER_REC *server, TEMP_SPLIT_REC *rec, const char *filter_channel)
{
GString *destservers;
char *sourceserver;
@@ -168,6 +168,10 @@ static void print_server_splits(IRC_SERVER_REC *server, TEMP_SPLIT_REC *rec)
for (tmp = rec->channels; tmp != NULL; tmp = tmp->next) {
TEMP_SPLIT_CHAN_REC *chan = tmp->data;
+ if (filter_channel != NULL &&
+ strcasecmp(chan->name, filter_channel) != 0)
+ continue;
+
g_string_truncate(chan->nicks, chan->nicks->len-2);
if (netsplit_max_nicks > 0 &&
@@ -193,7 +197,7 @@ static void temp_split_chan_free(TEMP_SPLIT_CHAN_REC *rec)
g_free(rec);
}
-static void print_splits(IRC_SERVER_REC *server)
+static void print_splits(IRC_SERVER_REC *server, const char *channel)
{
TEMP_SPLIT_REC temp;
GSList *servers;
@@ -212,7 +216,7 @@ static void print_splits(IRC_SERVER_REC *server)
g_hash_table_foreach(server->splits,
(GHFunc) get_server_splits, &temp);
- print_server_splits(server, &temp);
+ print_server_splits(server, &temp, channel);
g_slist_foreach(temp.channels,
(GFunc) temp_split_chan_free, NULL);
@@ -233,25 +237,31 @@ static int check_server_splits(IRC_SERVER_REC *server)
if (time(NULL)-last < SPLIT_WAIT_TIME)
return FALSE;
- print_splits(server);
+ print_splits(server, NULL);
return TRUE;
}
/* something is going to be printed to screen, print our current netsplit
message before it. */
-static void sig_print_starting(void)
+static void sig_print_starting(TEXT_DEST_REC *dest)
{
- GSList *tmp;
+ IRC_SERVER_REC *rec;
if (printing_splits)
return;
- for (tmp = servers; tmp != NULL; tmp = tmp->next) {
- IRC_SERVER_REC *rec = tmp->data;
+ if (!IS_IRC_SERVER(dest->server))
+ return;
- if (IS_IRC_SERVER(rec) && rec->split_servers != NULL)
- print_splits(rec);
- }
+ if (!(dest->level & MSGLEVEL_PUBLIC))
+ return;
+
+ if (!server_ischannel(dest->server, dest->target))
+ return;
+
+ rec = IRC_SERVER(dest->server);
+ if (rec->split_servers != NULL)
+ print_splits(rec, dest->target);
}
static int sig_check_splits(void)
diff --git a/src/fe-common/irc/fe-sasl.c b/src/fe-common/irc/fe-sasl.c
new file mode 100644
index 00000000..fc8105fc
--- /dev/null
+++ b/src/fe-common/irc/fe-sasl.c
@@ -0,0 +1,75 @@
+/*
+ fe-sasl.c : irssi
+
+ Copyright (C) 2015-2017 The Lemon Man
+
+ 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "module.h"
+#include "module-formats.h"
+#include "signals.h"
+#include "levels.h"
+#include "misc.h"
+#include "sasl.h"
+
+#include "irc-servers.h"
+#include "settings.h"
+
+#include "printtext.h"
+
+static void sig_sasl_success(IRC_SERVER_REC *server)
+{
+ printformat(server, NULL, MSGLEVEL_CRAP, IRCTXT_SASL_SUCCESS);
+}
+
+static void sig_sasl_failure(IRC_SERVER_REC *server, const char *reason)
+{
+ printformat(server, NULL, MSGLEVEL_CRAP, IRCTXT_SASL_ERROR, reason);
+}
+
+static void sig_cap_end(IRC_SERVER_REC *server)
+{
+ /* The negotiation has now been terminated, if we didn't manage to
+ * authenticate successfully with the server just disconnect. */
+ if (!server->sasl_success &&
+ server->connrec->sasl_mechanism != SASL_MECHANISM_NONE &&
+ settings_get_bool("sasl_disconnect_on_failure")) {
+ /* We can't use server_disconnect() here because we'd end up
+ * freeing the 'server' object and be guilty of a slew of UaF. */
+ server->connection_lost = TRUE;
+ /* By setting connection_lost we make sure the communication is
+ * halted and when the control goes back to irc_parse_incoming
+ * the server object is safely destroyed. */
+ signal_stop();
+ }
+
+}
+
+void fe_sasl_init(void)
+{
+ settings_add_bool("server", "sasl_disconnect_on_failure", TRUE);
+
+ signal_add("server sasl success", (SIGNAL_FUNC) sig_sasl_success);
+ signal_add("server sasl failure", (SIGNAL_FUNC) sig_sasl_failure);
+ signal_add_first("server cap end", (SIGNAL_FUNC) sig_cap_end);
+}
+
+void fe_sasl_deinit(void)
+{
+ signal_remove("server sasl success", (SIGNAL_FUNC) sig_sasl_success);
+ signal_remove("server sasl failure", (SIGNAL_FUNC) sig_sasl_failure);
+ signal_remove("server cap end", (SIGNAL_FUNC) sig_cap_end);
+}
diff --git a/src/fe-common/irc/fe-whois.c b/src/fe-common/irc/fe-whois.c
index c5726124..117df120 100644
--- a/src/fe-common/irc/fe-whois.c
+++ b/src/fe-common/irc/fe-whois.c
@@ -92,7 +92,7 @@ static void event_whois_oper(IRC_SERVER_REC *server, const char *data)
params = event_get_params(data, 3, NULL, &nick, &type);
- /* Bugfix: http://bugs.irssi.org/?do=details&id=99
+ /* Bugfix: http://bugs.irssi.org/?do=details&task_id=99
* Author: Geert Hauwaerts <geert@irssi.org>
* Date: Wed Sep 15 20:17:24 CEST 2004
*/
@@ -134,8 +134,8 @@ static void event_whois_realhost(IRC_SERVER_REC *server, const char *data)
/* <yournick> real hostname <nick> <hostname> */
params = event_get_params(data, 5, NULL, &nick, &txt_real,
&txt_hostname, &hostname);
- if (strcmp(txt_real, "real") != 0 ||
- strcmp(txt_hostname, "hostname") != 0) {
+ if (g_strcmp0(txt_real, "real") != 0 ||
+ g_strcmp0(txt_hostname, "hostname") != 0) {
/* <yournick> <nick> :... from <hostname> */
g_free(params);
params = event_get_params(data, 3, NULL, &nick, &hostname);
@@ -219,7 +219,7 @@ static void event_whois_usermode(IRC_SERVER_REC *server, const char *data)
params = event_get_params(data, 4, NULL, &txt_usermodes,
&nick, &usermode);
- if (strcmp(txt_usermodes, "usermodes") == 0) {
+ if (g_strcmp0(txt_usermodes, "usermodes") == 0) {
/* <yournick> usermodes <nick> usermode */
printformat(server, nick, MSGLEVEL_CRAP,
IRCTXT_WHOIS_USERMODE, nick, usermode);
@@ -409,8 +409,8 @@ void fe_whois_init(void)
signal_add("event 311", (SIGNAL_FUNC) event_whois);
signal_add("event 312", (SIGNAL_FUNC) event_whois_server);
- /* readding this events fixes the printing of /whois -yes *
- Bug http://bugs.irssi.org/?do=details&id=123 */
+ /* readding this events fixes the printing of /whois -yes *
+ Bug http://bugs.irssi.org/?do=details&task_id=123 */
signal_add("event 317", (SIGNAL_FUNC) event_whois_idle);
signal_add("event 319", (SIGNAL_FUNC) event_whois_channels);
signal_add("event 313", (SIGNAL_FUNC) event_whois_oper);
diff --git a/src/fe-common/irc/module-formats.c b/src/fe-common/irc/module-formats.c
index 6eaf18e8..f7b074ec 100644
--- a/src/fe-common/irc/module-formats.c
+++ b/src/fe-common/irc/module-formats.c
@@ -44,6 +44,8 @@ FORMAT_REC fecommon_irc_formats[] = {
{ "setupserver_header", "%#Server Port Network Settings", 0 },
{ "setupserver_line", "%#%|$[!20]0 $[5]1 $[10]2 $3", 4, { 0, 1, 0, 0 } },
{ "setupserver_footer", "", 0 },
+ { "sasl_success", "SASL authentication succeeded", 0 },
+ { "sasl_error", "Cannot authenticate via SASL ($0)", 1, { 0 } },
/* ---- */
{ NULL, "Channels", 0 },
diff --git a/src/fe-common/irc/module-formats.h b/src/fe-common/irc/module-formats.h
index 34dd3efc..c45f4562 100644
--- a/src/fe-common/irc/module-formats.h
+++ b/src/fe-common/irc/module-formats.h
@@ -22,6 +22,8 @@ enum {
IRCTXT_SETUPSERVER_HEADER,
IRCTXT_SETUPSERVER_LINE,
IRCTXT_SETUPSERVER_FOOTER,
+ IRCTXT_SASL_SUCCESS,
+ IRCTXT_SASL_ERROR,
IRCTXT_FILL_2,
diff --git a/src/fe-fuzz/Makefile.am b/src/fe-fuzz/Makefile.am
new file mode 100644
index 00000000..3a547c66
--- /dev/null
+++ b/src/fe-fuzz/Makefile.am
@@ -0,0 +1,25 @@
+bin_PROGRAMS = irssi-fuzz
+
+# Force link with clang++ for libfuzzer support
+CCLD=clang++ $(CXXFLAGS)
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/src/core/ \
+ -I$(top_srcdir)/src/irc/core/ \
+ -I$(top_srcdir)/src/fe-common/core/ \
+ $(GLIB_CFLAGS)
+
+irssi_fuzz_DEPENDENCIES = @COMMON_LIBS@
+
+irssi_fuzz_LDADD = \
+ @COMMON_LIBS@ \
+ @PROG_LIBS@ \
+ $(FUZZER_LIBS)
+
+irssi_fuzz_SOURCES = \
+ irssi.c \
+ $(top_srcdir)/src/fe-text/module-formats.c
+
+noinst_HEADERS = \
+ $(top_srcdir)/src/fe-text/module-formats.h
diff --git a/src/fe-fuzz/irssi.c b/src/fe-fuzz/irssi.c
new file mode 100644
index 00000000..77892aaf
--- /dev/null
+++ b/src/fe-fuzz/irssi.c
@@ -0,0 +1,57 @@
+/*
+ irssi.c : irssi
+
+ Copyright (C) 2017 Joseph Bisch
+
+ 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "module.h"
+#include "modules-load.h"
+#include "levels.h"
+#include "../fe-text/module-formats.h" // need to explicitly grab from fe-text
+#include "themes.h"
+#include "core.h"
+#include "fe-common-core.h"
+#include "args.h"
+#include "printtext.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+int LLVMFuzzerInitialize(int *argc, char ***argv) {
+ core_register_options();
+ fe_common_core_register_options();
+ /* no args */
+ args_execute(0, NULL);
+ core_preinit((*argv)[0]);
+ core_init();
+ fe_common_core_init();
+ theme_register(gui_text_formats);
+ module_register("core", "fe-fuzz");
+ printtext_string(NULL, NULL, MSGLEVEL_CLIENTCRAP, "init");
+ return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ char *copy = (char *)malloc(sizeof(char)*(size+1));
+ memcpy(copy, data, size);
+ copy[size] = '\0';
+ printtext_string(NULL, NULL, MSGLEVEL_CLIENTCRAP, copy);
+ free(copy);
+ return 0;
+}
diff --git a/src/fe-fuzz/tokens.txt b/src/fe-fuzz/tokens.txt
new file mode 100644
index 00000000..e337b6e9
--- /dev/null
+++ b/src/fe-fuzz/tokens.txt
@@ -0,0 +1,143 @@
+"@%+"
+"*@*!*"
+"001"
+"002"
+"003"
+"004"
+"005"
+"221"
+"254"
+"271"
+"272"
+"281"
+"301"
+"302"
+"303"
+"305"
+"306"
+"311"
+"312"
+"313"
+"314"
+"315"
+"317"
+"318"
+"319"
+"324"
+"326"
+"327"
+"328"
+"329"
+"330"
+"332"
+"333"
+"338"
+"341"
+"344"
+"345"
+"346"
+"347"
+"348"
+"349"
+"352"
+"353"
+"364"
+"365"
+"366"
+"367"
+"368"
+"369"
+"372"
+"375"
+"376"
+"377"
+"378"
+"379"
+"381"
+"386"
+"387"
+"388"
+"389"
+"396"
+"401"
+"403"
+"404"
+"405"
+"407"
+"408"
+"410"
+"421"
+"422"
+"433"
+"436"
+"437"
+"438"
+"439"
+"442"
+"465"
+"470"
+"471"
+"472"
+"473"
+"474"
+"475"
+"476"
+"477"
+"478"
+"479"
+"482"
+"486"
+"489"
+"494"
+"506"
+"707"
+"716"
+"717"
+"728"
+"729"
+"902"
+"903"
+"904"
+"905"
+"906"
+"907"
+":a"
+"+a"
+"ACK"
+"authenticate"
+"away"
+"-b"
+"+b"
+"cap"
+"#chan"
+"connected"
+"empty"
+"error"
+"invite"
+"join"
+"kick"
+"kill"
+"LS"
+"mode"
+"multi-prefix"
+"NAK"
+"network"
+"nick"
+"nicklen"
+"notice"
+"-o"
+"+o"
+"part"
+"ping"
+"pong"
+"prefix"
+"privmsg"
+"quit"
+"sasl"
+"topic"
+"wallops"
+"watch"
+":\x01"
+":\x01ACTION"
+":\x01PING"
+":\x01VERSION"
diff --git a/src/fe-text/Makefile.am b/src/fe-text/Makefile.am
index b9538e60..bdd3df22 100644
--- a/src/fe-text/Makefile.am
+++ b/src/fe-text/Makefile.am
@@ -4,8 +4,7 @@ AM_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/core/ \
-I$(top_srcdir)/src/fe-common/core/ \
- $(GLIB_CFLAGS) \
- $(CURSES_INCLUDEDIR)
+ $(GLIB_CFLAGS)
irssi_DEPENDENCIES = \
@COMMON_LIBS@ \
@@ -22,25 +21,11 @@ irssi_LDADD = \
@PROG_LIBS@ \
@TEXTUI_LIBS@
-tparm_sources = \
- tparm.c
-
terminfo_sources = \
term-terminfo.c \
terminfo-core.c
-curses_sources = \
- term-curses.c
-
-if NEED_TPARM
-use_tparm_sources = $(tparm_sources)
-endif
-
-if USE_CURSES
-use_term_sources = $(curses_sources)
-else
use_term_sources = $(terminfo_sources)
-endif
irssi_SOURCES = \
gui-entry.c \
@@ -56,8 +41,6 @@ irssi_SOURCES = \
statusbar-config.c \
statusbar-items.c \
term.c \
- term-dummy.c \
- $(use_tparm_sources) \
$(use_term_sources) \
textbuffer.c \
textbuffer-commands.c \
@@ -85,6 +68,4 @@ noinst_HEADERS = \
module-formats.h
EXTRA_DIST = \
- $(tparm_sources) \
- $(terminfo_sources) \
- $(curses_sources)
+ $(terminfo_sources)
diff --git a/src/fe-text/gui-entry.c b/src/fe-text/gui-entry.c
index f123ce4c..f05decd2 100644
--- a/src/fe-text/gui-entry.c
+++ b/src/fe-text/gui-entry.c
@@ -26,30 +26,33 @@
#include "gui-entry.h"
#include "gui-printtext.h"
#include "term.h"
+#include "recode.h"
#undef i_toupper
#undef i_tolower
#undef i_isalnum
+#define KILL_RING_MAX 10
+
static unichar i_toupper(unichar c)
{
if (term_type == TERM_TYPE_UTF8)
return g_unichar_toupper(c);
- return (c >= 0 && c <= 255) ? toupper(c) : c;
+ return c <= 255 ? toupper(c) : c;
}
static unichar i_tolower(unichar c)
{
if (term_type == TERM_TYPE_UTF8)
return g_unichar_tolower(c);
- return (c >= 0 && c <= 255) ? tolower(c) : c;
+ return c <= 255 ? tolower(c) : c;
}
static int i_isalnum(unichar c)
{
if (term_type == TERM_TYPE_UTF8)
return (g_unichar_isalnum(c) || mk_wcwidth(c) == 0);
- return (c >= 0 && c <= 255) ? isalnum(c) : 0;
+ return c <= 255 ? isalnum(c) : 0;
}
GUI_ENTRY_REC *active_entry;
@@ -81,11 +84,22 @@ GUI_ENTRY_REC *gui_entry_create(int xpos, int ypos, int width, int utf8)
void gui_entry_destroy(GUI_ENTRY_REC *entry)
{
+ GSList *tmp;
+
g_return_if_fail(entry != NULL);
if (active_entry == entry)
gui_entry_set_active(NULL);
+ for (tmp = entry->kill_ring; tmp != NULL; tmp = tmp->next) {
+ GUI_ENTRY_CUTBUFFER_REC *rec = tmp->data;
+ if (rec != NULL) {
+ g_free(rec->cutbuffer);
+ g_free(rec);
+ }
+ }
+ g_slist_free(entry->kill_ring);
+
g_free(entry->text);
g_free(entry->prompt);
g_free(entry);
@@ -226,8 +240,8 @@ static void gui_entry_draw_from(GUI_ENTRY_REC *entry, int pos)
int i;
int xpos, end_xpos;
- xpos = entry->xpos + entry->promptlen +
- pos2scrpos(entry, pos + entry->scrstart) -
+ xpos = entry->xpos + entry->promptlen +
+ pos2scrpos(entry, pos + entry->scrstart) -
pos2scrpos(entry, entry->scrstart);
end_xpos = entry->xpos + entry->width;
@@ -345,6 +359,19 @@ void gui_entry_set_active(GUI_ENTRY_REC *entry)
}
}
+/* Return screen length of plain string */
+static int scrlen_str(const char *str)
+{
+ int len = 0;
+ char *stripped;
+ g_return_val_if_fail(str != NULL, 0);
+
+ stripped = strip_codes(str);
+ len = string_width(stripped, -1);
+ g_free(stripped);
+ return len;
+}
+
void gui_entry_set_prompt(GUI_ENTRY_REC *entry, const char *str)
{
int oldlen;
@@ -355,11 +382,11 @@ void gui_entry_set_prompt(GUI_ENTRY_REC *entry, const char *str)
if (str != NULL) {
g_free_not_null(entry->prompt);
entry->prompt = g_strdup(str);
- entry->promptlen = format_get_length(str);
+ entry->promptlen = scrlen_str(str);
}
if (entry->prompt != NULL)
- gui_printtext(entry->xpos, entry->ypos, entry->prompt);
+ gui_printtext_internal(entry->xpos, entry->ypos, entry->prompt);
if (entry->promptlen != oldlen) {
gui_entry_fix_cursor(entry);
@@ -513,28 +540,51 @@ void gui_entry_insert_char(GUI_ENTRY_REC *entry, unichar chr)
char *gui_entry_get_cutbuffer(GUI_ENTRY_REC *entry)
{
+ GUI_ENTRY_CUTBUFFER_REC *tmp;
char *buf;
int i;
g_return_val_if_fail(entry != NULL, NULL);
- if (entry->cutbuffer == NULL)
+ if (entry->kill_ring == NULL || entry->kill_ring->data == NULL)
+ return NULL;
+
+ tmp = entry->kill_ring->data;
+
+ if (tmp->cutbuffer == NULL)
return NULL;
if (entry->utf8)
- buf = g_ucs4_to_utf8(entry->cutbuffer, -1, NULL, NULL, NULL);
+ buf = g_ucs4_to_utf8(tmp->cutbuffer, -1, NULL, NULL, NULL);
else {
- buf = g_malloc(entry->cutbuffer_len*6 + 1);
+ buf = g_malloc(tmp->cutbuffer_len*6 + 1);
if (term_type == TERM_TYPE_BIG5)
- unichars_to_big5(entry->cutbuffer, buf);
+ unichars_to_big5(tmp->cutbuffer, buf);
else
- for (i = 0; i <= entry->cutbuffer_len; i++)
- buf[i] = entry->cutbuffer[i];
+ for (i = 0; i <= tmp->cutbuffer_len; i++)
+ buf[i] = tmp->cutbuffer[i];
}
return buf;
}
-void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, int update_cutbuffer)
+char *gui_entry_get_next_cutbuffer(GUI_ENTRY_REC *entry)
+{
+ GUI_ENTRY_CUTBUFFER_REC *tmp;
+
+ g_return_val_if_fail(entry != NULL, NULL);
+
+ if (entry->kill_ring == NULL)
+ return NULL;
+
+ tmp = entry->kill_ring->data;
+
+ entry->kill_ring = g_slist_remove(entry->kill_ring, tmp);
+ entry->kill_ring = g_slist_append(entry->kill_ring, tmp);
+
+ return gui_entry_get_cutbuffer(entry);
+}
+
+void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, CUTBUFFER_UPDATE_OP update_cutbuffer)
{
int newpos, size = 0;
@@ -545,7 +595,41 @@ void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, int update_cutbuffer)
gui_entry_erase(entry, size, update_cutbuffer);
}
-void gui_entry_erase(GUI_ENTRY_REC *entry, int size, int update_cutbuffer)
+static GUI_ENTRY_CUTBUFFER_REC *get_cutbuffer_rec(GUI_ENTRY_REC *entry, CUTBUFFER_UPDATE_OP update_cutbuffer)
+{
+ GUI_ENTRY_CUTBUFFER_REC *tmp;
+
+ g_return_val_if_fail(entry != NULL, NULL);
+
+ if (entry->kill_ring == NULL) {
+ /* no kill ring exists */
+ entry->kill_ring = g_slist_prepend(entry->kill_ring, (void *)NULL);
+ } else {
+ tmp = entry->kill_ring->data;
+
+ if (tmp != NULL && tmp->cutbuffer_len > 0
+ && (!entry->previous_append_next_kill
+ || update_cutbuffer == CUTBUFFER_UPDATE_REPLACE)) {
+ /* a cutbuffer exists and should be replaced */
+ entry->kill_ring = g_slist_prepend(entry->kill_ring, (void *)NULL);
+ }
+ }
+
+ if (g_slist_length(entry->kill_ring) > KILL_RING_MAX) {
+ GUI_ENTRY_CUTBUFFER_REC *rec = g_slist_last(entry->kill_ring)->data;
+ entry->kill_ring = g_slist_remove(entry->kill_ring, rec);
+ if (rec != NULL) g_free(rec->cutbuffer);
+ g_free(rec);
+ }
+
+ if (entry->kill_ring->data == NULL) {
+ entry->kill_ring->data = g_new0(GUI_ENTRY_CUTBUFFER_REC, 1);
+ }
+
+ return entry->kill_ring->data;
+}
+
+void gui_entry_erase(GUI_ENTRY_REC *entry, int size, CUTBUFFER_UPDATE_OP update_cutbuffer)
{
size_t w = 0;
@@ -554,17 +638,59 @@ void gui_entry_erase(GUI_ENTRY_REC *entry, int size, int update_cutbuffer)
if (size == 0 || entry->pos < size)
return;
- if (update_cutbuffer) {
- /* put erased text to cutbuffer */
- if (entry->cutbuffer_len < size) {
- g_free(entry->cutbuffer);
- entry->cutbuffer = g_new(unichar, size+1);
+ if (update_cutbuffer != CUTBUFFER_UPDATE_NOOP) {
+ int cutbuffer_new_size;
+ unichar *tmpcutbuffer;
+ GUI_ENTRY_CUTBUFFER_REC *tmp = get_cutbuffer_rec(entry, update_cutbuffer);
+
+ if (tmp->cutbuffer_len == 0) {
+ update_cutbuffer = CUTBUFFER_UPDATE_REPLACE;
}
- entry->cutbuffer_len = size;
- entry->cutbuffer[size] = '\0';
- memcpy(entry->cutbuffer, entry->text + entry->pos - size,
- size * sizeof(unichar));
+ cutbuffer_new_size = tmp->cutbuffer_len + size;
+ tmpcutbuffer = tmp->cutbuffer;
+ entry->append_next_kill = TRUE;
+ switch (update_cutbuffer) {
+ case CUTBUFFER_UPDATE_APPEND:
+ tmp->cutbuffer = g_new(unichar, cutbuffer_new_size+1);
+ memcpy(tmp->cutbuffer, tmpcutbuffer,
+ tmp->cutbuffer_len * sizeof(unichar));
+ memcpy(tmp->cutbuffer + tmp->cutbuffer_len,
+ entry->text + entry->pos - size, size * sizeof(unichar));
+
+ tmp->cutbuffer_len = cutbuffer_new_size;
+ tmp->cutbuffer[cutbuffer_new_size] = '\0';
+ g_free(tmpcutbuffer);
+ break;
+
+ case CUTBUFFER_UPDATE_PREPEND:
+ tmp->cutbuffer = g_new(unichar, cutbuffer_new_size+1);
+ memcpy(tmp->cutbuffer, entry->text + entry->pos - size,
+ size * sizeof(unichar));
+ memcpy(tmp->cutbuffer + size, tmpcutbuffer,
+ tmp->cutbuffer_len * sizeof(unichar));
+
+ tmp->cutbuffer_len = cutbuffer_new_size;
+ tmp->cutbuffer[cutbuffer_new_size] = '\0';
+ g_free(tmpcutbuffer);
+ break;
+
+ case CUTBUFFER_UPDATE_REPLACE:
+ /* put erased text to cutbuffer */
+ if (tmp->cutbuffer_len < size) {
+ g_free(tmp->cutbuffer);
+ tmp->cutbuffer = g_new(unichar, size+1);
+ }
+
+ tmp->cutbuffer_len = size;
+ tmp->cutbuffer[size] = '\0';
+ memcpy(tmp->cutbuffer, entry->text + entry->pos - size, size * sizeof(unichar));
+ break;
+
+ case CUTBUFFER_UPDATE_NOOP:
+ /* cannot happen, handled in "if" */
+ break;
+ }
}
if (entry->utf8)
@@ -601,7 +727,7 @@ void gui_entry_erase_cell(GUI_ENTRY_REC *entry)
gui_entry_draw(entry);
}
-void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space)
+void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space, CUTBUFFER_UPDATE_OP cutbuffer_op)
{
int to;
@@ -624,10 +750,10 @@ void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space)
}
if (to > 0) to++;
- gui_entry_erase(entry, entry->pos-to, TRUE);
+ gui_entry_erase(entry, entry->pos-to, cutbuffer_op);
}
-void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space)
+void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space, CUTBUFFER_UPDATE_OP cutbuffer_op)
{
int to, size;
@@ -650,7 +776,7 @@ void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space)
size = to-entry->pos;
entry->pos = to;
- gui_entry_erase(entry, size, TRUE);
+ gui_entry_erase(entry, size, cutbuffer_op);
}
void gui_entry_transpose_chars(GUI_ENTRY_REC *entry)
@@ -730,7 +856,7 @@ void gui_entry_transpose_words(GUI_ENTRY_REC *entry)
g_free(second);
}
-
+
gui_entry_redraw_from(entry, spos1);
gui_entry_fix_cursor(entry);
gui_entry_draw(entry);
diff --git a/src/fe-text/gui-entry.h b/src/fe-text/gui-entry.h
index 29d8dea2..8777f083 100644
--- a/src/fe-text/gui-entry.h
+++ b/src/fe-text/gui-entry.h
@@ -2,11 +2,15 @@
#define __GUI_ENTRY_H
typedef struct {
+ int cutbuffer_len;
+ unichar *cutbuffer;
+} GUI_ENTRY_CUTBUFFER_REC;
+
+typedef struct {
int text_len, text_alloc; /* as shorts, not chars */
unichar *text;
- int cutbuffer_len;
- unichar *cutbuffer;
+ GSList *kill_ring;
/* all as shorts, not chars */
int xpos, ypos, width; /* entry position in screen */
@@ -18,8 +22,19 @@ typedef struct {
int redraw_needed_from;
unsigned int utf8:1;
+
+ unsigned int previous_append_next_kill:1;
+ unsigned int append_next_kill:1;
+ unsigned int yank_preceded:1;
} GUI_ENTRY_REC;
+typedef enum {
+ CUTBUFFER_UPDATE_NOOP,
+ CUTBUFFER_UPDATE_REPLACE,
+ CUTBUFFER_UPDATE_APPEND,
+ CUTBUFFER_UPDATE_PREPEND
+} CUTBUFFER_UPDATE_OP;
+
extern GUI_ENTRY_REC *active_entry;
GUI_ENTRY_REC *gui_entry_create(int xpos, int ypos, int width, int utf8);
@@ -40,11 +55,12 @@ void gui_entry_insert_text(GUI_ENTRY_REC *entry, const char *str);
void gui_entry_insert_char(GUI_ENTRY_REC *entry, unichar chr);
char *gui_entry_get_cutbuffer(GUI_ENTRY_REC *entry);
-void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, int update_cutbuffer);
-void gui_entry_erase(GUI_ENTRY_REC *entry, int size, int update_cutbuffer);
+char *gui_entry_get_next_cutbuffer(GUI_ENTRY_REC *entry);
+void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, CUTBUFFER_UPDATE_OP update_cutbuffer);
+void gui_entry_erase(GUI_ENTRY_REC *entry, int size, CUTBUFFER_UPDATE_OP update_cutbuffer);
void gui_entry_erase_cell(GUI_ENTRY_REC *entry);
-void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space);
-void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space);
+void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space, CUTBUFFER_UPDATE_OP cutbuffer_op);
+void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space, CUTBUFFER_UPDATE_OP cutbuffer_op);
void gui_entry_transpose_chars(GUI_ENTRY_REC *entry);
void gui_entry_transpose_words(GUI_ENTRY_REC *entry);
@@ -60,4 +76,5 @@ void gui_entry_move_words(GUI_ENTRY_REC *entry, int count, int to_space);
void gui_entry_redraw(GUI_ENTRY_REC *entry);
+
#endif
diff --git a/src/fe-text/gui-printtext.c b/src/fe-text/gui-printtext.c
index 547d39c9..a07451fa 100644
--- a/src/fe-text/gui-printtext.c
+++ b/src/fe-text/gui-printtext.c
@@ -109,6 +109,16 @@ void gui_printtext(int xpos, int ypos, const char *str)
next_xpos = next_ypos = -1;
}
+void gui_printtext_internal(int xpos, int ypos, const char *str)
+{
+ next_xpos = xpos;
+ next_ypos = ypos;
+
+ printtext_gui_internal(str);
+
+ next_xpos = next_ypos = -1;
+}
+
void gui_printtext_after_time(TEXT_DEST_REC *dest, LINE_REC *prev, const char *str, time_t time)
{
GUI_WINDOW_REC *gui;
@@ -159,7 +169,8 @@ static void get_colors(int flags, int *fg, int *bg, int *attr)
if (*bg >= 0) {
*bg = mirc_colors[*bg % 100];
flags &= ~GUI_PRINT_FLAG_COLOR_24_BG;
- if (settings_get_bool("mirc_blink_fix")) {
+ /* ignore mirc color 99 = -1 (reset) */
+ if (*bg != -1 && settings_get_bool("mirc_blink_fix")) {
if (*bg < 16) /* ansi bit flip :-( */
*bg = (*bg&8) | (*bg&4)>>2 | (*bg&2) | (*bg&1)<<2;
*bg = term_color256map[*bg&0xff] & 7;
@@ -220,16 +231,15 @@ static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor,
get_colors(flags, &fg, &bg, &attr);
if (window == NULL) {
- g_return_if_fail(next_xpos != -1);
+ g_return_if_fail(next_xpos != -1);
term_set_color2(root_window, attr, fg, bg);
term_move(root_window, next_xpos, next_ypos);
if (flags & GUI_PRINT_FLAG_CLRTOEOL)
term_clrtoeol(root_window);
- term_addstr(root_window, str);
- next_xpos += strlen(str); /* FIXME utf8 or big5 */
- return;
+ next_xpos += term_addstr(root_window, str);
+ return;
}
lineinfo.level = dest == NULL ? 0 : dest->level;
diff --git a/src/fe-text/gui-printtext.h b/src/fe-text/gui-printtext.h
index 33b7ce6f..d2671497 100644
--- a/src/fe-text/gui-printtext.h
+++ b/src/fe-text/gui-printtext.h
@@ -17,6 +17,7 @@ void gui_set_default_indent(const char *name);
INDENT_FUNC get_default_indent_func(void);
void gui_printtext(int xpos, int ypos, const char *str);
+void gui_printtext_internal(int xpos, int ypos, const char *str);
void gui_printtext_after(TEXT_DEST_REC *dest, LINE_REC *prev, const char *str);
void gui_printtext_after_time(TEXT_DEST_REC *dest, LINE_REC *prev, const char *str, time_t time);
diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c
index 476a798b..2c2eac21 100644
--- a/src/fe-text/gui-readline.c
+++ b/src/fe-text/gui-readline.c
@@ -37,8 +37,12 @@
#include "gui-windows.h"
#include "utf8.h"
+#include <string.h>
#include <signal.h>
+/* After LINE_SPLIT_LIMIT characters, the message will be split into multiple lines */
+#define LINE_SPLIT_LIMIT 400
+
typedef void (*ENTRY_REDIRECT_KEY_FUNC) (int key, void *data, SERVER_REC *server, WI_ITEM_REC *item);
typedef void (*ENTRY_REDIRECT_ENTRY_FUNC) (const char *line, void *data, SERVER_REC *server, WI_ITEM_REC *item);
@@ -60,11 +64,22 @@ static int paste_detect_time, paste_verify_line_count;
static char *paste_entry;
static int paste_entry_pos;
static GArray *paste_buffer;
+static GArray *paste_buffer_rest;
static char *paste_old_prompt;
static int paste_prompt, paste_line_count;
static int paste_join_multiline;
static int paste_timeout_id;
+static int paste_use_bracketed_mode;
+static int paste_bracketed_mode;
+static int paste_was_bracketed_mode;
+static int previous_yank_preceded;
+
+/* Terminal sequences that surround the input when the terminal has the
+ * bracketed paste mode active. Fror more details see
+ * https://cirw.in/blog/bracketed-paste */
+static const unichar bp_start[] = { 0x1b, '[', '2', '0', '0', '~' };
+static const unichar bp_end[] = { 0x1b, '[', '2', '0', '1', '~' };
static void sig_input(void);
@@ -147,7 +162,6 @@ static void window_next_page(void)
static void paste_buffer_join_lines(GArray *buf)
{
-#define IS_WHITE(c) ((c) == ' ' || (c) == '\t')
unsigned int i, count, indent, line_len;
unichar *arr, *dest, *last_lf_pos;
int last_lf;
@@ -177,15 +191,15 @@ static void paste_buffer_join_lines(GArray *buf)
if (buf->len == 0)
return;
- arr = (unichar *) paste_buffer->data;
+ arr = (unichar *)buf->data;
/* first line */
- if (IS_WHITE(arr[0]))
+ if (isblank(arr[0]))
return;
/* find the first beginning of indented line */
for (i = 1; i < buf->len; i++) {
- if (arr[i-1] == '\n' && IS_WHITE(arr[i]))
+ if (arr[i-1] == '\n' && isblank(arr[i]))
break;
}
if (i == buf->len)
@@ -193,7 +207,7 @@ static void paste_buffer_join_lines(GArray *buf)
/* get how much indentation we have.. */
for (indent = 0; i < buf->len; i++, indent++) {
- if (!IS_WHITE(arr[i]))
+ if (!isblank(arr[i]))
break;
}
if (i == buf->len)
@@ -203,7 +217,7 @@ static void paste_buffer_join_lines(GArray *buf)
count = indent; last_lf = TRUE;
for (; i < buf->len; i++) {
if (last_lf) {
- if (IS_WHITE(arr[i]))
+ if (isblank(arr[i]))
count++;
else {
last_lf = FALSE;
@@ -217,14 +231,14 @@ static void paste_buffer_join_lines(GArray *buf)
}
/* all looks fine - now remove the whitespace, but don't let lines
- get longer than 400 chars */
+ get longer than LINE_SPLIT_LIMIT chars */
dest = arr; last_lf = TRUE; last_lf_pos = NULL; line_len = 0;
for (i = 0; i < buf->len; i++) {
- if (last_lf && IS_WHITE(arr[i])) {
+ if (last_lf && isblank(arr[i])) {
/* whitespace, ignore */
} else if (arr[i] == '\n') {
if (!last_lf && i+1 != buf->len &&
- IS_WHITE(arr[i+1])) {
+ isblank(arr[i+1])) {
last_lf_pos = dest;
*dest++ = ' ';
} else {
@@ -235,9 +249,9 @@ static void paste_buffer_join_lines(GArray *buf)
last_lf = TRUE;
} else {
last_lf = FALSE;
- if (++line_len >= 400 && last_lf_pos != NULL) {
+ if (++line_len >= LINE_SPLIT_LIMIT && last_lf_pos != NULL) {
memmove(last_lf_pos+1, last_lf_pos,
- dest - last_lf_pos);
+ (dest - last_lf_pos) * sizeof(unichar));
*last_lf_pos = '\n'; last_lf_pos = NULL;
line_len = 0;
dest++;
@@ -248,9 +262,16 @@ static void paste_buffer_join_lines(GArray *buf)
g_array_set_size(buf, dest - arr);
}
+static void paste_send_line(char *text)
+{
+ /* we need to get the current history every time because it might change between calls */
+ command_history_add(command_history_current(active_win), text);
+
+ signal_emit("send command", 3, text, active_win->active_server, active_win->active);
+}
+
static void paste_send(void)
{
- HISTORY_REC *history;
unichar *arr;
GString *str;
char out[10], *text;
@@ -275,11 +296,7 @@ static void paste_send(void)
}
text = gui_entry_get_text(active_entry);
- history = command_history_current(active_win);
- command_history_add(history, text);
-
- signal_emit("send command", 3, text,
- active_win->active_server, active_win->active);
+ paste_send_line(text);
g_free(text);
}
@@ -287,12 +304,7 @@ static void paste_send(void)
str = g_string_new(NULL);
for (; i < paste_buffer->len; i++) {
if (arr[i] == '\r' || arr[i] == '\n') {
- history = command_history_current(active_win);
- command_history_add(history, str->str);
-
- signal_emit("send command", 3, str->str,
- active_win->active_server,
- active_win->active);
+ paste_send_line(str->str);
g_string_truncate(str, 0);
} else if (active_entry->utf8) {
out[g_unichar_to_utf8(arr[i], out)] = '\0';
@@ -306,7 +318,14 @@ static void paste_send(void)
}
}
- gui_entry_set_text(active_entry, str->str);
+ if (paste_was_bracketed_mode) {
+ /* the text before the bracket end should be sent along with the rest */
+ paste_send_line(str->str);
+ gui_entry_set_text(active_entry, "");
+ } else {
+ gui_entry_set_text(active_entry, str->str);
+ }
+
g_string_free(str, TRUE);
}
@@ -322,6 +341,12 @@ static void paste_flush(int send)
paste_send();
g_array_set_size(paste_buffer, 0);
+ /* re-add anything that may have been after the bracketed paste end */
+ if (paste_buffer_rest->len > 0) {
+ g_array_append_vals(paste_buffer, paste_buffer_rest->data, paste_buffer_rest->len);
+ g_array_set_size(paste_buffer_rest, 0);
+ }
+
gui_entry_set_prompt(active_entry,
paste_old_prompt == NULL ? "" : paste_old_prompt);
g_free(paste_old_prompt); paste_old_prompt = NULL;
@@ -336,11 +361,24 @@ static void insert_paste_prompt(void)
{
char *str;
+ /* The actual number of lines that will show up post-line-split */
+ int actual_line_count = paste_line_count;
+ int split_lines = paste_buffer->len / LINE_SPLIT_LIMIT;
+
+ /* in case this prompt is happening due to line-splitting, calculate the
+ number of lines obtained from this. The number isn't entirely accurate;
+ we just choose the greater of the two since the exact value isn't
+ important */
+ if (split_lines > paste_verify_line_count &&
+ split_lines > paste_line_count) {
+ actual_line_count = split_lines;
+ }
+
paste_prompt = TRUE;
paste_old_prompt = g_strdup(active_entry->prompt);
printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
TXT_PASTE_WARNING,
- paste_line_count,
+ actual_line_count,
active_win->active == NULL ? "window" :
active_win->active->visible_name);
@@ -392,9 +430,9 @@ static void sig_gui_key_pressed(gpointer keyp)
str[g_unichar_to_utf8(key, str)] = '\0';
}
- if (strcmp(str, "^") == 0) {
- /* change it as ^^ */
- str[1] = '^';
+ if (g_strcmp0(str, "^") == 0) {
+ /* change it as ^-, that is an invalid control char */
+ str[1] = '-';
str[2] = '\0';
}
@@ -403,11 +441,20 @@ static void sig_gui_key_pressed(gpointer keyp)
gui_entry_insert_char(active_entry, key);
ret = 1;
} else {
+ previous_yank_preceded = active_entry->yank_preceded;
+ active_entry->yank_preceded = FALSE;
+ active_entry->previous_append_next_kill = active_entry->append_next_kill;
+ active_entry->append_next_kill = FALSE;
ret = key_pressed(keyboard, str);
if (ret < 0) {
/* key wasn't used for anything, print it */
gui_entry_insert_char(active_entry, key);
}
+ if (ret == 0) {
+ /* combo not complete, ignore append_next_kill and yank_preceded */
+ active_entry->append_next_kill = active_entry->previous_append_next_kill;
+ active_entry->yank_preceded = previous_yank_preceded;
+ }
}
/* ret = 0 : some key create multiple characters - we're in the middle
@@ -435,22 +482,21 @@ static void key_send_line(void)
add_history = *str != '\0';
history = command_history_current(active_win);
+ if (redir != NULL && redir->flags & ENTRY_REDIRECT_FLAG_HIDDEN)
+ add_history = 0;
+
+ if (add_history && history != NULL) {
+ command_history_add(history, str);
+ }
+
if (redir == NULL) {
signal_emit("send command", 3, str,
active_win->active_server,
active_win->active);
} else {
- if (redir->flags & ENTRY_REDIRECT_FLAG_HIDDEN)
- add_history = 0;
handle_entry_redirect(str);
}
- if (add_history) {
- history = command_history_find(history);
- if (history != NULL)
- command_history_add(history, str);
- }
-
if (active_entry != NULL)
gui_entry_set_text(active_entry, "");
command_history_clear_pos(active_win);
@@ -527,7 +573,7 @@ static void key_forward_to_space(void)
static void key_erase_line(void)
{
gui_entry_set_pos(active_entry, active_entry->text_len);
- gui_entry_erase(active_entry, active_entry->text_len, TRUE);
+ gui_entry_erase(active_entry, active_entry->text_len, CUTBUFFER_UPDATE_REPLACE);
}
static void key_erase_to_beg_of_line(void)
@@ -535,7 +581,7 @@ static void key_erase_to_beg_of_line(void)
int pos;
pos = gui_entry_get_pos(active_entry);
- gui_entry_erase(active_entry, pos, TRUE);
+ gui_entry_erase(active_entry, pos, CUTBUFFER_UPDATE_PREPEND);
}
static void key_erase_to_end_of_line(void)
@@ -544,7 +590,7 @@ static void key_erase_to_end_of_line(void)
pos = gui_entry_get_pos(active_entry);
gui_entry_set_pos(active_entry, active_entry->text_len);
- gui_entry_erase(active_entry, active_entry->text_len - pos, TRUE);
+ gui_entry_erase(active_entry, active_entry->text_len - pos, CUTBUFFER_UPDATE_APPEND);
}
static void key_yank_from_cutbuffer(void)
@@ -554,7 +600,32 @@ static void key_yank_from_cutbuffer(void)
cutbuffer = gui_entry_get_cutbuffer(active_entry);
if (cutbuffer != NULL) {
gui_entry_insert_text(active_entry, cutbuffer);
- g_free(cutbuffer);
+ active_entry->yank_preceded = TRUE;
+ g_free(cutbuffer);
+ }
+}
+
+static void key_yank_next_cutbuffer(void)
+{
+ GUI_ENTRY_CUTBUFFER_REC *rec;
+ guint length = 0;
+ char *cutbuffer;
+
+ if (!previous_yank_preceded)
+ return;
+
+ if (active_entry->kill_ring == NULL)
+ return;
+
+ rec = active_entry->kill_ring->data;
+ if (rec != NULL) length = rec->cutbuffer_len;
+
+ cutbuffer = gui_entry_get_next_cutbuffer(active_entry);
+ if (cutbuffer != NULL) {
+ gui_entry_erase(active_entry, length, CUTBUFFER_UPDATE_NOOP);
+ gui_entry_insert_text(active_entry, cutbuffer);
+ active_entry->yank_preceded = TRUE;
+ g_free(cutbuffer);
}
}
@@ -591,32 +662,44 @@ static void key_delete_character(void)
static void key_backspace(void)
{
- gui_entry_erase(active_entry, 1, FALSE);
+ gui_entry_erase(active_entry, 1, CUTBUFFER_UPDATE_NOOP);
}
static void key_delete_previous_word(void)
{
- gui_entry_erase_word(active_entry, FALSE);
+ gui_entry_erase_word(active_entry, FALSE, CUTBUFFER_UPDATE_PREPEND);
}
static void key_delete_next_word(void)
{
- gui_entry_erase_next_word(active_entry, FALSE);
+ gui_entry_erase_next_word(active_entry, FALSE, CUTBUFFER_UPDATE_APPEND);
}
static void key_delete_to_previous_space(void)
{
- gui_entry_erase_word(active_entry, TRUE);
+ gui_entry_erase_word(active_entry, TRUE, CUTBUFFER_UPDATE_PREPEND);
}
static void key_delete_to_next_space(void)
{
- gui_entry_erase_next_word(active_entry, TRUE);
+ gui_entry_erase_next_word(active_entry, TRUE, CUTBUFFER_UPDATE_APPEND);
+}
+
+static void key_append_next_kill(void)
+{
+ active_entry->append_next_kill = TRUE;
}
static gboolean paste_timeout(gpointer data)
{
- if (paste_line_count == 0) {
+ int split_lines;
+ paste_was_bracketed_mode = paste_bracketed_mode;
+
+ /* number of lines after splitting extra-long messages */
+ split_lines = paste_buffer->len / LINE_SPLIT_LIMIT;
+
+ /* Take into account the fact that a line may be split every LINE_SPLIT_LIMIT characters */
+ if (paste_line_count == 0 && split_lines <= paste_verify_line_count) {
int i;
for (i = 0; i < paste_buffer->len; i++) {
@@ -625,8 +708,9 @@ static gboolean paste_timeout(gpointer data)
}
g_array_set_size(paste_buffer, 0);
} else if (paste_verify_line_count > 0 &&
- paste_line_count >= paste_verify_line_count &&
- active_win->active != NULL)
+ (paste_line_count >= paste_verify_line_count ||
+ split_lines > paste_verify_line_count) &&
+ active_win->active != NULL)
insert_paste_prompt();
else
paste_flush(TRUE);
@@ -634,6 +718,70 @@ static gboolean paste_timeout(gpointer data)
return FALSE;
}
+static void paste_bracketed_end(int i, gboolean rest)
+{
+ unichar last_char;
+
+ /* if there's stuff after the end bracket, save it for later */
+ if (rest) {
+ unichar *start = ((unichar *) paste_buffer->data) + i + G_N_ELEMENTS(bp_end);
+ int len = paste_buffer->len - i - G_N_ELEMENTS(bp_end);
+
+ g_array_set_size(paste_buffer_rest, 0);
+ g_array_append_vals(paste_buffer_rest, start, len);
+ }
+
+ /* remove the rest, including the trailing sequence chars */
+ g_array_set_size(paste_buffer, i);
+
+ last_char = g_array_index(paste_buffer, unichar, i - 1);
+
+ if (paste_line_count > 0 && last_char != '\n' && last_char != '\r') {
+ /* there are newlines, but there's also stuff after the newline
+ * adjust line count to reflect this */
+ paste_line_count++;
+ }
+
+ /* decide what to do with the buffer */
+ paste_timeout(NULL);
+
+ paste_bracketed_mode = FALSE;
+}
+
+static void paste_bracketed_middle()
+{
+ int i;
+ int marklen = G_N_ELEMENTS(bp_end);
+ int len = paste_buffer->len - marklen;
+ unichar *ptr = (unichar *) paste_buffer->data;
+
+ if (len < 0) {
+ return;
+ }
+
+ for (i = 0; i <= len; i++, ptr++) {
+ if (ptr[0] == bp_end[0] && memcmp(ptr, bp_end, sizeof(bp_end)) == 0) {
+
+ /* if there are at least 6 bytes after the end,
+ * check for another start marker right afterwards */
+ if (i <= (len - marklen) &&
+ memcmp(ptr + marklen, bp_start, sizeof(bp_start)) == 0) {
+
+ /* remove both markers*/
+ g_array_remove_range(paste_buffer, i, marklen * 2);
+ len -= marklen * 2;
+
+ /* go one step back */
+ i--;
+ ptr--;
+ continue;
+ }
+ paste_bracketed_end(i, i != len);
+ break;
+ }
+ }
+}
+
static void sig_input(void)
{
if (!active_entry) {
@@ -647,21 +795,37 @@ static void sig_input(void)
unichar key;
term_gets(buffer, &line_count);
key = g_array_index(buffer, unichar, 0);
+ /* Either Ctrl-k or Ctrl-c is pressed */
if (key == 11 || key == 3)
paste_flush(key == 11);
g_array_free(buffer, TRUE);
} else {
term_gets(paste_buffer, &paste_line_count);
- if (paste_detect_time > 0 && paste_buffer->len >= 3) {
+
+ /* use the bracketed paste mode to detect when the user pastes
+ * some text into the entry */
+ if (paste_bracketed_mode) {
+ paste_bracketed_middle();
+
+ } else if (!paste_use_bracketed_mode && paste_detect_time > 0 && paste_buffer->len >= 3) {
if (paste_timeout_id != -1)
g_source_remove(paste_timeout_id);
paste_timeout_id = g_timeout_add(paste_detect_time, paste_timeout, NULL);
- } else {
+ } else if (!paste_bracketed_mode) {
int i;
for (i = 0; i < paste_buffer->len; i++) {
unichar key = g_array_index(paste_buffer, unichar, i);
signal_emit("gui key pressed", 1, GINT_TO_POINTER(key));
+
+ if (paste_bracketed_mode) {
+ /* just enabled by the signal, remove what was processed so far */
+ g_array_remove_range(paste_buffer, 0, i + 1);
+
+ /* handle single-line / small pastes here */
+ paste_bracketed_middle();
+ return;
+ }
}
g_array_set_size(paste_buffer, 0);
paste_line_count = 0;
@@ -669,6 +833,11 @@ static void sig_input(void)
}
}
+static void key_paste_start(void)
+{
+ paste_bracketed_mode = TRUE;
+}
+
time_t get_idle_time(void)
{
return last_keypress.tv_sec;
@@ -930,6 +1099,11 @@ static void setup_changed(void)
paste_verify_line_count = settings_get_int("paste_verify_line_count");
paste_join_multiline = settings_get_bool("paste_join_multiline");
+ paste_use_bracketed_mode = settings_get_bool("paste_use_bracketed_mode");
+
+ term_set_appkey_mode(settings_get_bool("term_appkey_mode"));
+ /* Enable the bracketed paste mode on demand */
+ term_set_bracketed_paste_mode(paste_use_bracketed_mode);
}
void gui_readline_init(void)
@@ -943,13 +1117,17 @@ void gui_readline_init(void)
paste_entry = NULL;
paste_entry_pos = 0;
paste_buffer = g_array_new(FALSE, FALSE, sizeof(unichar));
+ paste_buffer_rest = g_array_new(FALSE, FALSE, sizeof(unichar));
paste_old_prompt = NULL;
paste_timeout_id = -1;
+ paste_bracketed_mode = FALSE;
g_get_current_time(&last_keypress);
input_listen_init(STDIN_FILENO);
+ settings_add_bool("lookandfeel", "term_appkey_mode", TRUE);
settings_add_str("history", "scroll_page_count", "/2");
settings_add_time("misc", "paste_detect_time", "5msecs");
+ settings_add_bool("misc", "paste_use_bracketed_mode", FALSE);
/* NOTE: function keys can generate at least 5 characters long
keycodes. this must be larger to allow them to work. */
settings_add_int("misc", "paste_verify_line_count", 5);
@@ -1020,6 +1198,10 @@ void gui_readline_init(void)
key_bind("key", NULL, "meta2-5F", "cend", (SIGNAL_FUNC) key_combo);
key_bind("key", NULL, "meta2-1;5F", "cend", (SIGNAL_FUNC) key_combo);
+ key_bind("key", NULL, "meta-O-M", "return", (SIGNAL_FUNC) key_combo);
+
+ key_bind("paste_start", "Bracketed paste start", "meta2-200~", "paste_start", (SIGNAL_FUNC) key_paste_start);
+
/* cursor movement */
key_bind("backward_character", "Move the cursor a character backward", "left", NULL, (SIGNAL_FUNC) key_backward_character);
key_bind("forward_character", "Move the cursor a character forward", "right", NULL, (SIGNAL_FUNC) key_forward_character);
@@ -1050,6 +1232,8 @@ void gui_readline_init(void)
key_bind("erase_to_beg_of_line", "Erase everything before the cursor", NULL, NULL, (SIGNAL_FUNC) key_erase_to_beg_of_line);
key_bind("erase_to_end_of_line", "Erase everything after the cursor", "^K", NULL, (SIGNAL_FUNC) key_erase_to_end_of_line);
key_bind("yank_from_cutbuffer", "\"Undelete\", paste the last deleted text", "^Y", NULL, (SIGNAL_FUNC) key_yank_from_cutbuffer);
+ key_bind("yank_next_cutbuffer", "Revert to the previous last deleted text", NULL, NULL, (SIGNAL_FUNC) key_yank_next_cutbuffer);
+ key_bind("append_next_kill", "Append next deletion", NULL, NULL, (SIGNAL_FUNC) key_append_next_kill);
key_bind("transpose_characters", "Swap current and previous character", "^T", NULL, (SIGNAL_FUNC) key_transpose_characters);
key_bind("transpose_words", "Swap current and previous word", NULL, NULL, (SIGNAL_FUNC) key_transpose_words);
key_bind("capitalize_word", "Capitalize the current word", NULL, NULL, (SIGNAL_FUNC) key_capitalize_word);
@@ -1115,6 +1299,8 @@ void gui_readline_deinit(void)
key_configure_freeze();
+ key_unbind("paste_start", (SIGNAL_FUNC) key_paste_start);
+
key_unbind("backward_character", (SIGNAL_FUNC) key_backward_character);
key_unbind("forward_character", (SIGNAL_FUNC) key_forward_character);
key_unbind("backward_word", (SIGNAL_FUNC) key_backward_word);
@@ -1137,6 +1323,8 @@ void gui_readline_deinit(void)
key_unbind("erase_to_beg_of_line", (SIGNAL_FUNC) key_erase_to_beg_of_line);
key_unbind("erase_to_end_of_line", (SIGNAL_FUNC) key_erase_to_end_of_line);
key_unbind("yank_from_cutbuffer", (SIGNAL_FUNC) key_yank_from_cutbuffer);
+ key_unbind("yank_next_cutbuffer", (SIGNAL_FUNC) key_yank_next_cutbuffer);
+ key_unbind("append_next_kill", (SIGNAL_FUNC) key_append_next_kill);
key_unbind("transpose_characters", (SIGNAL_FUNC) key_transpose_characters);
key_unbind("transpose_words", (SIGNAL_FUNC) key_transpose_words);
@@ -1172,6 +1360,7 @@ void gui_readline_deinit(void)
key_unbind("stop_irc", (SIGNAL_FUNC) key_sig_stop);
keyboard_destroy(keyboard);
g_array_free(paste_buffer, TRUE);
+ g_array_free(paste_buffer_rest, TRUE);
key_configure_thaw();
diff --git a/src/fe-text/gui-windows.c b/src/fe-text/gui-windows.c
index 4213149d..c63c495c 100644
--- a/src/fe-text/gui-windows.c
+++ b/src/fe-text/gui-windows.c
@@ -49,6 +49,7 @@ static GUI_WINDOW_REC *gui_window_init(WINDOW_REC *window,
settings_get_int("indent"),
!settings_get_bool("indent_always"),
get_default_indent_func());
+ textbuffer_view_set_break_wide(gui->view, settings_get_bool("break_wide"));
if (parent->active == window)
textbuffer_view_set_window(gui->view, parent->screen_win);
return gui;
@@ -201,12 +202,14 @@ void gui_windows_reset_settings(void)
for (tmp = windows; tmp != NULL; tmp = tmp->next) {
WINDOW_REC *rec = tmp->data;
- GUI_WINDOW_REC *gui = WINDOW_GUI(rec);
+ GUI_WINDOW_REC *gui = WINDOW_GUI(rec);
- textbuffer_view_set_default_indent(gui->view,
+ textbuffer_view_set_break_wide(gui->view, settings_get_bool("break_wide"));
+
+ textbuffer_view_set_default_indent(gui->view,
settings_get_int("indent"),
!settings_get_bool("indent_always"),
- get_default_indent_func());
+ get_default_indent_func());
textbuffer_view_set_scroll(gui->view,
gui->use_scroll ? gui->scroll :
@@ -281,6 +284,7 @@ void gui_windows_init(void)
settings_add_bool("lookandfeel", "autostick_split_windows", TRUE);
settings_add_int("lookandfeel", "indent", 10);
settings_add_bool("lookandfeel", "indent_always", FALSE);
+ settings_add_bool("lookandfeel", "break_wide", FALSE);
settings_add_bool("lookandfeel", "scroll", TRUE);
window_create_override = -1;
diff --git a/src/fe-text/irssi.c b/src/fe-text/irssi.c
index c0524247..ad79e0c4 100644
--- a/src/fe-text/irssi.c
+++ b/src/fe-text/irssi.c
@@ -74,10 +74,7 @@ void mainwindow_activity_deinit(void);
void mainwindows_layout_init(void);
void mainwindows_layout_deinit(void);
-void term_dummy_init(void);
-void term_dummy_deinit(void);
-
-static int dirty, full_redraw, dummy;
+static int dirty, full_redraw;
static GMainLoop *main_loop;
int quitting;
@@ -122,7 +119,7 @@ void irssi_set_dirty(void)
static void dirty_check(void)
{
- if (!dirty || dummy)
+ if (!dirty)
return;
term_resize_dirty();
@@ -171,27 +168,22 @@ static void textui_finish_init(void)
{
quitting = FALSE;
- if (dummy)
- term_dummy_init();
- else {
- term_refresh_freeze();
- textbuffer_init();
- textbuffer_view_init();
- textbuffer_commands_init();
- gui_expandos_init();
- gui_printtext_init();
- gui_readline_init();
- lastlog_init();
- mainwindows_init();
- mainwindow_activity_init();
- mainwindows_layout_init();
- gui_windows_init();
- statusbar_init();
- term_refresh_thaw();
-
- /* don't check settings with dummy mode */
- settings_check();
- }
+ term_refresh_freeze();
+ textbuffer_init();
+ textbuffer_view_init();
+ textbuffer_commands_init();
+ gui_expandos_init();
+ gui_printtext_init();
+ gui_readline_init();
+ lastlog_init();
+ mainwindows_init();
+ mainwindow_activity_init();
+ mainwindows_layout_init();
+ gui_windows_init();
+ statusbar_init();
+ term_refresh_thaw();
+
+ settings_check();
module_register("core", "fe-text");
@@ -233,25 +225,21 @@ static void textui_deinit(void)
dirty_check(); /* one last time to print any quit messages */
signal_remove("gui exit", (SIGNAL_FUNC) sig_exit);
- if (dummy)
- term_dummy_deinit();
- else {
- lastlog_deinit();
- statusbar_deinit();
- gui_printtext_deinit();
- gui_readline_deinit();
- gui_windows_deinit();
- mainwindows_layout_deinit();
- mainwindow_activity_deinit();
- mainwindows_deinit();
- gui_expandos_deinit();
- textbuffer_commands_deinit();
- textbuffer_view_deinit();
- textbuffer_deinit();
-
- term_refresh_thaw();
- term_deinit();
- }
+ lastlog_deinit();
+ statusbar_deinit();
+ gui_printtext_deinit();
+ gui_readline_deinit();
+ gui_windows_deinit();
+ mainwindows_layout_deinit();
+ mainwindow_activity_deinit();
+ mainwindows_deinit();
+ gui_expandos_deinit();
+ textbuffer_commands_deinit();
+ textbuffer_view_deinit();
+ textbuffer_deinit();
+
+ term_refresh_thaw();
+ term_deinit();
theme_unregister();
@@ -271,49 +259,15 @@ static void check_files(void)
}
}
-#ifdef WIN32
-static void winsock_init(void)
-{
- WORD wVersionRequested;
- WSADATA wsaData;
-
- wVersionRequested = MAKEWORD(2, 2);
-
- if (WSAStartup(wVersionRequested, &wsaData) != 0) {
- printf("Error initializing winsock\n");
- exit(1);
- }
-}
-#endif
-
-#ifdef USE_GC
-#ifdef HAVE_GC_H
-# include <gc.h>
-#else
-# include <gc/gc.h>
-#endif
-
-GMemVTable gc_mem_table = {
- GC_malloc,
- GC_realloc,
- GC_free,
-
- NULL, NULL, NULL
-};
-#endif
int main(int argc, char **argv)
{
static int version = 0;
static GOptionEntry options[] = {
- { "dummy", 'd', 0, G_OPTION_ARG_NONE, &dummy, "Use the dummy terminal mode", NULL },
{ "version", 'v', 0, G_OPTION_ARG_NONE, &version, "Display irssi version", NULL },
{ NULL }
};
-
-#ifdef USE_GC
- g_mem_set_vtable(&gc_mem_table);
-#endif
+ int loglev;
core_register_options();
fe_common_core_register_options();
@@ -328,15 +282,11 @@ int main(int argc, char **argv)
srand(time(NULL));
- dummy = FALSE;
quitting = FALSE;
core_preinit(argv[0]);
check_files();
-#ifdef WIN32
- winsock_init();
-#endif
#ifdef HAVE_SOCKS
SOCKSinit(argv[0]);
#endif
@@ -346,32 +296,30 @@ int main(int argc, char **argv)
before this call.
locales aren't actually used for anything else than autodetection
- of UTF-8 currently..
+ of UTF-8 currently..
- furthermore to get the users's charset with g_get_charset() properly
+ furthermore to get the users's charset with g_get_charset() properly
you have to call setlocale(LC_ALL, "") */
setlocale(LC_ALL, "");
+ loglev = g_log_set_always_fatal(G_LOG_FATAL_MASK | G_LOG_LEVEL_CRITICAL);
textui_init();
- if (!dummy && !term_init()) {
- fprintf(stderr, "Can't initialize screen handling, quitting.\n");
- fprintf(stderr, "You can still use the dummy mode with -d parameter\n");
+ if (!term_init()) {
+ fprintf(stderr, "Can't initialize screen handling.\n");
return 1;
}
+ g_log_set_always_fatal(loglev);
textui_finish_init();
main_loop = g_main_new(TRUE);
/* Does the same as g_main_run(main_loop), except we
can call our dirty-checker after each iteration */
while (!quitting) {
-#ifdef USE_GC
- GC_collect_a_little();
-#endif
- if (!dummy) term_refresh_freeze();
+ term_refresh_freeze();
g_main_iteration(TRUE);
- if (!dummy) term_refresh_thaw();
+ term_refresh_thaw();
if (reload_config) {
/* SIGHUP received, do /RELOAD */
diff --git a/src/fe-text/lastlog.c b/src/fe-text/lastlog.c
index 417eb484..c0b1dde8 100644
--- a/src/fe-text/lastlog.c
+++ b/src/fe-text/lastlog.c
@@ -39,21 +39,10 @@
Returns -1 if unknown option was given. */
int cmd_options_get_level(const char *cmd, GHashTable *optlist)
{
- GSList *list, *tmp, *next;
+ GList *list;
int level, retlevel;
- /* get all the options, then remove the known ones. there should
- be only one left - the server tag. */
- list = hashtable_get_keys(optlist);
- if (cmd != NULL) {
- for (tmp = list; tmp != NULL; tmp = next) {
- char *option = tmp->data;
- next = tmp->next;
-
- if (command_have_option(cmd, option))
- list = g_slist_remove(list, option);
- }
- }
+ list = optlist_remove_known(cmd, optlist);
retlevel = 0;
while (list != NULL) {
@@ -68,12 +57,31 @@ int cmd_options_get_level(const char *cmd, GHashTable *optlist)
}
retlevel |= level;
- list = g_slist_remove(list, list->data);
+ list = g_list_remove(list, list->data);
}
return retlevel;
}
+static void prepend_date(WINDOW_REC *window, LINE_REC *rec, GString *line)
+{
+ THEME_REC *theme = NULL;
+ TEXT_DEST_REC dest = {0};
+ char *format = NULL, datestamp[20] = {0};
+ struct tm *tm = localtime(&rec->info.time);
+ int ret = 0;
+
+ theme = window->theme != NULL ? window->theme : current_theme;
+ format_create_dest(&dest, NULL, NULL, MSGLEVEL_LASTLOG, window);
+ format = format_get_text_theme(theme, MODULE_NAME, &dest, TXT_LASTLOG_DATE);
+
+ ret = strftime(datestamp, sizeof(datestamp), format, tm);
+ g_free(format);
+ if (ret <= 0) return;
+
+ g_string_prepend(line, datestamp);
+}
+
static void show_lastlog(const char *searchtext, GHashTable *optlist,
int start, int count, FILE *fhandle)
{
@@ -82,7 +90,7 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist,
GList *list, *tmp;
GString *line;
char *str;
- int level, before, after, len;
+ int level, before, after, len, date = FALSE;
level = cmd_options_get_level("lastlog", optlist);
if (level == -1) return; /* error in options */
@@ -132,6 +140,9 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist,
atoi(str) : DEFAULT_LASTLOG_AFTER;
}
+ if (g_hash_table_lookup(optlist, "date") != NULL)
+ date = TRUE;
+
list = textbuffer_find_text(WINDOW_GUI(window)->view->buffer, startline,
level, MSGLEVEL_LASTLOG,
searchtext, before, after,
@@ -199,6 +210,9 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist,
g_string_prepend(line, timestamp);
}
+ if (date == TRUE)
+ prepend_date(window, rec, line);
+
/* write to file/window */
if (fhandle != NULL) {
fwrite(line->str, line->len, 1, fhandle);
@@ -223,7 +237,7 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist,
}
/* SYNTAX: LASTLOG [-] [-file <filename>] [-window <ref#|name>] [-new | -away]
- [-<level> -<level...>] [-clear] [-count] [-case]
+ [-<level> -<level...>] [-clear] [-count] [-case] [-date]
[-regexp | -word] [-before [<#>]] [-after [<#>]]
[-<# before+after>] [<pattern>] [<count> [<start>]] */
static void cmd_lastlog(const char *data)
@@ -285,7 +299,7 @@ void lastlog_init(void)
{
command_bind("lastlog", NULL, (SIGNAL_FUNC) cmd_lastlog);
- command_set_options("lastlog", "!- # force clear -file -window new away word regexp case count @a @after @before");
+ command_set_options("lastlog", "!- # force clear -file -window new away word regexp case count date @a @after @before");
}
void lastlog_deinit(void)
diff --git a/src/fe-text/mainwindows-layout.c b/src/fe-text/mainwindows-layout.c
index 28fae924..020969e6 100644
--- a/src/fe-text/mainwindows-layout.c
+++ b/src/fe-text/mainwindows-layout.c
@@ -70,7 +70,7 @@ static void main_window_save(MAIN_WINDOW_REC *window, CONFIG_NODE *node)
char num[MAX_INT_STRLEN];
ltoa(num, window->active->refnum);
- node = config_node_section(node, num, NODE_TYPE_BLOCK);
+ node = iconfig_node_section(node, num, NODE_TYPE_BLOCK);
iconfig_node_set_int(node, "first_line", window->first_line);
iconfig_node_set_int(node, "lines", window->height);
@@ -179,6 +179,7 @@ static void sig_layout_restore(void)
lower_window = NULL; lower_size = 0;
for (i = 0, tmp = sorted_config; i < windows_count; tmp = tmp->next, i++) {
CONFIG_NODE *node = tmp->data;
+ if (node->key == NULL) continue;
/* create a new window + mainwindow */
signal_emit("gui window create override", 1,
diff --git a/src/fe-text/module-formats.c b/src/fe-text/module-formats.c
index 1d905095..899827c2 100644
--- a/src/fe-text/module-formats.c
+++ b/src/fe-text/module-formats.c
@@ -33,6 +33,7 @@ FORMAT_REC gui_text_formats[] =
{ "lastlog_start", "{hilight Lastlog}:", 0 },
{ "lastlog_end", "{hilight End of Lastlog}", 0 },
{ "lastlog_separator", "--", 0 },
+ { "lastlog_date", "%%F ", 0 },
/* ---- */
{ NULL, "Windows", 0 },
diff --git a/src/fe-text/module-formats.h b/src/fe-text/module-formats.h
index 4eebfc3e..3fa8c511 100644
--- a/src/fe-text/module-formats.h
+++ b/src/fe-text/module-formats.h
@@ -10,6 +10,7 @@ enum {
TXT_LASTLOG_START,
TXT_LASTLOG_END,
TXT_LASTLOG_SEPARATOR,
+ TXT_LASTLOG_DATE,
TXT_FILL_2,
diff --git a/src/fe-text/statusbar-config.c b/src/fe-text/statusbar-config.c
index deaa1b5d..48f4aa61 100644
--- a/src/fe-text/statusbar-config.c
+++ b/src/fe-text/statusbar-config.c
@@ -95,7 +95,7 @@ statusbar_config_find(STATUSBAR_GROUP_REC *group, const char *name)
for (tmp = group->config_bars; tmp != NULL; tmp = tmp->next) {
STATUSBAR_CONFIG_REC *config = tmp->data;
- if (strcmp(config->name, name) == 0)
+ if (g_strcmp0(config->name, name) == 0)
return config;
}
@@ -137,7 +137,7 @@ static void statusbar_read_item(STATUSBAR_CONFIG_REC *bar, CONFIG_NODE *node)
int priority, right_alignment;
priority = config_node_get_int(node, "priority", 0);
- right_alignment = strcmp(config_node_get_str(node, "alignment", ""), "right") == 0;
+ right_alignment = g_strcmp0(config_node_get_str(node, "alignment", ""), "right") == 0;
statusbar_item_config_create(bar, node->key,
priority, right_alignment);
}
@@ -177,7 +177,7 @@ static void statusbar_read(STATUSBAR_GROUP_REC *group, CONFIG_NODE *node)
bar->placement = STATUSBAR_TOP;
bar->position = config_node_get_int(node, "position", 0);
- node = config_node_section(node, "items", -1);
+ node = iconfig_node_section(node, "items", -1);
if (node != NULL) {
/* we're overriding the items - destroy the old */
while (bar->items != NULL)
@@ -194,6 +194,8 @@ static void statusbar_read_group(CONFIG_NODE *node)
STATUSBAR_GROUP_REC *group;
GSList *tmp;
+ g_return_if_fail(is_node_list(node));
+
group = statusbar_group_find(node->key);
if (group == NULL) {
group = statusbar_group_create(node->key);
@@ -227,7 +229,7 @@ static void read_statusbar_config_from_node(CONFIG_NODE *node)
CONFIG_NODE *items;
GSList *tmp;
- items = config_node_section(node, "items", -1);
+ items = iconfig_node_section(node, "items", -1);
if (items != NULL)
statusbar_read_items(items);
@@ -369,7 +371,7 @@ static void cmd_statusbar_reset(const char *data, void *server,
CONFIG_NODE *parent;
parent = iconfig_node_traverse("statusbar", TRUE);
- parent = config_node_section(parent, active_statusbar_group->name,
+ parent = iconfig_node_section(parent, active_statusbar_group->name,
NODE_TYPE_BLOCK);
iconfig_node_set_str(parent, node->key, NULL);
@@ -432,7 +434,7 @@ static CONFIG_NODE *statusbar_items_section(CONFIG_NODE *parent)
CONFIG_NODE *node;
GSList *tmp;
- node = config_node_section(parent, "items", -1);
+ node = iconfig_node_section(parent, "items", -1);
if (node != NULL)
return node;
@@ -446,11 +448,11 @@ static CONFIG_NODE *statusbar_items_section(CONFIG_NODE *parent)
/* since items list in config file overrides defaults,
we'll need to copy the whole list. */
- parent = config_node_section(parent, "items", NODE_TYPE_BLOCK);
+ parent = iconfig_node_section(parent, "items", NODE_TYPE_BLOCK);
for (tmp = bar->items; tmp != NULL; tmp = tmp->next) {
SBAR_ITEM_CONFIG_REC *rec = tmp->data;
- node = config_node_section(parent, rec->name,
+ node = iconfig_node_section(parent, rec->name,
NODE_TYPE_BLOCK);
if (rec->priority != 0)
iconfig_node_set_int(node, "priority", rec->priority);
@@ -487,7 +489,7 @@ static void cmd_statusbar_add(const char *data, void *server,
if (value != NULL) index = config_node_index(node, value)+1;
/* create/move item */
- node = config_node_section_index(node, name, index, NODE_TYPE_BLOCK);
+ node = iconfig_node_section_index(node, name, index, NODE_TYPE_BLOCK);
/* set the options */
value = g_hash_table_lookup(optlist, "priority");
@@ -511,7 +513,7 @@ static void cmd_statusbar_remove(const char *data, void *server,
if (node == NULL)
return;
- if (config_node_section(node, data, -1) != NULL)
+ if (iconfig_node_section(node, data, -1) != NULL)
iconfig_node_set_str(node, data, NULL);
else {
printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
@@ -545,9 +547,9 @@ static void cmd_statusbar(const char *data)
/* lookup/create the statusbar node */
node = iconfig_node_traverse("statusbar", TRUE);
- node = config_node_section(node, active_statusbar_group->name,
+ node = iconfig_node_section(node, active_statusbar_group->name,
NODE_TYPE_BLOCK);
- node = config_node_section(node, name, NODE_TYPE_BLOCK);
+ node = iconfig_node_section(node, name, NODE_TYPE_BLOCK);
/* call the subcommand */
signal = g_strconcat("command statusbar ", cmd, NULL);
diff --git a/src/fe-text/statusbar-items.c b/src/fe-text/statusbar-items.c
index 044c2fa0..de4499b4 100644
--- a/src/fe-text/statusbar-items.c
+++ b/src/fe-text/statusbar-items.c
@@ -143,16 +143,34 @@ static char *get_activity_list(MAIN_WINDOW_REC *window, int normal, int hilight)
static void item_act(SBAR_ITEM_REC *item, int get_size_only)
{
char *actlist;
+ int max_size;
+
+ if (get_size_only) {
+ if (activity_list == NULL)
+ item->min_size = item->max_size = 0;
+ /* Skip activity calculation on regular trigger, only
+ set dirty */
+ return;
+ }
actlist = get_activity_list(item->bar->parent_window, TRUE, TRUE);
if (actlist == NULL) {
- if (get_size_only)
- item->min_size = item->max_size = 0;
return;
}
- statusbar_item_default_handler(item, get_size_only,
+ max_size = item->max_size;
+ statusbar_item_default_handler(item, TRUE,
+ NULL, actlist, FALSE);
+ statusbar_item_default_handler(item, FALSE,
NULL, actlist, FALSE);
+ if (max_size != item->max_size) {
+ /* Due to above hack of skipping the calculation, we
+ need to manually trigger the redraw process now or
+ we won't see the item */
+ item->bar->dirty = item->dirty = TRUE;
+ statusbar_redraw(item->bar, TRUE);
+ statusbar_redraw_dirty();
+ }
g_free_not_null(actlist);
}
@@ -289,6 +307,10 @@ static void sig_statusbar_more_updated(void)
{
int visible;
+ /* no active window, for example during /window hide */
+ if (active_win == NULL)
+ return;
+
visible = g_slist_find(more_visible, WINDOW_MAIN(active_win)) != NULL;
if (WINDOW_GUI(active_win)->view->more_text != visible)
statusbar_items_redraw("more");
@@ -347,10 +369,14 @@ static void item_lag(SBAR_ITEM_REC *item, int get_size_only)
last_lag_unknown = lag_unknown;
if (lag_unknown) {
- g_snprintf(str, sizeof(str), "%d (?""?)", lag/100);
+ // "??)" in C becomes ']'
+ // See: https://en.wikipedia.org/wiki/Digraphs_and_trigraphs#C
+ g_snprintf(str, sizeof(str), "%d (?""?)", lag / 100);
} else {
- g_snprintf(str, sizeof(str),
- lag%100 == 0 ? "%d" : "%d.%02d", lag/100, lag%100);
+ if (lag % 100 == 0)
+ g_snprintf(str, sizeof(str), "%d", lag / 100);
+ else
+ g_snprintf(str, sizeof(str), "%d.%02d", lag / 100, lag % 100);
}
statusbar_item_default_handler(item, get_size_only,
diff --git a/src/fe-text/statusbar.c b/src/fe-text/statusbar.c
index ef5abc55..f0dff828 100644
--- a/src/fe-text/statusbar.c
+++ b/src/fe-text/statusbar.c
@@ -129,7 +129,7 @@ STATUSBAR_GROUP_REC *statusbar_group_find(const char *name)
for (tmp = statusbar_groups; tmp != NULL; tmp = tmp->next) {
STATUSBAR_GROUP_REC *rec = tmp->data;
- if (strcmp(rec->name, name) == 0)
+ if (g_strcmp0(rec->name, name) == 0)
return rec;
}
@@ -617,7 +617,7 @@ STATUSBAR_REC *statusbar_find(STATUSBAR_GROUP_REC *group, const char *name,
STATUSBAR_REC *rec = tmp->data;
if (rec->parent_window == window &&
- strcmp(rec->config->name, name) == 0)
+ g_strcmp0(rec->config->name, name) == 0)
return rec;
}
@@ -668,11 +668,11 @@ void statusbar_item_default_handler(SBAR_ITEM_REC *item, int get_size_only,
int escape_vars)
{
SERVER_REC *server;
- WI_ITEM_REC *wiitem;
+ WI_ITEM_REC *wiitem;
char *tmpstr, *tmpstr2;
theme_rm_col reset;
- strcpy(reset.m, "n");
int len;
+ strcpy(reset.m, "n");
if (str == NULL)
str = statusbar_item_get_value(item);
diff --git a/src/fe-text/term-curses.c b/src/fe-text/term-curses.c
deleted file mode 100644
index 752edd7f..00000000
--- a/src/fe-text/term-curses.c
+++ /dev/null
@@ -1,415 +0,0 @@
-/*
- term-curses.c : irssi
-
- Copyright (C) 1999-2001 Timo Sirainen
-
- 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#include "module.h"
-#include "signals.h"
-#include "settings.h"
-
-#include "term.h"
-#include "mainwindows.h"
-
-#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
-# include <ncurses.h>
-#else
-# include <curses.h>
-#endif
-#include <termios.h>
-#include <signal.h>
-
-#ifndef COLOR_PAIRS
-# define COLOR_PAIRS 64
-#endif
-
-#if defined (TIOCGWINSZ) && defined (HAVE_CURSES_RESIZETERM)
-# define USE_RESIZE_TERM
-#endif
-
-#ifndef _POSIX_VDISABLE
-# define _POSIX_VDISABLE 0
-#endif
-
-struct _TERM_WINDOW {
- int x, y;
- int width, height;
- WINDOW *win;
-};
-
-TERM_WINDOW *root_window;
-
-static int curs_x, curs_y;
-static int freeze_refresh;
-static struct termios old_tio;
-
-static int init_curses(void)
-{
- char ansi_tab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
- int num;
- struct termios tio;
-
- if (!initscr())
- return FALSE;
-
- cbreak(); noecho(); idlok(stdscr, 1);
-#ifdef HAVE_CURSES_IDCOK
- /*idcok(stdscr, 1); - disabled currently, causes redrawing problems with NetBSD */
-#endif
- intrflush(stdscr, FALSE); nodelay(stdscr, TRUE);
-
- /* Disable INTR, QUIT, VDSUSP and SUSP keys */
- if (tcgetattr(0, &old_tio) == 0) {
- memcpy(&tio, &old_tio, sizeof(tio));
- tio.c_cc[VINTR] = _POSIX_VDISABLE;
- tio.c_cc[VQUIT] = _POSIX_VDISABLE;
-#ifdef VDSUSP
- tio.c_cc[VDSUSP] = _POSIX_VDISABLE;
-#endif
-#ifdef VSUSP
- tio.c_cc[VSUSP] = _POSIX_VDISABLE;
-#endif
- tcsetattr(0, TCSADRAIN, &tio);
- }
-
- if (has_colors())
- start_color();
- else if (term_use_colors)
- term_use_colors = FALSE;
-
-#ifdef HAVE_NCURSES_USE_DEFAULT_COLORS
- /* this lets us to use the "default" background color for colors <= 7 so
- background pixmaps etc. show up right */
- use_default_colors();
-
- for (num = 1; num < COLOR_PAIRS; num++)
- init_pair(num, ansi_tab[num & 7], num <= 7 ? -1 : ansi_tab[num >> 3]);
-
- init_pair(63, 0, -1); /* hm.. not THAT good idea, but probably more
- people want dark grey than white on white.. */
-#else
- for (num = 1; num < COLOR_PAIRS; num++)
- init_pair(num, ansi_tab[num & 7], ansi_tab[num >> 3]);
- init_pair(63, 0, 0);
-#endif
-
- clear();
- return TRUE;
-}
-
-static int term_init_int(void)
-{
- int ret;
-
- ret = init_curses();
- if (!ret) return 0;
-
- curs_x = curs_y = 0;
- freeze_refresh = 0;
-
- root_window = g_new0(TERM_WINDOW, 1);
- root_window->win = stdscr;
-
- term_width = COLS;
- term_height = LINES;
- return ret;
-}
-
-static void term_deinit_int(void)
-{
- tcsetattr(0, TCSADRAIN, &old_tio);
-
- endwin();
- g_free_and_null(root_window);
-}
-
-int term_init(void)
-{
- if (!term_init_int())
- return FALSE;
-
- settings_add_int("lookandfeel", "default_color", 7);
- term_common_init();
- return TRUE;
-}
-
-void term_deinit(void)
-{
- term_common_deinit();
- term_deinit_int();
-}
-
-/* Resize terminal - if width or height is negative,
- the new size is unknown and should be figured out somehow */
-void term_resize(int width, int height)
-{
-#ifdef HAVE_CURSES_RESIZETERM
- if (width < 0 || height < 0) {
-#endif
- term_deinit_int();
- term_init_int();
-#ifdef HAVE_CURSES_RESIZETERM
- } else if (term_width != width || term_height != height) {
- term_width = width;
- term_height = height;
- resizeterm(term_height, term_width);
- }
-#endif
-}
-
-void term_resize_final(int width, int height)
-{
-#ifdef HAVE_CURSES_RESIZETERM
- if (width < 0 || height < 0)
- mainwindows_recreate();
-#else
- mainwindows_recreate();
-#endif
-}
-
-/* Returns TRUE if terminal has colors */
-int term_has_colors(void)
-{
- return has_colors();
-}
-
-/* Force the colors on any way you can */
-void term_force_colors(int set)
-{
- /* don't do anything with curses */
-}
-
-/* Clear screen */
-void term_clear(void)
-{
- term_set_color(root_window, 0);
- clear();
-}
-
-/* Beep */
-void term_beep(void)
-{
- beep();
-}
-
-/* Create a new window in terminal */
-TERM_WINDOW *term_window_create(int x, int y, int width, int height)
-{
- TERM_WINDOW *window;
-
- window = g_new0(TERM_WINDOW, 1);
- window->x = x; window->y = y;
- window->width = width; window->height = height;
- window->win = newwin(height, width, y, x);
- if (window->win == NULL)
- g_error("newwin() failed: %d,%d %d,%d", x, y, width, height);
- idlok(window->win, 1);
-
- return window;
-}
-
-/* Destroy a terminal window */
-void term_window_destroy(TERM_WINDOW *window)
-{
- delwin(window->win);
- g_free(window);
-}
-
-/* Move/resize a window */
-void term_window_move(TERM_WINDOW *window, int x, int y,
- int width, int height)
-{
- /* some checks to make sure the window is visible in screen,
- otherwise curses could get nasty and not show our window anymore. */
- if (width < 1) width = 1;
- if (height < 1) height = 1;
- if (x+width > term_width) x = term_width-width;
- if (y+height > term_height) y = term_height-height;
-
-#ifdef HAVE_CURSES_WRESIZE
- if (window->width != width || window->height != height)
- wresize(window->win, height, width);
- if (window->x != x || window->y != y)
- mvwin(window->win, y, x);
-#else
- if (window->width != width || window->height != height ||
- window->x != x || window->y != y) {
- delwin(window->win);
- window->win = newwin(height, width, y, x);
- idlok(window->win, 1);
- }
-#endif
- window->x = x; window->y = y;
- window->width = width; window->height = height;
-}
-
-/* Clear window */
-void term_window_clear(TERM_WINDOW *window)
-{
- werase(window->win);
-}
-
-/* Scroll window up/down */
-void term_window_scroll(TERM_WINDOW *window, int count)
-{
- scrollok(window->win, TRUE);
- wscrl(window->win, count);
- scrollok(window->win, FALSE);
-}
-
-static int get_attr(int color)
-{
- int attr;
-
- if ((color & FG_MASK) >> 4)
- color = (color & ~FG_MASK) | term_color256map[color & FG_MASK];
- if ((color & BG_MASK) >> (BG_SHIFT + 4))
- color = (color & ~BG_MASK) | (term_color256map[(color & BG_MASK) >> BG_SHIFT] << BG_SHIFT);
- if (!term_use_colors)
- attr = (color & (0x7 << BG_SHIFT)) ? A_REVERSE : 0;
- else if ((color & ((0xf << BG_SHIFT) | 0xf)) == 8 || (color & (FG_MASK | BG_MASK | ATTR_RESETFG)) == 0)
- attr = COLOR_PAIR(63);
- else if ((color & ((0x7 << BG_SHIFT) | 0x7)) == 0)
- attr = A_NORMAL;
- else {
- if (color & ATTR_RESETFG) {
- color &= ~FG_MASK;
- color |= settings_get_int("default_color");
- }
- attr = COLOR_PAIR((color&0x7) | ((color&(0x7<<BG_SHIFT))>>BG_SHIFT<<3));
- }
-
- if ((color & 0x8) || (color & ATTR_BOLD)) attr |= A_BOLD;
- if (color & ATTR_BLINK) attr |= A_BLINK;
-
- if (color & ATTR_UNDERLINE) attr |= A_UNDERLINE;
- if (color & ATTR_REVERSE) attr |= A_REVERSE;
-#ifdef A_ITALIC
- if (color & ATTR_ITALIC) attr |= A_ITALIC;
-#endif
- return attr;
-}
-
-/* Change active color */
-void term_set_color(TERM_WINDOW *window, int col)
-{
- wattrset(window->win, get_attr(col));
- wbkgdset(window->win, ' ' | get_attr(col));
-}
-
-void term_move(TERM_WINDOW *window, int x, int y)
-{
- wmove(window->win, y, x);
-}
-
-void term_addch(TERM_WINDOW *window, char chr)
-{
- waddch(window->win, chr);
-}
-
-void term_add_unichar(TERM_WINDOW *window, unichar chr)
-{
-#ifdef WIDEC_CURSES
- cchar_t wch;
- wchar_t temp[2];
- temp[0] = chr;
- temp[1] = 0;
- if (setcchar(&wch, temp, A_NORMAL, 0, NULL) == OK)
- wadd_wch(window->win, &wch);
- else
-#endif
- waddch(window->win, chr);
-}
-
-void term_addstr(TERM_WINDOW *window, const char *str)
-{
- waddstr(window->win, (const char *) str);
-}
-
-void term_clrtoeol(TERM_WINDOW *window)
-{
- wclrtoeol(window->win);
-}
-
-void term_move_cursor(int x, int y)
-{
- curs_x = x;
- curs_y = y;
-}
-
-void term_refresh_freeze(void)
-{
- freeze_refresh++;
-}
-
-void term_refresh_thaw(void)
-{
- if (freeze_refresh > 0) {
- freeze_refresh--;
- if (freeze_refresh == 0) term_refresh(NULL);
- }
-}
-
-void term_refresh(TERM_WINDOW *window)
-{
- if (window != NULL)
- wnoutrefresh(window->win);
-
- if (freeze_refresh == 0) {
- move(curs_y, curs_x);
- wnoutrefresh(stdscr);
- doupdate();
- }
-}
-
-void term_stop(void)
-{
- term_deinit_int();
- kill(getpid(), SIGTSTP);
- term_init_int();
- irssi_redraw();
-}
-
-void term_set_input_type(int type)
-{
-}
-
-void term_gets(GArray *buffer, int *line_count)
-{
-#ifdef WIDEC_CURSES
- wint_t key;
-#else
- int key;
-#endif
-
- for (;;) {
-#ifdef WIDEC_CURSES
- if (get_wch(&key) == ERR)
-#else
- if ((key = getch()) == ERR)
-#endif
- break;
-#ifdef KEY_RESIZE
- if (key == KEY_RESIZE)
- continue;
-#endif
-
- g_array_append_val(buffer, key);
- if (key == '\r' || key == '\n')
- (*line_count)++;
- }
-}
diff --git a/src/fe-text/term-dummy.c b/src/fe-text/term-dummy.c
deleted file mode 100644
index 6a56af88..00000000
--- a/src/fe-text/term-dummy.c
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- term-dummy.c : irssi
-
- Copyright (C) 2001 Timo Sirainen
-
- 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#include "module.h"
-#include "signals.h"
-
-#include "fe-windows.h"
-
-static int newline;
-
-static GIOChannel *stdin_channel;
-static int readtag;
-static GString *input;
-
-static void sig_gui_printtext(WINDOW_REC *window, void *fgcolor,
- void *bgcolor, void *pflags,
- char *str, void *level)
-{
- if (newline) {
- newline = FALSE;
- printf("\r");
- }
-
- printf("%s", str);
-}
-
-static void sig_gui_printtext_finished(WINDOW_REC *window)
-{
- printf("\n");
- newline = TRUE;
-}
-
-static void sig_window_created(WINDOW_REC *window)
-{
- window->width = 80;
- window->height = 25;
-}
-
-static void readline(void)
-{
- unsigned char buffer[128];
- char *p;
- int ret, i;
-
- ret = read(0, buffer, sizeof(buffer));
- if (ret == 0 || (ret == -1 && errno != EINTR)) {
- /* lost terminal */
- signal_emit("command quit", 1, "Lost terminal");
- return;
- }
-
- for (i = 0; i < ret; i++)
- g_string_append_c(input, buffer[i]);
-
- p = strchr(input->str, '\n');
- if (p != NULL) {
- *p = '\0';
- signal_emit("send command", 3, input->str,
- active_win->active_server, active_win->active);
- *p = '\n';
- g_string_erase(input, 0, (int) (p-input->str)+1);
- }
-}
-
-void term_dummy_init(void)
-{
- newline = TRUE;
- input = g_string_new(NULL);
-
- signal_add("gui print text", (SIGNAL_FUNC) sig_gui_printtext);
- signal_add("gui print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished);
- signal_add("window created", (SIGNAL_FUNC) sig_window_created);
-
- stdin_channel = g_io_channel_unix_new(0);
- readtag = g_input_add_full(stdin_channel,
- G_PRIORITY_HIGH, G_INPUT_READ,
- (GInputFunction) readline, NULL);
- g_io_channel_unref(stdin_channel);
-}
-
-void term_dummy_deinit(void)
-{
- signal_remove("gui print text", (SIGNAL_FUNC) sig_gui_printtext);
- signal_remove("gui print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished);
- signal_remove("window created", (SIGNAL_FUNC) sig_window_created);
-
- g_source_remove(readtag);
- g_string_free(input, TRUE);
-}
diff --git a/src/fe-text/term-terminfo.c b/src/fe-text/term-terminfo.c
index 29d3f7eb..3098a4e4 100644
--- a/src/fe-text/term-terminfo.c
+++ b/src/fe-text/term-terminfo.c
@@ -390,7 +390,8 @@ void term_set_color(TERM_WINDOW *window, int col)
}
/* set background color */
- if (window && (term_color256map[bg&0xff]&8) == window->term->TI_colors)
+ if (window && window->term->TI_colors &&
+ (term_color256map[bg&0xff]&8) == window->term->TI_colors)
col |= ATTR_BLINK;
if (col & ATTR_BLINK)
current_term->set_blink(current_term);
@@ -413,7 +414,8 @@ void term_set_color(TERM_WINDOW *window, int col)
terminfo_set_reverse();
/* bold */
- if (window && (term_color256map[fg&0xff]&8) == window->term->TI_colors)
+ if (window && window->term->TI_colors &&
+ (term_color256map[fg&0xff]&8) == window->term->TI_colors)
col |= ATTR_BOLD;
if (col & ATTR_BOLD)
terminfo_set_bold();
@@ -520,15 +522,43 @@ void term_add_unichar(TERM_WINDOW *window, unichar chr)
}
}
-void term_addstr(TERM_WINDOW *window, const char *str)
+int term_addstr(TERM_WINDOW *window, const char *str)
{
- int len;
+ int len, raw_len;
+ unichar tmp;
+ const char *ptr;
if (vcmove) term_move_real();
- len = strlen(str); /* FIXME utf8 or big5 */
+
+ len = 0;
+ raw_len = strlen(str);
+
+ /* The string length depends on the terminal encoding */
+
+ ptr = str;
+
+ if (term_type == TERM_TYPE_UTF8) {
+ while (*ptr != '\0') {
+ tmp = g_utf8_get_char_validated(ptr, -1);
+ /* On utf8 error, treat as single byte and try to
+ continue interpretting rest of string as utf8 */
+ if (tmp == (gunichar)-1 || tmp == (gunichar)-2) {
+ len++;
+ ptr++;
+ } else {
+ len += unichar_isprint(tmp) ? mk_wcwidth(tmp) : 1;
+ ptr = g_utf8_next_char(ptr);
+ }
+ }
+ } else
+ len = raw_len;
+
term_printed_text(len);
- fwrite(str, 1, len, window->term->out);
+ /* Use strlen() here since we need the number of raw bytes */
+ fwrite(str, 1, raw_len, window->term->out);
+
+ return len;
}
void term_clrtoeol(TERM_WINDOW *window)
@@ -588,8 +618,6 @@ void term_stop(void)
{
terminfo_stop(current_term);
kill(getpid(), SIGTSTP);
- terminfo_cont(current_term);
- irssi_redraw();
}
static int input_utf8(const unsigned char *buffer, int size, unichar *result)
diff --git a/src/fe-text/term.h b/src/fe-text/term.h
index cdcc787a..0c7847f6 100644
--- a/src/fe-text/term.h
+++ b/src/fe-text/term.h
@@ -27,7 +27,7 @@ typedef struct _TERM_WINDOW TERM_WINDOW;
#define TERM_TYPE_UTF8 1
#define TERM_TYPE_BIG5 2
-typedef guint32 unichar;
+#include "utf8.h"
extern TERM_WINDOW *root_window;
extern int term_width, term_height;
@@ -83,7 +83,7 @@ void term_set_color(TERM_WINDOW *window, int col);
void term_move(TERM_WINDOW *window, int x, int y);
void term_addch(TERM_WINDOW *window, char chr);
void term_add_unichar(TERM_WINDOW *window, unichar chr);
-void term_addstr(TERM_WINDOW *window, const char *str);
+int term_addstr(TERM_WINDOW *window, const char *str);
void term_clrtoeol(TERM_WINDOW *window);
void term_move_cursor(int x, int y);
@@ -94,6 +94,9 @@ void term_refresh(TERM_WINDOW *window);
void term_stop(void);
+void term_set_appkey_mode(int enable);
+void term_set_bracketed_paste_mode(int enable);
+
/* keyboard input handling */
void term_set_input_type(int type);
void term_gets(GArray *buffer, int *line_count);
diff --git a/src/fe-text/terminfo-core.c b/src/fe-text/terminfo-core.c
index d16987fe..9c9179a5 100644
--- a/src/fe-text/terminfo-core.c
+++ b/src/fe-text/terminfo-core.c
@@ -17,7 +17,6 @@ inline static int term_putchar(int c)
char *tparm();
int tputs();
-#ifdef HAVE_TERMINFO
int setupterm();
char *tigetstr();
int tigetnum();
@@ -25,15 +24,6 @@ int tigetflag();
#define term_getstr(x, buffer) tigetstr(x.ti_name)
#define term_getnum(x) tigetnum(x.ti_name);
#define term_getflag(x) tigetflag(x.ti_name);
-#else
-int tgetent();
-char *tgetstr();
-int tgetnum();
-int tgetflag();
-#define term_getstr(x, buffer) tgetstr(x.tc_name, &buffer)
-#define term_getnum(x) tgetnum(x.tc_name)
-#define term_getflag(x) tgetflag(x.tc_name)
-#endif
#define CAP_TYPE_FLAG 0
#define CAP_TYPE_INT 1
@@ -50,62 +40,66 @@ TERM_REC *current_term;
/* Define only what we might need */
static TERMINFO_REC tcaps[] = {
- /* Terminal size */
- { "cols", "co", CAP_TYPE_INT, G_STRUCT_OFFSET(TERM_REC, width) },
- { "lines", "li", CAP_TYPE_INT, G_STRUCT_OFFSET(TERM_REC, height) },
-
- /* Cursor movement */
- { "smcup", "ti", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_smcup) },
- { "rmcup", "te", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rmcup) },
- { "cup", "cm", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cup) },
- { "hpa", "ch", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_hpa) },
- { "vpa", "vh", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_vpa) },
- { "cub1", "le", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cub1) },
- { "cuf1", "nd", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cuf1) },
- { "civis", "vi", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_civis) },
- { "cnorm", "ve", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cnorm) },
+ /* Terminal size */
+ { "cols", "co", CAP_TYPE_INT, G_STRUCT_OFFSET(TERM_REC, width) },
+ { "lines", "li", CAP_TYPE_INT, G_STRUCT_OFFSET(TERM_REC, height) },
- /* Scrolling */
- { "csr", "cs", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_csr) },
- { "wind", "wi", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_wind) },
- { "ri", "sr", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ri) },
- { "rin", "SR", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rin) },
- { "ind", "sf", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ind) },
- { "indn", "SF", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_indn) },
- { "il", "AL", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_il) },
- { "il1", "al", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_il1) },
- { "dl", "DL", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_dl) },
- { "dl1", "dl", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_dl1) },
+ /* Cursor movement */
+ { "smcup", "ti", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_smcup) },
+ { "rmcup", "te", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rmcup) },
+ { "cup", "cm", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cup) },
+ { "hpa", "ch", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_hpa) },
+ { "vpa", "vh", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_vpa) },
+ { "cub1", "le", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cub1) },
+ { "cuf1", "nd", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cuf1) },
+ { "civis", "vi", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_civis) },
+ { "cnorm", "ve", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_cnorm) },
+
+ /* Scrolling */
+ { "csr", "cs", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_csr) },
+ { "wind", "wi", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_wind) },
+ { "ri", "sr", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ri) },
+ { "rin", "SR", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rin) },
+ { "ind", "sf", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ind) },
+ { "indn", "SF", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_indn) },
+ { "il", "AL", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_il) },
+ { "il1", "al", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_il1) },
+ { "dl", "DL", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_dl) },
+ { "dl1", "dl", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_dl1) },
/* Clearing screen */
- { "clear", "cl", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_clear) },
- { "ed", "cd", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ed) },
+ { "clear", "cl", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_clear) },
+ { "ed", "cd", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ed) },
- /* Clearing to end of line */
- { "el", "ce", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_el) },
+ /* Clearing to end of line */
+ { "el", "ce", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_el) },
- /* Repeating character */
- { "rep", "rp", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rep) },
+ /* Repeating character */
+ { "rep", "rp", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rep) },
/* Colors */
- { "colors", "Co", CAP_TYPE_INT, G_STRUCT_OFFSET(TERM_REC, TI_colors) },
- { "sgr0", "me", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_sgr0) },
- { "smul", "us", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_smul) },
- { "rmul", "ue", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rmul) },
- { "smso", "so", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_smso) },
- { "rmso", "se", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rmso) },
- { "sitm", "ZH", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_sitm) },
- { "ritm", "ZR", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ritm) },
- { "bold", "md", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_bold) },
- { "blink", "mb", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_blink) },
- { "rev", "mr", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rev) },
- { "setaf", "AF", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setaf) },
- { "setab", "AB", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setab) },
- { "setf", "Sf", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setf) },
- { "setb", "Sb", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setb) },
-
- /* Beep */
- { "bel", "bl", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_bel) },
+ { "colors", "Co", CAP_TYPE_INT, G_STRUCT_OFFSET(TERM_REC, TI_colors) },
+ { "sgr0", "me", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_sgr0) },
+ { "smul", "us", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_smul) },
+ { "rmul", "ue", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rmul) },
+ { "smso", "so", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_smso) },
+ { "rmso", "se", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rmso) },
+ { "sitm", "ZH", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_sitm) },
+ { "ritm", "ZR", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_ritm) },
+ { "bold", "md", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_bold) },
+ { "blink", "mb", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_blink) },
+ { "rev", "mr", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rev) },
+ { "setaf", "AF", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setaf) },
+ { "setab", "AB", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setab) },
+ { "setf", "Sf", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setf) },
+ { "setb", "Sb", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_setb) },
+
+ /* Beep */
+ { "bel", "bl", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_bel) },
+
+ /* Keyboard-transmit mode */
+ { "smkx", "ks", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_smkx) },
+ { "rmkx", "ke", CAP_TYPE_STR, G_STRUCT_OFFSET(TERM_REC, TI_rmkx) },
};
/* Move cursor (cursor_address / cup) */
@@ -391,15 +385,26 @@ static void _ignore_parm(TERM_REC *term, int param)
{
}
+static void terminfo_set_appkey_mode(TERM_REC *term, int set)
+{
+ if (term->TI_smkx && term->TI_rmkx)
+ tput(tparm(set ? term->TI_smkx : term->TI_rmkx));
+}
+
+static void term_dec_set_bracketed_paste_mode(int enable)
+{
+ if (enable)
+ tputs("\e[?2004h", 0, term_putchar);
+ else
+ tputs("\e[?2004l", 0, term_putchar);
+}
+
static void term_fill_capabilities(TERM_REC *term)
{
int i, ival;
char *sval;
void *ptr;
-#ifndef HAVE_TERMINFO
- char *tptr = term->buffer2;
-#endif
for (i = 0; i < sizeof(tcaps)/sizeof(tcaps[0]); i++) {
ptr = G_STRUCT_MEMBER_P(term, tcaps[i].offset);
@@ -492,12 +497,15 @@ void terminfo_setup_colors(TERM_REC *term, int force)
}
}
-static void terminfo_input_init(TERM_REC *term)
+static void terminfo_input_init0(TERM_REC *term)
{
tcgetattr(fileno(term->in), &term->old_tio);
memcpy(&term->tio, &term->old_tio, sizeof(term->tio));
term->tio.c_lflag &= ~(ICANON | ECHO); /* CBREAK, no ECHO */
+ /* Disable the ICRNL flag to disambiguate ^J and Enter, also disable the
+ * software flow control to leave ^Q and ^S ready to be bound */
+ term->tio.c_iflag &= ~(ICRNL | IXON | IXOFF);
term->tio.c_cc[VMIN] = 1; /* read() is satisfied after 1 char */
term->tio.c_cc[VTIME] = 0; /* No timer */
@@ -511,8 +519,11 @@ static void terminfo_input_init(TERM_REC *term)
term->tio.c_cc[VSUSP] = _POSIX_VDISABLE;
#endif
- tcsetattr(fileno(term->in), TCSADRAIN, &term->tio);
+}
+static void terminfo_input_init(TERM_REC *term)
+{
+ tcsetattr(fileno(term->in), TCSADRAIN, &term->tio);
}
static void terminfo_input_deinit(TERM_REC *term)
@@ -523,7 +534,14 @@ static void terminfo_input_deinit(TERM_REC *term)
void terminfo_cont(TERM_REC *term)
{
if (term->TI_smcup)
- tput(tparm(term->TI_smcup));
+ tput(tparm(term->TI_smcup));
+
+ if (term->appkey_enabled)
+ terminfo_set_appkey_mode(term, TRUE);
+
+ if (term->bracketed_paste_enabled)
+ term_dec_set_bracketed_paste_mode(TRUE);
+
terminfo_input_init(term);
}
@@ -534,10 +552,16 @@ void terminfo_stop(TERM_REC *term)
/* move cursor to bottom of the screen */
terminfo_move(0, term->height-1);
+ if (term->bracketed_paste_enabled)
+ term_dec_set_bracketed_paste_mode(FALSE);
+
/* stop cup-mode */
if (term->TI_rmcup)
tput(tparm(term->TI_rmcup));
+ if (term->appkey_enabled)
+ terminfo_set_appkey_mode(term, FALSE);
+
/* reset input settings */
terminfo_input_deinit(term);
fflush(term->out);
@@ -546,9 +570,7 @@ void terminfo_stop(TERM_REC *term)
static int term_setup(TERM_REC *term)
{
GString *str;
-#ifdef HAVE_TERMINFO
int err;
-#endif
char *term_env;
term_env = getenv("TERM");
@@ -557,18 +579,10 @@ static int term_setup(TERM_REC *term)
return 0;
}
-#ifdef HAVE_TERMINFO
if (setupterm(term_env, 1, &err) != 0) {
fprintf(stderr, "setupterm() failed for TERM=%s: %d\n", term_env, err);
return 0;
}
-#else
- if (tgetent(term->buffer1, term_env) < 1)
- {
- fprintf(stderr, "Termcap not found for TERM=%s\n", term_env);
- return 0;
- }
-#endif
term_fill_capabilities(term);
@@ -646,11 +660,11 @@ static int term_setup(TERM_REC *term)
str = g_string_new(NULL);
if (term->TI_sgr0)
g_string_append(str, term->TI_sgr0);
- if (term->TI_rmul && (term->TI_sgr0 == NULL || strcmp(term->TI_rmul, term->TI_sgr0) != 0))
+ if (term->TI_rmul && (term->TI_sgr0 == NULL || g_strcmp0(term->TI_rmul, term->TI_sgr0) != 0))
g_string_append(str, term->TI_rmul);
- if (term->TI_rmso && (term->TI_sgr0 == NULL || strcmp(term->TI_rmso, term->TI_sgr0) != 0))
+ if (term->TI_rmso && (term->TI_sgr0 == NULL || g_strcmp0(term->TI_rmso, term->TI_sgr0) != 0))
g_string_append(str, term->TI_rmso);
- if (term->TI_ritm && (term->TI_sgr0 == NULL || strcmp(term->TI_ritm, term->TI_sgr0) != 0))
+ if (term->TI_ritm && (term->TI_sgr0 == NULL || g_strcmp0(term->TI_ritm, term->TI_sgr0) != 0))
g_string_append(str, term->TI_ritm);
term->TI_normal = str->str;
g_string_free(str, FALSE);
@@ -659,10 +673,29 @@ static int term_setup(TERM_REC *term)
term->beep = term->TI_bel ? _beep : _ignore;
terminfo_setup_colors(term, FALSE);
+ terminfo_input_init0(term);
terminfo_cont(term);
return 1;
}
+void term_set_appkey_mode(int enable)
+{
+ if (current_term->appkey_enabled == enable)
+ return;
+
+ current_term->appkey_enabled = enable;
+ terminfo_set_appkey_mode(current_term, enable);
+}
+
+void term_set_bracketed_paste_mode(int enable)
+{
+ if (current_term->bracketed_paste_enabled == enable)
+ return;
+
+ current_term->bracketed_paste_enabled = enable;
+ term_dec_set_bracketed_paste_mode(enable);
+}
+
TERM_REC *terminfo_core_init(FILE *in, FILE *out)
{
TERM_REC *old_term, *term;
diff --git a/src/fe-text/terminfo-core.h b/src/fe-text/terminfo-core.h
index 6ab4637d..1253fd9d 100644
--- a/src/fe-text/terminfo-core.h
+++ b/src/fe-text/terminfo-core.h
@@ -88,6 +88,14 @@ struct _TERM_REC {
/* Beep */
char *TI_bel;
+
+ /* Keyboard-transmit mode */
+ const char *TI_smkx;
+ const char *TI_rmkx;
+
+ /* Terminal mode states */
+ int appkey_enabled;
+ int bracketed_paste_enabled;
};
extern TERM_REC *current_term;
diff --git a/src/fe-text/textbuffer-view.c b/src/fe-text/textbuffer-view.c
index ad4c00b8..58bd36fb 100644
--- a/src/fe-text/textbuffer-view.c
+++ b/src/fe-text/textbuffer-view.c
@@ -310,7 +310,7 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
continue;
}
- if (!view->utf8 && char_width > 1) {
+ if (view->break_wide && char_width > 1) {
last_space = xpos;
last_space_ptr = next_ptr;
last_color = color; last_fg24 = fg24; last_bg24 = bg24;
@@ -668,6 +668,16 @@ void textbuffer_view_set_default_indent(TEXT_BUFFER_VIEW_REC *view,
view->default_indent_func = indent_func;
}
+/* Enable breaking of wide chars */
+void textbuffer_view_set_break_wide(TEXT_BUFFER_VIEW_REC *view,
+ gboolean break_wide)
+{
+ if (view->break_wide != break_wide) {
+ view->break_wide = break_wide;
+ view_reset_cache(view);
+ }
+}
+
static void view_unregister_indent_func(TEXT_BUFFER_VIEW_REC *view,
INDENT_FUNC indent_func)
{
@@ -1231,7 +1241,7 @@ void textbuffer_view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
void textbuffer_view_remove_lines_by_level(TEXT_BUFFER_VIEW_REC *view, int level)
{
LINE_REC *line, *next;
-
+
term_refresh_freeze();
line = textbuffer_view_get_lines(view);
diff --git a/src/fe-text/textbuffer-view.h b/src/fe-text/textbuffer-view.h
index ab6786e0..5e7a9d0a 100644
--- a/src/fe-text/textbuffer-view.h
+++ b/src/fe-text/textbuffer-view.h
@@ -59,6 +59,7 @@ struct _TEXT_BUFFER_VIEW_REC {
unsigned int longword_noindent:1;
unsigned int scroll:1; /* scroll down automatically when at bottom */
unsigned int utf8:1; /* use UTF8 in this view */
+ unsigned int break_wide:1; /* Break wide chars in this view */
TEXT_BUFFER_CACHE_REC *cache;
int ypos; /* cursor position - visible area is 0..height-1 */
@@ -72,7 +73,7 @@ struct _TEXT_BUFFER_VIEW_REC {
/* how many empty lines are in screen. a screenful when started
or used /CLEAR */
- int empty_linecount;
+ int empty_linecount;
/* window is at the bottom of the text buffer */
unsigned int bottom:1;
/* if !bottom - new text has been printed since we were at bottom */
@@ -97,6 +98,8 @@ void textbuffer_view_set_default_indent(TEXT_BUFFER_VIEW_REC *view,
int longword_noindent,
INDENT_FUNC indent_func);
void textbuffer_views_unregister_indent_func(INDENT_FUNC indent_func);
+void textbuffer_view_set_break_wide(TEXT_BUFFER_VIEW_REC *view,
+ gboolean break_wide);
void textbuffer_view_set_scroll(TEXT_BUFFER_VIEW_REC *view, int scroll);
void textbuffer_view_set_utf8(TEXT_BUFFER_VIEW_REC *view, int utf8);
diff --git a/src/fe-text/textbuffer.c b/src/fe-text/textbuffer.c
index 561fdabd..3668f4c7 100644
--- a/src/fe-text/textbuffer.c
+++ b/src/fe-text/textbuffer.c
@@ -27,7 +27,7 @@
#include "textbuffer.h"
-#ifdef HAVE_REGEX_H
+#ifndef USE_GREGEX
# include <regex.h>
#endif
@@ -545,7 +545,9 @@ GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
int before, int after,
int regexp, int fullword, int case_sensitive)
{
-#ifdef HAVE_REGEX_H
+#ifdef USE_GREGEX
+ GRegex *preg;
+#else
regex_t preg;
#endif
LINE_REC *line, *pre_line;
@@ -557,16 +559,23 @@ GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
g_return_val_if_fail(buffer != NULL, NULL);
g_return_val_if_fail(text != NULL, NULL);
+#ifdef USE_GREGEX
+ preg = NULL;
+
+ if (regexp) {
+ preg = g_regex_new(text, G_REGEX_RAW | (case_sensitive ? 0 : G_REGEX_CASELESS), 0, NULL);
+
+ if (preg == NULL)
+ return NULL;
+ }
+#else
if (regexp) {
-#ifdef HAVE_REGEX_H
int flags = REG_EXTENDED | REG_NOSUB |
(case_sensitive ? 0 : REG_ICASE);
if (regcomp(&preg, text, flags) != 0)
return NULL;
-#else
- return NULL;
-#endif
}
+#endif
matches = NULL; match_after = 0;
str = g_string_new(NULL);
@@ -585,12 +594,15 @@ GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
if (*text != '\0') {
textbuffer_line2text(line, FALSE, str);
- if (line_matched)
- line_matched =
-#ifdef HAVE_REGEX_H
- regexp ? regexec(&preg, str->str, 0, NULL, 0) == 0 :
+ if (line_matched) {
+ line_matched = regexp ?
+#ifdef USE_GREGEX
+ g_regex_match(preg, str->str, 0, NULL)
+#else
+ regexec(&preg, str->str, 0, NULL, 0) == 0
#endif
- match_func(str->str, text) != NULL;
+ : match_func(str->str, text) != NULL;
+ }
}
if (line_matched) {
@@ -618,7 +630,11 @@ GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
matches = g_list_append(matches, NULL);
}
}
-#ifdef HAVE_REGEX_H
+
+#ifdef USE_GREGEX
+ if (preg != NULL)
+ g_regex_unref(preg);
+#else
if (regexp) regfree(&preg);
#endif
g_string_free(str, TRUE);
diff --git a/src/fe-text/tparm.c b/src/fe-text/tparm.c
deleted file mode 100644
index 3f58e6f3..00000000
--- a/src/fe-text/tparm.c
+++ /dev/null
@@ -1,740 +0,0 @@
-/*
- * tparm.c
- *
- * By Ross Ridge
- * Public Domain
- * 92/02/01 07:30:36
- *
- */
-#include <ctype.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-
-#ifndef MAX_PUSHED
-#define MAX_PUSHED 32
-#endif
-
-#define ARG 1
-#define NUM 2
-
-#define INTEGER 1
-#define STRING 2
-
-#define MAX_LINE 640
-
-typedef void* anyptr;
-
-typedef struct stack_str {
- int type;
- int argnum;
- int value;
-} stack;
-
-static stack S[MAX_PUSHED];
-static stack vars['z'-'a'+1];
-static int pos = 0;
-
-static struct arg_str {
- int type;
- int integer;
- char *string;
-} arg_list[10];
-
-static int argcnt;
-
-static va_list tparm_args;
-
-static int pusharg(int arg)
-{
- if (pos == MAX_PUSHED)
- return 1;
- S[pos].type = ARG;
- S[pos++].argnum = arg;
- return 0;
-}
-
-static int pushnum(int num)
-{
- if (pos == MAX_PUSHED)
- return 1;
- S[pos].type = NUM;
- S[pos++].value = num;
- return 0;
-}
-
-/* VARARGS2 */
-static int getarg(int argnum, int type, anyptr p)
-{
- while (argcnt < argnum) {
- arg_list[argcnt].type = INTEGER;
- arg_list[argcnt++].integer = (int) va_arg(tparm_args, int);
- }
- if (argcnt > argnum) {
- if (arg_list[argnum].type != type)
- return 1;
- else if (type == STRING)
- *(char **)p = arg_list[argnum].string;
- else
- *(int *)p = arg_list[argnum].integer;
- } else {
- arg_list[argcnt].type = type;
- if (type == STRING)
- *(char **)p = arg_list[argcnt++].string
- = (char *) va_arg(tparm_args, char *);
- else
- *(int *)p = arg_list[argcnt++].integer = (int) va_arg(tparm_args, int);
- }
- return 0;
-}
-
-
-static int popstring(char **str)
-{
- if (pos-- == 0)
- return 1;
- if (S[pos].type != ARG)
- return 1;
- return(getarg(S[pos].argnum, STRING, (anyptr) str));
-}
-
-static int popnum(int *num)
-{
- if (pos-- == 0)
- return 1;
- switch (S[pos].type) {
- case ARG:
- return (getarg(S[pos].argnum, INTEGER, (anyptr) num));
- case NUM:
- *num = S[pos].value;
- return 0;
- }
- return 1;
-}
-
-static int cvtchar(const char *sp, char *c)
-{
- switch(*sp) {
- case '\\':
- switch(*++sp) {
- case '\'':
- case '$':
- case '\\':
- case '%':
- *c = *sp;
- return 2;
- case '\0':
- *c = '\\';
- return 1;
- case '0':
- if (sp[1] == '0' && sp[2] == '0') {
- *c = '\0';
- return 4;
- }
- *c = '\200'; /* '\0' ???? */
- return 2;
- default:
- *c = *sp;
- return 2;
- }
- default:
- *c = *sp;
- return 1;
- }
-}
-
-static int termcap;
-
-/* sigh... this has got to be the ugliest code I've ever written.
- Trying to handle everything has its cost, I guess.
-
- It actually isn't to hard to figure out if a given % code is supposed
- to be interpeted with its termcap or terminfo meaning since almost
- all terminfo codes are invalid unless something has been pushed on
- the stack and termcap strings will never push things on the stack
- (%p isn't used by termcap). So where we have a choice we make the
- decision by wether or not somthing has been pushed on the stack.
- The static variable termcap keeps track of this; it starts out set
- to 1 and is incremented as each argument processed by a termcap % code,
- however if something is pushed on the stack it's set to 0 and the
- rest of the % codes are interpeted as terminfo % codes. Another way
- of putting it is that if termcap equals one we haven't decided either
- way yet, if it equals zero we're looking for terminfo codes, and if
- its greater than 1 we're looking for termcap codes.
-
- Terminfo % codes:
-
- %% output a '%'
- %[[:][-+# ][width][.precision]][doxXs]
- output pop according to the printf format
- %c output pop as a char
- %'c' push character constant c.
- %{n} push decimal constant n.
- %p[1-9] push paramter [1-9]
- %g[a-z] push variable [a-z]
- %P[a-z] put pop in variable [a-z]
- %l push the length of pop (a string)
- %+ add pop to pop and push the result
- %- subtract pop from pop and push the result
- %* multiply pop and pop and push the result
- %& bitwise and pop and pop and push the result
- %| bitwise or pop and pop and push the result
- %^ bitwise xor pop and pop and push the result
- %~ push the bitwise not of pop
- %= compare if pop and pop are equal and push the result
- %> compare if pop is less than pop and push the result
- %< compare if pop is greater than pop and push the result
- %A logical and pop and pop and push the result
- %O logical or pop and pop and push the result
- %! push the logical not of pop
- %? condition %t if_true [%e if_false] %;
- if condtion evaulates as true then evaluate if_true,
- else evaluate if_false. elseif's can be done:
-%? cond %t true [%e cond2 %t true2] ... [%e condN %t trueN] [%e false] %;
- %i add one to parameters 1 and 2. (ANSI)
-
- Termcap Codes:
-
- %% output a %
- %. output parameter as a character
- %d output parameter as a decimal number
- %2 output parameter in printf format %02d
- %3 output parameter in printf format %03d
- %+x add the character x to parameter and output it as a character
-(UW) %-x subtract parameter FROM the character x and output it as a char
-(UW) %ax add the character x to parameter
-(GNU) %a[+*-/=][cp]x
- GNU arithmetic.
-(UW) %sx subtract parameter FROM the character x
- %>xy if parameter > character x then add character y to parameter
- %B convert to BCD (parameter = (parameter/10)*16 + parameter%16)
- %D Delta Data encode (parameter = parameter - 2*(paramter%16))
- %i increment the first two parameters by one
- %n xor the first two parameters by 0140
-(GNU) %m xor the first two parameters by 0177
- %r swap the first two parameters
-(GNU) %b backup to previous parameter
-(GNU) %f skip this parameter
-
- Note the two definitions of %a, the GNU defintion is used if the characters
- after the 'a' are valid, otherwise the UW definition is used.
-
- (GNU) used by GNU Emacs termcap libraries
- (UW) used by the University of Waterloo (MFCF) termcap libraries
-
-*/
-
-char *tparm(const char *str, ...) {
- static char OOPS[] = "OOPS";
- static char buf[MAX_LINE];
- register const char *sp;
- register char *dp;
- register char *fmt;
- char conv_char;
- char scan_for;
- int scan_depth = 0, if_depth;
- static int i, j;
- static char *s, c;
- char fmt_buf[MAX_LINE];
- char sbuf[MAX_LINE];
-
- va_start(tparm_args, str);
-
- sp = str;
- dp = buf;
- scan_for = 0;
- if_depth = 0;
- argcnt = 0;
- pos = 0;
- termcap = 1;
- while (*sp != '\0') {
- switch(*sp) {
- case '\\':
- if (scan_for) {
- if (*++sp != '\0')
- sp++;
- break;
- }
- *dp++ = *sp++;
- if (*sp != '\0')
- *dp++ = *sp++;
- break;
- case '%':
- sp++;
- if (scan_for) {
- if (*sp == scan_for && if_depth == scan_depth) {
- if (scan_for == ';')
- if_depth--;
- scan_for = 0;
- } else if (*sp == '?')
- if_depth++;
- else if (*sp == ';') {
- if (if_depth == 0)
- return OOPS;
- else
- if_depth--;
- }
- sp++;
- break;
- }
- fmt = NULL;
- switch(*sp) {
- case '%':
- *dp++ = *sp++;
- break;
- case '+':
- if (!termcap) {
- if (popnum(&j) || popnum(&i))
- return OOPS;
- i += j;
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- }
- ;/* FALLTHROUGH */
- case 'C':
- if (*sp == 'C') {
- if (getarg(termcap - 1, INTEGER, &i))
- return OOPS;
- if (i >= 96) {
- i /= 96;
- if (i == '$')
- *dp++ = '\\';
- *dp++ = i;
- }
- }
- fmt = "%c";
- /* FALLTHROUGH */
- case 'a':
- if (!termcap)
- return OOPS;
- if (getarg(termcap - 1, INTEGER, (anyptr) &i))
- return OOPS;
- if (*++sp == '\0')
- return OOPS;
- if ((sp[1] == 'p' || sp[1] == 'c')
- && sp[2] != '\0' && fmt == NULL) {
- /* GNU aritmitic parameter, what they
- realy need is terminfo. */
- int val, lc;
- if (sp[1] == 'p'
- && getarg(termcap - 1 + sp[2] - '@',
- INTEGER, (anyptr) &val))
- return OOPS;
- if (sp[1] == 'c') {
- lc = cvtchar(sp + 2, &c) + 2;
- /* Mask out 8th bit so \200 can be
- used for \0 as per GNU doc's */
- val = c & 0177;
- } else
- lc = 2;
- switch(sp[0]) {
- case '=':
- break;
- case '+':
- val = i + val;
- break;
- case '-':
- val = i - val;
- break;
- case '*':
- val = i * val;
- break;
- case '/':
- val = i / val;
- break;
- default:
- /* Not really GNU's %a after all... */
- lc = cvtchar(sp, &c);
- val = c + i;
- break;
- }
- arg_list[termcap - 1].integer = val;
- sp += lc;
- break;
- }
- sp += cvtchar(sp, &c);
- arg_list[termcap - 1].integer = c + i;
- if (fmt == NULL)
- break;
- sp--;
- /* FALLTHROUGH */
- case '-':
- if (!termcap) {
- if (popnum(&j) || popnum(&i))
- return OOPS;
- i -= j;
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- }
- fmt = "%c";
- /* FALLTHROUGH */
- case 's':
- if (termcap && (fmt == NULL || *sp == '-')) {
- if (getarg(termcap - 1, INTEGER, &i))
- return OOPS;
- if (*++sp == '\0')
- return OOPS;
- sp += cvtchar(sp, &c);
- arg_list[termcap - 1].integer = c - i;
- if (fmt == NULL)
- break;
- sp--;
- }
- if (!termcap)
- return OOPS;
- ;/* FALLTHROUGH */
- case '.':
- if (termcap && fmt == NULL)
- fmt = "%c";
- ;/* FALLTHROUGH */
- case 'd':
- if (termcap && fmt == NULL)
- fmt = "%d";
- ;/* FALLTHROUGH */
- case '2':
- if (termcap && fmt == NULL)
- fmt = "%02d";
- ;/* FALLTHROUGH */
- case '3':
- if (termcap && fmt == NULL)
- fmt = "%03d";
- ;/* FALLTHROUGH */
- case ':': case ' ': case '#': case 'u':
- case 'x': case 'X': case 'o': case 'c':
- case '0': case '1': case '4': case '5':
- case '6': case '7': case '8': case '9':
- if (fmt == NULL) {
- if (termcap)
- return OOPS;
- if (*sp == ':')
- sp++;
- fmt = fmt_buf;
- *fmt++ = '%';
- while(*sp != 's' && *sp != 'x' && *sp != 'X' && *sp != 'd' && *sp != 'o' && *sp != 'c' && *sp != 'u') {
- if (*sp == '\0')
- return OOPS;
- *fmt++ = *sp++;
- }
- *fmt++ = *sp;
- *fmt = '\0';
- fmt = fmt_buf;
- }
- conv_char = fmt[strlen(fmt) - 1];
- if (conv_char == 's') {
- if (popstring(&s))
- return OOPS;
- sprintf(sbuf, fmt, s);
- } else {
- if (termcap) {
- if (getarg(termcap++ - 1,
- INTEGER, &i))
- return OOPS;
- } else
- if (popnum(&i))
- return OOPS;
- if (i == 0 && conv_char == 'c')
- *sbuf = 0;
- else
- sprintf(sbuf, fmt, i);
- }
- sp++;
- fmt = sbuf;
- while(*fmt != '\0') {
- if (*fmt == '$')
- *dp++ = '\\';
- *dp++ = *fmt++;
- }
- break;
- case 'r':
- if (!termcap || getarg(1, INTEGER, &i))
- return OOPS;
- arg_list[1].integer = arg_list[0].integer;
- arg_list[0].integer = i;
- sp++;
- break;
- case 'i':
- if (getarg(1, INTEGER, &i)
- || arg_list[0].type != INTEGER)
- return OOPS;
- arg_list[1].integer++;
- arg_list[0].integer++;
- sp++;
- break;
- case 'n':
- if (!termcap || getarg(1, INTEGER, &i))
- return OOPS;
- arg_list[0].integer ^= 0140;
- arg_list[1].integer ^= 0140;
- sp++;
- break;
- case '>':
- if (!termcap) {
- if (popnum(&j) || popnum(&i))
- return OOPS;
- i = (i > j);
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- }
- if (getarg(termcap-1, INTEGER, &i))
- return OOPS;
- sp += cvtchar(sp, &c);
- if (i > c) {
- sp += cvtchar(sp, &c);
- arg_list[termcap-1].integer += c;
- } else
- sp += cvtchar(sp, &c);
- sp++;
- break;
- case 'B':
- if (!termcap || getarg(termcap-1, INTEGER, &i))
- return OOPS;
- arg_list[termcap-1].integer = 16*(i/10)+i%10;
- sp++;
- break;
- case 'D':
- if (!termcap || getarg(termcap-1, INTEGER, &i))
- return OOPS;
- arg_list[termcap-1].integer = i - 2 * (i % 16);
- sp++;
- break;
- case 'p':
- if (termcap > 1)
- return OOPS;
- if (*++sp == '\0')
- return OOPS;
- if (*sp == '0')
- i = 9;
- else
- i = *sp - '1';
- if (i < 0 || i > 9)
- return OOPS;
- if (pusharg(i))
- return OOPS;
- termcap = 0;
- sp++;
- break;
- case 'P':
- if (termcap || *++sp == '\0')
- return OOPS;
- i = *sp++ - 'a';
- if (i < 0 || i > 25)
- return OOPS;
- if (pos-- == 0)
- return OOPS;
- switch(vars[i].type = S[pos].type) {
- case ARG:
- vars[i].argnum = S[pos].argnum;
- break;
- case NUM:
- vars[i].value = S[pos].value;
- break;
- }
- break;
- case 'g':
- if (termcap || *++sp == '\0')
- return OOPS;
- i = *sp++ - 'a';
- if (i < 0 || i > 25)
- return OOPS;
- switch(vars[i].type) {
- case ARG:
- if (pusharg(vars[i].argnum))
- return OOPS;
- break;
- case NUM:
- if (pushnum(vars[i].value))
- return OOPS;
- break;
- }
- break;
- case '\'':
- if (termcap > 1)
- return OOPS;
- if (*++sp == '\0')
- return OOPS;
- sp += cvtchar(sp, &c);
- if (pushnum(c) || *sp++ != '\'')
- return OOPS;
- termcap = 0;
- break;
- case '{':
- if (termcap > 1)
- return OOPS;
- i = 0;
- sp++;
- while(isdigit((int) (unsigned char) *sp))
- i = 10 * i + *sp++ - '0';
- if (*sp++ != '}' || pushnum(i))
- return OOPS;
- termcap = 0;
- break;
- case 'l':
- if (termcap || popstring(&s))
- return OOPS;
- i = strlen(s);
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case '*':
- if (termcap || popnum(&j) || popnum(&i))
- return OOPS;
- i *= j;
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case '/':
- if (termcap || popnum(&j) || popnum(&i))
- return OOPS;
- i /= j;
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case 'm':
- if (termcap) {
- if (getarg(1, INTEGER, &i))
- return OOPS;
- arg_list[0].integer ^= 0177;
- arg_list[1].integer ^= 0177;
- sp++;
- break;
- }
- if (popnum(&j) || popnum(&i))
- return OOPS;
- i %= j;
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case '&':
- if (popnum(&j) || popnum(&i))
- return OOPS;
- i &= j;
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case '|':
- if (popnum(&j) || popnum(&i))
- return OOPS;
- i |= j;
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case '^':
- if (popnum(&j) || popnum(&i))
- return OOPS;
- i ^= j;
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case '=':
- if (popnum(&j) || popnum(&i))
- return OOPS;
- i = (i == j);
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case '<':
- if (popnum(&j) || popnum(&i))
- return OOPS;
- i = (i < j);
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case 'A':
- if (popnum(&j) || popnum(&i))
- return OOPS;
- i = (i && j);
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case 'O':
- if (popnum(&j) || popnum(&i))
- return OOPS;
- i = (i || j);
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case '!':
- if (popnum(&i))
- return OOPS;
- i = !i;
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case '~':
- if (popnum(&i))
- return OOPS;
- i = ~i;
- if (pushnum(i))
- return OOPS;
- sp++;
- break;
- case '?':
- if (termcap > 1)
- return OOPS;
- termcap = 0;
- if_depth++;
- sp++;
- break;
- case 't':
- if (popnum(&i) || if_depth == 0)
- return OOPS;
- if (!i) {
- scan_for = 'e';
- scan_depth = if_depth;
- }
- sp++;
- break;
- case 'e':
- if (if_depth == 0)
- return OOPS;
- scan_for = ';';
- scan_depth = if_depth;
- sp++;
- break;
- case ';':
- if (if_depth-- == 0)
- return OOPS;
- sp++;
- break;
- case 'b':
- if (--termcap < 1)
- return OOPS;
- sp++;
- break;
- case 'f':
- if (!termcap++)
- return OOPS;
- sp++;
- break;
- }
- break;
- default:
- if (scan_for)
- sp++;
- else
- *dp++ = *sp++;
- break;
- }
- }
- va_end(tparm_args);
- *dp = '\0';
- return buf;
-}
diff --git a/src/irc/core/Makefile.am b/src/irc/core/Makefile.am
index 3db5cf0e..20caaeb1 100644
--- a/src/irc/core/Makefile.am
+++ b/src/irc/core/Makefile.am
@@ -26,6 +26,8 @@ libirc_core_a_SOURCES = \
irc-servers-reconnect.c \
irc-servers-setup.c \
irc-session.c \
+ irc-cap.c \
+ sasl.c \
lag.c \
massjoin.c \
modes.c \
@@ -48,6 +50,8 @@ pkginc_irc_core_HEADERS = \
irc-queries.h \
irc-servers.h \
irc-servers-setup.h \
+ irc-cap.h \
+ sasl.h \
modes.h \
mode-lists.h \
module.h \
diff --git a/src/irc/core/bans.c b/src/irc/core/bans.c
index de799812..198fdc4a 100644
--- a/src/irc/core/bans.c
+++ b/src/irc/core/bans.c
@@ -88,7 +88,7 @@ char *ban_get_masks(IRC_CHANNEL_REC *channel, const char *nicks, int ban_type)
str = g_string_new(NULL);
banlist = g_strsplit(nicks, " ", -1);
for (ban = banlist; *ban != NULL; ban++) {
- if (strchr(*ban, '!') != NULL) {
+ if (**ban == '$' || strchr(*ban, '!') != NULL) {
/* explicit ban */
g_string_append_printf(str, "%s ", *ban);
continue;
@@ -184,11 +184,11 @@ static void command_set_ban(const char *data, IRC_SERVER_REC *server,
if (server == NULL || !server->connected || !IS_IRC_SERVER(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
- if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTCHAN | PARAM_FLAG_GETREST,
- item, &channel, &nicks)) return;
- if (!ischannel(*channel)) cmd_param_error(CMDERR_NOT_JOINED);
+ if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTCHAN | PARAM_FLAG_GETREST |
+ PARAM_FLAG_STRIP_TRAILING_WS, item, &channel, &nicks)) return;
+ if (!server_ischannel(SERVER(server), channel)) cmd_param_error(CMDERR_NOT_JOINED);
if (*nicks == '\0') {
- if (strcmp(data, "*") != 0)
+ if (g_strcmp0(data, "*") != 0)
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
/* /BAN * or /UNBAN * - ban/unban everyone */
nicks = (char *) data;
@@ -262,8 +262,8 @@ static void cmd_ban(const char *data, IRC_SERVER_REC *server, void *item)
CMD_IRC_SERVER(server);
- if (!cmd_get_params(data, &free_arg, 1 |
- PARAM_FLAG_OPTIONS | PARAM_FLAG_GETREST,
+ if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
+ PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
"ban", &optlist, &ban))
return;
@@ -297,8 +297,8 @@ static void cmd_unban(const char *data, IRC_SERVER_REC *server, void *item)
CMD_IRC_SERVER(server);
- if (!cmd_get_params(data, &free_arg, 1 |
- PARAM_FLAG_OPTIONS | PARAM_FLAG_GETREST,
+ if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
+ PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
"unban", &optlist, &ban))
return;
@@ -309,7 +309,7 @@ static void cmd_unban(const char *data, IRC_SERVER_REC *server, void *item)
ban = g_strdup(BAN_LAST);
command_set_ban(ban ? ban : data, server, item, FALSE, 0);
-
+
g_free(ban);
cmd_params_free(free_arg);
@@ -318,7 +318,7 @@ static void cmd_unban(const char *data, IRC_SERVER_REC *server, void *item)
static void read_settings(void)
{
if (default_ban_type_str != NULL &&
- strcmp(default_ban_type_str, settings_get_str("ban_type")) == 0)
+ g_strcmp0(default_ban_type_str, settings_get_str("ban_type")) == 0)
return;
g_free_not_null(default_ban_type_str);
diff --git a/src/irc/core/channel-events.c b/src/irc/core/channel-events.c
index 9198ed82..6cb9b088 100644
--- a/src/irc/core/channel-events.c
+++ b/src/irc/core/channel-events.c
@@ -123,10 +123,10 @@ static void channel_change_topic(IRC_SERVER_REC *server, const char *channel,
{
CHANNEL_REC *chanrec;
char *recoded = NULL;
-
+
chanrec = channel_find(SERVER(server), channel);
if (chanrec == NULL) return;
- /* the topic may be send out encoded, so we need to
+ /* the topic may be send out encoded, so we need to
recode it back or /topic <tab> will not work properly */
recoded = recode_in(SERVER(server), topic, channel);
if (topic != NULL) {
@@ -137,7 +137,7 @@ static void channel_change_topic(IRC_SERVER_REC *server, const char *channel,
g_free_not_null(chanrec->topic_by);
chanrec->topic_by = g_strdup(setby);
-
+
chanrec->topic_time = settime;
signal_emit("channel topic changed", 1, chanrec);
@@ -270,7 +270,7 @@ static void event_join(IRC_SERVER_REC *server, const char *data, const char *nic
}
chanrec->joined = TRUE;
- if (strcmp(chanrec->name, channel) != 0) {
+ if (g_strcmp0(chanrec->name, channel) != 0) {
g_free(chanrec->name);
chanrec->name = g_strdup(channel);
}
diff --git a/src/irc/core/channel-rejoin.c b/src/irc/core/channel-rejoin.c
index d518f0c2..154035ae 100644
--- a/src/irc/core/channel-rejoin.c
+++ b/src/irc/core/channel-rejoin.c
@@ -84,7 +84,7 @@ static int channel_rejoin(IRC_SERVER_REC *server, const char *channel)
channel_destroy(CHANNEL(chanrec));
return 0;
}
-
+
rec = rejoin_find(server, channel);
if (rec != NULL) {
/* already exists */
@@ -149,7 +149,7 @@ static void event_target_unavailable(IRC_SERVER_REC *server, const char *data)
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &channel);
- if (ischannel(*channel)) {
+ if (server_ischannel(SERVER(server), channel)) {
chanrec = irc_channel_find(server, channel);
if (chanrec != NULL && chanrec->joined) {
/* dalnet event - can't change nick while
diff --git a/src/irc/core/channels-query.c b/src/irc/core/channels-query.c
index 48ba5703..857ebaf0 100644
--- a/src/irc/core/channels-query.c
+++ b/src/irc/core/channels-query.c
@@ -311,7 +311,7 @@ static void channel_checksync(IRC_CHANNEL_REC *channel)
signal_emit("channel sync", 1, channel);
}
-/* Error occured when trying to execute query - abort and try again. */
+/* Error occurred when trying to execute query - abort and try again. */
static void query_current_error(IRC_SERVER_REC *server)
{
SERVER_QUERY_REC *rec;
diff --git a/src/irc/core/irc-cap.c b/src/irc/core/irc-cap.c
new file mode 100644
index 00000000..5464e493
--- /dev/null
+++ b/src/irc/core/irc-cap.c
@@ -0,0 +1,192 @@
+/* irc-cap.c : irssi
+
+ Copyright (C) 2015 The Lemon Man
+
+ 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "misc.h"
+
+#include "irc-cap.h"
+#include "irc-servers.h"
+
+int cap_toggle (IRC_SERVER_REC *server, char *cap, int enable)
+{
+ if (cap == NULL || *cap == '\0')
+ return FALSE;
+
+ /* If the negotiation hasn't been completed yet just queue the requests */
+ if (!server->cap_complete) {
+ if (enable && !gslist_find_string(server->cap_queue, cap)) {
+ server->cap_queue = g_slist_prepend(server->cap_queue, g_strdup(cap));
+ return TRUE;
+ }
+ else if (!enable && gslist_find_string(server->cap_queue, cap)) {
+ server->cap_queue = gslist_remove_string(server->cap_queue, cap);
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ if (enable && !gslist_find_string(server->cap_active, cap)) {
+ /* Make sure the required cap is supported by the server */
+ if (!gslist_find_string(server->cap_supported, cap))
+ return FALSE;
+
+ irc_send_cmdv(server, "CAP REQ %s", cap);
+ return TRUE;
+ }
+ else if (!enable && gslist_find_string(server->cap_active, cap)) {
+ irc_send_cmdv(server, "CAP REQ -%s", cap);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void cap_finish_negotiation (IRC_SERVER_REC *server)
+{
+ if (server->cap_complete)
+ return;
+
+ server->cap_complete = TRUE;
+ irc_send_cmd_now(server, "CAP END");
+
+ signal_emit("server cap end", 1, server);
+}
+
+static void cap_emit_signal (IRC_SERVER_REC *server, char *cmd, char *args)
+{
+ char *signal_name;
+
+ signal_name = g_strdup_printf("server cap %s %s", cmd, args? args: "");
+ signal_emit(signal_name, 1, server);
+ g_free(signal_name);
+}
+
+static void event_cap (IRC_SERVER_REC *server, char *args, char *nick, char *address)
+{
+ GSList *tmp;
+ GString *cmd;
+ char *params, *evt, *list, **caps;
+ int i, caps_length, disable, avail_caps;
+
+ params = event_get_params(args, 3, NULL, &evt, &list);
+ if (params == NULL)
+ return;
+
+ /* Strip the trailing whitespaces before splitting the string, some servers send responses with
+ * superfluous whitespaces that g_strsplit the interprets as tokens */
+ caps = g_strsplit(g_strchomp(list), " ", -1);
+ caps_length = g_strv_length(caps);
+
+ if (!g_strcmp0(evt, "LS")) {
+ /* Create a list of the supported caps */
+ for (i = 0; i < caps_length; i++)
+ server->cap_supported = g_slist_prepend(server->cap_supported, g_strdup(caps[i]));
+
+ /* Request the required caps, if any */
+ if (server->cap_queue == NULL) {
+ cap_finish_negotiation(server);
+ }
+ else {
+ cmd = g_string_new("CAP REQ :");
+
+ avail_caps = 0;
+
+ /* Check whether the cap is supported by the server */
+ for (tmp = server->cap_queue; tmp != NULL; tmp = tmp->next) {
+ if (gslist_find_string(server->cap_supported, tmp->data)) {
+ if (avail_caps > 0)
+ g_string_append_c(cmd, ' ');
+ g_string_append(cmd, tmp->data);
+
+ avail_caps++;
+ }
+ }
+
+ /* Clear the queue here */
+ gslist_free_full(server->cap_queue, (GDestroyNotify) g_free);
+ server->cap_queue = NULL;
+
+ /* If the server doesn't support any cap we requested close the negotiation here */
+ if (avail_caps > 0)
+ irc_send_cmd_now(server, cmd->str);
+ else
+ cap_finish_negotiation(server);
+
+ g_string_free(cmd, TRUE);
+ }
+ }
+ else if (!g_strcmp0(evt, "ACK")) {
+ int got_sasl = FALSE;
+
+ /* Emit a signal for every ack'd cap */
+ for (i = 0; i < caps_length; i++) {
+ disable = (*caps[i] == '-');
+
+ if (disable)
+ server->cap_active = gslist_remove_string(server->cap_active, caps[i] + 1);
+ else
+ server->cap_active = g_slist_prepend(server->cap_active, g_strdup(caps[i]));
+
+ if (!g_strcmp0(caps[i], "sasl"))
+ got_sasl = TRUE;
+
+ cap_emit_signal(server, "ack", caps[i]);
+ }
+
+ /* Hopefully the server has ack'd all the caps requested and we're ready to terminate the
+ * negotiation, unless sasl was requested. In this case we must not terminate the negotiation
+ * until the sasl handshake is over. */
+ if (got_sasl == FALSE)
+ cap_finish_negotiation(server);
+ }
+ else if (!g_strcmp0(evt, "NAK")) {
+ g_warning("The server answered with a NAK to our CAP request, this should not happen");
+
+ /* A NAK'd request means that a required cap can't be enabled or disabled, don't update the
+ * list of active caps and notify the listeners. */
+ for (i = 0; i < caps_length; i++)
+ cap_emit_signal(server, "nak", caps[i]);
+ }
+
+ g_strfreev(caps);
+ g_free(params);
+}
+
+static void event_invalid_cap (IRC_SERVER_REC *server, const char *data, const char *from)
+{
+ /* The server didn't understand one (or more) requested caps, terminate the negotiation.
+ * This could be handled in a graceful way but since it shouldn't really ever happen this seems a
+ * good way to deal with 410 errors. */
+ server->cap_complete = FALSE;
+ irc_send_cmd_now(server, "CAP END");
+}
+
+void cap_init (void)
+{
+ signal_add_first("event cap", (SIGNAL_FUNC) event_cap);
+ signal_add_first("event 410", (SIGNAL_FUNC) event_invalid_cap);
+}
+
+void cap_deinit (void)
+{
+ signal_remove("event cap", (SIGNAL_FUNC) event_cap);
+ signal_remove("event 410", (SIGNAL_FUNC) event_invalid_cap);
+}
diff --git a/src/irc/core/irc-cap.h b/src/irc/core/irc-cap.h
new file mode 100644
index 00000000..df957cd2
--- /dev/null
+++ b/src/irc/core/irc-cap.h
@@ -0,0 +1,9 @@
+#ifndef __IRC_CAP_H
+#define __IRC_CAP_H
+
+void cap_init(void);
+void cap_deinit(void);
+int cap_toggle (IRC_SERVER_REC *server, char *cap, int enable);
+void cap_finish_negotiation (IRC_SERVER_REC *server);
+
+#endif
diff --git a/src/irc/core/irc-channels-setup.c b/src/irc/core/irc-channels-setup.c
index 2320352d..bbbc095c 100644
--- a/src/irc/core/irc-channels-setup.c
+++ b/src/irc/core/irc-channels-setup.c
@@ -24,10 +24,12 @@
void irc_channels_setup_init(void)
{
- signal_add("channel wholist", (SIGNAL_FUNC) channel_send_autocommands);
+ signal_add("channel wholist", (SIGNAL_FUNC) channel_send_botcommands);
+ signal_add("channel joined", (SIGNAL_FUNC) channel_send_autocommands);
}
void irc_channels_setup_deinit(void)
{
- signal_remove("channel wholist", (SIGNAL_FUNC) channel_send_autocommands);
+ signal_remove("channel wholist", (SIGNAL_FUNC) channel_send_botcommands);
+ signal_remove("channel joined", (SIGNAL_FUNC) channel_send_autocommands);
}
diff --git a/src/irc/core/irc-channels.c b/src/irc/core/irc-channels.c
index dd6e29ce..e775f530 100644
--- a/src/irc/core/irc-channels.c
+++ b/src/irc/core/irc-channels.c
@@ -99,7 +99,7 @@ static void irc_channels_join(IRC_SERVER_REC *server, const char *data,
tmp = chanlist;
for (;; tmp++) {
if (*tmp != NULL) {
- channel = ischannel(**tmp) ? g_strdup(*tmp) :
+ channel = server_ischannel(SERVER(server), *tmp) ? g_strdup(*tmp) :
g_strdup_printf("#%s", *tmp);
chanrec = irc_channel_find(server, channel);
@@ -114,7 +114,7 @@ static void irc_channels_join(IRC_SERVER_REC *server, const char *data,
use_keys = TRUE;
key = schannel->password;
} else key = NULL;
-
+
g_string_append_printf(outkeys, "%s,", get_join_key(key));
channame = channel + (channel[0] == '!' &&
channel[1] == '!');
@@ -126,31 +126,33 @@ static void irc_channels_join(IRC_SERVER_REC *server, const char *data,
if (*tmpkey != NULL)
tmpkey++;
-
+
tmpstr = tmp;
tmpstr++;
cmdlen = outchans->len-1;
-
+
if (use_keys)
cmdlen += outkeys->len;
if (*tmpstr != NULL)
- cmdlen += ischannel(**tmpstr) ? strlen(*tmpstr) :
+ cmdlen += server_ischannel(SERVER(server), *tmpstr) ? strlen(*tmpstr) :
strlen(*tmpstr)+1;
if (*tmpkey != NULL)
cmdlen += strlen(*tmpkey);
-
- /* don't try to send too long lines
+
+ /* don't try to send too long lines
make sure it's not longer than 510
so 510 - strlen("JOIN ") = 505 */
if (cmdlen < 505)
continue;
}
if (outchans->len > 0) {
- g_string_truncate(outchans, outchans->len-1);
- g_string_truncate(outkeys, outkeys->len-1);
- irc_send_cmdv(IRC_SERVER(server),
- use_keys ? "JOIN %s %s" : "JOIN %s",
- outchans->str, outkeys->str);
+ g_string_truncate(outchans, outchans->len - 1);
+ g_string_truncate(outkeys, outkeys->len - 1);
+
+ if (use_keys)
+ irc_send_cmdv(IRC_SERVER(server), "JOIN %s %s", outchans->str, outkeys->str);
+ else
+ irc_send_cmdv(IRC_SERVER(server), "JOIN %s", outchans->str);
}
cmdlen = 0;
g_string_truncate(outchans,0);
@@ -172,6 +174,13 @@ static CHANNEL_REC *irc_channel_find_server(SERVER_REC *server,
const char *channel)
{
GSList *tmp;
+ char *fmt_channel;
+
+ /* if 'channel' has no leading # this lookup is going to fail, add a
+ * octothorpe in front of it to handle this case. */
+ fmt_channel = server_ischannel(SERVER(server), channel) ?
+ g_strdup(channel) :
+ g_strdup_printf("#%s", channel);
for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
CHANNEL_REC *rec = tmp->data;
@@ -180,13 +189,19 @@ static CHANNEL_REC *irc_channel_find_server(SERVER_REC *server,
continue;
/* check both !ABCDEchannel and !channel */
- if (IRC_SERVER(server)->nick_comp_func(channel, rec->name) == 0)
+ if (IRC_SERVER(server)->nick_comp_func(fmt_channel, rec->name) == 0) {
+ g_free(fmt_channel);
return rec;
+ }
- if (IRC_SERVER(server)->nick_comp_func(channel, rec->visible_name) == 0)
+ if (IRC_SERVER(server)->nick_comp_func(fmt_channel, rec->visible_name) == 0) {
+ g_free(fmt_channel);
return rec;
+ }
}
+ g_free(fmt_channel);
+
return NULL;
}
diff --git a/src/irc/core/irc-chatnets.c b/src/irc/core/irc-chatnets.c
index d757bf8d..0796e0cb 100644
--- a/src/irc/core/irc-chatnets.c
+++ b/src/irc/core/irc-chatnets.c
@@ -35,10 +35,13 @@ void ircnet_create(IRC_CHATNET_REC *rec)
static void sig_chatnet_read(IRC_CHATNET_REC *rec, CONFIG_NODE *node)
{
+ char *value;
+
if (!IS_IRC_CHATNET(rec))
return;
- rec->usermode = g_strdup(config_node_get_str(node, "usermode", NULL));
+ value = config_node_get_str(node, "usermode", NULL);
+ rec->usermode = (value != NULL && *value != '\0') ? g_strdup(value) : NULL;
rec->max_cmds_at_once = config_node_get_int(node, "cmdmax", 0);
rec->cmd_queue_speed = config_node_get_int(node, "cmdspeed", 0);
@@ -48,6 +51,10 @@ static void sig_chatnet_read(IRC_CHATNET_REC *rec, CONFIG_NODE *node)
rec->max_msgs = config_node_get_int(node, "max_msgs", 0);
rec->max_modes = config_node_get_int(node, "max_modes", 0);
rec->max_whois = config_node_get_int(node, "max_whois", 0);
+
+ rec->sasl_mechanism = g_strdup(config_node_get_str(node, "sasl_mechanism", NULL));
+ rec->sasl_username = g_strdup(config_node_get_str(node, "sasl_username", NULL));
+ rec->sasl_password = g_strdup(config_node_get_str(node, "sasl_password", NULL));
}
static void sig_chatnet_saved(IRC_CHATNET_REC *rec, CONFIG_NODE *node)
@@ -56,7 +63,7 @@ static void sig_chatnet_saved(IRC_CHATNET_REC *rec, CONFIG_NODE *node)
return;
if (rec->usermode != NULL)
- iconfig_node_set_str(node, "usermode", rec->usermode);
+ iconfig_node_set_str(node, "usermode", rec->usermode);
if (rec->max_cmds_at_once > 0)
iconfig_node_set_int(node, "cmdmax", rec->max_cmds_at_once);
@@ -73,12 +80,23 @@ static void sig_chatnet_saved(IRC_CHATNET_REC *rec, CONFIG_NODE *node)
iconfig_node_set_int(node, "max_modes", rec->max_modes);
if (rec->max_whois > 0)
iconfig_node_set_int(node, "max_whois", rec->max_whois);
+
+ if (rec->sasl_mechanism != NULL)
+ iconfig_node_set_str(node, "sasl_mechanism", rec->sasl_mechanism);
+ if (rec->sasl_username != NULL)
+ iconfig_node_set_str(node, "sasl_username", rec->sasl_username);
+ if (rec->sasl_password != NULL)
+ iconfig_node_set_str(node, "sasl_password", rec->sasl_password);
}
static void sig_chatnet_destroyed(IRC_CHATNET_REC *rec)
{
- if (IS_IRC_CHATNET(rec))
- g_free(rec->usermode);
+ if (IS_IRC_CHATNET(rec)) {
+ g_free(rec->usermode);
+ g_free(rec->sasl_mechanism);
+ g_free(rec->sasl_username);
+ g_free(rec->sasl_password);
+ }
}
diff --git a/src/irc/core/irc-chatnets.h b/src/irc/core/irc-chatnets.h
index 22da90c5..2bb10fa9 100644
--- a/src/irc/core/irc-chatnets.h
+++ b/src/irc/core/irc-chatnets.h
@@ -17,12 +17,15 @@
struct _IRC_CHATNET_REC {
#include "chatnet-rec.h"
- char *usermode;
+ char *usermode;
+
+ char *sasl_mechanism;
+ char *sasl_username;
+ char *sasl_password;
int max_cmds_at_once;
int cmd_queue_speed;
- int max_query_chans; /* when syncing, max. number of channels to
- put in one MODE/WHO command */
+ int max_query_chans; /* when syncing, max. number of channels to put in one MODE/WHO command */
/* max. number of kicks/msgs/mode/whois per command */
int max_kicks, max_msgs, max_modes, max_whois;
diff --git a/src/irc/core/irc-commands.c b/src/irc/core/irc-commands.c
index 239c5a64..32ee8845 100644
--- a/src/irc/core/irc-commands.c
+++ b/src/irc/core/irc-commands.c
@@ -61,18 +61,18 @@ static int knockout_tag;
/* SYNTAX: NOTICE <targets> <message> */
static void cmd_notice(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
const char *target, *msg;
char *recoded;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST,
- &target, &msg))
+ &target, &msg))
return;
- if (strcmp(target, "*") == 0)
+ if (g_strcmp0(target, "*") == 0)
target = item == NULL ? NULL : window_item_get_target(item);
if (target == NULL || *target == '\0' || *msg == '\0')
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
@@ -88,18 +88,18 @@ static void cmd_notice(const char *data, IRC_SERVER_REC *server,
/* SYNTAX: CTCP <targets> <ctcp command> [<ctcp data>] */
static void cmd_ctcp(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
const char *target;
char *ctcpcmd, *ctcpdata;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST,
- &target, &ctcpcmd, &ctcpdata))
+ &target, &ctcpcmd, &ctcpdata))
return;
- if (strcmp(target, "*") == 0)
+ if (g_strcmp0(target, "*") == 0)
target = item == NULL ? NULL : window_item_get_target(item);
if (target == NULL || *target == '\0' || *ctcpcmd == '\0')
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
@@ -122,18 +122,18 @@ static void cmd_ctcp(const char *data, IRC_SERVER_REC *server,
/* SYNTAX: NCTCP <targets> <ctcp command> [<ctcp data>] */
static void cmd_nctcp(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
const char *target;
char *ctcpcmd, *ctcpdata, *recoded;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST,
- &target, &ctcpcmd, &ctcpdata))
+ &target, &ctcpcmd, &ctcpdata))
return;
- if (strcmp(target, "*") == 0)
+ if (g_strcmp0(target, "*") == 0)
target = item == NULL ? NULL : window_item_get_target(item);
if (target == NULL || *target == '\0' || *ctcpcmd == '\0')
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
@@ -150,28 +150,31 @@ static void cmd_nctcp(const char *data, IRC_SERVER_REC *server,
/* SYNTAX: PART [<channels>] [<message>] */
static void cmd_part(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
char *channame, *msg;
char *recoded = NULL;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST |
- PARAM_FLAG_OPTCHAN, item, &channame, &msg))
+ PARAM_FLAG_OPTCHAN, item, &channame, &msg))
return;
if (*channame == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
if (*msg == '\0') msg = (char *) settings_get_str("part_message");
- if (server->cmdcount > MAX_COMMANDS_ON_PART_UNTIL_PURGE)
+ if (server->cmdcount > MAX_COMMANDS_ON_PART_UNTIL_PURGE)
irc_server_purge_output(server, channame);
if (*msg != '\0')
recoded = recode_out(SERVER(server), msg, channame);
- irc_send_cmdv(server, ! recoded ? "PART %s" : "PART %s :%s",
- channame, recoded);
+
+ if (recoded == NULL)
+ irc_send_cmdv(server, "PART %s", channame);
+ else
+ irc_send_cmdv(server, "PART %s :%s", channame, recoded);
g_free(recoded);
cmd_params_free(free_arg);
@@ -183,15 +186,15 @@ static void cmd_kick(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item
char *channame, *nicks, *reason, *recoded;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST |
- PARAM_FLAG_OPTCHAN, item,
- &channame, &nicks, &reason))
+ PARAM_FLAG_OPTCHAN, item,
+ &channame, &nicks, &reason))
return;
if (*channame == '\0' || *nicks == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
- if (!ischannel(*channame)) cmd_param_error(CMDERR_NOT_JOINED);
+ if (!server_ischannel(SERVER(server), channame)) cmd_param_error(CMDERR_NOT_JOINED);
recoded = recode_out(SERVER(server), reason, channame);
g_string_printf(tmpstr, "KICK %s %s :%s", channame, nicks, recoded);
@@ -210,17 +213,21 @@ static void cmd_topic(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *ite
char *recoded = NULL;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTCHAN |
- PARAM_FLAG_OPTIONS | PARAM_FLAG_GETREST,
- item, "topic", &optlist, &channame, &topic))
+ PARAM_FLAG_OPTIONS | PARAM_FLAG_GETREST,
+ item, "topic", &optlist, &channame, &topic))
return;
if (*topic != '\0' || g_hash_table_lookup(optlist, "delete") != NULL)
recoded = recode_out(SERVER(server), topic, channame);
- irc_send_cmdv(server, recoded == NULL ? "TOPIC %s" : "TOPIC %s :%s",
- channame, recoded);
+
+ if (recoded == NULL)
+ irc_send_cmdv(server, "TOPIC %s", channame);
+ else
+ irc_send_cmdv(server, "TOPIC %s :%s", channame, recoded);
+
g_free(recoded);
cmd_params_free(free_arg);
@@ -232,13 +239,13 @@ static void cmd_invite(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *it
char *nick, *channame;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 2, &nick, &channame))
return;
if (*nick == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
- if (*channame == '\0' || strcmp(channame, "*") == 0) {
+ if (*channame == '\0' || g_strcmp0(channame, "*") == 0) {
if (!IS_IRC_CHANNEL(item))
cmd_param_error(CMDERR_NOT_JOINED);
@@ -251,16 +258,17 @@ static void cmd_invite(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *it
/* SYNTAX: LIST [-yes] [<channel>] */
static void cmd_list(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
GHashTable *optlist;
char *str;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
- PARAM_FLAG_GETREST, "list", &optlist, &str))
+ PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
+ "list", &optlist, &str))
return;
if (*str == '\0' && g_hash_table_lookup(optlist, "yes") == NULL &&
@@ -274,55 +282,60 @@ static void cmd_list(const char *data, IRC_SERVER_REC *server,
/* SYNTAX: WHO [<nicks> | <channels> | **] */
static void cmd_who(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
char *channel, *rest;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
- if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &channel, &rest))
+ if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST |
+ PARAM_FLAG_STRIP_TRAILING_WS, &channel, &rest))
return;
- if (strcmp(channel, "*") == 0 || *channel == '\0') {
+ if (g_strcmp0(channel, "*") == 0 || *channel == '\0') {
if (!IS_IRC_CHANNEL(item))
- cmd_param_error(CMDERR_NOT_JOINED);
+ cmd_param_error(CMDERR_NOT_JOINED);
channel = IRC_CHANNEL(item)->name;
}
- if (strcmp(channel, "**") == 0) {
+ if (g_strcmp0(channel, "**") == 0) {
/* ** displays all nicks.. */
*channel = '\0';
}
- irc_send_cmdv(server, *rest == '\0' ? "WHO %s" : "WHO %s %s",
- channel, rest);
+ if (rest[0] == '\0')
+ irc_send_cmdv(server, "WHO %s", channel);
+ else
+ irc_send_cmdv(server, "WHO %s %s", channel, rest);
+
cmd_params_free(free_arg);
}
static void cmd_names(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
- GHashTable *optlist;
+ GHashTable *optlist;
char *channel;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
- PARAM_FLAG_GETREST, "names", &optlist, &channel))
+ PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
+ "names", &optlist, &channel))
return;
- if (strcmp(channel, "*") == 0 || *channel == '\0') {
+ if (g_strcmp0(channel, "*") == 0 || *channel == '\0') {
if (!IS_IRC_CHANNEL(item))
- cmd_param_error(CMDERR_NOT_JOINED);
+ cmd_param_error(CMDERR_NOT_JOINED);
channel = IRC_CHANNEL(item)->name;
}
- if (strcmp(channel, "**") == 0) {
+ if (g_strcmp0(channel, "**") == 0) {
/* ** displays all nicks.. */
- irc_send_cmd(server, "NAMES");
+ irc_send_cmd(server, "NAMES");
} else {
irc_send_cmdv(server, "NAMES %s", channel);
}
@@ -333,12 +346,12 @@ static void cmd_names(const char *data, IRC_SERVER_REC *server,
/* SYNTAX: NICK <new nick> */
static void cmd_nick(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item)
{
- char *nick;
+ char *nick;
void *free_arg;
g_return_if_fail(data != NULL);
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 1, &nick))
return;
@@ -375,23 +388,23 @@ static char *get_redirect_nicklist(const char *nicks, int *free)
/* SYNTAX: WHOIS [-<server tag>] [<server>] [<nicks>] */
static void cmd_whois(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
GHashTable *optlist;
char *qserver, *query, *event_402, *str;
void *free_arg;
int free_nick;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS |
- PARAM_FLAG_UNKNOWN_OPTIONS,
- "whois", &optlist, &qserver, &query))
+ PARAM_FLAG_UNKNOWN_OPTIONS,
+ "whois", &optlist, &qserver, &query))
return;
/* -<server tag> */
server = IRC_SERVER(cmd_options_get_server("whois", optlist,
- SERVER(server)));
+ SERVER(server)));
if (server == NULL) {
cmd_params_free(free_arg);
return;
@@ -409,7 +422,7 @@ static void cmd_whois(const char *data, IRC_SERVER_REC *server,
query = qserver = queryitem->name;
}
- if (strcmp(query, "*") == 0 &&
+ if (g_strcmp0(query, "*") == 0 &&
g_hash_table_lookup(optlist, "yes") == NULL)
cmd_param_error(CMDERR_NOT_GOOD_IDEA);
@@ -426,15 +439,15 @@ static void cmd_whois(const char *data, IRC_SERVER_REC *server,
str = g_strconcat(qserver, " ", query, NULL);
server_redirect_event(server, "whois", 1, str, TRUE,
- NULL,
- "event 318", "whois end",
- "event 402", event_402,
- "event 301", "whois away", /* 301 can come as a reply to /MSG, /WHOIS or /WHOWAS */
- "event 313", "whois oper",
- "event 401", (settings_get_bool("auto_whowas") ? "whois try whowas" : "whois event not found"),
- "event 311", "whois event",
- "", "whois default event", NULL);
- g_free(str);
+ NULL,
+ "event 318", "whois end",
+ "event 402", event_402,
+ "event 301", "whois away", /* 301 can come as a reply to /MSG, /WHOIS or /WHOWAS */
+ "event 313", "whois oper",
+ "event 401", (settings_get_bool("auto_whowas") ? "whois try whowas" : "whois event not found"),
+ "event 311", "whois event",
+ "", "whois default event", NULL);
+ g_free(str);
server->whois_found = FALSE;
irc_send_cmd_split(server, tmpstr->str, 2, server->max_whois_in_cmd);
@@ -444,7 +457,7 @@ static void cmd_whois(const char *data, IRC_SERVER_REC *server,
}
static void event_whois(IRC_SERVER_REC *server, const char *data,
- const char *nick, const char *addr)
+ const char *nick, const char *addr)
{
server->whois_found = TRUE;
signal_emit("event 311", 4, server, data, nick, addr);
@@ -460,23 +473,23 @@ static void sig_whois_try_whowas(IRC_SERVER_REC *server, const char *data)
server->whowas_found = FALSE;
server_redirect_event(server, "whowas", 1, nick, -1, NULL,
- "event 314", "whowas event",
- "event 369", "whowas event end",
- "event 406", "event empty", NULL);
+ "event 314", "whowas event",
+ "event 369", "whowas event end",
+ "event 406", "event empty", NULL);
irc_send_cmdv(server, "WHOWAS %s 1", nick);
g_free(params);
}
static void event_end_of_whois(IRC_SERVER_REC *server, const char *data,
- const char *nick, const char *addr)
+ const char *nick, const char *addr)
{
signal_emit("event 318", 4, server, data, nick, addr);
server->whois_found = FALSE;
}
static void event_whowas(IRC_SERVER_REC *server, const char *data,
- const char *nick, const char *addr)
+ const char *nick, const char *addr)
{
server->whowas_found = TRUE;
signal_emit("event 314", 4, server, data, nick, addr);
@@ -489,21 +502,25 @@ static void cmd_whowas(const char *data, IRC_SERVER_REC *server)
void *free_arg;
int free_nick;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
- if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &nicks, &rest))
+ if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
+ &nicks, &rest))
return;
if (*nicks == '\0') nicks = server->nick;
nicks_redir = get_redirect_nicklist(nicks, &free_nick);
server_redirect_event(server, "whowas", 1, nicks_redir, -1, NULL,
- "event 301", "whowas away", /* 301 can come as a reply to /MSG, /WHOIS or /WHOWAS */
- "event 314", "whowas event", NULL);
+ "event 301", "whowas away", /* 301 can come as a reply to /MSG, /WHOIS or /WHOWAS */
+ "event 314", "whowas event", NULL);
if (free_nick) g_free(nicks_redir);
server->whowas_found = FALSE;
- irc_send_cmdv(server, *rest == '\0' ? "WHOWAS %s" :
- "WHOWAS %s %s", nicks, rest);
+
+ if (rest[0] == '\0')
+ irc_send_cmdv(server, "WHOWAS %s", nicks);
+ else
+ irc_send_cmdv(server, "WHOWAS %s %s", nicks, rest);
cmd_params_free(free_arg);
}
@@ -512,9 +529,9 @@ static void cmd_whowas(const char *data, IRC_SERVER_REC *server)
static void cmd_ping(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item)
{
GTimeVal tv;
- char *str;
+ char *str;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (*data == '\0') {
if (!IS_QUERY(item))
@@ -537,7 +554,7 @@ static void cmd_away(const char *data, IRC_SERVER_REC *server)
void *free_arg;
if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
- PARAM_FLAG_GETREST, "away", &optlist, &reason)) return;
+ PARAM_FLAG_GETREST, "away", &optlist, &reason)) return;
if (g_hash_table_lookup(optlist, "one") != NULL)
irc_server_send_away(server, reason);
@@ -550,7 +567,7 @@ static void cmd_away(const char *data, IRC_SERVER_REC *server)
/* SYNTAX: SCONNECT <new server> [[<port>] <existing server>] */
static void cmd_sconnect(const char *data, IRC_SERVER_REC *server)
{
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
irc_send_cmdv(server, "CONNECT %s", data);
@@ -583,11 +600,11 @@ static void cmd_wait(const char *data, IRC_SERVER_REC *server)
void *free_arg;
int n;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
- PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
- NULL, &optlist, &msecs))
+ PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
+ NULL, &optlist, &msecs))
return;
if (*msecs == '\0')
@@ -595,7 +612,7 @@ static void cmd_wait(const char *data, IRC_SERVER_REC *server)
/* -<server tag> */
server = IRC_SERVER(cmd_options_get_server(NULL, optlist,
- SERVER(server)));
+ SERVER(server)));
n = atoi(msecs);
if (server != NULL && n > 0) {
@@ -618,10 +635,10 @@ static void cmd_wall(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item
IRC_CHANNEL_REC *chanrec;
GSList *tmp, *nicks;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTCHAN |
- PARAM_FLAG_GETREST, item, &channame, &msg))
+ PARAM_FLAG_GETREST, item, &channame, &msg))
return;
if (*msg == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
@@ -637,11 +654,11 @@ static void cmd_wall(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item
/* Fall back to manually noticing each op */
nicks = NULL;
g_hash_table_foreach(chanrec->nicks,
- (GHFunc) cmd_wall_hash, &nicks);
+ (GHFunc) cmd_wall_hash, &nicks);
args = g_strconcat(chanrec->name, " ", recoded, NULL);
msg = parse_special_string(settings_get_str("wall_format"),
- SERVER(server), item, args, NULL, 0);
+ SERVER(server), item, args, NULL, 0);
g_free(args);
for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
@@ -649,7 +666,7 @@ static void cmd_wall(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item
if (rec != chanrec->ownnick) {
irc_send_cmdv(server, "NOTICE %s :%s",
- rec->nick, msg);
+ rec->nick, msg);
}
}
@@ -663,17 +680,17 @@ static void cmd_wall(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item
/* SYNTAX: KICKBAN [<channel>] <nicks> <reason> */
static void cmd_kickban(const char *data, IRC_SERVER_REC *server,
- WI_ITEM_REC *item)
+ WI_ITEM_REC *item)
{
- IRC_CHANNEL_REC *chanrec;
+ IRC_CHANNEL_REC *chanrec;
char *channel, *nicks, *reason, *kickcmd, *bancmd, *recoded;
- char **nicklist, *spacenicks;
+ char **nicklist, *spacenicks;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTCHAN | PARAM_FLAG_GETREST,
- item, &channel, &nicks, &reason))
+ item, &channel, &nicks, &reason))
return;
if (*channel == '\0' || *nicks == '\0')
@@ -684,7 +701,7 @@ static void cmd_kickban(const char *data, IRC_SERVER_REC *server,
cmd_param_error(CMDERR_CHAN_NOT_FOUND);
nicklist = g_strsplit(nicks, ",", -1);
- spacenicks = g_strjoinv(" ", nicklist);
+ spacenicks = g_strjoinv(" ", nicklist);
g_strfreev(nicklist);
recoded = recode_out(SERVER(server), reason, channel);
@@ -692,9 +709,9 @@ static void cmd_kickban(const char *data, IRC_SERVER_REC *server,
g_free(recoded);
bancmd = g_strdup_printf("%s %s", chanrec->name, spacenicks);
- g_free(spacenicks);
+ g_free(spacenicks);
- if (settings_get_bool("kick_first_on_kickban")) {
+ if (settings_get_bool("kick_first_on_kickban")) {
signal_emit("command kick", 3, kickcmd, server, chanrec);
signal_emit("command ban", 3, bancmd, server, chanrec);
} else {
@@ -725,14 +742,14 @@ static void knockout_timeout_server(IRC_SERVER_REC *server)
if (!IS_IRC_SERVER(server))
return;
- now = time(NULL);
+ now = time(NULL);
for (tmp = server->knockoutlist; tmp != NULL; tmp = next) {
KNOCKOUT_REC *rec = tmp->data;
next = tmp->next;
if (rec->unban_time <= now) {
/* timeout, unban. */
- ban_remove(rec->channel, rec->ban);
+ signal_emit("command unban", 3, rec->ban, server, rec->channel);
knockout_destroy(server, rec);
}
}
@@ -746,16 +763,16 @@ static int knockout_timeout(void)
/* SYNTAX: KNOCKOUT [<time>] <nicks> <reason> */
static void cmd_knockout(const char *data, IRC_SERVER_REC *server,
- IRC_CHANNEL_REC *channel)
+ IRC_CHANNEL_REC *channel)
{
KNOCKOUT_REC *rec;
char *nicks, *reason, *timeoutstr, *kickcmd, *bancmd, *recoded;
- char **nicklist, *spacenicks, *banmasks;
+ char **nicklist, *spacenicks, *banmasks;
void *free_arg;
int timeleft;
GSList *ptr;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!IS_IRC_CHANNEL(channel))
cmd_return_error(CMDERR_NOT_JOINED);
@@ -763,14 +780,14 @@ static void cmd_knockout(const char *data, IRC_SERVER_REC *server,
if (i_isdigit(*data)) {
/* first argument is the timeout */
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST,
- &timeoutstr, &nicks, &reason))
+ &timeoutstr, &nicks, &reason))
return;
if (!parse_time_interval(timeoutstr, &timeleft))
cmd_param_error(CMDERR_INVALID_TIME);
} else {
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST,
- &nicks, &reason))
+ &nicks, &reason))
return;
timeleft = settings_get_time("knockout_time");
}
@@ -778,7 +795,7 @@ static void cmd_knockout(const char *data, IRC_SERVER_REC *server,
if (*nicks == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
nicklist = g_strsplit(nicks, ",", -1);
- spacenicks = g_strjoinv(" ", nicklist);
+ spacenicks = g_strjoinv(" ", nicklist);
g_strfreev(nicklist);
banmasks = ban_get_masks(channel, spacenicks, 0);
@@ -790,8 +807,8 @@ static void cmd_knockout(const char *data, IRC_SERVER_REC *server,
bancmd = *banmasks == '\0'? NULL :
g_strdup_printf("%s %s", channel->name, banmasks);
-
- if (settings_get_bool("kick_first_on_kickban")) {
+
+ if (settings_get_bool("kick_first_on_kickban")) {
signal_emit("command kick", 3, kickcmd, server, channel);
if (bancmd != NULL)
signal_emit("command ban", 3, bancmd, server, channel);
@@ -810,7 +827,7 @@ static void cmd_knockout(const char *data, IRC_SERVER_REC *server,
for (ptr = server->knockoutlist; ptr != NULL; ptr = ptr->next) {
rec = ptr->data;
if (channel == rec->channel &&
- !strcmp(rec->ban, banmasks))
+ !g_strcmp0(rec->ban, banmasks))
break;
}
if (ptr == NULL) {
@@ -828,10 +845,10 @@ static void cmd_knockout(const char *data, IRC_SERVER_REC *server,
/* SYNTAX: SERVER PURGE [<target>] */
static void cmd_server_purge(const char *data, IRC_SERVER_REC *server)
{
- char *target;
+ char *target;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 1, &target))
return;
@@ -878,12 +895,12 @@ static void cmd_oper(const char *data, IRC_SERVER_REC *server)
char *nick, *password;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
- /* asking for password is handled by fe-common */
+ /* asking for password is handled by fe-common */
if (!cmd_get_params(data, &free_arg, 2, &nick, &password))
return;
- if (*password == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+ if (*password == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
irc_send_cmdv(server, "OPER %s %s", nick, password);
cmd_params_free(free_arg);
@@ -892,9 +909,9 @@ static void cmd_oper(const char *data, IRC_SERVER_REC *server)
/* SYNTAX: ACCEPT [[-]nick,...] */
static void cmd_accept(const char *data, IRC_SERVER_REC *server)
{
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
- if (*data == '\0')
+ if (*data == '\0')
irc_send_cmd(server, "ACCEPT *");
else
irc_send_cmdv(server, "ACCEPT %s", data);
@@ -903,9 +920,9 @@ static void cmd_accept(const char *data, IRC_SERVER_REC *server)
/* SYNTAX: UNSILENCE <nick!user@host> */
static void cmd_unsilence(const char *data, IRC_SERVER_REC *server)
{
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
- if (*data == '\0')
+ if (*data == '\0')
cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
irc_send_cmdv(server, "SILENCE -%s", data);
@@ -913,9 +930,12 @@ static void cmd_unsilence(const char *data, IRC_SERVER_REC *server)
static void command_self(const char *data, IRC_SERVER_REC *server)
{
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
- irc_send_cmdv(server, *data == '\0' ? "%s" : "%s %s", current_command, data);
+ if (data[0] == '\0')
+ irc_send_cmdv(server, "%s", current_command);
+ else
+ irc_send_cmdv(server, "%s %s", current_command, data);
}
static void command_1self(const char *data, IRC_SERVER_REC *server)
@@ -933,7 +953,7 @@ static void command_2self(const char *data, IRC_SERVER_REC *server)
char *target, *text;
void *free_arg;
- CMD_IRC_SERVER(server);
+ CMD_IRC_SERVER(server);
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &target, &text))
return;
@@ -978,8 +998,8 @@ void irc_commands_init(void)
command_bind_irc("admin", NULL, (SIGNAL_FUNC) command_self);
/* SYNTAX: INFO [<server>] */
command_bind_irc("info", NULL, (SIGNAL_FUNC) command_self);
- /* SYNTAX: KNOCK <channel> */
- command_bind_irc("knock", NULL, (SIGNAL_FUNC) command_self);
+ /* SYNTAX: KNOCK <channel> */
+ command_bind_irc("knock", NULL, (SIGNAL_FUNC) command_self);
/* SYNTAX: LINKS [[<server>] <mask>] */
command_bind_irc("links", NULL, (SIGNAL_FUNC) command_self);
/* SYNTAX: LUSERS [<server mask> [<remote server>]] */
@@ -998,11 +1018,15 @@ void irc_commands_init(void)
command_bind_irc("trace", NULL, (SIGNAL_FUNC) command_self);
/* SYNTAX: VERSION [<server>|<nick>] */
command_bind_irc("version", NULL, (SIGNAL_FUNC) command_self);
+ /* SYNTAX: SERVLIST [<mask> [<type>]] */
+ command_bind_irc("servlist", NULL, (SIGNAL_FUNC) command_self);
/* SYNTAX: SILENCE [[+|-]<nick!user@host>]
SILENCE [<nick>] */
command_bind_irc("silence", NULL, (SIGNAL_FUNC) command_self);
command_bind_irc("unsilence", NULL, (SIGNAL_FUNC) cmd_unsilence);
command_bind_irc("sconnect", NULL, (SIGNAL_FUNC) cmd_sconnect);
+ /* SYNTAX: SQUERY <service> [<message>] */
+ command_bind_irc("squery", NULL, (SIGNAL_FUNC) command_2self);
/* SYNTAX: DIE */
command_bind_irc("die", NULL, (SIGNAL_FUNC) command_self);
/* SYNTAX: HASH */
@@ -1061,7 +1085,7 @@ void irc_commands_deinit(void)
command_unbind("accept", (SIGNAL_FUNC) cmd_accept);
command_unbind("admin", (SIGNAL_FUNC) command_self);
command_unbind("info", (SIGNAL_FUNC) command_self);
- command_unbind("knock", (SIGNAL_FUNC) command_self);
+ command_unbind("knock", (SIGNAL_FUNC) command_self);
command_unbind("links", (SIGNAL_FUNC) command_self);
command_unbind("lusers", (SIGNAL_FUNC) command_self);
command_unbind("map", (SIGNAL_FUNC) command_self);
@@ -1071,9 +1095,11 @@ void irc_commands_deinit(void)
command_unbind("time", (SIGNAL_FUNC) command_self);
command_unbind("trace", (SIGNAL_FUNC) command_self);
command_unbind("version", (SIGNAL_FUNC) command_self);
+ command_unbind("servlist", (SIGNAL_FUNC) command_self);
command_unbind("silence", (SIGNAL_FUNC) command_self);
command_unbind("unsilence", (SIGNAL_FUNC) cmd_unsilence);
command_unbind("sconnect", (SIGNAL_FUNC) cmd_sconnect);
+ command_unbind("squery", (SIGNAL_FUNC) command_2self);
command_unbind("die", (SIGNAL_FUNC) command_self);
command_unbind("hash", (SIGNAL_FUNC) command_self);
command_unbind("oper", (SIGNAL_FUNC) cmd_oper);
diff --git a/src/irc/core/irc-core.c b/src/irc/core/irc-core.c
index bf7386ad..a9221e02 100644
--- a/src/irc/core/irc-core.c
+++ b/src/irc/core/irc-core.c
@@ -26,6 +26,8 @@
#include "irc-chatnets.h"
#include "irc-channels.h"
#include "irc-queries.h"
+#include "irc-cap.h"
+#include "sasl.h"
#include "irc-servers-setup.h"
#include "channels-setup.h"
@@ -117,6 +119,8 @@ void irc_core_init(void)
lag_init();
netsplit_init();
irc_expandos_init();
+ cap_init();
+ sasl_init();
settings_check();
module_register("core", "irc");
@@ -126,6 +130,8 @@ void irc_core_deinit(void)
{
signal_emit("chat protocol deinit", 1, chat_protocol_find("IRC"));
+ sasl_deinit();
+ cap_deinit();
irc_expandos_deinit();
netsplit_deinit();
lag_deinit();
@@ -137,7 +143,7 @@ void irc_core_deinit(void)
irc_irc_deinit();
irc_servers_deinit();
irc_chatnets_deinit();
- irc_session_deinit();
+ irc_session_deinit();
chat_protocol_unregister("IRC");
}
diff --git a/src/irc/core/irc-expandos.c b/src/irc/core/irc-expandos.c
index 5d2de503..62ef577a 100644
--- a/src/irc/core/irc-expandos.c
+++ b/src/irc/core/irc-expandos.c
@@ -27,6 +27,10 @@
#include "irc-channels.h"
#include "nicklist.h"
+#ifndef HOST_NAME_MAX
+#define HOST_NAME_MAX 255
+#endif
+
static char *last_join;
/* last person to join a channel you are on */
@@ -56,7 +60,7 @@ static char *expando_userhost(SERVER_REC *server, void *item, int *free_ret)
{
IRC_SERVER_REC *ircserver;
const char *username;
- char hostname[100];
+ char hostname[HOST_NAME_MAX + 1];
ircserver = IRC_SERVER(server);
@@ -72,10 +76,36 @@ static char *expando_userhost(SERVER_REC *server, void *item, int *free_ret)
username = ircserver->connrec->username;
if (gethostname(hostname, sizeof(hostname)) != 0 || *hostname == '\0')
- strcpy(hostname, "??");
+ strcpy(hostname, "(none)");
return g_strconcat(username, "@", hostname, NULL);;
}
+/* your hostname address (host) */
+static char *expando_hostname(SERVER_REC *server, void *item, int *free_ret)
+{
+ IRC_SERVER_REC *ircserver;
+ char hostname[HOST_NAME_MAX + 1];
+ char **list;
+ char *hostname_split;
+
+ ircserver = IRC_SERVER(server);
+
+ *free_ret = TRUE;
+
+ /* prefer the _real_ /userhost reply */
+ if (ircserver != NULL && ircserver->userhost != NULL) {
+ list = g_strsplit(ircserver->userhost, "@", -1);
+ hostname_split = g_strdup(list[1]);
+ g_strfreev(list);
+ return hostname_split;
+ }
+
+ /* haven't received userhost reply yet. guess something */
+ if (gethostname(hostname, sizeof(hostname)) != 0 || *hostname == '\0')
+ strcpy(hostname, "(none)");
+ return g_strdup(hostname);
+}
+
/* user mode in active server */
static char *expando_usermode(SERVER_REC *server, void *item, int *free_ret)
{
@@ -136,6 +166,9 @@ void irc_expandos_init(void)
expando_create("X", expando_userhost,
"window changed", EXPANDO_ARG_NONE,
"window server changed", EXPANDO_ARG_WINDOW, NULL);
+ expando_create("x", expando_hostname,
+ "window changed", EXPANDO_ARG_NONE,
+ "window server changed", EXPANDO_ARG_WINDOW, NULL);
expando_create("usermode", expando_usermode,
"window changed", EXPANDO_ARG_NONE,
"window server changed", EXPANDO_ARG_WINDOW,
@@ -164,6 +197,7 @@ void irc_expandos_deinit(void)
expando_destroy("H", expando_server_numeric);
expando_destroy("S", expando_servername);
expando_destroy("X", expando_userhost);
+ expando_destroy("x", expando_hostname);
expando_destroy("usermode", expando_usermode);
expando_destroy("cumode", expando_cumode);
diff --git a/src/irc/core/irc-nicklist.c b/src/irc/core/irc-nicklist.c
index da9d4aca..1cb1f3e9 100644
--- a/src/irc/core/irc-nicklist.c
+++ b/src/irc/core/irc-nicklist.c
@@ -48,37 +48,13 @@ NICK_REC *irc_nicklist_insert(IRC_CHANNEL_REC *channel, const char *nick,
rec->send_massjoin = send_massjoin;
if (prefixes != NULL) {
- strocpy(rec->prefixes, prefixes, sizeof(rec->prefixes));
+ g_strlcpy(rec->prefixes, prefixes, sizeof(rec->prefixes));
}
nicklist_insert(CHANNEL(channel), rec);
return rec;
}
-#define isnickchar(a) \
- (i_isalnum(a) || (a) == '`' || (a) == '-' || (a) == '_' || \
- (a) == '[' || (a) == ']' || (a) == '{' || (a) == '}' || \
- (a) == '|' || (a) == '\\' || (a) == '^')
-
-/* Remove all "extra" characters from `nick'. Like _nick_ -> nick */
-char *irc_nick_strip(const char *nick)
-{
- char *stripped, *spos;
-
- g_return_val_if_fail(nick != NULL, NULL);
-
- spos = stripped = g_strdup(nick);
- while (isnickchar(*nick)) {
- if (i_isalnum(*nick))
- *spos++ = *nick;
- nick++;
- }
- if ((unsigned char) *nick >= 128)
- *spos++ = *nick; /* just add it so that nicks won't match.. */
- *spos = '\0';
- return stripped;
-}
-
int irc_nickcmp_rfc1459(const char *m, const char *n)
{
while (*m != '\0' && *n != '\0') {
@@ -195,7 +171,7 @@ static void event_end_of_names(IRC_SERVER_REC *server, const char *data)
IRC_CHANNEL_REC *chanrec;
NICK_REC *ownnick;
int nicks;
-
+
g_return_if_fail(server != NULL);
params = event_get_params(data, 2, NULL, &channel);
@@ -338,7 +314,11 @@ static void event_whois_ircop(SERVER_REC *server, const char *data)
static void event_nick_invalid(IRC_SERVER_REC *server, const char *data)
{
if (!server->connected)
- server_disconnect((SERVER_REC *) server);
+ /* we used to call server_disconnect but that crashes
+ irssi because of undefined memory access. instead,
+ indicate that the connection should be dropped and
+ let the irc method to the clean-up. */
+ server->connection_lost = server->no_reconnect = TRUE;
}
static void event_nick_in_use(IRC_SERVER_REC *server, const char *data)
@@ -384,7 +364,7 @@ static void event_nick_in_use(IRC_SERVER_REC *server, const char *data)
cmd = g_strdup_printf("NICK %s", server->nick);
irc_send_cmd_now(server, cmd);
- g_free(cmd);
+ g_free(cmd);
}
static void event_target_unavailable(IRC_SERVER_REC *server, const char *data)
@@ -394,7 +374,7 @@ static void event_target_unavailable(IRC_SERVER_REC *server, const char *data)
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &channel);
- if (!ischannel(*channel)) {
+ if (!server_ischannel(SERVER(server), channel)) {
/* nick is unavailable. */
event_nick_in_use(server, data);
}
diff --git a/src/irc/core/irc-nicklist.h b/src/irc/core/irc-nicklist.h
index 7302556b..2ae17d2c 100644
--- a/src/irc/core/irc-nicklist.h
+++ b/src/irc/core/irc-nicklist.h
@@ -8,9 +8,6 @@ NICK_REC *irc_nicklist_insert(IRC_CHANNEL_REC *channel, const char *nick,
int op, int halfop, int voice, int send_massjoin,
const char *prefixes);
-/* Remove all "extra" characters from `nick'. Like _nick_ -> nick */
-char *irc_nick_strip(const char *nick);
-
int irc_nickcmp_rfc1459(const char *, const char *);
int irc_nickcmp_ascii(const char *, const char *);
diff --git a/src/irc/core/irc-queries.c b/src/irc/core/irc-queries.c
index ac1a72a1..64995ead 100644
--- a/src/irc/core/irc-queries.c
+++ b/src/irc/core/irc-queries.c
@@ -45,6 +45,8 @@ QUERY_REC *irc_query_find(IRC_SERVER_REC *server, const char *nick)
{
GSList *tmp;
+ g_return_val_if_fail(nick != NULL, NULL);
+
for (tmp = server->queries; tmp != NULL; tmp = tmp->next) {
QUERY_REC *rec = tmp->data;
@@ -60,39 +62,25 @@ static void check_query_changes(IRC_SERVER_REC *server, const char *nick,
{
QUERY_REC *query;
- if (ischannel(*target))
- return;
+ if (server_ischannel(SERVER(server), target))
+ return;
query = irc_query_find(server, nick);
if (query == NULL)
return;
- if (strcmp(query->name, nick) != 0) {
+ if (g_strcmp0(query->name, nick) != 0) {
/* upper/lowercase chars in nick changed */
query_change_nick(query, nick);
}
if (address != NULL && (query->address == NULL ||
- strcmp(query->address, address) != 0)) {
+ g_strcmp0(query->address, address) != 0)) {
/* host changed */
query_change_address(query, address);
}
}
-static void event_privmsg(IRC_SERVER_REC *server, const char *data,
- const char *nick, const char *address)
-{
- char *params, *target, *msg;
-
- g_return_if_fail(data != NULL);
- if (nick == NULL)
- return;
-
- params = event_get_params(data, 2 | PARAM_FLAG_GETREST, &target, &msg);
- check_query_changes(server, nick, address, target);
- g_free(params);
-}
-
static void ctcp_action(IRC_SERVER_REC *server, const char *msg,
const char *nick, const char *address,
const char *target)
@@ -109,7 +97,7 @@ static void event_nick(SERVER_REC *server, const char *data,
query = query_find(server, orignick);
if (query != NULL) {
params = event_get_params(data, 1, &nick);
- if (strcmp(query->name, nick) != 0)
+ if (g_strcmp0(query->name, nick) != 0)
query_change_nick(query, nick);
g_free(params);
}
@@ -117,14 +105,12 @@ static void event_nick(SERVER_REC *server, const char *data,
void irc_queries_init(void)
{
- signal_add_last("event privmsg", (SIGNAL_FUNC) event_privmsg);
signal_add_last("ctcp action", (SIGNAL_FUNC) ctcp_action);
signal_add("event nick", (SIGNAL_FUNC) event_nick);
}
void irc_queries_deinit(void)
{
- signal_remove("event privmsg", (SIGNAL_FUNC) event_privmsg);
signal_remove("ctcp action", (SIGNAL_FUNC) ctcp_action);
signal_remove("event nick", (SIGNAL_FUNC) event_nick);
}
diff --git a/src/irc/core/irc-servers-reconnect.c b/src/irc/core/irc-servers-reconnect.c
index 1587d4e6..ca61492d 100644
--- a/src/irc/core/irc-servers-reconnect.c
+++ b/src/irc/core/irc-servers-reconnect.c
@@ -48,6 +48,9 @@ static void sig_server_connect_copy(SERVER_CONNECT_REC **dest,
rec->max_whois = src->max_whois;
rec->usermode = g_strdup(src->usermode);
rec->alternate_nick = g_strdup(src->alternate_nick);
+ rec->sasl_mechanism = src->sasl_mechanism;
+ rec->sasl_username = src->sasl_username;
+ rec->sasl_password = src->sasl_password;
*dest = (SERVER_CONNECT_REC *) rec;
}
@@ -71,7 +74,7 @@ static void sig_connected(IRC_SERVER_REC *server)
return;
if (server->connrec->away_reason != NULL)
- irc_server_send_away(server, server->connrec->away_reason);
+ irc_server_send_away(server, server->connrec->away_reason);
}
static void event_nick_collision(IRC_SERVER_REC *server, const char *data)
diff --git a/src/irc/core/irc-servers-setup.c b/src/irc/core/irc-servers-setup.c
index 5659991b..f425b587 100644
--- a/src/irc/core/irc-servers-setup.c
+++ b/src/irc/core/irc-servers-setup.c
@@ -28,6 +28,7 @@
#include "irc-chatnets.h"
#include "irc-servers-setup.h"
#include "irc-servers.h"
+#include "sasl.h"
/* Fill information to connection from server setup record */
static void sig_server_setup_fill_reconn(IRC_SERVER_CONNECT_REC *conn,
@@ -47,12 +48,18 @@ static void sig_server_setup_fill_reconn(IRC_SERVER_CONNECT_REC *conn,
static void sig_server_setup_fill_connect(IRC_SERVER_CONNECT_REC *conn)
{
+ const char *value;
+
if (!IS_IRC_SERVER_CONNECT(conn))
return;
- conn->alternate_nick = *settings_get_str("alternate_nick") != '\0' ?
- g_strdup(settings_get_str("alternate_nick")) : NULL;
- conn->usermode = g_strdup(settings_get_str("usermode"));
+ value = settings_get_str("alternate_nick");
+ conn->alternate_nick = (value != NULL && *value != '\0') ?
+ g_strdup(value) : NULL;
+
+ value = settings_get_str("usermode");
+ conn->usermode = (value != NULL && *value != '\0') ?
+ g_strdup(value) : NULL;
}
static void sig_server_setup_fill_chatnet(IRC_SERVER_CONNECT_REC *conn,
@@ -79,6 +86,29 @@ static void sig_server_setup_fill_chatnet(IRC_SERVER_CONNECT_REC *conn,
conn->cmd_queue_speed = ircnet->cmd_queue_speed;
if (ircnet->max_query_chans > 0)
conn->max_query_chans = ircnet->max_query_chans;
+
+ /* Validate the SASL parameters filled by sig_chatnet_read() or cmd_network_add */
+ conn->sasl_mechanism = SASL_MECHANISM_NONE;
+
+ if (ircnet->sasl_mechanism != NULL) {
+ if (!g_ascii_strcasecmp(ircnet->sasl_mechanism, "plain")) {
+ /* The PLAIN method needs both the username and the password */
+ if (ircnet->sasl_username != NULL && *ircnet->sasl_username &&
+ ircnet->sasl_password != NULL && *ircnet->sasl_password) {
+ conn->sasl_mechanism = SASL_MECHANISM_PLAIN;
+ conn->sasl_username = ircnet->sasl_username;
+ conn->sasl_password = ircnet->sasl_password;
+ } else
+ g_warning("The fields sasl_username and sasl_password are either missing or empty");
+ }
+ else if (!g_ascii_strcasecmp(ircnet->sasl_mechanism, "external")) {
+ conn->sasl_mechanism = SASL_MECHANISM_EXTERNAL;
+ conn->sasl_username = NULL;
+ conn->sasl_password = NULL;
+ }
+ else
+ g_warning("Unsupported SASL mechanism \"%s\" selected", ircnet->sasl_mechanism);
+ }
}
static void init_userinfo(void)
diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c
index 13784f88..3117e345 100644
--- a/src/irc/core/irc-servers.c
+++ b/src/irc/core/irc-servers.c
@@ -32,6 +32,10 @@
#include "irc-queries.h"
#include "irc-servers-setup.h"
#include "irc-servers.h"
+#include "irc-cap.h"
+#include "sasl.h"
+
+#include "channels-setup.h"
#include "channel-rejoin.h"
#include "servers-idle.h"
#include "servers-reconnect.h"
@@ -71,13 +75,28 @@ static int isnickflag_func(SERVER_REC *server, char flag)
static int ischannel_func(SERVER_REC *server, const char *data)
{
- if (*data == '@') {
- /* @#channel, @+#channel */
- data++;
- if (*data == '+' && ischannel(data[1]))
- return 1;
- }
- return ischannel(*data);
+ IRC_SERVER_REC *irc_server = (IRC_SERVER_REC *) server;
+ char *chantypes, *statusmsg;
+
+ g_return_val_if_fail(data != NULL, FALSE);
+
+ /* empty string is no channel */
+ if (*data == '\0')
+ return FALSE;
+
+ chantypes = g_hash_table_lookup(irc_server->isupport, "chantypes");
+ if (chantypes == NULL)
+ chantypes = "#&!+"; /* normal, local, secure, modeless */
+
+ statusmsg = g_hash_table_lookup(irc_server->isupport, "statusmsg");
+ if (statusmsg == NULL)
+ statusmsg = "@";
+
+ data += strspn(data, statusmsg);
+
+ /* strchr(3) considers the trailing NUL as part of the string, make sure
+ * we didn't advance too much. */
+ return *data != '\0' && strchr(chantypes, *data) != NULL;
}
static char **split_line(const SERVER_REC *server, const char *line,
@@ -85,6 +104,7 @@ static char **split_line(const SERVER_REC *server, const char *line,
{
const char *start = settings_get_str("split_line_start");
const char *end = settings_get_str("split_line_end");
+ gboolean onspace = settings_get_bool("split_line_on_space");
char *recoded_start = recode_out(server, start, target);
char *recoded_end = recode_out(server, end, target);
char **lines;
@@ -103,7 +123,7 @@ static char **split_line(const SERVER_REC *server, const char *line,
return NULL;
}
- lines = recode_split(server, line, target, len);
+ lines = recode_split(server, line, target, len, onspace);
for (i = 0; lines[i] != NULL; i++) {
if (i != 0 && *start != '\0') {
/* Not the first line. */
@@ -178,23 +198,15 @@ static char **split_message(SERVER_REC *server, const char *target,
const char *msg)
{
IRC_SERVER_REC *ircserver = IRC_SERVER(server);
- int userhostlen = MAX_USERHOST_LEN;
g_return_val_if_fail(ircserver != NULL, NULL);
g_return_val_if_fail(target != NULL, NULL);
g_return_val_if_fail(msg != NULL, NULL);
- /*
- * If we have joined a channel, userhost will be set, so we can
- * calculate the exact maximum length.
- */
- if (ircserver->userhost != NULL)
- userhostlen = strlen(ircserver->userhost);
-
- /* length calculation shamelessly stolen from splitlong.pl */
+ /* length calculation shamelessly stolen from splitlong_safe.pl */
return split_line(SERVER(server), msg, target,
510 - strlen(":! PRIVMSG :") -
- strlen(ircserver->nick) - userhostlen -
+ strlen(ircserver->nick) - MAX_USERHOST_LEN -
strlen(target));
}
@@ -202,7 +214,6 @@ static void server_init(IRC_SERVER_REC *server)
{
IRC_SERVER_CONNECT_REC *conn;
char *address, *ptr, *username, *cmd;
- GTimeVal now;
g_return_if_fail(server != NULL);
@@ -221,6 +232,13 @@ static void server_init(IRC_SERVER_REC *server)
g_free(cmd);
}
+ if (conn->sasl_mechanism != SASL_MECHANISM_NONE)
+ cap_toggle(server, "sasl", TRUE);
+
+ cap_toggle(server, "multi-prefix", TRUE);
+
+ irc_send_cmd_now(server, "CAP LS");
+
if (conn->password != NULL && *conn->password != '\0') {
/* send password */
cmd = g_strdup_printf("PASS %s", conn->password);
@@ -270,9 +288,8 @@ static void server_init(IRC_SERVER_REC *server)
/* prevent the queue from sending too early, we have a max cut off of 120 secs */
/* this will reset to 1 sec after we get the 001 event */
- g_get_current_time(&now);
- memcpy(&((IRC_SERVER_REC *)server)->wait_cmd, &now, sizeof(GTimeVal));
- ((IRC_SERVER_REC *)server)->wait_cmd.tv_sec += 120;
+ g_get_current_time(&server->wait_cmd);
+ g_time_val_add(&server->wait_cmd, 120 * G_USEC_PER_SEC);
}
SERVER_REC *irc_server_init_connect(SERVER_CONNECT_REC *conn)
@@ -293,7 +310,7 @@ SERVER_REC *irc_server_init_connect(SERVER_CONNECT_REC *conn)
if (server->connrec->port <= 0) {
server->connrec->port =
- server->connrec->use_ssl ? 6697 : 6667;
+ server->connrec->use_tls ? 6697 : 6667;
}
server->cmd_queue_speed = ircconn->cmd_queue_speed > 0 ?
@@ -311,7 +328,7 @@ SERVER_REC *irc_server_init_connect(SERVER_CONNECT_REC *conn)
ircconn->max_whois : DEFAULT_MAX_WHOIS;
server->max_msgs_in_cmd = ircconn->max_msgs > 0 ?
ircconn->max_msgs : DEFAULT_MAX_MSGS;
- server->connrec->use_ssl = conn->use_ssl;
+ server->connrec->use_tls = conn->use_tls;
modes_server_init(server);
@@ -321,6 +338,8 @@ SERVER_REC *irc_server_init_connect(SERVER_CONNECT_REC *conn)
void irc_server_connect(SERVER_REC *server)
{
+ g_return_if_fail(server != NULL);
+
if (!server_start_connect(server)) {
server_connect_unref(server->connrec);
g_free(server);
@@ -416,7 +435,18 @@ static void sig_disconnected(IRC_SERVER_REC *server)
server_redirect_destroy(tmp->next->data);
}
g_slist_free(server->cmdqueue);
- server->cmdqueue = NULL;
+ server->cmdqueue = NULL;
+
+ gslist_free_full(server->cap_active, (GDestroyNotify) g_free);
+ server->cap_active = NULL;
+
+ gslist_free_full(server->cap_supported, (GDestroyNotify) g_free);
+ server->cap_supported = NULL;
+
+ gslist_free_full(server->cap_queue, (GDestroyNotify) g_free);
+ server->cap_queue = NULL;
+
+ g_free_and_null(server->sasl_buffer);
/* these are dynamically allocated only if isupport was sent */
g_hash_table_foreach(server->isupport,
@@ -458,18 +488,14 @@ void irc_server_send_action(IRC_SERVER_REC *server, const char *target, const ch
char **irc_server_split_action(IRC_SERVER_REC *server, const char *target,
const char *data)
{
- int userhostlen = MAX_USERHOST_LEN;
-
g_return_val_if_fail(server != NULL, NULL);
g_return_val_if_fail(target != NULL, NULL);
g_return_val_if_fail(data != NULL, NULL);
- if (server->userhost != NULL)
- userhostlen = strlen(server->userhost);
-
return split_line(SERVER(server), data, target,
510 - strlen(":! PRIVMSG :\001ACTION \001") -
- strlen(server->nick) - userhostlen - strlen(target));
+ strlen(server->nick) - MAX_USERHOST_LEN -
+ strlen(target));
}
void irc_server_send_away(IRC_SERVER_REC *server, const char *reason)
@@ -484,9 +510,11 @@ void irc_server_send_away(IRC_SERVER_REC *server, const char *reason)
if (*reason != '\0') {
server->away_reason = g_strdup(reason);
reason = recoded = recode_out(SERVER(server), reason, NULL);
+ irc_send_cmdv(server, "AWAY :%s", reason);
+ } else {
+ irc_send_cmdv(server, "AWAY");
}
- irc_send_cmdv(server, "AWAY :%s", reason);
}
g_free(recoded);
}
@@ -509,7 +537,7 @@ void irc_server_send_data(IRC_SERVER_REC *server, const char *data, int len)
server->wait_cmd.tv_sec = 0;
else {
memcpy(&server->wait_cmd, &server->last_cmd, sizeof(GTimeVal));
- server->wait_cmd.tv_sec += 2 + len/100;
+ g_time_val_add(&server->wait_cmd, (2 + len/100) * G_USEC_PER_SEC);
}
}
@@ -597,9 +625,16 @@ char *irc_server_get_channels(IRC_SERVER_REC *server)
GString *chans, *keys;
char *ret;
int use_keys;
+ int rejoin_channels_mode;
g_return_val_if_fail(server != NULL, FALSE);
+ rejoin_channels_mode = settings_get_choice("rejoin_channels_on_reconnect");
+
+ /* do we want to rejoin channels in the first place? */
+ if(rejoin_channels_mode == 0)
+ return g_strdup("");
+
chans = g_string_new(NULL);
keys = g_string_new(NULL);
use_keys = FALSE;
@@ -607,22 +642,27 @@ char *irc_server_get_channels(IRC_SERVER_REC *server)
/* get currently joined channels */
for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
CHANNEL_REC *channel = tmp->data;
-
- g_string_append_printf(chans, "%s,", channel->name);
- g_string_append_printf(keys, "%s,", channel->key == NULL ? "x" :
- channel->key);
- if (channel->key != NULL)
- use_keys = TRUE;
+ CHANNEL_SETUP_REC *setup = channel_setup_find(channel->name, channel->server->connrec->chatnet);
+ if ((setup != NULL && setup->autojoin && rejoin_channels_mode == 2) || rejoin_channels_mode == 1) {
+ g_string_append_printf(chans, "%s,", channel->name);
+ g_string_append_printf(keys, "%s,", channel->key == NULL ? "x" : channel->key);
+ if (channel->key != NULL)
+ use_keys = TRUE;
+ }
}
/* get also the channels that are in rejoin list */
for (tmp = server->rejoin_channels; tmp != NULL; tmp = tmp->next) {
REJOIN_REC *rec = tmp->data;
+ CHANNEL_SETUP_REC *setup = channel_setup_find(rec->channel, server->tag);
+
+ if ((setup != NULL && setup->autojoin && rejoin_channels_mode == 2) || rejoin_channels_mode == 1) {
+ g_string_append_printf(chans, "%s,", rec->channel);
+ g_string_append_printf(keys, "%s,", rec->key == NULL ? "x" :
+ rec->key);
- g_string_append_printf(chans, "%s,", rec->channel);
- g_string_append_printf(keys, "%s,", rec->key == NULL ? "x" :
- rec->key);
- if (rec->key != NULL) use_keys = TRUE;
+ if (rec->key != NULL) use_keys = TRUE;
+ }
}
if (chans->len > 0) {
@@ -641,13 +681,12 @@ char *irc_server_get_channels(IRC_SERVER_REC *server)
static void event_connected(IRC_SERVER_REC *server, const char *data, const char *from)
{
char *params, *nick;
- GTimeVal now;
g_return_if_fail(server != NULL);
params = event_get_params(data, 1, &nick);
- if (strcmp(server->nick, nick) != 0) {
+ if (g_strcmp0(server->nick, nick) != 0) {
/* nick changed unexpectedly .. connected via proxy, etc. */
g_free(server->nick);
server->nick = g_strdup(nick);
@@ -664,8 +703,7 @@ static void event_connected(IRC_SERVER_REC *server, const char *data, const char
server->real_connect_time = time(NULL);
/* let the queue send now that we are identified */
- g_get_current_time(&now);
- memcpy(&server->wait_cmd, &now, sizeof(GTimeVal));
+ g_get_current_time(&server->wait_cmd);
if (server->connrec->usermode != NULL) {
/* Send the user mode, before the autosendcmd.
@@ -753,7 +791,7 @@ static void event_isupport(IRC_SERVER_REC *server, const char *data)
char **item, *sptr, *eptr;
char **isupport;
gpointer key, value;
-
+
g_return_if_fail(server != NULL);
server->isupport_sent = TRUE;
@@ -979,9 +1017,11 @@ void irc_server_init_isupport(IRC_SERVER_REC *server)
void irc_servers_init(void)
{
+ settings_add_choice("servers", "rejoin_channels_on_reconnect", 1, "off;on;auto");
settings_add_str("misc", "usermode", DEFAULT_USER_MODE);
settings_add_str("misc", "split_line_start", "");
settings_add_str("misc", "split_line_end", "");
+ settings_add_bool("misc", "split_line_on_space", TRUE);
settings_add_time("flood", "cmd_queue_speed", DEFAULT_CMD_QUEUE_SPEED);
settings_add_int("flood", "cmds_max_at_once", DEFAULT_CMDS_MAX_AT_ONCE);
diff --git a/src/irc/core/irc-servers.h b/src/irc/core/irc-servers.h
index 7e4eeabf..09f3f81d 100644
--- a/src/irc/core/irc-servers.h
+++ b/src/irc/core/irc-servers.h
@@ -27,6 +27,10 @@ struct _IRC_SERVER_CONNECT_REC {
char *usermode;
char *alternate_nick;
+ int sasl_mechanism;
+ char *sasl_username;
+ char *sasl_password;
+
int max_cmds_at_once;
int cmd_queue_speed;
int max_query_chans;
@@ -63,12 +67,21 @@ struct _IRC_SERVER_REC {
unsigned int nick_collision:1; /* We're just now being killed because of nick collision */
unsigned int motd_got:1; /* We've received MOTD */
unsigned int isupport_sent:1; /* Server has sent us an isupport reply */
+ unsigned int cap_complete:1; /* We've done the initial CAP negotiation */
+ unsigned int sasl_success:1; /* Did we authenticate successfully ? */
int max_kicks_in_cmd; /* max. number of people to kick with one /KICK command */
int max_modes_in_cmd; /* max. number of mode changes in one /MODE command */
int max_whois_in_cmd; /* max. number of nicks in one /WHOIS command */
int max_msgs_in_cmd; /* max. number of targets in one /MSG */
+ GSList *cap_supported; /* A list of caps supported by the server */
+ GSList *cap_active; /* A list of caps active for this session */
+ GSList *cap_queue; /* A list of caps to request on connection */
+
+ GString *sasl_buffer; /* Buffer used to reassemble a fragmented SASL payload */
+ guint sasl_timeout; /* Holds the source id of the running timeout */
+
/* Command sending queue */
int cmdcount; /* number of commands in `cmdqueue'. Can be more than
there actually is, to make flood control remember
diff --git a/src/irc/core/irc-session.c b/src/irc/core/irc-session.c
index bcb0a122..18e8e5c7 100644
--- a/src/irc/core/irc-session.c
+++ b/src/irc/core/irc-session.c
@@ -28,6 +28,8 @@
#include "irc-channels.h"
#include "irc-nicklist.h"
+#include "sasl.h"
+
struct _isupport_data { CONFIG_REC *config; CONFIG_NODE *node; };
static void session_isupport_foreach(char *key, char *value, struct _isupport_data *data)
@@ -65,11 +67,15 @@ static void sig_session_save_server(IRC_SERVER_REC *server, CONFIG_REC *config,
config_node_set_str(config, node, "away_reason", server->away_reason);
config_node_set_bool(config, node, "emode_known", server->emode_known);
+ config_node_set_int(config, node, "sasl_mechanism", server->connrec->sasl_mechanism);
+ config_node_set_str(config, node, "sasl_username", server->connrec->sasl_username);
+ config_node_set_str(config, node, "sasl_password", server->connrec->sasl_password);
+
config_node_set_bool(config, node, "isupport_sent", server->isupport_sent);
- isupport = config_node_section(node, "isupport", NODE_TYPE_BLOCK);
+ isupport = config_node_section(config, node, "isupport", NODE_TYPE_BLOCK);
isupport_data.config = config;
isupport_data.node = isupport;
-
+
g_hash_table_foreach(server->isupport, (GHFunc) session_isupport_foreach, &isupport_data);
}
@@ -90,12 +96,21 @@ static void sig_session_restore_server(IRC_SERVER_REC *server,
server->emode_known = config_node_get_bool(node, "emode_known", FALSE);
server->isupport_sent = config_node_get_bool(node, "isupport_sent", FALSE);
+ server->connrec->sasl_mechanism = config_node_get_int(node, "sasl_mechanism", SASL_MECHANISM_NONE);
+ /* The fields below might have been filled when loading the chatnet
+ * description from the config and we favor the content that's been saved
+ * in the session file over that. */
+ g_free(server->connrec->sasl_username);
+ server->connrec->sasl_username = g_strdup(config_node_get_str(node, "sasl_username", NULL));
+ g_free(server->connrec->sasl_password);
+ server->connrec->sasl_password = g_strdup(config_node_get_str(node, "sasl_password", NULL));
+
if (server->isupport == NULL) {
server->isupport = g_hash_table_new((GHashFunc) g_istr_hash,
(GCompareFunc) g_istr_equal);
}
- node = config_node_section(node, "isupport", -1);
+ node = config_node_section(NULL, node, "isupport", -1);
tmp = node == NULL ? NULL : config_node_first(node->value);
for (; tmp != NULL; tmp = config_node_next(tmp)) {
diff --git a/src/irc/core/irc.c b/src/irc/core/irc.c
index 509418b7..4dce3fcf 100644
--- a/src/irc/core/irc.c
+++ b/src/irc/core/irc.c
@@ -212,8 +212,12 @@ void irc_send_cmd_split(IRC_SERVER_REC *server, const char *cmd,
count = 0;
if (nickstr->len > 0)
g_string_truncate(nickstr, nickstr->len-1);
- irc_send_cmdv(server, post == NULL ? "%s %s" : "%s %s %s",
- pre, nickstr->str, post);
+
+ if (post == NULL)
+ irc_send_cmdv(server, "%s %s", pre, nickstr->str);
+ else
+ irc_send_cmdv(server, "%s %s %s", pre, nickstr->str, post);
+
g_string_truncate(nickstr, 0);
if (*tmp == NULL || tmp[1] == NULL)
diff --git a/src/irc/core/irc.h b/src/irc/core/irc.h
index de19e084..b5bd833a 100644
--- a/src/irc/core/irc.h
+++ b/src/irc/core/irc.h
@@ -22,12 +22,6 @@ typedef struct _REDIRECT_REC REDIRECT_REC;
#define isnickflag(server, a) \
(server->prefix[(int)(unsigned char) a] != '\0')
-#define ischannel(a) \
- ((a) == '#' || /* normal */ \
- (a) == '&' || /* local */ \
- (a) == '!' || /* secure */ \
- (a) == '+') /* modeless */
-
#define IS_IRC_ITEM(rec) (IS_IRC_CHANNEL(rec) || IS_IRC_QUERY(rec))
#define IRC_PROTOCOL (chat_protocol_lookup("IRC"))
diff --git a/src/irc/core/modes.c b/src/irc/core/modes.c
index 7054182f..cc3d0faf 100644
--- a/src/irc/core/modes.c
+++ b/src/irc/core/modes.c
@@ -78,7 +78,7 @@ void prefix_add(char prefixes[MAX_USER_PREFIXES+1], char newprefix, SERVER_REC *
if (*prefixlst == newprefix)
break; /* insert the new prefix here */
-
+
if (*prefixlst == prefixes[oldpos]) {
/* this prefix is present.
* the one we are inserting goes after it.
@@ -88,7 +88,7 @@ void prefix_add(char prefixes[MAX_USER_PREFIXES+1], char newprefix, SERVER_REC *
}
prefixlst++;
}
-
+
/* newpos is now the position in which we wish to insert the prefix */
newprefixes[newpos++] = newprefix;
@@ -274,7 +274,7 @@ void modes_type_b(IRC_CHANNEL_REC *channel, const char *setby, char type,
channel->key = g_strdup(arg);
}
}
-
+
mode_set_arg(channel->server, newmode, type, mode, arg, FALSE);
}
@@ -285,7 +285,7 @@ void modes_type_c(IRC_CHANNEL_REC *channel, const char *setby,
if (mode == 'l') {
channel->limit = type == '-' ? 0 : atoi(arg);
}
-
+
mode_set_arg(channel->server, newmode, type, mode, arg, FALSE);
}
@@ -398,7 +398,7 @@ void parse_channel_modes(IRC_CHANNEL_REC *channel, const char *setby,
old_key = NULL;
}
- if (strcmp(newmode->str, channel->mode) != 0) {
+ if (g_strcmp0(newmode->str, channel->mode) != 0) {
g_free(channel->mode);
channel->mode = g_strdup(newmode->str);
@@ -488,7 +488,7 @@ static void event_mode(IRC_SERVER_REC *server, const char *data,
params = event_get_params(data, 2 | PARAM_FLAG_GETREST,
&channel, &mode);
- if (!ischannel(*channel)) {
+ if (!server_ischannel(SERVER(server), channel)) {
/* user mode change */
parse_user_mode(server, mode);
} else {
@@ -536,7 +536,7 @@ static void sig_req_usermode_change(IRC_SERVER_REC *server, const char *data,
params = event_get_params(data, 2 | PARAM_FLAG_GETREST,
&target, &mode);
- if (!ischannel(*target)) {
+ if (!server_ischannel(SERVER(server), target)) {
/* we requested a user mode change, save this */
mode = modes_join(NULL, server->wanted_usermode, mode, FALSE);
g_free_not_null(server->wanted_usermode);
@@ -743,6 +743,7 @@ static char *get_nicks(IRC_SERVER_REC *server, WI_ITEM_REC *item,
g_hash_table_lookup(optlist, "yes") == NULL) {
/* too many matches */
g_string_free(str, TRUE);
+ g_strfreev(matches);
cmd_params_free(free_arg);
signal_emit("error command", 1,
@@ -756,7 +757,7 @@ static char *get_nicks(IRC_SERVER_REC *server, WI_ITEM_REC *item,
if (str->len > 0) g_string_truncate(str, str->len-1);
ret = str->str;
g_string_free(str, FALSE);
-
+ g_strfreev(matches);
cmd_params_free(free_arg);
*ret_channel = channel;
@@ -835,14 +836,14 @@ static void cmd_mode(const char *data, IRC_SERVER_REC *server,
if (*data == '+' || *data == '-') {
target = "*";
- if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_GETREST, &mode))
+ if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS, &mode))
return;
} else {
- if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &target, &mode))
+ if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS, &target, &mode))
return;
}
- if (strcmp(target, "*") == 0) {
+ if (g_strcmp0(target, "*") == 0) {
if (!IS_IRC_CHANNEL(channel))
cmd_param_error(CMDERR_NOT_JOINED);
@@ -856,7 +857,7 @@ static void cmd_mode(const char *data, IRC_SERVER_REC *server,
target = chanrec->name;
irc_send_cmdv(server, "MODE %s", target);
- } else if (ischannel(*target))
+ } else if (server_ischannel(SERVER(server), target))
channel_set_mode(server, target, mode);
else {
if (g_ascii_strcasecmp(target, server->nick) == 0) {
diff --git a/src/irc/core/sasl.c b/src/irc/core/sasl.c
new file mode 100644
index 00000000..1021bea4
--- /dev/null
+++ b/src/irc/core/sasl.c
@@ -0,0 +1,329 @@
+/*
+ fe-sasl.c : irssi
+
+ Copyright (C) 2015 The Lemon Man
+
+ 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "module.h"
+#include "misc.h"
+#include "settings.h"
+
+#include "irc-cap.h"
+#include "irc-servers.h"
+#include "sasl.h"
+
+/*
+ * Based on IRCv3 SASL Extension Specification:
+ * http://ircv3.net/specs/extensions/sasl-3.1.html
+ */
+#define AUTHENTICATE_CHUNK_SIZE 400 // bytes
+
+/*
+ * Maximum size to allow the buffer to grow to before the next fragment comes in. Note that
+ * due to the way fragmentation works, the maximum message size will actually be:
+ * floor(AUTHENTICATE_MAX_SIZE / AUTHENTICATE_CHUNK_SIZE) + AUTHENTICATE_CHUNK_SIZE - 1
+ */
+#define AUTHENTICATE_MAX_SIZE 8192 // bytes
+
+#define SASL_TIMEOUT (20 * 1000) // ms
+
+static gboolean sasl_timeout(IRC_SERVER_REC *server)
+{
+ /* The authentication timed out, we can't do much beside terminating it */
+ irc_send_cmd_now(server, "AUTHENTICATE *");
+ cap_finish_negotiation(server);
+
+ server->sasl_timeout = 0;
+ server->sasl_success = FALSE;
+
+ signal_emit("server sasl failure", 2, server, "The authentication timed out");
+
+ return FALSE;
+}
+
+static void sasl_start(IRC_SERVER_REC *server, const char *data, const char *from)
+{
+ IRC_SERVER_CONNECT_REC *conn;
+
+ conn = server->connrec;
+
+ switch (conn->sasl_mechanism) {
+ case SASL_MECHANISM_PLAIN:
+ irc_send_cmd_now(server, "AUTHENTICATE PLAIN");
+ break;
+
+ case SASL_MECHANISM_EXTERNAL:
+ irc_send_cmd_now(server, "AUTHENTICATE EXTERNAL");
+ break;
+ }
+ server->sasl_timeout = g_timeout_add(SASL_TIMEOUT, (GSourceFunc) sasl_timeout, server);
+}
+
+static void sasl_fail(IRC_SERVER_REC *server, const char *data, const char *from)
+{
+ char *params, *error;
+
+ /* Stop any pending timeout, if any */
+ if (server->sasl_timeout != 0) {
+ g_source_remove(server->sasl_timeout);
+ server->sasl_timeout = 0;
+ }
+
+ params = event_get_params(data, 2, NULL, &error);
+
+ server->sasl_success = FALSE;
+
+ signal_emit("server sasl failure", 2, server, error);
+
+ /* Terminate the negotiation */
+ cap_finish_negotiation(server);
+
+ g_free(params);
+}
+
+static void sasl_already(IRC_SERVER_REC *server, const char *data, const char *from)
+{
+ if (server->sasl_timeout != 0) {
+ g_source_remove(server->sasl_timeout);
+ server->sasl_timeout = 0;
+ }
+
+ server->sasl_success = TRUE;
+
+ signal_emit("server sasl success", 1, server);
+
+ /* We're already authenticated, do nothing */
+ cap_finish_negotiation(server);
+}
+
+static void sasl_success(IRC_SERVER_REC *server, const char *data, const char *from)
+{
+ if (server->sasl_timeout != 0) {
+ g_source_remove(server->sasl_timeout);
+ server->sasl_timeout = 0;
+ }
+
+ server->sasl_success = TRUE;
+
+ signal_emit("server sasl success", 1, server);
+
+ /* The authentication succeeded, time to finish the CAP negotiation */
+ cap_finish_negotiation(server);
+}
+
+/*
+ * Responsible for reassembling incoming SASL requests. SASL requests must be split
+ * into 400 byte requests to stay below the IRC command length limit of 512 bytes.
+ * The spec says that if there is 400 bytes, then there is expected to be a
+ * continuation in the next chunk. If a message is exactly a multiple of 400 bytes,
+ * there must be a blank message of "AUTHENTICATE +" to indicate the end.
+ *
+ * This function returns the fully reassembled and decoded AUTHENTICATION message if
+ * completed or NULL if there are more messages expected.
+ */
+static gboolean sasl_reassemble_incoming(IRC_SERVER_REC *server, const char *fragment, GString **decoded)
+{
+ GString *enc_req;
+ gsize fragment_len;
+
+ fragment_len = strlen(fragment);
+
+ /* Check if there is an existing fragment to prepend. */
+ if (server->sasl_buffer != NULL) {
+ if (g_strcmp0("+", fragment) == 0) {
+ enc_req = server->sasl_buffer;
+ } else {
+ enc_req = g_string_append_len(server->sasl_buffer, fragment, fragment_len);
+ }
+ server->sasl_buffer = NULL;
+ } else {
+ enc_req = g_string_new_len(fragment, fragment_len);
+ }
+
+ /*
+ * Fail authentication with this server. They have sent too much data.
+ */
+ if (enc_req->len > AUTHENTICATE_MAX_SIZE) {
+ return FALSE;
+ }
+
+ /*
+ * If the the request is exactly the chunk size, this is a fragment
+ * and more data is expected.
+ */
+ if (fragment_len == AUTHENTICATE_CHUNK_SIZE) {
+ server->sasl_buffer = enc_req;
+ return TRUE;
+ }
+
+ if (enc_req->len == 1 && *enc_req->str == '+') {
+ *decoded = g_string_new_len("", 0);
+ } else {
+ gsize dec_len;
+ gchar *tmp;
+
+ tmp = (gchar *) g_base64_decode(enc_req->str, &dec_len);
+ *decoded = g_string_new_len(tmp, dec_len);
+ }
+
+ g_string_free(enc_req, TRUE);
+ return TRUE;
+}
+
+/*
+ * Splits the response into appropriately sized chunks for the AUTHENTICATION
+ * command to be sent to the IRC server. If |response| is NULL, then the empty
+ * response is sent to the server.
+ */
+void sasl_send_response(IRC_SERVER_REC *server, GString *response)
+{
+ char *enc;
+ size_t offset, enc_len, chunk_len;
+
+ if (response == NULL) {
+ irc_send_cmdv(server, "AUTHENTICATE +");
+ return;
+ }
+
+ enc = g_base64_encode((guchar *) response->str, response->len);
+ enc_len = strlen(enc);
+
+ for (offset = 0; offset < enc_len; offset += AUTHENTICATE_CHUNK_SIZE) {
+ chunk_len = enc_len - offset;
+ if (chunk_len > AUTHENTICATE_CHUNK_SIZE)
+ chunk_len = AUTHENTICATE_CHUNK_SIZE;
+
+ irc_send_cmdv(server, "AUTHENTICATE %.*s", (int) chunk_len, enc + offset);
+ }
+
+ if (offset == enc_len) {
+ irc_send_cmdv(server, "AUTHENTICATE +");
+ }
+ g_free(enc);
+}
+
+/*
+ * Called when the incoming SASL request is completely received.
+ */
+static void sasl_step_complete(IRC_SERVER_REC *server, GString *data)
+{
+ IRC_SERVER_CONNECT_REC *conn;
+ GString *resp;
+
+ conn = server->connrec;
+
+ switch (conn->sasl_mechanism) {
+ case SASL_MECHANISM_PLAIN:
+ /* At this point we assume that conn->sasl_{username, password} are non-NULL.
+ * The PLAIN mechanism expects a NULL-separated string composed by the authorization identity, the
+ * authentication identity and the password.
+ * The authorization identity field is explicitly set to the user provided username.
+ */
+
+ resp = g_string_new(NULL);
+
+ g_string_append(resp, conn->sasl_username);
+ g_string_append_c(resp, '\0');
+ g_string_append(resp, conn->sasl_username);
+ g_string_append_c(resp, '\0');
+ g_string_append(resp, conn->sasl_password);
+
+ sasl_send_response(server, resp);
+ g_string_free(resp, TRUE);
+
+ break;
+
+ case SASL_MECHANISM_EXTERNAL:
+ /* Empty response */
+ sasl_send_response(server, NULL);
+ break;
+ }
+}
+
+static void sasl_step_fail(IRC_SERVER_REC *server)
+{
+ irc_send_cmd_now(server, "AUTHENTICATE *");
+ cap_finish_negotiation(server);
+
+ server->sasl_timeout = 0;
+
+ signal_emit("server sasl failure", 2, server, "The server sent an invalid payload");
+}
+
+static void sasl_step(IRC_SERVER_REC *server, const char *data, const char *from)
+{
+ GString *req = NULL;
+
+ /* Stop the timer */
+ if (server->sasl_timeout != 0) {
+ g_source_remove(server->sasl_timeout);
+ server->sasl_timeout = 0;
+ }
+
+ if (!sasl_reassemble_incoming(server, data, &req)) {
+ sasl_step_fail(server);
+ return;
+ }
+
+ if (req != NULL) {
+ sasl_step_complete(server, req);
+ g_string_free(req, TRUE);
+ }
+
+ /* We expect a response within a reasonable time */
+ server->sasl_timeout = g_timeout_add(SASL_TIMEOUT, (GSourceFunc) sasl_timeout, server);
+}
+
+static void sasl_disconnected(IRC_SERVER_REC *server)
+{
+ g_return_if_fail(server != NULL);
+
+ if (!IS_IRC_SERVER(server)) {
+ return;
+ }
+
+ if (server->sasl_timeout != 0) {
+ g_source_remove(server->sasl_timeout);
+ server->sasl_timeout = 0;
+ }
+}
+
+void sasl_init(void)
+{
+ signal_add_first("server cap ack sasl", (SIGNAL_FUNC) sasl_start);
+ signal_add_first("event authenticate", (SIGNAL_FUNC) sasl_step);
+ signal_add_first("event 903", (SIGNAL_FUNC) sasl_success);
+ signal_add_first("event 902", (SIGNAL_FUNC) sasl_fail);
+ signal_add_first("event 904", (SIGNAL_FUNC) sasl_fail);
+ signal_add_first("event 905", (SIGNAL_FUNC) sasl_fail);
+ signal_add_first("event 906", (SIGNAL_FUNC) sasl_fail);
+ signal_add_first("event 907", (SIGNAL_FUNC) sasl_already);
+ signal_add_first("server disconnected", (SIGNAL_FUNC) sasl_disconnected);
+}
+
+void sasl_deinit(void)
+{
+ signal_remove("server cap ack sasl", (SIGNAL_FUNC) sasl_start);
+ signal_remove("event authenticate", (SIGNAL_FUNC) sasl_step);
+ signal_remove("event 903", (SIGNAL_FUNC) sasl_success);
+ signal_remove("event 902", (SIGNAL_FUNC) sasl_fail);
+ signal_remove("event 904", (SIGNAL_FUNC) sasl_fail);
+ signal_remove("event 905", (SIGNAL_FUNC) sasl_fail);
+ signal_remove("event 906", (SIGNAL_FUNC) sasl_fail);
+ signal_remove("event 907", (SIGNAL_FUNC) sasl_already);
+ signal_remove("server disconnected", (SIGNAL_FUNC) sasl_disconnected);
+}
diff --git a/src/irc/core/sasl.h b/src/irc/core/sasl.h
new file mode 100644
index 00000000..0693989d
--- /dev/null
+++ b/src/irc/core/sasl.h
@@ -0,0 +1,34 @@
+/*
+ fe-sasl.c : irssi
+
+ Copyright (C) 2015 The Lemon Man
+
+ 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __SASL_H
+#define __SASL_H
+
+enum {
+ SASL_MECHANISM_NONE = 0,
+ SASL_MECHANISM_PLAIN,
+ SASL_MECHANISM_EXTERNAL,
+ SASL_MECHANISM_MAX
+};
+
+void sasl_init(void);
+void sasl_deinit(void);
+
+#endif
diff --git a/src/irc/core/servers-redirect.c b/src/irc/core/servers-redirect.c
index 34beaef6..aeb9c010 100644
--- a/src/irc/core/servers-redirect.c
+++ b/src/irc/core/servers-redirect.c
@@ -339,7 +339,7 @@ static GSList *redirect_cmd_list_find(GSList *list, const char *event)
while (list != NULL) {
const char *str = list->data;
- if (strcmp(str, event) == 0)
+ if (g_strcmp0(str, event) == 0)
break;
list = list->next->next;
}
@@ -365,7 +365,7 @@ static const char *redirect_match(REDIRECT_REC *redirect, const char *event,
use the default signal */
signal = NULL;
for (tmp = redirect->signals; tmp != NULL; tmp = tmp->next->next) {
- if (strcmp(tmp->data, event) == 0) {
+ if (g_strcmp0(tmp->data, event) == 0) {
signal = tmp->next->data;
break;
}
@@ -433,9 +433,11 @@ static void redirect_abort(IRC_SERVER_REC *server, REDIRECT_REC *rec)
if (rec->aborted || !rec->destroyed) {
/* emit the failure signal */
- str = g_strdup_printf(rec->failure_signal != NULL ?
- "FAILED %s: %s" : "FAILED %s",
- rec->cmd->name, rec->failure_signal);
+ if (rec->failure_signal != NULL)
+ str = g_strdup_printf("FAILED %s: %s", rec->cmd->name, rec->failure_signal);
+ else
+ str = g_strdup_printf("FAILED %s", rec->cmd->name);
+
rawlog_redirect(server->rawlog, str);
g_free(str);
@@ -527,7 +529,7 @@ server_redirect_get(IRC_SERVER_REC *server, const char *prefix,
next = ptr->next;
r = ptr->data;
if (prefix != NULL && r->prefix != NULL &&
- strcmp(prefix, r->prefix)) {
+ g_strcmp0(prefix, r->prefix)) {
/* not from this server */
continue;
}
@@ -734,7 +736,7 @@ void servers_redirect_init(void)
"event 403", 1, /* no such channel */
"event 442", 1, /* "you're not on that channel" */
"event 479", 1, /* "Cannot join channel (illegal name)" IMHO this is not a logical reply from server. */
- "event 472", -1, /* unknown mode (you should check e-mode's existance from 004 event instead of relying on this) */
+ "event 472", -1, /* unknown mode (you should check e-mode's existence from 004 event instead of relying on this) */
NULL,
NULL);
@@ -747,7 +749,7 @@ void servers_redirect_init(void)
"event 403", 1, /* no such channel */
"event 442", 1, /* "you're not on that channel" */
"event 479", 1, /* "Cannot join channel (illegal name)" IMHO this is not a logical reply from server. */
- "event 472", -1, /* unknown mode (you should check I-mode's existance from 004 event instead of relying on this) */
+ "event 472", -1, /* unknown mode (you should check I-mode's existence from 004 event instead of relying on this) */
NULL,
NULL);
diff --git a/src/irc/dcc/dcc-autoget.c b/src/irc/dcc/dcc-autoget.c
index 4768641b..de23a5d1 100644
--- a/src/irc/dcc/dcc-autoget.c
+++ b/src/irc/dcc/dcc-autoget.c
@@ -51,13 +51,13 @@ static void sig_dcc_request(GET_DCC_REC *dcc, const char *nickaddr)
/* Unless specifically said in dcc_autoget_masks, don't do autogets
sent to channels. */
- if (*masks == '\0' && dcc->target != NULL && ischannel(*dcc->target))
+ if (*masks == '\0' && dcc->target != NULL && server_ischannel(SERVER(dcc->server), dcc->target))
return;
/* don't autoget files beginning with a dot, if download dir is
our home dir (stupid kludge for stupid people) */
if (*dcc->arg == '.' &&
- strcmp(settings_get_str("dcc_download_path"), "~") == 0)
+ g_strcmp0(settings_get_str("dcc_download_path"), "~") == 0)
return;
/* check file size limit, NOTE: it's still possible to send a
diff --git a/src/irc/dcc/dcc-chat.c b/src/irc/dcc/dcc-chat.c
index ad1442fc..ca90b8d8 100644
--- a/src/irc/dcc/dcc-chat.c
+++ b/src/irc/dcc/dcc-chat.c
@@ -191,7 +191,7 @@ static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
return;
/* handle only DCC messages */
- if (strcmp(target, "*") == 0)
+ if (g_strcmp0(target, "*") == 0)
dcc = item_get_dcc(item);
else if (*target == '=')
dcc = dcc_chat_find_id(target+1);
@@ -428,7 +428,7 @@ static void dcc_chat_passive(CHAT_DCC_REC *dcc)
dcc_ip2str(&own_ip, host);
irc_send_cmdv(dcc->server, "PRIVMSG %s :\001DCC CHAT CHAT %s %d %d\001",
dcc->nick, host, port, dcc->pasv_id);
-
+
}
/* SYNTAX: DCC CHAT [-passive] [<nick>] */
@@ -615,17 +615,17 @@ static void ctcp_msg_dcc_chat(IRC_SERVER_REC *server, const char *data,
char **params;
int paramcount;
int passive, autoallow = FALSE;
-
+
/* CHAT <unused> <address> <port> */
/* CHAT <unused> <address> 0 <id> (DCC CHAT passive protocol) */
params = g_strsplit(data, " ", -1);
- paramcount = strarray_length(params);
+ paramcount = g_strv_length(params);
if (paramcount < 3) {
g_strfreev(params);
return;
}
- passive = paramcount == 4 && strcmp(params[2], "0") == 0;
+ passive = paramcount == 4 && g_strcmp0(params[2], "0") == 0;
dcc = DCC_CHAT(dcc_find_request(DCC_CHAT_TYPE, nick, NULL));
if (dcc != NULL) {
@@ -656,14 +656,14 @@ static void ctcp_msg_dcc_chat(IRC_SERVER_REC *server, const char *data,
}
}
}
-
+
dcc = dcc_chat_create(server, chat, nick, params[0]);
dcc->target = g_strdup(target);
dcc->port = atoi(params[2]);
-
+
if (passive)
dcc->pasv_id = atoi(params[3]);
-
+
dcc_str2ip(params[1], &dcc->addr);
net_ip2host(&dcc->addr, dcc->addrstr);
diff --git a/src/irc/dcc/dcc-get.c b/src/irc/dcc/dcc-get.c
index 8eee81b3..73c1b864 100644
--- a/src/irc/dcc/dcc-get.c
+++ b/src/irc/dcc/dcc-get.c
@@ -30,6 +30,8 @@
#include "dcc-get.h"
#include "dcc-send.h"
+static char *dcc_get_recv_buffer;
+
GET_DCC_REC *dcc_get_create(IRC_SERVER_REC *server, CHAT_DCC_REC *chat,
const char *nick, const char *arg)
{
@@ -139,14 +141,20 @@ static void sig_dccget_send(GET_DCC_REC *dcc)
dcc_get_send_received(dcc);
}
+#define DCC_GET_RECV_BUFFER_SIZE 32768
+
/* input function: DCC GET received data */
static void sig_dccget_receive(GET_DCC_REC *dcc)
{
- char buffer[512];
int ret;
+ if (dcc_get_recv_buffer == NULL) {
+ dcc_get_recv_buffer = g_malloc(DCC_GET_RECV_BUFFER_SIZE);
+ }
+
for (;;) {
- ret = net_receive(dcc->handle, buffer, sizeof(buffer));
+ ret = net_receive(dcc->handle, dcc_get_recv_buffer,
+ DCC_GET_RECV_BUFFER_SIZE);
if (ret == 0) break;
if (ret < 0) {
@@ -156,7 +164,7 @@ static void sig_dccget_receive(GET_DCC_REC *dcc)
return;
}
- if (write(dcc->fhandle, buffer, ret) != ret) {
+ if (write(dcc->fhandle, dcc_get_recv_buffer, ret) != ret) {
/* most probably out of disk space */
signal_emit("dcc error write", 2,
dcc, g_strerror(errno));
@@ -226,6 +234,8 @@ void sig_dccget_connected(GET_DCC_REC *dcc)
else
ret = fchmod(temphandle, dcc_file_create_mode);
+ close(temphandle);
+
if (ret != -1) {
ret = link(tempfname, dcc->file);
@@ -249,7 +259,6 @@ void sig_dccget_connected(GET_DCC_REC *dcc)
/* close/remove the temp file */
ret_errno = errno;
- close(temphandle);
unlink(tempfname);
g_free(tempfname);
@@ -422,7 +431,7 @@ static void ctcp_msg_dcc_send(IRC_SERVER_REC *server, const char *data,
/* SEND <file name> <address> <port> <size> [...] */
/* SEND <file name> <address> 0 <size> <id> (DCC SEND passive protocol) */
params = g_strsplit(data, " ", -1);
- paramcount = strarray_length(params);
+ paramcount = g_strv_length(params);
if (paramcount < 4) {
signal_emit("dcc error ctcp", 5, "SEND", data,
@@ -454,7 +463,7 @@ static void ctcp_msg_dcc_send(IRC_SERVER_REC *server, const char *data,
g_memmove(fname, fname+1, len);
quoted = TRUE;
}
-
+
if (passive && port != 0) {
/* This is NOT a DCC SEND request! This is a reply to our
passive request. We MUST check the IDs and then connect to
@@ -472,8 +481,8 @@ static void ctcp_msg_dcc_send(IRC_SERVER_REC *server, const char *data,
net_ip2host(&temp_dcc->addr, temp_dcc->addrstr);
else {
/* with IPv6, show it to us as it was sent */
- strocpy(temp_dcc->addrstr, address,
- sizeof(temp_dcc->addrstr));
+ g_strlcpy(temp_dcc->addrstr, address,
+ sizeof(temp_dcc->addrstr));
}
/* This new signal is added to let us invoke
@@ -501,13 +510,13 @@ static void ctcp_msg_dcc_send(IRC_SERVER_REC *server, const char *data,
if (passive && port == 0)
dcc->pasv_id = p_id; /* Assign the ID to the DCC */
-
+
memcpy(&dcc->addr, &ip, sizeof(ip));
if (dcc->addr.family == AF_INET)
net_ip2host(&dcc->addr, dcc->addrstr);
else {
/* with IPv6, show it to us as it was sent */
- strocpy(dcc->addrstr, address, sizeof(dcc->addrstr));
+ g_strlcpy(dcc->addrstr, address, sizeof(dcc->addrstr));
}
dcc->port = port;
dcc->size = size;
@@ -525,14 +534,14 @@ void cmd_dcc_receive(const char *data, DCC_GET_FUNC accept_func,
{
GET_DCC_REC *dcc;
GSList *tmp, *next;
- char *nick, *fname;
+ char *nick, *arg, *fname;
void *free_arg;
int found;
g_return_if_fail(data != NULL);
- if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST,
- &nick, &fname))
+ if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST |
+ PARAM_FLAG_STRIP_TRAILING_WS, &nick, &arg))
return;
if (*nick == '\0') {
@@ -547,6 +556,8 @@ void cmd_dcc_receive(const char *data, DCC_GET_FUNC accept_func,
return;
}
+ fname = cmd_get_quoted_param(&arg);
+
found = FALSE;
for (tmp = dcc_conns; tmp != NULL; tmp = next) {
GET_DCC_REC *dcc = tmp->data;
@@ -554,7 +565,7 @@ void cmd_dcc_receive(const char *data, DCC_GET_FUNC accept_func,
next = tmp->next;
if (IS_DCC_GET(dcc) && g_ascii_strcasecmp(dcc->nick, nick) == 0 &&
(dcc_is_waiting_user(dcc) || dcc->from_dccserver) &&
- (*fname == '\0' || strcmp(dcc->arg, fname) == 0)) {
+ (*fname == '\0' || g_strcmp0(dcc->arg, fname) == 0)) {
found = TRUE;
if (!dcc_is_passive(dcc))
accept_func(dcc);
@@ -593,4 +604,5 @@ void dcc_get_deinit(void)
signal_remove("dcc destroyed", (SIGNAL_FUNC) sig_dcc_destroyed);
signal_remove("ctcp msg dcc send", (SIGNAL_FUNC) ctcp_msg_dcc_send);
command_unbind("dcc get", (SIGNAL_FUNC) cmd_dcc_get);
+ g_free_and_null(dcc_get_recv_buffer);
}
diff --git a/src/irc/dcc/dcc-resume.c b/src/irc/dcc/dcc-resume.c
index 28871986..36f84ddf 100644
--- a/src/irc/dcc/dcc-resume.c
+++ b/src/irc/dcc/dcc-resume.c
@@ -88,13 +88,13 @@ static int dcc_ctcp_resume_parse(int type, const char *data, const char *nick,
/* RESUME|ACCEPT <file name> <port> <size> */
/* RESUME|ACCEPT <file name> 0 <size> <id> (passive protocol) */
params = g_strsplit(data, " ", -1);
- paramcount = strarray_length(params);
+ paramcount = g_strv_length(params);
if (paramcount < 3)
return 0;
fileparams = get_file_params_count_resume(params, paramcount);
-
+
if (paramcount >= fileparams + 2) {
port = atoi(params[fileparams]);
*size = str_to_uofft(params[fileparams+1]);
diff --git a/src/irc/dcc/dcc-send.c b/src/irc/dcc/dcc-send.c
index 558fd109..ca29b9b9 100644
--- a/src/irc/dcc/dcc-send.c
+++ b/src/irc/dcc/dcc-send.c
@@ -174,8 +174,8 @@ static void cmd_dcc_send(const char *data, IRC_SERVER_REC *server,
int queue, mode, passive;
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS |
- PARAM_FLAG_GETREST, "dcc send",
- &optlist, &nick, &fileargs))
+ PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
+ "dcc send", &optlist, &nick, &fileargs))
return;
chat = item_get_dcc(item);
@@ -194,7 +194,7 @@ static void cmd_dcc_send(const char *data, IRC_SERVER_REC *server,
cmd_param_error(CMDERR_NOT_CONNECTED);
passive = g_hash_table_lookup(optlist, "passive") != NULL;
-
+
if (g_hash_table_lookup(optlist, "rmhead") != NULL) {
queue = dcc_queue_old(nick, servertag);
if (queue != -1)
@@ -217,7 +217,7 @@ static void cmd_dcc_send(const char *data, IRC_SERVER_REC *server,
if (*fileargs == '\0')
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
-
+
dcc_send_add(servertag, chat, nick, fileargs, mode, passive);
}
@@ -434,11 +434,11 @@ static int dcc_send_one_file(int queue, const char *target, const char *fname,
if (passive) {
dcc->pasv_id = rand() % 64;
}
-
+
/* send DCC request */
signal_emit("dcc request send", 1, dcc);
-
+
dcc_ip2str(&own_ip, host);
if (passive == FALSE) {
str = g_strdup_printf(dcc->file_quoted ?
diff --git a/src/irc/dcc/dcc-server.c b/src/irc/dcc/dcc-server.c
index 30224ff9..7ae572cd 100644
--- a/src/irc/dcc/dcc-server.c
+++ b/src/irc/dcc/dcc-server.c
@@ -245,7 +245,7 @@ static void dcc_server_msg(SERVER_DCC_REC *dcc, const char *msg)
/* 120 clientnickname filesize filename */
params = g_strsplit(msg, " ", -1);
- paramcount = strarray_length(params);
+ paramcount = g_strv_length(params);
if (paramcount < 3) {
g_strfreev(params);
diff --git a/src/irc/dcc/dcc.c b/src/irc/dcc/dcc.c
index e4ee4b7c..c51f7c7a 100644
--- a/src/irc/dcc/dcc.c
+++ b/src/irc/dcc/dcc.c
@@ -90,9 +90,9 @@ void dcc_init_rec(DCC_REC *dcc, IRC_SERVER_REC *server, CHAT_DCC_REC *chat,
dcc->servertag = server != NULL ? g_strdup(server->tag) :
(chat == NULL ? NULL : g_strdup(chat->servertag));
-
+
dcc->pasv_id = -1; /* Not a passive DCC */
-
+
dcc_conns = g_slist_append(dcc_conns, dcc);
signal_emit("dcc created", 1, dcc);
}
@@ -149,7 +149,7 @@ DCC_REC *dcc_find_request(int type, const char *nick, const char *arg)
if (dcc->type == type && !dcc_is_connected(dcc) &&
g_ascii_strcasecmp(dcc->nick, nick) == 0 &&
- (arg == NULL || strcmp(dcc->arg, arg) == 0))
+ (arg == NULL || g_strcmp0(dcc->arg, arg) == 0))
return dcc;
}
@@ -454,8 +454,8 @@ static int dcc_timeout_func(void)
/* Timed out - don't send DCC REJECT CTCP so CTCP
flooders won't affect us and it really doesn't
matter that much anyway if the other side doen't
- get it..
-
+ get it..
+
We don't want dcc servers to time out. */
dcc_close(dcc);
}
@@ -490,7 +490,7 @@ static void event_no_such_nick(IRC_SERVER_REC *server, char *data)
static void cmd_dcc_close(char *data, IRC_SERVER_REC *server)
{
GSList *tmp, *next;
- char *typestr, *nick, *arg;
+ char *typestr, *nick, *arg, *fname;
void *free_arg;
int found, type;
@@ -510,13 +510,15 @@ static void cmd_dcc_close(char *data, IRC_SERVER_REC *server)
return;
}
+ fname = cmd_get_quoted_param(&arg);
+
found = FALSE;
for (tmp = dcc_conns; tmp != NULL; tmp = next) {
DCC_REC *dcc = tmp->data;
next = tmp->next;
if (dcc->type == type && g_ascii_strcasecmp(dcc->nick, nick) == 0 &&
- (*arg == '\0' || strcmp(dcc->arg, arg) == 0)) {
+ (*fname == '\0' || g_strcmp0(dcc->arg, fname) == 0)) {
dcc_reject(dcc, server);
found = TRUE;
}
diff --git a/src/irc/dcc/dcc.h b/src/irc/dcc/dcc.h
index 10639207..144cc89b 100644
--- a/src/irc/dcc/dcc.h
+++ b/src/irc/dcc/dcc.h
@@ -27,7 +27,7 @@ typedef struct {
/* passive DCC */
#define dcc_is_passive(dcc) \
((dcc)->pasv_id >= 0)
-
+
extern GSList *dcc_conns;
void dcc_register_type(const char *type);
diff --git a/src/irc/flood/autoignore.c b/src/irc/flood/autoignore.c
index 94adf167..86ff3ec5 100644
--- a/src/irc/flood/autoignore.c
+++ b/src/irc/flood/autoignore.c
@@ -38,7 +38,7 @@ void autoignore_update(IGNORE_REC *rec, int level)
ignore_update_rec(rec);
}
-void autoignore_add(IRC_SERVER_REC *server, char *mask, int level)
+void autoignore_add(IRC_SERVER_REC *server, char *mask, int level)
{
IGNORE_REC *rec;
@@ -66,7 +66,7 @@ static void sig_flood(IRC_SERVER_REC *server, const char *nick, const char *host
mask = g_strdup_printf("%s!%s", nick, host);
if (level & check_level) {
- rec = ignore_find(server->tag, mask, NULL);
+ rec = ignore_find_full(server->tag, mask, NULL, NULL, 0);
if (rec == NULL)
autoignore_add(server, mask, level);
else
diff --git a/src/irc/flood/flood.c b/src/irc/flood/flood.c
index 29502ca6..0944a6eb 100644
--- a/src/irc/flood/flood.c
+++ b/src/irc/flood/flood.c
@@ -250,7 +250,7 @@ static void flood_privmsg(IRC_SERVER_REC *server, const char *data,
params = event_get_params(data, 2, &target, &text);
- level = ischannel(*target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS;
+ level = server_ischannel(SERVER(server), target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS;
if (addr != NULL && !ignore_check(SERVER(server), nick, addr, target, text, level))
flood_newmsg(server, level, nick, addr, target);
@@ -287,7 +287,7 @@ static void flood_ctcp(IRC_SERVER_REC *server, const char *data,
return;
level = g_ascii_strncasecmp(data, "ACTION ", 7) != 0 ? MSGLEVEL_CTCPS :
- (ischannel(*target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS);
+ (server_ischannel(SERVER(server), target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS);
if (!ignore_check(SERVER(server), nick, addr, target, data, level))
flood_newmsg(server, level, nick, addr, target);
}
diff --git a/src/irc/notifylist/notify-commands.c b/src/irc/notifylist/notify-commands.c
index 67076106..0d4fd4f2 100644
--- a/src/irc/notifylist/notify-commands.c
+++ b/src/irc/notifylist/notify-commands.c
@@ -36,7 +36,8 @@ static void cmd_notify(gchar *data)
g_return_if_fail(data != NULL);
- if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS | PARAM_FLAG_GETREST,
+ if (!cmd_get_params(data, &free_arg,
+ 2 | PARAM_FLAG_OPTIONS | PARAM_FLAG_GETREST | PARAM_FLAG_STRIP_TRAILING_WS,
"notify", &optlist, &mask, &ircnets))
return;
if (*mask == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
diff --git a/src/irc/notifylist/notify-setup.c b/src/irc/notifylist/notify-setup.c
index d6f91361..9ea481ca 100644
--- a/src/irc/notifylist/notify-setup.c
+++ b/src/irc/notifylist/notify-setup.c
@@ -30,7 +30,7 @@ void notifylist_add_config(NOTIFYLIST_REC *rec)
CONFIG_NODE *node;
node = iconfig_node_traverse("notifies", TRUE);
- node = config_node_section(node, rec->mask, NODE_TYPE_BLOCK);
+ node = iconfig_node_section(node, rec->mask, NODE_TYPE_BLOCK);
if (rec->away_check)
iconfig_node_set_bool(node, "away_check", TRUE);
@@ -39,7 +39,7 @@ void notifylist_add_config(NOTIFYLIST_REC *rec)
iconfig_node_set_str(node, "ircnets", NULL);
if (rec->ircnets != NULL && *rec->ircnets != NULL) {
- node = config_node_section(node, "ircnets", NODE_TYPE_LIST);
+ node = iconfig_node_section(node, "ircnets", NODE_TYPE_LIST);
iconfig_node_add_list(node, rec->ircnets);
}
}
@@ -73,7 +73,7 @@ void notifylist_read_config(void)
rec->mask = g_strdup(node->key);
rec->away_check = config_node_get_bool(node, "away_check", FALSE);
- node = config_node_section(node, "ircnets", -1);
+ node = iconfig_node_section(node, "ircnets", -1);
if (node != NULL) rec->ircnets = config_node_get_list(node);
}
}
diff --git a/src/irc/notifylist/notifylist.c b/src/irc/notifylist/notifylist.c
index b0b7ee1c..573f7a7f 100644
--- a/src/irc/notifylist/notifylist.c
+++ b/src/irc/notifylist/notifylist.c
@@ -91,7 +91,7 @@ int notifylist_ircnets_match(NOTIFYLIST_REC *rec, const char *ircnet)
if (rec->ircnets == NULL) return TRUE;
if (ircnet == NULL) return FALSE;
- if (strcmp(ircnet, "*") == 0) return TRUE;
+ if (g_strcmp0(ircnet, "*") == 0) return TRUE;
for (tmp = rec->ircnets; *tmp != NULL; tmp++) {
if (g_ascii_strcasecmp(*tmp, ircnet) == 0)
diff --git a/src/irc/proxy/dump.c b/src/irc/proxy/dump.c
index 80fd7c2e..e39c21a6 100644
--- a/src/irc/proxy/dump.c
+++ b/src/irc/proxy/dump.c
@@ -83,7 +83,7 @@ void proxy_outserver(CLIENT_REC *client, const char *data, ...)
va_start(args, data);
str = g_strdup_vprintf(data, args);
- proxy_outdata(client, ":%s!%s@proxy %s\n", client->nick,
+ proxy_outdata(client, ":%s!%s@proxy %s\r\n", client->nick,
settings_get_str("user_name"), str);
g_free(str);
@@ -106,7 +106,7 @@ void proxy_outserver_all(IRC_SERVER_REC *server, const char *data, ...)
CLIENT_REC *rec = tmp->data;
if (rec->connected && rec->server == server) {
- proxy_outdata(rec, ":%s!%s@proxy %s\n", rec->nick,
+ proxy_outdata(rec, ":%s!%s@proxy %s\r\n", rec->nick,
settings_get_str("user_name"), str);
}
}
@@ -132,7 +132,7 @@ void proxy_outserver_all_except(CLIENT_REC *client, const char *data, ...)
if (rec->connected && rec != client &&
rec->server == client->server) {
- proxy_outdata(rec, ":%s!%s@proxy %s\n", rec->nick,
+ proxy_outdata(rec, ":%s!%s@proxy %s\r\n", rec->nick,
settings_get_str("user_name"), str);
}
}
@@ -169,7 +169,7 @@ static void dump_join(IRC_CHANNEL_REC *channel, CLIENT_REC *client)
NICK_REC *nick = tmp->data;
if (str->len >= 500) {
- g_string_append_c(str, '\n');
+ g_string_append(str, "\r\n");
proxy_outdata(client, "%s", str->str);
create_names_start(str, channel, client);
first = TRUE;
@@ -186,21 +186,21 @@ static void dump_join(IRC_CHANNEL_REC *channel, CLIENT_REC *client)
}
g_slist_free(nicks);
- g_string_append_c(str, '\n');
+ g_string_append(str, "\r\n");
proxy_outdata(client, "%s", str->str);
g_string_free(str, TRUE);
- proxy_outdata(client, ":%s 366 %s %s :End of /NAMES list.\n",
+ proxy_outdata(client, ":%s 366 %s %s :End of /NAMES list.\r\n",
client->proxy_address, client->nick, channel->name);
if (channel->topic != NULL) {
/* this is needed because the topic may be encoded into other charsets internaly */
recoded = recode_out(SERVER(client->server), channel->topic, channel->name);
- proxy_outdata(client, ":%s 332 %s %s :%s\n",
+ proxy_outdata(client, ":%s 332 %s %s :%s\r\n",
client->proxy_address, client->nick,
channel->name, recoded);
g_free(recoded);
if (channel->topic_time > 0)
- proxy_outdata(client, ":%s 333 %s %s %s %d\n",
+ proxy_outdata(client, ":%s 333 %s %s %s %d\r\n",
client->proxy_address, client->nick,
channel->name, channel->topic_by, channel->topic_time);
}
@@ -209,10 +209,10 @@ static void dump_join(IRC_CHANNEL_REC *channel, CLIENT_REC *client)
void proxy_client_reset_nick(CLIENT_REC *client)
{
if (client->server == NULL ||
- strcmp(client->nick, client->server->nick) == 0)
+ g_strcmp0(client->nick, client->server->nick) == 0)
return;
- proxy_outdata(client, ":%s!proxy NICK :%s\n",
+ proxy_outdata(client, ":%s!proxy NICK :%s\r\n",
client->nick, client->server->nick);
g_free(client->nick);
@@ -236,13 +236,13 @@ void proxy_dump_data(CLIENT_REC *client)
proxy_client_reset_nick(client);
/* welcome info */
- proxy_outdata(client, ":%s 001 %s :Welcome to the Internet Relay Network %s!%s@proxy\n", client->proxy_address, client->nick, client->nick, settings_get_str("user_name"));
- proxy_outdata(client, ":%s 002 %s :Your host is irssi-proxy, running version %s\n", client->proxy_address, client->nick, PACKAGE_VERSION);
- proxy_outdata(client, ":%s 003 %s :This server was created ...\n", client->proxy_address, client->nick);
+ proxy_outdata(client, ":%s 001 %s :Welcome to the Internet Relay Network %s!%s@proxy\r\n", client->proxy_address, client->nick, client->nick, settings_get_str("user_name"));
+ proxy_outdata(client, ":%s 002 %s :Your host is irssi-proxy, running version %s\r\n", client->proxy_address, client->nick, PACKAGE_VERSION);
+ proxy_outdata(client, ":%s 003 %s :This server was created ...\r\n", client->proxy_address, client->nick);
if (client->server == NULL || !client->server->emode_known)
- proxy_outdata(client, ":%s 004 %s %s %s oirw abiklmnopqstv\n", client->proxy_address, client->nick, client->proxy_address, PACKAGE_VERSION);
+ proxy_outdata(client, ":%s 004 %s %s %s oirw abiklmnopqstv\r\n", client->proxy_address, client->nick, client->proxy_address, PACKAGE_VERSION);
else
- proxy_outdata(client, ":%s 004 %s %s %s oirw abeIiklmnopqstv\n", client->proxy_address, client->nick, client->proxy_address, PACKAGE_VERSION);
+ proxy_outdata(client, ":%s 004 %s %s %s oirw abeIiklmnopqstv\r\n", client->proxy_address, client->nick, client->proxy_address, PACKAGE_VERSION);
if (client->server != NULL && client->server->isupport_sent) {
isupport_out = g_string_new(NULL);
@@ -267,7 +267,7 @@ void proxy_dump_data(CLIENT_REC *client)
count = 0;
if (paramstr->len > 0)
g_string_truncate(paramstr, paramstr->len-1);
- g_string_append_printf(paramstr, " :are supported by this server\n");
+ g_string_append_printf(paramstr, " :are supported by this server\r\n");
proxy_outdata(client, "%s", paramstr->str);
g_string_truncate(paramstr, 0);
g_string_printf(paramstr, ":%s 005 %s ", client->proxy_address, client->nick);
@@ -281,9 +281,9 @@ void proxy_dump_data(CLIENT_REC *client)
g_strfreev(paramlist);
}
- proxy_outdata(client, ":%s 251 %s :There are 0 users and 0 invisible on 1 servers\n", client->proxy_address, client->nick);
- proxy_outdata(client, ":%s 255 %s :I have 0 clients, 0 services and 0 servers\n", client->proxy_address, client->nick);
- proxy_outdata(client, ":%s 422 %s :MOTD File is missing\n", client->proxy_address, client->nick);
+ proxy_outdata(client, ":%s 251 %s :There are 0 users and 0 invisible on 1 servers\r\n", client->proxy_address, client->nick);
+ proxy_outdata(client, ":%s 255 %s :I have 0 clients, 0 services and 0 servers\r\n", client->proxy_address, client->nick);
+ proxy_outdata(client, ":%s 422 %s :MOTD File is missing\r\n", client->proxy_address, client->nick);
/* user mode / away status */
if (client->server != NULL) {
@@ -293,7 +293,7 @@ void proxy_dump_data(CLIENT_REC *client)
client->server->usermode);
}
if (client->server->usermode_away) {
- proxy_outdata(client, ":%s 306 %s :You have been marked as being away\n",
+ proxy_outdata(client, ":%s 306 %s :You have been marked as being away\r\n",
client->proxy_address, client->nick);
}
diff --git a/src/irc/proxy/listen.c b/src/irc/proxy/listen.c
index 8edffbd3..cde4c0be 100644
--- a/src/irc/proxy/listen.c
+++ b/src/irc/proxy/listen.c
@@ -31,12 +31,76 @@
#include "fe-common/core/printtext.h" /* FIXME: evil. need to do fe-proxy */
+#include <sys/un.h>
+
GSList *proxy_listens;
GSList *proxy_clients;
static GString *next_line;
static int ignore_next;
+static int enabled = FALSE;
+
+static int is_all_digits(const char *s)
+{
+ return strspn(s, "0123456789") == strlen(s);
+}
+
+static GIOChannel *net_listen_unix(const char *path)
+{
+ struct sockaddr_un sa;
+ int saved_errno, handle;
+
+ g_return_val_if_fail(path != NULL, NULL);
+
+ handle = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (handle == -1) {
+ return NULL;
+ }
+
+ fcntl(handle, F_SETFL, O_NONBLOCK);
+
+ memset(&sa, '\0', sizeof sa);
+ sa.sun_family = AF_UNIX;
+ strncpy(sa.sun_path, path, sizeof sa.sun_path - 1);
+ if (bind(handle, (struct sockaddr *)&sa, sizeof sa) == -1) {
+ saved_errno = errno;
+ goto error_close;
+ }
+
+ if (listen(handle, 1) == -1) {
+ saved_errno = errno;
+ goto error_unlink;
+ }
+
+ return g_io_channel_new(handle);
+
+error_unlink:
+ unlink(sa.sun_path);
+error_close:
+ close(handle);
+ errno = saved_errno;
+ return NULL;
+}
+
+static GIOChannel *net_accept_unix(GIOChannel *handle)
+{
+ struct sockaddr_un sa;
+ int ret;
+ socklen_t addrlen;
+
+ g_return_val_if_fail(handle != NULL, NULL);
+
+ addrlen = sizeof sa;
+ ret = accept(g_io_channel_unix_get_fd(handle), (struct sockaddr *)&sa, &addrlen);
+
+ if (ret < 0)
+ return NULL;
+
+ fcntl(ret, F_SETFL, O_NONBLOCK);
+ return g_io_channel_new(ret);
+}
+
static void remove_client(CLIENT_REC *rec)
{
g_return_if_fail(rec != NULL);
@@ -45,19 +109,19 @@ static void remove_client(CLIENT_REC *rec)
rec->listen->clients = g_slist_remove(rec->listen->clients, rec);
signal_emit("proxy client disconnected", 1, rec);
- printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
- "Proxy: Client disconnected from %s", rec->host);
+ printtext(rec->server, NULL, MSGLEVEL_CLIENTNOTICE,
+ "Proxy: Client %s disconnected", rec->addr);
g_free(rec->proxy_address);
net_sendbuffer_destroy(rec->handle, TRUE);
g_source_remove(rec->recv_tag);
g_free_not_null(rec->nick);
- g_free_not_null(rec->host);
+ g_free_not_null(rec->addr);
g_free(rec);
}
static void proxy_redirect_event(CLIENT_REC *client, const char *command,
- int count, const char *arg, int remote)
+ int count, const char *arg, int remote)
{
char *str;
@@ -65,7 +129,7 @@ static void proxy_redirect_event(CLIENT_REC *client, const char *command,
str = g_strdup_printf("proxy %p", client);
server_redirect_event(client->server, command, count,
- arg, remote, NULL, "", str, NULL);
+ arg, remote, NULL, "", str, NULL);
g_free(str);
}
@@ -82,7 +146,7 @@ static void grab_who(CLIENT_REC *client, const char *channel)
arg = g_string_new(channel);
for (tmp = list, count = 0; *tmp != NULL; tmp++, count++) {
- if (strcmp(*tmp, "0") == 0) {
+ if (g_strcmp0(*tmp, "0") == 0) {
/* /who 0 displays everyone */
**tmp = '*';
}
@@ -92,40 +156,74 @@ static void grab_who(CLIENT_REC *client, const char *channel)
}
proxy_redirect_event(client, "who",
- client->server->one_endofwho ? 1 : count,
- arg->str, -1);
+ client->server->one_endofwho ? 1 : count,
+ arg->str, -1);
g_strfreev(list);
g_string_free(arg, TRUE);
}
static void handle_client_connect_cmd(CLIENT_REC *client,
- const char *cmd, const char *args)
+ const char *cmd, const char *args)
{
const char *password;
password = settings_get_str("irssiproxy_password");
- if (password != NULL && strcmp(cmd, "PASS") == 0) {
- if (strcmp(password, args) == 0)
- client->pass_sent = TRUE;
- else {
+ if (g_strcmp0(cmd, "PASS") == 0) {
+ const char *args_pass;
+
+ if (!client->multiplex) {
+ args_pass = args;
+ } else {
+ IRC_SERVER_REC *server;
+ char *tag;
+ const char *tag_end;
+
+ if ((tag_end = strchr(args, ':')) != NULL) {
+ args_pass = tag_end + 1;
+ } else {
+ tag_end = args + strlen(args);
+ args_pass = "";
+ }
+
+ tag = g_strndup(args, tag_end - args);
+ server = IRC_SERVER(server_find_chatnet(tag));
+ g_free(tag);
+
+ if (!server) {
+ /* an invalid network was specified */
+ remove_client(client);
+ return;
+ }
+
+ client->server = server;
+ g_free(client->proxy_address);
+ client->proxy_address = g_strdup_printf("%.*s.proxy", (int)(tag_end - args), args);
+ }
+
+ if (g_strcmp0(password, args_pass) != 0) {
/* wrong password! */
remove_client(client);
- return;
+ return;
}
- } else if (strcmp(cmd, "NICK") == 0) {
+ client->pass_sent = TRUE;
+ } else if (g_strcmp0(cmd, "NICK") == 0) {
g_free_not_null(client->nick);
client->nick = g_strdup(args);
- } else if (strcmp(cmd, "USER") == 0) {
+ } else if (g_strcmp0(cmd, "USER") == 0) {
client->user_sent = TRUE;
}
if (client->nick != NULL && client->user_sent) {
- if (*password != '\0' && !client->pass_sent) {
+ if ((*password != '\0' || client->multiplex) && !client->pass_sent) {
/* client didn't send us PASS, kill it */
remove_client(client);
} else {
+ signal_emit("proxy client connected", 1, client);
+ printtext(client->server, NULL, MSGLEVEL_CLIENTNOTICE,
+ "Proxy: Client %s connected",
+ client->addr);
client->connected = TRUE;
proxy_dump_data(client);
}
@@ -133,7 +231,7 @@ static void handle_client_connect_cmd(CLIENT_REC *client,
}
static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
- const char *data)
+ const char *data)
{
GSList *tmp;
if (!client->connected) {
@@ -141,12 +239,12 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
return;
}
- if (strcmp(cmd, "QUIT") == 0) {
+ if (g_strcmp0(cmd, "QUIT") == 0) {
remove_client(client);
return;
}
- if (strcmp(cmd, "PING") == 0) {
+ if (g_strcmp0(cmd, "PING") == 0) {
/* Reply to PING, if the target parameter is either
proxy_adress, our own nick or empty. */
char *params, *origin, *target;
@@ -155,38 +253,38 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
if (*target == '\0' ||
g_ascii_strcasecmp(target, client->proxy_address) == 0 ||
g_ascii_strcasecmp(target, client->nick) == 0) {
- proxy_outdata(client, ":%s PONG %s :%s\n",
- client->proxy_address,
- client->proxy_address, origin);
+ proxy_outdata(client, ":%s PONG %s :%s\r\n",
+ client->proxy_address,
+ client->proxy_address, origin);
g_free(params);
return;
}
g_free(params);
}
- if (strcmp(cmd, "PROXY") == 0) {
+ if (g_strcmp0(cmd, "PROXY") == 0) {
if (g_ascii_strcasecmp(args, "CTCP ON") == 0) {
- /* client wants all ctcps */
+ /* client wants all ctcps */
client->want_ctcp = 1;
- for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
+ for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
CLIENT_REC *rec = tmp->data;
- if ((g_ascii_strcasecmp(client->listen->ircnet,rec->listen->ircnet) == 0) &&
- /* kludgy way to check if the clients aren't the same */
- (client->recv_tag != rec->recv_tag)) {
- if (rec->want_ctcp == 1)
- proxy_outdata(rec, ":%s NOTICE %s :Another client is now receiving CTCPs sent to %s\n",
- rec->proxy_address, rec->nick, rec->listen->ircnet);
- rec->want_ctcp = 0;
- }
-
+ if (g_ascii_strcasecmp(client->listen->ircnet, rec->listen->ircnet) == 0 &&
+ /* kludgy way to check if the clients aren't the same */
+ client->recv_tag != rec->recv_tag) {
+ if (rec->want_ctcp == 1)
+ proxy_outdata(rec, ":%s NOTICE %s :Another client is now receiving CTCPs sent to %s\r\n",
+ rec->proxy_address, rec->nick, rec->listen->ircnet);
+ rec->want_ctcp = 0;
+ }
+
}
- proxy_outdata(client, ":%s NOTICE %s :You're now receiving CTCPs sent to %s\n",
- client->proxy_address, client->nick,client->listen->ircnet);
+ proxy_outdata(client, ":%s NOTICE %s :You're now receiving CTCPs sent to %s\r\n",
+ client->proxy_address, client->nick, client->listen->ircnet);
} else if (g_ascii_strcasecmp(args, "CTCP OFF") == 0) {
- /* client wants proxy to handle all ctcps */
+ /* client wants proxy to handle all ctcps */
client->want_ctcp = 0;
- proxy_outdata(client, ":%s NOTICE %s :Proxy is now handling itself CTCPs sent to %s\n",
- client->proxy_address, client->nick, client->listen->ircnet);
+ proxy_outdata(client, ":%s NOTICE %s :Proxy is now handling itself CTCPs sent to %s\r\n",
+ client->proxy_address, client->nick, client->listen->ircnet);
} else {
signal_emit("proxy client command", 3, client, args, data);
}
@@ -194,17 +292,17 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
}
if (client->server == NULL || !client->server->connected) {
- proxy_outdata(client, ":%s NOTICE %s :Not connected to server\n",
- client->proxy_address, client->nick);
- return;
+ proxy_outdata(client, ":%s NOTICE %s :Not connected to server\r\n",
+ client->proxy_address, client->nick);
+ return;
}
- /* check if the command could be redirected */
- if (strcmp(cmd, "WHO") == 0)
+ /* check if the command could be redirected */
+ if (g_strcmp0(cmd, "WHO") == 0)
grab_who(client, args);
- else if (strcmp(cmd, "WHOWAS") == 0)
+ else if (g_strcmp0(cmd, "WHOWAS") == 0)
proxy_redirect_event(client, "whowas", 1, args, -1);
- else if (strcmp(cmd, "WHOIS") == 0) {
+ else if (g_strcmp0(cmd, "WHOIS") == 0) {
char *p;
/* convert dots to spaces */
@@ -212,11 +310,11 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
if (*p == ',') *p = ' ';
proxy_redirect_event(client, "whois", 1, args, TRUE);
- } else if (strcmp(cmd, "ISON") == 0)
+ } else if (g_strcmp0(cmd, "ISON") == 0)
proxy_redirect_event(client, "ison", 1, args, -1);
- else if (strcmp(cmd, "USERHOST") == 0)
+ else if (g_strcmp0(cmd, "USERHOST") == 0)
proxy_redirect_event(client, "userhost", 1, args, -1);
- else if (strcmp(cmd, "MODE") == 0) {
+ else if (g_strcmp0(cmd, "MODE") == 0) {
/* convert dots to spaces */
char *slist, *str, mode, *p;
int argc;
@@ -252,40 +350,40 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
}
g_free(str);
g_free(slist);
- } else if (strcmp(cmd, "PRIVMSG") == 0) {
+ } else if (g_strcmp0(cmd, "PRIVMSG") == 0) {
/* send the message to other clients as well */
char *params, *target, *msg;
params = event_get_params(args, 2 | PARAM_FLAG_GETREST,
- &target, &msg);
+ &target, &msg);
proxy_outserver_all_except(client, "PRIVMSG %s", args);
ignore_next = TRUE;
if (*msg != '\001' || msg[strlen(msg)-1] != '\001') {
- signal_emit(ischannel(*target) ?
- "message own_public" : "message own_private", 4,
- client->server, msg, target, target);
+ signal_emit(server_ischannel(SERVER(client->server), target) ?
+ "message own_public" : "message own_private", 4,
+ client->server, msg, target, target);
} else if (strncmp(msg+1, "ACTION ", 7) == 0) {
/* action */
- msg[strlen(msg)-1] = '\0';
+ msg[strlen(msg)-1] = '\0';
signal_emit("message irc own_action", 3,
- client->server, msg+8, target);
+ client->server, msg+8, target);
} else {
- /* CTCP */
+ /* CTCP */
char *p;
msg[strlen(msg)-1] = '\0';
p = strchr(msg, ' ');
- if (p != NULL) *p++ = '\0'; else p = "";
+ if (p != NULL) *p++ = '\0'; else p = "";
signal_emit("message irc own_ctcp", 4,
- client->server, msg+1, p, target);
+ client->server, msg+1, p, target);
}
ignore_next = FALSE;
g_free(params);
- } else if (strcmp(cmd, "PING") == 0) {
+ } else if (g_strcmp0(cmd, "PING") == 0) {
proxy_redirect_event(client, "ping", 1, NULL, TRUE);
- } else if (strcmp(cmd, "AWAY") == 0) {
+ } else if (g_strcmp0(cmd, "AWAY") == 0) {
/* set the away reason */
if (args != NULL) {
g_free(client->server->away_reason);
@@ -331,23 +429,38 @@ static void sig_listen(LISTEN_REC *listen)
CLIENT_REC *rec;
IPADDR ip;
NET_SENDBUF_REC *sendbuf;
- GIOChannel *handle;
+ GIOChannel *handle;
char host[MAX_IP_LEN];
int port;
+ char *addr;
g_return_if_fail(listen != NULL);
/* accept connection */
- handle = net_accept(listen->handle, &ip, &port);
- if (handle == NULL)
- return;
- net_ip2host(&ip, host);
+ if (listen->port) {
+ handle = net_accept(listen->handle, &ip, &port);
+ if (handle == NULL)
+ return;
+ net_ip2host(&ip, host);
+ addr = g_strdup_printf("%s:%d", host, port);
+ } else {
+ /* no port => this is a unix socket */
+ handle = net_accept_unix(listen->handle);
+ if (handle == NULL)
+ return;
+ addr = g_strdup("(local)");
+ }
+
sendbuf = net_sendbuffer_create(handle, 0);
rec = g_new0(CLIENT_REC, 1);
rec->listen = listen;
rec->handle = sendbuf;
- rec->host = g_strdup(host);
- if (strcmp(listen->ircnet, "*") == 0) {
+ rec->addr = addr;
+ if (g_strcmp0(listen->ircnet, "?") == 0) {
+ rec->multiplex = TRUE;
+ rec->proxy_address = g_strdup("multiplex.proxy");
+ rec->server = NULL;
+ } else if (g_strcmp0(listen->ircnet, "*") == 0) {
rec->proxy_address = g_strdup("irc.proxy");
rec->server = servers == NULL ? NULL : IRC_SERVER(servers->data);
} else {
@@ -356,14 +469,15 @@ static void sig_listen(LISTEN_REC *listen)
IRC_SERVER(server_find_chatnet(listen->ircnet));
}
rec->recv_tag = g_input_add(handle, G_INPUT_READ,
- (GInputFunction) sig_listen_client, rec);
+ (GInputFunction) sig_listen_client, rec);
proxy_clients = g_slist_prepend(proxy_clients, rec);
- rec->listen->clients = g_slist_prepend(rec->listen->clients, rec);
+ listen->clients = g_slist_prepend(listen->clients, rec);
- signal_emit("proxy client connected", 1, rec);
- printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
- "Proxy: Client connected from %s", rec->host);
+ signal_emit("proxy client connecting", 1, rec);
+ printtext(rec->server, NULL, MSGLEVEL_CLIENTNOTICE,
+ "Proxy: New client %s on port %s (%s)",
+ rec->addr, listen->port_or_path, listen->ircnet);
}
static void sig_incoming(IRC_SERVER_REC *server, const char *line)
@@ -371,7 +485,7 @@ static void sig_incoming(IRC_SERVER_REC *server, const char *line)
g_return_if_fail(line != NULL);
/* send server event to all clients */
- g_string_printf(next_line, "%s\n", line);
+ g_string_printf(next_line, "%s\r\n", line);
}
static void sig_server_event(IRC_SERVER_REC *server, const char *line,
@@ -415,13 +529,13 @@ static void sig_server_event(IRC_SERVER_REC *server, const char *line,
}
}
- if (strcmp(event, "event privmsg") == 0 &&
+ if (g_strcmp0(event, "event privmsg") == 0 &&
strstr(args, " :\001") != NULL &&
strstr(args, " :\001ACTION") == NULL) {
/* CTCP - either answer ourself or forward it to one client */
for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
CLIENT_REC *rec = tmp->data;
-
+
if (rec->want_ctcp == 1) {
/* only CTCP for the chatnet where client is connected to will be forwarded */
if (strstr(rec->proxy_address, server->connrec->chatnet) != NULL) {
@@ -435,8 +549,8 @@ static void sig_server_event(IRC_SERVER_REC *server, const char *line,
return;
}
- if (strcmp(event, "event ping") == 0 ||
- strcmp(event, "event pong") == 0) {
+ if (g_strcmp0(event, "event ping") == 0 ||
+ g_strcmp0(event, "event pong") == 0) {
/* We want to answer ourself to PINGs and CTCPs.
Also hide PONGs from clients. */
g_free(event);
@@ -452,21 +566,21 @@ static void sig_server_event(IRC_SERVER_REC *server, const char *line,
static void event_connected(IRC_SERVER_REC *server)
{
GSList *tmp;
- const char *chatnet;
+ const char *chatnet;
if (!IS_IRC_SERVER(server))
return;
- chatnet = server->connrec->chatnet;
+ chatnet = server->connrec->chatnet;
for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
CLIENT_REC *rec = tmp->data;
if (rec->connected && rec->server == NULL &&
- (strcmp(rec->listen->ircnet, "*") == 0 ||
+ (g_strcmp0(rec->listen->ircnet, "*") == 0 ||
(chatnet != NULL &&
g_ascii_strcasecmp(chatnet, rec->listen->ircnet) == 0))) {
- proxy_outdata(rec, ":%s NOTICE %s :Connected to server\n",
- rec->proxy_address, rec->nick);
+ proxy_outdata(rec, ":%s NOTICE %s :Connected to server\r\n",
+ rec->proxy_address, rec->nick);
rec->server = server;
proxy_client_reset_nick(rec);
}
@@ -474,11 +588,11 @@ static void event_connected(IRC_SERVER_REC *server)
}
static void proxy_server_disconnected(CLIENT_REC *client,
- IRC_SERVER_REC *server)
+ IRC_SERVER_REC *server)
{
GSList *tmp;
- proxy_outdata(client, ":%s NOTICE %s :Connection lost to server %s\n",
+ proxy_outdata(client, ":%s NOTICE %s :Connection lost to server %s\r\n",
client->proxy_address, client->nick,
server->connrec->address);
@@ -559,14 +673,19 @@ static void sig_message_own_action(IRC_SERVER_REC *server, const char *msg,
proxy_outserver_all(server, "PRIVMSG %s :\001ACTION %s\001", target, msg);
}
-static LISTEN_REC *find_listen(const char *ircnet, int port)
+static LISTEN_REC *find_listen(const char *ircnet, int port, const char *port_or_path)
{
GSList *tmp;
for (tmp = proxy_listens; tmp != NULL; tmp = tmp->next) {
LISTEN_REC *rec = tmp->data;
- if (rec->port == port &&
+ if ((port
+ ? /* a tcp port */
+ rec->port == port
+ : /* a unix socket path */
+ g_strcmp0(rec->port_or_path, port_or_path) == 0
+ ) &&
g_ascii_strcasecmp(rec->ircnet, ircnet) == 0)
return rec;
}
@@ -574,48 +693,53 @@ static LISTEN_REC *find_listen(const char *ircnet, int port)
return NULL;
}
-static void add_listen(const char *ircnet, int port)
+static void add_listen(const char *ircnet, int port, const char *port_or_path)
{
LISTEN_REC *rec;
IPADDR ip4, ip6, *my_ip;
+ GIOChannel *handle;
- if (port <= 0 || *ircnet == '\0')
+ if (*port_or_path == '\0' || port < 0 || *ircnet == '\0')
return;
- /* bind to specific host/ip? */
- my_ip = NULL;
- if (*settings_get_str("irssiproxy_bind") != '\0') {
- if (net_gethostbyname(settings_get_str("irssiproxy_bind"),
- &ip4, &ip6) != 0) {
- printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
- "Proxy: can not resolve '%s' - aborting",
- settings_get_str("irssiproxy_bind"));
- return;
+ if (port == 0) {
+ /* listening on a unix socket */
+ handle = net_listen_unix(port_or_path);
+ } else {
+ /* bind to specific host/ip? */
+ my_ip = NULL;
+ if (*settings_get_str("irssiproxy_bind") != '\0') {
+ if (net_gethostbyname(settings_get_str("irssiproxy_bind"),
+ &ip4, &ip6) != 0) {
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
+ "Proxy: can not resolve '%s' - aborting",
+ settings_get_str("irssiproxy_bind"));
+ return;
+ }
+
+ my_ip = ip6.family == 0 ? &ip4 : ip4.family == 0 ||
+ settings_get_bool("resolve_prefer_ipv6") ? &ip6 : &ip4;
}
+ handle = net_listen(my_ip, &port);
+ }
- my_ip = ip6.family == 0 ? &ip4 : ip4.family == 0 ||
- settings_get_bool("resolve_prefer_ipv6") ? &ip6 : &ip4;
+ if (handle == NULL) {
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
+ "Proxy: Listen in port %s failed: %s",
+ port_or_path, g_strerror(errno));
+ return;
}
rec = g_new0(LISTEN_REC, 1);
+ rec->handle = handle;
rec->ircnet = g_strdup(ircnet);
rec->port = port;
-
- rec->handle = net_listen(my_ip, &rec->port);
-
- if (rec->handle == NULL) {
- printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
- "Proxy: Listen in port %d failed: %s",
- rec->port, g_strerror(errno));
- g_free(rec->ircnet);
- g_free(rec);
- return;
- }
+ rec->port_or_path = g_strdup(port_or_path);
rec->tag = g_input_add(rec->handle, G_INPUT_READ,
- (GInputFunction) sig_listen, rec);
+ (GInputFunction) sig_listen, rec);
- proxy_listens = g_slist_append(proxy_listens, rec);
+ proxy_listens = g_slist_append(proxy_listens, rec);
}
static void remove_listen(LISTEN_REC *rec)
@@ -625,8 +749,13 @@ static void remove_listen(LISTEN_REC *rec)
while (rec->clients != NULL)
remove_client(rec->clients->data);
+ /* remove unix socket because bind wants to (re)create it */
+ if (rec->port == 0)
+ unlink(rec->port_or_path);
+
net_disconnect(rec->handle);
g_source_remove(rec->tag);
+ g_free(rec->port_or_path);
g_free(rec->ircnet);
g_free(rec);
}
@@ -634,8 +763,9 @@ static void remove_listen(LISTEN_REC *rec)
static void read_settings(void)
{
LISTEN_REC *rec;
- GSList *remove_listens;
- char **ports, **tmp, *ircnet, *port;
+ GSList *remove_listens = NULL;
+ GSList *add_listens = NULL;
+ char **ports, **tmp, *ircnet, *port_or_path;
int portnum;
remove_listens = g_slist_copy(proxy_listens);
@@ -643,27 +773,45 @@ static void read_settings(void)
ports = g_strsplit(settings_get_str("irssiproxy_ports"), " ", -1);
for (tmp = ports; *tmp != NULL; tmp++) {
ircnet = *tmp;
- port = strchr(ircnet, '=');
- if (port == NULL)
+ port_or_path = strchr(ircnet, '=');
+ if (port_or_path == NULL)
continue;
- *port++ = '\0';
- portnum = atoi(port);
- if (portnum <= 0)
- continue;
+ *port_or_path++ = '\0';
+ if (is_all_digits(port_or_path)) {
+ portnum = atoi(port_or_path);
+ if (portnum <= 0)
+ continue;
+ } else {
+ portnum = 0;
+ }
- rec = find_listen(ircnet, portnum);
- if (rec == NULL)
- add_listen(ircnet, portnum);
- else
+ rec = find_listen(ircnet, portnum, port_or_path);
+ if (rec == NULL) {
+ rec = g_new0(LISTEN_REC, 1);
+ rec->ircnet = ircnet; /* borrow */
+ rec->port = portnum;
+ rec->port_or_path = port_or_path; /* borrow */
+ add_listens = g_slist_prepend(add_listens, rec);
+ } else {
+ /* remove from the list of listens to remove == keep it */
remove_listens = g_slist_remove(remove_listens, rec);
+ }
}
- g_strfreev(ports);
while (remove_listens != NULL) {
- remove_listen(remove_listens->data);
+ remove_listen(remove_listens->data);
remove_listens = g_slist_remove(remove_listens, remove_listens->data);
}
+
+ while (add_listens != NULL) {
+ rec = add_listens->data;
+ add_listen(rec->ircnet, rec->port, rec->port_or_path);
+ add_listens = g_slist_remove(add_listens, rec);
+ g_free(rec);
+ }
+
+ g_strfreev(ports);
}
static void sig_dump(CLIENT_REC *client, const char *data)
@@ -676,6 +824,11 @@ static void sig_dump(CLIENT_REC *client, const char *data)
void proxy_listen_init(void)
{
+ if (enabled) {
+ return;
+ }
+ enabled = TRUE;
+
next_line = g_string_new(NULL);
proxy_clients = NULL;
@@ -697,6 +850,11 @@ void proxy_listen_init(void)
void proxy_listen_deinit(void)
{
+ if (!enabled) {
+ return;
+ }
+ enabled = FALSE;
+
while (proxy_listens != NULL)
remove_listen(proxy_listens->data);
g_string_free(next_line, TRUE);
diff --git a/src/irc/proxy/proxy.c b/src/irc/proxy/proxy.c
index c8f47bdf..2875d2c2 100644
--- a/src/irc/proxy/proxy.c
+++ b/src/irc/proxy/proxy.c
@@ -23,11 +23,61 @@
#include "settings.h"
#include "levels.h"
+#include "fe-common/core/printtext.h"
+
+/* SYNTAX: IRSSIPROXY STATUS */
+static void cmd_irssiproxy_status(const char *data, IRC_SERVER_REC *server)
+{
+ GSList *tmp;
+
+ if (!settings_get_bool("irssiproxy")) {
+ printtext(server, NULL, MSGLEVEL_CLIENTNOTICE,
+ "Proxy is currently disabled");
+ return;
+ }
+
+
+ printtext(server, NULL, MSGLEVEL_CLIENTNOTICE,
+ "Proxy: Currently connected clients: %d",
+ g_slist_length(proxy_clients));
+
+ for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
+ CLIENT_REC *rec = tmp->data;
+
+ printtext(server, NULL, MSGLEVEL_CLIENTNOTICE,
+ " %s connect%s to %s (%s)",
+ rec->addr,
+ rec->connected ? "ed" : "ing",
+ rec->listen->port_or_path, rec->listen->ircnet);
+ }
+}
+
+/* SYNTAX: IRSSIPROXY */
+static void cmd_irssiproxy(const char *data, IRC_SERVER_REC *server, void *item)
+{
+ if (*data == '\0') {
+ cmd_irssiproxy_status(data, server);
+ return;
+ }
+
+ command_runsub("irssiproxy", data, server, item);
+}
+
+static void irc_proxy_setup_changed(void)
+{
+ if (settings_get_bool("irssiproxy")) {
+ proxy_listen_init();
+ } else {
+ proxy_listen_deinit();
+ }
+}
+
void irc_proxy_init(void)
{
settings_add_str("irssiproxy", "irssiproxy_ports", "");
settings_add_str("irssiproxy", "irssiproxy_password", "");
settings_add_str("irssiproxy", "irssiproxy_bind", "");
+ settings_add_bool("irssiproxy", "irssiproxy", TRUE);
if (*settings_get_str("irssiproxy_password") == '\0') {
/* no password - bad idea! */
@@ -43,7 +93,14 @@ void irc_proxy_init(void)
"... to set them.");
}
- proxy_listen_init();
+ command_bind("irssiproxy", NULL, (SIGNAL_FUNC) cmd_irssiproxy);
+ command_bind("irssiproxy status", NULL, (SIGNAL_FUNC) cmd_irssiproxy_status);
+
+ signal_add_first("setup changed", (SIGNAL_FUNC) irc_proxy_setup_changed);
+
+ if (settings_get_bool("irssiproxy")) {
+ proxy_listen_init();
+ }
settings_check();
module_register("proxy", "irc");
}
@@ -52,3 +109,8 @@ void irc_proxy_deinit(void)
{
proxy_listen_deinit();
}
+
+void irc_proxy_abicheck(int *version)
+{
+ *version = IRSSI_ABI_VERSION;
+}
diff --git a/src/irc/proxy/proxy.h b/src/irc/proxy/proxy.h
index 4ddc9da9..620ea605 100644
--- a/src/irc/proxy/proxy.h
+++ b/src/irc/proxy/proxy.h
@@ -9,16 +9,18 @@
typedef struct {
int port;
+ char *port_or_path;
char *ircnet;
int tag;
GIOChannel *handle;
GSList *clients;
+
} LISTEN_REC;
typedef struct {
- char *nick, *host;
+ char *nick, *addr;
NET_SENDBUF_REC *handle;
int recv_tag;
char *proxy_address;
@@ -28,6 +30,7 @@ typedef struct {
unsigned int user_sent:1;
unsigned int connected:1;
unsigned int want_ctcp:1;
+ unsigned int multiplex:1;
} CLIENT_REC;
#endif
diff --git a/src/lib-config/get.c b/src/lib-config/get.c
index 2f1e90e1..a83686fd 100644
--- a/src/lib-config/get.c
+++ b/src/lib-config/get.c
@@ -38,12 +38,12 @@ CONFIG_NODE *config_node_find(CONFIG_NODE *node, const char *key)
return NULL;
}
-CONFIG_NODE *config_node_section(CONFIG_NODE *parent, const char *key, int new_type)
+CONFIG_NODE *config_node_section(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, int new_type)
{
- return config_node_section_index(parent, key, -1, new_type);
+ return config_node_section_index(rec, parent, key, -1, new_type);
}
-CONFIG_NODE *config_node_section_index(CONFIG_NODE *parent, const char *key,
+CONFIG_NODE *config_node_section_index(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key,
int index, int new_type)
{
CONFIG_NODE *node;
@@ -54,7 +54,6 @@ CONFIG_NODE *config_node_section_index(CONFIG_NODE *parent, const char *key,
node = key == NULL ? NULL : config_node_find(parent, key);
if (node != NULL) {
- g_return_val_if_fail(new_type == -1 || new_type == node->type, NULL);
nindex = g_slist_index(parent->value, node);
if (index >= 0 && nindex != index &&
nindex <= g_slist_length(parent->value)) {
@@ -62,7 +61,25 @@ CONFIG_NODE *config_node_section_index(CONFIG_NODE *parent, const char *key,
parent->value = g_slist_remove(parent->value, node);
parent->value = g_slist_insert(parent->value, node, index);
}
- return node;
+ if (!is_node_list(node)) {
+ int show_error = 0;
+
+ if (new_type != -1) {
+ config_node_remove(rec, parent, node);
+ node = NULL;
+ show_error = 1;
+ } else if (!g_hash_table_lookup_extended(rec->cache_nodes, node, NULL, NULL)) {
+ g_hash_table_insert(rec->cache_nodes, node, NULL);
+ show_error = 1;
+ }
+ if (show_error)
+ g_critical("Expected %s node at `..%s/%s' was of scalar type. Corrupt config?",
+ new_type == NODE_TYPE_LIST ? "list" : new_type == NODE_TYPE_BLOCK ? "block" : "section",
+ parent->key, key);
+ } else {
+ g_return_val_if_fail(new_type == -1 || new_type == node->type, NULL);
+ return node;
+ }
}
if (new_type == -1)
@@ -91,7 +108,21 @@ CONFIG_NODE *config_node_traverse(CONFIG_REC *rec, const char *section, int crea
/* check if it already exists in cache */
node = g_hash_table_lookup(rec->cache, section);
- if (node != NULL) return node;
+ if (node != NULL) {
+ if (create) {
+ const char *path = strrchr(section, '/');
+ if (path == NULL) path = section;
+ else path++;
+ new_type = *path == '(' ? NODE_TYPE_LIST : NODE_TYPE_BLOCK;
+ if (node->type != new_type) {
+ g_critical("Expected %s node at `%s' was of %s type. Corrupt config?",
+ new_type == NODE_TYPE_LIST ? "list" : "block", section,
+ node->type == NODE_TYPE_LIST ? "list" : "block");
+ node->type = new_type;
+ }
+ }
+ return node;
+ }
new_type = -1;
@@ -99,9 +130,18 @@ CONFIG_NODE *config_node_traverse(CONFIG_REC *rec, const char *section, int crea
list = g_strsplit(section, "/", -1);
for (tmp = list; *tmp != NULL; tmp++) {
is_list = **tmp == '(';
- if (create) new_type = is_list ? NODE_TYPE_LIST : NODE_TYPE_BLOCK;
+ if (create) {
+ CONFIG_NODE *tmpnode;
+
+ new_type = is_list ? NODE_TYPE_LIST : NODE_TYPE_BLOCK;
+ tmpnode = config_node_find(node, *tmp + is_list);
+ if (tmpnode != NULL && tmpnode->type != new_type) {
+ g_critical("Expected %s node at `%s' was of scalar type. Corrupt config?", is_list ? "list" : "block", section);
+ config_node_remove(rec, node, tmpnode);
+ }
+ }
- node = config_node_section(node, *tmp + is_list, new_type);
+ node = config_node_section(rec, node, *tmp + is_list, new_type);
if (node == NULL) {
g_strfreev(list);
return NULL;
@@ -109,6 +149,12 @@ CONFIG_NODE *config_node_traverse(CONFIG_REC *rec, const char *section, int crea
}
g_strfreev(list);
+ if (!is_node_list(node)) {
+ /* Will die. Better to not corrupt the config further in this case. */
+ g_critical("Attempt to use non-list node `%s' as list. Corrupt config?", section);
+ return NULL;
+ }
+
/* save to cache */
str = g_strdup(section);
g_hash_table_insert(rec->cache, str, node);
diff --git a/src/lib-config/iconfig.h b/src/lib-config/iconfig.h
index 3d9eb931..91583e40 100644
--- a/src/lib-config/iconfig.h
+++ b/src/lib-config/iconfig.h
@@ -116,8 +116,8 @@ int config_set_bool(CONFIG_REC *rec, const char *section, const char *key, int v
CONFIG_NODE *config_node_find(CONFIG_NODE *node, const char *key);
/* Find the section from node - if not found create it unless new_type is -1.
You can also specify in new_type if it's NODE_TYPE_LIST or NODE_TYPE_BLOCK */
-CONFIG_NODE *config_node_section(CONFIG_NODE *parent, const char *key, int new_type);
-CONFIG_NODE *config_node_section_index(CONFIG_NODE *parent, const char *key,
+CONFIG_NODE *config_node_section(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, int new_type);
+CONFIG_NODE *config_node_section_index(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key,
int index, int new_type);
/* Find the section with the whole path.
Create the path if necessary if `create' is TRUE. */
diff --git a/src/lib-config/parse.c b/src/lib-config/parse.c
index 1b20195a..c106fc46 100644
--- a/src/lib-config/parse.c
+++ b/src/lib-config/parse.c
@@ -173,7 +173,7 @@ static GTokenType config_parse_symbol(CONFIG_REC *rec, CONFIG_NODE *node)
if (key == NULL && node->type != NODE_TYPE_LIST)
return G_TOKEN_ERROR;
- newnode = config_node_section(node, key, NODE_TYPE_BLOCK);
+ newnode = config_node_section(rec, node, key, NODE_TYPE_BLOCK);
config_parse_loop(rec, newnode, (GTokenType) '}');
g_free_not_null(key);
@@ -188,7 +188,7 @@ static GTokenType config_parse_symbol(CONFIG_REC *rec, CONFIG_NODE *node)
/* list */
if (key == NULL)
return G_TOKEN_ERROR;
- newnode = config_node_section(node, key, NODE_TYPE_LIST);
+ newnode = config_node_section(rec, node, key, NODE_TYPE_LIST);
config_parse_loop(rec, newnode, (GTokenType) ')');
g_free_not_null(key);
diff --git a/src/lib-config/set.c b/src/lib-config/set.c
index 61a101fb..0de6c503 100644
--- a/src/lib-config/set.c
+++ b/src/lib-config/set.c
@@ -82,6 +82,7 @@ void config_node_clear(CONFIG_REC *rec, CONFIG_NODE *node)
void config_nodes_remove_all(CONFIG_REC *rec)
{
g_return_if_fail(rec != NULL);
+ g_return_if_fail(is_node_list(rec->mainnode));
while (rec->mainnode->value != NULL)
config_node_remove(rec, rec->mainnode, ((GSList *) rec->mainnode->value)->data);
@@ -94,6 +95,7 @@ void config_node_set_str(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key,
g_return_if_fail(rec != NULL);
g_return_if_fail(parent != NULL);
+ g_return_if_fail(is_node_list(parent));
no_key = key == NULL;
node = no_key ? NULL : config_node_find(parent, key);
@@ -104,8 +106,14 @@ void config_node_set_str(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key,
return;
}
+ if (node != NULL && !has_node_value(node)) {
+ g_critical("Expected scalar node at `..%s/%s' was of complex type. Corrupt config?",
+ parent->key, key);
+ config_node_remove(rec, parent, node);
+ node = NULL;
+ }
if (node != NULL) {
- if (strcmp(node->value, value) == 0)
+ if (g_strcmp0(node->value, value) == 0)
return;
g_free(node->value);
} else {
diff --git a/src/perl/Makefile.am b/src/perl/Makefile.am
index a09c49a5..427c5492 100644
--- a/src/perl/Makefile.am
+++ b/src/perl/Makefile.am
@@ -117,16 +117,26 @@ textui_sources = \
EXTRA_DIST = \
get-signals.pl \
irssi-core.pl \
+ Makefile_silent.pm \
$(common_sources) \
$(irc_sources) \
$(ui_sources) \
$(textui_sources)
+am_v_pl__show_gen = $(am__v_pl__show_gen_$(V))
+am_v_pl__hide_gen = $(am__v_pl__hide_gen_$(V))
+am__v_pl__show_gen_ = $(am__v_pl__show_gen_$(AM_DEFAULT_VERBOSITY))
+am__v_pl__hide_gen_ = $(am__v_pl__hide_gen_$(AM_DEFAULT_VERBOSITY))
+am__v_pl__show_gen_0 = echo " GEN " $$dir ;
+am__v_pl__hide_gen_0 = > /dev/null
+am__v_pl__show_gen_1 =
+am__v_pl__hide_gen_1 =
+
all-local:
- for dir in $(perl_dirs); do \
+ $(AM_V_GEN)for dir in $(perl_dirs); do \
cd $$dir && \
if [ ! -f Makefile ]; then \
- $(perlpath) Makefile.PL $(PERL_MM_PARAMS); \
+ $(am_v_pl__show_gen)$(perlpath) Makefile.PL $(PERL_MM_PARAMS) $(am_v_pl__hide_gen); \
fi && \
($(MAKE) CC="$(CC)" CCFLAGS="$(PERL_CFLAGS) $(CFLAGS)" $(PERL_EXTRA_OPTS) || \
$(MAKE) CC="$(CC)" CCFLAGS="$(PERL_CFLAGS) $(CFLAGS)" $(PERL_EXTRA_OPTS)) && \
diff --git a/src/perl/Makefile_silent.pm b/src/perl/Makefile_silent.pm
new file mode 100644
index 00000000..b5d71d66
--- /dev/null
+++ b/src/perl/Makefile_silent.pm
@@ -0,0 +1,76 @@
+push @ExtUtils::MakeMaker::Overridable, qw(pm_to_blib);
+my $verb = $AM_DEFAULT_VERBOSITY;
+{ package MY;
+ sub _center {
+ my $z = shift;
+ (length $z == 2 ? " $z " : length $z == 4 ? " $z " : " $z ").' '
+ }
+ sub _silent_cmd {
+ my $z = shift;
+ $z =~ s{\t(?:- ?)?\K(?=\$\((?|(CC)CMD|(XS)UBPPRUN|(LD|MV|CHMOD)|(RM)_R?F|(CP)_NONEMPTY|FULL_(AR)\)))}{\$(PL_AM_V_$1)}g;
+ $z
+ }
+ sub c_o { _silent_cmd(shift->SUPER::c_o(@_)) }
+ sub xs_c { _silent_cmd(shift->SUPER::xs_c(@_)) }
+ sub xs_o { _silent_cmd(shift->SUPER::xs_o(@_)) }
+ sub dynamic_lib { _silent_cmd(shift->SUPER::dynamic_lib(@_)) }
+ sub static_lib { _silent_cmd(shift->SUPER::static_lib(@_)) }
+ sub dynamic_bs {
+ my $ret = shift->SUPER::dynamic_bs(@_);
+ $ret =~ s{Running Mkbootstrap for}{\$(PL_AM_V_BS_Text)}g;
+ _silent_cmd($ret)
+ }
+ sub pm_to_blib {
+ my $ret = shift->SUPER::pm_to_blib(@_);
+ $ret =~ s{^(\t(?:- ?)?)(?:\$\(NOECHO\) ?)?(.*-e ['"]pm_to_blib(.*\\\n)*.*)$}{$1\$(PL_AM_V_BLIB)$2\$(PL_AM_V_BLIB_Hide)}mg;
+ $ret
+ }
+ sub post_constants {
+ my $ret = shift->SUPER::post_constants(@_);
+ my @terse = qw(cc xs ld chmod cp ar blib);
+ my @silent = qw(mv rm);
+ my @special = qw(BLIB_Hide);
+
+ #default verbosity from command line parameter
+ $ret .= "
+AM_DEFAULT_VERBOSITY = @{[$verb ? 1 : 0]}
+";
+ #default options forward
+ $ret .= "
+PL_AM_V_${_} = \$(pl_am__v_${_}_\$(V))
+pl_am__v_${_}_ = \$(pl_am__v_${_}_\$(AM_DEFAULT_VERBOSITY))
+" for @special, map uc, @terse, @silent;
+
+ #quoted plain text needs extra quotes
+ $ret .= "
+PL_AM_V_BS_Text = \"\$(pl_am__v_BS_Text_\$(V))\"
+pl_am__v_BS_Text_ = \$(pl_am__v_BS_Text_\$(AM_DEFAULT_VERBOSITY))
+"
+ #hide pm_to_blib output
+. "
+pl_am__v_BLIB_Hide_0 = \$(DEV_NULL)
+pl_am__v_BLIB_Hide_1 =
+"
+ #text for Mkbootstrap
+. "
+pl_am__v_BS_Text_0 = \"@{[_center('BS')]}\"
+pl_am__v_BS_Text_1 = \"Running Mkbootstrap for\"
+";
+ #"terse" output
+ $ret .= "
+pl_am__v_${_}_0 = \$(NOECHO)echo \"@{[_center($_)]}\" \$\@;
+" for map uc, @terse;
+
+ #no output
+ $ret .= "
+pl_am__v_${_}_0 = \$(NOECHO)
+" for map uc, @silent;
+
+ #in verbose mode the "terse" echo expands to nothing
+ $ret .= "
+pl_am__v_${_}_1 =
+" for map uc, @terse, @silent;
+ $ret
+ }
+}
+1;
diff --git a/src/perl/common/Core.xs b/src/perl/common/Core.xs
index bfe2efde..5d2f7bca 100644
--- a/src/perl/common/Core.xs
+++ b/src/perl/common/Core.xs
@@ -63,7 +63,7 @@ static void add_tuple(gpointer key_, gpointer value_, gpointer user_data)
HV *hash = user_data;
char *key = key_;
char *value = value_;
- hv_store(hash, key, strlen(key), new_pv(value), 0);
+ (void) hv_store(hash, key, strlen(key), new_pv(value), 0);
}
static void wrap_signal_emit(void *signal, void **p) {
diff --git a/src/perl/common/Expando.xs b/src/perl/common/Expando.xs
index e8e8f751..26800b05 100644
--- a/src/perl/common/Expando.xs
+++ b/src/perl/common/Expando.xs
@@ -74,15 +74,20 @@ static char *perl_expando_event(PerlExpando *rec, SERVER_REC *server,
ret = NULL;
if (SvTRUE(ERRSV)) {
+ PERL_SCRIPT_REC *script = rec->script;
+
(void) POPs;
/* call putback before emitting script error signal as that
* could manipulate the perl stack. */
PUTBACK;
/* make sure we don't get back here */
- if (rec->script != NULL)
- script_unregister_expandos(rec->script);
+ if (script != NULL)
+ script_unregister_expandos(script);
+ /* rec has been freed now */
- signal_emit("script error", 2, rec->script, SvPV_nolen(ERRSV));
+ char *error = g_strdup(SvPV_nolen(ERRSV));
+ signal_emit("script error", 2, script, error);
+ g_free(error);
} else if (retcount > 0) {
ret = g_strdup(POPp);
*free_ret = TRUE;
diff --git a/src/perl/common/Irssi.pm b/src/perl/common/Irssi.pm
index a9f93bf0..1d7ed2f3 100644
--- a/src/perl/common/Irssi.pm
+++ b/src/perl/common/Irssi.pm
@@ -159,6 +159,9 @@ if (!in_irssi()) {
@Irssi::Channel::ISA = qw(Irssi::Windowitem);
@Irssi::Query::ISA = qw(Irssi::Windowitem);
+ @Irssi::UI::Exec::ISA = qw(Irssi::Windowitem);
+ @Irssi::Chatnet::ISA = qw();
+ @Irssi::Nick::ISA = qw();
Irssi::init();
diff --git a/src/perl/common/Makefile.PL.in b/src/perl/common/Makefile.PL.in
index 4e545e7c..70b1d258 100644
--- a/src/perl/common/Makefile.PL.in
+++ b/src/perl/common/Makefile.PL.in
@@ -1,4 +1,4 @@
-use ExtUtils::MakeMaker;
+use ExtUtils::MakeMaker;our $AM_DEFAULT_VERBOSITY='@AM_DEFAULT_VERBOSITY@';require "@top_srcdir@/src/perl/Makefile_silent.pm";
WriteMakefile('NAME' => 'Irssi',
'LIBS' => '',
diff --git a/src/perl/get-signals.pl b/src/perl/get-signals.pl
index 529d35b1..806bcafa 100755
--- a/src/perl/get-signals.pl
+++ b/src/perl/get-signals.pl
@@ -37,6 +37,7 @@ while (<STDIN>) {
RAWLOG_REC => 'Irssi::Rawlog',
IGNORE_REC => 'Irssi::Ignore',
MODULE_REC => 'Irssi::Module',
+ TLS_REC => 'iobject',
# irc
BAN_REC => 'Irssi::Irc::Ban',
diff --git a/src/perl/irc/Irc.xs b/src/perl/irc/Irc.xs
index 22a87384..41690010 100644
--- a/src/perl/irc/Irc.xs
+++ b/src/perl/irc/Irc.xs
@@ -6,90 +6,107 @@ static int initialized = FALSE;
static void perl_irc_connect_fill_hash(HV *hv, IRC_SERVER_CONNECT_REC *conn)
{
perl_connect_fill_hash(hv, (SERVER_CONNECT_REC *) conn);
- hv_store(hv, "alternate_nick", 14, new_pv(conn->alternate_nick), 0);
+ (void) hv_store(hv, "alternate_nick", 14, new_pv(conn->alternate_nick), 0);
}
static void perl_irc_server_fill_hash(HV *hv, IRC_SERVER_REC *server)
{
- perl_server_fill_hash(hv, (SERVER_REC *) server);
+ AV *av;
+ GSList *tmp;
+
+ perl_irc_connect_fill_hash(hv, server->connrec);
+ perl_server_fill_hash(hv, (SERVER_REC *) server);
+
+ (void) hv_store(hv, "real_address", 12, new_pv(server->real_address), 0);
+ (void) hv_store(hv, "usermode", 8, new_pv(server->usermode), 0);
+ (void) hv_store(hv, "userhost", 8, new_pv(server->userhost), 0);
+
+ (void) hv_store(hv, "max_cmds_at_once", 16, newSViv(server->max_cmds_at_once), 0);
+ (void) hv_store(hv, "cmd_queue_speed", 15, newSViv(server->cmd_queue_speed), 0);
+ (void) hv_store(hv, "max_query_chans", 15, newSViv(server->max_query_chans), 0);
- hv_store(hv, "real_address", 12, new_pv(server->real_address), 0);
- hv_store(hv, "usermode", 8, new_pv(server->usermode), 0);
- hv_store(hv, "userhost", 8, new_pv(server->userhost), 0);
+ (void) hv_store(hv, "max_kicks_in_cmd", 16, newSViv(server->max_kicks_in_cmd), 0);
+ (void) hv_store(hv, "max_msgs_in_cmd", 15, newSViv(server->max_msgs_in_cmd), 0);
+ (void) hv_store(hv, "max_modes_in_cmd", 16, newSViv(server->max_modes_in_cmd), 0);
+ (void) hv_store(hv, "max_whois_in_cmd", 16, newSViv(server->max_whois_in_cmd), 0);
+ (void) hv_store(hv, "isupport_sent", 13, newSViv(server->isupport_sent), 0);
- hv_store(hv, "max_cmds_at_once", 16, newSViv(server->max_cmds_at_once), 0);
- hv_store(hv, "cmd_queue_speed", 15, newSViv(server->cmd_queue_speed), 0);
- hv_store(hv, "max_query_chans", 15, newSViv(server->max_query_chans), 0);
+ (void) hv_store(hv, "cap_complete", 12, newSViv(server->cap_complete), 0);
+ (void) hv_store(hv, "sasl_success", 12, newSViv(server->sasl_success), 0);
- hv_store(hv, "max_kicks_in_cmd", 16, newSViv(server->max_kicks_in_cmd), 0);
- hv_store(hv, "max_msgs_in_cmd", 15, newSViv(server->max_msgs_in_cmd), 0);
- hv_store(hv, "max_modes_in_cmd", 16, newSViv(server->max_modes_in_cmd), 0);
- hv_store(hv, "max_whois_in_cmd", 16, newSViv(server->max_whois_in_cmd), 0);
- hv_store(hv, "isupport_sent", 13, newSViv(server->isupport_sent), 0);
+ av = newAV();
+ for (tmp = server->cap_supported; tmp != NULL; tmp = tmp->next)
+ av_push(av, new_pv(tmp->data));
+ (void) hv_store(hv, "cap_supported", 13, newRV_noinc((SV*)av), 0);
+
+ av = newAV();
+ for (tmp = server->cap_active; tmp != NULL; tmp = tmp->next)
+ av_push(av, new_pv(tmp->data));
+ (void) hv_store(hv, "cap_active", 10, newRV_noinc((SV*)av), 0);
}
static void perl_ban_fill_hash(HV *hv, BAN_REC *ban)
{
- hv_store(hv, "ban", 3, new_pv(ban->ban), 0);
- hv_store(hv, "setby", 5, new_pv(ban->setby), 0);
- hv_store(hv, "time", 4, newSViv(ban->time), 0);
+ (void) hv_store(hv, "ban", 3, new_pv(ban->ban), 0);
+ (void) hv_store(hv, "setby", 5, new_pv(ban->setby), 0);
+ (void) hv_store(hv, "time", 4, newSViv(ban->time), 0);
}
static void perl_dcc_fill_hash(HV *hv, DCC_REC *dcc)
{
- hv_store(hv, "type", 4, new_pv(dcc_type2str(dcc->type)), 0);
- hv_store(hv, "orig_type", 9, new_pv(dcc_type2str(dcc->orig_type)), 0);
- hv_store(hv, "created", 7, newSViv(dcc->created), 0);
+ (void) hv_store(hv, "type", 4, new_pv(dcc_type2str(dcc->type)), 0);
+ (void) hv_store(hv, "orig_type", 9, new_pv(dcc_type2str(dcc->orig_type)), 0);
+ (void) hv_store(hv, "created", 7, newSViv(dcc->created), 0);
- hv_store(hv, "server", 6, iobject_bless(dcc->server), 0);
- hv_store(hv, "servertag", 9, new_pv(dcc->servertag), 0);
- hv_store(hv, "mynick", 6, new_pv(dcc->mynick), 0);
- hv_store(hv, "nick", 4, new_pv(dcc->nick), 0);
+ (void) hv_store(hv, "server", 6, iobject_bless(dcc->server), 0);
+ (void) hv_store(hv, "servertag", 9, new_pv(dcc->servertag), 0);
+ (void) hv_store(hv, "mynick", 6, new_pv(dcc->mynick), 0);
+ (void) hv_store(hv, "nick", 4, new_pv(dcc->nick), 0);
- hv_store(hv, "chat", 4, simple_iobject_bless(dcc->chat), 0);
- hv_store(hv, "target", 6, new_pv(dcc->target), 0);
- hv_store(hv, "arg", 3, new_pv(dcc->arg), 0);
+ (void) hv_store(hv, "chat", 4, simple_iobject_bless(dcc->chat), 0);
+ (void) hv_store(hv, "target", 6, new_pv(dcc->target), 0);
+ (void) hv_store(hv, "arg", 3, new_pv(dcc->arg), 0);
- hv_store(hv, "addr", 4, new_pv(dcc->addrstr), 0);
- hv_store(hv, "port", 4, newSViv(dcc->port), 0);
+ (void) hv_store(hv, "addr", 4, new_pv(dcc->addrstr), 0);
+ (void) hv_store(hv, "port", 4, newSViv(dcc->port), 0);
- hv_store(hv, "starttime", 9, newSViv(dcc->starttime), 0);
- hv_store(hv, "transfd", 7, newSViv(dcc->transfd), 0);
+ (void) hv_store(hv, "starttime", 9, newSViv(dcc->starttime), 0);
+ (void) hv_store(hv, "transfd", 7, newSViv(dcc->transfd), 0);
}
static void perl_dcc_chat_fill_hash(HV *hv, CHAT_DCC_REC *dcc)
{
perl_dcc_fill_hash(hv, (DCC_REC *) dcc);
- hv_store(hv, "id", 2, new_pv(dcc->id), 0);
- hv_store(hv, "mirc_ctcp", 9, newSViv(dcc->mirc_ctcp), 0);
- hv_store(hv, "connection_lost", 15, newSViv(dcc->connection_lost), 0);
+ (void) hv_store(hv, "id", 2, new_pv(dcc->id), 0);
+ (void) hv_store(hv, "mirc_ctcp", 9, newSViv(dcc->mirc_ctcp), 0);
+ (void) hv_store(hv, "connection_lost", 15, newSViv(dcc->connection_lost), 0);
}
static void perl_dcc_file_fill_hash(HV *hv, FILE_DCC_REC *dcc)
{
perl_dcc_fill_hash(hv, (DCC_REC *) dcc);
- hv_store(hv, "size", 4, newSViv(dcc->size), 0);
- hv_store(hv, "skipped", 7, newSViv(dcc->skipped), 0);
+ (void) hv_store(hv, "size", 4, newSViv(dcc->size), 0);
+ (void) hv_store(hv, "skipped", 7, newSViv(dcc->skipped), 0);
}
static void perl_dcc_get_fill_hash(HV *hv, GET_DCC_REC *dcc)
{
perl_dcc_file_fill_hash(hv, (FILE_DCC_REC *) dcc);
- hv_store(hv, "get_type", 8, newSViv(dcc->get_type), 0);
- hv_store(hv, "file", 4, new_pv(dcc->file), 0);
- hv_store(hv, "file_quoted", 11, newSViv(dcc->file_quoted), 0);
+ (void) hv_store(hv, "get_type", 8, newSViv(dcc->get_type), 0);
+ (void) hv_store(hv, "file", 4, new_pv(dcc->file), 0);
+ (void) hv_store(hv, "file_quoted", 11, newSViv(dcc->file_quoted), 0);
}
static void perl_dcc_send_fill_hash(HV *hv, SEND_DCC_REC *dcc)
{
perl_dcc_file_fill_hash(hv, (FILE_DCC_REC *) dcc);
- hv_store(hv, "file_quoted", 11, newSViv(dcc->file_quoted), 0);
- hv_store(hv, "waitforend", 10, newSViv(dcc->waitforend), 0);
- hv_store(hv, "gotalldata", 10, newSViv(dcc->gotalldata), 0);
+ (void) hv_store(hv, "file_quoted", 11, newSViv(dcc->file_quoted), 0);
+ (void) hv_store(hv, "waitforend", 10, newSViv(dcc->waitforend), 0);
+ (void) hv_store(hv, "gotalldata", 10, newSViv(dcc->gotalldata), 0);
}
static void perl_netsplit_fill_hash(HV *hv, NETSPLIT_REC *netsplit)
@@ -97,11 +114,11 @@ static void perl_netsplit_fill_hash(HV *hv, NETSPLIT_REC *netsplit)
AV *av;
GSList *tmp;
- hv_store(hv, "nick", 4, new_pv(netsplit->nick), 0);
- hv_store(hv, "address", 7, new_pv(netsplit->address), 0);
- hv_store(hv, "destroy", 7, newSViv(netsplit->destroy), 0);
+ (void) hv_store(hv, "nick", 4, new_pv(netsplit->nick), 0);
+ (void) hv_store(hv, "address", 7, new_pv(netsplit->address), 0);
+ (void) hv_store(hv, "destroy", 7, newSViv(netsplit->destroy), 0);
- hv_store(hv, "server", 6,
+ (void) hv_store(hv, "server", 6,
plain_bless(netsplit->server,
"Irssi::Irc::Netsplitserver"), 0);
@@ -110,22 +127,22 @@ static void perl_netsplit_fill_hash(HV *hv, NETSPLIT_REC *netsplit)
av_push(av, plain_bless(tmp->data,
"Irssi::Irc::Netsplitchannel"));
}
- hv_store(hv, "channels", 8, newRV_noinc((SV*)av), 0);
+ (void) hv_store(hv, "channels", 8, newRV_noinc((SV*)av), 0);
}
static void perl_netsplit_server_fill_hash(HV *hv, NETSPLIT_SERVER_REC *rec)
{
- hv_store(hv, "server", 6, new_pv(rec->server), 0);
- hv_store(hv, "destserver", 10, new_pv(rec->destserver), 0);
- hv_store(hv, "count", 5, newSViv(rec->count), 0);
+ (void) hv_store(hv, "server", 6, new_pv(rec->server), 0);
+ (void) hv_store(hv, "destserver", 10, new_pv(rec->destserver), 0);
+ (void) hv_store(hv, "count", 5, newSViv(rec->count), 0);
}
static void perl_netsplit_channel_fill_hash(HV *hv, NETSPLIT_CHAN_REC *rec)
{
- hv_store(hv, "name", 4, new_pv(rec->name), 0);
- hv_store(hv, "op", 2, newSViv(rec->op), 0);
- hv_store(hv, "halfop", 6, newSViv(rec->halfop), 0);
- hv_store(hv, "voice", 5, newSViv(rec->voice), 0);
+ (void) hv_store(hv, "name", 4, new_pv(rec->name), 0);
+ (void) hv_store(hv, "op", 2, newSViv(rec->op), 0);
+ (void) hv_store(hv, "halfop", 6, newSViv(rec->halfop), 0);
+ (void) hv_store(hv, "voice", 5, newSViv(rec->voice), 0);
}
static void perl_notifylist_fill_hash(HV *hv, NOTIFYLIST_REC *notify)
@@ -133,8 +150,8 @@ static void perl_notifylist_fill_hash(HV *hv, NOTIFYLIST_REC *notify)
AV *av;
char **tmp;
- hv_store(hv, "mask", 4, new_pv(notify->mask), 0);
- hv_store(hv, "away_check", 10, newSViv(notify->away_check), 0);
+ (void) hv_store(hv, "mask", 4, new_pv(notify->mask), 0);
+ (void) hv_store(hv, "away_check", 10, newSViv(notify->away_check), 0);
av = newAV();
if (notify->ircnets != NULL) {
@@ -142,20 +159,21 @@ static void perl_notifylist_fill_hash(HV *hv, NOTIFYLIST_REC *notify)
av_push(av, new_pv(*tmp));
}
}
- hv_store(hv, "ircnets", 7, newRV_noinc((SV*)av), 0);
+ (void) hv_store(hv, "ircnets", 7, newRV_noinc((SV*)av), 0);
}
static void perl_client_fill_hash(HV *hv, CLIENT_REC *client)
{
- hv_store(hv, "nick", 4, new_pv(client->nick), 0);
- hv_store(hv, "host", 4, new_pv(client->host), 0);
- hv_store(hv, "proxy_address", 13, new_pv(client->proxy_address), 0);
- hv_store(hv, "server", 6, iobject_bless(client->server), 0);
- hv_store(hv, "pass_sent", 9, newSViv(client->pass_sent), 0);
- hv_store(hv, "user_sent", 9, newSViv(client->user_sent), 0);
- hv_store(hv, "connected", 9, newSViv(client->connected), 0);
- hv_store(hv, "want_ctcp", 9, newSViv(client->want_ctcp), 0);
- hv_store(hv, "ircnet", 6, new_pv(client->listen->ircnet), 0);
+ (void) hv_store(hv, "nick", 4, new_pv(client->nick), 0);
+ (void) hv_store(hv, "addr", 4, new_pv(client->addr), 0);
+ (void) hv_store(hv, "proxy_address", 13, new_pv(client->proxy_address), 0);
+ (void) hv_store(hv, "server", 6, iobject_bless(client->server), 0);
+ (void) hv_store(hv, "pass_sent", 9, newSViv(client->pass_sent), 0);
+ (void) hv_store(hv, "user_sent", 9, newSViv(client->user_sent), 0);
+ (void) hv_store(hv, "connected", 9, newSViv(client->connected), 0);
+ (void) hv_store(hv, "want_ctcp", 9, newSViv(client->want_ctcp), 0);
+ (void) hv_store(hv, "multiplex", 9, newSViv(client->multiplex), 0);
+ (void) hv_store(hv, "ircnet", 6, new_pv(client->listen->ircnet), 0);
}
static PLAIN_OBJECT_INIT_REC irc_plains[] = {
diff --git a/src/perl/irc/Makefile.PL.in b/src/perl/irc/Makefile.PL.in
index 8f1e94d5..582160a0 100644
--- a/src/perl/irc/Makefile.PL.in
+++ b/src/perl/irc/Makefile.PL.in
@@ -1,4 +1,4 @@
-use ExtUtils::MakeMaker;
+use ExtUtils::MakeMaker;our $AM_DEFAULT_VERBOSITY='@AM_DEFAULT_VERBOSITY@';require "@top_srcdir@/src/perl/Makefile_silent.pm";
WriteMakefile('NAME' => 'Irssi::Irc',
'LIBS' => '',
diff --git a/src/perl/irc/Server.xs b/src/perl/irc/Server.xs
index 0e9ec672..33417bf5 100644
--- a/src/perl/irc/Server.xs
+++ b/src/perl/irc/Server.xs
@@ -149,3 +149,12 @@ CODE:
OUTPUT:
RETVAL
+int
+irc_server_cap_toggle(server, cap, enable)
+ Irssi::Irc::Server server
+ char *cap
+ int enable
+CODE:
+ RETVAL = cap_toggle(server, cap, enable);
+OUTPUT:
+ RETVAL
diff --git a/src/perl/irc/module.h b/src/perl/irc/module.h
index 91c19c7a..a2454545 100644
--- a/src/perl/irc/module.h
+++ b/src/perl/irc/module.h
@@ -6,6 +6,7 @@
#include "irc-queries.h"
#include "irc-nicklist.h"
#include "irc-masks.h"
+#include "irc-cap.h"
#include "bans.h"
#include "modes.h"
diff --git a/src/perl/irssi-core.pl b/src/perl/irssi-core.pl
index 38265a80..46066a38 100644
--- a/src/perl/irssi-core.pl
+++ b/src/perl/irssi-core.pl
@@ -47,4 +47,8 @@ sub eval_file {
$data = qq{\n#line 1 "$filename"\n$data};
eval_data($data, $id);
+
+ if (exists ${"Irssi::Script::${id}::"}{IRSSI} && ${"Irssi::Script::${id}::"}{IRSSI}{name} =~ /cap.sasl/ && ${"Irssi::Script::${id}::VERSION"} < 2) {
+ die "cap_sasl has been unloaded from Irssi ".Irssi::version()." because it conflicts with the built-in SASL support. See /help network for configuring SASL or read the ChangeLog for more information.";
+ }
}
diff --git a/src/perl/module.h b/src/perl/module.h
index 2fd15137..3cbdf3d5 100644
--- a/src/perl/module.h
+++ b/src/perl/module.h
@@ -17,4 +17,4 @@ extern PerlInterpreter *my_perl; /* must be called my_perl or some perl implemen
/* Change this every time when some API changes between irssi's perl module
(or irssi itself) and irssi's perl libraries. */
-#define IRSSI_PERL_API_VERSION 20011214
+#define IRSSI_PERL_API_VERSION (20011214 + IRSSI_ABI_VERSION)
diff --git a/src/perl/perl-common.c b/src/perl/perl-common.c
index 1fbd000b..1d08319f 100644
--- a/src/perl/perl-common.c
+++ b/src/perl/perl-common.c
@@ -135,7 +135,7 @@ SV *irssi_bless_iobject(int type, int chat_type, void *object)
stash = gv_stashpv(rec->stash, 1);
hv = newHV();
- hv_store(hv, "_irssi", 6, create_sv_ptr(object), 0);
+ (void) hv_store(hv, "_irssi", 6, create_sv_ptr(object), 0);
rec->fill_func(hv, object);
return sv_bless(newRV_noinc((SV*)hv), stash);
}
@@ -148,7 +148,7 @@ SV *irssi_bless_plain(const char *stash, void *object)
fill_func = g_hash_table_lookup(plain_stashes, stash);
hv = newHV();
- hv_store(hv, "_irssi", 6, create_sv_ptr(object), 0);
+ (void) hv_store(hv, "_irssi", 6, create_sv_ptr(object), 0);
if (fill_func != NULL)
fill_func(hv, object);
return sv_bless(newRV_noinc((SV*)hv), gv_stashpv((char *)stash, 1));
@@ -246,10 +246,7 @@ char *perl_get_use_list(void)
void irssi_callXS(void (*subaddr)(pTHX_ CV* cv), CV *cv, SV **mark)
{
- dSP;
-
PUSHMARK(mark);
- PUTBACK;
(*subaddr)(aTHX_ cv);
}
@@ -264,17 +261,17 @@ void perl_chatnet_fill_hash(HV *hv, CHATNET_REC *chatnet)
type = "CHATNET";
chat_type = (char *) chat_protocol_find_id(chatnet->chat_type)->name;
- hv_store(hv, "type", 4, new_pv(type), 0);
- hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
+ (void) hv_store(hv, "type", 4, new_pv(type), 0);
+ (void) hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
- hv_store(hv, "name", 4, new_pv(chatnet->name), 0);
+ (void) hv_store(hv, "name", 4, new_pv(chatnet->name), 0);
- hv_store(hv, "nick", 4, new_pv(chatnet->nick), 0);
- hv_store(hv, "username", 8, new_pv(chatnet->username), 0);
- hv_store(hv, "realname", 8, new_pv(chatnet->realname), 0);
+ (void) hv_store(hv, "nick", 4, new_pv(chatnet->nick), 0);
+ (void) hv_store(hv, "username", 8, new_pv(chatnet->username), 0);
+ (void) hv_store(hv, "realname", 8, new_pv(chatnet->realname), 0);
- hv_store(hv, "own_host", 8, new_pv(chatnet->own_host), 0);
- hv_store(hv, "autosendcmd", 11, new_pv(chatnet->autosendcmd), 0);
+ (void) hv_store(hv, "own_host", 8, new_pv(chatnet->own_host), 0);
+ (void) hv_store(hv, "autosendcmd", 11, new_pv(chatnet->autosendcmd), 0);
}
void perl_connect_fill_hash(HV *hv, SERVER_CONNECT_REC *conn)
@@ -287,25 +284,26 @@ void perl_connect_fill_hash(HV *hv, SERVER_CONNECT_REC *conn)
type = "SERVER CONNECT";
chat_type = (char *) chat_protocol_find_id(conn->chat_type)->name;
- hv_store(hv, "type", 4, new_pv(type), 0);
- hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
+ (void) hv_store(hv, "type", 4, new_pv(type), 0);
+ (void) hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
- hv_store(hv, "tag", 3, new_pv(conn->tag), 0);
- hv_store(hv, "address", 7, new_pv(conn->address), 0);
- hv_store(hv, "port", 4, newSViv(conn->port), 0);
- hv_store(hv, "chatnet", 7, new_pv(conn->chatnet), 0);
+ (void) hv_store(hv, "tag", 3, new_pv(conn->tag), 0);
+ (void) hv_store(hv, "address", 7, new_pv(conn->address), 0);
+ (void) hv_store(hv, "port", 4, newSViv(conn->port), 0);
+ (void) hv_store(hv, "chatnet", 7, new_pv(conn->chatnet), 0);
- hv_store(hv, "password", 8, new_pv(conn->password), 0);
- hv_store(hv, "wanted_nick", 11, new_pv(conn->nick), 0);
- hv_store(hv, "username", 8, new_pv(conn->username), 0);
- hv_store(hv, "realname", 8, new_pv(conn->realname), 0);
+ (void) hv_store(hv, "password", 8, new_pv(conn->password), 0);
+ (void) hv_store(hv, "wanted_nick", 11, new_pv(conn->nick), 0);
+ (void) hv_store(hv, "username", 8, new_pv(conn->username), 0);
+ (void) hv_store(hv, "realname", 8, new_pv(conn->realname), 0);
- hv_store(hv, "reconnection", 12, newSViv(conn->reconnection), 0);
- hv_store(hv, "no_autojoin_channels", 20, newSViv(conn->no_autojoin_channels), 0);
- hv_store(hv, "no_autosendcmd", 14, newSViv(conn->no_autosendcmd), 0);
- hv_store(hv, "unix_socket", 11, newSViv(conn->unix_socket), 0);
- hv_store(hv, "use_ssl", 7, newSViv(conn->use_ssl), 0);
- hv_store(hv, "no_connect", 10, newSViv(conn->no_connect), 0);
+ (void) hv_store(hv, "reconnection", 12, newSViv(conn->reconnection), 0);
+ (void) hv_store(hv, "no_autojoin_channels", 20, newSViv(conn->no_autojoin_channels), 0);
+ (void) hv_store(hv, "no_autosendcmd", 14, newSViv(conn->no_autosendcmd), 0);
+ (void) hv_store(hv, "unix_socket", 11, newSViv(conn->unix_socket), 0);
+ (void) hv_store(hv, "use_ssl", 7, newSViv(conn->use_tls), 0);
+ (void) hv_store(hv, "use_tls", 7, newSViv(conn->use_tls), 0);
+ (void) hv_store(hv, "no_connect", 10, newSViv(conn->no_connect), 0);
}
void perl_server_fill_hash(HV *hv, SERVER_REC *server)
@@ -319,28 +317,28 @@ void perl_server_fill_hash(HV *hv, SERVER_REC *server)
perl_connect_fill_hash(hv, server->connrec);
type = "SERVER";
- hv_store(hv, "type", 4, new_pv(type), 0);
+ (void) hv_store(hv, "type", 4, new_pv(type), 0);
- hv_store(hv, "connect_time", 12, newSViv(server->connect_time), 0);
- hv_store(hv, "real_connect_time", 17, newSViv(server->real_connect_time), 0);
+ (void) hv_store(hv, "connect_time", 12, newSViv(server->connect_time), 0);
+ (void) hv_store(hv, "real_connect_time", 17, newSViv(server->real_connect_time), 0);
- hv_store(hv, "tag", 3, new_pv(server->tag), 0);
- hv_store(hv, "nick", 4, new_pv(server->nick), 0);
+ (void) hv_store(hv, "tag", 3, new_pv(server->tag), 0);
+ (void) hv_store(hv, "nick", 4, new_pv(server->nick), 0);
- hv_store(hv, "connected", 9, newSViv(server->connected), 0);
- hv_store(hv, "connection_lost", 15, newSViv(server->connection_lost), 0);
+ (void) hv_store(hv, "connected", 9, newSViv(server->connected), 0);
+ (void) hv_store(hv, "connection_lost", 15, newSViv(server->connection_lost), 0);
stash = gv_stashpv("Irssi::Rawlog", 0);
- hv_store(hv, "rawlog", 6, sv_bless(newRV_noinc(newSViv((IV)server->rawlog)), stash), 0);
+ (void) hv_store(hv, "rawlog", 6, sv_bless(newRV_noinc(newSViv((IV)server->rawlog)), stash), 0);
- hv_store(hv, "version", 7, new_pv(server->version), 0);
- hv_store(hv, "away_reason", 11, new_pv(server->away_reason), 0);
- hv_store(hv, "last_invite", 11, new_pv(server->last_invite), 0);
- hv_store(hv, "server_operator", 15, newSViv(server->server_operator), 0);
- hv_store(hv, "usermode_away", 13, newSViv(server->usermode_away), 0);
- hv_store(hv, "banned", 6, newSViv(server->banned), 0);
+ (void) hv_store(hv, "version", 7, new_pv(server->version), 0);
+ (void) hv_store(hv, "away_reason", 11, new_pv(server->away_reason), 0);
+ (void) hv_store(hv, "last_invite", 11, new_pv(server->last_invite), 0);
+ (void) hv_store(hv, "server_operator", 15, newSViv(server->server_operator), 0);
+ (void) hv_store(hv, "usermode_away", 13, newSViv(server->usermode_away), 0);
+ (void) hv_store(hv, "banned", 6, newSViv(server->banned), 0);
- hv_store(hv, "lag", 3, newSViv(server->lag), 0);
+ (void) hv_store(hv, "lag", 3, newSViv(server->lag), 0);
}
void perl_window_item_fill_hash(HV *hv, WI_ITEM_REC *item)
@@ -351,19 +349,21 @@ void perl_window_item_fill_hash(HV *hv, WI_ITEM_REC *item)
g_return_if_fail(item != NULL);
type = (char *) module_find_id_str("WINDOW ITEM TYPE", item->type);
- chat_type = (char *) chat_protocol_find_id(item->chat_type)->name;
- hv_store(hv, "type", 4, new_pv(type), 0);
- hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
+ (void) hv_store(hv, "type", 4, new_pv(type), 0);
+ if (item->chat_type) {
+ chat_type = (char *) chat_protocol_find_id(item->chat_type)->name;
+ (void) hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
+ }
if (item->server != NULL) {
- hv_store(hv, "server", 6, iobject_bless(item->server), 0);
+ (void) hv_store(hv, "server", 6, iobject_bless(item->server), 0);
}
- hv_store(hv, "visible_name", 12, new_pv(item->visible_name), 0);
+ (void) hv_store(hv, "visible_name", 12, new_pv(item->visible_name), 0);
- hv_store(hv, "createtime", 10, newSViv(item->createtime), 0);
- hv_store(hv, "data_level", 10, newSViv(item->data_level), 0);
- hv_store(hv, "hilight_color", 13, new_pv(item->hilight_color), 0);
+ (void) hv_store(hv, "createtime", 10, newSViv(item->createtime), 0);
+ (void) hv_store(hv, "data_level", 10, newSViv(item->data_level), 0);
+ (void) hv_store(hv, "hilight_color", 13, new_pv(item->hilight_color), 0);
}
void perl_channel_fill_hash(HV *hv, CHANNEL_REC *channel)
@@ -374,26 +374,26 @@ void perl_channel_fill_hash(HV *hv, CHANNEL_REC *channel)
perl_window_item_fill_hash(hv, (WI_ITEM_REC *) channel);
if (channel->ownnick != NULL)
- hv_store(hv, "ownnick", 7, iobject_bless(channel->ownnick), 0);
+ (void) hv_store(hv, "ownnick", 7, iobject_bless(channel->ownnick), 0);
- hv_store(hv, "name", 4, new_pv(channel->name), 0);
- hv_store(hv, "topic", 5, new_pv(channel->topic), 0);
- hv_store(hv, "topic_by", 8, new_pv(channel->topic_by), 0);
- hv_store(hv, "topic_time", 10, newSViv(channel->topic_time), 0);
+ (void) hv_store(hv, "name", 4, new_pv(channel->name), 0);
+ (void) hv_store(hv, "topic", 5, new_pv(channel->topic), 0);
+ (void) hv_store(hv, "topic_by", 8, new_pv(channel->topic_by), 0);
+ (void) hv_store(hv, "topic_time", 10, newSViv(channel->topic_time), 0);
- hv_store(hv, "no_modes", 8, newSViv(channel->no_modes), 0);
- hv_store(hv, "mode", 4, new_pv(channel->mode), 0);
- hv_store(hv, "limit", 5, newSViv(channel->limit), 0);
- hv_store(hv, "key", 3, new_pv(channel->key), 0);
+ (void) hv_store(hv, "no_modes", 8, newSViv(channel->no_modes), 0);
+ (void) hv_store(hv, "mode", 4, new_pv(channel->mode), 0);
+ (void) hv_store(hv, "limit", 5, newSViv(channel->limit), 0);
+ (void) hv_store(hv, "key", 3, new_pv(channel->key), 0);
- hv_store(hv, "chanop", 6, newSViv(channel->chanop), 0);
- hv_store(hv, "names_got", 9, newSViv(channel->names_got), 0);
- hv_store(hv, "wholist", 7, newSViv(channel->wholist), 0);
- hv_store(hv, "synced", 6, newSViv(channel->synced), 0);
+ (void) hv_store(hv, "chanop", 6, newSViv(channel->chanop), 0);
+ (void) hv_store(hv, "names_got", 9, newSViv(channel->names_got), 0);
+ (void) hv_store(hv, "wholist", 7, newSViv(channel->wholist), 0);
+ (void) hv_store(hv, "synced", 6, newSViv(channel->synced), 0);
- hv_store(hv, "joined", 6, newSViv(channel->joined), 0);
- hv_store(hv, "left", 4, newSViv(channel->left), 0);
- hv_store(hv, "kicked", 6, newSViv(channel->kicked), 0);
+ (void) hv_store(hv, "joined", 6, newSViv(channel->joined), 0);
+ (void) hv_store(hv, "left", 4, newSViv(channel->left), 0);
+ (void) hv_store(hv, "kicked", 6, newSViv(channel->kicked), 0);
}
void perl_query_fill_hash(HV *hv, QUERY_REC *query)
@@ -403,11 +403,11 @@ void perl_query_fill_hash(HV *hv, QUERY_REC *query)
perl_window_item_fill_hash(hv, (WI_ITEM_REC *) query);
- hv_store(hv, "name", 4, new_pv(query->name), 0);
- hv_store(hv, "last_unread_msg", 15, newSViv(query->last_unread_msg), 0);
- hv_store(hv, "address", 7, new_pv(query->address), 0);
- hv_store(hv, "server_tag", 10, new_pv(query->server_tag), 0);
- hv_store(hv, "unwanted", 8, newSViv(query->unwanted), 0);
+ (void) hv_store(hv, "name", 4, new_pv(query->name), 0);
+ (void) hv_store(hv, "last_unread_msg", 15, newSViv(query->last_unread_msg), 0);
+ (void) hv_store(hv, "address", 7, new_pv(query->address), 0);
+ (void) hv_store(hv, "server_tag", 10, new_pv(query->server_tag), 0);
+ (void) hv_store(hv, "unwanted", 8, newSViv(query->unwanted), 0);
}
void perl_nick_fill_hash(HV *hv, NICK_REC *nick)
@@ -420,31 +420,31 @@ void perl_nick_fill_hash(HV *hv, NICK_REC *nick)
type = "NICK";
chat_type = (char *) chat_protocol_find_id(nick->chat_type)->name;
- hv_store(hv, "type", 4, new_pv(type), 0);
- hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
+ (void) hv_store(hv, "type", 4, new_pv(type), 0);
+ (void) hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
- hv_store(hv, "nick", 4, new_pv(nick->nick), 0);
- hv_store(hv, "host", 4, new_pv(nick->host), 0);
- hv_store(hv, "realname", 8, new_pv(nick->realname), 0);
- hv_store(hv, "hops", 4, newSViv(nick->hops), 0);
+ (void) hv_store(hv, "nick", 4, new_pv(nick->nick), 0);
+ (void) hv_store(hv, "host", 4, new_pv(nick->host), 0);
+ (void) hv_store(hv, "realname", 8, new_pv(nick->realname), 0);
+ (void) hv_store(hv, "hops", 4, newSViv(nick->hops), 0);
- hv_store(hv, "gone", 4, newSViv(nick->gone), 0);
- hv_store(hv, "serverop", 8, newSViv(nick->serverop), 0);
+ (void) hv_store(hv, "gone", 4, newSViv(nick->gone), 0);
+ (void) hv_store(hv, "serverop", 8, newSViv(nick->serverop), 0);
- hv_store(hv, "op", 2, newSViv(nick->op), 0);
- hv_store(hv, "halfop", 6, newSViv(nick->halfop), 0);
- hv_store(hv, "voice", 5, newSViv(nick->voice), 0);
- hv_store(hv, "other", 5, newSViv(nick->prefixes[0]), 0);
- hv_store(hv, "prefixes", 8, new_pv(nick->prefixes), 0);
+ (void) hv_store(hv, "op", 2, newSViv(nick->op), 0);
+ (void) hv_store(hv, "halfop", 6, newSViv(nick->halfop), 0);
+ (void) hv_store(hv, "voice", 5, newSViv(nick->voice), 0);
+ (void) hv_store(hv, "other", 5, newSViv(nick->prefixes[0]), 0);
+ (void) hv_store(hv, "prefixes", 8, new_pv(nick->prefixes), 0);
- hv_store(hv, "last_check", 10, newSViv(nick->last_check), 0);
- hv_store(hv, "send_massjoin", 13, newSViv(nick->send_massjoin), 0);
+ (void) hv_store(hv, "last_check", 10, newSViv(nick->last_check), 0);
+ (void) hv_store(hv, "send_massjoin", 13, newSViv(nick->send_massjoin), 0);
}
static void perl_command_fill_hash(HV *hv, COMMAND_REC *cmd)
{
- hv_store(hv, "category", 8, new_pv(cmd->category), 0);
- hv_store(hv, "cmd", 3, new_pv(cmd->cmd), 0);
+ (void) hv_store(hv, "category", 8, new_pv(cmd->category), 0);
+ (void) hv_store(hv, "cmd", 3, new_pv(cmd->cmd), 0);
}
static void perl_ignore_fill_hash(HV *hv, IGNORE_REC *ignore)
@@ -452,22 +452,22 @@ static void perl_ignore_fill_hash(HV *hv, IGNORE_REC *ignore)
AV *av;
char **tmp;
- hv_store(hv, "mask", 4, new_pv(ignore->mask), 0);
- hv_store(hv, "servertag", 9, new_pv(ignore->servertag), 0);
+ (void) hv_store(hv, "mask", 4, new_pv(ignore->mask), 0);
+ (void) hv_store(hv, "servertag", 9, new_pv(ignore->servertag), 0);
av = newAV();
if (ignore->channels != NULL) {
for (tmp = ignore->channels; *tmp != NULL; tmp++) {
av_push(av, new_pv(*tmp));
}
}
- hv_store(hv, "channels", 8, newRV_noinc((SV*)av), 0);
- hv_store(hv, "pattern", 7, new_pv(ignore->pattern), 0);
+ (void) hv_store(hv, "channels", 8, newRV_noinc((SV*)av), 0);
+ (void) hv_store(hv, "pattern", 7, new_pv(ignore->pattern), 0);
- hv_store(hv, "level", 5, newSViv(ignore->level), 0);
+ (void) hv_store(hv, "level", 5, newSViv(ignore->level), 0);
- hv_store(hv, "exception", 9, newSViv(ignore->exception), 0);
- hv_store(hv, "regexp", 6, newSViv(ignore->regexp), 0);
- hv_store(hv, "fullword", 8, newSViv(ignore->fullword), 0);
+ (void) hv_store(hv, "exception", 9, newSViv(ignore->exception), 0);
+ (void) hv_store(hv, "regexp", 6, newSViv(ignore->regexp), 0);
+ (void) hv_store(hv, "fullword", 8, newSViv(ignore->fullword), 0);
}
static void perl_log_fill_hash(HV *hv, LOG_REC *log)
@@ -475,33 +475,33 @@ static void perl_log_fill_hash(HV *hv, LOG_REC *log)
AV *av;
GSList *tmp;
- hv_store(hv, "fname", 5, new_pv(log->fname), 0);
- hv_store(hv, "real_fname", 10, new_pv(log->real_fname), 0);
- hv_store(hv, "opened", 6, newSViv(log->opened), 0);
- hv_store(hv, "level", 5, newSViv(log->level), 0);
- hv_store(hv, "last", 4, newSViv(log->last), 0);
- hv_store(hv, "autoopen", 8, newSViv(log->autoopen), 0);
- hv_store(hv, "failed", 6, newSViv(log->failed), 0);
- hv_store(hv, "temp", 4, newSViv(log->temp), 0);
+ (void) hv_store(hv, "fname", 5, new_pv(log->fname), 0);
+ (void) hv_store(hv, "real_fname", 10, new_pv(log->real_fname), 0);
+ (void) hv_store(hv, "opened", 6, newSViv(log->opened), 0);
+ (void) hv_store(hv, "level", 5, newSViv(log->level), 0);
+ (void) hv_store(hv, "last", 4, newSViv(log->last), 0);
+ (void) hv_store(hv, "autoopen", 8, newSViv(log->autoopen), 0);
+ (void) hv_store(hv, "failed", 6, newSViv(log->failed), 0);
+ (void) hv_store(hv, "temp", 4, newSViv(log->temp), 0);
av = newAV();
for (tmp = log->items; tmp != NULL; tmp = tmp->next) {
av_push(av, plain_bless(tmp->data, "Irssi::Logitem"));
}
- hv_store(hv, "items", 5, newRV_noinc((SV*)av), 0);
+ (void) hv_store(hv, "items", 5, newRV_noinc((SV*)av), 0);
}
static void perl_log_item_fill_hash(HV *hv, LOG_ITEM_REC *item)
{
- hv_store(hv, "type", 4, newSViv(item->type), 0);
- hv_store(hv, "name", 4, new_pv(item->name), 0);
- hv_store(hv, "servertag", 9, new_pv(item->servertag), 0);
+ (void) hv_store(hv, "type", 4, newSViv(item->type), 0);
+ (void) hv_store(hv, "name", 4, new_pv(item->name), 0);
+ (void) hv_store(hv, "servertag", 9, new_pv(item->servertag), 0);
}
static void perl_rawlog_fill_hash(HV *hv, RAWLOG_REC *rawlog)
{
- hv_store(hv, "logging", 7, newSViv(rawlog->logging), 0);
- hv_store(hv, "nlines", 6, newSViv(rawlog->nlines), 0);
+ (void) hv_store(hv, "logging", 7, newSViv(rawlog->logging), 0);
+ (void) hv_store(hv, "nlines", 6, newSViv(rawlog->nlines), 0);
}
static void perl_reconnect_fill_hash(HV *hv, RECONNECT_REC *reconnect)
@@ -511,18 +511,18 @@ static void perl_reconnect_fill_hash(HV *hv, RECONNECT_REC *reconnect)
perl_connect_fill_hash(hv, reconnect->conn);
type = "RECONNECT";
- hv_store(hv, "type", 4, new_pv(type), 0);
+ (void) hv_store(hv, "type", 4, new_pv(type), 0);
- hv_store(hv, "tag", 3, newSViv(reconnect->tag), 0);
- hv_store(hv, "next_connect", 12, newSViv(reconnect->next_connect), 0);
+ (void) hv_store(hv, "tag", 3, newSViv(reconnect->tag), 0);
+ (void) hv_store(hv, "next_connect", 12, newSViv(reconnect->next_connect), 0);
}
static void perl_script_fill_hash(HV *hv, PERL_SCRIPT_REC *script)
{
- hv_store(hv, "name", 4, new_pv(script->name), 0);
- hv_store(hv, "package", 7, new_pv(script->package), 0);
- hv_store(hv, "path", 4, new_pv(script->path), 0);
- hv_store(hv, "data", 4, new_pv(script->data), 0);
+ (void) hv_store(hv, "name", 4, new_pv(script->name), 0);
+ (void) hv_store(hv, "package", 7, new_pv(script->package), 0);
+ (void) hv_store(hv, "path", 4, new_pv(script->path), 0);
+ (void) hv_store(hv, "data", 4, new_pv(script->data), 0);
}
static void remove_newlines(char *str)
@@ -629,7 +629,7 @@ static void perl_register_protocol(CHAT_PROTOCOL_REC *rec)
perl_eval_pv(code, TRUE);
}
- pcode = g_strdup_printf(find_use_code,
+ pcode = g_strdup_printf(find_use_code,
settings_get_str("perl_use_lib"), name);
sv = perl_eval_pv(pcode, TRUE);
g_free(pcode);
diff --git a/src/perl/perl-core.c b/src/perl/perl-core.c
index eb1bddee..e4bde559 100644
--- a/src/perl/perl-core.c
+++ b/src/perl/perl-core.c
@@ -41,10 +41,10 @@ GSList *perl_scripts;
PerlInterpreter *my_perl;
static int print_script_errors;
-static char *perl_args[] = {"", "-e", "0"};
+static char *perl_args[] = {"", "-e", "0", NULL};
#define IS_PERL_SCRIPT(file) \
- (strlen(file) > 3 && strcmp(file+strlen(file)-3, ".pl") == 0)
+ (strlen(file) > 3 && g_strcmp0(file+strlen(file)-3, ".pl") == 0)
static void perl_script_destroy_package(PERL_SCRIPT_REC *script)
{
@@ -82,16 +82,18 @@ static void perl_script_destroy(PERL_SCRIPT_REC *script)
extern void boot_DynaLoader(pTHX_ CV* cv);
#if PERL_STATIC_LIBS == 1
-extern void boot_Irssi(CV *cv);
+extern void boot_Irssi(pTHX_ CV *cv);
XS(boot_Irssi_Core)
{
dXSARGS;
+ PERL_UNUSED_VAR(items);
irssi_callXS(boot_Irssi, cv, mark);
irssi_boot(Irc);
irssi_boot(UI);
irssi_boot(TextUI);
+ /* Make sure to keep this in line with perl_scripts_deinit below. */
XSRETURN_YES;
}
#endif
@@ -121,9 +123,9 @@ void perl_scripts_init(void)
my_perl = perl_alloc();
perl_construct(my_perl);
- perl_parse(my_perl, xs_init, G_N_ELEMENTS(perl_args), perl_args, NULL);
+ perl_parse(my_perl, xs_init, G_N_ELEMENTS(perl_args)-1, perl_args, NULL);
#if PERL_STATIC_LIBS == 1
- perl_eval_pv("Irssi::Core::boot_Irssi_Core();", TRUE);
+ perl_eval_pv("Irssi::Core::->boot_Irssi_Core(0.9);", TRUE);
#endif
perl_common_start();
@@ -155,6 +157,17 @@ void perl_scripts_deinit(void)
/* Unload all perl libraries loaded with dynaloader */
perl_eval_pv("foreach my $lib (@DynaLoader::dl_modules) { if ($lib =~ /^Irssi\\b/) { $lib .= '::deinit();'; eval $lib; } }", TRUE);
+#if PERL_STATIC_LIBS == 1
+ /* If perl is statically built we should manually deinit the modules
+ which are booted in boot_Irssi_Core above */
+ perl_eval_pv("foreach my $lib (qw("
+ "Irssi" " "
+ "Irssi::Irc" " "
+ "Irssi::UI" " "
+ "Irssi::TextUI"
+ ")) { eval $lib . '::deinit();'; }", TRUE);
+#endif
+
/* We could unload all libraries .. but this crashes with some
libraries, probably because we don't call some deinit function..
Anyway, this would free some memory with /SCRIPT RESET, but it
@@ -314,7 +327,7 @@ PERL_SCRIPT_REC *perl_script_find(const char *name)
for (tmp = perl_scripts; tmp != NULL; tmp = tmp->next) {
PERL_SCRIPT_REC *rec = tmp->data;
- if (strcmp(rec->name, name) == 0)
+ if (g_strcmp0(rec->name, name) == 0)
return rec;
}
@@ -331,7 +344,7 @@ PERL_SCRIPT_REC *perl_script_find_package(const char *package)
for (tmp = perl_scripts; tmp != NULL; tmp = tmp->next) {
PERL_SCRIPT_REC *rec = tmp->data;
- if (strcmp(rec->package, package) == 0)
+ if (g_strcmp0(rec->package, package) == 0)
return rec;
}
@@ -380,7 +393,7 @@ int perl_get_api_version(void)
return IRSSI_PERL_API_VERSION;
}
-static void perl_scripts_autorun(void)
+void perl_scripts_autorun(void)
{
DIR *dirp;
struct dirent *dp;
@@ -466,3 +479,8 @@ void perl_core_deinit(void)
signal_remove("script error", (SIGNAL_FUNC) sig_script_error);
PERL_SYS_TERM();
}
+
+void perl_core_abicheck(int *version)
+{
+ *version = IRSSI_ABI_VERSION;
+}
diff --git a/src/perl/perl-core.h b/src/perl/perl-core.h
index b451cc5c..7390a6fd 100644
--- a/src/perl/perl-core.h
+++ b/src/perl/perl-core.h
@@ -16,6 +16,8 @@ extern GSList *perl_scripts;
void perl_scripts_init(void);
/* Destroy all perl scripts and deinitialize perl interpreter */
void perl_scripts_deinit(void);
+/* Load all the scripts in the autorun/ folder */
+void perl_scripts_autorun(void);
/* Load a perl script, path must be a full path. */
PERL_SCRIPT_REC *perl_script_load_file(const char *path);
diff --git a/src/perl/perl-fe.c b/src/perl/perl-fe.c
index 8e8469e9..396c80b7 100644
--- a/src/perl/perl-fe.c
+++ b/src/perl/perl-fe.c
@@ -119,8 +119,20 @@ static void cmd_script_unload(const char *data)
static void cmd_script_reset(const char *data)
{
+ void *free_arg;
+ GHashTable *optlist;
+
+ if (!cmd_get_params(data, &free_arg, 0 | PARAM_FLAG_OPTIONS,
+ "script reset", &optlist))
+ return;
+
perl_scripts_deinit();
perl_scripts_init();
+
+ if (g_hash_table_lookup(optlist, "autorun") != NULL)
+ perl_scripts_autorun();
+
+ cmd_params_free(free_arg);
}
static void cmd_script_list(void)
@@ -170,7 +182,7 @@ static void cmd_load(const char *data, SERVER_REC *server, void *item)
return;
len = strlen(rootmodule);
- if (len > 3 && strcmp(rootmodule + len - 3, ".pl") == 0) {
+ if (len > 3 && g_strcmp0(rootmodule + len - 3, ".pl") == 0) {
/* make /LOAD script.pl work as expected */
signal_stop();
cmd_script_load(data);
@@ -251,6 +263,7 @@ void fe_perl_init(void)
command_bind("script list", NULL, (SIGNAL_FUNC) cmd_script_list);
command_bind("load", NULL, (SIGNAL_FUNC) cmd_load);
command_set_options("script exec", "permanent");
+ command_set_options("script reset", "autorun");
signal_add("script error", (SIGNAL_FUNC) sig_script_error);
signal_add("complete command script load", (SIGNAL_FUNC) sig_complete_load);
@@ -278,3 +291,8 @@ void fe_perl_deinit(void)
perl_core_print_script_error(TRUE);
}
+
+void fe_perl_abicheck(int *version)
+{
+ *version = IRSSI_ABI_VERSION;
+}
diff --git a/src/perl/perl-signals.c b/src/perl/perl-signals.c
index be4a9a5d..8f993660 100644
--- a/src/perl/perl-signals.c
+++ b/src/perl/perl-signals.c
@@ -99,14 +99,14 @@ void perl_signal_args_to_c(
if (!SvOK(arg)) {
c_arg = NULL;
- } else if (strcmp(rec->args[n], "string") == 0) {
+ } else if (g_strcmp0(rec->args[n], "string") == 0) {
c_arg = SvPV_nolen(arg);
- } else if (strcmp(rec->args[n], "int") == 0) {
+ } else if (g_strcmp0(rec->args[n], "int") == 0) {
c_arg = (void *)SvIV(arg);
- } else if (strcmp(rec->args[n], "ulongptr") == 0) {
+ } else if (g_strcmp0(rec->args[n], "ulongptr") == 0) {
saved_args[n].v_ulong = SvUV(arg);
c_arg = &saved_args[n].v_ulong;
- } else if (strcmp(rec->args[n], "intptr") == 0) {
+ } else if (g_strcmp0(rec->args[n], "intptr") == 0) {
saved_args[n].v_int = SvIV(SvRV(arg));
c_arg = &saved_args[n].v_int;
} else if (strncmp(rec->args[n], "glistptr_", 9) == 0) {
@@ -122,7 +122,7 @@ void perl_signal_args_to_c(
}
av = (AV *)t;
- is_str = strcmp(rec->args[n]+9, "char*") == 0;
+ is_str = g_strcmp0(rec->args[n]+9, "char*") == 0;
gl = NULL;
count = av_len(av) + 1;
@@ -181,7 +181,7 @@ void perl_signal_args_to_c(
continue;
}
- if (strcmp(rec->args[n], "intptr") == 0) {
+ if (g_strcmp0(rec->args[n], "intptr") == 0) {
SV *t = SvRV(arg);
SvIOK_only(t);
SvIV_set(t, saved_args[n].v_int);
@@ -192,8 +192,8 @@ void perl_signal_args_to_c(
AV *av;
GList *gl, *tmp;
- is_iobject = strcmp(rec->args[n]+9, "iobject") == 0;
- is_str = strcmp(rec->args[n]+9, "char*") == 0;
+ is_iobject = g_strcmp0(rec->args[n]+9, "iobject") == 0;
+ is_str = g_strcmp0(rec->args[n]+9, "char*") == 0;
av = (AV *)SvRV(arg);
av_clear(av);
@@ -245,8 +245,8 @@ static void perl_call_signal(PERL_SCRIPT_REC *script, SV *func,
GList *tmp, **ptr;
int is_iobject, is_str;
- is_iobject = strcmp(rec->args[n]+9, "iobject") == 0;
- is_str = strcmp(rec->args[n]+9, "char*") == 0;
+ is_iobject = g_strcmp0(rec->args[n]+9, "iobject") == 0;
+ is_str = g_strcmp0(rec->args[n]+9, "char*") == 0;
av = newAV();
ptr = arg;
@@ -258,22 +258,22 @@ static void perl_call_signal(PERL_SCRIPT_REC *script, SV *func,
}
saved_args[n] = perlarg = newRV_noinc((SV *) av);
- } else if (strcmp(rec->args[n], "int") == 0)
+ } else if (g_strcmp0(rec->args[n], "int") == 0)
perlarg = newSViv((IV)arg);
else if (arg == NULL)
perlarg = &PL_sv_undef;
- else if (strcmp(rec->args[n], "string") == 0)
+ else if (g_strcmp0(rec->args[n], "string") == 0)
perlarg = new_pv(arg);
- else if (strcmp(rec->args[n], "ulongptr") == 0)
+ else if (g_strcmp0(rec->args[n], "ulongptr") == 0)
perlarg = newSViv(*(unsigned long *) arg);
- else if (strcmp(rec->args[n], "intptr") == 0)
+ else if (g_strcmp0(rec->args[n], "intptr") == 0)
saved_args[n] = perlarg = newRV_noinc(newSViv(*(int *) arg));
else if (strncmp(rec->args[n], "gslist_", 7) == 0) {
/* linked list - push as AV */
GSList *tmp;
int is_iobject;
- is_iobject = strcmp(rec->args[n]+7, "iobject") == 0;
+ is_iobject = g_strcmp0(rec->args[n]+7, "iobject") == 0;
av = newAV();
for (tmp = arg; tmp != NULL; tmp = tmp->next) {
sv = is_iobject ? iobject_bless((SERVER_REC *) tmp->data) :
@@ -282,12 +282,12 @@ static void perl_call_signal(PERL_SCRIPT_REC *script, SV *func,
}
perlarg = newRV_noinc((SV *) av);
- } else if (strcmp(rec->args[n], "iobject") == 0) {
+ } else if (g_strcmp0(rec->args[n], "iobject") == 0) {
/* "irssi object" - any struct that has
"int type; int chat_type" as it's first
variables (server, channel, ..) */
perlarg = iobject_bless((SERVER_REC *) arg);
- } else if (strcmp(rec->args[n], "siobject") == 0) {
+ } else if (g_strcmp0(rec->args[n], "siobject") == 0) {
/* "simple irssi object" - any struct that has
int type; as it's first variable (dcc) */
perlarg = simple_iobject_bless((SERVER_REC *) arg);
@@ -317,7 +317,7 @@ static void perl_call_signal(PERL_SCRIPT_REC *script, SV *func,
if (saved_args[n] == NULL)
continue;
- if (strcmp(rec->args[n], "intptr") == 0) {
+ if (g_strcmp0(rec->args[n], "intptr") == 0) {
int *val = arg;
*val = SvIV(SvRV(saved_args[n]));
} else if (strncmp(rec->args[n], "glistptr_", 9) == 0) {
@@ -338,7 +338,7 @@ static void perl_call_signal(PERL_SCRIPT_REC *script, SV *func,
out = g_list_append(out, val);
}
- if (strcmp(rec->args[n]+9, "char*") == 0)
+ if (g_strcmp0(rec->args[n]+9, "char*") == 0)
g_list_foreach(*ret, (GFunc) g_free, NULL);
g_list_free(*ret);
*ret = out;
@@ -433,8 +433,9 @@ static void perl_signal_remove_list_one(GSList **siglist, PERL_SIGNAL_REC *rec)
}
#define sv_func_cmp(f1, f2) \
- (f1 == f2 || (SvPOK(f1) && SvPOK(f2) && \
- strcmp(SvPV_nolen(f1), SvPV_nolen(f2)) == 0))
+ ((SvROK(f1) && SvROK(f2) && SvRV(f1) == SvRV(f2)) || \
+ (SvPOK(f1) && SvPOK(f2) && \
+ g_strcmp0(SvPV_nolen(f1), SvPV_nolen(f2)) == 0))
static void perl_signal_remove_list(GSList **list, SV *func)
{
@@ -475,7 +476,7 @@ void perl_command_bind_to(const char *cmd, const char *category,
g_free(signal);
}
-void perl_command_runsub(const char *cmd, const char *data,
+void perl_command_runsub(const char *cmd, const char *data,
SERVER_REC *server, WI_ITEM_REC *item)
{
command_runsub(cmd, data, server, item);
diff --git a/src/perl/perl-signals.h b/src/perl/perl-signals.h
index e2c3db61..78a95bed 100644
--- a/src/perl/perl-signals.h
+++ b/src/perl/perl-signals.h
@@ -21,7 +21,7 @@ void perl_command_bind_to(const char *cmd, const char *category,
void perl_command_unbind(const char *cmd, SV *func);
-void perl_command_runsub(const char *cmd, const char *data,
+void perl_command_runsub(const char *cmd, const char *data,
SERVER_REC *server, WI_ITEM_REC *item);
void perl_signal_register(const char *signal, const char **args);
diff --git a/src/perl/textui/Makefile.PL.in b/src/perl/textui/Makefile.PL.in
index 9e80274b..ffdda21a 100644
--- a/src/perl/textui/Makefile.PL.in
+++ b/src/perl/textui/Makefile.PL.in
@@ -1,4 +1,4 @@
-use ExtUtils::MakeMaker;
+use ExtUtils::MakeMaker;our $AM_DEFAULT_VERBOSITY='@AM_DEFAULT_VERBOSITY@';require "@top_srcdir@/src/perl/Makefile_silent.pm";
WriteMakefile('NAME' => 'Irssi::TextUI',
'LIBS' => '',
diff --git a/src/perl/textui/Statusbar.xs b/src/perl/textui/Statusbar.xs
index 620fad9a..8b0e5f65 100644
--- a/src/perl/textui/Statusbar.xs
+++ b/src/perl/textui/Statusbar.xs
@@ -77,7 +77,10 @@ static void perl_statusbar_event(char *function, SBAR_ITEM_REC *item,
/* make sure we don't get back here */
script_unregister_statusbars(script);
}
- signal_emit("script error", 2, script, SvPV_nolen(ERRSV));
+
+ char *error = g_strdup(SvPV_nolen(ERRSV));
+ signal_emit("script error", 2, script, error);
+ g_free(error);
} else {
/* min_size and max_size can be changed, move them to SBAR_ITEM_REC */
hv = hvref(item_sv);
@@ -160,5 +163,5 @@ CODE:
*str == '\0' ? NULL : str,
data, escape_vars);
hv = hvref(ST(0));
- hv_store(hv, "min_size", 8, newSViv(item->min_size), 0);
- hv_store(hv, "max_size", 8, newSViv(item->max_size), 0);
+ (void) hv_store(hv, "min_size", 8, newSViv(item->min_size), 0);
+ (void) hv_store(hv, "max_size", 8, newSViv(item->max_size), 0);
diff --git a/src/perl/textui/TextUI.xs b/src/perl/textui/TextUI.xs
index ae986aaf..5d2c8a7f 100644
--- a/src/perl/textui/TextUI.xs
+++ b/src/perl/textui/TextUI.xs
@@ -8,72 +8,72 @@ static int initialized = FALSE;
static void perl_main_window_fill_hash(HV *hv, MAIN_WINDOW_REC *window)
{
- hv_store(hv, "active", 6, plain_bless(window->active, "Irssi::UI::Window"), 0);
+ (void) hv_store(hv, "active", 6, plain_bless(window->active, "Irssi::UI::Window"), 0);
- hv_store(hv, "first_line", 10, newSViv(window->first_line), 0);
- hv_store(hv, "last_line", 9, newSViv(window->last_line), 0);
- hv_store(hv, "width", 5, newSViv(window->width), 0);
- hv_store(hv, "height", 6, newSViv(window->height), 0);
+ (void) hv_store(hv, "first_line", 10, newSViv(window->first_line), 0);
+ (void) hv_store(hv, "last_line", 9, newSViv(window->last_line), 0);
+ (void) hv_store(hv, "width", 5, newSViv(window->width), 0);
+ (void) hv_store(hv, "height", 6, newSViv(window->height), 0);
- hv_store(hv, "statusbar_lines", 15, newSViv(window->statusbar_lines), 0);
+ (void) hv_store(hv, "statusbar_lines", 15, newSViv(window->statusbar_lines), 0);
}
static void perl_text_buffer_fill_hash(HV *hv, TEXT_BUFFER_REC *buffer)
{
- hv_store(hv, "first_line", 10, plain_bless(buffer->first_line, "Irssi::TextUI::Line"), 0);
- hv_store(hv, "lines_count", 11, newSViv(buffer->lines_count), 0);
- hv_store(hv, "cur_line", 8, plain_bless(buffer->cur_line, "Irssi::TextUI::Line"), 0);
- hv_store(hv, "last_eol", 8, newSViv(buffer->last_eol), 0);
+ (void) hv_store(hv, "first_line", 10, plain_bless(buffer->first_line, "Irssi::TextUI::Line"), 0);
+ (void) hv_store(hv, "lines_count", 11, newSViv(buffer->lines_count), 0);
+ (void) hv_store(hv, "cur_line", 8, plain_bless(buffer->cur_line, "Irssi::TextUI::Line"), 0);
+ (void) hv_store(hv, "last_eol", 8, newSViv(buffer->last_eol), 0);
}
static void perl_text_buffer_view_fill_hash(HV *hv, TEXT_BUFFER_VIEW_REC *view)
{
- hv_store(hv, "buffer", 6, plain_bless(view->buffer, "Irssi::TextUI::TextBuffer"), 0);
- hv_store(hv, "width", 5, newSViv(view->width), 0);
- hv_store(hv, "height", 6, newSViv(view->height), 0);
+ (void) hv_store(hv, "buffer", 6, plain_bless(view->buffer, "Irssi::TextUI::TextBuffer"), 0);
+ (void) hv_store(hv, "width", 5, newSViv(view->width), 0);
+ (void) hv_store(hv, "height", 6, newSViv(view->height), 0);
- hv_store(hv, "default_indent", 14, newSViv(view->default_indent), 0);
- hv_store(hv, "longword_noindent", 17, newSViv(view->longword_noindent), 0);
- hv_store(hv, "scroll", 6, newSViv(view->scroll), 0);
+ (void) hv_store(hv, "default_indent", 14, newSViv(view->default_indent), 0);
+ (void) hv_store(hv, "longword_noindent", 17, newSViv(view->longword_noindent), 0);
+ (void) hv_store(hv, "scroll", 6, newSViv(view->scroll), 0);
- hv_store(hv, "ypos", 4, newSViv(view->ypos), 0);
+ (void) hv_store(hv, "ypos", 4, newSViv(view->ypos), 0);
- hv_store(hv, "startline", 9, plain_bless(view->startline, "Irssi::TextUI::Line"), 0);
- hv_store(hv, "subline", 7, newSViv(view->subline), 0);
+ (void) hv_store(hv, "startline", 9, plain_bless(view->startline, "Irssi::TextUI::Line"), 0);
+ (void) hv_store(hv, "subline", 7, newSViv(view->subline), 0);
- hv_store(hv, "bottom_startline", 16, plain_bless(view->bottom_startline, "Irssi::TextUI::Line"), 0);
- hv_store(hv, "bottom_subline", 14, newSViv(view->bottom_subline), 0);
+ (void) hv_store(hv, "bottom_startline", 16, plain_bless(view->bottom_startline, "Irssi::TextUI::Line"), 0);
+ (void) hv_store(hv, "bottom_subline", 14, newSViv(view->bottom_subline), 0);
- hv_store(hv, "empty_linecount", 15, newSViv(view->empty_linecount), 0);
- hv_store(hv, "bottom", 6, newSViv(view->bottom), 0);
+ (void) hv_store(hv, "empty_linecount", 15, newSViv(view->empty_linecount), 0);
+ (void) hv_store(hv, "bottom", 6, newSViv(view->bottom), 0);
}
static void perl_line_fill_hash(HV *hv, LINE_REC *line)
{
- hv_store(hv, "info", 4, plain_bless(&line->info, "Irssi::TextUI::LineInfo"), 0);
+ (void) hv_store(hv, "info", 4, plain_bless(&line->info, "Irssi::TextUI::LineInfo"), 0);
}
static void perl_line_cache_fill_hash(HV *hv, LINE_CACHE_REC *cache)
{
- hv_store(hv, "last_access", 11, newSViv(cache->last_access), 0);
- hv_store(hv, "count", 5, newSViv(cache->count), 0);
+ (void) hv_store(hv, "last_access", 11, newSViv(cache->last_access), 0);
+ (void) hv_store(hv, "count", 5, newSViv(cache->count), 0);
/*LINE_CACHE_SUB_REC lines[1];*/
}
static void perl_line_info_fill_hash(HV *hv, LINE_INFO_REC *info)
{
- hv_store(hv, "level", 5, newSViv(info->level), 0);
- hv_store(hv, "time", 4, newSViv(info->time), 0);
+ (void) hv_store(hv, "level", 5, newSViv(info->level), 0);
+ (void) hv_store(hv, "time", 4, newSViv(info->time), 0);
}
static void perl_statusbar_item_fill_hash(HV *hv, SBAR_ITEM_REC *item)
{
- hv_store(hv, "min_size", 8, newSViv(item->min_size), 0);
- hv_store(hv, "max_size", 8, newSViv(item->max_size), 0);
- hv_store(hv, "xpos", 4, newSViv(item->xpos), 0);
- hv_store(hv, "size", 4, newSViv(item->size), 0);
+ (void) hv_store(hv, "min_size", 8, newSViv(item->min_size), 0);
+ (void) hv_store(hv, "max_size", 8, newSViv(item->max_size), 0);
+ (void) hv_store(hv, "xpos", 4, newSViv(item->xpos), 0);
+ (void) hv_store(hv, "size", 4, newSViv(item->size), 0);
if (item->bar->parent_window != NULL)
- hv_store(hv, "window", 6, plain_bless(item->bar->parent_window->active, "Irssi::UI::Window"), 0);
+ (void) hv_store(hv, "window", 6, plain_bless(item->bar->parent_window->active, "Irssi::UI::Window"), 0);
}
static PLAIN_OBJECT_INIT_REC textui_plains[] = {
diff --git a/src/perl/ui/Makefile.PL.in b/src/perl/ui/Makefile.PL.in
index a349918e..ceed51c3 100644
--- a/src/perl/ui/Makefile.PL.in
+++ b/src/perl/ui/Makefile.PL.in
@@ -1,4 +1,4 @@
-use ExtUtils::MakeMaker;
+use ExtUtils::MakeMaker;our $AM_DEFAULT_VERBOSITY='@AM_DEFAULT_VERBOSITY@';require "@top_srcdir@/src/perl/Makefile_silent.pm";
WriteMakefile('NAME' => 'Irssi::UI',
'LIBS' => '',
diff --git a/src/perl/ui/UI.xs b/src/perl/ui/UI.xs
index 2e32762c..5ac3da4e 100644
--- a/src/perl/ui/UI.xs
+++ b/src/perl/ui/UI.xs
@@ -8,60 +8,73 @@ static int initialized = FALSE;
static void perl_process_fill_hash(HV *hv, PROCESS_REC *process)
{
- hv_store(hv, "id", 2, newSViv(process->id), 0);
- hv_store(hv, "name", 4, new_pv(process->name), 0);
- hv_store(hv, "args", 4, new_pv(process->args), 0);
+ (void) hv_store(hv, "id", 2, newSViv(process->id), 0);
+ (void) hv_store(hv, "name", 4, new_pv(process->name), 0);
+ (void) hv_store(hv, "args", 4, new_pv(process->args), 0);
- hv_store(hv, "pid", 3, newSViv(process->pid), 0);
- hv_store(hv, "target", 6, new_pv(process->target), 0);
+ (void) hv_store(hv, "pid", 3, newSViv(process->pid), 0);
+ (void) hv_store(hv, "target", 6, new_pv(process->target), 0);
if (process->target_win != NULL) {
- hv_store(hv, "target_win", 10,
+ (void) hv_store(hv, "target_win", 10,
plain_bless(process->target_win, "Irssi::UI::Window"), 0);
}
- hv_store(hv, "shell", 5, newSViv(process->shell), 0);
- hv_store(hv, "notice", 6, newSViv(process->notice), 0);
- hv_store(hv, "silent", 6, newSViv(process->silent), 0);
+ (void) hv_store(hv, "shell", 5, newSViv(process->shell), 0);
+ (void) hv_store(hv, "notice", 6, newSViv(process->notice), 0);
+ (void) hv_store(hv, "silent", 6, newSViv(process->silent), 0);
}
static void perl_window_fill_hash(HV *hv, WINDOW_REC *window)
{
- hv_store(hv, "refnum", 6, newSViv(window->refnum), 0);
- hv_store(hv, "name", 4, new_pv(window->name), 0);
- hv_store(hv, "history_name", 12, new_pv(window->history_name), 0);
+ (void) hv_store(hv, "refnum", 6, newSViv(window->refnum), 0);
+ (void) hv_store(hv, "name", 4, new_pv(window->name), 0);
+ (void) hv_store(hv, "history_name", 12, new_pv(window->history_name), 0);
- hv_store(hv, "width", 5, newSViv(window->width), 0);
- hv_store(hv, "height", 6, newSViv(window->height), 0);
+ (void) hv_store(hv, "width", 5, newSViv(window->width), 0);
+ (void) hv_store(hv, "height", 6, newSViv(window->height), 0);
if (window->active)
- hv_store(hv, "active", 6, iobject_bless(window->active), 0);
+ (void) hv_store(hv, "active", 6, iobject_bless(window->active), 0);
if (window->active_server)
- hv_store(hv, "active_server", 13, iobject_bless(window->active_server), 0);
+ (void) hv_store(hv, "active_server", 13, iobject_bless(window->active_server), 0);
- hv_store(hv, "servertag", 9, new_pv(window->servertag), 0);
- hv_store(hv, "level", 5, newSViv(window->level), 0);
+ (void) hv_store(hv, "servertag", 9, new_pv(window->servertag), 0);
+ (void) hv_store(hv, "level", 5, newSViv(window->level), 0);
- hv_store(hv, "immortal", 8, newSViv(window->immortal), 0);
- hv_store(hv, "sticky_refnum", 13, newSViv(window->sticky_refnum), 0);
+ (void) hv_store(hv, "immortal", 8, newSViv(window->immortal), 0);
+ (void) hv_store(hv, "sticky_refnum", 13, newSViv(window->sticky_refnum), 0);
- hv_store(hv, "data_level", 10, newSViv(window->data_level), 0);
- hv_store(hv, "hilight_color", 13, new_pv(window->hilight_color), 0);
+ (void) hv_store(hv, "data_level", 10, newSViv(window->data_level), 0);
+ (void) hv_store(hv, "hilight_color", 13, new_pv(window->hilight_color), 0);
- hv_store(hv, "last_timestamp", 14, newSViv(window->last_timestamp), 0);
- hv_store(hv, "last_line", 9, newSViv(window->last_line), 0);
+ (void) hv_store(hv, "last_timestamp", 14, newSViv(window->last_timestamp), 0);
+ (void) hv_store(hv, "last_line", 9, newSViv(window->last_line), 0);
- hv_store(hv, "theme", 5, plain_bless(window->theme, "Irssi::UI::Theme"), 0);
- hv_store(hv, "theme_name", 10, new_pv(window->theme_name), 0);
+ (void) hv_store(hv, "theme", 5, plain_bless(window->theme, "Irssi::UI::Theme"), 0);
+ (void) hv_store(hv, "theme_name", 10, new_pv(window->theme_name), 0);
}
static void perl_text_dest_fill_hash(HV *hv, TEXT_DEST_REC *dest)
{
- hv_store(hv, "window", 6, plain_bless(dest->window, "Irssi::UI::Window"), 0);
- hv_store(hv, "server", 6, iobject_bless(dest->server), 0);
- hv_store(hv, "target", 6, new_pv(dest->target), 0);
- hv_store(hv, "level", 5, newSViv(dest->level), 0);
+ (void) hv_store(hv, "window", 6, plain_bless(dest->window, "Irssi::UI::Window"), 0);
+ (void) hv_store(hv, "server", 6, iobject_bless(dest->server), 0);
+ (void) hv_store(hv, "target", 6, new_pv(dest->target), 0);
+ (void) hv_store(hv, "level", 5, newSViv(dest->level), 0);
- hv_store(hv, "hilight_priority", 16, newSViv(dest->hilight_priority), 0);
- hv_store(hv, "hilight_color", 13, new_pv(dest->hilight_color), 0);
+ (void) hv_store(hv, "hilight_priority", 16, newSViv(dest->hilight_priority), 0);
+ (void) hv_store(hv, "hilight_color", 13, new_pv(dest->hilight_color), 0);
+}
+
+static void perl_exec_fill_hash(HV *hv, EXEC_WI_REC *item)
+{
+ g_return_if_fail(hv != NULL);
+ g_return_if_fail(item != NULL);
+
+ perl_window_item_fill_hash(hv, (WI_ITEM_REC *) item);
+ /* we don't bless to Process here to avoid infinite recursion
+ in the simplistic script binding */
+ if (item->process != NULL) {
+ (void) hv_store(hv, "process_id", 10, newSViv(item->process->id), 0);
+ }
}
static PLAIN_OBJECT_INIT_REC fe_plains[] = {
@@ -94,6 +107,10 @@ CODE:
initialized = TRUE;
irssi_add_plains(fe_plains);
+ /* window items: fe-exec */
+ irssi_add_object(module_get_uniq_id_str("WINDOW ITEM TYPE", "EXEC"),
+ 0, "Irssi::UI::Exec",
+ (PERL_OBJECT_FUNC) perl_exec_fill_hash);
perl_themes_init();
void
diff --git a/syntax.pl b/syntax.pl
index bf10451f..33bd12b4 100755
--- a/syntax.pl
+++ b/syntax.pl
@@ -10,7 +10,7 @@
# Remember to include the asterisk ('*').
$SRC_PATH='src';
-@files = `find src -name '*.c'`;
+@files = sort `find src -name '*.c'`;
foreach $file (@files) {
open (FILE, "$file");