diff options
-rwxr-xr-x | autogen.sh | 4 | ||||
-rw-r--r-- | docs/Makefile.am | 2 | ||||
-rw-r--r-- | docs/help/in/lastlog.in | 2 | ||||
-rw-r--r-- | docs/help/in/list.in | 15 | ||||
-rw-r--r-- | docs/irssi.1 | 64 | ||||
-rw-r--r-- | docs/signals.txt | 5 | ||||
-rwxr-xr-x | irssi-version.sh | 6 | ||||
-rw-r--r-- | scripts/Makefile.am | 1 | ||||
-rw-r--r-- | scripts/autoop.pl | 73 | ||||
-rw-r--r-- | scripts/autorejoin.pl | 71 | ||||
-rw-r--r-- | scripts/buf.pl | 17 | ||||
-rw-r--r-- | scripts/dns.pl | 22 | ||||
-rw-r--r-- | scripts/kills.pl | 9 | ||||
-rw-r--r-- | scripts/mail.pl | 4 | ||||
-rw-r--r-- | scripts/mlock.pl | 2 | ||||
-rw-r--r-- | scripts/quitmsg.pl | 4 | ||||
-rw-r--r-- | scripts/sb_search.pl | 142 | ||||
-rw-r--r-- | scripts/usercount.pl | 3 | ||||
-rw-r--r-- | src/core/misc.c | 24 | ||||
-rw-r--r-- | src/core/misc.h | 3 | ||||
-rw-r--r-- | src/fe-common/core/module-formats.c | 4 | ||||
-rw-r--r-- | src/fe-text/terminfo-core.c | 111 | ||||
-rw-r--r-- | src/fe-text/terminfo-core.h | 4 | ||||
-rw-r--r-- | src/irc/core/Makefile.am | 2 | ||||
-rw-r--r-- | src/irc/core/irc-cap.c | 191 | ||||
-rw-r--r-- | src/irc/core/irc-cap.h | 9 | ||||
-rw-r--r-- | src/irc/core/irc-core.c | 3 | ||||
-rw-r--r-- | src/irc/core/irc-servers.c | 16 | ||||
-rw-r--r-- | src/irc/core/irc-servers.h | 7 | ||||
-rw-r--r-- | src/irc/proxy/dump.c | 38 | ||||
-rw-r--r-- | src/irc/proxy/listen.c | 16 |
31 files changed, 543 insertions, 331 deletions
@@ -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 diff --git a/docs/Makefile.am b/docs/Makefile.am index 861a2ca4..5e222564 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -1,3 +1,5 @@ +docdir = $(datadir)/doc/irssi + man_MANS = \ irssi.1 diff --git a/docs/help/in/lastlog.in b/docs/help/in/lastlog.in index 25879d31..e96e2ed5 100644 --- a/docs/help/in/lastlog.in +++ b/docs/help/in/lastlog.in @@ -35,7 +35,7 @@ /LASTLOG holiday /LASTLOG 'is on vacation' 10 - /LASTLOG -file -force ~/mike.log 'mike' + /LASTLOG -force -file ~/mike.log 'mike' /LASTLOG -hilight /LASTLOG -5 searchterm diff --git a/docs/help/in/list.in b/docs/help/in/list.in index 14858337..33f05e8b 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 + +%9Remarks:%9 + + Not all networks support server-side filtering and may provide a network + service instead; on IRCnet, you may use the ALIS service: + + /QUOTE SQUERY ALIS :HELP + /MSG ALIS HELP %9See also:%9 QUOTE, STATS, WHOIS diff --git a/docs/irssi.1 b/docs/irssi.1 index e86be7ca..f38b639e 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,53 @@ 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. +for your irc session .TP .BI "\-d, \-\-dummy" -use dummy terminal mode. +use dummy terminal mode .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/signals.txt b/docs/signals.txt index f0860d3e..45658b79 100644 --- a/docs/signals.txt +++ b/docs/signals.txt @@ -131,6 +131,11 @@ 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 + irc.c: "server event", SERVER_REC, char *data, char *sender_nick, char *sender_address diff --git a/irssi-version.sh b/irssi-version.sh index 7588182d..1e9eae34 100755 --- a/irssi-version.sh +++ b/irssi-version.sh @@ -5,6 +5,12 @@ 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" diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 23eb7198..cd795153 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -11,7 +11,6 @@ script_DATA = \ mail.pl \ mlock.pl \ quitmsg.pl \ - sb_search.pl \ scriptassist.pl \ usercount.pl 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..da50e821 100644 --- a/scripts/buf.pl +++ b/scripts/buf.pl @@ -40,7 +40,7 @@ use Data::Dumper; my %suppress; sub upgrade { - open BUF, sprintf('>%s/scrollbuffer', get_irssi_dir) or die $!; + open BUF, q{>}, sprintf('%s/scrollbuffer', get_irssi_dir) or die $!; print BUF join("\0", map $_->{server}->{address} . $_->{name}, channels), "\n"; for my $window (windows) { next unless defined $window; @@ -66,7 +66,7 @@ sub upgrade { } sub restore { - open BUF, sprintf('<%s/scrollbuffer', get_irssi_dir) or die $!; + open BUF, q{<}, sprintf('%s/scrollbuffer', get_irssi_dir) or die $!; my @suppress = split /\0/, <BUF>; if (settings_get_bool 'upgrade_suppress_join') { chomp $suppress[-1]; @@ -98,14 +98,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}; } } 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..190c33af 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", @@ -114,7 +116,7 @@ sub mbox_count { $last_mtime = $mtime; my $f = gensym; - return 0 if (!open($f, $mailfile)); + return 0 if (!open($f, "<", $mailfile)); # count new mails only my $internal_removed = 0; 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/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/core/misc.c b/src/core/misc.c index ef8501d5..88c27255 100644 --- a/src/core/misc.c +++ b/src/core/misc.c @@ -211,6 +211,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) { diff --git a/src/core/misc.h b/src/core/misc.h index c6369489..7e78d3b9 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); diff --git a/src/fe-common/core/module-formats.c b/src/fe-common/core/module-formats.c index db11fae1..4ae26950 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 }, diff --git a/src/fe-text/terminfo-core.c b/src/fe-text/terminfo-core.c index 0516cc5f..6339e6f4 100644 --- a/src/fe-text/terminfo-core.c +++ b/src/fe-text/terminfo-core.c @@ -50,62 +50,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) */ @@ -523,7 +527,11 @@ 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->TI_smkx) + tput(tparm(term->TI_smkx)); + terminfo_input_init(term); } @@ -538,6 +546,9 @@ void terminfo_stop(TERM_REC *term) if (term->TI_rmcup) tput(tparm(term->TI_rmcup)); + if (term->TI_rmkx) + tput(tparm(term->TI_rmkx)); + /* reset input settings */ terminfo_input_deinit(term); fflush(term->out); diff --git a/src/fe-text/terminfo-core.h b/src/fe-text/terminfo-core.h index 6ab4637d..21398791 100644 --- a/src/fe-text/terminfo-core.h +++ b/src/fe-text/terminfo-core.h @@ -88,6 +88,10 @@ struct _TERM_REC { /* Beep */ char *TI_bel; + + /* Keyboard-transmit mode */ + const char *TI_smkx; + const char *TI_rmkx; }; extern TERM_REC *current_term; diff --git a/src/irc/core/Makefile.am b/src/irc/core/Makefile.am index 3db5cf0e..7d885d20 100644 --- a/src/irc/core/Makefile.am +++ b/src/irc/core/Makefile.am @@ -26,6 +26,7 @@ libirc_core_a_SOURCES = \ irc-servers-reconnect.c \ irc-servers-setup.c \ irc-session.c \ + irc-cap.c \ lag.c \ massjoin.c \ modes.c \ @@ -48,6 +49,7 @@ pkginc_irc_core_HEADERS = \ irc-queries.h \ irc-servers.h \ irc-servers-setup.h \ + irc-cap.h \ modes.h \ mode-lists.h \ module.h \ diff --git a/src/irc/core/irc-cap.c b/src/irc/core/irc-cap.c new file mode 100644 index 00000000..cd3ae677 --- /dev/null +++ b/src/irc/core/irc-cap.c @@ -0,0 +1,191 @@ +/* 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)) { + 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-core.c b/src/irc/core/irc-core.c index bf7386ad..e3ceeeef 100644 --- a/src/irc/core/irc-core.c +++ b/src/irc/core/irc-core.c @@ -26,6 +26,7 @@ #include "irc-chatnets.h" #include "irc-channels.h" #include "irc-queries.h" +#include "irc-cap.h" #include "irc-servers-setup.h" #include "channels-setup.h" @@ -117,6 +118,7 @@ void irc_core_init(void) lag_init(); netsplit_init(); irc_expandos_init(); + cap_init(); settings_check(); module_register("core", "irc"); @@ -126,6 +128,7 @@ void irc_core_deinit(void) { signal_emit("chat protocol deinit", 1, chat_protocol_find("IRC")); + cap_deinit(); irc_expandos_deinit(); netsplit_deinit(); lag_deinit(); diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c index 31ba397b..b2718ae1 100644 --- a/src/irc/core/irc-servers.c +++ b/src/irc/core/irc-servers.c @@ -32,6 +32,7 @@ #include "irc-queries.h" #include "irc-servers-setup.h" #include "irc-servers.h" +#include "irc-cap.h" #include "channel-rejoin.h" #include "servers-idle.h" #include "servers-reconnect.h" @@ -221,6 +222,8 @@ static void server_init(IRC_SERVER_REC *server) g_free(cmd); } + irc_send_cmd_now(server, "CAP LS"); + if (conn->password != NULL && *conn->password != '\0') { /* send password */ cmd = g_strdup_printf("PASS %s", conn->password); @@ -321,6 +324,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 +421,16 @@ 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; /* these are dynamically allocated only if isupport was sent */ g_hash_table_foreach(server->isupport, diff --git a/src/irc/core/irc-servers.h b/src/irc/core/irc-servers.h index 7e4eeabf..f809fab5 100644 --- a/src/irc/core/irc-servers.h +++ b/src/irc/core/irc-servers.h @@ -69,6 +69,13 @@ struct _IRC_SERVER_REC { 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 */ + int cap_complete:1; /* We've done the initial CAP negotiation */ + + 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/proxy/dump.c b/src/irc/proxy/dump.c index 3d7bf546..455a2fe3 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_c(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_c(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); } @@ -212,7 +212,7 @@ void proxy_client_reset_nick(CLIENT_REC *client) 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 ebbbdcfa..ae5af5fb 100644 --- a/src/irc/proxy/listen.c +++ b/src/irc/proxy/listen.c @@ -155,7 +155,7 @@ 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", + proxy_outdata(client, ":%s PONG %s :%s\r\n", client->proxy_address, client->proxy_address, origin); g_free(params); @@ -174,18 +174,18 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args, /* 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", + 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", + 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->want_ctcp = 0; - proxy_outdata(client, ":%s NOTICE %s :Proxy is now handling itself CTCPs sent to %s\n", + 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,7 +194,7 @@ 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", + proxy_outdata(client, ":%s NOTICE %s :Not connected to server\r\n", client->proxy_address, client->nick); return; } @@ -371,7 +371,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, @@ -465,7 +465,7 @@ static void event_connected(IRC_SERVER_REC *server) (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", + proxy_outdata(rec, ":%s NOTICE %s :Connected to server\r\n", rec->proxy_address, rec->nick); rec->server = server; proxy_client_reset_nick(rec); @@ -478,7 +478,7 @@ static void proxy_server_disconnected(CLIENT_REC *client, { 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); |