#!/usr/bin/perl

# 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($siteInfo @smonths @lmonths %smonthHash %lmonthHash);
use vars qw($entityMap $errorMenuContent);
use Getopt::Long;
use Socket;
use POSIX qw(strftime);
use Time::Local;

my $version = "2.5.18";

local $siteInfo = {
	'freshmeat' => {
		'name' => "FreshMeat",
		'host' => "freshmeat.net",
		'path' => "/backend/recentnews.txt",
		'func' => \&processFreshMeat,
		'flds' => 'headline, date, url',
	},
	'slashdot' => {
		'name' => "Slashdot",
		'host' => "slashdot.org",
		'path' => "/slashdot.xml",
		'func' => \&processSlashdot,
		'flds' => 'title, url, time, author, department, topic, comments, section, image',
	},
	'linuxtoday' => {
		'name' => "LinuxToday",
		'host' => "linuxtoday.com",
		'path' => "/lthead.txt",
		'func' => \&processLinuxToday,
		'flds' => 'headline, url, date',
	},
	'old-segfault' => {
		'name' => "Old-Segfault (empty now)",
		'host' => "segfault.org",
		'path' => "/stories.txt",
		'func' => \&processSegfault,
		'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' => \&processPoorRdf,
		'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' => \&processPoorRdf,
		'flds' => 'title, link',
	},
	'daemonnews' => {
		'name' => "DaemonNews",
		'host' => "daily.daemonnews.org",
		'path' => "/ddn.rdf.php3",
		'func' => \&processPoorRdf,
		'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' => \&processPoorRdf,
		'flds' => 'title, link',
	},
	'kde-news' => {
		'name' => "KDE-News",
		'host' => "news.kde.org",
		'path' => "/rdf",
		'func' => \&processKDENews,
		'flds' => 'title, link',
	},
	'old-freekde' => {
		'name' => "Old-FreeKDE (taken off?)",
		'host' => "freekde.org",
		'path' => "/freekdeorg.rdf",
		'func' => \&processFreeKDE,
		'flds' => 'title, link',
	},
	'rootprompt' => {
		'name' => "RootPrompt",
		'host' => "rootprompt.org",
		'path' => "/rss/",
		'func' => \&processRootPrompt,
		'flds' => 'title, link, description',
	},
	'newsforge' => {
		'name' => "NewsForge",
		'host' => "www.newsforge.com",
		'path' => "/newsforge.xml",
		'func' => \&processSlashdot,
		'flds' => 'title, url, time, author, department, topic, comments, section, image',
	},
	'kuro5hin' => {
		'name' => "Kuro5hin",
		'host' => "www.kuro5hin.org",
		'path' => "/backend.rdf",
		'func' =>  \&processKuro5hin,
		'flds' => 'title, link, description',
	},
	'bbspot' => {
		'name' => "BBSpot",
		'host' => "bbspot.com",
		'path' => "/bbspot.rdf",
		'func' => \&processPoorRdf,
		'flds' => 'title, link',
	},
	'linuxfr' => {
		'name' => "LinuxFr",
		'host' => "linuxfr.org",
#		'path' => "/short.php3",
		'path' => "/backend.rss",
		'func' => \&processLinuxFr,
#		'flds' => 'headline, url, author_name, author_email, type',
		'flds' => 'title, link',
	},
	'thinkgeek' => {
		'name' => "ThinkGeek",
		'host' => "www.thinkgeek.com",
		'path' => "/thinkgeek.rdf",
		'func' => \&processPoorRdf,
		'flds' => 'title, link',
	},
	'cnn' => {
		'name' => "CNN",
		'host' => "www.cnn.com",
		'path' => "/desktop/content.html",
		'func' => \&processCNN,
		'flds' => 'headline, url',
	},
	# to be removed
	'bbc-world' => {
		'name' => "BBC-World (obsolete)",
		'host' => "news.bbc.co.uk",
		'path' => "/low/english/world/default.stm",
		'func' => \&processOldBBC,
		'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' => \&processOldBBC,
		'flds' => 'headline, url, abstract',
	},
	'bbc' => {
		'name' => "BBC",
		'host' => "news.bbc.co.uk", 'host0' => "tickers.bbc.co.uk",
		'path' => "/tickerdata/story2.dat",
		'func' => \&processBBC,
		'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 (%smonthHash, %lmonthHash) = ();
foreach (0 .. 11) { $smonthHash{$smonths[$_]} = $_; $lmonthHash{$lmonths[$_]} = $_; }

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

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

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

my $info  = undef;
my $defaultSite = 'freshmeat';
my $site  = undef;
my $name  = undef;
my $title = undef;
my $itemF = '%h\t%[(%Y-%m-%d %H:%M)]';
my $execF = q(netscape -remote 'openURL(%u, new-window)' || netscape '%u');
my $commF = undef;
my $iconT = '';
my $iconI = '';
my $iconH = '';
my $iconE = '';
my $wmIcons = 0;

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

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

GetOptions(
	"help|h|?"  => \&showHelp,
	"version|V" => \&showVersion,
	"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"  => \$wmIcons,
	"proxy=s"   => \$proxy,
	"frontpage:s" => \$frontpage,
	"file:s"    => \$menuFile,
	"fake:s"    => \$fakeFile,
	"timeout=i" => \$timeout,
) || wrongUsage();
wrongUsage() if @ARGV;

if (defined $info) {
	if ($info) {
		my $_info = $siteInfo->{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(", ", &getAllSiteNames()),
			"\n\nSpecify a site name after --info to get a site headlines info.\n";
	}
	exit(0);
}

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

my $siteName = $siteInfo->{$site}->{'name'};
my $siteHost = $siteInfo->{$site}->{'host'};
my $sitePath = $siteInfo->{$site}->{'path'};
my $siteFunc = $siteInfo->{$site}->{'func'};

$commF ||= "Exec $execF";

$title =~ s/\\t/\t/g;
$itemF =~ s/\\t/\t/g;
$commF =~ s/\\t/\t/g;

if ($wmIcons) {
	$iconT ||= "";
	$iconI ||= "menu/information.xpm";
	$iconH ||= "menu/home.xpm";
	$iconE ||= "menu/choice-no.xpm";
}

my $iconTStr = $iconT? "%$iconT%": "";
my $iconIStr = $iconI? "%$iconI%": "";
my $iconHStr = $iconH? "%$iconH%": "";
my $iconEStr = $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)
$menuFile = undef if defined $menuFile && $menuFile eq '-';
if ($menuFile) {
	$menuFile =~ s:^~(/|$):$home$1:;
	$menuFile =~ m:^(.+)/[^/]+$:; $workHome = $1 || ".";
} elsif (defined $menuFile) {
	$menuFile = "$workHome/$site.menu";
}

my $content = "";

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

my $frontpageEntry = "";
if (defined $frontpage) {
	my $cmd = &expandAllWidthSpecifiers($commF, {'u' => "http://$siteHost/"});
	$frontpageEntry = qq(+ "$iconHStr$siteName Frontpage" $cmd\n);
	$errorMenuContent .= qq($frontpageEntry\n+ "" Nop\n);
}

$errorMenuContent .= "+ `$iconEStr<msg>` DestroyMenu $name\n";

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

unless (defined $fakeFile) {
	$siteHost = $siteInfo->{$site}->{'host0'}
		if defined $siteInfo->{$site}->{'host0'};
	my $redirectDepth = 0;

HTTP_CONNECTION:
	my $host = $proxy || $siteHost;
	my $iaddr = inet_aton($host) || &dieNet("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)
	} || &dieNet("Can't connect host $host");
	alarm(0);
	select(SOCK); $| = 1; select(STDOUT);

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

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

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

my $entries = &$siteFunc;

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

foreach (@$entries) {
	my $text = &expandAllWidthSpecifiers($itemF, $_);
	my $comm = &expandAllWidthSpecifiers($commF, $_);
	$text =~ s/"/\\"/g;
	$content .= qq(+ "$iconIStr$text" $comm\n);
}

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

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

exit();

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

sub readLine {
	local $SIG{ALRM} = sub { die "timeout\n"; };
	alarm($timeout);
	my $line = eval { <SOCK> };
	if ($@) {
		&dieNet("Timeout of $timeout seconds reached") if $@ eq "timeout\n";
		&dieNet($@);
	}
	alarm(0);
	print STDERR $line if $ENV{"DEBUG_DUMP_RESPONSE"};
	return $line;
}

sub readAllLines {
	local $SIG{ALRM} = sub { die "timeout\n"; };
	alarm($timeout * 2);
	my $lines = eval { join('', <SOCK>) };
	if ($@) {
		&dieNet("Timeout of $timeout seconds reached") if $@ eq "timeout\n";
		&dieNet($@);
	}
	alarm(0);
	print STDERR $lines if $ENV{"DEBUG_DUMP_RESPONSE"};
	return $lines;
}

# make unix time from year (2001 or 101), mon (0..11), day, hour, min, sec
sub makeTime {  # ($$$$$$$)
	my ($hourD, $year, $mon, $day, $hour, $min, $sec) = @_;
	$hourD ||= 0;
	my $type = $TIMEFIELDS_DATE_TIME;

	unless (defined $hour || defined $min) {
		unless ($year || $day) {
			$type = $TIMEFIELDS_NONE;
			return [ 0, $type ];
		} else {
			$type = $TIMEFIELDS_ONLY_DATE;
		}
	}

	$year = 1973 unless $year && $year > 0;  # it's my year :-)
	$mon  = 0 unless $mon && $mon > 0 && $mon <= 11;
	$day  = 1 unless $day && $day > 0 && $day <= 31;
	$hour = 12 unless $hour && $hour >= 0 && $hour < 24;
	$min  = 0 unless $min && $min >= 0 && $min < 60;
	$sec  = 0 unless $sec && $sec >= 0 && $sec < 60;

	return [
		timegm($sec, $min, $hour, $day, $mon, $year) - $hourD * 60 * 60,
		$type
	];
}

sub setEntryAliasesAndTime ($$$$) {
	my $entry = shift;
	my $aliases = shift;
	my $timeSub = shift;
	my $hOffset = shift;

	my ($alias, $orig);
	while (($alias, $orig) = each %$aliases) {
		$entry->{$alias} = !$orig? "":
			ref($orig) eq 'CODE'? &{$orig}($entry): $entry->{$orig};
		$entry->{$alias} = "" unless defined $entry->{$alias};
	}

	$entry->{'_'} = makeTime($hOffset, &{$timeSub}($entry->{'d'}));
}

BEGIN {
	$entityMap = {
		'gt'    => '>',
		'lt'    => '<',
		'quot'  => '"',
		'amp'   => '&',
	};
}

sub processXml ($$$$) {
	my $entryTag = shift;
	my $aliases = shift;
	my $timeSub = shift;
	my $hOffset = shift;
	my @entries = ();

	my $doc = readAllLines();

	ENTRY:
	foreach ($doc =~ m!<$entryTag\b[^>]*>(.*?)</$entryTag>!sg) {
		s/&amp;quot;/"/g;  # fix buggy html in some backends
		# replace &#039; with single quote and &quot; with double quote
		s/&(?:(\w+)|#(\d+));/ $1? $entityMap->{$1} || "{$1}": chr($2) /ge;

		my $entry = {};

		foreach (m!(<.*?>.*?</.*?>)!sg) {
			m!<(.*?)>\s*(.*?)\s*</(.*?)>!s;
			# ignore incorect fields or throw error?
			next unless $1 && $2 && $3;
			next if $1 ne $3;
			$entry->{$1} = $2;
		}

		setEntryAliasesAndTime($entry, $aliases, $timeSub, $hOffset);
		push @entries, $entry;
	}
	return \@entries;
}

sub processText ($$$$) {
	my $fields = shift;
	my $aliases = shift;
	my $timeSub = shift;
	my $hOffset = shift;
	my @entries = ();

	ENTRY:
	while (1) {
		my $entry = {};
		foreach (@$fields) {
			my $line = readLine();
			last ENTRY unless defined $line;
			next if $_ eq '_ignore_';

			chomp($line);
			$line =~ s/"/\\"/g;
#			$line =~ s/<.*?>//g;
#			$line =~ s/&\w{1,5}?;/ /g;
			$entry->{$_} = $line;
		}

		setEntryAliasesAndTime($entry, $aliases, $timeSub, $hOffset);
		push @entries, $entry;
	}
	return \@entries;
}

sub processSlashdot () {
	return processXml(
		'story',
		{ 'h' => 'title', 'u' => 'url', 'd' => 'time' },
		sub ($) {
			$_[0] =~ /(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)/;
			($1, ($2 || 0) - 1, $3, $4, $5, $6);
		}, +0,
	);
}

sub processFreshMeat () {
	return processText(
		[ qw( headline date url ) ],
		{ 'h' => 'headline', 'u' => 'url', 'd' => 'date' },
		sub ($) {
			$_[0] =~ /^(?:\w+, )?(\w+) (\d+)\w* (\d+),? (\d+):(\d+)/;
			($3, $lmonthHash{$1}, $2, $4, $5, 0);
		}, -5 + (abs((localtime())[4] - 5.5) < 3),
	);
}

sub processLinuxToday () {
	my $line;
	while ($line = readLine()) {
		last if $line =~ /linuxtoday.com/;         # skip the text note
		last if $line =~ /&&/ and readLine() x 3;  # if it was replaced
	}
	return processText(
		[ qw( _ignore_ headline url date ) ],
		{ 'h' => 'headline', 'u' => 'url', 'd' => 'date' },
		sub ($) {
			$_[0] =~ /(\w+) (\d+), (\d+), (\d+):(\d+):(\d+)/;
			($3, $smonthHash{$1}, $2, $4, $5, $6);
		}, +0,
	);
}

sub processSegfault () {
	my $line;
	while ($line = readLine()) {
		last if $line =~ /^%%/;  # skip the text note
	}
	return processText(
		[ qw( headline url date author_name author_email type _ignore_ ) ],
		{ 'h' => 'headline', 'u' => 'url', 'd' => 'date' },
		sub ($) {
			$_[0] =~ /(\d+) (\w+) (\d+):(\d+):(\d+) (\d+)/;
			($6, $smonthHash{$2}, $1, $3, $4, $5);
		}, -8 + (abs((localtime())[4] - 5.5) < 3),
	);
}

sub processPoorRdf () {
	return processXml(
		'item',
		{ 'h' => 'title', 'u' => 'link', 'd' => undef },
		sub ($) {
			# this site's rdf does not supply the time, how weird...
			#(gmtime())[5,4,3,2,1,0];
			()
		}, +0,
	);
}

sub processLinuxApps_old () {
	return processText(
		[ qw( headline date url ) ],
		{ 'h' => 'headline', 'u' => 'url', 'd' => 'date' },
		sub ($) {
			$_[0] =~ /(\w+) (\d+) (\d+):(\d+):(\d+) \w+ (\d+)/;
			($6, $smonthHash{$1}, $2, $3, $4, $5);
		}, -5,
	);
}

sub processKDENews () {
	my $linkToTime = sub ($) { $_[0]->{'link'} =~ m|/(\d+)/?$|; $1; };
	return processXml(
		'item',
		{ 'h' => 'title', 'u' => 'link', 'd' => $linkToTime },
		sub ($) {
			(gmtime($_[0]))[5,4,3,2,1,0];
		}, +0,
	);
}

sub processFreeKDE () {
	my $linkToDate = sub ($) {
		$_[0]->{'link'} =~ m|/(\d\d/\d\d/\d\d)/|; $1? "20$1": '';
	};
	return processXml(
		'item',
		{ 'h' => 'title', 'u' => 'link', 'd' => $linkToDate },
		sub ($) {
			$_[0] =~ m|(\d+)/(\d+)/(\d+)|;
			($1, ($2 || 0) - 1, $3);
		}, +0,
	);
}

sub processRootPrompt () {
	my $descToDate = sub ($) {
		$_[0]->{'description'} =~ /^(\d+ \w{3} \d{4}):/; $1;
	};
	return processXml(
		'item',
		{ 'h' => 'title', 'u' => 'link', 'd' => $descToDate },
		sub ($) {
			$_[0] =~ /(\d+) (\w+) (\d+)/;
			($3, $smonthHash{$2}, $1);
		}, +0,
	);
}

sub processKuro5hin () {
	my $linkToDate = sub ($) {
		$_[0]->{'link'} =~ m|/(\d\d\d\d/\d{1,2}/\d{1,2})/|; $1;
	};
	return processXml(
		'item',
		{ 'h' => 'title', 'u' => 'link', 'd' => $linkToDate },
		sub ($) {
			$_[0] =~ m|(\d+)/(\d+)/(\d+)|;
			($1, ($2 || 0) - 1, $3);
		}, +0,
	);
}

sub processLinuxFr () {
	my $linkToDate = sub ($) {
		$_[0]->{'link'} =~ m|/(\d\d\d\d/\d\d/\d\d)/|; $1;
	};
	my $hackForUrl = sub ($) {
		# hack for netscape -remote openURL
		my $u = $_[0]->{'link'};
		$u =~ s|,|\%2c|g; $u;
	};
	return processXml(
		'item',
		{ 'h' => 'title', 'u' => $hackForUrl, 'd' => $linkToDate },
		sub ($) {
			$_[0] =~ m|(\d+)/(\d+)/(\d+)|;
			($1, ($2 || 0) - 1, $3);
		}, +0,
	);
}

sub processLinuxFr_old () {
	my $linkToDate = sub ($) {
		$_[0]->{'url'} =~ m|/(\d\d\d\d/\d\d/\d\d)/|; $1;
	};
	my $hackForUrl = sub ($) {
		# hack for netscape -remote openURL
		my $u = $_[0]->{'url'};
		$u =~ s|,|\%2c|g; $u;
	};
	my $line;
	while ($line = readLine()) {
		last if $line =~ /^%%/;  # skip the text note
	}
	return processText(
		[ qw( headline url author_name author_email type _ignore_ ) ],
		{ 'h' => 'headline', 'u' => $hackForUrl, 'd' => $linkToDate },
		sub ($) {
			$_[0] =~ m|(\d+)/(\d+)/(\d+)|;
			($1, ($2 || 0) - 1, $3);
		}, +0,
	);
}

sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub processB
sub 