//
// $Id: RPMInfoProbe.cpp,v 1.2 2004/10/12 18:04:16 bakerj Exp $
//
//************************** Property of the MITRE Corporation ***************************//
//
// Copyright (c) 2003 - The MITRE Corporation
//
// This file is part of the Query-based Network Assessment project.
//
// The Query-based Network Assessment 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.
//
// The Query-based Network Assessment 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
// Query-based Network Assessment; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
//****************************************************************************************//

#include "RPMInfoProbe.h"

//****************************************************************************************//
//								RPMInfoProbe Class										  //	
//****************************************************************************************//
RPMInfoProbe::RPMInfoProbe()
{
  //------------------------------------------------------------------------------------//
  //
  //  ABSTRACT
  // Do nothing for now.
  //------------------------------------------------------------------------------------//
 
}

RPMInfoProbe::~RPMInfoProbe()
{
  //------------------------------------------------------------------------------------//
  //
  //  ABSTRACT
  // Do nothing for now.
  //------------------------------------------------------------------------------------//
 
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  Public Members  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
pdVector RPMInfoProbe::Run(ProbeData *probeDataIn)
{
  //------------------------------------------------------------------------------------//
  //
  //  ABSTRACT
  //
  //  Take a probe data object determine if there is a pattern match specified. 
  //  If no pattern match just use the name provided and gather info. If a pattern
  //  match go through all names and get each matching name into a new data object
  //  then get info for each name.
  //------------------------------------------------------------------------------------//
  RPMInfoData *dataIn = (RPMInfoData*)probeDataIn;
  pdVector resultsVector;

  // Test for pattern match
  if(dataIn->name->type == LITTERAL_TYPE)
    {
      // Gather new data and put in results vector
      GetRPMInfo(dataIn, &resultsVector);

      // Ensure that at least one item is in the result vector
      if(resultsVector.size() == 0)
	{
	  RPMInfoData *tmp = new RPMInfoData();
	  tmp->SetTestId(dataIn->GetTestId());
	  tmp->msg = "(RPMInfoProbe) Unable to locate provided rpm name.";
	  resultsVector.push_back(tmp);    
	}
    }else
      {
	// Gather new data and put in results vector
	GetRPMInfoPatternMatching(dataIn, &resultsVector);     

	// Ensure that at least one item is in the result vector
	if(resultsVector.size() == 0)
	  {
	    RPMInfoData *tmp = new RPMInfoData();
	    tmp->SetTestId(dataIn->GetTestId());
	    tmp->msg = "(RPMInfoProbe) Unable to locate provided rpm name.";
	    resultsVector.push_back(tmp);     
	  }
      }

  return resultsVector;

}  

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  Private Members  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//

void RPMInfoProbe::GetRPMInfo(RPMInfoData *dataIn, pdVector *resultVector)
{
  //------------------------------------------------------------------------------------//
  //
  //  ABSTRACT
  //
  //  Get the data for all packages that have the name that matches rpm_name.  
  //
  //------------------------------------------------------------------------------------//

  /* Get the rpm_name form the data object. */
  const char *rpm_name = dataIn->name->data.c_str();

  /* Create a tmp data object reference. */
  RPMInfoData *tmp = NULL;
  
  /* Transaction sets are the modern way to read the RPM database. */
  rpmts ts;
  /* We use an iterator to walk the RPM database. */
  rpmdbMatchIterator iterator;
  /* Header object for the installed package. */
  Header header;
  /* Epoch, version, release and architecture data for output. */
  string installed_epoch, installed_version, installed_release,installed_architecture;

  /* Read in the RPM config files */
  if (rpmReadConfigFiles( (const char*) NULL, (const char*) NULL)) {
    tmp = new RPMInfoData();
	tmp->SetTestId(dataIn->GetTestId());
    tmp->msg = "Error: (RPMInfoProbe) Could not read RPM config files, which is necessary to read the RPM database.";
    resultVector->push_back(tmp);
    return;
  }

  /* Create an rpm database transaction set. */
  ts = rpmtsCreate();

  /* Create an iterator to walk the database. */	
  iterator = rpmtsInitIterator(ts, RPMTAG_NAME, rpm_name, 0);
  if (iterator == NULL) {
    tmp = new RPMInfoData();
	tmp->SetTestId(dataIn->GetTestId());
    tmp->msg = "Error: (RPMInfoProbe) Could not create an iterator to walk the RPM database.";
    resultVector->push_back(tmp);
    return;
  }

  /* Look at each installed package matching this name.  Generally, there is only one.*/
  while ( (header = rpmdbNextIterator(iterator)) != NULL) {

    /* epoch is an int_32 -- we'll display a string to handle the None case well. */
    char intermediate_string[11];
    int_32 epoch = readHeaderInt32(header, RPMTAG_EPOCH);
    if (epoch == -1 )
      installed_epoch = "NULL";
    else {
      snprintf(intermediate_string,11,"%d",epoch);
      installed_epoch = intermediate_string;
    }

    /* the remaining arguments are all normal strings */
    installed_version = readHeaderString(header, RPMTAG_VERSION);
    installed_release = readHeaderString(header, RPMTAG_RELEASE);
    installed_architecture = readHeaderString(header, RPMTAG_ARCH);

    /* Put the data in a data object. */
    tmp = new RPMInfoData();
    tmp->SetTestId(dataIn->GetTestId());
    tmp->name->data = rpm_name;
    tmp->epoch = installed_epoch;
    tmp->version = installed_version;
    tmp->release = installed_release;
    tmp->arch = installed_architecture;
    /* add the new item to the vector. */
    resultVector->push_back(tmp);
  }

  /* Free the iterator and transaction set data structures. */
  rpmdbFreeIterator(iterator);
  rpmtsFree(ts);

}
void RPMInfoProbe::GetRPMInfoPatternMatching(RPMInfoData *dataIn, pdVector *resultVector)
{
  //------------------------------------------------------------------------------------//
  //
  //  ABSTRACT
  //
  //  Get the data for all packages that have the name that matches rpm_name.  
  //  Use pattern matching to determine if a match is found
  //------------------------------------------------------------------------------------//

  /* Get the rpm_name form the data object. */
  const char *rpm_name = dataIn->name->data.c_str();

  /* Create a tmp data object reference. */
  RPMInfoData *tmp = NULL;
  
  /* Transaction sets are the modern way to read the RPM database. */
  rpmts ts;
  /* We use an iterator to walk the RPM database. */
  rpmdbMatchIterator iterator;
  /* Header object for the installed package. */
  Header header;
  /* Epoch, version, release and architecture data for output. */
  string installed_epoch;
  string installed_version;
  string installed_release;
  string installed_architecture;
  string installed_rpm_name;

  /* Read in the RPM config files */
  if (rpmReadConfigFiles( (const char*) NULL, (const char*) NULL)) {
    tmp = new RPMInfoData();
	tmp->SetTestId(dataIn->GetTestId());
    tmp->msg = "Error: (RPMInfoProbe) Could not read RPM config files, which is necessary to read the RPM database.";
    resultVector->push_back(tmp);
    return;
  }

  /* Create an rpm database transaction set. */
  ts = rpmtsCreate();

  /* Create an iterator to walk the database. */	
  iterator = rpmtsInitIterator(ts, RPMTAG_NAME, NULL, 0);
  if (iterator == NULL) {
    tmp = new RPMInfoData();
	tmp->SetTestId(dataIn->GetTestId());
    tmp->msg = "Error: (RPMInfoProbe) Could not create an iterator to walk the RPM database.";
    resultVector->push_back(tmp);
    return;
  }
  /* Look at each installed package matching this name.  Generally, there is only one.*/
  while ( (header = rpmdbNextIterator(iterator)) != NULL) {
    /* Get the rpm_name value for comparision. */
    installed_rpm_name = readHeaderString(header, RPMTAG_NAME);

    /* Check to see if name found matches input pattern. */
    if(myMatcher->IsMatch(rpm_name, installed_rpm_name.c_str()))
    {
      /* Get remaining data if a pattern match was successful. */

      /* epoch is an int_32 -- we'll display a string to handle the None case well. */
      char intermediate_string[11];
      int_32 epoch = readHeaderInt32(header, RPMTAG_EPOCH);
      if (epoch == -1 )
	installed_epoch = "NULL";
      else {
	snprintf(intermediate_string,11,"%d",epoch);
	installed_epoch = intermediate_string;
      }

      /* the remaining arguments are all normal strings */
      installed_version = readHeaderString(header, RPMTAG_VERSION);
      installed_release = readHeaderString(header, RPMTAG_RELEASE);
      installed_architecture = readHeaderString(header, RPMTAG_ARCH);

      /* Put the data in a data object. */
      tmp = new RPMInfoData();
      tmp->SetTestId(dataIn->GetTestId());
      tmp->name->data = rpm_name;
      tmp->name->type = PATTERN_MATCH_TYPE;
      tmp->name_found = installed_rpm_name;
      tmp->epoch = installed_epoch;
      tmp->version = installed_version;
      tmp->release = installed_release;
      tmp->arch = installed_architecture;
      /* add the new item to the vector. */
      resultVector->push_back(tmp);
    }
  }

  /* Free the iterator and transaction set data structures. */
  rpmdbFreeIterator(iterator);
  rpmtsFree(ts);
}

char* RPMInfoProbe::readHeaderString(Header header, int_32 tag_id)
{
  // This function is from the Red Hat RPM Guide //
  int_32 type;
  void *pointer;
  int_32 data_size;

  int header_status = headerGetEntry(header,
				     tag_id,
				     &type,
				     &pointer,
				     &data_size);

	
  if (header_status) {
    if (type == RPM_STRING_TYPE) {
      return (char *) pointer;
    }
  }

  return (NULL);
}

int_32 RPMInfoProbe::readHeaderInt32(Header header, int_32 tag_id)
{
  // This function is from the Red Hat RPM Guide //
  int_32 type;
  void *pointer;
  int_32 data_size;

  int header_status = headerGetEntry(header,
				     tag_id,
				     &type,
				     (void **) &pointer,
				     &data_size);
	
  if (header_status) {
    if (type == RPM_INT32_TYPE) {
      int_32 *p = (int_32 *) pointer;
      return *p;
    }
  }
  return( -1 );
}


