#!/usr/bin/perl

# Copyright (c) 1999-2009 Mikhael Goikhman
#
# 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, see: <http://www.gnu.org/licenses/>

# Filter this script to pod2man to get a man page:
#   pod2man -c "Fvwm Utilities" fvwm-menu-headlines | nroff -man | less -e

require 5.002;
use strict;
use vars qw($site_info @smonths @lmonths %smonth_hash %lmonth_hash);
use vars qw($entity_map $error_menu_content);
use Getopt::Long;
use Socket;
use POSIX qw(strftime);
use Time::Local;

my $version = "2.7.0";

local $site_info = {
	'freshmeat' => {
		'name' => "FreshMeat",
		'host' => "freshmeat.net",
		'path' => "/backend/recentnews.txt",
		'func' => \&process_freshmeat,
		'flds' => 'headline, date, url',
	},
	'slashdot' => {
		'name' => "Slashdot",
		'host' => "slashdot.org",
		'path' => "/slashdot.xml",
		'func' => \&process_slashdot,
		'flds' => 'title, url, time, author, department, topic, comments, section, image',
	},
	'linuxtoday' => {
		'name' => "LinuxToday",
		'host' => "linuxtoday.com",
		'path' => "/lthead.txt",
		'func' => \&process_linuxtoday,
		'flds' => 'headline, url, date',
	},
	'old-segfault' => {
		'name' => "Old-Segfault (empty now)",
		'host' => "segfault.org",
		'path' => "/stories.txt",
		'func' => \&process_segfault,
		'flds' => 'headline, url, date, author_name, author_email, type',
	},
	'old-appwatch' => {
		'name' => "Old-AppWatch (closed by ZDNet)",
		'host' => "www.appwatch.com",
		'path' => "/appwatch.rdf",
		'func' => \&process_poor_rdf,
		'flds' => 'title, link, description',
	},
	'old-linuxapps' => {
		'name' => "Old-LinuxApps (moved/closed)",
		'host' => "www.linuxapps.com-closed",
		'path' => "/backend/linux_basic.txt",
		'func' => undef,
		'flds' => 'headline, date, url',
	},
	'old-justlinux' => {
		'name' => "Old-JustLinux (no headlines?)",
		'host' => "www.justlinux.com",
		'path' => "/backend/discussion.rdf",
		'func' => \&process_poor_rdf,
		'flds' => 'title, link',
	},
	'daemonnews' => {
		'name' => "DaemonNews",
		'host' => "daily.daemonnews.org",
		'path' => "/ddn.rdf.php3",
		'func' => \&process_poor_rdf,
		'flds' => 'title, link',
	},
	# this is now called FootNotes or GNOME Desktop News, was news.gnome.org
	'gnome-news' => {
		'name' => "GNOME-News",
		'host' => "www.gnomedesktop.org",
		'path' => "/backend.php",
		'func' => \&process_poor_rdf,
		'flds' => 'title, link',
	},
	'kde-news' => {
		'name' => "KDE-News",
		'host' => "news.kde.org",
		'path' => "/rdf",
		'func' => \&process_kde_news,
		'flds' => 'title, link',
	},
	'old-freekde' => {
		'name' => "Old-FreeKDE (taken off?)",
		'host' => "freekde.org",
		'path' => "/freekdeorg.rdf",
		'func' => \&process_freekde,
		'flds' => 'title, link',
	},
	'rootprompt' => {
		'name' => "RootPrompt",
		'host' => "rootprompt.org",
		'path' => "/rss/",
		'func' => \&process_rootprompt,
		'flds' => 'title, link, description',
	},
	'newsforge' => {
		'name' => "NewsForge",
		'host' => "www.newsforge.com",
		'path' => "/newsforge.xml",
		'func' => \&process_slashdot,
		'flds' => 'title, url, time, author, department, topic, comments, section, image',
	},
	'kuro5hin' => {
		'name' => "Kuro5hin",
		'host' => "www.kuro5hin.org",
		'path' => "/backend.rdf",
		'func' =>  \&process_kuro5hin,
		'flds' => 'title, link, description',
	},
	'bbspot' => {
		'name' => "BBSpot",
		'host' => "bbspot.com",
		'path' => "/bbspot.rdf",
		'func' => \&process_poor_rdf,
		'flds' => 'title, link',
	},
	'linuxfr' => {
		'name' => "LinuxFr",
		'host' => "linuxfr.org",
#		'path' => "/short.php3",
		'path' => "/backend.rss",
		'func' => \&process_linuxfr,
#		'flds' => 'headline, url, author_name, author_email, type',
		'flds' => 'title, link',
	},
	'thinkgeek' => {
		'name' => "ThinkGeek",
		'host' => "www.thinkgeek.com",
		'path' => "/thinkgeek.rdf",
		'func' => \&process_poor_rdf,
		'flds' => 'title, link',
	},
	'cnn' => {
		'name' => "CNN",
		'host' => "www.cnn.com",
		'path' => "/desktop/content.html",
		'func' => \&process_cnn,
		'flds' => 'headline, url',
	},
	# to be removed
	'bbc-world' => {
		'name' => "BBC-World (obsolete)",
		'host' => "news.bbc.co.uk",
		'path' => "/low/english/world/default.stm",
		'func' => \&process_old_bbc,
		'flds' => 'headline, url, abstract',
	},
	# to be removed
	'bbc-scitech' => {
		'name' => "BBC-SciTech (obsolete)",
		'host' => "news.bbc.co.uk",
		'path' => "/low/english/sci/tech/default.stm",
		'func' => \&process_old_bbc,
		'flds' => 'headline, url, abstract',
	},
	'bbc' => {
		'name' => "BBC",
		'host' => "news.bbc.co.uk", 'host0' => "tickers.bbc.co.uk",
		'path' => "/tickerdata/story2.dat",
		'func' => \&process_bcc,
		'flds' => 'story, headline, url',
	},
};

