/*

    File: testdisk.c

    Copyright (C) 1998-2007 Christophe GRENIER <grenier@cgsecurity.org>
  
    This software 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 the Free Software Foundation, Inc., 51
    Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_LOCALE_H
#include <locale.h>	/* setlocale */
#endif
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
#include "types.h"
#include "common.h"
#include "testdisk.h"
#include "intrf.h"
#include "intrfn.h"
#include "intrface.h"
#include "fnctdsk.h"
#include "dir.h"
#include "ext2_dir.h"
#include "rfs_dir.h"
#include "ntfs_dir.h"
#include "hdcache.h"
#include "ewf.h"
#include "log.h"

extern const arch_fnct_t arch_i386;
extern const arch_fnct_t arch_mac;
extern const arch_fnct_t arch_sun;

#ifdef HAVE_SIGACTION
void sighup_hdlr(int shup)
{
  log_critical("SIGHUP detected! TestDisk has been killed.\n");
  log_close();
  exit(1);
}
#endif

void aff_copy(WINDOW *window)
{
  wclear(window);
  keypad(window, TRUE); /* Need it to get arrow key */
  wmove(window,0,0);
  wdoprintf(window, "TestDisk %s, Data Recovery Utility, %s",VERSION,TESTDISKDATE);
  wmove(window,1,0);
  wdoprintf(window,"Christophe GRENIER <grenier@cgsecurity.org>");
  wmove(window,2,0);
  wdoprintf(window,"http://www.cgsecurity.org");
}

