fix #1819: fork_worker: ensure sync'ed workers control terminal
[pve-common.git] / src / PVE / CalendarEvent.pm
index d235e04..3c08eb0 100644 (file)
@@ -4,6 +4,8 @@ use strict;
 use warnings;
 use Data::Dumper;
 use Time::Local;
+use PVE::JSONSchema;
+use PVE::Tools qw(trim);
 
 # Note: This class implements a parser/utils for systemd like calender exents
 # Date specification is currently not implemented
@@ -18,11 +20,29 @@ my $dow_names = {
     sat => 6,
 };
 
+PVE::JSONSchema::register_format('pve-calendar-event', \&pve_verify_calendar_event);
+sub pve_verify_calendar_event {
+    my ($text, $noerr) = @_;
+
+    eval { parse_calendar_event($text); };
+    if (my $err = $@) {
+       return undef if $noerr;
+       die "invalid calendar event '$text' - $err\n";
+    }
+    return $text;
+}
+
 # The parser.
 # returns a $calspec hash which can be passed to compute_next_event()
 sub parse_calendar_event {
     my ($event) = @_;
 
+    $event = trim($event);
+
+    if ($event eq '') {
+       die "unable to parse calendar event - event is empty\n";
+    }
+
     my $parse_single_timespec = sub {
        my ($p, $max, $matchall_ref, $res_hash) = @_;
 
@@ -42,6 +62,7 @@ sub parse_calendar_event {
                    $$matchall_ref = 1;
                } else {
                    $start = int($start);
+                   die "value '$start' out of range\n" if $start >= $max;
                    $res_hash->{$start} = 1;
                }
            }
@@ -107,8 +128,12 @@ sub parse_calendar_event {
 
     if ($time_spec =~ m/^($chars+):($chars+)$/) {
        my ($p1, $p2) = ($1, $2);
-       $parse_single_timespec->($p1, 24, \$matchall_hours, $hours_hash);
-       $parse_single_timespec->($p2, 60, \$matchall_minutes, $minutes_hash);
+       foreach my $p (split(',', $p1)) {
+           $parse_single_timespec->($p, 24, \$matchall_hours, $hours_hash);
+       }
+       foreach my $p (split(',', $p2)) {
+           $parse_single_timespec->($p, 60, \$matchall_minutes, $minutes_hash);
+       }
     } elsif ($time_spec =~ m/^($chars)+$/) { # minutes only
        $matchall_hours = 1;
        foreach my $p (split(',', $time_spec)) {
@@ -124,13 +149,13 @@ sub parse_calendar_event {
     if ($matchall_hours) {
        $h = '*';
     } else {
-       $h = [ sort keys %$hours_hash ];
+       $h = [ sort { $a <=> $b } keys %$hours_hash ];
     }
 
     if ($matchall_minutes) {
        $m = '*';
     } else {
-       $m = [ sort keys %$minutes_hash ];
+       $m = [ sort { $a <=> $b } keys %$minutes_hash ];
     }
 
     return { h => $h, m => $m, dow => [ sort keys %$dow_hash ]};
@@ -152,9 +177,13 @@ sub compute_next_event {
 
        if ($utc) {
            (undef, $min, $hour, $mday, $mon, $year, $wday) = gmtime($last);
+           # gmtime and timegm interpret two-digit years differently
+           $year += 1900;
            $startofday = timegm(0, 0, 0, $mday, $mon, $year);
        } else {
            (undef, $min, $hour, $mday, $mon, $year, $wday) = localtime($last);
+           # localtime and timelocal interpret two-digit years differently
+           $year += 1900;
            $startofday = timelocal(0, 0, 0, $mday, $mon, $year);
        }