diff --git a/apache2/Makefile.win b/apache2/Makefile.win index 87a576d3be..c6c4bb6bc8 100644 --- a/apache2/Makefile.win +++ b/apache2/Makefile.win @@ -52,16 +52,13 @@ all: $(DLL) dll: $(DLL) -mod_security2_config.h: mod_security2_config.hw - @type mod_security2_config.hw > modsecurity_config.h - .c.obj: $(CC) $(CFLAGS) -c $< -Fo$@ .cpp.obj: $(CC) $(CFLAGS) -c $< -Fo$@ -$(DLL): mod_security2_config.h $(OBJS) +$(DLL): $(OBJS) $(CC) $(CFLAGS) $(LDFLAGS) -LD $(OBJS) -Fe$(DLL) $(LIBS) /link IF EXIST $(DLL).manifest $(MT) -manifest $(DLL).manifest -outputresource:$(DLL);2 @@ -69,4 +66,4 @@ install: $(DLL) copy /Y $(DLL) $(APACHE)\modules clean: - del $(OBJS) $(DLL) *.dll *.lib *.pdb *.idb *.ilk *.exp *.res *.rc *.bin mod_security2_config.h *.manifest + del $(OBJS) $(DLL) *.dll *.lib *.pdb *.idb *.ilk *.exp *.res *.rc *.bin *.manifest diff --git a/apache2/mod_security2_config.hw b/apache2/mod_security2_config.hw deleted file mode 100644 index c95004462f..0000000000 --- a/apache2/mod_security2_config.hw +++ /dev/null @@ -1 +0,0 @@ -/* This file is left empty for building on Windows. */ diff --git a/apache2/modsecurity_config.h b/apache2/modsecurity_config.h index 720a3c5fdd..7de60afe71 100644 --- a/apache2/modsecurity_config.h +++ b/apache2/modsecurity_config.h @@ -2,6 +2,8 @@ * so this is here to prevent that by removing them. */ +#ifndef WIN32 + /* Undefine all these so there are no conflicts */ #undef PACKAGE #undef PACKAGE_BUGREPORT @@ -22,3 +24,5 @@ #undef PACKAGE_TARNAME #undef PACKAGE_URL #undef PACKAGE_VERSION + +#endif diff --git a/apache2/msc_release.h b/apache2/msc_release.h index f960d61bd4..ff95235b77 100644 --- a/apache2/msc_release.h +++ b/apache2/msc_release.h @@ -50,7 +50,7 @@ /* Apache Module Defines */ #ifdef VERSION_IIS -#define MODSEC_MODULE_NAME "ModSecurity for IIS (Beta)" +#define MODSEC_MODULE_NAME "ModSecurity for IIS (RC)" #else #ifdef VERSION_NGINX #define MODSEC_MODULE_NAME "ModSecurity for nginx (Beta)" diff --git a/apache2/msc_util.c b/apache2/msc_util.c index 1e20473d09..a46312211f 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -1206,7 +1206,26 @@ char *log_escape(apr_pool_t *mp, const char *text) { } char *log_escape_nq(apr_pool_t *mp, const char *text) { +#ifdef VERSION_IIS + int l = 0; + + // this is a workaround for unknown bug that causes 'text' sometimes to lack zero-termination + // + __try + { + l = text ? strlen(text) : 0; + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + l = -1; + } + if(l < 0) + return _log_escape(mp, "BUG: see log_escape_nq()", 24, 0, 0, 0); + + return _log_escape(mp, (const unsigned char *)text, l, 0, 0, 0); +#else return _log_escape(mp, (const unsigned char *)text, text ? strlen(text) : 0, 0, 0, 0); +#endif } char *log_escape_ex(apr_pool_t *mp, const char *text, unsigned long int text_length) { diff --git a/iis/Makefile.win b/iis/Makefile.win index ae6acdbbf2..dafbacda9a 100644 --- a/iis/Makefile.win +++ b/iis/Makefile.win @@ -57,9 +57,6 @@ all: $(DLL) dll: $(DLL) -..\apache2\mod_security2_config.h: ..\apache2\mod_security2_config.hw - @type ..\apache2\mod_security2_config.hw > ..\apache2\modsecurity_config.h - $(OBJS1): ..\apache2\$*.c $(CC) $(CFLAGS) -c ..\apache2\$*.c -Fo$@ @@ -69,7 +66,7 @@ $(OBJS2): ..\standalone\$*.c .cpp.obj: $(CC) $(CFLAGS) -c $< -Fo$@ -$(DLL): ..\apache2\mod_security2_config.h $(OBJS1) $(OBJS2) $(OBJS3) +$(DLL): $(OBJS1) $(OBJS2) $(OBJS3) $(LINK) $(LDFLAGS) $(OBJS1) $(OBJS2) $(OBJS3) $(LIBS) IF EXIST $(DLL).manifest $(MT) -manifest $(DLL).manifest -outputresource:$(DLL);#1 diff --git a/iis/ModSecurityIIS/Installer/Installer.cpp b/iis/ModSecurityIIS/Installer/Installer.cpp new file mode 100644 index 0000000000..24f6d90726 --- /dev/null +++ b/iis/ModSecurityIIS/Installer/Installer.cpp @@ -0,0 +1,1994 @@ +// Installer.cpp : Defines the entry point for the console application. +// + +#include "stdafx.h" +#include +#include +#include "XUnzip.h" + +// Logging level definition. +#define EVENT_ERROR 100 +#define EVENT_CRITICAL 75 +#define EVENT_IMPORTANT 50 +#define EVENT_NORMAL 25 +#define EVENT_LEVEL_MASK 0xFFFF + +#define EVENT_APPLOG (1 << 17) +#define EVENT_SYSLOG (1 << 18) +#define EVENT_STDOUT (1 << 19) +#define EVENT_STDERR (1 << 20) + +// This class uese IIS7.0 native API to operate on +// %windir%\system32\inetsrv\config\applicationHost.config +class Iis7Config +{ + public: + Iis7Config() {} + virtual ~Iis7Config() {} + + // Defines the IIS running mode. + enum AppMode { + MODE_X64, // IIS runs under x86_64 mode. + MODE_X86, // IIS runs under i386 mode. + MODE_UNKNOWN, // IIS run under an unknow mode. + MODE_FAIL // Failed to retrieve app mode. + }; + + static void Log(int event_meta, const char *event_pattern, ...) { + const int kErrorLen = 2048; + char event_message[kErrorLen]; + va_list argptr; + + // If current event is less than log_level_, return directly. + int event_level = event_meta & EVENT_LEVEL_MASK; + //if (event_level < log_level_) return; + + // Combile log type with default log type. + int event_type = event_meta;// | log_type_; + + va_start(argptr, event_pattern); + vsnprintf(event_message, kErrorLen, event_pattern, argptr); + va_end(argptr); + +// #ifdef WIN32 + // Only write error and critical event to windows event log +// if ((event_type & EVENT_SYSLOG) != 0 && event_level >= EVENT_CRITICAL) { + WORD windows_event_type; + if (event_level == EVENT_ERROR) { + windows_event_type = EVENTLOG_ERROR_TYPE; + } else { // if (event_level == EVENT_CRITICAL) + windows_event_type= EVENTLOG_INFORMATION_TYPE; + } + + LPCSTR event_message_pointer = event_message; + + // Get a handle to the event log. + HANDLE h = RegisterEventSource(NULL, L"ModSecurityIIS Installer"); + if (h != NULL) { + // Report the event. + ReportEventA(h, // event log handle + windows_event_type, // event type + 0, // event category + 1, // event identifier + NULL, // no user security identifier + 1, // number of strings + 0, // no data + &event_message_pointer, // pointer to strings + NULL); // no data + DeregisterEventSource(h); + } +// } +// #endif // WIN32 + + // Output log to file +/* if ((event_type & EVENT_APPLOG) != 0) { + if (CreateLogFile()) { + FILE* file = fopen(log_file_name_.c_str(), "a+"); + if (file != NULL) { + fputs(FormatW3CTime(time(NULL)).c_str(), file); + fputs(": ", file); + fputs(event_message, file); + fputs("\n", file); + fclose(file); + } else if ((event_type & EVENT_STDERR) != 0) { + fprintf(stderr, "%s: Failed to open log file (%d).\n", + FormatW3CTime(time(NULL)).c_str(), errno); + } + } else if ((event_type & EVENT_STDERR) != 0) { + fprintf(stderr, "%s: Failed to create log file (%d)\n", + FormatW3CTime(time(NULL)).c_str(), errno); + } + } + + // Output to std err. + if ((event_type & EVENT_STDERR) != 0) { + fprintf(stderr, "%s: %s\n", + FormatW3CTime(time(NULL)).c_str(), event_message); + }*/ + } + + static void Log(int event_meta, const wchar_t *event_pattern, ...) { + const int kErrorLen = 2048; + wchar_t event_message[kErrorLen]; + va_list argptr; + + // If current event is less than log_level_, return directly. + int event_level = event_meta & EVENT_LEVEL_MASK; + //if (event_level < log_level_) return; + + // Combile log type with default log type. + int event_type = event_meta;// | log_type_; + + va_start(argptr, event_pattern); + _vsnwprintf(event_message, kErrorLen, event_pattern, argptr); + va_end(argptr); + +// #ifdef WIN32 + // Only write error and critical event to windows event log +// if ((event_type & EVENT_SYSLOG) != 0 && event_level >= EVENT_CRITICAL) { + WORD windows_event_type; + if (event_level == EVENT_ERROR) { + windows_event_type = EVENTLOG_ERROR_TYPE; + } else { // if (event_level == EVENT_CRITICAL) + windows_event_type= EVENTLOG_INFORMATION_TYPE; + } + + LPCWSTR event_message_pointer = event_message; + + // Get a handle to the event log. + HANDLE h = RegisterEventSource(NULL, L"ModSecurityIIS Installer"); + if (h != NULL) { + // Report the event. + ReportEventW(h, // event log handle + windows_event_type, // event type + 0, // event category + 1, // event identifier + NULL, // no user security identifier + 1, // number of strings + 0, // no data + &event_message_pointer, // pointer to strings + NULL); // no data + DeregisterEventSource(h); + } +// } +// #endif // WIN32 + + // Output log to file +/* if ((event_type & EVENT_APPLOG) != 0) { + if (CreateLogFile()) { + FILE* file = fopen(log_file_name_.c_str(), "a+"); + if (file != NULL) { + fputs(FormatW3CTime(time(NULL)).c_str(), file); + fputs(": ", file); + fputs(event_message, file); + fputs("\n", file); + fclose(file); + } else if ((event_type & EVENT_STDERR) != 0) { + fprintf(stderr, "%s: Failed to open log file (%d).\n", + FormatW3CTime(time(NULL)).c_str(), errno); + } + } else if ((event_type & EVENT_STDERR) != 0) { + fprintf(stderr, "%s: Failed to create log file (%d)\n", + FormatW3CTime(time(NULL)).c_str(), errno); + } + } + + // Output to std err. + if ((event_type & EVENT_STDERR) != 0) { + fprintf(stderr, "%s: %s\n", + FormatW3CTime(time(NULL)).c_str(), event_message); + }*/ + } + + // Install/uninstall filters in IIS. + // Actually, add/remove entry to/from + // adn sections. + static bool InstallFilter(const wchar_t* dll_file); + static bool UninstallFilter(); + + // Install/uninstall a site for Admin Console. + // Permissions for CGI exe are set/unset accordingly. +/* static bool InstallAdminConsole(const char* site_root, const char* cgi_path); + static bool UninstallAdminConsole(const char* site_root, + const char* cgi_path); +*/ + // Determine whether version 7 mode supported. + static bool IsSupported(); + + // Load configuration from IIS + // Actually, load from section. + virtual bool Load(); + + // Get IIS 7 running mode. + static AppMode GetAppMode(); + + private: + // Module name + static BSTR kModuleName; + static BSTR kConfigName; + + // Admin Console site name, "Google Sitemap Generator Admin Console". + //static BSTR kSiteName; + + static bool Iis7Config::AddToConfigSections(IAppHostWritableAdminManager* manager, + BSTR config); + + // Add an entry to section, like: + // + static bool AddToGlobalModules(IAppHostWritableAdminManager* manager, + BSTR image); + + // Add an entry to section, like: + // + static bool AddToModules(IAppHostWritableAdminManager* manager); + + // All CGI exe to be executed. + // "cgi_path" is the executable file path. + //static bool SetCGIRestriction(IAppHostWritableAdminManager* manager, + // BSTR cgi_path); + + // Create Admin Console site. + // "site_root" is the site root directory. + //static bool CreateAdminConsoleSite(IAppHostWritableAdminManager *manager, + // BSTR site_root); + + // Remove customization for Admin Console site. + // This customzation is added in CustomizeAdminConsole method. + //static bool RemoveAdminConsoleCustomization(IAppHostWritableAdminManager* manager); + + // Add customization for Admin Console site. + // Actually, it adds a tag for the Admin Console site. + //static bool CustomizeAdminConsole(IAppHostWritableAdminManager* manager, + // BSTR cgi_path); + + // Set property value. + static bool SetProperty(IAppHostElement* element, BSTR name, BSTR value); + + // Get property value. + static bool GetProperty(IAppHostElement* element, BSTR name, VARIANT* value); + + // Get the index from given collection, whose key=value. + // *index == -1 indicates nothing found. + static bool GetFromCollection(IAppHostElementCollection* collection, + BSTR property_key, BSTR property_value, + short* index); + + // Get the element from given colletion. + // The element should contains a propery, whose key/value is given by arg. + // "*element = NULL" indicates nothing can be found. + static bool GetFromCollection(IAppHostElementCollection* collection, + BSTR property_key, BSTR property_value, + IAppHostElement** element); + + // Get child's index from given collection, whose key = value. + // *index == -1 indicates no such child. + static bool GetFromChildren(IAppHostChildElementCollection* children, + BSTR child_name, short* index); + + // Get child element form given child collection. + // The child element should contains a property, whose key/value is given by + // arg. *element = NULL indicates no such child. + static bool GetFromChildren(IAppHostChildElementCollection* children, + BSTR child_name, IAppHostElement** element); + + // Remove an entry from given section. + static bool RemoveFromCollection(IAppHostWritableAdminManager* manager, + BSTR section, BSTR property_name, BSTR property_value); + + // Load site configuration from an element. + //bool LoadSite(IAppHostElement* site_element); + + static int Iis7Config::GetNativeSystemInfo(SYSTEM_INFO* system_info); +}; + +BSTR Iis7Config::kModuleName = L"ModSecurityIIS"; +BSTR Iis7Config::kConfigName = L"ModSecurity"; +//BSTR Iis7Config::kSiteName = L"ModSecurityIIS Admin Console"; + +#define CHECK_HRESULT(expression, msg) \ +{ \ +HRESULT _hr = (expression); \ +if (FAILED(_hr)) { \ + Log(EVENT_ERROR, msg " (0x%0x)", _hr); \ + break; \ +} \ +} +#define CHECK_BOOL(expression, msg) \ + if (!expression) { \ + Log(EVENT_ERROR, msg); \ + break; \ + } + +bool Iis7Config::Load() { + IAppHostAdminManager* admin_manager = NULL; + IAppHostElement* parent = NULL; + IAppHostElementCollection* collection = NULL; + IAppHostElement* element = NULL; + + //BSTR section_name = SysAllocString(L"system.applicationHost/sites"); + BSTR bstr_config_path = SysAllocString(L"MACHINE/WEBROOT/APPHOST"); + + HRESULT hresult = S_OK; + bool result = false; + do { + // Initialize com + hresult = CoInitializeEx(NULL, COINIT_MULTITHREADED); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to intialize COM."); + break; + } + + // Create an admin manager + hresult = CoCreateInstance(__uuidof(AppHostAdminManager), NULL, + CLSCTX_INPROC_SERVER, + __uuidof(IAppHostAdminManager), + reinterpret_cast(&admin_manager)); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to create AppHostAdminManager."); + break; + } +/* + // Get section + hresult = admin_manager->GetAdminSection(section_name, bstr_config_path, &parent); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to access sites configuration."); + break; + } else if (&parent == NULL) { + Log(EVENT_ERROR, "Unable to get sites configuration."); + break; + } + + // Get all the child elements of + hresult = parent->get_Collection(&collection); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to access child collection."); + break; + } else if (&collection == NULL) { + Log(EVENT_ERROR, "Unable to get child collection."); + break; + } + + // Iterate all the children of + DWORD element_count = 0; + hresult = collection->get_Count(&element_count); + for(USHORT i = 0; i < element_count; ++i) { + VARIANT item_index; + item_index.vt = VT_I2; + item_index.iVal = i; + + // Get the child element + hresult = collection->get_Item(item_index, &element); + if (FAILED(hresult) || &element == NULL) { + Log(EVENT_ERROR, "Failed to iterate child (%d) of sites.", i); + continue; + } + + if (!LoadSite(element)) { + Log(EVENT_ERROR, "Failed to load site from site (%d).", i); + continue; + } + + element->Release(); + element = NULL; + } +*/ + result = true; + } while (false); + + // Exiting / Unwinding + if (element != NULL) element->Release(); + if (collection != NULL) collection->Release(); + if (parent != NULL) parent->Release(); + if (admin_manager != NULL) admin_manager->Release(); + + //SysFreeString(section_name); + SysFreeString(bstr_config_path); + CoUninitialize(); + + return result; +} + +bool Iis7Config::InstallFilter(const wchar_t* dll_file) { + IAppHostWritableAdminManager* admin_manager = NULL; + BSTR bstr_config_path = SysAllocString(L"MACHINE/WEBROOT/APPHOST"); + + // Build the full path of ModSecurity module DLL. + BSTR module_wpath = SysAllocString(dll_file); + BSTR config = SysAllocString(kConfigName); + + HRESULT hresult = S_OK; + bool result = false; + do { + // Initialize com + hresult = CoInitializeEx(NULL, COINIT_MULTITHREADED); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to intialize COM."); + break; + } + + // Create a writable admin manager + hresult = CoCreateInstance(__uuidof(AppHostWritableAdminManager), NULL, + CLSCTX_INPROC_SERVER, + __uuidof(IAppHostWritableAdminManager), + reinterpret_cast(&admin_manager)); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to create AppHostWritableAdminManager."); + break; + } + + // set commit path + hresult = admin_manager->put_CommitPath(L"MACHINE/WEBROOT/APPHOST"); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to put commit path."); + break; + } + + // Add ModSecurity entry to section + if (!AddToGlobalModules(admin_manager, module_wpath)) { + Log(EVENT_ERROR, "Failed to add entry to globalModules."); + break; + } + + // Add ModSecurity entry to section + if (!AddToModules(admin_manager)) { + Log(EVENT_ERROR, "Failed to add entry to modules."); + break; + } + + if(!AddToConfigSections(admin_manager, config)) { + Log(EVENT_ERROR, "Failed to add entry to config sections."); + break; + } + + // Save changes. + hresult = admin_manager->CommitChanges(); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to save changes to install module."); + break; + } + + result = true; + } while (false); + + // Exiting / Unwinding + if (admin_manager != NULL) admin_manager->Release(); + + SysFreeString(module_wpath); + SysFreeString(bstr_config_path); + SysFreeString(config); + + CoUninitialize(); + + return result; +} + +bool Iis7Config::UninstallFilter() { + IAppHostWritableAdminManager* admin_manager = NULL; + BSTR bstr_config_path = SysAllocString(L"MACHINE/WEBROOT/APPHOST"); + + HRESULT hresult = S_OK; + bool result = false; + do { + // Initialize com + hresult = CoInitializeEx(NULL, COINIT_MULTITHREADED); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to intialize COM."); + break; + } + + // Create a writable admin manager + hresult = CoCreateInstance(__uuidof(AppHostWritableAdminManager), NULL, + CLSCTX_INPROC_SERVER, + __uuidof(IAppHostWritableAdminManager), + reinterpret_cast(&admin_manager)); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to create AppHostWritableAdminManager."); + break; + } + + // set commit path + hresult = admin_manager->put_CommitPath(bstr_config_path); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to put commit path."); + break; + } + + // Remove ModSecurity entry from section. + if (!RemoveFromCollection(admin_manager, L"system.webServer/globalModules", L"name", kModuleName)) { + Log(EVENT_ERROR, "Failed to remove entry from globalModules."); + break; + } + + // Remove ModSecurity entry from section. + if (!RemoveFromCollection(admin_manager, L"system.webServer/modules", L"name", kModuleName)) { + Log(EVENT_ERROR, "Failed to remove entry from modules."); + break; + } + + // Save changes. + hresult = admin_manager->CommitChanges(); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to save changes to install module."); + break; + } + + result = true; + } while (false); + + // Exiting / Unwinding + if (admin_manager != NULL) admin_manager->Release(); + SysFreeString(bstr_config_path); + + CoUninitialize(); + + return result; +} + +// Add section to , like: +//
+bool Iis7Config::AddToConfigSections(IAppHostWritableAdminManager* manager, + BSTR config) { + HRESULT hr; + bool result = false; + IAppHostConfigManager * pCfgMgr = NULL; + IAppHostConfigFile * pCfgFile = NULL; + IAppHostSectionGroup * pRtSctnGrp = NULL; + IAppHostSectionGroup * pSctnGrp = NULL; + IAppHostSectionDefinitionCollection * pSctnDefCol = NULL; + IAppHostSectionDefinition * pSctnDef = NULL; + + BSTR bstrConfigCommitPath = SysAllocString(L"MACHINE/WEBROOT/APPHOST"); + BSTR bstrSctnGrpName = SysAllocString(L"system.webServer"); + BSTR bstrDeny = SysAllocString(L"Allow"); + BSTR bstrAppHostOnly = SysAllocString(L"Everywhere"); + + // Get an IAppHostConfigManager + hr = manager -> get_ConfigManager ( &pCfgMgr ); + if ( FAILED( hr ) || ( &pCfgMgr == NULL ) ) + { + Log(EVENT_ERROR, "ERROR: Unable to get a config manager.\n" ); + goto exit; + } + + // Get an IAppHostConfigFile + hr = pCfgMgr -> GetConfigFile ( bstrConfigCommitPath, &pCfgFile ); + if ( FAILED ( hr ) || ( &pCfgFile == NULL ) ) + { + if ( E_ACCESSDENIED == hr ) + { + Log(EVENT_ERROR, "ERROR: Access to configuration denied.\n" ); + Log(EVENT_ERROR, " Run sample as an administrator.\n" ); + } + else + { + Log(EVENT_ERROR, "ERROR: Unable to get config file.\n" ); + } + goto exit; + } + + // Get the root section group + hr = pCfgFile -> get_RootSectionGroup ( &pRtSctnGrp ); + if ( FAILED ( hr ) || ( &pRtSctnGrp == NULL ) ) + { + Log(EVENT_ERROR, "ERROR: Unable to access root section group\n" ); + goto exit; + } + + ULONG i, cnt; + + hr = pRtSctnGrp ->get_Count(&cnt); + if ( FAILED ( hr )) + { + Log(EVENT_ERROR, "ERROR: Unable to enumerate root section group\n" ); + goto exit; + } + + for(i = 0; i < cnt; i++) + { + VARIANT v; + BSTR name; + + v.vt = VT_I4; + v.intVal = i; + + hr = pRtSctnGrp->get_Item(v, &pSctnGrp); + if ( FAILED ( hr ) || ( &pSctnGrp == NULL ) ) + { + Log(EVENT_ERROR, "ERROR: Unable to access root section item\n" ); + goto exit; + } + + hr = pSctnGrp->get_Name(&name); + if ( FAILED ( hr )) + { + Log(EVENT_ERROR, "ERROR: Unable to get root section name\n" ); + goto exit; + } + + if(wcscmp(name, L"system.webServer") == 0) + break; + + pSctnGrp->Release(); + } + + if(i == cnt) + { + Log(EVENT_ERROR, "ERROR: Unable to find system.webServer section group\n"); + goto exit; + } + + // Get the section collection + hr = pSctnGrp -> get_Sections ( &pSctnDefCol ); + if ( FAILED ( hr ) || ( &pSctnDefCol == NULL ) ) + { + Log(EVENT_ERROR, "ERROR: Unable to access section collection\n" ); + goto exit; + } + + hr = pSctnDefCol->get_Count(&cnt); + if ( FAILED ( hr )) + { + Log(EVENT_ERROR, "ERROR: Unable to enumerate section collection\n" ); + goto exit; + } + + for(i = 0; i < cnt; i++) + { + VARIANT v; + BSTR name; + + v.vt = VT_I4; + v.intVal = i; + + hr = pSctnDefCol->get_Item(v, &pSctnDef); + if ( FAILED ( hr ) || ( &pSctnDef == NULL ) ) + { + Log(EVENT_ERROR, "ERROR: Unable to access section item\n" ); + goto exit; + } + + hr = pSctnDef->get_Name(&name); + if ( FAILED ( hr )) + { + Log(EVENT_ERROR, "ERROR: Unable to get section name\n" ); + goto exit; + } + + if(wcscmp(name, config) == 0) + break; + + pSctnDef->Release(); + } + + if(i == cnt) + { + // Add the new section + hr = pSctnDefCol -> AddSection ( config, &pSctnDef ); + if ( FAILED ( hr ) || ( &pSctnDef == NULL ) ) + { + Log(EVENT_ERROR, "ERROR: Unable to add new section\n" ); + goto exit; + } + } + + // Set the section attributes + pSctnDef -> put_OverrideModeDefault ( bstrDeny ); + pSctnDef -> put_AllowDefinition ( bstrAppHostOnly ); + + result = true; +exit: + // Exiting / Unwinding + if ( pRtSctnGrp != NULL ) + { + pRtSctnGrp->Release(); + pRtSctnGrp = NULL; + } + if ( pSctnGrp != NULL ) + { + pSctnGrp->Release(); + pSctnGrp = NULL; + } + if ( pSctnDefCol != NULL ) + { + pSctnDefCol->Release(); + pSctnDefCol = NULL; + } + if ( pSctnDef != NULL ) + { + pSctnDef->Release(); + pSctnDef = NULL; + } + if ( pCfgFile != NULL ) + { + pCfgFile->Release(); + pCfgFile = NULL; + } + if ( pCfgMgr != NULL ) + { + pCfgMgr->Release(); + pCfgMgr = NULL; + } + + SysFreeString( bstrConfigCommitPath ); + SysFreeString( bstrSctnGrpName ); + SysFreeString( bstrDeny ); + SysFreeString( bstrAppHostOnly ); + + return result; +} + +// Add an entry to section, like: +// +bool Iis7Config::AddToGlobalModules(IAppHostWritableAdminManager* manager, + BSTR image) { + IAppHostElement* parent = NULL; + IAppHostElementCollection* collection = NULL; + IAppHostElement* element = NULL; + + BSTR bstr_section_name = SysAllocString(L"system.webServer/globalModules"); + BSTR bstr_config_path = SysAllocString(L"MACHINE/WEBROOT/APPHOST"); + BSTR bstr_element_name = SysAllocString(L"add"); + + HRESULT hresult = S_OK; + bool result = false; + do { + // Get section + hresult = manager->GetAdminSection(bstr_section_name, + bstr_config_path, &parent); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to access globalModules configuration."); + break; + } else if (&parent == NULL) { + Log(EVENT_ERROR, "Unable to get globalModules configuration."); + break; + } + + // Get collection of + hresult = parent->get_Collection(&collection); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to access globalModules child collection."); + break; + } else if (&collection == NULL) { + Log(EVENT_ERROR, "Unable to get globalModules child collection."); + break; + } + + // create a global modules child element, like: + // + + // First to detect old modules. + if (!GetFromCollection(collection, L"name", kModuleName, &element)) { + Log(EVENT_ERROR, "Failed to try detect old modules."); + break; + } + + // No old ModSecurity module, create/add one. + if (element == NULL) { + hresult = collection->CreateNewElement(bstr_element_name, &element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to create globalModules/add element."); + break; + } + + if (!SetProperty(element, L"name", kModuleName)) { + Log(EVENT_ERROR, "Failed to set name property."); + break; + } + // Add the new element to collection + hresult = collection->AddElement(element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to add globalModule/add element."); + break; + } + } + + if (!SetProperty(element, L"image", image)) { + Log(EVENT_ERROR, "Failed to set image property."); + break; + } + + result = true; + } while (false); + + // Exiting / Unwinding + if (element != NULL) element->Release(); + if (collection != NULL) collection->Release(); + if (parent != NULL) parent->Release(); + + SysFreeString(bstr_section_name); + SysFreeString(bstr_config_path); + SysFreeString(bstr_element_name); + + return result; +} + + +// Add an entry to section, like: +// +bool Iis7Config::AddToModules(IAppHostWritableAdminManager* manager) { + IAppHostElement* parent = NULL; + IAppHostElementCollection* collection = NULL; + IAppHostElement* element = NULL; + + BSTR bstr_section_name = SysAllocString(L"system.webServer/modules"); + BSTR bstr_config_path = SysAllocString(L"MACHINE/WEBROOT/APPHOST"); + BSTR bstr_element_name = SysAllocString(L"add"); + + HRESULT hresult = S_OK; + bool result = false; + do { + // Get section + hresult = manager->GetAdminSection(bstr_section_name, + bstr_config_path, &parent); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to access modules configuration."); + break; + } else if (&parent == NULL) { + Log(EVENT_ERROR, "Unable to get modules configuration."); + break; + } + + // Get collection of + hresult = parent->get_Collection(&collection); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to access modules child collection."); + break; + } else if (&collection == NULL) { + Log(EVENT_ERROR, "Unable to get modules child collection."); + break; + } + + // Try to detect old ModSecurity module. + if (!GetFromCollection(collection, L"name", kModuleName, &element)) { + Log(EVENT_ERROR, "Failed to try detect old modules."); + break; + } + + // No old ModSecurity module exists. + if (element == NULL) { + hresult = collection->CreateNewElement(bstr_element_name, &element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to create modules/add element."); + break; + } + + if (!SetProperty(element, L"name", kModuleName)) { + Log(EVENT_ERROR, "Failed to set name property."); + break; + } + + // Add the new element to collection + hresult = collection->AddElement(element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to add modules/add element."); + break; + } + } + + result = true; + } while (false); + + // Exiting / Unwinding + if (element != NULL) element->Release(); + if (collection != NULL) collection->Release(); + if (parent != NULL) parent->Release(); + + SysFreeString(bstr_section_name); + SysFreeString(bstr_config_path); + SysFreeString(bstr_element_name); + + return result; +} + +bool Iis7Config::RemoveFromCollection(IAppHostWritableAdminManager* manager, + BSTR section, BSTR property_name, BSTR property_value) { + IAppHostElement* parent = NULL; + IAppHostElementCollection* collection = NULL; + IAppHostElement* element = NULL; + IAppHostProperty* name_property = NULL; + + BSTR bstr_section_name = SysAllocString(section); + BSTR bstr_config_path = SysAllocString(L"MACHINE/WEBROOT/APPHOST"); + + HRESULT hresult = S_OK; + bool result = false; + do { + // Get section contains ModSecurityModule entry + hresult = manager->GetAdminSection(bstr_section_name, + bstr_config_path, &parent); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to access section to remove module."); + break; + } else if (&parent == NULL) { + Log(EVENT_ERROR, "Unable to get section to remove module."); + break; + } + + // Get child collection of the section + hresult = parent->get_Collection(&collection); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to access collection to remove module."); + break; + } else if (&collection == NULL) { + Log(EVENT_ERROR, "Unable to get collection to remove module."); + break; + } + + // Get number of children. + DWORD count; + hresult = collection->get_Count(&count); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to get the count of collection."); + break; + } + + short sitemap_index; + if (!GetFromCollection(collection, property_name, property_value, &sitemap_index)) { + Log(EVENT_ERROR, "Failed to find ModSecurity module."); + break; + } + + if (sitemap_index != -1) { + VARIANT var_index; + var_index.vt = VT_I2; + var_index.iVal = sitemap_index; + + hresult = collection->DeleteElement(var_index); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to remove ModSecurity module."); + } + } else { + Log(EVENT_IMPORTANT, "No ModSecurity module is found."); + } + + result = SUCCEEDED(hresult); + } while (false); + + // Exiting / Unwinding + if (name_property != NULL) name_property->Release(); + if (element != NULL) element->Release(); + if (collection != NULL) collection->Release(); + if (parent != NULL) parent->Release(); + + SysFreeString(bstr_section_name); + SysFreeString(bstr_config_path); + + return result; +} +/* +bool Iis7Config::LoadSite(IAppHostElement* element) { + std::string name, id, phys_path, log_path; + + // Get name. + VARIANT name_var; + if (!GetProperty(element, L"name", &name_var)) { + Log(EVENT_ERROR, "Failed to get name property value."); + return false; + } else if (name_var.vt != VT_BSTR) { + Log(EVENT_ERROR, "site name should be VT_BSTR"); + return false; + } else { + name = static_cast(bstr_t(name_var.bstrVal)); + } + + // Get id. + VARIANT id_var; + if (!GetProperty(element, L"id", &id_var)) { + Log(EVENT_ERROR, "Failed to get id value."); + return false; + } else if (id_var.vt == VT_I4) { + char buffer[16]; + itoa(id_var.intVal, buffer, 10); + id = buffer; + } else if (id_var.vt == VT_UI4) { + char buffer[16]; + itoa(id_var.uintVal, buffer, 10); + id = buffer; + } else if (id_var.vt == VT_BSTR) { + id = static_cast(bstr_t(id_var.bstrVal)); + } else { + Log(EVENT_ERROR, "site id should be VT_BSTR or VT_I4"); + } + + // Get logfile directory. + IAppHostElement* logfile_element = NULL; + HRESULT hresult = element->GetElementByName(L"logfile", &logfile_element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to get logfile element."); + } else { + VARIANT dir_var; + if (!GetProperty(logfile_element, L"directory", &dir_var)) { + Log(EVENT_ERROR, "Failed to get directory value."); + } else if (dir_var.vt == VT_BSTR) { + log_path = static_cast(bstr_t(dir_var.bstrVal)); + log_path.append("\\W3SVC").append(id); + } else { + Log(EVENT_ERROR, "directory should be VT_BSTR"); + } + } + + // Get pysical path. + IAppHostElementCollection* application_collection = NULL; + IAppHostElementCollection* virtualdir_collection = NULL; + IAppHostElement* application_element = NULL; + IAppHostElement* virtualdir_element = NULL; + + hresult = S_OK; + bool result = false; + do { + // Get collection in element + hresult = element->get_Collection(&application_collection); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to get application collection."); + break; + } + + // Find element wiht path="/" + short root_app = -1; + if (!GetFromCollection(application_collection, L"path", L"/", &root_app)) { + Log(EVENT_ERROR, "Failed to get root application index."); + break; + } else if (root_app == -1) { + Log(EVENT_ERROR, "No root application defined."); + break; + } + + // Get the root element + VARIANT var_rootapp; + var_rootapp.vt = VT_I2; + var_rootapp.iVal = root_app; + hresult = application_collection->get_Item(var_rootapp, &application_element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to get root application element."); + break; + } + + // Get collection in element. + hresult = application_element->get_Collection(&virtualdir_collection); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to get virtualdir collection."); + break; + } + + // Find element with path="/" + short root_dir = -1; + if (!GetFromCollection(virtualdir_collection, L"path", L"/", &root_dir)) { + Log(EVENT_ERROR, "Failed to get root virtualDirectory index."); + break; + } else if (root_dir == -1) { + Log(EVENT_ERROR, "No root virtualDirectory defined."); + break; + } + + // Get the root element. + VARIANT var_rootdir; + var_rootdir.vt = VT_I2; + var_rootdir.iVal = root_dir; + hresult = virtualdir_collection->get_Item(var_rootdir, &virtualdir_element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to get root virtualDirectory element."); + break; + } + + VARIANT var_path; + if (!GetProperty(virtualdir_element, L"physicalPath", &var_path)) { + Log(EVENT_ERROR, "Failed to get physicalPath property."); + break; + } else if (var_path.vt != VT_BSTR) { + Log(EVENT_ERROR, "physicalPath should be instance of VT_BSTR."); + break; + } + + phys_path = static_cast(bstr_t(var_path.bstrVal)); + result = true; + } while (false); + + if (logfile_element != NULL) logfile_element->Release(); + if (application_collection != NULL) application_collection->Release(); + if (virtualdir_collection != NULL) virtualdir_collection->Release(); + if (application_element != NULL) application_element->Release(); + if (virtualdir_element != NULL) virtualdir_element->Release(); + + // At last we store all the values. + if (result) { + site_ids_.push_back(id); + names_.push_back(name); + physical_paths_.push_back(phys_path); + log_paths_.push_back(log_path); + host_urls_.push_back(""); + } + + return result; +} +*/ +bool Iis7Config::GetFromCollection(IAppHostElementCollection* collection, + BSTR property_key, BSTR property_value, + short* index) { + IAppHostProperty* property = NULL; + IAppHostElement* element = NULL; + + HRESULT hresult = S_OK; + *index = -1; + do { + // Get number of children. + DWORD count; + hresult = collection->get_Count(&count); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to get the count of collection."); + break; + } + + // Iterate every child to check whether it should be removed. + for (USHORT i = 0; i < count && SUCCEEDED(hresult); ++i) { + // Get item at index i. + VARIANT idx; + idx.vt = VT_I2; + idx.iVal = i; + hresult = collection->get_Item(idx, &element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to get item (%d).", i); + break; + } + + // Get name property of item i. + VARIANT var_value; + if (!GetProperty(element, property_key, &var_value)) { + Log(EVENT_ERROR, "Failed to get property value."); + hresult = S_FALSE; + break; + } + + // Check the property value + if (wcscmp(property_value, var_value.bstrVal) == 0) { + *index = static_cast(i); + break; + } + + element->Release(); + element = NULL; + } + } while (false); + + if (property != NULL) property->Release(); + if (element != NULL) element->Release(); + + return SUCCEEDED(hresult); +} + +bool Iis7Config::GetFromCollection(IAppHostElementCollection* collection, + BSTR property_key, BSTR property_value, + IAppHostElement** element) { + short idx; + if (!GetFromCollection(collection, property_key, property_value, &idx)) { + Log(EVENT_ERROR, "Failed to get child from collection."); + } + + if (idx != -1) { + VARIANT idx_var; + idx_var.vt = VT_I2; + idx_var.iVal = idx; + + HRESULT hresult = collection->get_Item(idx_var, element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to get element from collection."); + return false; + } else { + return true; + } + } else { + *element = NULL; + return true; + } +} + + +bool Iis7Config::SetProperty(IAppHostElement* element, BSTR name, BSTR value) { + IAppHostProperty* property = NULL; + BSTR bstr_name = SysAllocString(name); + BSTR bstr_value = SysAllocString(value); + + HRESULT hresult = S_OK; + do { + // Get the property by name. + hresult = element->GetPropertyByName(bstr_name, &property); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to get property."); + break; + } + + // Set the property value. + VARIANT value_variant; + value_variant.vt = VT_BSTR; + value_variant.bstrVal = bstr_value; + hresult = property->put_Value(value_variant); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to set property value."); + break; + } + } while (false); + + if (property != NULL) property->Release(); + SysFreeString(bstr_name); + SysFreeString(bstr_value); + + return SUCCEEDED(hresult); +} + +bool Iis7Config::GetProperty(IAppHostElement* element, BSTR name, VARIANT* value) { + IAppHostProperty* property = NULL; + BSTR bstr_name = SysAllocString(name); + + HRESULT hresult = S_OK; + do { + // Get the property by name. + hresult = element->GetPropertyByName(bstr_name, &property); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to get property."); + break; + } + + // Get the property value. + hresult = property->get_Value(value); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to get property value."); + break; + } + } while (false); + + if (property != NULL) property->Release(); + SysFreeString(bstr_name); + + return SUCCEEDED(hresult); +} + + +bool Iis7Config::IsSupported() { + HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); + if (FAILED(hr)) { + Log(EVENT_ERROR, "Failed to check IIS 7 compatibility. 0x%0x", hr); + return false; + } + + // Try with IIS 7.0... + IAppHostAdminManager* admin_manager = NULL; + hr = CoCreateInstance(__uuidof(AppHostAdminManager), NULL, + CLSCTX_INPROC_SERVER, __uuidof(IAppHostAdminManager), + reinterpret_cast(&admin_manager)); + if (SUCCEEDED(hr)) { + admin_manager->Release(); + } + + CoUninitialize(); + + return SUCCEEDED(hr); +} + +int Iis7Config::GetNativeSystemInfo(SYSTEM_INFO* system_info) { + HMODULE module = GetModuleHandle(L"Kernel32.dll"); + if (module == NULL) { + Log(EVENT_ERROR, "Failed to GetModuleHandle of Kernel32.dll. (%d)", + GetLastError()); + return -1; + } + + typedef void (WINAPI *Procedure)(LPSYSTEM_INFO); + Procedure proc = (Procedure) GetProcAddress(module, "GetNativeSystemInfo"); + if (proc == NULL) { + if (GetLastError() == ERROR_PROC_NOT_FOUND) { + return 0; + } else { + Log(EVENT_ERROR, "Failed to GetProcAddress (%d).", GetLastError()); + return -1; + } + return 0; + } else { + proc(system_info); + return 1; + } +} + +Iis7Config::AppMode Iis7Config::GetAppMode() { + SYSTEM_INFO sys_info; + if (Iis7Config::GetNativeSystemInfo(&sys_info) != 1) { + Log(EVENT_ERROR, "Failed to GetNativeSystemInfo for IIS7."); + return MODE_FAIL; + } + + if (sys_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) { + return MODE_X86; + } else if (sys_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { + return MODE_X64; + } else { + return MODE_UNKNOWN; + } +} +/* +bool Iis7Config::InstallAdminConsole(const char* site_root, + const char* cgi_path) { + IAppHostWritableAdminManager* admin_manager = NULL; + bool result = false; + do { + // Initialize com + CHECK_HRESULT(CoInitializeEx(NULL, COINIT_MULTITHREADED), "Failed to intialize COM."); + + // Create a writable admin manager + CHECK_HRESULT(CoCreateInstance(__uuidof(AppHostWritableAdminManager), NULL, + CLSCTX_INPROC_SERVER, + __uuidof(IAppHostWritableAdminManager), + reinterpret_cast(&admin_manager)), + "Failed to create AppHostWritableAdminManager."); + + // set commit path + CHECK_HRESULT(admin_manager->put_CommitPath(L"MACHINE/WEBROOT/APPHOST"), + "Failed to put commit path."); + + + CHECK_BOOL(CreateAdminConsoleSite(admin_manager, bstr_t(site_root)), + "Failed to create AdminConsole Site."); + CHECK_BOOL(SetCGIRestriction(admin_manager, bstr_t(cgi_path)), + "Failed to set CGI restriction."); + CHECK_BOOL(CustomizeAdminConsole(admin_manager, bstr_t(cgi_path)), + "Failed to customize admin console."); + + // Save changes. + CHECK_HRESULT(admin_manager->CommitChanges(), + "Failed to save changes to install module."); + + Log(EVENT_CRITICAL, "Admin Console installed successfully."); + result = true; + } while (false); + + // Exiting / Unwinding + if (admin_manager != NULL) admin_manager->Release(); + + CoUninitialize(); + + return result; +} + +bool Iis7Config::UninstallAdminConsole(const char* site_root, + const char* cgi_path) { + IAppHostWritableAdminManager* admin_manager = NULL; + BSTR bstr_config_path = SysAllocString(L"MACHINE/WEBROOT/APPHOST"); + + bool result = false; + do { + // Initialize com + CHECK_HRESULT(CoInitializeEx(NULL, COINIT_MULTITHREADED), "Failed to intialize COM."); + + // Create a writable admin manager + CHECK_HRESULT(CoCreateInstance(__uuidof(AppHostWritableAdminManager), NULL, + CLSCTX_INPROC_SERVER, + __uuidof(IAppHostWritableAdminManager), + reinterpret_cast(&admin_manager)), + "Failed to create AppHostWritableAdminManager."); + + // set commit path + CHECK_HRESULT(admin_manager->put_CommitPath(L"MACHINE/WEBROOT/APPHOST"), + "Failed to put commit path."); + + CHECK_BOOL(RemoveFromCollection(admin_manager, L"system.applicationHost/sites", L"name", kSiteName), "Failed to remove Admin console site."); + CHECK_BOOL(RemoveFromCollection(admin_manager, L"system.webServer/security/isapiCgiRestriction", L"path", bstr_t(cgi_path)), + "Failed to remove CGI restriction."); + CHECK_BOOL(RemoveAdminConsoleCustomization(admin_manager), + "Failed to remove customization of admin console."); + + // Save changes. + CHECK_HRESULT(admin_manager->CommitChanges(), + "Failed to save changes to install module."); + + result = true; + } while (false); + + // Exiting / Unwinding + if (admin_manager != NULL) admin_manager->Release(); + + SysFreeString(bstr_config_path); + + CoUninitialize(); + + return result; +} + + +bool Iis7Config::SetCGIRestriction(IAppHostWritableAdminManager* manager, BSTR cgi_path) { + IAppHostElement* parent = NULL; + IAppHostElementCollection* collection = NULL; + IAppHostElement* element = NULL; + + BSTR bstr_section_name = SysAllocString(L"system.webServer/security/isapiCgiRestriction"); + BSTR bstr_config_path = SysAllocString(L"MACHINE/WEBROOT/APPHOST"); + BSTR bstr_element_name = SysAllocString(L"add"); + + HRESULT hresult = S_OK; + bool result = false; + do { + // Get section + hresult = manager->GetAdminSection(bstr_section_name, + bstr_config_path, &parent); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to access isapiCgiRestriction configuration. (0x%0x)", hresult); + break; + } else if (&parent == NULL) { + Log(EVENT_ERROR, "Unable to get isapiCgiRestriction configuration."); + break; + } + + // Get collection of + hresult = parent->get_Collection(&collection); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to access isapiCgiRestriction child collection. (0x%0x)", hresult); + break; + } else if (&collection == NULL) { + Log(EVENT_ERROR, "Unable to get isapiCgiRestriction child collection."); + break; + } + + // create a child element, like: + // + + // First to detect old cgi restriction. + if (!GetFromCollection(collection, L"path", cgi_path, &element)) { + Log(EVENT_ERROR, "Failed to try detect old cgi restriction."); + break; + } + + // No old cgi restriction, create one. + if (element == NULL) { + hresult = collection->CreateNewElement(bstr_element_name, &element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to create isapiCgiRestriction/add element. (0x%0x)", hresult); + break; + } + + if (!SetProperty(element, L"path", cgi_path)) { + Log(EVENT_ERROR, "Failed to set path property."); + break; + } + + if (!SetProperty(element, L"allowed", L"true")) { + Log(EVENT_ERROR, "Failed to set allowed property."); + break; + } + + // Add the new element to collection + hresult = collection->AddElement(element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to add isapiCgiRestriction/add element. (0x%0x)", hresult); + break; + } + } + + Log(EVENT_NORMAL, "CGIRestriction set properly."); + result = true; + } while (false); + + // Exiting / Unwinding + if (element != NULL) element->Release(); + if (collection != NULL) collection->Release(); + if (parent != NULL) parent->Release(); + + SysFreeString(bstr_section_name); + SysFreeString(bstr_config_path); + SysFreeString(bstr_element_name); + + return result; +} + + +bool Iis7Config::CreateAdminConsoleSite(IAppHostWritableAdminManager *manager, BSTR site_root) { + IAppHostElement* parent = NULL; + IAppHostElementCollection* collection = NULL; + IAppHostElement* element = NULL; + + BSTR bstr_section_name = SysAllocString(L"system.applicationHost/sites"); + BSTR bstr_config_path = SysAllocString(L"MACHINE/WEBROOT/APPHOST"); + + HRESULT hresult = S_OK; + bool result = false; + do { + // Get section + hresult = manager->GetAdminSection(bstr_section_name, + bstr_config_path, &parent); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to access sites configuration. (0x%0x)", hresult); + break; + } else if (&parent == NULL) { + Log(EVENT_ERROR, "Unable to get sites configuration."); + break; + } + + // Get collection of + hresult = parent->get_Collection(&collection); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to access sites child collection. (0x%0x)", hresult); + break; + } else if (&collection == NULL) { + Log(EVENT_ERROR, "Unable to get sites child collection."); + break; + } + + // create a element, like: + + // First to detect old site. + if (!GetFromCollection(collection, L"name", kSiteName, &element)) { + Log(EVENT_ERROR, "Failed to try detect old site."); + break; + } + if (element != NULL) { + Log(EVENT_CRITICAL, "Old Admin Console Site exists. Skip."); + result = true; + break; + } + + DWORD site_count; + CHECK_HRESULT(collection->get_Count(&site_count), + "Failed to get site count."); + char site_id[64]; + itoa(site_count + 466453, site_id, 10); + + CHECK_HRESULT(collection->CreateNewElement(L"site", &element), + "Failed to create site element."); + CHECK_BOOL(SetProperty(element, L"name", kSiteName), + "Failed to set site name property."); + CHECK_BOOL(SetProperty(element, L"id", bstr_t(site_id)), + "Failed to set site id property."); + CHECK_HRESULT(collection->AddElement(element), + "Failed to add site element."); + + // Create and set element. + IAppHostElementCollection* app_collection = NULL; + IAppHostElement* app_element = NULL; + CHECK_HRESULT(element->get_Collection(&app_collection), + "Failed to get app collection."); + CHECK_HRESULT(app_collection->CreateNewElement(L"application", &app_element), + "Failed to create application element."); + CHECK_BOOL(SetProperty(app_element, L"path", L"/"), + "Failed to set app path."); + // CHECK_BOOL(SetProperty(app_element, L"applicationPool", kSiteName), "Failed to set app applicationPool."); + CHECK_HRESULT(app_collection->AddElement(app_element), + "Failed to add application element."); + + // Create and set element. + IAppHostElementCollection* dir_collection = NULL; + IAppHostElement* dir_element = NULL; + CHECK_HRESULT(app_element->get_Collection(&dir_collection), + "Failed to get virtualDirectory collection."); + CHECK_HRESULT(dir_collection->CreateNewElement(L"virtualDirectory", &dir_element), + "Failed to create virtualDirectory."); + CHECK_BOOL(SetProperty(dir_element, L"path", L"/"), + "Failed to set dir path."); + CHECK_BOOL(SetProperty(dir_element, L"physicalPath", site_root), + "Failed to set dir physicalPath."); + CHECK_HRESULT(dir_collection->AddElement(dir_element), + "Failed to add virtualDirectory."); + + // Get element. + IAppHostElement* bindings_element = NULL; + CHECK_HRESULT(element->GetElementByName(L"bindings", &bindings_element), + "Failed to get bindings element."); + CHECK_BOOL((bindings_element != NULL), + "Failed to find bindings element."); + + // Create and set element. + IAppHostElementCollection* binding_collection = NULL; + IAppHostElement* binding_element = NULL; + CHECK_HRESULT(bindings_element->get_Collection(&binding_collection), + "Failed to get binding collection."); + CHECK_HRESULT(binding_collection->CreateNewElement(L"binding", &binding_element), + "Failed to create binding."); + CHECK_BOOL(SetProperty(binding_element, L"protocol", L"http"), + "Failed to set binding protocol."); + CHECK_BOOL(SetProperty(binding_element, L"bindingInformation", bstr_t(IisConfig::kAdminConsoleBinding)), + "Failed to set binding bindingInformation."); + CHECK_HRESULT(binding_collection->AddElement(binding_element), + "Failed to add binding element."); + + Log(EVENT_NORMAL, "Admin Console site created successfully."); + result = true; + } while (false); + + // Exiting / Unwinding + if (element != NULL) element->Release(); + if (collection != NULL) collection->Release(); + if (parent != NULL) parent->Release(); + + SysFreeString(bstr_section_name); + SysFreeString(bstr_config_path); + + return result; +} +*/ +bool Iis7Config::GetFromChildren(IAppHostChildElementCollection* children, + BSTR child_name, short* index) { + IAppHostElement* element = NULL; + + HRESULT hresult = S_OK; + *index = -1; + do { + // Get number of children. + DWORD count; + hresult = children->get_Count(&count); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to get the count of children."); + break; + } + + // Iterate every child to check whether it should be removed. + for (USHORT i = 0; i < count && SUCCEEDED(hresult); ++i) { + // Get item at index i. + VARIANT idx; + idx.vt = VT_I2; + idx.iVal = i; + hresult = children->get_Item(idx, &element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to get item [%d]. (0x%0x)", i, hresult); + break; + } + + // Get name property of item i. + + BSTR name = NULL; + if (!element->get_Name(&name)) { + Log(EVENT_ERROR, "Failed to get property value."); + hresult = S_FALSE; + break; + } + + // Check the property value + if (wcscmp(child_name, name) == 0) { + *index = static_cast(i); + SysFreeString(name); + break; + } + + SysFreeString(name); + element->Release(); + element = NULL; + } + } while (false); + + + if (element != NULL) element->Release(); + + return SUCCEEDED(hresult); +} + +bool Iis7Config::GetFromChildren(IAppHostChildElementCollection* children, + BSTR child_name, IAppHostElement** element) { + short idx; + if (!GetFromChildren(children, child_name, &idx)) { + Log(EVENT_ERROR, "Failed to get element from children."); + } + + if (idx != -1) { + VARIANT idx_var; + idx_var.vt = VT_I2; + idx_var.iVal = idx; + + HRESULT hresult = children->get_Item(idx_var, element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to get element from children."); + return false; + } else { + return true; + } + } else { + *element = NULL; + return true; + } +} +/* +bool Iis7Config::CustomizeAdminConsole(IAppHostWritableAdminManager* manager, BSTR cgi_path) { + IAppHostConfigManager* config_manager = NULL; + IAppHostConfigFile * config_file = NULL; + IAppHostConfigLocationCollection* location_collection = NULL; + IAppHostConfigLocation* location = NULL; + IAppHostElement* handlers = NULL; + IAppHostElementCollection* handlers_collection = NULL; + IAppHostElement* remove_element = NULL; + IAppHostElement* add_element = NULL; + + bool result = false; + do { + CHECK_HRESULT(manager->get_ConfigManager(&config_manager), + "Failed to get config manager."); + CHECK_HRESULT(config_manager->GetConfigFile(L"MACHINE/WEBROOT/APPHOST", &config_file), + "Failed to get config file."); + CHECK_HRESULT(config_file->get_Locations(&location_collection), + "Failed to get location collection."); + + bool success = false; + do { + // Get number of children. + DWORD count; + CHECK_HRESULT(location_collection->get_Count(&count), "Unable to get the count of collection."); + + // Iterate every child to check whether it should be removed. + USHORT i = 0; + for (; i < count; ++i) { + // Get item at index i. + VARIANT idx; + idx.vt = VT_I2; + idx.iVal = i; + CHECK_HRESULT(location_collection->get_Item(idx, &location), "Unable to get item."); + + BSTR location_path; + CHECK_HRESULT(location->get_Path(&location_path), "Failed to get location path."); + if (wcscmp(location_path, kSiteName) == 0) { + success = true; + break; + } + + SysFreeString(location_path); + location->Release(); + location = NULL; + } + + if (i == count) success = true; + } while (false); + + if (!success) { + break; + } + + if (location != NULL) { + Log(EVENT_CRITICAL, "Location already exists. Skip."); + result = true; + break; + } + + CHECK_HRESULT(location_collection->AddLocation(kSiteName, &location), + "Failed to add location."); + CHECK_HRESULT(location->AddConfigSection(L"system.webServer/handlers", &handlers), + "Failed to add config section."); + CHECK_HRESULT(SetProperty(handlers, L"accessPolicy", L"Read, Execute, Script"), + "Failed to set accessPolicy."); + + CHECK_HRESULT(handlers->get_Collection(&handlers_collection), + "Failed to get handlers collection."); + CHECK_HRESULT(handlers_collection->CreateNewElement(L"remove", &remove_element), + "Failed to create remove element."); + CHECK_BOOL(SetProperty(remove_element, L"name", L"CGI-exe"), + "Failed to set name of remove element."); + CHECK_HRESULT(handlers_collection->AddElement(remove_element), + "Failed to add remove element."); + + CHECK_HRESULT(handlers_collection->CreateNewElement(L"add", &add_element), + "Failed to create add element."); + CHECK_BOOL(SetProperty(add_element, L"name", L"CGI-exe"), + "Failed to set name of add element."); + CHECK_BOOL(SetProperty(add_element, L"path", L"*.cgi"), + "Failed to set path of add element."); + CHECK_BOOL(SetProperty(add_element, L"verb", L"*"), + "Failed to set verb of element."); + CHECK_BOOL(SetProperty(add_element, L"modules", L"CgiModule"), + "Failed to set modules of add element."); + CHECK_BOOL(SetProperty(add_element, L"scriptProcessor", cgi_path), + "Failed to set scriptProcessor of add element."); + CHECK_BOOL(SetProperty(add_element, L"resourceType", L"File"), + "Failed to set resourceType of add element."); + CHECK_BOOL(SetProperty(add_element, L"requireAccess", L"Execute"), + "Failed to set requireAccess of add element."); + CHECK_BOOL(SetProperty(add_element, L"allowPathInfo", L"true"), + "Failed to set naallowPathInfome of add element."); + CHECK_HRESULT(handlers_collection->AddElement(add_element), + "Failed to add add element."); + + Log(EVENT_NORMAL, "Admin Console site customized successfully."); + result = true; + } while (false); + + return result; +} + + +bool Iis7Config::RemoveAdminConsoleCustomization(IAppHostWritableAdminManager* manager) { + IAppHostConfigManager* config_manager = NULL; + IAppHostConfigFile * config_file = NULL; + IAppHostConfigLocationCollection* location_collection = NULL; + IAppHostConfigLocation* location = NULL; + + bool result = false; + do { + CHECK_HRESULT(manager->get_ConfigManager(&config_manager), "Failed to get config manager."); + CHECK_HRESULT(config_manager->GetConfigFile(L"MACHINE/WEBROOT/APPHOST", &config_file), "Failed to get config file."); + CHECK_HRESULT(config_file->get_Locations(&location_collection), "Failed to get location collection."); + + bool success = false; + VARIANT idx; + do { + // Get number of children. + DWORD count; + CHECK_HRESULT(location_collection->get_Count(&count), "Unable to get the count of collection."); + + // Iterate every child to check whether it should be removed. + USHORT i = 0; + for (; i < count; ++i) { + // Get item at index i. + idx.vt = VT_I2; + idx.iVal = i; + CHECK_HRESULT(location_collection->get_Item(idx, &location), "Unable to get item."); + + BSTR location_path; + CHECK_HRESULT(location->get_Path(&location_path), "Failed to get location path."); + if (wcscmp(location_path, kSiteName) == 0) { + success = true; + break; + } + + SysFreeString(location_path); + location->Release(); + location = NULL; + } + + if (i == count) success = true; + } while (false); + + if (!success) { + break; + } + + if (location == NULL) { + Log(EVENT_CRITICAL, "No location exists. Skip."); + result = true; + break; + } + + CHECK_HRESULT(location_collection->DeleteLocation(idx), "Failed to remove location."); + + result = true; + } while (false); + + return result; +} +*/ + +void CopyBinaries(wchar_t *srcpath, wchar_t *dstpath) +{ + wchar_t *files[] = { L"libapr-1.dll", L"libapriconv-1.dll", L"libaprutil-1.dll", + L"libxml2.dll", L"lua5.1.dll", L"pcre.dll", L"zlib1.dll", L"ModSecurityIIS.dll" }; + + for(int i = 0; i < 8; i++) + { + wchar_t src[4096]; + wchar_t dst[4096]; + + wcsncpy(src,srcpath,4096); + wcsncat(src,files[i],4096); + wcsncpy(dst,dstpath,4096); + wcsncat(dst,files[i],4096); + + if(!CopyFile(src, dst, FALSE)) + { + Iis7Config::Log(EVENT_ERROR, L"Failed to copy binary: %s %s", src, dst); + } + } +} + +void fixpath(wchar_t *srcdir) +{ + int l = wcslen(srcdir); + + if(srcdir[l-1] != '\\') + { + srcdir[l] = '\\'; + srcdir[l + 1] = 0; + } +} + +int _tmain(int argc, _TCHAR* argv[]) +{ + if(argc < 2) + return -1; + + if(wcscmp(argv[1], L"uninstall") == 0) + { + Iis7Config::UninstallFilter(); + return 0; + } + + wchar_t srcdir[4096]; + + if(argv[1][0] == '"') + wcsncpy(srcdir, &argv[1][1], 4096); + else + wcsncpy(srcdir, argv[1], 4096); + + int l = wcslen(srcdir); + + if(srcdir[l-1] == '"') + srcdir[l-1] = 0; + + fixpath(srcdir); + + wchar_t windir[4096]; + + GetWindowsDirectory(windir, 4096); + + fixpath(windir); + + wchar_t crsdir[4096]; + + crsdir[0] = windir[0]; + crsdir[1] = windir[1]; + crsdir[2] = windir[2]; + crsdir[3] = 0; + + wcsncat(crsdir, L"inetpub", 4096); + + CreateDirectory(crsdir, NULL); + + wcsncat(crsdir, L"\\wwwroot", 4096); + + CreateDirectory(crsdir, NULL); + + wchar_t zipdir[4096]; + + wcsncpy(zipdir, srcdir, 4096); + wcsncat(zipdir, L"owasp_crs.zip", 4096); + + HZIP hzip = OpenZip(crsdir, zipdir, 0, ZIP_FILENAME); + + if(hzip != 0) + { + int z = 0; + ZIPENTRYW ze; + + while(GetZipItemW(hzip, z, &ze) == ZR_OK) + { + UnzipItem(hzip, z++, ze.name, 0, ZIP_FILENAME); + } + + CloseZip(hzip); + } + + wchar_t sys32[4096]; + wchar_t syswow[4096]; + wchar_t sysnat[4096]; + + wcsncpy(sys32, windir, 4096); + wcsncpy(syswow, windir, 4096); + wcsncpy(sysnat, windir, 4096); + + wcsncat(sys32, L"system32\\inetsrv\\", 4096); + wcsncat(syswow, L"SysWow64\\inetsrv\\", 4096); + wcsncat(sysnat, L"SysNative\\inetsrv\\", 4096); + + wchar_t src32[4096]; + wchar_t src64[4096]; + + wcsncpy(src32, srcdir, 4096); + wcsncpy(src64, srcdir, 4096); + wcsncat(src32, L"x86\\", 4096); + wcsncat(src64, L"amd64\\", 4096); + + wchar_t dstconf[4096]; + + if(Iis7Config::GetAppMode() == Iis7Config::MODE_X64) + { + CopyBinaries(src32, syswow); + CopyBinaries(src64, sysnat); + wcsncpy(dstconf, sysnat, 4096); + } + else + { + CopyBinaries(src32, sys32); + wcsncpy(dstconf, sys32, 4096); + } + + wcsncat(dstconf, L"config\\schema\\ModSecurity.xml", 4096); + + wcsncat(srcdir, L"ModSecurity.xml", 4096); + + if(!CopyFile(srcdir, dstconf, FALSE)) + { + Iis7Config::Log(EVENT_ERROR, L"Failed to copy config XML: %s %s", srcdir, dstconf); + } + + wcsncat(sys32, L"ModSecurityIIS.dll", 4096); + + Iis7Config::InstallFilter(sys32); + + return 0; +} diff --git a/iis/ModSecurityIIS/Installer/Installer.vcxproj b/iis/ModSecurityIIS/Installer/Installer.vcxproj new file mode 100644 index 0000000000..c584c349b7 --- /dev/null +++ b/iis/ModSecurityIIS/Installer/Installer.vcxproj @@ -0,0 +1,96 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C} + Win32Proj + Installer + + + + Application + true + Unicode + + + Application + false + true + Unicode + Static + + + + + + + + + + + + + true + + + false + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + ole32.lib;oleaut32.lib;advapi32.lib + RequireAdministrator + + + + + + + + + + + + + + Create + Create + + + + + + + \ No newline at end of file diff --git a/iis/ModSecurityIIS/Installer/Installer.vcxproj.filters b/iis/ModSecurityIIS/Installer/Installer.vcxproj.filters new file mode 100644 index 0000000000..cc4e9ce41c --- /dev/null +++ b/iis/ModSecurityIIS/Installer/Installer.vcxproj.filters @@ -0,0 +1,42 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/iis/ModSecurityIIS/Installer/Installer.vcxproj.user b/iis/ModSecurityIIS/Installer/Installer.vcxproj.user new file mode 100644 index 0000000000..6a61bbba94 --- /dev/null +++ b/iis/ModSecurityIIS/Installer/Installer.vcxproj.user @@ -0,0 +1,7 @@ + + + + "c:\Program Files (x86)\ModSecurity IIS" + WindowsLocalDebugger + + \ No newline at end of file diff --git a/iis/ModSecurityIIS/Installer/ReadMe.txt b/iis/ModSecurityIIS/Installer/ReadMe.txt new file mode 100644 index 0000000000..87d59aebc8 --- /dev/null +++ b/iis/ModSecurityIIS/Installer/ReadMe.txt @@ -0,0 +1,40 @@ +======================================================================== + CONSOLE APPLICATION : Installer Project Overview +======================================================================== + +AppWizard has created this Installer application for you. + +This file contains a summary of what you will find in each of the files that +make up your Installer application. + + +Installer.vcxproj + This is the main project file for VC++ projects generated using an Application Wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + Application Wizard. + +Installer.vcxproj.filters + This is the filters file for VC++ projects generated using an Application Wizard. + It contains information about the association between the files in your project + and the filters. This association is used in the IDE to show grouping of files with + similar extensions under a specific node (for e.g. ".cpp" files are associated with the + "Source Files" filter). + +Installer.cpp + This is the main application source file. + +///////////////////////////////////////////////////////////////////////////// +Other standard files: + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named Installer.pch and a precompiled types file named StdAfx.obj. + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" comments to indicate parts of the source code you +should add to or customize. + +///////////////////////////////////////////////////////////////////////////// diff --git a/iis/ModSecurityIIS/Installer/XUnzip.cpp b/iis/ModSecurityIIS/Installer/XUnzip.cpp new file mode 100644 index 0000000000..d84710a7ad --- /dev/null +++ b/iis/ModSecurityIIS/Installer/XUnzip.cpp @@ -0,0 +1,4384 @@ +// XUnzip.cpp Version 1.3 +// +// Authors: Mark Adler et al. (see below) +// +// Modified by: Lucian Wischik +// lu@wischik.com +// +// Version 1.0 - Turned C files into just a single CPP file +// - Made them compile cleanly as C++ files +// - Gave them simpler APIs +// - Added the ability to zip/unzip directly in memory without +// any intermediate files +// +// Modified by: Hans Dietrich +// hdietrich@gmail.com +// +// Version 1.3: - Corrected size bug introduced by 1.2 +// +// Version 1.2: - Many bug fixes. See CodeProject article for list. +// +// Version 1.1: - Added Unicode support to CreateZip() and ZipAdd() +// - Changed file names to avoid conflicts with Lucian's files +// +/////////////////////////////////////////////////////////////////////////////// +// +// Lucian Wischik's comments: +// -------------------------- +// THIS FILE is almost entirely based upon code by Info-ZIP. +// It has been modified by Lucian Wischik. +// The original code may be found at http://www.info-zip.org +// The original copyright text follows. +// +/////////////////////////////////////////////////////////////////////////////// +// +// Original authors' comments: +// --------------------------- +// This is version 2002-Feb-16 of the Info-ZIP copyright and license. The +// definitive version of this document should be available at +// ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely. +// +// Copyright (c) 1990-2002 Info-ZIP. All rights reserved. +// +// For the purposes of this copyright and license, "Info-ZIP" is defined as +// the following set of individuals: +// +// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, +// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase, +// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, +// David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, +// Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, +// Kai Uwe Rommel, Steve Salisbury, Dave Smith, Christian Spieler, +// Antoine Verheijen, Paul von Behren, Rich Wales, Mike White +// +// This software is provided "as is", without warranty of any kind, express +// or implied. In no event shall Info-ZIP or its contributors be held liable +// for any direct, indirect, incidental, special or consequential damages +// arising out of the use of or inability to use this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. Redistributions of source code must retain the above copyright notice, +// definition, disclaimer, and this list of conditions. +// +// 2. Redistributions in binary form (compiled executables) must reproduce +// the above copyright notice, definition, disclaimer, and this list of +// conditions in documentation and/or other materials provided with the +// distribution. The sole exception to this condition is redistribution +// of a standard UnZipSFX binary as part of a self-extracting archive; +// that is permitted without inclusion of this license, as long as the +// normal UnZipSFX banner has not been removed from the binary or disabled. +// +// 3. Altered versions--including, but not limited to, ports to new +// operating systems, existing ports with new graphical interfaces, and +// dynamic, shared, or static library versions--must be plainly marked +// as such and must not be misrepresented as being the original source. +// Such altered versions also must not be misrepresented as being +// Info-ZIP releases--including, but not limited to, labeling of the +// altered versions with the names "Info-ZIP" (or any variation thereof, +// including, but not limited to, different capitalizations), +// "Pocket UnZip", "WiZ" or "MacZip" without the explicit permission of +// Info-ZIP. Such altered versions are further prohibited from +// misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or +// of the Info-ZIP URL(s). +// +// 4. Info-ZIP retains the right to use the names "Info-ZIP", "Zip", "UnZip", +// "UnZipSFX", "WiZ", "Pocket UnZip", "Pocket Zip", and "MacZip" for its +// own source and binary releases. +// +/////////////////////////////////////////////////////////////////////////////// + + +#define _USE_32BIT_TIME_T //+++1.2 + + +#define STRICT +#define WIN32_LEAN_AND_MEAN +#include "stdafx.h" +#include +#include +#include +#include +#include +#include +#include "XUnzip.h" + +#pragma warning(disable : 4996) // disable bogus deprecation warning + +// THIS FILE is almost entirely based upon code by Jean-loup Gailly +// and Mark Adler. It has been modified by Lucian Wischik. +// The original code may be found at http://www.gzip.org/zlib/ +// The original copyright text follows. +// +// +// +// zlib.h -- interface of the 'zlib' general purpose compression library +// version 1.1.3, July 9th, 1998 +// +// Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Jean-loup Gailly Mark Adler +// jloup@gzip.org madler@alumni.caltech.edu +// +// +// The data format used by the zlib library is described by RFCs (Request for +// Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt +// (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +// +// +// The 'zlib' compression library provides in-memory compression and +// decompression functions, including integrity checks of the uncompressed +// data. This version of the library supports only one compression method +// (deflation) but other algorithms will be added later and will have the same +// stream interface. +// +// Compression can be done in a single step if the buffers are large +// enough (for example if an input file is mmap'ed), or can be done by +// repeated calls of the compression function. In the latter case, the +// application must provide more input and/or consume the output +// (providing more output space) before each call. +// +// The library also supports reading and writing files in gzip (.gz) format +// with an interface similar to that of stdio. +// +// The library does not install any signal handler. The decoder checks +// the consistency of the compressed data, so the library should never +// crash even in case of corrupted input. +// +// for more info about .ZIP format, see ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip +// PkWare has also a specification at ftp://ftp.pkware.com/probdesc.zip + +#define zmalloc(len) malloc(len) + +#define zfree(p) free(p) + +/* +void *zmalloc(unsigned int len) +{ char *buf = new char[len+32]; + for (int i=0; i<16; i++) + { buf[i]=i; + buf[len+31-i]=i; + } + *((unsigned int*)buf) = len; + char c[1000]; wsprintf(c,"malloc 0x%lx - %lu",buf+16,len); + OutputDebugString(c); + return buf+16; +} + +void zfree(void *buf) +{ char c[1000]; wsprintf(c,"free 0x%lx",buf); + OutputDebugString(c); + char *p = ((char*)buf)-16; + unsigned int len = *((unsigned int*)p); + bool blown=false; + for (int i=0; i<16; i++) + { char lo = p[i]; + char hi = p[len+31-i]; + if (hi!=i || (lo!=i && i>4)) blown=true; + } + if (blown) + { OutputDebugString("BLOWN!!!"); + } + delete[] p; +} +*/ + +#pragma warning(disable : 4702) // unreachable code + +static ZRESULT zopenerror = ZR_OK; //+++1.2 + +typedef struct tm_unz_s +{ unsigned int tm_sec; // seconds after the minute - [0,59] + unsigned int tm_min; // minutes after the hour - [0,59] + unsigned int tm_hour; // hours since midnight - [0,23] + unsigned int tm_mday; // day of the month - [1,31] + unsigned int tm_mon; // months since January - [0,11] + unsigned int tm_year; // years - [1980..2044] +} tm_unz; + + +// unz_global_info structure contain global data about the ZIPfile +typedef struct unz_global_info_s +{ unsigned long number_entry; // total number of entries in the central dir on this disk + unsigned long size_comment; // size of the global comment of the zipfile +} unz_global_info; + +// unz_file_info contain information about a file in the zipfile +typedef struct unz_file_info_s +{ unsigned long version; // version made by 2 bytes + unsigned long version_needed; // version needed to extract 2 bytes + unsigned long flag; // general purpose bit flag 2 bytes + unsigned long compression_method; // compression method 2 bytes + unsigned long dosDate; // last mod file date in Dos fmt 4 bytes + unsigned long crc; // crc-32 4 bytes + unsigned long compressed_size; // compressed size 4 bytes + unsigned long uncompressed_size; // uncompressed size 4 bytes + unsigned long size_filename; // filename length 2 bytes + unsigned long size_file_extra; // extra field length 2 bytes + unsigned long size_file_comment; // file comment length 2 bytes + unsigned long disk_num_start; // disk number start 2 bytes + unsigned long internal_fa; // internal file attributes 2 bytes + unsigned long external_fa; // external file attributes 4 bytes + tm_unz tmu_date; +} unz_file_info; + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + + + + + + + +#define ZLIB_VERSION "1.1.3" + + +// Allowed flush values; see deflate() for details +#define Z_NO_FLUSH 0 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 + + +// compression levels +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) + +// compression strategy; see deflateInit2() for details +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_DEFAULT_STRATEGY 0 + +// Possible values of the data_type field +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 + +// The deflate compression method (the only one supported in this version) +#define Z_DEFLATED 8 + +// for initializing zalloc, zfree, opaque +#define Z_NULL 0 + +// case sensitivity when searching for filenames +#define CASE_SENSITIVE 1 +#define CASE_INSENSITIVE 2 + + +// Return codes for the compression/decompression functions. Negative +// values are errors, positive values are used for special but normal events. +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) + + + +// Basic data types +typedef unsigned char Byte; // 8 bits +typedef unsigned int uInt; // 16 bits or more +typedef unsigned long uLong; // 32 bits or more +typedef void *voidpf; +typedef void *voidp; +typedef long z_off_t; + + + + + + + + + + + + +typedef voidpf (*alloc_func) (voidpf opaque, uInt items, uInt size); +typedef void (*free_func) (voidpf opaque, voidpf address); + +struct internal_state; + +typedef struct z_stream_s { + Byte *next_in; // next input byte + uInt avail_in; // number of bytes available at next_in + uLong total_in; // total nb of input bytes read so far + + Byte *next_out; // next output byte should be put there + uInt avail_out; // remaining free space at next_out + uLong total_out; // total nb of bytes output so far + + char *msg; // last error message, NULL if no error + struct internal_state *state; // not visible by applications + + alloc_func zalloc; // used to allocate the internal state + free_func zfree; // used to free the internal state + voidpf opaque; // private data object passed to zalloc and zfree + + int data_type; // best guess about the data type: ascii or binary + uLong adler; // adler32 value of the uncompressed data + uLong reserved; // reserved for future use +} z_stream; + +typedef z_stream *z_streamp; + + +// The application must update next_in and avail_in when avail_in has +// dropped to zero. It must update next_out and avail_out when avail_out +// has dropped to zero. The application must initialize zalloc, zfree and +// opaque before calling the init function. All other fields are set by the +// compression library and must not be updated by the application. +// +// The opaque value provided by the application will be passed as the first +// parameter for calls of zalloc and zfree. This can be useful for custom +// memory management. The compression library attaches no meaning to the +// opaque value. +// +// zalloc must return Z_NULL if there is not enough memory for the object. +// If zlib is used in a multi-threaded application, zalloc and zfree must be +// thread safe. +// +// The fields total_in and total_out can be used for statistics or +// progress reports. After compression, total_in holds the total size of +// the uncompressed data and may be saved for use in the decompressor +// (particularly if the decompressor wants to decompress everything in +// a single step). +// + + +// basic functions + +const char *zlibVersion (); +// The application can compare zlibVersion and ZLIB_VERSION for consistency. +// If the first character differs, the library code actually used is +// not compatible with the zlib.h header file used by the application. +// This check is automatically made by inflateInit. + + + + + + +int inflate (z_streamp strm, int flush); +// +// inflate decompresses as much data as possible, and stops when the input +// buffer becomes empty or the output buffer becomes full. It may some +// introduce some output latency (reading input without producing any output) +// except when forced to flush. +// +// The detailed semantics are as follows. inflate performs one or both of the +// following actions: +// +// - Decompress more input starting at next_in and update next_in and avail_in +// accordingly. If not all input can be processed (because there is not +// enough room in the output buffer), next_in is updated and processing +// will resume at this point for the next call of inflate(). +// +// - Provide more output starting at next_out and update next_out and avail_out +// accordingly. inflate() provides as much output as possible, until there +// is no more input data or no more space in the output buffer (see below +// about the flush parameter). +// +// Before the call of inflate(), the application should ensure that at least +// one of the actions is possible, by providing more input and/or consuming +// more output, and updating the next_* and avail_* values accordingly. +// The application can consume the uncompressed output when it wants, for +// example when the output buffer is full (avail_out == 0), or after each +// call of inflate(). If inflate returns Z_OK and with zero avail_out, it +// must be called again after making room in the output buffer because there +// might be more output pending. +// +// If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much +// output as possible to the output buffer. The flushing behavior of inflate is +// not specified for values of the flush parameter other than Z_SYNC_FLUSH +// and Z_FINISH, but the current implementation actually flushes as much output +// as possible anyway. +// +// inflate() should normally be called until it returns Z_STREAM_END or an +// error. However if all decompression is to be performed in a single step +// (a single call of inflate), the parameter flush should be set to +// Z_FINISH. In this case all pending input is processed and all pending +// output is flushed; avail_out must be large enough to hold all the +// uncompressed data. (The size of the uncompressed data may have been saved +// by the compressor for this purpose.) The next operation on this stream must +// be inflateEnd to deallocate the decompression state. The use of Z_FINISH +// is never required, but can be used to inform inflate that a faster routine +// may be used for the single inflate() call. +// +// If a preset dictionary is needed at this point (see inflateSetDictionary +// below), inflate sets strm-adler to the adler32 checksum of the +// dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise +// it sets strm->adler to the adler32 checksum of all output produced +// so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or +// an error code as described below. At the end of the stream, inflate() +// checks that its computed adler32 checksum is equal to that saved by the +// compressor and returns Z_STREAM_END only if the checksum is correct. +// +// inflate() returns Z_OK if some progress has been made (more input processed +// or more output produced), Z_STREAM_END if the end of the compressed data has +// been reached and all uncompressed output has been produced, Z_NEED_DICT if a +// preset dictionary is needed at this point, Z_DATA_ERROR if the input data was +// corrupted (input stream not conforming to the zlib format or incorrect +// adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent +// (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not +// enough memory, Z_BUF_ERROR if no progress is possible or if there was not +// enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR +// case, the application may then call inflateSync to look for a good +// compression block. +// + + +int inflateEnd (z_streamp strm); +// +// All dynamically allocated data structures for this stream are freed. +// This function discards any unprocessed input and does not flush any +// pending output. +// +// inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state +// was inconsistent. In the error case, msg may be set but then points to a +// static string (which must not be deallocated). + + // Advanced functions + +// The following functions are needed only in some special applications. + + + + + +int inflateSetDictionary (z_streamp strm, + const Byte *dictionary, + uInt dictLength); +// +// Initializes the decompression dictionary from the given uncompressed byte +// sequence. This function must be called immediately after a call of inflate +// if this call returned Z_NEED_DICT. The dictionary chosen by the compressor +// can be determined from the Adler32 value returned by this call of +// inflate. The compressor and decompressor must use exactly the same +// dictionary. +// +// inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a +// parameter is invalid (such as NULL dictionary) or the stream state is +// inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the +// expected one (incorrect Adler32 value). inflateSetDictionary does not +// perform any decompression: this will be done by subsequent calls of +// inflate(). + + +int inflateSync (z_streamp strm); +// +// Skips invalid compressed data until a full flush point can be found, or until all +// available input is skipped. No output is provided. +// +// inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR +// if no more input was provided, Z_DATA_ERROR if no flush point has been found, +// or Z_STREAM_ERROR if the stream structure was inconsistent. In the success +// case, the application may save the current current value of total_in which +// indicates where valid compressed data was found. In the error case, the +// application may repeatedly call inflateSync, providing more input each time, +// until success or end of the input data. + + +int inflateReset (z_streamp strm); +// This function is equivalent to inflateEnd followed by inflateInit, +// but does not free and reallocate all the internal decompression state. +// The stream will keep attributes that may have been set by inflateInit2. +// +// inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source +// stream state was inconsistent (such as zalloc or state being NULL). +// + + + +// checksum functions +// These functions are not related to compression but are exported +// anyway because they might be useful in applications using the +// compression library. + +uLong adler32 (uLong adler, const Byte *buf, uInt len); +// Update a running Adler-32 checksum with the bytes buf[0..len-1] and +// return the updated checksum. If buf is NULL, this function returns +// the required initial value for the checksum. +// An Adler-32 checksum is almost as reliable as a CRC32 but can be computed +// much faster. Usage example: +// +// uLong adler = adler32(0L, Z_NULL, 0); +// +// while (read_buffer(buffer, length) != EOF) { +// adler = adler32(adler, buffer, length); +// } +// if (adler != original_adler) error(); + +uLong ucrc32 (uLong crc, const Byte *buf, uInt len); +// Update a running crc with the bytes buf[0..len-1] and return the updated +// crc. If buf is NULL, this function returns the required initial value +// for the crc. Pre- and post-conditioning (one's complement) is performed +// within this function so it shouldn't be done by the application. +// Usage example: +// +// uLong crc = crc32(0L, Z_NULL, 0); +// +// while (read_buffer(buffer, length) != EOF) { +// crc = crc32(crc, buffer, length); +// } +// if (crc != original_crc) error(); + + + + +const char *zError (int err); +int inflateSyncPoint (z_streamp z); +const uLong *get_crc_table (void); + + + +typedef unsigned char uch; +typedef uch uchf; +typedef unsigned short ush; +typedef ush ushf; +typedef unsigned long ulg; + + + +const char * const z_errmsg[10] = { // indexed by 2-zlib_error +"need dictionary", // Z_NEED_DICT 2 +"stream end", // Z_STREAM_END 1 +"", // Z_OK 0 +"file error", // Z_ERRNO (-1) +"stream error", // Z_STREAM_ERROR (-2) +"data error", // Z_DATA_ERROR (-3) +"insufficient memory", // Z_MEM_ERROR (-4) +"buffer error", // Z_BUF_ERROR (-5) +"incompatible version",// Z_VERSION_ERROR (-6) +""}; + + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +// To be used only when the state is known to be valid + + // common constants + + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +// The three kinds of block type + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +// The minimum and maximum match lengths + +#define PRESET_DICT 0x20 // preset dictionary flag in zlib header + + // target dependencies + +#define OS_CODE 0x0b // Window 95 & Windows NT + + + + // functions + +#define zmemzero(dest, len) memset(dest, 0, len) + +// Diagnostic functions +#undef Assert +#undef Trace +#undef Tracev +#undef Tracevv +#undef Tracec +#undef Tracecv + +#ifdef DEBUG + + int z_verbose = 0; + void z_error (char *m) {fprintf(stderr, "%s\n", m); exit(1);} + +#define Assert(cond,msg) {if(!(cond)) z_error(msg);} +#define Trace(x) {if (z_verbose>=0) fprintf x ;} +#define Tracev(x) {if (z_verbose>0) fprintf x ;} +#define Tracevv(x) {if (z_verbose>1) fprintf x ;} +#define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +#define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} + +#else + +#ifndef __noop +#if _MSC_VER < 1300 +#define __noop ((void)0) +#endif +#endif + +#define Assert(cond,msg) __noop +#define Trace(x) __noop +#define Tracev(x) __noop +#define Tracevv(x) __noop +#define Tracec(c,x) __noop +#define Tracecv(c,x) __noop + +#endif + + +typedef uLong (*check_func) (uLong check, const Byte *buf, uInt len); +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size); +void zcfree (voidpf opaque, voidpf ptr); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) + +//void ZFREE(z_streamp strm,voidpf addr) +//{ *((strm)->zfree))((strm)->opaque, addr); +//} + +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + + + + +// Huffman code lookup table entry--this entry is four bytes for machines +// that have 16-bit pointers (e.g. PC's in the small or medium model). + + +typedef struct inflate_huft_s inflate_huft; + +struct inflate_huft_s { + union { + struct { + Byte Exop; // number of extra bits or operation + Byte Bits; // number of bits in this code or subcode + } what; + uInt pad; // pad structure to a power of 2 (4 bytes for + } word; // 16-bit, 8 bytes for 32-bit int's) + uInt base; // literal, length base, distance base, or table offset +}; + +// Maximum size of dynamic tree. The maximum found in a long but non- +// exhaustive search was 1004 huft structures (850 for length/literals +// and 154 for distances, the latter actually the result of an +// exhaustive search). The actual maximum is not known, but the +// value below is more than safe. +#define MANY 1440 + +int inflate_trees_bits ( + uInt *, // 19 code lengths + uInt *, // bits tree desired/actual depth + inflate_huft * *, // bits tree result + inflate_huft *, // space for trees + z_streamp); // for messages + +int inflate_trees_dynamic ( + uInt, // number of literal/length codes + uInt, // number of distance codes + uInt *, // that many (total) code lengths + uInt *, // literal desired/actual bit depth + uInt *, // distance desired/actual bit depth + inflate_huft * *, // literal/length tree result + inflate_huft * *, // distance tree result + inflate_huft *, // space for trees + z_streamp); // for messages + +int inflate_trees_fixed ( + uInt *, // literal desired/actual bit depth + uInt *, // distance desired/actual bit depth + const inflate_huft * *, // literal/length tree result + const inflate_huft * *, // distance tree result + z_streamp); // for memory allocation + + + + + +struct inflate_blocks_state; +typedef struct inflate_blocks_state inflate_blocks_statef; + +inflate_blocks_statef * inflate_blocks_new ( + z_streamp z, + check_func c, // check function + uInt w); // window size + +int inflate_blocks ( + inflate_blocks_statef *, + z_streamp , + int); // initial return code + +void inflate_blocks_reset ( + inflate_blocks_statef *, + z_streamp , + uLong *); // check value on output + +int inflate_blocks_free ( + inflate_blocks_statef *, + z_streamp); + +void inflate_set_dictionary ( + inflate_blocks_statef *s, + const Byte *d, // dictionary + uInt n); // dictionary length + +int inflate_blocks_sync_point ( + inflate_blocks_statef *s); + + + + +struct inflate_codes_state; +typedef struct inflate_codes_state inflate_codes_statef; + +inflate_codes_statef *inflate_codes_new ( + uInt, uInt, + const inflate_huft *, const inflate_huft *, + z_streamp ); + +int inflate_codes ( + inflate_blocks_statef *, + z_streamp , + int); + +void inflate_codes_free ( + inflate_codes_statef *, + z_streamp ); + + + + +typedef enum { + IBM_TYPE, // get type bits (3, including end bit) + IBM_LENS, // get lengths for stored + IBM_STORED, // processing stored block + IBM_TABLE, // get table lengths + IBM_BTREE, // get bit lengths tree for a dynamic block + IBM_DTREE, // get length, distance trees for a dynamic block + IBM_CODES, // processing fixed or dynamic block + IBM_DRY, // output remaining window bytes + IBM_DONE, // finished last block, done + IBM_BAD} // got a data error--stuck here +inflate_block_mode; + +// inflate blocks semi-private state +struct inflate_blocks_state { + + // mode + inflate_block_mode mode; // current inflate_block mode + + // mode dependent information + union { + uInt left; // if STORED, bytes left to copy + struct { + uInt table; // table lengths (14 bits) + uInt index; // index into blens (or border) + uInt *blens; // bit lengths of codes + uInt bb; // bit length tree depth + inflate_huft *tb; // bit length decoding tree + } trees; // if DTREE, decoding info for trees + struct { + inflate_codes_statef + *codes; + } decode; // if CODES, current state + } sub; // submode + uInt last; // true if this block is the last block + + // mode independent information + uInt bitk; // bits in bit buffer + uLong bitb; // bit buffer + inflate_huft *hufts; // single malloc for tree space + Byte *window; // sliding window + Byte *end; // one byte after sliding window + Byte *read; // window read pointer + Byte *write; // window write pointer + check_func checkfn; // check function + uLong check; // check on output + +}; + + +// defines for inflate input/output +// update pointers and return +#define UPDBITS {s->bitb=b;s->bitk=k;} +#define UPDIN {z->avail_in=n;z->total_in+=(uLong)(p-z->next_in);z->next_in=p;} +#define UPDOUT {s->write=q;} +#define UPDATE {UPDBITS UPDIN UPDOUT} +#define LEAVE {UPDATE return inflate_flush(s,z,r);} +// get bytes and bits +#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} +#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} +#define NEXTBYTE (n--,*p++) +#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} +// output bytes +#define WAVAIL (uInt)(qread?s->read-q-1:s->end-q) +#define LOADOUT {q=s->write;m=(uInt)WAVAIL;m;} +#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}} +#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} +#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} +#define OUTBYTE(a) {*q++=(Byte)(a);m--;} +// load local pointers +#define LOAD {LOADIN LOADOUT} + +// masks for lower bits (size given to avoid silly warnings with Visual C++) +// And'ing with mask[n] masks the lower n bits +const uInt inflate_mask[17] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +// copy as much as possible from the sliding window to the output area +int inflate_flush (inflate_blocks_statef *, z_streamp, int); + +int inflate_fast (uInt, uInt, const inflate_huft *, const inflate_huft *, inflate_blocks_statef *, z_streamp ); + + + +const uInt fixed_bl = 9; +const uInt fixed_bd = 5; +const inflate_huft fixed_tl[] = { + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254}, + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255} + }; +const inflate_huft fixed_td[] = { + {{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097}, + {{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385}, + {{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193}, + {{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577}, + {{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145}, + {{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577}, + {{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289}, + {{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577} + }; + + + + + + + +// copy as much as possible from the sliding window to the output area +int inflate_flush(inflate_blocks_statef *s,z_streamp z,int r) +{ + uInt n; + Byte *p; + Byte *q; + + // local copies of source and destination pointers + p = z->next_out; + q = s->read; + + // compute number of bytes to copy as far as end of window + n = (uInt)((q <= s->write ? s->write : s->end) - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z->avail_out -= n; + z->total_out += n; + + // update check information + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + // copy as far as end of window + if (n!=0) // check for n!=0 to avoid waking up CodeGuard + { memcpy(p, q, n); + p += n; + q += n; + } + + // see if more to copy at beginning of window + if (q == s->end) + { + // wrap pointers + q = s->window; + if (s->write == s->end) + s->write = s->window; + + // compute bytes to copy + n = (uInt)(s->write - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z->avail_out -= n; + z->total_out += n; + + // update check information + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + // copy + memcpy(p, q, n); + p += n; + q += n; + } + + // update pointers + z->next_out = p; + s->read = q; + + // done + return r; +} + + + + + + +// simplify the use of the inflate_huft type with some defines +#define exop word.what.Exop +#define bits word.what.Bits + +typedef enum { // waiting for "i:"=input, "o:"=output, "x:"=nothing + START, // x: set up for LEN + LEN, // i: get length/literal/eob next + LENEXT, // i: getting length extra (have base) + DIST, // i: get distance next + DISTEXT, // i: getting distance extra + COPY, // o: copying bytes in window, waiting for space + LIT, // o: got literal, waiting for output space + WASH, // o: got eob, possibly still output waiting + END, // x: got eob and all data flushed + BADCODE} // x: got error +inflate_codes_mode; + +// inflate codes private state +struct inflate_codes_state { + + // mode + inflate_codes_mode mode; // current inflate_codes mode + + // mode dependent information + uInt len; + union { + struct { + const inflate_huft *tree; // pointer into tree + uInt need; // bits needed + } code; // if LEN or DIST, where in tree + uInt lit; // if LIT, literal + struct { + uInt get; // bits to get for extra + uInt dist; // distance back to copy from + } copy; // if EXT or COPY, where and how much + } sub; // submode + + // mode independent information + Byte lbits; // ltree bits decoded per branch + Byte dbits; // dtree bits decoder per branch + const inflate_huft *ltree; // literal/length/eob tree + const inflate_huft *dtree; // distance tree + +}; + + +inflate_codes_statef *inflate_codes_new( +uInt bl, uInt bd, +const inflate_huft *tl, +const inflate_huft *td, // need separate declaration for Borland C++ +z_streamp z) +{ + inflate_codes_statef *c; + + if ((c = (inflate_codes_statef *) + ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) + { + c->mode = START; + c->lbits = (Byte)bl; + c->dbits = (Byte)bd; + c->ltree = tl; + c->dtree = td; + Tracev((stderr, "inflate: codes new\n")); + } + return c; +} + + +int inflate_codes(inflate_blocks_statef *s, z_streamp z, int r) +{ + uInt j; // temporary storage + const inflate_huft *t; // temporary pointer + uInt e; // extra bits or operation + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + Byte *f; // pointer to copy strings from + inflate_codes_statef *c = s->sub.decode.codes; // codes state + + // copy input/output information to locals (UPDATE macro restores) + LOAD + + // process input and output based on current state + for(;;) switch (c->mode) + { // waiting for "i:"=input, "o:"=output, "x:"=nothing + case START: // x: set up for LEN +#ifndef SLOW + if (m >= 258 && n >= 10) + { + UPDATE + r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); + LOAD + if (r != Z_OK) + { + c->mode = r == Z_STREAM_END ? WASH : BADCODE; + break; + } + } +#endif // !SLOW + c->sub.code.need = c->lbits; + c->sub.code.tree = c->ltree; + c->mode = LEN; + case LEN: // i: get length/literal/eob next + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e == 0) // literal + { + c->sub.lit = t->base; + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", t->base)); + c->mode = LIT; + break; + } + if (e & 16) // length + { + c->sub.copy.get = e & 15; + c->len = t->base; + c->mode = LENEXT; + break; + } + if ((e & 64) == 0) // next table + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + if (e & 32) // end of block + { + Tracevv((stderr, "inflate: end of block\n")); + c->mode = WASH; + break; + } + c->mode = BADCODE; // invalid code + z->msg = (char*)"invalid literal/length code"; + r = Z_DATA_ERROR; + LEAVE + case LENEXT: // i: getting length extra (have base) + j = c->sub.copy.get; + NEEDBITS(j) + c->len += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + c->sub.code.need = c->dbits; + c->sub.code.tree = c->dtree; + Tracevv((stderr, "inflate: length %u\n", c->len)); + c->mode = DIST; + case DIST: // i: get distance next + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e & 16) // distance + { + c->sub.copy.get = e & 15; + c->sub.copy.dist = t->base; + c->mode = DISTEXT; + break; + } + if ((e & 64) == 0) // next table + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + c->mode = BADCODE; // invalid code + z->msg = (char*)"invalid distance code"; + r = Z_DATA_ERROR; + LEAVE + case DISTEXT: // i: getting distance extra + j = c->sub.copy.get; + NEEDBITS(j) + c->sub.copy.dist += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); + c->mode = COPY; + case COPY: // o: copying bytes in window, waiting for space + f = (uInt)(q - s->window) < c->sub.copy.dist ? + s->end - (c->sub.copy.dist - (q - s->window)) : + q - c->sub.copy.dist; + while (c->len) + { + NEEDOUT + OUTBYTE(*f++) + if (f == s->end) + f = s->window; + c->len--; + } + c->mode = START; + break; + case LIT: // o: got literal, waiting for output space + NEEDOUT + OUTBYTE(c->sub.lit) + c->mode = START; + break; + case WASH: // o: got eob, possibly more output + if (k > 7) // return unused byte, if any + { + Assert(k < 16, "inflate_codes grabbed too many bytes"); + k -= 8; + n++; + p--; // can always return one + } + FLUSH + if (s->read != s->write) + LEAVE + c->mode = END; + case END: + r = Z_STREAM_END; + LEAVE + case BADCODE: // x: got error + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +void inflate_codes_free(inflate_codes_statef *c,z_streamp z) +{ ZFREE(z, c); + Tracev((stderr, "inflate: codes free\n")); +} + + + +// infblock.c -- interpret and process block types to last block +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +//struct inflate_codes_state {int dummy;}; // for buggy compilers + + + +// Table for deflate from PKZIP's appnote.txt. +const uInt border[] = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +// +// Notes beyond the 1.93a appnote.txt: +// +// 1. Distance pointers never point before the beginning of the output stream. +// 2. Distance pointers can point back across blocks, up to 32k away. +// 3. There is an implied maximum of 7 bits for the bit length table and +// 15 bits for the actual data. +// 4. If only one code exists, then it is encoded using one bit. (Zero +// would be more efficient, but perhaps a little confusing.) If two +// codes exist, they are coded using one bit each (0 and 1). +// 5. There is no way of sending zero distance codes--a dummy must be +// sent if there are none. (History: a pre 2.0 version of PKZIP would +// store blocks with no distance codes, but this was discovered to be +// too harsh a criterion.) Valid only for 1.93a. 2.04c does allow +// zero distance codes, which is sent as one code of zero bits in +// length. +// 6. There are up to 286 literal/length codes. Code 256 represents the +// end-of-block. Note however that the static length tree defines +// 288 codes just to fill out the Huffman codes. Codes 286 and 287 +// cannot be used though, since there is no length base or extra bits +// defined for them. Similarily, there are up to 30 distance codes. +// However, static trees define 32 codes (all 5 bits) to fill out the +// Huffman codes, but the last two had better not show up in the data. +// 7. Unzip can check dynamic Huffman blocks for complete code sets. +// The exception is that a single code would not be complete (see #4). +// 8. The five bits following the block type is really the number of +// literal codes sent minus 257. +// 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits +// (1+6+6). Therefore, to output three times the length, you output +// three codes (1+1+1), whereas to output four times the same length, +// you only need two codes (1+3). Hmm. +//10. In the tree reconstruction algorithm, Code = Code + Increment +// only if BitLength(i) is not zero. (Pretty obvious.) +//11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) +//12. Note: length code 284 can represent 227-258, but length code 285 +// really is 258. The last length deserves its own, short code +// since it gets used a lot in very redundant files. The length +// 258 is special since 258 - 3 (the min match length) is 255. +//13. The literal/length and distance code bit lengths are read as a +// single stream of lengths. It is possible (and advantageous) for +// a repeat code (16, 17, or 18) to go across the boundary between +// the two sets of lengths. + + +void inflate_blocks_reset(inflate_blocks_statef *s, z_streamp z, uLong *c) +{ + if (c != Z_NULL) + *c = s->check; + if (s->mode == IBM_BTREE || s->mode == IBM_DTREE) + ZFREE(z, s->sub.trees.blens); + if (s->mode == IBM_CODES) + inflate_codes_free(s->sub.decode.codes, z); + s->mode = IBM_TYPE; + s->bitk = 0; + s->bitb = 0; + s->read = s->write = s->window; + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(0L, (const Byte *)Z_NULL, 0); + Tracev((stderr, "inflate: blocks reset\n")); +} + + +inflate_blocks_statef *inflate_blocks_new(z_streamp z, check_func c, uInt w) +{ + inflate_blocks_statef *s; + + if ((s = (inflate_blocks_statef *)ZALLOC + (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) + return s; + if ((s->hufts = + (inflate_huft *)ZALLOC(z, sizeof(inflate_huft), MANY)) == Z_NULL) + { + ZFREE(z, s); + return Z_NULL; + } + if ((s->window = (Byte *)ZALLOC(z, 1, w)) == Z_NULL) + { + ZFREE(z, s->hufts); + ZFREE(z, s); + return Z_NULL; + } + s->end = s->window + w; + s->checkfn = c; + s->mode = IBM_TYPE; + Tracev((stderr, "inflate: blocks allocated\n")); + inflate_blocks_reset(s, z, Z_NULL); + return s; +} + + +int inflate_blocks(inflate_blocks_statef *s, z_streamp z, int r) +{ + uInt t; // temporary storage + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + LOAD + + // process input based on current state + for(;;) switch (s->mode) + { + case IBM_TYPE: + NEEDBITS(3) + t = (uInt)b & 7; + s->last = t & 1; + switch (t >> 1) + { + case 0: // stored + Tracev((stderr, "inflate: stored block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + t = k & 7; // go to byte boundary + DUMPBITS(t) + s->mode = IBM_LENS; // get length of stored block + break; + case 1: // fixed + Tracev((stderr, "inflate: fixed codes block%s\n", + s->last ? " (last)" : "")); + { + uInt bl, bd; + const inflate_huft *tl, *td; + + inflate_trees_fixed(&bl, &bd, &tl, &td, z); + s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); + if (s->sub.decode.codes == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + } + DUMPBITS(3) + s->mode = IBM_CODES; + break; + case 2: // dynamic + Tracev((stderr, "inflate: dynamic codes block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + s->mode = IBM_TABLE; + break; + case 3: // illegal + DUMPBITS(3) + s->mode = IBM_BAD; + z->msg = (char*)"invalid block type"; + r = Z_DATA_ERROR; + LEAVE + } + break; + case IBM_LENS: + NEEDBITS(32) + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + s->mode = IBM_BAD; + z->msg = (char*)"invalid stored block lengths"; + r = Z_DATA_ERROR; + LEAVE + } + s->sub.left = (uInt)b & 0xffff; + b = k = 0; // dump bits + Tracev((stderr, "inflate: stored length %u\n", s->sub.left)); + s->mode = s->sub.left ? IBM_STORED : (s->last ? IBM_DRY : IBM_TYPE); + break; + case IBM_STORED: + if (n == 0) + LEAVE + NEEDOUT + t = s->sub.left; + if (t > n) t = n; + if (t > m) t = m; + memcpy(q, p, t); + p += t; n -= t; + q += t; m -= t; + if ((s->sub.left -= t) != 0) + break; + Tracev((stderr, "inflate: stored end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + s->mode = s->last ? IBM_DRY : IBM_TYPE; + break; + case IBM_TABLE: + NEEDBITS(14) + s->sub.trees.table = t = (uInt)b & 0x3fff; + // remove this section to workaround bug in pkzip + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + s->mode = IBM_BAD; + z->msg = (char*)"too many length or distance symbols"; + r = Z_DATA_ERROR; + LEAVE + } + // end remove + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if ((s->sub.trees.blens = (uInt*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + DUMPBITS(14) + s->sub.trees.index = 0; + Tracev((stderr, "inflate: table sizes ok\n")); + s->mode = IBM_BTREE; + case IBM_BTREE: + while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) + { + NEEDBITS(3) + s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; + DUMPBITS(3) + } + while (s->sub.trees.index < 19) + s->sub.trees.blens[border[s->sub.trees.index++]] = 0; + s->sub.trees.bb = 7; + t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, + &s->sub.trees.tb, s->hufts, z); + if (t != Z_OK) + { + ZFREE(z, s->sub.trees.blens); + r = t; + if (r == Z_DATA_ERROR) + s->mode = IBM_BAD; + LEAVE + } + s->sub.trees.index = 0; + Tracev((stderr, "inflate: bits tree ok\n")); + s->mode = IBM_DTREE; + case IBM_DTREE: + while (t = s->sub.trees.table, + s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) + { + inflate_huft *h; + uInt i, j, c; + + t = s->sub.trees.bb; + NEEDBITS(t) + h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); + t = h->bits; + c = h->base; + if (c < 16) + { + DUMPBITS(t) + s->sub.trees.blens[s->sub.trees.index++] = c; + } + else // c == 16..18 + { + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + NEEDBITS(t + i) + DUMPBITS(t) + j += (uInt)b & inflate_mask[i]; + DUMPBITS(i) + i = s->sub.trees.index; + t = s->sub.trees.table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + ZFREE(z, s->sub.trees.blens); + s->mode = IBM_BAD; + z->msg = (char*)"invalid bit length repeat"; + r = Z_DATA_ERROR; + LEAVE + } + c = c == 16 ? s->sub.trees.blens[i - 1] : 0; + do { + s->sub.trees.blens[i++] = c; + } while (--j); + s->sub.trees.index = i; + } + } + s->sub.trees.tb = Z_NULL; + { + uInt bl, bd; + inflate_huft *tl, *td; + inflate_codes_statef *c; + + bl = 9; // must be <= 9 for lookahead assumptions + bd = 6; // must be <= 9 for lookahead assumptions + t = s->sub.trees.table; + t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), + s->sub.trees.blens, &bl, &bd, &tl, &td, + s->hufts, z); + ZFREE(z, s->sub.trees.blens); + if (t != Z_OK) + { + if (t == (uInt)Z_DATA_ERROR) + s->mode = IBM_BAD; + r = t; + LEAVE + } + Tracev((stderr, "inflate: trees ok\n")); + if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.decode.codes = c; + } + s->mode = IBM_CODES; + case IBM_CODES: + UPDATE + if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) + return inflate_flush(s, z, r); + r = Z_OK; + inflate_codes_free(s->sub.decode.codes, z); + LOAD + Tracev((stderr, "inflate: codes end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + if (!s->last) + { + s->mode = IBM_TYPE; + break; + } + s->mode = IBM_DRY; + case IBM_DRY: + FLUSH + if (s->read != s->write) + LEAVE + s->mode = IBM_DONE; + case IBM_DONE: + r = Z_STREAM_END; + LEAVE + case IBM_BAD: + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +int inflate_blocks_free(inflate_blocks_statef *s, z_streamp z) +{ + inflate_blocks_reset(s, z, Z_NULL); + ZFREE(z, s->window); + ZFREE(z, s->hufts); + ZFREE(z, s); + Tracev((stderr, "inflate: blocks freed\n")); + return Z_OK; +} + + + +// inftrees.c -- generate Huffman trees for efficient decoding +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h +// + + + +extern const char inflate_copyright[] = + " ";//inflate 1.1.3 Copyright 1995-1998 Mark Adler "; +// If you use the zlib library in a product, an acknowledgment is welcome +// in the documentation of your product. If for some reason you cannot +// include such an acknowledgment, I would appreciate that you keep this +// copyright string in the executable of your product. + + + +int huft_build ( + uInt *, // code lengths in bits + uInt, // number of codes + uInt, // number of "simple" codes + const uInt *, // list of base values for non-simple codes + const uInt *, // list of extra bits for non-simple codes + inflate_huft **,// result: starting table + uInt *, // maximum lookup bits (returns actual) + inflate_huft *, // space for trees + uInt *, // hufts used in space + uInt * ); // space for values + +// Tables for deflate from PKZIP's appnote.txt. +const uInt cplens[31] = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + // see note #13 above about 258 +const uInt cplext[31] = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; // 112==invalid +const uInt cpdist[30] = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +const uInt cpdext[30] = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + +// +// Huffman code decoding is performed using a multi-level table lookup. +// The fastest way to decode is to simply build a lookup table whose +// size is determined by the longest code. However, the time it takes +// to build this table can also be a factor if the data being decoded +// is not very long. The most common codes are necessarily the +// shortest codes, so those codes dominate the decoding time, and hence +// the speed. The idea is you can have a shorter table that decodes the +// shorter, more probable codes, and then point to subsidiary tables for +// the longer codes. The time it costs to decode the longer codes is +// then traded against the time it takes to make longer tables. +// +// This results of this trade are in the variables lbits and dbits +// below. lbits is the number of bits the first level table for literal/ +// length codes can decode in one step, and dbits is the same thing for +// the distance codes. Subsequent tables are also less than or equal to +// those sizes. These values may be adjusted either when all of the +// codes are shorter than that, in which case the longest code length in +// bits is used, or when the shortest code is *longer* than the requested +// table size, in which case the length of the shortest code in bits is +// used. +// +// There are two different values for the two tables, since they code a +// different number of possibilities each. The literal/length table +// codes 286 possible values, or in a flat code, a little over eight +// bits. The distance table codes 30 possible values, or a little less +// than five bits, flat. The optimum values for speed end up being +// about one bit more than those, so lbits is 8+1 and dbits is 5+1. +// The optimum values may differ though from machine to machine, and +// possibly even between compilers. Your mileage may vary. +// + + +// If BMAX needs to be larger than 16, then h and x[] should be uLong. +#define BMAX 15 // maximum bit length of any code + +int huft_build( +uInt *b, // code lengths in bits (all assumed <= BMAX) +uInt n, // number of codes (assumed <= 288) +uInt s, // number of simple-valued codes (0..s-1) +const uInt *d, // list of base values for non-simple codes +const uInt *e, // list of extra bits for non-simple codes +inflate_huft * *t, // result: starting table +uInt *m, // maximum lookup bits, returns actual +inflate_huft *hp, // space for trees +uInt *hn, // hufts used in space +uInt *v) // working area: values in order of bit length +// Given a list of code lengths and a maximum table size, make a set of +// tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR +// if the given code set is incomplete (the tables are still built in this +// case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of +// lengths), or Z_MEM_ERROR if not enough memory. +{ + + uInt a; // counter for codes of length k + uInt c[BMAX+1]; // bit length count table + uInt f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + register uInt i; // counter, current code + register uInt j; // counter + register int k; // number of bits in current code + int l; // bits per table (returned in m) + uInt mask; // (1 << w) - 1, to avoid cc -O bug on HP + register uInt *p; // pointer into c[], b[], or v[] + inflate_huft *q; // points to current table + struct inflate_huft_s r; // table entry for structure assignment + inflate_huft *u[BMAX]; // table stack + register int w; // bits before this table == (l * h) + uInt x[BMAX+1]; // bit offsets, then code stack + uInt *xp; // pointer into x + int y; // number of dummy codes added + uInt z; // number of entries in current table + + + // Generate counts for each bit length + p = c; +#define C0 *p++ = 0; +#define C2 C0 C0 C0 C0 +#define C4 C2 C2 C2 C2 + C4; p; // clear c[]--assume BMAX+1 is 16 + p = b; i = n; + do { + c[*p++]++; // assume all entries <= BMAX + } while (--i); + if (c[0] == n) // null input--all zero length codes + { + *t = (inflate_huft *)Z_NULL; + *m = 0; + return Z_OK; + } + + + // Find minimum and maximum length, bound *m by those + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; // minimum code length + if ((uInt)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; // maximum code length + if ((uInt)l > i) + l = i; + *m = l; + + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return Z_DATA_ERROR; + if ((y -= c[i]) < 0) + return Z_DATA_ERROR; + c[i] += y; + + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { // note that i == g from above + *xp++ = (j += *p++); + } + + + // Make a table of values in order of bit lengths + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + n = x[g]; // set n to length of v + + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = v; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = (inflate_huft *)Z_NULL; // just to keep compilers happy + q = (inflate_huft *)Z_NULL; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) + { + a = c[k]; + while (a--) + { + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) + { + h++; + w += l; // previous table always l bits + + // compute minimum size table less than or equal to l bits + z = g - w; + z = z > (uInt)l ? l : z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) // try a k-w bit table + { // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = c + k; + if (j < z) + while (++j < z) // try smaller tables up to z bits + { + if ((f <<= 1) <= *++xp) + break; // enough codes to use up j bits + f -= *xp; // else deduct codes from patterns + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (*hn + z > MANY) // (note: doesn't matter for fixed) + return Z_MEM_ERROR; // not enough memory + u[h] = q = hp + *hn; + *hn += z; + + // connect to last table, if there is one + if (h) + { + x[h] = i; // save pattern for backing up + r.bits = (Byte)l; // bits to dump before this table + r.exop = (Byte)j; // bits in this table + j = i >> (w - l); + r.base = (uInt)(q - u[h-1] - j); // offset to this table + u[h-1][j] = r; // connect to last table + } + else + *t = q; // first table is returned result + } + + // set up table entry in r + r.bits = (Byte)(k - w); + if (p >= v + n) + r.exop = 128 + 64; // out of values--invalid code + else if (*p < s) + { + r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); // 256 is end-of-block + r.base = *p++; // simple code is just the value + } + else + { + r.exop = (Byte)(e[*p - s] + 16 + 64);// non-simple--look up in lists + r.base = d[*p++ - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) + { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + + + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; +} + + +int inflate_trees_bits( +uInt *c, // 19 code lengths +uInt *bb, // bits tree desired/actual depth +inflate_huft * *tb, // bits tree result +inflate_huft *hp, // space for trees +z_streamp z) // for messages +{ + int r; + uInt hn = 0; // hufts used in space + uInt *v; // work area for huft_build + + if ((v = (uInt*)ZALLOC(z, 19, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + r = huft_build(c, 19, 19, (uInt*)Z_NULL, (uInt*)Z_NULL, + tb, bb, hp, &hn, v); + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed dynamic bit lengths tree"; + else if (r == Z_BUF_ERROR || *bb == 0) + { + z->msg = (char*)"incomplete dynamic bit lengths tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; +} + + +int inflate_trees_dynamic( +uInt nl, // number of literal/length codes +uInt nd, // number of distance codes +uInt *c, // that many (total) code lengths +uInt *bl, // literal desired/actual bit depth +uInt *bd, // distance desired/actual bit depth +inflate_huft * *tl, // literal/length tree result +inflate_huft * *td, // distance tree result +inflate_huft *hp, // space for trees +z_streamp z) // for messages +{ + int r; + uInt hn = 0; // hufts used in space + uInt *v; // work area for huft_build + + // allocate work area + if ((v = (uInt*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + + // build literal/length tree + r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v); + if (r != Z_OK || *bl == 0) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed literal/length tree"; + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; + } + + // build distance tree + r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v); + if (r != Z_OK || (*bd == 0 && nl > 257)) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed distance tree"; + else if (r == Z_BUF_ERROR) { + z->msg = (char*)"incomplete distance tree"; + r = Z_DATA_ERROR; + } + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"empty distance tree with lengths"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; + } + + // done + ZFREE(z, v); + return Z_OK; +} + + + + + +int inflate_trees_fixed( +uInt *bl, // literal desired/actual bit depth +uInt *bd, // distance desired/actual bit depth +const inflate_huft * * tl, // literal/length tree result +const inflate_huft * *td, // distance tree result +z_streamp ) // for memory allocation +{ + *bl = fixed_bl; + *bd = fixed_bd; + *tl = fixed_tl; + *td = fixed_td; + return Z_OK; +} + + +// inffast.c -- process literals and length/distance pairs fast +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h +// + + +//struct inflate_codes_state {int dummy;}; // for buggy compilers + + +// macros for bit input with no checking and for returning unused bytes +#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3;} + +// Called with number of bytes left to write in window at least 258 +// (the maximum string length) and number of input bytes available +// at least ten. The ten bytes are six bytes for the longest length/ +// distance pair plus four bytes for overloading the bit buffer. + +int inflate_fast( +uInt bl, uInt bd, +const inflate_huft *tl, +const inflate_huft *td, // need separate declaration for Borland C++ +inflate_blocks_statef *s, +z_streamp z) +{ + const inflate_huft *t; // temporary pointer + uInt e; // extra bits or operation + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + uInt ml; // mask for literal/length tree + uInt md; // mask for distance tree + uInt c; // bytes to copy + uInt d; // distance back to copy from + Byte *r; // copy source pointer + + // load input, output, bit values + LOAD + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do { // assume called with m >= 258 && n >= 10 + // get literal/length code + GRABBITS(20) // max bits for literal/length code + if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + continue; + } + for (;;) { + DUMPBITS(t->bits) + if (e & 16) + { + // get extra bits for length + e &= 15; + c = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * length %u\n", c)); + + // decode distance base of block to copy + GRABBITS(15); // max bits for distance code + e = (t = td + ((uInt)b & md))->exop; + for (;;) { + DUMPBITS(t->bits) + if (e & 16) + { + // get extra bits to add to distance base + e &= 15; + GRABBITS(e) // get extra bits (up to 13) + d = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * distance %u\n", d)); + + // do the copy + m -= c; + if ((uInt)(q - s->window) >= d) // offset before dest + { // just copy + r = q - d; + *q++ = *r++; c--; // minimum count is three, + *q++ = *r++; c--; // so unroll loop a little + } + else // else offset after destination + { + e = d - (uInt)(q - s->window); // bytes from offset to end + r = s->end - e; // pointer to offset + if (c > e) // if source crosses, + { + c -= e; // copy to end of window + do { + *q++ = *r++; + } while (--e); + r = s->window; // copy rest from start of window + } + } + do { // copy all or what's left + *q++ = *r++; + } while (--c); + break; + } + else if ((e & 64) == 0) + { + t += t->base; + e = (t += ((uInt)b & inflate_mask[e]))->exop; + } + else + { + z->msg = (char*)"invalid distance code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + }; + break; + } + if ((e & 64) == 0) + { + t += t->base; + if ((e = (t += ((uInt)b & inflate_mask[e]))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + break; + } + } + else if (e & 32) + { + Tracevv((stderr, "inflate: * end of block\n")); + UNGRAB + UPDATE + return Z_STREAM_END; + } + else + { + z->msg = (char*)"invalid literal/length code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + }; + } while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + UNGRAB + UPDATE + return Z_OK; +} + + + + + + +// crc32.c -- compute the CRC-32 of a data stream +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +// @(#) $Id$ + + + + + + +// Table of CRC-32's of all single-byte values (made by make_crc_table) +const uLong crc_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +const uLong * get_crc_table() +{ return (const uLong *)crc_table; +} + +#define CRC_DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); +#define CRC_DO2(buf) CRC_DO1(buf); CRC_DO1(buf); +#define CRC_DO4(buf) CRC_DO2(buf); CRC_DO2(buf); +#define CRC_DO8(buf) CRC_DO4(buf); CRC_DO4(buf); + +uLong ucrc32(uLong crc, const Byte *buf, uInt len) +{ if (buf == Z_NULL) return 0L; + crc = crc ^ 0xffffffffL; + while (len >= 8) {CRC_DO8(buf); len -= 8;} + if (len) do {CRC_DO1(buf);} while (--len); + return crc ^ 0xffffffffL; +} + + +// adler32.c -- compute the Adler-32 checksum of a data stream +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +// @(#) $Id$ + + +#define BASE 65521L // largest prime smaller than 65536 +#define NMAX 5552 +// NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + +#define AD_DO1(buf,i) {s1 += buf[i]; s2 += s1;} +#define AD_DO2(buf,i) AD_DO1(buf,i); AD_DO1(buf,i+1); +#define AD_DO4(buf,i) AD_DO2(buf,i); AD_DO2(buf,i+2); +#define AD_DO8(buf,i) AD_DO4(buf,i); AD_DO4(buf,i+4); +#define AD_DO16(buf) AD_DO8(buf,0); AD_DO8(buf,8); + +// ========================================================================= +uLong adler32(uLong adler, const Byte *buf, uInt len) +{ + unsigned long s1 = adler & 0xffff; + unsigned long s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == Z_NULL) return 1L; + + while (len > 0) { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) { + AD_DO16(buf); + buf += 16; + k -= 16; + } + if (k != 0) do { + s1 += *buf++; + s2 += s1; + } while (--k); + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; +} + + + +// zutil.c -- target dependent utility functions for the compression library +// Copyright (C) 1995-1998 Jean-loup Gailly. +// For conditions of distribution and use, see copyright notice in zlib.h +// @(#) $Id$ + + + + + + +const char * zlibVersion() +{ + return ZLIB_VERSION; +} + +// exported to allow conversion of error code to string for compress() and +// uncompress() +const char * zError(int err) +{ return ERR_MSG(err); +} + + + + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) items += size - size; // make compiler happy + return (voidpf)calloc(items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + zfree(ptr); + if (opaque) return; // make compiler happy +} + + + +// inflate.c -- zlib interface to inflate modules +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +//struct inflate_blocks_state {int dummy;}; // for buggy compilers + +typedef enum { + IM_METHOD, // waiting for method byte + IM_FLAG, // waiting for flag byte + IM_DICT4, // four dictionary check bytes to go + IM_DICT3, // three dictionary check bytes to go + IM_DICT2, // two dictionary check bytes to go + IM_DICT1, // one dictionary check byte to go + IM_DICT0, // waiting for inflateSetDictionary + IM_BLOCKS, // decompressing blocks + IM_CHECK4, // four check bytes to go + IM_CHECK3, // three check bytes to go + IM_CHECK2, // two check bytes to go + IM_CHECK1, // one check byte to go + IM_DONE, // finished check, done + IM_BAD} // got an error--stay here +inflate_mode; + +// inflate private state +struct internal_state { + + // mode + inflate_mode mode; // current inflate mode + + // mode dependent information + union { + uInt method; // if IM_FLAGS, method byte + struct { + uLong was; // computed check value + uLong need; // stream check value + } check; // if CHECK, check values to compare + uInt marker; // if IM_BAD, inflateSync's marker bytes count + } sub; // submode + + // mode independent information + int nowrap; // flag for no wrapper + uInt wbits; // log2(window size) (8..15, defaults to 15) + inflate_blocks_statef + *blocks; // current inflate_blocks state + +}; + +int inflateReset(z_streamp z) +{ + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + z->total_in = z->total_out = 0; + z->msg = Z_NULL; + z->state->mode = z->state->nowrap ? IM_BLOCKS : IM_METHOD; + inflate_blocks_reset(z->state->blocks, z, Z_NULL); + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int inflateEnd(z_streamp z) +{ + if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->blocks != Z_NULL) + inflate_blocks_free(z->state->blocks, z); + ZFREE(z, z->state); + z->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + + +int inflateInit2(z_streamp z) +{ const char *version = ZLIB_VERSION; int stream_size = sizeof(z_stream); + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || stream_size != sizeof(z_stream)) return Z_VERSION_ERROR; + + int w = -15; // MAX_WBITS: 32K LZ77 window. + // Warning: reducing MAX_WBITS makes minigzip unable to extract .gz files created by gzip. + // The memory requirements for deflate are (in bytes): + // (1 << (windowBits+2)) + (1 << (memLevel+9)) + // that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + // plus a few kilobytes for small objects. For example, if you want to reduce + // the default memory requirements from 256K to 128K, compile with + // make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + // Of course this will generally degrade compression (there's no free lunch). + // + // The memory requirements for inflate are (in bytes) 1 << windowBits + // that is, 32K for windowBits=15 (default value) plus a few kilobytes + // for small objects. + + // initialize state + if (z == Z_NULL) return Z_STREAM_ERROR; + z->msg = Z_NULL; + if (z->zalloc == Z_NULL) + { + z->zalloc = zcalloc; + z->opaque = (voidpf)0; + } + if (z->zfree == Z_NULL) z->zfree = zcfree; + if ((z->state = (struct internal_state *) + ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) + return Z_MEM_ERROR; + z->state->blocks = Z_NULL; + + // handle undocumented nowrap option (no zlib header or check) + z->state->nowrap = 0; + if (w < 0) + { + w = - w; + z->state->nowrap = 1; + } + + // set window size + if (w < 8 || w > 15) + { + inflateEnd(z); + return Z_STREAM_ERROR; + } + z->state->wbits = (uInt)w; + + // create inflate_blocks state + if ((z->state->blocks = + inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w)) + == Z_NULL) + { + inflateEnd(z); + return Z_MEM_ERROR; + } + Tracev((stderr, "inflate: allocated\n")); + + // reset state + inflateReset(z); + return Z_OK; +} + + + +#define IM_NEEDBYTE {if(z->avail_in==0)return r;r=f;} +#define IM_NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) + +int inflate(z_streamp z, int f) +{ + int r; + uInt b; + + if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL) + return Z_STREAM_ERROR; + f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; + r = Z_BUF_ERROR; + for (;;) switch (z->state->mode) + { + case IM_METHOD: + IM_NEEDBYTE + if (((z->state->sub.method = IM_NEXTBYTE) & 0xf) != Z_DEFLATED) + { + z->state->mode = IM_BAD; + z->msg = (char*)"unknown compression method"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + if ((z->state->sub.method >> 4) + 8 > z->state->wbits) + { + z->state->mode = IM_BAD; + z->msg = (char*)"invalid window size"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + z->state->mode = IM_FLAG; + case IM_FLAG: + IM_NEEDBYTE + b = IM_NEXTBYTE; + if (((z->state->sub.method << 8) + b) % 31) + { + z->state->mode = IM_BAD; + z->msg = (char*)"incorrect header check"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + Tracev((stderr, "inflate: zlib header ok\n")); + if (!(b & PRESET_DICT)) + { + z->state->mode = IM_BLOCKS; + break; + } + z->state->mode = IM_DICT4; + case IM_DICT4: + IM_NEEDBYTE + z->state->sub.check.need = (uLong)IM_NEXTBYTE << 24; + z->state->mode = IM_DICT3; + case IM_DICT3: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 16; + z->state->mode = IM_DICT2; + case IM_DICT2: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 8; + z->state->mode = IM_DICT1; + case IM_DICT1: + IM_NEEDBYTE; r; + z->state->sub.check.need += (uLong)IM_NEXTBYTE; + z->adler = z->state->sub.check.need; + z->state->mode = IM_DICT0; + return Z_NEED_DICT; + case IM_DICT0: + z->state->mode = IM_BAD; + z->msg = (char*)"need dictionary"; + z->state->sub.marker = 0; // can try inflateSync + return Z_STREAM_ERROR; + case IM_BLOCKS: + r = inflate_blocks(z->state->blocks, z, r); + if (r == Z_DATA_ERROR) + { + z->state->mode = IM_BAD; + z->state->sub.marker = 0; // can try inflateSync + break; + } + if (r == Z_OK) + r = f; + if (r != Z_STREAM_END) + return r; + r = f; + inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); + if (z->state->nowrap) + { + z->state->mode = IM_DONE; + break; + } + z->state->mode = IM_CHECK4; + case IM_CHECK4: + IM_NEEDBYTE + z->state->sub.check.need = (uLong)IM_NEXTBYTE << 24; + z->state->mode = IM_CHECK3; + case IM_CHECK3: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 16; + z->state->mode = IM_CHECK2; + case IM_CHECK2: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 8; + z->state->mode = IM_CHECK1; + case IM_CHECK1: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE; + + if (z->state->sub.check.was != z->state->sub.check.need) + { + z->state->mode = IM_BAD; + z->msg = (char*)"incorrect data check"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + Tracev((stderr, "inflate: zlib check ok\n")); + z->state->mode = IM_DONE; + case IM_DONE: + return Z_STREAM_END; + case IM_BAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } +} + + + +#ifdef _UNICODE + +static int GetAnsiFileName(LPCWSTR name, char * buf, int nBufSize) +{ + memset(buf, 0, nBufSize); + + int n = WideCharToMultiByte(CP_ACP, // code page + 0, // performance and mapping flags + name, // wide-character string + -1, // number of chars in string + buf, // buffer for new string + nBufSize, // size of buffer + NULL, // default for unmappable chars + NULL); // set when default char used + return n; +} + +static int GetUnicodeFileName(const char * name, LPWSTR buf, int nBufSize) +{ + memset(buf, 0, nBufSize*sizeof(TCHAR)); + + int n = MultiByteToWideChar(CP_ACP, // code page + 0, // character-type options + name, // string to map + -1, // number of bytes in string + buf, // wide-character buffer + nBufSize); // size of buffer + + return n; +} + +#endif + + +// unzip.c -- IO on .zip files using zlib +// Version 0.15 beta, Mar 19th, 1998, +// Read unzip.h for more info + + + + +#define UNZ_BUFSIZE (16384) +#define UNZ_MAXFILENAMEINZIP (256) +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + + + +const char unz_copyright[] = " ";//unzip 0.15 Copyright 1998 Gilles Vollant "; + +// unz_file_info_interntal contain internal info about a file in zipfile +typedef struct unz_file_info_internal_s +{ + uLong offset_curfile;// relative offset of local header 4 bytes +} unz_file_info_internal; + + +typedef struct +{ bool is_handle; // either a handle or memory + bool canseek; + // for handles: + HANDLE h; bool herr; unsigned long initial_offset; + // for memory: + void *buf; unsigned int len,pos; // if it's a memory block +} LUFILE; + + +LUFILE *lufopen(void *z,unsigned int len,DWORD flags,ZRESULT *err) +{ + if (flags!=ZIP_HANDLE && flags!=ZIP_FILENAME && flags!=ZIP_MEMORY) + { + *err=ZR_ARGS; + return NULL; + } + // + HANDLE h=0; bool canseek=false; *err=ZR_OK; + if (flags==ZIP_HANDLE||flags==ZIP_FILENAME) + { + if (flags==ZIP_HANDLE) + { + HANDLE hf = z; + + BOOL res = DuplicateHandle(GetCurrentProcess(),hf,GetCurrentProcess(),&h,0,FALSE,DUPLICATE_SAME_ACCESS); + + if (!res) + { + *err=ZR_NODUPH; + return NULL; + } + } + else + { + h = CreateFile((const TCHAR *)z, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (h == INVALID_HANDLE_VALUE) + { + *err = ZR_NOFILE; + return NULL; + } + } + DWORD type = GetFileType(h); + canseek = (type==FILE_TYPE_DISK); + } + LUFILE *lf = new LUFILE; + if (flags==ZIP_HANDLE||flags==ZIP_FILENAME) + { + lf->is_handle=true; + lf->canseek=canseek; + lf->h=h; lf->herr=false; + lf->initial_offset=0; + if (canseek) + lf->initial_offset = SetFilePointer(h,0,NULL,FILE_CURRENT); + } + else + { + lf->is_handle=false; + lf->canseek=true; + lf->buf=z; + lf->len=len; + lf->pos=0; + lf->initial_offset=0; + } + *err=ZR_OK; + return lf; +} + + +int lufclose(LUFILE *stream) +{ if (stream==NULL) return EOF; + if (stream->is_handle) CloseHandle(stream->h); + delete stream; + return 0; +} + +int luferror(LUFILE *stream) +{ if (stream->is_handle && stream->herr) return 1; + else return 0; +} + +long int luftell(LUFILE *stream) +{ if (stream->is_handle && stream->canseek) return SetFilePointer(stream->h,0,NULL,FILE_CURRENT)-stream->initial_offset; + else if (stream->is_handle) return 0; + else return stream->pos; +} + +int lufseek(LUFILE *stream, long offset, int whence) +{ if (stream->is_handle && stream->canseek) + { if (whence==SEEK_SET) SetFilePointer(stream->h,stream->initial_offset+offset,0,FILE_BEGIN); + else if (whence==SEEK_CUR) SetFilePointer(stream->h,offset,NULL,FILE_CURRENT); + else if (whence==SEEK_END) SetFilePointer(stream->h,offset,NULL,FILE_END); + else return 19; // EINVAL + return 0; + } + else if (stream->is_handle) return 29; // ESPIPE + else + { if (whence==SEEK_SET) stream->pos=offset; + else if (whence==SEEK_CUR) stream->pos+=offset; + else if (whence==SEEK_END) stream->pos=stream->len+offset; + return 0; + } +} + + +size_t lufread(void *ptr,size_t size,size_t n,LUFILE *stream) +{ unsigned int toread = (unsigned int)(size*n); + if (stream->is_handle) + { DWORD red; BOOL res = ReadFile(stream->h,ptr,toread,&red,NULL); + if (!res) stream->herr=true; + return red/size; + } + if (stream->pos+toread > stream->len) toread = stream->len-stream->pos; + memcpy(ptr, (char*)stream->buf + stream->pos, toread); DWORD red = toread; + stream->pos += red; + return red/size; +} + + + + +// file_in_zip_read_info_s contain internal information about a file in zipfile, +// when reading and decompress it +typedef struct +{ + char *read_buffer; // internal buffer for compressed data + z_stream stream; // zLib stream structure for inflate + + uLong pos_in_zipfile; // position in byte on the zipfile, for fseek + uLong stream_initialised; // flag set if stream structure is initialised + + uLong offset_local_extrafield;// offset of the local extra field + uInt size_local_extrafield;// size of the local extra field + uLong pos_local_extrafield; // position in the local extra field in read + + uLong crc32; // crc32 of all data uncompressed + uLong crc32_wait; // crc32 we must obtain after decompress all + uLong rest_read_compressed; // number of byte to be decompressed + uLong rest_read_uncompressed;//number of byte to be obtained after decomp + LUFILE* file; // io structore of the zipfile + uLong compression_method; // compression method (0==store) + uLong byte_before_the_zipfile;// byte before the zipfile, (>0 for sfx) +} file_in_zip_read_info_s; + + +// unz_s contain internal information about the zipfile +typedef struct +{ + LUFILE* file; // io structore of the zipfile + unz_global_info gi; // public global information + uLong byte_before_the_zipfile;// byte before the zipfile, (>0 for sfx) + uLong num_file; // number of the current file in the zipfile + uLong pos_in_central_dir; // pos of the current file in the central dir + uLong current_file_ok; // flag about the usability of the current file + uLong central_pos; // position of the beginning of the central dir + + uLong size_central_dir; // size of the central directory + uLong offset_central_dir; // offset of start of central directory with respect to the starting disk number + + unz_file_info cur_file_info; // public info about the current file in zip + unz_file_info_internal cur_file_info_internal; // private info about it + file_in_zip_read_info_s* pfile_in_zip_read; // structure about the current file if we are decompressing it +} unz_s, *unzFile; + + +int unzStringFileNameCompare (const char* fileName1,const char* fileName2,int iCaseSensitivity); +// Compare two filename (fileName1,fileName2). + +z_off_t unztell (unzFile file); +// Give the current position in uncompressed data + +int unzeof (unzFile file); +// return 1 if the end of file was reached, 0 elsewhere + +int unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len); +// Read extra field from the current file (opened by unzOpenCurrentFile) +// This is the local-header version of the extra field (sometimes, there is +// more info in the local-header version than in the central-header) +// +// if buf==NULL, it return the size of the local extra field +// +// if buf!=NULL, len is the size of the buffer, the extra header is copied in +// buf. +// the return value is the number of bytes copied in buf, or (if <0) +// the error code + + + +// =========================================================================== +// Read a byte from a gz_stream; update next_in and avail_in. Return EOF +// for end of file. +// IN assertion: the stream s has been sucessfully opened for reading. + +int unzlocal_getByte(LUFILE *fin,int *pi) +{ unsigned char c; + int err = (int)lufread(&c, 1, 1, fin); + if (err==1) + { *pi = (int)c; + return UNZ_OK; + } + else + { if (luferror(fin)) return UNZ_ERRNO; + else return UNZ_EOF; + } +} + + +// =========================================================================== +// Reads a long in LSB order from the given gz_stream. Sets +int unzlocal_getShort (LUFILE *fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +int unzlocal_getLong (LUFILE *fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + + +// My own strcmpi / strcasecmp +int strcmpcasenosensitive_internal (const char* fileName1,const char *fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= (char)0x20; + if ((c2>='a') && (c2<='z')) + c2 -= (char)0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + + + +// +// Compare two filename (fileName1,fileName2). +// If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) +// If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi or strcasecmp) +// +int unzStringFileNameCompare (const char*fileName1,const char*fileName2,int iCaseSensitivity) +{ if (iCaseSensitivity==1) return strcmp(fileName1,fileName2); + else return strcmpcasenosensitive_internal(fileName1,fileName2); +} + +#define BUFREADCOMMENT (0x400) + + +// Locate the Central directory of a zipfile (at the end, just before +// the global comment) +uLong unzlocal_SearchCentralDir(LUFILE *fin) +{ if (lufseek(fin,0,SEEK_END) != 0) return 0; + uLong uSizeFile = luftell(fin); + + uLong uMaxBack=0xffff; // maximum size of global comment + if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; + + unsigned char *buf = (unsigned char*)zmalloc(BUFREADCOMMENT+4); + if (buf==NULL) return 0; + uLong uPosFound=0; + + uLong uBackRead = 4; + while (uBackReaduMaxBack) uBackRead = uMaxBack; + else uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (lufseek(fin,uReadPos,SEEK_SET)!=0) break; + if (lufread(buf,(uInt)uReadSize,1,fin)!=1) break; + for (i=(int)uReadSize-3; (i--)>0;) + { if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { uPosFound = uReadPos+i; break; + } + } + if (uPosFound!=0) break; + } + if (buf) zfree(buf); + return uPosFound; +} + + +int unzGoToFirstFile (unzFile file); +int unzCloseCurrentFile (unzFile file); + +// Open a Zip file. +// If the zipfile cannot be opened (file don't exist or in not valid), return NULL. +// Otherwise, the return value is a unzFile Handle, usable with other unzip functions +unzFile unzOpenInternal(LUFILE *fin) +{ + zopenerror = ZR_OK; //+++1.2 + if (fin==NULL) { zopenerror = ZR_ARGS; return NULL; } //+++1.2 + if (unz_copyright[0]!=' ') {lufclose(fin); zopenerror = ZR_CORRUPT; return NULL; } //+++1.2 + + int err=UNZ_OK; + unz_s us; + uLong central_pos,uL; + central_pos = unzlocal_SearchCentralDir(fin); + if (central_pos==0) err=UNZ_ERRNO; + if (lufseek(fin,central_pos,SEEK_SET)!=0) err=UNZ_ERRNO; + // the signature, already checked + if (unzlocal_getLong(fin,&uL)!=UNZ_OK) err=UNZ_ERRNO; + // number of this disk + uLong number_disk; // number of the current dist, used for spanning ZIP, unsupported, always 0 + if (unzlocal_getShort(fin,&number_disk)!=UNZ_OK) err=UNZ_ERRNO; + // number of the disk with the start of the central directory + uLong number_disk_with_CD; // number the the disk with central dir, used for spaning ZIP, unsupported, always 0 + if (unzlocal_getShort(fin,&number_disk_with_CD)!=UNZ_OK) err=UNZ_ERRNO; + // total number of entries in the central dir on this disk + if (unzlocal_getShort(fin,&us.gi.number_entry)!=UNZ_OK) err=UNZ_ERRNO; + // total number of entries in the central dir + uLong number_entry_CD; // total number of entries in the central dir (same than number_entry on nospan) + if (unzlocal_getShort(fin,&number_entry_CD)!=UNZ_OK) err=UNZ_ERRNO; + if ((number_entry_CD!=us.gi.number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=UNZ_BADZIPFILE; + // size of the central directory + if (unzlocal_getLong(fin,&us.size_central_dir)!=UNZ_OK) err=UNZ_ERRNO; + // offset of start of central directory with respect to the starting disk number + if (unzlocal_getLong(fin,&us.offset_central_dir)!=UNZ_OK) err=UNZ_ERRNO; + // zipfile comment length + if (unzlocal_getShort(fin,&us.gi.size_comment)!=UNZ_OK) err=UNZ_ERRNO; + if ((central_pos+fin->initial_offsetinitial_offset - (us.offset_central_dir+us.size_central_dir); + us.central_pos = central_pos; + us.pfile_in_zip_read = NULL; + fin->initial_offset = 0; // since the zipfile itself is expected to handle this + + unz_s *s = (unz_s*)zmalloc(sizeof(unz_s)); + *s=us; + unzGoToFirstFile((unzFile)s); + return (unzFile)s; +} + + + +// Close a ZipFile opened with unzipOpen. +// If there is files inside the .Zip opened with unzipOpenCurrentFile (see later), +// these files MUST be closed with unzipCloseCurrentFile before call unzipClose. +// return UNZ_OK if there is no problem. +int unzClose (unzFile file) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + if (s->pfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + lufclose(s->file); + if (s) zfree(s); // unused s=0; + return UNZ_OK; +} + + +// Write info about the ZipFile in the *pglobal_info structure. +// No preparation of the structure is needed +// return UNZ_OK if there is no problem. +int unzGetGlobalInfo (unzFile file,unz_global_info *pglobal_info) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +// Translate date/time from Dos format to tm_unz (readable more easilty) +void unzlocal_DosDateToTmuDate (uLong ulDosDate, tm_unz* ptm) +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +// Get Info about the current file in the zipfile, with internal only info +int unzlocal_GetCurrentFileInfoInternal (unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize); + +int unzlocal_GetCurrentFileInfoInternal (unzFile file, unz_file_info *pfile_info, + unz_file_info_internal *pfile_info_internal, char *szFileName, + uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, + char *szComment, uLong commentBufferSize) +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (lufseek(s->file,s->pos_in_central_dir+s->byte_before_the_zipfile,SEEK_SET)!=0) + err=UNZ_ERRNO; + + + // we check the magic + if (err==UNZ_OK) + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(s->file,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (lufread(szFileName,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extrafile,lSeek,SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (lufread(extraField,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentfile,lSeek,SEEK_CUR)==0) + {} // unused lSeek=0; + else + err=UNZ_ERRNO; + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (lufread(szComment,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + //unused lSeek+=file_info.size_file_comment - uSizeRead; + } + else {} //unused lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +// Write info about the ZipFile in the *pglobal_info structure. +// No preparation of the structure is needed +// return UNZ_OK if there is no problem. +int unzGetCurrentFileInfo (unzFile file, unz_file_info *pfile_info, + char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, + char *szComment, uLong commentBufferSize) +{ return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL,szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, szComment,commentBufferSize); +} + + +// Set the current file of the zipfile to the first file. +// return UNZ_OK if there is no problem +int unzGoToFirstFile (unzFile file) +{ + int err; + unz_s* s; + if (file==NULL) return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +// Set the current file of the zipfile to the next file. +// return UNZ_OK if there is no problem +// return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +int unzGoToNextFile (unzFile file) +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +// Try locate the file szFileName in the zipfile. +// For the iCaseSensitivity signification, see unzStringFileNameCompare +// return value : +// UNZ_OK if the file is found. It becomes the current file. +// UNZ_END_OF_LIST_OF_FILE if the file is not found +int unzLocateFile (unzFile file, const TCHAR *szFileName, int iCaseSensitivity) +{ + unz_s* s; + int err; + + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + if (file==NULL) + return UNZ_PARAMERROR; + + if (_tcslen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + char szFileNameA[MAX_PATH]; + +#ifdef _UNICODE + GetAnsiFileName(szFileName, szFileNameA, MAX_PATH-1); +#else + strcpy(szFileNameA, szFileName); +#endif + + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (unzStringFileNameCompare(szCurrentFileName,szFileNameA,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + return err; +} + + +// Read the local header of the current zipfile +// Check the coherency of the local header and info in the end of central +// directory about this file +// store in *piSizeVar the size of extra info in local header +// (filename and size of extra field data) +int unzlocal_CheckCurrentFileCoherencyHeader (unz_s *s,uInt *piSizeVar, + uLong *poffset_local_extrafield, uInt *psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (lufseek(s->file,s->cur_file_info_internal.offset_curfile + s->byte_before_the_zipfile,SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; +// else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) +// err=UNZ_BADZIPFILE; + if (unzlocal_getShort(s->file,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // date/time + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // crc + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // size compr + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // size uncompr + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(s->file,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(s->file,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + + + + + +// Open for reading data the current file in the zipfile. +// If there is no error and the file is opened, the return value is UNZ_OK. +int unzOpenCurrentFile (unzFile file) +{ + int err; + int Store; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; // offset of the local extra field + uInt size_local_extrafield; // size of the local extra field + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*)zmalloc(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)zmalloc(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + if (pfile_in_zip_read_info!=0) zfree(pfile_in_zip_read_info); //unused pfile_in_zip_read_info=0; + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if ((s->cur_file_info.compression_method!=0) && (s->cur_file_info.compression_method!=Z_DEFLATED)) + { // unused err=UNZ_BADZIPFILE; + } + Store = s->cur_file_info.compression_method==0; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->file=s->file; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if (!Store) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + + err=inflateInit2(&pfile_in_zip_read_info->stream); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + // windowBits is passed < 0 to tell that there is no zlib header. + // Note that in this case inflate *requires* an extra "dummy" byte + // after the compressed stream in order to complete decompression and + // return Z_STREAM_END. + // In unzip, i don't wait absolutely Z_STREAM_END because I known the + // size of both compressed and uncompressed data + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + + s->pfile_in_zip_read = pfile_in_zip_read_info; + return UNZ_OK; +} + + +// Read bytes from the current file. +// buf contain buffer where data must be copied +// len the size of buf. +// return the number of byte copied if somes bytes are copied +// return 0 if the end of file was reached +// return <0 with error code if there is an error +// (UNZ_ERRNO for IO error, or zLib error for uncompress error) +int unzReadCurrentFile (unzFile file, voidp buf, unsigned len) +{ int err=UNZ_OK; + uInt iRead = 0; + + unz_s *s = (unz_s*)file; + if (s==NULL) return UNZ_PARAMERROR; + + file_in_zip_read_info_s* pfile_in_zip_read_info = s->pfile_in_zip_read; + if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; + if ((pfile_in_zip_read_info->read_buffer == NULL)) return UNZ_END_OF_LIST_OF_FILE; + if (len==0) return 0; + + pfile_in_zip_read_info->stream.next_out = (Byte*)buf; + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if (len>pfile_in_zip_read_info->rest_read_uncompressed) + { pfile_in_zip_read_info->stream.avail_out = (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + } + + while (pfile_in_zip_read_info->stream.avail_out>0) + { if ((pfile_in_zip_read_info->stream.avail_in==0) && (pfile_in_zip_read_info->rest_read_compressed>0)) + { uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) return UNZ_EOF; + if (lufseek(pfile_in_zip_read_info->file, pfile_in_zip_read_info->pos_in_zipfile + pfile_in_zip_read_info->byte_before_the_zipfile,SEEK_SET)!=0) return UNZ_ERRNO; + if (lufread(pfile_in_zip_read_info->read_buffer,uReadThis,1,pfile_in_zip_read_info->file)!=1) return UNZ_ERRNO; + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + pfile_in_zip_read_info->stream.next_in = (Byte*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if (pfile_in_zip_read_info->compression_method==0) + { uInt uDoCopy,i ; + if (pfile_in_zip_read_info->stream.avail_out < pfile_in_zip_read_info->stream.avail_in) + { uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + } + else + { uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + } + for (i=0;istream.next_out+i) = *(pfile_in_zip_read_info->stream.next_in+i); + } + pfile_in_zip_read_info->crc32 = ucrc32(pfile_in_zip_read_info->crc32,pfile_in_zip_read_info->stream.next_out,uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { uLong uTotalOutBefore,uTotalOutAfter; + const Byte *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + err=inflate(&pfile_in_zip_read_info->stream,flush); + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + pfile_in_zip_read_info->crc32 = ucrc32(pfile_in_zip_read_info->crc32,bufBefore,(uInt)(uOutThis)); + pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + if (err==Z_STREAM_END) return (iRead==0) ? UNZ_EOF : iRead; //+++1.3 + //if (err==Z_STREAM_END) return (iRead==len) ? UNZ_EOF : iRead; //+++1.2 + + if (err != Z_OK) break; + } + } + + if (err==Z_OK) return iRead; + + return iRead; +} + + +// Give the current position in uncompressed data +z_off_t unztell (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + + +// return 1 if the end of file was reached, 0 elsewhere +int unzeof (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +// Read extra field from the current file (opened by unzOpenCurrentFile) +// This is the local-header version of the extra field (sometimes, there is +// more info in the local-header version than in the central-header) +// if buf==NULL, it return the size of the local extra field that can be read +// if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. +// the return value is the number of bytes copied in buf, or (if <0) the error code +int unzGetLocalExtrafield (unzFile file,voidp buf,unsigned len) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (lufseek(pfile_in_zip_read_info->file, pfile_in_zip_read_info->offset_local_extrafield + pfile_in_zip_read_info->pos_local_extrafield,SEEK_SET)!=0) + return UNZ_ERRNO; + + if (lufread(buf,(uInt)size_to_read,1,pfile_in_zip_read_info->file)!=1) + return UNZ_ERRNO; + + return (int)read_now; +} + +// Close the file in zip opened with unzipOpenCurrentFile +// Return UNZ_CRCERROR if all the file was read but the CRC is not good +int unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + if (pfile_in_zip_read_info->read_buffer!=0) + { void *buf = pfile_in_zip_read_info->read_buffer; + zfree(buf); + pfile_in_zip_read_info->read_buffer=0; + } + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + if (pfile_in_zip_read_info!=0) zfree(pfile_in_zip_read_info); // unused pfile_in_zip_read_info=0; + + s->pfile_in_zip_read=NULL; + + return err; +} + + +// Get the global comment string of the ZipFile, in the szComment buffer. +// uSizeBuf is the size of the szComment buffer. +// return the number of byte copied or an error code <0 +int unzGetGlobalComment (unzFile file, char *szComment, uLong uSizeBuf) +{ //int err=UNZ_OK; + unz_s* s; + uLong uReadThis ; + if (file==NULL) return UNZ_PARAMERROR; + s=(unz_s*)file; + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) uReadThis = s->gi.size_comment; + if (lufseek(s->file,s->central_pos+22,SEEK_SET)!=0) return UNZ_ERRNO; + if (uReadThis>0) + { *szComment='\0'; + if (lufread(szComment,(uInt)uReadThis,1,s->file)!=1) return UNZ_ERRNO; + } + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + + + + + +int unzOpenCurrentFile (unzFile file); +int unzReadCurrentFile (unzFile file, void *buf, unsigned len); +int unzCloseCurrentFile (unzFile file); + + +FILETIME timet2filetime(time_t timer) +{ + struct tm *tm = gmtime(&timer); + SYSTEMTIME st; + st.wYear = (WORD)(tm->tm_year+1900); + st.wMonth = (WORD)(tm->tm_mon+1); + st.wDay = (WORD)(tm->tm_mday); + st.wHour = (WORD)(tm->tm_hour); + st.wMinute = (WORD)(tm->tm_min); + st.wSecond = (WORD)(tm->tm_sec); + st.wMilliseconds=0; + FILETIME ft; + SystemTimeToFileTime(&st,&ft); + return ft; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +class TUnzip +{ public: + TUnzip() : uf(0), currentfile(-1), czei(-1) {} + + unzFile uf; int currentfile; ZIPENTRY cze; int czei; + TCHAR rootdir[MAX_PATH]; + + ZRESULT Open(void *rd, void *z,unsigned int len,DWORD flags); + ZRESULT Get(int index,ZIPENTRY *ze); + ZRESULT Find(const TCHAR *name,bool ic,int *index,ZIPENTRY *ze); + ZRESULT Unzip(int index,void *dst,unsigned int len,DWORD flags); + ZRESULT Close(); +}; + + +ZRESULT TUnzip::Open(void *rd, void *z,unsigned int len,DWORD flags) +{ + if (uf!=0 || currentfile!=-1) + return ZR_NOTINITED; + //GetCurrentDirectory(MAX_PATH,rootdir); + _tcscpy(rootdir, (const wchar_t *)rd); + _tcscat(rootdir,_T("\\")); + if (flags==ZIP_HANDLE) + { + DWORD type = GetFileType(z); + if (type!=FILE_TYPE_DISK) + return ZR_SEEK; + } + ZRESULT e; + LUFILE *f = lufopen(z,len,flags,&e); + if (f==NULL) + return e; + uf = unzOpenInternal(f); + //return ZR_OK; + return zopenerror; //+++1.2 +} + +ZRESULT TUnzip::Get(int index,ZIPENTRY *ze) +{ if (index<-1 || index>=(int)uf->gi.number_entry) + return ZR_ARGS; + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index==czei && index!=-1) {memcpy(ze,&cze,sizeof(ZIPENTRY)); return ZR_OK;} + if (index==-1) + { ze->index = uf->gi.number_entry; + ze->name[0]=0; + ze->attr=0; + ze->atime.dwLowDateTime=0; ze->atime.dwHighDateTime=0; + ze->ctime.dwLowDateTime=0; ze->ctime.dwHighDateTime=0; + ze->mtime.dwLowDateTime=0; ze->mtime.dwHighDateTime=0; + ze->comp_size=0; + ze->unc_size=0; + return ZR_OK; + } + if (index<(int)uf->num_file) unzGoToFirstFile(uf); + while ((int)uf->num_filefile,offset,SEEK_SET)!=0) return ZR_READ; + char *extra = new char[extralen]; + if (lufread(extra,1,(uInt)extralen,uf->file)!=extralen) {delete[] extra; return ZR_READ;} + // + ze->index=uf->num_file; + strcpy(ze->name,fn); + // zip has an 'attribute' 32bit value. Its lower half is windows stuff + // its upper half is standard unix attr. + unsigned long a = ufi.external_fa; + bool uisdir = (a&0x40000000)!=0; + //bool uwriteable= (a&0x08000000)!=0; + bool uwriteable= (a&0x00800000)!=0; // ***hd*** + //bool ureadable= (a&0x01000000)!=0; + //bool uexecutable=(a&0x00400000)!=0; + bool wreadonly= (a&0x00000001)!=0; + bool whidden= (a&0x00000002)!=0; + bool wsystem= (a&0x00000004)!=0; + bool wisdir= (a&0x00000010)!=0; + bool warchive= (a&0x00000020)!=0; + ze->attr=FILE_ATTRIBUTE_NORMAL; + if (uisdir || wisdir) ze->attr |= FILE_ATTRIBUTE_DIRECTORY; + if (warchive) ze->attr|=FILE_ATTRIBUTE_ARCHIVE; + if (whidden) ze->attr|=FILE_ATTRIBUTE_HIDDEN; + if (!uwriteable||wreadonly) ze->attr|=FILE_ATTRIBUTE_READONLY; + if (wsystem) ze->attr|=FILE_ATTRIBUTE_SYSTEM; + ze->comp_size = ufi.compressed_size; + ze->unc_size = ufi.uncompressed_size; + // + WORD dostime = (WORD)(ufi.dosDate&0xFFFF); + WORD dosdate = (WORD)((ufi.dosDate>>16)&0xFFFF); + FILETIME ft; + DosDateTimeToFileTime(dosdate,dostime,&ft); + ze->atime=ft; ze->ctime=ft; ze->mtime=ft; + // the zip will always have at least that dostime. But if it also has + // an extra header, then we'll instead get the info from that. + unsigned int epos=0; + while (epos+4mtime = timet2filetime(mtime); + } + if (hasatime) + { time_t atime = *(time_t*)(extra+epos); epos+=4; + ze->atime = timet2filetime(atime); + } + if (hasctime) + { time_t ctime = *(time_t*)(extra+epos); + ze->ctime = timet2filetime(ctime); + } + break; + } + // + if (extra!=0) delete[] extra; + memcpy(&cze,ze,sizeof(ZIPENTRY)); czei=index; + return ZR_OK; +} + +ZRESULT TUnzip::Find(const TCHAR *name, bool ic, int *index, ZIPENTRY *ze) +{ + int res = unzLocateFile(uf,name,ic?CASE_INSENSITIVE:CASE_SENSITIVE); + if (res!=UNZ_OK) + { + if (index!=0) + *index=-1; + if (ze!=NULL) + { + ZeroMemory(ze,sizeof(ZIPENTRY)); ze->index=-1; + } + return ZR_NOTFOUND; + } + if (currentfile!=-1) + unzCloseCurrentFile(uf); currentfile=-1; + int i = (int)uf->num_file; + if (index!=NULL) + *index=i; + if (ze!=NULL) + { + ZRESULT zres = Get(i,ze); + if (zres!=ZR_OK) + return zres; + } + return ZR_OK; +} + +void EnsureDirectory(const TCHAR *rootdir, const TCHAR *dir) +{ + if (dir==NULL || dir[0] == _T('\0')) + return; + const TCHAR *lastslash = dir, *c = lastslash; + while (*c != _T('\0')) + { + if (*c==_T('/') || *c==_T('\\')) + lastslash=c; + c++; + } + const TCHAR *name=lastslash; + if (lastslash!=dir) + { + TCHAR tmp[MAX_PATH]; + _tcsncpy(tmp, dir, lastslash-dir); + tmp[lastslash-dir] = _T('\0'); + EnsureDirectory(rootdir,tmp); + name++; + } + TCHAR cd[MAX_PATH]; + _tcscpy(cd,rootdir); + //_tcscat(cd,name); + _tcscat(cd,dir); //+++1.2 + CreateDirectory(cd,NULL); +} + +ZRESULT TUnzip::Unzip(int index,void *dst,unsigned int len,DWORD flags) +{ + if (flags!=ZIP_MEMORY && flags!=ZIP_FILENAME && flags!=ZIP_HANDLE) + return ZR_ARGS; + if (flags==ZIP_MEMORY) + { + if (index!=currentfile) + { + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index>=(int)uf->gi.number_entry) + return ZR_ARGS; + if (index<(int)uf->num_file) + unzGoToFirstFile(uf); + while ((int)uf->num_file0) + return ZR_MORE; + unzCloseCurrentFile(uf); + currentfile=-1; + if (res==0) + return ZR_OK; + else + return ZR_FLATE; + } + + // otherwise we're writing to a handle or a file + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index >= (int)uf->gi.number_entry) + return ZR_ARGS; + if (index < (int)uf->num_file) + unzGoToFirstFile(uf); + while ((int)uf->num_filelen) n=len-1; + strncpy(buf,msg,n); buf[n]=0; + return mlen; +} + + +typedef struct +{ DWORD flag; + TUnzip *unz; +} TUnzipHandleData; + +HZIP OpenZipU(void *rd, void *z,unsigned int len,DWORD flags) +{ + TUnzip *unz = new TUnzip(); + lasterrorU = unz->Open(rd, z,len,flags); + if (lasterrorU!=ZR_OK) + { + delete unz; + return 0; + } + TUnzipHandleData *han = new TUnzipHandleData; + han->flag=1; + han->unz=unz; + return (HZIP)han; +} + +ZRESULT GetZipItemA(HZIP hz, int index, ZIPENTRY *ze) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Get(index,ze); + return lasterrorU; +} + +ZRESULT GetZipItemW(HZIP hz, int index, ZIPENTRYW *zew) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + ZIPENTRY ze; + lasterrorU = unz->Get(index,&ze); + if (lasterrorU == ZR_OK) + { + zew->index = ze.index; + zew->attr = ze.attr; + zew->atime = ze.atime; + zew->ctime = ze.ctime; + zew->mtime = ze.mtime; + zew->comp_size = ze.comp_size; + zew->unc_size = ze.unc_size; +#ifdef _UNICODE + GetUnicodeFileName(ze.name, zew->name, MAX_PATH-1); +#else + strcpy(zew->name, ze.name); +#endif + } + return lasterrorU; +} + +ZRESULT FindZipItemA(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Find(name,ic,index,ze); + return lasterrorU; +} + +ZRESULT FindZipItemW(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRYW *zew) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + ZIPENTRY ze; + lasterrorU = unz->Find(name,ic,index,&ze); + if (lasterrorU == ZR_OK) + { + zew->index = ze.index; + zew->attr = ze.attr; + zew->atime = ze.atime; + zew->ctime = ze.ctime; + zew->mtime = ze.mtime; + zew->comp_size = ze.comp_size; + zew->unc_size = ze.unc_size; +#ifdef _UNICODE + GetUnicodeFileName(ze.name, zew->name, MAX_PATH-1); +#else + strcpy(zew->name, ze.name); +#endif + } + + return lasterrorU; +} + +ZRESULT UnzipItem(HZIP hz, int index, void *dst, unsigned int len, DWORD flags) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Unzip(index,dst,len,flags); + return lasterrorU; +} + +ZRESULT CloseZipU(HZIP hz) +{ if (hz==0) {lasterrorU=ZR_ARGS;return ZR_ARGS;} + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) {lasterrorU=ZR_ZMODE;return ZR_ZMODE;} + TUnzip *unz = han->unz; + lasterrorU = unz->Close(); + delete unz; + delete han; + return lasterrorU; +} + +bool IsZipHandleU(HZIP hz) +{ if (hz==0) return true; + TUnzipHandleData *han = (TUnzipHandleData*)hz; + return (han->flag==1); +} + + diff --git a/iis/ModSecurityIIS/Installer/XUnzip.h b/iis/ModSecurityIIS/Installer/XUnzip.h new file mode 100644 index 0000000000..baf5020c63 --- /dev/null +++ b/iis/ModSecurityIIS/Installer/XUnzip.h @@ -0,0 +1,382 @@ +// XUnzip.h Version 1.3 +// +// Authors: Mark Adler et al. (see below) +// +// Modified by: Lucian Wischik +// lu@wischik.com +// +// Version 1.0 - Turned C files into just a single CPP file +// - Made them compile cleanly as C++ files +// - Gave them simpler APIs +// - Added the ability to zip/unzip directly in memory without +// any intermediate files +// +// Modified by: Hans Dietrich +// hdietrich@gmail.com +// +/////////////////////////////////////////////////////////////////////////////// +// +// Lucian Wischik's comments: +// -------------------------- +// THIS FILE is almost entirely based upon code by info-zip. +// It has been modified by Lucian Wischik. +// The original code may be found at http://www.info-zip.org +// The original copyright text follows. +// +/////////////////////////////////////////////////////////////////////////////// +// +// Original authors' comments: +// --------------------------- +// This is version 2002-Feb-16 of the Info-ZIP copyright and license. The +// definitive version of this document should be available at +// ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely. +// +// Copyright (c) 1990-2002 Info-ZIP. All rights reserved. +// +// For the purposes of this copyright and license, "Info-ZIP" is defined as +// the following set of individuals: +// +// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, +// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase, +// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, +// David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, +// Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, +// Kai Uwe Rommel, Steve Salisbury, Dave Smith, Christian Spieler, +// Antoine Verheijen, Paul von Behren, Rich Wales, Mike White +// +// This software is provided "as is", without warranty of any kind, express +// or implied. In no event shall Info-ZIP or its contributors be held liable +// for any direct, indirect, incidental, special or consequential damages +// arising out of the use of or inability to use this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. Redistributions of source code must retain the above copyright notice, +// definition, disclaimer, and this list of conditions. +// +// 2. Redistributions in binary form (compiled executables) must reproduce +// the above copyright notice, definition, disclaimer, and this list of +// conditions in documentation and/or other materials provided with the +// distribution. The sole exception to this condition is redistribution +// of a standard UnZipSFX binary as part of a self-extracting archive; +// that is permitted without inclusion of this license, as long as the +// normal UnZipSFX banner has not been removed from the binary or disabled. +// +// 3. Altered versions--including, but not limited to, ports to new +// operating systems, existing ports with new graphical interfaces, and +// dynamic, shared, or static library versions--must be plainly marked +// as such and must not be misrepresented as being the original source. +// Such altered versions also must not be misrepresented as being +// Info-ZIP releases--including, but not limited to, labeling of the +// altered versions with the names "Info-ZIP" (or any variation thereof, +// including, but not limited to, different capitalizations), +// "Pocket UnZip", "WiZ" or "MacZip" without the explicit permission of +// Info-ZIP. Such altered versions are further prohibited from +// misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or +// of the Info-ZIP URL(s). +// +// 4. Info-ZIP retains the right to use the names "Info-ZIP", "Zip", "UnZip", +// "UnZipSFX", "WiZ", "Pocket UnZip", "Pocket Zip", and "MacZip" for its +// own source and binary releases. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef XUNZIP_H +#define XUNZIP_H + + +#ifndef XZIP_H +DECLARE_HANDLE(HZIP); // An HZIP identifies a zip file that has been opened +#endif + +typedef DWORD ZRESULT; +// return codes from any of the zip functions. Listed later. + +#define ZIP_HANDLE 1 +#define ZIP_FILENAME 2 +#define ZIP_MEMORY 3 + +typedef struct +{ int index; // index of this file within the zip + char name[MAX_PATH]; // filename within the zip + DWORD attr; // attributes, as in GetFileAttributes. + FILETIME atime,ctime,mtime;// access, create, modify filetimes + long comp_size; // sizes of item, compressed and uncompressed. These + long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRY; + +typedef struct +{ int index; // index of this file within the zip + TCHAR name[MAX_PATH]; // filename within the zip + DWORD attr; // attributes, as in GetFileAttributes. + FILETIME atime,ctime,mtime;// access, create, modify filetimes + long comp_size; // sizes of item, compressed and uncompressed. These + long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRYW; + + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenZip() +// +// Purpose: Open an existing zip archive file +// +// Parameters: z - archive file name if flags is ZIP_FILENAME; for other +// uses see below +// len - for memory (ZIP_MEMORY) should be the buffer size; +// for other uses, should be 0 +// flags - indicates usage, see below; for files, this will be +// ZIP_FILENAME +// +// Returns: HZIP - non-zero if zip archive opened ok, otherwise 0 +// +HZIP OpenZip(void *rd, void *z, unsigned int len, DWORD flags); +// OpenZip - opens a zip file and returns a handle with which you can +// subsequently examine its contents. You can open a zip file from: +// from a pipe: OpenZip(hpipe_read,0, ZIP_HANDLE); +// from a file (by handle): OpenZip(hfile,0, ZIP_HANDLE); +// from a file (by name): OpenZip("c:\\test.zip",0, ZIP_FILENAME); +// from a memory block: OpenZip(bufstart, buflen, ZIP_MEMORY); +// If the file is opened through a pipe, then items may only be +// accessed in increasing order, and an item may only be unzipped once, +// although GetZipItem can be called immediately before and after unzipping +// it. If it's opened i n any other way, then full random access is possible. +// Note: pipe input is not yet implemented. + + +/////////////////////////////////////////////////////////////////////////////// +// +// GetZipItem() +// +// Purpose: Get information about an item in an open zip archive +// +// Parameters: hz - handle of open zip archive +// index - index number (0 based) of item in zip +// ze - pointer to a ZIPENTRY (if ANSI) or ZIPENTRYW struct +// (if Unicode) +// +// Returns: ZRESULT - ZR_OK if success, otherwise some other value +// + +#ifdef _UNICODE +#define GetZipItem GetZipItemW +#else +#define GetZipItem GetZipItemA +#endif + +ZRESULT GetZipItemA(HZIP hz, int index, ZIPENTRY *ze); +ZRESULT GetZipItemW(HZIP hz, int index, ZIPENTRYW *ze); +// GetZipItem - call this to get information about an item in the zip. +// If index is -1 and the file wasn't opened through a pipe, +// then it returns information about the whole zipfile +// (and in particular ze.index returns the number of index items). +// Note: the item might be a directory (ze.attr & FILE_ATTRIBUTE_DIRECTORY) +// See below for notes on what happens when you unzip such an item. +// Note: if you are opening the zip through a pipe, then random access +// is not possible and GetZipItem(-1) fails and you can't discover the number +// of items except by calling GetZipItem on each one of them in turn, +// starting at 0, until eventually the call fails. Also, in the event that +// you are opening through a pipe and the zip was itself created into a pipe, +// then then comp_size and sometimes unc_size as well may not be known until +// after the item has been unzipped. + + +/////////////////////////////////////////////////////////////////////////////// +// +// FindZipItem() +// +// Purpose: Find item by name and return information about it +// +// Parameters: hz - handle of open zip archive +// name - name of file to look for inside zip archive +// ic - TRUE = case insensitive +// index - pointer to index number returned, or -1 +// ze - pointer to a ZIPENTRY (if ANSI) or ZIPENTRYW struct +// (if Unicode) +// +// Returns: ZRESULT - ZR_OK if success, otherwise some other value +// + +#ifdef _UNICODE +#define FindZipItem FindZipItemW +#else +#define FindZipItem FindZipItemA +#endif + +ZRESULT FindZipItemA(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze); +ZRESULT FindZipItemW(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRYW *ze); +// FindZipItem - finds an item by name. ic means 'insensitive to case'. +// It returns the index of the item, and returns information about it. +// If nothing was found, then index is set to -1 and the function returns +// an error code. + + +/////////////////////////////////////////////////////////////////////////////// +// +// UnzipItem() +// +// Purpose: Find item by index and unzip it +// +// Parameters: hz - handle of open zip archive +// index - index number of file to unzip +// dst - target file name of unzipped file +// len - for memory (ZIP_MEMORY. length of buffer; +// otherwise 0 +// flags - indicates usage, see below; for files, this will be +// ZIP_FILENAME +// +// Returns: ZRESULT - ZR_OK if success, otherwise some other value +// + +ZRESULT UnzipItem(HZIP hz, int index, void *dst, unsigned int len, DWORD flags); +// UnzipItem - given an index to an item, unzips it. You can unzip to: +// to a pipe: UnzipItem(hz,i, hpipe_write,0,ZIP_HANDLE); +// to a file (by handle): UnzipItem(hz,i, hfile,0,ZIP_HANDLE); +// to a file (by name): UnzipItem(hz,i, ze.name,0,ZIP_FILENAME); +// to a memory block: UnzipItem(hz,i, buf,buflen,ZIP_MEMORY); +// In the final case, if the buffer isn't large enough to hold it all, +// then the return code indicates that more is yet to come. If it was +// large enough, and you want to know precisely how big, GetZipItem. +// Note: zip files are normally stored with relative pathnames. If you +// unzip with ZIP_FILENAME a relative pathname then the item gets created +// relative to the current directory - it first ensures that all necessary +// subdirectories have been created. Also, the item may itself be a directory. +// If you unzip a directory with ZIP_FILENAME, then the directory gets created. +// If you unzip it to a handle or a memory block, then nothing gets created +// and it emits 0 bytes. + + +/////////////////////////////////////////////////////////////////////////////// +// +// CloseZip() +// +// Purpose: Close an open zip archive +// +// Parameters: hz - handle to an open zip archive +// +// Returns: ZRESULT - ZR_OK if success, otherwise some other value +// +ZRESULT CloseZip(HZIP hz); +// CloseZip - the zip handle must be closed with this function. + +unsigned int FormatZipMessage(ZRESULT code, char *buf,unsigned int len); +// FormatZipMessage - given an error code, formats it as a string. +// It returns the length of the error message. If buf/len points +// to a real buffer, then it also writes as much as possible into there. + + +// These are the result codes: +#define ZR_OK 0x00000000 // nb. the pseudo-code zr-recent is never returned, +#define ZR_RECENT 0x00000001 // but can be passed to FormatZipMessage. +// The following come from general system stuff (e.g. files not openable) +#define ZR_GENMASK 0x0000FF00 +#define ZR_NODUPH 0x00000100 // couldn't duplicate the handle +#define ZR_NOFILE 0x00000200 // couldn't create/open the file +#define ZR_NOALLOC 0x00000300 // failed to allocate some resource +#define ZR_WRITE 0x00000400 // a general error writing to the file +#define ZR_NOTFOUND 0x00000500 // couldn't find that file in the zip +#define ZR_MORE 0x00000600 // there's still more data to be unzipped +#define ZR_CORRUPT 0x00000700 // the zipfile is corrupt or not a zipfile +#define ZR_READ 0x00000800 // a general error reading the file +// The following come from mistakes on the part of the caller +#define ZR_CALLERMASK 0x00FF0000 +#define ZR_ARGS 0x00010000 // general mistake with the arguments +#define ZR_NOTMMAP 0x00020000 // tried to ZipGetMemory, but that only works on mmap zipfiles, which yours wasn't +#define ZR_MEMSIZE 0x00030000 // the memory size is too small +#define ZR_FAILED 0x00040000 // the thing was already failed when you called this function +#define ZR_ENDED 0x00050000 // the zip creation has already been closed +#define ZR_MISSIZE 0x00060000 // the indicated input file size turned out mistaken +#define ZR_PARTIALUNZ 0x00070000 // the file had already been partially unzipped +#define ZR_ZMODE 0x00080000 // tried to mix creating/opening a zip +// The following come from bugs within the zip library itself +#define ZR_BUGMASK 0xFF000000 +#define ZR_NOTINITED 0x01000000 // initialisation didn't work +#define ZR_SEEK 0x02000000 // trying to seek in an unseekable file +#define ZR_NOCHANGE 0x04000000 // changed its mind on storage, but not allowed +#define ZR_FLATE 0x05000000 // an internal error in the de/inflation code + + + + + +// e.g. +// +// SetCurrentDirectory("c:\\docs\\stuff"); +// HZIP hz = OpenZip("c:\\stuff.zip",0,ZIP_FILENAME); +// ZIPENTRY ze; GetZipItem(hz,-1,&ze); int numitems=ze.index; +// for (int i=0; i +#include + + + +// TODO: reference additional headers your program requires here diff --git a/iis/ModSecurityIIS/Installer/targetver.h b/iis/ModSecurityIIS/Installer/targetver.h new file mode 100644 index 0000000000..90e767bfce --- /dev/null +++ b/iis/ModSecurityIIS/Installer/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/iis/ModSecurityIIS/ModSecurityIIS.sln b/iis/ModSecurityIIS/ModSecurityIIS.sln index 35ead3fc18..e7961d5dab 100644 --- a/iis/ModSecurityIIS/ModSecurityIIS.sln +++ b/iis/ModSecurityIIS/ModSecurityIIS.sln @@ -3,35 +3,51 @@ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "ModSecurityIIS", "ModSecurityIIS\ModSecurityIIS.vdproj", "{3352AEF1-9F2A-47CD-9F63-658553063040}" ProjectSection(ProjectDependencies) = postProject - {023E10BD-4FF6-4401-9A40-AED9717073F2} = {023E10BD-4FF6-4401-9A40-AED9717073F2} + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C} = {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C} EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "configure", "ModSecurityIIS\installer project\configure.csproj", "{023E10BD-4FF6-4401-9A40-AED9717073F2}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Installer", "Installer\Installer.vcxproj", "{990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 + Release|Mixed Platforms = Release|Mixed Platforms + Release|Win32 = Release|Win32 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3352AEF1-9F2A-47CD-9F63-658553063040}.Debug|Mixed Platforms.ActiveCfg = Debug + {3352AEF1-9F2A-47CD-9F63-658553063040}.Debug|Mixed Platforms.Build.0 = Debug + {3352AEF1-9F2A-47CD-9F63-658553063040}.Debug|Win32.ActiveCfg = Debug + {3352AEF1-9F2A-47CD-9F63-658553063040}.Debug|Win32.Build.0 = Debug {3352AEF1-9F2A-47CD-9F63-658553063040}.Debug|x64.ActiveCfg = Debug {3352AEF1-9F2A-47CD-9F63-658553063040}.Debug|x64.Build.0 = Debug {3352AEF1-9F2A-47CD-9F63-658553063040}.Debug|x86.ActiveCfg = Debug {3352AEF1-9F2A-47CD-9F63-658553063040}.Debug|x86.Build.0 = Debug + {3352AEF1-9F2A-47CD-9F63-658553063040}.Release|Mixed Platforms.ActiveCfg = Release + {3352AEF1-9F2A-47CD-9F63-658553063040}.Release|Mixed Platforms.Build.0 = Release + {3352AEF1-9F2A-47CD-9F63-658553063040}.Release|Win32.ActiveCfg = Release + {3352AEF1-9F2A-47CD-9F63-658553063040}.Release|Win32.Build.0 = Release {3352AEF1-9F2A-47CD-9F63-658553063040}.Release|x64.ActiveCfg = Release {3352AEF1-9F2A-47CD-9F63-658553063040}.Release|x64.Build.0 = Release {3352AEF1-9F2A-47CD-9F63-658553063040}.Release|x86.ActiveCfg = Release {3352AEF1-9F2A-47CD-9F63-658553063040}.Release|x86.Build.0 = Release - {023E10BD-4FF6-4401-9A40-AED9717073F2}.Debug|x64.ActiveCfg = Debug|x64 - {023E10BD-4FF6-4401-9A40-AED9717073F2}.Debug|x64.Build.0 = Debug|x64 - {023E10BD-4FF6-4401-9A40-AED9717073F2}.Debug|x86.ActiveCfg = Debug|x86 - {023E10BD-4FF6-4401-9A40-AED9717073F2}.Debug|x86.Build.0 = Debug|x86 - {023E10BD-4FF6-4401-9A40-AED9717073F2}.Release|x64.ActiveCfg = Release|x86 - {023E10BD-4FF6-4401-9A40-AED9717073F2}.Release|x64.Build.0 = Release|x86 - {023E10BD-4FF6-4401-9A40-AED9717073F2}.Release|x86.ActiveCfg = Release|x86 - {023E10BD-4FF6-4401-9A40-AED9717073F2}.Release|x86.Build.0 = Release|x86 + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}.Debug|Win32.ActiveCfg = Debug|Win32 + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}.Debug|Win32.Build.0 = Debug|Win32 + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}.Debug|x64.ActiveCfg = Debug|Win32 + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}.Debug|x86.ActiveCfg = Debug|Win32 + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}.Release|Mixed Platforms.Build.0 = Release|Win32 + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}.Release|Win32.ActiveCfg = Release|Win32 + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}.Release|Win32.Build.0 = Release|Win32 + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}.Release|x64.ActiveCfg = Release|Win32 + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}.Release|x86.ActiveCfg = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/iis/ModSecurityIIS/ModSecurityIIS/readme.rtf b/iis/ModSecurityIIS/ModSecurityIIS/readme.rtf index c2d06d3ff3..39ca023452 100644 --- a/iis/ModSecurityIIS/ModSecurityIIS/readme.rtf +++ b/iis/ModSecurityIIS/ModSecurityIIS/readme.rtf @@ -58,10 +58,10 @@ \leveltemplateid1225817962\'01\'95;}{\levelnumbers;}\f1\fbias0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext \leveltemplateid1897401684\'01\'95;}{\levelnumbers;}\f1\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid652101753}}{\*\listoverridetable{\listoverride\listid625282538\listoverridecount0\ls1}{\listoverride\listid652101753 \listoverridecount0\ls2}}{\*\pgptbl {\pgp\ipgp10\itap0\li720\ri0\sb0\sa240}{\pgp\ipgp10\itap0\li720\ri0\sb0\sa240}{\pgp\ipgp10\itap0\li720\ri0\sb0\sa240}{\pgp\ipgp10\itap0\li720\ri0\sb0\sa240}{\pgp\ipgp6\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp8\itap0\li0\ri0\sb0 -\sa0}{\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp7\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp10\itap0\li720\ri0\sb0\sa240}{\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}}{\*\rsidtbl \rsid1197496\rsid2315486\rsid3168183\rsid4593034\rsid6385683\rsid6967166\rsid9512544 -\rsid10098429\rsid12742482\rsid13639667\rsid16350125\rsid16593896}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\author Suha Can}{\operator Greg} -{\creatim\yr2012\mo6\dy4\hr11\min43}{\revtim\yr2012\mo7\dy12\hr17\min47}{\version7}{\edmins6}{\nofpages1}{\nofwords134}{\nofchars767}{\*\company Microsoft Corporation}{\nofcharsws900}{\vern49273}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/w -ord/2003/wordml}}\paperw12240\paperh15840\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect +\sa0}{\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp7\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp10\itap0\li720\ri0\sb0\sa240}{\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}}{\*\rsidtbl \rsid1197496\rsid2315486\rsid3168183\rsid4593034\rsid6385683\rsid6633290\rsid6754893\rsid6967166 +\rsid9512544\rsid10098429\rsid12742482\rsid13639667\rsid16350125\rsid16593896}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\author Suha Can}{\operator Greg} +{\creatim\yr2012\mo6\dy4\hr11\min43}{\revtim\yr2013\mo1\dy17\hr16\min15}{\version9}{\edmins12}{\nofpages1}{\nofwords173}{\nofchars992}{\*\company Microsoft Corporation}{\nofcharsws1163}{\vern49275}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office +/word/2003/wordml}}\paperw12240\paperh15840\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect \widowctrl\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont1\relyonvml0\donotembedlingdata0\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors1\noxlattoyen \expshrtn\noultrlspc\dntblnsbdb\nospaceforul\formshade\horzdoc\dgmargin\dghspace180\dgvspace180\dghorigin1440\dgvorigin1440\dghshow1\dgvshow1 \jexpand\viewkind1\viewscale100\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct @@ -75,38 +75,44 @@ ord/2003/wordml}}\paperw12240\paperh15840\margl1440\margr1440\margt1440\margb144 \par }\pard\plain \ltrpar\ql \li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid16350125 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 { \rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6385683\charrsid16350125 ModSecurity home page }{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid2315486 HYPERLINK "http://www.modsecurity.org"}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid2315486\charrsid16350125 {\*\datafield -00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b5000000068007400740070003a002f002f007700770077002e006d006f006400730065006300750072006900740079002e006f00720067002f000000795881f43b1d7f48af2c825dc485276300000000a5ab0000}} -}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\insrsid6385683\charrsid16350125 http://www.modsecurity.org}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0\afs24 +00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b5000000068007400740070003a002f002f007700770077002e006d006f006400730065006300750072006900740079002e006f00720067002f000000795881f43b1d7f48af2c825dc485276300000000a5ab00000000} +}}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\insrsid6385683\charrsid16350125 http://www.modsecurity.org}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6385683\charrsid16350125 HYPERLINK "http://engineering/" }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid2315486\charrsid16350125 {\*\datafield -00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b4000000068007400740070003a002f002f0065006e00670069006e0065006500720069006e0067002f000000795881f43b1d7f48af2c825dc485276300000000a5ab0000}}}{\fldrslt {\rtlch\fcs1 \af0\afs24 -\ltrch\fcs0 \cs16\f0\fs24\ul\cf2\insrsid6385683\charrsid16350125 /}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6385683\charrsid16350125 +00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b4000000068007400740070003a002f002f0065006e00670069006e0065006500720069006e0067002f000000795881f43b1d7f48af2c825dc485276300000000a5ab00000000}}}{\fldrslt {\rtlch\fcs1 +\af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\insrsid6385683\charrsid16350125 /}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6385683\charrsid16350125 \par OWASP Core Rule Set for ModSecurity: }{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6385683\charrsid16350125 HYPERLINK "https://www.owasp.org/index.php/Category:OWASP_ModSecurity_Core_Rule_Set_Project" }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid2315486\charrsid16350125 {\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90bba000000680074007400700073003a002f002f007700770077002e006f0077006100730070002e006f00720067002f0069006e006400650078002e007000680070002f00430061007400650067006f00720079003a00 -4f0057004100530050005f004d006f006400530065006300750072006900740079005f0043006f00720065005f00520075006c0065005f005300650074005f00500072006f006a006500630074000000795881f43b1d7f48af2c825dc485276300000000a5ab0000}}}{\fldrslt {\rtlch\fcs1 \af0\afs24 +4f0057004100530050005f004d006f006400530065006300750072006900740079005f0043006f00720065005f00520075006c0065005f005300650074005f00500072006f006a006500630074000000795881f43b1d7f48af2c825dc485276300000000a5ab00000016}}}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\insrsid6385683\charrsid16350125 https://}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid2315486 HYPERLINK "https://www.owasp.org/index.php/Category:OWASP_ModSecurity_Core_Rule_Set_Project"}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid2315486\charrsid16350125 {\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90bba000000680074007400700073003a002f002f007700770077002e006f0077006100730070002e006f00720067002f0069006e006400650078002e007000680070002f00430061007400650067006f00720079003a00 -4f0057004100530050005f004d006f006400530065006300750072006900740079005f0043006f00720065005f00520075006c0065005f005300650074005f00500072006f006a006500630074000000795881f43b1d7f48af2c825dc485276300000000a5ab0000}}}{\fldrslt {\rtlch\fcs1 \af0\afs24 +4f0057004100530050005f004d006f006400530065006300750072006900740079005f0043006f00720065005f00520075006c0065005f005300650074005f00500072006f006a006500630074000000795881f43b1d7f48af2c825dc485276300000000a5ab00000000}}}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\insrsid6385683\charrsid16350125 www.owasp.org/index.php/Category:OWASP_ModSecurity_Core_Rule_Set_Project}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6385683\charrsid16350125 +\par }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6633290 The OWASP CRS was installed on your system drive, under }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid6633290\charrsid6633290 inetpub\\wwwroot\\owasp_crs}{\rtlch\fcs1 \af0\afs24 +\ltrch\fcs0 \f0\fs24\insrsid6633290 +\par You can include it in your website by adding to your }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid6633290\charrsid6633290 web.config}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6633290 file, in }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 +\b\f0\fs24\insrsid6633290\charrsid6633290 system.webServer}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6633290 section: +\par }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6633290\charrsid6633290 \par }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\lang1045\langfe1033\langnp1045\insrsid6385683\charrsid16350125 MSRC blog }{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\lang1045\langfe1033\langnp1045\insrsid6385683\charrsid16350125 HYPERLINK "http://blogs.technet.com/b/srd/" }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\lang1045\langfe1033\langnp1045\insrsid2315486\charrsid16350125 {\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b5800000068007400740070003a002f002f0062006c006f00670073002e0074006500630068006e00650074002e0063006f006d002f0062002f007300720064002f000000795881f43b1d7f48af2c825dc48527630000 -0000a5ab0000}}}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\lang1045\langfe1033\langnp1045\insrsid6385683\charrsid16350125 http://blogs.technet.com/b/srd/}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj { -\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\lang1045\langfe1033\langnp1045\insrsid6385683\charrsid16350125 +0000a5ab00000000}}}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\lang1045\langfe1033\langnp1045\insrsid6385683\charrsid16350125 http://blogs.technet.com/b/srd/}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj { +\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\lang1045\langfe1033\langnp1045\insrsid6385683\charrsid16350125 }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\lang1045\langfe1033\langnp1045\insrsid6385683\charrsid6633290 \par }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6385683\charrsid16350125 Trustwave SpiderLabs blog: }{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid2315486 HYPERLINK "http://blog.spiderlabs.com/"}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid2315486\charrsid16350125 {\*\datafield -00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b5000000068007400740070003a002f002f0062006c006f0067002e007300700069006400650072006c006100620073002e0063006f006d002f000000795881f43b1d7f48af2c825dc485276300000000a5ab0000}} -}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\insrsid6385683\charrsid16350125 http://blog.spiderlabs.com}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0\afs24 +00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b5000000068007400740070003a002f002f0062006c006f0067002e007300700069006400650072006c006100620073002e0063006f006d002f000000795881f43b1d7f48af2c825dc485276300000000a5ab00000000} +}}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\insrsid6385683\charrsid16350125 http://blog.spiderlabs.com}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6385683\charrsid16350125 HYPERLINK "http://blog.spiderlabs.com/" }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid2315486\charrsid16350125 {\*\datafield -00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b5000000068007400740070003a002f002f0062006c006f0067002e007300700069006400650072006c006100620073002e0063006f006d002f000000795881f43b1d7f48af2c825dc485276300000000a5ab0000}} -}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\insrsid6385683\charrsid16350125 /}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6385683\charrsid16350125 +00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b5000000068007400740070003a002f002f0062006c006f0067002e007300700069006400650072006c006100620073002e0063006f006d002f000000795881f43b1d7f48af2c825dc485276300000000a5ab00000000} +}}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\insrsid6385683\charrsid16350125 /}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6385683\charrsid16350125 \par Trustwave Commercial Rule Set for ModSecurity: }{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid2315486 HYPERLINK "https://www.trustwave.com/modsecurity-rules-support.php"}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid2315486\charrsid16350125 {\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b88000000680074007400700073003a002f002f007700770077002e007400720075007300740077006100760065002e0063006f006d002f006d006f006400730065006300750072006900740079002d00720075006c00 -650073002d0073007500700070006f00720074002e007000680070000000795881f43b1d7f48af2c825dc485276300000000a5ab0000}}}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\insrsid6385683\charrsid16350125 +650073002d0073007500700070006f00720074002e007000680070000000795881f43b1d7f48af2c825dc485276300000000a5ab00000000}}}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\insrsid6385683\charrsid16350125 https://www.trustwave.com/modsecurity-rules-support.php}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6385683\charrsid16350125 \par }\pard \ltrpar\ql \li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid6967166 \par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a @@ -120,43 +126,43 @@ c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725 a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865 6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b 4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b -4757e8d3f729e245eb2b260a0238fd010000ffff0300504b03041400060008000000210030dd4329a8060000a41b0000160000007468656d652f7468656d652f -7468656d65312e786d6cec594f6fdb3614bf0fd87720746f6327761a07758ad8b19b2d4d1bc46e871e698996d850a240d2497d1bdae38001c3ba618715d86d87 -615b8116d8a5fb34d93a6c1dd0afb0475292c5585e9236d88aad3e2412f9e3fbff1e1fa9abd7eec70c1d1221294fda5efd72cd4324f1794093b0eddd1ef62fad -79482a9c0498f184b4bd2991deb58df7dfbb8ad755446282607d22d771db8b944ad79796a40fc3585ee62949606ecc458c15bc8a702910f808e8c66c69b9565b -5d8a314d3c94e018c8de1a8fa94fd05093f43672e23d06af89927ac06762a049136785c10607758d9053d965021d62d6f6804fc08f86e4bef210c352c144dbab -999fb7b4717509af678b985ab0b6b4ae6f7ed9ba6c4170b06c788a705430adf71bad2b5b057d03606a1ed7ebf5babd7a41cf00b0ef83a6569632cd467faddec9 -699640f6719e76b7d6ac355c7c89feca9cccad4ea7d36c65b258a206641f1b73f8b5da6a6373d9c11b90c537e7f08dce66b7bbeae00dc8e257e7f0fd2badd586 -8b37a088d1e4600ead1ddaef67d40bc898b3ed4af81ac0d76a197c86826828a24bb318f3442d8ab518dfe3a20f000d6458d104a9694ac6d88728eee2782428d6 -0cf03ac1a5193be4cbb921cd0b495fd054b5bd0f530c1931a3f7eaf9f7af9e3f45c70f9e1d3ff8e9f8e1c3e3073f5a42ceaa6d9c84e5552fbffdeccfc71fa33f -9e7ef3f2d117d57859c6fffac327bffcfc793510d26726ce8b2f9ffcf6ecc98baf3efdfdbb4715f04d814765f890c644a29be408edf3181433567125272371be -15c308d3f28acd249438c19a4b05fd9e8a1cf4cd296699771c393ac4b5e01d01e5a30a787d72cf1178108989a2159c77a2d801ee72ce3a5c545a6147f32a9979 -3849c26ae66252c6ed637c58c5bb8b13c7bfbd490a75330f4b47f16e441c31f7184e140e494214d273fc80900aedee52ead87597fa824b3e56e82e451d4c2b4d -32a423279a668bb6690c7e9956e90cfe766cb37b077538abd27a8b1cba48c80acc2a841f12e698f13a9e281c57911ce298950d7e03aba84ac8c154f8655c4f2a -f074481847bd804859b5e696007d4b4edfc150b12addbecba6b18b148a1e54d1bc81392f23b7f84137c2715a851dd0242a633f900710a218ed715505dfe56e86 -e877f0034e16bafb0e258ebb4faf06b769e888340b103d331115bebc4eb813bf83291b63624a0d1475a756c734f9bbc2cd28546ecbe1e20a3794ca175f3fae90 -fb6d2dd99bb07b55e5ccf68942bd0877b23c77b908e8db5f9db7f024d9239010f35bd4bbe2fcae387bfff9e2bc289f2fbe24cfaa301468dd8bd846dbb4ddf1c2 -ae7b4c191ba8292337a469bc25ec3d411f06f53a73e224c5292c8de0516732307070a1c0660d125c7d44553488700a4d7bddd3444299910e254ab984c3a219ae -a4adf1d0f82b7bd46cea4388ad1c12ab5d1ed8e1153d9c9f350a3246aad01c6873462b9ac05999ad5cc988826eafc3acae853a33b7ba11cd1445875ba1b236b1 -399483c90bd560b0b0263435085a21b0f22a9cf9356b38ec6046026d77eba3dc2dc60b17e92219e180643ed27acffba86e9c94c7ca9c225a0f1b0cfae0788ad5 -4adc5a9aec1b703b8b93caec1a0bd8e5de7b132fe5113cf312503b998e2c2927274bd051db6b35979b1ef271daf6c6704e86c73805af4bdd476216c26593af84 -0dfb5393d964f9cc9bad5c313709ea70f561ed3ea7b053075221d51696910d0d339585004b34272bff7213cc7a510a5454a3b349b1b206c1f0af490176745d4b -c663e2abb2b34b23da76f6352ba57ca2881844c1111ab189d8c7e07e1daaa04f40255c77988aa05fe06e4e5bdb4cb9c5394bbaf28d98c1d971ccd20867e556a7 -689ec9166e0a522183792b8907ba55ca6e943bbf2a26e52f48957218ffcf54d1fb09dc3eac04da033e5c0d0b8c74a6b43d2e54c4a10aa511f5fb021a07533b20 -5ae07e17a621a8e082dafc17e450ffb739676998b48643a4daa7211214f623150942f6a02c99e83b85583ddbbb2c4996113211551257a656ec1139246ca86be0 -aadedb3d1441a89b6a929501833b197fee7b9641a3503739e57c732a59b1f7da1cf8a73b1f9bcca0945b874d4393dbbf10b1680f66bbaa5d6f96e77b6f59113d -316bb31a795600b3d256d0cad2fe354538e7566b2bd69cc6cbcd5c38f0e2bcc63058344429dc2121fd07f63f2a7c66bf76e80d75c8f7a1b622f878a18941d840 -545fb28d07d205d20e8ea071b283369834296bdaac75d256cb37eb0bee740bbe278cad253b8bbfcf69eca23973d939b97891c6ce2cecd8da8e2d343578f6648a -c2d0383fc818c798cf64e52f597c740f1cbd05df0c264c49134cf09d4a60e8a107260f20f92d47b374e32f000000ffff0300504b030414000600080000002100 -0dd1909fb60000001b010000270000007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f7 -8277086f6fd3ba109126dd88d0add40384e4350d363f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89 -d93b64b060828e6f37ed1567914b284d262452282e3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd500 -1996509affb3fd381a89672f1f165dfe514173d9850528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0f -bfff0000001c0200001300000000000000000000000000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6 -a7e7c0000000360100000b00000000000000000000000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a -0000001c00000000000000000000000000190200007468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d00140006000800000021 -0030dd4329a8060000a41b00001600000000000000000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d001400060008 -00000021000dd1909fb60000001b0100002700000000000000000000000000b20900007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000ad0a00000000} +4757e8d3f729e245eb2b260a0238fd010000ffff0300504b030414000600080000002100da7e1364a9060000a41b0000160000007468656d652f7468656d652f +7468656d65312e786d6cec594d6f1b4518be23f11f467b6f6327761a4775aad8b11b48d346b15bd4e37877bc3bcdecce6a669cd437d41e9190100571a012370e +08a8d44a5ccaaf09144191fa1778676677bd13af49d24650417b68bcb3cffbfd31efcc5ebd763f66e890084979d2f6ea976b1e2289cf039a846deff6b07f69cd +4352e124c08c27a4ed4d89f4ae6dbcffde55bcae22121304f4895cc76d2f522a5d5f5a923e2c637999a7248177632e62ace051844b81c047c037664bcbb5daea +528c69e2a104c7c0f6d6784c7d82869aa5b79133ef31784c94d40b3e1303cd9a3814061b1cd435424e659709748859db0339013f1a92fbca430c4b052fda5ead +d6efd76aded2c6d525bc9e1131b580d6a1abc1bf8c2e23080e968d4c118e0aa1f57ea37565abe06f004ccde37abd5eb7572ff81900f67db0d4ea52e6d9e8afd5 +3b39cf12c8fe9ce7ddad356b0d175fe2bf32a773abd3e9345b992e96a901d99f8d39fc5a6db5b1b9ece00dc8e29b73f84667b3db5d75f00664f1ab73f8fe95d6 +6ac3c51b50c468723087d671e9f733ee0564ccd976257c0de06b79206728c88622bbb488314fd4a25c8bf13d2efa00d04086154d909aa6648c7dc8e22e8e4782 +62ad0f5e27b8f4c62ef9726e49cb42d21734556defc3144345ccf8bd7afefdabe74fd1f18367c70f7e3a7ef8f0f8c18f969143b58d93b04cf5f2dbcffe7cfc31 +fae3e9372f1f7d518d9765fcaf3f7cf2cbcf9f5703a17c66eabcf8f2c96fcf9ebcf8ead3dfbf7b5401df147854860f694c24ba498ed03e8fc130e31557733212 +e7a31846989629369350e2046b2915fc7b2a72d037a79865d171f4e810d7837704b48f2ae0f5c93d47e14124268a5648de896207b8cb39eb7051e9851d2dabe4 +e6e12409ab858b4919b78ff16195ec2e4e9cf8f62629f4cd3c2d1dc3bb1171d4dc6338513824095148bfe307845458779752c7afbbd4175cf2b1427729ea605a +e992211d39d93423daa631c4655a6533c4dbf1cdee1dd4e1accaea2d72e822a12a30ab507e4898e3c6eb78a2705cc57288635676f80daca22a250753e197713d +a920d221611cf502226515cd2d01f69682be83a16355867d974d631729143da8e27903735e466ef1836e84e3b40a3ba04954c67e200f204531dae3aa0abecbdd +0ad1cf10079c2c0cf71d4a9c709fde0d6ed3d051699620facd4454c4f23ae14efe0ea66c8c896935d0d49d5e1dd3e4ef1a37a3d0b9ad848b6bdcd02a5f7cfdb8 +42efb7b5656fc2ee555533db271af522dcc9f6dce522a06f7f77dec293648f4041cc6f51ef9af3bbe6ecfde79bf3a27abef8963cebc2d0a0f52c62076d3376c7 +0ba7ee31656ca0a68cdc9066f096b0f7047d58d474e6c4498a53581ac14f5dc920c0c185021b1a24b8fa88aa6810e11486f6baa7998432631d4a9472098745b3 +5cc95be361f057f6a8d9d48710db392456bb3cb0cb2b7a393f6b146c8c56a139d0e682563483b30a5bb9923105db5e47585d2b75666975a39a698a8eb4c264ed +627328079717a6c162e14d186a108c42e0e55538f36bd170d8c18c04daef364679584c142e324432c201c962a4ed9e8f51dd0429cf953943b41d3619f4c1f114 +af95a4b534db379076962095c5351688cba3f72651ca33781625e076b21c59522e4e96a0a3b6d76a2e373de4e3b4ed8de19c0c3fe314a22ef51c895908974dbe +1236ed4f2d6653e5b368b672c3dc22a8c3d587f5fb9cc14e1f4885545b58463635ccab2c0558a22559fd979be0d68b32a0a21b9d4d8b953548867f4d0bf0a31b +5a321e135f95835d5ad1beb38f592be51345c4200a8ed0884dc43e86f0eb54057b022ae1bac37404fd007773dadbe695db9cb3a22bdf88199c5dc72c8d70d66e +7589e6956ce1a621153a98a7927a605ba5eec6b8f39b624afe824c29a7f1ffcc14bd9fc0edc34aa023e0c3d5b0c048574adbe342451cba501a51bf2f607030bd +03b205ee77e13524155c509bbf821ceabfb6e62c0f53d6708854fb344482c27ea42241c81eb425937da730ab677b9765c9324626a34aeacad4aa3d2287840d75 +0f5cd57bbb87224875d34db236607027f3cf7dce2a6814ea21a75c6f4e272bf65e5b03fff4e4638b198c72fbb0196872ff172a16e3c16c57b5f4863cdf7bcb86 +e817b331ab915705082b6d05adacec5f5385736eb5b663cd59bcdccc958328ce5b0c8bc54094c21d12d2ffc1fe4785cfecd70ebda10ef93ef456041f2f343348 +1bc8ea4b76f040ba41dac5110c4e76d1269366655d9b8d4eda6bf9667dc1936e21f784b3b5666789f7399d5d0c67ae38a7162fd2d999871d5fdbb585ae86c89e +2c51581ae707191318f399acfc258b8fee41a0b7e09bc18429699209be53090c33f4c0d40114bf95684837fe020000ffff0300504b0304140006000800000021 +000dd1909fb60000001b010000270000007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484 +f78277086f6fd3ba109126dd88d0add40384e4350d363f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e +89d93b64b060828e6f37ed1567914b284d262452282e3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5 +001996509affb3fd381a89672f1f165dfe514173d9850528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de +0fbfff0000001c0200001300000000000000000000000000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5 +d6a7e7c0000000360100000b00000000000000000000000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b79961683000000 +8a0000001c00000000000000000000000000190200007468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d001400060008000000 +2100da7e1364a9060000a41b00001600000000000000000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d0014000600 +0800000021000dd1909fb60000001b0100002700000000000000000000000000b30900007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000ae0a00000000} {\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d 617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 @@ -214,8 +220,8 @@ fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000301c -57149160cd01feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 +ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000f0d5 +16df10f5cd01feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000105000000000000}} \ No newline at end of file diff --git a/iis/mymodule.cpp b/iis/mymodule.cpp index 164a26be99..c2b63b4179 100644 --- a/iis/mymodule.cpp +++ b/iis/mymodule.cpp @@ -367,8 +367,12 @@ HRESULT CMyHttpModule::ReadFileChunk(HTTP_DATA_CHUNK *chunk, char *buf) { bytesRead = (DWORD)chunk->FromFileHandle.ByteRange.Length.QuadPart; } + if ((bytesTotal + bytesRead) > chunk->FromFileHandle.ByteRange.Length.QuadPart) + { + bytesRead = chunk->FromFileHandle.ByteRange.Length.QuadPart - bytesTotal; + } - memcpy(buf, pIoBuffer, bytesRead); + memcpy(buf, pIoBuffer + dwDataStartOffset, bytesRead); buf += bytesRead; bytesTotal += bytesRead; @@ -403,6 +407,8 @@ CMyHttpModule::OnSendResponse( EnterCriticalSection(&m_csLock); + // here we must check if response body processing is enabled + // if(rsc == NULL || rsc->m_pRequestRec == NULL || rsc->m_pResponseBuffer != NULL || !modsecIsResponseBodyAccessEnabled(rsc->m_pRequestRec)) { goto Exit; @@ -501,9 +507,6 @@ CMyHttpModule::OnSendResponse( *(const char **)apr_array_push(r->content_languages) = lng; } - // here we must check if response body processing is enabled - // - // Disable kernel caching for this response // Probably we don't have to do it for ModSecurity @@ -765,17 +768,33 @@ CMyHttpModule::OnBeginRequest( pConfig->m_Config = modsecGetDefaultConfig(); + PCWSTR servpath = pHttpContext->GetApplication()->GetApplicationPhysicalPath(); + char *apppath; + USHORT apppathlen; + + hr = pConfig->GlobalWideCharToMultiByte((WCHAR *)servpath, wcslen(servpath), &apppath, &apppathlen); + + if ( FAILED( hr ) ) + { + delete path; + hr = E_UNEXPECTED; + goto Finished; + } + if(path[0] != 0) { - const char * err = modsecProcessConfig((directory_config *)pConfig->m_Config, path); + const char * err = modsecProcessConfig((directory_config *)pConfig->m_Config, path, apppath); if(err != NULL) { WriteEventViewerLog(err, EVENTLOG_ERROR_TYPE); + delete apppath; + delete path; + goto Finished; } } + delete apppath; } - delete path; } @@ -788,6 +807,11 @@ CMyHttpModule::OnBeginRequest( r = modsecNewRequest(c, (directory_config *)pConfig->m_Config); + // on IIS we force input stream inspection flag, because its absence does not add any performance gain + // it's because on IIS request body must be restored each time it was read + // + modsecSetConfigForIISRequestBody(r); + REQUEST_STORED_CONTEXT *rsc = new REQUEST_STORED_CONTEXT(); rsc->m_pConnRec = c; diff --git a/nginx/modsecurity/ngx_http_modsecurity.c b/nginx/modsecurity/ngx_http_modsecurity.c index 8945508ca1..a78a153524 100644 --- a/nginx/modsecurity/ngx_http_modsecurity.c +++ b/nginx/modsecurity/ngx_http_modsecurity.c @@ -664,7 +664,7 @@ ngx_http_modsecurity_config(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) mscf->config = modsecGetDefaultConfig(); - msg = modsecProcessConfig(mscf->config, (const char *)value[1].data); + msg = modsecProcessConfig(mscf->config, (const char *)value[1].data, ""); if (msg != NULL) { ngx_conf_log_error(NGX_LOG_INFO, cf, 0, "modSecurity: modsecProcessConfig() %s", msg); return NGX_CONF_ERROR; diff --git a/standalone/api.c b/standalone/api.c index 966d824bd4..10cd65f080 100644 --- a/standalone/api.c +++ b/standalone/api.c @@ -1,536 +1,578 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2011 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include -#include - -#include "http_core.h" -#include "http_request.h" - -#include "modsecurity.h" -#include "apache2.h" -#include "http_main.h" -#include "http_connection.h" - -#include "apr_optional.h" -#include "mod_log_config.h" - -#include "msc_logging.h" -#include "msc_util.h" - -#include "ap_mpm.h" -#include "scoreboard.h" - -#include "apr_version.h" - -#include "apr_lib.h" -#include "ap_config.h" -#include "http_config.h" - - -extern void *modsecLogObj; -extern void (*modsecLogHook)(void *obj, int level, char *str); -extern int (*modsecDropAction)(request_rec *r); -apr_status_t (*modsecReadBody)(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos); -apr_status_t (*modsecReadResponse)(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos); -apr_status_t (*modsecWriteBody)(request_rec *r, char *buf, unsigned int length); -apr_status_t (*modsecWriteResponse)(request_rec *r, char *buf, unsigned int length); - -extern const char *process_command_config(server_rec *s, - void *mconfig, - apr_pool_t *p, - apr_pool_t *ptemp, - const char *filename); - -#define DECLARE_EXTERNAL_HOOK(ns,link,ret,name,args) \ -extern ns##_HOOK_##name##_t *hookfn_##name; - -#define DECLARE_HOOK(ret,name,args) \ - DECLARE_EXTERNAL_HOOK(ap,AP,ret,name,args) - -DECLARE_HOOK(int,pre_config,(apr_pool_t *pconf,apr_pool_t *plog, apr_pool_t *ptemp)) -DECLARE_HOOK(int,post_config,(apr_pool_t *pconf,apr_pool_t *plog, apr_pool_t *ptemp,server_rec *s)) -DECLARE_HOOK(void,child_init,(apr_pool_t *pchild, server_rec *s)) -DECLARE_HOOK(int,process_connection,(conn_rec *c)) -DECLARE_HOOK(int,post_read_request,(request_rec *r)) -DECLARE_HOOK(int,fixups,(request_rec *r)) -DECLARE_HOOK(void, error_log, (const char *file, int line, int level, - apr_status_t status, const server_rec *s, - const request_rec *r, apr_pool_t *pool, - const char *errstr)) -DECLARE_HOOK(int,log_transaction,(request_rec *r)) -DECLARE_HOOK(void,insert_filter,(request_rec *r)) -DECLARE_HOOK(void,insert_error_filter,(request_rec *r)) - -char *sa_name = "standalone"; -server_rec *server; -apr_pool_t *pool = NULL; - -apr_status_t ap_http_in_filter(ap_filter_t *f, apr_bucket_brigade *b, - ap_input_mode_t mode, apr_read_type_e block, - apr_off_t readbytes); -apr_status_t ap_http_out_filter(ap_filter_t *f, apr_bucket_brigade *b); - -server_rec *modsecInit() { - apr_initialize(); - - apr_pool_create(&pool, NULL); - - apr_hook_global_pool = pool; - - server = apr_palloc(pool, sizeof(server_rec)); - - server->addrs = apr_palloc(pool, sizeof(server_addr_rec)); - server->addrs->host_addr = apr_palloc(pool, sizeof(apr_sockaddr_t)); - server->addrs->host_addr->addr_str_len = 16; - server->addrs->host_addr->family = AF_INET; - server->addrs->host_addr->hostname = sa_name; -#ifdef WIN32 - server->addrs->host_addr->ipaddr_len = sizeof(IN_ADDR); -#else - server->addrs->host_addr->ipaddr_len = sizeof(struct in_addr); -#endif - server->addrs->host_addr->ipaddr_ptr = &server->addrs->host_addr->sa.sin.sin_addr; - server->addrs->host_addr->pool = pool; - server->addrs->host_addr->port = 80; -#ifdef WIN32 - server->addrs->host_addr->sa.sin.sin_addr.S_un.S_addr = 0x0100007f; -#else - server->addrs->host_addr->sa.sin.sin_addr.s_addr = 0x0100007f; -#endif - server->addrs->host_addr->sa.sin.sin_family = AF_INET; - server->addrs->host_addr->sa.sin.sin_port = 80; - server->addrs->host_addr->salen = sizeof(server->addrs->host_addr->sa); - server->addrs->host_addr->servname = sa_name; - server->addrs->host_port = 80; - server->error_fname = "error.log"; - server->error_log = NULL; - server->limit_req_fields = 1024; - server->limit_req_fieldsize = 1024; - server->limit_req_line = 1024; -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 - server->loglevel = APLOG_DEBUG; -#endif - server->lookup_defaults = NULL; - server->module_config = NULL; - server->names = NULL; -#ifdef WIN32 - server->path = "c:\\inetpub\\wwwroot"; -#else - server->path = "/var/www"; -#endif - server->pathlen = strlen(server->path); - server->port = 80; - server->process = apr_palloc(pool, sizeof(process_rec)); - server->process->argc = 1; - server->process->argv = &sa_name; - server->process->pconf = pool; - server->process->pool = pool; - server->process->short_name = sa_name; - server->server_admin = sa_name; - server->server_hostname = sa_name; - server->server_scheme = ""; - server->timeout = 60 * 1000000;// 60 seconds - server->wild_names = NULL; - server->is_virtual = 0; - - ap_server_config_defines = apr_array_make(pool, 1, sizeof(char *)); - - // here we should add scoreboard handling for multiple processes and threads - // - ap_scoreboard_image = (scoreboard *)apr_palloc(pool, sizeof(scoreboard)); - - memset(ap_scoreboard_image, 0, sizeof(scoreboard)); - - // ---------- - - security2_module.module_index = 0; - - security2_module.register_hooks(pool); - - ap_register_input_filter("HTTP_IN", ap_http_in_filter, NULL, AP_FTYPE_RESOURCE); - ap_register_output_filter("HTTP_OUT", ap_http_out_filter, NULL, AP_FTYPE_CONTENT_SET); - - return server; -} - -apr_status_t ap_http_in_filter(ap_filter_t *f, apr_bucket_brigade *b, - ap_input_mode_t mode, apr_read_type_e block, - apr_off_t readbytes) { - char *tmp = NULL; - apr_bucket *e = NULL; - unsigned int readcnt = 0; - int is_eos = 0; - - if(modsecReadBody == NULL) - return AP_NOBODY_READ; - - tmp = (char *)apr_palloc(f->r->pool, readbytes); - modsecReadBody(f->r, tmp, readbytes, &readcnt, &is_eos); - - e = apr_bucket_pool_create(tmp, readcnt, f->r->pool, f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(b, e); - - if(is_eos) { - e = apr_bucket_eos_create(f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(b, e); - } - - return APR_SUCCESS; -} - -apr_status_t ap_http_out_filter(ap_filter_t *f, apr_bucket_brigade *b) { - modsec_rec *msr = (modsec_rec *)f->ctx; - apr_status_t rc; - - // is there a way to tell whether the response body was modified or not? - // - if((msr->txcfg->content_injection_enabled || msr->content_prepend_len != 0 || msr->content_append_len != 0) - && modsecWriteResponse != NULL && msr->txcfg->resbody_access) { - char *data = NULL; - apr_size_t length; - - rc = apr_brigade_pflatten(msr->of_brigade, &data, &length, msr->mp); - - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "Output filter: Failed to flatten brigade (%d): %s", rc, - get_apr_error(msr->mp, rc)); - return -1; - } - - modsecWriteResponse(msr->r, data, msr->stream_output_length); - } - - return APR_SUCCESS; -} - -void modsecTerminate() { - apr_pool_destroy(pool); - pool = NULL; - apr_terminate(); -} - -void modsecStartConfig() { - apr_pool_t *ptemp = NULL; - - apr_pool_create(&ptemp, pool); - - hookfn_pre_config(pool, pool, ptemp); - - apr_pool_destroy(ptemp); -} - -directory_config *modsecGetDefaultConfig() { - return (directory_config *)security2_module.create_dir_config(pool, NULL); -} - -const char *modsecProcessConfig(directory_config *config, const char *dir) { - apr_pool_t *ptemp = NULL; - const char *err; - - apr_pool_create(&ptemp, pool); - - err = process_command_config(server, config, pool, ptemp, dir); - - apr_pool_destroy(ptemp); - - return err; -} - -void modsecFinalizeConfig() { - apr_pool_t *ptemp = NULL; - - apr_pool_create(&ptemp, pool); - - hookfn_post_config(pool, pool, ptemp, server); - hookfn_post_config(pool, pool, ptemp, server); - - apr_pool_destroy(ptemp); -} - -void modsecInitProcess() { - hookfn_child_init(pool, server); -} - -conn_rec *modsecNewConnection() { - conn_rec *c; - apr_pool_t *pc = NULL; - - apr_pool_create(&pc, pool); - - c = apr_pcalloc(pc, sizeof(conn_rec)); - - c->base_server = server; - c->id = 1; - c->local_addr = server->addrs->host_addr; - c->local_host = sa_name; - c->local_ip = "127.0.0.1"; - c->pool = pc; - c->remote_host = sa_name; -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 - c->remote_ip = "127.0.0.1"; - c->remote_addr = server->addrs->host_addr; -#else - c->client_ip = "127.0.0.1"; - c->client_addr = server->addrs->host_addr; -#endif - c->input_filters = NULL; - c->output_filters = NULL; - c->bucket_alloc = apr_bucket_alloc_create(pc); - - return c; -} - -void modsecProcessConnection(conn_rec *c) { - hookfn_process_connection(c); -} - -request_rec *modsecNewRequest(conn_rec *connection, directory_config *config) { - request_rec *r; - apr_pool_t *pr = NULL; - - apr_pool_create(&pr, connection->pool); - - r = apr_pcalloc(pr, sizeof(request_rec)); - - r->connection = connection; - r->server = server; - r->pool = pr; - r->main = NULL; - r->next = NULL; - r->notes = apr_table_make(pr, 10); - r->per_dir_config = apr_palloc(pr, sizeof(void *)); - ((void **)r->per_dir_config)[0] = config; - r->prev = NULL; - r->subprocess_env = apr_table_make(pr, 10); - apr_table_setn(r->subprocess_env, "UNIQUE_ID", "unique_id"); - r->user = NULL; - - r->headers_in = apr_table_make(pr, 10); - r->headers_out = apr_table_make(pr, 10); - r->err_headers_out = apr_table_make(pr, 10); - //apr_table_setn(r->headers_in, "Host", "www.google.com"); - //apr_table_setn(r->headers_in, "", ""); - - r->the_request = "GET /../../index.html HTTP/1.1"; - r->method = "GET"; - r->method_number = M_GET; - r->protocol = "HTTP/1.1"; - r->uri = "http://www.google.com/../../index.html"; - r->args = ""; - r->filename = "/../../index.html"; - r->handler = "IIS"; - - r->parsed_uri.scheme = "http"; - r->parsed_uri.path = "/../../index.html"; - r->parsed_uri.hostname = "www.google.com"; - r->parsed_uri.is_initialized = 1; - r->parsed_uri.port = 1234; - r->parsed_uri.port_str = "1234"; - r->parsed_uri.query = ""; - r->parsed_uri.dns_looked_up = 0; - r->parsed_uri.dns_resolved = 0; - r->parsed_uri.password = NULL; - r->parsed_uri.user = NULL; - r->parsed_uri.fragment = ""; - - r->input_filters = NULL; - r->output_filters = NULL; - - return r; -} - -static modsec_rec *retrieve_msr(request_rec *r) { - modsec_rec *msr = NULL; - request_rec *rx = NULL; - - /* Look in the current request first. */ - msr = (modsec_rec *)apr_table_get(r->notes, NOTE_MSR); - if (msr != NULL) { - msr->r = r; - return msr; - } - - /* If this is a subrequest then look in the main request. */ - if (r->main != NULL) { - msr = (modsec_rec *)apr_table_get(r->main->notes, NOTE_MSR); - if (msr != NULL) { - msr->r = r; - return msr; - } - } - - /* If the request was redirected then look in the previous requests. */ - rx = r->prev; - while(rx != NULL) { - msr = (modsec_rec *)apr_table_get(rx->notes, NOTE_MSR); - if (msr != NULL) { - msr->r = r; - return msr; - } - rx = rx->prev; - } - - return NULL; -} - -int modsecProcessRequest(request_rec *r) { - int status = DECLINED; - modsec_rec *msr = NULL; - - ap_filter_t *f = ap_add_input_filter("HTTP_IN", NULL, r, r->connection); - - status = hookfn_post_read_request(r); - status = hookfn_fixups(r); - - ap_remove_input_filter(f); - - hookfn_insert_filter(r); - - /* Find the transaction context first. */ - msr = retrieve_msr(r); - - if (msr == NULL) - return status; - - if(msr->stream_input_data != NULL && modsecWriteBody != NULL) - { - // target is responsible for copying the data into correctly managed buffer - // - modsecWriteBody(r, msr->stream_input_data, msr->stream_input_length); - - free(msr->stream_input_data); - - msr->stream_input_data = NULL; - } - - // leftover code possibly for future use - // - //if(r->input_filters != NULL && r->input_filters->frec->filter_init_func != NULL) - //r->input_filters->frec->filter_init_func(r->input_filters); - //if(r->input_filters != NULL && r->input_filters->frec->filter_func.in_func != NULL) - //r->input_filters->frec->filter_func.in_func(r->input_filters, NULL, 0, 0, 0); - - return status; -} - -int modsecIsResponseBodyAccessEnabled(request_rec *r) -{ - modsec_rec *msr = retrieve_msr(r); - - if(msr == NULL || msr->txcfg == NULL) - return 0; - - return msr->txcfg->resbody_access; -} - -int modsecProcessResponse(request_rec *r) { - int status = DECLINED; - - if(r->output_filters != NULL) { - modsec_rec *msr = (modsec_rec *)r->output_filters->ctx; - char buf[8192]; - char *tmp = NULL; - apr_bucket *e = NULL; - unsigned int readcnt = 0; - int is_eos = 0; - ap_filter_t *f = NULL; - apr_bucket_brigade *bb = NULL; - - if (msr == NULL) { - ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server, - "ModSecurity: Internal Error: msr is null in output filter."); - ap_remove_output_filter(r->output_filters); - return send_error_bucket(msr, r->output_filters, HTTP_INTERNAL_SERVER_ERROR); - } - - bb = apr_brigade_create(msr->mp, r->connection->bucket_alloc); - - if (bb == NULL) { - msr_log(msr, 1, "Process response: Failed to create brigade."); - return -1; - } - - msr->r = r; - - if(modsecReadResponse == NULL) - return AP_NOBODY_WROTE; - - f = ap_add_output_filter("HTTP_OUT", msr, r, r->connection); - - while(!is_eos) { - modsecReadResponse(r, buf, 8192, &readcnt, &is_eos); - - if(readcnt > 0) { - tmp = (char *)apr_palloc(r->pool, readcnt); - memcpy(tmp, buf, readcnt); - - e = apr_bucket_pool_create(tmp, readcnt, r->pool, r->connection->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, e); - } - - if(is_eos) { - e = apr_bucket_eos_create(r->connection->bucket_alloc); - - APR_BRIGADE_INSERT_TAIL(bb, e); - } - } - - status = ap_pass_brigade(r->output_filters, bb); - - ap_remove_output_filter(f); - } - - return status; -} - -int modsecFinishRequest(request_rec *r) { - // run output filter - //if(r->output_filters != NULL && r->output_filters->frec->filter_init_func != NULL) - //r->output_filters->frec->filter_init_func(r->output_filters); - - hookfn_log_transaction(r); - - // make sure you cleanup before calling apr_terminate() - // otherwise double-free might occur, because of the request body pool cleanup function - // - apr_pool_destroy(r->connection->pool); - - return DECLINED; -} - -void modsecSetLogHook(void *obj, void (*hook)(void *obj, int level, char *str)) { - modsecLogObj = obj; - modsecLogHook = hook; -} - -void modsecSetReadBody(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos)) { - modsecReadBody = func; -} - -void modsecSetReadResponse(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos)) { - modsecReadResponse = func; -} - -void modsecSetWriteBody(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length)) { - modsecWriteBody = func; -} - -void modsecSetWriteResponse(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length)) { - modsecWriteResponse = func; -} - -void modsecSetDropAction(int (*func)(request_rec *r)) { - modsecDropAction = func; -} +/* +* ModSecurity for Apache 2.x, http://www.modsecurity.org/ +* Copyright (c) 2004-2011 Trustwave Holdings, Inc. (http://www.trustwave.com/) +* +* You may not use this file except in compliance with +* the License.  You may obtain a copy of the License at +* +*     http://www.apache.org/licenses/LICENSE-2.0 +* +* If any of the files related to licensing are missing or if you have any +* other questions related to licensing please contact Trustwave Holdings, Inc. +* directly using the email address security@modsecurity.org. +*/ + +#include +#include + +#include "http_core.h" +#include "http_request.h" + +#include "modsecurity.h" +#include "apache2.h" +#include "http_main.h" +#include "http_connection.h" + +#include "apr_optional.h" +#include "mod_log_config.h" + +#include "msc_logging.h" +#include "msc_util.h" + +#include "ap_mpm.h" +#include "scoreboard.h" + +#include "apr_version.h" + +#include "apr_lib.h" +#include "ap_config.h" +#include "http_config.h" + + +extern void *modsecLogObj; +extern void (*modsecLogHook)(void *obj, int level, char *str); +extern int (*modsecDropAction)(request_rec *r); +apr_status_t (*modsecReadBody)(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos); +apr_status_t (*modsecReadResponse)(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos); +apr_status_t (*modsecWriteBody)(request_rec *r, char *buf, unsigned int length); +apr_status_t (*modsecWriteResponse)(request_rec *r, char *buf, unsigned int length); + +extern const char *process_command_config(server_rec *s, + void *mconfig, + apr_pool_t *p, + apr_pool_t *ptemp, + const char *filename); + +#define DECLARE_EXTERNAL_HOOK(ns,link,ret,name,args) \ +extern ns##_HOOK_##name##_t *hookfn_##name; + +#define DECLARE_HOOK(ret,name,args) \ + DECLARE_EXTERNAL_HOOK(ap,AP,ret,name,args) + +DECLARE_HOOK(int,pre_config,(apr_pool_t *pconf,apr_pool_t *plog, apr_pool_t *ptemp)) +DECLARE_HOOK(int,post_config,(apr_pool_t *pconf,apr_pool_t *plog, apr_pool_t *ptemp,server_rec *s)) +DECLARE_HOOK(void,child_init,(apr_pool_t *pchild, server_rec *s)) +DECLARE_HOOK(int,process_connection,(conn_rec *c)) +DECLARE_HOOK(int,post_read_request,(request_rec *r)) +DECLARE_HOOK(int,fixups,(request_rec *r)) +DECLARE_HOOK(void, error_log, (const char *file, int line, int level, + apr_status_t status, const server_rec *s, + const request_rec *r, apr_pool_t *pool, + const char *errstr)) +DECLARE_HOOK(int,log_transaction,(request_rec *r)) +DECLARE_HOOK(void,insert_filter,(request_rec *r)) +DECLARE_HOOK(void,insert_error_filter,(request_rec *r)) + +char *sa_name = "standalone"; +server_rec *server; +apr_pool_t *pool = NULL; + +apr_status_t ap_http_in_filter(ap_filter_t *f, apr_bucket_brigade *b, + ap_input_mode_t mode, apr_read_type_e block, + apr_off_t readbytes); +apr_status_t ap_http_out_filter(ap_filter_t *f, apr_bucket_brigade *b); + +server_rec *modsecInit() { + apr_initialize(); + + apr_pool_create(&pool, NULL); + + apr_hook_global_pool = pool; + + server = apr_palloc(pool, sizeof(server_rec)); + + server->addrs = apr_palloc(pool, sizeof(server_addr_rec)); + server->addrs->host_addr = apr_palloc(pool, sizeof(apr_sockaddr_t)); + server->addrs->host_addr->addr_str_len = 16; + server->addrs->host_addr->family = AF_INET; + server->addrs->host_addr->hostname = sa_name; +#ifdef WIN32 + server->addrs->host_addr->ipaddr_len = sizeof(IN_ADDR); +#else + server->addrs->host_addr->ipaddr_len = sizeof(struct in_addr); +#endif + server->addrs->host_addr->ipaddr_ptr = &server->addrs->host_addr->sa.sin.sin_addr; + server->addrs->host_addr->pool = pool; + server->addrs->host_addr->port = 80; +#ifdef WIN32 + server->addrs->host_addr->sa.sin.sin_addr.S_un.S_addr = 0x0100007f; +#else + server->addrs->host_addr->sa.sin.sin_addr.s_addr = 0x0100007f; +#endif + server->addrs->host_addr->sa.sin.sin_family = AF_INET; + server->addrs->host_addr->sa.sin.sin_port = 80; + server->addrs->host_addr->salen = sizeof(server->addrs->host_addr->sa); + server->addrs->host_addr->servname = sa_name; + server->addrs->host_port = 80; + server->error_fname = "error.log"; + server->error_log = NULL; + server->limit_req_fields = 1024; + server->limit_req_fieldsize = 1024; + server->limit_req_line = 1024; +#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 + server->loglevel = APLOG_DEBUG; +#endif + server->lookup_defaults = NULL; + server->module_config = NULL; + server->names = NULL; +#ifdef WIN32 + server->path = "c:\\inetpub\\wwwroot"; +#else + server->path = "/var/www"; +#endif + server->pathlen = strlen(server->path); + server->port = 80; + server->process = apr_palloc(pool, sizeof(process_rec)); + server->process->argc = 1; + server->process->argv = &sa_name; + server->process->pconf = pool; + server->process->pool = pool; + server->process->short_name = sa_name; + server->server_admin = sa_name; + server->server_hostname = sa_name; + server->server_scheme = ""; + server->timeout = 60 * 1000000;// 60 seconds + server->wild_names = NULL; + server->is_virtual = 0; + + ap_server_config_defines = apr_array_make(pool, 1, sizeof(char *)); + + // here we should add scoreboard handling for multiple processes and threads + // + ap_scoreboard_image = (scoreboard *)apr_palloc(pool, sizeof(scoreboard)); + + memset(ap_scoreboard_image, 0, sizeof(scoreboard)); + + // ---------- + + security2_module.module_index = 0; + + security2_module.register_hooks(pool); + + ap_register_input_filter("HTTP_IN", ap_http_in_filter, NULL, AP_FTYPE_RESOURCE); + ap_register_output_filter("HTTP_OUT", ap_http_out_filter, NULL, AP_FTYPE_CONTENT_SET); + + return server; +} + +apr_status_t ap_http_in_filter(ap_filter_t *f, apr_bucket_brigade *b, + ap_input_mode_t mode, apr_read_type_e block, + apr_off_t readbytes) { + char *tmp = NULL; + apr_bucket *e = NULL; + unsigned int readcnt = 0; + int is_eos = 0; + + if(modsecReadBody == NULL) + return AP_NOBODY_READ; + + tmp = (char *)apr_palloc(f->r->pool, readbytes); + modsecReadBody(f->r, tmp, readbytes, &readcnt, &is_eos); + + e = apr_bucket_pool_create(tmp, readcnt, f->r->pool, f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(b, e); + + if(is_eos) { + e = apr_bucket_eos_create(f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(b, e); + } + + return APR_SUCCESS; +} + +apr_status_t ap_http_out_filter(ap_filter_t *f, apr_bucket_brigade *b) { + modsec_rec *msr = (modsec_rec *)f->ctx; + apr_status_t rc; + + // is there a way to tell whether the response body was modified or not? + // + if((msr->txcfg->content_injection_enabled || msr->content_prepend_len != 0 || msr->content_append_len != 0) + && modsecWriteResponse != NULL && msr->txcfg->resbody_access) { + char *data = NULL; + apr_size_t length; + + rc = apr_brigade_pflatten(msr->of_brigade, &data, &length, msr->mp); + + if (rc != APR_SUCCESS) { + msr_log(msr, 1, "Output filter: Failed to flatten brigade (%d): %s", rc, + get_apr_error(msr->mp, rc)); + return -1; + } + + modsecWriteResponse(msr->r, data, msr->stream_output_length); + } + + return APR_SUCCESS; +} + +void modsecTerminate() { + apr_pool_destroy(pool); + pool = NULL; + apr_terminate(); +} + +void modsecStartConfig() { + apr_pool_t *ptemp = NULL; + + apr_pool_create(&ptemp, pool); + + hookfn_pre_config(pool, pool, ptemp); + + apr_pool_destroy(ptemp); +} + +directory_config *modsecGetDefaultConfig() { + return (directory_config *)security2_module.create_dir_config(pool, NULL); +} + +const char *modsecProcessConfig(directory_config *config, const char *file, const char *dir) { + apr_pool_t *ptemp = NULL; + const char *err; + apr_status_t status; + const char *rootpath, *incpath; + + if(dir == NULL || strlen(dir) == 0) +#ifdef WIN32 + dir = "\\"; +#else + dir = "/"; +#endif + + incpath = file; + + /* locate the start of the directories proper */ + status = apr_filepath_root(&rootpath, &incpath, APR_FILEPATH_TRUENAME | APR_FILEPATH_NATIVE, pool); + + /* we allow APR_SUCCESS and APR_EINCOMPLETE */ + if (APR_ERELATIVE == status) { + int li = strlen(dir) - 1; + + if(dir[li] != '/' && dir[li] != '\\') +#ifdef WIN32 + file = apr_pstrcat(pool, dir, "\\", file, NULL); +#else + file = apr_pstrcat(pool, dir, "/", file, NULL); +#endif + else + file = apr_pstrcat(pool, dir, file, NULL); + } + else if (APR_EBADPATH == status) { + return apr_pstrcat(pool, "Config file has a bad path, ", file, NULL); + } + + apr_pool_create(&ptemp, pool); + + err = process_command_config(server, config, pool, ptemp, file); + + apr_pool_destroy(ptemp); + + return err; +} + +void modsecFinalizeConfig() { + apr_pool_t *ptemp = NULL; + + apr_pool_create(&ptemp, pool); + + hookfn_post_config(pool, pool, ptemp, server); + hookfn_post_config(pool, pool, ptemp, server); + + apr_pool_destroy(ptemp); +} + +void modsecInitProcess() { + hookfn_child_init(pool, server); +} + +conn_rec *modsecNewConnection() { + conn_rec *c; + apr_pool_t *pc = NULL; + + apr_pool_create(&pc, pool); + + c = apr_pcalloc(pc, sizeof(conn_rec)); + + c->base_server = server; + c->id = 1; + c->local_addr = server->addrs->host_addr; + c->local_host = sa_name; + c->local_ip = "127.0.0.1"; + c->pool = pc; + c->remote_host = sa_name; +#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 + c->remote_ip = "127.0.0.1"; + c->remote_addr = server->addrs->host_addr; +#else + c->client_ip = "127.0.0.1"; + c->client_addr = server->addrs->host_addr; +#endif + c->input_filters = NULL; + c->output_filters = NULL; + c->bucket_alloc = apr_bucket_alloc_create(pc); + + return c; +} + +void modsecProcessConnection(conn_rec *c) { + hookfn_process_connection(c); +} + +request_rec *modsecNewRequest(conn_rec *connection, directory_config *config) { + request_rec *r; + apr_pool_t *pr = NULL; + + apr_pool_create(&pr, connection->pool); + + r = apr_pcalloc(pr, sizeof(request_rec)); + + r->connection = connection; + r->server = server; + r->pool = pr; + r->main = NULL; + r->next = NULL; + r->notes = apr_table_make(pr, 10); + r->per_dir_config = apr_palloc(pr, sizeof(void *)); + ((void **)r->per_dir_config)[0] = config; + r->prev = NULL; + r->subprocess_env = apr_table_make(pr, 10); + apr_table_setn(r->subprocess_env, "UNIQUE_ID", "unique_id"); + r->user = NULL; + + r->headers_in = apr_table_make(pr, 10); + r->headers_out = apr_table_make(pr, 10); + r->err_headers_out = apr_table_make(pr, 10); + //apr_table_setn(r->headers_in, "Host", "www.google.com"); + //apr_table_setn(r->headers_in, "", ""); + + r->the_request = "GET /../../index.html HTTP/1.1"; + r->method = "GET"; + r->method_number = M_GET; + r->protocol = "HTTP/1.1"; + r->uri = "http://www.google.com/../../index.html"; + r->args = ""; + r->filename = "/../../index.html"; + r->handler = "IIS"; + + r->parsed_uri.scheme = "http"; + r->parsed_uri.path = "/../../index.html"; + r->parsed_uri.hostname = "www.google.com"; + r->parsed_uri.is_initialized = 1; + r->parsed_uri.port = 1234; + r->parsed_uri.port_str = "1234"; + r->parsed_uri.query = ""; + r->parsed_uri.dns_looked_up = 0; + r->parsed_uri.dns_resolved = 0; + r->parsed_uri.password = NULL; + r->parsed_uri.user = NULL; + r->parsed_uri.fragment = ""; + + r->input_filters = NULL; + r->output_filters = NULL; + + return r; +} + +static modsec_rec *retrieve_msr(request_rec *r) { + modsec_rec *msr = NULL; + request_rec *rx = NULL; + + /* Look in the current request first. */ + msr = (modsec_rec *)apr_table_get(r->notes, NOTE_MSR); + if (msr != NULL) { + msr->r = r; + return msr; + } + + /* If this is a subrequest then look in the main request. */ + if (r->main != NULL) { + msr = (modsec_rec *)apr_table_get(r->main->notes, NOTE_MSR); + if (msr != NULL) { + msr->r = r; + return msr; + } + } + + /* If the request was redirected then look in the previous requests. */ + rx = r->prev; + while(rx != NULL) { + msr = (modsec_rec *)apr_table_get(rx->notes, NOTE_MSR); + if (msr != NULL) { + msr->r = r; + return msr; + } + rx = rx->prev; + } + + return NULL; +} + +int modsecProcessRequest(request_rec *r) { + int status = DECLINED; + modsec_rec *msr = NULL; + + ap_filter_t *f = ap_add_input_filter("HTTP_IN", NULL, r, r->connection); + + status = hookfn_post_read_request(r); + status = hookfn_fixups(r); + + ap_remove_input_filter(f); + + hookfn_insert_filter(r); + + /* Find the transaction context first. */ + msr = retrieve_msr(r); + + if (msr == NULL) + return status; + + if(msr->stream_input_data != NULL && modsecWriteBody != NULL) + { + // target is responsible for copying the data into correctly managed buffer + // + modsecWriteBody(r, msr->stream_input_data, msr->stream_input_length); + + free(msr->stream_input_data); + + msr->stream_input_data = NULL; + } + + // leftover code possibly for future use + // + //if(r->input_filters != NULL && r->input_filters->frec->filter_init_func != NULL) + //r->input_filters->frec->filter_init_func(r->input_filters); + //if(r->input_filters != NULL && r->input_filters->frec->filter_func.in_func != NULL) + //r->input_filters->frec->filter_func.in_func(r->input_filters, NULL, 0, 0, 0); + + return status; +} + +void modsecSetConfigForIISRequestBody(request_rec *r) +{ + modsec_rec *msr = retrieve_msr(r); + + if(msr == NULL || msr->txcfg == NULL) + return; + + if(msr->txcfg->reqbody_access) + msr->txcfg->stream_inbody_inspection = 1; +} + +int modsecIsResponseBodyAccessEnabled(request_rec *r) +{ + modsec_rec *msr = retrieve_msr(r); + + if(msr == NULL || msr->txcfg == NULL) + return 0; + + return msr->txcfg->resbody_access; +} + +int modsecProcessResponse(request_rec *r) { + int status = DECLINED; + + if(r->output_filters != NULL) { + modsec_rec *msr = (modsec_rec *)r->output_filters->ctx; + char buf[8192]; + char *tmp = NULL; + apr_bucket *e = NULL; + unsigned int readcnt = 0; + int is_eos = 0; + ap_filter_t *f = NULL; + apr_bucket_brigade *bb = NULL; + + if (msr == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server, + "ModSecurity: Internal Error: msr is null in output filter."); + ap_remove_output_filter(r->output_filters); + return send_error_bucket(msr, r->output_filters, HTTP_INTERNAL_SERVER_ERROR); + } + + bb = apr_brigade_create(msr->mp, r->connection->bucket_alloc); + + if (bb == NULL) { + msr_log(msr, 1, "Process response: Failed to create brigade."); + return -1; + } + + msr->r = r; + + if(modsecReadResponse == NULL) + return AP_NOBODY_WROTE; + + f = ap_add_output_filter("HTTP_OUT", msr, r, r->connection); + + while(!is_eos) { + modsecReadResponse(r, buf, 8192, &readcnt, &is_eos); + + if(readcnt > 0) { + tmp = (char *)apr_palloc(r->pool, readcnt); + memcpy(tmp, buf, readcnt); + + e = apr_bucket_pool_create(tmp, readcnt, r->pool, r->connection->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, e); + } + + if(is_eos) { + e = apr_bucket_eos_create(r->connection->bucket_alloc); + + APR_BRIGADE_INSERT_TAIL(bb, e); + } + } + + status = ap_pass_brigade(r->output_filters, bb); + + ap_remove_output_filter(f); + } + + return status; +} + +int modsecFinishRequest(request_rec *r) { + // run output filter + //if(r->output_filters != NULL && r->output_filters->frec->filter_init_func != NULL) + //r->output_filters->frec->filter_init_func(r->output_filters); + + hookfn_log_transaction(r); + + // make sure you cleanup before calling apr_terminate() + // otherwise double-free might occur, because of the request body pool cleanup function + // + apr_pool_destroy(r->connection->pool); + + return DECLINED; +} + +void modsecSetLogHook(void *obj, void (*hook)(void *obj, int level, char *str)) { + modsecLogObj = obj; + modsecLogHook = hook; +} + +void modsecSetReadBody(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos)) { + modsecReadBody = func; +} + +void modsecSetReadResponse(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos)) { + modsecReadResponse = func; +} + +void modsecSetWriteBody(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length)) { + modsecWriteBody = func; +} + +void modsecSetWriteResponse(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length)) { + modsecWriteResponse = func; +} + +void modsecSetDropAction(int (*func)(request_rec *r)) { + modsecDropAction = func; +} diff --git a/standalone/api.h b/standalone/api.h index 825f722cc4..7ba998d562 100644 --- a/standalone/api.h +++ b/standalone/api.h @@ -51,7 +51,7 @@ void modsecTerminate(); void modsecStartConfig(); directory_config *modsecGetDefaultConfig(); -const char *modsecProcessConfig(directory_config *config, const char *dir); +const char *modsecProcessConfig(directory_config *config, const char *file, const char *dir); void modsecFinalizeConfig(); void modsecInitProcess(); @@ -71,8 +71,11 @@ void modsecSetReadResponse(apr_status_t (*func)(request_rec *r, char *buf, unsig void modsecSetWriteBody(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length)); void modsecSetWriteResponse(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length)); void modsecSetDropAction(int (*func)(request_rec *r)); + int modsecIsResponseBodyAccessEnabled(request_rec *r); +void modsecSetConfigForIISRequestBody(request_rec *r); + #ifdef __cplusplus } #endif diff --git a/standalone/config.c b/standalone/config.c index 95c363d269..b1a57dadc9 100644 --- a/standalone/config.c +++ b/standalone/config.c @@ -1,1134 +1,1188 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2011 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include -#include - -#include "http_core.h" -#include "http_request.h" - -#include "modsecurity.h" -#include "apache2.h" -#include "http_main.h" -#include "http_connection.h" - -#include "apr_optional.h" -#include "mod_log_config.h" - -#include "msc_logging.h" -#include "msc_util.h" - -#include "ap_mpm.h" -#include "scoreboard.h" - -#include "apr_version.h" - -#include "apr_lib.h" -#include "ap_config.h" -#include "http_config.h" -#include "apr_fnmatch.h" - -AP_DECLARE(int) ap_cfg_closefile(ap_configfile_t *cfp) -{ -#ifdef DEBUG - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, - "Done with config file %s", cfp->name); -#endif - return (cfp->close == NULL) ? 0 : cfp->close(cfp->param); -} - -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 -static apr_status_t cfg_close(void *param) -{ - apr_file_t *cfp = (apr_file_t *) param; - return (apr_file_close(cfp)); -} - -static int cfg_getch(void *param) -{ - char ch; - apr_file_t *cfp = (apr_file_t *) param; - if (apr_file_getc(&ch, cfp) == APR_SUCCESS) - return ch; - return (int)EOF; -} - -static void *cfg_getstr(void *buf, size_t bufsiz, void *param) -{ - apr_file_t *cfp = (apr_file_t *) param; - apr_status_t rv; - rv = apr_file_gets(buf, bufsiz, cfp); - if (rv == APR_SUCCESS) { - return buf; - } - return NULL; -} -#else -/* we can't use apr_file_* directly because of linking issues on Windows */ -static apr_status_t cfg_close(void *param) -{ - return apr_file_close(param); -} - -static apr_status_t cfg_getch(char *ch, void *param) -{ - return apr_file_getc(ch, param); -} - -static apr_status_t cfg_getstr(void *buf, apr_size_t bufsiz, void *param) -{ - return apr_file_gets(buf, bufsiz, param); -} -#endif - -/* Read one line from open ap_configfile_t, strip LF, increase line number */ -/* If custom handler does not define a getstr() function, read char by char */ - -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 -AP_DECLARE(int) ap_cfg_getline(char *buf, size_t bufsize, ap_configfile_t *cfp) -{ - /* If a "get string" function is defined, use it */ - if (cfp->getstr != NULL) { - char *src, *dst; - char *cp; - char *cbuf = buf; - size_t cbufsize = bufsize; - - while (1) { - ++cfp->line_number; - if (cfp->getstr(cbuf, cbufsize, cfp->param) == NULL) - return 1; - - /* - * check for line continuation, - * i.e. match [^\\]\\[\r]\n only - */ - cp = cbuf; - while (cp < cbuf+cbufsize && *cp != '\0') - cp++; - if (cp > cbuf && cp[-1] == LF) { - cp--; - if (cp > cbuf && cp[-1] == CR) - cp--; - if (cp > cbuf && cp[-1] == '\\') { - cp--; - if (!(cp > cbuf && cp[-1] == '\\')) { - /* - * line continuation requested - - * then remove backslash and continue - */ - cbufsize -= (cp-cbuf); - cbuf = cp; - continue; - } - else { - /* - * no real continuation because escaped - - * then just remove escape character - */ - for ( ; cp < cbuf+cbufsize && *cp != '\0'; cp++) - cp[0] = cp[1]; - } - } - } - break; - } - - /* - * Leading and trailing white space is eliminated completely - */ - src = buf; - while (apr_isspace(*src)) - ++src; - /* blast trailing whitespace */ - dst = &src[strlen(src)]; - while (--dst >= src && apr_isspace(*dst)) - *dst = '\0'; - /* Zap leading whitespace by shifting */ - if (src != buf) - for (dst = buf; (*dst++ = *src++) != '\0'; ) - ; - -#ifdef DEBUG_CFG_LINES - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "Read config: %s", buf); -#endif - return 0; - } else { - /* No "get string" function defined; read character by character */ - register int c; - register size_t i = 0; - - buf[0] = '\0'; - /* skip leading whitespace */ - do { - c = cfp->getch(cfp->param); - } while (c == '\t' || c == ' '); - - if (c == EOF) - return 1; - - if(bufsize < 2) { - /* too small, assume caller is crazy */ - return 1; - } - - while (1) { - if ((c == '\t') || (c == ' ')) { - buf[i++] = ' '; - while ((c == '\t') || (c == ' ')) - c = cfp->getch(cfp->param); - } - if (c == CR) { - /* silently ignore CR (_assume_ that a LF follows) */ - c = cfp->getch(cfp->param); - } - if (c == LF) { - /* increase line number and return on LF */ - ++cfp->line_number; - } - if (c == EOF || c == 0x4 || c == LF || i >= (bufsize - 2)) { - /* - * check for line continuation - */ - if (i > 0 && buf[i-1] == '\\') { - i--; - if (!(i > 0 && buf[i-1] == '\\')) { - /* line is continued */ - c = cfp->getch(cfp->param); - continue; - } - /* else nothing needs be done because - * then the backslash is escaped and - * we just strip to a single one - */ - } - /* blast trailing whitespace */ - while (i > 0 && apr_isspace(buf[i - 1])) - --i; - buf[i] = '\0'; -#ifdef DEBUG_CFG_LINES - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, - "Read config: %s", buf); -#endif - return 0; - } - buf[i] = c; - ++i; - c = cfp->getch(cfp->param); - } - } -} -#else -static apr_status_t ap_cfg_getline_core(char *buf, apr_size_t bufsize, - ap_configfile_t *cfp) -{ - apr_status_t rc; - /* If a "get string" function is defined, use it */ - if (cfp->getstr != NULL) { - char *cp; - char *cbuf = buf; - apr_size_t cbufsize = bufsize; - - while (1) { - ++cfp->line_number; - rc = cfp->getstr(cbuf, cbufsize, cfp->param); - if (rc == APR_EOF) { - if (cbuf != buf) { - *cbuf = '\0'; - break; - } - else { - return APR_EOF; - } - } - if (rc != APR_SUCCESS) { - return rc; - } - - /* - * check for line continuation, - * i.e. match [^\\]\\[\r]\n only - */ - cp = cbuf; - cp += strlen(cp); - if (cp > cbuf && cp[-1] == LF) { - cp--; - if (cp > cbuf && cp[-1] == CR) - cp--; - if (cp > cbuf && cp[-1] == '\\') { - cp--; - /* - * line continuation requested - - * then remove backslash and continue - */ - cbufsize -= (cp-cbuf); - cbuf = cp; - continue; - } - } - else if (cp - buf >= bufsize - 1) { - return APR_ENOSPC; - } - break; - } - } else { - /* No "get string" function defined; read character by character */ - apr_size_t i = 0; - - if (bufsize < 2) { - /* too small, assume caller is crazy */ - return APR_EINVAL; - } - buf[0] = '\0'; - - while (1) { - char c; - rc = cfp->getch(&c, cfp->param); - if (rc == APR_EOF) { - if (i > 0) - break; - else - return APR_EOF; - } - if (rc != APR_SUCCESS) - return rc; - if (c == LF) { - ++cfp->line_number; - /* check for line continuation */ - if (i > 0 && buf[i-1] == '\\') { - i--; - continue; - } - else { - break; - } - } - else if (i >= bufsize - 2) { - return APR_ENOSPC; - } - buf[i] = c; - ++i; - } - buf[i] = '\0'; - } - return APR_SUCCESS; -} - -static int cfg_trim_line(char *buf) -{ - char *start, *end; - /* - * Leading and trailing white space is eliminated completely - */ - start = buf; - while (apr_isspace(*start)) - ++start; - /* blast trailing whitespace */ - end = &start[strlen(start)]; - while (--end >= start && apr_isspace(*end)) - *end = '\0'; - /* Zap leading whitespace by shifting */ - if (start != buf) - memmove(buf, start, end - start + 2); -#ifdef DEBUG_CFG_LINES - ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, APLOGNO(00555) "Read config: '%s'", buf); -#endif - return end - start + 1; -} - -AP_DECLARE(apr_status_t) ap_cfg_getline(char *buf, apr_size_t bufsize, - ap_configfile_t *cfp) -{ - apr_status_t rc = ap_cfg_getline_core(buf, bufsize, cfp); - if (rc == APR_SUCCESS) - cfg_trim_line(buf); - return rc; -} -#endif - -static char *substring_conf(apr_pool_t *p, const char *start, int len, - char quote) -{ - char *result = apr_palloc(p, len + 2); - char *resp = result; - int i; - - for (i = 0; i < len; ++i) { - if (start[i] == '\\' && (start[i + 1] == '\\' - || (quote && start[i + 1] == quote))) - *resp++ = start[++i]; - else - *resp++ = start[i]; - } - - *resp++ = '\0'; -#if RESOLVE_ENV_PER_TOKEN - return (char *)ap_resolve_env(p,result); -#else - return result; -#endif -} - -AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line) -{ - const char *str = *line, *strend; - char *res; - char quote; - - while (*str && apr_isspace(*str)) - ++str; - - if (!*str) { - *line = str; - return ""; - } - - if ((quote = *str) == '"' || quote == '\'') { - strend = str + 1; - while (*strend && *strend != quote) { - if (*strend == '\\' && strend[1] && - (strend[1] == quote || strend[1] == '\\')) { - strend += 2; - } - else { - ++strend; - } - } - res = substring_conf(p, str + 1, strend - str - 1, quote); - - if (*strend == quote) - ++strend; - } - else { - strend = str; - while (*strend && !apr_isspace(*strend)) - ++strend; - - res = substring_conf(p, str, strend - str, 0); - } - - while (*strend && apr_isspace(*strend)) - ++strend; - *line = strend; - return res; -} - -/* Open a ap_configfile_t as FILE, return open ap_configfile_t struct pointer */ -AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg, - apr_pool_t *p, const char *name) -{ - ap_configfile_t *new_cfg; - apr_file_t *file = NULL; - apr_finfo_t finfo; - apr_status_t status; -#ifdef DEBUG - char buf[120]; -#endif - - if (name == NULL) { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, - "Internal error: pcfg_openfile() called with NULL filename"); - return APR_EBADF; - } - - status = apr_file_open(&file, name, APR_READ | APR_BUFFERED, - APR_OS_DEFAULT, p); -#ifdef DEBUG - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, - "Opening config file %s (%s)", - name, (status != APR_SUCCESS) ? - apr_strerror(status, buf, sizeof(buf)) : "successful"); -#endif - if (status != APR_SUCCESS) - return status; - - status = apr_file_info_get(&finfo, APR_FINFO_TYPE, file); - if (status != APR_SUCCESS) - return status; - - if (finfo.filetype != APR_REG && -#if defined(WIN32) || defined(OS2) || defined(NETWARE) - strcasecmp(apr_filepath_name_get(name), "nul") != 0) { -#else - strcmp(name, "/dev/null") != 0) { -#endif /* WIN32 || OS2 */ - ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, - "Access to file %s denied by server: not a regular file", - name); - apr_file_close(file); - return APR_EBADF; - } - -#ifdef WIN32 - /* Some twisted character [no pun intended] at MS decided that a - * zero width joiner as the lead wide character would be ideal for - * describing Unicode text files. This was further convoluted to - * another MSism that the same character mapped into utf-8, EF BB BF - * would signify utf-8 text files. - * - * Since MS configuration files are all protecting utf-8 encoded - * Unicode path, file and resource names, we already have the correct - * WinNT encoding. But at least eat the stupid three bytes up front. - */ - { - unsigned char buf[4]; - apr_size_t len = 3; - status = apr_file_read(file, buf, &len); - if ((status != APR_SUCCESS) || (len < 3) - || memcmp(buf, "\xEF\xBB\xBF", 3) != 0) { - apr_off_t zero = 0; - apr_file_seek(file, APR_SET, &zero); - } - } -#endif - - new_cfg = apr_palloc(p, sizeof(*new_cfg)); - new_cfg->param = file; - new_cfg->name = apr_pstrdup(p, name); -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 - new_cfg->getch = (int (*)(void *)) cfg_getch; - new_cfg->getstr = (void *(*)(void *, size_t, void *)) cfg_getstr; - new_cfg->close = (int (*)(void *)) cfg_close; -#else - new_cfg->getch = cfg_getch; - new_cfg->getstr = cfg_getstr; - new_cfg->close = cfg_close; -#endif - new_cfg->line_number = 0; - *ret_cfg = new_cfg; - return APR_SUCCESS; -} - -AP_CORE_DECLARE(const command_rec *) ap_find_command(const char *name, - const command_rec *cmds) -{ - while (cmds->name) { - if (!strcasecmp(name, cmds->name)) - return cmds; - - ++cmds; - } - - return NULL; -} - -#define AP_MAX_ARGC 64 - -static const char *invoke_cmd(const command_rec *cmd, cmd_parms *parms, - void *mconfig, const char *args) -{ - char *w, *w2, *w3; - const char *errmsg = NULL; - - if ((parms->override & cmd->req_override) == 0) - return apr_pstrcat(parms->pool, cmd->name, " not allowed here", NULL); - - parms->info = cmd->cmd_data; - parms->cmd = cmd; - - switch (cmd->args_how) { - case RAW_ARGS: -#ifdef RESOLVE_ENV_PER_TOKEN - args = ap_resolve_env(parms->pool,args); -#endif - return cmd->AP_RAW_ARGS(parms, mconfig, args); - - case TAKE_ARGV: - { - char *argv[AP_MAX_ARGC]; - int argc = 0; - - do { - w = ap_getword_conf(parms->pool, &args); - if (*w == '\0' && *args == '\0') { - break; - } - argv[argc] = w; - argc++; - } while (argc < AP_MAX_ARGC && *args != '\0'); - - return cmd->AP_TAKE_ARGV(parms, mconfig, argc, argv); - } - - case NO_ARGS: - if (*args != 0) - return apr_pstrcat(parms->pool, cmd->name, " takes no arguments", - NULL); - - return cmd->AP_NO_ARGS(parms, mconfig); - - case TAKE1: - w = ap_getword_conf(parms->pool, &args); - - if (*w == '\0' || *args != 0) - return apr_pstrcat(parms->pool, cmd->name, " takes one argument", - cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); - - return cmd->AP_TAKE1(parms, mconfig, w); - - case TAKE2: - w = ap_getword_conf(parms->pool, &args); - w2 = ap_getword_conf(parms->pool, &args); - - if (*w == '\0' || *w2 == '\0' || *args != 0) - return apr_pstrcat(parms->pool, cmd->name, " takes two arguments", - cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); - - return cmd->AP_TAKE2(parms, mconfig, w, w2); - - case TAKE12: - w = ap_getword_conf(parms->pool, &args); - w2 = ap_getword_conf(parms->pool, &args); - - if (*w == '\0' || *args != 0) - return apr_pstrcat(parms->pool, cmd->name, " takes 1-2 arguments", - cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); - - return cmd->AP_TAKE2(parms, mconfig, w, *w2 ? w2 : NULL); - - case TAKE3: - w = ap_getword_conf(parms->pool, &args); - w2 = ap_getword_conf(parms->pool, &args); - w3 = ap_getword_conf(parms->pool, &args); - - if (*w == '\0' || *w2 == '\0' || *w3 == '\0' || *args != 0) - return apr_pstrcat(parms->pool, cmd->name, " takes three arguments", - cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); - - return cmd->AP_TAKE3(parms, mconfig, w, w2, w3); - - case TAKE23: - w = ap_getword_conf(parms->pool, &args); - w2 = ap_getword_conf(parms->pool, &args); - w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL; - - if (*w == '\0' || *w2 == '\0' || *args != 0) - return apr_pstrcat(parms->pool, cmd->name, - " takes two or three arguments", - cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); - - return cmd->AP_TAKE3(parms, mconfig, w, w2, w3); - - case TAKE123: - w = ap_getword_conf(parms->pool, &args); - w2 = *args ? ap_getword_conf(parms->pool, &args) : NULL; - w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL; - - if (*w == '\0' || *args != 0) - return apr_pstrcat(parms->pool, cmd->name, - " takes one, two or three arguments", - cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); - - return cmd->AP_TAKE3(parms, mconfig, w, w2, w3); - - case TAKE13: - w = ap_getword_conf(parms->pool, &args); - w2 = *args ? ap_getword_conf(parms->pool, &args) : NULL; - w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL; - - if (*w == '\0' || (w2 && *w2 && !w3) || *args != 0) - return apr_pstrcat(parms->pool, cmd->name, - " takes one or three arguments", - cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); - - return cmd->AP_TAKE3(parms, mconfig, w, w2, w3); - - case ITERATE: - while (*(w = ap_getword_conf(parms->pool, &args)) != '\0') { - - errmsg = cmd->AP_TAKE1(parms, mconfig, w); - - if (errmsg && strcmp(errmsg, DECLINE_CMD) != 0) - return errmsg; - } - - return errmsg; - - case ITERATE2: - w = ap_getword_conf(parms->pool, &args); - - if (*w == '\0' || *args == 0) - return apr_pstrcat(parms->pool, cmd->name, - " requires at least two arguments", - cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); - - while (*(w2 = ap_getword_conf(parms->pool, &args)) != '\0') { - - errmsg = cmd->AP_TAKE2(parms, mconfig, w, w2); - - if (errmsg && strcmp(errmsg, DECLINE_CMD) != 0) - return errmsg; - } - - return errmsg; - - case FLAG: - w = ap_getword_conf(parms->pool, &args); - - if (*w == '\0' || (strcasecmp(w, "on") && strcasecmp(w, "off"))) - return apr_pstrcat(parms->pool, cmd->name, " must be On or Off", - NULL); - - return cmd->AP_FLAG(parms, mconfig, strcasecmp(w, "off") != 0); - - default: - return apr_pstrcat(parms->pool, cmd->name, - " is improperly configured internally (server bug)", - NULL); - } -} - -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 -static cmd_parms default_parms = -{NULL, 0, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; -#endif - -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 3 -static cmd_parms default_parms = -{NULL, 0, 0, NULL, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; -#endif - -typedef struct { - const char *fname; -} fnames; - -AP_DECLARE(int) ap_is_directory(apr_pool_t *p, const char *path) -{ - apr_finfo_t finfo; - - if (apr_stat(&finfo, path, APR_FINFO_TYPE, p) != APR_SUCCESS) - return 0; /* in error condition, just return no */ - - return (finfo.filetype == APR_DIR); -} - -AP_DECLARE(char *) ap_make_full_path(apr_pool_t *a, const char *src1, - const char *src2) -{ - apr_size_t len1, len2; - char *path; - - len1 = strlen(src1); - len2 = strlen(src2); - /* allocate +3 for '/' delimiter, trailing NULL and overallocate - * one extra byte to allow the caller to add a trailing '/' - */ - path = (char *)apr_palloc(a, len1 + len2 + 3); - if (len1 == 0) { - *path = '/'; - memcpy(path + 1, src2, len2 + 1); - } - else { - char *next; - memcpy(path, src1, len1); - next = path + len1; - if (next[-1] != '/') { - *next++ = '/'; - } - memcpy(next, src2, len2 + 1); - } - return path; -} - -static int fname_alphasort(const void *fn1, const void *fn2) -{ - const fnames *f1 = fn1; - const fnames *f2 = fn2; - - return strcmp(f1->fname,f2->fname); -} - -AP_DECLARE(const char *) process_resource_config(const char *fname, - apr_array_header_t *ari, - apr_pool_t *ptemp) -{ - *(char **)apr_array_push(ari) = (char *)fname; - - return NULL; -} - -static const char *process_resource_config_nofnmatch(const char *fname, - apr_array_header_t *ari, - apr_pool_t *p, - apr_pool_t *ptemp, - unsigned depth, - int optional) -{ - const char *error; - apr_status_t rv; - - if (ap_is_directory(ptemp, fname)) { - apr_dir_t *dirp; - apr_finfo_t dirent; - int current; - apr_array_header_t *candidates = NULL; - fnames *fnew; - char *path = apr_pstrdup(ptemp, fname); - - if (++depth > 100) { - return apr_psprintf(p, "Directory %s exceeds the maximum include " - "directory nesting level of %u. You have " - "probably a recursion somewhere.", path, - 100); - } - - /* - * first course of business is to grok all the directory - * entries here and store 'em away. Recall we need full pathnames - * for this. - */ - rv = apr_dir_open(&dirp, path, ptemp); - if (rv != APR_SUCCESS) { - char errmsg[120]; - return apr_psprintf(p, "Could not open config directory %s: %s", - path, apr_strerror(rv, errmsg, sizeof errmsg)); - } - - candidates = apr_array_make(ptemp, 1, sizeof(fnames)); - while (apr_dir_read(&dirent, APR_FINFO_DIRENT, dirp) == APR_SUCCESS) { - /* strip out '.' and '..' */ - if (strcmp(dirent.name, ".") - && strcmp(dirent.name, "..")) { - fnew = (fnames *) apr_array_push(candidates); - fnew->fname = ap_make_full_path(ptemp, path, dirent.name); - } - } - - apr_dir_close(dirp); - if (candidates->nelts != 0) { - qsort((void *) candidates->elts, candidates->nelts, - sizeof(fnames), fname_alphasort); - - /* - * Now recurse these... we handle errors and subdirectories - * via the recursion, which is nice - */ - for (current = 0; current < candidates->nelts; ++current) { - fnew = &((fnames *) candidates->elts)[current]; - error = process_resource_config_nofnmatch(fnew->fname, - ari, p, ptemp, - depth, optional); - if (error) { - return error; - } - } - } - - return NULL; - } - - return process_resource_config(fname, ari, ptemp); -} - -static const char *process_resource_config_fnmatch(const char *path, - const char *fname, - apr_array_header_t *ari, - apr_pool_t *p, - apr_pool_t *ptemp, - unsigned depth, - int optional) -{ - const char *rest; - apr_status_t rv; - apr_dir_t *dirp; - apr_finfo_t dirent; - apr_array_header_t *candidates = NULL; - fnames *fnew; - int current; - - /* find the first part of the filename */ - rest = ap_strchr_c(fname, '/'); - - if(rest == NULL) - rest = ap_strchr_c(fname, '\\'); - - if (rest) { - fname = apr_pstrndup(ptemp, fname, rest - fname); - rest++; - } - - /* optimisation - if the filename isn't a wildcard, process it directly */ - if (!apr_fnmatch_test(fname)) { - path = ap_make_full_path(ptemp, path, fname); - if (!rest) { - return process_resource_config_nofnmatch(path, - ari, p, - ptemp, 0, optional); - } - else { - return process_resource_config_fnmatch(path, rest, - ari, p, - ptemp, 0, optional); - } - } - - /* - * first course of business is to grok all the directory - * entries here and store 'em away. Recall we need full pathnames - * for this. - */ - rv = apr_dir_open(&dirp, path, ptemp); - if (rv != APR_SUCCESS) { - char errmsg[120]; - return apr_psprintf(p, "Could not open config directory %s: %s", - path, apr_strerror(rv, errmsg, sizeof errmsg)); - } - - candidates = apr_array_make(ptemp, 1, sizeof(fnames)); - while (apr_dir_read(&dirent, APR_FINFO_DIRENT | APR_FINFO_TYPE, dirp) == APR_SUCCESS) { - /* strip out '.' and '..' */ - if (strcmp(dirent.name, ".") - && strcmp(dirent.name, "..") - && (apr_fnmatch(fname, dirent.name, - APR_FNM_PERIOD) == APR_SUCCESS)) { - const char *full_path = ap_make_full_path(ptemp, path, dirent.name); - /* If matching internal to path, and we happen to match something - * other than a directory, skip it - */ - if (rest && (rv == APR_SUCCESS) && (dirent.filetype != APR_DIR)) { - continue; - } - fnew = (fnames *) apr_array_push(candidates); - fnew->fname = full_path; - } - } - - apr_dir_close(dirp); - if (candidates->nelts != 0) { - const char *error; - - qsort((void *) candidates->elts, candidates->nelts, - sizeof(fnames), fname_alphasort); - - /* - * Now recurse these... we handle errors and subdirectories - * via the recursion, which is nice - */ - for (current = 0; current < candidates->nelts; ++current) { - fnew = &((fnames *) candidates->elts)[current]; - if (!rest) { - error = process_resource_config_nofnmatch(fnew->fname, - ari, p, - ptemp, 0, optional); - } - else { - error = process_resource_config_fnmatch(fnew->fname, rest, - ari, p, - ptemp, 0, optional); - } - if (error) { - return error; - } - } - } - else { - - if (!optional) { - return apr_psprintf(p, "No matches for the wildcard '%s' in '%s', failing " - "(use IncludeOptional if required)", fname, path); - } - } - - return NULL; -} - -AP_DECLARE(const char *) process_fnmatch_configs(apr_array_header_t *ari, - const char *fname, - apr_pool_t *p, - apr_pool_t *ptemp, - int optional) -{ - if (!apr_fnmatch_test(fname)) { - return process_resource_config(fname, ari, p); - } - else { - apr_status_t status; - const char *rootpath, *filepath = fname; - - /* locate the start of the directories proper */ - status = apr_filepath_root(&rootpath, &filepath, APR_FILEPATH_TRUENAME | APR_FILEPATH_NATIVE, ptemp); - - /* we allow APR_SUCCESS and APR_EINCOMPLETE */ - if (APR_ERELATIVE == status) { - return apr_pstrcat(p, "Include must have an absolute path, ", fname, NULL); - } - else if (APR_EBADPATH == status) { - return apr_pstrcat(p, "Include has a bad path, ", fname, NULL); - } - - /* walk the filepath */ - return process_resource_config_fnmatch(rootpath, filepath, ari, p, ptemp, - 0, optional); - } -} - -const char *populate_include_files(apr_pool_t *p, apr_pool_t *ptemp, apr_array_header_t *ari, const char *fname, int optional) -{ - return process_fnmatch_configs(ari, fname, p, ptemp, optional); -} - -const char *process_command_config(server_rec *s, - void *mconfig, - apr_pool_t *p, - apr_pool_t *ptemp, - const char *filename) -{ - const char *errmsg; - char *l = apr_palloc (ptemp, MAX_STRING_LEN); - const char *args = l; - char *cmd_name, *w; - const command_rec *cmd; - apr_array_header_t *arr = apr_array_make(p, 1, sizeof(cmd_parms)); - apr_array_header_t *ari = apr_array_make(p, 1, sizeof(char *)); - cmd_parms *parms; - apr_status_t status; - ap_directive_t *newdir; - int optional; - char *err = NULL; - - errmsg = populate_include_files(p, ptemp, ari, filename, 0); - - if(errmsg != NULL) - goto Exit; - - while(ari->nelts != 0 || arr->nelts != 0) - { - if(ari->nelts > 0) - { - char *fn = *(char **)apr_array_pop(ari); - - parms = (cmd_parms *)apr_array_push(arr); - *parms = default_parms; - parms->pool = p; - parms->temp_pool = ptemp; - parms->server = s; - parms->override = (RSRC_CONF | OR_ALL) & ~(OR_AUTHCFG | OR_LIMIT); - parms->override_opts = OPT_ALL | OPT_SYM_OWNER | OPT_MULTI; - - status = ap_pcfg_openfile(&parms->config_file, p, fn); - - if(status != APR_SUCCESS) - { - apr_array_pop(arr); - errmsg = apr_pstrcat(p, "Cannot open config file: ", fn, NULL); - goto Exit; - } - } - - if (arr->nelts > 1024) { - errmsg = "Exceeded the maximum include directory nesting level. You have " - "probably a recursion somewhere."; - goto Exit; - } - - parms = (cmd_parms *)apr_array_pop(arr); - - if(parms == NULL) - break; - - while (!(ap_cfg_getline(l, MAX_STRING_LEN, parms->config_file))) { - if (*l == '#' || *l == '\0') - continue; - - args = l; - - cmd_name = ap_getword_conf(p, &args); - - if (*cmd_name == '\0') - continue; - - if (!strcasecmp(cmd_name, "IncludeOptional")) - { - optional = 1; - goto ProcessInclude; - } - - if (!strcasecmp(cmd_name, "Include")) - { - optional = 0; -ProcessInclude: - w = ap_getword_conf(parms->pool, &args); - - if (*w == '\0' || *args != 0) - { - ap_cfg_closefile(parms->config_file); - errmsg = apr_pstrcat(parms->pool, "Include takes one argument", NULL); - goto Exit; - } - - errmsg = populate_include_files(p, ptemp, ari, w, optional); - - *(cmd_parms *)apr_array_push(arr) = *parms; - - if(errmsg != NULL) - goto Exit; - - // we don't want to close the current file yet - // - parms = NULL; - break; - } - - cmd = ap_find_command(cmd_name, security2_module.cmds); - - if(cmd == NULL) - { - // unknown command, should error - // - ap_cfg_closefile(parms->config_file); - errmsg = apr_pstrcat(p, "Unknown command in config: ", cmd_name, NULL); - goto Exit; - } - - newdir = apr_pcalloc(p, sizeof(ap_directive_t)); - newdir->filename = parms->config_file->name; - newdir->line_num = parms->config_file->line_number; - newdir->directive = cmd_name; - newdir->args = apr_pstrdup(p, args); - - parms->directive = newdir; - - errmsg = invoke_cmd(cmd, parms, mconfig, args); - - if(errmsg != NULL) - break; - } - - if(parms != NULL) - ap_cfg_closefile(parms->config_file); - - if(errmsg != NULL) - break; - } - - if (errmsg) { - err = (char *)apr_palloc(p, 1024); - - if(parms != NULL) - apr_snprintf(err, 1024, "Syntax error in config file %s, line %d: %s", parms->config_file->name, - parms->config_file->line_number, errmsg); - else - apr_snprintf(err, 1024, "Syntax error in config file: %s", errmsg); - } - - errmsg = err; - -Exit: - while((parms = (cmd_parms *)apr_array_pop(arr)) != NULL) - { - ap_cfg_closefile(parms->config_file); - } - - return errmsg; -} +/* +* ModSecurity for Apache 2.x, http://www.modsecurity.org/ +* Copyright (c) 2004-2011 Trustwave Holdings, Inc. (http://www.trustwave.com/) +* +* You may not use this file except in compliance with +* the License.  You may obtain a copy of the License at +* +*     http://www.apache.org/licenses/LICENSE-2.0 +* +* If any of the files related to licensing are missing or if you have any +* other questions related to licensing please contact Trustwave Holdings, Inc. +* directly using the email address security@modsecurity.org. +*/ + +#include +#include + +#include "http_core.h" +#include "http_request.h" + +#include "modsecurity.h" +#include "apache2.h" +#include "http_main.h" +#include "http_connection.h" + +#include "apr_optional.h" +#include "mod_log_config.h" + +#include "msc_logging.h" +#include "msc_util.h" + +#include "ap_mpm.h" +#include "scoreboard.h" + +#include "apr_version.h" + +#include "apr_lib.h" +#include "ap_config.h" +#include "http_config.h" +#include "apr_fnmatch.h" + +AP_DECLARE(int) ap_cfg_closefile(ap_configfile_t *cfp) +{ +#ifdef DEBUG + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, + "Done with config file %s", cfp->name); +#endif + return (cfp->close == NULL) ? 0 : cfp->close(cfp->param); +} + +#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 +static apr_status_t cfg_close(void *param) +{ + apr_file_t *cfp = (apr_file_t *) param; + return (apr_file_close(cfp)); +} + +static int cfg_getch(void *param) +{ + char ch; + apr_file_t *cfp = (apr_file_t *) param; + if (apr_file_getc(&ch, cfp) == APR_SUCCESS) + return ch; + return (int)EOF; +} + +static void *cfg_getstr(void *buf, size_t bufsiz, void *param) +{ + apr_file_t *cfp = (apr_file_t *) param; + apr_status_t rv; + rv = apr_file_gets(buf, bufsiz, cfp); + if (rv == APR_SUCCESS) { + return buf; + } + return NULL; +} +#else +/* we can't use apr_file_* directly because of linking issues on Windows */ +static apr_status_t cfg_close(void *param) +{ + return apr_file_close(param); +} + +static apr_status_t cfg_getch(char *ch, void *param) +{ + return apr_file_getc(ch, param); +} + +static apr_status_t cfg_getstr(void *buf, apr_size_t bufsiz, void *param) +{ + return apr_file_gets(buf, bufsiz, param); +} +#endif + +/* Read one line from open ap_configfile_t, strip LF, increase line number */ +/* If custom handler does not define a getstr() function, read char by char */ + +#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 +AP_DECLARE(int) ap_cfg_getline(char *buf, size_t bufsize, ap_configfile_t *cfp) +{ + /* If a "get string" function is defined, use it */ + if (cfp->getstr != NULL) { + char *src, *dst; + char *cp; + char *cbuf = buf; + size_t cbufsize = bufsize; + + while (1) { + ++cfp->line_number; + if (cfp->getstr(cbuf, cbufsize, cfp->param) == NULL) + return 1; + + /* + * check for line continuation, + * i.e. match [^\\]\\[\r]\n only + */ + cp = cbuf; + while (cp < cbuf+cbufsize && *cp != '\0') + cp++; + if (cp > cbuf && cp[-1] == LF) { + cp--; + if (cp > cbuf && cp[-1] == CR) + cp--; + if (cp > cbuf && cp[-1] == '\\') { + cp--; + if (!(cp > cbuf && cp[-1] == '\\')) { + /* + * line continuation requested - + * then remove backslash and continue + */ + cbufsize -= (cp-cbuf); + cbuf = cp; + continue; + } + else { + /* + * no real continuation because escaped - + * then just remove escape character + */ + for ( ; cp < cbuf+cbufsize && *cp != '\0'; cp++) + cp[0] = cp[1]; + } + } + } + break; + } + + /* + * Leading and trailing white space is eliminated completely + */ + src = buf; + while (apr_isspace(*src)) + ++src; + /* blast trailing whitespace */ + dst = &src[strlen(src)]; + while (--dst >= src && apr_isspace(*dst)) + *dst = '\0'; + /* Zap leading whitespace by shifting */ + if (src != buf) + for (dst = buf; (*dst++ = *src++) != '\0'; ) + ; + +#ifdef DEBUG_CFG_LINES + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "Read config: %s", buf); +#endif + return 0; + } else { + /* No "get string" function defined; read character by character */ + register int c; + register size_t i = 0; + + buf[0] = '\0'; + /* skip leading whitespace */ + do { + c = cfp->getch(cfp->param); + } while (c == '\t' || c == ' '); + + if (c == EOF) + return 1; + + if(bufsize < 2) { + /* too small, assume caller is crazy */ + return 1; + } + + while (1) { + if ((c == '\t') || (c == ' ')) { + buf[i++] = ' '; + while ((c == '\t') || (c == ' ')) + c = cfp->getch(cfp->param); + } + if (c == CR) { + /* silently ignore CR (_assume_ that a LF follows) */ + c = cfp->getch(cfp->param); + } + if (c == LF) { + /* increase line number and return on LF */ + ++cfp->line_number; + } + if (c == EOF || c == 0x4 || c == LF || i >= (bufsize - 2)) { + /* + * check for line continuation + */ + if (i > 0 && buf[i-1] == '\\') { + i--; + if (!(i > 0 && buf[i-1] == '\\')) { + /* line is continued */ + c = cfp->getch(cfp->param); + continue; + } + /* else nothing needs be done because + * then the backslash is escaped and + * we just strip to a single one + */ + } + /* blast trailing whitespace */ + while (i > 0 && apr_isspace(buf[i - 1])) + --i; + buf[i] = '\0'; +#ifdef DEBUG_CFG_LINES + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, + "Read config: %s", buf); +#endif + return 0; + } + buf[i] = c; + ++i; + c = cfp->getch(cfp->param); + } + } +} +#else +static apr_status_t ap_cfg_getline_core(char *buf, apr_size_t bufsize, + ap_configfile_t *cfp) +{ + apr_status_t rc; + /* If a "get string" function is defined, use it */ + if (cfp->getstr != NULL) { + char *cp; + char *cbuf = buf; + apr_size_t cbufsize = bufsize; + + while (1) { + ++cfp->line_number; + rc = cfp->getstr(cbuf, cbufsize, cfp->param); + if (rc == APR_EOF) { + if (cbuf != buf) { + *cbuf = '\0'; + break; + } + else { + return APR_EOF; + } + } + if (rc != APR_SUCCESS) { + return rc; + } + + /* + * check for line continuation, + * i.e. match [^\\]\\[\r]\n only + */ + cp = cbuf; + cp += strlen(cp); + if (cp > cbuf && cp[-1] == LF) { + cp--; + if (cp > cbuf && cp[-1] == CR) + cp--; + if (cp > cbuf && cp[-1] == '\\') { + cp--; + /* + * line continuation requested - + * then remove backslash and continue + */ + cbufsize -= (cp-cbuf); + cbuf = cp; + continue; + } + } + else if (cp - buf >= bufsize - 1) { + return APR_ENOSPC; + } + break; + } + } else { + /* No "get string" function defined; read character by character */ + apr_size_t i = 0; + + if (bufsize < 2) { + /* too small, assume caller is crazy */ + return APR_EINVAL; + } + buf[0] = '\0'; + + while (1) { + char c; + rc = cfp->getch(&c, cfp->param); + if (rc == APR_EOF) { + if (i > 0) + break; + else + return APR_EOF; + } + if (rc != APR_SUCCESS) + return rc; + if (c == LF) { + ++cfp->line_number; + /* check for line continuation */ + if (i > 0 && buf[i-1] == '\\') { + i--; + continue; + } + else { + break; + } + } + else if (i >= bufsize - 2) { + return APR_ENOSPC; + } + buf[i] = c; + ++i; + } + buf[i] = '\0'; + } + return APR_SUCCESS; +} + +static int cfg_trim_line(char *buf) +{ + char *start, *end; + /* + * Leading and trailing white space is eliminated completely + */ + start = buf; + while (apr_isspace(*start)) + ++start; + /* blast trailing whitespace */ + end = &start[strlen(start)]; + while (--end >= start && apr_isspace(*end)) + *end = '\0'; + /* Zap leading whitespace by shifting */ + if (start != buf) + memmove(buf, start, end - start + 2); +#ifdef DEBUG_CFG_LINES + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, APLOGNO(00555) "Read config: '%s'", buf); +#endif + return end - start + 1; +} + +AP_DECLARE(apr_status_t) ap_cfg_getline(char *buf, apr_size_t bufsize, + ap_configfile_t *cfp) +{ + apr_status_t rc = ap_cfg_getline_core(buf, bufsize, cfp); + if (rc == APR_SUCCESS) + cfg_trim_line(buf); + return rc; +} +#endif + +static char *substring_conf(apr_pool_t *p, const char *start, int len, + char quote) +{ + char *result = apr_palloc(p, len + 2); + char *resp = result; + int i; + + for (i = 0; i < len; ++i) { + if (start[i] == '\\' && (start[i + 1] == '\\' + || (quote && start[i + 1] == quote))) + *resp++ = start[++i]; + else + *resp++ = start[i]; + } + + *resp++ = '\0'; +#if RESOLVE_ENV_PER_TOKEN + return (char *)ap_resolve_env(p,result); +#else + return result; +#endif +} + +AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line) +{ + const char *str = *line, *strend; + char *res; + char quote; + + while (*str && apr_isspace(*str)) + ++str; + + if (!*str) { + *line = str; + return ""; + } + + if ((quote = *str) == '"' || quote == '\'') { + strend = str + 1; + while (*strend && *strend != quote) { + if (*strend == '\\' && strend[1] && + (strend[1] == quote || strend[1] == '\\')) { + strend += 2; + } + else { + ++strend; + } + } + res = substring_conf(p, str + 1, strend - str - 1, quote); + + if (*strend == quote) + ++strend; + } + else { + strend = str; + while (*strend && !apr_isspace(*strend)) + ++strend; + + res = substring_conf(p, str, strend - str, 0); + } + + while (*strend && apr_isspace(*strend)) + ++strend; + *line = strend; + return res; +} + +/* Open a ap_configfile_t as FILE, return open ap_configfile_t struct pointer */ +AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg, + apr_pool_t *p, const char *name) +{ + ap_configfile_t *new_cfg; + apr_file_t *file = NULL; + apr_finfo_t finfo; + apr_status_t status; +#ifdef DEBUG + char buf[120]; +#endif + + if (name == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "Internal error: pcfg_openfile() called with NULL filename"); + return APR_EBADF; + } + + status = apr_file_open(&file, name, APR_READ | APR_BUFFERED, + APR_OS_DEFAULT, p); +#ifdef DEBUG + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, + "Opening config file %s (%s)", + name, (status != APR_SUCCESS) ? + apr_strerror(status, buf, sizeof(buf)) : "successful"); +#endif + if (status != APR_SUCCESS) + return status; + + status = apr_file_info_get(&finfo, APR_FINFO_TYPE, file); + if (status != APR_SUCCESS) + return status; + + if (finfo.filetype != APR_REG && +#if defined(WIN32) || defined(OS2) || defined(NETWARE) + strcasecmp(apr_filepath_name_get(name), "nul") != 0) { +#else + strcmp(name, "/dev/null") != 0) { +#endif /* WIN32 || OS2 */ + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "Access to file %s denied by server: not a regular file", + name); + apr_file_close(file); + return APR_EBADF; + } + +#ifdef WIN32 + /* Some twisted character [no pun intended] at MS decided that a + * zero width joiner as the lead wide character would be ideal for + * describing Unicode text files. This was further convoluted to + * another MSism that the same character mapped into utf-8, EF BB BF + * would signify utf-8 text files. + * + * Since MS configuration files are all protecting utf-8 encoded + * Unicode path, file and resource names, we already have the correct + * WinNT encoding. But at least eat the stupid three bytes up front. + */ + { + unsigned char buf[4]; + apr_size_t len = 3; + status = apr_file_read(file, buf, &len); + if ((status != APR_SUCCESS) || (len < 3) + || memcmp(buf, "\xEF\xBB\xBF", 3) != 0) { + apr_off_t zero = 0; + apr_file_seek(file, APR_SET, &zero); + } + } +#endif + + new_cfg = apr_palloc(p, sizeof(*new_cfg)); + new_cfg->param = file; + new_cfg->name = apr_pstrdup(p, name); +#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 + new_cfg->getch = (int (*)(void *)) cfg_getch; + new_cfg->getstr = (void *(*)(void *, size_t, void *)) cfg_getstr; + new_cfg->close = (int (*)(void *)) cfg_close; +#else + new_cfg->getch = cfg_getch; + new_cfg->getstr = cfg_getstr; + new_cfg->close = cfg_close; +#endif + new_cfg->line_number = 0; + *ret_cfg = new_cfg; + return APR_SUCCESS; +} + +AP_CORE_DECLARE(const command_rec *) ap_find_command(const char *name, + const command_rec *cmds) +{ + while (cmds->name) { + if (!strcasecmp(name, cmds->name)) + return cmds; + + ++cmds; + } + + return NULL; +} + +#define AP_MAX_ARGC 64 + +static const char *invoke_cmd(const command_rec *cmd, cmd_parms *parms, + void *mconfig, const char *args) +{ + char *w, *w2, *w3; + const char *errmsg = NULL; + + if ((parms->override & cmd->req_override) == 0) + return apr_pstrcat(parms->pool, cmd->name, " not allowed here", NULL); + + parms->info = cmd->cmd_data; + parms->cmd = cmd; + + switch (cmd->args_how) { + case RAW_ARGS: +#ifdef RESOLVE_ENV_PER_TOKEN + args = ap_resolve_env(parms->pool,args); +#endif + return cmd->AP_RAW_ARGS(parms, mconfig, args); + + case TAKE_ARGV: + { + char *argv[AP_MAX_ARGC]; + int argc = 0; + + do { + w = ap_getword_conf(parms->pool, &args); + if (*w == '\0' && *args == '\0') { + break; + } + argv[argc] = w; + argc++; + } while (argc < AP_MAX_ARGC && *args != '\0'); + + return cmd->AP_TAKE_ARGV(parms, mconfig, argc, argv); + } + + case NO_ARGS: + if (*args != 0) + return apr_pstrcat(parms->pool, cmd->name, " takes no arguments", + NULL); + + return cmd->AP_NO_ARGS(parms, mconfig); + + case TAKE1: + w = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || *args != 0) + return apr_pstrcat(parms->pool, cmd->name, " takes one argument", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return cmd->AP_TAKE1(parms, mconfig, w); + + case TAKE2: + w = ap_getword_conf(parms->pool, &args); + w2 = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || *w2 == '\0' || *args != 0) + return apr_pstrcat(parms->pool, cmd->name, " takes two arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return cmd->AP_TAKE2(parms, mconfig, w, w2); + + case TAKE12: + w = ap_getword_conf(parms->pool, &args); + w2 = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || *args != 0) + return apr_pstrcat(parms->pool, cmd->name, " takes 1-2 arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return cmd->AP_TAKE2(parms, mconfig, w, *w2 ? w2 : NULL); + + case TAKE3: + w = ap_getword_conf(parms->pool, &args); + w2 = ap_getword_conf(parms->pool, &args); + w3 = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || *w2 == '\0' || *w3 == '\0' || *args != 0) + return apr_pstrcat(parms->pool, cmd->name, " takes three arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return cmd->AP_TAKE3(parms, mconfig, w, w2, w3); + + case TAKE23: + w = ap_getword_conf(parms->pool, &args); + w2 = ap_getword_conf(parms->pool, &args); + w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL; + + if (*w == '\0' || *w2 == '\0' || *args != 0) + return apr_pstrcat(parms->pool, cmd->name, + " takes two or three arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return cmd->AP_TAKE3(parms, mconfig, w, w2, w3); + + case TAKE123: + w = ap_getword_conf(parms->pool, &args); + w2 = *args ? ap_getword_conf(parms->pool, &args) : NULL; + w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL; + + if (*w == '\0' || *args != 0) + return apr_pstrcat(parms->pool, cmd->name, + " takes one, two or three arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return cmd->AP_TAKE3(parms, mconfig, w, w2, w3); + + case TAKE13: + w = ap_getword_conf(parms->pool, &args); + w2 = *args ? ap_getword_conf(parms->pool, &args) : NULL; + w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL; + + if (*w == '\0' || (w2 && *w2 && !w3) || *args != 0) + return apr_pstrcat(parms->pool, cmd->name, + " takes one or three arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return cmd->AP_TAKE3(parms, mconfig, w, w2, w3); + + case ITERATE: + while (*(w = ap_getword_conf(parms->pool, &args)) != '\0') { + + errmsg = cmd->AP_TAKE1(parms, mconfig, w); + + if (errmsg && strcmp(errmsg, DECLINE_CMD) != 0) + return errmsg; + } + + return errmsg; + + case ITERATE2: + w = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || *args == 0) + return apr_pstrcat(parms->pool, cmd->name, + " requires at least two arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + while (*(w2 = ap_getword_conf(parms->pool, &args)) != '\0') { + + errmsg = cmd->AP_TAKE2(parms, mconfig, w, w2); + + if (errmsg && strcmp(errmsg, DECLINE_CMD) != 0) + return errmsg; + } + + return errmsg; + + case FLAG: + w = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || (strcasecmp(w, "on") && strcasecmp(w, "off"))) + return apr_pstrcat(parms->pool, cmd->name, " must be On or Off", + NULL); + + return cmd->AP_FLAG(parms, mconfig, strcasecmp(w, "off") != 0); + + default: + return apr_pstrcat(parms->pool, cmd->name, + " is improperly configured internally (server bug)", + NULL); + } +} + +#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 +static cmd_parms default_parms = +{NULL, 0, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; +#endif + +#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 3 +static cmd_parms default_parms = +{NULL, 0, 0, NULL, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; +#endif + +typedef struct { + const char *fname; +} fnames; + +AP_DECLARE(int) ap_is_directory(apr_pool_t *p, const char *path) +{ + apr_finfo_t finfo; + + if (apr_stat(&finfo, path, APR_FINFO_TYPE, p) != APR_SUCCESS) + return 0; /* in error condition, just return no */ + + return (finfo.filetype == APR_DIR); +} + +AP_DECLARE(char *) ap_make_full_path(apr_pool_t *a, const char *src1, + const char *src2) +{ + apr_size_t len1, len2; + char *path; + + len1 = strlen(src1); + len2 = strlen(src2); + /* allocate +3 for '/' delimiter, trailing NULL and overallocate + * one extra byte to allow the caller to add a trailing '/' + */ + path = (char *)apr_palloc(a, len1 + len2 + 3); + if (len1 == 0) { + *path = '/'; + memcpy(path + 1, src2, len2 + 1); + } + else { + char *next; + memcpy(path, src1, len1); + next = path + len1; + if (next[-1] != '/') { + *next++ = '/'; + } + memcpy(next, src2, len2 + 1); + } + return path; +} + +static int fname_alphasort(const void *fn1, const void *fn2) +{ + const fnames *f1 = fn1; + const fnames *f2 = fn2; + + return strcmp(f1->fname,f2->fname); +} + +int fnmatch_test(const char *pattern) +{ + int nesting; + + nesting = 0; + while (*pattern) { + switch (*pattern) { + case '?': + case '*': + return 1; + +/* case '\\': + if (*++pattern == '\0') { + return 0; + } + break;*/ // this breaks on Windows + + case '[': /* '[' is only a glob if it has a matching ']' */ + ++nesting; + break; + + case ']': + if (nesting) { + return 1; + } + break; + } + ++pattern; } + return 0; +} + +AP_DECLARE(const char *) process_resource_config(const char *fname, + apr_array_header_t *ari, + apr_pool_t *ptemp) +{ + *(char **)apr_array_push(ari) = (char *)fname; + + return NULL; +} + +static const char *process_resource_config_nofnmatch(const char *fname, + apr_array_header_t *ari, + apr_pool_t *p, + apr_pool_t *ptemp, + unsigned depth, + int optional) +{ + const char *error; + apr_status_t rv; + + if (ap_is_directory(ptemp, fname)) { + apr_dir_t *dirp; + apr_finfo_t dirent; + int current; + apr_array_header_t *candidates = NULL; + fnames *fnew; + char *path = apr_pstrdup(ptemp, fname); + + if (++depth > 100) { + return apr_psprintf(p, "Directory %s exceeds the maximum include " + "directory nesting level of %u. You have " + "probably a recursion somewhere.", path, + 100); + } + + /* + * first course of business is to grok all the directory + * entries here and store 'em away. Recall we need full pathnames + * for this. + */ + rv = apr_dir_open(&dirp, path, ptemp); + if (rv != APR_SUCCESS) { + char errmsg[120]; + return apr_psprintf(p, "Could not open config directory %s: %s", + path, apr_strerror(rv, errmsg, sizeof errmsg)); + } + + candidates = apr_array_make(ptemp, 1, sizeof(fnames)); + while (apr_dir_read(&dirent, APR_FINFO_DIRENT, dirp) == APR_SUCCESS) { + /* strip out '.' and '..' */ + if (strcmp(dirent.name, ".") + && strcmp(dirent.name, "..")) { + fnew = (fnames *) apr_array_push(candidates); + fnew->fname = ap_make_full_path(ptemp, path, dirent.name); + } + } + + apr_dir_close(dirp); + if (candidates->nelts != 0) { + qsort((void *) candidates->elts, candidates->nelts, + sizeof(fnames), fname_alphasort); + + /* + * Now recurse these... we handle errors and subdirectories + * via the recursion, which is nice + */ + for (current = 0; current < candidates->nelts; ++current) { + fnew = &((fnames *) candidates->elts)[current]; + error = process_resource_config_nofnmatch(fnew->fname, + ari, p, ptemp, + depth, optional); + if (error) { + return error; + } + } + } + + return NULL; + } + + return process_resource_config(fname, ari, ptemp); +} + +static const char *process_resource_config_fnmatch(const char *path, + const char *fname, + apr_array_header_t *ari, + apr_pool_t *p, + apr_pool_t *ptemp, + unsigned depth, + int optional) +{ + const char *rest; + apr_status_t rv; + apr_dir_t *dirp; + apr_finfo_t dirent; + apr_array_header_t *candidates = NULL; + fnames *fnew; + int current; + + /* find the first part of the filename */ + rest = ap_strchr_c(fname, '/'); + + if(rest == NULL) + rest = ap_strchr_c(fname, '\\'); + + if (rest) { + fname = apr_pstrndup(ptemp, fname, rest - fname); + rest++; + } + + /* optimisation - if the filename isn't a wildcard, process it directly */ + if (!fnmatch_test(fname)) { + path = ap_make_full_path(ptemp, path, fname); + if (!rest) { + return process_resource_config_nofnmatch(path, + ari, p, + ptemp, 0, optional); + } + else { + return process_resource_config_fnmatch(path, rest, + ari, p, + ptemp, 0, optional); + } + } + + /* + * first course of business is to grok all the directory + * entries here and store 'em away. Recall we need full pathnames + * for this. + */ + rv = apr_dir_open(&dirp, path, ptemp); + if (rv != APR_SUCCESS) { + char errmsg[120]; + return apr_psprintf(p, "Could not open config directory %s: %s", + path, apr_strerror(rv, errmsg, sizeof errmsg)); + } + + candidates = apr_array_make(ptemp, 1, sizeof(fnames)); + while (apr_dir_read(&dirent, APR_FINFO_DIRENT | APR_FINFO_TYPE, dirp) == APR_SUCCESS) { + /* strip out '.' and '..' */ + if (strcmp(dirent.name, ".") + && strcmp(dirent.name, "..") + && (apr_fnmatch(fname, dirent.name, + APR_FNM_PERIOD | APR_FNM_NOESCAPE | APR_FNM_PATHNAME) == APR_SUCCESS)) { + const char *full_path = ap_make_full_path(ptemp, path, dirent.name); + /* If matching internal to path, and we happen to match something + * other than a directory, skip it + */ + if (rest && (rv == APR_SUCCESS) && (dirent.filetype != APR_DIR)) { + continue; + } + fnew = (fnames *) apr_array_push(candidates); + fnew->fname = full_path; + } + } + + apr_dir_close(dirp); + if (candidates->nelts != 0) { + const char *error; + + qsort((void *) candidates->elts, candidates->nelts, + sizeof(fnames), fname_alphasort); + + /* + * Now recurse these... we handle errors and subdirectories + * via the recursion, which is nice + */ + for (current = 0; current < candidates->nelts; ++current) { + fnew = &((fnames *) candidates->elts)[current]; + if (!rest) { + error = process_resource_config_nofnmatch(fnew->fname, + ari, p, + ptemp, 0, optional); + } + else { + error = process_resource_config_fnmatch(fnew->fname, rest, + ari, p, + ptemp, 0, optional); + } + if (error) { + return error; + } + } + } + else { + + if (!optional) { + return apr_psprintf(p, "No matches for the wildcard '%s' in '%s', failing " + "(use IncludeOptional if required)", fname, path); + } + } + + return NULL; +} + +AP_DECLARE(const char *) process_fnmatch_configs(apr_array_header_t *ari, + const char *fname, + apr_pool_t *p, + apr_pool_t *ptemp, + int optional) +{ + if (!fnmatch_test(fname)) { + return process_resource_config(fname, ari, p); + } + else { + apr_status_t status; + const char *rootpath, *filepath = fname; + + /* locate the start of the directories proper */ + status = apr_filepath_root(&rootpath, &filepath, APR_FILEPATH_TRUENAME | APR_FILEPATH_NATIVE, ptemp); + + /* we allow APR_SUCCESS and APR_EINCOMPLETE */ + if (APR_ERELATIVE == status) { + return apr_pstrcat(p, "Include must have an absolute path, ", fname, NULL); + } + else if (APR_EBADPATH == status) { + return apr_pstrcat(p, "Include has a bad path, ", fname, NULL); + } + + /* walk the filepath */ + return process_resource_config_fnmatch(rootpath, filepath, ari, p, ptemp, + 0, optional); + } +} + +const char *populate_include_files(apr_pool_t *p, apr_pool_t *ptemp, apr_array_header_t *ari, const char *fname, int optional) +{ + return process_fnmatch_configs(ari, fname, p, ptemp, optional); +} + +const char *process_command_config(server_rec *s, + void *mconfig, + apr_pool_t *p, + apr_pool_t *ptemp, + const char *filename) +{ + const char *errmsg; + char *l = apr_palloc (ptemp, MAX_STRING_LEN); + const char *args = l; + char *cmd_name, *w; + const command_rec *cmd; + apr_array_header_t *arr = apr_array_make(p, 1, sizeof(cmd_parms)); + apr_array_header_t *ari = apr_array_make(p, 1, sizeof(char *)); + cmd_parms *parms; + apr_status_t status; + ap_directive_t *newdir; + int optional; + char *err = NULL; + char *rootpath, *incpath; + int li; + + errmsg = populate_include_files(p, ptemp, ari, filename, 0); + + if(errmsg != NULL) + goto Exit; + + while(ari->nelts != 0 || arr->nelts != 0) + { + if(ari->nelts > 0) + { + char *fn = *(char **)apr_array_pop(ari); + + parms = (cmd_parms *)apr_array_push(arr); + *parms = default_parms; + parms->pool = p; + parms->temp_pool = ptemp; + parms->server = s; + parms->override = (RSRC_CONF | OR_ALL) & ~(OR_AUTHCFG | OR_LIMIT); + parms->override_opts = OPT_ALL | OPT_SYM_OWNER | OPT_MULTI; + + status = ap_pcfg_openfile(&parms->config_file, p, fn); + + if(status != APR_SUCCESS) + { + apr_array_pop(arr); + errmsg = apr_pstrcat(p, "Cannot open config file: ", fn, NULL); + goto Exit; + } + } + + if (arr->nelts > 1024) { + errmsg = "Exceeded the maximum include directory nesting level. You have " + "probably a recursion somewhere."; + goto Exit; + } + + parms = (cmd_parms *)apr_array_pop(arr); + + if(parms == NULL) + break; + + while (!(ap_cfg_getline(l, MAX_STRING_LEN, parms->config_file))) { + if (*l == '#' || *l == '\0') + continue; + + args = l; + + cmd_name = ap_getword_conf(p, &args); + + if (*cmd_name == '\0') + continue; + + if (!strcasecmp(cmd_name, "IncludeOptional")) + { + optional = 1; + goto ProcessInclude; + } + + if (!strcasecmp(cmd_name, "Include")) + { + optional = 0; +ProcessInclude: + w = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || *args != 0) + { + ap_cfg_closefile(parms->config_file); + errmsg = apr_pstrcat(parms->pool, "Include takes one argument", NULL); + goto Exit; + } + + incpath = w; + + /* locate the start of the directories proper */ + status = apr_filepath_root(&rootpath, &incpath, APR_FILEPATH_TRUENAME | APR_FILEPATH_NATIVE, ptemp); + + /* we allow APR_SUCCESS and APR_EINCOMPLETE */ + if (APR_ERELATIVE == status) { + rootpath = apr_pstrdup(ptemp, parms->config_file->name); + li = strlen(rootpath) - 1; + + while(li >= 0 && rootpath[li] != '/' && rootpath[li] != '\\') + rootpath[li--] = 0; + + w = apr_pstrcat(p, rootpath, w, NULL); + } + else if (APR_EBADPATH == status) { + ap_cfg_closefile(parms->config_file); + errmsg = apr_pstrcat(p, "Include file has a bad path, ", w, NULL); + goto Exit; + } + + errmsg = populate_include_files(p, ptemp, ari, w, optional); + + *(cmd_parms *)apr_array_push(arr) = *parms; + + if(errmsg != NULL) + goto Exit; + + // we don't want to close the current file yet + // + parms = NULL; + break; + } + + cmd = ap_find_command(cmd_name, security2_module.cmds); + + if(cmd == NULL) + { + // unknown command, should error + // + ap_cfg_closefile(parms->config_file); + errmsg = apr_pstrcat(p, "Unknown command in config: ", cmd_name, NULL); + goto Exit; + } + + newdir = apr_pcalloc(p, sizeof(ap_directive_t)); + newdir->filename = parms->config_file->name; + newdir->line_num = parms->config_file->line_number; + newdir->directive = cmd_name; + newdir->args = apr_pstrdup(p, args); + + parms->directive = newdir; + + errmsg = invoke_cmd(cmd, parms, mconfig, args); + + if(errmsg != NULL) + break; + } + + if(parms != NULL) + ap_cfg_closefile(parms->config_file); + + if(errmsg != NULL) + break; + } + + if (errmsg) { + err = (char *)apr_palloc(p, 1024); + + if(parms != NULL) + apr_snprintf(err, 1024, "Syntax error in config file %s, line %d: %s", parms->config_file->name, + parms->config_file->line_number, errmsg); + else + apr_snprintf(err, 1024, "Syntax error in config file: %s", errmsg); + } + + errmsg = err; + +Exit: + while((parms = (cmd_parms *)apr_array_pop(arr)) != NULL) + { + ap_cfg_closefile(parms->config_file); + } + + return errmsg; +} diff --git a/standalone/main.cpp b/standalone/main.cpp index 7e0a1163d5..73265b1ab2 100644 --- a/standalone/main.cpp +++ b/standalone/main.cpp @@ -239,7 +239,7 @@ void main(int argc, char *argv[]) config = modsecGetDefaultConfig(); - const char * err = modsecProcessConfig(config, config_file); + const char * err = modsecProcessConfig(config, config_file, "c:\\inetpub\\wwwroot"); if(err != NULL) { diff --git a/standalone/standalone.vcxproj.user b/standalone/standalone.vcxproj.user index 40e70632b2..0be15d4d97 100644 --- a/standalone/standalone.vcxproj.user +++ b/standalone/standalone.vcxproj.user @@ -1,7 +1,7 @@  - -c d:\xss.conf d:\test.dat + -c owasp_crs\modsecurity_iis.conf d:\test.dat WindowsLocalDebugger $(TargetPath) false