/*
 * Tool name   : IEBrowserHistory
 * Description : Tool to dump IE browser history file content.
 * Version     : 0.2
 * Todo        : Implementing Firefox browser history dumps.
 *
 * Changes     : 2008.04.27 - Renamed functions and output text.
 *             : 2008.04.30 - Minor bug fixes.
 *
 */

#include <windows.h>
#include <shellapi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_BUF_SIZE 1024

#pragma comment(lib, "Shell32.lib")



/*
 * Constants, data types and function forward declarations.
 *
 */

const int TYPE_UNDEF = 0x00;
const int TYPE_URL   = 0x01;
const int TYPE_REDR  = 0x02;
const int TYPE_UNKN  = 0x03;

const int URL_URL_OFFSET  = 104;
const int URL_TIME_OFFSET = 16;
const int REDR_URL_OFFSET = 16;


struct sHistory 
{
  int lType;
  char *lURL;
  SYSTEMTIME st;
};


DWORD searchHistoryFiles(char *pBaseDirectory);
void dumpHistoryEntries(char *pFileName);
int extractURL(char *pBuf, int pType, struct sHistory *pHistory);



/*
 * Program entry point
 *
 */

int main()
{
  int lRetVal = 0;

  searchHistoryFiles(getenv("HOMEPATH"));
  printf("\n\nHit enter to stop execution ...\n");
  getc(stdin);

  return(lRetVal);
}


/*
 * Find Index.dat files by searching recursively
 * a base directory. Tested on Windows XP with IE 8.
 *
 */

