iecache

IEのキャッシュ情報をテキスト形式で取得して抽出/定型処理したいなーと思いコマンドラインツールを作ってみる。

調べたら、WindowsAPIで、以下の様に使用できるらしい。

1. まずFindFirstUrlCacheEntryA()をコール。
2. FindNextUrlCacheEntryA()を呼び続けることで次々にキュッシュ情報をゲット。
3. 最後にFindCloseUrlCache()をコール。

ゲットできるキャッシュ情報は以下の様な構造体。
c:/cygwin/usr/include/w32api/wininet.h:648:

typedef struct _INTERNET_CACHE_ENTRY_INFOA {
	DWORD dwStructSize;
	LPSTR lpszSourceUrlName;
	LPSTR lpszLocalFileName;
	DWORD CacheEntryType;
	DWORD dwUseCount;
	DWORD dwHitRate;
	DWORD dwSizeLow;
	DWORD dwSizeHigh;
	FILETIME LastModifiedTime;
	FILETIME ExpireTime;
	FILETIME LastAccessTime;
	FILETIME LastSyncTime;
	PBYTE lpHeaderInfo;
	DWORD dwHeaderInfoSize;
	LPSTR lpszFileExtension;
	DWORD dwReserved;
} INTERNET_CACHE_ENTRY_INFOA,*LPINTERNET_CACHE_ENTRY_INFOA;

↑この情報をテキストでゲットして、5/26 0:00 〜 5/27 0:00 のアクセス履歴をgrepできる様にしたい。以下の様な感じで。

~/$ iecache -f 05260000 -t 05270000 | grep twitter

以下の様にしてクッキー情報を抽出するとか。

~/$ iecache -f 05260000 -t 05270000 | grep ^Cookie

で、大体以下の様な感じで、とりあえず動作するのを確認。

~/s/c/iecache/iecache.c:0:

/*
$ gcc -mwindows iecache.c -lwininet -o iecache
 */
#include "wininet.h"
#include <stdio.h>
#include <unistd.h> /* getopt用 */

/*
winerror.h:190:#define ERROR_NO_MORE_ITEMS 259L
*/
UINT64 ftime2u64(FILETIME * pFt)
{
  UINT64 u64 = pFt->dwHighDateTime;
  return (u64<<32) + pFt->dwLowDateTime;
}

UINT64 str2u64(char * s)
{/* hhmmss で渡された文字列を元に FILETIME と同等の uint64 を生成する */
  SYSTEMTIME st;
  int len = strlen(s);
  int hhmm = atoi(s);
  UINT64 u64;
  FILETIME ft;

  GetLocalTime(&st);
  if (hhmm < 0) {
	  SystemTimeToFileTime(&st, &ft);
	  u64 = ftime2u64(&ft); /* FileTime は100ナノ秒単位の64bit値 */
/* "-100" と指定された時、100分前と解釈すべきなのか 1:00 前(一時間前)と解釈すべきなのか
悩ましいところだが、とりあえずは 100分前と解釈するものとする。
*/
	  u64 = u64 / 600000000UL - (-hhmm);
	  u64 *= 600000000UL;
  } else {	  
	  st.wSecond = st.wMilliseconds = 0; /* 大小比較時にややこしくなるのでゼロに初期化しておく */
	  /* 例えば現在が 12:34 で 1.23 と指定された場合は 12:01.23 と解釈 */
	  if (len > 3 && s[len-3] == '.') {
		  st.wSecond = atoi(s+len-2);
		  len -= 3; /* 末尾の ".XX" の分をマイナスする */
	  }
	  if (len > 8) { /* MMDDhhmm で8桁。20億を越えると 32bit桁溢れするので2回に分けて処理する */
		  if (len == 10 || len == 12) { /* yyYYMMDDhhmm */
			  char yy[5];
			  hhmm = atoi(s + len - 8); /*     MMDDhhmm */
			  strncpy(yy, s, len - 8);    /* yyYY */
			  st.wYear = atoi(yy);
			  if (len == 10) st.wYear += 2000;
		  } else {
			  fprintf(stderr, "Strange date : %s\n", s);
			  exit(0);
		  }
	  }
	  st.wMinute = hhmm%100;
	  hhmm /= 100;
	  if (hhmm < 100) {
		  if (hhmm != 0) st.wHour = hhmm;/* hourの桁がゼロの時は、現在時刻の wHourをそのまま使用(ってどうなんだろ。わかりにくい?? */
	  } else { /* 上位桁に DD が付いてる */
		  st.wHour = hhmm%100;
		  hhmm /= 100;
		  if (hhmm < 100) {
			  st.wDay = hhmm;
		  } else { /* 上位桁に MM(月) が付いてる */
			  st.wDay = hhmm%100;
			  hhmm /= 100;
			  if (hhmm < 100) {
				  st.wMonth = hhmm;
			  } else { /* 上位桁に YY が付いてる */
				  st.wMonth = hhmm%100;
				  hhmm /= 100;
				  if (hhmm < 100) hhmm += 2000;
				  st.wYear = hhmm;
			  }
		  }
	  }
	  SystemTimeToFileTime(&st, &ft);
	  u64 = ftime2u64(&ft);
  }
  return u64;
}

