# ScripCondition: Crontab.pm
# IsApplicable returns true if the current time is within the
# range specified by the Argument, undef otherwise.

# Bruce Campbell says to always give yourself Credit, and
# to use RCS (man rcs):
# $Id: Crontab.pm,v 1.3 2002/02/05 12:15:42 bc Exp $

=head1 NAME

RT::Condition::Crontab

=head1 SYNOPSIS

This is a ScripCondition to fire when 'Now' is within certain time
ranges.  The Argument supplied is the standard 'crontab' format.

Multiple crontab-style time ranges can be specified by seperating 
them with the ':' character.

=head1 ARGUMENT FORMAT

Crontab format is defined as 5 fields, seperated by spaces.  The
specific fields are described in order as:

              field          allowed values
              -----          --------------
              minute         0-59
              hour           0-23
              day of month   1-31
              month          1-12
              day of week    0-7 (0 or 7 is Sun)

Any of the fields may consist of specific numbers within the allowed
values, ranges (identified by 'start-end'), lists (seperated by 
commas, eg '1,4,6,4'), steps (every 'step' value, indicated by a '/',
eg every 2nd item from 4 to 9 '4-9/2') or a wildcard ('*').

Set::Crontab also defines the following extensions:

	<N and >N

		Elements from the allowed range less than or greater
		than 'N'.

	!N

		Excludes 'N' from the set.

=head1 EXAMPLES

The following matches all times between 9am and 4:59pm (close enough
to 5pm), Monday to Friday:

	* 9-16 * * 1-5

The following (in the Argument) matches all times between 8:45am and 5:15pm
Monday to Friday (note the ':' seperations):

	45-59 8 * * 1-5:* 9-16 * * 1-5:0-15 17 * * 1-5

=cut

# Who are we?
package RT::Condition::Crontab;

# Bring all the RT Goodness into our lives.
require RT::Condition::Generic;

# and our namespace
@ISA = qw(RT::Condition::Generic);

# We're going to use this later.
use Set::Crontab;

# Override RT::Condition::Generic's IsApplicable method:

sub IsApplicable {
	# Get our OO $self.
	my $self = shift;

	# Prepare our default return value
	my $retval = undef;

	# Get the current time.
	my $cur_time = new RT::Date( $RT::Nobody );
	$cur_time->SetToNow();

	# Split apart the multiple time ranges.
	my @time_ranges = split( ':', $self->Argument );

	# Prepare 'now'.
	# We'll use localtime on the Unix() method of RT::Date
	# This returns a @list of time components.
	my @nowlist = localtime( $cur_time->Unix );

	# Build up a compare list.
	my @wantlist = ();

	# Names are not supported in Set::Crontab
	$wantlist[0] = $nowlist[1];	# minute 0-59
	$wantlist[1] = $nowlist[2];	# hour 0-23
	$wantlist[2] = $nowlist[3];	# day of month 1-31
	$wantlist[3] = $nowlist[4] + 1;	# month 1-12 (need to adjust)
	$wantlist[4] = $nowlist[6];	# day of week 0-7

	# What about the specific ranges?
	my @known_ranges = (
		[0..59],		# minute 0-59
		[0..23],		# hour 0-23
		[1..31],		# day of month 1-31
		[1..12],		# month 1-12
		[0..7],			# day of week 0-7
		);



	# We loop through the various @time_ranges that we've found.

	foreach my $this_range ( @time_ranges ){

		# Remove leading or trailing space (we split() on that later)
		$this_range =~ s/^\s*//g;
		$this_range =~ s/\s*$//g;

		# $RT::Logger->debug("$self: Range is $this_range\n");

		# How many of the fields have we matched so far?
		my $matched = 0;

		# Split apart the fields based on whitespace.
		my @this_list = split( /\s+/, $this_range );

		# See if each field is in within range.
		my $loop = 0;
		while( ( $loop < scalar @wantlist ) && ( $loop < scalar @this_list ) ){
			my $tst = Set::Crontab->new( $this_list[$loop], $known_ranges[$loop] );
			# $RT::Logger->debug( "$self: Testing $wantlist[$loop] against $this_list[$loop]\n");
			if( $tst->contains( $wantlist[$loop] ) ){
				$matched++;
			}

			# Remember to increment the loop.
			$loop++;
		}

		# Now we check to see whether we matched them all.  
		# If we didn't, 'now' is obviously not within this time range.

		# Did we match them all?
		if( ( $matched == $loop ) && ( $loop == scalar @wantlist ) ){
			# We did.  Set our return value.
			$retval++;
		}
	}

	# retval is undef when declared, and only incremented
	# if a time value matches.
	return( $retval );

}

1;

