# -*- Perl -*-
#
# Copyright (C) 1996 Craig Ruefenacht <ruefenac@cs.utah.edu> and the
#                    University of Utah Computer Science Department
#
# $Id: Rust_Action.pm,v 1.43 1997/04/16 20:18:32 ruefenac Exp $
#
# This is part of RUST-1.0 Beta 6
#
# Abstract: This library contains a class defination to use to
#           deal with actions performed on a RUST ticket
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with the source distribution of RUST, in the license
# directory. If you don't have a copy of it, write to: 
#
# Free Software Foundation, Inc.
# 675 Mass Ave
# Cambridge, MA 02139, USA.

{
package Rust_Action;

use Rust_Configs;
use Rust_Lib;
use Rust_Exit;
use Rust_Debug;
use Lock;

require Exporter;
@ISA = qw(Exporter);
@EXPORT =
    (
     # name                       brief description
     # --------------------------------------------------------     
     'init_action',		# Initialize this library
     'do_action',		# Decide what needs to be done and do it
     'do_assign',		# Assign a ticket to a user
     'do_create',		# Create a ticket
     'do_comment',		# Comment a ticket
     'do_assign',		# Assign a ticket
     'do_move',			# Move a ticket
     'do_merge',		# Merge two tickets together
     'do_number',		# Manipulate number files
     'do_kill',			# Kill a ticket
     'do_archive',		# Archive a range of tickets
     'add_time',		# Add time to the work_hours header
     'create_queue_files'	# Make files for a new queue
     );

 
#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub init_action {
    #
    # Type: Can be called by external scripts
    #
    # Abstract: Initialize this library by adding the $global
    #           hash to this object.
    
    $globals = &get_globals;
}
#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub do_action {
    #
    # Type: Can be called by external scripts
    #
    # Abstract: Do whatever action we need to do.
    #
    #           If file couldn't be read, then &read_file will
    #           not return to here but will rather quit the
    #           program on its own, so there is no need to
    #           make sure that &read_file completed.  
    #
    #           Also, get_filenames will not return if it can't
    #           find the header file for the specified ticket number.
    #           This usually means that the ticket doesn't exist.  If
    #           get_file_names does not return, then the program terminates,
    #           spitting out to stdout that there isn't a ticket assigned
    #           to the specified number.
    #
    #           The $type variable tells us what we are to do.  
    #           If $type is equal to 0, then we are setting a header
    #           to some value.  If $type is 1, then we want to append
    #           a value to a header

    &ddebug("Entering Rust_Action::do_action\n");

    my ($caller, $type, $opt_num, $opt_set, $opt_append,
	$opt_value, $queue_index) = @_;

    my (@action_list, $stdout_message, $log_message) = ((), (), ());
    my (@messages, $error, @errors, $original, $new_value, $date);
    my ($locked_files) = new Lock;

    &debug("Going to set the $opt_set header to $opt_value\n")
	if ($opt_set);
    &debug("Going to append $opt_value to the $opt_append header\n")
	if ($opt_append);

    # Read in the file.  This creates $rust_info
    @errors = &read_file($caller, $locked_files, "", $opt_num, $queue_index);

    if ($errors[0]) {
	$locked_files->unlock_all;
	return $errors[0];
    }

    $rust_info = $errors[1];
    $opt_num = $rust_info->get_value('number');
    $queue_index = $rust_info->get_value('queue_index');
    $date = $rust_info->get_value('current_date');

    &debug("The ticket number is $opt_num and the queue index is $queue_index\n");

    # First check to see if what we are setting we can do.  Don't want
    # to be able to set the "type" header.
    if ($opt_set eq 'type') {
	$locked_files->unlock_all;
	return 19;
    }

    # Only do the action if the caller can do actions on this ticket
    if (&check_caller($caller, $rust_info->get_value('owner'), $queue_index)) {

	$stdout_message = &check_for_change($opt_append, $opt_value, $rust_info)
	    if $type;
	$stdout_message = &check_for_change($opt_set, $opt_value, $rust_info)
	    if !$type;
	
	# If stdout_message is null, then there are no changes to do.
	if(!($stdout_message)) {

	    @messages = &prep_actions(1, $caller, $opt_append, $opt_value, $rust_info)
		if $type;
	    @messages = &prep_actions(0, $caller, $opt_set, $opt_value, $rust_info)
		if !$type;

	    push(@action_list, $messages[0]);
	    $log_message = $messages[1];
	    $stdout_message = $messages[2];

	    if ($type) {
		$original = $rust_info->get_value($opt_append);
		$new_value = join(',', $original, $opt_value);
		$rust_info->set_value($opt_append, $new_value);
	    } else {
		$rust_info->set_value($opt_set, $opt_value);
	    }

	    $rust_info->set_value('updated',$date);
	    
	    $error = &write_to_rust_log(1, $rust_info, @action_list);
	    if ($error) {
		$locked_files->unlock_all;
		&ddebug("Leaving Rust_Action::do_action\n");
		return $error;
	    }

	    $error = &write_log(1, $log_message,$queue_index);
	    if ($error) {
		$locked_files->unlock_all;
		&ddebug("Leaving Rust_Action::do_action\n");
		return $error;
	    }

	    $error = &resolve_ticket($caller, $rust_info)
		if (($opt_set eq 'status') && ($opt_value eq 'resolved'));
	    if ($error) {
		$locked_files->unlock_all;
		&ddebug("Leaving Rust_Action::do_action\n");
		return $error;
	    }

	    if (($opt_set eq 'status') && ((!(($opt_value eq 'resolved') ||
					      ($opt_value eq 'archived'))) &&
					   (($rust_info->get_value('type') eq 'resolved') ||
					    ($rust_info->get_value('type') eq 'archived')))) {

		@errors = &reopen_ticket($caller, $opt_value, $rust_info);
		if (@errors[0]) {
		    $locked_files->unlock_all;
		    &ddebug("Leaving Rust_Action::do_action\n");
		    return $errors[0];
		}
		
		# Need to do this because $rust_info changed in reopen_ticket
		$rust_info = $errors[1];
	    }
	    
	    $rust_info->write_headers;
    	}
	
    } else {
	$locked_files->unlock_all;
	&ddebug("Leaving Rust_Action::do_action\n");
	return 1;
    }
    
    $locked_files->unlock_all;
    &ddebug("Leaving Rust_Action::do_action\n");
    return (0, $stdout_message);
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub do_create {
    #
    # Type: Can be called by external scripts
    #
    # Abstract: Create a new ticket from either the command-line
    #           or from stdin.
    #
    #           $headers is a string, passed in if $type = 1, of
    #                    the following format:
    #
    #                   user:owner:subject:priority

    &ddebug("Entering Rust_Action::do_create\n");

    my ($caller, $type, $queue_index, $headers, $rust_info, @ticket_log) = @_;

    my ($user, $owner, $subject, $prio, $substring, $log_message,
	$tag, $new_number, $error, @errors);
    my ($locked_files) = new Lock();
    my ($rfc_date) = &rfc_822_date;
    my ($date) = &main::ctime($^T);  
    $date .= " (" . time() . ")";
    chomp $date;

    &debug("Going to create a new ticket\n");

    # Only do the action if the caller is supposed to be doing this
    # Type=1 when called from the modules in Rust_Mail.pm (ie mailer daemon)

    if (($type == 1) || (&check_caller($caller, "", $queue_index))) { 

	$tag = &extract_element('tag',$queue_index);

	if (!($type)) {
	    @ticket_log = ();
	    &debug("Creating ticket via user input\n");
	    $user = &read_user_input(1, 'User', ());
	    $owner = &read_user_input(1, 'Owner', ());
	    $subject = &read_user_input(1, 'Subject', ());
	    $prio = &read_user_input(1, 'Priority', 'normal');
	    push(@ticket_log, "From $user  $rfc_date\n",
		 "Subject: $subject\n\n");
	    push(@ticket_log, &read_user_input(0, 'Enter message (End with ^D):', ())) ;
	} else {
	    &debug("Creating ticket non interactively\n");
	    ($user, $owner, $subject, $prio) = split(':-:-:',$headers);
	}
	

	if (!($rust_info)) {
	    @errors = &create_ticket_headers($caller, $locked_files, $queue_index);

	    if ($errors[0]) {
		$locked_files->unlock_all;
		return $errors[0];
	    }
	    
	    $new_number = $errors[1];
	    $rust_info = $errors[2];

	} else { $new_number = $rust_info->get_value('number'); }

	&debug("The new number for the created ticket is $new_number\n");

	$date = $rust_info->get_value('current_date');
	$substring = sprintf($tag, $new_number);
	
	&debug("The substring for the ticket is $substring\n");

	$rust_info->set_value('user', $user);
	$rust_info->set_value('number', $new_number);
	$rust_info->set_value('subject',$subject);
	$rust_info->set_value('substring',$substring);
	$rust_info->set_value('priority', $prio);
	$rust_info->set_value('owner', $owner);
	$rust_info->set_value('type','active');
    
	# Now check to see if this is a user ticket or if it should be made
	# into a "staff" (ie work) ticket

	if (&check_for_staff($user, $queue_index)) {
	    &debug("User $user is creating ticket, making it a work ticket\n");
	    $rust_info->set_value('category','work');
	} else {
	    &debug("User $user is creating ticket, making it a user ticket\n");
	    $rust_info->set_value('category','user');
	}

	$log_message = "$new_number : ticket created by $user. $date\n";
	$stdout_message = "Ticket $new_number has been created\n";
	
	$error = &write_to_rust_log(0, $rust_info, @ticket_log);
	if ($error) {
	    $locked_files->unlock_all;
	    &ddebug("Leaving Rust_Action::do_create\n");
	    return $error;
	}
	
	$error = &write_log(2, $log_message,$queue_index)
	    if ($type);
	$error = &write_log(1, $log_message,$queue_index)
	    if (!$type);
	if ($error) {
	    $locked_files->unlock_all;
	    &ddebug("Leaving Rust_Action::do_create\n");
	    return $error;
	}

	# If $type = 1 (called from the process_mail functions, don't
	# write out the headers because we will do it later.
	$rust_info->write_headers if ($type != 1);
    
    } else {
	$locked_files->unlock_all;
	&ddebug("Leaving Rust_Action::do_create\n");
	return 1;
    }

    &ddebug("Leaving Rust_Action::do_create\n");

    # Don't unlock the header file if we were called from the
    # process_mail functions.  It will be unlocked later.
    $locked_files->unlock_all if ($type != 1);

    return (0, $stdout_message);
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub do_comment {
    #
    # Type: Can be called by external scripts
    #
    # Abstract: Add comments to a ticket logfile.
    #
    #           $type = 0 for interactive commenting
    #                   @contents is ignored in this case
    #
    #           $type = 1 for non-interacitve commenting
    #                   @contents contains the text to add
    
    &ddebug("Entering Rust_Action::do_comment\n");

    my ($caller, $type, $opt_num, $queue_index, @contents) = @_;

    my (@action_list, $log_message, $stdout_message, @ticket_log,
	$rust_info, $error, @errorsm, $date);
    my $locked_files = new Lock;

    &debug("Addint comments to a ticket\n");

    if (&check_caller($caller, (), $queue_index)) {

	@errors = &read_file($caller, $locked_files, "", $opt_num, $queue_index);
	if ($errors[0]) {
	    $locked_files->unlock_all;
	    return $errors[0];
	}

	$rust_info = $errors[1];
	$queue_index = $rust_info->get_value('queue_index');
	$opt_num = $rust_info->get_value('number');
	$date = $rust_info->get_value('current_date');

	&debug("The ticket number being commented is $opt_num\n");

	@action_list = ("Comments added by $caller\n");
	$log_message = "$opt_num : comments added by $caller. $date\n";
	$stdout_message = "Comments added\n";
	
	if ($type == 1) {
	    foreach $each (@contents) {
		push(@ticket_log, $each, "\n");
	    }
	} else {
	    # This call requires interaction by a human
	    push(@ticket_log, &read_user_input(0, 'Enter comments (End with ^D):', ()));
	}

	push(@ticket_log, "\n",$action_list[0], "\n");

	$error = &write_to_rust_log(3, $rust_info, @ticket_log);
	
	if ($error) {
	    $locked_files->unlock_all;
	    &ddebug("Leaving Rust_Action::do_comment\n");
	    return $error;
	}

	$error = &write_log(1,$log_message,$queue_index);
	if ($error) {
	    $locked_files->unlock_all;
	    &ddebug("Leaving Rust_Action::do_comment\n");
	    return $error;
	}

	$rust_info->set_value('updated',$date);
	$rust_info->write_headers;

    } else {
	$locked_files->unlock_all;
    &ddebug("Leaving Rust_Action::do_comment\n");
	return 1;
    }

    &ddebug("Leaving Rust_Action::do_comment\n");
    $locked_files->unlock_all;
    return (0, $stdout_message);
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub do_assign {
    #
    # Type: Can be called by external scripts
    #
    # Abstract: Assign the specified ticket to the specified
    #           user.  If it is assigned to a RUST queue, then
    #           the ticket is moved to that queue.
    #
    #           $type = 2 means the caller already owns it
    #           $type = 3 means we need to move it to another queue
    #           $type = 1 means that we can go for it

    &ddebug("Entering Rust_Action::do_assign\n");

    my ($caller, $opt_num, $opt_assign, $queue_index) = @_;

    my ($type, $rust_info, $error, @errors, $date);
    my ($locked_files) = new Lock;

    @errors = &read_file($caller, $locked_files, "", $opt_num, $queue_index);
    if ($errors[0]) {
	$locked_files->unlock_all;
	return $errors[0];
    }
    
    $rust_info = $errors[1];
    $queue_index = $rust_info->get_value('queue_index');
    $opt_num = (split(',',$rust_info->get_value('number')))[0];
    $date = $rust_info->get_value('current_date');

    if (&check_caller($caller, $rust_info->get_value('owner'), $queue_index)) {

	&debug("Assigning ticket number $opt_num to $opt_assign\n");

	$type = &prep_assign($opt_assign, $rust_info);

	  if (($type == 0) || ($type == 2)) {
	    $locked_files->unlock_all;
	    &ddebug("Leaving Rust_Action::do_assign\n");
	    return 12 if $type == 0;
	    return 7 if $type == 2;

	} elsif ($type == 3) {
	    $stdout_message = "Ticket $opt_num has been assigned to $opt_assign\n";
	    $error = &do_move($caller, $locked_files, $opt_assign, $rust_info);

	    $locked_files->unlock_all;
	    &ddebug("Leaving Rust_Action::do_assign\n");
	    return ($error, $stdout_message);

	} elsif ($type == 1) {
	    if ($opt_assign eq "none") {
	        &debug("Unassigning the ticket\n");
		@action_list = ("Unassigned by $caller\n");
		$log_message = "$opt_num : unassigned by $caller. $date\n";
		$rust_info->set_value('owner','');
	    } else {
	        &debug("Assigning the ticket to a person\n");
		@action_list = ("Assigned to $opt_assign by $caller\n");
		$log_message = "$opt_num : assigned to $opt_assign by $caller. $date\n";
		$stdout_message = "$opt_num has been assigned to $opt_assign\n";
		$rust_info->set_value('owner', $opt_assign);
	    }

	    $rust_info->set_value('updated',$date);
	    	    
	    $error = &write_to_rust_log(1, $rust_info, @action_list);
	    if ($error) {
		$locked_files->unlock_all;
		&ddebug("Leaving Rust_Action::do_assign\n");
		return $error;
	    }

	    $error = &write_log(1,$log_message,$queue_index);
	    if ($error) {
		$locked_files->unlock_all;
		&ddebug("Leaving Rust_Action::do_assign\n");
		return $error;
	    }

	    $rust_info->set_value('updated', $date);
	    $rust_info->write_headers;
	      
	    if (!(&extract_element("send_to_humans",$queue_index)) &&
	      &extract_element('send_on_assign',$queue_index)) {
	      $error = Rust_Mail::send_logfile($caller, $opt_assign, $opt_num, $queue_index);
	    }

	} elsif ($type == 4) {
	    $locked_files->unlock_all;
	    return 20;
	}
    } else {
	
	$locked_files->unlock_all;
	&ddebug("Leaving Rust_Action::do_assign\n");
	return 1;
    }

    &ddebug("Leaving Rust_Action::do_assign\n");
    $locked_files->unlock_all;
    return (0, $stdout_message);
}    

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub do_move {
    #
    # Type: Not to be called by external scripts
    #
    # Abstract: Move a ticket to a different RUST queue.  This will
    #           also resolve it out of the current queue.
    #
    #           By the time we get to here, we already know that
    #           it goes to a RUST queue.  So just need to find
    #           out which one.  Not necessary to verify that it
    #           goes to a RUST queue.

    &ddebug("Entering Rust_Action::do_move\n");

    my ($caller, $locked_files, $opt_assign, $rust_info) = @_;

    my ($tag, $new_number, @action_list, $log_message, $stdout_message,
	$substring, @results, $error);
    my ($count, $new_index) = (-1, -1);
    my ($opt_num) = (split(',',$rust_info->get_value('number')))[0];
    my ($queue_index) = $rust_info->get_value('queue_index');
    my ($date) = $rust_info->get_value('current_date');
    my ($creation_date) = $rust_info->get_value('created');
    my ($original_owner) = $rust_info->get_value('owner');

    &debug("Doing the actual move of ticket $opt_num to another RUST queue\n");

    foreach $each (split(':',$globals->{'alias_name'})) {
	$count++;
	$new_index = $count if ($each eq $opt_assign);
    }
   
    $tag = &extract_element('tag',$new_index);
    @results = &create_ticket_headers($caller, $locked_files, $new_index);
    $error = $results[0];

    if (!$error) {

	$new_number = $results[1];
	$new_rust_info = $results[2];
	$new_rust_info->set_value('number',$new_number);
	
	&debug("The index for the new queue is $new_index\n");
    	&debug("The new ticket number for the ticket is $new_number\n");

	# Set the headers in the new ticket
	&prep_move($rust_info, $new_rust_info);
	
	$substring = sprintf($tag, $new_number);	
	$new_rust_info->set_value('substring',$substring);
	
	@action_list = ("Ticket assigned to $opt_assign queue by $caller\n");
	$log_message = "$opt_num : ticket moved to the $opt_assign queue by $caller. $date\n";
	
	$error = &write_to_rust_log(1, $rust_info, @action_list);
	if ($error) {
	    &ddebug("Leaving Rust_Action::do_move\n");
	    return $error;
	}
	    
	$error = &write_log(1,$log_message,$queue_index);
	if ($error) {
	    &ddebug("Leaving Rust_Action::do_move\n");
	    return $error;
	}
	   
	$rust_info->set_value('updated',$date);

	$log_message = "$new_number : ticket created by $caller. $date\n";
	&move_logfile($rust_info, $new_rust_info);
	if ($error) {
	    &ddebug("Leaving Rust_Action::do_move\n");
	    return $error;
	}

	$error = &write_log(1,$log_message,$new_index);
	if ($error) {
	    &ddebug("Leaving Rust_Action::do_move\n");
	    return $error;
	}
	
	$new_rust_info->set_value('created',$creation_date);

	# Retain ownership if owner in old queue is in new queue
	if (&check_caller($original_owner, "", $new_index)) {
	    $new_rust_info->set_value('owner',$original_owner);
	}

	$rust_info->write_headers;
	$new_rust_info->write_headers;
	
    } else {
	return $error;
    }

    &ddebug("Leaving Rust_Action::do_move\n");
    return 0;			
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub do_merge {
    #
    # Type: Can be called by external scripts
    #
    # Abstract: Merge one ticket into another ticket.  The
    #           tickets need to both be in the same queue to
    #           do this.  If one or both of the tickets are
    #           resolved, then they are re-opened first and then
    #           the merge takes place.

    &ddebug("Entering Rust_Action::do_merge\n");

    my ($caller, $opt_num1, $opt_num2, $queue_index) = @_;

    my ($stdout_message, $log_message) = ((),());
    my ($rust_info, $merge_info, $error);
    my ($locked_files) = new Lock;

    &debug("Going to merge a ticket into another ticket\n");

    # Read in the file to be merged.  This creates $rust_in
    @errors = &read_file($caller, $locked_files, "", $opt_num1, $queue_index);
    if ($errors[0]) {
	$locked_files->unlock_all;
	return $errors[0];
    }
    
    $rust_info = $errors[1];
    $queue_index = $rust_info->get_value('queue_index');
    $opt_num1 = $rust_info->get_value('number');

    # Read in the file that we are merging into.  This creates $merge_info

    @errors = &read_file($caller, $locked_files, "", $opt_num2, $queue_index);
    if ($errors[0]) {
	$locked_files->unlock_all;
	return $errors[0];
    }
    
    $merge_info = $errors[1];
    $queue_index = $rust_info->get_value('queue_index');
    $opt_num2 = $rust_info->get_value('number');

    # Only do the merge if the caller is authroized to do it.
    if (&check_caller($caller, $rust_info->get_value('owner'), $queue_index)) {

	&debug("Going to merge ticket $opt_num1 into ticket $opt_num2\n");

	@messages = &prep_merge($caller, $rust_info, $merge_info);
	if ($messages[0] == 18) {
	    $locked_files->unlock_all;
	    return $messages[0];
	}

	push(@action_list, $messages[1]);
	$log_message = $messages[2];
	$stdout_message = $messages[3];

	# Now merge the logfiles together
	$error = merge_logfiles($caller, $rust_info, $merge_info);
	if ($error) {
	    $locked_files->unlock_all;
	    return $error;
	}

	# Write out the actions to the receiving ticket log
	$error = &write_to_rust_log(1, $merge_info, @action_list);
	if ($error) {
	    $locked_files->unlock_all;
	    return $error;
	}

	$error = &write_log(1,$log_message,$queue_index);
	if ($error) {
	    $locked_files->unlock_all;
	    return $error;
	}
	
	$rust_info->write_headers;
	$merge_info->write_headers;

    } else {
	&ddebug("Leaving Rust_Action::do_merge\n");	
	$locked_files->unlock_all;
	return 1;
    }

    &ddebug("Leaving Rust_Action::do_merge\n");	
    $locked_files->unlock_all;
    return (0, $stdout_message);
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub do_number {
    #
    # Type: Can be called by external scripts
    # 
    # Abstract: Do a specified action on a RUST queue number
    #           file.
    #
    
    &ddebug("Entering Rust_Action::do_number\n");

    my ($caller, $opt_curnum, $opt_value, $queue_index) = @_;

    &ddebug("Leaving Rust_Action::do_number\n");

    return &number_funcs($caller, $opt_curnum, $opt_value, $queue_index)
	if (&check_caller($caller, (), $queue_index));

    return 1;
} 

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub do_kill {
    #
    # Type: Can be called by external scripts
    #
    # Abstract: Kill a ticket.  This *permanently* deletes
    #           the specified ticket.
    #

    &ddebug("Entering Rust_Action::do_kill\n");

    my ($caller, $opt_num, $queue_index) = @_;

    my ($rust_type, $ticket_filename, $date);
    my ($locked_files) = new Lock;
    my ($alias_name) = &extract_element('alias_name', $queue_index);

    &debug("Going to kill a ticket\n");

    # Check caller authorization
    if (&check_caller($caller, (), $queue_index)) {
	
	# First check to make sure we have a real ticket
	@errors = &read_file($caller, $locked_files, "", $opt_num, $queue_index);
	if ($errors[0]) {
	    $locked_files->unlock_all;
	    return $errors[0];
	}
    
	$rust_info = $errors[1];
	$queue_index = $rust_info->get_value('queue_index');
	$opt_num = (split(',',$rust_info->get_value('number')))[0];
	$rust_type = $rust_info->get_value('type');
	$date = $rust_info->get_value('current_date');

	&debug("Killing ticket nubmer $opt_num\n");

	@errors = &get_ticket_filename($alias_name, $opt_num, $rust_type);
	return $errors[0] if ($errors[0]);

	$ticket_filename = $errors[1];
	$header_file = $rust_info->get_value('header_file');

	print "WARNING: This will *permanently* remove ticket $opt_num\n";

	# Confirm action first

	print "Are you sure you want to kill ticket $opt_num? (\"yes\" to confirm) ";
	$confirm = scalar(<STDIN>);
	chop $confirm;

	# Only do if the entire word "yes" was typed in
	if ($confirm eq "yes") {

	    $message = "$alias_name ticket $opt_num was killed by $caller. $date\n";
	    @error = &write_log(0, $message, $queue_index);
	    if ($error) {
		$locked_files->unlock_all;
		return $error;
	    }

	    $header_file = &untaint_obj($header_file);
	    $ticket_filename = &untaint_obj($ticket_filename);

	    unlink($header_file) if (-f $header_file);
	    unlink($ticket_filename) if (-f $ticket_filename);

	    print "Ticket $opt_num was killed\n";

	} else {
	    print "Ticket was not killed.  Aborting...\n";
	}

	$locked_files->unlock_all;

	&ddebug("Leaving Rust_Action::do_kill\n");
	return 0;

    } 

    $locked_files->unlock_all;

    &ddebug("Leaving Rust_Action::do_kill\n");
    return 1;
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub do_archive {
    #
    # Type: Can be called by external scripts
    #
    # Abstract: Move a range of tickets to the archived queue
    #
    
    my ($caller, $num_to_leave, $queue_index) = @_;

    my ($count) = 0;
    my ($path_to_resolved) = $globals->{'path_to_resolved'};
    my ($path_to_archived) = $globals->{'path_to_archived'};
    my (@file_list) = (sort {$a <=> $b} &get_ticket_list("resolved", $queue_index));
    my ($locked_files) = new Lock;
    my (@errors, $error, $rust_info, $date, $num_to_move);

    # Only archive if the caller can archive tickets
    if (&check_caller($caller, "", $queue_index)) {

	if ($#file_list <= $num_to_leave) {
	    return (33, "");
	}

	$num_to_move = $#file_list - $num_to_leave;

	while ($count < $num_to_move) {

	    # Read in the file.  This creates $rust_info
	    @errors = &read_file($caller, $locked_files, "", $file_list[$count], $queue_index);

	    if ($errors[0]) {
		$locked_files->unlock_all;
		return $errors[0];
	    }
	    
	    $rust_info = $errors[1];
	    $date = $rust_info->get_value('current_date');
	    print "Now archiving resolved ticket number $file_list[$count].\n";
	    &debug("Now archiving resolved ticket number $file_list[$count].\n");

	    $log_message = "$file_list[$count] : archived by $caller.  $date\n";
	    $rust_info->set_value('updated',$date);
	    
	    $error = &write_log(1, $log_message,$queue_index);
	    if ($error) {
		$locked_files->unlock_all;
		&ddebug("Leaving Rust_Action::do_archive\n");
		return $error;
	    }

	    $error = &archive_ticket($caller, $rust_info);
	    if ($error) {
		$locked_files->unlock_all;
		&ddebug("Leaving Rust_Action::do_archive\n");
		return $error;
	    }
	    
	    $rust_info->write_headers;

	    $locked_files->unlock_all;

	    $count++;
    	}
	
    } else {
	$locked_files->unlock_all;
	&ddebug("Leaving Rust_Action::do_archive\n");
	return 1;
    }
    
    &ddebug("Leaving Rust_Action::do_archive\n");
    return (0, "Tickets were successfully archived.\n");
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub add_time {
    #
    # Type: Can be called by external scripts
    #
    # Abstract: Add time to the "work_hours" RUST header of the
    #           specified ticket.
    #

    &ddebug("Entering Rust_Action::add_time\n");

    my ($caller, $opt_add_time, $opt_num, $queue_index) = @_;

    my ($rust_info, @hour_min, $minutes, $date, $ticket_work_hours,
	@ticket_hour_min);
    my ($locked_files) = new Lock; 

    &debug("Going to add time to the work_hours RUST header for ticket\n");

    # Check the time format.  It needs to be HH:MM
    @hour_min = split(':',$opt_add_time);

    &debug("The hours entered are $hour_min[0]\n",
		      "The minutes entered are $hour_min[1]\n");

    if ((int($hour_min[0]) && int($hour_min[1])) ||
	(($hour_min[0] >= 0) && ($hour_min[1] >= 0))) {
	$minutes = $hour_min[1]/60;
	if ($minutes >= 1) {
	    $hour_min[0]+= int($minutes);
	    $hour_min[1] = ((int($minutes)*60) - $hour_min[1]);
	}
    } else {
	return(22, "The time must be HH:MM, where HH are hours and MM are minutes (numeric)\n");
    }

    # Read in the file.  This creates $rust_info
    @errors = read_file($caller, $locked_files, "", $opt_num, $queue_index);

    if ($errors[0]) {
	$locked_files->unlock_all;
	return $errors[0];
    }

    $rust_info = $errors[1];
    $opt_num = $rust_info->get_value('number');
    $queue_index = $rust_info->get_value('queue_index');
    $ticket_work_hours = $rust_info->get_value('work_hours');
    $date = $rust_info->get_value('current_date');

    # The below situation should *never* happen.  But if it does, then
    # take care of it.
    if (($ticket_work_hours eq 'unknown') || ($ticket_work_hours == 0)) {
	$ticket_work_hours = '0:0';
    }

    &debug("The ticket number is $opt_num and the queue index is $queue_index\n");

    # Only do the action if the caller can do actions on this ticket
    if (&check_caller($caller, $rust_info->get_value('owner'), $queue_index)) {

	# Check the time format.  It needs to be HH:MM
	@ticket_hour_min = split(':',$ticket_work_hours);

	$ticket_hour_min[0]+=$hour_min[0];
	$ticket_hour_min[1]+=$hour_min[1];

	$minutes = $ticket_hour_min[1]/60;
	if ($minutes >= 1) {
	    $ticket_hour_min[0]+= int($minutes);
	    $ticket_hour_min[1] = ((int($minutes)*60) - $ticket_hour_min[1]);
	}
	
        &debug("The new hours for the ticket are $ticket_hour_min[0]\n",
			"The new minutes for the ticket are $ticket_hour_min[1]\n");

	$rust_info->set_value('work_hours', join(':',$ticket_hour_min[0],
						 $ticket_hour_min[1]));
	$rust_info->write_headers;

    } else {
	$locked_files->unlock_all;
        &ddebug("Leaving Rust_Action::add_time\n");
	return 1;
    }

    $locked_files->unlock_all;
    &ddebug("Leaving Rust_Action::add_time\n");
    return (0, "The time of $opt_add_time (HH:MM) was added to ticket \# $opt_num\n");
}

1;

}
# EOF
