// Scalpel Copyright (C) 2005-6 by Golden G. Richard III.
// Written by Golden G. Richard III.
//
// 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., 51 Franklin Street, Fifth Floor, Boston, MA
// 02110-1301, USA.

// Scalpel is a complete rewrite of the foremost 0.69 file carver to
// increase speed and support execution on machines with minimal
// resources (e.g., < 256MB RAM).
//
// Thanks to Kris Kendall, Jesse Kornblum, et al for their work on
// foremost.


#include "scalpel.h"


void checkMemoryAllocation(struct scalpelState *state, void *ptr, int line, 
			   char *file, char *structure) {
  if (ptr) {
    return;
  }
  else {
    fprintf(stderr, "** MEMORY ALLOCATION FAILURE **\n");
    fprintf (stderr,
	     "ERROR: Memory exhausted at line %d in file %s. Scalpel was \n",
  	     line, file);
    fprintf(stderr, 
	    "allocating memory for %s when this condition occurred.\n", 
	    structure);

    fprintf (state->auditFile,
	     "ERROR: Memory exhausted at line %d in file %s. Scalpel was \n",
  	     line, file);
    fprintf(state->auditFile, 
	    "allocating memory for %s when this condition occurred.\n", 
	    structure);

    handleError(state, SCALPEL_GENERAL_ABORT);  // fatal
  }
}


#ifndef __GLIBC__
void setProgramName(char *s) {
  char *fn = (char *)malloc(sizeof(char) * PATH_MAX);
  if (realpath(s,fn) == NULL) {
    __progname = strdup("scalpel");
    return;
  }

  __progname = basename(fn);
}

#endif /* ifndef __GLIBC__ */


// write entry to both the screen and the audit file 
void scalpelLog(struct scalpelState *state, char *format, ...) {

  va_list argp;

  va_start(argp,format);
  vfprintf (stderr,format,argp);
  vfprintf (state->auditFile,format,argp);
  va_end(argp);
}

// determine if two characters match, with optional case 
// insensitivity.  If a is the Scalpel wildcard character,
// then a and b will always match.
int charactersMatch(char a, char b, int caseSensitive) {

  if (a == wildcard || a == b) {
    return 1;
  }
  if (caseSensitive || (a < 'A' || a > 'z' || b < 'A' || b > 'z')) {
    return 0;
  }

  /* This line is equivalent to (abs(a-b)) == 'a' - 'A' */
  return (abs(a-b) == 32);
}


// memwildcardcmp is a memcmp() clone, except that single
// character wildcards are supported.  The default wildcard character is '?',
// but this can be redefined in the configuration file.  A wildcard in s1 will 
// match any single character in s2.  
int memwildcardcmp(const void *s1, const void *s2, 
		   size_t n,int caseSensitive){
  if (n != 0) {
    register const unsigned char *p1 = s1, *p2 = s2;
    do {
      if (!charactersMatch(*p1++, *p2++, caseSensitive)) {
	return (*--p1 - *--p2);
      }
    } while (--n !=0);
  }
  return 0;
}

// initialize Boyer-Moore "jump table" for search. Dependence
// on search type (e.g., FORWARD, REVERSE, etc.) from Foremost 
// has been removed, because Scalpel always performs searches across
// a buffer in a forward direction.
void init_bm_table(char *needle, size_t table[UCHAR_MAX + 1], 
		   size_t len, int casesensitive) {

  size_t i=0,j=0,currentindex=0;
  
  for (i = 0; i <= UCHAR_MAX; i++) {
    table[i] = len;
  }

  for (i = 0; i < len; i++) {
    currentindex = len-i-1; //Count from the back of string
    //No skip entry can advance us past the last wildcard in the string
    if (needle[i] == wildcard) {           
      for (j=0; j<=UCHAR_MAX; j++) {
	table[j] = currentindex; 
      }
    }	
    table[(unsigned char)needle[i]] = currentindex;
    if (! casesensitive && needle[i] > 0) {
      table[tolower(needle[i])] = currentindex;
      table[toupper(needle[i])] = currentindex;
    }
  }	  
}

// Perform a modified Boyer-Moore string search, supporting wildcards,
// case-insensitive searches, and specifiable start locations in the buffer.
// Dependence on search type (e.g., FORWARD, REVERSe, etc.) from Foremost has 
// been removed, because Scalpel always performs forward searching.

char *bm_needleinhaystack_skipnchars(char *needle, size_t needle_len,
				     char *haystack, size_t haystack_len,
				     size_t table[UCHAR_MAX + 1], 
				     int casesensitive,
				     int start_pos) {
  register size_t shift=0;
  register size_t pos = start_pos;
  char *here; 
  
  if(needle_len == 0) {
    return haystack;
  }
  
  while (pos < haystack_len){
    while( pos < haystack_len && (shift = table[(unsigned char)haystack[pos]]) > 0) {
      pos += shift;
    }
    if (0 == shift) {
      if (0 == memwildcardcmp(needle,here = (char *)&haystack[pos-needle_len+1], needle_len, casesensitive)) {
	return(here);
      }
      else {
	pos++;
      }
    }
  }
  return NULL;
}


char *bm_needleinhaystack(char *needle, size_t needle_len,
                          char *haystack, size_t haystack_len,
                          size_t table[UCHAR_MAX + 1], int casesensitive) {

  return bm_needleinhaystack_skipnchars(needle, 
					needle_len, 
					haystack, 
					haystack_len, 
					table, 
					casesensitive, 
					needle_len - 1);
}


// find longest header OR footer
int findLongestNeedle(struct SearchSpecLine *SearchSpec) {
  int longest = 0;
  int i=0;
  for(i=0; SearchSpec[i].suffix != NULL; i++) {
    if (SearchSpec[i].beginlength > longest) {
      longest = SearchSpec[i].beginlength;
    }
    if (SearchSpec[i].endlength > longest) {
      longest = SearchSpec[i].endlength;
    }
  }
  return longest;  
}     


// decode strings with embedded escape sequences and return the total length of the
// translated string.

int translate(char *str) {
  char next;
  char *rd=str,*wr=str,*bad;
  char temp[1+3+1];
  char ch;
  
  if(!*rd) {  //If it's a null string just return
    return 0;
  }
  
  while (*rd) {
    // Is it an escaped character?
    if (*rd=='\\') {
      rd++;
      switch(*rd) {
      case '\\': rd++;*(wr++)='\\'; break;
      case 'a':  rd++;*(wr++)='\a'; break;
      case 's':  rd++;*(wr++)=' '; break;
      case 'n':  rd++;*(wr++)='\n'; break;
      case 'r':  rd++;*(wr++)='\r'; break;
      case 't':  rd++;*(wr++)='\t'; break;
      case 'v':  rd++;*(wr++)='\v'; break;
	// Hexadecimal/Octal values are treated in one place using strtoul() 
      case 'x':
      case '0': case '1': case '2': case '3':
	next = *(rd+1); 
	if (next < 48 || (57 < next && next < 65) || 
	    (70 < next && next < 97) || next > 102) 
	  break;  //break if not a digit or a-f, A-F 
	next = *(rd+2); 
	if (next < 48 || (57 < next && next < 65) || 
	    (70 < next && next < 97) || next > 102) 
	  break;  //break if not a digit or a-f, A-F 
	
	temp[0]='0'; bad=temp;
	strncpy(temp+1,rd,3);
	temp[4] = '\0';
	ch=strtoul(temp,&bad,0);
	if (*bad=='\0') {
	  *(wr++)=ch;
	  rd+=3;
	} // else INVALID CHARACTER IN INPUT ('\\' followed by *rd) 
	break;
      default: // INVALID CHARACTER IN INPUT (*rd)
	*(wr++)='\\';
	break;
      }
    }
    // just copy un-escaped characters
    else {
      *(wr++)=*(rd++);
    }
  }
  *wr = '\0';  // null termination
  return wr-str;
}