# Site specific parsers may use these constants to convert month to unix time.
local @smonths = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
local @lmonths = qw(January February March April May June July August September October November December);
local (%smonth_hash, %lmonth_hash) = ();
foreach (0 .. 11) { $smonth_hash{$smonths[$_]} = $_; $lmonth_hash{$lmonths[$_]} = $_; }

my $TIMEFIELDS_DATE_TIME = 1;
my $TIMEFIELDS_ONLY_DATE = 2;
my $TIMEFIELDS_NONE = 3;

my $home  = $ENV{'HOME'} || '/tmp';
my $fvwm_user_dir = $ENV{'FVWM_USERDIR'} || "$home/.fvwm";
$fvwm_user_dir = $home unless -d $fvwm_user_dir;
my $work_home = "$fvwm_user_dir/.fvwm-menu-headlines";

require "$work_home/extension.pl" if -r "$work_home/extension.pl";

my $info  = undef;
my $default_site = 'freshmeat';
my $site  = undef;
my $name  = undef;
my $title = undef;
my $itemf = '%h\t%[(%Y-%m-%d %H:%M)]';
my $execf = q(firefox '%u');
my $commf = undef;
my $icont = '';
my $iconi = '';
my $iconh = '';
my $icone = '';
my $wm_icons = 0;

my $proxy = undef;
my $port  = 80;
my $frontpage = undef;

my @time  = localtime();
my $menu_filename = undef;
my $fake_filename = undef;
my $timeout = 20;
my $endl = "\r\n";  # this is preferable for http sockets to "\n"

GetOptions(
	"help|h|?"  => \&show_help,
	"version|V" => \&show_version,
	"info:s"    => \$info,
	"site=s"    => \$site,
	"name=s"    => \$name,
	"title=s"   => \$title,
	"item=s"    => \$itemf,
	"exec=s"    => \$execf,
	"command=s"    => \$commf,
	"icon-title=s" => \$icont,
	"icon-item=s"  => \$iconi,
	"icon-home=s"  => \$iconh,
	"icon-error=s" => \$icone,
	"wm-icons"  => \$wm_icons,
	"proxy=s"   => \$proxy,
	"frontpage:s" => \$frontpage,
	"file:s"    => \$menu_filename,
	"fake:s"    => \$fake_filename,
	"timeout=i" => \$timeout,
) || wrong_usage();
wrong_usage() if @ARGV;

if (defined $info) {
	if ($info) {
		my $_info = $site_info->{lc($info)};
		die "Unsupported site '$info'; try --info.\n" unless $_info;
		my $host0 = $_info->{'host0'} || $_info->{'host'};
		print
			"Site Name:\n\t$_info->{'name'}\n",
			"Home Page:\n\thttp://$_info->{'host'}/\n",
			"Headlines:\n\thttp://$host0$_info->{'path'}\n",
			"Headline fields:\n\t$_info->{'flds'}\n";
	} else {
		print "All supported sites:\n\t", join(", ", &get_all_site_names()),
			"\n\nSpecify a site name after --info to get a site headlines info.\n";
	}
	exit(0);
}

$site  ||= $default_site; $site = lc($site);
die "Unsupported site '$site'; try --info.\n" unless exists $site_info->{$site};
#$name ||= "MenuHeadlines$site_info->{$site}->{'name'}";
$name  ||= $site;
$title ||= "$site_info->{$site}->{'name'} Headlines";

my $site_name = $site_info->{$site}->{'name'};
my $site_host = $site_info->{$site}->{'host'};
my $site_path = $site_info->{$site}->{'path'};
my $site_func = $site_info->{$site}->{'func'};

$commf ||= "Exec $execf";

$title =~ s/\\t/\t/g;
$itemf =~ s/\\t/\t/g;
$commf =~ s/\\t/\t/g;

if ($wm_icons) {
	$icont ||= "";
	$iconi ||= "menu/information.xpm";
	$iconh ||= "menu/home.xpm";
	$icone ||= "menu/choice-no.xpm";
}

my $icont_str = $icont ? "%$icont%" : "";
my $iconi_str = $iconi ? "%$iconi%" : "";
my $iconh_str = $iconh ? "%$iconh%" : "";
my $icone_str = $icone ? "%$icone%" : "";

