Merge pull request #1326 from BrzVlad/master
[mono.git] / mcs / class / System / System.Net / MacProxy.cs
1 // 
2 // MacProxy.cs
3 //  
4 // Author: Jeffrey Stedfast <jeff@xamarin.com>
5 // 
6 // Copyright (c) 2012-2014 Xamarin Inc.
7 // 
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:
14 // 
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
17 // 
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
24 // THE SOFTWARE.
25 // 
26
27 using System;
28 using System.Collections.Generic;
29 using System.Runtime.InteropServices;
30 using System.Threading;
31
32 namespace System.Net
33 {
34         internal class CFObject : IDisposable
35         {
36                 public const string CoreFoundationLibrary = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation";
37                 const string SystemLibrary = "/usr/lib/libSystem.dylib";
38
39                 [DllImport (SystemLibrary)]
40                 public static extern IntPtr dlopen (string path, int mode);
41
42                 [DllImport (SystemLibrary)]
43                 public static extern IntPtr dlsym (IntPtr handle, string symbol);
44
45                 [DllImport (SystemLibrary)]
46                 public static extern void dlclose (IntPtr handle);
47
48                 public static IntPtr GetIndirect (IntPtr handle, string symbol)
49                 {
50                         return dlsym (handle, symbol);
51                 }
52
53                 public static IntPtr GetCFObjectHandle (IntPtr handle, string symbol)
54                 {
55                         var indirect = dlsym (handle, symbol);
56                         if (indirect == IntPtr.Zero)
57                                 return IntPtr.Zero;
58
59                         return Marshal.ReadIntPtr (indirect);
60                 }
61
62                 public CFObject (IntPtr handle, bool own)
63                 {
64                         Handle = handle;
65
66                         if (!own)
67                                 Retain ();
68                 }
69
70                 ~CFObject ()
71                 {
72                         Dispose (false);
73                 }
74
75                 public IntPtr Handle { get; private set; }
76
77                 [DllImport (CoreFoundationLibrary)]
78                 extern static IntPtr CFRetain (IntPtr handle);
79
80                 void Retain ()
81                 {
82                         CFRetain (Handle);
83                 }
84
85                 [DllImport (CoreFoundationLibrary)]
86                 extern static void CFRelease (IntPtr handle);
87
88                 void Release ()
89                 {
90                         CFRelease (Handle);
91                 }
92
93                 protected virtual void Dispose (bool disposing)
94                 {
95                         if (Handle != IntPtr.Zero) {
96                                 Release ();
97                                 Handle = IntPtr.Zero;
98                         }
99                 }
100
101                 public void Dispose ()
102                 {
103                         Dispose (true);
104                         GC.SuppressFinalize (this);
105                 }
106         }
107
108         internal class CFArray : CFObject
109         {
110                 public CFArray (IntPtr handle, bool own) : base (handle, own) { }
111
112                 [DllImport (CoreFoundationLibrary)]
113                 extern static IntPtr CFArrayCreate (IntPtr allocator, IntPtr values, /* CFIndex */ IntPtr numValues, IntPtr callbacks);
114                 static readonly IntPtr kCFTypeArrayCallbacks;
115
116                 static CFArray ()
117                 {
118                         var handle = dlopen (CoreFoundationLibrary, 0);
119                         if (handle == IntPtr.Zero)
120                                 return;
121
122                         try {
123                                 kCFTypeArrayCallbacks = GetIndirect (handle, "kCFTypeArrayCallBacks");
124                         } finally {
125                                 dlclose (handle);
126                         }
127                 }
128
129                 static unsafe CFArray Create (params IntPtr[] values)
130                 {
131                         if (values == null)
132                                 throw new ArgumentNullException ("values");
133
134                         fixed (IntPtr *pv = values) {
135                                 IntPtr handle = CFArrayCreate (IntPtr.Zero, (IntPtr) pv, (IntPtr) values.Length, kCFTypeArrayCallbacks);
136
137                                 return new CFArray (handle, false);
138                         }
139                 }
140
141                 public static CFArray Create (params CFObject[] values)
142                 {
143                         if (values == null)
144                                 throw new ArgumentNullException ("values");
145
146                         IntPtr[] _values = new IntPtr [values.Length];
147                         for (int i = 0; i < _values.Length; i++)
148                                 _values[i] = values[i].Handle;
149
150                         return Create (_values);
151                 }
152
153                 [DllImport (CoreFoundationLibrary)]
154                 extern static /* CFIndex */ IntPtr CFArrayGetCount (IntPtr handle);
155
156                 public int Count {
157                         get { return (int) CFArrayGetCount (Handle); }
158                 }
159
160                 [DllImport (CoreFoundationLibrary)]
161                 extern static IntPtr CFArrayGetValueAtIndex (IntPtr handle, /* CFIndex */ IntPtr index);
162
163                 public IntPtr this[int index] {
164                         get {
165                                 return CFArrayGetValueAtIndex (Handle, (IntPtr) index);
166                         }
167                 }
168         }
169
170         internal class CFNumber : CFObject
171         {
172                 public CFNumber (IntPtr handle, bool own) : base (handle, own) { }
173
174                 [DllImport (CoreFoundationLibrary)]
175                 [return: MarshalAs (UnmanagedType.I1)]
176                 extern static bool CFNumberGetValue (IntPtr handle, /* CFNumberType */ IntPtr type, [MarshalAs (UnmanagedType.I1)] out bool value);
177
178                 public static bool AsBool (IntPtr handle)
179                 {
180                         bool value;
181
182                         if (handle == IntPtr.Zero)
183                                 return false;
184
185                         CFNumberGetValue (handle, (IntPtr) 1, out value);
186
187                         return value;
188                 }
189
190                 public static implicit operator bool (CFNumber number)
191                 {
192                         return AsBool (number.Handle);
193                 }
194
195                 [DllImport (CoreFoundationLibrary)]
196                 [return: MarshalAs (UnmanagedType.I1)]
197                 extern static bool CFNumberGetValue (IntPtr handle, /* CFNumberType */ IntPtr type, out int value);
198
199                 public static int AsInt32 (IntPtr handle)
200                 {
201                         int value;
202
203                         if (handle == IntPtr.Zero)
204                                 return 0;
205
206                         // 9 == kCFNumberIntType == C int
207                         CFNumberGetValue (handle, (IntPtr) 9, out value);
208
209                         return value;
210                 }
211
212                 public static implicit operator int (CFNumber number)
213                 {
214                         return AsInt32 (number.Handle);
215                 }
216         }
217
218         internal struct CFRange {
219                 public IntPtr Location, Length;
220                 
221                 public CFRange (int loc, int len)
222                 {
223                         Location = (IntPtr) loc;
224                         Length = (IntPtr) len;
225                 }
226         }
227
228         internal struct CFStreamClientContext {
229                 public IntPtr Version;
230                 public IntPtr Info;
231                 public IntPtr Retain;
232                 public IntPtr Release;
233                 public IntPtr CopyDescription;
234         }
235
236         internal class CFString : CFObject
237         {
238                 string str;
239
240                 public CFString (IntPtr handle, bool own) : base (handle, own) { }
241
242                 [DllImport (CoreFoundationLibrary)]
243                 extern static IntPtr CFStringCreateWithCharacters (IntPtr alloc, IntPtr chars, /* CFIndex */ IntPtr length);
244
245                 public static CFString Create (string value)
246                 {
247                         IntPtr handle;
248
249                         unsafe {
250                                 fixed (char *ptr = value) {
251                                         handle = CFStringCreateWithCharacters (IntPtr.Zero, (IntPtr) ptr, (IntPtr) value.Length);
252                                 }
253                         }
254
255                         if (handle == IntPtr.Zero)
256                                 return null;
257
258                         return new CFString (handle, true);
259                 }
260
261                 [DllImport (CoreFoundationLibrary)]
262                 extern static /* CFIndex */ IntPtr CFStringGetLength (IntPtr handle);
263
264                 public int Length {
265                         get {
266                                 if (str != null)
267                                         return str.Length;
268
269                                 return (int) CFStringGetLength (Handle);
270                         }
271                 }
272
273                 [DllImport (CoreFoundationLibrary)]
274                 extern static IntPtr CFStringGetCharactersPtr (IntPtr handle);
275
276                 [DllImport (CoreFoundationLibrary)]
277                 extern static IntPtr CFStringGetCharacters (IntPtr handle, CFRange range, IntPtr buffer);
278
279                 public static string AsString (IntPtr handle)
280                 {
281                         if (handle == IntPtr.Zero)
282                                 return null;
283                         
284                         int len = (int) CFStringGetLength (handle);
285                         
286                         if (len == 0)
287                                 return string.Empty;
288                         
289                         IntPtr chars = CFStringGetCharactersPtr (handle);
290                         IntPtr buffer = IntPtr.Zero;
291                         
292                         if (chars == IntPtr.Zero) {
293                                 CFRange range = new CFRange (0, len);
294                                 buffer = Marshal.AllocHGlobal (len * 2);
295                                 CFStringGetCharacters (handle, range, buffer);
296                                 chars = buffer;
297                         }
298
299                         string str;
300
301                         unsafe {
302                                 str = new string ((char *) chars, 0, len);
303                         }
304                         
305                         if (buffer != IntPtr.Zero)
306                                 Marshal.FreeHGlobal (buffer);
307
308                         return str;
309                 }
310
311                 public override string ToString ()
312                 {
313                         if (str == null)
314                                 str = AsString (Handle);
315
316                         return str;
317                 }
318
319                 public static implicit operator string (CFString str)
320                 {
321                         return str.ToString ();
322                 }
323
324                 public static implicit operator CFString (string str)
325                 {
326                         return Create (str);
327                 }
328         }
329
330         internal class CFDictionary : CFObject
331         {
332                 public CFDictionary (IntPtr handle, bool own) : base (handle, own) { }
333
334                 [DllImport (CoreFoundationLibrary)]
335                 extern static IntPtr CFDictionaryGetValue (IntPtr handle, IntPtr key);
336
337                 public IntPtr GetValue (IntPtr key)
338                 {
339                         return CFDictionaryGetValue (Handle, key);
340                 }
341
342                 public IntPtr this[IntPtr key] {
343                         get {
344                                 return GetValue (key);
345                         }
346                 }
347         }
348
349         internal class CFUrl : CFObject
350         {
351                 public CFUrl (IntPtr handle, bool own) : base (handle, own) { }
352
353                 [DllImport (CoreFoundationLibrary)]
354                 extern static IntPtr CFURLCreateWithString (IntPtr allocator, IntPtr str, IntPtr baseURL);
355
356                 public static CFUrl Create (string absolute)
357                 {
358                         if (string.IsNullOrEmpty (absolute))
359                                 return null;
360
361                         CFString str = CFString.Create (absolute);
362                         IntPtr handle = CFURLCreateWithString (IntPtr.Zero, str.Handle, IntPtr.Zero);
363                         str.Dispose ();
364
365                         if (handle == IntPtr.Zero)
366                                 return null;
367
368                         return new CFUrl (handle, true);
369                 }
370         }
371
372         internal class CFRunLoop : CFObject
373         {
374                 [DllImport (CFObject.CoreFoundationLibrary)]
375                 static extern void CFRunLoopAddSource (IntPtr rl, IntPtr source, IntPtr mode);
376
377                 [DllImport (CFObject.CoreFoundationLibrary)]
378                 static extern void CFRunLoopRemoveSource (IntPtr rl, IntPtr source, IntPtr mode);
379
380                 [DllImport (CFObject.CoreFoundationLibrary)]
381                 static extern int CFRunLoopRunInMode (IntPtr mode, double seconds, bool returnAfterSourceHandled);
382
383                 [DllImport (CFObject.CoreFoundationLibrary)]
384                 static extern IntPtr CFRunLoopGetCurrent ();
385
386                 [DllImport (CFObject.CoreFoundationLibrary)]
387                 static extern void CFRunLoopStop (IntPtr rl);
388
389                 public CFRunLoop (IntPtr handle, bool own): base (handle, own)
390                 {
391                 }
392
393                 public static CFRunLoop CurrentRunLoop {
394                         get { return new CFRunLoop (CFRunLoopGetCurrent (), false); }
395                 }
396
397                 public void AddSource (IntPtr source, CFString mode)
398                 {
399                         CFRunLoopAddSource (Handle, source, mode.Handle);
400                 }
401
402                 public void RemoveSource (IntPtr source, CFString mode)
403                 {
404                         CFRunLoopRemoveSource (Handle, source, mode.Handle);
405                 }
406
407                 public int RunInMode (CFString mode, double seconds, bool returnAfterSourceHandled)
408                 {
409                         return CFRunLoopRunInMode (mode.Handle, seconds, returnAfterSourceHandled);
410                 }
411
412                 public void Stop ()
413                 {
414                         CFRunLoopStop (Handle);
415                 }
416         }
417
418         internal enum CFProxyType {
419                 None,
420                 AutoConfigurationUrl,
421                 AutoConfigurationJavaScript,
422                 FTP,
423                 HTTP,
424                 HTTPS,
425                 SOCKS
426         }
427         
428         internal class CFProxy {
429                 //static IntPtr kCFProxyAutoConfigurationHTTPResponseKey;
430                 static IntPtr kCFProxyAutoConfigurationJavaScriptKey;
431                 static IntPtr kCFProxyAutoConfigurationURLKey;
432                 static IntPtr kCFProxyHostNameKey;
433                 static IntPtr kCFProxyPasswordKey;
434                 static IntPtr kCFProxyPortNumberKey;
435                 static IntPtr kCFProxyTypeKey;
436                 static IntPtr kCFProxyUsernameKey;
437
438                 //static IntPtr kCFProxyTypeNone;
439                 static IntPtr kCFProxyTypeAutoConfigurationURL;
440                 static IntPtr kCFProxyTypeAutoConfigurationJavaScript;
441                 static IntPtr kCFProxyTypeFTP;
442                 static IntPtr kCFProxyTypeHTTP;
443                 static IntPtr kCFProxyTypeHTTPS;
444                 static IntPtr kCFProxyTypeSOCKS;
445
446                 static CFProxy ()
447                 {
448                         IntPtr handle = CFObject.dlopen (CFNetwork.CFNetworkLibrary, 0);
449
450                         //kCFProxyAutoConfigurationHTTPResponseKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationHTTPResponseKey");
451                         kCFProxyAutoConfigurationJavaScriptKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationJavaScriptKey");
452                         kCFProxyAutoConfigurationURLKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationURLKey");
453                         kCFProxyHostNameKey = CFObject.GetCFObjectHandle (handle, "kCFProxyHostNameKey");
454                         kCFProxyPasswordKey = CFObject.GetCFObjectHandle (handle, "kCFProxyPasswordKey");
455                         kCFProxyPortNumberKey = CFObject.GetCFObjectHandle (handle, "kCFProxyPortNumberKey");
456                         kCFProxyTypeKey = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeKey");
457                         kCFProxyUsernameKey = CFObject.GetCFObjectHandle (handle, "kCFProxyUsernameKey");
458
459                         //kCFProxyTypeNone = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeNone");
460                         kCFProxyTypeAutoConfigurationURL = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeAutoConfigurationURL");
461                         kCFProxyTypeAutoConfigurationJavaScript = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeAutoConfigurationJavaScript");
462                         kCFProxyTypeFTP = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeFTP");
463                         kCFProxyTypeHTTP = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeHTTP");
464                         kCFProxyTypeHTTPS = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeHTTPS");
465                         kCFProxyTypeSOCKS = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeSOCKS");
466
467                         CFObject.dlclose (handle);
468                 }
469
470                 CFDictionary settings;
471                 
472                 internal CFProxy (CFDictionary settings)
473                 {
474                         this.settings = settings;
475                 }
476                 
477                 static CFProxyType CFProxyTypeToEnum (IntPtr type)
478                 {
479                         if (type == kCFProxyTypeAutoConfigurationJavaScript)
480                                 return CFProxyType.AutoConfigurationJavaScript;
481
482                         if (type == kCFProxyTypeAutoConfigurationURL)
483                                 return CFProxyType.AutoConfigurationUrl;
484
485                         if (type == kCFProxyTypeFTP)
486                                 return CFProxyType.FTP;
487
488                         if (type == kCFProxyTypeHTTP)
489                                 return CFProxyType.HTTP;
490
491                         if (type == kCFProxyTypeHTTPS)
492                                 return CFProxyType.HTTPS;
493
494                         if (type == kCFProxyTypeSOCKS)
495                                 return CFProxyType.SOCKS;
496                         
497                         return CFProxyType.None;
498                 }
499                 
500 #if false
501                 // AFAICT these get used with CFNetworkExecuteProxyAutoConfiguration*()
502                 
503                 // TODO: bind CFHTTPMessage so we can return the proper type here.
504                 public IntPtr AutoConfigurationHTTPResponse {
505                         get { return settings[kCFProxyAutoConfigurationHTTPResponseKey]; }
506                 }
507 #endif
508
509                 public IntPtr AutoConfigurationJavaScript {
510                         get {
511                                 if (kCFProxyAutoConfigurationJavaScriptKey == IntPtr.Zero)
512                                         return IntPtr.Zero;
513                                 
514                                 return settings[kCFProxyAutoConfigurationJavaScriptKey];
515                         }
516                 }
517                 
518                 public IntPtr AutoConfigurationUrl {
519                         get {
520                                 if (kCFProxyAutoConfigurationURLKey == IntPtr.Zero)
521                                         return IntPtr.Zero;
522                                 
523                                 return settings[kCFProxyAutoConfigurationURLKey];
524                         }
525                 }
526                 
527                 public string HostName {
528                         get {
529                                 if (kCFProxyHostNameKey == IntPtr.Zero)
530                                         return null;
531                                 
532                                 return CFString.AsString (settings[kCFProxyHostNameKey]);
533                         }
534                 }
535                 
536                 public string Password {
537                         get {
538                                 if (kCFProxyPasswordKey == IntPtr.Zero)
539                                         return null;
540
541                                 return CFString.AsString (settings[kCFProxyPasswordKey]);
542                         }
543                 }
544                 
545                 public int Port {
546                         get {
547                                 if (kCFProxyPortNumberKey == IntPtr.Zero)
548                                         return 0;
549                                 
550                                 return CFNumber.AsInt32 (settings[kCFProxyPortNumberKey]);
551                         }
552                 }
553                 
554                 public CFProxyType ProxyType {
555                         get {
556                                 if (kCFProxyTypeKey == IntPtr.Zero)
557                                         return CFProxyType.None;
558                                 
559                                 return CFProxyTypeToEnum (settings[kCFProxyTypeKey]);
560                         }
561                 }
562                 
563                 public string Username {
564                         get {
565                                 if (kCFProxyUsernameKey == IntPtr.Zero)
566                                         return null;
567
568                                 return CFString.AsString (settings[kCFProxyUsernameKey]);
569                         }
570                 }
571         }
572         
573         internal class CFProxySettings {
574                 static IntPtr kCFNetworkProxiesHTTPEnable;
575                 static IntPtr kCFNetworkProxiesHTTPPort;
576                 static IntPtr kCFNetworkProxiesHTTPProxy;
577                 static IntPtr kCFNetworkProxiesProxyAutoConfigEnable;
578                 static IntPtr kCFNetworkProxiesProxyAutoConfigJavaScript;
579                 static IntPtr kCFNetworkProxiesProxyAutoConfigURLString;
580
581                 static CFProxySettings ()
582                 {
583                         IntPtr handle = CFObject.dlopen (CFNetwork.CFNetworkLibrary, 0);
584
585                         kCFNetworkProxiesHTTPEnable = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPEnable");
586                         kCFNetworkProxiesHTTPPort = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPPort");
587                         kCFNetworkProxiesHTTPProxy = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPProxy");
588                         kCFNetworkProxiesProxyAutoConfigEnable = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigEnable");
589                         kCFNetworkProxiesProxyAutoConfigJavaScript = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigJavaScript");
590                         kCFNetworkProxiesProxyAutoConfigURLString = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigURLString");
591
592                         CFObject.dlclose (handle);
593                 }
594
595                 CFDictionary settings;
596                 
597                 public CFProxySettings (CFDictionary settings)
598                 {
599                         this.settings = settings;
600                 }
601                 
602                 public CFDictionary Dictionary {
603                         get { return settings; }
604                 }
605                 
606                 public bool HTTPEnable {
607                         get {
608                                 if (kCFNetworkProxiesHTTPEnable == IntPtr.Zero)
609                                         return false;
610
611                                 return CFNumber.AsBool (settings[kCFNetworkProxiesHTTPEnable]);
612                         }
613                 }
614                 
615                 public int HTTPPort {
616                         get {
617                                 if (kCFNetworkProxiesHTTPPort == IntPtr.Zero)
618                                         return 0;
619                                 
620                                 return CFNumber.AsInt32 (settings[kCFNetworkProxiesHTTPPort]);
621                         }
622                 }
623                 
624                 public string HTTPProxy {
625                         get {
626                                 if (kCFNetworkProxiesHTTPProxy == IntPtr.Zero)
627                                         return null;
628                                 
629                                 return CFString.AsString (settings[kCFNetworkProxiesHTTPProxy]);
630                         }
631                 }
632                 
633                 public bool ProxyAutoConfigEnable {
634                         get {
635                                 if (kCFNetworkProxiesProxyAutoConfigEnable == IntPtr.Zero)
636                                         return false;
637                                 
638                                 return CFNumber.AsBool (settings[kCFNetworkProxiesProxyAutoConfigEnable]);
639                         }
640                 }
641                 
642                 public string ProxyAutoConfigJavaScript {
643                         get {
644                                 if (kCFNetworkProxiesProxyAutoConfigJavaScript == IntPtr.Zero)
645                                         return null;
646                                 
647                                 return CFString.AsString (settings[kCFNetworkProxiesProxyAutoConfigJavaScript]);
648                         }
649                 }
650                 
651                 public string ProxyAutoConfigURLString {
652                         get {
653                                 if (kCFNetworkProxiesProxyAutoConfigURLString == IntPtr.Zero)
654                                         return null;
655                                 
656                                 return CFString.AsString (settings[kCFNetworkProxiesProxyAutoConfigURLString]);
657                         }
658                 }
659         }
660         
661         internal static class CFNetwork {
662 #if !MONOTOUCH
663                 public const string CFNetworkLibrary = "/System/Library/Frameworks/CoreServices.framework/Frameworks/CFNetwork.framework/CFNetwork";
664 #else
665                 public const string CFNetworkLibrary = "/System/Library/Frameworks/CFNetwork.framework/CFNetwork";
666 #endif
667                 
668                 [DllImport (CFNetworkLibrary, EntryPoint = "CFNetworkCopyProxiesForAutoConfigurationScript")]
669                 // CFArrayRef CFNetworkCopyProxiesForAutoConfigurationScript (CFStringRef proxyAutoConfigurationScript, CFURLRef targetURL, CFErrorRef* error);
670                 extern static IntPtr CFNetworkCopyProxiesForAutoConfigurationScriptSequential (IntPtr proxyAutoConfigurationScript, IntPtr targetURL, out IntPtr error);
671
672                 [DllImport (CFNetworkLibrary)]
673                 extern static IntPtr CFNetworkExecuteProxyAutoConfigurationURL (IntPtr proxyAutoConfigURL, IntPtr targetURL, CFProxyAutoConfigurationResultCallback cb, ref CFStreamClientContext clientContext);
674
675
676                 class GetProxyData : IDisposable {
677                         public IntPtr script;
678                         public IntPtr targetUri;
679                         public IntPtr error;
680                         public IntPtr result;
681                         public ManualResetEvent evt = new ManualResetEvent (false);
682
683                         public void Dispose ()
684                         {
685                                 evt.Close ();
686                         }
687                 }
688
689                 static object lock_obj = new object ();
690                 static Queue<GetProxyData> get_proxy_queue;
691                 static AutoResetEvent proxy_event;
692
693                 static void CFNetworkCopyProxiesForAutoConfigurationScriptThread ()
694                 {
695                         GetProxyData data;
696                         var data_left = true;
697
698                         while (true) {
699                                 proxy_event.WaitOne ();
700
701                                 do {
702                                         lock (lock_obj) {
703                                                 if (get_proxy_queue.Count == 0)
704                                                         break;
705                                                 data = get_proxy_queue.Dequeue ();
706                                                 data_left = get_proxy_queue.Count > 0;
707                                         }
708
709                                         data.result = CFNetworkCopyProxiesForAutoConfigurationScriptSequential (data.script, data.targetUri, out data.error);
710                                         data.evt.Set ();
711                                 } while (data_left);
712                         }
713                 }
714
715                 static IntPtr CFNetworkCopyProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, IntPtr targetURL, out IntPtr error)
716                 {
717                         // This method must only be called on only one thread during an application's life time.
718                         // Note that it's not enough to use a lock to make calls sequential across different threads,
719                         // it has to be one thread. Also note that that thread can't be the main thread, because the
720                         // main thread might be blocking waiting for this network request to finish.
721                         // Another possibility would be to use JavaScriptCore to execute this piece of
722                         // javascript ourselves, but unfortunately it's not available before iOS7.
723                         // See bug #7923 comment #21+.
724
725                         using (var data = new GetProxyData ()) {
726                                 data.script = proxyAutoConfigurationScript;
727                                 data.targetUri = targetURL;
728
729                                 lock (lock_obj) {
730                                         if (get_proxy_queue == null) {
731                                                 get_proxy_queue = new Queue<GetProxyData> ();
732                                                 proxy_event = new AutoResetEvent (false);
733                                                 new Thread (CFNetworkCopyProxiesForAutoConfigurationScriptThread) {
734                                                         IsBackground = true,
735                                                 }.Start ();
736                                         }
737                                         get_proxy_queue.Enqueue (data);
738                                         proxy_event.Set ();
739                                 }
740
741                                 data.evt.WaitOne ();
742
743                                 error = data.error;
744
745                                 return data.result;
746                         }
747                 }
748
749                 static CFArray CopyProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, CFUrl targetURL)
750                 {
751                         IntPtr err = IntPtr.Zero;
752                         IntPtr native = CFNetworkCopyProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL.Handle, out err);
753                         
754                         if (native == IntPtr.Zero)
755                                 return null;
756                         
757                         return new CFArray (native, true);
758                 }
759                 
760                 public static CFProxy[] GetProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, CFUrl targetURL)
761                 {
762                         if (proxyAutoConfigurationScript == IntPtr.Zero)
763                                 throw new ArgumentNullException ("proxyAutoConfigurationScript");
764                         
765                         if (targetURL == null)
766                                 throw new ArgumentNullException ("targetURL");
767                         
768                         CFArray array = CopyProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL);
769                         
770                         if (array == null)
771                                 return null;
772                         
773                         CFProxy[] proxies = new CFProxy [array.Count];
774                         for (int i = 0; i < proxies.Length; i++) {
775                                 CFDictionary dict = new CFDictionary (array[i], false);
776                                 proxies[i] = new CFProxy (dict);
777                         }
778
779                         array.Dispose ();
780                         
781                         return proxies;
782                 }
783                 
784                 public static CFProxy[] GetProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, Uri targetUri)
785                 {
786                         if (proxyAutoConfigurationScript == IntPtr.Zero)
787                                 throw new ArgumentNullException ("proxyAutoConfigurationScript");
788                         
789                         if (targetUri == null)
790                                 throw new ArgumentNullException ("targetUri");
791                         
792                         CFUrl targetURL = CFUrl.Create (targetUri.AbsoluteUri);
793                         CFProxy[] proxies = GetProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL);
794                         targetURL.Dispose ();
795                         
796                         return proxies;
797                 }
798
799                 delegate void CFProxyAutoConfigurationResultCallback (IntPtr client, IntPtr proxyList, IntPtr error);
800
801                 public static CFProxy[] ExecuteProxyAutoConfigurationURL (IntPtr proxyAutoConfigURL, Uri targetURL)
802                 {
803                         CFUrl url = CFUrl.Create (targetURL.AbsoluteUri);
804                         if (url == null)
805                                 return null;
806
807                         CFProxy[] proxies = null;
808
809                         var runLoop = CFRunLoop.CurrentRunLoop;
810
811                         // Callback that will be called after executing the configuration script
812                         CFProxyAutoConfigurationResultCallback cb = delegate (IntPtr client, IntPtr proxyList, IntPtr error) {
813                                 if (proxyList != IntPtr.Zero) {
814                                         var array = new CFArray (proxyList, false);
815                                         proxies = new CFProxy [array.Count];
816                                         for (int i = 0; i < proxies.Length; i++) {
817                                                 CFDictionary dict = new CFDictionary (array[i], false);
818                                                 proxies[i] = new CFProxy (dict);
819                                         }
820                                         array.Dispose ();
821                                 }
822                                 runLoop.Stop ();
823                         };
824
825                         var clientContext = new CFStreamClientContext ();
826                         var loopSource = CFNetworkExecuteProxyAutoConfigurationURL (proxyAutoConfigURL, url.Handle, cb, ref clientContext);
827
828                         // Create a private mode
829                         var mode = CFString.Create ("Mono.MacProxy");
830
831                         runLoop.AddSource (loopSource, mode);
832                         runLoop.RunInMode (mode, double.MaxValue, false);
833                         runLoop.RemoveSource (loopSource, mode);
834
835                         return proxies;
836                 }
837                 
838                 [DllImport (CFNetworkLibrary)]
839                 // CFArrayRef CFNetworkCopyProxiesForURL (CFURLRef url, CFDictionaryRef proxySettings);
840                 extern static IntPtr CFNetworkCopyProxiesForURL (IntPtr url, IntPtr proxySettings);
841                 
842                 static CFArray CopyProxiesForURL (CFUrl url, CFDictionary proxySettings)
843                 {
844                         IntPtr native = CFNetworkCopyProxiesForURL (url.Handle, proxySettings != null ? proxySettings.Handle : IntPtr.Zero);
845                         
846                         if (native == IntPtr.Zero)
847                                 return null;
848                         
849                         return new CFArray (native, true);
850                 }
851                 
852                 public static CFProxy[] GetProxiesForURL (CFUrl url, CFProxySettings proxySettings)
853                 {
854                         if (url == null || url.Handle == IntPtr.Zero)
855                                 throw new ArgumentNullException ("url");
856                         
857                         if (proxySettings == null)
858                                 proxySettings = GetSystemProxySettings ();
859                         
860                         CFArray array = CopyProxiesForURL (url, proxySettings.Dictionary);
861                         
862                         if (array == null)
863                                 return null;
864
865                         CFProxy[] proxies = new CFProxy [array.Count];
866                         for (int i = 0; i < proxies.Length; i++) {
867                                 CFDictionary dict = new CFDictionary (array[i], false);
868                                 proxies[i] = new CFProxy (dict);
869                         }
870
871                         array.Dispose ();
872                         
873                         return proxies;
874                 }
875                 
876                 public static CFProxy[] GetProxiesForUri (Uri uri, CFProxySettings proxySettings)
877                 {
878                         if (uri == null)
879                                 throw new ArgumentNullException ("uri");
880                         
881                         CFUrl url = CFUrl.Create (uri.AbsoluteUri);
882                         if (url == null)
883                                 return null;
884                         
885                         CFProxy[] proxies = GetProxiesForURL (url, proxySettings);
886                         url.Dispose ();
887                         
888                         return proxies;
889                 }
890                 
891                 [DllImport (CFNetworkLibrary)]
892                 // CFDictionaryRef CFNetworkCopySystemProxySettings (void);
893                 extern static IntPtr CFNetworkCopySystemProxySettings ();
894                 
895                 public static CFProxySettings GetSystemProxySettings ()
896                 {
897                         IntPtr native = CFNetworkCopySystemProxySettings ();
898                         
899                         if (native == IntPtr.Zero)
900                                 return null;
901                         
902                         var dict = new CFDictionary (native, true);
903
904                         return new CFProxySettings (dict);
905                 }
906                 
907                 class CFWebProxy : IWebProxy {
908                         ICredentials credentials;
909                         bool userSpecified;
910                         
911                         public CFWebProxy ()
912                         {
913                         }
914                         
915                         public ICredentials Credentials {
916                                 get { return credentials; }
917                                 set {
918                                         userSpecified = true;
919                                         credentials = value;
920                                 }
921                         }
922                         
923                         static Uri GetProxyUri (CFProxy proxy, out NetworkCredential credentials)
924                         {
925                                 string protocol;
926                                 
927                                 switch (proxy.ProxyType) {
928                                 case CFProxyType.FTP:
929                                         protocol = "ftp://";
930                                         break;
931                                 case CFProxyType.HTTP:
932                                 case CFProxyType.HTTPS:
933                                         protocol = "http://";
934                                         break;
935                                 default:
936                                         credentials = null;
937                                         return null;
938                                 }
939                                 
940                                 string username = proxy.Username;
941                                 string password = proxy.Password;
942                                 string hostname = proxy.HostName;
943                                 int port = proxy.Port;
944                                 string uri;
945                                 
946                                 if (username != null)
947                                         credentials = new NetworkCredential (username, password);
948                                 else
949                                         credentials = null;
950                                 
951                                 uri = protocol + hostname + (port != 0 ? ':' + port.ToString () : string.Empty);
952                                 
953                                 return new Uri (uri, UriKind.Absolute);
954                         }
955                         
956                         static Uri GetProxyUriFromScript (IntPtr script, Uri targetUri, out NetworkCredential credentials)
957                         {
958                                 CFProxy[] proxies = CFNetwork.GetProxiesForAutoConfigurationScript (script, targetUri);
959                                 return SelectProxy (proxies, targetUri, out credentials);
960                         }
961
962                         static Uri ExecuteProxyAutoConfigurationURL (IntPtr proxyAutoConfigURL, Uri targetUri, out NetworkCredential credentials)
963                         {
964                                 CFProxy[] proxies = CFNetwork.ExecuteProxyAutoConfigurationURL (proxyAutoConfigURL, targetUri);
965                                 return SelectProxy (proxies, targetUri, out credentials);
966                         }
967
968
969                         static Uri SelectProxy (CFProxy[] proxies, Uri targetUri, out NetworkCredential credentials)
970                         {
971                                 if (proxies == null) {
972                                         credentials = null;
973                                         return targetUri;
974                                 }
975                                 
976                                 for (int i = 0; i < proxies.Length; i++) {
977                                         switch (proxies[i].ProxyType) {
978                                         case CFProxyType.HTTPS:
979                                         case CFProxyType.HTTP:
980                                         case CFProxyType.FTP:
981                                                 // create a Uri based on the hostname/port/etc info
982                                                 return GetProxyUri (proxies[i], out credentials);
983                                         case CFProxyType.SOCKS:
984                                         default:
985                                                 // unsupported proxy type, try the next one
986                                                 break;
987                                         case CFProxyType.None:
988                                                 // no proxy should be used
989                                                 credentials = null;
990                                                 return targetUri;
991                                         }
992                                 }
993                                 
994                                 credentials = null;
995                                 
996                                 return null;
997                         }
998                         
999                         public Uri GetProxy (Uri targetUri)
1000                         {
1001                                 NetworkCredential credentials = null;
1002                                 Uri proxy = null;
1003                                 
1004                                 if (targetUri == null)
1005                                         throw new ArgumentNullException ("targetUri");
1006                                 
1007                                 try {
1008                                         CFProxySettings settings = CFNetwork.GetSystemProxySettings ();
1009                                         CFProxy[] proxies = CFNetwork.GetProxiesForUri (targetUri, settings);
1010                                         
1011                                         if (proxies != null) {
1012                                                 for (int i = 0; i < proxies.Length && proxy == null; i++) {
1013                                                         switch (proxies[i].ProxyType) {
1014                                                         case CFProxyType.AutoConfigurationJavaScript:
1015                                                                 proxy = GetProxyUriFromScript (proxies[i].AutoConfigurationJavaScript, targetUri, out credentials);
1016                                                                 break;
1017                                                         case CFProxyType.AutoConfigurationUrl:
1018                                                                 proxy = ExecuteProxyAutoConfigurationURL (proxies[i].AutoConfigurationUrl, targetUri, out credentials);
1019                                                                 break;
1020                                                         case CFProxyType.HTTPS:
1021                                                         case CFProxyType.HTTP:
1022                                                         case CFProxyType.FTP:
1023                                                                 // create a Uri based on the hostname/port/etc info
1024                                                                 proxy = GetProxyUri (proxies[i], out credentials);
1025                                                                 break;
1026                                                         case CFProxyType.SOCKS:
1027                                                                 // unsupported proxy type, try the next one
1028                                                                 break;
1029                                                         case CFProxyType.None:
1030                                                                 // no proxy should be used
1031                                                                 proxy = targetUri;
1032                                                                 break;
1033                                                         }
1034                                                 }
1035                                                 
1036                                                 if (proxy == null) {
1037                                                         // no supported proxies for this Uri, fall back to trying to connect to targetUri directly
1038                                                         proxy = targetUri;
1039                                                 }
1040                                         } else {
1041                                                 proxy = targetUri;
1042                                         }
1043                                 } catch {
1044                                         // ignore errors while retrieving proxy data
1045                                         proxy = targetUri;
1046                                 }
1047                                 
1048                                 if (!userSpecified)
1049                                         this.credentials = credentials;
1050                                 
1051                                 return proxy;
1052                         }
1053                         
1054                         public bool IsBypassed (Uri targetUri)
1055                         {
1056                                 if (targetUri == null)
1057                                         throw new ArgumentNullException ("targetUri");
1058                                 
1059                                 return GetProxy (targetUri) == targetUri;
1060                         }
1061                 }
1062                 
1063                 public static IWebProxy GetDefaultProxy ()
1064                 {
1065                         return new CFWebProxy ();
1066                 }
1067         }
1068 }