using System;
using System.Runtime.InteropServices;
using Vanara.Extensions;
using Vanara.InteropServices;
using Vanara.PInvoke;
using static Vanara.PInvoke.WinINet;
using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
namespace Vanara.Network
{
/// Provides access to proxy settings for an internet connection.
///
public class InternetProxyOptions : IDisposable
{
private SafeHINTERNET hInet = SafeHINTERNET.Null;
///
/// Initializes a new instance of the class that provides access to system options for the local machine.
///
public InternetProxyOptions() { }
///
/// Initializes a new instance of the InternetProxyOptions class by creating a session with options.
///
///
/// String that specifies the name of the application or entity calling the functions. This name is used as the user agent in the
/// HTTP protocol.
///
///
/// String that specifies the name of the proxy server(s) to use. Do not use an empty string, because it will be used as the proxy
/// name. Only CERN type proxies (HTTP only) and the TIS FTP gateway (FTP only) are recognized.
///
///
/// An optional list of host names or IP addresses, or both, that should not be routed through the proxy. The list can contain
/// wildcards. Do not use an empty string, because it will be used as a proxy bypass. If this parameter specifies the
/// "<local>" macro, the function bypasses the proxy for any host name that does not contain a period.
///
/// By default, the proxy will bypass requests that use the host names "localhost", "loopback", "127.0.0.1", or "[::1]". This
/// behavior exists because a remote proxy server typically will not resolve these addresses properly.
///
///
///
/// If , does not make network requests. All entities are returned from the cache. If the requested item is
/// not in the cache, a suitable error is returned.
///
/// If , makes only asynchronous requests.
public InternetProxyOptions(string agentName, string manualProxyUrl, string[] proxyBypassEntries = null, bool offline = false, bool asyncOnly = false)
{
InternetApiFlags flags = 0;
if (offline) flags |= InternetApiFlags.INTERNET_FLAG_OFFLINE;
if (asyncOnly) flags |= InternetApiFlags.INTERNET_FLAG_ASYNC;
hInet = InternetOpen(agentName, InternetOpenType.INTERNET_OPEN_TYPE_PROXY, manualProxyUrl, proxyBypassEntries is null ? null : string.Join(";", proxyBypassEntries), flags);
Win32Error.ThrowLastErrorIfInvalid(hInet);
}
//public int DisplayInternetControlPanel(HWND windowHandle) => LaunchInternetControlPanel(windowHandle);
/// Gets or sets a value indicating whether the connection automatically detects settings.
/// if the connection automatically detects settings; otherwise, .
public bool AutomaticallyDetectSettings
{
get => HasProxyFlag(PER_CONN_FLAGS.PROXY_TYPE_AUTO_DETECT);
set => SetOptionFlag(PER_CONN_FLAGS.PROXY_TYPE_AUTO_DETECT, value);
}
/// Gets a value indicating whether this session has any proxy set.
/// if this session has a proxy set; otherwise, .
public bool HasProxy => AutomaticallyDetectSettings || SetupScriptUrl != null || ManualProxyUrl != null;
/// Gets or sets a string containing the proxy server.
public string ManualProxyUrl
{
get => HasProxyFlag(PER_CONN_FLAGS.PROXY_TYPE_PROXY) ? GetOption(INTERNET_PER_CONN_OPTION_ID.INTERNET_PER_CONN_PROXY_SERVER) : null;
set
{
SetOptionString(INTERNET_PER_CONN_OPTION_ID.INTERNET_PER_CONN_PROXY_SERVER, value);
SetOptionFlag(PER_CONN_FLAGS.PROXY_TYPE_PROXY, value != null);
}
}
/// Gets or sets an array of URLs that do not use the proxy server.
public string[] ProxyBypassEntries
{
get => GetOption(INTERNET_PER_CONN_OPTION_ID.INTERNET_PER_CONN_PROXY_BYPASS)?.Split(new[] { ';', ' ' }, StringSplitOptions.RemoveEmptyEntries);
set => SetOptionString(INTERNET_PER_CONN_OPTION_ID.INTERNET_PER_CONN_PROXY_BYPASS, value is null ? null : string.Join(";", value));
}
/// Gets or sets a string containing the URL to the automatic configuration script.
public string SetupScriptUrl
{
get => HasProxyFlag(PER_CONN_FLAGS.PROXY_TYPE_AUTO_PROXY_URL) ? GetOption(INTERNET_PER_CONN_OPTION_ID.INTERNET_PER_CONN_AUTOCONFIG_URL) : null;
set
{
SetOptionString(INTERNET_PER_CONN_OPTION_ID.INTERNET_PER_CONN_AUTOCONFIG_URL, value);
SetOptionFlag(PER_CONN_FLAGS.PROXY_TYPE_AUTO_PROXY_URL, value != null);
}
}
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
public void Dispose() => hInet?.Dispose();
///
/// Notifies the system that the registry settings have been changed so that it verifies the settings on the next call to InternetConnect.
///
public void NotifyInternetOptionSettingsChanged() => Win32Error.ThrowLastErrorIfFalse(InternetSetOption(hInet, InternetOptionFlags.INTERNET_OPTION_SETTINGS_CHANGED));
/// Causes the proxy data to be reread from the registry for a handle.
public bool RefreshProxyData() => InternetSetOption(hInet, InternetOptionFlags.INTERNET_OPTION_REFRESH);
private T GetOption(INTERNET_PER_CONN_OPTION_ID pco)
{
var getType = typeof(T);
if (getType.IsEnum) getType = typeof(uint);
if (!CorrespondingTypeAttribute.CanGet(pco, getType))
throw new InvalidOperationException("Invalid output type specified.");
var ico = new INTERNET_PER_CONN_OPTION { dwOption = pco };
using var pIco = new SafeCoTaskMemStruct(ico, Marshal.SizeOf(typeof(INTERNET_PER_CONN_OPTION)) + (Kernel32.MAX_PATH * Vanara.Extensions.StringHelper.GetCharSize()));
var perConnOptList = INTERNET_PER_CONN_OPTION_LIST.Default;
perConnOptList.dwOptionCount = 1;
perConnOptList.pOptions = pIco;
using var pPerConnOptList = SafeCoTaskMemHandle.CreateFromStructure(perConnOptList);
int size = pPerConnOptList.Size + pIco.Size;
Win32Error.ThrowLastErrorIfFalse(InternetQueryOption(hInet, InternetOptionFlags.INTERNET_OPTION_PER_CONNECTION_OPTION, pPerConnOptList, ref size));
ico = pIco.Value;
if (getType == typeof(string))
return (T)(object)ico.Value.pszValue.ToString();
if (getType == typeof(FILETIME))
return (T)(object)ico.Value.ftValue;
return (T)(object)ico.Value.dwValue;
}
private bool HasProxyFlag(PER_CONN_FLAGS proxyFlagToCheck) => GetOption(INTERNET_PER_CONN_OPTION_ID.INTERNET_PER_CONN_FLAGS).IsFlagSet(proxyFlagToCheck);
private void SetOptionFlag(PER_CONN_FLAGS flagToSet, bool enable)
{
var currentFlags = GetOption(INTERNET_PER_CONN_OPTION_ID.INTERNET_PER_CONN_FLAGS);
var option = new INTERNET_PER_CONN_OPTION { dwOption = INTERNET_PER_CONN_OPTION_ID.INTERNET_PER_CONN_FLAGS };
option.Value.dwValue = (uint)currentFlags.SetFlags(flagToSet, enable);
SetOptions(new[] { option });
}
private void SetOptions(INTERNET_PER_CONN_OPTION[] options)
{
using var pOptions = new PinnedObject(options);
var optionList = new INTERNET_PER_CONN_OPTION_LIST
{
dwSize = (uint)Marshal.SizeOf(typeof(INTERNET_PER_CONN_OPTION_LIST)),
dwOptionCount = (uint)options.Length,
pOptions = pOptions
};
Win32Error.ThrowLastErrorIfFalse(InternetSetOption(hInet, InternetOptionFlags.INTERNET_OPTION_PER_CONNECTION_OPTION, optionList));
NotifyInternetOptionSettingsChanged();
}
private void SetOptionString(INTERNET_PER_CONN_OPTION_ID optionId, string value)
{
using var pProxy = new SafeCoTaskMemString(value, CharSet.Auto);
var option = new INTERNET_PER_CONN_OPTION { dwOption = optionId };
option.Value.pszValue = (IntPtr)pProxy;
SetOptions(new[] { option });
}
}
}