michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsParentalControlsServiceWin.h" michael@0: #include "nsString.h" michael@0: #include "nsIArray.h" michael@0: #include "nsIWidget.h" michael@0: #include "nsIInterfaceRequestor.h" michael@0: #include "nsIInterfaceRequestorUtils.h" michael@0: #include "nsIFile.h" michael@0: #include "nsILocalFileWin.h" michael@0: #include "nsArrayUtils.h" michael@0: #include "nsIXULAppInfo.h" michael@0: michael@0: static const CLSID CLSID_WinParentalControls = {0xE77CC89B,0x7401,0x4C04,{0x8C,0xED,0x14,0x9D,0xB3,0x5A,0xDD,0x04}}; michael@0: static const IID IID_IWinParentalControls = {0x28B4D88B,0xE072,0x49E6,{0x80,0x4D,0x26,0xED,0xBE,0x21,0xA7,0xB9}}; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsParentalControlsServiceWin, nsIParentalControlsService) michael@0: michael@0: static HINSTANCE gAdvAPIDLLInst = nullptr; michael@0: michael@0: decltype(EventWrite)* gEventWrite = nullptr; michael@0: decltype(EventRegister)* gEventRegister = nullptr; michael@0: decltype(EventUnregister)* gEventUnregister = nullptr; michael@0: michael@0: nsParentalControlsServiceWin::nsParentalControlsServiceWin() : michael@0: mEnabled(false) michael@0: , mProvider(0) michael@0: , mPC(nullptr) michael@0: { michael@0: HRESULT hr; michael@0: CoInitialize(nullptr); michael@0: hr = CoCreateInstance(CLSID_WinParentalControls, nullptr, CLSCTX_INPROC, michael@0: IID_IWinParentalControls, (void**)&mPC); michael@0: if (FAILED(hr)) michael@0: return; michael@0: michael@0: nsRefPtr wpcs; michael@0: if (FAILED(mPC->GetUserSettings(nullptr, getter_AddRefs(wpcs)))) { michael@0: // Not available on this os or not enabled for this user account or we're running as admin michael@0: mPC->Release(); michael@0: mPC = nullptr; michael@0: return; michael@0: } michael@0: michael@0: DWORD settings = 0; michael@0: wpcs->GetRestrictions(&settings); michael@0: michael@0: if (settings) { // WPCFLAG_NO_RESTRICTION = 0 michael@0: gAdvAPIDLLInst = ::LoadLibrary("Advapi32.dll"); michael@0: if(gAdvAPIDLLInst) michael@0: { michael@0: gEventWrite = (decltype(EventWrite)*) GetProcAddress(gAdvAPIDLLInst, "EventWrite"); michael@0: gEventRegister = (decltype(EventRegister)*) GetProcAddress(gAdvAPIDLLInst, "EventRegister"); michael@0: gEventUnregister = (decltype(EventUnregister)*) GetProcAddress(gAdvAPIDLLInst, "EventUnregister"); michael@0: } michael@0: mEnabled = true; michael@0: } michael@0: } michael@0: michael@0: nsParentalControlsServiceWin::~nsParentalControlsServiceWin() michael@0: { michael@0: if (mPC) michael@0: mPC->Release(); michael@0: michael@0: if (gEventUnregister && mProvider) michael@0: gEventUnregister(mProvider); michael@0: michael@0: if (gAdvAPIDLLInst) michael@0: ::FreeLibrary(gAdvAPIDLLInst); michael@0: } michael@0: michael@0: //------------------------------------------------------------------------ michael@0: michael@0: NS_IMETHODIMP michael@0: nsParentalControlsServiceWin::GetParentalControlsEnabled(bool *aResult) michael@0: { michael@0: *aResult = false; michael@0: michael@0: if (mEnabled) michael@0: *aResult = true; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsParentalControlsServiceWin::GetBlockFileDownloadsEnabled(bool *aResult) michael@0: { michael@0: *aResult = false; michael@0: michael@0: if (!mEnabled) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: nsRefPtr wpcws; michael@0: if (SUCCEEDED(mPC->GetWebSettings(nullptr, getter_AddRefs(wpcws)))) { michael@0: DWORD settings = 0; michael@0: wpcws->GetSettings(&settings); michael@0: if (settings == WPCFLAG_WEB_SETTING_DOWNLOADSBLOCKED) michael@0: *aResult = true; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsParentalControlsServiceWin::GetLoggingEnabled(bool *aResult) michael@0: { michael@0: *aResult = false; michael@0: michael@0: if (!mEnabled) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: // Check the general purpose logging flag michael@0: nsRefPtr wpcs; michael@0: if (SUCCEEDED(mPC->GetUserSettings(nullptr, getter_AddRefs(wpcs)))) { michael@0: BOOL enabled = FALSE; michael@0: wpcs->IsLoggingRequired(&enabled); michael@0: if (enabled) michael@0: *aResult = true; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Post a log event to the system michael@0: NS_IMETHODIMP michael@0: nsParentalControlsServiceWin::Log(int16_t aEntryType, bool blocked, nsIURI *aSource, nsIFile *aTarget) michael@0: { michael@0: if (!mEnabled) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: NS_ENSURE_ARG_POINTER(aSource); michael@0: michael@0: // Confirm we should be logging michael@0: bool enabled; michael@0: GetLoggingEnabled(&enabled); michael@0: if (!enabled) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: // Register a Vista log event provider associated with the parental controls channel. michael@0: if (!mProvider) { michael@0: if (!gEventRegister) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: if (gEventRegister(&WPCPROV, nullptr, nullptr, &mProvider) != ERROR_SUCCESS) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: switch(aEntryType) { michael@0: case ePCLog_URIVisit: michael@0: // Not needed, Vista's web content filter handles this for us michael@0: break; michael@0: case ePCLog_FileDownload: michael@0: LogFileDownload(blocked, aSource, aTarget); michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Override a single URI michael@0: NS_IMETHODIMP michael@0: nsParentalControlsServiceWin::RequestURIOverride(nsIURI *aTarget, nsIInterfaceRequestor *aWindowContext, bool *_retval) michael@0: { michael@0: *_retval = false; michael@0: michael@0: if (!mEnabled) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: NS_ENSURE_ARG_POINTER(aTarget); michael@0: michael@0: nsAutoCString spec; michael@0: aTarget->GetSpec(spec); michael@0: if (spec.IsEmpty()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: HWND hWnd = nullptr; michael@0: // If we have a native window, use its handle instead michael@0: nsCOMPtr widget(do_GetInterface(aWindowContext)); michael@0: if (widget) michael@0: hWnd = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW); michael@0: if (hWnd == nullptr) michael@0: hWnd = GetDesktopWindow(); michael@0: michael@0: BOOL ret; michael@0: nsRefPtr wpcws; michael@0: if (SUCCEEDED(mPC->GetWebSettings(nullptr, getter_AddRefs(wpcws)))) { michael@0: wpcws->RequestURLOverride(hWnd, NS_ConvertUTF8toUTF16(spec).get(), michael@0: 0, nullptr, &ret); michael@0: *_retval = ret; michael@0: } michael@0: michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Override a web page michael@0: NS_IMETHODIMP michael@0: nsParentalControlsServiceWin::RequestURIOverrides(nsIArray *aTargets, nsIInterfaceRequestor *aWindowContext, bool *_retval) michael@0: { michael@0: *_retval = false; michael@0: michael@0: if (!mEnabled) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: NS_ENSURE_ARG_POINTER(aTargets); michael@0: michael@0: uint32_t arrayLength = 0; michael@0: aTargets->GetLength(&arrayLength); michael@0: if (!arrayLength) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: if (arrayLength == 1) { michael@0: nsCOMPtr uri = do_QueryElementAt(aTargets, 0); michael@0: if (!uri) michael@0: return NS_ERROR_INVALID_ARG; michael@0: return RequestURIOverride(uri, aWindowContext, _retval); michael@0: } michael@0: michael@0: HWND hWnd = nullptr; michael@0: // If we have a native window, use its handle instead michael@0: nsCOMPtr widget(do_GetInterface(aWindowContext)); michael@0: if (widget) michael@0: hWnd = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW); michael@0: if (hWnd == nullptr) michael@0: hWnd = GetDesktopWindow(); michael@0: michael@0: // The first entry should be the root uri michael@0: nsAutoCString rootSpec; michael@0: nsCOMPtr rootURI = do_QueryElementAt(aTargets, 0); michael@0: if (!rootURI) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: rootURI->GetSpec(rootSpec); michael@0: if (rootSpec.IsEmpty()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: // Allocate an array of sub uri michael@0: int32_t count = arrayLength - 1; michael@0: nsAutoArrayPtr arrUrls(new LPCWSTR[count]); michael@0: if (!arrUrls) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: uint32_t uriIdx = 0, idx; michael@0: for (idx = 1; idx < arrayLength; idx++) michael@0: { michael@0: nsCOMPtr uri = do_QueryElementAt(aTargets, idx); michael@0: if (!uri) michael@0: continue; michael@0: michael@0: nsAutoCString subURI; michael@0: if (NS_FAILED(uri->GetSpec(subURI))) michael@0: continue; michael@0: michael@0: arrUrls[uriIdx] = (LPCWSTR)UTF8ToNewUnicode(subURI); // allocation michael@0: if (!arrUrls[uriIdx]) michael@0: continue; michael@0: michael@0: uriIdx++; michael@0: } michael@0: michael@0: if (!uriIdx) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: BOOL ret; michael@0: nsRefPtr wpcws; michael@0: if (SUCCEEDED(mPC->GetWebSettings(nullptr, getter_AddRefs(wpcws)))) { michael@0: wpcws->RequestURLOverride(hWnd, NS_ConvertUTF8toUTF16(rootSpec).get(), michael@0: uriIdx, (LPCWSTR*)arrUrls.get(), &ret); michael@0: *_retval = ret; michael@0: } michael@0: michael@0: // Free up the allocated strings in our array michael@0: for (idx = 0; idx < uriIdx; idx++) michael@0: NS_Free((void*)arrUrls[idx]); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------ michael@0: michael@0: // Sends a file download event to the Vista Event Log michael@0: void michael@0: nsParentalControlsServiceWin::LogFileDownload(bool blocked, nsIURI *aSource, nsIFile *aTarget) michael@0: { michael@0: nsAutoCString curi; michael@0: michael@0: if (!gEventWrite) michael@0: return; michael@0: michael@0: // Note, EventDataDescCreate is a macro defined in the headers, not a function michael@0: michael@0: aSource->GetSpec(curi); michael@0: nsAutoString uri = NS_ConvertUTF8toUTF16(curi); michael@0: michael@0: // Get the name of the currently running process michael@0: nsCOMPtr appInfo = do_GetService("@mozilla.org/xre/app-info;1"); michael@0: nsAutoCString asciiAppName; michael@0: if (appInfo) michael@0: appInfo->GetName(asciiAppName); michael@0: nsAutoString appName = NS_ConvertUTF8toUTF16(asciiAppName); michael@0: michael@0: static const WCHAR fill[] = L""; michael@0: michael@0: // See wpcevent.h and msdn for event formats michael@0: EVENT_DATA_DESCRIPTOR eventData[WPC_ARGS_FILEDOWNLOADEVENT_CARGS]; michael@0: DWORD dwBlocked = blocked; michael@0: michael@0: EventDataDescCreate(&eventData[WPC_ARGS_FILEDOWNLOADEVENT_URL], (const void*)uri.get(), michael@0: ((ULONG)uri.Length()+1)*sizeof(WCHAR)); michael@0: EventDataDescCreate(&eventData[WPC_ARGS_FILEDOWNLOADEVENT_APPNAME], (const void*)appName.get(), michael@0: ((ULONG)appName.Length()+1)*sizeof(WCHAR)); michael@0: EventDataDescCreate(&eventData[WPC_ARGS_FILEDOWNLOADEVENT_VERSION], (const void*)fill, sizeof(fill)); michael@0: EventDataDescCreate(&eventData[WPC_ARGS_FILEDOWNLOADEVENT_BLOCKED], (const void*)&dwBlocked, michael@0: sizeof(dwBlocked)); michael@0: michael@0: nsCOMPtr local(do_QueryInterface(aTarget)); // May be null michael@0: if (local) { michael@0: nsAutoString path; michael@0: local->GetCanonicalPath(path); michael@0: EventDataDescCreate(&eventData[WPC_ARGS_FILEDOWNLOADEVENT_PATH], (const void*)path.get(), michael@0: ((ULONG)path.Length()+1)*sizeof(WCHAR)); michael@0: } michael@0: else { michael@0: EventDataDescCreate(&eventData[WPC_ARGS_FILEDOWNLOADEVENT_PATH], (const void*)fill, sizeof(fill)); michael@0: } michael@0: michael@0: gEventWrite(mProvider, &WPCEVENT_WEB_FILEDOWNLOAD, ARRAYSIZE(eventData), eventData); michael@0: } michael@0: