#!/usr/bin/perl

use strict;
use vars qw($VERSION %IRSSI);

use Irssi;
$VERSION = '0.2';
%IRSSI = (
	authors		=> 'Hannes \'epzilon\' Forster',
	contact		=> 'h\@nnes.info',
	name		=> 'cICQ',
	description => 'This script notifies you in your Irssi client about ' . 
				   'incoming messages in your Centericq client.',
	license		=> 'GPL',
);


# Settings that you can modify using Irssi also.
#
# Centericq folder location.
# default: $ENV{'HOME'} . "/.centericq"
my $CENTERICQ = $ENV{'HOME'} . "/.centericq";
#
# Centericq log file location.
# default: $ENV{'HOME'} . "/.centericq/log"
my $CENTERICQ_LOG = $ENV{'HOME'} . "/.centericq/log";
#
# Time in seconds between checking for new messages, events, ...
# default: 1
my $REFRESH_TIME = 5;
#
# Time in seconds for which incoming messages are ignored from a user after
# having received a message.
# '0' to disable.
# default: 300
my $IGNORE_TIME = 300;
#
# '1' if cICQ is supposed to check for new messages.
# '0' if not.
# defualt: 1
my $CHECK_MESSAGES = 1;
#
# '1' if cICQ is supposed to check for authorization requests.
# '0' if not.
# You might want to have that turned off if you get a lot of spam (as I do).
# default: 0
my $AUTHORIZATION_REQUEST = 0;
#
#'1' if cICQ is supposed to print status changes.
# '0' if not.
# default: 0
my $STATUS_CHANGE = 0;
#
# The color in which alarming messages are supposed to be displayed.
# Color formats can be found here:
# http://www.irssi.org/documentation/formats
# default: blue ('%b')
my $MESSAGE_COLOR = '%b';
#
# '1' if cICQ is supposed to ignore messages from users not on your contact
# list.
# '0' if not.
# default: 1
my $IGNORE_NOT_ON_LIST = 1;



# Tell Irssi the settings.
Irssi::settings_add_str('cICQ', 'centericq_log', $CENTERICQ_LOG);
Irssi::settings_add_str('cICQ', 'centericq_folder', $CENTERICQ);
Irssi::settings_add_int('cICQ', 'refresh_time', $REFRESH_TIME);
Irssi::settings_add_int('cICQ', 'ignore_time', $IGNORE_TIME);
Irssi::settings_add_bool('cICQ', 'check_for_messages', $CHECK_MESSAGES);
Irssi::settings_add_bool('cICQ', 'authorization_requests',
	$AUTHORIZATION_REQUEST);
Irssi::settings_add_bool('cICQ', 'status_change', $STATUS_CHANGE);
Irssi::settings_add_bool('cICQ', 'ignore_not_on_list', $IGNORE_NOT_ON_LIST);
Irssi::settings_add_str('cICQ', 'message_color', $MESSAGE_COLOR);


# 'focuson' command binding.
Irssi::command_bind('focuson', 'focus_on');



# Internal variables.
my @stat = stat($CENTERICQ_LOG);
my $last_change_time = $stat[9];
my $timeout_tag;
my $last_position = $stat[7];
my $logfile_size = $stat[7];
my $line;
my %last_received;
my @focuson_list;
my $pretext = "$MESSAGE_COLOR-%n!$MESSAGE_COLOR- cICQ:%9";
my %auth_pending;


# Checks for any changes in the logfile and starts appropriate subroutines. 
# This subroutine is being repeatedly executed by Irssi in intervalls
# specified by $REFRESH_TIME .
sub check_for_new {

	#Refresh the variables.
	refresh_variables();

	@stat = stat($CENTERICQ_LOG);

	if ( $stat[9] ne $last_change_time ) {

		# Idea: Logfile might be deleted after some time / a reached filesize.
		# If so, the new filesize is most certainly smaller than the old. -> Start
		# from the beginning of the file.
		if ( $stat[7] < $logfile_size ) {
			$last_position = 0;
		}
		$logfile_size = $stat[7];

		open FILE, $CENTERICQ_LOG or die $!;
		seek(FILE, $last_position, 0);

		while (<FILE>) {

			# '$line' is needed to print the information.
			$line = $_;
			chop $line;

			if ( $CHECK_MESSAGES ) {
				if ( check_messages() ) {
					next;
				}
			}
			if (/incoming authorization from/) {
				if ( $AUTHORIZATION_REQUEST ) {
					print_incoming_messages();
				} else {
					$auth_pending{get_user()} = time();
				}
				next;
			}
			if ( /went/ or /changed status to/ ) {
				if ( $STATUS_CHANGE or
					( exists($focuson_list[0]) and nick_in_focuson_list() )) {
					print_incoming_messages();
					next;
				} 
			}
			# Authorization request accepted? => Remove contact from pending
			# authorizations.
			if (/outgoing authorization to/) {
				delete $auth_pending{get_user()};
				next;
			}
		}

		$last_position = tell FILE;

		close FILE;

		$last_change_time = $stat[9];
	}
}


