#!/usr/athena/bin/perl

# weather -- gateway to weather
#
# $Id: weather,v 1.48 2015/09/25 18:56:43 wwwmaint Exp $
#
# Matthew Gray (mkgray@athena.mit.edu)
#
# Kevin Fu (fubob@mit.edu) -- Added Cache stuff, cleaned HTML, debugged
#				Fixed citycodes (no duplicates)
unshift(@INC, '/var/www/cgi-bin/lib');

require 'our-chat2.pl';
require 'newcitycode.pl';
require 'ourfork.pl';
use Fcntl;

$TimeOut       = 60;  # Number of minutes before weather cache times out
# %CacheCities   = ('bos',1,'nyc',1,'bwi',1,'ord',1,'sea',1,'dsm',1); 
# Cities to cache
$UseThisServer = "rainmaker.wunderground.com";
$UseThisPort   = 23;
#$UseThisServer = "um-weather.sprl.umich.edu";
#$UseThisPort   = 13023;
#$UseThisServer = "downwind.sprl.umich.edu";
#$UseThisPort   = 3000;
$UseThisBase   = "http://stuff.mit.edu/weather"; # the basetag
$UseThisDirectory = "/var/www/data/weather/";
#$DrinkThisCup ="";
#$HaveANiceDay = 0;
#$WeWurMur= "";

# the next statement nices this process's priority to 4.  The last
# argument is the priority.
# note: setpriority won't work under Solaris.

#setpriority(&PRIO_PROCESS,0,4);

$| = 1;
open(STDERR,">&STDOUT");

$query = join(' ', @ARGV) if $ARGV[0];

&ourfork'safefork(16, "do_weather", $query);

# package weather;

sub do_weather {
    local($station) = @_;

    print("Content-Type: text/html\n\n");

    $cmd = $ENV{'PATH_INFO'};

    $cmd =~ s%^/%%;

    if ($cmd eq "usmap") {
	&do_usmap($station);
    }
    elsif ($station) {
	print"<html><head><title>Weather Conditions for $station</title>\n" .
	    "<base href=\"$UseThisBase\"></head>";	 
	&get_weather($station);
	print "</html>";
    }
    else{
	&do_default();
    }
}

sub do_default {
    # Print the default page (where there is no query)
    print <<"EndOfInfo";
<!doctype HTML public "-//IETF//DTD HTML 2.0//EN">
<html>
<head>
<title>Weather</title>
<base href="$UseThisBase">
</head>

<body>
<h1>Weather</h1>

<p>The weather server at the University of Michigan (which we have
been using since October 1995) has been discontinued.  This weather
gateway is back online but may be terminated with little notice.
Consider yourself warned.</p>

<p>For those that wish to prepare for the inevitable, we have put together
a <a href="/doc/weather-back.html">list</a> of alternate weather servers. </p>
<h3>How to search for weather reports</h3>

<p>You can search for local weather reports in several ways.  The
most efficient way is to enter your city's <a
href="/doc/city-codes.html">3-character code</a> if you already know it;
check the list of city codes if you do not know it.</p>

<p>You may also try searching by entering the name of your city.  The
gateway will return a list of possible matching city names for your
selection.  You may also request all the reports for a given state.
As an example, to get a list of all reporting cities in Massachusetts,
enter &quot;, MA&quot;.  Note that this includes a comma, a space, and
a postal abbreviation; entering just &quot;MA&quot; will return any
city whose name contains the letters "ma", including Bismarck, ND. 

<p>To summarize with some example searches:</p>
<dl>
<dt><b>BOS</b></dt><dd>Weather report for Boston, MA (BOS).</dd>
<dt><b>Boston</b></dt><dd>List of all reporting cities named Boston.</dd>
<dt><b>, MA</b></dt><dd>List of all reporting cities in the state MA.</dd>
</dl>

<h3>Problems?</h3>

<p>If you have trouble using this gateway or other services, please
see our <a href="/faq/">FAQ</a>, which includes answers to questions
such as &quot;<a href="http://stuff.mit.edu/faq/weather.html">Why isn't
my town on your weather map?</a>&quot; or &quot;<a
href="http://stuff.mit.edu/faq/no-report.html">Why is my weather report
blank?</a>&quot; If you still have trouble or if you find any bugs
that appear to be specific to our gateway, please <a
href="/help/before-you-mail.html">send us mail</a> with a complete
detailed description of your problem.</p>

<p>Please note that all our information originates from the <a
href="http://www.nws.noaa.gov/">National Weather Service</a>.  As
such, we are limited to delivering whatever information the NWS
provides.  We do not have reports for cities outside the United States
nor do we keep an archive of old weather reports; <a
href="http://stuff.mit.edu/faq/no.html">don't bother asking</a>.</p>

<hr>

<h2><a href="$UseThisBase?bos">Boston area forecast</a></h2>

<isindex>
</body>
</html>

EndOfInfo
}