int main(int ac, char ** av)
{
	HANDLE	hFind;
	DWORD	dwLen;
	DWORD	dwLen_pre = MAX_CACHE_ENTRY_INFO_SIZE;
	INTERNET_CACHE_ENTRY_INFO*	psInfo;
	SYSTEMTIME st;
	FILETIME ft;
	long er = 0;
	int ch;
	UINT64 u64;
	UINT64 from = 0;
	UINT64 to = 0;
	BOOL ret;
	char def_wildcard[] = "*.*";
	char * wildcard = def_wildcard;
	extern char	*optarg;
	extern int	optind, opterr;

	while ((ch = getopt(ac, av, "f:t:")) != -1){
	  switch (ch){
/*
FromTime から ToTime までの間のIEキャッシュのみ取り出す。
引数の指定方式としては touch と大体同じ形式を採用。
つまり、
    [[CC]YY]MMDDhhmm[.ss]
という感じ。
ただし、ちょっと拡張して、hhmmのみ/mmのみの指定も許す。
    [[[[CC]YY]MMDD]hh]mm[.ss]
という感じ。
あと、負数指定も許し、「-5」で「5分前」と指定できる様にしてみる。
 */
	  case 'f': // FromTime
		from = str2u64(optarg);
		break;
	  case 't': // ToTime
		to = str2u64(optarg);
		break;
	  default:
		;
	  }
	}
	ac -= optind;
	av += optind;

	/* これ以降は,av[0]〜av[ac-1]にオプションを除いた引数が入る */
	if (ac == 1) wildcard = av[0];
	dwLen = dwLen_pre;
	psInfo = (INTERNET_CACHE_ENTRY_INFO*) malloc(dwLen_pre);
/*	hFind = FindFirstUrlCacheEntryA("visited:", psInfo, &dwLen); //例えば "cookie:" とか "visited:" とかが指定可能 */
	hFind = FindFirstUrlCacheEntryA(wildcard, psInfo, &dwLen);
	if (dwLen == 0 || hFind == 0) {
		if (hFind) FindCloseUrlCache(hFind);
		return 0;
	}
	if (dwLen > dwLen_pre) {
		psInfo = realloc(psInfo, dwLen);
		if (psInfo == NULL) {
			FindCloseUrlCache(hFind);			
			return 0;
		}
		dwLen_pre = dwLen;
		hFind = FindFirstUrlCacheEntryA(wildcard, psInfo, &dwLen);
	}
	FileTimeToLocalFileTime(&psInfo->LastAccessTime, &ft);
	FileTimeToSystemTime(&ft, &st);
	while (1) {
	  ret = 1;
	  u64 = ftime2u64(&ft);
		if (from && u64 < from) ret = 0;
		if (to && u64 > to) ret = 0;
		if (ret == 1) {
			char * s = psInfo->lpszLocalFileName;
			printf("%s\r%04d/%02d/%02d %02d:%02d:%02d\r%s\n", 
				   psInfo->lpszSourceUrlName,
				   st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond,
				   s?s:""
				 );
		}
		ret = FindNextUrlCacheEntryA(hFind, psInfo, &dwLen);
		if (dwLen == 0) break;
		if (dwLen > dwLen_pre) {
			psInfo = realloc(psInfo, dwLen);
			if (psInfo == NULL) {
				FindCloseUrlCache(hFind);			
				return 0;
			}
			dwLen_pre = dwLen;
			ret = FindNextUrlCacheEntryA(hFind, psInfo, &dwLen);
		}
		if (ret == 0) {
			er = GetLastError();
			if (er == ERROR_NO_MORE_ITEMS) {
				break;
			}
		}
		FileTimeToLocalFileTime(&psInfo->LastAccessTime, &ft);
		FileTimeToSystemTime(&ft, &st);
	}
	FindCloseUrlCache(hFind);
	return 0;
}