#!/usr/bin/perl
# modified to fix directories with new rsi application
# NJP 11-12-04
#!C:/perl/bin/perl
# could possibly be loosened
require 5.004;
# these are a good idea
use strict;
use English;
use CGI qw( :standard );
# CGI::Pretty is the same as CGI.pm but makes html human-readable
# 2000-11-20 CGI::Pretty is now broken (tables are missing parameters)
#use CGI::Pretty qw( :standard );
# our entry point
&::main;
sub main
{
&::Initialize;
if (!CGI::ReadParse()) {
&::PrintForm;
}
else {
&::ProcessForm;
}
}
sub Initialize
{
# CGI.pm initializaton
# $CGI::Pretty::INDENT = " ";
# paths to IDL, temporary directory, etc.
$::idlExecutable = '/Applications/rsi/idl/bin/idl';
$::cgiTmpDir = '/Library/WebServer/CGI-Executables/maccs/tmp/';
$::cgiTmpURL = '/Library/WebServer/CGI-Executables/maccs/tmp/';
$::htmlTmpDir = '/Library/WebServer/Documents/maccs/tmp/';
$::htmlTmpURL = '/maccs/tmp/';
$::htmlDataDir = '/Library/WebServer/Documents/maccs/Webdata/';
$::htmlDataURL = '/maccs/Webdata/';
# $::idlExecutable = 'C:\\RSI\\idl50sv\\idlde.exe';
# $::cgiTmpDir = 'C:\\WWW\\cgi-bin\\maccs\\tmp\\';
# $::cgiTmpURL = '/cgi-bin/maccs/tmp/';
# $::htmlTmpDir = 'C:\\WWW\\htdocs\\maccs\\tmp\\';
# $::htmlTmpURL = '/htdocs/tmp/';
# $::htmlDataDir = 'C:\\WWW\\htdocs\\space\\webdata\\';
# $::htmlDataURL = '/space/webdata/';
# cgi parameter names passed upon html form submission
$::stationParam = 'stn';
$::beginParam = 'beg';
$::endParam = 'end';
$::outputTypeParam = 'output';
$::componentParam = 'comp';
# $::binParam = 'bin';
# $::iagaParam = 'iaga';
# $::pngParam = 'png';
# $::psParam = 'ps';
# station names
@::stationNames = (
'Cape Dorset',
'Coral Harbour',
'Clyde River',
'Gjoa Haven',
'Igloolik',
'Nain',
'Pelly Bay',
'Pangnirtung',
'Repulse Bay');
$::binName = 'Binary Data';
$::iagaName = 'IAGA ASCII Data';
$::pngName = 'PNG Plot';
$::psName = 'PostScript Plot';
$::bxName = 'Bx';
$::byName = 'By';
$::bzName = 'Bz';
# @::yearNames = qw( 1992 1993 1994 1995 1996 1997 1998 Other );
# @::monthNames = qw( January February March April May June July August
# September October November December );
# @::dayNames = qw( 1 2 3 4 5 6 7
# 8 9 10 11 12 13 14
# 15 16 17 18 19 20 21
# 22 23 24 25 26 27 28
# 29 30 31);
$::formName = 'dataRequestForm';
# JavaScript test
$::javaScript=<<END;
function selectAllStations(bSelect)
{
var frm = document.$::formName;
for (i=0;i<9; i++)
{
frm.elements[i].checked = bSelect;
}
}
END
}
sub PrintForm
{
print
&header,
&start_html({-title=>'MACCS Data Request Application',
-bgcolor=>'#ffffff',
-link=>"#000099",
-vlink=>"#336600",
-alink=>"#993300",
-script=>$::javaScript});
print <<END;
<center>
<table width="100%" border="0">
<tr>
<td height="50" bgcolor="#140766"><font size="+3" color="#FFFFFF" face="Arial, Helvetica, sans-serif">Data Request Application</font></td>
</tr>
</table>
</center>
<table bgcolor="#ffffff" border="0" align="center" cellpadding="10" width="768" height="73">
<tr>
<td> <font face="Arial, Helvetica, sans-serif">Use the following form to determine
data availability and download binary files; generate and download IAGA
2000 ASCII-formatted data files; and generate, view and download plots in
PNG or PostScript format. For more information, see the <a href="
http://space.augsburg.edu/space/map.html">Data
Availability Page</a> and the <a
href="
http://space.augsburg.edu/space/read_data.html"> Read Me Page</a>.</font>
</tr>
</table>
END
print
&startform({-name=>$::formName, -method=>'get'});
my $scrollLines = 12;
# TODO - figure out how to get hashes working in checkbox_group
my $stationCell = &h4('Station')
.&checkbox_group({-name=>$::stationParam,
-values=>\@::stationNames,
-linebreak=>'true'})
.&br
.&button({-name=>'selectAllStationsButton',
-value=>'Select All',
-onClick=>"selectAllStations(true)"})
.' '
.&button({-name=>'clearStationsButton',
-value=>'Clear',
-onClick=>"selectAllStations(false)"});
# .&scrolling_list({-name=>$::stationParam,
# -values =>\@::stationNames,
# -size=>$scrollLines}); # single selection
# -size=>$scrollLines,
# -multiple=>'true'}); # multiple selection
my $beginEndCell = &h4('Beginning and Ending Times')
.&h5('Beginning Time')
.&code('(YYYY/MM/DD HH)').&br
.&textfield({-name=>$::beginParam,
-default=>'1995/01/01 00',
-maxlength=>13}).&br.&br
.&h5('Ending Time')
.&code('(YYYY/MM/DD HH)').&br
.&textfield(-name=>$::endParam,
-default=>'1995/01/02 00',
-maxlength=>13);
# my $outputTypeCell = &h4('Output Type')
# .&font({-color=>'#ff0000', -size=>'-1'}, 'PNG and PostScript <br> are under construction.')
# .&radio_group({-name=>$::outputTypeParam,
my $outputTypeCell = &h4('Output Type')
.&radio_group({-name=>$::outputTypeParam,
-values=>[$::binName, $::iagaName, $::pngName, $::psName],
-default=>$::binName,
-columns=>1});
my $componentCell = &h4('Plot Component')
.&radio_group({-name=>$::componentParam,
-values=>[$::bxName, $::byName, $::bzName],
-default=>$::bxName,
-columns=>1})
.&font({-size=>'-1'}, '<br> Bx = geographic north <br> By = geographic east <br> Bz = vertical down <br>');
# print
# &table({-bgcolor=>'#ffffff', -border=>'1', -width=>600, -align=>'center', -cellpadding=>10},
# &TR({-align=>'left', -valign=>'top'},
# [&th({-align=>'left', -valign=>'top'}, [$stationCell, $beginEndCell, $outputTypeCell])]) );
print
&table({-bgcolor=>'#ffffff',
-border=>'1',
-width=>768,
-align=>'center',
-cellpadding=>10},
&TR({-align=>'left', -valign=>'top'},
[&th({-align=>'left', -valign=>'top'},
[$stationCell, $beginEndCell, $outputTypeCell, $componentCell])]) );
print
&br,
'<CENTER>',
&submit({-name=>'Submit'}),
' ',
&reset({-name=>'Reset'}),
# ' ',
# &button({-name=>'jsbutton',
# -value=>'JavaScript Button',
# -onClick=>"showAlert()"}),
'</CENTER>',
&endform();
print <<END;
<ul>
<li>
<div align="left"> <font face="Arial, Helvetica, sans-serif"><u><a href="
http://space.augsburg.edu/space/dataavail.htm">Data
availability</a></u> list of days available from each station</font></div>
</li>
</ul>
END
print
&end_html;
}
sub ProcessForm
{
# get CGI form data parameters
my @stations = ¶m($::stationParam);
my $beginTime = ¶m($::beginParam);
my $endTime = ¶m($::endParam);
my $outputType = ¶m($::outputTypeParam);
my $binData = $outputType eq $::binName;
my $iagaData = $outputType eq $::iagaName;
my $pngPlot = $outputType eq $::pngName;
my $psPlot = $outputType eq $::psName;
my $component = ¶m($::componentParam);
# &::PrintDiagnosticMessage("stations: @stations, beginTime: $beginTime, endTime: $endTime, "
# ."binData: $binData, iagaData: $iagaData, "
# ."pngPlot: $pngPlot, psPlot: $psPlot");
# boolean values to determine if form is complete
my $hasStation = 1;
my $hasBeginTime = 1;
my $hasEndTime = 1;
my $hasOutput = 1;
if (!$stations[0]) {
$hasStation = 0;
}
if (!$beginTime) {
$hasBeginTime = 0;
}
if (!$endTime) {
$hasEndTime = 0;
}
if (!$binData && !$iagaData && !$pngPlot && !$psPlot) {
$hasOutput = 0;
}
# &::PrintDiagnosticMessage("hasBeginTime: $hasBeginTime");
# inform user if form is incomplete
if (!$hasStation || !$hasBeginTime || !$hasEndTime || !$hasOutput) {
&::PrintErrorMessage('Please return to the form and make sure station, '
.'beginning time, ending time, and output type have been specified.');
}
my $beginYear;
my $beginMonth;
my $beginDay;
my $beginHour;
my $beginMinute;
my $beginSecond;
my $endYear;
my $endMonth;
my $endDay;
my $endHour;
my $endMinute;
my $endSecond;
# use pattern matching to find the year, month, etc. allowing for user input errors
# '\d' is any numeric character; '\D' is any non-numeric character; '+' is one or more
# ($beginYear, $beginMonth, $beginDay, $beginHour, $beginMinute, $beginSecond)
# = ($beginTime =~ /(\d+)\D+(\d+)\D+(\d+)\D+(\d+)\D+(\d+)\D+(\d+)/);
# ($endYear, $endMonth, $endDay, $endHour, $endMinute, $endSecond)
# = ($endTime =~ /(\d+)\D+(\d+)\D+(\d+)\D+(\d+)\D+(\d+)\D+(\d+)/);
# my $isValidBeginTime = &::IsValidDateTime($beginSecond, $beginMinute, $beginHour,
# $beginDay, $beginMonth, $beginYear);
# my $isValidEndTime = &::IsValidDateTime($endSecond, $endMinute, $endHour,
# $endDay, $endMonth, $endYear);
($beginYear, $beginMonth, $beginDay, $beginHour)
= ($beginTime =~ /(\d+)\D+(\d+)\D+(\d+)\D+(\d+)/);
($endYear, $endMonth, $endDay, $endHour)
= ($endTime =~ /(\d+)\D+(\d+)\D+(\d+)\D+(\d+)/);
# &::PrintDiagnosticMessage("Begin YMDH: $beginYear, $beginMonth, $beginDay, $beginHour, "
# ."End YMDH: $endYear, $endMonth, $endDay, $endHour");
my $isValidBeginTime = &::IsValidDateTime($beginHour, $beginDay, $beginMonth, $beginYear);
my $isValidEndTime = &::IsValidDateTime($endHour, $endDay, $endMonth, $endYear);
if (!$isValidBeginTime || !$isValidEndTime) {
&::PrintErrorMessage('Please return to the form and make sure '
.'the entered dates and times are valid.');
}
# prepare to create the IDL batch file...
# specify filename bases (prefixes) and extensions (suffixes)
my $batchBase = 'idl';
my $batchExt = '.pro';
my $outBase = 'idl';
my $outExt = '.out';
my $htmlBase = ;
my $htmlExt = '.html';
# get time for making filename time stamp
my ($sec, $min, $hour, $mday, $foo) = localtime;
my $timestamp = sprintf("%02d%02d%02d", $hour, $min, $sec);
# remove the following comment if we need day number as well...
# my $timestamp = sprintf("%02d%02d%02d%02d", $mday, $hour, $min, $sec);
# test the version letter algorithm by setting timestamp to nearest minute
# my $timestamp = sprintf("%02d%02d", $hour, $min);
# chances are this time stamp will give us a unique filename, but just in case
# more than one IDL batch file is created in a given second, we append a
# version letter ('a' to 'z') to our time stamp, incrementing it if the file
# already exists...
my $fileID = "";
my $file = "";
my $version = "";
# my $count = 0;
my $count = ord('a');
do {
# $version = sprintf("%02d", $count);
$version = chr($count);
$fileID = $timestamp.$version;
# use batch file for existence (-e $file) test below
$file = $::cgiTmpDir.$batchBase.$fileID.$batchExt;
$count++;
if ($count > (ord('z') + 1)) {
&::PrintErrorMessage('Please re-submit the form.');
}
} while (-e $file);
my $batchFile = $::cgiTmpDir.$batchBase.$fileID.$batchExt;
my $outFile = $::cgiTmpDir.$outBase.$fileID.$outExt;
# open batch file; inform user if batch file couldn't be opened
# ">" means "open for output and truncate if necessary"
# system("whoami");
if (!open(IDLBATCHFILE, "> $batchFile")) {
# &::PrintErrorMessage("Unable to create IDL batch file.");
&::PrintErrorMessage("Unable to create $batchFile.");
}
# my @beginDateTime = ($beginYear, $beginMonth, $beginDay,
# $beginHour, $beginMinute, $beginSecond);
# my @endDateTime = ($endYear, $endMonth, $endDay,
# $endHour, $endMinute, $endSecond);
my @beginDateTime = ($beginYear, $beginMonth, $beginDay, $beginHour);
my @endDateTime = ($endYear, $endMonth, $endDay, $endHour);
my @outputTypes;
if ($binData) {
@outputTypes = (@outputTypes, 'bin');
}
if ($iagaData) {
@outputTypes = (@outputTypes, 'iaga');
}
if ($pngPlot) {
@outputTypes = (@outputTypes, 'png');
}
if ($psPlot) {
@outputTypes = (@outputTypes, 'ps');
}
my $htmlFilePath = $::htmlTmpDir.$htmlBase.$fileID.$htmlExt;
# IDL student version under Windows doesn't allow creation of .sav files
# Our workaround is to just compile datareq.pro every time—which is actually
# convenient for testing. This might impact performance, but it might not.
# print IDLBATCHFILE "restore, 'datareq.sav'\n";
print IDLBATCHFILE ".compile datareq.pro\n";
# separator for interpolation-printed arrays (e.g., "@foobar")
$LIST_SEPARATOR = ', ';
print IDLBATCHFILE "doDataRequest, ";
print IDLBATCHFILE "\'@outputTypes\', ";
print IDLBATCHFILE "\'$component\', ";
print IDLBATCHFILE "\'@beginDateTime\', ";
print IDLBATCHFILE "\'@endDateTime\', ";
print IDLBATCHFILE "\'@stations\', ";
print IDLBATCHFILE "\'$::htmlDataDir\', ";
print IDLBATCHFILE "\'$::htmlDataURL\', ";
print IDLBATCHFILE "\'$::htmlTmpDir\', ";
print IDLBATCHFILE "\'$::htmlTmpURL\', ";
print IDLBATCHFILE "\'$fileID\', ";
print IDLBATCHFILE "\'$htmlFilePath\'";
print IDLBATCHFILE "\n";
print IDLBATCHFILE "exit";
print IDLBATCHFILE "\n";
close(IDLBATCHFILE);
# execute idl procedure; ">" means "redirect standard output to file"
# "2>&1" means "redirect standard error to file also"
# redirecting 'standard out' and 'standard error' doesn't seem to work since upgrading
# to IDL 5.4; this could be due to changes in IDL or the license manager or something else
# system("$::idlExecutable $batchFile > $outFile 2>&1");
# it would be nice to have the errors issued somewhere so we can debug; at
# this point, we'll jst let it all hang out...
# system("$::idlExecutable $batchFile > /dev/null 2>&1");
system("$::idlExecutable $batchFile > output.txt");
# my $process = "$::idlExecutable";
# my $command = " "."\@"."$batchFile";
# my $pid = 0;
# &Win32::Spawn($process, $command, $pid);
# HTML output
print &redirect(-location=>$::htmlTmpURL.$htmlBase.$fileID.$htmlExt);
# keep size of temporary file directories at the specified size in megs:
&PruneDirectory("$::htmlTmpDir", 200);
&PruneDirectory("$::cgiTmpDir", 10);
}
# returns false if any of the values in the specified date is not in the allowed
# range; parameter order follows that of localtime(), timelocal(), etc.
sub IsValidDateTime
{
# my $second = $_[0];
# my $minute = $_[1];
# my $hour = $_[2];
# my $day = $_[3];
# my $month = $_[4];
# my $year = $_[5];
my $hour = $_[0];
my $day = $_[1];
my $month = $_[2];
my $year = $_[3];
# if ($second < 0 || $second > 59) {
# return 0;
# }
# if ($minute < 0 || $minute > 59) {
# return 0;
# }
if ($hour < 0 || $hour > 23) {
return 0;
}
if ($day < 1 || $day > 31) {
return 0;
}
if ($month < 1 || $month > 12) {
return 0;
}
if ($year < 1970 || $year > 2050) {
return 0;
}
return 1;
}
sub PrintDiagnosticMessage
{
my $theMessage = $_[0];
# prevent the CGI.pm functions called herein from losing their minds; they seem
# to rely on the @_ array ($_[0], $_[1], etc.; maybe there's a better way to do
# this?
local @_;
print &header;
print &start_html(-title=>'Diagnostic', -bgcolor=>'#ffffcc');
print &h3('Diagnostic');
print &h5("$theMessage");
print &end_html;
# print '<HTML>';
# print '<HEAD>';
# print ' <TITLE>Test</TITLE>';
# print '</HEAD>';
# print '<BODY bgcolor=white link="#000080">';
# print '</BODY>';
# print '</HTML>';
exit(0);
}
sub PrintErrorMessage
{
my $theMessage = $_[0];
# prevent the CGI.pm functions called herein from losing their minds; they seem
# to rely on the @_ array ($_[0], $_[1], etc.; maybe there's a better way to do
# this?
local @_;
print &header;
print &start_html(-title=>'Error', -bgcolor=>'#ffffff');
print &h3('Error');
print &h5("$theMessage");
print &end_html;
# print '<HTML>';
# print '<HEAD>';
# print ' <TITLE>Test</TITLE>';
# print '</HEAD>';
# print '<BODY bgcolor=white link="#000080">';
# print '</BODY>';
# print '</HTML>';
exit(0);
}
# removes files from the specified directory making the total size of the
# directory equal to or less than the specified size in megabytes
sub PruneDirectory
{
my $dirName = $_[0];
my $maxMegabytes = $_[1];
my $maxBytes = $maxMegabytes * 1048576;
my $fileName;
my $filePath;
my @status;
my $accessTime;
my $bytes;
my $oldestTime;
my $totalBytes;
do {
# determine access time of oldest file
opendir(DIR, $dirName) or die "Unable to open $dirName. ($!)\n";
$oldestTime = time();
$totalBytes = 0;
while (defined($fileName = readdir(DIR))) {
next if $fileName =~ /^\.\.?$/; # skip '.' and '..'
$filePath = $dirName.$fileName;
@status = stat($filePath);
$accessTime = $status[8];
$bytes = $status[7];
$totalBytes += $bytes;
if ($accessTime < $oldestTime) {
$oldestTime = $accessTime;
}
}
closedir DIR;
if ($totalBytes > $maxBytes) {
# my $theLocalTime = localtime($oldestTime);
# print "OLDEST FILE: $theLocalTime | TOTAL BYTES: $totalBytes bytes\n";
# repeat; delete files with access time equal to that of oldest file
opendir(DIR, $dirName) or die "Unable to open $dirName. ($!)\n";
$totalBytes = 0;
while (defined($fileName = readdir(DIR))) {
next if $fileName =~ /^\.\.?$/; # skip '.' and '..'
$filePath = $dirName.'/'.$fileName;
@status = stat($filePath);
$accessTime = $status[8];
$bytes = $status[7];
$totalBytes += $bytes;
if ($accessTime <= $oldestTime) {
# my $theAccessTime = localtime($accessTime);
# print " DELETING '$filePath'\n $theAccessTime\n";
unlink($filePath) or die "Unable to delete $filePath. ($!)\n";
}
}
}
closedir DIR;
# ++$oldestTime;
} while ($totalBytes > $maxBytes)
}

