#!/usr/local/bin/perl5

use GD;
use Sprite;

$webmaster = "Shishir Gundavaram (shishir\@bu\.edu)";

$cal = "/usr/bin/cal";
$database = "/home/shishir/calendar.db";
$delimiter = "::";

($current_month, $current_year) = (localtime(time))[4,5];
$current_month += 1;
$current_year += 1900;

$action_types = '^(add|delete|modify|search)$';
$delete_password = "CGI Super Source";

&check_database ();
&parse_query_and_form_data (*CALENDAR);

$action = $CALENDAR{'action'};
$month = $CALENDAR{'month'};
($temp_month, $temp_year) = split ("/", $month, 2);

if ( ($temp_month =~ /^\d{1,2}$/) && ($temp_year =~ /^\d{4}$/) ) {
    if ( ($temp_month >= 1) && ($temp_month <= 12) ) {
        $current_month = $temp_month;
        $current_year = $temp_year;
    }
}

@month_names =    ('January', 'Febraury', 'March', 'April', 'May', 'June',
                   'July', 'August', 'September', 'October', 'November', 
                   'December');

$weekday_names = "Sun,Mon,Tue,Wed,Thu,Fri,Sat";

$current_month_name = $month_names[$current_month - 1];
$current_month_year = join ("/", $current_month, $current_year);

if ($action eq "full") {
    &display_year_calendar ();

} elsif ($action eq "view") {
    $date = $CALENDAR{'date'};
    &display_all_appointments ($date);

} elsif ($action =~ /$action_types/) {
    $type = $CALENDAR{'type'};

    if ($type eq "form") {
        $dynamic_sub = "display_${action}_form";
        &$dynamic_sub ();

    } elsif ($type eq "execute") {
        $dynamic_sub = "${action}_appointment";
        &$dynamic_sub ();

    } else {
        &return_error (500, "Calendar Manager",
                    "An invalid query was passed!");
    }

} else {
    &display_month_calendar ();
}

exit (0);

#-------------------------------< End of Main >--------------------------------

sub check_database
{
        local ($exclusive_lock, $unlock, $header);

        $exclusive_lock = 2;
        $unlock = 8;

        if (! (-e $database) ) {
                if ( open (DATABASE, ">" . $database) ) {
                        flock (DATABASE, $exclusive_lock);

                        $header = join ($delimiter, "ID", "Month", "Day", 
                    "Year", "Keywords", "Description");

                        print DATABASE $header, "\n";

                        flock (DATABASE, $unlock);
                        close (DATABASE);
                } else {
                        &return_error (500, "Calendar Manager",
                                "Cannot create new calendar database.");
                }
        }
}

sub Sprite_error
{
    &return_error (500, "Calendar Manager",
          "Sprite Database Error. Check the server log file.");
}

sub open_database
{
    local (*INFO, $command, $rdb_query) = @_;
    local ($rdb, $status, $no_matches);

    $rdb = new Sprite ();
    $rdb->set_delimiter ("Read", $delimiter);
    $rdb->set_delimiter ("Write", $delimiter);

    if ($command eq "select") {
        @INFO = $rdb->sql ($rdb_query);
        $status = shift (@INFO);
        $no_matches = scalar (@INFO);
        $rdb->close ();

        if (!$status) {
            &Sprite_error ();
        } else {
            return ($no_matches);
        }
    } else {
        $rdb->sql ($rdb_query) || &Sprite_error ();
        $rdb->close ($database);
    }
}

sub print_header
{
    local ($title, $header) = @_;

    print "Content-type: text/html", "\n\n";
    print "<HTML>", "\n";
    print "<HEAD><TITLE>", $title, "</TITLE></HEAD>", "\n";
    print "<BODY>", "\n";
    
    $header = $title unless ($header);

    print "<H1>", $header, "</H1>", "\n";
    print "<HR>", "\n";
}

sub print_footer
{
    print "<HR>", "\n";
    print "<ADDRESS>", $webmaster, "</ADDRESS>", "\n";
    print "</BODY></HTML>", "\n";
}

sub print_location
{
    local ($location_URL);

    $location_URL = join ("", $ENV{'SCRIPT_NAME'},                 "?",
                  "browser=", $ENV{'HTTP_USER_AGENT'}, "&",
                  "month=", $current_month_year);

    print "Location: ", $location_URL, "\n\n";
}

#----------------------------< Full Year Calendar >----------------------------

