2
use vars qw/$VERSION %IRSSI/;
5
use POSIX qw/ strftime /;
13
authors => 'SymKat','Benjamin Rubin',
14
contact => 'symkat@symkat.com','bnrubin@ubuntu.com',
16
decsription => 'Records and correlates nick!user@host information',
18
url => "http://github.com/symkat/stalker",
19
changed => "2010-10-06",
20
changes => "See Change Log",
24
Irssi::signal_add_last( 'event 311', \&whois_request );
25
Irssi::signal_add_last( 'event 314', \&whois_request );
26
Irssi::signal_add( 'message join', \&nick_joined );
27
Irssi::signal_add( 'nicklist changed', \&nick_changed_channel );
28
Irssi::signal_add( 'channel sync', \&channel_sync );
30
Irssi::command_bind( 'host_lookup', \&host_request );
31
Irssi::command_bind( 'nick_lookup', \&nick_request );
33
Irssi::theme_register([$IRSSI{'name'} => '{whois stalker %|$1}']);
36
Irssi::settings_add_str( 'Stalker', $IRSSI{name} . "_max_recursion", 20 );
37
Irssi::settings_add_str( 'Stalker', $IRSSI{name} . "_guest_nick_regex", "/^Guest.*/i" );
38
Irssi::settings_add_str( 'Stalker', $IRSSI{name} . "_debug_log_file", "stalker.log" );
40
Irssi::settings_add_bool( 'Stalker', $IRSSI{name} . "_verbose", 0 );
41
Irssi::settings_add_bool( 'Stalker', $IRSSI{name} . "_debug", 0 );
42
Irssi::settings_add_bool( 'Stalker', $IRSSI{name} . "_recursive_search", 1 );
43
Irssi::settings_add_bool( 'Stalker', $IRSSI{name} . "_search_this_network_only", 0 );
44
Irssi::settings_add_bool( 'Stalker', $IRSSI{name} . "_ignore_guest_nicks", 1 );
45
Irssi::settings_add_bool( 'Stalker', $IRSSI{name} . "_debug_log", 0 );
47
Irssi::settings_add_str( 'Stalker', $IRSSI{name} . "_db_path", "" );
48
Irssi::settings_add_str( 'Stalker', $IRSSI{name} . "_db_driver", "SQLite" );
49
Irssi::settings_add_str( 'Stalker', $IRSSI{name} . "_db_username", "stalker" );
50
Irssi::settings_add_str( 'Stalker', $IRSSI{name} . "_db_password", "" );
51
Irssi::settings_add_str( 'Stalker', $IRSSI{name} . "_db_server", "localhost" );
52
Irssi::settings_add_str( 'Stalker', $IRSSI{name} . "_db_port", "3306" );
53
Irssi::settings_add_str( 'Stalker', $IRSSI{name} . "_db_name", "stalker" );
61
my $db_driver = Irssi::settings_get_str($IRSSI{name} . '_db_driver');
62
my $db_username = Irssi::settings_get_str($IRSSI{name} . '_db_username');
63
my $db_password = Irssi::settings_get_str($IRSSI{name} . '_db_password');
64
my $db_server = Irssi::settings_get_str($IRSSI{name} . '_db_server');
65
my $db_port = Irssi::settings_get_str($IRSSI{name} . '_db_port');
66
my $db_name = Irssi::settings_get_str($IRSSI{name} . '_db_name');
67
my $db_path = Irssi::settings_get_str($IRSSI{name} . '_db_path');
70
if ( $db_path == "" ) {
71
$data_source = 'dbi:'.$db_driver.':database='.$db_name.';host='.$db_server.';port='.$db_port;
73
if ( $db_path and ! File::Spec->file_name_is_absolute($db_path) ) {
74
$db_path = File::Spec->catfile( Irssi::get_irssi_dir(), $db_path );
76
if ( ! -e $db_path ) {
77
open my $fh, '>', $db_path
78
or die "Cannot create database file. Abort.";
83
$data_source = 'dbi:'.$db_driver.':'.$db_path;
87
my $DBH = DBI->connect(
88
$data_source, $db_username, $db_password,
94
) or die "Failed to connect to database $data_source: " . $DBI::errstr;
96
stat_database( $DBH );
98
# Automatic Database Creation And Checking
100
create_table( $DBH ) if $do;
102
my $sth = $DBH->prepare( "SELECT nick from records WHERE serv = ?" );
103
$sth->execute( 'script-test-string' );
104
my $sane = $sth->fetchrow_array;
106
create_table( $DBH ) if $sane == undef;
112
my $query = "CREATE TABLE records (nick VARCHAR(50) NOT NULL," .
113
"user VARCHAR(255) NOT NULL, host VARCHAR(255) NOT NULL, serv
114
VARCHAR(255) NOT NULL)";
116
$DBH->do( "DROP TABLE IF EXISTS records" );
118
my $sth = $DBH->prepare( "INSERT INTO records (nick, user, host, serv) VALUES( ?, ?, ?, ? )" );
119
$sth->execute( 1, 1, 1, 'script-test-string' );
126
my ( $server, $data, $server_name ) = @_;
127
my ( $me, $n, $u, $h ) = split(" ", $data );
129
$server->printformat($n,MSGLEVEL_CRAP,$IRSSI{'name'},$n,
130
join( ", ", (get_records('host', $h, $server->{address}))));
134
windowPrint( join( ", ", (get_records('host', $_[0], $_[1]->{address}))));
138
windowPrint( join( ", ", (get_records('nick', $_[0], $_[1]->{address}))));
141
# Record Adding Functions
143
add_record($_[2], (split('@', $_[3]), $_[0]->{address}));
146
sub nick_changed_channel {
147
add_record( $_[1]->{nick}, (split( '@', $_[1]->{host} )), $_[0]->{server}->{address} );
151
my ( $channel ) = @_;
153
my $serv = $channel->{server}->{address};
155
for my $nick ( $channel->nicks() ) {
156
last if $nick->{host} eq ''; # Sometimes channel sync doesn't give us this...
157
add_record( $nick->{nick}, ( split( '@', $nick->{host} ) ), $serv );
158
#my $thread = threads->create("add_record",( $nick->{nick}, ( split( '@', $nick->{host} ) ), $serv ));
166
my ( $nick, $user, $host, $serv ) = @_;
168
# Check if we already have this record.
169
my $q = "SELECT nick FROM records WHERE nick = ? AND user = ? AND host = ? AND serv = ? ORDER BY nick";
170
my $sth = $DBH->prepare( $q );
171
$sth->execute( $nick, $user, $host, $serv );
172
my $result = $sth->fetchrow_hashref;
174
if ( $result->{nick} eq $nick ) {
175
debugPrint( "info", "Record for $nick skipped - already exists." );
179
debugPrint( "info", "Adding to DB: nick = $nick, user = $user, host = $host, serv = $serv" );
181
# We don't have the record, add it.
183
("INSERT INTO records (nick,user,host,serv) VALUES( ?, ?, ?, ? )" );
184
eval { $sth->execute( $nick, $user, $host, $serv ) };
186
debugPrint( "crit", "Failed to process record, database said: $@" );
189
debugPrint( "info", "Added record for $nick!$user\@$host to $serv" );
193
my ( $type, $query, $serv, @return ) = @_;
195
$count = 0; %data = ( );
196
my %data = _r_search( $serv, $type, $query );
197
for my $k ( keys %data ) {
198
#_msg("$type query for records on $query from server $serv returned: $k" );
199
push @return, $k if $data{$k} eq 'nick';
205
my ( $serv, $type, @input ) = @_;
206
return %data if $count > 1000;
207
return %data if $count > Irssi::settings_get_str($IRSSI{name} . "_max_recursion");
208
return %data if $count == 2 and ! Irssi::settings_get_bool( $IRSSI{name} . "_recursive_search" );
210
debugPrint( "info", "Recursion Level: $count" );
212
if ( $type eq 'nick' ) {
214
for my $nick ( @input ) {
215
next if exists $data{$nick};
216
$data{$nick} = 'nick';
217
my @hosts = _get_hosts_from_nick( $nick, $serv );
218
_r_search( $serv, 'host', @hosts );
220
} elsif ( $type eq 'host' ) {
222
for my $host ( @input ) {
223
next if exists $data{$host};
224
$data{$host} = 'host';
225
my @nicks = _get_nicks_from_host( $host, $serv );
226
verbosePrint( "Got nicks: " . join( ", ", @nicks ) . " from host $host" );
227
_r_search( $serv, 'nick', @nicks );
234
sub _get_hosts_from_nick {
235
my ( $nick, $serv, @return ) = @_;
238
if ( Irssi::settings_get_bool( $IRSSI{name} . "_search_this_network_only" ) ){
239
$sth = $DBH->prepare( "select host from records where nick = ? and serv = ?" );
240
$sth->execute( $nick, $serv );
242
$sth = $DBH->prepare( "select host from records where nick = ?" );
243
$sth->execute( $nick );
246
while ( my $row = $sth->fetchrow_hashref ) {
247
push @return, $row->{host};
252
sub _get_nicks_from_host {
253
my ( $host, $serv, @return ) = @_;
256
if ( Irssi::settings_get_bool( $IRSSI{name} . "_search_this_network_only" ) ){
257
$sth = $DBH->prepare( "select nick from records where host = ? and serv = ? order by nick" );
258
$sth->execute( $host, $serv );
260
$sth = $DBH->prepare( "select nick from records where host = ? order by nick" );
261
$sth->execute( $host );
264
while ( my $row = $sth->fetchrow_hashref ) {
265
if ( Irssi::settings_get_bool($IRSSI{name} . "_ignore_guest_nicks") ) {
266
my $regex = Irssi::settings_get_str( $IRSSI{name} . "_guest_nick_regex" );
267
next if $row->{nick} =~ m/$regex/i;
269
push @return, $row->{nick};
276
# Short cut - instead of two debug statements thoughout the code,
277
# we'll send all debugPrint's to the debugLog function as well
279
windowPrint( $IRSSI{name} . " Debug: " . $_[1] )
280
if Irssi::settings_get_bool($IRSSI{name} . "_debug");
281
debugLog( $_[0], $_[1] );
285
windowPrint( $IRSSI{name} . " Verbose: " . $_[0] )
286
if Irssi::settings_get_bool($IRSSI{name} . "_verbose");
290
my ( $lvl, $msg ) = @_;
291
return unless Irssi::settings_get_bool($IRSSI{name} . "_debug_log" );
292
my $now = strftime( "[%D %H:%M:%S]", localtime );
294
my $logpath = Irssi::settings_get_str( $IRSSI{name} . "_debug_log_file" );
295
if ( ! File::Spec->file_name_is_absolute($logpath) ) {
296
$logpath = File::Spec->catfile( Irssi::get_irssi_dir(), $logpath );
299
open my $fh, ">>", $logpath
300
or die "Fatal error: Cannot open my logfile at " . $IRSSI{name} . "_debug_log_file for writing: $!";
301
print $fh "[$lvl] $now $msg\n";
306
Irssi::active_win()->print( $_[0] );
311
my $win = Irssi::active_win();
312
$win->print($msg, Irssi::MSGLEVEL_CLIENTERROR);
317
my $win = Irssi::active_win();
318
$win->print($msg, Irssi::MSGLEVEL_CLIENTCRAP);
321
windowPrint( "Loaded $IRSSI{'name'}" );