<?php
/*  
 * Analysis Console for Intrusion Databases (ACID)
 *
 * Author: Roman Danyliw <rdd@cert.org>, <roman@danyliw.com>
 *         MSSQL support by Charles Hand <charlieh@silicondefense.com>
 *
 * Copyright (C) 2001 Carnegie Mellon University
 * (see the file 'acid_main.php' for license details)
 *
 * Purpose: Alert action (e.g. add to AG, delete, email,
 *          archive) operations   
 *
 */

include_once("acid_ag_common.php");
include_once("acid_constants.inc");

function IsValidAction($action, $valid_actions)
{
  return in_array($action, $valid_actions);
}

function IsValidActionOp($action_op, $valid_action_op)
{
  return in_array($action_op, $valid_action_op);
}

/*
  = action: action to perform (e.g. ag_by_id, ag_by_name, clear_alerts, delete_alerts, email_alerts)
  = valid_action: array of valid actions ($action must be in valid_action)
  = action_op: select operation to perform with $action (e.g. Selected, ALL on Screen, Entire Query)
               $action_op needs to be passed by reference, because its value will need to get
               changed in order for alerts to be re-displayed after the operation.
  = valid_action_op: array of valid action operations ($action_op must be in $valid_action_op)
  = $action_arg: argument for the action
  = $context: what page is the $action being performed in? 
       - 1: from query results page
       - 2: from signature/alert page
       - 3: from sensor page
       - 4: from AG maintenance page
  = $action_chk_lst: (used only when 'Selected' is the $action_op)
                    a sparse array where each element contains a key to alerts which should be acted 
                    on.  Some elements will be blank based on the checkbox state.
                    Depending on the setting of $context, these keys may be either
                    sid/cid pairs ($context=1), signature IDs ($context=2), or sensor IDs ($context=3)

  = $action_lst: (used only when 'ALL on Screen' is the $action_op)
                 an array denoting all elements on the screen, where each element contains a key to 
                 alerts which should be acted on. Depending on the setting of $context, these keys 
                 may be either sid/cid pairs ($context=1), signature IDs ($context=2), or sensor 
                 IDs ($context=3) 
  = $num_alert_on_screen: count of alerts on screen (used to parse through $alert_chk_lst for
                          Selected and ALL on Screen $action_op).
  = $num_alert_in_query: count of alerts in entire query. Passed by reference since delete operations
                         will decrement its value
  = $action_sql: (used only when 'Entire Query' is the $action_op)
                 SQL used to extract all the alerts to operate on
  = $page_caller: $caller variable from page
  = $db: handle to the database 
  = $action_param: extra data passed about an alert in addition to what is
                   entered by users in $action_arg
 */

function ActOnSelectedAlerts($action, $valid_action, &$action_op, $valid_action_op, $action_arg,
                             $context,
                             $action_chk_lst, $action_lst,
                             $num_alert_on_screen, &$num_alert_in_query,
                             $action_sql, $page_caller,
                             $db, $action_param = "" )
{ 
  GLOBAL $current_view, $last_num_alerts, $freq_num_alerts, $caller, 
         $ag_action, $debug_mode, $max_script_runtime;

  /* Verify that an action was actually selected */
  if ( !IsValidActionOp($action_op, $valid_action_op) )
     return;

  /* Verify that action was selected when action operation is clicked */
  if ( IsValidActionOp($action_op, $valid_action_op) &&
       $action == " " )
  {
     ErrorMessage("No action was specified on the alerts");
     return;
  }

  /* Verify that validity of action   */
  if ( !(IsValidAction($action, $valid_action) && 
         IsValidActionOp($action_op, $valid_action_op)) ) 
  {
     ErrorMessage("'".$action."' is an invalid action");
     return;
  }

  /* Verify that those actions that need an argument have it
   *
   * Verify #1: Adding to an AG needs an argument 
   */
  if ( ($action_arg == "") && ( ($action == "ag_by_id") || ($action == "ag_by_name") ) )
  {
      ErrorMessage("Could not add alerts since no AG was specified");
      return;
  }

  /* Verify #2: Emailing alerts needs an argument */
  if ( ($action_arg == "") && ( ($action == "email_alert") || ($action == "email_alert2") ||
       ($action_arg == "csv_alert") ) )
  {
      ErrorMessage("Could not email alerts since no email address was specified");
      return;
  }

  if ( $debug_mode > 0 )
     echo "==== ACTION ======<BR>
           context = $context<BR><BR>";

  set_time_limit($max_script_runtime);
  if ($action_op == "Selected")
  {
     /* on packet lookup, only examine the first packet */
     if ( $context == PAGE_ALERT_DISPLAY ) 
     {
        $tmp = 1;
        ProcessSelectedAlerts($action, $action_op, $action_arg, $action_param,
                              $context,
                              $action_chk_lst,
                              $tmp,
                              $action_sql,
                              $db);
        $num_alert_in_query = $tmp;
     }
     else
     {
        ProcessSelectedAlerts($action, $action_op, $action_arg, $action_param,
                              $context,
                              $action_chk_lst,
                              $num_alert_in_query,
                              $action_sql,
                              $db);
     }
   }
   else if ($action_op == "ALL on Screen")
   {
      ProcessSelectedAlerts($action, $action_op, $action_arg, $action_param,
                            $context,
                            $action_lst,
                            $num_alert_in_query,
                            $action_sql,
                            $db);
   }
   else if ($action_op == "Entire Query")      
   {
      if ( ($context == PAGE_QRY_ALERTS) )  /* on alert listing page */
      {
         if ( $page_caller == "last_tcp"  || $page_caller == "last_udp" || 
              $page_caller == "last_icmp" || $page_caller == "last_any" )
         {
           $limit_start = 0;
           $limit_offset = $last_num_alerts; 
           $tmp_num = $last_num_alerts;
         }
         else
         {
           $tmp_num = $num_alert_in_query;
           $limit_start = $limit_offset = -1;
         }
      }
      else if ( $context == PAGE_ALERT_DISPLAY )
      {
         $tmp_num = 1;
         $limit_start = $limit_offset = -1;
      }
      else if ( $context == PAGE_STAT_ALERTS )  /* on unique alerts page */
      {
         if (  $page_caller == "most_frequent" || $page_caller == "last_alerts" )
         {
            $limit_start = 0;
            if ( $page_caller == "last_alerts" )  $limit_offset = $tmp_num = $last_num_ualerts;
            if ( $page_caller == "most_frequent" ) $limit_offset = $tmp_num = $freq_num_alerts;
         }
         else
         {
            $tmp_num = $num_alert_in_query;
            $limit_start = $limit_offset = -1;
         }
      }
      else if ( $context == PAGE_STAT_SENSOR )  /* on unique sensor page */
      {
         $tmp_num = $num_alert_in_query;
         $limit_start = $limit_offset = -1;
      }
      else if ( $context == PAGE_QRY_AG )  /* on the AG page */
      {
         $tmp_num = $num_alert_in_query;
         $limit_start = $limit_offset = -1;
      }
    
      ProcessSelectedAlerts($action, $action_op, $action_arg, $action_param,
                            $context,
                            $action_lst,
                            $tmp_num,/*&$num_alert_in_query*/
                            $action_sql,
                            $db, $limit_start, $limit_offset);
      $num_alert_in_query = $tmp_num;
   }

   /* In unique alert or unique sensor:
    * Reset the "$submit" to be a view # to mimic a browsing operation
    * so the alerts are re-displayed after the operation completes
    */
   if ( $context == PAGE_STAT_ALERTS || $context == PAGE_STAT_SENSOR )
      $action_op = $current_view;   

   /* In Query results, alert lookup, or AG maintenance:
    * Reset the "$submit" to be a view # to mimic a browsing operation
    * However if in alert lookup, set "$submit" to be the $caller (i.e. sid, cid)
    */ 
   if ( ($context == PAGE_QRY_ALERTS) || ($context == PAGE_QRY_AG) )
   {
     /* Reset $submit to a browsing view # */
     if ( (strstr($page_caller, "#") == "") && ($action_op != "Query DB") )
     {
        $action_op = $current_view;
     }
     /* but if in Alert Lookup, set $submit to (sid,cid) */
     //else if ($action_op != "Query DB" )
     else
     {
        $action_op = $page_caller;
     }
   }

   /* If action from AG maintenance, set operation to 'view' after
    * running the specified action;
    */
   if ( $context == PAGE_QRY_AG )
   {
     $ag_action = "view";
   } 
}