sub display_year_calendar
{
    local (@full_year);

    @full_year = `$cal $current_year`;
    @full_year = @full_year[5..$#full_year-3];

    grep (s|(\w{3})|<B>$1</B>|g, @full_year);

    &print_header ("Calendar for $current_year");
    print "<PRE>", @full_year, "</PRE>", "\n";
    &print_footer ();
}

#-------------------------------< Search Action >------------------------------

sub display_search_form
{
    local ($search_URL);

    $search_URL = join ("", $ENV{'SCRIPT_NAME'}, "?", 
                  "action=search",   "&",
                  "type=execute",    "&", 
                  "month=", $current_month_year);

    &print_header ("Calendar Search");

    print <<End_of_Search_Form;

This form allows you to search the calendar database for certain
information. The Keywords and Description fields are searched for the
string you enter.
<P>
<FORM ACTION="$search_URL" METHOD="POST">
Enter the string you would like to search for: 
<P>
<INPUT TYPE="text" NAME="search_string" SIZE=40 MAXLENGTH=40>
<P>
Please enter the <B>numerical</B> month and the year in which to search.
Leaving these fields empty will default to the current month and year:
<P>
<PRE>
Month: <INPUT TYPE="text" NAME="search_month" SIZE=4 MAXLENGTH=4><BR>
Year:  <INPUT TYPE="text" NAME="search_year"  SIZE=4 MAXLENGTH=4>
</PRE>
<P>
<INPUT TYPE="submit" VALUE="Search the Calendar!">
<INPUT TYPE="reset"  VALUE="Clear the form">
</FORM>

End_of_Search_Form

    &print_footer ();
}

sub search_appointment
{
    local ($search_string, $search_month, $search_year, @RESULTS,
           $matches, $loop, $day, $month, $year, $keywords, 
           $description, $search_URL, $month_name);

    $search_string = $CALENDAR{'search_string'};
    $search_month = $CALENDAR{'search_month'};
    $search_year = $CALENDAR{'search_year'};

    if ( ($search_month < 1) || ($search_month > 12) ) {
        $CALENDAR{'search_month'} = $search_month = $current_month;
    }

    if ($search_year !~ /^\d{2,4}$/) {
        $CALENDAR{'search_year'} = $search_year = $current_year;

    } elsif (length ($search_year) < 4) {
        $CALENDAR{'search_year'} = $search_year += 1900;
    }

    $search_string =~ s/(\W)/\\$1/g;

    $matches = &open_database (*RESULTS, "select", <<End_of_Select);

select Day, Month, Year, Keywords, Description from $database
where ( (Keywords    =~  /$search_string/i)      or
        (Description =~  /$search_string/i) )   and
        (Month       =   $search_month)         and
        (Year        =   $search_year)

End_of_Select

    unless ($matches) {
        &return_error (500, "Calendar Manager",
              "No appointments containing $search_string are found.");
    }

    &print_header ("Search Results for: $search_string");

    for ($loop=0; $loop < $matches; $loop++) {
            $RESULTS[$loop] =~ s/([^\w\s\0])/sprintf ("&#%d;", ord ($1))/ge;

        ($day, $month, $year, $keywords, $description) =
            split (/\0/, $RESULTS[$loop], 5);

        $search_URL = join ("", $ENV{'SCRIPT_NAME'}, "?", 
                    "action=view",       "&",
                    "date=", $day,       "&",
                    "month=", $month, "/", $year);

        $keywords = "No Keywords Specified!"   unless ($keywords);
        $description = "-- No Description --"  unless ($description);

        $description =~ s/&#60;BR&#62;/<BR>/g;
        $month_name = $month_names[$month - 1];

        print <<End_of_Appointment;

<A HREF="$search_URL">$month_name $day, $year</A><BR>
<B>$keywords</B><BR>
$description

End_of_Appointment

        print "<HR>" if ($loop < $matches - 1);

    }

    &print_footer ();
}

#---------------------------------< Add Action >-------------------------------

sub display_add_form
{
    local ($add_URL, $date, $message);

    $date = $CALENDAR{'date'};
    $message = join ("", "Adding Appointment for ",
             $current_month_name, " ", $date, ", ", $current_year);

    $add_URL = join ("", $ENV{'SCRIPT_NAME'},           "?",
                 "action=add",                  "&",
                 "type=execute",                "&", 
                 "month=", $current_month_year, "&",
                 "date=", $date);

    &print_header ("Add Appointment", $message);
              
    print <<End_of_Add_Form;

This form allows you to enter an appointment to be stored in the calendar
database. To make it easier for you to search for specific appointments
later on, please use descriptive words to describe an appointment.
<P>
<FORM ACTION="$add_URL" METHOD="POST">
Enter a brief message (keywords) describing the appointment:
<P>
<INPUT TYPE="text" NAME="add_keywords" SIZE=40 MAXLENGTH=40>
<P>
Enter some comments about the appointment:
<TEXTAREA ROWS=4 COLS=60 NAME="add_description"></TEXTAREA><P>
<P>
<INPUT TYPE="submit" VALUE="Add Appointment!">
<INPUT TYPE="reset"  VALUE="Clear Form">
</FORM>

End_of_Add_Form

    &print_footer();
}

sub add_appointment
{
    local ($time, $date, $keywords, $description);

    $time = time;
    $date = $CALENDAR{'date'};
    ($keywords = $CALENDAR{'add_keywords'}) =~ s/(['"])/\\$1/g;
    ($description = $CALENDAR{'add_description'}) =~ s/\n/<BR>/g;
        $description =~ s/(['"])/\\$1/g;

    &open_database (undef, "insert", <<End_of_Insert);

insert into $database
   (ID, Day, Month, Year, Keywords, Description)
values
   ($time, $date, $current_month, $current_year, '$keywords', '$description')

End_of_Insert

    &print_location ();
}

#-------------------------------< Delete Action >------------------------------

sub display_delete_form
{
    local ($delete_URL, $id);

    $id = $CALENDAR{'id'};
    $delete_URL = join ("", $ENV{'SCRIPT_NAME'}, "?",
                    "action=delete",     "&",
                    "type=execute",      "&",
                    "id=", $id,          "&",
                "month=", $current_month_year);

    &print_header ("Deleting appointment");

    print <<End_of_Delete_Form;

In order to delete calendar entries, you need to enter a valid
identification code (or password):
<HR>
<FORM ACTION="$delete_URL" METHOD="POST">
<INPUT TYPE="password" NAME="code" SIZE=40>
<P>
<INPUT TYPE="submit" VALUE="Delete Entry!">
<INPUT TYPE="reset"  VALUE="Clear the form">
</FORM>

End_of_Delete_Form

    &print_footer ();
}

sub delete_appointment
{
    local ($password, $id);

    $password = $CALENDAR{'code'};
    $id = $CALENDAR{'id'};

    if ($password ne $delete_password) {
        &return_error (500, "Calendar Manager",
            "The password you entered is not valid!");
    } else {
        &open_database (undef, "delete", <<End_of_Delete);

delete from $database
where (ID = $id)

End_of_Delete
    }

    &print_location ();
}

#---------------------------------< Modify Form >------------------------------

sub display_modify_form
{
    local ($id, $matches, @RESULTS, $keywords, $description, $modify_URL);

    $id = $CALENDAR{'id'};

    $matches = &open_database (*RESULTS, "select", <<End_of_Select);

select Keywords, Description from $database
where (ID = $id)

End_of_Select

    unless ($matches) {
        &return_error (500, "Calendar Manager",
          "Oops! The appointment that you selected no longer exists!");
    }

    ($keywords, $description) = split (/\0/, shift (@RESULTS), 2);
    $keywords = &escape_html ($keywords);
    $description =~ s/<BR>/\n/g;

    $modify_URL = join ("", $ENV{'SCRIPT_NAME'}, "?",
                "action=modify",     "&",
                    "type=execute",      "&", 
                "id=", $id,          "&",
                "month=", $current_month_year);

    &print_header ("Modify Form");

    print <<End_of_Modify_Form;

This form allows you to modify the <B>description</B> field for an
existing appointment in the calendar database.
<P>
<FORM ACTION="$modify_URL" METHOD="POST">
Enter a brief message (keywords) describing the appointment:
<P>
<INPUT TYPE="text" NAME="modify_keywords" SIZE=40 
       VALUE="$keywords" MAXLENGTH=40>
<P>
Enter some comments about the appointment:
<TEXTAREA ROWS=4 COLS=60 NAME="modify_description">
$description
</TEXTAREA><P>
<P>
<INPUT TYPE="submit" VALUE="Modify Appointment!">
<INPUT TYPE="reset"  VALUE="Clear Form">
</FORM>

End_of_Modify_Form

    &print_footer ();
}

sub escape_html
{
    local ($string) = @_;
    local (%html_chars, $html_string);

        %html_chars = ('&', '&amp;',
                       '>', '&gt;',
                       '<', '&lt;',
                       '"
