sub check_data() { if ($data_storage_mode == 0 ) # flat text files { if (!(-e "$calendars_file")) {$fatal_error=1;$error_info .= "Calendars file $calendars_file not found!\n";} if (!(-e $new_calendars_file)) {$fatal_error=1;$error_info .= "New calendars file $new_calendars_file not found!\n";} if (!(-e $events_file)) {$fatal_error=1;$error_info .= "Events file $events_file not found!\n";} if ($fatal_error == 0) { # Remember which files are writable. $writable{calendars_file} = (-w $calendars_file); $writable{new_calendars_file} = (-w $new_calendars_file); $writable{events_file} = (-w $events_file); $writable{email_reminders_datafile} = (-w $email_reminders_datafile); # If the events file is not writable then we shouldn't # show the Add/Edit events tab on the main page. delete($tab_text[1]) unless $writable{events_file}; } } elsif ($data_storage_mode == 1 ) # DBI { $writable{calendars_file} = 1; $writable{new_calendars_file} = 1; $writable{events_file} = 1; $writable{email_reminders_datafile} = 1; my $calendars_table_exists=1; my $new_calendars_table_exists=1; my $events_table_exists=1; # if successful, check whether the calendars table exists my $query_string="select * from $calendars_table limit 0"; my $sth = $dbh->prepare($query_string) || ($error_info .= "Can't prepare $statement: $dbh->errstr\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $calendars_table_exists=0; $error_info .= $dbh->errstr."\n"; } $sth->finish(); # check whether the new_calendars table exists my $query_string="select * from $new_calendars_table limit 0"; my $sth = $dbh->prepare($query_string) || ($error_info .= "Can't prepare $statement: $dbh->errstr\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $new_calendars_table_exists=0; $error_info .= $dbh->errstr."\n"; } $sth->finish(); # check whether the events table exists my $query_string="select * from $events_table limit 0"; my $sth = $dbh->prepare($query_string) || ($error_info .= "Can't prepare $statement: $dbh->errstr\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $events_table_exists=0; $error_info .= $dbh->errstr."\n"; } $sth->finish(); if ($events_table_exists + $new_calendars_table_exists + $calendars_table_exists == 3) { # everything's ok } elsif ($events_table_exists + $new_calendars_table_exists + $calendars_table_exists > 0) { $fatal_error = 1; $error_info .= "Ok, this is a serious problem. Some of the required tables exist, but not all.\n Plans can't fix this automatically.\n"; } elsif ($events_table_exists + $new_calendars_table_exists + $calendars_table_exists == 0) { if ($q->param('create_tables') ne "1") { $fatal_error = 1; if ((-e "$calendars_file") && (-e $new_calendars_file) && (-e $events_file)) { $error_info .= <Yes, please create them (but don't import anything) \nYes, please create them, and import all all existing data from $calendars_file, $new_calendars_file, and $events_file. p1 } else { $error_info .= <Yes, please create them p1 } } else # create the tables! { $error_info .= "\nCreating calendar and event tables...\n"; # create the calendars table my $query_string="create table $calendars_table(id int(5),xml_data text,update_timestamp int(15));"; my $sth = $dbh->prepare($query_string) || ($error_info .= "Can't prepare $statement: $dbh->errstr\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $fatal_error = 1; $error_info .= "error creating table \"$calendars_table\"!\n".$dbh->errstr."\n"; } $sth->finish(); # create the new calendars table my $query_string="create table $new_calendars_table(id int(5),xml_data text,update_timestamp int(15));"; my $sth = $dbh->prepare($query_string) || ($error_info .= "Can't prepare $statement: $dbh->errstr\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $fatal_error = 1; $error_info .= "error creating table \"$new_calendars_table\"!\n".$dbh->errstr."\n"; } $sth->finish(); # create the events table my $query_string="create table $events_table(id int(5),cal_ids text,start int(15),end int(15),xml_data text,update_timestamp int(15));"; my $sth = $dbh->prepare($query_string) || ($error_info .= "Can't prepare $statement: $dbh->errstr\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $fatal_error = 1; $error_info .= "error creating table \"$events_table\"!\n".$dbh->errstr."\n"; } $sth->finish(); # either import existing text data, or create a record for the primary calendar if ($q->param('import_data') ne "1" && $fatal_error != 1) # create primary calendar { $error_info .= "\nAdding primary calendar...\n"; $fatal_error = 0; # data for the primary calendar my %primary_cal = %default_cal; $primary_cal{id}=0; $primary_cal{title}="Main Calendar"; $primary_cal{password}=crypt("12345", substr("12345",0,2)); $primary_cal{details}=<prepare($query_string) or ($error_info .= "Can't prepare $query_string:\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $fatal_error = 1; $error_info .= "Error adding primary calendar!\n".$dbh->errstr."\n"; $error_info .= "$query_string\n"; } else { $fatal_error = 1; $error_info = < (you shouldn't ever see this message again. To prove it, refresh the page or click here.) p1 } $sth->finish(); } else # import data { $error_info .= "\nImporting data from flat files...\n"; my $temp = $data_storage_mode; $data_storage_mode = 0; &load_calendars(); &load_new_calendars(); &load_events("all"); $data_storage_mode = $temp; my @temp_cal_ids = keys %calendars; &add_calendars(\@temp_cal_ids); my @temp_new_cal_ids = keys %new_calendars; &add_new_calendars(\@temp_new_cal_ids); my @temp_event_ids = keys %events; &add_events(\@temp_event_ids); if ($dbh->errstr ne "") { $fatal_error = 1; $error_info .= "Error adding primary calendar!\n".$dbh->errstr."\n"; $error_info .= "$query_string\n"; } else { $fatal_error = 1; $error_info = < (you shouldn't ever see this message again. To prove it, refresh the page or click here.) p1 } } } } #$dbh->disconnect; } } sub load_calendars { my $max_update_timestamp=0; my $latest_cal_id=0; if ($data_storage_mode == 0 ) # flat text files { open (FH, "$calendars_file") || {$debug_info.= "unable to open file $calendars_file\n"}; flock FH,2; my @calendar_lines=; close FH; # For the calendars, we do "complete" xml parsing (no validation or DTD though) foreach $line (@calendar_lines) { if ($line !~ /\S/) {next;} # ignore blank lines #if ($line =~ /<\/?xml>/) {next;} # ignore and my %calendar = %{&xml2calendar($line)}; $calendars{$calendar{id}} = \%calendar; #the calendar with id 0 is assumed to be the master calendar. #its password can be used to approve/edit/delete any event #for any calendar if ($calendar{id} eq "0") {$master_password = $calendar{password};} if ($calendar{update_timestamp} > $max_update_timestamp) { $max_update_timestamp = $calendar{update_timestamp}; $latest_cal_id = $calendar{id}; } } %latest_calendar = %{$calendars{$latest_cal_id}}; } elsif ($data_storage_mode == 1 ) # SQL database { my $query_string="select * from $calendars_table;"; my $sth = $dbh->prepare($query_string) or ($error_info .= "Can't prepare $query_string:\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $debug_info .= "Error loading calendars!\n".$dbh->errstr."\n"; $debug_info .= "query string:\n$query_string\n"; } while(@row = $sth->fetchrow_array) { my $cal_id = $row[0]; my $line = $row[1]; my %calendar = %{&xml2calendar($line)}; $calendars{$calendar{id}} = \%calendar; #the calendar with id 0 is assumed to be the master calendar. #its password can be used to approve/edit/delete any event #for any calendar if ($calendar{id} eq "0") {$master_password = $calendar{password};} if ($calendar{update_timestamp} > $max_update_timestamp) { $max_update_timestamp = $calendar{update_timestamp}; $latest_cal_id = $calendar{id}; } } %latest_calendar = %{$calendars{$latest_cal_id}}; $sth->finish(); } } sub load_new_calendars() { my $latest_new_cal_id=0; my $max_update_timestamp=0; if ($data_storage_mode == 0 ) # flat text files { open (FH, "$new_calendars_file") || {$debug_info.= "unable to open file $new_calendars_file\n"}; flock FH,2; my @calendar_lines=; close FH; # For the calendars, we do "complete" xml parsing (no validation or DTD though) foreach $line (@calendar_lines) { if ($line !~ /\S/) {next;} # ignore blank lines my %calendar = %{&xml2calendar($line)}; $new_calendars{$calendar{id}} = \%calendar; #the calendar with id 0 is assumed to be the master calendar. #its password can be used to approve/edit/delete any event #for any calendar if ($calendar{update_timestamp} > $max_update_timestamp) { $max_update_timestamp = $calendar{update_timestamp}; $latest_new_cal_id = $calendar{id}; } } %latest_new_calendar = %{$new_calendars{$latest_new_cal_id}}; } elsif ($data_storage_mode == 1 ) # SQL database { my $query_string="select * from $new_calendars_table;"; my $sth = $dbh->prepare($query_string) or ($error_info .= "Can't prepare $query_string:\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $debug_info .= "Error loading new calendars!\n".$dbh->errstr."\n"; $debug_info .= "query string:\n$query_string\n"; } while(@row = $sth->fetchrow_array) { my $cal_id = $row[0]; my $line = $row[1]; my %calendar = %{&xml2calendar($line)}; $new_calendars{$calendar{id}} = \%calendar; #the calendar with id 0 is assumed to be the master calendar. #its password can be used to approve/edit/delete any event #for any calendar if ($calendar{update_timestamp} > $max_update_timestamp) { $max_update_timestamp = $calendar{update_timestamp}; $latest_new_cal_id = $calendar{id}; } } $sth->finish(); } %latest_new_calendar = %{$new_calendars{$latest_new_cal_id}}; } sub load_events() { # load events for a given number of calendars, within a given time range. my ($start, $end, $temp) = @_; my @calendar_ids = @{$temp}; #$debug_info .="start: $start\n"; #$debug_info .="end: $end\n"; if ($data_storage_mode == 0 ) # flat text files { open (FH, "$events_file") || {$debug_info.= "unable to open file $events_file\n"}; flock FH,2; my @event_lines=; close FH; my $max_update_timestamp = 0; my $latest_event_id = 0; my $event_loaded_count = 0; foreach $line (@event_lines) { my $temp_line = substr($line,0,120); # grab first 180 characters $temp_line =~ s///; # remove $temp_line =~ /(\d+)/; my $evt_id = $1; my $temp_cal_ids; if ($temp_line =~ /(\d+)/) { $temp_cal_ids = $1; } elsif ($temp_line =~ /(.+?)(\d+)/; my $temp_start_timestamp = $1; $temp_line =~ /(\d+)/; my $temp_end_timestamp = $1; my $cal_valid=0; foreach $cal_id (@calendar_ids) { if ($temp_cal_ids =~ /\b$cal_id\b/) {$cal_valid=1;} } if ($cal_valid == 0 && $start ne "all") {next;} # event on some other calendar that we don't care about if ($temp_end_timestamp < $start && $start ne "all") {next;} # in the past if ($temp_start_timestamp > $end && $start ne "all") {next;} # in the future $event_loaded_count++; my %event = %{&xml2event($line)}; $events{$event{id}} = \%event; if ($event{id} > $max_event_id) {$max_event_id = $event{id};} if ($event{series_id} > $max_series_id) {$max_series_id = $event{series_id};} if ($event{update_timestamp} > $max_update_timestamp) { $max_update_timestamp = $event{update_timestamp}; $latest_event_id = $event{id}; } } #$debug_info .= "$event_loaded_count events total.\n"; #$debug_info .= "loaded event $evt_id\n"; %latest_event = %{$events{$latest_event_id}}; } elsif ($data_storage_mode == 1 ) # SQL database { my $query_string; if ($start eq "all") { $query_string="select * from $events_table;"; $loaded_all_events = 1; } else { $query_string="select * from $events_table where (start > $start and end < $end )"; if ($calendar_ids[0] ne "" && $calendar_ids[0] !~ /\D/) { $query_string .= " and ( cal_ids='$calendar_ids[0]' or cal_ids like '$calendar_ids[0],' or cal_ids like '%,$calendar_ids[0]' or cal_ids like '%,$calendar_ids[0],%'"; for ($l1=1;$l1prepare($query_string) or ($error_info .= "Can't prepare $query_string:\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $debug_info .= "Error loading events!\n".$dbh->errstr."\n"; $debug_info .= "query string:\n$query_string\n"; } while(@row = $sth->fetchrow_array) { my $evt_id = $row[0]; my $temp_cal_id = $row[1]; #my $temp_start_timestamp = $row[2]; #$temp_line =~ /(\d+)/; #my $temp_end_timestamp = $row[3]; my $cal_valid=0; foreach $cal_id (@calendar_ids) { if ($temp_cal_id == $cal_id) {$cal_valid=1;} } my $line = $row[4]; my %event = %{&xml2event($line)}; $events{$event{id}} = \%event; if ($event{series_id} > $max_series_id) {$max_series_id = $event{series_id};} } # get max event id my $query_string="select max(id) from $events_table;"; my $sth = $dbh->prepare($query_string) or ($error_info .= "Can't prepare $query_string:\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $debug_info .= "$dbh->errstr\n"; $debug_info .= "query string:\n$query_string\n"; } $max_event_id = $sth->fetchrow_array; # get latest event my $query_string="select * from $events_table order by update_timestamp desc limit 0, 1;"; my $sth = $dbh->prepare($query_string) or ($error_info .= "Can't prepare $query_string:\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $debug_info .= "$dbh->errstr\n"; $debug_info .= "query string:\n$query_string\n"; } while(@row = $sth->fetchrow_array) { my $evt_id = $row[0]; my $temp_cal_id = $row[1]; my $line = $row[4]; $line =~ s/<\/?event>//g; # remove and %latest_event = %{&xml2event($line)}; $latest_event_id = $latest_event{id}; } } } sub load_event() { # load a single event. my ($event_id) = @_; if ($data_storage_mode == 0 ) # flat text files { open (FH, "$events_file") || {$debug_info.= "unable to open file $events_file\n"}; flock FH,2; my @event_lines=; close FH; my $max_update_timestamp = 0; my $latest_event_id = 0; my $event_loaded_count = 0; foreach $line (@event_lines) { my $temp_line = substr($line,0,120); # grab first 180 characters $temp_line =~ s///; # remove $temp_line =~ /(\d+)/; my $evt_id = $1; if ($evt_id != $event_id) {next;} # some other event that we don't care about my %event = %{&xml2event($line)}; $events{$event{id}} = \%event; if ($event{id} > $max_event_id) {$max_event_id = $event{id};} if ($event{update_timestamp} > $max_update_timestamp) { $max_update_timestamp = $event{update_timestamp}; $latest_event_id = $event{id}; } last; } } elsif ($data_storage_mode == 1 ) # SQL database { my $query_string; $query_string="select * from $events_table where (id = $event_id);"; my $sth = $dbh->prepare($query_string) or ($error_info .= "Can't prepare $query_string:\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $debug_info .= "Error loading events!\n".$dbh->errstr."\n"; $debug_info .= "query string:\n$query_string\n"; } while(@row = $sth->fetchrow_array) { $debug_info .= "(load_event) loaded event $row[0]\n"; my $evt_id = $row[0]; my $temp_cal_ids = $row[1]; my $line = $row[4]; my %event = %{&xml2event($line)}; $events{$event{id}} = \%event; } } } sub load_remote_events() { my ($remote_events_xml, $rcl) = @_; %remote_calendar_link=%{$rcl}; my @remote_calendars = &xml_quick_extract($remote_events_xml, "calendar"); foreach $temp (@remote_calendars) { my %remote_calendar = %{&xml2calendar($temp)}; #$debug_info .= "successfully fetched remote calendar: $remote_calendar{title}\n"; } my @remote_events = &xml_quick_extract($remote_events_xml, "event"); foreach $temp (@remote_events) { my %remote_event = %{&xml2event($temp)}; #$debug_info .= "successfully fetched remote event: $remote_event{title}\n"; my $new_remote_event_id = "r".($max_remote_event_id); $remote_event{remote_event_id} = $remote_event{id}; $remote_event{id} = $new_remote_event_id; $remote_event{remote_calendar}=\%remote_calendar_link; #$debug_info .= "remote url: $remote_event{remote_calendar}{url}\n"; #$debug_info .= "new remote id: $new_remote_event_id\n"; $events{$new_remote_event_id} = \%remote_event; $max_remote_event_id++; } #$debug_info .= "event r2: $events{r2}{title} ($events{r2}{start})\n"; #$debug_info .= "\n\n"; } sub get_events_in_series() { my ($series_id) = @_; my @series_ids=(); &load_events("all") unless $loaded_all_events; foreach $event_id (keys %events) { if ($events{$event_id}{series_id} eq $series_id) { push @series_ids, $event_id } } return @series_ids; } # add an event to the data file sub add_event() { my ($event_id) = @_; # temporary copy of the event in question my %temp_event = %{$events{$event_id}}; if ($data_storage_mode == 0 ) # flat text files { my $out_text=""; my $event_xml .= &event2xml($events{$event_id})."\n"; $event_xml =~ s/()\d*(<\/update_timestamp>)/$1$rightnow$2/; open (FH, ">>$events_file") || {$debug_info .= "unable to open file $events_file for writing!\n"}; flock FH,2; print FH $event_xml; close FH; } elsif ($data_storage_mode == 1 ) # DBI { my $event_xml = &event2xml(\%temp_event); my $cal_ids_string = ""; foreach $cal_id (@{$temp_event{cal_ids}}) { $cal_ids_string .= "$cal_id"; if ($cal_id ne @{$temp_event{cal_ids}}[-1]) { $cal_ids_string .= ","; } } $cal_ids_string =~ s/,$//; my $query_string="insert into $events_table (id, cal_ids, start, end, xml_data, update_timestamp) values ($temp_event{id}, '$cal_ids_string', $temp_event{start}, $temp_event{end}, '$event_xml', $temp_event{update_timestamp});"; my $sth = $dbh->prepare($query_string) or ($error_info .= "Can't prepare $query_string:\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $fatal_error = 1; $debug_info .= "Error adding event!\n".$dbh->errstr."\n"; $debug_info .= "query string:\n$query_string\n"; } $sth->finish(); } } # add multiple events to the data file sub add_events() { my ($event_ids_ref) = @_; my @event_ids = @{$event_ids_ref}; if ($data_storage_mode == 0 ) # flat text files { my $out_text=""; foreach $id (sort {$a <=> $b} @event_ids) {$out_text .= &event2xml($events{$id})."\n";} open (FH, ">>$events_file") || {$debug_info .= "unable to open file $events_file for writing!\n"}; flock FH,2; print FH $out_text; close FH; } elsif ($data_storage_mode == 1 ) # DBI { foreach $id (@event_ids) { if ($id eq "") {next}; my %temp_event = %{$events{$id}}; my $event_xml = &event2xml(\%temp_event); my $cal_ids_string = ""; foreach $cal_id (@{$temp_event{cal_ids}}) { $cal_ids_string .= "$cal_id"; if ($cal_id ne @{$temp_event{cal_ids}}[-1]) { $cal_ids_string .= ","; } } $cal_ids_string =~ s/,$//; $debug_info .= "(add events) event $id cal_ids_string: $cal_ids_string\n"; my $query_string = "insert into $events_table (id, cal_ids, start, end, xml_data, update_timestamp) values ($temp_event{id}, '$cal_ids_string', $temp_event{start}, $temp_event{end}, '$event_xml', $temp_event{update_timestamp});"; my $sth = $dbh->prepare($query_string) or ($error_info .= "Can't prepare $query_string:\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $fatal_error = 1; $debug_info .= "Error adding event!\n($query_string)\n".$dbh->errstr."\n"; $debug_info .= "query string:\n$query_string\n"; } $sth->finish(); } } } # update an event (already present in the data file) sub update_event() { my ($event_id) = @_; # temporary copy of the event in question my %temp_event = %{$events{$event_id}}; if ($data_storage_mode == 0 ) # flat text files { my $out_text=""; foreach $id (sort {$a <=> $b} keys %events) { if ($id =~ /\D/) {next}; my $event_xml = &event2xml($events{$id})."\n"; $out_text .= $event_xml; #if ($id eq $event_id) # {$event_xml =~ s/()\d*(<\/update_timestamp>)/$1$rightnow$2/;} } open (FH, ">$events_file") || {$debug_info .= "unable to open file $events_file for writing!\n"}; flock FH,2; print FH $out_text; close FH; } elsif ($data_storage_mode == 1 ) # DBI { my $event_xml = &event2xml(\%temp_event); my $cal_ids_string = ""; foreach $cal_id (@{$temp_event{cal_ids}}) { $cal_ids_string .= "$cal_id"; if ($cal_id ne @{$temp_event{cal_ids}}[-1]) { $cal_ids_string .= ","; } } $cal_ids_string =~ s/,$//; my $query_string="update $events_table set cal_ids='$cal_ids_string', start=$temp_event{start}, end=$temp_event{end}, xml_data='$event_xml', update_timestamp=$temp_event{update_timestamp} where id=$temp_event{id};"; my $sth = $dbh->prepare($query_string) or ($error_info .= "Can't prepare $query_string:\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $fatal_error = 1; $debug_info .= "Error updating event!\n".$dbh->errstr."\n"; $debug_info .= "query string:\n$query_string\n"; } $sth->finish(); } } # update multiple events sub update_events() { my ($event_ids_ref) = @_; my @event_ids = @{$event_ids_ref}; if ($data_storage_mode == 0 ) # flat text files { my $out_text=""; foreach $id (sort {$a <=> $b} keys %events) { if ($id =~ /\D/) {next}; my $event_xml = &event2xml($events{$id})."\n"; $out_text .= $event_xml; #if ($id eq $event_id) # {$event_xml =~ s/()\d*(<\/update_timestamp>)/$1$rightnow$2/;} } open (FH, ">$events_file") || {$debug_info .= "unable to open file $events_file for writing!\n"}; flock FH,2; print FH $out_text; close FH; } elsif ($data_storage_mode == 1 ) # DBI { # temporary copy of the event in question foreach $event_id (@event_ids) { my %temp_event = %{$events{$event_id}}; my $event_xml = &event2xml(\%temp_event); my $cal_ids_string = ""; foreach $cal_id (@{$temp_event{cal_ids}}) { $cal_ids_string .= "$cal_id"; if ($cal_id ne @{$temp_event{cal_ids}}[-1]) { $cal_ids_string .= ","; } } $cal_ids_string =~ s/,$//; my $query_string="update $events_table set cal_ids='$cal_ids_string', start=$temp_event{start}, end=$temp_event{end}, xml_data='$event_xml', update_timestamp=$temp_event{update_timestamp} where id=$temp_event{id};"; my $sth = $dbh->prepare($query_string) or ($error_info .= "Can't prepare $query_string:\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $fatal_error = 1; $debug_info .= "Error updating event!\n".$dbh->errstr."\n"; $debug_info .= "query string:\n$query_string\n"; } $sth->finish(); } } } # delete an event sub delete_event() { my ($event_id) = @_; if ($data_storage_mode == 0 ) # flat text files { delete $events{$event_id}; my $out_text=""; foreach $id (sort {$a <=> $b} keys%events) { if ($id =~ /\D/) {next}; $out_text .= &event2xml($events{$id})."\n"; } open (FH, ">$events_file") || {$debug_info .= "unable to open file $events_file for writing!\n"}; flock FH,2; print FH $out_text; close FH; } elsif ($data_storage_mode == 1 ) # DBI { my $query_string="delete from $events_table where id=$event_id;"; my $sth = $dbh->prepare($query_string) or ($error_info .= "Can't prepare $query_string:\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $fatal_error = 1; $debug_info .= "Error delteing event!\n".$dbh->errstr."\n"; $debug_info .= "query string:\n$query_string\n"; } $sth->finish(); } } # delete multiple events sub delete_events() { my ($event_ids_ref) = @_; my @event_ids = @{$event_ids_ref}; if ($data_storage_mode == 0 ) # flat text files { foreach $event_id (@event_ids) {delete $events{$event_id};} my $out_text=""; foreach $id (sort {$a <=> $b} keys%events) { if ($id =~ /\D/) {next}; $out_text .= &event2xml($events{$id})."\n"; } open (FH, ">$events_file") || {$debug_info .= "unable to open file $events_file for writing!\n"}; flock FH,2; print FH $out_text; close FH; } elsif ($data_storage_mode == 1 ) # DBI { foreach $event_id (@event_ids) { my $query_string="delete from $events_table where id=$event_id;"; my $sth = $dbh->prepare($query_string) or ($error_info .= "Can't prepare $query_string:\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $fatal_error = 1; $debug_info .= "Error deleting event!\n".$dbh->errstr."\n"; $debug_info .= "query string:\n$query_string\n"; } $sth->finish(); } } } sub add_new_calendar() { my ($cal_id) = @_; #$debug_info .= "adding new calendar $cal_id\n"; if ($data_storage_mode == 0 ) # flat text files { # write out the entire file! Grossly inefficient, but that's how it goes if you don't use a DB. foreach $id (sort {$a <=> $b} keys %new_calendars) { #$debug_info .= "calendar2xml $id\n"; my $cal_xml = &calendar2xml($new_calendars{$id})."\n"; #if ($id eq $cal_id) # {$cal_xml =~ s/()\d*(<\/update_timestamp>)/$1$rightnow$2/;} $out_text .= $cal_xml; } open (FH, ">$new_calendars_file") || {$debug_info.= "unable to open file $new_calendars_file for writing!\n"}; flock FH,2; print FH $out_text; close FH; } elsif ($data_storage_mode == 1 ) # DBI { my $cal_xml = &calendar2xml($new_calendars{$cal_id})."\n"; # add the primary calendar to the table my $query_string="insert into $new_calendars_table (id, xml_data, update_timestamp) values ($cal_id, '$cal_xml', $new_calendars{$cal_id}{update_timestamp});"; my $sth = $dbh->prepare($query_string) or ($error_info .= "Can't prepare $query_string:\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $fatal_error = 1; $error_info .= "Error adding new calendar!\n".$dbh->errstr."\n"; $error_info .= "$query_string\n"; } } } sub add_new_calendars() # add multiple calendars (this is used for data conversion) { my ($add_new_cal_ids_ref) = @_; my @add_new_cal_ids = @{$add_new_cal_ids_ref}; if ($data_storage_mode == 0 ) # flat text files { } elsif ($data_storage_mode == 1 ) # DBI { foreach $new_cal_id (@add_new_cal_ids) { my $new_cal_xml = &calendar2xml($new_calendars{$new_cal_id}); my $query_string="insert into $calendars_table (id, xml_data, update_timestamp) values ($new_cal_id, '$new_cal_xml', $new_calendars{$new_cal_id}{update_timestamp});"; my $sth = $dbh->prepare($query_string) or ($error_info .= "Can't prepare $query_string:\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $debug_info .= "Error adding new calendar!\n".$dbh->errstr."\n"; $debug_info .= "$query_string\n"; } } } } sub delete_pending_calendars() # this is called after a record is trandferred from new_calendars to calendars { my ($temp1) = @_; my @pending_calendars_to_delete = @{$temp1}; if ($data_storage_mode == 0 ) # flat text files { # write out the entire file! Grossly inefficient, but that's how it goes if you don't use a DB. foreach $calendar_id (sort {$a <=> $b} keys %new_calendars) { $out_text .= &calendar2xml($new_calendars{$calendar_id})."\n"; } open (FH, ">$new_calendars_file") || {$html_output.= "unable to open file $new_calendars_file for writing!\n"}; flock FH,2; print FH $out_text; close FH; } elsif ($data_storage_mode == 1 ) # DBI { foreach $cal_id (@pending_calendars_to_delete) { my $query_string="delete from $new_calendars_table where id=$cal_id;"; my $sth = $dbh->prepare($query_string) or ($error_info .= "Can't prepare $query_string:\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $debug_info .= "Error deleting pending calendar after approval!\n".$dbh->errstr."\n"; $debug_info .= "$query_string\n"; } } } } sub add_calendars() # add multiple calendars (this is used for data conversion) { my ($add_cal_ids_ref) = @_; my @add_cal_ids = @{$add_cal_ids_ref}; if ($data_storage_mode == 0 ) # flat text files { my $out_text=""; # write out the entire file! Grossly inefficient, but that's how it goes if you don't use a DB. foreach $calendar_id (sort {$a <=> $b} keys %calendars) { my $cal_xml = &calendar2xml($calendars{$calendar_id})."\n"; $out_text .= $cal_xml; } open (FH, ">$calendars_file") || {$debug_info.= "unable to open file $calendars_file for writing!\n"}; flock FH,2; print FH $out_text; close FH; } elsif ($data_storage_mode == 1 ) # DBI { foreach $cal_id (@add_cal_ids) { if ($cal_id eq "") {next}; my $cal_xml = &calendar2xml($calendars{$cal_id}); my $query_string="insert into $calendars_table (id, xml_data, update_timestamp) values ($cal_id, '$cal_xml', $calendars{$cal_id}{update_timestamp});"; my $sth = $dbh->prepare($query_string) or ($error_info .= "Can't prepare $query_string:\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $debug_info .= "Error adding calendar!\n($query_string)\n".$dbh->errstr."\n"; $debug_info .= "$query_string\n"; } } } } sub update_calendar() { my ($cal_id) = @_; if ($data_storage_mode == 0 ) # flat text files { my $out_text=""; # write out the entire file! Grossly inefficient, but that's how it goes if you don't use a DB. foreach $calendar_id (sort {$a <=> $b} keys %calendars) { my $cal_xml = &calendar2xml($calendars{$calendar_id})."\n"; $out_text .= $cal_xml; } open (FH, ">$calendars_file") || {$debug_info.= "unable to open file $calendars_file for writing!\n"}; flock FH,2; print FH $out_text; close FH; } elsif ($data_storage_mode == 1 ) # DBI { my $cal_xml = &calendar2xml($calendars{$cal_id}); # add the primary calendar to the table my $query_string="update $calendars_table set xml_data='$cal_xml', update_timestamp=$calendars{$cal_id}{update_timestamp} where id=$cal_id;"; my $sth = $dbh->prepare($query_string) or ($error_info .= "Can't prepare $query_string:\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $fatal_error = 1; $error_info .= "Error updating calendar!\n".$dbh->errstr."\n"; $error_info .= "$query_string\n"; } } } sub update_calendars() # update multiple calendars { my ($update_cal_ids_ref) = @_; my @update_cal_ids = @{$update_cal_ids_ref}; if ($data_storage_mode == 0 ) # flat text files { my $out_text=""; # write out the entire file! Grossly inefficient, but that's how it goes if you don't use a DB. foreach $calendar_id (sort {$a <=> $b} keys %calendars) { my $cal_xml = &calendar2xml($calendars{$calendar_id})."\n"; $out_text .= $cal_xml; } open (FH, ">$calendars_file") || {$debug_info.= "unable to open file $calendars_file for writing!\n"}; flock FH,2; print FH $out_text; close FH; } elsif ($data_storage_mode == 1 ) # DBI { foreach $cal_id (@update_cal_ids) { my $cal_xml = &calendar2xml($calendars{$cal_id}); my $query_string="update $calendars_table set xml_data='$cal_xml', update_timestamp=$calendars{$cal_id}{update_timestamp} where id=$cal_id;"; my $sth = $dbh->prepare($query_string) or ($error_info .= "Can't prepare $query_string:\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $debug_info .= "Error updating calendar!\n".$dbh->errstr."\n"; $debug_info .= "$query_string\n"; } } } } sub delete_calendar() { my ($cal_id) = @_; delete $calendars{$cal_id}; if ($data_storage_mode == 0 ) # flat text files { my $out_text =""; # write out the entire file! Grossly inefficient, but that's how it goes if you don't use a DB. foreach $calendar_id (sort {$a <=> $b} keys %calendars) { my $cal_xml = &calendar2xml($calendars{$calendar_id})."\n"; $out_text .= $cal_xml; } open (FH, ">$calendars_file") || {$debug_info.= "unable to open file $calendars_file for writing!\n"}; flock FH,2; print FH $out_text; close FH; } elsif ($data_storage_mode == 1 ) # DBI { my $query_string="delete from $calendars_table where id=$cal_id;"; my $sth = $dbh->prepare($query_string) or ($error_info .= "Can't prepare $query_string:\n"); my $rv = $sth->execute(); if ($dbh->errstr ne "") { $debug_info .= "Error deleting calendar!\n".$dbh->errstr."\n"; $debug_info .= "$query_string\n"; } } } sub calendar2xml() { my ($calendar_ref) = @_; my %calendar = %{$calendar_ref}; #$error_info .= "Calendar title: $calendar{title}\n"; my $xml_data = ""; $xml_data .= &xml_store($calendar{id}, "id"); $xml_data .= &xml_store($calendar{title}, "title"); $xml_data .= &xml_store($calendar{details}, "details"); $xml_data .= &xml_store($calendar{link}, "link"); $xml_data .= &xml_store($calendar{password}, "admin_password"); # add local background calendars foreach $local_background_calendar_id (sort {$a <=> $b} keys %{$calendar{local_background_calendars}}) {$xml_data .= "$local_background_calendar_id";} # add remote background calendars #$debug_info .= "adding remote background calendars\n"; foreach $remote_background_calendar_id (sort {$a <=> $b} keys %{$calendar{remote_background_calendars}}) { #$debug_info .= "  id $remote_background_calendar_id\n"; my %c = %{$calendar{remote_background_calendars}{$remote_background_calendar_id}}; if (lc $c{type} eq "plans") { #$debug_info .= " type: $c{type}\n"; $xml_data .= "$remote_background_calendar_id$c{type}$c{version}$c{remote_id}$c{url}$c{password}"; } } # add selectable calendars foreach $selectable_calendar (sort {$a <=> $b} keys %{$calendar{selectable_calendars}}) {$xml_data .= "$selectable_calendar";} # add other fields $xml_data .= &xml_store($calendar{new_calendars_automatically_selectable}, "new_calendars_automatically_selectable"); $xml_data .= &xml_store($calendar{list_background_calendars_together}, "list_background_calendars_together"); $xml_data .= &xml_store($calendar{background_events_display_style}, "background_events_display_style"); $xml_data .= &xml_store($calendar{background_events_fade_factor}, "background_events_fade_factor"); $xml_data .= &xml_store($calendar{background_events_color}, "background_events_color"); $xml_data .= &xml_store($calendar{default_number_of_months}, "default_number_of_months"); $xml_data .= &xml_store($calendar{max_number_of_months}, "max_number_of_months"); $xml_data .= &xml_store($calendar{gmtime_diff}, "gmtime_diff"); $xml_data .= &xml_store($calendar{date_format}, "date_format"); $xml_data .= &xml_store($calendar{week_start_day}, "week_start_day"); $xml_data .= &xml_store($calendar{preload_event_details}, "preload_event_details"); $xml_data .= &xml_store($calendar{info_window_size}, "info_window_size"); $xml_data .= &xml_store($calendar{custom_template}, "custom_template"); $xml_data .= &xml_store($calendar{custom_stylesheet}, "custom_stylesheet"); $xml_data .= &xml_store($calendar{update_timestamp}, "update_timestamp"); $xml_data .= &xml_store($calendar{allow_remote_calendar_requests}, "allow_remote_calendar_requests"); $xml_data .= &xml_store($calendar{remote_calendar_requests_require_password}, "remote_calendar_requests_require_password"); $xml_data .= &xml_store($calendar{remote_calendar_requests_password}, "remote_calendar_requests_password"); $xml_data .= ""; return $xml_data; } sub xml2event() { my ($xml) = @_; my $event; $xml =~ s/<\/?event>//g; # remove and my ($id) = &xml_quick_extract($xml, "id"); $id = &decode($id); my ($cal_id) = &xml_quick_extract($xml, "cal_id"); $cal_id = &decode($cal_id); my ($cal_ids) = &xml_quick_extract($xml, "cal_ids"); $cal_ids = &decode($cal_ids); my ($evt_start) = &xml_quick_extract($xml, "start"); my ($evt_end) = &xml_quick_extract($xml, "end"); my ($series_id) = &xml_quick_extract($xml, "series_id"); $series_id = &decode($series_id); my ($evt_title) = &xml_quick_extract($xml, "title"); $evt_title = &decode($evt_title); my ($evt_details) = &xml_quick_extract($xml, "details"); $evt_details = &decode($evt_details); my ($evt_icon) = &xml_quick_extract($xml, "icon"); $evt_icon = &decode($evt_icon); my ($evt_bgcolor) = &xml_quick_extract($xml, "bgcolor"); $evt_bgcolor = &decode($evt_bgcolor); my ($evt_unit_number) = &xml_quick_extract($xml, "unit_number"); $evt_unit_number = &decode($evt_unit_number); my $update_timestamp = 0; ($update_timestamp) = &xml_quick_extract($xml, "update_timestamp"); my $evt_days = int(($evt_end - $evt_start)/86400)+1; my $event_duration = $evt_end-$evt_start; #$debug_info .= "event $id ($evt_title) duration: ($event_duration)\n"; # create cal_ids hash my @cal_ids_array; if ($cal_id ne "") { push @cal_ids_array, $cal_id; } else { @cal_ids_array = split(',', $cal_ids); } my $all_day_event =""; if (($event_duration+1) % 86400 == 0) { $all_day_event = 1; #$debug_info .= "event $id ($evt_title) is an all day event\n"; } else { # offset start and end by calendar offset. #my $timezone_offset = $calendars{$cal_ids_array[0]}{gmtime_diff} - $current_calendar{gmtime_diff}; if ($current_cal_id eq "") { $current_cal_id = $cal_ids_array[0]; %current_calendar = %{$calendars{$current_cal_id}}; } my $timezone_offset = $current_calendar{gmtime_diff}; #$debug_info .= "(xml2event) event $id, timezone offset: $timezone_offset\n"; $evt_start += $timezone_offset * 3600; $evt_end += $timezone_offset * 3600; #$debug_info .= "(xml2event) event $id start: $evt_start\n"; # experimental--may cause problems. # used to stretch an event that crosses midnight over 2 days. my @temp1=gmtime $evt_start; my @temp2=gmtime $evt_end; if ($temp1[3] != $temp2[3]) { $evt_days++; } } #$debug_info .= "loaded event $id\n"; #$debug_info .= "current calendar: $current_calendar{id}\n"; #$debug_info .= "gmtime diff: $current_calendar{gmtime_diff}\n"; $event = {id => $id, cal_ids => \@cal_ids_array, start => $evt_start, end => $evt_end, days => $evt_days, series_id => $series_id, all_day_event => $all_day_event, title => $evt_title, details => $evt_details, icon => $evt_icon, bgcolor => $evt_bgcolor, unit_number => $evt_unit_number, update_timestamp => $update_timestamp}; return $event; } sub xml2calendar() { my ($xml) = @_; my $calendar; $xml =~ s/<\/?calendar>//g; # remove and my ($cal_id) = &xml_quick_extract($xml, "id"); my ($cal_title) = &xml_quick_extract($xml, "title"); $cal_title = &decode($cal_title); my ($cal_details) = &xml_quick_extract($xml, "details"); $cal_details = &decode($cal_details); my ($cal_link) = &xml_quick_extract($xml, "link"); $cal_link = &decode($cal_link); my ($cal_password) = &xml_quick_extract($xml, "admin_password"); $cal_password = &decode($cal_password); my $update_timestamp=0; ($update_timestamp) = &xml_quick_extract($xml, "update_timestamp"); $update_timestamp = 0 if ($update_timestamp eq ""); # extract local background calendars my @temp = &xml_quick_extract($xml, "background_calendar"); my %local_background_calendars; my $num_background_calendars = scalar @temp; foreach $background_calendar (@temp) { my ($id) = &xml_quick_extract($background_calendar, "id"); $local_background_calendars{$id} = 1; } # extract remote background calendars my @temp = &xml_quick_extract($xml, "remote_background_calendar"); my %remote_background_calendars; my $num_remote_background_calendars = scalar @temp; foreach $remote_background_calendar (@temp) { my ($id) = &xml_quick_extract($remote_background_calendar, "id"); my ($type) = &xml_quick_extract($remote_background_calendar, "type"); my ($version) = &xml_quick_extract($remote_background_calendar, "version"); my ($remote_id) = &xml_quick_extract($remote_background_calendar, "remote_id"); my ($url) = &xml_quick_extract($remote_background_calendar, "url"); my ($password) = &xml_quick_extract($remote_background_calendar, "password"); $remote_background_calendars{$id} = {id => $id, type => $type, version => $version, remote_id => $remote_id, url => $url, password => $password} } # extract selectable calendars my %selectable_calendars; @temp = &xml_quick_extract($xml, "selectable_calendar"); foreach $selectable_calendar (@temp) { $selectable_calendars{$selectable_calendar} = 1; } my ($new_calendars_automatically_selectable) = &xml_quick_extract($xml, "new_calendars_automatically_selectable"); $new_calendars_automatically_selectable = "no" if ($new_calendars_automatically_selectable eq ""); my ($list_background_calendars_together) = &xml_quick_extract($xml, "list_background_calendars_together"); $list_background_calendars_together = "no" if ($list_background_calendars_together eq ""); my ($background_events_display_style) = &xml_quick_extract($xml, "background_events_display_style"); $background_events_display_style = "normal" if ($background_events_display_style eq ""); my ($background_events_fade_factor) = &xml_quick_extract($xml, "background_events_fade_factor"); $background_events_fade_factor = 1 if ($background_events_fade_factor eq "" || $background_events_fade_factor < 1); my ($background_events_color) = &xml_quick_extract($xml, "background_events_color"); $background_events_color = &decode($background_events_color); $background_events_color = "#ffffff" if ($background_events_color eq ""); my ($default_number_of_months) = &xml_quick_extract($xml, "default_number_of_months"); $default_number_of_months = 1 if ($default_number_of_months eq ""); my ($max_number_of_months) = &xml_quick_extract($xml, "max_number_of_months"); $max_number_of_months = 24 if ($max_number_of_months eq ""); my ($gmtime_diff) = &xml_quick_extract($xml, "gmtime_diff"); $gmtime_diff = &decode($gmtime_diff); $gmtime_diff = 0 if ($gmtime_diff eq ""); my ($date_format) = &xml_quick_extract($xml, "date_format"); $date_format = &decode($date_format); $date_format = "mm/dd/yy" if ($date_format eq ""); my ($week_start_day) = &xml_quick_extract($xml, "week_start_day"); $week_start_day = "0" if ($week_start_day eq ""); my ($preload_event_details) = &xml_quick_extract($xml, "preload_event_details"); $preload_event_details = "no" if ($preload_event_details eq ""); my ($info_window_size) = &xml_quick_extract($xml, "info_window_size"); $info_window_size = "400x400" if ($info_window_size eq ""); my ($custom_template) = &xml_quick_extract($xml, "custom_template"); $custom_template = &decode($custom_template); my ($custom_stylesheet) = &xml_quick_extract($xml, "custom_stylesheet"); $custom_stylesheet = &decode($custom_stylesheet); my ($allow_remote_calendar_requests) = &xml_quick_extract($xml, "allow_remote_calendar_requests"); $allow_remote_calendar_requests = &decode($allow_remote_calendar_requests); my ($remote_calendar_requests_require_password) = &xml_quick_extract($xml, "remote_calendar_requests_require_password"); $remote_calendar_requests_require_password = &decode($remote_calendar_requests_require_password); my ($remote_calendar_requests_password) = &xml_quick_extract($xml, "remote_calendar_requests_password"); $remote_calendar_requests_password = &decode($remote_calendar_requests_password); if ($cal_id > $max_cal_id) { $max_cal_id = $cal_id; } $calendar = {id => $cal_id, title => $cal_title, details => $cal_details, link => $cal_link, local_background_calendars => \%local_background_calendars, remote_background_calendars => \%remote_background_calendars, selectable_calendars => \%selectable_calendars, new_calendars_automatically_selectable => $new_calendars_automatically_selectable, list_background_calendars_together => $list_background_calendars_together, background_events_display_style => $background_events_display_style, background_events_fade_factor => $background_events_fade_factor, background_events_color => $background_events_color, allow_remote_calendar_requests => $allow_remote_calendar_requests, remote_calendar_requests_require_password => $remote_calendar_requests_require_password, remote_calendar_requests_password => $remote_calendar_requests_password, default_number_of_months => $default_number_of_months, max_number_of_months => $max_number_of_months, gmtime_diff => $gmtime_diff, date_format => $date_format, week_start_day => $week_start_day, preload_event_details => $preload_event_details, info_window_size => $info_window_size, custom_template => $custom_template, custom_stylesheet => $custom_stylesheet, password => $cal_password, update_timestamp => $update_timestamp}; return $calendar; } sub event2xml() { my ($event_ref) = @_; my %event = %{$event_ref}; my $xml_data = ""; $xml_data .= &xml_store($event{id}, "id"); my $cal_ids_string = ""; foreach $cal_id (@{$event{cal_ids}}) { $cal_ids_string .= "$cal_id"; if ($cal_id ne @{$event{cal_ids}}[-1]) { $cal_ids_string .= ","; } } $cal_ids_string =~ s/,$//; $xml_data .= "$cal_ids_string"; $xml_data .= &xml_store($event{start}, "start"); $xml_data .= &xml_store($event{end}, "end"); $xml_data .= &xml_store($event{series_id}, "series_id"); $xml_data .= &xml_store($event{title}, "title"); $xml_data .= &xml_store($event{details}, "details"); $xml_data .= &xml_store($event{icon}, "icon"); $xml_data .= &xml_store($event{bgcolor}, "bgcolor"); $xml_data .= &xml_store($event{unit_number}, "unit_number"); $xml_data .= &xml_store($event{update_timestamp}, "update_timestamp"); $xml_data .= ""; return $xml_data; } sub event2ical() { my ($event_ref) = @_; my %event = %{$event_ref}; my $results; my $start_timestamp = $event{start}; my $end_timestamp = $event{end}; my $cal_name=$calendars{$event{cal_ids}[0]}{title}; # need to add titles for all other cal_ids my $dtstart_string = &outlook_date_time($event{start}); my $dtend_string = &outlook_date_time($event{end}); $cal_name=$calendars{$event{cal_ids}[0]}{title}; # replace newlines with carraige-returns (otherwise two newlines in a row # causes errors. Not sure whether this is an outlook error or an IE error. $event{details} =~ s/\n/\r/g; $event{title} =~ s/\n/\r/g; $results =< 11) { $next_month=0; $year++; } my $month_end_timestamp = timegm(0,0,0,1,$next_month,$year); return $month_end_timestamp; } sub xml_store { my ($data_string, $tag_name) = @_; my $result_string = ""; $data_string = &encode($data_string); my $result_string = "<$tag_name>$data_string"; return $result_string; } sub xml_quick_extract # it doesn't get any dumber than this. ignores attributes, element order, fooled by duplicate tag names at different depths. { my ($data, $tag_name) = @_; my @results_array = (); while ($data =~ /<$tag_name>(.+?)<\/$tag_name>/gs) { push @results_array, $1; } return @results_array; } sub xml_extract # Slow, but can handle attributes, element order, same tag names at different depths. Can't handle encodings, DTDs. { my ($data, $tag_name, $debug) = @_; my @results_array = (); my $results = ""; my $final_results = ""; my $depth_count=0; my $start_index=0; my $end_index=0; my $match_index=0; my $attributes=(); my $position=0; # position is the position of the element we're looking for, with respect to all other elements # under the parent element while ($data =~ /(<.*?>|<\/.*?>)/g) { my $match=$1; my $temp_index = $+[1]; if ($match =~ /<$tag_name\b.*?>/ && $depth_count==0) # the opening tag we're looking for { $start_index = $temp_index; $depth_count++; if ($debug) {$debug_info .= "active opening tag, $match \ndepth count $depth_count\n";} if ($debug) {$debug_info .= "start index $start_index\n";} my $attribute_text = $match; $attribute_text =~ s/\s*=\s*/=/g; # compress whitespace on either side of = sign $attribute_text =~ s/=([^"])(.+?\b)/="$1$2"/g; # properly format attributes with quote marks #if ($debug) {$debug_info .= "rejiggered attribute text: $attribute_text\n";} # extract attributes while ($attribute_text =~ /\w+?=".+?[^\\]"/g) { my $a_match = $&; my ($name, $value)= split('=',$a_match); $value =~ s/\\"/"/g; # remove first and last characters (the quotes) from value $value = substr $value, 1,-1; if ($debug) {$debug_info .= "attribute: $a_match\n";} if ($debug) {$debug_info .= " name: $name\n";} if ($debug) {$debug_info .= " value: $value\n";} $attributes->{$name} = $value; } #%attributes=(); #$debug_info .= "end position, $+[0]\n\n"; } elsif ($match =~ /<[^\/].*?>/) # some other opening tag { $depth_count++; if ($debug) {$debug_info .= "other opening tag, $match \ndepth count $depth_count\n";} } elsif ($match eq "<\/$tag_name>" && $depth_count == 1) # the closing tag we're looking for { $depth_count--; if ($debug) {$debug_info .= "active closing tag, $match \ndepth count $depth_count\n";} if ($depth_count==0) # done! return results { $end_index = $-[0]; $results = substr $data, $start_index,($end_index-$start_index); my $results_hash=(); $results_hash -> {data} = "".$results; $results_hash -> {attributes} = $attributes; $results_hash -> {position} = $position; push @results_array, $results_hash; if ($debug) {$debug_info .= " pushing results: \"$results\" onto array\n";} if ($debug) {$debug_info .= " attributes: \"$attributes\" \n";} if ($debug) {$debug_info .= " position: \"$position\" \n";} if ($debug) {$debug_info .= " start: $start_index end $end_index\n\n";} #if ($debug) {$debug_info .= " $results\n\n";} $start_index=0; $end_index=0; $attributes=(); } #$debug_info .= "closing tag, $1 \ndepth count $depth_count\n"; #$debug_info .= "start position, $-[0]\n\n"; $position++; } else # other closing tag { $depth_count--; if ($depth_count==0) {$position++;} if ($debug) {$debug_info .= "other closing tag, $match \ndepth count $depth_count\n";} } $match_index++; } return @results_array; } #******************** end xml_extract ********************** sub xml_tags { my ($data, $debug) = @_; my @results_array = (); my %tags_hash; my $depth_count=0; while ($data =~ /(<.*?>|<\/.*?>)/g) { my $match=$1; if ($match =~ /<[^\/].*?>/) # any opening tag { if ($depth_count == 0) # level 0 opening tag { $tag_name = $match; $tag_name =~ s/).+/$1/; $depth_count--; if ($debug) {$debug_info .= "level 1 closing tag, $tag_name \n";} $tags_hash{$tag_name}=1; if ($debug) {$debug_info .= " pushing tag name $tag_name onto array\n\n";} } else # other closing tag { $depth_count--; if ($debug) {$debug_info .= "other closing tag, $match \ndepth count $depth_count\n";} } } return keys %tags_hash; } #******************** end xml_tags ********************** sub xml2hash { my ($xml_data, $debug) = @_; my $item; my @item_tags = &xml_tags($xml_data); if (scalar @item_tags == 0) { if ($debug) {$debug_info .= " plain text item data: ($xml_data) \n";} return $xml_data; } else { if ($debug) {$debug_info .= " xml data: ($xml_data) \n";} my %results_hash; foreach $tag (@item_tags) { my @tag_data = &xml_extract($xml_data,"$tag"); if (scalar @tag_data == 1) { if ($debug) {$debug_info .= " extracting xml for tag $tag (single data)\n";} $results_hash{$tag} = &xml2hash($tag_data[0]->{data},$debug); } else { if ($debug) {$debug_info .= " extracting xml for tag $tag (array data)\n";} my @tag_array; foreach $thing (@tag_data) { push @tag_array, &xml2hash($thing->{data},$debug); } $results_hash{$tag}=\@tag_array; } } if ($debug) {$debug_info .= "\n";} return \%results_hash; } } #******************** end xml2hash ********************** sub hash2xml { my ($temp, $parent_tag, $order_hashref) = @_; my $results=""; my %order_hash = %{$order_hashref}; #$debug_info .= "\nhash2xml: $temp, $parent_tag\n"; if ($temp =~ /ARRAY\(0x/) # array { my @temp_array = @{$temp}; foreach $element (@temp_array) { $results .= "<$parent_tag>"; $results .= $element; $results .= ""; } } elsif ($temp =~ /HASH\(0x/) # hash { $results .= "<$parent_tag>"; my %temp_hash = %{$temp}; foreach $key (sort {$order_hash{$a} <=> $order_hash{$b}} keys %temp_hash) { $results .= &hash2xml($temp_hash{$key}, $key, $order_hashref); } $results .= ""; } else # data { #$debug_info .= "hash2xml: data\n"; $results .= "<$parent_tag>".&encode($temp).""; } return $results; } #******************** end hash2xml ********************** sub get_remote_file { my ($url) = @_; $url =~ s/http:\/\///; my $hostname = $url; $hostname =~ s/\/.+//g; my $document = $url; $document =~ s/.+?\//\//; #$debug_info .= "url: $url
"; #$debug_info .= "hostname: $hostname
"; #$debug_info .= "document: $document
"; if ($hostname eq "" | $document eq "") {return;} $remote = IO::Socket::INET->new( Proto => "tcp", PeerAddr => $hostname, PeerPort => "http(80)" ); unless ($remote) { $debug_info .= "cannot connect to http daemon on $hostname
"; return; } $remote->autoflush(1); print $remote "GET $document HTTP/1.0\r\n"; print $remote "User-Agent: Mozilla 4.0 (compatible; I; Linux-2.0.35i586)\r\n"; print $remote "Host: $hostname\r\n"; #without this line, virtual hosts won't work (multiple domain names on a single IP) print $remote "\r\n\r\n"; @textbuffer=<$remote>; my $textstring = join "", @textbuffer; $textstring =~ s/\r//gs; #some servers sneak these in. $textstring =~ s/.+?\n\n//si; return $textstring; } sub time_overlap { my ($start1, $end1, $start2, $end2) = @_; my $temp1 = $end2 - $start1; my $temp2 = $end1 - $start2; my $range_total = $end2 - $start2; #$debug_info .= "temp1:$temp1 temp2:$temp2 range_total:$range_total\n"; # if the event falls in or overlaps this week (there are 3 cases), the third being an event # that *completely* overlaps the week. if ( ($temp1 <= $range_total && $temp1 > 0) || ($temp2 <= $range_total && $temp2 > 0) || ($temp1 > 0 && $temp2 > 0)) {return 1;} else {return 0;} } sub make_email_link { my ($string) = @_; my $new_string = ""; #remove all newlines $string =~ s/\n//g; #insert newlines after > characters $string =~ s/]+?\@[^ <>]+)/$1<\/a>/g; #ignore substitution if the email address was already a link. if ($1 =~ /(:|")/) {$new_string .= $line;} else { $new_line =~ s/\n//g; $new_string .= $new_line; } } return $new_string; } sub formatted_time { my ($input_time, $format_string) = @_; my @input_time_array = gmtime ($input_time+0); my $ampm = $lang{pm}; if ($input_time_array[5]<1900) {$input_time_array[5]+=1900;} $month_name=$months[$input_time_array[4]]; $input_time_array[4]++; if ($input_time_array[1]<10) {$input_time_array[1]="0".$input_time_array[1];} if ($input_time_array[2] < 12) { #$debug_info .= "$input_time -> $input_time_array[2] (am)\n"; $ampm = $lang{am}; } if ($input_time_array[2] > 12) #convert from 24-hour to am/pm { $input_time_array[2] = $input_time_array[2] - 12; } if ($input_time_array[2] == 0) #convert from 24-hour to am/pm { $input_time_array[2] = 12; } $format_string =~ s/ampm/$ampm/g; $format_string =~ s/hh/$input_time_array[2]/g; $format_string =~ s/mm/$input_time_array[1]/g; $format_string =~ s/ss/$input_time_array[0]/g; $format_string =~ s/mo/$input_time_array[4]/g; $format_string =~ s/mn/$month_name/g; $format_string =~ s/md/$input_time_array[3]/g; $format_string =~ s/yy/$input_time_array[5]/g; return $format_string; } sub nice_date_range_format { my ($timestamp1, $timestamp2, $separator_string) = @_; my $result_string = ""; #make sure the timestamps are in the correct order if ($timestamp1 > $timestamp2) { $temp=$timestamp2; $timestamp2=$timestamp1; $timestamp1=$temp; } my @timestamp1_array = gmtime $timestamp1; my @timestamp2_array = gmtime $timestamp2; #format the year for humans $timestamp1_array[5] +=1900; $timestamp2_array[5] +=1900; if (lc $current_calendar{date_format} eq "dd/mm/yy") { if ($timestamp1_array[4] == $timestamp2_array[4] && $timestamp1_array[5] == $timestamp2_array[5] && $timestamp1_array[3] == $timestamp2_array[3]) { #same year, same month, same day $result_string = " $timestamp1_array[3] $months[$timestamp1_array[4]], $timestamp1_array[5]"; } elsif ($timestamp1_array[4] == $timestamp2_array[4] && $timestamp1_array[5] == $timestamp2_array[5]) { #same year, same month $result_string = "$timestamp1_array[3]$separator_string$timestamp2_array[3] $months[$timestamp1_array[4]], $timestamp1_array[5]"; } elsif ($timestamp1_array[5] != $timestamp2_array[5]) { #different year $result_string = "$timestamp1_array[3] $months[$timestamp1_array[4]], $timestamp1_array[5]$separator_string$timestamp2_array[3] $months[$timestamp2_array[4]], $timestamp2_array[5]"; } else { #same year, different months $result_string = "$timestamp1_array[3] $months[$timestamp1_array[4]]$separator_string$timestamp2_array[3] $months[$timestamp2_array[4]], $timestamp2_array[5]"; } } #elsif (lc $current_calendar{date_format} eq "mm/dd/yy") else { if ($timestamp1_array[4] == $timestamp2_array[4] && $timestamp1_array[5] == $timestamp2_array[5] && $timestamp1_array[3] == $timestamp2_array[3]) { #same year, same month, same day $result_string = "$months[$timestamp1_array[4]] $timestamp1_array[3], $timestamp1_array[5]"; } elsif ($timestamp1_array[4] == $timestamp2_array[4] && $timestamp1_array[5] == $timestamp2_array[5]) { #same year, same month $result_string = "$months[$timestamp1_array[4]] $timestamp1_array[3]$separator_string$timestamp2_array[3], $timestamp1_array[5]"; } elsif ($timestamp1_array[5] != $timestamp2_array[5]) { #different year $result_string = "$months[$timestamp1_array[4]] $timestamp1_array[3], $timestamp1_array[5]$separator_string$months[$timestamp2_array[4]] $timestamp2_array[3], $timestamp2_array[5]"; } else { #same year, different months $result_string = "$months[$timestamp1_array[4]] $timestamp1_array[3]$separator_string$months[$timestamp2_array[4]] $timestamp2_array[3], $timestamp2_array[5]"; } } return $result_string; } sub nice_time_range_format { my ($start, $end) = @_; my $results = ""; $results = &formatted_time($start,"hh:mm ampm")." - ".&formatted_time($end,"hh:mm ampm"); # if times are the same, remove the second one. if ($start eq $end) { $results =~ s/s*-.+//; return $results; } # if both times are am or pm, remove the first one (it's redundant!) $results =~ s/(.*) $lang{am}(.*$lang{am}.*)/$1$2/; $results =~ s/(.*) $lang{pm}(.*$lang{pm}.*)/$1$2/; return $results; } sub encode { my ($input_string) = @_; return if ($input_string eq ""); my $output_string=$input_string; $output_string =~ s/(\W)/"\%".sprintf("%02x", (ord $1))/ge; $output_string =~ s/\%20/+/g; return $output_string; } sub decode { my ($input_string) = @_; return if ($input_string eq ""); my $output_string = $input_string; $output_string =~ s/\+/ /g; $output_string =~ s/%([0-9A-Fa-f]{2})/pack("c",hex($1))/ge; return $output_string; } sub min { @_ = sort {$a <=> $b} @_; shift; } sub max { @_ = sort {$a <=> $b} @_; pop; } sub generate_event_details { my ($event_ref, $preview) = @_; my %event = %{$event_ref}; my $return_text = $event_details_template; my @event_start_timestamp_array = gmtime $event{start}; my $event_cal_title_text = ""; foreach $temp_cal_id (@{$event{cal_ids}}) { my $event_cal_name = "$calendars{$temp_cal_id}{title}"; if ($calendars{$temp_cal_id}{link} =~ /\S/) { $event_cal_name = "$calendars{$temp_cal_id}{title}"; } $event_cal_title_text .= $event_cal_name.","; } $event_cal_title_text =~ s/,$//; # remove trailing comma $return_text =~ s/###event calendar name###/$event_cal_title_text/g; my $date_string = $lang{event_details_date_goes_here}; my $event_time = ""; if ($event{start} ne "") { $date_string = &nice_date_range_format($event{start}, $event{end}, " - "); if ($event{all_day_event} ne "1") { $event_time = &nice_time_range_format($event{start},$event{end}); # if both times are am or pm, remove the first one (it's redundant!) $event_time = "$event_time"; } } $return_text =~ s/###event date###/$date_string/g; $return_text =~ s/###event time###/$event_time/g; $return_text =~ s/###event title###/$event{title}/g; $return_text =~ s/###event id###/$event{id}/g; $return_text =~ s/###event calendar id###/$event{cal_id}[0]/g; $return_text =~ s/###event background color###/$event{bgcolor}/g; my $event_details = $event{details}; #replace \n characters with
tags $event_details =~ s/\n/\n
\n/g; # check the event details, and see if there are any non-htmlified # links. If so, turn them into links. $event_details =~ s/[^"](htt.:\/\/.+?),*\.?(\s|\n|<|$)/ $1<\/a>$2/g; # convert email addresses to links. $event_details = &make_email_link($event_details); # make sure all links open up in a new window $event_details =~ s/
"; } $return_text =~ s/###event icon###/$event_icon_text/g; my $unit_number_text = $event{unit_number}; $unit_number_text =~ s/(\d)//g; $return_text =~ s/###unit number icon###/$unit_number_text/g; my $edit_event_link = "
$lang{context_menu_edit_event}"; $edit_event_link = $lang{event_details_edit_disable} unless $writable{events_file}; $return_text =~ s/###edit event link###/$edit_event_link/g; my $delete_event_link = "$lang{context_menu_delete_event}"; $delete_event_link = $lang{context_menu_delete_event_disable} unless $writable{events_file}; $return_text =~ s/###delete event link###/$delete_event_link/g; my $email_reminder_link = ""; if ($email_mode != 0) { $email_reminder_link = "$lang{email_reminder_link}"; $email_reminder_link = $lang{event_email_reminder_disable} unless $writable{email_reminders_datafile}; $return_text =~ s/###email reminder link###/$email_reminder_link/g; } else { $email_reminder_link = $lang{event_email_reminder_disable} unless $writable{email_reminders_datafile}; $return_text =~ s/()?###email reminder link###/$email_reminder_link/g; } my $temp = &export_event_link(\%event); $return_text =~ s/###export event link###/$temp/g; my $cal_detail_text .= < $lang{this_event_to}
p1 } #export_event_link sub validate_emails() { my ($email_string) = @_; # support multiple email addresses my @to_addresses = split (',', "$email_string"); foreach $to_address (@to_addresses) { if (!($to_address =~ /^[\w\-\_\.]+\@([\w\-\_]+\.)+[a-zA-Z]{2,}$/)) { return $to_address; } } return ""; } sub send_email_reminder() { my ($event_ref, $to_address, $email_text) = @_; my %event = %{$event_ref}; if ($email_mode == 0) {return $lang{send_email_reminder2};} $date_string = &nice_date_range_format($event{start}, $event{end}, " - "); $to_address =~ s/\s//g; chomp $to_address; my $email_valid = &validate_emails($to_address); if ($email_valid ne "") { return "$lang{send_email_reminder1} ($email_valid)."; } my @to_addresses = split (',', "$to_address"); foreach $temp (@to_addresses) { my $subject = $lang{send_email_reminder_subject}; $subject =~ s/###title###/$event{title}/g; &send_email($temp, $reply_address, $reply_address, $subject, $email_text); } return "1"; #return "$lang{send_email_reminder3} ($email_mode)."; } # send_email_reminder sub send_email() { my ($to, $from, $reply_to, $subject, $body) = @_; $body =~ s/\n/\r\n/g; if ($email_mode == 1) { open(SENDMAIL, "| $sendmail_location -t") || ($debug_info .= "Can't open sendmail at $sendmail_location!\n"); print SENDMAIL <mail($reply_to); $smtp->to($to); $smtp->data(); $smtp->datasend("To: $to\n"); $smtp->datasend("From: $from\n"); $smtp->datasend("Subject: $subject\n"); $smtp->datasend("$body\n"); $smtp->dataend(); $smtp->reset(); } else { } } sub deep_copy { my $this = shift; if (not ref $this) { $this; } elsif (ref $this eq "ARRAY") { [map deep_copy($_), @$this]; } elsif (ref $this eq "HASH") { +{map { $_ => deep_copy($this->{$_}) } keys %$this}; } else { die "what type is $_?" } } 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"; } } # default calendar data structure #%default_cal; %default_cal = (id => "", title => "", details => $new_calendar_default_details, link => "", local_background_calendars => {}, selectable_calendars => {}, make_new_calendars_selectable => $new_calendars_automatically_selectable, list_background_calendars_together => "", background_events_display_style => "normal", background_events_fade_factor => "", background_events_color => "#ffffff", default_number_of_months => 1, max_number_of_months => 24, gmtime_diff => 0, date_format => "mm/dd/yy", week_start_day => 0, preload_event_details => "yes", info_window_size => "400x400", custom_template => "", custom_stylesheet => "", password => "", update_timestamp => 0); # If an included file contains only subroutines, perl will complain # that it "did not return a true value". The "return 1;" at the end fixes this. return 1;