# Checks for an incoming message, returns true if it is.
# Notifies Irssi if all checks out.
sub check_messages {
	if (/incoming message from/) {
		my $user = get_user();
		
		# User on contact list?
		if ( $IGNORE_NOT_ON_LIST ) {
			if ( !user_on_list() ) {
				return 1;
			}
		}

		if ( $IGNORE_TIME eq 0 ) {
			print_incoming_messages();
			return 1;
		}

		if ( !exists($last_received{$user}) ) {
			$last_received{$user} = 0;
		}

		if ( ($stat[9] - $last_received{$user}) > $IGNORE_TIME ) {
			print_incoming_messages();
		}
		
		$last_received{$user} = $stat[9];
		return 1;
	} # if
	else { 
		return 0;
	}
}


# Checks whether a user is on the Centericq contact list.
# Returns 'false', if the users authorization is pending.
sub user_on_list {
	my $user = get_user();

	# User authorization pending?
	if ( exists( $auth_pending{$user} ) ) {
		return 0;
	}

	# Look for a contact directory.
	opendir DIR, $CENTERICQ or die $!;
	for ( readdir(DIR) ) {
		if ( m{$user} ) {
			closedir DIR;
			return 1;
		}
	}

	closedir DIR;
	return 0;
}


# Refrehes the values of variables which might have been changed within Irssi.
sub refresh_variables {
	if ( Irssi::settings_get_int('refresh_time') ne $REFRESH_TIME ) {
		update_refresh_time();
	}

	$IGNORE_TIME = Irssi::settings_get_int('ignore_time');
	$CENTERICQ_LOG = Irssi::settings_get_str('centericq_log');
	$CENTERICQ = Irssi::settings_get_str('centericq_folder');
	$MESSAGE_COLOR = Irssi::settings_get_str('message_color');
	$AUTHORIZATION_REQUEST =
			Irssi::settings_get_bool('authorization_requests');
	 $STATUS_CHANGE = Irssi::settings_get_bool('status_change');
	 $IGNORE_NOT_ON_LIST = Irssi::settings_get_bool('ignore_not_on_list');

	 $pretext = "$MESSAGE_COLOR-%n!$MESSAGE_COLOR- cICQ:%9";
}


# Changes the intervall time in which 'check_for_new()' is being executed by
# Irssi.
sub update_refresh_time {
	if ( defined($timeout_tag) ) {
		Irssi::timeout_remove($timeout_tag);
	}
	
	$REFRESH_TIME = Irssi::settings_get_int('refresh_time');
	$timeout_tag = Irssi::timeout_add($REFRESH_TIME * 1000,
			'check_for_new', undef);
}


# Extracts the user ID from a log file line and returns it.
sub get_user {
	my $anfang = index($line, "]");
	$anfang += 2;

	my $substring = substr($line, $anfang);
	my $ende = index($substring, " ");

	if ( $ende < 0 ) {
		return $substring;
	} else {
		return substr($line, $anfang, $ende);
	}
}


# Subroutine which is called by the 'focuson' command in Irssi.
# Stores focused nicks in array.
sub focus_on {
	my ($data, $server, $witem) = @_;

	if ($data) {

		# List all focused contacts when parameter is '?'.
		if ( $data eq '?' ) {
			print_focuson_list();
			return;
		}

		# Create new focus array.
		undef @focuson_list;
		@focuson_list = split(/ /, lc $data );

		if ( !exists($focuson_list[0]) ) {
			$focuson_list[0] = lc $data;
		}
		
		print_focuson_list();

	} else {

		# No parameter => delete all focuses.
		undef @focuson_list;

		my $window = Irssi::active_win();
		if ( defined($window) ) {
			$window->print("$pretext \"focuson\" switched off.",
				MSGLEVEL_NOTICES);
		} else {
			Irssi::print("$pretext \"focuson\" switched off.",
				MSGLEVEL_NOTICES);
		}
	}
}


