;# NAME
;#    mqueue.pl - functions to work with the sendmail queue
;#
;# DESCRIPTION
;#    Both Get_Queue_IDs and Parse_Control_File are available to get 
;#    information about the sendmail queue. The cqueue program is a good
;#    example of how these functions work.
;#
;# AUTHOR
;#    Michael S. Muegel (mmuegel@mot.com)  
;#
;# RCS INFORMATION
;#    mmuegel
;#    /usr/local/ustart/src/mail-tools/dist/foo/libs/mqueue.pl,v
;#    1.1 of 1993/07/28 08:07:19

package mqueue;

;###############################################################################
;# Get_Queue_IDs
;#
;# Will figure out the queue IDs in $Queue that have both control and data
;# files. They are returned in @Valid_IDs. Those IDs that have a
;# control file and no data file are saved to the array globbed by 
;# *Missing_Control_IDs. Likewise, those IDs that have a data file and no 
;# control file are saved to the array globbed by *Missing_Data_IDs.
;#
;# If $Skip_Locked is true they a message that has a lock file is skipped
;# and will not show up in any of the arrays.
;#
;# If everything went AOK then $Status is 1; otherwise, $Status is 0 and
;# $Msg tells what went wrong.
;#
;# Globals:
;#    None
;#
;# Arguments:
;#    $Queue, $Skip_Locked, *Missing_Control_IDs, *Missing_Data_IDs
;#
;# Returns:
;#    $Status, $Msg, @Valid_IDs
;###############################################################################
sub main'Get_Queue_IDs
{
   local ($Queue, $Skip_Locked, *Missing_Control_IDs, 
          *Missing_Data_IDs) = @_;
   local (*QUEUE, @Files, %Lock_IDs, %Data_IDs, %Control_IDs, $_);

   # Make sure that the * argument @arrays ar empty
   @Missing_Control_IDs = @Missing_Data_IDs = ();

   # Save each data, lock, and queue file in @Files
   opendir (QUEUE, $Queue) || return (0, "error getting directory listing of $Queue");
   @Files = grep (/^(df|lf|qf)/, readdir (QUEUE));
   closedir (QUEUE);
   
   # Create indexed list of data and control files. IF $Skip_Locked is true
   # then skip either if there is a lock file present.
   if ($Skip_Locked)
   {
      grep ((s/^lf//) && ($Lock_IDs {$_} = 1), @Files);
      grep ((s/^df//) && (! $Lock_IDs {$_}) && ($Data_IDs {$_} = 1), @Files);
      grep ((s/^qf//) && (! $Lock_IDs {$_}) && ($Control_IDs {$_} = 1), @Files);
   }
   else
   {
      grep ((s/^df//) && ($Data_IDs {$_} = 1), @Files);
      grep ((s/^qf//) && ($Control_IDs {$_} = 1), @Files);
   };
   
   # Find missing control and data files and remove them from the lists of each
   @Missing_Control_IDs = sort (grep ((! $Control_IDs {$_}) && (delete $Data_IDs {$_}), keys (%Data_IDs)));
   @Missing_Data_IDs = sort (grep ((! $Data_IDs {$_} && (delete $Control_IDs {$_})), keys (%Control_IDs)));
   
   
   # Return the IDs in an appartently random order
   return (1, "", keys (%Control_IDs));
};


;###############################################################################
;# Parse_Control_File
;#
;# Will pase a sendmail queue control file for useful information. See the
;# Sendmail Installtion and Operation Guide (SMM:07) for a complete
;# explanation of each field.
;#
;# The following globbed variables are set (or cleared) by this function:
;#
;#    $Sender           The sender's address. 
;#
;#    @Recipients       One or more addresses for the recipient of the mail.
;#
;#    @Errors_To        One or more addresses for addresses to which mail
;#                      delivery errors should be sent.
;#
;#    $Creation_Time    The job creation time in time(3) format. That is,
;#                      seconds since 00:00:00 GMT 1/1/70.
;#
;#    $Priority         An integer representing the current message priority.
;#                      This is used to order the queue. Higher numbers mean 
;#                      lower priorities.
;#
;#    $Status_Message   The status of the mail message. It can contain any
;#                      text.
;#
;#    @Headers          Message headers unparsed but in their original order.
;#                      Headers that span multiple lines are not mucked with,
;#                      embedded \ns will be evident.
;#
;# In all e-mail addresses bounding <> pairs are stripped.
;#
;# If everything went AOK then $Status is 1. If the message with queue ID
;# $Queue_ID just does not exist anymore -1 is returned. This is very
;# possible and should be allowed for. Otherwise, $Status is 0 and $Msg 
;# tells what went wrong.
;#
;# Globals:
;#    None
;#
;# Arguments:
;#    $Queue, $Queue_ID, *Sender, *Recipients, *Errors_To, *Creation_Time, 
;#    *Priority, *Status_Message, *Headers
;#
;# Returns:
;#    $Status, $Msg
;###############################################################################
sub main'Parse_Control_File
{
   local ($Queue, $Queue_ID, *Sender, *Recipients, *Errors_To, *Creation_Time,
          *Priority, *Status_Message, *Headers) = @_;
   local (*Control, $_, $Not_Empty);

   # Required variables and the associated control. If empty at the end of
   # parsing we return a bad status.
   @REQUIRED_INFO = ('$Creation_Time', 'T', '$Sender', 'S', '@Recipients', 'R',
		     '$Priority', 'P');

   # Open up the control file for read
   $Control = "$Queue/qf$Queue_ID";
   if (! open (Control)) 
   {
      return (-1) if ((-x $Queue) && (! -f "$Queue/qf$Queue_ID") &&
       (! -f "$Queue/df$Queue_ID"));
      return (0, "error opening $Control for read: $!");
   };

   # Reset the globbed variables just in case
   $Sender = $Creation_Time = $Priority = $Status_Message = "";
   @Recipients = @Errors_To = @Headers = ();

   # Look for a few things in the control file
   READ: while (<Control>)
   {
      $Not_Empty = 1;
      chop;

      PARSE:
      {
         if (/^T(\d+)$/)
         {
            $Creation_Time = $1;
         }
         elsif (/^S(<)?([^>]+)/)
         {
            $Sender = $2;
         }
         elsif (/^R(<)?([^>]+)/)
         {
            push (@Recipients, $2);
         }
         elsif (/^E(<)?([^>]+)/)
         {
            push (@Errors_To, $2);
         }
         elsif (/^M(.*)/)
         {
            $Status_Message = $1;
         }
         elsif (/^P(\d+)$/)
         {
            $Priority = $1;
         }
         elsif (/^H(.*)/)
         {
            $Header = $1;
            while (<Control>)
            {
               chop;
               last if (/^[A-Z]/);
               $Header .= "\n$_";
            };
            push (@Headers, $Header);
	    redo PARSE if ($_);
	    last if (eof);
         };
      };
   };

   # If the file was empty scream bloody murder
   return (0, "empty control file") if (! $Not_Empty);

   # Yell if we could not find a required field
   while (($Var, $Control) = splice (@REQUIRED_INFO, 0, 2))
   {
      eval "return (0, 'required control field $Control not found')
	       if (! $Var)";
      return (0, "error checking \$Var: $@") if ($@);
   };

   # Everything went AOK
   return (1);
};

1;
