Skip to content

Instantly share code, notes, and snippets.

@atifaziz
Created August 13, 2008 08:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save atifaziz/5216 to your computer and use it in GitHub Desktop.
Save atifaziz/5216 to your computer and use it in GitHub Desktop.
iecache
//
// Copyright (c) 2005 Atif Aziz. All rights reserved.
//
// Author(s):
//
// Atif Aziz, http://www.raboof.com
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the New BSD License, a copy of which should have
// been delivered along with this distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#include "stdafx.h"
#pragma comment(lib, "wininet")
#pragma comment(lib, "shlwapi")
using namespace std;
/////////////////////////////////////////////////////////////////////////////
//
// Globals
//
TCHAR g_executableName[MAX_PATH];
/////////////////////////////////////////////////////////////////////////////
//
// Macros
//
#define DIM(a) (sizeof(a) / sizeof((a)[0]))
/////////////////////////////////////////////////////////////////////////////
//
// Prototypes
//
static void Help(const int argsLength, LPCTSTR args[]);
static void List(const int argsLength, LPCTSTR args[]);
static bool ListCallback(LPINTERNET_CACHE_ENTRY_INFO entry);
static bool ListVerboseCallback(LPINTERNET_CACHE_ENTRY_INFO entry);
static void ListCookies(const int argsLength, LPCTSTR args[]);
static bool ListCookieCallback(LPINTERNET_CACHE_ENTRY_INFO entry);
static void OnError(DWORD error);
static SIZE_T FormatLongUniversalTime(const FILETIME* const universalFileTime, LPTSTR outputString, SIZE_T outputStringSize);
/////////////////////////////////////////////////////////////////////////////
//
// Allocates a block of memory, by default, on the process heap.
//
template <class P>
inline P WHeapAlloc
(
P& p,
SIZE_T size = 0,
DWORD options = HEAP_ZERO_MEMORY,
HANDLE heap = ::GetProcessHeap()
)
{
if (0 == size) size = sizeof(*p);
return (p = static_cast<P>(::HeapAlloc(heap, options, size)));
}
inline void* WHeapAlloc
(
void*& p,
SIZE_T size = 0,
DWORD options = HEAP_ZERO_MEMORY,
HANDLE heap = ::GetProcessHeap()
)
{
return (p = ::HeapAlloc(heap, options, size));
}
/////////////////////////////////////////////////////////////////////////////
//
// Frees a block of memory allocated on a heap using HeapAlloc. By default,
// the process heap is used. Unlike HeapFree, NULL pointers are ignored.
//
template <class P>
inline BOOL WHeapFree
(
P& p,
DWORD options = 0,
HANDLE heap = ::GetProcessHeap()
)
{
if (NULL == p) return TRUE;
BOOL succeeded = ::HeapFree(heap, options, p);
p = NULL;
_ASSERTE(succeeded);
return succeeded;
}
/////////////////////////////////////////////////////////////////////////////
//
// Command-line parser
//
template<class Handler>
class CommandLineParser : public Handler
{
public:
CommandLineParser() {}
bool Parse(int argumentsLength, LPCTSTR arguments[])
{
for (int i = 0; i < argumentsLength; i++)
{
LPCTSTR argument = arguments[i];
if (argument[0] == _T('-') ||
argument[0] == _T('/'))
{
const bool isLastArgument = (i + 1) == argumentsLength ;
LPCTSTR nextArgument = isLastArgument ? NULL : arguments[i + 1];
if (!Handler::HandleOption(argument + 1, nextArgument))
return false;
if (!nextArgument)
i++;
}
else
{
if (!Handler::HandleUnnamed(argument))
return false;
}
}
return Handler::EndOfParse();
}
private:
CommandLineParser(const CommandLineParser&);
CommandLineParser& operator=(const CommandLineParser&);
};
/////////////////////////////////////////////////////////////////////////////
//
// Command-line handler.
//
class CommandLineHandler
{
private:
bool m_verbose;
public:
CommandLineHandler() :
m_verbose(false) {}
bool Verbose() const { return m_verbose; }
bool HandleUnnamed(LPCTSTR unnamed)
{
_ASSERT(unnamed);
cerr << _T("Invalid argument: ") << unnamed << endl;
return false;
}
bool HandleOption(LPCTSTR option, LPCTSTR& argument)
{
_ASSERT(option);
if (IsOption(option, _T("verbose")))
{
m_verbose = true;
}
else
{
cerr << _T("Invalid option: ") << option << endl;
return false;
}
return true;
}
bool EndOfParse()
{
return true;
}
private:
static bool IsOption(LPCTSTR test, LPCTSTR option)
{
return CSTR_EQUAL == CompareString(LOCALE_INVARIANT, 0, test, -1, option, -1);
}
#pragma warning (push)
#pragma warning(disable: 4822/* local class member function does not have a body */)
CommandLineHandler(const CommandLineHandler&);
CommandLineHandler& operator=(const CommandLineHandler&);
#pragma warning (pop)
};
/////////////////////////////////////////////////////////////////////////////
//
// Application entry-point.
//
int _tmain(int argsLength, LPCTSTR args[])
{
#ifdef _DEBUG
_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) |
_CRTDBG_CHECK_ALWAYS_DF /* Check memory at every (de)allocation */ |
_CRTDBG_LEAK_CHECK_DF /* Automatic leak checking and reporting at program exit */);
#endif
//
// Discover the executable name (without path and extension).
//
StringCchCopy(g_executableName, DIM(g_executableName), args[0]);
PathStripPath(g_executableName);
PathRemoveExtension(g_executableName);
args++; argsLength--;
//
// Nothing told to do? Hint on trying help.
//
if (0 == argsLength)
{
cout << _T("Try '") << g_executableName << _T(" help' for usage.") << endl;
return 0;
}
//
// What's the command?
//
LPCTSTR command = args[0];
args++; argsLength--;
if (0 == _tcscmp(_T("help"), command))
{
Help(argsLength, args);
return 0;
}
else if (0 == _tcscmp(_T("list"), command))
{
List(argsLength, args);
return 0;
}
else if (0 == _tcscmp(_T("cookies"), command))
{
ListCookies(argsLength, args);
return 0;
}
else
{
//
// Invalid command.
//
cerr << _T("'") << command << _T("' is not a command. ")
<< _T("See '") << g_executableName << _T(" help' for usage.") << endl;
return -1;
}
return 0;
}
/////////////////////////////////////////////////////////////////////////////
//
// Enumerates all entries in the Inernet client cache to a callback.
//
template<class T>
void EnumInternetCache(T& callback)
{
HANDLE enumeration = NULL;
DWORD entryCapacity = 0;
DWORD entrySize = 0;
LPINTERNET_CACHE_ENTRY_INFO entry = NULL;
DWORD error;
bool proceed = false;
do
{
do
{
if (entrySize > entryCapacity)
{
WHeapFree(entry);
entryCapacity = 0;
if (!WHeapAlloc(entry, entrySize))
{
error = ERROR_OUTOFMEMORY;
break;
}
entryCapacity = entrySize;
}
bool succeeded;
if (NULL == enumeration)
{
enumeration = FindFirstUrlCacheEntry(NULL, entry, &entrySize);
succeeded = NULL != enumeration;
}
else
{
succeeded = FALSE != FindNextUrlCacheEntry(enumeration, entry, &entrySize);
}
error = succeeded ? ERROR_SUCCESS : GetLastError();
}
while (ERROR_INSUFFICIENT_BUFFER == error);
if (ERROR_SUCCESS == error)
proceed = callback(entry);
}
while (ERROR_NO_MORE_ITEMS != error && proceed);
WHeapFree(entry);
if (ERROR_SUCCESS != error &&
ERROR_NO_MORE_ITEMS != error)
OnError(error);
if (NULL != enumeration)
FindCloseUrlCache(enumeration);
}
/////////////////////////////////////////////////////////////////////////////
//
// Lists all entries (excluding cookies and visited URLs) in the Inernet
// client cache.
//
void List(const int argsLength, LPCTSTR args[])
{
_ASSERTE(argsLength >= 0);
_ASSERTE(0 == argsLength || args);
CommandLineParser<CommandLineHandler> parser;
if (!parser.Parse(argsLength, args))
exit(-1);
if (parser.Verbose())
EnumInternetCache(ListVerboseCallback);
else
EnumInternetCache(ListCallback);
}
bool ListCallback(LPINTERNET_CACHE_ENTRY_INFO entry)
{
_ASSERTE(entry);
if (0 == (entry->CacheEntryType & (URLHISTORY_CACHE_ENTRY | COOKIE_CACHE_ENTRY)))
{
cout << entry->lpszSourceUrlName << endl;
}
return true;
}
bool ListVerboseCallback(LPINTERNET_CACHE_ENTRY_INFO entry)
{
_ASSERTE(entry);
if (0 == (entry->CacheEntryType & (URLHISTORY_CACHE_ENTRY | COOKIE_CACHE_ENTRY)))
{
cout << _T("URL : ") << entry->lpszSourceUrlName << endl;
cout << _T("Local file : ") << entry->lpszLocalFileName << endl;
cout << _T("Use count : ") << entry->dwUseCount << endl;
cout << _T("Hit rate : ") << entry->dwHitRate << endl;
ULARGE_INTEGER size;
size.LowPart = entry->dwSizeLow;
size.HighPart = entry->dwSizeHigh;
cout << _T("Byte size : ") << size.QuadPart << endl;
TCHAR timeString[100];
FormatLongUniversalTime(&entry->LastAccessTime, timeString, DIM(timeString));
cout << _T("Accessed : ") << timeString << endl;
FormatLongUniversalTime(&entry->LastModifiedTime, timeString, DIM(timeString));
cout << _T("Modified : ") << timeString << endl;
FormatLongUniversalTime(&entry->LastSyncTime, timeString, DIM(timeString));
cout << _T("Synchronized: ") << timeString << endl;
cout << _T("Expiration : ");
if (0 == entry->ExpireTime.dwHighDateTime &&
0 == entry->ExpireTime.dwLowDateTime)
{
cout << _T("N/A");
}
else
{
FormatLongUniversalTime(&entry->ExpireTime, timeString, DIM(timeString));
cout << timeString;
}
cout << endl;
if (entry->dwHeaderInfoSize > 0)
{
cout << endl;
cout << (LPCSTR) entry->lpHeaderInfo << endl;
}
cout << _T("---------------------------------------") << endl;
}
return true;
}
/////////////////////////////////////////////////////////////////////////////
//
// Lists all cookies in the Inernet client cache.
//
void ListCookies(const int argsLength, LPCTSTR args[])
{
_ASSERTE(0 == argsLength || args);
_ASSERTE(argsLength >= 0);
EnumInternetCache(ListCookieCallback);
}
bool ListCookieCallback(LPINTERNET_CACHE_ENTRY_INFO entry)
{
_ASSERTE(entry);
if (COOKIE_CACHE_ENTRY == (entry->CacheEntryType & COOKIE_CACHE_ENTRY))
{
cout << entry->lpszSourceUrlName << endl;
}
return true;
}
/////////////////////////////////////////////////////////////////////////////
//
// Displays an error message and then ends the application!
//
void OnError(DWORD error)
{
//
// Get and display the error message corresponding to the error code.
//
LPTSTR message = NULL;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, error, 0,
reinterpret_cast<LPTSTR>(&message), 0, NULL);
cerr << endl << error << _T(": ");
if (message)
{
cerr << message << endl;
LocalFree(message);
}
else
{
cerr << _T("(No description available for this error code)\n");
}
exit(error);
}
/////////////////////////////////////////////////////////////////////////////
//
// Display usage help
//
void Help(const int argsLength, LPCTSTR args[])
{
_ASSERTE(0 == argsLength || args);
_ASSERTE(argsLength >= 0);
cout << _T("Usage: ") << g_executableName << _T(" help | list | cookies");
}
/////////////////////////////////////////////////////////////////////////////
//
// Formats UTC time using the long date and time patterns from user locale.
//
SIZE_T FormatLongUniversalTime(const FILETIME* const universalFileTime, LPTSTR outputString, SIZE_T outputStringSize)
{
_ASSERTE(universalFileTime);
_ASSERTE(outputString);
_ASSERTE(outputStringSize >= 0);
if (outputStringSize == 0)
return 0;
*outputString = 0;
//
// Convert FILETIME in UTC to SYSTEMTIME UTC and then to SYSTEMTIME in local
// time. Next, use components of SYSTEMTIME to load the corresponding
// components of the CRT tm structure. The tm structure allows us the use
// of strftime for date/time formatting.
//
SYSTEMTIME localSystemTime;
FileTimeToSystemTime(universalFileTime, &localSystemTime);
SystemTimeToTzSpecificLocalTime(NULL, &localSystemTime, &localSystemTime);
//
// Format using long date pattern followed by the long time pattern.
//
SIZE_T dateLength = GetDateFormat(LOCALE_USER_DEFAULT,
LOCALE_USE_CP_ACP | DATE_LONGDATE, &localSystemTime, /* lpFormat = */ NULL,
outputString, static_cast<int>(outputStringSize));
LPTSTR timeString = outputString + dateLength;
SIZE_T timeStringSize = outputStringSize - dateLength;
SIZE_T timeLength = GetTimeFormat(LOCALE_USER_DEFAULT,
LOCALE_USE_CP_ACP, &localSystemTime, /* lpFormat = */ NULL,
timeString, static_cast<int>(outputStringSize));
//
// Add a separation space between the date and time if both components are
// present in the output.
//
// NOTE! Lengths returns by GetDateFormat and GetTimeFormat include the
// null terminator so one of these has to be adjusted because the output
// string only contains one terminator.
//
if (dateLength > 0 && timeLength > 0)
outputString[dateLength - 1] = ' ';
return (0 == dateLength + timeLength) ? 0 : (dateLength + timeLength - 1);
}
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once
#include <iostream>
#include <iomanip>
#include <tchar.h>
#include <crtdbg.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <wininet.h>
#include <shlwapi.h>
#include <strsafe.h>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment