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 public static CFDictionary FromKeysAndObjects (IList<Tuple<IntPtr,IntPtr>> items)
475 var keys = new IntPtr [items.Count];
476 var values = new IntPtr [items.Count];
477 for (int i = 0; i < items.Count; i++) {
478 keys [i] = items [i].Item1;
479 values [i] = items [i].Item2;
481 return new CFDictionary (CFDictionaryCreate (IntPtr.Zero, keys, values, (IntPtr)items.Count, KeyCallbacks, ValueCallbacks), true);
484 [DllImport (CoreFoundationLibrary)]
485 extern static IntPtr CFDictionaryCreate (IntPtr allocator, IntPtr[] keys, IntPtr[] vals, IntPtr len, IntPtr keyCallbacks, IntPtr valCallbacks);
487 [DllImport (CoreFoundationLibrary)]
488 extern static IntPtr CFDictionaryGetValue (IntPtr handle, IntPtr key);
490 [DllImport (CoreFoundationLibrary)]
491 extern static IntPtr CFDictionaryCreateCopy (IntPtr allocator, IntPtr handle);
493 public CFDictionary Copy ()
495 return new CFDictionary (CFDictionaryCreateCopy (IntPtr.Zero, Handle), true);
498 public CFMutableDictionary MutableCopy ()
500 return new CFMutableDictionary (CFDictionaryCreateMutableCopy (IntPtr.Zero, IntPtr.Zero, Handle), true);
503 [DllImport (CoreFoundationLibrary)]
504 extern static IntPtr CFDictionaryCreateMutableCopy (IntPtr allocator, IntPtr capacity, IntPtr theDict);
506 public IntPtr GetValue (IntPtr key)
508 return CFDictionaryGetValue (Handle, key);
511 public IntPtr this[IntPtr key] {
513 return GetValue (key);
518 internal class CFMutableDictionary : CFDictionary
520 public CFMutableDictionary (IntPtr handle, bool own) : base (handle, own) { }
522 public void SetValue (IntPtr key, IntPtr val)
524 CFDictionarySetValue (Handle, key, val);
527 public static CFMutableDictionary Create ()
529 var handle = CFDictionaryCreateMutable (IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
530 if (handle == IntPtr.Zero)
531 throw new InvalidOperationException ();
532 return new CFMutableDictionary (handle, true);
535 [DllImport (CoreFoundationLibrary)]
536 extern static void CFDictionarySetValue (IntPtr handle, IntPtr key, IntPtr val);
538 [DllImport (CoreFoundationLibrary)]
539 extern static IntPtr CFDictionaryCreateMutable (IntPtr allocator, IntPtr capacity, IntPtr keyCallback, IntPtr valueCallbacks);
543 internal class CFUrl : CFObject
545 public CFUrl (IntPtr handle, bool own) : base (handle, own) { }
547 [DllImport (CoreFoundationLibrary)]
548 extern static IntPtr CFURLCreateWithString (IntPtr allocator, IntPtr str, IntPtr baseURL);
550 public static CFUrl Create (string absolute)
552 if (string.IsNullOrEmpty (absolute))
555 CFString str = CFString.Create (absolute);
556 IntPtr handle = CFURLCreateWithString (IntPtr.Zero, str.Handle, IntPtr.Zero);
559 if (handle == IntPtr.Zero)
562 return new CFUrl (handle, true);
566 internal class CFRunLoop : CFObject
568 [DllImport (CFObject.CoreFoundationLibrary)]
569 static extern void CFRunLoopAddSource (IntPtr rl, IntPtr source, IntPtr mode);
571 [DllImport (CFObject.CoreFoundationLibrary)]
572 static extern void CFRunLoopRemoveSource (IntPtr rl, IntPtr source, IntPtr mode);
574 [DllImport (CFObject.CoreFoundationLibrary)]
575 static extern int CFRunLoopRunInMode (IntPtr mode, double seconds, bool returnAfterSourceHandled);
577 [DllImport (CFObject.CoreFoundationLibrary)]
578 static extern IntPtr CFRunLoopGetCurrent ();
580 [DllImport (CFObject.CoreFoundationLibrary)]
581 static extern void CFRunLoopStop (IntPtr rl);
583 public CFRunLoop (IntPtr handle, bool own): base (handle, own)
587 public static CFRunLoop CurrentRunLoop {
588 get { return new CFRunLoop (CFRunLoopGetCurrent (), false); }
591 public void AddSource (IntPtr source, CFString mode)
593 CFRunLoopAddSource (Handle, source, mode.Handle);
596 public void RemoveSource (IntPtr source, CFString mode)
598 CFRunLoopRemoveSource (Handle, source, mode.Handle);
601 public int RunInMode (CFString mode, double seconds, bool returnAfterSourceHandled)
603 return CFRunLoopRunInMode (mode.Handle, seconds, returnAfterSourceHandled);
608 CFRunLoopStop (Handle);
612 internal enum CFProxyType {
614 AutoConfigurationUrl,
615 AutoConfigurationJavaScript,
622 internal class CFProxy {
623 //static IntPtr kCFProxyAutoConfigurationHTTPResponseKey;
624 static IntPtr kCFProxyAutoConfigurationJavaScriptKey;
625 static IntPtr kCFProxyAutoConfigurationURLKey;
626 static IntPtr kCFProxyHostNameKey;
627 static IntPtr kCFProxyPasswordKey;
628 static IntPtr kCFProxyPortNumberKey;
629 static IntPtr kCFProxyTypeKey;
630 static IntPtr kCFProxyUsernameKey;
632 //static IntPtr kCFProxyTypeNone;
633 static IntPtr kCFProxyTypeAutoConfigurationURL;
634 static IntPtr kCFProxyTypeAutoConfigurationJavaScript;
635 static IntPtr kCFProxyTypeFTP;
636 static IntPtr kCFProxyTypeHTTP;
637 static IntPtr kCFProxyTypeHTTPS;
638 static IntPtr kCFProxyTypeSOCKS;
642 IntPtr handle = CFObject.dlopen (CFNetwork.CFNetworkLibrary, 0);
644 //kCFProxyAutoConfigurationHTTPResponseKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationHTTPResponseKey");
645 kCFProxyAutoConfigurationJavaScriptKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationJavaScriptKey");
646 kCFProxyAutoConfigurationURLKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationURLKey");
647 kCFProxyHostNameKey = CFObject.GetCFObjectHandle (handle, "kCFProxyHostNameKey");
648 kCFProxyPasswordKey = CFObject.GetCFObjectHandle (handle, "kCFProxyPasswordKey");
649 kCFProxyPortNumberKey = CFObject.GetCFObjectHandle (handle, "kCFProxyPortNumberKey");
650 kCFProxyTypeKey = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeKey");
651 kCFProxyUsernameKey = CFObject.GetCFObjectHandle (handle, "kCFProxyUsernameKey");
653 //kCFProxyTypeNone = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeNone");
654 kCFProxyTypeAutoConfigurationURL = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeAutoConfigurationURL");
655 kCFProxyTypeAutoConfigurationJavaScript = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeAutoConfigurationJavaScript");
656 kCFProxyTypeFTP = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeFTP");
657 kCFProxyTypeHTTP = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeHTTP");
658 kCFProxyTypeHTTPS = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeHTTPS");
659 kCFProxyTypeSOCKS = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeSOCKS");
661 CFObject.dlclose (handle);
664 CFDictionary settings;
666 internal CFProxy (CFDictionary settings)
668 this.settings = settings;
671 static CFProxyType CFProxyTypeToEnum (IntPtr type)
673 if (type == kCFProxyTypeAutoConfigurationJavaScript)
674 return CFProxyType.AutoConfigurationJavaScript;
676 if (type == kCFProxyTypeAutoConfigurationURL)
677 return CFProxyType.AutoConfigurationUrl;
679 if (type == kCFProxyTypeFTP)
680 return CFProxyType.FTP;
682 if (type == kCFProxyTypeHTTP)
683 return CFProxyType.HTTP;
685 if (type == kCFProxyTypeHTTPS)
686 return CFProxyType.HTTPS;
688 if (type == kCFProxyTypeSOCKS)
689 return CFProxyType.SOCKS;
691 return CFProxyType.None;
695 // AFAICT these get used with CFNetworkExecuteProxyAutoConfiguration*()
697 // TODO: bind CFHTTPMessage so we can return the proper type here.
698 public IntPtr AutoConfigurationHTTPResponse {
699 get { return settings[kCFProxyAutoConfigurationHTTPResponseKey]; }
703 public IntPtr AutoConfigurationJavaScript {
705 if (kCFProxyAutoConfigurationJavaScriptKey == IntPtr.Zero)
708 return settings[kCFProxyAutoConfigurationJavaScriptKey];
712 public IntPtr AutoConfigurationUrl {
714 if (kCFProxyAutoConfigurationURLKey == IntPtr.Zero)
717 return settings[kCFProxyAutoConfigurationURLKey];
721 public string HostName {
723 if (kCFProxyHostNameKey == IntPtr.Zero)
726 return CFString.AsString (settings[kCFProxyHostNameKey]);
730 public string Password {
732 if (kCFProxyPasswordKey == IntPtr.Zero)
735 return CFString.AsString (settings[kCFProxyPasswordKey]);
741 if (kCFProxyPortNumberKey == IntPtr.Zero)
744 return CFNumber.AsInt32 (settings[kCFProxyPortNumberKey]);
748 public CFProxyType ProxyType {
750 if (kCFProxyTypeKey == IntPtr.Zero)
751 return CFProxyType.None;
753 return CFProxyTypeToEnum (settings[kCFProxyTypeKey]);
757 public string Username {
759 if (kCFProxyUsernameKey == IntPtr.Zero)
762 return CFString.AsString (settings[kCFProxyUsernameKey]);
767 internal class CFProxySettings {
768 static IntPtr kCFNetworkProxiesHTTPEnable;
769 static IntPtr kCFNetworkProxiesHTTPPort;
770 static IntPtr kCFNetworkProxiesHTTPProxy;
771 static IntPtr kCFNetworkProxiesProxyAutoConfigEnable;
772 static IntPtr kCFNetworkProxiesProxyAutoConfigJavaScript;
773 static IntPtr kCFNetworkProxiesProxyAutoConfigURLString;
775 static CFProxySettings ()
777 IntPtr handle = CFObject.dlopen (CFNetwork.CFNetworkLibrary, 0);
779 kCFNetworkProxiesHTTPEnable = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPEnable");
780 kCFNetworkProxiesHTTPPort = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPPort");
781 kCFNetworkProxiesHTTPProxy = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPProxy");
782 kCFNetworkProxiesProxyAutoConfigEnable = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigEnable");
783 kCFNetworkProxiesProxyAutoConfigJavaScript = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigJavaScript");
784 kCFNetworkProxiesProxyAutoConfigURLString = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigURLString");
786 CFObject.dlclose (handle);
789 CFDictionary settings;
791 public CFProxySettings (CFDictionary settings)
793 this.settings = settings;
796 public CFDictionary Dictionary {
797 get { return settings; }
800 public bool HTTPEnable {
802 if (kCFNetworkProxiesHTTPEnable == IntPtr.Zero)
805 return CFNumber.AsBool (settings[kCFNetworkProxiesHTTPEnable]);
809 public int HTTPPort {
811 if (kCFNetworkProxiesHTTPPort == IntPtr.Zero)
814 return CFNumber.AsInt32 (settings[kCFNetworkProxiesHTTPPort]);
818 public string HTTPProxy {
820 if (kCFNetworkProxiesHTTPProxy == IntPtr.Zero)
823 return CFString.AsString (settings[kCFNetworkProxiesHTTPProxy]);
827 public bool ProxyAutoConfigEnable {
829 if (kCFNetworkProxiesProxyAutoConfigEnable == IntPtr.Zero)
832 return CFNumber.AsBool (settings[kCFNetworkProxiesProxyAutoConfigEnable]);
836 public string ProxyAutoConfigJavaScript {
838 if (kCFNetworkProxiesProxyAutoConfigJavaScript == IntPtr.Zero)
841 return CFString.AsString (settings[kCFNetworkProxiesProxyAutoConfigJavaScript]);
845 public string ProxyAutoConfigURLString {
847 if (kCFNetworkProxiesProxyAutoConfigURLString == IntPtr.Zero)
850 return CFString.AsString (settings[kCFNetworkProxiesProxyAutoConfigURLString]);
855 internal static class CFNetwork {
857 public const string CFNetworkLibrary = "/System/Library/Frameworks/CoreServices.framework/Frameworks/CFNetwork.framework/CFNetwork";
859 public const string CFNetworkLibrary = "/System/Library/Frameworks/CFNetwork.framework/CFNetwork";
862 [DllImport (CFNetworkLibrary, EntryPoint = "CFNetworkCopyProxiesForAutoConfigurationScript")]
863 // CFArrayRef CFNetworkCopyProxiesForAutoConfigurationScript (CFStringRef proxyAutoConfigurationScript, CFURLRef targetURL, CFErrorRef* error);
864 extern static IntPtr CFNetworkCopyProxiesForAutoConfigurationScriptSequential (IntPtr proxyAutoConfigurationScript, IntPtr targetURL, out IntPtr error);
866 [DllImport (CFNetworkLibrary)]
867 extern static IntPtr CFNetworkExecuteProxyAutoConfigurationURL (IntPtr proxyAutoConfigURL, IntPtr targetURL, CFProxyAutoConfigurationResultCallback cb, ref CFStreamClientContext clientContext);
870 class GetProxyData : IDisposable {
871 public IntPtr script;
872 public IntPtr targetUri;
874 public IntPtr result;
875 public ManualResetEvent evt = new ManualResetEvent (false);
877 public void Dispose ()
883 static object lock_obj = new object ();
884 static Queue<GetProxyData> get_proxy_queue;
885 static AutoResetEvent proxy_event;
887 static void CFNetworkCopyProxiesForAutoConfigurationScriptThread ()
890 var data_left = true;
893 proxy_event.WaitOne ();
897 if (get_proxy_queue.Count == 0)
899 data = get_proxy_queue.Dequeue ();
900 data_left = get_proxy_queue.Count > 0;
903 data.result = CFNetworkCopyProxiesForAutoConfigurationScriptSequential (data.script, data.targetUri, out data.error);
909 static IntPtr CFNetworkCopyProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, IntPtr targetURL, out IntPtr error)
911 // This method must only be called on only one thread during an application's life time.
912 // Note that it's not enough to use a lock to make calls sequential across different threads,
913 // it has to be one thread. Also note that that thread can't be the main thread, because the
914 // main thread might be blocking waiting for this network request to finish.
915 // Another possibility would be to use JavaScriptCore to execute this piece of
916 // javascript ourselves, but unfortunately it's not available before iOS7.
917 // See bug #7923 comment #21+.
919 using (var data = new GetProxyData ()) {
920 data.script = proxyAutoConfigurationScript;
921 data.targetUri = targetURL;
924 if (get_proxy_queue == null) {
925 get_proxy_queue = new Queue<GetProxyData> ();
926 proxy_event = new AutoResetEvent (false);
927 new Thread (CFNetworkCopyProxiesForAutoConfigurationScriptThread) {
931 get_proxy_queue.Enqueue (data);
943 static CFArray CopyProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, CFUrl targetURL)
945 IntPtr err = IntPtr.Zero;
946 IntPtr native = CFNetworkCopyProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL.Handle, out err);
948 if (native == IntPtr.Zero)
951 return new CFArray (native, true);
954 public static CFProxy[] GetProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, CFUrl targetURL)
956 if (proxyAutoConfigurationScript == IntPtr.Zero)
957 throw new ArgumentNullException ("proxyAutoConfigurationScript");
959 if (targetURL == null)
960 throw new ArgumentNullException ("targetURL");
962 CFArray array = CopyProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL);
967 CFProxy[] proxies = new CFProxy [array.Count];
968 for (int i = 0; i < proxies.Length; i++) {
969 CFDictionary dict = new CFDictionary (array[i], false);
970 proxies[i] = new CFProxy (dict);
978 public static CFProxy[] GetProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, Uri targetUri)
980 if (proxyAutoConfigurationScript == IntPtr.Zero)
981 throw new ArgumentNullException ("proxyAutoConfigurationScript");
983 if (targetUri == null)
984 throw new ArgumentNullException ("targetUri");
986 CFUrl targetURL = CFUrl.Create (targetUri.AbsoluteUri);
987 CFProxy[] proxies = GetProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL);
988 targetURL.Dispose ();
993 delegate void CFProxyAutoConfigurationResultCallback (IntPtr client, IntPtr proxyList, IntPtr error);
995 public static CFProxy[] ExecuteProxyAutoConfigurationURL (IntPtr proxyAutoConfigURL, Uri targetURL)
997 CFUrl url = CFUrl.Create (targetURL.AbsoluteUri);
1001 CFProxy[] proxies = null;
1003 var runLoop = CFRunLoop.CurrentRunLoop;
1005 // Callback that will be called after executing the configuration script
1006 CFProxyAutoConfigurationResultCallback cb = delegate (IntPtr client, IntPtr proxyList, IntPtr error) {
1007 if (proxyList != IntPtr.Zero) {
1008 var array = new CFArray (proxyList, false);
1009 proxies = new CFProxy [array.Count];
1010 for (int i = 0; i < proxies.Length; i++) {
1011 CFDictionary dict = new CFDictionary (array[i], false);
1012 proxies[i] = new CFProxy (dict);
1019 var clientContext = new CFStreamClientContext ();
1020 var loopSource = CFNetworkExecuteProxyAutoConfigurationURL (proxyAutoConfigURL, url.Handle, cb, ref clientContext);
1022 // Create a private mode
1023 var mode = CFString.Create ("Mono.MacProxy");
1025 runLoop.AddSource (loopSource, mode);
1026 runLoop.RunInMode (mode, double.MaxValue, false);
1027 runLoop.RemoveSource (loopSource, mode);
1032 [DllImport (CFNetworkLibrary)]
1033 // CFArrayRef CFNetworkCopyProxiesForURL (CFURLRef url, CFDictionaryRef proxySettings);
1034 extern static IntPtr CFNetworkCopyProxiesForURL (IntPtr url, IntPtr proxySettings);
1036 static CFArray CopyProxiesForURL (CFUrl url, CFDictionary proxySettings)
1038 IntPtr native = CFNetworkCopyProxiesForURL (url.Handle, proxySettings != null ? proxySettings.Handle : IntPtr.Zero);
1040 if (native == IntPtr.Zero)
1043 return new CFArray (native, true);
1046 public static CFProxy[] GetProxiesForURL (CFUrl url, CFProxySettings proxySettings)
1048 if (url == null || url.Handle == IntPtr.Zero)
1049 throw new ArgumentNullException ("url");
1051 if (proxySettings == null)
1052 proxySettings = GetSystemProxySettings ();
1054 CFArray array = CopyProxiesForURL (url, proxySettings.Dictionary);
1059 CFProxy[] proxies = new CFProxy [array.Count];
1060 for (int i = 0; i < proxies.Length; i++) {
1061 CFDictionary dict = new CFDictionary (array[i], false);
1062 proxies[i] = new CFProxy (dict);
1070 public static CFProxy[] GetProxiesForUri (Uri uri, CFProxySettings proxySettings)
1073 throw new ArgumentNullException ("uri");
1075 CFUrl url = CFUrl.Create (uri.AbsoluteUri);
1079 CFProxy[] proxies = GetProxiesForURL (url, proxySettings);
1085 [DllImport (CFNetworkLibrary)]
1086 // CFDictionaryRef CFNetworkCopySystemProxySettings (void);
1087 extern static IntPtr CFNetworkCopySystemProxySettings ();
1089 public static CFProxySettings GetSystemProxySettings ()
1091 IntPtr native = CFNetworkCopySystemProxySettings ();
1093 if (native == IntPtr.Zero)
1096 var dict = new CFDictionary (native, true);
1098 return new CFProxySettings (dict);
1101 class CFWebProxy : IWebProxy {
1102 ICredentials credentials;
1105 public CFWebProxy ()
1109 public ICredentials Credentials {
1110 get { return credentials; }
1112 userSpecified = true;
1113 credentials = value;
1117 static Uri GetProxyUri (CFProxy proxy, out NetworkCredential credentials)
1121 switch (proxy.ProxyType) {
1122 case CFProxyType.FTP:
1123 protocol = "ftp://";
1125 case CFProxyType.HTTP:
1126 case CFProxyType.HTTPS:
1127 protocol = "http://";
1134 string username = proxy.Username;
1135 string password = proxy.Password;
1136 string hostname = proxy.HostName;
1137 int port = proxy.Port;
1140 if (username != null)
1141 credentials = new NetworkCredential (username, password);
1145 uri = protocol + hostname + (port != 0 ? ':' + port.ToString () : string.Empty);
1147 return new Uri (uri, UriKind.Absolute);
1150 static Uri GetProxyUriFromScript (IntPtr script, Uri targetUri, out NetworkCredential credentials)
1152 CFProxy[] proxies = CFNetwork.GetProxiesForAutoConfigurationScript (script, targetUri);
1153 return SelectProxy (proxies, targetUri, out credentials);
1156 static Uri ExecuteProxyAutoConfigurationURL (IntPtr proxyAutoConfigURL, Uri targetUri, out NetworkCredential credentials)
1158 CFProxy[] proxies = CFNetwork.ExecuteProxyAutoConfigurationURL (proxyAutoConfigURL, targetUri);
1159 return SelectProxy (proxies, targetUri, out credentials);
1163 static Uri SelectProxy (CFProxy[] proxies, Uri targetUri, out NetworkCredential credentials)
1165 if (proxies == null) {
1170 for (int i = 0; i < proxies.Length; i++) {
1171 switch (proxies[i].ProxyType) {
1172 case CFProxyType.HTTPS:
1173 case CFProxyType.HTTP:
1174 case CFProxyType.FTP:
1175 // create a Uri based on the hostname/port/etc info
1176 return GetProxyUri (proxies[i], out credentials);
1177 case CFProxyType.SOCKS:
1179 // unsupported proxy type, try the next one
1181 case CFProxyType.None:
1182 // no proxy should be used
1193 public Uri GetProxy (Uri targetUri)
1195 NetworkCredential credentials = null;
1198 if (targetUri == null)
1199 throw new ArgumentNullException ("targetUri");
1202 CFProxySettings settings = CFNetwork.GetSystemProxySettings ();
1203 CFProxy[] proxies = CFNetwork.GetProxiesForUri (targetUri, settings);
1205 if (proxies != null) {
1206 for (int i = 0; i < proxies.Length && proxy == null; i++) {
1207 switch (proxies[i].ProxyType) {
1208 case CFProxyType.AutoConfigurationJavaScript:
1209 proxy = GetProxyUriFromScript (proxies[i].AutoConfigurationJavaScript, targetUri, out credentials);
1211 case CFProxyType.AutoConfigurationUrl:
1212 proxy = ExecuteProxyAutoConfigurationURL (proxies[i].AutoConfigurationUrl, targetUri, out credentials);
1214 case CFProxyType.HTTPS:
1215 case CFProxyType.HTTP:
1216 case CFProxyType.FTP:
1217 // create a Uri based on the hostname/port/etc info
1218 proxy = GetProxyUri (proxies[i], out credentials);
1220 case CFProxyType.SOCKS:
1221 // unsupported proxy type, try the next one
1223 case CFProxyType.None:
1224 // no proxy should be used
1230 if (proxy == null) {
1231 // no supported proxies for this Uri, fall back to trying to connect to targetUri directly
1238 // ignore errors while retrieving proxy data
1243 this.credentials = credentials;
1248 public bool IsBypassed (Uri targetUri)
1250 if (targetUri == null)
1251 throw new ArgumentNullException ("targetUri");
1253 return GetProxy (targetUri) == targetUri;
1257 public static IWebProxy GetDefaultProxy ()
1259 return new CFWebProxy ();
1263 class CFBoolean : INativeObject, IDisposable {
1266 public static readonly CFBoolean True;
1267 public static readonly CFBoolean False;
1271 var handle = CFObject.dlopen (CFObject.CoreFoundationLibrary, 0);
1272 if (handle == IntPtr.Zero)
1275 True = new CFBoolean (CFObject.GetCFObjectHandle (handle, "kCFBooleanTrue"), false);
1276 False = new CFBoolean (CFObject.GetCFObjectHandle (handle, "kCFBooleanFalse"), false);
1279 CFObject.dlclose (handle);
1283 internal CFBoolean (IntPtr handle, bool owns)
1285 this.handle = handle;
1287 CFObject.CFRetain (handle);
1295 public IntPtr Handle {
1301 public void Dispose ()
1304 GC.SuppressFinalize (this);
1307 protected virtual void Dispose (bool disposing)
1309 if (handle != IntPtr.Zero){
1310 CFObject.CFRelease (handle);
1311 handle = IntPtr.Zero;
1315 public static implicit operator bool (CFBoolean value)
1320 public static explicit operator CFBoolean (bool value)
1322 return FromBoolean (value);
1325 public static CFBoolean FromBoolean (bool value)
1327 return value ? True : False;
1330 [DllImport (CFObject.CoreFoundationLibrary)]
1331 [return: MarshalAs (UnmanagedType.I1)]
1332 extern static /* Boolean */ bool CFBooleanGetValue (/* CFBooleanRef */ IntPtr boolean);
1335 get {return CFBooleanGetValue (handle);}
1338 public static bool GetValue (IntPtr boolean)
1340 return CFBooleanGetValue (boolean);