#!/usr/bin/perl
# before anything else, the script needs to find out its own name
#
# some servers (notably IIS on windows) don't set the cwd to the script's
# directory before executing it. So we get that information
# from $0 (the full name & path of the script).
BEGIN{($_=$0)=~s![\\/][^\\/]+$!!;push@INC,$_}
$name = $0;
$name =~ s/.+\/?.+\///; # for unix
$name =~ s/.+\\.+\\//; # for windows
$path = $0;
$path =~ s/(.+\/).+/$1/g; # for unix
$path =~ s/(.+\\).+/$1/g; # for windows
# The "use Cwd" method would be nice, but it doesn't work with
# some versions of IIS/ActivePerl
#use Cwd;
#$path = cwd;
if ($path ne "")
{
chdir $path;
push @INC,$path;
}
# finished discovering name
# some global variables (more further down)
local $plans_version = "6.6.3"; # version
local $debug_info;
local %options;
local $perl_version = (sprintf ("%vd",$^V));
local $options{data_storage_mode};
local $fatal_error = 0; # fatal errors cause plans to abort and print an error message to the browser
local $error_info = "";
local $html_output;
local $event_details_template;
local %calendars;
local %current_calendar;
local %latest_calendar;
local %latest_new_calendar;
local $max_cal_id = 0;
local $max_new_cal_id = 0;
local $max_series_id = 0;
local %events;
local %current_event;
local %latest_event;
local $max_event_id = 0;
local %text;
local $max_remote_event_id = 0;
local $options{default_template_path} = "";
local $theme_url = "";
local $options{choose_themes} = "";
local $graphics_url = "";
local $icons_url = "";
local $input_cal_id_valid = 0;
local $options{right_click_menus_enabled} = 0;
local %cal_options;
local $rightnow;
local @months;
local @months_abv;
local @day_names;
local $loaded_all_events; # flag used to avoid calling load_events("all") twice
# not needed for calendars (we always load all calendars)
# check for required modules.
my $module_found=0;
foreach $temp_path (@INC)
{
if (-e "$temp_path/plans_config.pl")
{$module_found=1;}
}
if ($module_found == 0)
{
$fatal_error=1;
$error_info .= "Unable to locate plans_config.pl! It should be in the same directory as plans.cgi!\n";
}
else {require "plans_config.pl";}
$module_found=0;
foreach $temp_path (@INC)
{
if (-e "$temp_path/CGI")
{$module_found=1;}
}
if ($module_found == 0)
{
$fatal_error=1;
$error_info .= "unable to locate required module CGI!\n";
}
else
{use CGI;}
$module_found=0;
foreach $temp_path (@INC)
{
if (-e "$temp_path/CGI/Carp.pm")
{$module_found=1;}
}
if ($module_found == 0)
{
$fatal_error=1;
$error_info .= "unable to locate required module CGI::Carp!\n";
}
else
{use CGI::Carp qw/fatalsToBrowser/;}
$module_found=0;
foreach $temp_path (@INC)
{
if (-e "$temp_path/Time")
{$module_found=1;}
}
if ($module_found == 0)
{
$fatal_error=1;
$error_info .= "unable to locate required module Time.pm!\n";
}
else
{use Time::Local;}
$module_found=0;
foreach $temp_path (@INC)
{
if (-e "$temp_path/IO.pm")
{$module_found=1;}
}
if ($module_found == 0)
{
$fatal_error=1;
$error_info .= "unable to locate required module IO.pm!\n";
}
else
{use IO::Socket;}
if ($fatal_error == 1) # print error and bail out
{
&fatal_error();
}
$module_found=0;
foreach $temp_path (@INC)
{
if (-r "$temp_path/plans_lib.pl")
{$module_found=1;}
}
if ($module_found == 0)
{
$fatal_error=1;
$error_info .= "Unable to locate plans_lib.pl! It should be in the same directory as plans.cgi!\n";
}
else {require "plans_lib.pl";}
# get the language file, if one is defined
if (defined $options{language_files})
{
my @language_files = split(',', $options{language_files});
foreach $language_file (@language_files)
{
$module_found=0;
foreach $temp_path (@INC)
{
if (-r "$temp_path/$language_file")
{$module_found=1;}
}
if ($module_found == 0)
{
$fatal_error=1;
$error_info .= "Unable to locate language file $language_file! It should be in the same directory as plans.cgi!\n";
}
else {require $language_file;}
}
}
else
{
$fatal_error=1;
$error_info .= "No language files defined in plans.config!\n";
}
# check for perl version
my $temp = substr($perl_version,0,3);
if ($temp < 5.6) {
$fatal_error=1;
$error_info .= "Your version of perl ($perl_version) is too old! Plans requires perl version 5.6 or better.\n";
}
if ($fatal_error == 1) # print error and bail out
{
&fatal_error();
}
# init cgi stuff
$q = new CGI;
$script_url = $q->url(-path_info>=1);
$script_url =~ /(.*)\//; # remove trailing / and all text after
$script_url = $1; # remove trailing / and all text after
# check if data files or tables are present
&check_data();
# fatal error? Print error and bail out
if ($fatal_error == 1)
{&fatal_error();}
if ($options{choose_themes})
{
$theme_url=$q->param('theme_url');
$theme_url=$theme_url;
}
if ($theme_url eq "")
{$theme_url = "$script_url/theme";}
$graphics_url ="$theme_url/graphics"; # where misc. graphics are
$icons_url = "$theme_url/icons"; # where icons are
$css_path = "$theme_url/plans.css"; # css file
# globals for http parameters
my $active_tab = $q->param('active_tab');
my $add_edit_cal_action = $q->param('add_edit_cal_action');
my $add_edit_event = $q->param('add_edit_event');
local $current_event_id = $q->param('evt_id');
local $cal_num_months = $q->param('cal_num_months');
local $cal_start_month = $q->param('cal_start_month');
local $cal_start_year = $q->param('cal_start_year');
my $special_action = $q->param('special_action');
local $display_type = $q->param('display_type');
$display_type = 0 if ($display_type eq "");
# other globals
my $event_start_date;
my $event_start_timestamp;
my $event_days;
my $start_mday;
my $start_mon;
my $start_year;
my @timestamp_array;
my $recur_end_timestamp;
# load calendar data
&load_calendars();
local $current_cal_id = $q->param('cal_id');
# if calendar id not supplied, but evt_id is supplied (like when viewing an event) use that event's calendar as the current calendar
#if ($current_event_id ne "")
#{
# &load_event($current_event_id);
#
# my %temp_current_event = %{$events{$current_event_id}};
# if ($current_cal_id eq "")
# {
# $current_cal_id = $temp_current_event{cal_ids}[0];
# }
#}
foreach $cal_id (keys %calendars)
{
if ($cal_id eq $current_cal_id)
{$input_cal_id_valid = 1;}
}
if ($current_cal_id eq "")
{$input_cal_id_valid = 0;}
if ($current_cal_id =~ /\D/)
{$input_cal_id_valid = 0;}
$current_cal_id = 0 if ($current_event_id eq "" && !$input_cal_id_valid);
# make all calendars selectable by default
foreach $cal_id (keys %calendars)
{$default_cal{selectable_calendars}{$cal_id} = 1;}
%current_calendar = %{$calendars{$current_cal_id}};
# time-related globals
$rightnow = time() + 3600 * $current_calendar{gmtime_diff};
@rightnow_array = gmtime $rightnow;
$rightnow_year = $rightnow_array[5]+1900;
$rightnow_month = $rightnow_array[4];
$rightnow_mday = $rightnow_array[3];
$next_year = $rightnow_year+1;
$rightnow_description = formatted_time($rightnow, "hh:mm:ss mn md yy");
@weekday_sequence = @day_names;
# custom stylesheet?
if ($current_calendar{custom_stylesheet} ne "")
{
$css_path = "http://$current_calendar{custom_stylesheet}";
}
# if this is a custom calendar request, shoehorn the request parameters in
if ($q->param('custom_calendar') == 1)
{
$current_cal_id = $q->param('custom_calendar_calendar');
@custom_calendar_backgound_calendars = $q->param('custom_calendar_background_calendars');
foreach $local_background_calendar (keys %{$calendars{$current_cal_id}{local_background_calendars}})
{delete $calendars{$current_cal_id}{local_background_calendars}{$local_background_calendar};}
foreach $local_background_calendar (@custom_calendar_backgound_calendars)
{$calendars{$current_cal_id}{local_background_calendars}{$local_background_calendar} = 1;}
%current_calendar = %{$calendars{$current_cal_id}};
}
# make sure we can select the current calendar
#$current_calendar{selectable_calendars}{$current_cal_id} = 1;
# set info window height & width
$current_calendar{info_window_size} ="400x400" if ($current_calendar{info_window_size} eq ""); # default
my ($info_window_width, $info_window_height) = split("x", $current_calendar{info_window_size});
# rotate weekday_sequence by the offset defined in the week start day.
for ($l1=0;$l1 < $current_calendar{week_start_day};$l1++)
{push @weekday_sequence, (shift @weekday_sequence);}
# load background_colors
my @temp_lines = split ("\n", $event_background_colors);
foreach $temp_line (@temp_lines)
{
if ($temp_line !~ /\w/) # skip any blank lines
{next;}
$temp_line =~ s/^\s+//;
my ($hex_color, $hex_color_title) = split (/,*\s+/, $temp_line, 2);
if ($hex_color_title eq "")
{$hex_color_title = " ";}
push @event_bgcolors, {color => $hex_color, title => $hex_color_title};
}
my $template_html;
#load template
my $custom_template_file_found=1;
my $local_template_file = 0; # tells whether the template was loaded via a filesystem open or through a http request.
if ($current_calendar{custom_template} ne "") # custom template
{
$template_html = &get_remote_file("$current_calendar{custom_template}");
if ($template_html !~ /###/)
{
$custom_template_file_found=0;
$lang{custom_template_fail} =~ s/###template###/$current_calendar{custom_template}/;
$debug_info .= "$lang{custom_template_fail}\n";
}
}
if ($current_calendar{custom_template} eq "" || $custom_template_file_found ==0)
{
if (!(-e "$options{default_template_path}"))
{
$fatal_error=1;
$lang{default_template_fail} =~ s/###template###/$options{default_template_path}/;
$error_info .= "$lang{default_template_fail}\n";
&fatal_error();
}
else
{
open (FH, "$options{default_template_path}") || ($debug_info .=" Unable to open default template file $options{default_template_path} for reading ");
flock FH,2;
@template_lines=;
close FH;
$template_html = join "", @template_lines;
$local_template_file = 1;
}
}
# separate the main calendar template and the event details template
$event_details_template = $template_html;
$list_item_template = $template_html;
$template_html =~ s/<\/html>.+/<\/html>/s;
$template_html =~ s/.+<\/event_details>//s;
$template_html =~ s/.+<\/event_list_item>//s;
if ($event_details_template =~ // && $event_details_template =~ /<\/event_details>/)
{
$event_details_template =~ s/.*//s;
$event_details_template =~ s/<\/event_details>.+//s;
}
else
{
$debug_info .= "Warning! No event details template found. (The template file doesn't contain <event_details>...</event_details>\n";
$event_details_template = "";
}
if ($list_item_template =~ // && $list_item_template =~ /<\/event_list_item>/)
{
$list_item_template =~ s/.*//s;
$list_item_template =~ s/<\/event_list_item>.+//s;
}
else
{
$debug_info .= "Warning! No event event list item template found. (The template file doesn't contain <event_list_item>...</event_list_item>\n";
$event_details_template = "";
}
# ssi-style includes in the template
if ($local_template_file)
{
my $new_html = $template_html;
$template_html =~ s/###include\s+(.+)###/&load_file($1)/ge;
#while ($new_html =~ s/###include\s+(.+)###//g)
if(0)
{
my $include_file=$1;
if (-e $include_file)
{
open (FH, "$include_file") || ($debug_info .=" unable to open include file $include_file for reading ");
flock FH,2;
my @include_lines=;
close FH;
$include_html = join "", @include_lines;
}
$template_html =~ s/###include\s+(.+)###/$include_html/;
}
}
sub load_file()
{
my ($file)=@_;
if (-e $file)
{
open (FH, "$file") || (return "unable to open include file $file for reading");
flock FH,2;
my @lines=;
close FH;
$text = join "", @lines;
return $text;
}
else
{
return "file $file does not exist";
}
}
if($options{choose_themes})
{
my $theme_file="choose_theme.html";
my $theme_html="";
if (-e $theme_file)
{
open (FH, "$theme_file") || ($debug_info .=" unable to open theme file $theme_file for reading ");
flock FH,2;
my @theme_lines=;
close FH;
$theme_html = join "", @theme_lines;
}
$template_html =~ s/###choose theme###/$theme_html/;
}
else
{
$template_html =~ s/###choose theme###//;
}
#evaluate browser type and version
$_ = $ENV{HTTP_USER_AGENT};
if (/Mozilla/) {
if (/Opera.([0-9\.]+)/) { $browser_type = 'Opera'; $browser_version=$1;}
elsif (/MSIE.([0-9.]+)/) { $browser_type = 'IE'; $browser_version = $1;}
elsif (/Mozilla\/([0-9\.]+)/) {$browser_type = 'Mozilla'; $browser_version=$1;
if (($browser_version<5) || (/Netscape/)) {$browser_type = "Netscape";} }
if (/\)[^0-9.]+[0-9]*[\/\ ]([0-9.]+)/) {$browser_version=$1;}
} elsif (/(\w+)\/([0-9\.]+)/) {$browser_type = $1; $browser_version = $2}
#evaluate, transform, tweak, adjust, modify input values
#$debug_info .= "browser type: $browser_type ";
#if the input is greater than the number of tabs, things look goofy
if ($q->param('active_tab') >= scalar @tab_text || $q->param('active_tab') eq "")
{
$active_tab = 0;
}
#if no month is selected, use the current month
if ($cal_start_month eq "")
{
$cal_start_month = $rightnow_month;
#$cal_start_month = 2;
}
#if the input year is out of range use the current year
if (($cal_start_year+0) < 1902 || ($cal_start_year+0)> 2037)
{
$cal_start_year = $rightnow_year;
}
if ($cal_num_months eq "" || $cal_num_months > $current_calendar{max_number_of_months})
{
$cal_num_months = "$current_calendar{default_number_of_months}";
}
if ($cal_num_months eq "" || $cal_num_months > $current_calendar{max_number_of_months})
{
$cal_num_months = 1;
}
#calculate calendar end month and year
$cal_end_month = $cal_start_month;
$cal_end_year = $cal_start_year;
for ($l1=1;$l1<$cal_num_months;$l1++)
{
$cal_end_month++;
if ($cal_end_month == 12)
{
$cal_end_month=0;
$cal_end_year++;
}
}
#check to make sure num_months+cal_start_date doesn't go out of bounds
if ($cal_end_year < 1902 || $cal_end_year> 2037)
{
$cal_end_year = $cal_start_year;
$cal_end_month = $cal_start_month;
$cal_num_months = 1;
}
# time window for loading events
my $cal_start_timestamp = timegm(0,0,0,1,$cal_start_month,$cal_start_year) - 2592000;
my $cal_end_timestamp = timegm(0,0,0,1,$cal_end_month,$cal_end_year) + 5184000;
if ($q->param('cal_start_timestamp') ne "" && $q->param('cal_start_timestamp') !~ /\D/)
{$cal_start_timestamp = $q->param('cal_start_timestamp');}
if ($q->param('cal_end_timestamp') ne "" && $q->param('cal_end_timestamp') !~ /\D/)
{$cal_end_timestamp = $q->param('cal_end_timestamp');}
#$debug_info .="start: $cal_start_timestamp\nend: $cal_end_timestamp\nrightnow: $rightnow\n";
# load event data, for main calendar and its background calendars
my @temp_calendars = ($current_cal_id);
foreach $local_background_calendar (keys %{$current_calendar{local_background_calendars}})
{push @temp_calendars, $local_background_calendar;}
&load_events($cal_start_timestamp, $cal_end_timestamp, \@temp_calendars);
if ($current_event_id ne "")
{
&load_event($current_event_id);
%current_event = %{$events{$current_event_id}};
}
# load events from remote background calendars
if (scalar keys %{$current_calendar{remote_background_calendars}} > 0)
{
$remote_calendars_status="";
my $temp = scalar keys %{$current_calendar{remote_background_calendars}};
foreach $remote_calendar_id (keys %{$current_calendar{remote_background_calendars}})
{
# pull in remote calendar name
my $remote_calendar_url = $current_calendar{remote_background_calendars}{$remote_calendar_id}{url};
$remote_calendar_complete_url = $remote_calendar_url;
#$debug_info .= "remote calendar: $remote_calendar_complete_url\n";
$remote_calendar_complete_url .= "?remote_calendar_request=1&cal_id=$current_calendar{remote_background_calendars}{$remote_calendar_id}{remote_id}&cal_start_year=$cal_start_year&cal_start_month=$cal_start_month&num_months=$cal_num_months";
#$debug_info .= "remote calendar url: $remote_calendar_complete_url\n";
my $xml_results = &get_remote_file($remote_calendar_complete_url);
if ($xml_results =~ //)
{
$xml_results =~ s/</g;
$xml_results =~ s/>/>/g;
$debug_info .= "Error fetching remote calendar: $xml_results\n";
}
else
{
my %remote_calendars = %{&xml2hash($xml_results)};
my $remote_cal_title=$remote_calendars{'xml'}{calendar}{title};
my $temp=$xml_results;
$temp=~ s/>/>/g;
$temp=~ s/</g;
#$debug_info .= "xml results: $temp\n";
&load_remote_events($xml_results, $current_calendar{remote_background_calendars}{$remote_calendar_id});
}
}
}
# calculate previous X months range.
my $previous_cal_start_month = $cal_start_month - $cal_num_months;
my $previous_cal_start_year = $cal_start_year;
if ($previous_cal_start_month < 0)
{
$previous_cal_start_year = $cal_start_year - 1 - int(abs($cal_num_months - $cal_start_month) / 12);
$previous_cal_start_month = 12 - abs($previous_cal_start_month) % 12;
}
# for the case when num_months = 12 and start_month=0
if ($previous_cal_start_month == 12)
{
$previous_cal_start_month=0;
$previous_cal_start_year++;
}
# singular or plural?
if ($cal_num_months > 1)
{
$prev_string = $lang{previous_months};
$prev_string =~ s/###num###/$cal_num_months/;
}
else
{
$prev_string = $lang{previous_month};
}
# calculate next X months range.
my $next_cal_start_month = $cal_start_month + $cal_num_months;
my $next_cal_start_year = $cal_start_year;
if ($next_cal_start_month > 11)
{
$next_cal_start_year = $cal_start_year + int(abs($cal_num_months + $cal_start_month) / 12);
$next_cal_start_month = abs($cal_start_month + $cal_num_months) % 12;
}
# singular or plural?
if ($cal_num_months > 1)
{
$next_string = $lang{next_months};
$next_string =~ s/###num###/$cal_num_months/;
}
else
{
$next_string = $lang{next_month};
}
$consistent_parameter_string="";
&make_consistent ("cal_id");
&make_consistent ("cal_start_month");
&make_consistent ("cal_start_year");
&make_consistent ("cal_num_months");
&make_consistent ("display_type");
&make_consistent ("theme_url");
if ($q->param('diagnostic_mode') eq "1")
{
my $diagnostic_results = &diagnostic_info;
$html_output = <Diagnostic mode
p1
# this kludge sucks!
if ($browser_type eq "IE")
{$tab_vert_offset=4;}
else
{$tab_vert_offset=0;}
#lay out the actual menu tabs
for ($l1=0;$l1 $$menu_tab{html}
p1
$noinsert_text .=<$$menu_tab{html}
p1
}
$insert_text .=<
p1
chomp $insert_text;
if ($q->param('custom_calendar') == 1)
{
$html_output =~ s/###tab menu stuff###//g;
}
else
{
$html_output =~ s/###tab menu stuff###/$insert_text/g;
}
$insert_text ="";
#invisible html for context menu
$insert_text .=<
p1
#main box stuff
$insert_text .=<
p1
if ($q->param('custom_calendar') == 1)
{
$html_output =~ s/###calendar controls###//g;
}
else
{
$html_output =~ s/###calendar controls###/$cal_controls_text/g;
}
$insert_text .= &do_calendar_list_view();
#select event range
$cal_month_start_date = timegm(0,0,0,1,$cal_start_month,$cal_start_year);
@cal_month_start_date_array = gmtime $cal_month_start_date;
$events_start_timestamp = $cal_month_start_date - 604800; # +7 day margin
$events_end_timestamp = &find_end_of_month($cal_end_month, $cal_end_year) + 604800; # +7 day margin
#now that we have selected the appropriate events, we can
#generate the corresponding javascript and calendar view
#and insert/add it to the html output.
$common_javascript = &common_javascript();
$page_javascript = &calendar_view_javascript($events_start_timestamp, $events_end_timestamp);
#display browser-appropriate javascript
if ($browser_type eq "IE" || $browser_type eq "Opera")
{$browser_javascript = &IE_javascript();}
else
{$browser_javascript = &NS6_javascript();}
#replace javascript placeholders with actual html/javascript code
$html_output =~ s/###common javascript###/$common_javascript/;
$html_output =~ s/###page-specific javascript###/$page_javascript/;
$html_output =~ s/###browser-specific javascript###/$browser_javascript/;
my $temp1 .=<$prev_string
p1
my $temp2 .=<$next_string
p1
$html_output =~ s/###previous month link###/$temp1/g;
$html_output =~ s/###next month link###/$temp2/g;
}
elsif ($active_tab eq "1") #the second tab is the add/edit events view
{
$html_output =~ s/###calendar controls###//;
$insert_text .=<
p1
$insert_text .= &add_edit_events();
$insert_text .=<
p1
#generate javascript for add/edit events page
$common_javascript = &common_javascript();
$page_javascript = &add_edit_events_javascript();
#display browser-appropriate javascript
if ($browser_type eq "IE" || $browser_type eq "Opera")
{$browser_javascript = &IE_javascript();}
else
{$browser_javascript = &NS6_javascript();}
#replace javascript placeholders with actual html/javascript code
$html_output =~ s/###common javascript###/$common_javascript/;
$html_output =~ s/###page-specific javascript###/$page_javascript/;
$html_output =~ s/###browser-specific javascript###/$browser_javascript/;
# sneak in the color select javascript before all other javascript.
my $temp =<
p1
$html_output =~ s/(
p1
$html_output =~ s/(