# -*- Perl -*-
#
# Copyright (C) 1996 Craig Ruefenacht <ruefenac@cs.utah.edu> and the
#                    University of Utah Computer Science Department
#
# $Id: Rust_Lib.pm,v 1.41 1997/05/21 15:13:08 ruefenac Exp $
#
# This is part of RUST-1.0 Beta 6
#
# Abstract: This library contains miscallaneous library 
#           functions used for Rust.  These include functions
#           that manipulate number files, and logging functions
#
# 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_Lib;

use Rust_Configs;
use Rust_Debug;
use Rust_Data;

require Exporter;
@ISA = qw(Exporter);
@EXPORT =
    (
     # name                       brief description
     # --------------------------------------------------------     
     'init_lib',		# Initialize this library
     'rfc_822_date',		# Generate a RFC822 Date
     'untaint_obj',		# Untaint given paremeter
     'set_queue_index',		# Figure out command-line queue
     'check_caller',		# Check authorization of caller
     'get_caller_queues',	# Return a list of queues the caller is on
     'check_for_staff',		# Check to see if a staff member made ticket
     'get_real_name',		# Returns the real name of a given username
     'read_user_input',		# Read input from the keyboard
     'number_funcs',		# top-level number function
     'get_ticket_number',	# used by number_funcs
     'inc_ticket_number',	# used by number_funcs
     'check_for_change',	# See if action will change ticket
     'prep_actions',		# Prep actions (ie find what to do)
     'prep_assign',		# Prep assignment
     'prep_move',		# Prep moving a ticket
     'prep_merge',		# Prep merge (figure out header info)
     'get_ticket_filename',	# Get the name of the ticket logfile
     'get_ticket_list',		# Get a directory listing of a queue type
     'move_logfile',		# Copy a ticket logfile to another queue
     'read_file',		# Read a ticket header file
     'write_to_rust_log',	# Write actions out to ticket logfile
     'read_rust_log',		# Read a ticket logfile
     'write_log',		# Write out to a log file
     'existing_ticket',		# Check to see if parameters point to an existing ticket
     'merge_logfiles',		# Merge two logfiles together
     'reopen_ticket',		# Reopen a ticket
     'resolve_ticket',		# Resolve a ticket
     'archive_ticket',		# Resolve a ticket
     'create_ticket_headers'	# Create a new ticket with initialized headers
     );

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub init_lib {
    #
    # Type: Can be called by external scripts
    #
    # Abstract: Initialize this library by adding the $global
    #           hash to this object.
    
    $globals = &Rust_Configs::get_globals;

}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
# Miscallaneous Rust_Lib functions
#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub rfc_822_date {
    #
    # Type: Can be called by external scripts
    #
    # Abstract: Generate a RFC822 compliant date string
    #

    &ddebug("Entering Rust_Lib::rfc_822_date\n");

    my ($rfc_822_date, @date_parts);
    my ($date) = &main::ctime($^T);  
    chomp $date;
    my $is_dst = (localtime())[8];
 
    # Now to ensure that the date will be RFC822 compliant :-)
    @date_parts = split(' ',$date);
 
    $rfc_822_date = join(' ',"$date_parts[0],",$date_parts[2],$date_parts[1],
			 $date_parts[$#date_parts], $date_parts[3],
			 $is_dst ? "-0500 (CDT)" : " -0600 (CST)");
 
    #if ($#date_parts > 4) {
    #    $rfc_822_date = join(' ',$rfc_822_date,$date_parts[4]);
    #}

    &debug("The RFC822 date string is $rfc_822_date\n");
    &ddebug("Leaving Rust_Lib::rfc_822_date\n");
 
    return $rfc_822_date;
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub untaint_obj {
    #
    # Type: Can be called by external scripts
    #
    # Abstract: Untaint whatever is passed in.  This is to
    #           accomidate setgid scripts.  I didn't want to
    #           have to do this, but....
    #
    #           Secure scripts is a good thing, but there are
    #           times when you have to work around it.
    
    my ($tainted_obj) = @_;

    $tainted_obj =~ /^(.*)$/;

    return $1;
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub set_queue_index {
    #
    # Type: Can be called by external scripts
    #
    # Abstract: Determine what queue to use for this script.
    #           First, the RUST_QUEUE envirornment variable is
    #           checked, then the command line args are checked.
    #
    # The GetOption arg parser is now used to find the queue on the
    # command line.

    &ddebug("Entering Rust_Lib::set_queue_index\n");

    my ($type, $queue) = @_;

    my (@queue_array) = ();
    my ($count, $queue_index, $found) = (-1, -1, 0);

    &debug("Trying to determine which queue to work with\n");
	&debug("type is $type and queue is $queue\n");

    # Check the RUST_QUEUE enviornment variable first...

    if ($type) {
	while ($count < ($globals->{'number_of_queues'} - 1)) {
	    for $each_queue (split(':',$globals->{'alias_name'})) {
		$count++;
		&debug("count is $count and each_queue is $each_queue\n");
		foreach $each_env (split(',',$ENV{RUST_QUEUE})) {
			&debug("count is $count and each_env is $each_env\n");
		    if ($each_env eq $each_queue) {
			push(@queue_array, $count);
			$found = 1;
		    }
		}
	    }
	}
    }

    &debug("Found $queue_index for RUST queue in RUST_QUEUE env var\n")
	if ($found);

    # Now check the command line args.  If the user specified
    # a queue on the command line, it overrides the RUST_QUEUE
    # enviornment variable.  It expects the queue name (if
    # any) to be the first command-line argument.

    $count = -1;
    while ($count < ($globals->{'number_of_queues'} - 1)) {

	for $each_queue (split(':',$globals->{'alias_name'})) {
	    $count++;
		&debug("count is $count and each_queue is $each_queue\n");
	    foreach $each_clq (split(',',$queue)) {
		if ($each_clq eq $each_queue) {
			&debug("count is $count and each_clq is $each_clq\n");
		    if ($found == 1) {
			@queue_array = ();
		    }
		    push(@queue_array, $count);
		    $found = 2;
		}
	    }
	}
    }

    # If $queue_index is still -1, then no valid queue was found :-(
    
    &debug("Found $queue_index for RUST queue from command-line\n")
	if ($found == 2);

    if (!$found) { 
      &debug("Was unable to determine RUST queue.! Please check.\n");
	return -1; 
    }

    $queue_index = join(',',@queue_array);
    &debug("The queue index returning is $queue_index.\n");
    &ddebug("Leaving Rust_Lib::set_queue_index\n");
    return $queue_index;
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub check_caller {
    #
    # Type: Can be called by external scripts
    #
    # Abstract: Check the permissions of the caller to make sure
    #           the can do the action they asked for
    #
    #           The caller can do a given action if these
    #           conditions are met:
    #
    #              * The caller is the owner of the ticket
    #                             or
    #              * The caller is in the authorization file
    #

    &ddebug("Entering Rust_Lib::check_caller\n");

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

    # EVIL!!! If there's an @ sign, we must check that "cs.wisc.edu"
    # is after it, or else we don't want to grant permisson.  This 
    # prevents "wright@cupcake.maf.wisc.edu" from being considered to
    # have valid permissions.
    my( $domain );
    if( $caller =~ /.*@(.*)/ ) {
	$domain = $1;
	if( !($domain =~ /.*cs\.wisc\.edu.*/)  && 
	 !($domain =~ /.*ncsa\.uiuc\.edu.*/) &&  
	 !($domain =~ /.*mcs\.anl\.gov.*/) )  {
	    # We can't be staff.
	    &ddebug("Email isn't from cs.wisc.edu, can't have permission\n");
	    &ddebug("Leaving Rust_Lib::check_caller\n");
	    return 0;
	}	    
    }

    my ($alias_name) = Rust_Configs::extract_element('alias_name', $queue_index);
    my ($path_to_auth) = $globals->{'path_to_auth'};
    $caller = (split('@',$caller))[0];
    my ($auth_file) = "$path_to_auth/$caller";
    my (@auth_info, $queues);  

    &debug("Checking to see if $caller is on $alias_name RUST queue\n");
    &debug("Using $auth_file for the authentication\n");
    $auth_file = &untaint_obj($auth_file);

    if (open(AUTH, "<$auth_file")) {

	$queues = 0;
        while (<AUTH>) {
            next if /^#/;
            next if /^\s*$/;
            chomp;

	    if ($_ eq "QUEUES") {
		&debug("Found the QUEUES section in $auth_file\n");
		$queues = 1;
	    }
	    if (($_ eq "END") && ($queues)) {
		last;
	    }

	    if ($queues) {
		
		if ($_ eq $alias_name) {
		    &debug("User is on $alias_name RUST queue\n");
		    &ddebug("Leaving Rust_Lib::check_caller\n");
		    return 1;
		}
	    }
	}

	close(AUTH);
    }

    if ($owner eq $caller) {
	&debug("User is on $alias_name RUST queue via owner = caller\n");
	&ddebug("Leaving Rust_Lib::check_caller\n");
	return 1;
    }

    &debug("User is NOT on $alias_name RUST queue\n");
    &ddebug("Leaving Rust_Lib::check_caller\n");
    return 0;
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub get_caller_queues {
    #
    # Type: Can be called by external scripts
    #
    # Abstract: Return a list of queues the caller is on.

    &ddebug("Entering Rust_Lib::get_caller_queues\n");

    my ($caller) = @_;

    $caller = (split('@',$caller))[0];
    my ($path_to_auth) = $globals->{'path_to_auth'};
    my ($auth_file) = "$path_to_auth/$caller";
    my (@auth_info, $queues, @queue_list);  

    &debug("Getting a list of the queues $caller is on\n");
    &debug("Using $auth_file for the authentication\n");
    $auth_file = &untaint_obj($auth_file);

    if (open(AUTH, "<$auth_file")) {

	$queues = 0;
        while (<AUTH>) {
            next if /^#/;
            next if /^\s*$/;
            chomp;

	    if ($_ eq "QUEUES") {
		&debug("Found the QUEUES section in $auth_file\n");
		$queues = 1;
		next;
	    }
	    if (($_ eq "END") && ($queues)) {
		last;
	    }

	    if ($queues) {
		push(@queue_list, $_);
		next;
	    }
	}

	close(AUTH);
    }

    $return_list = join(',',@queue_list);
    &debug("$caller is on $return_list queues\n");
    &ddebug("Leaving Rust_Lib::get_caller_queues\n");
    return $return_list;
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub check_for_staff {
    #
    # Type: Can be called by external programs
    #
    # Abstract: This checks the person who is making a new
    #           ticket against the list of people who are
    #           on the RUST queue list.  If there is a
    #           match, then return 1 

    &ddebug("Entering Rust_Lib::check_for_staff\n");

    my ($user, $queue_index) = @_;

    # EVIL!!! If there's an @ sign, we must check that "cs.wisc.edu"
    # is after it, or else we don't have a staff person.  This
    # prevents "wright@cupcake.maf.wisc.edu" from being considered staff.
    my( $domain );
    if( $user =~ /.*@(.*)/ ) {
	$domain = $1;
	if( !($domain =~ /.*cs\.wisc\.edu.*/)  && 
	 !($domain =~ /.*ncsa\.uiuc\.edu.*/) &&  
	 !($domain =~ /.*mcs\.anl\.gov.*/) )  {
	    # We can't be staff.
	    &ddebug("Email isn't from cs.wisc.edu, can't be staff\n");
	    &ddebug("Leaving Rust_Lib::check_for_staff\n");
	    return 0;
	}	    
    }

    $user = (split('@',$user))[0];
    my ($alias_name) = Rust_Configs::extract_element('alias_name', $queue_index);
    my ($path_to_auth) = $globals->{'path_to_auth'};
    my ($auth_file) = "$path_to_auth/$user";
    my (@auth_info, $staff_list);  

    &debug("Using $auth_file to check staff status\n");

    $auth_file = &untaint_obj($auth_file);

    if (open(STAFF, "<$auth_file")) {

	$staff_list = 0;

        while (<STAFF>) {
            next if /^#/;
            next if /^\s*$/;
            chomp;

	    if ($_ eq "STAFF") {
		&debug("Found the STAFF section in $auth_file\n");
		$staff_list = 1;
	    }
	    if (($_ eq "END") && ($staff_list)) {
		last;
	    }

	    if ($staff_list) {
		
		if ($_ eq $alias_name) {
		    &debug("User is a staff member of $alias_name RUST queue\n");
		    &ddebug("Leaving Rust_Lib::check_for_staff\n");
		    return 1;
		}
	    }
	}
	
	close(STAFF);
    }

    &ddebug("Leaving Rust_Lib::check_for_staff\n");
    return 0;
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub get_real_name {
    #
    # Type: Can be called by external scripts
    #
    # Abstract: Given a username, this function will return
    #           the real name of the user, as it appears
    #           in the auth file for the specified queue.
    #
    #           This is to be used only to return real names
    #           of people who are on the specified RUST queue.

    &ddebug("Entering Rust_Lib::get_real_name\n");

    my ($queue_index, $username) = @_;

    $username = (split('@',$username))[0];
    my ($alias_name) = Rust_Configs::extract_element('alias_name', $queue_index);
    my ($path_to_auth) = $globals->{'path_to_auth'};
    my ($auth_file) = "$path_to_auth/$username";

    &debug("Reading $auth_file to get real name of $username\n");
    $auth_file = &untaint_obj($auth_file);

    if (open(AUTH, "<$auth_file")) {
	
	while (<AUTH>) {
	    next if /^#/;
	    next if /^\s*$/;
	    chomp;
	    
	    if ($_ =~ /(REAL_NAME)/) {
		$user_realname = (split('=',$_))[1];
		&debug("The real name of $username is $user_realname\n");
		return $user_realname;
	    }
	}
	
	close(AUTH);
    }
    
    &debug("Could not get real name of $username.  Please check $auth_file\n");
    &ddebug("Leaving Rust_Lib::get_real_name\n");
    return 'unknown';
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub read_user_input {
    #
    # Type: Can be called by external scripts
    #
    # Abstract: This reads input from the caller.  This is used
    #           to get information that the caller types in from
    #           the keyboard.
 
    &ddebug("Entering Rust_Lib::read_user_input\n");
 
    my ($type, $prompt, $default) = @_;

    my ($one_line);
    my (@multiple_lines) = ();

    if ($type) {

	print $prompt;
	if ($default) {
	    print " [$default]";
	}
	print ": ";

	$one_line = scalar(<STDIN>);
	chop $one_line;

	if($one_line =~ /^\s*$/) {
	    $one_line = $default;
	}

	&ddebug("Leaving Rust_Lib::read_user_input\n");
	return $one_line;
    }
 
    print $prompt,"\n";

    while ($_ = <STDIN>) {
        push(@multiple_lines, $_);
    }

    &ddebug("Leaving Rust_Lib::read_user_input\n");
    return @multiple_lines;
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
# Rust_Lib functions that deal with the number files
#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub number_funcs {
    #
    # Type: Not to be called by external scripts
    #
    # Abstract: Do whatever is needed to the number file
    # for the specified queue.  The reason why I pass the
    # name of the queue into here ($queue) is so that I
    # can call this function with whatever queue I want,
    # not just the one specified by the $queue_index var.
    #
    # A special case was added where if $opt_curnum is set
    # to "create", then a new number file is created,
    # using the value in $opt_value for the queue name.

    &ddebug("Entering Rust_Lib::number_funcs\n");

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

    if (!($opt_curnum eq 'create')) {
	$alias_name = Rust_Configs::extract_element('alias_name',$queue_index);
    } else {
	$alias_name = $opt_value;
    }
    my ($pathname) = $globals->{'path_to_etc'};
    my ($file_name) = "$pathname/numbers/number\-$alias_name";

    &debug("Working with the current number files\n");

    # We only care about $num if this is a "set" action.
    # Otherwise we can overwrite its initial value.

    if ($opt_curnum eq 'show') {
	#
	# If there is an error in reading the current ticket
	# number, just print error to screen.  The "show"
	# action will only be run by a human.

	&debug("User requested to see current number, so showing it\n");
	&ddebug("Leaving Rust_Lib::number_funcs\n");
	return &get_ticket_number($file_name);

    } else {
	&ddebug("Leaving Rust_Lib::number_funcs\n");
	return &inc_ticket_number($caller, $opt_curnum, $file_name, $alias_name, $opt_value);
    }
}    

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub get_ticket_number {
    #
    # Type: Not to be called by external scripts
    #
    # Abstract: Read the number file and get the current
    #           ticket number for the given queue.
    #           The ticket number file is autogenerated
    #           with the inc_ticket_number function
    #           below this one.

    &ddebug("Entering Rust_Lib::get_ticket_number\n");

    my ($file_name) = @_;

    $file_name = &untaint_obj($file_name);

    if (open(NEWNUM, "<$file_name")) {

	while (<NEWNUM>) {
	    next if /^#/;
	    chomp;
	    $input = $_;
	}
	close(NEWNUM);

	&ddebug("Leaving Rust_Lib::get_ticket_number\n");
        &debug("Read $input as the current ticket number\n");
        return (0, $input);

    } else {
        &debug("Had problems reading the number file $file_name!\n");
	&ddebug("Leaving Rust_Lib::get_ticket_number\n");
        return -1;
    }
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub inc_ticket_number {
    #
    # Type: Not to be called by external scripts
    #
    # Abstract: Increment the ticket number by one and write
    #           out the ticket number file.  This writes over
    #           the previous ticket number file.  
    #
    #           Need to be careful here, because if it can't
    #           write out a new ticket number file, then there
    #           may be some serious problems with the rust system.
    #           The most common one will probably be a full
    #           filesystem.  If there is a serious error like
    #           this, then we need to send mail to some human
    #           being to let them know.
    #
    #           If we are creating a new file, then $opt_curnum
    #           will be set to "create" and $opt_value will contain
    #           the name of the queue we are creating for.  The $opt_value
    #           variable can be ignored in this case, because $alias_name
    #           contains the same thing.
    
    &ddebug("Entering Rust_Lib::inc_ticket_number\n");

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

    my ($locked_file);
    my ($hostname)  = $globals->{'hostname'};
    my ($number) = $opt_value;

    # Need to untaint the file.
    $file_name = Rust_Lib::untaint_obj($file_name);

    # Increment the number count if we are doing a increment.

    if ($opt_curnum eq 'inc') {
        &debug("Incrementing the current number file\n");
	$number = (&get_ticket_number($file_name) + 1);
	return 11 if ($number == 0);
    } 

    # Now we have to lock the file that holds the number.  This is because
    # there may be multiple instances of this script running.
    # This uses the Lock.pm module.

    $locked_file = new Lock();

    # First lock the file.  If the file is locked, then
    # retry to lock the file a few times.  If file locking
    # failes, then exit and do nothing. This shoule ALWAYS lock
    # the file.  If it has problems, then we need to send mail
    # to the rust maintainer and let them know there is a potential
    # problem.
    #
    # This will try to lock the file 20 times, at 5 second
    # intervals.  If it can't lock the file by then, then there may
    # be something wrong.  If it can't lock the file, then the
    # entire pipe process is killed, the mail that is being
    # processed is sent to the dist mailing list directly, and
    # the rust maintainer is notified.


    &debug("Trying to lock $file_name\n");
    $locked = $locked_file->add($file_name,$hostname,$caller, 20); 
    
    # Now update the file if it was locked successfully

    if (($locked) && ($locked != 28)) {

        &debug("$file_name is now locked\n");

	$file_name = &untaint_obj($file_name);

	open(OUT, ">$file_name") || ($locked_file->unlock_all && return -1);

	print OUT "# AUTO-GENERATED FILE -- DO NOT EDIT!\n";
	print OUT "#\n";
	print OUT "# The next number to use for a new ticket in the $alias_name\n";
	print OUT "# RUST queue is one more than the one below.\n";
	print OUT "#\n";
	
	$number = 0 if ($opt_curnum eq 'create');

	print OUT "$number\n";
	close(OUT);

	$locked_file->unlock_all;

	&debug("Successfully updated the current number file\n");
	&ddebug("Leaving Rust_Lib::inc_ticket_number\n");
	return (0, $number);

    } else { 
	&debug("Didn't have success updating the current number file\n");
	&ddebug("Leaving Rust_Lib::inc_ticket_number\n");
	return 9;
    }
}    

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
# Rust_Lib functions which are helper action functions 
#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub check_for_change {
    #
    # Type: Not to be called by external scripts
    #
    # Abstract: Check to make sure the new header info is different
    #           than what it is currently
    #

    &ddebug("Entering Rust_Lib::check_for_change\n");

    my ($opt_set, $opt_value, $rust_info) = @_;

    my ($num)= $rust_info->get_value('number');

    &ddebug("Leaving Rust_Lib::check_for_change\n");
    if (($opt_set eq "user") && ($rust_info->get_value("user") eq $opt_value)) {
	&debug("The user header already has $opt_value\n");
	return "The user for ticket $num is already $opt_value.\n";
    } elsif(($opt_set eq "status") && ($rust_info->get_value("status") eq $opt_value)) {
	&debug("The status header is already set to $opt_value\n");
	return "The status for ticket $num is already $opt_value.\n"
    } elsif(($opt_set eq "priority") && ($rust_info->get_value("priority") eq $opt_value)) {
	&debug("The priority header is already set to $opt_value\n");
	return "The priority for ticket $num is already $opt_value.\n";
    } elsif(($opt_set eq "subject") && ($rust_info->get_value("subject") eq $opt_value)) {
	&debug("The subject header is already set to $opt_value\n");
	return "The subject for ticket $num is already $opt_value.\n";
    } elsif(($opt_set eq "eta") && ($rust_info->get_value("eta") eq $opt_value)) {
	&debug("The eta header is already set to $opt_value\n");
	return "The eta for ticket $num is already $opt_value.\n";
    } elsif(($opt_set eq "category") && ($rust_info->get_value("category") eq $opt_value)) {
	&debug("The category is already set to $opt_value\n");
	return "The category for ticket $num is already $opt_value.\n";
    } elsif(($opt_set eq "accounting") && ($rust_info->get_value("accounting") eq $opt_value)) {
	&debug("The accounting header is already set to $opt_value\n");
	return "The accounting header for ticket $num is already $opt_value.\n";
    } elsif(($opt_set eq "access") && ($rust_info->get_value("access") eq $opt_value)) {
	&debug("The access flag is already set to $opt_value\n");
	return "The access flag for ticket $num is already $opt_value.\n";
    } else {
	return ();
    }
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub prep_actions {
    #
    # Type: Not to be called by external scripts
    #
    # Abstract: Prepare some stuff before the header file
    #           for the ticket is updated with whatever
    #           action is going on.
    #
    #           If $type = 1, then we are appending a value onto
    #           either the owner or user list.  This is so that
    #           you can add a user to a ticket, and keep the old
    #           ones too.  If $type = 0, then we are setting a header
    #           to something, ie, wipe out the old value and replace it.
    #
    #           This should return three messages:
    #
    #           1 - appended to individual ticket logfile
    #           2 - appended to system logfile
    #           3 - available to be printed out if wanted

    &ddebug("Entering Rust_Lib::prep_actions\n");

    my($type, $caller, $modify, $opt_value, $rust_info) = @_;

    my ($old_value);
    my ($opt_num) = $rust_info->get_value('number');
    my ($date) = $rust_info->get_value('current_date');
    my ($queue_index) = $rust_info->get_value('queue_index');
    my ($queue_name) = Rust_Configs::extract_element('alias_name',$queue_index); 
    my ($rust_subject) = $rust_info->get_value('subject');

    if ($type) {
	if ($modify eq 'user') {
	    &debug("Preparing to append to the user header\n");
	    &ddebug("Leaving Rust_Lib::prep_actions\n");
	    return ("$opt_value appended to user list by $caller\n",
		    "$opt_num : $opt_value appended to user list by $caller. $date\n",
		    "$opt_value was appended to user list of ticket $opt_num.\n");
	}
    } else {
	$old_value = $rust_info->get_value($modify);
	if ($modify eq 'owner') {
	    if (!($old_value eq 'unknown')) {
		&debug("Preparing to give ownership of an unowned ticket to $opt_value\n");
		&ddebug("Leaving Rust_Lib::prep_actions\n");
		return ("Ownership changed from $old_value to $opt_value by $caller\n",
			"$opt_num : ownership changed from $old_value to $opt_value by $caller. $date\n",
			"Ownership changed from $old_value to $opt_value.\n");
	    } else {
		&debug("Preparing to change ownership of ticket to $opt_value\n");
		&ddebug("Leaving Rust_Lib::prep_actions\n");
		return ("Ticket assigned to $opt_value by $caller\n",
			"$opt_num : ticket assigned to $opt_value by $caller. $date\n",
			"Ticket assigned to $opt_value.\n");
	    }
	} elsif ($modify eq 'user') {
	    &debug("Preparing to change the user header of ticket\n");
	    &ddebug("Leaving Rust_Lib::prep_actions\n");
	    return ("User changed from $old_value to $opt_value by $caller\n",
		    "$opt_num : user changed from $old_value to $opt_value by $caller. $date\n",
		    "User of ticket $opt_num changed from $old_value to $opt_value.\n");
	} elsif ($modify eq 'status') {
	    if ($opt_value eq 'resolved') {
		&debug("Preparing to resolve ticket\n");
		&ddebug("Leaving Rust_Lib::prep_actions\n");
		return ("Ticket resolved by $caller\n",
			"$opt_num : ticket resolved by $caller. $date\n",
			"Ticket $queue_name \#$opt_num ($rust_subject) was resolved\n");
           } elsif (!(($opt_value eq 'resolved') || ($opt_value eq 'archived')) && 
                     (($old_value eq 'resolved') || ($old_value eq 'archived'))) {
                &debug("Preparing to reopen ticket\n");
                &ddebug("Leaving Rust_Lib::prep_actions\n");
		return ("Ticket reopened by $caller\n",
			"$opt_num : Ticket reopened by $caller. $date\n",
			"Ticket $opt_num ($rust_subject) was reopened\n");
	    } else {
		&debug("Preparing to change status of ticket\n");
		&ddebug("Leaving Rust_Lib::prep_actions\n");
		return ("Status changed from $old_value to $opt_value by $caller\n",
			"$opt_num : Ticket status changed from $old_value to $opt_value by $caller. $date\n",
			"Status of ticket $opt_num changed from $old_value to $opt_value.\n");
	    }
	} elsif ($modify eq 'priority') {
	    &debug("Preparing to change priority of ticket\n");
	    &ddebug("Leaving Rust_Lib::prep_actions\n");
	    return ("Priority changed from $old_value to $opt_value by $caller\n",
		    "$opt_num : Ticket priority changed from $old_value to $opt_value. $date\n",
		    "Priority of ticket $opt_num changed from $old_value to $opt_value.\n");
	} elsif ($modify eq 'subject') {
	    &debug("Preparing to change subject of ticket\n");
	    &ddebug("Leaving Rust_Lib::prep_actions\n");
	    return ("Subject changed from $old_value to $opt_value by $caller\n",
		    "$opt_num : Ticket subject changed from $old_value to $opt_value. $date\n",
		    "Subject of ticket $opt_num changed from $old_value to $opt_value.\n");
	} elsif ($modify eq 'accounting') {
	    &debug("Preparing to set accounting header of request\n");
	    &ddebug("Leaving Rust_Lib::prep_actions\n");
	    return ("Accounting header set to $opt_value by $caller\n",
		    "$opt_num : Ticket accounting header set to $opt_value by $caller. $date\n",
		    "Accounting header of toclet $opt_num set to $opt_value.\n");
	} elsif ($modify eq 'eta') {
	    &debug("Preparing to change eta of ticket\n");
	    &ddebug("Leaving Rust_Lib::prep_actions\n");
	    return ("Eta changed from $old_value to $opt_value by $caller\n",
		    "$opt_num : Ticket eta changed from $old_value to $opt_value. $date\n",
		    "Eta of ticket $opt_num changed from $old_value to $opt_value.\n");
	} elsif ($modify eq 'category') {
	    &debug("Preparing to change category of ticket\n");
	    &ddebug("Leaving Rust_Lib::prep_actions\n");
	    return ("category changed from $old_value to $opt_value by $caller\n",
		    "$opt_num : category changed from $old_value to $opt_value. $date\n",
		    "Category of ticket $opt_num changed from $old_value to $opt_value.\n");
	} elsif ($modify eq 'access') {
	    &debug("Preparing to change access flag of ticket\n");
	    &ddebug("Leaving Rust_Lib::prep_actions\n");
	    return ("access flag changed from $old_value to $opt_value by $caller\n",
		    "$opt_num : access changed from $old_value to $opt_value. $date\n",
		    "Access flag of ticket $opt_num changed from $old_value to $opt_value.\n");
	}
    } 

    &ddebug("Leaving Rust_Lib::prep_actions\n");
    return 10;
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub prep_assign {
    #
    # Type: Not to be called by external scripts
    # 
    # Abstract: Do some preliminary checks if a ticket is
    #           going to be assigned to somebody.
    # 
    #           First, see if it is being assigned to another
    #           RUST queue.  If not, see if the user to whom
    #           the ticket is being assigned to is in the
    #           authorization files.  If so, then proceed,
    #           otherwise fail.

    &ddebug("Entering Rust_Lib::prep_assign\n");

    my ($user, $rust_info) = @_;

    my ($queue_index) = $rust_info->get_value('queue_index');

    # Check first to see if we even need to do this...

    if ($rust_info->get_value('owner') eq $user) {
	&debug("The owner of the ticket is already $user, so not assigning\n");
	&ddebug("Leaving Rust_Lib::prep_assign\n");
	return 2;
    }

    # Next, check to see if we are giving this to another
    # queue

    foreach $each (split(':',$globals->{'alias_name'})) {
	if (($user eq $each) && (!($user eq $rust_info->get_value('alias_name')))) {
	    &debug("The ticket is being assigned to the $each RUST queue\n");
	    &ddebug("Leaving Rust_Lib::prep_assign\n");
	    return 3;
	} elsif ($user eq $rust_info->get_value('alias_name')) {
	    &debug("The ticket is already in the $user RUST queue\n");
	    return 4;
	}
    }

    # Lastly, check the authorization files

    &ddebug("Leaving Rust_Lib::prep_assign\n");
    return 0 if ($user eq 'none');
    return &check_caller($user, "", $queue_index);
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub prep_move {
    #
    # Type: Not to be called by external scripts
    #
    # Abstract: Prepare to move a ticket to a different queue.
    #           This function sets all the header information
    #           for the new ticket

    &ddebug("Entering Rust_Lib::prep_move\n");

    my ($rust_info, $new_rust_info) = @_;

    $new_rust_info->set_value('user',$rust_info->get_value('user'));
    $new_rust_info->set_value('subject',$rust_info->get_value('subject'));
    $new_rust_info->set_value('email',$rust_info->get_value('email'));
    $new_rust_info->set_value('category',$rust_info->get_value('category'));
    $new_rust_info->set_value('status','new');
    $new_rust_info->set_value('priority','normal');
    $new_rust_info->set_value('type','active');

    $move_header = join(',',$new_rust_info->get_value('alias_name'),
			$new_rust_info->get_value('number'));
    $rust_info->set_value('moved', $move_header);
	      
    &ddebug("Leaving Rust_Lib::prep_move\n");

}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub prep_merge {
    #
    # Type: Not to be called by external scripts
    #
    # Abstract: Prepare for a merge.  This function makes
    #           sure that both tickets are open, ie 
    #           not resolved are archived.  If one or both
    #           tickets are not open, then don't do the merge
    #
    #           $rust_info = ticket 1
    #           $merge_info = ticket 2
    #           Merge will put $rust_info INTO $merge_info

    &ddebug("Entering Rust_Lib::prep_merge\n");

    my ($caller, $rust_info, $merge_info) = @_;
   
    my ($new_value);
    my ($opt_num1) = $rust_info->get_value('number');
    my ($opt_num2) = $merge_info->get_value('number');
    my ($date) = $rust_info->get_value('current_date');

    &debug("Preparing to merge two tickets into one\n");

    # Make sure that both tickets are opened.  If not, stop.

    if (($rust_info->get_value('type') eq 'active') &&
	($merge_info->get_value('type') eq 'active')) {

	# First deal with the owners.  If $merge_info has no
	# owner, and $rust_info does, then $merge_info becomes
	# owned by the $rust_info owner.  If $merged_info has
	# an owner already, ownership doesn't change.

	$merge_info->set_value('owner', $rust_info->get_value('owner'))
	    if ($merge_info->get_value('owner') eq 'unknown');

	# If both tickets "new" status, the status
	# stays the same.  If $merge_info has "new" and
	# $rust_info has something else, then $merge_info
	# gets the status of $rust_info.  Otherwise,
	# status doesn't change.	

	$merge_info->set_value('status', $rust_info->get_value('status'))
	    if ($merge_info->get_value('status') eq 'new');

	# Now for the users of the tickets.  Just merge them
	# together into a comma seperated list

	if (!($rust_info->get_value('user') eq $merge_info->get_value('user'))) {
	    $new_value = join(',', $rust_info->get_value('user'), $merge_info->get_value('user'));
	    $merge_info->set_value('user',$new_value);
	}

	# Now deal with the numbers.  The "number" header in the ticket being
	# merged DOES NOT change.  So if #22 is being merged into #34, the
	# "number" header for #22 is still 22.  The "number" header in the
	# ticket receiving the merge, the numbers from BOTH ticket headers
	# are combined in a comma seperated list.  So in the example above,
	# ticket #34 will have "34,22" as its "number" header.
	#
	# We also store the receiving ticket number in the "merged" header
	# of the ticket being merged.  This way we can trace thorugh a merge
	# to find the actual file for a ticket.  So ticket #22's "merged"
	# header will contain "34".

	$rust_info->set_value('merged',$opt_num2);
	$new_value = join(',', $merge_info->get_value('number'), $opt_num1);
	$merge_info->set_value('number',$new_value);

	# Update the updated date, and set the merge flag in $rust_info
	# to be true.
	
	$rust_info->set_value('updated',$date);
	$merge_info->set_value('updated',$date);

	&ddebug("Leaving Rust_Lib::prep_merge\n");
	return (0, "Ticket $opt_num1 merged into ticket $opt_num2 by $caller\n",
		"$opt_num1 : Ticket merged into ticket $opt_num2 by $caller.  $date\n",
		"Ticket $opt_num1 was merged into ticket $opt_num2.\n");
    } 

    &ddebug("Leaving Rust_Lib::prep_merge\n");
    return(18);
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub merge_logfiles {
    #
    # Type: Not to be called by external scripts
    #
    # Abstract: Take the logfile associated with $num and
    #           merge it into $newnum.  Leave the $num logfile
    #           around in case we want to "unmerge" the ticket.
    #
    #           This assumes that both files are in the
    #           active queue.

    &ddebug("Entering Rust_Lib::merge_logfiles\n");

    my ($caller, $rust_info, $merge_info) = @_;

    my ($source_file, $dest_file, @errors);
    my ($path_to_active) = $globals->{'path_to_active'};
    my ($alias_name) = $rust_info->get_value('alias_name');
    my ($opt_num1) = $rust_info->get_value('number');
    my ($opt_num2) = (split(',',$merge_info->get_value('number')))[0];
    my ($queue_index) = $rust_info->get_value('queue_index');
    my ($date) = $rust_info->get_value('current_date');
    my ($rfc_date) = $rust_info->get_value('rfc_date');

    &debug("Merging ticket $opt_num1 logfile into ticket $opt_num2 logfile\n");

    @errors = &get_ticket_filename($alias_name, $opt_num1, 'active');
    return $errors[0] if $errors[0];
    $source_file = $errors[1];

    @errors = &get_ticket_filename($alias_name, $opt_num2, 'active');
    return $errors[0] if $errors[0];
    $dest_file = $errors[1];
 
    # Keep the RFC822 conformity!
 
    $dest_file = &untaint_obj($dest_file);
    open(DEST_FILE, ">>$dest_file") || return 8;
    print DEST_FILE "\n";
    print DEST_FILE "From RUST $rfc_date\n";
    print DEST_FILE "Subject: BEGIN MERGE\n\n";
    print DEST_FILE "Ticket $opt_num1 is being merged into this log\n";
    print DEST_FILE "Logfile of ticket $opt_num1 follows\n\n";
    print DEST_FILE "=" x 75, "\n";
    print DEST_FILE "Date of merge: $date\n\n";
 
    $source_file = &untaint_obj($source_file);
    open (SOURCE_FILE, "$source_file") || return 8;
    while (<SOURCE_FILE>) {
        print DEST_FILE $_;
    }
    close (SOURCE_FILE);
 
    print DEST_FILE "\n\n";
    print DEST_FILE "From RUST $rfc_date\n";
    print DEST_FILE "Subject: END MERGE\n\n";
    print DEST_FILE "Ticket $opt_num1 was merged into this log\n";
    print DEST_FILE "Logfile of ticket $opt_num1 is above\n\n";
    print DEST_FILE "=" x 75, "\n";
    print DEST_FILE "Date of merge: $date\n\n";
    close (DEST_FILE);
 
    # Now remove the ticket logfile of the ticket which was just merged
    &debug("Removing $source_file from existance\n");
    unlink($source_file);

    &ddebug("Leaving Rust_Lib::merge_logfiles\n");
    return 0;
}
   
#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub reopen_ticket {
    #
    # Type: Not to be called by external scripts
    #
    # Abstract: Reopen a ticket if it is either resolved or
    #           archived.  This can happen for a variety of
    #           reasons.
    
    &ddebug("Entering Rust_Lib::reopen_ticket\n");

    my ($caller, $status, $rust_info) =  @_;
    
    my (@errors, $ticket_filename, @action_list);
    my ($path_to_active) = $globals->{'path_to_active'};
    my ($rust_type) = $rust_info->get_value('type');
    my ($alias_name) = $rust_info->get_value('alias_name');
    my ($opt_num) = $rust_info->get_value('number');
    my ($queue_index) = $rust_info->get_value('queue_index');
    my ($date) = $rust_info->get_value('current_date');
    my ($new_file) = "$path_to_active/$alias_name/$opt_num";

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

    @errors = &get_ticket_filename($alias_name, $opt_num, $rust_type); 
    $ticket_filename = $errors[1] if !$errors[0];
    
    if (!($ticket_filename)) {
	&ddebug("Leaving Rust_Lib::reopen_ticket\n");
	return 5;
    }

    if ((-f $ticket_filename) && (!($rust_type eq 'active')))  {
	$ticket_filename = &untaint_obj($ticket_filename);
	$new_file = &untaint_obj($new_file);
	rename($ticket_filename, $new_file);
	$rust_info->set_value('type', 'active');
	$rust_info->set_value('status', $status);
	$rust_info->set_value('updated',$date);

	@action_list = ("Ticket was reopened by $caller\n");
	$error = &write_to_rust_log(1, $rust_info, @action_list);
	return $error if $error;

	&ddebug("Leaving Rust_Lib::reopen\n");
	return (0, $rust_info);

    } elsif ((-f $ticket_filename) && ($rust_type eq 'active')) { 
	&ddebug("Leaving Rust_Lib::reopen\n");
	return (0, $rust_info);
    }

    &ddebug("Leaving Rust_Lib::reopen\n");
    return 8;
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub resolve_ticket {
    #
    # Type: Not to be called by external scripts
    #
    # Abstract: Resolve a ticket
    #

    &ddebug("Entering Rust_Lib::resolve_ticket\n");

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

    my ($error, @errors, @action_list, $log_message, $ticket_filename, $resolved_filename);
    my ($rust_type) = $rust_info->get_value('type');
    my ($alias_name) = $rust_info->get_value('alias_name');
    my ($opt_num) = $rust_info->get_value('number');
    my ($queue_index) = $rust_info->get_value('queue_index');
    my ($date) = $rust_info->get_value('current_date');

    &debug("Going to resolve the ticket\n");

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

    @errors = &get_ticket_filename($alias_name, $opt_num, 'resolved');
    return $errors[0] if $errors[0];
    $resolved_filename = $errors[1];
	

    if (-f $ticket_filename) {
	&debug("Moving the ticket file to the resolved dir\n");
	$ticket_filename = &untaint_obj($ticket_filename);
	$resolved_filename = &untaint_obj($resolved_filename);
	rename($ticket_filename, $resolved_filename); 
	$rust_info->set_value('type', 'resolved');
	$rust_info->set_value('status', 'resolved');

	&ddebug("Leaving Rust_Lib::resolve_ticket\n");
	return 0;
    } else {
	&ddebug("Leaving Rust_Lib::resolve_ticket\n");
	return 8;
    }
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub archive_ticket {
    #
    # Type: Not to be called by external scripts
    #
    # Abstract: Move a ticket to the archived queue
    #

    &ddebug("Entering Rust_Lib::archive_ticket\n");

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

    my ($error, @errors, @action_list, $log_message, $archived_filename, $resolved_filename);
    my ($rust_type) = $rust_info->get_value('type');
    my ($alias_name) = $rust_info->get_value('alias_name');
    my ($opt_num) = $rust_info->get_value('number');
    my ($queue_index) = $rust_info->get_value('queue_index');
    my ($date) = $rust_info->get_value('current_date');

    &debug("Going to move a ticket from $rust_type to archive\n");

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

    @errors = &get_ticket_filename($alias_name, $opt_num, 'archived');
    return $errors[0] if $errors[0];
    $archived_filename = $errors[1];
	
    if (-f $resolved_filename) {
	&debug("Moving the ticket file to the archived dir\n");
	$resolved_filename = &untaint_obj($resolved_filename);
	$archived_filename = &untaint_obj($archived_filename);
	rename($resolved_filename, $archived_filename); 
	$rust_info->set_value('type', 'archived');

	&ddebug("Leaving Rust_Lib::archive_ticket\n");
	return 0;
    } else {
	&ddebug("Leaving Rust_Lib::archive_ticket\n");
	return 8;
    }
}


#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
# Rust_Lib functions that deal with filenames and file I/O
#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub get_ticket_filename {
    #
    # Type: Not to be called by external scripts
    #
    # Abstract: Simply return the name of a RUST ticket logfile
    #           I was doing this often enough to constitute
    #           a function to do it
    #

    &ddebug("Entering Rust_Lib::get_ticket_filename\n");

    my ($opt_queue, $opt_num, $ticket_type) = @_;

    my ($path_to_active) = $globals->{'path_to_active'};
    my ($path_to_resolved) = $globals->{'path_to_resolved'};
    my ($path_to_archived) = $globals->{'path_to_archived'};

    &debug("Using $ticket_type as the type of ticket\n");

    &ddebug("Leaving Rust_Lib::get_ticket_filename\n");
    return (0, "$path_to_active/$opt_queue/$opt_num") if ($ticket_type eq 'active');
    return (0, "$path_to_resolved/$opt_queue/$opt_num") if ($ticket_type eq 'resolved');
    return (0, "$path_to_archived/$opt_queue/$opt_num") if ($ticket_type eq 'archived');
    
    &debug("Was unable to create a ticket logfile name\n");
    return 6;

}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub get_ticket_list {
    #
    # Type: Not to be called by external scripts
    #
    # Abstract: Read a directory and return an array of all filenames
 
    Rust_Debug::ddebug("Entering Rust_Lib::get_ticket_list\n");
 
    my ($type, $queue_index) = @_;
 
    my ($type_path, $path_to_flies);
    my ($alias_name) = Rust_Configs::extract_element('alias_name',$queue_index);
    my (@file_list) = ();
 
    if ($type eq 'resolved') {
        $type_path = $globals->{'path_to_resolved'};
    } elsif ($type eq 'archived') {
        $type_path = $globals->{'path_to_archived'};
    } else {
        $type_path = $globals->{'path_to_active'};
    }
 
    $path_to_files = "$type_path/$alias_name";
      &debug("The file path is $path_to_files\n");
 
    opendir(DIR, $path_to_files);
    @file_list = grep(/^\d+$/, readdir(DIR));
    closedir(DIR);
 
    Rust_Debug::ddebug("Leaving Rust_Lib::get_ticket_list\n");
    return @file_list;
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub move_logfile {
    #
    # Type: Not to be called by external scripts
    #
    # Abstract: Copy a logfile to another RUST queue.  This is
    #           done when a ticket is being moved into a different
    #           queue.

    &ddebug("Entering Rust_Lib::move_logfile\n");

    my ($rust_info, $new_rust_info) = @_;

    my (@errors, $source_file, $dest_file);

    my ($alias_name) = $rust_info->get_value('alias_name');
    my ($new_alias_name) = $new_rust_info->get_value('alias_name');
    my ($num) = (split(',',$rust_info->get_value('number')))[0];
    my ($newnum) = $new_rust_info->get_value('number');
    my ($index) = $rust_info->get_value('queue_index');
    my ($new_index) = $new_rust_info->get_value('queue_index');

    &debug("Moving ticket $num logfile to the $new_alias_name RUST queue\n");

    @errors =  &get_ticket_filename($alias_name, $num, 'active');
    if ($errors[0]) {
	&ddebug("Leaving Rust_Lib::move_logfile\n");
	return $errors[0];
    }

    $source_file = $errors[1];

    @errors = &get_ticket_filename($new_alias_name, $newnum, 'active');
    if ($errors[0]) {
	&ddebug("Leaving Rust_Lib::move_logfile\n");
	return $errors[0];
    }

    $dest_file = $errors[1];
    
    &debug("Renaming $source_file to $dest_file\n");
    $source_file = &untaint_obj($source_file);
    $dest_file = &untaint_obj($dest_file);
    rename($source_file,$dest_file);

    &ddebug("Leaving Rust_Lib::move_logfile\n");
    return 1;
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub read_file {
    #
    # Type: Not to be called by external scripts
    #
    # Abstract: Read in a log file from an RUST queue
    #
    #           The structure of the RUST log file system is
    #           as follows:
    #
    #           - A header file for each ticket will contain
    #             information such as the ticket number, the
    #             user who made the ticket, the status/priority/eta
    #             of the ticket, etc...
    #
    #           - A "contents" file which will contain the original
    #             message and all other appendages to that ticket.  
    #
    
    &ddebug("Entering Rust_Lib::read_file\n");

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

    my ($header_file, @errors);
    my ($locked) = 1;
    my ($hostname) = $globals->{'hostname'};

    # Continue reading files until the merged header is 0.
    # Also continue reading files if the ticket has been moved.

    &debug("Attempting to find and read a ticket header file\n");

    if (!($rust_info)) {
	$rust_info = new Rust_Data;
	@errors = $rust_info->init($opt_num, $queue_index);
	if ($errors[0]) {
	    &debug("Unable to initialize the ticket\n");
	    return $errors[0];
	}
	
	$opt_num = $errors[1];
	$queue_index = $errors[2];
    }
    
    $header_file = $rust_info->get_value('header_file');
    &debug("Using $header_file for the ticket header file\n");

    # See if header file exists.  If it does, lock it and continue.
    # If the header file does not exist, then the ticket doesn't exist.

    if ($rust_info->check_for_header_file) {

	$header_file = &untaint_obj($header_file);

	# Try to lock the file
	if ($locked_files) {
	    $locked = $locked_files->add($header_file,$hostname,$caller, 10); 
	}
	
	# Read the header file and add the info into the rust_info
	# object.  Only do if file was locked.
	if (($locked) && ($locked != 28)) {
	    $rust_info->read_header_file;
	} else {
	    &ddebug("Leaving Rust_Lib::read_file\n");
	    return 29;
	}
	
    } else {
	&debug("Unable to find header file\n");
	&ddebug("Leaving Rust_Lib::read_file\n");
	return (13, $rust_info);
    }

    &ddebug("Leaving Rust_Lib::read_file\n");
    return (0, $rust_info);    

}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub write_to_rust_log {
    #
    # Type: Not to be called by external scripts
    #
    # Abstract: Append stuff to a RUST ticket logfile.
    #           This function is generic enough that it can
    #           be used to append anything to the logfile for
    #           a specified ticket.
    #
    #           if $type = 0, we are creating a ticket
    #           if $type = 1, we are appending actions to the logfile,
    #                         ie @append is a list of actions.
    #           if $type = 2, we are appending mail to the logfile,
    #                         ie @append is the mail to append.
    #           if $type = 3, we are adding comments to the logfile,
    #                         ie @append is the text to append.
    
    &ddebug("Entering Rust_Lib::write_to_rust_log\n");

    my ($type, $rust_info, @append) = @_;

    my ($ticket_filename, @errors);
    my ($rust_type) = $rust_info->get_value('type');
    my ($alias_name) = $rust_info->get_value('alias_name');
    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 ($rfc_date) = $rust_info->get_value('rfc_date');

    if($#append >= 0) {

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

	if (!($ticket_filename)) { 
	    &ddebug("Leaving Rust_Lib::write_to_rust_log\n");
	    return 5;
	}

	$ticket_filename = &untaint_obj($ticket_filename);
	if ($type == 1) {

	    &debug("Appending to $ticket_filename\n");
	    open(TICKET_FILE, ">>$ticket_filename") || return 2;
	    print TICKET_FILE "\n";
	    print TICKET_FILE "From RUST $rfc_date\n";
	    print TICKET_FILE "Subject: Actions\n\n";
	    print TICKET_FILE @append;
	    print TICKET_FILE "=" x 75, "\n";
	    print TICKET_FILE "Date of actions: $date\n\n";
	    close(TICKET_FILE);

	    &ddebug("Leaving Rust_Lib::write_to_rust_log\n");
	    return 0;

	} elsif ($type == 2) {
	    
	    &debug("Appending mail message to $ticket_filename\n");
	    open(TICKET_FILE, ">>$ticket_filename") || return 2;
	    print TICKET_FILE "\n";
	    print TICKET_FILE @append;
	    print TICKET_FILE "\n";
	    print TICKET_FILE "=" x 75, "\n";
	    print TICKET_FILE "Date mail was appended: $date\n\n";
	    close(TICKET_FILE);

	    &ddebug("Leaving Rust_Lib::write_to_rust_log\n");
	    return 0;

	} elsif ($type == 3) {
	    
	    &debug("Appending comments to $ticket_filename\n");
	    open(TICKET_FILE, ">>$ticket_filename") || return 2;
	    print TICKET_FILE "\n";
	    print TICKET_FILE "From RUST $rfc_date\n";
	    print TICKET_FILE "Subject: Comments added\n\n";
	    print TICKET_FILE @append;
	    print TICKET_FILE "=" x 75, "\n";
	    print TICKET_FILE "Date comments were added: $date\n\n";
	    close(TICKET_FILE);

	    &ddebug("Leaving Rust_Lib::write_to_rust_log\n");
	    return 0;

	} else {

	    &debug("Creating $ticket_filename\n");
	    open(TICKET_FILE, ">$ticket_filename") || return 2;
	    print TICKET_FILE @append;
	    print TICKET_FILE "\n";
	    print TICKET_FILE "=" x 75, "\n";
	    print TICKET_FILE "Date of creation: $date\n\n";
	    close(TICKET_FILE);

	    &ddebug("Leaving Rust_Lib::write_to_rust_log\n");
	    return 0;
	}
    }
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub read_rust_log {
    #
    # Type: Not to be called by external scripts
    #
    # Abstract: Read a RUST ticket logfile, returning the
    #           contents as an array

    &ddebug("Entering Rust_Lib::read_rust_log\n");

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

    my ($ticket_filename, @errors, @return_array);
    my ($rust_type) = $rust_info->get_value('type');
    my ($alias_name) = Rust_Configs::extract_element('alias_name',$queue_index);

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

    if (!($ticket_filename)) { 
	&ddebug("Leaving Rust_Lib::write_to_rust_log\n");
	return 13;
    }

    &debug("Attempting to read $ticket_filename\n");

    $ticket_filename = &untaint_obj($ticket_filename);
    open(TICKET_FILE, "<$ticket_filename") || return 2;

    while (<TICKET_FILE>) {
	push(@return_array, $_);
    }

    close(TICKET_FILE);

    &debug("Read the file, returning contents as an array\n");
    &ddebug("Leaving Rust_Lib::read_rust_log\n");
    return @return_array;
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub write_log {
    #
    # Type: Can be called by external scripts
    #
    # Abstract: Write a line to a logfile so that we can
    #           keep track of what happens.  This is
    #           configurable, and doesn't need to be done if
    #           you don't want it.
    #
    
    &ddebug("Entering Rust_Lib::write_log\n");

    my ($type, $message, $queue_index) = @_;

    my ($alias_name) = Rust_Configs::extract_element('alias_name',$queue_index);
    my ($path_to_log) = $globals->{'path_to_log'};
    
    if ($type == 1) {
	$logfile = "$path_to_log/action_$alias_name.log";
    } elsif ($type == 2) {
	$logfile = "$path_to_log/mail_$alias_name.log";
    } else {
	$logfile = "$path_to_log/kill.log";
    }

    if($globals->{'do_logging'}) {

	&debug("Attempting to write log entry to $logfile\n");

	$logfile = &untaint_obj($logfile);

	((open(LOG_FILE, ">>$logfile")) || (return 3)) if (-f $logfile);
	((open(LOG_FILE, ">$logfile")) || (return 3)) if (!(-f $logfile));

	print LOG_FILE $message;
	close(LOG_FILE);
    }

    &ddebug("Leaving Rust_Lib::write_log\n");
    return 0;
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub existing_ticket {
    #
    # Type: Can be called by external scripts
    #
    # Abstract: Take arguments and see if there is a ticket
    #           that matches them.  The ticket has to exit
    #           in the queue for this to be successful

    &ddebug("Entering Rust_Lib::existing_ticket\n");

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

    my ($ticket_filename, @errors);
    my ($alias_name) = Rust_Configs::extract_element('alias_name',$queue_index);

    &debug("Looking for the ticket logfile\n");

    foreach $each ('active', 'resolved', 'archived') {
	@errors = &get_ticket_filename($alias_name, $opt_num, $each);
	return $errors[0] if $errors[0];
	$ticket_filename = $errors[1];
	(&debug("Found $ticket_filename\n") && return 0) if (-f $ticket_filename);
	&debug("Didn't find $ticket_filename\n");
    }
   
    &ddebug("Leaving Rust_Lib::existing_ticket\n");
    return 14;
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub create_ticket_headers {
    #
    # Type: Not to be called by external scripts
    #
    # Abstract: Create a new ticket, createing the ticket 
    #           header file to contain info.
    #
    #           Most headers ARE NOT set here, just some basic
    #           ones such as ticket number, status, priority,
    #           and created date.  The header file is also
    #           created here.  
    #
    # The index for the queue gets passed into this function

    &ddebug("Entering Rust_Lib::create_ticket_headers\n");

    my ($caller, $locked_files, $new_index) = @_;

    my ($locked, @output, $new_number, $rust_info);
    my ($header) = $globals->{'hostname'};

    &debug("Creating a new ticket with initialized headers\n");

    # Get a new ticket number.
    @output =  &number_funcs($caller, 'inc', 0, $new_index);
    
    if ($output[0]) {
	&ddebug("Leaving Rust_Lib::create_ticketheaders\n");
	return $output[0];
    }

    my ($new_number) = $output[1];

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

    # Create a new object for the new ticket
    $rust_info = new Rust_Data;
    $rust_info->create_header_info;
    $rust_info->set_value('number',$new_number);
    $rust_info->init($new_number, $new_index);
    $header_file = $rust_info->get_value('header_file');

    # At this point, the header file for this ticket should NOT
    # exist.  If it does, then it is possible that things need to
    # be checked.  If the header file exists, then exit the program,
    # sending mail to the admin mailing list reporting the problem.
    # If the header file does not exist, then create it, lock it,
    # and return

    if (!($rust_info->check_for_header_file)) {
	    
	$header_file = &untaint_obj($header_file);

	# Try to lock the file
	$locked = $locked_files->add($header_file,$hostname,$caller, 10); 

	# Create a new header file only if locking succeeded.
	
	if ($locked == 28) {
	    &ddebug("Leaving Rust_Lib::create_ticket_headers\n");
	    return 4;
	}
    } else {
	&ddebug("Leaving Rust_Lib::create_ticket_headers\n");
	return 5;
    }

    &ddebug("Leaving Rust_Lib::create_ticket_headers\n");
    return (0,$new_number, $rust_info);
}

}
1;

# EOF