if (defined $proxy && $proxy =~ /^(.+):(\d+)$/) {
	$proxy = $1;
	$port = $2;
}

# Three cases:
#   1) no --file option or value '-' specified (STDOUT is used)
#   2) no or empty menu file in --file specified (the default name is used)
#   3) non-empty menu file specified (use it)
$menu_filename = undef if defined $menu_filename && $menu_filename eq '-';
if ($menu_filename) {
	$menu_filename =~ s:^~(/|$):$home$1:;
	$menu_filename =~ m:^(.+)/[^/]+$:; $work_home = $1 || ".";
} elsif (defined $menu_filename) {
	$menu_filename = "$work_home/$site.menu";
}

my $content = "";

$content .= qq(DestroyMenu $name\n);
$content .= qq(AddToMenu $name "$icont_str$title" Title\n);
local $error_menu_content = $content;

my $frontpage_entry = "";
if (defined $frontpage) {
	my $cmd = &expand_all_width_specifiers($commf, {'u' => "http://$site_host/"});
	$frontpage_entry = qq(+ "$iconh_str$site_name Frontpage" $cmd\n);
	$error_menu_content .= qq($frontpage_entry\n+ "" Nop\n);
}

$error_menu_content .= "+ `$icone_str<msg>` DestroyMenu $name\n";

if (defined $frontpage && $frontpage !~ /^b/) {
	$content .= qq($frontpage_entry\n+ "" Nop\n);
}

unless (defined $fake_filename) {
	$site_host = $site_info->{$site}->{'host0'}
		if defined $site_info->{$site}->{'host0'};
	my $redirect_depth = 0;

HTTP_CONNECTION:
	my $host = $proxy || $site_host;
	my $iaddr = inet_aton($host) || &die_net("Can't resolve host $host");
	my $paddr = sockaddr_in($port, $iaddr);
	my $proto = getprotobyname('tcp');

	local $SIG{ALRM} = sub { die "timeout\n"; };
	alarm($timeout);
	eval {
		socket(SOCK, PF_INET, SOCK_STREAM, $proto) &&
		connect(SOCK, $paddr)
	} || &die_net("Can't connect host $host");
	alarm(0);
	select(SOCK); $| = 1; select(STDOUT);

	# do http request
	my $http_headers = "$endl" .
		"Host: $site_host$endl" .
		"Connection: close$endl" .
		"User-Agent: fvwm-menu-headlines/$version$endl" .
		"Pragma: no-cache$endl" .
		"$endl";
	if (defined $proxy) {
		print SOCK "GET http://$site_host$site_path HTTP/1.1$http_headers";
	} else {
		print SOCK "GET $site_path HTTP/1.1$http_headers";
	}

	unless (read_line() =~ m{HTTP/1\.\d (\d+) \w+}) {
		&die_net("Invalid HTTP response from http://$site_host$site_path", 0);
	}
	my $status = $1;
	if ($status =~ /^301|302$/ && ++$redirect_depth < 5) {
		# redirection
		while (1) {
			my $line = read_line();
			$line =~ s/[\n\r]+$//s;
			last unless $line;
			if ($line =~ m{Location: http://([^/]+)(/.*)}i) {
				$site_host = $1;
				$site_path = $2;
				goto HTTP_CONNECTION;
			}
		}
	}
	&die_net("Unexpected HTTP response $status from http://$site_host$site_path", 0)
		unless $status eq "200";

	# skip http response headers
	while (read_line() !~ /^\r?\n?$/s) {}
} else {
	if ($fake_filename) {
		$fake_filename =~ s:^~(/|$):$home$1:;
	} else {
		$fake_filename = "$work_home/$site.in";
	}
	open(SOCK, "<$fake_filename") || &die_sys("Can't open $fake_filename");
}

my $entries = &$site_func;

close(SOCK) || &die_net("Error closing socket");

foreach (@$entries) {
	my $text = &expand_all_width_specifiers($itemf, $_);
	my $comm = &expand_all_width_specifiers($commf, $_);
	$text =~ s/"/\\"/g;
	$content .= qq(+ "$iconi_str$text" $comm\n);
}

if (defined $frontpage && $frontpage =~ /^b/) {
	$content .= qq(+ "" Nop\n$frontpage_entry);
}

if (defined $menu_filename) {
	unless (-d $work_home) {
		mkdir($work_home, 0775) || &die_sys("Can't create $work_home");
	}
	open(MENU_FILE, ">$menu_filename") || &die_sys("Can't open $menu_filename");
	print MENU_FILE $content;
	close(MENU_FILE) || &die_sys("Can't close $menu_filename");
} else {
	print $content;
}

exit();

# ---------------------------------------------------------------------------

sub read_line {
	local $SIG{ALRM} = sub { die "timeout\n"; };
	alarm($timeout);
	my $line = eval { <SOCK> };
	if ($@) {
		&die_net("Timeout of $timeout seconds reached") if $@ eq "timeo                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 