Drop duplicate code

https://github.com/webmin/webmin/pull/2193#discussion_r1632362334
This commit is contained in:
Ilia Ross
2024-06-09 01:53:36 +03:00
parent 535d4173b3
commit 0221a092b9

View File

@@ -1286,336 +1286,4 @@ if (!glob("\Q$rv\E.*")) {
return $rv;
}
# parse_calendar_file(calendar-file|lines)
# Parses an iCalendar file and returns a list of events
sub parse_calendar_file
{
my ($calendar_file) = @_;
my (@events, %event, $line);
eval "use DateTime; use DateTime::TimeZone;";
return \@events if ($@);
# Timezone map
my %timezone_map = (
'Afghanistan Time' => 'AFT',
'Alaskan Daylight Time' => 'AKDT',
'Alaskan Standard Time' => 'AKST',
'Anadyr Time' => 'ANAT',
'Arabian Standard Time' => 'AST',
'Argentina Time' => 'ART',
'Atlantic Daylight Time' => 'ADT',
'Atlantic Standard Time' => 'AST',
'Australian Central Daylight Time' => 'ACDT',
'Australian Central Standard Time' => 'ACST',
'Australian Eastern Daylight Time' => 'AEDT',
'Australian Eastern Standard Time' => 'AEST',
'Bangladesh Standard Time' => 'BST',
'Brasília Time' => 'BRT',
'British Summer Time' => 'BST',
'Central Africa Time' => 'CAT',
'Central Asia Time' => 'ALMT',
'Central Daylight Time' => 'CDT',
'Central Daylight Time (US)' => 'CDT',
'Central European Summer Time' => 'CEST',
'Central European Time' => 'CET',
'Central Indonesia Time' => 'WITA',
'Central Standard Time (Australia)' => 'CST',
'Central Standard Time (US)' => 'CST',
'Central Standard Time' => 'CST',
'Chamorro Daylight Time' => 'CHDT',
'Chamorro Standard Time' => 'CHST',
'China Standard Time' => 'CST',
'Coordinated Universal Time' => 'UTC',
'East Africa Time' => 'EAT',
'Eastern Africa Time' => 'EAT',
'Eastern Daylight Time' => 'EDT',
'Eastern Daylight Time (US)' => 'EDT',
'Eastern European Summer Time' => 'EEST',
'Eastern European Time' => 'EET',
'Eastern Indonesia Time' => 'WIT',
'Eastern Standard Time (Australia)' => 'EST',
'Eastern Standard Time (US)' => 'EST',
'Eastern Standard Time' => 'EST',
'Fiji Time' => 'FJT',
'Greenwich Mean Time' => 'GMT',
'Hawaii-Aleutian Daylight Time' => 'HADT',
'Hawaii-Aleutian Standard Time' => 'HAST',
'Hawaiian Standard Time' => 'HST',
'Hong Kong Time' => 'HKT',
'Indian Standard Time' => 'IST',
'Iran Standard Time' => 'IRST',
'Irish Standard Time' => 'IST',
'Israel Standard Time' => 'IST',
'Japan Standard Time' => 'JST',
'Korea Standard Time' => 'KST',
'Magadan Time' => 'MAGT',
'Malaysia Time' => 'MYT',
'Moscow Standard Time' => 'MSK',
'Mountain Daylight Time' => 'MDT',
'Mountain Standard Time' => 'MST',
'Myanmar Standard Time' => 'MMT',
'Nepal Time' => 'NPT',
'New Caledonia Time' => 'NCT',
'New Zealand Daylight Time' => 'NZDT',
'New Zealand Standard Time' => 'NZST',
'Newfoundland Daylight Time' => 'NDT',
'Newfoundland Standard Time' => 'NST',
'Pacific Daylight Time' => 'PDT',
'Pacific Standard Time' => 'PST',
'Pakistan Standard Time' => 'PKT',
'Philippine Time' => 'PHT',
'Sakhalin Time' => 'SAKT',
'Samoa Standard Time' => 'SST',
'Singapore Standard Time' => 'SGT',
'South Africa Standard Time' => 'SAST',
'Tahiti Time' => 'TAHT',
'Venezuelan Standard Time' => 'VET',
'West Africa Time' => 'WAT',
'Western European Summer Time' => 'WEST',
'Western European Time' => 'WET',
'Western Indonesia Time' => 'WIB',
'Western Standard Time (Australia)' => 'WST',
);
# Make a date from a special timestamp
my $adjust_time_with_timezone = sub {
my ($time, $tzid) = @_;
my $dt = DateTime->new(
year => substr($time, 0, 4),
month => substr($time, 4, 2),
day => substr($time, 6, 2),
hour => substr($time, 9, 2),
minute => substr($time, 11, 2),
second => substr($time, 13, 2),
time_zone => $tzid);
my $local_dt = $dt->clone->set_time_zone('local');
return {
formatted => $dt->strftime("%Y-%m-%d %H:%M:%S"),
timestamp => $dt->epoch,
formatted_local => $local_dt->strftime('%Y-%m-%d %H:%M:%S'),
timestamp_local => $local_dt->epoch,
};
};
# Lines processor
my $process_line = sub
{
my ($line) = @_;
# Start a new event
if ($line =~ /^BEGIN:VEVENT/) {
%event = ();
$event{'description'} = [ ];
$event{'attendees'} = [ ];
}
# Convert times using the timezone
elsif ($line =~ /^END:VEVENT/) {
# Local timezone
$event{'tzid_local'} = DateTime::TimeZone->new(name => 'local')->name();
$event{'tzid'} = 'UTC', $event{'tzid_missing'} = 1 if (!$event{'tzid'});
# Adjust times with timezone
my ($adjusted_start, $adjusted_end);
$event{'tzid'} = $timezone_map{$event{'tzid'}} || $event{'tzid'};
# Add single start/end time
if ($event{'dtstart'}) {
$adjusted_start =
$adjust_time_with_timezone->($event{'dtstart'},
$event{'tzid'});
$event{'dtstart_timestamp'} = $adjusted_start->{'timestamp'};
my $dtstart_date =
&make_date($event{'dtstart_timestamp'},
{ tz => $event{'tzid'} });
$event{'dtstart_date'} =
"$dtstart_date->{'short'} $dtstart_date->{'timeshort'}";
$event{'dtstart_local_timestamp'} =
$adjusted_start->{'timestamp_local'};
$event{'dtstart_local_date'} =
&make_date($event{'dtstart_local_timestamp'});
}
if ($event{'dtend'}) {
$adjusted_end =
$adjust_time_with_timezone->($event{'dtend'}, $event{'tzid'});
$event{'dtend_timestamp'} = $adjusted_end->{'timestamp'};
my $dtend_date = &make_date($event{'dtend_timestamp'},
{ tz => $event{'tzid'} });
$event{'dtend_date'} =
"$dtend_date->{'short'} $dtend_date->{'timeshort'}";
$event{'dtend_local_timestamp'} =
$adjusted_end->{'timestamp_local'};
$event{'dtend_local_date'} =
&make_date($event{'dtend_local_timestamp'});
}
if ($event{'dtstart'} && $event{'dtend'}) {
# Try to add local 'when (period)'
my $dtstart_local_obj =
$event{'_obj_dtstart_local_time'} =
make_date($event{'dtstart_local_timestamp'}, { _ });
my $dtend_local_obj =
$event{'_obj_dtend_local_time'} =
make_date($event{'dtend_local_timestamp'}, { _ });
# Build when local, e.g.:
# Tue Jun 04, 2024 04:30 PM 05:15
# PM (Asia/Nicosia +0300)
# or
# Tue Jun 04, 2024 04:30 PM Wed Jun 05, 2024 01:15
# AM (Asia/Nicosia +0300)
$event{'dtwhen_local'} =
# Start local
$dtstart_local_obj->{'week'}.' '.
$dtstart_local_obj->{'month'}.' '.
$dtstart_local_obj->{'day'}.', '.
$dtstart_local_obj->{'year'}.' '.
$dtstart_local_obj->{'timeshort'}.' ';
# End local
if ($dtstart_local_obj->{'year'} eq
$dtend_local_obj->{'year'} &&
$dtstart_local_obj->{'month'} eq
$dtend_local_obj->{'month'} &&
$dtstart_local_obj->{'day'} eq
$dtend_local_obj->{'day'}) {
$event{'dtwhen_local'} .=
$dtend_local_obj->{'timeshort'};
}
else {
$event{'dtwhen_local'} .=
$dtend_local_obj->{'week'}.' '.
$dtend_local_obj->{'month'}.' '.
$dtend_local_obj->{'day'}.', '.
$dtend_local_obj->{'year'}.' '.
$dtend_local_obj->{'timeshort'};
}
# Timezone local
if ($event{'tzid_local'} ||
$dtstart_local_obj->{'tz'}) {
if ($event{'tzid_local'} &&
$dtstart_local_obj->{'tz'}) {
if ($event{'tzid_local'} eq
$dtstart_local_obj->{'tz'}) {
$event{'dtwhen_local'} .=
" ($event{'tzid_local'})";
}
else {
$event{'dtwhen_local'} .=
" ($event{'tzid_local'} ".
"$dtstart_local_obj->{'tz'})";
}
}
elsif ($event{'tzid_local'}) {
$event{'dtwhen_local'} .=
" ($event{'tzid_local'})";
}
else {
$event{'dtwhen_local'} .=
" ($dtstart_local_obj->{'tz'})";
}
}
# Try to add original 'when (period)'
my $dtstart_obj =
$event{'_obj_dtstart_time'} =
make_date($event{'dtstart_timestamp'},
{ tz => $event{'tzid'} });
my $dtend_obj =
$event{'_obj_dtend_time'} =
make_date($event{'dtend_timestamp'},
{ tz => $event{'tzid'} });
# Build original when
if (!$event{'tzid_missing'}) {
$event{'dtwhen'} =
# Start original
$dtstart_obj->{'week'}.' '.
$dtstart_obj->{'month'}.' '.
$dtstart_obj->{'day'}.', '.
$dtstart_obj->{'year'}.' '.
$dtstart_obj->{'timeshort'}.' ';
# End original
if ($dtstart_obj->{'year'} eq
$dtend_obj->{'year'} &&
$dtstart_obj->{'month'} eq
$dtend_obj->{'month'} &&
$dtstart_obj->{'day'} eq
$dtend_obj->{'day'}) {
$event{'dtwhen'} .=
$dtend_obj->{'timeshort'};
}
else {
$event{'dtwhen'} .=
$dtend_obj->{'week'}.' '.
$dtend_obj->{'month'}.' '.
$dtend_obj->{'day'}.', '.
$dtend_obj->{'year'}.' '.
$dtend_obj->{'timeshort'};
}
# Timezone original
if ($dtstart_obj->{'tz'}) {
$event{'dtwhen'} .=
" ($dtstart_obj->{'tz'})";
}
}
}
# Add the event to the list
push(@events, { %event });
}
# Parse fields
elsif ($line =~ /^SUMMARY.*?:(.*)$/) {
$event{'summary'} = $1;
}
elsif ($line =~ /^DTSTART:(.*)$/) {
$event{'dtstart'} = $1;
}
elsif ($line =~ /^DTSTART;TZID=(.*?):(.*)$/) {
$event{'tzid'} = $1;
$event{'dtstart'} = $2;
}
elsif ($line =~ /^DTEND:(.*)$/) {
$event{'dtend'} = $1;
}
elsif ($line =~ /^DTEND;TZID=(.*?):(.*)$/) {
$event{'tzid'} = $1;
$event{'dtend'} = $2;
}
elsif ($line =~ /^DESCRIPTION:(.*)$/) {
my $description = $1;
$description =~ s/\\n/<br>/g;
$description =~ s/\\//g;
unshift(@{$event{'description'}}, $description);
}
elsif ($line =~ /^DESCRIPTION;LANGUAGE=([a-z]{2}-[A-Z]{2}):(.*)$/) {
my $description = $2;
$description =~ s/\\n/<br>/g;
$description =~ s/\\//g;
unshift(@{$event{'description'}}, $description);
}
elsif ($line =~ /^LOCATION.*?:(.*)$/) {
$event{'location'} = $1;
}
elsif ($line =~ /^ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=(.*?):mailto:(.*)$/ ||
$line =~ /^ATTENDEE;.*CN=(.*?);.*mailto:(.*)$/ ||
$line =~ /^ATTENDEE:mailto:(.*)$/) {
push(@{$event{'attendees'}}, { 'name' => $1, 'email' => $2 });
}
elsif ($line =~ /^ORGANIZER;CN=(.*?):(?:mailto:)?(.*)$/) {
$event{'organizer_name'} = $1;
$event{'organizer_email'} = $2;
}
};
# Read the ICS file lines or just use the lines
my $ics_file_lines =
-r $calendar_file ?
&read_file_lines($calendar_file, 1) :
[ split(/\r?\n/, $calendar_file) ];
# Process each line of the ICS file
foreach my $ics_file_line (@$ics_file_lines) {
# Check if the line is a continuation of the previous line
if ($ics_file_line =~ /^[ \t](.*)$/) {
$line .= $1; # Concatenate with the previous line
}
else {
# Process the previous line
$process_line->($line) if ($line);
$line = $ics_file_line; # Start a new line
}
}
# Process the last line
$process_line->($line) if ($line);
# Return the list of events
return \@events;
}
1;