int main( int argc, char **argv )
{
  int i;
  int help=0, create_log=0, debug=0, dump_ind=0;
  int do_list=0;
  int write_used;
  int saveheader=0;
  int create_backup=0;
  int run_setlocale=1;
  int done=0;
  int safe=0;
  int testdisk_mode=TESTDISK_O_RDWR|TESTDISK_O_READAHEAD_8K;
  list_disk_t *list_disk=NULL;
  list_disk_t *element_disk;
  const char *cmd_device=NULL;
  char *cmd_run=NULL;
#ifdef TARGET_SOLARIS
  const arch_fnct_t *arch=&arch_sun;
#elif defined __APPLE__
  const arch_fnct_t *arch=&arch_mac;
#else
  const arch_fnct_t *arch=&arch_i386;
#endif
#ifdef HAVE_SIGACTION
  struct sigaction action;
#endif
#ifdef TESTING
  srand(1);
#endif
#ifdef HAVE_SIGACTION
  /* set up the signal handler for SIGHUP */
  action.sa_handler  = sighup_hdlr;
  action.sa_flags = 0;
  if(sigaction(SIGHUP, &action, NULL)==-1)
  {
    printf("Error on SIGACTION call\n");
    return -1;
  }
#endif
  for(i=1;i<argc;i++)
  {
    if((strcmp(argv[i],"/dump")==0) || (strcmp(argv[i],"-dump")==0))
      dump_ind=1;
    else if((strcmp(argv[i],"/log")==0) ||(strcmp(argv[i],"-log")==0))
    {
      create_log=1;
    }
    else if((strcmp(argv[i],"/debug")==0) || (strcmp(argv[i],"-debug")==0))
    {
      debug++;
      create_log=1;
    }
    else if((strcmp(argv[i],"/all")==0) || (strcmp(argv[i],"-all")==0))
      testdisk_mode|=TESTDISK_O_ALL;
    else if((strcmp(argv[i],"/backup")==0) || (strcmp(argv[i],"-backup")==0))
      create_backup=1;
    else if((strcmp(argv[i],"/direct")==0) || (strcmp(argv[i],"-direct")==0))
      testdisk_mode|=TESTDISK_O_DIRECT;
    else if((strcmp(argv[i],"/help")==0) || (strcmp(argv[i],"-help")==0) || (strcmp(argv[i],"--help")==0) ||
      (strcmp(argv[i],"/h")==0) || (strcmp(argv[i],"-h")==0))
      help=1;
    else if((strcmp(argv[i],"/list")==0) || (strcmp(argv[i],"-list")==0))
      do_list=1;
    else if((strcmp(argv[i],"/nosetlocale")==0) || (strcmp(argv[i],"-nosetlocale")==0))
      run_setlocale=0;
    else if((strcmp(argv[i],"/safe")==0) || (strcmp(argv[i],"-safe")==0))
      safe=1;
    else if((strcmp(argv[i],"/saveheader")==0) || (strcmp(argv[i],"-saveheader")==0))
      saveheader=1;
    else if(strcmp(argv[i],"/cmd")==0)
    {
      if(i+2>=argc)
	help=1;
      else
      {
	disk_t *disk_car;
	cmd_device=argv[++i];
	cmd_run=argv[++i];
	disk_car=file_test_availability(cmd_device,debug,arch,testdisk_mode);
	if(disk_car==NULL)
	{
	  printf("\nUnable to open file or device %s\n",cmd_device);
	  help=1;
	}
	else
	  list_disk=insert_new_disk(list_disk,disk_car);
      }
    }
    else
    {
      disk_t *disk_car=file_test_availability(argv[i],debug,arch,testdisk_mode);
      if(disk_car==NULL)
      {
	printf("\nUnable to open file or device %s\n",argv[i]);
	help=1;
      }
      else
	list_disk=insert_new_disk(list_disk,disk_car);
    }
  }
  printf("TestDisk %s, Data Recovery Utility, %s\nChristophe GRENIER <grenier@cgsecurity.org>\nhttp://www.cgsecurity.org\n",VERSION,TESTDISKDATE);
  if(help!=0)
  {
    printf("\n" \
	"Usage: testdisk [/log] [/debug] [file or device]\n"\
	"       testdisk /list  [/log]   [file or device]\n" \
	"\n" \
	"/log          : create a testdisk.log file\n" \
	"/debug        : add debug information\n" \
	"/list         : display current partitions\n" \
	"\n" \
	"TestDisk checks and recovers lost partitions\n" \
	"It works with :\n" \
	"- BeFS (BeOS)                           - BSD disklabel (Free/Open/Net BSD)\n" \
	"- CramFS, Compressed File System        - DOS/Windows FAT12, FAT16 and FAT32\n" \
	"- HFS, HFS+, Hierarchical File System   - JFS, IBM's Journaled File System\n" \
	"- Linux Ext2 and Ext3                   - Linux Raid\n" \
	"- Linux Swap                            - LVM, LVM2, Logical Volume Manager\n" \
	"- Netware NSS                           - NTFS (Windows NT/2K/XP/2003)\n" \
	"- ReiserFS 3.5, 3.6 and 4               - Sun Solaris i386 disklabel\n" \
	"- UFS and UFS2 (Sun/BSD/...)            - XFS, SGI's Journaled File System\n" \
	"\n" \
	"If you have problems with TestDisk or bug reports, please contact me.\n");
    return 0;
  }
  aff_buffer(BUFFER_RESET,"Q");
  if(do_list!=0)
  {
    printf("Please wait...\n");
    /* Scan for available device only if no device or image has been supplied in parameter */
    if(list_disk==NULL)
      list_disk=hd_parse(list_disk,debug,arch,testdisk_mode);
    /* Activate the cache */
    for(element_disk=list_disk;element_disk!=NULL;element_disk=element_disk->next)
      element_disk->disk=new_diskcache(element_disk->disk,testdisk_mode);
#ifdef DJGPP
    for(element_disk=list_disk;element_disk!=NULL;element_disk=element_disk->next)
    {
      printf("%s\n",element_disk->disk->description(element_disk->disk));
    }
#endif
    if(safe==0)
      hd_update_all_geometry(list_disk,0,debug);
    for(element_disk=list_disk;element_disk!=NULL;element_disk=element_disk->next)
    {
      printf("%s, sector size=%u\n",element_disk->disk->description(element_disk->disk),element_disk->disk->sector_size);
    }
    printf("\n");

    for(element_disk=list_disk;element_disk!=NULL;element_disk=element_disk->next)
    {
      interface_list(element_disk->disk,debug,0,saveheader,create_backup);
    }
    delete_list_disk(list_disk);
    return 0;
  }
  if(start_ncurses("TestDisk",argv[0]))
    return 1;
  if(argc==1)
  {
    debug=1;
    create_log=ask_log_creation();
  }
  if(create_log>0)
    log_open("testdisk.log",(create_log==1?"a":"w"),"TestDisk",argc,argv);
  log_info("TestDisk %s, Data Recovery Utility, %s\nChristophe GRENIER <grenier@cgsecurity.org>\nhttp://www.cgsecurity.org\n",VERSION,TESTDISKDATE);
  log_info(TESTDISK_OS);
  log_info(" (ext2fs lib: %s, ntfs lib: %s, reiserfs lib: %s, ewf lib: %s)\n",td_ext2fs_version(),td_ntfs_version(),td_reiserfs_version(), td_ewf_version());
#ifdef HAVE_SETLOCALE
  if(run_setlocale>0)
  {
    const char *locale;
    locale = setlocale (LC_ALL, "");
    if (locale==NULL) {
      locale = setlocale (LC_ALL, NULL);
      log_error("Failed to set locale, using default '%s'.\n", locale);
    } else {
      log_info("Using locale '%s'.\n", locale);
    }
  }
#endif
  aff_copy(stdscr);
  wmove(stdscr,5,0);
  wdoprintf(stdscr, "Please wait...\n");
  /* Scan for available device only if no device or image has been supplied in parameter */
  if(list_disk==NULL)
    list_disk=hd_parse(list_disk,debug,arch,testdisk_mode);
  /* Activate the cache */
  for(element_disk=list_disk;element_disk!=NULL;element_disk=element_disk->next)
    element_disk->disk=new_diskcache(element_disk->disk,testdisk_mode);
  wmove(stdscr,6,0);
  for(element_disk=list_disk;element_disk!=NULL;element_disk=element_disk->next)
  {
    wdoprintf(stdscr,"%s\n",element_disk->disk->description(element_disk->disk));
  }
  wrefresh(stdscr);
  if(safe==0)
    hd_update_all_geometry(list_disk,0,debug);
  /* save disk parameters to rapport */
  log_info("Hard disk list\n");
  for(element_disk=list_disk;element_disk!=NULL;element_disk=element_disk->next)
    log_info("%s, sector size=%u\n",element_disk->disk->description(element_disk->disk),element_disk->disk->sector_size);
  log_info("\n");
  do_curses_testdisk(debug,dump_ind,list_disk,saveheader,cmd_device,&cmd_run);
  end_ncurses();
  log_info("\n");
  while(done==0)
  {
    int command='Q';
    if(cmd_run!=NULL)
    {
      while(cmd_run[0]==',')
	(cmd_run)++;
      if(strncmp(cmd_run,"list",4)==0)
      {
	(cmd_run)+=4;
	command='L';
      }
      else if(cmd_run[0]!='\0')
      {
	log_critical("error >%s<\n",cmd_run);
      }
    }
    switch(command)
    {
      case 'L':
	for(element_disk=list_disk;element_disk!=NULL;element_disk=element_disk->next)
	  interface_list(element_disk->disk,debug,0, saveheader, create_backup);
	break;
      case 'q':
      case 'Q':
	done = 1;
	break;
    }
  }
  cmd_device=NULL;
  cmd_run=NULL;
  write_used=delete_list_disk(list_disk);
  log_info("TestDisk exited normally.\n");
  if(log_close()!=0)
  {
    printf("TestDisk: Log file corrupted!\n");
  }
  else
  {
    printf("TestDisk exited normally.\n");
  }
  if(write_used!=0)
  {
    printf("You have to reboot for the change to take effect.\n");
  }
  return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1