function GetActionDesc($action_name)
{
  $action_desc["ag_by_id"] = "ADD to AG (by ID)";
  $action_desc["ag_by_name"] = "Add to AG (by Name)";
  $action_desc["clear_alert"] = "Clear from AG";
  $action_desc["del_alert"] = "Delete alert(s)";
  $action_desc["email_alert"] = "Email alert(s) (full)";
  $action_desc["email_alert2"] = "Email alert(s) (summary)";
  $action_desc["csv_alert"] = "Email alert(s) (csv)";
  $action_desc["archive_alert"] = "Archive alert(s) (copy)";
  $action_desc["archive_alert2"] = "Archive alert(s) (move)";

  return $action_desc[$action_name];
}

function ProcessSelectedAlerts($action, &$action_op, $action_arg, $action_param,
                               $context,
                               $action_lst,
                               &$num_alert,
                               $action_sql,
                               $db, $limit_start=-1, $limit_offset=-1 )
{

  GLOBAL $debug_mode;
  $action_cnt = 0;
  $dup_cnt = 0;

  $action_desc = "";
  if ( $action == "ag_by_id" )          $action_desc = "ADD-by-ID to AG";
  else if ($action == "ag_by_name")     $action_desc = "ADD-by-name to AG";
  else if ($action == "del_alert")      $action_desc = "DELETE";
  else if ($action == "email_alert")    $action_desc = "EXPORT-full";
  else if ($action == "email_alert2")   $action_desc = "EXPORT-summary";
  else if ($action == "clear_alert")    $action_desc = "CLEAR";
  else if ($action == "archive_alert")  $action_desc = "ARCHIVE-copy";
  else if ($action == "archive_alert2") $action_desc = "ARCHIVE-move";
 
  if ( $action == "" )  
    return;

  if ( $debug_mode > 0 )
  {
     echo "<BR>==== $action_desc Alerts ========<BR>
           num_alert = $num_alert<BR>
           action_sql = $action_sql<BR>
           action_op = $action_op<BR>
           action_arg = $action_arg<BR>
           action_param = $action_param<BR>
           context = $context<BR>
           limit_start = $limit_start<BR>
           limit_offset = $limit_offset<BR>";
  }

  /* Depending from which page/listing the action was spawned,
   * the entities selected may not necessarily be specific
   * alerts.  For example, sensors or alert names may be
   * selected.  Thus, each one of these entities referred to as 
   * alert_blobs, the specific alerts associated with them must
   * be explicitly extracted.  This blob structures SQL must be
   * used to extract the list, where the passed selected keyed
   * will be the criteria in this SQL.
   *
   * Note: When acting on any page where "Entire Query" is
   * selected this is also a blob. 
   */ 

  /* if only manipulating specific alerts -- 
   * (in the Query results or AG contents list)
   */
  if ( ($context == PAGE_QRY_ALERTS) || ($context == PAGE_QRY_AG) ||
       ($context == PAGE_ALERT_DISPLAY) )
  {  
     $num_alert_blobs = 1;
     if ( $action_op == "Entire Query" ) 
        $using_blobs = true;
     else
        $using_blobs = false;
  }
  /* else manipulating by alert blobs -- e.g. signature, sensor */
  else
  {
     $num_alert_blobs = $num_alert;
     $using_blobs = true;
  }

  $blob_alert_cnt = $num_alert;

  if ( $debug_mode > 0 )   echo "using_blobs = $using_blobs<BR>";

  /* ******* SOME PRE ACTION ********* */
  $function_pre = "Action_".$action."_Pre";
  $action_ctx = $function_pre($action_arg, $action_param, $db);
 
  if ( $debug_mode > 0 ) 
     echo "<BR>Gathering elements from ".sizeof($action_lst)." alert blobs<BR>";

  /* Loop through all the alert blobs */
  for ( $j = 0; $j < $num_alert_blobs; $j++ )
  {
      /* If acting on a blob construct, or on the "Entire Query"
       * of a non-blob structure (which is equivalent to 1-blob)
       * run a query to get the results.  
       *
       * For each unique blob construct two SQL statement are
       * generated: one to retrieve the alerts ($sql), and another
       * to count the number of actual alerts in this blob
       */
      if ( ($using_blobs) /*&& isset($action_lst[$j])*/ )
      {
         $sql = $action_sql;

         /* Unique Signature listing */
         if ( $context == PAGE_STAT_ALERTS )
         {
            if ( !isset($action_lst[$j]) ) $tmp = -1;  else $tmp = $action_lst[$j];            
            $sql = "SELECT acid_event.sid, acid_event.cid ".$action_sql." AND signature='".
                     $tmp."'"; //rawurldecode($action_lst[$j])."'";
            $sql2 = "SELECT count(acid_event.sid) ".$action_sql." AND signature='".
                     $tmp."'"; //rawurldecode($action_lst[$j])."'";            
         }
         /* Unique Sensor listing */
         else if ( $context == PAGE_STAT_SENSOR )
         {
            if ( !isset($action_lst[$j]) ) $tmp = -1;  else $tmp = $action_lst[$j];
            $sql = "SELECT sid, cid FROM acid_event WHERE sid=".$tmp;  /*$alert_del[$j];*/  
            $sql2 = "SELECT count(sid) FROM acid_event WHERE sid=".$tmp; /*$alert_del[$j];*/
         }
         /* Single Alert listing */
         //if ( $context == PAGE_ALERT_DISPLAY )
         //{
            //if ( !isset($action_lst[$j]) ) $tmp = -1;  else $tmp = $action_lst[$j];            
            //$sql = "SELECT acid_event.sid, acid_event.cid ".$action_sql." AND signature='".
            //         $tmp."'"; //rawurldecode($action_lst[$j])."'";
            //$sql2 = "SELECT count(acid_event.sid) ".$action_sql." AND signature='".
            //         $tmp."'"; //rawurldecode($action_lst[$j])."'";            
         //}
         /* if acting on alerts by signature or sensor, count the 
          * the number of alerts 
          */
         if ( ($context == PAGE_STAT_ALERTS) || ($context == PAGE_STAT_SENSOR)
              /*($context == PAGE_QRY_ALERTS) || ($context == PAGE_QRY_AG)*/ )
         {
            $result2 = $db->acidExecute($sql2);
            $myrow2 = $result2->acidFetchRow();
            //$num_alert = $myrow2[0];
            $blob_alert_cnt = $myrow2[0];
            $result2->acidFreeRows();
         }

         if ( $debug_mode > 0 )
            echo "$j = [using SQL $num_alert for blob ".
                       ( isset($action_lst[$j]) ? $action_lst[$j] : "")."]: $sql<BR>";

         /* Execute the SQL to get the alert listing */
         if ( $limit_start == -1 )
            $result = $db->acidExecute($sql, -1, -1, false);
         else
            $result = $db->acidExecute($sql, $limit_start, $limit_offset, false);

         if ( $db->acidErrorMessage() != "" )
         {
              ErrorMessage("Error retrieving alert list to $action_desc");
              if ( $debug_mode > 0 )  ErrorMessage($db->acidErrorMessage());
              return -1;
         }           
     }
     //else if ( !isset($action_lst[$j]) )
     //{
     //   $blob_alert_cnt = 0;
     //}

     /* Limit the number of alerts acted on if in "top x alerts" */
     if ( $limit_start != -1 )    $blob_alert_cnt = $limit_offset; //$num_alert = $limit_offset;     

     /* Loop through the specific alerts in a particular blob */
     for ( $i = 0; $i < $blob_alert_cnt; $i++ )
     {
        /* Verify that have a selected alert */
        if ( isset($action_lst[$i]) || $using_blobs )
        {
           /* If acting on a blob */
           if ( $using_blobs )
           { 
              $myrow = $result->acidFetchRow();
              $sid = $myrow[0];
              $cid = $myrow[1];  
           }
           else 
              GetQueryResultID($action_lst[$i], $seq, $sid, $cid);

           if ( $sid != "" )
           {
              if ( $debug_mode > 0 ) echo $sid.' - '.$cid.'<BR>';

              /* **** SOME ACTION on (sid, cid) ********** */
              $function_op = "Action_".$action."_op";
              $tmp = $function_op($sid, $cid, $db, $action_arg, 
                               &$action_ctx);

              if ( $tmp == 0 )
              {
                ++$dup_cnt; 
              }
              else if ( $tmp == 1 )
              {
                ++$action_cnt; 
              }
           } 
        }
     }

     /* If acting on a blob, free the result set used to get alert list */
     if ( $using_blobs )
        $result->acidFreeRows();
  }

  /* **** SOME POST-ACTION ******* */
  $function_post = "Action_".$action."_post";
  $function_post($action_arg, $action_ctx, $db, $num_alert, $action_cnt);

  if ( $dup_cnt > 0 )
     ErrorMessage("Ignored ".$dup_cnt." duplicate alert(s)");

  if ( $action_cnt > 0 )
  {
    /* 
     *  Print different message if alert action units (e.g. sensor
     *  or signature) are not individual alerts
     */ 
    if ( ($context == PAGE_STAT_ALERTS) || ($context == PAGE_STAT_SENSOR) )
        ErrorMessage("Successful $action_desc - on $action_cnt alerts(s) (in $num_alert_blobs blobs)");
     else
        ErrorMessage("Successful $action_desc - ".$action_cnt." alert(s)");
  }
  else if ( $action_cnt == 0 )
     ErrorMessage("No alerts were selected or the $action_desc was not successful"); 

  if ( $debug_mode > 0 )
  {
     echo "-------------------------------------<BR>
          action_cnt = $action_cnt<BR>
          dup_cnt = $dup_cnt<BR>
          num_alert = $num_alert<BR> 
          ==== $action_desc Alerts END ========<BR>";
  }
}