DWORD searchHistoryFiles(char *pBaseDirectory)
{
  WIN32_FIND_DATA lFileData; 
  HANDLE lSearchHandle;  
  char lFilePattern[MAX_BUF_SIZE + 1]; 
  char lSubDirectory[MAX_BUF_SIZE + 1];
  char lHistoryFile[MAX_BUF_SIZE + 1];
  int lRetVal = 0;
  char lHomeDirectory[MAX_BUF_SIZE + 1];


  if (pBaseDirectory != NULL)
  {
    /*
	 * Prepare file pattern. We want to check all the files
	 * to detect directories and search inside them, too.
	 *
	 */

    ZeroMemory(lHomeDirectory, sizeof(lHomeDirectory));
	strncpy(lHomeDirectory, pBaseDirectory, sizeof(lHomeDirectory));

    if (lHomeDirectory[strlen(lHomeDirectory) - 1] != '\\')
      strcat(lHomeDirectory, "\\");

    ZeroMemory(lFilePattern, sizeof(lFilePattern));
	_snprintf(lFilePattern, sizeof(lFilePattern) - 1, "%s*", lHomeDirectory);


	/*
	 * Start the search.
	 *
	 */

    if ((lSearchHandle = FindFirstFile(lFilePattern, &lFileData)) != INVALID_HANDLE_VALUE) 
    {
      do 
      { 
        /*
         * File is a directory. Do a recursive function call to 
         * find index.dat files in that directory.
         *
         */

        if(lFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        {
          if (strcmp(lFileData.cFileName, ".") && strcmp(lFileData.cFileName, ".."))
          {
            ZeroMemory(lSubDirectory, sizeof(lSubDirectory));
            _snprintf(lSubDirectory, sizeof(lSubDirectory) - 1, "%s\\%s", pBaseDirectory, lFileData.cFileName);
            searchHistoryFiles(lSubDirectory);
          } // if (strcmp(lFileDa...
        } else {

          /*
           * File is a Internet Explorer history file.
           *
           */

          if (strstr(lFileData.cFileName, "ndex.dat") && (strstr(pBaseDirectory, "nternet") || strstr(pBaseDirectory, "istory.IE5")))
          {
            ZeroMemory(lHistoryFile, sizeof(lHistoryFile));
            _snprintf(lHistoryFile, sizeof(lHistoryFile) - 1, "%s\\%s", pBaseDirectory, lFileData.cFileName);
            dumpHistoryEntries(lHistoryFile);
          } // if (strstr(lFileData...
        } // if(lFileData.dwFileAt...
      } while (FindNextFile(lSearchHandle, &lFileData));
    } // if ((lSearchHandle = FindFirstFile(l...
  } // if (pBaseDirectory != NULL)

  return(lRetVal);
}




/*
 * Dump URLs from a cache and history file. 
 *
 */

void dumpHistoryEntries(char *pFileName)
{
  char *lBufPointer = NULL;
  char *lTempPointer = NULL;
  long lFileSize;
  int lCounter = 0;
  struct sHistory lHistory;
  int lType = 0;
  char lTemp[MAX_BUF_SIZE + 1];
  char lTimeStamp[MAX_BUF_SIZE + 1];
  HANDLE lFileHandle = INVALID_HANDLE_VALUE;
  DWORD lBytesRead = 0;


  if ((lFileHandle = CreateFile(pFileName,  GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)
  {
    if ((lFileSize = GetFileSize(lFileHandle, NULL)) > 0 && (lTempPointer = (char *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lFileSize)) != NULL)
    {
      if (ReadFile(lFileHandle, lTempPointer, lFileSize,  &lBytesRead, NULL))
      {

        /*
         * Iterate byte after byte through the .dat file.
         *
         */

        for (lCounter = 0; lCounter < lFileSize; lCounter++) 
        {
          lBufPointer = lTempPointer + lCounter;

          /*
           * Determine URL type
           *
           */

          if (strncmp(lBufPointer, "URL ", 4) == 0)
            lType = TYPE_URL;
          else if (strncmp(lBufPointer, "REF ", 4) == 0)
            lType = TYPE_REDR;
          else 
            lType = TYPE_UNDEF;

//printf("%d %s\n", lType, lBufPointer);

          /*
	       * If a valid data type was found, print it.
	       *
	       */

          if (lType != TYPE_UNDEF)
          {
            ZeroMemory(&lHistory, sizeof(lHistory));
            if (extractURL(lBufPointer, lType, &lHistory) > 0 && lHistory.lURL != NULL)  
	        {
              ZeroMemory(lTimeStamp, sizeof(lTimeStamp));
              ZeroMemory(lTemp, sizeof(lTemp));

              /*
               * Regular entry in cache file format.
               *
               */

              if (strstr(lHistory.lURL, "@http@"))
	          {
                printf("Cache\t%d/%02d/%02d %02d:%02d\t%s\n", lHistory.st.wYear, lHistory.st.wMonth, lHistory.st.wDay, lHistory.st.wHour, lHistory.st.wMinute, lHistory.lURL);


              /*
               * Regular Internet Explorer entry in history file format.
               *
               */
              } else if (strstr(lHistory.lURL, "@http://")) {
                printf("Inet\t%d/%02d/%02d %02d:%02d\t%s\n", lHistory.st.wYear, lHistory.st.wMonth, lHistory.st.wDay, lHistory.st.wHour, lHistory.st.wMinute, lHistory.lURL);


              /*
               * Regular local file explorer entry in history file format.
               *
               */
  	          } else if (strstr(lHistory.lURL, "@file:///")) {
                printf("Local\t%d/%02d/%02d %02d:%02d\t%s\n", lHistory.st.wYear, lHistory.st.wMonth, lHistory.st.wDay, lHistory.st.wHour, lHistory.st.wMinute, lHistory.lURL);
              } // if (strstr(lHistory...
            } // if (extractURL(lBufPoint...

            if (lHistory.lURL != INVALID_HANDLE_VALUE)
              HeapFree(GetProcessHeap(), 0, lHistory.lURL);

	      } // if (lType != TYPE_UNDEF) 
        } // for (lCounter = 0; lCoun...
	  } // if (ReadFile(lFileHa...

      HeapFree(GetProcessHeap(), 0, lTempPointer);
	} // if ((lTempPointer = (char *) HeapA...

    CloseHandle(lFileHandle);
  } // if ((lFileHandle = CreateFile(pFi...  
}





/*
 * Extract first entry from a buffer and
 * copy it to the history data structure.
 *
 */

int extractURL(char *pBuf, int pType, struct sHistory *pHistory) 
{
  int lCounter = 0;
  int lRetVal = 1;
  FILETIME lFileTime;

  ZeroMemory(pHistory, sizeof(struct sHistory));


  if (pType == TYPE_URL) 
  {
    CopyMemory((DWORD *) &lFileTime.dwLowDateTime, pBuf + URL_TIME_OFFSET, sizeof(DWORD));
	CopyMemory((DWORD *) &lFileTime.dwHighDateTime, pBuf + URL_TIME_OFFSET + 4, sizeof(DWORD));
    FileTimeToSystemTime(&lFileTime, &pHistory->st);
    pHistory->lType = TYPE_URL;

    pBuf += URL_URL_OFFSET;
  } else if (pType == TYPE_REDR) {
    lFileTime.dwHighDateTime = 0;
    lFileTime.dwLowDateTime = 0;
    FileTimeToSystemTime( &lFileTime, &pHistory->st );
    pHistory->lType = TYPE_REDR;

    pBuf += REDR_URL_OFFSET;
  } else {
    lRetVal = TYPE_UNDEF;
    goto END;
  } // if (pType == TYPE_URL ...


  while (pBuf[lCounter] != 0 && lCounter < 1026)
    lCounter++;


  if (lCounter > 1024)
  {
    goto END;
  } else if ((pHistory->lURL = (char *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 1024)) != NULL) {
    ZeroMemory(pHistory->lURL, 1024);
  	strncpy(pHistory->lURL, pBuf, lCounter);
  } // if ((pHistory->lURL = (cha...

END:

  return(lRetVal);	
}

