#!/usr/bin/perl
# This file is part of Greenwich.
# 
# PerlPanel 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.
# 
# PerlPanel 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 PerlPanel; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Copyright: (C) 2003-2004 Gavin Brown <gavin.brown@uk.com>
#
# $Id: greenwich.pl,v 1.9 2006/02/07 12:33:57 gavin Exp $

use Gtk2 -init;
use Gtk2::GladeXML;
use Gtk2::Helper;
use Gnome2;
use Gnome2::GConf;
use IO::Socket;
use POSIX qw(setlocale);
use Locale::gettext;
use File::Basename qw(basename);
use strict;

my $NAME 		= 'Greenwich';
my $VERSION		= '0.8.2';
my $PREFIX		= '/usr';
my $GLADE_FILE		= (-d $PREFIX ? "$PREFIX/share/greenwich"	: $ENV{PWD}.'/share').'/greenwich.glade';
my $ICON_FILE		= (-d $PREFIX ? "$PREFIX/share/pixmaps"		: $ENV{PWD}.'/share').'/greenwich.png';
my $WHOIS_MAP		= (-d $PREFIX ? "$PREFIX/lib/greenwich"		: $ENV{PWD}.'/lib').'/WhoisMap.pm';
my $LOCALE_DIR		= (-d $PREFIX ? "$PREFIX/share/locale"		: $ENV{PWD}.'/locale');
my $FONT_PATH		= '/desktop/gnome/interface/monospace_font_name';
my $GCONF_BASE		= sprintf('/apps/%s', lc($NAME));
my $LAST_FILE		= '';
my $RUNNING		= 0;
my @DEFAULT_SERVERS	= qw(
	whois.godaddy.com
	whois.networksolutions.com
	whois.publicinterestregistry.org
	whois.ripe.net
	whois.centralnic.net
	whois.nic.us
	whois.nic.uk
	whois.aunic.net
	whois.cnnic.net.cn
	whois.crsnic.net
	whois.krnic.net
	whois.nic.ad.jp
	whois.nic.br
	whois.nic.fr
	whois.nic.nu
	whois.ripe.net
	whois.tonic.to
	whois.twnic.net
);

require($WHOIS_MAP);

my $AUTHORS = [
	'Gavin Brown <gavin.brown@uk.com> (main development)',
	'Mark Howard <mh@tildemh.com> (Debian packages and manpage)',
	'Dag Wieers <dag@wieers.com> (RPM packages)',
];

my $TRANSLATORS = [
	'Peter Wouda <peter.wouda@btinternet.com> (Dutch translation)',
	'Marcus Eskilsson <marcus_eskilsson@muf.se> (Swedish translation)',
	'Franck Routier <franck.routier@axege.com> (French Translation)',
	'Ricardo Mones Lastra <ricardo.mones@hispalinux.es> (Spanish Translation)',
	'Dario Conigliaro <mad.horse@tiscalinet.it> (Italian Translation)',
];

my $COPYRIGHT = sprintf('%s %04d Gavin Brown', chr(169), (localtime())[5]+1900);

if (!setlocale(LC_MESSAGES, $ENV{LANG})) {
	print STDERR "*** setlocale failed!\n";
} else {
	bindtextdomain(lc($NAME), $LOCALE_DIR);
	textdomain(lc($NAME));;
}

my $NORMAL_CURSOR	= Gtk2::Gdk::Cursor->new('left_ptr');
my $BUSY_CURSOR		= Gtk2::Gdk::Cursor->new('watch');

Gnome2::Program->init($NAME, $VERSION);

my $gconf = Gnome2::GConf::Client->get_default;
$gconf->add_dir($GCONF_BASE, 'preload-onelevel');

my $app = Gtk2::GladeXML->new($GLADE_FILE);
$app->signal_autoconnect_from_package('main');

$app->get_widget('output_view')->get_buffer->create_tag(
	'monospace',
	font		=> $gconf->get_string($FONT_PATH),
	wrap_mode	=> 'none',
);

my $servers_string = $gconf->get_string("$GCONF_BASE/servers");
my @SERVERS = ($servers_string ne '' ? split(',', $servers_string) : @DEFAULT_SERVERS);
$app->get_widget('server_combo')->set_popdown_strings(@SERVERS);
$app->get_widget('server_combo')->entry->set_text('');