/* 
 *
 *  function Action_*_Pre($action, $action_arg)
 *
 *  RETURNS: action context
 */

/*
 *  function Action_*_Op($sid, $cid, &$db, $action_arg, &$action_ctx)
 *
 *  RETURNS: 1: successful act on an alert
 *           0: ignored (duplicate) or error
 */

/*
 * function Action_post($action_arg, &$action_ctx, $db, &$num_alert, $action_cnt)
 *
 */

/* ADD to AG (by ID) ****************************************/
function Action_ag_by_id_Pre($action_arg, $action_param, $db)
/*
 * $action_arg: a AG ID
 */
{
  if ( VerifyAGID($action_arg, $db ) == 0 )
     ErrorMessage("Unknown AG ID specified (AG probably does not exist)");

  return null;
}

function Action_ag_by_id_Op($sid, $cid, $db, $action_arg, &$ctx)
{
   $sql2 = "INSERT INTO acid_ag_alert (ag_id, ag_sid, ag_cid) ".
           "VALUES ('".$action_arg."','".$sid."','".$cid."');";
   $db->acidExecute($sql2, -1, -1, false);

   if ( $db->acidErrorMessage() != "" )
      return 0; 
   else
      return 1;
}

function Action_ag_by_id_Post($action_arg, &$action_ctx, $db, &$num_alert, $action_cnt)
{
  /* none */
}