sub do_usmap {
    local($args) = @_;

    if ($args !~ /,/) {
 	print "<html><head><title>Weather Conditions for $station</title>\n" .
	    "<base href=\"$UseThisBase\"></head>";
	&get_weather($args);
	print "</html>\n";
        return;
    }

    local($x, $y);
    ($x, $y) = split(',', $args);

    @name = ('BOS','LAX','MCO','TOP','JFK','SLC','BWI','DFW','ORD','MSY',
	     'DSM','DEN','MEM','PHX','SEA','ATL');
    @x = (750,100,675,440,700,225,700,400,550,500,475,300,600,200,150,613);
    @y = (175,325,475,320,200,250,250,450,250,475,260,300,350,375,100,382);

    $min = 1000000;
    foreach $n (0..$#x){
	$locale = $name[$n];
	if(($dist=&distance($x, $y, $x[$n], $y[$n])) < $min){
	    $min = $dist;
	    $whereami = $locale;
	}
    }

    print "<html><head><title>Nearest Weather Station is $whereami</title>\n".
	"<base href=\"$UseThisBase\"></head>\n";
    &get_weather($whereami);
    print "</html>\n";
}

sub distance {
    local($x1, $y1, $x2, $y2) = @_;
    local($xd) = (($x1-$x2)*($x1-$x2));
    local($sqd)=$xd + (($y1-$y2)*($y1-$y2));
    $sqd;
}

