Merge pull request #838 from desdesdes/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 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 IntPtr 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, int 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, 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 int CFArrayGetCount (IntPtr handle);
155
156                 public int Count {
157                         get { return CFArrayGetCount (Handle); }
158                 }
159
160                 [DllImport (CoreFoundationLibrary)]
161                 extern static IntPtr CFArrayGetValueAtIndex (IntPtr handle, int index);
162
163                 public IntPtr this[int index] {
164                         get {
165                                 return CFArrayGetValueAtIndex (Handle, 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                 extern static bool CFNumberGetValue (IntPtr handle, int type, out bool value);
176
177                 public static bool AsBool (IntPtr handle)
178                 {
179                         bool value;
180
181                         if (handle == IntPtr.Zero)
182                                 return false;
183
184                         CFNumberGetValue (handle, 1, out value);
185
186                         return value;
187                 }
188
189                 public static implicit operator bool (CFNumber number)
190                 {
191                         return AsBool (number.Handle);
192                 }
193
194                 [DllImport (CoreFoundationLibrary)]
195                 extern static bool CFNumberGetValue (IntPtr handle, int type, out int value);
196
197                 public static int AsInt32 (IntPtr handle)
198                 {
199                         int value;
200
201                         if (handle == IntPtr.Zero)
202                                 return 0;
203
204                         CFNumberGetValue (handle, 9, out value);
205
206                         return value;
207                 }
208
209                 public static implicit operator int (CFNumber number)
210                 {
211                         return AsInt32 (number.Handle);
212                 }
213         }
214
215         internal struct CFRange {
216                 public int Location, Length;
217                 
218                 public CFRange (int loc, int len)
219                 {
220                         Location = loc;
221                         Length = len;
222                 }
223         }
224
225         internal class CFString : CFObject
226         {
227                 string str;
228
229                 public CFString (IntPtr handle, bool own) : base (handle, own) { }
230
231                 [DllImport (CoreFoundationLibrary)]
232                 extern static IntPtr CFStringCreateWithCharacters (IntPtr alloc, IntPtr chars, int length);
233
234                 public static CFString Create (string value)
235                 {
236                         IntPtr handle;
237
238                         unsafe {
239                                 fixed (char *ptr = value) {
240                                         handle = CFStringCreateWithCharacters (IntPtr.Zero, (IntPtr) ptr, value.Length);
241                                 }
242                         }
243
244                         if (handle == IntPtr.Zero)
245                                 return null;
246
247                         return new CFString (handle, true);
248                 }
249
250                 [DllImport (CoreFoundationLibrary)]
251                 extern static int CFStringGetLength (IntPtr handle);
252
253                 public int Length {
254                         get {
255                                 if (str != null)
256                                         return str.Length;
257
258                                 return CFStringGetLength (Handle);
259                         }
260                 }
261
262                 [DllImport (CoreFoundationLibrary)]
263                 extern static IntPtr CFStringGetCharactersPtr (IntPtr handle);
264
265                 [DllImport (CoreFoundationLibrary)]
266                 extern static IntPtr CFStringGetCharacters (IntPtr handle, CFRange range, IntPtr buffer);
267
268                 public static string AsString (IntPtr handle)
269                 {
270                         if (handle == IntPtr.Zero)
271                                 return null;
272                         
273                         int len = CFStringGetLength (handle);
274                         
275                         if (len == 0)
276                                 return string.Empty;
277                         
278                         IntPtr chars = CFStringGetCharactersPtr (handle);
279                         IntPtr buffer = IntPtr.Zero;
280                         
281                         if (chars == IntPtr.Zero) {
282                                 CFRange range = new CFRange (0, len);
283                                 buffer = Marshal.AllocHGlobal (len * 2);
284                                 CFStringGetCharacters (handle, range, buffer);
285                                 chars = buffer;
286                         }
287
288                         string str;
289
290                         unsafe {
291                                 str = new string ((char *) chars, 0, len);
292                         }
293                         
294                         if (buffer != IntPtr.Zero)
295                                 Marshal.FreeHGlobal (buffer);
296
297                         return str;
298                 }
299
300                 public override string ToString ()
301                 {
302                         if (str == null)
303                                 str = AsString (Handle);
304
305                         return str;
306                 }
307
308                 public static implicit operator string (CFString str)
309                 {
310                         return str.ToString ();
311                 }
312
313                 public static implicit operator CFString (string str)
314                 {
315                         return Create (str);
316                 }
317         }
318
319         internal class CFDictionary : CFObject
320         {
321                 public CFDictionary (IntPtr handle, bool own) : base (handle, own) { }
322
323                 [DllImport (CoreFoundationLibrary)]
324                 extern static IntPtr CFDictionaryGetValue (IntPtr handle, IntPtr key);
325
326                 public IntPtr GetValue (IntPtr key)
327                 {
328                         return CFDictionaryGetValue (Handle, key);
329                 }
330
331                 public IntPtr this[IntPtr key] {
332                         get {
333                                 return GetValue (key);
334                         }
335                 }
336         }
337
338         internal class CFUrl : CFObject
339         {
340                 public CFUrl (IntPtr handle, bool own) : base (handle, own) { }
341
342                 [DllImport (CoreFoundationLibrary)]
343                 extern static IntPtr CFURLCreateWithString (IntPtr allocator, IntPtr str, IntPtr baseURL);
344
345                 public static CFUrl Create (string absolute)
346                 {
347                         if (string.IsNullOrEmpty (absolute))
348                                 return null;
349
350                         CFString str = CFString.Create (absolute);
351                         IntPtr handle = CFURLCreateWithString (IntPtr.Zero, str.Handle, IntPtr.Zero);
352                         str.Dispose ();
353
354                         if (handle == IntPtr.Zero)
355                                 return null;
356
357                         return new CFUrl (handle, true);
358                 }
359         }
360
361         internal enum CFProxyType {
362                 None,
363                 AutoConfigurationUrl,
364                 AutoConfigurationJavaScript,
365                 FTP,
366                 HTTP,
367                 HTTPS,
368                 SOCKS
369         }
370         
371         internal class CFProxy {
372                 //static IntPtr kCFProxyAutoConfigurationHTTPResponseKey;
373                 static IntPtr kCFProxyAutoConfigurationJavaScriptKey;
374                 static IntPtr kCFProxyAutoConfigurationURLKey;
375                 static IntPtr kCFProxyHostNameKey;
376                 static IntPtr kCFProxyPasswordKey;
377                 static IntPtr kCFProxyPortNumberKey;
378                 static IntPtr kCFProxyTypeKey;
379                 static IntPtr kCFProxyUsernameKey;
380
381                 //static IntPtr kCFProxyTypeNone;
382                 static IntPtr kCFProxyTypeAutoConfigurationURL;
383                 static IntPtr kCFProxyTypeAutoConfigurationJavaScript;
384                 static IntPtr kCFProxyTypeFTP;
385                 static IntPtr kCFProxyTypeHTTP;
386                 static IntPtr kCFProxyTypeHTTPS;
387                 static IntPtr kCFProxyTypeSOCKS;
388
389                 static CFProxy ()
390                 {
391                         IntPtr handle = CFObject.dlopen (CFNetwork.CFNetworkLibrary, 0);
392
393                         //kCFProxyAutoConfigurationHTTPResponseKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationHTTPResponseKey");
394                         kCFProxyAutoConfigurationJavaScriptKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationJavaScriptKey");
395                         kCFProxyAutoConfigurationURLKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationURLKey");
396                         kCFProxyHostNameKey = CFObject.GetCFObjectHandle (handle, "kCFProxyHostNameKey");
397                         kCFProxyPasswordKey = CFObject.GetCFObjectHandle (handle, "kCFProxyPasswordKey");
398                         kCFProxyPortNumberKey = CFObject.GetCFObjectHandle (handle, "kCFProxyPortNumberKey");
399                         kCFProxyTypeKey = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeKey");
400                         kCFProxyUsernameKey = CFObject.GetCFObjectHandle (handle, "kCFProxyUsernameKey");
401
402                         //kCFProxyTypeNone = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeNone");
403                         kCFProxyTypeAutoConfigurationURL = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeAutoConfigurationURL");
404                         kCFProxyTypeAutoConfigurationJavaScript = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeAutoConfigurationJavaScript");
405                         kCFProxyTypeFTP = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeFTP");
406                         kCFProxyTypeHTTP = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeHTTP");
407                         kCFProxyTypeHTTPS = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeHTTPS");
408                         kCFProxyTypeSOCKS = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeSOCKS");
409
410                         CFObject.dlclose (handle);
411                 }
412
413                 CFDictionary settings;
414                 
415                 internal CFProxy (CFDictionary settings)
416                 {
417                         this.settings = settings;
418                 }
419                 
420                 static CFProxyType CFProxyTypeToEnum (IntPtr type)
421                 {
422                         if (type == kCFProxyTypeAutoConfigurationJavaScript)
423                                 return CFProxyType.AutoConfigurationJavaScript;
424
425                         if (type == kCFProxyTypeAutoConfigurationURL)
426                                 return CFProxyType.AutoConfigurationUrl;
427
428                         if (type == kCFProxyTypeFTP)
429                                 return CFProxyType.FTP;
430
431                         if (type == kCFProxyTypeHTTP)
432                                 return CFProxyType.HTTP;
433
434                         if (type == kCFProxyTypeHTTPS)
435                                 return CFProxyType.HTTPS;
436
437                         if (type == kCFProxyTypeSOCKS)
438                                 return CFProxyType.SOCKS;
439                         
440                         return CFProxyType.None;
441                 }
442                 
443 #if false
444                 // AFAICT these get used with CFNetworkExecuteProxyAutoConfiguration*()
445                 
446                 // TODO: bind CFHTTPMessage so we can return the proper type here.
447                 public IntPtr AutoConfigurationHTTPResponse {
448                         get { return settings[kCFProxyAutoConfigurationHTTPResponseKey]; }
449                 }
450 #endif
451
452                 public IntPtr AutoConfigurationJavaScript {
453                         get {
454                                 if (kCFProxyAutoConfigurationJavaScriptKey == IntPtr.Zero)
455                                         return IntPtr.Zero;
456                                 
457                                 return settings[kCFProxyAutoConfigurationJavaScriptKey];
458                         }
459                 }
460                 
461                 public IntPtr AutoConfigurationUrl {
462                         get {
463                                 if (kCFProxyAutoConfigurationURLKey == IntPtr.Zero)
464                                         return IntPtr.Zero;
465                                 
466                                 return settings[kCFProxyAutoConfigurationURLKey];
467                         }
468                 }
469                 
470                 public string HostName {
471                         get {
472                                 if (kCFProxyHostNameKey == IntPtr.Zero)
473                                         return null;
474                                 
475                                 return CFString.AsString (settings[kCFProxyHostNameKey]);
476                         }
477                 }
478                 
479                 public string Password {
480                         get {
481                                 if (kCFProxyPasswordKey == IntPtr.Zero)
482                                         return null;
483
484                                 return CFString.AsString (settings[kCFProxyPasswordKey]);
485                         }
486                 }
487                 
488                 public int Port {
489                         get {
490                                 if (kCFProxyPortNumberKey == IntPtr.Zero)
491                                         return 0;
492                                 
493                                 return CFNumber.AsInt32 (settings[kCFProxyPortNumberKey]);
494                         }
495                 }
496                 
497                 public CFProxyType ProxyType {
498                         get {
499                                 if (kCFProxyTypeKey == IntPtr.Zero)
500                                         return CFProxyType.None;
501                                 
502                                 return CFProxyTypeToEnum (settings[kCFProxyTypeKey]);
503                         }
504                 }
505                 
506                 public string Username {
507                         get {
508                                 if (kCFProxyUsernameKey == IntPtr.Zero)
509                                         return null;
510
511                                 return CFString.AsString (settings[kCFProxyUsernameKey]);
512                         }
513                 }
514         }
515         
516         internal class CFProxySettings {
517                 static IntPtr kCFNetworkProxiesHTTPEnable;
518                 static IntPtr kCFNetworkProxiesHTTPPort;
519                 static IntPtr kCFNetworkProxiesHTTPProxy;
520                 static IntPtr kCFNetworkProxiesProxyAutoConfigEnable;
521                 static IntPtr kCFNetworkProxiesProxyAutoConfigJavaScript;
522                 static IntPtr kCFNetworkProxiesProxyAutoConfigURLString;
523
524                 static CFProxySettings ()
525                 {
526                         IntPtr handle = CFObject.dlopen (CFNetwork.CFNetworkLibrary, 0);
527
528                         kCFNetworkProxiesHTTPEnable = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPEnable");
529                         kCFNetworkProxiesHTTPPort = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPPort");
530                         kCFNetworkProxiesHTTPProxy = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPProxy");
531                         kCFNetworkProxiesProxyAutoConfigEnable = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigEnable");
532                         kCFNetworkProxiesProxyAutoConfigJavaScript = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigJavaScript");
533                         kCFNetworkProxiesProxyAutoConfigURLString = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigURLString");
534
535                         CFObject.dlclose (handle);
536                 }
537
538                 CFDictionary settings;
539                 
540                 public CFProxySettings (CFDictionary settings)
541                 {
542                         this.settings = settings;
543                 }
544                 
545                 public CFDictionary Dictionary {
546                         get { return settings; }
547                 }
548                 
549                 public bool HTTPEnable {
550                         get {
551                                 if (kCFNetworkProxiesHTTPEnable == IntPtr.Zero)
552                                         return false;
553
554                                 return CFNumber.AsBool (settings[kCFNetworkProxiesHTTPEnable]);
555                         }
556                 }
557                 
558                 public int HTTPPort {
559                         get {
560                                 if (kCFNetworkProxiesHTTPPort == IntPtr.Zero)
561                                         return 0;
562                                 
563                                 return CFNumber.AsInt32 (settings[kCFNetworkProxiesHTTPPort]);
564                         }
565                 }
566                 
567                 public string HTTPProxy {
568                         get {
569                                 if (kCFNetworkProxiesHTTPProxy == IntPtr.Zero)
570                                         return null;
571                                 
572                                 return CFString.AsString (settings[kCFNetworkProxiesHTTPProxy]);
573                         }
574                 }
575                 
576                 public bool ProxyAutoConfigEnable {
577                         get {
578                                 if (kCFNetworkProxiesProxyAutoConfigEnable == IntPtr.Zero)
579                                         return false;
580                                 
581                                 return CFNumber.AsBool (settings[kCFNetworkProxiesProxyAutoConfigEnable]);
582                         }
583                 }
584                 
585                 public string ProxyAutoConfigJavaScript {
586                         get {
587                                 if (kCFNetworkProxiesProxyAutoConfigJavaScript == IntPtr.Zero)
588                                         return null;
589                                 
590                                 return CFString.AsString (settings[kCFNetworkProxiesProxyAutoConfigJavaScript]);
591                         }
592                 }
593                 
594                 public string ProxyAutoConfigURLString {
595                         get {
596                                 if (kCFNetworkProxiesProxyAutoConfigURLString == IntPtr.Zero)
597                                         return null;
598                                 
599                                 return CFString.AsString (settings[kCFNetworkProxiesProxyAutoConfigURLString]);
600                         }
601                 }
602         }
603         
604         internal static class CFNetwork {
605 #if !MONOTOUCH
606                 public const string CFNetworkLibrary = "/System/Library/Frameworks/CoreServices.framework/Frameworks/CFNetwork.framework/CFNetwork";
607 #else
608                 public const string CFNetworkLibrary = "/System/Library/Frameworks/CFNetwork.framework/CFNetwork";
609 #endif
610                 
611                 [DllImport (CFNetworkLibrary, EntryPoint = "CFNetworkCopyProxiesForAutoConfigurationScript")]
612                 // CFArrayRef CFNetworkCopyProxiesForAutoConfigurationScript (CFStringRef proxyAutoConfigurationScript, CFURLRef targetURL, CFErrorRef* error);
613                 extern static IntPtr CFNetworkCopyProxiesForAutoConfigurationScriptSequential (IntPtr proxyAutoConfigurationScript, IntPtr targetURL, out IntPtr error);
614
615                 class GetProxyData : IDisposable {
616                         public IntPtr script;
617                         public IntPtr targetUri;
618                         public IntPtr error;
619                         public IntPtr result;
620                         public ManualResetEvent evt = new ManualResetEvent (false);
621
622                         public void Dispose ()
623                         {
624                                 evt.Close ();
625                         }
626                 }
627
628                 static object lock_obj = new object ();
629                 static Queue<GetProxyData> get_proxy_queue;
630                 static AutoResetEvent proxy_event;
631
632                 static void CFNetworkCopyProxiesForAutoConfigurationScriptThread ()
633                 {
634                         GetProxyData data;
635                         var data_left = true;
636
637                         while (true) {
638                                 proxy_event.WaitOne ();
639
640                                 do {
641                                         lock (lock_obj) {
642                                                 if (get_proxy_queue.Count == 0)
643                                                         break;
644                                                 data = get_proxy_queue.Dequeue ();
645                                                 data_left = get_proxy_queue.Count > 0;
646                                         }
647
648                                         data.result = CFNetworkCopyProxiesForAutoConfigurationScriptSequential (data.script, data.targetUri, out data.error);
649                                         data.evt.Set ();
650                                 } while (data_left);
651                         }
652                 }
653
654                 static IntPtr CFNetworkCopyProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, IntPtr targetURL, out IntPtr error)
655                 {
656                         // This method must only be called on only one thread during an application's life time.
657                         // Note that it's not enough to use a lock to make calls sequential across different threads,
658                         // it has to be one thread. Also note that that thread can't be the main thread, because the
659                         // main thread might be blocking waiting for this network request to finish.
660                         // Another possibility would be to use JavaScriptCore to execute this piece of
661                         // javascript ourselves, but unfortunately it's not available before iOS7.
662                         // See bug #7923 comment #21+.
663
664                         using (var data = new GetProxyData ()) {
665                                 data.script = proxyAutoConfigurationScript;
666                                 data.targetUri = targetURL;
667
668                                 lock (lock_obj) {
669                                         if (get_proxy_queue == null) {
670                                                 get_proxy_queue = new Queue<GetProxyData> ();
671                                                 proxy_event = new AutoResetEvent (false);
672                                                 new Thread (CFNetworkCopyProxiesForAutoConfigurationScriptThread) {
673                                                         IsBackground = true,
674                                                 }.Start ();
675                                         }
676                                         get_proxy_queue.Enqueue (data);
677                                         proxy_event.Set ();
678                                 }
679
680                                 data.evt.WaitOne ();
681
682                                 error = data.error;
683
684                                 return data.result;
685                         }
686                 }
687
688                 static CFArray CopyProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, CFUrl targetURL)
689                 {
690                         IntPtr err = IntPtr.Zero;
691                         IntPtr native = CFNetworkCopyProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL.Handle, out err);
692                         
693                         if (native == IntPtr.Zero)
694                                 return null;
695                         
696                         return new CFArray (native, true);
697                 }
698                 
699                 public static CFProxy[] GetProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, CFUrl targetURL)
700                 {
701                         if (proxyAutoConfigurationScript == IntPtr.Zero)
702                                 throw new ArgumentNullException ("proxyAutoConfigurationScript");
703                         
704                         if (targetURL == null)
705                                 throw new ArgumentNullException ("targetURL");
706                         
707                         CFArray array = CopyProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL);
708                         
709                         if (array == null)
710                                 return null;
711                         
712                         CFProxy[] proxies = new CFProxy [array.Count];
713                         for (int i = 0; i < proxies.Length; i++) {
714                                 CFDictionary dict = new CFDictionary (array[i], false);
715                                 proxies[i] = new CFProxy (dict);
716                         }
717
718                         array.Dispose ();
719                         
720                         return proxies;
721                 }
722                 
723                 public static CFProxy[] GetProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, Uri targetUri)
724                 {
725                         if (proxyAutoConfigurationScript == IntPtr.Zero)
726                                 throw new ArgumentNullException ("proxyAutoConfigurationScript");
727                         
728                         if (targetUri == null)
729                                 throw new ArgumentNullException ("targetUri");
730                         
731                         CFUrl targetURL = CFUrl.Create (targetUri.AbsoluteUri);
732                         CFProxy[] proxies = GetProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL);
733                         targetURL.Dispose ();
734                         
735                         return proxies;
736                 }
737                 
738                 [DllImport (CFNetworkLibrary)]
739                 // CFArrayRef CFNetworkCopyProxiesForURL (CFURLRef url, CFDictionaryRef proxySettings);
740                 extern static IntPtr CFNetworkCopyProxiesForURL (IntPtr url, IntPtr proxySettings);
741                 
742                 static CFArray CopyProxiesForURL (CFUrl url, CFDictionary proxySettings)
743                 {
744                         IntPtr native = CFNetworkCopyProxiesForURL (url.Handle, proxySettings != null ? proxySettings.Handle : IntPtr.Zero);
745                         
746                         if (native == IntPtr.Zero)
747                                 return null;
748                         
749                         return new CFArray (native, true);
750                 }
751                 
752                 public static CFProxy[] GetProxiesForURL (CFUrl url, CFProxySettings proxySettings)
753                 {
754                         if (url == null || url.Handle == IntPtr.Zero)
755                                 throw new ArgumentNullException ("url");
756                         
757                         if (proxySettings == null)
758                                 proxySettings = GetSystemProxySettings ();
759                         
760                         CFArray array = CopyProxiesForURL (url, proxySettings.Dictionary);
761                         
762                         if (array == null)
763                                 return null;
764
765                         CFProxy[] proxies = new CFProxy [array.Count];
766                         for (int i = 0; i < proxies.Length; i++) {
767                                 CFDictionary dict = new CFDictionary (array[i], false);
768                                 proxies[i] = new CFProxy (dict);
769                         }
770
771                         array.Dispose ();
772                         
773                         return proxies;
774                 }
775                 
776                 public static CFProxy[] GetProxiesForUri (Uri uri, CFProxySettings proxySettings)
777                 {
778                         if (uri == null)
779                                 throw new ArgumentNullException ("uri");
780                         
781                         CFUrl url = CFUrl.Create (uri.AbsoluteUri);
782                         if (url == null)
783                                 return null;
784                         
785                         CFProxy[] proxies = GetProxiesForURL (url, proxySettings);
786                         url.Dispose ();
787                         
788                         return proxies;
789                 }
790                 
791                 [DllImport (CFNetworkLibrary)]
792                 // CFDictionaryRef CFNetworkCopySystemProxySettings (void);
793                 extern static IntPtr CFNetworkCopySystemProxySettings ();
794                 
795                 public static CFProxySettings GetSystemProxySettings ()
796                 {
797                         IntPtr native = CFNetworkCopySystemProxySettings ();
798                         
799                         if (native == IntPtr.Zero)
800                                 return null;
801                         
802                         var dict = new CFDictionary (native, true);
803
804                         return new CFProxySettings (dict);
805                 }
806                 
807                 class CFWebProxy : IWebProxy {
808                         ICredentials credentials;
809                         bool userSpecified;
810                         
811                         public CFWebProxy ()
812                         {
813                         }
814                         
815                         public ICredentials Credentials {
816                                 get { return credentials; }
817                                 set {
818                                         userSpecified = true;
819                                         credentials = value;
820                                 }
821                         }
822                         
823                         static Uri GetProxyUri (CFProxy proxy, out NetworkCredential credentials)
824                         {
825                                 string protocol;
826                                 
827                                 switch (proxy.ProxyType) {
828                                 case CFProxyType.FTP:
829                                         protocol = "ftp://";
830                                         break;
831                                 case CFProxyType.HTTP:
832                                 case CFProxyType.HTTPS:
833                                         protocol = "http://";
834                                         break;
835                                 default:
836                                         credentials = null;
837                                         return null;
838                                 }
839                                 
840                                 string username = proxy.Username;
841                                 string password = proxy.Password;
842                                 string hostname = proxy.HostName;
843                                 int port = proxy.Port;
844                                 string uri;
845                                 
846                                 if (username != null)
847                                         credentials = new NetworkCredential (username, password);
848                                 else
849                                         credentials = null;
850                                 
851                                 uri = protocol + hostname + (port != 0 ? ':' + port.ToString () : string.Empty);
852                                 
853                                 return new Uri (uri, UriKind.Absolute);
854                         }
855                         
856                         static Uri GetProxyUriFromScript (IntPtr script, Uri targetUri, out NetworkCredential credentials)
857                         {
858                                 CFProxy[] proxies = CFNetwork.GetProxiesForAutoConfigurationScript (script, targetUri);
859                                 
860                                 if (proxies == null) {
861                                         credentials = null;
862                                         return targetUri;
863                                 }
864                                 
865                                 for (int i = 0; i < proxies.Length; i++) {
866                                         switch (proxies[i].ProxyType) {
867                                         case CFProxyType.HTTPS:
868                                         case CFProxyType.HTTP:
869                                         case CFProxyType.FTP:
870                                                 // create a Uri based on the hostname/port/etc info
871                                                 return GetProxyUri (proxies[i], out credentials);
872                                         case CFProxyType.SOCKS:
873                                         default:
874                                                 // unsupported proxy type, try the next one
875                                                 break;
876                                         case CFProxyType.None:
877                                                 // no proxy should be used
878                                                 credentials = null;
879                                                 return targetUri;
880                                         }
881                                 }
882                                 
883                                 credentials = null;
884                                 
885                                 return null;
886                         }
887                         
888                         public Uri GetProxy (Uri targetUri)
889                         {
890                                 NetworkCredential credentials = null;
891                                 Uri proxy = null;
892                                 
893                                 if (targetUri == null)
894                                         throw new ArgumentNullException ("targetUri");
895                                 
896                                 try {
897                                         CFProxySettings settings = CFNetwork.GetSystemProxySettings ();
898                                         CFProxy[] proxies = CFNetwork.GetProxiesForUri (targetUri, settings);
899                                         
900                                         if (proxies != null) {
901                                                 for (int i = 0; i < proxies.Length && proxy == null; i++) {
902                                                         switch (proxies[i].ProxyType) {
903                                                         case CFProxyType.AutoConfigurationJavaScript:
904                                                                 proxy = GetProxyUriFromScript (proxies[i].AutoConfigurationJavaScript, targetUri, out credentials);
905                                                                 break;
906                                                         case CFProxyType.AutoConfigurationUrl:
907                                                                 // unsupported proxy type (requires fetching script from remote url)
908                                                                 break;
909                                                         case CFProxyType.HTTPS:
910                                                         case CFProxyType.HTTP:
911                                                         case CFProxyType.FTP:
912                                                                 // create a Uri based on the hostname/port/etc info
913                                                                 proxy = GetProxyUri (proxies[i], out credentials);
914                                                                 break;
915                                                         case CFProxyType.SOCKS:
916                                                                 // unsupported proxy type, try the next one
917                                                                 break;
918                                                         case CFProxyType.None:
919                                                                 // no proxy should be used
920                                                                 proxy = targetUri;
921                                                                 break;
922                                                         }
923                                                 }
924                                                 
925                                                 if (proxy == null) {
926                                                         // no supported proxies for this Uri, fall back to trying to connect to targetUri directly
927                                                         proxy = targetUri;
928                                                 }
929                                         } else {
930                                                 proxy = targetUri;
931                                         }
932                                 } catch {
933                                         // ignore errors while retrieving proxy data
934                                         proxy = targetUri;
935                                 }
936                                 
937                                 if (!userSpecified)
938                                         this.credentials = credentials;
939                                 
940                                 return proxy;
941                         }
942                         
943                         public bool IsBypassed (Uri targetUri)
944                         {
945                                 if (targetUri == null)
946                                         throw new ArgumentNullException ("targetUri");
947                                 
948                                 return GetProxy (targetUri) == targetUri;
949                         }
950                 }
951                 
952                 public static IWebProxy GetDefaultProxy ()
953                 {
954                         return new CFWebProxy ();
955                 }
956         }
957 }