/* ADD to AG (by Name ) *************************************/
function Action_ag_by_name_Pre($action_arg, $action_param, $db)
/*
 * $action_arg: a AG name
 */
{
  return GetAGIDbyName($action_arg, $db);
}

function Action_ag_by_name_Op($sid, $cid, $db, $action_arg, &$ctx)
{
   $sql2 = "INSERT INTO acid_ag_alert (ag_id, ag_sid, ag_cid) ".
           "VALUES ('".$ctx."','".$sid."','".$cid."');";
   $db->acidExecute($sql2, -1, -1, false);

   if ( $db->acidErrorMessage() != "" )
      return 0; 
   else
      return 1;
}

function Action_ag_by_name_Post($action_arg, &$action_ctx, $db, &$num_alert, $action_cnt)
{
 /* none */
}

/* DELETE **************************************************/
function Action_del_alert_pre($action_arg, $action_param, $db)
{
  GLOBAL $num_alert_blobs;

  return $num_alert_blobs;
}

function Action_del_alert_op($sid, $cid, $db, $action_arg, &$ctx)
{
  return PurgeAlert($sid, $cid, $db);
}

function Action_del_alert_post($action_arg, &$action_ctx, $db, &$num_alert, $action_cnt)
{
  GLOBAL $context;

  /* If unit of delete (i.e. by sensor, signature) is not individual alerts, 
   * decrement by units not individual alerts 
   */
  if ( ($context == 2) || ($context == 3) )
    $num_alert -= $action_ctx;
  else  
    $num_alert -= $action_cnt;
}

/* Email ***************************************************/
function Action_email_alert_pre($action_arg, $action_param, $db)
{
  return "";
}

function Action_email_alert_op($sid, $cid, $db, $action_arg, &$ctx)
{
  $tmp = ExportPacket($sid, $cid, $db);
  $ctx = $ctx.$tmp;

  if ( $tmp == "" )
     return 0;
  else
     return 1;
}

