4 // Author: Jeffrey Stedfast <jeff@xamarin.com>
6 // Copyright (c) 2012-2014 Xamarin Inc.
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to deal
10 // in the Software without restriction, including without limitation the rights
11 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 // copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29 using System.Collections.Generic;
30 using System.Runtime.InteropServices;
31 using System.Threading;
36 internal class CFType {
37 [DllImport (CFObject.CoreFoundationLibrary, EntryPoint="CFGetTypeID")]
38 public static extern IntPtr GetTypeID (IntPtr typeRef);
41 internal class CFObject : IDisposable, INativeObject
43 public const string CoreFoundationLibrary = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation";
44 const string SystemLibrary = "/usr/lib/libSystem.dylib";
46 [DllImport (SystemLibrary)]
47 public static extern IntPtr dlopen (string path, int mode);
49 [DllImport (SystemLibrary)]
50 static extern IntPtr dlsym (IntPtr handle, string symbol);
52 [DllImport (SystemLibrary)]
53 public static extern void dlclose (IntPtr handle);
55 public static IntPtr GetIndirect (IntPtr handle, string symbol)
57 return dlsym (handle, symbol);
60 public static CFString GetStringConstant (IntPtr handle, string symbol)
62 var indirect = dlsym (handle, symbol);
63 if (indirect == IntPtr.Zero)
65 var actual = Marshal.ReadIntPtr (indirect);
66 if (actual == IntPtr.Zero)
68 return new CFString (actual, false);
71 public static IntPtr GetIntPtr (IntPtr handle, string symbol)
73 var indirect = dlsym (handle, symbol);
74 if (indirect == IntPtr.Zero)
76 return Marshal.ReadIntPtr (indirect);
79 public static IntPtr GetCFObjectHandle (IntPtr handle, string symbol)
81 var indirect = dlsym (handle, symbol);
82 if (indirect == IntPtr.Zero)
85 return Marshal.ReadIntPtr (indirect);
88 public CFObject (IntPtr handle, bool own)
101 public IntPtr Handle { get; private set; }
103 [DllImport (CoreFoundationLibrary)]
104 internal extern static IntPtr CFRetain (IntPtr handle);
111 [DllImport (CoreFoundationLibrary)]
112 internal extern static void CFRelease (IntPtr handle);
119 protected virtual void Dispose (bool disposing)
121 if (Handle != IntPtr.Zero) {
123 Handle = IntPtr.Zero;
127 public void Dispose ()
130 GC.SuppressFinalize (this);
134 internal class CFArray : CFObject
136 public CFArray (IntPtr handle, bool own) : base (handle, own) { }
138 [DllImport (CoreFoundationLibrary)]
139 extern static IntPtr CFArrayCreate (IntPtr allocator, IntPtr values, /* CFIndex */ IntPtr numValues, IntPtr callbacks);
140 static readonly IntPtr kCFTypeArrayCallbacks;
144 var handle = dlopen (CoreFoundationLibrary, 0);
145 if (handle == IntPtr.Zero)
149 kCFTypeArrayCallbacks = GetIndirect (handle, "kCFTypeArrayCallBacks");
155 public static CFArray FromNativeObjects (params INativeObject[] values)
157 return new CFArray (Create (values), true);
160 public static unsafe IntPtr Create (params IntPtr[] values)
163 throw new ArgumentNullException ("values");
164 fixed (IntPtr* pv = values) {
165 return CFArrayCreate (IntPtr.Zero, (IntPtr) pv, (IntPtr)values.Length, kCFTypeArrayCallbacks);
169 internal static unsafe CFArray CreateArray (params IntPtr[] values)
172 throw new ArgumentNullException ("values");
174 fixed (IntPtr *pv = values) {
175 IntPtr handle = CFArrayCreate (IntPtr.Zero, (IntPtr) pv, (IntPtr) values.Length, kCFTypeArrayCallbacks);
177 return new CFArray (handle, false);
181 public static CFArray CreateArray (params INativeObject[] values)
183 return new CFArray (Create (values), true);
186 public static IntPtr Create (params INativeObject[] values)
189 throw new ArgumentNullException ("values");
190 IntPtr[] _values = new IntPtr [values.Length];
191 for (int i = 0; i < _values.Length; ++i)
192 _values [i] = values [i].Handle;
193 return Create (_values);
196 [DllImport (CoreFoundationLibrary)]
197 extern static /* CFIndex */ IntPtr CFArrayGetCount (IntPtr handle);
200 get { return (int) CFArrayGetCount (Handle); }
203 [DllImport (CoreFoundationLibrary)]
204 extern static IntPtr CFArrayGetValueAtIndex (IntPtr handle, /* CFIndex */ IntPtr index);
206 public IntPtr this[int index] {
208 return CFArrayGetValueAtIndex (Handle, (IntPtr) index);
212 static public T [] ArrayFromHandle<T> (IntPtr handle, Func<IntPtr, T> creation) where T : class, INativeObject
214 if (handle == IntPtr.Zero)
217 var c = CFArrayGetCount (handle);
218 T [] ret = new T [(int)c];
220 for (uint i = 0; i < (uint)c; i++) {
221 ret [i] = creation (CFArrayGetValueAtIndex (handle, (IntPtr)i));
227 internal class CFNumber : CFObject
229 public CFNumber (IntPtr handle, bool own) : base (handle, own) { }
231 [DllImport (CoreFoundationLibrary)]
232 [return: MarshalAs (UnmanagedType.I1)]
233 extern static bool CFNumberGetValue (IntPtr handle, /* CFNumberType */ IntPtr type, [MarshalAs (UnmanagedType.I1)] out bool value);
235 public static bool AsBool (IntPtr handle)
239 if (handle == IntPtr.Zero)
242 CFNumberGetValue (handle, (IntPtr) 1, out value);
247 public static implicit operator bool (CFNumber number)
249 return AsBool (number.Handle);
252 [DllImport (CoreFoundationLibrary)]
253 [return: MarshalAs (UnmanagedType.I1)]
254 extern static bool CFNumberGetValue (IntPtr handle, /* CFNumberType */ IntPtr type, out int value);
256 public static int AsInt32 (IntPtr handle)
260 if (handle == IntPtr.Zero)
263 // 9 == kCFNumberIntType == C int
264 CFNumberGetValue (handle, (IntPtr) 9, out value);
269 [DllImport (CoreFoundationLibrary)]
270 extern static IntPtr CFNumberCreate (IntPtr allocator, IntPtr theType, IntPtr valuePtr);
272 public static CFNumber FromInt32 (int number)
274 // 9 == kCFNumberIntType == C int
275 return new CFNumber (CFNumberCreate (IntPtr.Zero, (IntPtr)9, (IntPtr)number), true);
278 public static implicit operator int (CFNumber number)
280 return AsInt32 (number.Handle);
284 internal struct CFRange {
285 public IntPtr Location, Length;
287 public CFRange (int loc, int len)
289 Location = (IntPtr) loc;
290 Length = (IntPtr) len;
294 internal struct CFStreamClientContext {
295 public IntPtr Version;
297 public IntPtr Retain;
298 public IntPtr Release;
299 public IntPtr CopyDescription;
302 internal class CFString : CFObject
306 public CFString (IntPtr handle, bool own) : base (handle, own) { }
308 [DllImport (CoreFoundationLibrary)]
309 extern static IntPtr CFStringCreateWithCharacters (IntPtr alloc, IntPtr chars, /* CFIndex */ IntPtr length);
311 public static CFString Create (string value)
316 fixed (char *ptr = value) {
317 handle = CFStringCreateWithCharacters (IntPtr.Zero, (IntPtr) ptr, (IntPtr) value.Length);
321 if (handle == IntPtr.Zero)
324 return new CFString (handle, true);
327 [DllImport (CoreFoundationLibrary)]
328 extern static /* CFIndex */ IntPtr CFStringGetLength (IntPtr handle);
335 return (int) CFStringGetLength (Handle);
339 [DllImport (CoreFoundationLibrary)]
340 extern static IntPtr CFStringGetCharactersPtr (IntPtr handle);
342 [DllImport (CoreFoundationLibrary)]
343 extern static IntPtr CFStringGetCharacters (IntPtr handle, CFRange range, IntPtr buffer);
345 public static string AsString (IntPtr handle)
347 if (handle == IntPtr.Zero)
350 int len = (int) CFStringGetLength (handle);
355 IntPtr chars = CFStringGetCharactersPtr (handle);
356 IntPtr buffer = IntPtr.Zero;
358 if (chars == IntPtr.Zero) {
359 CFRange range = new CFRange (0, len);
360 buffer = Marshal.AllocHGlobal (len * 2);
361 CFStringGetCharacters (handle, range, buffer);
368 str = new string ((char *) chars, 0, len);
371 if (buffer != IntPtr.Zero)
372 Marshal.FreeHGlobal (buffer);
377 public override string ToString ()
380 str = AsString (Handle);
385 public static implicit operator string (CFString str)
387 return str.ToString ();
390 public static implicit operator CFString (string str)
397 internal class CFData : CFObject
399 public CFData (IntPtr handle, bool own) : base (handle, own) { }
401 [DllImport (CoreFoundationLibrary)]
402 extern static /* CFDataRef */ IntPtr CFDataCreate (/* CFAllocatorRef */ IntPtr allocator, /* UInt8* */ IntPtr bytes, /* CFIndex */ IntPtr length);
403 public unsafe static CFData FromData (byte [] buffer)
405 fixed (byte* p = buffer)
407 return FromData ((IntPtr)p, (IntPtr)buffer.Length);
411 public static CFData FromData (IntPtr buffer, IntPtr length)
413 return new CFData (CFDataCreate (IntPtr.Zero, buffer, length), true);
416 public IntPtr Length {
417 get { return CFDataGetLength (Handle); }
420 [DllImport (CoreFoundationLibrary)]
421 extern static /* CFIndex */ IntPtr CFDataGetLength (/* CFDataRef */ IntPtr theData);
423 [DllImport (CoreFoundationLibrary)]
424 extern static /* UInt8* */ IntPtr CFDataGetBytePtr (/* CFDataRef */ IntPtr theData);
427 * Exposes a read-only pointer to the underlying storage.
429 public IntPtr Bytes {
430 get { return CFDataGetBytePtr (Handle); }
433 public byte this [long idx] {
435 if (idx < 0 || (ulong) idx > (ulong) Length)
436 throw new ArgumentException ("idx");
437 return Marshal.ReadByte (new IntPtr (Bytes.ToInt64 () + idx));
441 throw new NotImplementedException ("NSData arrays can not be modified, use an NSMutableData instead");
447 internal class CFDictionary : CFObject
449 static readonly IntPtr KeyCallbacks;
450 static readonly IntPtr ValueCallbacks;
452 static CFDictionary ()
454 var handle = dlopen (CoreFoundationLibrary, 0);
455 if (handle == IntPtr.Zero)
459 KeyCallbacks = GetIndirect (handle, "kCFTypeDictionaryKeyCallBacks");
460 ValueCallbacks = GetIndirect (handle, "kCFTypeDictionaryValueCallBacks");
466 public CFDictionary (IntPtr handle, bool own) : base (handle, own) { }
468 public static CFDictionary FromObjectAndKey (IntPtr obj, IntPtr key)
470 return new CFDictionary (CFDictionaryCreate (IntPtr.Zero, new IntPtr[] { key }, new IntPtr [] { obj }, (IntPtr)1, KeyCallbacks, ValueCallbacks), true);
473 [DllImport (CoreFoundationLibrary)]
474 extern static IntPtr CFDictionaryCreate (IntPtr allocator, IntPtr[] keys, IntPtr[] vals, IntPtr len, IntPtr keyCallbacks, IntPtr valCallbacks);
476 [DllImport (CoreFoundationLibrary)]
477 extern static IntPtr CFDictionaryGetValue (IntPtr handle, IntPtr key);
479 [DllImport (CoreFoundationLibrary)]
480 extern static IntPtr CFDictionaryCreateCopy (IntPtr allocator, IntPtr handle);
482 public CFDictionary Copy ()
484 return new CFDictionary (CFDictionaryCreateCopy (IntPtr.Zero, Handle), true);
487 public CFMutableDictionary MutableCopy ()
489 return new CFMutableDictionary (CFDictionaryCreateMutableCopy (IntPtr.Zero, IntPtr.Zero, Handle), true);
492 [DllImport (CoreFoundationLibrary)]
493 extern static IntPtr CFDictionaryCreateMutableCopy (IntPtr allocator, IntPtr capacity, IntPtr theDict);
495 public IntPtr GetValue (IntPtr key)
497 return CFDictionaryGetValue (Handle, key);
500 public IntPtr this[IntPtr key] {
502 return GetValue (key);
507 internal class CFMutableDictionary : CFDictionary
509 public CFMutableDictionary (IntPtr handle, bool own) : base (handle, own) { }
511 public void SetValue (IntPtr key, IntPtr val)
513 CFDictionarySetValue (Handle, key, val);
516 [DllImport (CoreFoundationLibrary)]
517 extern static void CFDictionarySetValue (IntPtr handle, IntPtr key, IntPtr val);
520 internal class CFUrl : CFObject
522 public CFUrl (IntPtr handle, bool own) : base (handle, own) { }
524 [DllImport (CoreFoundationLibrary)]
525 extern static IntPtr CFURLCreateWithString (IntPtr allocator, IntPtr str, IntPtr baseURL);
527 public static CFUrl Create (string absolute)
529 if (string.IsNullOrEmpty (absolute))
532 CFString str = CFString.Create (absolute);
533 IntPtr handle = CFURLCreateWithString (IntPtr.Zero, str.Handle, IntPtr.Zero);
536 if (handle == IntPtr.Zero)
539 return new CFUrl (handle, true);
543 internal class CFRunLoop : CFObject
545 [DllImport (CFObject.CoreFoundationLibrary)]
546 static extern void CFRunLoopAddSource (IntPtr rl, IntPtr source, IntPtr mode);
548 [DllImport (CFObject.CoreFoundationLibrary)]
549 static extern void CFRunLoopRemoveSource (IntPtr rl, IntPtr source, IntPtr mode);
551 [DllImport (CFObject.CoreFoundationLibrary)]
552 static extern int CFRunLoopRunInMode (IntPtr mode, double seconds, bool returnAfterSourceHandled);
554 [DllImport (CFObject.CoreFoundationLibrary)]
555 static extern IntPtr CFRunLoopGetCurrent ();
557 [DllImport (CFObject.CoreFoundationLibrary)]
558 static extern void CFRunLoopStop (IntPtr rl);
560 public CFRunLoop (IntPtr handle, bool own): base (handle, own)
564 public static CFRunLoop CurrentRunLoop {
565 get { return new CFRunLoop (CFRunLoopGetCurrent (), false); }
568 public void AddSource (IntPtr source, CFString mode)
570 CFRunLoopAddSource (Handle, source, mode.Handle);
573 public void RemoveSource (IntPtr source, CFString mode)
575 CFRunLoopRemoveSource (Handle, source, mode.Handle);
578 public int RunInMode (CFString mode, double seconds, bool returnAfterSourceHandled)
580 return CFRunLoopRunInMode (mode.Handle, seconds, returnAfterSourceHandled);
585 CFRunLoopStop (Handle);
589 internal enum CFProxyType {
591 AutoConfigurationUrl,
592 AutoConfigurationJavaScript,
599 internal class CFProxy {
600 //static IntPtr kCFProxyAutoConfigurationHTTPResponseKey;
601 static IntPtr kCFProxyAutoConfigurationJavaScriptKey;
602 static IntPtr kCFProxyAutoConfigurationURLKey;
603 static IntPtr kCFProxyHostNameKey;
604 static IntPtr kCFProxyPasswordKey;
605 static IntPtr kCFProxyPortNumberKey;
606 static IntPtr kCFProxyTypeKey;
607 static IntPtr kCFProxyUsernameKey;
609 //static IntPtr kCFProxyTypeNone;
610 static IntPtr kCFProxyTypeAutoConfigurationURL;
611 static IntPtr kCFProxyTypeAutoConfigurationJavaScript;
612 static IntPtr kCFProxyTypeFTP;
613 static IntPtr kCFProxyTypeHTTP;
614 static IntPtr kCFProxyTypeHTTPS;
615 static IntPtr kCFProxyTypeSOCKS;
619 IntPtr handle = CFObject.dlopen (CFNetwork.CFNetworkLibrary, 0);
621 //kCFProxyAutoConfigurationHTTPResponseKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationHTTPResponseKey");
622 kCFProxyAutoConfigurationJavaScriptKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationJavaScriptKey");
623 kCFProxyAutoConfigurationURLKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationURLKey");
624 kCFProxyHostNameKey = CFObject.GetCFObjectHandle (handle, "kCFProxyHostNameKey");
625 kCFProxyPasswordKey = CFObject.GetCFObjectHandle (handle, "kCFProxyPasswordKey");
626 kCFProxyPortNumberKey = CFObject.GetCFObjectHandle (handle, "kCFProxyPortNumberKey");
627 kCFProxyTypeKey = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeKey");
628 kCFProxyUsernameKey = CFObject.GetCFObjectHandle (handle, "kCFProxyUsernameKey");
630 //kCFProxyTypeNone = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeNone");
631 kCFProxyTypeAutoConfigurationURL = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeAutoConfigurationURL");
632 kCFProxyTypeAutoConfigurationJavaScript = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeAutoConfigurationJavaScript");
633 kCFProxyTypeFTP = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeFTP");
634 kCFProxyTypeHTTP = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeHTTP");
635 kCFProxyTypeHTTPS = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeHTTPS");
636 kCFProxyTypeSOCKS = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeSOCKS");
638 CFObject.dlclose (handle);
641 CFDictionary settings;
643 internal CFProxy (CFDictionary settings)
645 this.settings = settings;
648 static CFProxyType CFProxyTypeToEnum (IntPtr type)
650 if (type == kCFProxyTypeAutoConfigurationJavaScript)
651 return CFProxyType.AutoConfigurationJavaScript;
653 if (type == kCFProxyTypeAutoConfigurationURL)
654 return CFProxyType.AutoConfigurationUrl;
656 if (type == kCFProxyTypeFTP)
657 return CFProxyType.FTP;
659 if (type == kCFProxyTypeHTTP)
660 return CFProxyType.HTTP;
662 if (type == kCFProxyTypeHTTPS)
663 return CFProxyType.HTTPS;
665 if (type == kCFProxyTypeSOCKS)
666 return CFProxyType.SOCKS;
668 return CFProxyType.None;
672 // AFAICT these get used with CFNetworkExecuteProxyAutoConfiguration*()
674 // TODO: bind CFHTTPMessage so we can return the proper type here.
675 public IntPtr AutoConfigurationHTTPResponse {
676 get { return settings[kCFProxyAutoConfigurationHTTPResponseKey]; }
680 public IntPtr AutoConfigurationJavaScript {
682 if (kCFProxyAutoConfigurationJavaScriptKey == IntPtr.Zero)
685 return settings[kCFProxyAutoConfigurationJavaScriptKey];
689 public IntPtr AutoConfigurationUrl {
691 if (kCFProxyAutoConfigurationURLKey == IntPtr.Zero)
694 return settings[kCFProxyAutoConfigurationURLKey];
698 public string HostName {
700 if (kCFProxyHostNameKey == IntPtr.Zero)
703 return CFString.AsString (settings[kCFProxyHostNameKey]);
707 public string Password {
709 if (kCFProxyPasswordKey == IntPtr.Zero)
712 return CFString.AsString (settings[kCFProxyPasswordKey]);
718 if (kCFProxyPortNumberKey == IntPtr.Zero)
721 return CFNumber.AsInt32 (settings[kCFProxyPortNumberKey]);
725 public CFProxyType ProxyType {
727 if (kCFProxyTypeKey == IntPtr.Zero)
728 return CFProxyType.None;
730 return CFProxyTypeToEnum (settings[kCFProxyTypeKey]);
734 public string Username {
736 if (kCFProxyUsernameKey == IntPtr.Zero)
739 return CFString.AsString (settings[kCFProxyUsernameKey]);
744 internal class CFProxySettings {
745 static IntPtr kCFNetworkProxiesHTTPEnable;
746 static IntPtr kCFNetworkProxiesHTTPPort;
747 static IntPtr kCFNetworkProxiesHTTPProxy;
748 static IntPtr kCFNetworkProxiesProxyAutoConfigEnable;
749 static IntPtr kCFNetworkProxiesProxyAutoConfigJavaScript;
750 static IntPtr kCFNetworkProxiesProxyAutoConfigURLString;
752 static CFProxySettings ()
754 IntPtr handle = CFObject.dlopen (CFNetwork.CFNetworkLibrary, 0);
756 kCFNetworkProxiesHTTPEnable = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPEnable");
757 kCFNetworkProxiesHTTPPort = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPPort");
758 kCFNetworkProxiesHTTPProxy = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPProxy");
759 kCFNetworkProxiesProxyAutoConfigEnable = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigEnable");
760 kCFNetworkProxiesProxyAutoConfigJavaScript = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigJavaScript");
761 kCFNetworkProxiesProxyAutoConfigURLString = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigURLString");
763 CFObject.dlclose (handle);
766 CFDictionary settings;
768 public CFProxySettings (CFDictionary settings)
770 this.settings = settings;
773 public CFDictionary Dictionary {
774 get { return settings; }
777 public bool HTTPEnable {
779 if (kCFNetworkProxiesHTTPEnable == IntPtr.Zero)
782 return CFNumber.AsBool (settings[kCFNetworkProxiesHTTPEnable]);
786 public int HTTPPort {
788 if (kCFNetworkProxiesHTTPPort == IntPtr.Zero)
791 return CFNumber.AsInt32 (settings[kCFNetworkProxiesHTTPPort]);
795 public string HTTPProxy {
797 if (kCFNetworkProxiesHTTPProxy == IntPtr.Zero)
800 return CFString.AsString (settings[kCFNetworkProxiesHTTPProxy]);
804 public bool ProxyAutoConfigEnable {
806 if (kCFNetworkProxiesProxyAutoConfigEnable == IntPtr.Zero)
809 return CFNumber.AsBool (settings[kCFNetworkProxiesProxyAutoConfigEnable]);
813 public string ProxyAutoConfigJavaScript {
815 if (kCFNetworkProxiesProxyAutoConfigJavaScript == IntPtr.Zero)
818 return CFString.AsString (settings[kCFNetworkProxiesProxyAutoConfigJavaScript]);
822 public string ProxyAutoConfigURLString {
824 if (kCFNetworkProxiesProxyAutoConfigURLString == IntPtr.Zero)
827 return CFString.AsString (settings[kCFNetworkProxiesProxyAutoConfigURLString]);
832 internal static class CFNetwork {
834 public const string CFNetworkLibrary = "/System/Library/Frameworks/CoreServices.framework/Frameworks/CFNetwork.framework/CFNetwork";
836 public const string CFNetworkLibrary = "/System/Library/Frameworks/CFNetwork.framework/CFNetwork";
839 [DllImport (CFNetworkLibrary, EntryPoint = "CFNetworkCopyProxiesForAutoConfigurationScript")]
840 // CFArrayRef CFNetworkCopyProxiesForAutoConfigurationScript (CFStringRef proxyAutoConfigurationScript, CFURLRef targetURL, CFErrorRef* error);
841 extern static IntPtr CFNetworkCopyProxiesForAutoConfigurationScriptSequential (IntPtr proxyAutoConfigurationScript, IntPtr targetURL, out IntPtr error);
843 [DllImport (CFNetworkLibrary)]
844 extern static IntPtr CFNetworkExecuteProxyAutoConfigurationURL (IntPtr proxyAutoConfigURL, IntPtr targetURL, CFProxyAutoConfigurationResultCallback cb, ref CFStreamClientContext clientContext);
847 class GetProxyData : IDisposable {
848 public IntPtr script;
849 public IntPtr targetUri;
851 public IntPtr result;
852 public ManualResetEvent evt = new ManualResetEvent (false);
854 public void Dispose ()
860 static object lock_obj = new object ();
861 static Queue<GetProxyData> get_proxy_queue;
862 static AutoResetEvent proxy_event;
864 static void CFNetworkCopyProxiesForAutoConfigurationScriptThread ()
867 var data_left = true;
870 proxy_event.WaitOne ();
874 if (get_proxy_queue.Count == 0)
876 data = get_proxy_queue.Dequeue ();
877 data_left = get_proxy_queue.Count > 0;
880 data.result = CFNetworkCopyProxiesForAutoConfigurationScriptSequential (data.script, data.targetUri, out data.error);
886 static IntPtr CFNetworkCopyProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, IntPtr targetURL, out IntPtr error)
888 // This method must only be called on only one thread during an application's life time.
889 // Note that it's not enough to use a lock to make calls sequential across different threads,
890 // it has to be one thread. Also note that that thread can't be the main thread, because the
891 // main thread might be blocking waiting for this network request to finish.
892 // Another possibility would be to use JavaScriptCore to execute this piece of
893 // javascript ourselves, but unfortunately it's not available before iOS7.
894 // See bug #7923 comment #21+.
896 using (var data = new GetProxyData ()) {
897 data.script = proxyAutoConfigurationScript;
898 data.targetUri = targetURL;
901 if (get_proxy_queue == null) {
902 get_proxy_queue = new Queue<GetProxyData> ();
903 proxy_event = new AutoResetEvent (false);
904 new Thread (CFNetworkCopyProxiesForAutoConfigurationScriptThread) {
908 get_proxy_queue.Enqueue (data);
920 static CFArray CopyProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, CFUrl targetURL)
922 IntPtr err = IntPtr.Zero;
923 IntPtr native = CFNetworkCopyProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL.Handle, out err);
925 if (native == IntPtr.Zero)
928 return new CFArray (native, true);
931 public static CFProxy[] GetProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, CFUrl targetURL)
933 if (proxyAutoConfigurationScript == IntPtr.Zero)
934 throw new ArgumentNullException ("proxyAutoConfigurationScript");
936 if (targetURL == null)
937 throw new ArgumentNullException ("targetURL");
939 CFArray array = CopyProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL);
944 CFProxy[] proxies = new CFProxy [array.Count];
945 for (int i = 0; i < proxies.Length; i++) {
946 CFDictionary dict = new CFDictionary (array[i], false);
947 proxies[i] = new CFProxy (dict);
955 public static CFProxy[] GetProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, Uri targetUri)
957 if (proxyAutoConfigurationScript == IntPtr.Zero)
958 throw new ArgumentNullException ("proxyAutoConfigurationScript");
960 if (targetUri == null)
961 throw new ArgumentNullException ("targetUri");
963 CFUrl targetURL = CFUrl.Create (targetUri.AbsoluteUri);
964 CFProxy[] proxies = GetProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL);
965 targetURL.Dispose ();
970 delegate void CFProxyAutoConfigurationResultCallback (IntPtr client, IntPtr proxyList, IntPtr error);
972 public static CFProxy[] ExecuteProxyAutoConfigurationURL (IntPtr proxyAutoConfigURL, Uri targetURL)
974 CFUrl url = CFUrl.Create (targetURL.AbsoluteUri);
978 CFProxy[] proxies = null;
980 var runLoop = CFRunLoop.CurrentRunLoop;
982 // Callback that will be called after executing the configuration script
983 CFProxyAutoConfigurationResultCallback cb = delegate (IntPtr client, IntPtr proxyList, IntPtr error) {
984 if (proxyList != IntPtr.Zero) {
985 var array = new CFArray (proxyList, false);
986 proxies = new CFProxy [array.Count];
987 for (int i = 0; i < proxies.Length; i++) {
988 CFDictionary dict = new CFDictionary (array[i], false);
989 proxies[i] = new CFProxy (dict);
996 var clientContext = new CFStreamClientContext ();
997 var loopSource = CFNetworkExecuteProxyAutoConfigurationURL (proxyAutoConfigURL, url.Handle, cb, ref clientContext);
999 // Create a private mode
1000 var mode = CFString.Create ("Mono.MacProxy");
1002 runLoop.AddSource (loopSource, mode);
1003 runLoop.RunInMode (mode, double.MaxValue, false);
1004 runLoop.RemoveSource (loopSource, mode);
1009 [DllImport (CFNetworkLibrary)]
1010 // CFArrayRef CFNetworkCopyProxiesForURL (CFURLRef url, CFDictionaryRef proxySettings);
1011 extern static IntPtr CFNetworkCopyProxiesForURL (IntPtr url, IntPtr proxySettings);
1013 static CFArray CopyProxiesForURL (CFUrl url, CFDictionary proxySettings)
1015 IntPtr native = CFNetworkCopyProxiesForURL (url.Handle, proxySettings != null ? proxySettings.Handle : IntPtr.Zero);
1017 if (native == IntPtr.Zero)
1020 return new CFArray (native, true);
1023 public static CFProxy[] GetProxiesForURL (CFUrl url, CFProxySettings proxySettings)
1025 if (url == null || url.Handle == IntPtr.Zero)
1026 throw new ArgumentNullException ("url");
1028 if (proxySettings == null)
1029 proxySettings = GetSystemProxySettings ();
1031 CFArray array = CopyProxiesForURL (url, proxySettings.Dictionary);
1036 CFProxy[] proxies = new CFProxy [array.Count];
1037 for (int i = 0; i < proxies.Length; i++) {
1038 CFDictionary dict = new CFDictionary (array[i], false);
1039 proxies[i] = new CFProxy (dict);
1047 public static CFProxy[] GetProxiesForUri (Uri uri, CFProxySettings proxySettings)
1050 throw new ArgumentNullException ("uri");
1052 CFUrl url = CFUrl.Create (uri.AbsoluteUri);
1056 CFProxy[] proxies = GetProxiesForURL (url, proxySettings);
1062 [DllImport (CFNetworkLibrary)]
1063 // CFDictionaryRef CFNetworkCopySystemProxySettings (void);
1064 extern static IntPtr CFNetworkCopySystemProxySettings ();
1066 public static CFProxySettings GetSystemProxySettings ()
1068 IntPtr native = CFNetworkCopySystemProxySettings ();
1070 if (native == IntPtr.Zero)
1073 var dict = new CFDictionary (native, true);
1075 return new CFProxySettings (dict);
1078 class CFWebProxy : IWebProxy {
1079 ICredentials credentials;
1082 public CFWebProxy ()
1086 public ICredentials Credentials {
1087 get { return credentials; }
1089 userSpecified = true;
1090 credentials = value;
1094 static Uri GetProxyUri (CFProxy proxy, out NetworkCredential credentials)
1098 switch (proxy.ProxyType) {
1099 case CFProxyType.FTP:
1100 protocol = "ftp://";
1102 case CFProxyType.HTTP:
1103 case CFProxyType.HTTPS:
1104 protocol = "http://";
1111 string username = proxy.Username;
1112 string password = proxy.Password;
1113 string hostname = proxy.HostName;
1114 int port = proxy.Port;
1117 if (username != null)
1118 credentials = new NetworkCredential (username, password);
1122 uri = protocol + hostname + (port != 0 ? ':' + port.ToString () : string.Empty);
1124 return new Uri (uri, UriKind.Absolute);
1127 static Uri GetProxyUriFromScript (IntPtr script, Uri targetUri, out NetworkCredential credentials)
1129 CFProxy[] proxies = CFNetwork.GetProxiesForAutoConfigurationScript (script, targetUri);
1130 return SelectProxy (proxies, targetUri, out credentials);
1133 static Uri ExecuteProxyAutoConfigurationURL (IntPtr proxyAutoConfigURL, Uri targetUri, out NetworkCredential credentials)
1135 CFProxy[] proxies = CFNetwork.ExecuteProxyAutoConfigurationURL (proxyAutoConfigURL, targetUri);
1136 return SelectProxy (proxies, targetUri, out credentials);
1140 static Uri SelectProxy (CFProxy[] proxies, Uri targetUri, out NetworkCredential credentials)
1142 if (proxies == null) {
1147 for (int i = 0; i < proxies.Length; i++) {
1148 switch (proxies[i].ProxyType) {
1149 case CFProxyType.HTTPS:
1150 case CFProxyType.HTTP:
1151 case CFProxyType.FTP:
1152 // create a Uri based on the hostname/port/etc info
1153 return GetProxyUri (proxies[i], out credentials);
1154 case CFProxyType.SOCKS:
1156 // unsupported proxy type, try the next one
1158 case CFProxyType.None:
1159 // no proxy should be used
1170 public Uri GetProxy (Uri targetUri)
1172 NetworkCredential credentials = null;
1175 if (targetUri == null)
1176 throw new ArgumentNullException ("targetUri");
1179 CFProxySettings settings = CFNetwork.GetSystemProxySettings ();
1180 CFProxy[] proxies = CFNetwork.GetProxiesForUri (targetUri, settings);
1182 if (proxies != null) {
1183 for (int i = 0; i < proxies.Length && proxy == null; i++) {
1184 switch (proxies[i].ProxyType) {
1185 case CFProxyType.AutoConfigurationJavaScript:
1186 proxy = GetProxyUriFromScript (proxies[i].AutoConfigurationJavaScript, targetUri, out credentials);
1188 case CFProxyType.AutoConfigurationUrl:
1189 proxy = ExecuteProxyAutoConfigurationURL (proxies[i].AutoConfigurationUrl, targetUri, out credentials);
1191 case CFProxyType.HTTPS:
1192 case CFProxyType.HTTP:
1193 case CFProxyType.FTP:
1194 // create a Uri based on the hostname/port/etc info
1195 proxy = GetProxyUri (proxies[i], out credentials);
1197 case CFProxyType.SOCKS:
1198 // unsupported proxy type, try the next one
1200 case CFProxyType.None:
1201 // no proxy should be used
1207 if (proxy == null) {
1208 // no supported proxies for this Uri, fall back to trying to connect to targetUri directly
1215 // ignore errors while retrieving proxy data
1220 this.credentials = credentials;
1225 public bool IsBypassed (Uri targetUri)
1227 if (targetUri == null)
1228 throw new ArgumentNullException ("targetUri");
1230 return GetProxy (targetUri) == targetUri;
1234 public static IWebProxy GetDefaultProxy ()
1236 return new CFWebProxy ();
1240 class CFBoolean : INativeObject, IDisposable {
1243 public static readonly CFBoolean True;
1244 public static readonly CFBoolean False;
1248 var handle = CFObject.dlopen (CFObject.CoreFoundationLibrary, 0);
1249 if (handle == IntPtr.Zero)
1252 True = new CFBoolean (CFObject.GetCFObjectHandle (handle, "kCFBooleanTrue"), false);
1253 False = new CFBoolean (CFObject.GetCFObjectHandle (handle, "kCFBooleanFalse"), false);
1256 CFObject.dlclose (handle);
1260 internal CFBoolean (IntPtr handle, bool owns)
1262 this.handle = handle;
1264 CFObject.CFRetain (handle);
1272 public IntPtr Handle {
1278 public void Dispose ()
1281 GC.SuppressFinalize (this);
1284 protected virtual void Dispose (bool disposing)
1286 if (handle != IntPtr.Zero){
1287 CFObject.CFRelease (handle);
1288 handle = IntPtr.Zero;
1292 public static implicit operator bool (CFBoolean value)
1297 public static explicit operator CFBoolean (bool value)
1299 return FromBoolean (value);
1302 public static CFBoolean FromBoolean (bool value)
1304 return value ? True : False;
1307 [DllImport (CFObject.CoreFoundationLibrary)]
1308 [return: MarshalAs (UnmanagedType.I1)]
1309 extern static /* Boolean */ bool CFBooleanGetValue (/* CFBooleanRef */ IntPtr boolean);
1312 get {return CFBooleanGetValue (handle);}
1315 public static bool GetValue (IntPtr boolean)
1317 return CFBooleanGetValue (boolean);