/* $Id: chkinteg.c,v 1.12 2002/07/21 18:23:45 richdawe Exp $ */

/*
 *  chkinteg.c - Integrity checking routines for pakke
 *  Copyright (C) 2000-2002 by Richard Dawe
 *      
 *  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
 *  (at your option) 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "common.h"

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <unistd.h>

#include <assert.h>

/* libpakke includes */
#include <libpakke/package.h>
#include <libpakke/packlist.h>
#include <libpakke/packdep.h>
#include <libpakke/dsm.h>
#include <libpakke/mft.h>
#include <libpakke/util.h>

#include "pakke.h"
#include "chkinteg.h"
#include "md5hash.h"
#include "misc.h"

/* Forward declarations */
static int check_package (PACKAGE_INFO *package, const char *db_path,
			  const char *prefix, int verbosity);

/* ---------------------------
 * - perform_check_integrity -
 * --------------------------- */

int
perform_check_integrity (const PAKKE_CHECK_INTEGRITY *req)
{
  PACKAGE_INFO    *packages = NULL;
  PACKAGE_INFO   **matched  = NULL;
  DSM_FILE_ERROR  *dfe      = NULL; /* List of err's in main DSM parse. */
  char db_path[PATH_MAX];
  int i;
  int ret = EXIT_SUCCESS;

  /* Get a list of installed packages */
  packages = dsm_load_all((const char **) req->dsm_path, NULL, &dfe);
  packages = ver_load_all((const char **) req->mft_path, packages);
	
  packages = packlist_dedupe(packages);
  packlist_xref(packages);
	
  if (packages == NULL)
    warn("No installed packages found");

  /* Display DSM errors. */
  if (dsm_file_errors_fatal(dfe))
    warn("Parsing failed for some DSM(s) in installed database");
	
  show_dsm_file_errors(dfe, req->verbosity);
  dsm_free_file_error_list(dfe);
  dfe = NULL;

  /* Build the DSM database path */
  strcpy(db_path, req->root);
  addforwardslash(db_path);
  strcat(db_path, PAKKE_DB_PREFIX);
  addforwardslash(db_path);

  /* Find the package in the package list. */  
  matched = packlist_find(packages, req->name,
			  PACKLIST_FIND_SIMPLE|PACKLIST_FIND_USER);

  if ((matched == NULL) || (*matched == NULL)) {
    /* No matches */
    warnf("No package was found matching '%s'!", req->name);

    packlist_free(packages);
    return(EXIT_FAILURE);
  }

  /* Check integrity of each matched package */
  for (i = 0; matched[i] != NULL; i++) {
    /* Skip virtual & group packages, as well as packages of no type. */
    if (   (matched[i]->version.type == TYPE_NONE)
	|| (matched[i]->version.type == TYPE_VIRTUAL)
	|| (matched[i]->version.type == TYPE_GROUP))
      continue;

    printf(". Checking package: %s %s...\n",
	   matched[i]->name, package_version_string(&matched[i]->version));

    if (!check_package(matched[i], db_path, req->prefix, req->verbosity)) {
      printf("Check FAILED\n");
      ret = EXIT_FAILURE;
    } else {
      printf("Check OK\n");
    }
  }

  /* Tidy up */
  free(matched);
  packlist_free(packages);

  return(ret);
}

/* -----------------
 * - check_package -
 * ----------------- */

static int
check_package (PACKAGE_INFO *package, const char *db_path, const char *prefix,
	       int verbosity)
{
  md5hash_entry **list = NULL;
  char temp_file[PATH_MAX];
  char md5_hash[MD5_HASH_SIZE];
  int i;
  int ret = 1; /* Succeed by default */

  /* Get all the MD5 hash values calculated previously. */
  list = md5hash_get(package, db_path);
  if (list == NULL) {
    /* No MD5 => failed */
    return(0);
  }
  
  /* Calculate MD5 hash of each file in the list. */
  for (i = 0; list[i] != NULL; i++) {
    assert(list[i]->filename != NULL);

    /* Build the filename */
    /*strcpy(temp_file, prefix);
      strcat(temp_file, list[i]->filename);*/
    strcpy(temp_file, list[i]->filename);

    /* Does the file still exist? */
    if (access(temp_file, R_OK) != 0) {
      warnf("File '%s' no longer exists", temp_file);
      continue;
    }

    /* Calculate & compare MD5 hash */
    if (!md5hash_calculate(temp_file, md5_hash)) {
      warnf("Error calculating MD5 hash for file '%s'", temp_file);
      continue;
    }

    if (memcmp(list[i]->md5hash, md5_hash, MD5_HASH_SIZE) != 0) {
      warnf("MD5 hash for file '%s' has changed", temp_file);
      continue;
    }
  }

  /* Tidy up */
  md5hash_entries_free(list);
  free(list), list = NULL;

  return(ret);
}