function Action_email_alert_post($action_arg, &$action_ctx, $db, &$num_alert, $action_cnt)
{
  GLOBAL $ACID_VERSION, 
         $action_email_from, $action_email_mode, $action_email_subject, $action_email_msg;

  $mail_subject = $action_email_subject;
  $mail_content = $action_email_msg.
                  "Generated by ACID v$ACID_VERSION on ".date("D F d, Y H:i:s",time())."\n";
  $mail_recip = $action_arg;
  $mail_header = "From: ".$action_email_from;

  /* alerts inline */
  if ( $action_email_mode == 0 )
  {
    $body = $mail_content."\n".$action_ctx; 
  }
  /* alerts as attachment */
  else
  {
    $boundary = strtoupper(md5(uniqid(time())));
    $file_name = "acid_report_".date("Ymd",time()).".log";

    $mail_header .= "\nMIME-Version: 1.0";
    $mail_header .= "\nContent-Type: multipart/mixed; boundary=\"$boundary\"\n\n";
    $mail_header .= "\nContent-transfer-encoding: 7bit";
    $mail_header .= "\nX-attachments: \"$file_name\"\n\n";

    $body = "--$boundary";
    $body .= "\nContent-Type: text/plain";
    $body .= "\n\n$mail_content";
    $body .= "\n--$boundary";
    $body .= "\nContent-Type: text/plain; name=\"$file_name\"";
    $body .= "\nContent-Transfer-Encoding: 8bit";
    $body .= "\nContent-Disposition: attachment; filename=\"$file_name\"";
    $body .= "\n\n$mail_content\n\n$action_ctx";
    $body .= "\n--$boundary--\n";
  }

  if ( !send_email($mail_recip, $mail_subject, $body, $mail_header) )
     ErrorMessage("EXPORT ERROR: Could not send exported alerts to '".$mail_recip."'.  Check the mail configuration in PHP."); 
}

/* Email ***************************************************/
function Action_email_alert2_pre($action_arg, $action_param, $db)
{
  return "";
}

function Action_email_alert2_op($sid, $cid, $db, $action_arg, &$ctx)
{
  $tmp = ExportPacket_summary($sid, $cid, $db);
  $ctx = $ctx.$tmp;

  if ( $tmp == "" )
     return 0;
  else
     return 1;
}

function Action_email_alert2_post($action_arg, &$action_ctx, $db, &$num_alert, $action_cnt)
{
  Action_email_alert_post($action_arg, &$action_ctx, $db, &$num_alert, $action_cnt);
}

/* CSV    ***************************************************/
function Action_csv_alert_pre($action_arg, $action_param, $db)
{
  return "";
}

function Action_csv_alert_op($sid, $cid, $db, $action_arg, &$ctx)
{
  $tmp = ExportPacket_summary($sid, $cid, $db, 1);
  $ctx = $ctx.$tmp;

  if ( $tmp == "" )
     return 0;
  else
     return 1;
}

function Action_csv_alert_post($action_arg, &$action_ctx, $db, &$num_alert, $action_cnt)
{
  Action_email_alert_post($action_arg, &$action_ctx, $db, &$num_alert, $action_cnt);
}

/* Clear ***************************************************/
function Action_clear_alert_pre($action_arg, $action_param, $db)
{
  return $action_param;
}

function Action_clear_alert_op($sid, $cid, $db, $action_arg, &$ctx)
{
  $cnt = 0;
  $clear_table_list[0] = "acid_ag_alert";

  for ( $j = 0; $j < count($clear_table_list); $j++ )
  {
     $sql2 = "DELETE FROM ".$clear_table_list[$j].
             " WHERE ag_sid=".$sid." AND ag_cid=".$cid." AND ag_id=".$action_arg;//$ctx;
     $db->acidExecute($sql2);

     if ( $db->acidErrorMessage() != "" )
        ErrorMessage("Error Deleting Alert"." ".$del_table_list[$j]);
     else 
        ++$cnt; 
  }

  return $cnt; 
}

function Action_clear_alert_post($action_arg, &$action_ctx, $db, &$num_alert, $action_cnt)
{
  $num_alert -= $action_cnt;
}

/* Archive ***************************************************/
function Action_archive_alert_pre($action_arg, $action_param, $db)
{
  GLOBAL $DBlib_path, $DBtype,
         $archive_dbname, $archive_host, $archive_port, 
         $archive_user, $archive_password;

  $db2 = NewACIDDBConnection($DBlib_path, $DBtype);
  $db2->acidConnect($archive_dbname, $archive_host, $archive_port, 
                    $archive_user, $archive_password);

  return $db2;
}