// skip leading whitespace
char *skipWhiteSpace(char* str) {
  while (isspace(str[0])) {
    str++;
  }
  return str;
}

// describe Scalpel error conditions.  Some errors are fatal, while
// others are advisory.
void handleError(struct scalpelState *state, int error) {
  
  switch(error) {
    
  case SCALPEL_ERROR_FILE_OPEN:
    // non-fatal
    scalpelLog(state,"Scalpel was unable to open the image file: %s\n"
		"Skipping...\n\n",
		state->imagefile);
    break;

  case SCALPEL_ERROR_FILE_READ:
    // non-fatal
    scalpelLog(state,"Scalpel was unable to read the image file: %s\n"
		"Skipping...\n\n",
		state->imagefile);
    break;

  case SCALPEL_ERROR_FATAL_READ:
    // fatal
    scalpelLog(state,"Scalpel was unable to read a needed file and will abort.\n");
    closeFile(state->auditFile);
    exit (-1);
    break;

  case SCALPEL_ERROR_FILE_WRITE:
    // fatal--unable to write files, which may mean that disk space is exhausted.
    fprintf(stderr,
	    "Scalpel was unable to write output files and will abort.\n");
    fprintf(stderr,
	    "This error generally indicates that disk space is exhausted.\n");
    closeFile(state->auditFile);
    exit (-1);

  case SCALPEL_ERROR_NO_SEARCH_SPEC:
    // fatal, configuration file didn't specify anything to carve
    scalpelLog(state,
		 "ERROR: The configuration file didn't specify any file types to carve.\n");
    scalpelLog(state, "(If you're using the default configuration file, you'll have to\n");
    scalpelLog(state, "uncomment some of the file types.)\n");
    closeFile(state->auditFile);
    exit (-1);

  case SCALPEL_GENERAL_ABORT:
    // fatal
    scalpelLog(state,"Scalpel will abort.\n");
    closeFile(state->auditFile);
    exit (-1);
    break;
  
  default:
    // fatal
    scalpelLog(state,
		"Scalpel has encountered an error it doesn't know"
		"how to handle.\nError code: %d\n", error);
    closeFile(state->auditFile);
    exit (-1);
  }
}


void setttywidth(){
#if defined (__WIN32)
  CONSOLE_SCREEN_BUFFER_INFO csbi;  
  HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
  if(! GetConsoleScreenBufferInfo(hConsole, &csbi)){
    ttywidth = 80;
  }else{
    ttywidth = csbi.dwSize.X;
  }
#else
  //#if defined(__BSD)
  //  ttywidth = 80;
  //#else
  struct winsize winsize;
  if(ioctl(fileno(stdout),TIOCGWINSZ, &winsize) != -1) {
    ttywidth = winsize.ws_col;
  }
  else {
    // 80 is a reasonable default
    ttywidth = 80; 
  }
  //#endif
#endif
}


int skipInFile(struct scalpelState *state, FILE *infile) {

  int retries = 0;
  while(TRUE) {
    if ((fseeko(infile,state->skip,SEEK_SET))) {

#ifdef __WIN32      
      fprintf(stderr,
	      "ERROR: Couldn't skip %I64u bytes at the start of image file %s\n",
	      state->skip,state->imagefile);
#else
      fprintf(stderr,
	      "ERROR: Couldn't skip %lld bytes at the start of image file %s\n",
	      state->skip,state->imagefile);
#endif
	
      if (retries++ > 3) {
	fprintf(stderr,"Sorry, maximum retries exceeded...\n");
	return FALSE;
      } 
      else {
	fprintf (stderr,"Waiting to try again... \n");
	sleep(3);
      }
    }
    else {
#ifdef __WIN32
      fprintf(stderr,"Skipped the first %I64u bytes of %s...\n",state->skip,
	      state->imagefile);
#else
      fprintf(stderr,"Skipped the first %lld bytes of %s...\n",state->skip,
	      state->imagefile);
#endif


      return TRUE;
    }
  }
}