# Prints the contacts which are focused to Irssi.
sub print_focuson_list {
	my $window = Irssi::active_win();

	if ( defined($window) ) {
		if ( exists($focuson_list[0]) ) {
			$window->print("$pretext watching out for @focuson_list", MSGLEVEL_NOTICES);
		} else {
			$window->print("$pretext watching out for no one.",	MSGLEVEL_NOTICES);
		}

	} else {
		if ( exists($focuson_list[0]) ) {
			Irssi::print("$pretext watching out for @focuson_list",	MSGLEVEL_NOTICES);
		} else {
			Irssi::print("$pretext watching out for no one.", MSGLEVEL_NOTICES);
		}
	}
}


# Checks whether a nick is in the focus list.
sub nick_in_focuson_list {
	my $nick;
	my $anfang;
	my $ende;
	my %hash = ();

	$anfang = index($line, "(") + 1;
	$ende = index($line, ")");

	$nick = lc substr($line, $anfang, $ende - $anfang);

	for ( @focuson_list ) { $hash{$_} = 1 }
	
	if ( exists($hash{$nick}) ) {
		return 1;
	} else {
		return 0;
	}
}


# Checks the pending authorization list. Removes entrys older than one day. Is
# being called by Irssi once a day.
sub check_pending_auths {
	my $time = time();

	for ( keys %auth_pending ) {
		# 1 day = 60 * 60 * 24 = 86400
		if ( $time - $auth_pending{$_} > 86400 ) {
			delete $auth_pending{$_};
		}
	}
}


# Prints information about an incoming messages.
sub print_incoming_messages {
	my $window = Irssi::active_win();
	if ( defined($window) ) {
		$window->print("$pretext" . substr($line, 25), MSGLEVEL_NOTICES);
	} else {
		Irssi::print("$pretext" . substr($line, 25), MSGLEVEL_NOTICES);
	}
}


# Usage info text.
sub print_usage {
	my @usage = (	"\n%9cICQ v$VERSION %9",
				"by Hannes 'epzilon' Forster",
				"http://epzilon.org/cicq",
				"\nThis script prints notifications of incoming events in",
				"Centericq to Irssi. You need to have the 'Detailed IM",
				"events' option in Centericq enabled in order for this",
				"script to work.",
				"\nOptions can be modified in the script file as well.",
				"%9Settings:%9",
				"centericq_folder       Location of Centericq folder.",
				"centericq_log          Location of logfile.",
				"refresh_time           Time between event checks. [in",
				"                       seconds]",
				"ignore_time            Ignore time after an incoming",
				"                       message. [in seconds]",
				"check_for_messages     Notify about incoming messages.",
				"authorization_requests Notify about authorization requests.",
				"status_change          Notify about status changes.",
				"ignore_not_on_list     Ignore messages from users not on",
				"                       contact list.",
				"message_color          Color of notifying messages. (see",
				 						"http://www.irssi.org/documentation/formats)",
				"\ncICQ commands:",
				"%9cicq\%_ Prints this usage info.",
				"%9focuson [nick1 nick2 ..]\%_ Notifies about status changes",
				"   of contact with nick1 (..). Can be turned off by using",
				"   this command without any parameter.",
				"   Use '?' as parameter to list all currently watched",
				"   contacts.\n");

	for ( @usage ) {
		Irssi::print($_, MSGLEVEL_NOTICES);
	}
}


# Bind the command 'cicq' to print the usage info.
Irssi::command_bind('cicq', 'print_usage');


# This command is for debugging only. -> no mentioning in the usage text.
Irssi::command_bind('authpending', 'print_auth_pending');
sub print_auth_pending {
	Irssi::print("Auth pending:");
	for ( keys %auth_pending ) {
		Irssi::print("$_ since $auth_pending{$_}");
	}
}

# Print a usage info text to Irssi after script has been loaded.
print_usage();

# Tell Irssi to execute 'check_for_new()' repeatedly.
$timeout_tag = Irssi::timeout_add($REFRESH_TIME * 1000, 'check_for_new', undef);

# Tell Irssi to execute 'check_pending_auths()' once a day.
Irssi::timeout_add(86400 * 1000, 'check_pending_auths', undef);