function Action_archive_alert_op($sid, $cid, &$db, $action_arg, &$ctx)
{
  GLOBAL $DBlib_path, $DBtype, $db_connect_method,
         $alert_dbname, $alert_host, $alert_port, 
         $alert_user, $alert_password,
         $archive_dbname, $archive_host, $archive_port, 
         $archive_user, $archive_password,
         $debug_mode;

   $db2 = &$ctx;

   $insert_sql = array(20);
   $sql_cnt = 0;
   $archive_cnt = 0;

   $sql = "SELECT hostname, interface, filter, detail, encoding FROM sensor ".
          "WHERE sid=$sid";
   $tmp_result = $db->acidExecute($sql);
   $tmp_row = $tmp_result->acidFetchRow();

   if ( $tmp_row )
   {
      $sql = "INSERT INTO sensor (sid,hostname,interface,filter,detail,encoding) ".
             "VALUES ($sid,'".$tmp_row[0]."','".$tmp_row[1]."','".$tmp_row[2]."','".
             $tmp_row[3]."','".$tmp_row[4]."')";
      if ($db->DB_type == "mssql")
      {
         $insert_sql[$sql_cnt++] = "SET IDENTITY_INSERT sensor ON";
      }
      $insert_sql[$sql_cnt++] = $sql;
      if ($db->DB_type == "mssql")
      {
         $insert_sql[$sql_cnt++] = "SET IDENTITY_INSERT sensor OFF";
      }
      $tmp_result->acidFreeRows();
   }

   $sql = "SELECT signature, timestamp FROM event WHERE sid=$sid AND cid=$cid";
   $tmp_result = $db->acidExecute($sql);   
   $tmp_row = $tmp_result->acidFetchRow();
   $sig = $tmp_row[0];
   $timestamp = $tmp_row[1];
   $tmp_result->acidFreeRows();

   $sig_name = "";
   if ( $db->acidGetDBVersion() < 100 )
   {
      $sql = "INSERT INTO event (sid,cid,signature,timestamp) VALUES ".
             "($sid, $cid, '".$sig."', '".$timestamp."')"; 
      $insert_sql[$sql_cnt++] = $sql;
   }
   /* Catch alerts with a null signature (e.g. with use of tag rule option) */
   else if ( $sig != "" )
   {
      $sig_name = GetSignatureName($sig, $db);

      if ( $db->acidGetDBVersion() >= 103 )
      {
         $result = $db->acidExecute("SELECT sig_class_id, sig_priority,
                                     sig_rev, sig_sid FROM signature
                                     WHERE sig_id = ".$sig);
         $row = $result->acidFetchRow();
         $sig_class_id = $row[0];
         $sig_class_name = GetSigClassName($sig_class_id, $db);
         $sig_priority = $row[1];
         $sig_rev = $row[2];
         $sig_sid = $row[3];
      }

      $MAX_REF_CNT = 6;
      $sig_reference = array($MAX_REF_CNT);
      $sig_reference_cnt = 0;
      $sql = "SELECT ref_id FROM sig_reference WHERE sig_id=".$sig;
      $tmp_result = $db->acidExecute($sql);   
 
      while ( (($tmp_row = $tmp_result->acidFetchRow()) != "") &&
              ($sig_reference_cnt < $MAX_REF_CNT) )
      {
         $ref_id = $tmp_row[0];

         $sql = "SELECT ref_system_id, ref_tag FROM reference ".
                "WHERE ref_id=".$ref_id;
         $tmp_result2 = $db->acidExecute($sql);
         $tmp_row2 = $tmp_result2->acidFetchRow();   

         $sig_reference[$sig_reference_cnt++] = array ($tmp_row2[0],
                                                       $tmp_row2[1],
                                                       GetRefSystemName($tmp_row2[0], $db));
         $tmp_result2->acidFreeRows();
         //$tmp_row = $tmp_result->acidFetchRow();
      }
      $tmp_result->acidFreeRows();

      if ( $debug_mode > 1 )
      {
        echo "<PRE>";
        print_r($sig_reference);
        echo "</PRE>";
      }
   }

   $sql = "SELECT ip_src,
                  ip_dst,
                  ip_ver, ip_hlen, ip_tos, ip_len, ip_id, ip_flags,
                  ip_off, ip_ttl, ip_proto, ip_csum ".
          "FROM iphdr WHERE sid=$sid AND cid=$cid";
   $tmp_result = $db->acidExecute($sql);   
   $tmp_row = $tmp_result->acidFetchRow();
   if ( $tmp_row )
   {
      $sql = "INSERT INTO iphdr (sid,cid,
                              ip_src,
                              ip_dst,
                              ip_ver,ip_hlen,ip_tos,ip_len,ip_id,ip_flags,
                              ip_off,ip_ttl,ip_proto,ip_csum) VALUES ".
          "($sid, $cid, '".$tmp_row[0]."', '".$tmp_row[1]."',".
          "'".$tmp_row[2]."','".$tmp_row[3]."','".$tmp_row[4]."','".$tmp_row[5]."',".
          "'".$tmp_row[6]."','".$tmp_row[7]."','".$tmp_row[8]."','".$tmp_row[9]."',".
          "'".$tmp_row[10]."','".$tmp_row[11]."')";
      $tmp_result->acidFreeRows();
      $insert_sql[$sql_cnt++] = $sql;

      $ip_proto = $tmp_row[10];
   }
   else
      $ip_proto = -1;

   if ( $ip_proto == 6 )
   {
      $sql = "SELECT tcp_sport, tcp_dport, tcp_seq, tcp_ack, tcp_off,
                  tcp_res, tcp_flags, tcp_win, tcp_csum, tcp_urp ".
             "FROM tcphdr WHERE sid=$sid AND cid=$cid";
      $tmp_result = $db->acidExecute($sql);   
      $tmp_row = $tmp_result->acidFetchRow();
      $sql = "INSERT INTO tcphdr (sid,cid,
                               tcp_sport, tcp_dport, tcp_seq,
                               tcp_ack, tcp_off, tcp_res, tcp_flags,
                               tcp_win, tcp_csum, tcp_urp) VALUES ".
          "($sid, $cid, '".$tmp_row[0]."', '".$tmp_row[1]."',".
          "'".$tmp_row[2]."','".$tmp_row[3]."','".$tmp_row[4]."','".$tmp_row[5]."',".
          "'".$tmp_row[6]."','".$tmp_row[7]."','".$tmp_row[8]."','".$tmp_row[9]."')";
      $tmp_result->acidFreeRows();
      $insert_sql[$sql_cnt++] = $sql;
   }
   else if ( $ip_proto == 17 )
   {
      $sql = "SELECT udp_sport, udp_dport, udp_len, udp_csum ".
             "FROM udphdr WHERE sid=$sid AND cid=$cid";
      $tmp_result = $db->acidExecute($sql);   
      $tmp_row = $tmp_result->acidFetchRow();
      $sql = "INSERT INTO udphdr (sid,cid, udp_sport, udp_dport, ".
             "udp_len, udp_csum) VALUES ".
             "($sid, $cid, '".$tmp_row[0]."', '".$tmp_row[1]."',".
             "'".$tmp_row[2]."','".$tmp_row[3]."')";
      $tmp_result->acidFreeRows();
      $insert_sql[$sql_cnt++] = $sql;
   }
   else if ( $ip_proto == 1 )
   {
      $sql = "SELECT icmp_type, icmp_code, icmp_csum, icmp_id, icmp_seq ".
             "FROM icmphdr WHERE sid=$sid AND cid=$cid";
      $tmp_result = $db->acidExecute($sql);   
      $tmp_row = $tmp_result->acidFetchRow();
      $sql = "INSERT INTO icmphdr (sid,cid,icmp_type,icmp_code,".
             "icmp_csum,icmp_id,icmp_seq) VALUES ".
             "($sid, $cid, '".$tmp_row[0]."', '".$tmp_row[1]."',".
             "'".$tmp_row[2]."','".$tmp_row[3]."','".$tmp_row[4]."')";
      $tmp_result->acidFreeRows();
      $insert_sql[$sql_cnt++] = $sql;
   }

   $sql = "SELECT data_payload FROM data WHERE sid=$sid AND cid=$cid";
   $tmp_result = $db->acidExecute($sql);   
   $tmp_row = $tmp_result->acidFetchRow();
   if ( $tmp_row )
   {
      $sql = "INSERT INTO data (sid,cid, data_payload) VALUES ".
             "($sid, $cid, '".$tmp_row[0]."')";
      $tmp_result->acidFreeRows(); 
      $insert_sql[$sql_cnt++] = $sql;
   }

   $sql = "SELECT optid, opt_proto, opt_code, opt_len, opt_data ".
          "FROM opt WHERE sid=$sid AND cid=$cid";
   $tmp_result = $db->acidExecute($sql);

   while ( (($tmp_row = $tmp_result->acidFetchRow()) != "") 
           && ($opt_cnt < 19) )
   {
      $sql = "INSERT INTO opt (sid,cid,optid,opt_proto,".
             "opt_code,opt_len,opt_data) VALUES ".
             "($sid, $cid, '".$tmp_row[0]."', '".$tmp_row[1]."',".
              "'".$tmp_row[2]."','".$tmp_row[3]."','".$tmp_row[4]."')";
      
     $tmp_result->acidFreeRows();
     $insert_sql[$sql_cnt++] = $sql;
   }

  $archive_cnt = 0;

  /* If signatures are normalized (schema v100+), then it is 
   * impossible to merely copy the event table completely.  Rather
   * the signatures must be written to the archive DB, and their
   * new ID must be written into the archived event table
   */
  if ( $db->acidGetDBVersion() >= 100 )
  {
     /* Check whether this signature already exists in
      * the archive DB.  If so, get the ID, otherwise first
      * write the signature into the archive DB, and then
      * get the newly inserted ID
      */
     $sig_id = GetSignatureID($sig_name, $db2);
     if ( $sig_id == "" && $sig_name != "" )
     {
        if ( $db->acidGetDBVersion() >= 103 )
        {
           if ( $sig_class_id == "" )  
           {
              $sig_class_id = 'NULL';
           }
           else
           {
              /* get the ID of the classification */
              $tmp_sql = "SELECT sig_class_id FROM sig_class WHERE ".
                         "sig_class_name = '".$sig_class_name."'";
              $tmp_result = $db2->acidExecute($tmp_sql);
              $tmp_row = $tmp_result->acidFetchRow();
              $tmp_result->acidFreeRows();

              if ( $tmp_row == "" )
              {
                  $sql = "INSERT INTO sig_class (sig_class_name) ".
                         " VALUES ('".$sig_class_name."')";
                  $db2->acidExecute($sql);
                  $sig_class_id = $db2->acidInsertID();
              }
              else
              {
                  $sig_class_id = $tmp_row[0];
              }
           }

           if ( $sig_priority == "" )  $sig_priority = 'NULL';
           
           $sql = "INSERT INTO signature ".
                  "(sig_name, sig_class_id, sig_priority, sig_rev, sig_sid) ". 
                  "VALUES ('$sig_name',".$sig_class_id.", ".$sig_priority.",".
                  "'".$sig_rev."', '".$sig_sid."')";
        }
        else
           $sql = "INSERT INTO signature (sig_name) VALUES ('".$sig_name."')";

        $db2->acidExecute($sql);
        $sig_id = $db2->acidInsertID();     
     }

     /* add reference information */
     for ( $j = 0; $j < $sig_reference_cnt; $j++ )
     {
        /* get the ID of the reference system */
        $tmp_sql = "SELECT ref_system_id FROM reference_system WHERE ".
                   "ref_system_name = '".$sig_reference[$j][2]."'";
        $tmp_result = $db2->acidExecute($tmp_sql);
        $tmp_row = $tmp_result->acidFetchRow();
        $tmp_result->acidFreeRows();

        if ( $tmp_row == "" )
        {
           $sql = "INSERT INTO reference_system (ref_system_name) ".
                  " VALUES ('".$sig_reference[$j][2]."')";
           $db2->acidExecute($sql);
           $ref_system_id = $db2->acidInsertID();          
        }
        else
        {
           $ref_system_id = $tmp_row[0];
        }

        $sql = "SELECT ref_id FROM reference WHERE ".
               "ref_system_id='".$ref_system_id."' AND ".
               "ref_tag='".$sig_reference[$j][1]."'";

        if ($db->DB_type == "mssql")
        {
           /* MSSQL doesn't allow "=" with TEXT data types, but it does
            * allow LIKE. By escaping all the characters in the search
            * string, we make LIKE work like =.
            */
           $mssql_kludge_sig_tag = MssqlKludgeValue($sig_reference[$j][1]);
           $sql = "SELECT ref_id FROM reference WHERE ".
                  "ref_system_id='".$ref_system_id."' AND ".
                  "ref_tag LIKE '".$mssql_kludge_sig_tag."'";
        }

        $tmp_result = $db2->acidExecute($sql);
        $tmp_row = $tmp_result->acidFetchRow();

        if ( $tmp_row != "")
        {
           $ref_id = $tmp_row[0];
           $tmp_result->acidFreeRows();
        }
        else
        {
           $sql = "INSERT INTO reference (ref_system_id, ref_tag) ".
                  " VALUES (".$sig_reference[$j][0].",'".
                              $sig_reference[$j][1]."')";
           $db2->acidExecute($sql);
           $ref_id = $db2->acidInsertID(); 
        }        

        if ( ($ref_id != "") && ($ref_id > 0) )
        {
           $sql = "INSERT INTO sig_reference (sig_id, ref_seq, ref_id) ".
                  "VALUES (".$sig_id.",".($j+1).",".$ref_id.")";
           
           $insert_sql[$sql_cnt++] = $sql;
        }
     }

     $sql = "INSERT INTO event (sid,cid,signature,timestamp) VALUES ".
             "($sid, $cid, '".$sig_id."', '".$timestamp."')"; 
     $insert_sql[$sql_cnt++] = $sql;
  }

  if ( $debug_mode > 1 )
  {
    echo "<PRE>";
    print_r($insert_sql);
    echo "</PRE>";
  }
 
  /* Write Alerts into archive database */
  for ( $j = 0; $j < count($insert_sql); $j++)
  {
     $db2->acidExecute($insert_sql[$j], -1, -1, false);
     if ( $db2->acidErrorMessage() == "" )
       ++$archive_cnt;
     else
     {
        if ($db2->DB_type == "mssql")
        {
          // MSSQL must be reset in this case, or else the same error message
          //  will be returned for all subsequent INSERTS, even though they
          //  succeed.
          $db2->acidConnect($archive_dbname, $archive_host, $archive_port,
                         $archive_user, $archive_password);
        }

        /* Sometimes INSERTs will be made for records which
         * already exist (e.g. sensor or sig_reference table).
         * When we get such an error, assume that this is ok
         */
        if ( strstr($insert_sql[$j], "INSERT INTO sensor") ||
             strstr($insert_sql[$j], "INSERT INTO sig_reference") ||
             strstr($insert_sql[$j], "SET IDENTITY_INSERT") )
           ++$archive_cnt;
        else
        { 
          if ( $debug_mode > 1 )
            ErrorMessage("Archive error:".$db2->acidErrorMessage()."<BR>".
                         $insert_sql[$j]);

          /* When detect a duplicate then stop */
          break;
        }
     }
  } 

  /* Check if all data was written to archive database,
   * before purging the alert from the current database
   */
  if ( $archive_cnt == $sql_cnt )
     $archive_cnt = 1;  //PurgeAlert($sid, $cid, $db);
  else
     $archive_cnt = 0;

  return $archive_cnt;  
}