sub get_weather {
    # This should only respond with the weather surrounded by <body> tags
    # It should only return data by printing it, not returning a value.

    local($station, $more_than_one_match) = @_;

    $rawstation = $station;

    $station =~ tr/[A-Z]/[a-z]/;
    $station =~ tr/[_\-\.\+]/ /;
    $station =~ s/%20/ /g;

    print <<"EndOfInfo2";
<body>
<p>Weather conditions for <strong>$rawstation</strong> should appear
below.</p>

<p>This <a href="/doc/weather-back.html">weather gateway is back</a>
online with some modifications to more efficiently use the <a
href="/cgi/word?studly">studlier</a> weather server at the University
of Michigan (which we have been using since October 1995).  Please see
the <a href="/weather/">initial weather page</a> for more information
on how to conduct searches.</p>

<h3>Problems?</h3>

<p>If you have trouble using this gateway or other services, please
see our <a href="/faq/">FAQ</a>, which includes answers to questions
such as &quot;<a href="http://stuff.mit.edu/faq/weather.html">Why isn't
my town on your weather map?</a>&quot; or &quot;<a
href="http://stuff.mit.edu/faq/no-report.html">Why is my weather report
blank?</a>&quot; If you still have trouble or if you find any bugs
that appear to be specific to our gateway, please <a
href="/help/before-you-mail.html">send us mail</a> with a complete
detailed description of your problem.</p>

<hr>

<h2><a href="$UseThisBase?bos">Boston area forecast</a></h2>

<isindex>
EndOfInfo2

    %citycodes = &citycode'citycode_assoc_array; 
    if (length($station) == 3) {
	# Check if the station is a valid 3-character code
        $check=0;

        while((($k, $v) = each %citycodes))
	{ if ($station eq $v)
	  { $check=1; last; }               
        }
        if ($check) {
	    # Return forecast
            # Only cache the popular cities
            # Record stats into a file (each city-> num of requests)

            $LOCK_SH = 1;		
            $LOCK_EX = 2;
            $LOCK_NB = 4;
            $LOCK_UN = 8;	
	    
	    # First do some accounting of the city popularity
            open(LOCK,">>$UseThisDirectory"."weatherdb.lock");
            #flock(LOCK,$LOCK_EX);
	    $lock = pack('s s l l s', &F_WRLCK, 0, 0, 0, 0);
	    fcntl(LOCK, &F_SETLKW, $lock);

            dbmopen(WEATHERDB,"$UseThisDirectory"."weatherdb",0666);
            if ($WEATHERDB{$station}>=1)
	    { $WEATHERDB{$station}++;  }
            else
	    { $WEATHERDB{$station}=1;  }
            dbmclose(WEATHERDB);	
            #flock(LOCK,$LOCK_UN); 
	    $lock = pack('s s l l s', &F_UNLCK, 0, 0, 0, 0);
	    fcntl(LOCK, &F_SETLK, $lock);

            # Check if the station is cacheable, the cache file exists, 
            # and whether the cache is outdated

            if                         #(($CacheCities{$station}) &&
                ((-e "$UseThisDirectory"."weather.$station") &&
		((-M "$UseThisDirectory"."weather.$station")*1440 <= $TimeOut))
	    {
		open(LOCK2,">$UseThisDirectory"."weathercachedb.lock");
		#flock(LOCK2,$LOCK_EX);
		$lock = pack('s s l l s', &F_WRLCK, 0, 0, 0, 0);
		fcntl(LOCK2, &F_SETLKW, $lock);
		dbmopen(WEATHERCACHEDB,"$UseThisDirectory"."weathercachedb",
			0666);
		if ($WEATHERCACHEDB{$station}>=1)
		{ $WEATHERCACHEDB{$station}++;  }
		else
		{ $WEATHERCACHEDB{$station}=1;  }
		dbmclose(WEATHERCACHEDB);
		#flock(LOCK2,$LOCK_UN);
		$lock = pack('s s l l s', &F_UNLCK, 0, 0, 0, 0);
		fcntl(LOCK2, &F_SETLK, $lock);

		open(WEATHER,"$UseThisDirectory"."weather.$station");
                #flock(WEATHER,$LOCK_EX);
		$lock = pack('s s l l s', &F_WRLCK, 0, 0, 0, 0);
		fcntl(WEATHER, &F_SETLKW, $lock);
		$/ = undef;
		$forecast = <WEATHER>."</pre>";
		$/ = "\n";
       	        close(WEATHER);   
                #flock(WEATHER,$LOCK_UN);
		$lock = pack('s s l l s', &F_UNLCK, 0, 0, 0, 0);
		fcntl(WEATHER, &F_SETLK, $lock);
	    }
            else
	    {
	        $closeme = &chat'open_port($UseThisServer, $UseThisPort);
		if ($closeme) {	
                    &listen(1);
                    &chat'print("\n");
                    &listen(1);
		    &chat'print($station."\n");
		    while($forecast!~/CITY FORECAST MENU/){
                        $forecast .= &listen(1);
			&chat'print("\n");
                    }
                    &chat'print("x\n");
                    &chat'close($closeme);
		    $forecast=~s/Press Return to continue, M to return to menu, X to exit://g;
                    $forecast=~s/\n\r/\n/g;
		    $forecast=~s/CITY FORECAST MENU[\w\W]+//;
                    $forecast=~s/\s+\.([^\.\n]+)\.\.\./\n\n<h2>\1<\/h2>/g;
                    $forecast=~s/(Weather Conditions at .+)/<h1>\1<\/h1><pre>/;
                    $forecast=~s/\n(.+)FORECAST\n/\n<h2>\1FORECAST<\/h2>\n/;  
		    $forecast=~s/Press Return for menu://;

		    # If the station is cachable, cache it.
#		    if ($station}) {            
			open(WEATHER, ">$UseThisDirectory"."weather.$station");
			#flock(WEATHER, $LOCK_EX);
			$lock = pack('s s l l s', &F_WRLCK, 0, 0, 0, 0);
			fcntl(WEATHER, &F_SETLKW, $lock);
			print WEATHER "$forecast\n";
			#flock(WEATHER, $LOCK_UN);
			$lock = pack('s s l l s', &F_UNLCK, 0, 0, 0, 0);
			fcntl(WEATHER, &F_SETLK, $lock);
			close(WEATHER);
#		    }
		    $forecast="<pre>".$forecast."</pre>";
		} else {
		    $forecast = 
			"<h2>The weather is currently unavailable.</h2>\n",
			"<p>Unable to access weather service.\n",
			"Please try again later.</p>\n"; 
		}
	    }
	    print "$forecast</body>";
	    return;
	}
    }
    # Print error (either not a city code, too many cities, or none at all )

    print"<h1>Search results for $rawstation</h1>";
    
    @matches=();
    foreach $thiscity (keys (%citycodes)) {
      if ($thiscity =~ /$station/o) { @matches= ($thiscity,@matches);}
    }

    if (@matches) {

	$more_than_one_match = "yup" if ($#matches != 0);
	 
	print"\nThis gateway is designed to accept three-character ";
	print"<a href=\"/doc/city-codes.html\">city codes</a>.\n";
	print"Your query was not a valid city code, but it\n";
	print"matched ", $more_than_one_match?"several ":"one ";
	print"of the entries we have in our\n";
	print"local table of cities.\n</p>";
	print"<p>For your convenience, you may select ";
	print $more_than_one_match?"one of these links.":"this link.","\n";
	print"To minimize the load on our server, we\n";
	print"ask that you use ";
	print $more_than_one_match?"one of these three-character codes":"this three-character code";
	print" in the future, rather than the\n";
	print"query you just made (\"$rawstation\").<p>\n";
	print"<ul>\n";
	for (@matches) {
	    print "<li><b><a href=\"$UseThisBase?";
	    print($citycodes{$_}."\">".$_."</a></b> (".$citycodes{$_},")\n");
	}
	print "</body>";
	return;
    } else {
	print"Your query was not a three-character ";
	print"<a href=\"/doc/city-codes.html\">city code</a>,\n";
	print"and it did not match any of the city-to-city-code\n";
	print"mappings we have on our server.\n";
	print"We're sorry, but <a href=\"/doc/city-codes.html\">we\n";
	print"can't add other city codes</a>.\n</p></body>";
	return;
    }
}

sub listen {
    local($secs) = @_;
    local($return,$tmp) = "";
    while (length($tmp = &chat'expect($secs, '(.|\n)+', '$&'))) {
      print $tmp if $trace;
      $return .= $tmp;
    }
    $return;
}

1;
