summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/defaultagent/rust
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/mozapps/defaultagent/rust')
-rw-r--r--toolkit/mozapps/defaultagent/rust/Cargo.toml23
-rw-r--r--toolkit/mozapps/defaultagent/rust/moz.build7
-rw-r--r--toolkit/mozapps/defaultagent/rust/src/lib.rs166
-rw-r--r--toolkit/mozapps/defaultagent/rust/src/viaduct_wininet/internet_handle.rs53
-rw-r--r--toolkit/mozapps/defaultagent/rust/src/viaduct_wininet/mod.rs257
-rw-r--r--toolkit/mozapps/defaultagent/rust/wineventlog/Cargo.toml15
-rw-r--r--toolkit/mozapps/defaultagent/rust/wineventlog/src/lib.rs76
7 files changed, 597 insertions, 0 deletions
diff --git a/toolkit/mozapps/defaultagent/rust/Cargo.toml b/toolkit/mozapps/defaultagent/rust/Cargo.toml
new file mode 100644
index 0000000000..4bd44a9df8
--- /dev/null
+++ b/toolkit/mozapps/defaultagent/rust/Cargo.toml
@@ -0,0 +1,23 @@
+[package]
+name = "defaultagent-static"
+version = "0.1.0"
+authors = ["The Mozilla Install/Update Team <install-update@mozilla.com>"]
+edition = "2018"
+description = "FFI to Rust for use in Firefox's default browser agent."
+repository = "https://github.com/mozilla/defaultagent-static"
+license = "MPL-2.0"
+
+[dependencies]
+log = { version = "0.4", features = ["std"] }
+mozilla-central-workspace-hack = { path = "../../../../build/workspace-hack" }
+serde = "1.0"
+serde_derive = "1.0"
+serde_json = "1.0"
+url = "2.1"
+viaduct = "0.1"
+wineventlog = { path = "wineventlog"}
+wio = "0.2"
+winapi = { version = "0.3", features = ["errhandlingapi", "handleapi", "minwindef", "winerror", "wininet", "winuser"] }
+
+[lib]
+crate-type = ["staticlib"]
diff --git a/toolkit/mozapps/defaultagent/rust/moz.build b/toolkit/mozapps/defaultagent/rust/moz.build
new file mode 100644
index 0000000000..accce52265
--- /dev/null
+++ b/toolkit/mozapps/defaultagent/rust/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+RustLibrary("defaultagent-static")
diff --git a/toolkit/mozapps/defaultagent/rust/src/lib.rs b/toolkit/mozapps/defaultagent/rust/src/lib.rs
new file mode 100644
index 0000000000..de5f1ba03a
--- /dev/null
+++ b/toolkit/mozapps/defaultagent/rust/src/lib.rs
@@ -0,0 +1,166 @@
+/* -*- Mode: rust; rust-indent-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#![allow(non_snake_case)]
+
+use std::ffi::{CStr, OsString};
+use std::os::raw::c_char;
+
+#[macro_use]
+extern crate serde_derive;
+#[macro_use]
+extern crate log;
+use winapi::shared::ntdef::HRESULT;
+use winapi::shared::winerror::{HRESULT_FROM_WIN32, S_OK};
+use wio::wide::FromWide;
+
+mod viaduct_wininet;
+use viaduct_wininet::WinInetBackend;
+
+// HRESULT with 0x80000000 is an error, 0x20000000 set is a customer error code.
+#[allow(overflowing_literals)]
+const HR_NETWORK_ERROR: HRESULT = 0x80000000 | 0x20000000 | 0x1;
+#[allow(overflowing_literals)]
+const HR_SETTINGS_ERROR: HRESULT = 0x80000000 | 0x20000000 | 0x2;
+
+#[derive(Debug, Deserialize)]
+pub struct EnabledRecord {
+ // Unknown fields are ignored by serde: see the docs for `#[serde(deny_unknown_fields)]`.
+ pub(crate) enabled: bool,
+}
+
+pub enum Error {
+ /// A backend error with an attached Windows error code from `GetLastError()`.
+ WindowsError(u32),
+
+ /// A network or otherwise transient error.
+ NetworkError,
+
+ /// A configuration or settings data error that probably requires code, configuration, or
+ /// server-side changes to address.
+ SettingsError,
+}
+
+impl From<viaduct::UnexpectedStatus> for Error {
+ fn from(_err: viaduct::UnexpectedStatus) -> Self {
+ Error::NetworkError
+ }
+}
+
+impl From<viaduct::Error> for Error {
+ fn from(err: viaduct::Error) -> Self {
+ match err {
+ viaduct::Error::NetworkError(_) => Error::NetworkError,
+ viaduct::Error::BackendError(raw) => {
+ // If we have a string that's a hex error code like
+ // "0xabcde", that's a Windows error.
+ if raw.starts_with("0x") {
+ let without_prefix = raw.trim_start_matches("0x");
+ let parse_result = u32::from_str_radix(without_prefix, 16);
+ if let Ok(parsed) = parse_result {
+ return Error::WindowsError(parsed);
+ }
+ }
+ Error::SettingsError
+ }
+ _ => Error::SettingsError,
+ }
+ }
+}
+
+impl From<serde_json::Error> for Error {
+ fn from(_err: serde_json::Error) -> Self {
+ Error::SettingsError
+ }
+}
+
+impl From<url::ParseError> for Error {
+ fn from(_err: url::ParseError) -> Self {
+ Error::SettingsError
+ }
+}
+
+fn is_agent_remote_disabled<S: AsRef<str>>(url: S) -> Result<bool, Error> {
+ // Be careful setting the viaduct backend twice. If the backend
+ // has been set already, assume that it's our backend: we may as
+ // well at least try to continue.
+ match viaduct::set_backend(&WinInetBackend) {
+ Ok(_) => {}
+ Err(viaduct::Error::SetBackendError) => {}
+ e => e?,
+ }
+
+ let url = url::Url::parse(url.as_ref())?;
+ let req = viaduct::Request::new(viaduct::Method::Get, url);
+ let resp = req.send()?;
+
+ let resp = resp.require_success()?;
+
+ let body: serde_json::Value = resp.json()?;
+ let data = body.get("data").ok_or(Error::SettingsError)?;
+ let record: EnabledRecord = serde_json::from_value(data.clone())?;
+
+ let disabled = !record.enabled;
+ Ok(disabled)
+}
+
+// This is an easy way to consume `MOZ_APP_DISPLAYNAME` from Rust code.
+extern "C" {
+ static gWinEventLogSourceName: *const u16;
+}
+
+#[allow(dead_code)]
+#[no_mangle]
+extern "C" fn IsAgentRemoteDisabledRust(szUrl: *const c_char, lpdwDisabled: *mut u32) -> HRESULT {
+ let wineventlog_name = unsafe { OsString::from_wide_ptr_null(gWinEventLogSourceName) };
+ let logger = wineventlog::EventLogger::new(&wineventlog_name);
+ // It's fine to initialize logging twice.
+ let _ = log::set_boxed_logger(Box::new(logger));
+ log::set_max_level(log::LevelFilter::Info);
+
+ // Use an IIFE for `?`.
+ let disabled_result = (|| {
+ if lpdwDisabled.is_null() {
+ return Err(Error::SettingsError);
+ }
+
+ let url = unsafe { CStr::from_ptr(szUrl).to_str().map(|x| x.to_string()) }
+ .map_err(|_| Error::SettingsError)?;
+
+ info!("Using remote settings URL: {}", url);
+
+ is_agent_remote_disabled(url)
+ })();
+
+ match disabled_result {
+ Err(e) => {
+ return match e {
+ Error::WindowsError(errno) => {
+ let hr = HRESULT_FROM_WIN32(errno);
+ error!("Error::WindowsError({}) (HRESULT: 0x{:x})", errno, hr);
+ hr
+ }
+ Error::NetworkError => {
+ let hr = HR_NETWORK_ERROR;
+ error!("Error::NetworkError (HRESULT: 0x{:x})", hr);
+ hr
+ }
+ Error::SettingsError => {
+ let hr = HR_SETTINGS_ERROR;
+ error!("Error::SettingsError (HRESULT: 0x{:x})", hr);
+ hr
+ }
+ };
+ }
+
+ Ok(remote_disabled) => {
+ // We null-checked `lpdwDisabled` earlier, but just to be safe.
+ if !lpdwDisabled.is_null() {
+ unsafe { *lpdwDisabled = if remote_disabled { 1 } else { 0 } };
+ }
+ return S_OK;
+ }
+ }
+}
diff --git a/toolkit/mozapps/defaultagent/rust/src/viaduct_wininet/internet_handle.rs b/toolkit/mozapps/defaultagent/rust/src/viaduct_wininet/internet_handle.rs
new file mode 100644
index 0000000000..85f4254c88
--- /dev/null
+++ b/toolkit/mozapps/defaultagent/rust/src/viaduct_wininet/internet_handle.rs
@@ -0,0 +1,53 @@
+// Licensed under the Apache License, Version 2.0
+// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// All files in the project carrying such notice may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Wrapping and automatically closing Internet handles. Copy-pasted from
+//! [comedy-rs](https://github.com/agashlin/comedy-rs/blob/c244b91e9237c887f6a7bc6cd03db98b51966494/src/handle.rs).
+
+use winapi::shared::minwindef::DWORD;
+use winapi::shared::ntdef::NULL;
+use winapi::um::errhandlingapi::GetLastError;
+use winapi::um::wininet::{InternetCloseHandle, HINTERNET};
+
+/// Check and automatically close a Windows `HINTERNET`.
+#[repr(transparent)]
+#[derive(Debug)]
+pub struct InternetHandle(HINTERNET);
+
+impl InternetHandle {
+ /// Take ownership of a `HINTERNET`, which will be closed with `InternetCloseHandle` upon drop.
+ /// Returns an error in case of `NULL`.
+ ///
+ /// # Safety
+ ///
+ /// `h` should be the only copy of the handle. `GetLastError()` is called to
+ /// return an error, so the last Windows API called on this thread should have been
+ /// what produced the invalid handle.
+ pub unsafe fn new(h: HINTERNET) -> Result<InternetHandle, DWORD> {
+ if h == NULL {
+ Err(GetLastError())
+ } else {
+ Ok(InternetHandle(h))
+ }
+ }
+
+ /// Obtains the raw `HINTERNET` without transferring ownership.
+ ///
+ /// Do __not__ close this handle because it is still owned by the `InternetHandle`.
+ ///
+ /// Do __not__ use this handle beyond the lifetime of the `InternetHandle`.
+ pub fn as_raw(&self) -> HINTERNET {
+ self.0
+ }
+}
+
+impl Drop for InternetHandle {
+ fn drop(&mut self) {
+ unsafe {
+ InternetCloseHandle(self.0);
+ }
+ }
+}
diff --git a/toolkit/mozapps/defaultagent/rust/src/viaduct_wininet/mod.rs b/toolkit/mozapps/defaultagent/rust/src/viaduct_wininet/mod.rs
new file mode 100644
index 0000000000..1abd170f8f
--- /dev/null
+++ b/toolkit/mozapps/defaultagent/rust/src/viaduct_wininet/mod.rs
@@ -0,0 +1,257 @@
+// Licensed under the Apache License, Version 2.0
+// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// All files in the project carrying such notice may not be copied, modified, or distributed
+// except according to those terms.
+
+use winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER;
+use winapi::um::errhandlingapi::GetLastError;
+use winapi::um::wininet;
+use wio::wide::ToWide;
+
+use viaduct::Backend;
+
+mod internet_handle;
+use internet_handle::InternetHandle;
+
+pub struct WinInetBackend;
+
+/// Errors
+fn to_viaduct_error(e: u32) -> viaduct::Error {
+ // Like "0xabcde".
+ viaduct::Error::BackendError(format!("{:#x}", e))
+}
+
+fn get_status(req: wininet::HINTERNET) -> Result<u16, viaduct::Error> {
+ let mut status: u32 = 0;
+ let mut size: u32 = std::mem::size_of::<u32>() as u32;
+ let result = unsafe {
+ wininet::HttpQueryInfoW(
+ req,
+ wininet::HTTP_QUERY_STATUS_CODE | wininet::HTTP_QUERY_FLAG_NUMBER,
+ &mut status as *mut _ as *mut _,
+ &mut size,
+ std::ptr::null_mut(),
+ )
+ };
+ if 0 == result {
+ return Err(to_viaduct_error(unsafe { GetLastError() }));
+ }
+
+ Ok(status as u16)
+}
+
+fn get_headers(req: wininet::HINTERNET) -> Result<viaduct::Headers, viaduct::Error> {
+ // We follow https://docs.microsoft.com/en-us/windows/win32/wininet/retrieving-http-headers.
+ //
+ // Per
+ // https://docs.microsoft.com/en-us/windows/win32/api/wininet/nf-wininet-httpqueryinfoa:
+ // The `HttpQueryInfoA` function represents headers as ISO-8859-1 characters
+ // not ANSI characters.
+ let mut size: u32 = 0;
+
+ let result = unsafe {
+ wininet::HttpQueryInfoA(
+ req,
+ wininet::HTTP_QUERY_RAW_HEADERS,
+ std::ptr::null_mut(),
+ &mut size,
+ std::ptr::null_mut(),
+ )
+ };
+ if 0 == result {
+ let error = unsafe { GetLastError() };
+ if error == wininet::ERROR_HTTP_HEADER_NOT_FOUND {
+ return Ok(viaduct::Headers::new());
+ } else if error != ERROR_INSUFFICIENT_BUFFER {
+ return Err(to_viaduct_error(error));
+ }
+ }
+
+ let mut buffer = vec![0 as u8; size as usize];
+ let result = unsafe {
+ wininet::HttpQueryInfoA(
+ req,
+ wininet::HTTP_QUERY_RAW_HEADERS,
+ buffer.as_mut_ptr() as *mut _,
+ &mut size,
+ std::ptr::null_mut(),
+ )
+ };
+ if 0 == result {
+ let error = unsafe { GetLastError() };
+ if error == wininet::ERROR_HTTP_HEADER_NOT_FOUND {
+ return Ok(viaduct::Headers::new());
+ } else {
+ return Err(to_viaduct_error(error));
+ }
+ }
+
+ // The API returns all of the headers as a single char buffer in
+ // ISO-8859-1 encoding. Each header is terminated by '\0' and
+ // there's a trailing '\0' terminator as well.
+ //
+ // We want UTF-8. It's not worth include a non-trivial encoding
+ // library like `encoding_rs` just for these headers, so let's use
+ // the fact that ISO-8859-1 and UTF-8 intersect on the lower 7 bits
+ // and decode lossily. It will at least be reasonably clear when
+ // there is an encoding issue.
+ let allheaders = String::from_utf8_lossy(&buffer);
+
+ let mut headers = viaduct::Headers::new();
+ for header in allheaders.split(0 as char) {
+ let mut it = header.splitn(2, ":");
+ if let (Some(name), Some(value)) = (it.next(), it.next()) {
+ headers.insert(name.trim().to_string(), value.trim().to_string())?;
+ }
+ }
+
+ return Ok(headers);
+}
+
+fn get_body(req: wininet::HINTERNET) -> Result<Vec<u8>, viaduct::Error> {
+ let mut body = Vec::new();
+
+ const BUFFER_SIZE: usize = 65535;
+ let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
+
+ loop {
+ let mut bytes_downloaded: u32 = 0;
+ let result = unsafe {
+ wininet::InternetReadFile(
+ req,
+ buffer.as_mut_ptr() as *mut _,
+ BUFFER_SIZE as u32,
+ &mut bytes_downloaded,
+ )
+ };
+ if 0 == result {
+ return Err(to_viaduct_error(unsafe { GetLastError() }));
+ }
+ if bytes_downloaded == 0 {
+ break;
+ }
+
+ body.extend_from_slice(&buffer[0..bytes_downloaded as usize]);
+ }
+ Ok(body)
+}
+
+impl Backend for WinInetBackend {
+ fn send(&self, request: viaduct::Request) -> Result<viaduct::Response, viaduct::Error> {
+ viaduct::note_backend("wininet.dll");
+
+ let request_method = request.method;
+ let url = request.url;
+
+ let session = unsafe {
+ InternetHandle::new(wininet::InternetOpenW(
+ "DefaultAgent/1.0".to_wide_null().as_ptr(),
+ wininet::INTERNET_OPEN_TYPE_PRECONFIG,
+ std::ptr::null_mut(),
+ std::ptr::null_mut(),
+ 0,
+ ))
+ }
+ .map_err(to_viaduct_error)?;
+
+ // Consider asserting the scheme here too, for documentation purposes.
+ // Viaduct itself only allows HTTPS at this time, but that might change.
+ let host = url
+ .host_str()
+ .ok_or(viaduct::Error::BackendError("no host".to_string()))?;
+
+ let conn = unsafe {
+ InternetHandle::new(wininet::InternetConnectW(
+ session.as_raw(),
+ host.to_wide_null().as_ptr(),
+ wininet::INTERNET_DEFAULT_HTTPS_PORT as u16,
+ std::ptr::null_mut(),
+ std::ptr::null_mut(),
+ wininet::INTERNET_SERVICE_HTTP,
+ 0,
+ 0,
+ ))
+ }
+ .map_err(to_viaduct_error)?;
+
+ let path = url[url::Position::BeforePath..].to_string();
+ let req = unsafe {
+ wininet::HttpOpenRequestW(
+ conn.as_raw(),
+ request_method.as_str().to_wide_null().as_ptr(),
+ path.to_wide_null().as_ptr(),
+ std::ptr::null_mut(), /* lpszVersion */
+ std::ptr::null_mut(), /* lpszReferrer */
+ std::ptr::null_mut(), /* lplpszAcceptTypes */
+ // Avoid the cache as best we can.
+ wininet::INTERNET_FLAG_NO_AUTH
+ | wininet::INTERNET_FLAG_NO_CACHE_WRITE
+ | wininet::INTERNET_FLAG_NO_COOKIES
+ | wininet::INTERNET_FLAG_NO_UI
+ | wininet::INTERNET_FLAG_PRAGMA_NOCACHE
+ | wininet::INTERNET_FLAG_RELOAD
+ | wininet::INTERNET_FLAG_SECURE,
+ 0,
+ )
+ };
+ if req.is_null() {
+ return Err(to_viaduct_error(unsafe { GetLastError() }));
+ }
+
+ for header in request.headers {
+ // Per
+ // https://docs.microsoft.com/en-us/windows/win32/api/wininet/nf-wininet-httpaddrequestheadersw,
+ // "Each header must be terminated by a CR/LF (carriage return/line
+ // feed) pair."
+ let h = format!("{}: {}\r\n", header.name(), header.value());
+ let result = unsafe {
+ wininet::HttpAddRequestHeadersW(
+ req,
+ h.to_wide_null().as_ptr(), /* lpszHeaders */
+ -1i32 as u32, /* dwHeadersLength */
+ wininet::HTTP_ADDREQ_FLAG_ADD | wininet::HTTP_ADDREQ_FLAG_REPLACE, /* dwModifiers */
+ )
+ };
+ if 0 == result {
+ return Err(to_viaduct_error(unsafe { GetLastError() }));
+ }
+ }
+
+ // Future work: support sending a body.
+ if request.body.is_some() {
+ return Err(viaduct::Error::BackendError(
+ "non-empty body is not yet supported".to_string(),
+ ));
+ }
+
+ let result = unsafe {
+ wininet::HttpSendRequestW(
+ req,
+ std::ptr::null_mut(), /* lpszHeaders */
+ 0, /* dwHeadersLength */
+ std::ptr::null_mut(), /* lpOptional */
+ 0, /* dwOptionalLength */
+ )
+ };
+ if 0 == result {
+ return Err(to_viaduct_error(unsafe { GetLastError() }));
+ }
+
+ let status = get_status(req)?;
+ let headers = get_headers(req)?;
+
+ // Not all responses have a body.
+ let has_body = headers.get_header("content-type").is_some()
+ || headers.get_header("content-length").is_some();
+ let body = if has_body { get_body(req)? } else { Vec::new() };
+
+ Ok(viaduct::Response {
+ request_method,
+ body,
+ url,
+ status,
+ headers,
+ })
+ }
+}
diff --git a/toolkit/mozapps/defaultagent/rust/wineventlog/Cargo.toml b/toolkit/mozapps/defaultagent/rust/wineventlog/Cargo.toml
new file mode 100644
index 0000000000..b38f704ca1
--- /dev/null
+++ b/toolkit/mozapps/defaultagent/rust/wineventlog/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "wineventlog"
+version = "0.1.0"
+authors = ["The Mozilla Project Developers"]
+license = "MPL-2.0"
+autobins = false
+edition = "2018"
+
+[target."cfg(windows)".dependencies]
+log = "0.4"
+wio = "0.2"
+
+[target."cfg(windows)".dependencies.winapi]
+version = "0.3.7"
+features = ["errhandlingapi", "minwindef", "ntdef", "oaidl", "oleauto", "sysinfoapi", "taskschd", "winbase", "winerror", "winnt", "winreg", "wtypes"]
diff --git a/toolkit/mozapps/defaultagent/rust/wineventlog/src/lib.rs b/toolkit/mozapps/defaultagent/rust/wineventlog/src/lib.rs
new file mode 100644
index 0000000000..c98b75c53a
--- /dev/null
+++ b/toolkit/mozapps/defaultagent/rust/wineventlog/src/lib.rs
@@ -0,0 +1,76 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Very simple implementation of logging via the Windows Event Log
+
+use std::ffi::OsStr;
+use std::ptr;
+
+use log::{Level, Metadata, Record};
+use winapi::shared::minwindef::WORD;
+use winapi::um::{winbase, winnt};
+use wio::wide::ToWide;
+
+pub struct EventLogger {
+ pub name: Vec<u16>,
+}
+
+impl EventLogger {
+ pub fn new(name: impl AsRef<OsStr>) -> Self {
+ EventLogger {
+ name: name.to_wide_null(),
+ }
+ }
+}
+
+impl log::Log for EventLogger {
+ fn enabled(&self, metadata: &Metadata) -> bool {
+ metadata.level() <= log::max_level()
+ }
+
+ fn log(&self, record: &Record) {
+ if !self.enabled(record.metadata()) {
+ return;
+ }
+
+ let name = self.name.as_ptr();
+ let msg = format!("{} - {}", record.level(), record.args()).to_wide_null();
+
+ // Open and close the event log handle on every message, for simplicity.
+ let event_log;
+ unsafe {
+ event_log = winbase::RegisterEventSourceW(ptr::null(), name);
+ if event_log.is_null() {
+ return;
+ }
+ }
+
+ let level = match record.level() {
+ Level::Error => winnt::EVENTLOG_ERROR_TYPE,
+ Level::Warn => winnt::EVENTLOG_WARNING_TYPE,
+ Level::Info | Level::Debug | Level::Trace => winnt::EVENTLOG_INFORMATION_TYPE,
+ };
+
+ unsafe {
+ // mut only to match the LPCWSTR* signature
+ let mut msg_array: [*const u16; 1] = [msg.as_ptr()];
+
+ let _ = winbase::ReportEventW(
+ event_log,
+ level,
+ 0, // no category
+ 0, // event id 0
+ ptr::null_mut(), // no user sid
+ msg_array.len() as WORD, // string count
+ 0, // 0 bytes raw data
+ msg_array.as_mut_ptr(), // strings
+ ptr::null_mut(), // no raw data
+ );
+
+ let _ = winbase::DeregisterEventSource(event_log);
+ }
+ }
+
+ fn flush(&self) {}
+}