function Action_archive_alert_post($action_arg, &$action_ctx, $db, &$num_alert, $action_cnt)
{
  /* none */
}

function Action_archive_alert2_pre($action_arg, $action_param, $db)
{
  return Action_archive_alert_pre($action_arg, $action_param, $db);
}


function Action_archive_alert2_op($sid, $cid, &$db, $action_arg, &$ctx)
{
  $cnt = $cnt2 = 0;

  $cnt = Action_archive_alert_op($sid, $cid, $db, $action_arg, $ctx);
  if ( $cnt == 1 )
     $cnt2 = PurgeAlert($sid, $cid, $db);

  /* Note: the inconsistent state possible if alerts are copied to
   * the archive DB, but not deleted 
   */

  if ( ($cnt == 1) && ($cnt2 == 1) )
    return 1;
  else
    return 0;
}

function Action_archive_alert2_post($action_arg, &$action_ctx, $db, &$num_alert, $action_cnt)
{
  /* Reset the alert count that the query is re-executed to reflect the deletion */
  $num_alert -= $action_cnt;
}


/* This function accepts a (sid,cid) and purges it
 * from the database 
 *
 * - (sid,cid) : sensor, event id pair to delete
 * - db        : database handle
 *
 * RETURNS: 0 or 1 depending on whether the alert was deleted
 */
function PurgeAlert($sid, $cid, $db)
{
  $del_table_list = array("event",
                          "iphdr",
                          "tcphdr",
                          "udphdr",
                          "icmphdr",
                          "opt",
                          "data",
                          "acid_ag_alert",
                          "acid_event");

  $del_cnt = 0;

  for ( $k = 0; $k < count($del_table_list); $k++ )
  {
     /* If trying to add to an ACID table append ag_ to the fields */
     if ( strstr($del_table_list[$k], "acid_ag") == "" )
        $sql2 = "DELETE FROM ".$del_table_list[$k]." WHERE sid=".$sid." AND cid=".$cid;
     else
        $sql2 = "DELETE FROM ".$del_table_list[$k]." WHERE ag_sid=".$sid." AND ag_cid=".$cid;
     
     $db->acidExecute($sql2);

     if ( $db->acidErrorMessage() != "" )
        ErrorMessage("Error Deleting Alert"." ".$del_table_list[$k]);
     else if ( $k == 0 ) 
        $del_cnt = 1; 
  }

  return $del_cnt;  
}

/* This function accepts a TO, SUBJECT, BODY, and MIME information and
 * sends the appropriate message 
 *
 * RETURNS: boolean on success of sending message 
 *
 */

function send_email($to, $subject, $body, $mime)
{
  if ($to != "")
  {
     return mail($to, $subject, $body, $mime);
  }
  else
  {
     ErrorMessage("MAIL ERROR: No recipient Specified");
     return false;
  }
} 

?>