my @HISTORY = split(',', $gconf->get_string("$GCONF_BASE/history"));
$app->get_widget('query_combo')->set_popdown_strings(@HISTORY);
$app->get_widget('query_combo')->entry->set_text('');

$app->get_widget('auto_checkbutton')->set_active($gconf->get_bool("$GCONF_BASE/auto"));
if ($gconf->get_bool("$GCONF_BASE/auto")) {
	$app->get_widget('optional_parts')->hide_all;
} else {
	$app->get_widget('optional_parts')->show_all;
}

$app->get_widget('recursion_checkbutton')->set_active($gconf->get_bool("$GCONF_BASE/recursive"));

$app->get_widget('server_combo')->disable_activate;
$app->get_widget('query_combo')->disable_activate;

$app->get_widget('server_combo')->entry->signal_connect('activate', \&make_request);
$app->get_widget('query_combo')->entry->signal_connect('activate', \&make_request);

$app->get_widget('query_combo')->entry->grab_focus;

set_status(gettext("Ready."));

my $icon = Gtk2::Gdk::Pixbuf->new_from_file($ICON_FILE);
$app->get_widget('main_window')->set_icon($icon);
$app->get_widget('save_question')->set_icon($icon);

$gconf->notify_add($FONT_PATH, sub {
	my $tag = $app->get_widget('output_view')->get_buffer->get_tag_table->lookup('monospace');
	$tag->set('font', $_[2]->{value}->{value});
	return 1;
});

$gconf->notify_add("$GCONF_BASE/auto", sub {
	$app->get_widget('auto_checkbutton')->set_active($_[2]->{value}->{value});
	return 1;
});

Gtk2->main;

sub close_program {
	Gtk2->main_quit;
}

sub save_output {

	my $dialog;
	if ('' ne (my $msg = Gtk2->check_version (2, 4, 0)) or $Gtk2::VERSION < 1.040) {
		$dialog = Gtk2::FileSelection->new(gettext("Choose File"));
	} else {
		$dialog = Gtk2::FileChooserDialog->new(
			gettext("Choose File"),
			$app->get_widget('main_window'),
			'save',
			'gtk-cancel'	=> 'cancel',
			'gtk-ok' => 'ok'
		);
	}

	$dialog->set_modal(1);
	$dialog->set_filename($LAST_FILE ne '' ? $LAST_FILE : $ENV{HOME}.'/');
	$dialog->signal_connect('response', sub {
		my $file = $dialog->get_filename;
		$dialog->destroy;
		if ($_[1] eq 'ok') {
			if (-e $file) {
				my $id;
				$app->get_widget('question_label')->set_text(sprintf(gettext("The file '%s' already exists.\n\nDo you want to replace it?"), basename($file)));
				$id = $app->get_widget('save_question')->signal_connect('response', sub {
					if ($_[1] == 0) {
						write_output_to_file($file);
					}
					$app->get_widget('save_question')->hide_all;
					$app->get_widget('save_question')->signal_handler_disconnect($id);
				});
				$app->get_widget('save_question')->show_all;
			} else {
				write_output_to_file($file);
			}
		}
	});
	$dialog->show_all;
}

sub write_output_to_file {
	my $file = shift;
	if (!open(FILE, ">$file")) {
		my $dialog = Gtk2::MessageDialog->new($app->get_widget('main_window'), 'modal', 'error', 'ok', $!);
		$dialog->signal_connect('response', sub { $dialog->destroy });
		$dialog->show_all;
	} else {
		print FILE $app->get_widget('output_view')->get_buffer->get_text(
			$app->get_widget('output_view')->get_buffer->get_start_iter,
			$app->get_widget('output_view')->get_buffer->get_end_iter,
			0,
		);
	}
	close (FILE);
	return 1;
}

sub open_aboutwindow {
	my $dialog = Gnome2::About->new(
		$NAME,
		$VERSION,
		$COPYRIGHT,
		gettext("A whois client for GNOME."),
		$AUTHORS,
		'',
		join("\n", @{$TRANSLATORS}),
		Gtk2::Gdk::Pixbuf->new_from_file($ICON_FILE),
	);
	$dialog->set_icon($icon);
	$dialog->show();
	return 1;
}

sub auto_toggled {
	$gconf->set_bool("$GCONF_BASE/auto", $app->get_widget('auto_checkbutton')->get_active);
	if ($app->get_widget('auto_checkbutton')->get_active) {
		$app->get_widget('optional_parts')->hide_all;
	} else {
		$app->get_widget('optional_parts')->show_all;
	}
	return 1;
}

sub recursion_toggled {
	$gconf->set_bool("$GCONF_BASE/recursive", $app->get_widget('recursion_checkbutton')->get_active);
	return 1;
}


sub make_request {
	return undef if $RUNNING == 1;
	my ($server, $port, $query);
	$query  = $app->get_widget('query_combo')->entry->get_text;
	return undef unless ($query ne '');

	@HISTORY = grep { $query ne $_ } grep { $_ ne '' } @HISTORY;
	splice(@HISTORY, 0, 0, $query);
	@HISTORY = splice(@HISTORY, 0, 50);
	$app->get_widget('query_combo')->set_popdown_strings(@HISTORY);
	my $str = join(',', @HISTORY);
	$gconf->set_string("$GCONF_BASE/history", $str);

	if ($app->get_widget('auto_checkbutton')->get_active) {
		$port = 43;
		$server = whois_server($query);
	} else {
		$server = $app->get_widget('server_combo')->entry->get_text;
		$port	= $app->get_widget('port_spinbutton')->get_value;

		@SERVERS = grep { $server ne $_ } grep { $_ ne '' } @SERVERS;
		splice(@SERVERS, 0, 0, $server);
		@SERVERS = splice(@SERVERS, 0, 50);
		$app->get_widget('server_combo')->set_popdown_strings();
		$gconf->set_string("$GCONF_BASE/servers", join(',', @SERVERS));

	}
	if ($server eq '') {
		return undef;
	} else {
		$app->get_widget('main_window')->window->set_cursor($BUSY_CURSOR);
		$app->get_widget('output_view')->get_window('text')->set_cursor($BUSY_CURSOR);
		set_output(get_whois($server, $port, $query, $gconf->get_int("$GCONF_BASE/timeout"), $gconf->get_bool("$GCONF_BASE/recursive")));
		$app->get_widget('main_window')->window->set_cursor($NORMAL_CURSOR);
		$app->get_widget('output_view')->get_window('text')->set_cursor($NORMAL_CURSOR);
	}
	set_status(gettext("Ready."));
	update_ui();
	return 1;
}

sub set_output {
	my $output = shift;
	$app->get_widget('output_view')->get_buffer->set_text('');
	$app->get_widget('output_view')->get_buffer->insert_with_tags_by_name(
		$app->get_widget('output_view')->get_buffer->get_start_iter,
		$output,
		'monospace'
	);
	return 1;
}

sub get_whois {
	my ($server, $port, $query, $timeout, $recursive) = @_;
	set_status(sprintf(gettext("Querying %s:%d..."), $server, $port));
	update_ui();
	my $socket = IO::Socket::INET->new(
		PeerAddr	=> $server,
		PeerPort	=> $port,
		Type		=> SOCK_STREAM,
		Proto		=> 'tcp',
		Timeout		=> $timeout
	);
	update_ui();
	if (!$socket) {
		$@ =~ s/^IO::Socket::INET: connect: //;
		my $dialog = Gtk2::MessageDialog->new($app->get_widget('main_window'), 'modal', 'error', 'ok', gettext($@));
		$dialog->set_icon($icon);
		$dialog->signal_connect('response', sub { $dialog->destroy });
		$dialog->show_all;
		return undef;
	}
	printf($socket "%s\r\n", $query);
	my $buffer = sprintf("[%s]\n", $server);
	while (<$socket>) {
		update_ui();
		$buffer .= $_;
	}
	close($socket);
	if ($buffer =~ /whois server:\s?([A-Za-z0-0\.\-]+)/i && $recursive == 1) {
		update_ui();
		$buffer = sprintf("[%s]\n\n", $server).get_whois($1, $port, $query, $timeout);
	}
	return $buffer;
}

sub set_status {
	my $text = shift;
	$app->get_widget('status_bar')->push($text);
}

sub query_changed {
	my $entry = shift;
	$app->get_widget('query_button')->set_sensitive($entry->get_text eq '' ? undef : 1);
	return 1;
}

sub update_ui { Gtk2->main_iteration while (Gtk2->events_pending) }
