1# Some code taken from `nickserv.pl' for convenience.
2# Credits Sami Haahtinen / ZaNaGa
3#
4
5# Don't forget to create the necessary chatnets in your irssi config file.
6#
7# Example:
8# ....
9# {
10#   address = "irc.undernet.org";
11#   chatnet = "Undernet";
12#   port = "6668";
13#   autoconnect = no;
14#  }
15# .....
16#
17#
18# Then connect with the server like this:
19# /server undernet (or set autoconnect to yes)
20
21# Make sure you fill in *all* necessary information without typos.
22#
23# Files you need to edit after first run:
24# x.users     -> For your x user/pw information.
25# x.channels  -> Channels to join after authing. (optional)
26#
27# Use /xrehash to reload if you edit the files.
28#
29# Var:
30# my (%masks) -> See help there.
31
32# Tested with X versions
33# Undernet P10 Channel Services II Release 1.1pl7
34#
35
36#
37# This program is free software; you can redistribute it and/or modify
38# it under the terms of the GNU General Public License as published by
39# the Free Software Foundation; either version 2, or (at your option)
40# any later version.
41#
42# This program is distributed in the hope that it will be useful, but
43# WITHOUT ANY WARRANTY; without even the implied warranty of
44# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the file
45# COPYING (included with this distribution) or the GNU General Public
46# License for more details.
47#
48# You should have received a copy of the GNU General Public License
49# along with this program; if not, write to the Free Software
50# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
51#
52
53use Irssi;
54use Irssi::Irc;
55
56use strict;
57
58use vars qw($VERSION %IRSSI);
59
60$VERSION = '1.02';
61
62%IRSSI = (
63    authors     => 'Toshio R. Spoor',
64    contact     => 't.spoor@gmail.com',
65    name        => 'xauth',
66    description => 'Undernet X Service Authentication Program',
67    license     => 'GNU GPLv2 or later',
68    changed     => '$Date: 2004/12/17 08:39:47 $'
69);
70
71my (%CONFIG) = (
72    autostart	=> '',
73    autojoin	=> '',
74    hiddenhost	=> ''
75);
76
77Irssi::theme_register([
78  xauth_rehash    => '{comment $0} %KRehashing configuration files and settings%n',
79  xauth_autostart => '{comment $0} %KAuto-Start :%n $1',
80  xauth_autojoin  => '{comment $0} %KAuto-Join  :%n $1',
81  xauth_hiddenhost=> '{comment $0} %KHiddenhost :%n $1',
82  xauth_auth      => '{comment $0} %KAuthorising%n $1 %Kwith%n $2 %Kon%n $3',
83  xauth_load      => '{comment $0} %KScript %nv$1 %Kloaded ...%n',
84  xauth_nocon     => '{comment $0} %KNot connected to server%n',
85  xauth_noconn    => '{comment $0} %KThere does not exist a connection to $1%n',
86  xauth_success   => '{comment $0} %KLogged in successfully on %n$1',
87  xauth_failed    => '{comment $0} %KFailed to login on %n$1 ($2)',
88  xauth_already   => '{comment $0} %KI am already logged in on%n $1',
89  xauth_nouser    => '{comment $0} $1 %Kdoes not know who %n$2 %Kis on %n$3',
90  xauth_nohost    => '{comment $0} %KNo hostmask found for %n$1%K, to fix this edit this script, see masks',
91  xauth_noentry   => '{comment $0} %KI did not find an entry for %n$1 %Kcheck%n $2',
92  xauth_missing   => '{comment $0} %KI am missing username, password or authentication host login information%n',
93  xauth_join      => '{comment $0} %KJoined on%n $1%K : %n$2-'
94]);
95
96my ($usage) = qq!X-Authentication v$VERSION by Toshio Spoor
97
98Usage:
99/auth <chatnet>
100
101Settings:
102/set xauth                      Shows current settings
103/toggle xauth_autostart         Toggle Auto Start
104/toggle xauth_autojoin          Toggle Auto Join
105/toggle xauth_hiddenhost        Toggle Hiddenhost (ircu u2.10.11+)
106
107Rehashing settings and user/channel file:
108/xrehash                        Run this after any changes
109                                made to settings/files
110
111/save                           Make settings permanent
112!;
113
114# The `masks' hash is very important:
115# Here we fill in the masks we need to authenticate with.
116#
117# <chatnet> = <host> <authhost>
118#
119# You can find this very easily:
120# /msg x login
121#
122# 08:49 -!- Irssi: Starting query in Undernet with x
123# 08:49 <Foo> login
124# 08:49 -X(channels@undernet.org)- To use LOGIN, you must /msg X@services.undernet.org
125#
126# Keep the chatnet lowercase
127
128my (%masks) = (
129       undernet    => [ 'cservice@undernet.org', 'X@channels.undernet.org' ],
130       worldirc    => [ 'cservice@worldirc.org','X@channels.worldirc.org' ]
131);
132
133# 0 = None
134# 1 = Normal
135# 2 = More
136
137my ($verbose) = 1;
138
139# Don't touch these, unless the signature changes.
140#
141my ($success) = "AUTHENTICATION SUCCESSFUL";
142my ($already) = "Sorry, You are already authenticated";
143my ($failed)  = "AUTHENTICATION FAILED";
144my ($remind)  = "Remember: Nobody from CService will ever ask you for your password, do NOT give";
145my ($nouser)  = "I don't know who";
146
147# Global Vars, don't change these.
148#
149my ($x_passfile) = Irssi::get_irssi_dir() ."/x.users";
150my ($x_chanfile) = Irssi::get_irssi_dir() ."/x.channels";
151
152my (@users) = ();
153my (@chans) = ();
154
155# Core Code
156#
157#
158
159sub putlog() {
160
161        my ($window) = Irssi::active_win();
162	Irssi::print("[$IRSSI{'name'}] @_", MSGLEVEL_CLIENTNOTICE);
163
164}
165
166sub haltdef() {
167
168        Irssi::signal_stop();
169
170}
171
172sub conn($) {
173
174        my ($server) = @_;
175
176        if (!$server || !$server->{connected}) {
177                return 0;
178        } else {
179        	return 1;
180        }
181
182}
183
184sub join_channels($) {
185
186        my ($chatnet)  = @_;
187        my (@channels) = ();
188        my ($server)   = Irssi::server_find_tag($chatnet);
189
190        if (!$server) {
191        	Irssi::printformat(MSGLEVEL_CLIENTNOTICE, "xauth_nocon", "$IRSSI{'name'}");
192        	return;
193        }
194
195        foreach (@chans) {
196
197                my ($channel, $ircnet) = split(/:/);
198
199                if (lc($chatnet) eq lc($ircnet)) {
200                	# If we do it like this, the status window stays active.
201                	push (@channels, $channel);
202                	$server->send_raw("JOIN #$channel");
203                }
204        }
205
206        if ($verbose) {
207        	if (@channels) {
208        		Irssi::printformat(MSGLEVEL_CLIENTNOTICE, "xauth_join", "$IRSSI{'name'}", $chatnet, @channels);
209	        }
210	}
211}
212
213sub mask_check($) {
214
215	my ($address) = @_;
216
217	foreach my $key (keys %masks) {
218		if (lc($masks{$key}->[0]) eq lc($address)) {
219			return $key;
220			last;
221		}
222        }
223
224	return 0;
225
226}
227
228
229sub event_notice() {
230
231        my ($server, $args, $nick, $nickad) = @_;
232
233	return unless (&mask_check($nickad));
234
235        my ($cnet) = $server->{'tag'};
236        my ($version) = $server->{'version'};
237
238        my ($target, $data) = $args =~ /^(\S*)\s+:(.*)$/;
239
240        $_ = $data;
241
242        if (/^$already/i) {
243                Irssi::printformat(MSGLEVEL_CLIENTNOTICE, "xauth_already", "$IRSSI{'name'}", $cnet);
244                &haltdef();
245        }
246
247        if (/^$success/i) {
248                Irssi::printformat(MSGLEVEL_CLIENTNOTICE, "xauth_success", "$IRSSI{'name'}", $cnet);
249
250                if (($version) && ($CONFIG{'hiddenhost'})) {
251
252        		my($app,$hi,$lo) = $version =~ /^(..).(..).(..)/;
253        		$app =~ s/\D//g;
254
255        		if (($app >= 2) && ($lo >= 11)) {
256        			&putlog("Found ircu $version, setting umode +x") if ($verbose > 1);
257        			$server->command("mode $target +x");
258        		}
259        	}
260
261                if ($CONFIG{'autojoin'}) {
262                	&join_channels($cnet);
263                }
264                &haltdef();
265        }
266
267        if (/^$failed/i) {
268                if (/\((.*?)\)/) { $args = $1 };
269                Irssi::printformat(MSGLEVEL_CLIENTNOTICE, "xauth_failed", "$IRSSI{'name'}", $cnet, $args);
270                &haltdef();
271        }
272
273        if (/^$remind/i) {
274                &haltdef();
275        }
276
277        if (/^$nouser/i) {
278        	if (/who\s(.*?)\s/) { $args = $1 };
279        	Irssi::printformat(MSGLEVEL_CLIENTNOTICE, "xauth_nouser", "$IRSSI{'name'}", "$nick", $args, $cnet);
280        	&haltdef();
281        }
282}
283
284sub cmd_auth() {
285
286        my ($data, $server, $witem) = @_;
287        my ($username, $ircnet, $password, $xlogin, $xmask, $chatnet, $found);
288
289        if ($data) {
290                $chatnet = $data;
291        } else {
292                &putlog("$usage");
293                return;
294        }
295
296        if (! &conn($server)) {
297                Irssi::printformat(MSGLEVEL_CLIENTNOTICE, "xauth_nocon", "$IRSSI{'name'}");
298                return;
299        }
300
301        my ($authserver) = Irssi::server_find_tag($chatnet);
302
303        if (! $authserver) {
304                Irssi::printformat(MSGLEVEL_CLIENTNOTICE, "xauth_noconn", "$IRSSI{'name'}", $chatnet);
305                return;
306        }
307
308        foreach (@users) {
309
310                ($username, $ircnet, $password) = split(/:/);
311
312                if (lc($ircnet) eq lc($chatnet)) {
313                        $xmask  = $masks{lc($ircnet)}->[0];
314                        $xlogin = $masks{lc($ircnet)}->[1];
315
316                        if ((!$xmask) || (!$xlogin)) {
317                        	Irssi::printformat(MSGLEVEL_CLIENTNOTICE, "xauth_nohost", "$IRSSI{'name'}", $chatnet);
318                        	return;
319                        }
320
321                        $found=1;
322                        last;
323                }
324        }
325
326        if (! $found ) {
327                Irssi::printformat(MSGLEVEL_CLIENTNOTICE, "xauth_noentry", "$IRSSI{'name'}", $chatnet, qq/"$x_passfile"/);
328                return;
329        }
330
331        if (($username) && ($password) && ($xlogin)) {
332                Irssi::printformat(MSGLEVEL_CLIENTNOTICE, "xauth_auth", "$IRSSI{'name'}", $username, $xlogin, $chatnet);
333                $authserver->send_raw("PRIVMSG $xlogin :login $username $password");
334        } else {
335                Irssi::printformat(MSGLEVEL_CLIENTNOTICE, "xauth_missing", "$IRSSI{'name'}");
336        }
337}
338
339# Code taken from nickserv.pl
340
341sub read_users() {
342        my $count = 0;
343
344        # Lets reset @users so we can call this as a function.
345        @users = ();
346
347        if (!(open XUSERS, "<", $x_passfile)) {
348                &create_users;
349        };
350       	&putlog("Running checks on the userfile.") if ($verbose > 1);
351        # first we test the file with mask 066 (we don't actually care if the
352        # file is executable by others.. what could they do with it =)
353
354        # Well, according to my calculations umask 066 should be 54, go figure.
355
356        my $mode = (stat($x_passfile))[2];
357        if ($mode & 54) {
358                &putlog("your password file should be mode 0600. Go fix it!");
359                &putlog("use command: chmod 0600 $x_passfile");
360        }
361
362        # and then we read the userfile.
363        # apparently Irssi resets $/, so we set it here.
364
365        local $/ = "\n";
366        while( my $line = <XUSERS>) {
367                if( $line !~ /^(#|\s*$)/ ) {
368                        my ($nick, $ircnet, $password) =
369				$line =~ /^\s*(\S+)\s+(\S+)\s+(.*?)$/;
370                        push @users, "$nick:$ircnet:$password";
371                        $count++;
372                }
373        }
374       	&putlog("Found $count accounts") if ($verbose > 1);
375        close XUSERS;
376}
377
378sub create_users() {
379
380        &putlog("Creating basic userfile in $x_passfile. Edit File.");
381
382        if(!(open XUSERS, ">", $x_passfile)) {
383               &putlog("Unable to create file $x_passfile");
384        }
385
386        print XUSERS "# username and IrcNet Tag are case insensitive\n";
387        print XUSERS "#\n";
388        print XUSERS "# username      IrcNet Tag      Password\n";
389        print XUSERS "# --------      ----------      --------\n";
390
391        close XUSERS;
392        chmod 0600, $x_passfile;
393}
394
395sub create_chans() {
396        &putlog("Creating basic channelfile in $x_chanfile. Edit File.");
397        if(!(open NICKCHANS, ">", $x_chanfile)) {
398                &putlog("Unable to create file $x_chanfile");
399        }
400
401        print NICKCHANS "# This file should contain a list of all channels\n";
402        print NICKCHANS "# which you don't want to join until after you've\n";
403        print NICKCHANS "# successfully identified with x.  This is\n";
404        print NICKCHANS "# useful if you have a hidden host (+x).\n";
405        print NICKCHANS "# Enter Channel without `#'\n";
406        print NICKCHANS "#\n";
407        print NICKCHANS "# Channel       IrcNet Tag\n";
408        print NICKCHANS "# --------      ----------\n";
409
410        close NICKCHANS;
411        chmod 0600, $x_chanfile;
412}
413
414sub read_chans() {
415        my $count = 0;
416
417        # Lets reset @users so we can call this as a function.
418        @chans = ();
419
420        if (!(open NICKCHANS, "<", $x_chanfile)) {
421                create_chans;
422        };
423       	&putlog("Running checks on the channelfile.") if ($verbose > 1);
424        # first we test the file with mask 066 (we don't actually care if the
425        # file is executable by others.. what could they do with it =)
426
427        # Well, according to my calculations umask 066 should be 54, go figure.
428
429        my $mode = (stat($x_chanfile))[2];
430        if ($mode & 54) {
431                &putlog("your channels file should be mode 0600. Go fix it!");
432                &putlog("use command: chmod 0600 $x_chanfile");
433        }
434
435        # and then we read the channelfile.
436        # apparently Irssi resets $/, so we set it here.
437
438        local $/ = "\n";
439        while( my $line = <NICKCHANS>) {
440                if( $line !~ /^(#|\s*$)/ ) {
441                        my ($channel, $ircnet) =
442                                $line =~ /\s*(\S+)\s+(\S+)/;
443                        push @chans, "$channel:$ircnet";
444                        $count++;
445                }
446        }
447       	&putlog("Found $count channels") if ($verbose > 1);
448        close NICKCHANS;
449}
450
451# End code from nickserv.pl
452
453sub event_connect() {
454
455	$CONFIG{'autostart'}  = Irssi::settings_get_bool('xauth_autostart');
456
457	return unless ($CONFIG{'autostart'});
458
459        my ($server) = @_;
460        my ($cnet) = $server->{'tag'};
461        my ($found);
462
463        foreach my $key (keys %masks) {
464        	if (lc($key) eq lc($cnet)) {
465        		$found=1;
466        		last;
467        	}
468	}
469
470	return unless($found);
471
472        $server->command("auth $cnet");
473
474}
475
476sub x_rehash() {
477
478	Irssi::printformat(MSGLEVEL_CLIENTNOTICE, "xauth_rehash", "$IRSSI{'name'}") if (($verbose) && (@_));
479
480	&read_users();
481	&read_chans();
482	&get_set(@_);
483
484}
485
486sub init_set() {
487
488	Irssi::settings_add_bool('misc', 'xauth_autostart', '0');
489	Irssi::settings_add_bool('misc', 'xauth_autojoin',  '1');
490	Irssi::settings_add_bool('misc', 'xauth_hiddenhost','0');
491
492}
493
494sub onoff($) {
495
496	my ($value) = @_;
497
498	if ($value) {
499		return "On";
500	} else {
501		return "Off";
502	}
503
504}
505
506sub get_set() {
507
508	$CONFIG{'autostart'}  = Irssi::settings_get_bool('xauth_autostart');
509	$CONFIG{'autojoin'}   = Irssi::settings_get_bool('xauth_autojoin');
510	$CONFIG{'hiddenhost'} = Irssi::settings_get_bool('xauth_hiddenhost');
511
512	Irssi::printformat(MSGLEVEL_CLIENTNOTICE, "xauth_autostart", "$IRSSI{'name'}", &onoff("$CONFIG{'autostart'}"))   if (($verbose) && (@_));
513	Irssi::printformat(MSGLEVEL_CLIENTNOTICE, "xauth_autojoin",  "$IRSSI{'name'}", &onoff("$CONFIG{'autojoin'}"))    if (($verbose) && (@_));
514	Irssi::printformat(MSGLEVEL_CLIENTNOTICE, "xauth_hiddenhost", "$IRSSI{'name'}", &onoff("$CONFIG{'hiddenhost'}")) if (($verbose) && (@_));
515
516}
517
518sub init() {
519
520	&init_set();
521	&x_rehash();
522
523
524}
525
526sub x_help() {
527
528	&putlog("$usage");
529
530}
531
532
533# Main
534#
535#
536
537&init();
538
539Irssi::command_bind("auth", "cmd_auth");
540Irssi::command_bind("xrehash", "x_rehash");
541Irssi::command_bind("xhelp", "x_help");
542
543Irssi::signal_add("event notice", "event_notice");
544Irssi::signal_add("event connected", "event_connect");
545
546Irssi::printformat(MSGLEVEL_CLIENTNOTICE, "xauth_load", "$IRSSI{'name'}", $VERSION);
547