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