context: // 
  // MacProxy.cs
  //  
  // Author: Jeffrey Stedfast <jeff@xamarin.com>
  // 
  // Copyright (c) 2012-2014 Xamarin Inc.
  // 
  // Permission is hereby granted, free of charge, to any person obtaining a copy
  // of this software and associated documentation files (the "Software"), to deal
  // in the Software without restriction, including without limitation the rights
  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  // copies of the Software, and to permit persons to whom the Software is
  // furnished to do so, subject to the following conditions:
  // 
  // The above copyright notice and this permission notice shall be included in
  // all copies or substantial portions of the Software.
  // 
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  // THE SOFTWARE.
  // 
  
  using System;
  using System.Collections.Generic;
  using System.Runtime.InteropServices;
  using System.Threading;
  
  namespace System.Net
  {
  	internal class CFObject : IDisposable
  	{
  		public const string CoreFoundationLibrary = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation";
  		const string SystemLibrary = "/usr/lib/libSystem.dylib";
  
  		[DllImport (SystemLibrary)]
  		public static extern IntPtr dlopen (string path, int mode);
  
  		[DllImport (SystemLibrary)]
  		public static extern IntPtr dlsym (IntPtr handle, string symbol);
  
  		[DllImport (SystemLibrary)]
  		public static extern void dlclose (IntPtr handle);
  
  		public static IntPtr GetIndirect (IntPtr handle, string symbol)
  		{
  			return dlsym (handle, symbol);
  		}
  
  		public static IntPtr GetCFObjectHandle (IntPtr handle, string symbol)
  		{
  			var indirect = dlsym (handle, symbol);
  			if (indirect == IntPtr.Zero)
  				return IntPtr.Zero;
  
  			return Marshal.ReadIntPtr (indirect);
  		}
  
  		public CFObject (IntPtr handle, bool own)
  		{
  			Handle = handle;
  
  			if (!own)
  				Retain ();
  		}
  
  		~CFObject ()
  		{
  			Dispose (false);
  		}
  
  		public IntPtr Handle { get; private set; }
  
  		[DllImport (CoreFoundationLibrary)]
  		extern static IntPtr CFRetain (IntPtr handle);
  
  		void Retain ()
  		{
  			CFRetain (Handle);
  		}
  
  		[DllImport (CoreFoundationLibrary)]
  		extern static void CFRelease (IntPtr handle);
  
  		void Release ()
  		{
  			CFRelease (Handle);
  		}
  
  		protected virtual void Dispose (bool disposing)
  		{
  			if (Handle != IntPtr.Zero) {
  				Release ();
  				Handle = IntPtr.Zero;
  			}
  		}
  
  		public void Dispose ()
  		{
  			Dispose (true);
  			GC.SuppressFinalize (this);
  		}
  	}
  
  	internal class CFArray : CFObject
  	{
  		public CFArray (IntPtr handle, bool own) : base (handle, own) { }
  
  		[DllImport (CoreFoundationLibrary)]
  		extern static IntPtr CFArrayCreate (IntPtr allocator, IntPtr values, /* CFIndex */ IntPtr numValues, IntPtr callbacks);
  		static readonly IntPtr kCFTypeArrayCallbacks;
  
  		static CFArray ()
  		{
  			var handle = dlopen (CoreFoundationLibrary, 0);
  			if (handle == IntPtr.Zero)
  				return;
  
  			try {
  				kCFTypeArrayCallbacks = GetIndirect (handle, "kCFTypeArrayCallBacks");
  			} finally {
  				dlclose (handle);
  			}
  		}
  
  		static unsafe CFArray Create (params IntPtr[] values)
  		{
  			if (values == null)
  				throw new ArgumentNullException ("values");
  
  			fixed (IntPtr *pv = values) {
  				IntPtr handle = CFArrayCreate (IntPtr.Zero, (IntPtr) pv, (IntPtr) values.Length, kCFTypeArrayCallbacks);
  
  				return new CFArray (handle, false);
  			}
  		}
  
  		public static CFArray Create (params CFObject[] values)
  		{
  			if (values == null)
  				throw new ArgumentNullException ("values");
  
  			IntPtr[] _values = new IntPtr [values.Length];
  			for (int i = 0; i < _values.Length; i++)
  				_values[i] = values[i].Handle;
  
  			return Create (_values);
  		}
  
  		[DllImport (CoreFoundationLibrary)]
  		extern static /* CFIndex */ IntPtr CFArrayGetCount (IntPtr handle);
  
  		public int Count {
  			get { return (int) CFArrayGetCount (Handle); }
  		}
  
  		[DllImport (CoreFoundationLibrary)]
  		extern static IntPtr CFArrayGetValueAtIndex (IntPtr handle, /* CFIndex */ IntPtr index);
  
  		public IntPtr this[int index] {
  			get {
  				return CFArrayGetValueAtIndex (Handle, (IntPtr) index);
  			}
  		}
  	}
  
  	internal class CFNumber : CFObject
  	{
  		public CFNumber (IntPtr handle, bool own) : base (handle, own) { }
  
  		[DllImport (CoreFoundationLibrary)]
  		[return: MarshalAs (UnmanagedType.I1)]
  		extern static bool CFNumberGetValue (IntPtr handle, /* CFNumberType */ IntPtr type, [MarshalAs (UnmanagedType.I1)] out bool value);
  
  		public static bool AsBool (IntPtr handle)
  		{
  			bool value;
  
  			if (handle == IntPtr.Zero)
  				return false;
  
  			CFNumberGetValue (handle, (IntPtr) 1, out value);
  
  			return value;
  		}
  
  		public static implicit operator bool (CFNumber number)
  		{
  			return AsBool (number.Handle);
  		}
  
  		[DllImport (CoreFoundationLibrary)]
  		[return: MarshalAs (UnmanagedType.I1)]
  		extern static bool CFNumberGetValue (IntPtr handle, /* CFNumberType */ IntPtr type, out int value);
  
  		public static int AsInt32 (IntPtr handle)
  		{
  			int value;
  
  			if (handle == IntPtr.Zero)
  				return 0;
  
  			// 9 == kCFNumberIntType == C int
  			CFNumberGetValue (handle, (IntPtr) 9, out value);
  
  			return value;
  		}
  
  		public static implicit operator int (CFNumber number)
  		{
  			return AsInt32 (number.Handle);
  		}
  	}
  
  	internal struct CFRange {
  		public IntPtr Location, Length;
  		
  		public CFRange (int loc, int len)
  		{
  			Location = (IntPtr) loc;
  			Length = (IntPtr) len;
  		}
  	}
  
  	internal class CFString : CFObject
  	{
  		string str;
  
  		public CFString (IntPtr handle, bool own) : base (handle, own) { }
  
  		[DllImport (CoreFoundationLibrary)]
  		extern static IntPtr CFStringCreateWithCharacters (IntPtr alloc, IntPtr chars, /* CFIndex */ IntPtr length);
  
  		public static CFString Create (string value)
  		{
  			IntPtr handle;
  
  			unsafe {
  				fixed (char *ptr = value) {
  					handle = CFStringCreateWithCharacters (IntPtr.Zero, (IntPtr) ptr, (IntPtr) value.Length);
  				}
  			}
  
  			if (handle == IntPtr.Zero)
  				return null;
  
  			return new CFString (handle, true);
  		}
  
  		[DllImport (CoreFoundationLibrary)]
  		extern static /* CFIndex */ IntPtr CFStringGetLength (IntPtr handle);
  
  		public int Length {
  			get {
  				if (str != null)
  					return str.Length;
  
  				return (int) CFStringGetLength (Handle);
  			}
  		}
  
  		[DllImport (CoreFoundationLibrary)]
  		extern static IntPtr CFStringGetCharactersPtr (IntPtr handle);
  
  		[DllImport (CoreFoundationLibrary)]
  		extern static IntPtr CFStringGetCharacters (IntPtr handle, CFRange range, IntPtr buffer);
  
  		public static string AsString (IntPtr handle)
  		{
  			if (handle == IntPtr.Zero)
  				return null;
  			
  			int len = (int) CFStringGetLength (handle);
  			
  			if (len == 0)
  				return string.Empty;
  			
  			IntPtr chars = CFStringGetCharactersPtr (handle);
  			IntPtr buffer = IntPtr.Zero;
  			
  			if (chars == IntPtr.Zero) {
  				CFRange range = new CFRange (0, len);
  				buffer = Marshal.AllocHGlobal (len * 2);
  				CFStringGetCharacters (handle, range, buffer);
  				chars = buffer;
  			}
  
  			string str;
  
  			unsafe {
  				str = new string ((char *) chars, 0, len);
  			}
  			
  			if (buffer != IntPtr.Zero)
  				Marshal.FreeHGlobal (buffer);
  
  			return str;
  		}
  
  		public override string ToString ()
  		{
  			if (str == null)
  				str = AsString (Handle);
  
  			return str;
  		}
  
  		public static implicit operator string (CFString str)
  		{
  			return str.ToString ();
  		}
  
  		public static implicit operator CFString (string str)
  		{
  			return Create (str);
  		}
  	}
  
  	internal class CFDictionary : CFObject
  	{
  		public CFDictionary (IntPtr handle, bool own) : base (handle, own) { }
  
  		[DllImport (CoreFoundationLibrary)]
  		extern static IntPtr CFDictionaryGetValue (IntPtr handle, IntPtr key);
  
  		public IntPtr GetValue (IntPtr key)
  		{
  			return CFDictionaryGetValue (Handle, key);
  		}
  
  		public IntPtr this[IntPtr key] {
  			get {
  				return GetValue (key);
  			}
  		}
  	}
  
  	internal class CFUrl : CFObject
  	{
  		public CFUrl (IntPtr handle, bool own) : base (handle, own) { }
  
  		[DllImport (CoreFoundationLibrary)]
  		extern static IntPtr CFURLCreateWithString (IntPtr allocator, IntPtr str, IntPtr baseURL);
  
  		public static CFUrl Create (string absolute)
  		{
  			if (string.IsNullOrEmpty (absolute))
  				return null;
  
  			CFString str = CFString.Create (absolute);
  			IntPtr handle = CFURLCreateWithString (IntPtr.Zero, str.Handle, IntPtr.Zero);
  			str.Dispose ();
  
  			if (handle == IntPtr.Zero)
  				return null;
  
  			return new CFUrl (handle, true);
  		}
  	}
  
  	internal enum CFProxyType {
  		None,
  		AutoConfigurationUrl,
  		AutoConfigurationJavaScript,
  		FTP,
  		HTTP,
  		HTTPS,
  		SOCKS
  	}
  	
  	internal class CFProxy {
  		//static IntPtr kCFProxyAutoConfigurationHTTPResponseKey;
  		static IntPtr kCFProxyAutoConfigurationJavaScriptKey;
  		static IntPtr kCFProxyAutoConfigurationURLKey;
  		static IntPtr kCFProxyHostNameKey;
  		static IntPtr kCFProxyPasswordKey;
  		static IntPtr kCFProxyPortNumberKey;
  		static IntPtr kCFProxyTypeKey;
  		static IntPtr kCFProxyUsernameKey;
  
  		//static IntPtr kCFProxyTypeNone;
  		static IntPtr kCFProxyTypeAutoConfigurationURL;
  		static IntPtr kCFProxyTypeAutoConfigurationJavaScript;
  		static IntPtr kCFProxyTypeFTP;
  		static IntPtr kCFProxyTypeHTTP;
  		static IntPtr kCFProxyTypeHTTPS;
  		static IntPtr kCFProxyTypeSOCKS;
  
  		static CFProxy ()
  		{
  			IntPtr handle = CFObject.dlopen (CFNetwork.CFNetworkLibrary, 0);
  
  			//kCFProxyAutoConfigurationHTTPResponseKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationHTTPResponseKey");
  			kCFProxyAutoConfigurationJavaScriptKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationJavaScriptKey");
  			kCFProxyAutoConfigurationURLKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationURLKey");
  			kCFProxyHostNameKey = CFObject.GetCFObjectHandle (handle, "kCFProxyHostNameKey");
  			kCFProxyPasswordKey = CFObject.GetCFObjectHandle (handle, "kCFProxyPasswordKey");
  			kCFProxyPortNumberKey = CFObject.GetCFObjectHandle (handle, "kCFProxyPortNumberKey");
  			kCFProxyTypeKey = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeKey");
  			kCFProxyUsernameKey = CFObject.GetCFObjectHandle (handle, "kCFProxyUsernameKey");
  
  			//kCFProxyTypeNone = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeNone");
  			kCFProxyTypeAutoConfigurationURL = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeAutoConfigurationURL");
  			kCFProxyTypeAutoConfigurationJavaScript = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeAutoConfigurationJavaScript");
  			kCFProxyTypeFTP = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeFTP");
  			kCFProxyTypeHTTP = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeHTTP");
  			kCFProxyTypeHTTPS = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeHTTPS");
  			kCFProxyTypeSOCKS = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeSOCKS");
  
  			CFObject.dlclose (handle);
  		}
  
  		CFDictionary settings;
  		
  		internal CFProxy (CFDictionary settings)
  		{
  			this.settings = settings;
  		}
  		
  		static CFProxyType CFProxyTypeToEnum (IntPtr type)
  		{
  			if (type == kCFProxyTypeAutoConfigurationJavaScript)
  				return CFProxyType.AutoConfigurationJavaScript;
  
  			if (type == kCFProxyTypeAutoConfigurationURL)
  				return CFProxyType.AutoConfigurationUrl;
  
  			if (type == kCFProxyTypeFTP)
  				return CFProxyType.FTP;
  
  			if (type == kCFProxyTypeHTTP)
  				return CFProxyType.HTTP;
  
  			if (type == kCFProxyTypeHTTPS)
  				return CFProxyType.HTTPS;
  
  			if (type == kCFProxyTypeSOCKS)
  				return CFProxyType.SOCKS;
  			
  			return CFProxyType.None;
  		}
  		
  #if false
  		// AFAICT these get used with CFNetworkExecuteProxyAutoConfiguration*()
  		
  		// TODO: bind CFHTTPMessage so we can return the proper type here.
  		public IntPtr AutoConfigurationHTTPResponse {
  			get { return settings[kCFProxyAutoConfigurationHTTPResponseKey]; }
  		}
  #endif
  
  		public IntPtr AutoConfigurationJavaScript {
  			get {
  				if (kCFProxyAutoConfigurationJavaScriptKey == IntPtr.Zero)
  					return IntPtr.Zero;
  				
  				return settings[kCFProxyAutoConfigurationJavaScriptKey];
  			}
  		}
  		
  		public IntPtr AutoConfigurationUrl {
  			get {
  				if (kCFProxyAutoConfigurationURLKey == IntPtr.Zero)
  					return IntPtr.Zero;
  				
  				return settings[kCFProxyAutoConfigurationURLKey];
  			}
  		}
  		
  		public string HostName {
  			get {
  				if (kCFProxyHostNameKey == IntPtr.Zero)
  					return null;
  				
  				return CFString.AsString (settings[kCFProxyHostNameKey]);
  			}
  		}
  		
  		public string Password {
  			get {
  				if (kCFProxyPasswordKey == IntPtr.Zero)
  					return null;
  
  				return CFString.AsString (settings[kCFProxyPasswordKey]);
  			}
  		}
  		
  		public int Port {
  			get {
  				if (kCFProxyPortNumberKey == IntPtr.Zero)
  					return 0;
  				
  				return CFNumber.AsInt32 (settings[kCFProxyPortNumberKey]);
  			}
  		}
  		
  		public CFProxyType ProxyType {
  			get {
  				if (kCFProxyTypeKey == IntPtr.Zero)
  					return CFProxyType.None;
  				
  				return CFProxyTypeToEnum (settings[kCFProxyTypeKey]);
  			}
  		}
  		
  		public string Username {
  			get {
  				if (kCFProxyUsernameKey == IntPtr.Zero)
  					return null;
  
  				return CFString.AsString (settings[kCFProxyUsernameKey]);
  			}
  		}
  	}
  	
  	internal class CFProxySettings {
  		static IntPtr kCFNetworkProxiesHTTPEnable;
  		static IntPtr kCFNetworkProxiesHTTPPort;
  		static IntPtr kCFNetworkProxiesHTTPProxy;
  		static IntPtr kCFNetworkProxiesProxyAutoConfigEnable;
  		static IntPtr kCFNetworkProxiesProxyAutoConfigJavaScript;
  		static IntPtr kCFNetworkProxiesProxyAutoConfigURLString;
  
  		static CFProxySettings ()
  		{
  			IntPtr handle = CFObject.dlopen (CFNetwork.CFNetworkLibrary, 0);
  
  			kCFNetworkProxiesHTTPEnable = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPEnable");
  			kCFNetworkProxiesHTTPPort = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPPort");
  			kCFNetworkProxiesHTTPProxy = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPProxy");
  			kCFNetworkProxiesProxyAutoConfigEnable = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigEnable");
  			kCFNetworkProxiesProxyAutoConfigJavaScript = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigJavaScript");
  			kCFNetworkProxiesProxyAutoConfigURLString = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigURLString");
  
  			CFObject.dlclose (handle);
  		}
  
  		CFDictionary settings;
  		
  		public CFProxySettings (CFDictionary settings)
  		{
  			this.settings = settings;
  		}
  		
  		public CFDictionary Dictionary {
  			get { return settings; }
  		}
  		
  		public bool HTTPEnable {
  			get {
  				if (kCFNetworkProxiesHTTPEnable == IntPtr.Zero)
  					return false;
  
  				return CFNumber.AsBool (settings[kCFNetworkProxiesHTTPEnable]);
  			}
  		}
  		
  		public int HTTPPort {
  			get {
  				if (kCFNetworkProxiesHTTPPort == IntPtr.Zero)
  					return 0;
  				
  				return CFNumber.AsInt32 (settings[kCFNetworkProxiesHTTPPort]);
  			}
  		}
  		
  		public string HTTPProxy {
  			get {
  
gt: 				if (kCFNetworkProxiesHTTPProxy == IntPtr.Zero)