Add unit test for AggregateException.GetBaseException that works on .net but is broke...
[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.Runtime.InteropServices;
29
30 namespace System.Net
31 {
32         internal class CFObject : IDisposable
33         {
34                 public const string CoreFoundationLibrary = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation";
35                 const string SystemLibrary = "/usr/lib/libSystem.dylib";
36
37                 [DllImport (SystemLibrary)]
38                 public static extern IntPtr dlopen (string path, int mode);
39
40                 [DllImport (SystemLibrary)]
41                 public static extern IntPtr dlsym (IntPtr handle, string symbol);
42
43                 [DllImport (SystemLibrary)]
44                 public static extern void dlclose (IntPtr handle);
45
46                 public static IntPtr GetIndirect (IntPtr handle, string symbol)
47                 {
48                         return dlsym (handle, symbol);
49                 }
50
51                 public static IntPtr GetCFObjectHandle (IntPtr handle, string symbol)
52                 {
53                         var indirect = dlsym (handle, symbol);
54                         if (indirect == IntPtr.Zero)
55                                 return IntPtr.Zero;
56
57                         return Marshal.ReadIntPtr (indirect);
58                 }
59
60                 public CFObject (IntPtr handle, bool own)
61                 {
62                         Handle = handle;
63
64                         if (!own)
65                                 Retain ();
66                 }
67
68                 ~CFObject ()
69                 {
70                         Dispose (false);
71                 }
72
73                 public IntPtr Handle { get; private set; }
74
75                 [DllImport (CoreFoundationLibrary)]
76                 extern static IntPtr CFRetain (IntPtr handle);
77
78                 void Retain ()
79                 {
80                         CFRetain (Handle);
81                 }
82
83                 [DllImport (CoreFoundationLibrary)]
84                 extern static IntPtr CFRelease (IntPtr handle);
85
86                 void Release ()
87                 {
88                         CFRelease (Handle);
89                 }
90
91                 protected virtual void Dispose (bool disposing)
92                 {
93                         if (Handle != IntPtr.Zero) {
94                                 Release ();
95                                 Handle = IntPtr.Zero;
96                         }
97                 }
98
99                 public void Dispose ()
100                 {
101                         Dispose (true);
102                         GC.SuppressFinalize (this);
103                 }
104         }
105
106         internal class CFArray : CFObject
107         {
108                 public CFArray (IntPtr handle, bool own) : base (handle, own) { }
109
110                 [DllImport (CoreFoundationLibrary)]
111                 extern static IntPtr CFArrayCreate (IntPtr allocator, IntPtr values, int numValues, IntPtr callbacks);
112                 static readonly IntPtr kCFTypeArrayCallbacks;
113
114                 static CFArray ()
115                 {
116                         var handle = dlopen (CoreFoundationLibrary, 0);
117                         if (handle == IntPtr.Zero)
118                                 return;
119
120                         try {
121                                 kCFTypeArrayCallbacks = GetIndirect (handle, "kCFTypeArrayCallBacks");
122                         } finally {
123                                 dlclose (handle);
124                         }
125                 }
126
127                 static unsafe CFArray Create (params IntPtr[] values)
128                 {
129                         if (values == null)
130                                 throw new ArgumentNullException ("values");
131
132                         fixed (IntPtr *pv = values) {
133                                 IntPtr handle = CFArrayCreate (IntPtr.Zero, (IntPtr) pv, values.Length, kCFTypeArrayCallbacks);
134
135                                 return new CFArray (handle, false);
136                         }
137                 }
138
139                 public static CFArray Create (params CFObject[] values)
140                 {
141                         if (values == null)
142                                 throw new ArgumentNullException ("values");
143
144                         IntPtr[] _values = new IntPtr [values.Length];
145                         for (int i = 0; i < _values.Length; i++)
146                                 _values[i] = values[i].Handle;
147
148                         return Create (_values);
149                 }
150
151                 [DllImport (CoreFoundationLibrary)]
152                 extern static int CFArrayGetCount (IntPtr handle);
153
154                 public int Count {
155                         get { return CFArrayGetCount (Handle); }
156                 }
157
158                 [DllImport (CoreFoundationLibrary)]
159                 extern static IntPtr CFArrayGetValueAtIndex (IntPtr handle, int index);
160
161                 public IntPtr this[int index] {
162                         get {
163                                 return CFArrayGetValueAtIndex (Handle, index);
164                         }
165                 }
166         }
167
168         internal class CFNumber : CFObject
169         {
170                 public CFNumber (IntPtr handle, bool own) : base (handle, own) { }
171
172                 [DllImport (CoreFoundationLibrary)]
173                 extern static bool CFNumberGetValue (IntPtr handle, int type, out bool value);
174
175                 public static bool AsBool (IntPtr handle)
176                 {
177                         bool value;
178
179                         if (handle == IntPtr.Zero)
180                                 return false;
181
182                         CFNumberGetValue (handle, 1, out value);
183
184                         return value;
185                 }
186
187                 public static implicit operator bool (CFNumber number)
188                 {
189                         return AsBool (number.Handle);
190                 }
191
192                 [DllImport (CoreFoundationLibrary)]
193                 extern static bool CFNumberGetValue (IntPtr handle, int type, out int value);
194
195                 public static int AsInt32 (IntPtr handle)
196                 {
197                         int value;
198
199                         if (handle == IntPtr.Zero)
200                                 return 0;
201
202                         CFNumberGetValue (handle, 9, out value);
203
204                         return value;
205                 }
206
207                 public static implicit operator int (CFNumber number)
208                 {
209                         return AsInt32 (number.Handle);
210                 }
211         }
212
213         internal struct CFRange {
214                 public int Location, Length;
215                 
216                 public CFRange (int loc, int len)
217                 {
218                         Location = loc;
219                         Length = len;
220                 }
221         }
222
223         internal class CFString : CFObject
224         {
225                 string str;
226
227                 public CFString (IntPtr handle, bool own) : base (handle, own) { }
228
229                 [DllImport (CoreFoundationLibrary)]
230                 extern static IntPtr CFStringCreateWithCharacters (IntPtr alloc, IntPtr chars, int length);
231
232                 public static CFString Create (string value)
233                 {
234                         IntPtr handle;
235
236                         unsafe {
237                                 fixed (char *ptr = value) {
238                                         handle = CFStringCreateWithCharacters (IntPtr.Zero, (IntPtr) ptr, value.Length);
239                                 }
240                         }
241
242                         if (handle == IntPtr.Zero)
243                                 return null;
244
245                         return new CFString (handle, true);
246                 }
247
248                 [DllImport (CoreFoundationLibrary)]
249                 extern static int CFStringGetLength (IntPtr handle);
250
251                 public int Length {
252                         get {
253                                 if (str != null)
254                                         return str.Length;
255
256                                 return CFStringGetLength (Handle);
257                         }
258                 }
259
260                 [DllImport (CoreFoundationLibrary)]
261                 extern static IntPtr CFStringGetCharactersPtr (IntPtr handle);
262
263                 [DllImport (CoreFoundationLibrary)]
264                 extern static IntPtr CFStringGetCharacters (IntPtr handle, CFRange range, IntPtr buffer);
265
266                 public static string AsString (IntPtr handle)
267                 {
268                         if (handle == IntPtr.Zero)
269                                 return null;
270                         
271                         int len = CFStringGetLength (handle);
272                         
273                         if (len == 0)
274                                 return string.Empty;
275                         
276                         IntPtr chars = CFStringGetCharactersPtr (handle);
277                         IntPtr buffer = IntPtr.Zero;
278                         
279                         if (chars == IntPtr.Zero) {
280                                 CFRange range = new CFRange (0, len);
281                                 buffer = Marshal.AllocHGlobal (len * 2);
282                                 CFStringGetCharacters (handle, range, buffer);
283                                 chars = buffer;
284                         }
285
286                         string str;
287
288                         unsafe {
289                                 str = new string ((char *) chars, 0, len);
290                         }
291                         
292                         if (buffer != IntPtr.Zero)
293                                 Marshal.FreeHGlobal (buffer);
294
295                         return str;
296                 }
297
298                 public override string ToString ()
299                 {
300                         if (str == null)
301                                 str = AsString (Handle);
302
303                         return str;
304                 }
305
306                 public static implicit operator string (CFString str)
307                 {
308                         return str.ToString ();
309                 }
310
311                 public static implicit operator CFString (string str)
312                 {
313                         return Create (str);
314                 }
315         }
316
317         internal class CFDictionary : CFObject
318         {
319                 public CFDictionary (IntPtr handle, bool own) : base (handle, own) { }
320
321                 [DllImport (CoreFoundationLibrary)]
322                 extern static IntPtr CFDictionaryGetValue (IntPtr handle, IntPtr key);
323
324                 public IntPtr GetValue (IntPtr key)
325                 {
326                         return CFDictionaryGetValue (Handle, key);
327                 }
328
329                 public IntPtr this[IntPtr key] {
330                         get {
331                                 return GetValue (key);
332                         }
333                 }
334         }
335
336         internal class CFUrl : CFObject
337         {
338                 public CFUrl (IntPtr handle, bool own) : base (handle, own) { }
339
340                 [DllImport (CoreFoundationLibrary)]
341                 extern static IntPtr CFURLCreateWithString (IntPtr allocator, IntPtr str, IntPtr baseURL);
342
343                 public static CFUrl Create (string absolute)
344                 {
345                         if (string.IsNullOrEmpty (absolute))
346                                 return null;
347
348                         CFString str = CFString.Create (absolute);
349                         IntPtr handle = CFURLCreateWithString (IntPtr.Zero, str.Handle, IntPtr.Zero);
350                         str.Dispose ();
351
352                         if (handle == IntPtr.Zero)
353                                 return null;
354
355                         return new CFUrl (handle, true);
356                 }
357         }
358
359         internal enum CFProxyType {
360                 None,
361                 AutoConfigurationUrl,
362                 AutoConfigurationJavaScript,
363                 FTP,
364                 HTTP,
365                 HTTPS,
366                 SOCKS
367         }
368         
369         internal class CFProxy {
370                 //static IntPtr kCFProxyAutoConfigurationHTTPResponseKey;
371                 static IntPtr kCFProxyAutoConfigurationJavaScriptKey;
372                 static IntPtr kCFProxyAutoConfigurationURLKey;
373                 static IntPtr kCFProxyHostNameKey;
374                 static IntPtr kCFProxyPasswordKey;
375                 static IntPtr kCFProxyPortNumberKey;
376                 static IntPtr kCFProxyTypeKey;
377                 static IntPtr kCFProxyUsernameKey;
378
379                 //static IntPtr kCFProxyTypeNone;
380                 static IntPtr kCFProxyTypeAutoConfigurationURL;
381                 static IntPtr kCFProxyTypeAutoConfigurationJavaScript;
382                 static IntPtr kCFProxyTypeFTP;
383                 static IntPtr kCFProxyTypeHTTP;
384                 static IntPtr kCFProxyTypeHTTPS;
385                 static IntPtr kCFProxyTypeSOCKS;
386
387                 static CFProxy ()
388                 {
389                         IntPtr handle = CFObject.dlopen (CFNetwork.CFNetworkLibrary, 0);
390
391                         //kCFProxyAutoConfigurationHTTPResponseKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationHTTPResponseKey");
392                         kCFProxyAutoConfigurationJavaScriptKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationJavaScriptKey");
393                         kCFProxyAutoConfigurationURLKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationURLKey");
394                         kCFProxyHostNameKey = CFObject.GetCFObjectHandle (handle, "kCFProxyHostNameKey");
395                         kCFProxyPasswordKey = CFObject.GetCFObjectHandle (handle, "kCFProxyPasswordKey");
396                         kCFProxyPortNumberKey = CFObject.GetCFObjectHandle (handle, "kCFProxyPortNumberKey");
397                         kCFProxyTypeKey = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeKey");
398                         kCFProxyUsernameKey = CFObject.GetCFObjectHandle (handle, "kCFProxyUsernameKey");
399
400                         //kCFProxyTypeNone = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeNone");
401                         kCFProxyTypeAutoConfigurationURL = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeAutoConfigurationURL");
402                         kCFProxyTypeAutoConfigurationJavaScript = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeAutoConfigurationJavaScript");
403                         kCFProxyTypeFTP = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeFTP");
404                         kCFProxyTypeHTTP = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeHTTP");
405                         kCFProxyTypeHTTPS = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeHTTPS");
406                         kCFProxyTypeSOCKS = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeSOCKS");
407
408                         CFObject.dlclose (handle);
409                 }
410
411                 CFDictionary settings;
412                 
413                 internal CFProxy (CFDictionary settings)
414                 {
415                         this.settings = settings;
416                 }
417                 
418                 static CFProxyType CFProxyTypeToEnum (IntPtr type)
419                 {
420                         if (type == kCFProxyTypeAutoConfigurationJavaScript)
421                                 return CFProxyType.AutoConfigurationJavaScript;
422
423                         if (type == kCFProxyTypeAutoConfigurationURL)
424                                 return CFProxyType.AutoConfigurationUrl;
425
426                         if (type == kCFProxyTypeFTP)
427                                 return CFProxyType.FTP;
428
429                         if (type == kCFProxyTypeHTTP)
430                                 return CFProxyType.HTTP;
431
432                         if (type == kCFProxyTypeHTTPS)
433                                 return CFProxyType.HTTPS;
434
435                         if (type == kCFProxyTypeSOCKS)
436                                 return CFProxyType.SOCKS;
437                         
438                         return CFProxyType.None;
439                 }
440                 
441 #if false
442                 // AFAICT these get used with CFNetworkExecuteProxyAutoConfiguration*()
443                 
444                 // TODO: bind CFHTTPMessage so we can return the proper type here.
445                 public IntPtr AutoConfigurationHTTPResponse {
446                         get { return settings[kCFProxyAutoConfigurationHTTPResponseKey]; }
447                 }
448 #endif
449
450                 public IntPtr AutoConfigurationJavaScript {
451                         get {
452                                 if (kCFProxyAutoConfigurationJavaScriptKey == IntPtr.Zero)
453                                         return IntPtr.Zero;
454                                 
455                                 return settings[kCFProxyAutoConfigurationJavaScriptKey];
456                         }
457                 }
458                 
459                 public IntPtr AutoConfigurationUrl {
460                         get {
461                                 if (kCFProxyAutoConfigurationURLKey == IntPtr.Zero)
462                                         return IntPtr.Zero;
463                                 
464                                 return settings[kCFProxyAutoConfigurationURLKey];
465                         }
466                 }
467                 
468                 public string HostName {
469                         get {
470                                 if (kCFProxyHostNameKey == IntPtr.Zero)
471                                         return null;
472                                 
473                                 return CFString.AsString (settings[kCFProxyHostNameKey]);
474                         }
475                 }
476                 
477                 public string Password {
478                         get {
479                                 if (kCFProxyPasswordKey == IntPtr.Zero)
480                                         return null;
481
482                                 return CFString.AsString (settings[kCFProxyPasswordKey]);
483                         }
484                 }
485                 
486                 public int Port {
487                         get {
488                                 if (kCFProxyPortNumberKey == IntPtr.Zero)
489                                         return 0;
490                                 
491                                 return CFNumber.AsInt32 (settings[kCFProxyPortNumberKey]);
492                         }
493                 }
494                 
495                 public CFProxyType ProxyType {
496                         get {
497                                 if (kCFProxyTypeKey == IntPtr.Zero)
498                                         return CFProxyType.None;
499                                 
500                                 return CFProxyTypeToEnum (settings[kCFProxyTypeKey]);
501                         }
502                 }
503                 
504                 public string Username {
505                         get {
506                                 if (kCFProxyUsernameKey == IntPtr.Zero)
507                                         return null;
508
509                                 return CFString.AsString (settings[kCFProxyUsernameKey]);
510                         }
511                 }
512         }
513         
514         internal class CFProxySettings {
515                 static IntPtr kCFNetworkProxiesHTTPEnable;
516                 static IntPtr kCFNetworkProxiesHTTPPort;
517                 static IntPtr kCFNetworkProxiesHTTPProxy;
518                 static IntPtr kCFNetworkProxiesProxyAutoConfigEnable;
519                 static IntPtr kCFNetworkProxiesProxyAutoConfigJavaScript;
520                 static IntPtr kCFNetworkProxiesProxyAutoConfigURLString;
521
522                 static CFProxySettings ()
523                 {
524                         IntPtr handle = CFObject.dlopen (CFNetwork.CFNetworkLibrary, 0);
525
526                         kCFNetworkProxiesHTTPEnable = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPEnable");
527                         kCFNetworkProxiesHTTPPort = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPPort");
528                         kCFNetworkProxiesHTTPProxy = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPProxy");
529                         kCFNetworkProxiesProxyAutoConfigEnable = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigEnable");
530                         kCFNetworkProxiesProxyAutoConfigJavaScript = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigJavaScript");
531                         kCFNetworkProxiesProxyAutoConfigURLString = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigURLString");
532
533                         CFObject.dlclose (handle);
534                 }
535
536                 CFDictionary settings;
537                 
538                 public CFProxySettings (CFDictionary settings)
539                 {
540                         this.settings = settings;
541                 }
542                 
543                 public CFDictionary Dictionary {
544                         get { return settings; }
545                 }
546                 
547                 public bool HTTPEnable {
548                         get {
549                                 if (kCFNetworkProxiesHTTPEnable == IntPtr.Zero)
550                                         return false;
551
552                                 return CFNumber.AsBool (settings[kCFNetworkProxiesHTTPEnable]);
553                         }
554                 }
555                 
556                 public int HTTPPort {
557                         get {
558                                 if (kCFNetworkProxiesHTTPPort == IntPtr.Zero)
559                                         return 0;
560                                 
561                                 return CFNumber.AsInt32 (settings[kCFNetworkProxiesHTTPPort]);
562                         }
563                 }
564                 
565                 public string HTTPProxy {
566                         get {
567                                 if (kCFNetworkProxiesHTTPProxy == IntPtr.Zero)
568                                         return null;
569                                 
570                                 return CFString.AsString (settings[kCFNetworkProxiesHTTPProxy]);
571                         }
572                 }
573                 
574                 public bool ProxyAutoConfigEnable {
575                         get {
576                                 if (kCFNetworkProxiesProxyAutoConfigEnable == IntPtr.Zero)
577                                         return false;
578                                 
579                                 return CFNumber.AsBool (settings[kCFNetworkProxiesProxyAutoConfigEnable]);
580                         }
581                 }
582                 
583                 public string ProxyAutoConfigJavaScript {
584                         get {
585                                 if (kCFNetworkProxiesProxyAutoConfigJavaScript == IntPtr.Zero)
586                                         return null;
587                                 
588                                 return CFString.AsString (settings[kCFNetworkProxiesProxyAutoConfigJavaScript]);
589                         }
590                 }
591                 
592                 public string ProxyAutoConfigURLString {
593                         get {
594                                 if (kCFNetworkProxiesProxyAutoConfigURLString == IntPtr.Zero)
595                                         return null;
596                                 
597                                 return CFString.AsString (settings[kCFNetworkProxiesProxyAutoConfigURLString]);
598                         }
599                 }
600         }
601         
602         internal static class CFNetwork {
603 #if !MONOTOUCH
604                 public const string CFNetworkLibrary = "/System/Library/Frameworks/CoreServices.framework/Frameworks/CFNetwork.framework/CFNetwork";
605 #else
606                 public const string CFNetworkLibrary = "/System/Library/Frameworks/CFNetwork.framework/CFNetwork";
607 #endif
608                 
609                 [DllImport (CFNetworkLibrary)]
610                 // CFArrayRef CFNetworkCopyProxiesForAutoConfigurationScript (CFStringRef proxyAutoConfigurationScript, CFURLRef targetURL, CFErrorRef* error);
611                 extern static IntPtr CFNetworkCopyProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, IntPtr targetURL, out IntPtr error);
612                 
613                 static CFArray CopyProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, CFUrl targetURL)
614                 {
615                         IntPtr err = IntPtr.Zero;
616                         IntPtr native = CFNetworkCopyProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL.Handle, out err);
617                         
618                         if (native == IntPtr.Zero)
619                                 return null;
620                         
621                         return new CFArray (native, true);
622                 }
623                 
624                 public static CFProxy[] GetProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, CFUrl targetURL)
625                 {
626                         if (proxyAutoConfigurationScript == IntPtr.Zero)
627                                 throw new ArgumentNullException ("proxyAutoConfigurationScript");
628                         
629                         if (targetURL == null)
630                                 throw new ArgumentNullException ("targetURL");
631                         
632                         CFArray array = CopyProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL);
633                         
634                         if (array == null)
635                                 return null;
636                         
637                         CFProxy[] proxies = new CFProxy [array.Count];
638                         for (int i = 0; i < proxies.Length; i++) {
639                                 CFDictionary dict = new CFDictionary (array[i], false);
640                                 proxies[i] = new CFProxy (dict);
641                         }
642
643                         array.Dispose ();
644                         
645                         return proxies;
646                 }
647                 
648                 public static CFProxy[] GetProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, Uri targetUri)
649                 {
650                         if (proxyAutoConfigurationScript == IntPtr.Zero)
651                                 throw new ArgumentNullException ("proxyAutoConfigurationScript");
652                         
653                         if (targetUri == null)
654                                 throw new ArgumentNullException ("targetUri");
655                         
656                         CFUrl targetURL = CFUrl.Create (targetUri.AbsoluteUri);
657                         CFProxy[] proxies = GetProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL);
658                         targetURL.Dispose ();
659                         
660                         return proxies;
661                 }
662                 
663                 [DllImport (CFNetworkLibrary)]
664                 // CFArrayRef CFNetworkCopyProxiesForURL (CFURLRef url, CFDictionaryRef proxySettings);
665                 extern static IntPtr CFNetworkCopyProxiesForURL (IntPtr url, IntPtr proxySettings);
666                 
667                 static CFArray CopyProxiesForURL (CFUrl url, CFDictionary proxySettings)
668                 {
669                         IntPtr native = CFNetworkCopyProxiesForURL (url.Handle, proxySettings != null ? proxySettings.Handle : IntPtr.Zero);
670                         
671                         if (native == IntPtr.Zero)
672                                 return null;
673                         
674                         return new CFArray (native, true);
675                 }
676                 
677                 public static CFProxy[] GetProxiesForURL (CFUrl url, CFProxySettings proxySettings)
678                 {
679                         if (url == null || url.Handle == IntPtr.Zero)
680                                 throw new ArgumentNullException ("url");
681                         
682                         if (proxySettings == null)
683                                 proxySettings = GetSystemProxySettings ();
684                         
685                         CFArray array = CopyProxiesForURL (url, proxySettings.Dictionary);
686                         
687                         if (array == null)
688                                 return null;
689
690                         CFProxy[] proxies = new CFProxy [array.Count];
691                         for (int i = 0; i < proxies.Length; i++) {
692                                 CFDictionary dict = new CFDictionary (array[i], false);
693                                 proxies[i] = new CFProxy (dict);
694                         }
695
696                         array.Dispose ();
697                         
698                         return proxies;
699                 }
700                 
701                 public static CFProxy[] GetProxiesForUri (Uri uri, CFProxySettings proxySettings)
702                 {
703                         if (uri == null)
704                                 throw new ArgumentNullException ("uri");
705                         
706                         CFUrl url = CFUrl.Create (uri.AbsoluteUri);
707                         if (url == null)
708                                 return null;
709                         
710                         CFProxy[] proxies = GetProxiesForURL (url, proxySettings);
711                         url.Dispose ();
712                         
713                         return proxies;
714                 }
715                 
716                 [DllImport (CFNetworkLibrary)]
717                 // CFDictionaryRef CFNetworkCopySystemProxySettings (void);
718                 extern static IntPtr CFNetworkCopySystemProxySettings ();
719                 
720                 public static CFProxySettings GetSystemProxySettings ()
721                 {
722                         IntPtr native = CFNetworkCopySystemProxySettings ();
723                         
724                         if (native == IntPtr.Zero)
725                                 return null;
726                         
727                         var dict = new CFDictionary (native, true);
728
729                         return new CFProxySettings (dict);
730                 }
731                 
732                 class CFWebProxy : IWebProxy {
733                         ICredentials credentials;
734                         bool userSpecified;
735                         
736                         public CFWebProxy ()
737                         {
738                         }
739                         
740                         public ICredentials Credentials {
741                                 get { return credentials; }
742                                 set {
743                                         userSpecified = true;
744                                         credentials = value;
745                                 }
746                         }
747                         
748                         static Uri GetProxyUri (CFProxy proxy, out NetworkCredential credentials)
749                         {
750                                 string protocol;
751                                 
752                                 switch (proxy.ProxyType) {
753                                 case CFProxyType.FTP:
754                                         protocol = "ftp://";
755                                         break;
756                                 case CFProxyType.HTTP:
757                                 case CFProxyType.HTTPS:
758                                         protocol = "http://";
759                                         break;
760                                 default:
761                                         credentials = null;
762                                         return null;
763                                 }
764                                 
765                                 string username = proxy.Username;
766                                 string password = proxy.Password;
767                                 string hostname = proxy.HostName;
768                                 int port = proxy.Port;
769                                 string uri;
770                                 
771                                 if (username != null)
772                                         credentials = new NetworkCredential (username, password);
773                                 else
774                                         credentials = null;
775                                 
776                                 uri = protocol + hostname + (port != 0 ? ':' + port.ToString () : string.Empty);
777                                 
778                                 return new Uri (uri, UriKind.Absolute);
779                         }
780                         
781                         static Uri GetProxyUriFromScript (IntPtr script, Uri targetUri, out NetworkCredential credentials)
782                         {
783                                 CFProxy[] proxies = CFNetwork.GetProxiesForAutoConfigurationScript (script, targetUri);
784                                 
785                                 if (proxies == null) {
786                                         credentials = null;
787                                         return targetUri;
788                                 }
789                                 
790                                 for (int i = 0; i < proxies.Length; i++) {
791                                         switch (proxies[i].ProxyType) {
792                                         case CFProxyType.HTTPS:
793                                         case CFProxyType.HTTP:
794                                         case CFProxyType.FTP:
795                                                 // create a Uri based on the hostname/port/etc info
796                                                 return GetProxyUri (proxies[i], out credentials);
797                                         case CFProxyType.SOCKS:
798                                         default:
799                                                 // unsupported proxy type, try the next one
800                                                 break;
801                                         case CFProxyType.None:
802                                                 // no proxy should be used
803                                                 credentials = null;
804                                                 return targetUri;
805                                         }
806                                 }
807                                 
808                                 credentials = null;
809                                 
810                                 return null;
811                         }
812                         
813                         public Uri GetProxy (Uri targetUri)
814                         {
815                                 NetworkCredential credentials = null;
816                                 Uri proxy = null;
817                                 
818                                 if (targetUri == null)
819                                         throw new ArgumentNullException ("targetUri");
820                                 
821                                 try {
822                                         CFProxySettings settings = CFNetwork.GetSystemProxySettings ();
823                                         CFProxy[] proxies = CFNetwork.GetProxiesForUri (targetUri, settings);
824                                         
825                                         if (proxies != null) {
826                                                 for (int i = 0; i < proxies.Length && proxy == null; i++) {
827                                                         switch (proxies[i].ProxyType) {
828                                                         case CFProxyType.AutoConfigurationJavaScript:
829                                                                 proxy = GetProxyUriFromScript (proxies[i].AutoConfigurationJavaScript, targetUri, out credentials);
830                                                                 break;
831                                                         case CFProxyType.AutoConfigurationUrl:
832                                                                 // unsupported proxy type (requires fetching script from remote url)
833                                                                 break;
834                                                         case CFProxyType.HTTPS:
835                                                         case CFProxyType.HTTP:
836                                                         case CFProxyType.FTP:
837                                                                 // create a Uri based on the hostname/port/etc info
838                                                                 proxy = GetProxyUri (proxies[i], out credentials);
839                                                                 break;
840                                                         case CFProxyType.SOCKS:
841                                                                 // unsupported proxy type, try the next one
842                                                                 break;
843                                                         case CFProxyType.None:
844                                                                 // no proxy should be used
845                                                                 proxy = targetUri;
846                                                                 break;
847                                                         }
848                                                 }
849                                                 
850                                                 if (proxy == null) {
851                                                         // no supported proxies for this Uri, fall back to trying to connect to targetUri directly
852                                                         proxy = targetUri;
853                                                 }
854                                         } else {
855                                                 proxy = targetUri;
856                                         }
857                                 } catch {
858                                         // ignore errors while retrieving proxy data
859                                         proxy = targetUri;
860                                 }
861                                 
862                                 if (!userSpecified)
863                                         this.credentials = credentials;
864                                 
865                                 return proxy;
866                         }
867                         
868                         public bool IsBypassed (Uri targetUri)
869                         {
870                                 if (targetUri == null)
871                                         throw new ArgumentNullException ("targetUri");
872                                 
873                                 return GetProxy (targetUri) == targetUri;
874                         }
875                 }
876                 
877                 public static IWebProxy GetDefaultProxy ()
878                 {
879                         return new CFWebProxy ();
880                 }
881         }
882 }