Fixed MacOS proxy support for proxies requiring authentication
[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);
611                 extern static IntPtr CFNetworkCopyProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, IntPtr targetURL);
612                 
613                 static CFArray CopyProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, CFUrl targetURL)
614                 {
615                         IntPtr native = CFNetworkCopyProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL.Handle);
616                         
617                         if (native == IntPtr.Zero)
618                                 return null;
619                         
620                         return new CFArray (native, true);
621                 }
622                 
623                 public static CFProxy[] GetProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, CFUrl targetURL)
624                 {
625                         if (proxyAutoConfigurationScript == IntPtr.Zero)
626                                 throw new ArgumentNullException ("proxyAutoConfigurationScript");
627                         
628                         if (targetURL == null)
629                                 throw new ArgumentNullException ("targetURL");
630                         
631                         CFArray array = CopyProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL);
632                         
633                         if (array == null)
634                                 return null;
635                         
636                         CFProxy[] proxies = new CFProxy [array.Count];
637                         for (int i = 0; i < proxies.Length; i++) {
638                                 CFDictionary dict = new CFDictionary (array[i], false);
639                                 proxies[i] = new CFProxy (dict);
640                         }
641
642                         array.Dispose ();
643                         
644                         return proxies;
645                 }
646                 
647                 public static CFProxy[] GetProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, Uri targetUri)
648                 {
649                         if (proxyAutoConfigurationScript == IntPtr.Zero)
650                                 throw new ArgumentNullException ("proxyAutoConfigurationScript");
651                         
652                         if (targetUri == null)
653                                 throw new ArgumentNullException ("targetUri");
654                         
655                         CFUrl targetURL = CFUrl.Create (targetUri.AbsoluteUri);
656                         CFProxy[] proxies = GetProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL);
657                         targetURL.Dispose ();
658                         
659                         return proxies;
660                 }
661                 
662                 [DllImport (CFNetworkLibrary)]
663                 // CFArrayRef CFNetworkCopyProxiesForURL (CFURLRef url, CFDictionaryRef proxySettings);
664                 extern static IntPtr CFNetworkCopyProxiesForURL (IntPtr url, IntPtr proxySettings);
665                 
666                 static CFArray CopyProxiesForURL (CFUrl url, CFDictionary proxySettings)
667                 {
668                         IntPtr native = CFNetworkCopyProxiesForURL (url.Handle, proxySettings != null ? proxySettings.Handle : IntPtr.Zero);
669                         
670                         if (native == IntPtr.Zero)
671                                 return null;
672                         
673                         return new CFArray (native, true);
674                 }
675                 
676                 public static CFProxy[] GetProxiesForURL (CFUrl url, CFProxySettings proxySettings)
677                 {
678                         if (url == null || url.Handle == IntPtr.Zero)
679                                 throw new ArgumentNullException ("url");
680                         
681                         if (proxySettings == null)
682                                 proxySettings = GetSystemProxySettings ();
683                         
684                         CFArray array = CopyProxiesForURL (url, proxySettings.Dictionary);
685                         
686                         if (array == null)
687                                 return null;
688
689                         CFProxy[] proxies = new CFProxy [array.Count];
690                         for (int i = 0; i < proxies.Length; i++) {
691                                 CFDictionary dict = new CFDictionary (array[i], false);
692                                 proxies[i] = new CFProxy (dict);
693                         }
694
695                         array.Dispose ();
696                         
697                         return proxies;
698                 }
699                 
700                 public static CFProxy[] GetProxiesForUri (Uri uri, CFProxySettings proxySettings)
701                 {
702                         if (uri == null)
703                                 throw new ArgumentNullException ("uri");
704                         
705                         CFUrl url = CFUrl.Create (uri.AbsoluteUri);
706                         if (url == null)
707                                 return null;
708                         
709                         CFProxy[] proxies = GetProxiesForURL (url, proxySettings);
710                         url.Dispose ();
711                         
712                         return proxies;
713                 }
714                 
715                 [DllImport (CFNetworkLibrary)]
716                 // CFDictionaryRef CFNetworkCopySystemProxySettings (void);
717                 extern static IntPtr CFNetworkCopySystemProxySettings ();
718                 
719                 public static CFProxySettings GetSystemProxySettings ()
720                 {
721                         IntPtr native = CFNetworkCopySystemProxySettings ();
722                         
723                         if (native == IntPtr.Zero)
724                                 return null;
725                         
726                         var dict = new CFDictionary (native, true);
727
728                         return new CFProxySettings (dict);
729                 }
730                 
731                 class CFWebProxy : IWebProxy {
732                         ICredentials credentials;
733                         bool userSpecified;
734                         
735                         public CFWebProxy ()
736                         {
737                         }
738                         
739                         public ICredentials Credentials {
740                                 get { return credentials; }
741                                 set {
742                                         userSpecified = true;
743                                         credentials = value;
744                                 }
745                         }
746                         
747                         static Uri GetProxyUri (CFProxy proxy, out NetworkCredential credentials)
748                         {
749                                 string protocol;
750                                 
751                                 switch (proxy.ProxyType) {
752                                 case CFProxyType.FTP:
753                                         protocol = "ftp://";
754                                         break;
755                                 case CFProxyType.HTTP:
756                                 case CFProxyType.HTTPS:
757                                         protocol = "http://";
758                                         break;
759                                 default:
760                                         credentials = null;
761                                         return null;
762                                 }
763                                 
764                                 string username = proxy.Username;
765                                 string password = proxy.Password;
766                                 string hostname = proxy.HostName;
767                                 int port = proxy.Port;
768                                 string uri;
769                                 
770                                 if (username != null)
771                                         credentials = new NetworkCredential (username, password);
772                                 else
773                                         credentials = null;
774                                 
775                                 uri = protocol + hostname + (port != 0 ? ':' + port.ToString () : string.Empty);
776                                 
777                                 return new Uri (uri, UriKind.Absolute);
778                         }
779                         
780                         static Uri GetProxyUriFromScript (IntPtr script, Uri targetUri, out NetworkCredential credentials)
781                         {
782                                 CFProxy[] proxies = CFNetwork.GetProxiesForAutoConfigurationScript (script, targetUri);
783                                 
784                                 if (proxies == null) {
785                                         credentials = null;
786                                         return targetUri;
787                                 }
788                                 
789                                 for (int i = 0; i < proxies.Length; i++) {
790                                         switch (proxies[i].ProxyType) {
791                                         case CFProxyType.HTTPS:
792                                         case CFProxyType.HTTP:
793                                         case CFProxyType.FTP:
794                                                 // create a Uri based on the hostname/port/etc info
795                                                 return GetProxyUri (proxies[i], out credentials);
796                                         case CFProxyType.SOCKS:
797                                         default:
798                                                 // unsupported proxy type, try the next one
799                                                 break;
800                                         case CFProxyType.None:
801                                                 // no proxy should be used
802                                                 credentials = null;
803                                                 return targetUri;
804                                         }
805                                 }
806                                 
807                                 credentials = null;
808                                 
809                                 return null;
810                         }
811                         
812                         public Uri GetProxy (Uri targetUri)
813                         {
814                                 NetworkCredentials credentials = null;
815                                 Uri proxy = null;
816                                 
817                                 if (targetUri == null)
818                                         throw new ArgumentNullException ("targetUri");
819                                 
820                                 try {
821                                         CFProxySettings settings = CFNetwork.GetSystemProxySettings ();
822                                         CFProxy[] proxies = CFNetwork.GetProxiesForUri (targetUri, settings);
823                                         
824                                         if (proxies != null) {
825                                                 for (int i = 0; i < proxies.Length && proxy == null; i++) {
826                                                         switch (proxies[i].ProxyType) {
827                                                         case CFProxyType.AutoConfigurationJavaScript:
828                                                                 proxy = GetProxyUriFromScript (proxies[i].AutoConfigurationJavaScript, targetUri, out credentials);
829                                                                 break;
830                                                         case CFProxyType.AutoConfigurationUrl:
831                                                                 // unsupported proxy type (requires fetching script from remote url)
832                                                                 break;
833                                                         case CFProxyType.HTTPS:
834                                                         case CFProxyType.HTTP:
835                                                         case CFProxyType.FTP:
836                                                                 // create a Uri based on the hostname/port/etc info
837                                                                 proxy = GetProxyUri (proxies[i], out credentials);
838                                                                 break;
839                                                         case CFProxyType.SOCKS:
840                                                                 // unsupported proxy type, try the next one
841                                                                 break;
842                                                         case CFProxyType.None:
843                                                                 // no proxy should be used
844                                                                 proxy = targetUri;
845                                                                 break;
846                                                         }
847                                                 }
848                                                 
849                                                 if (proxy == null) {
850                                                         // no supported proxies for this Uri, fall back to trying to connect to targetUri directly
851                                                         proxy = targetUri;
852                                                 }
853                                         } else {
854                                                 proxy = targetUri;
855                                         }
856                                 } catch {
857                                         // ignore errors while retrieving proxy data
858                                         proxy = targetUri;
859                                 }
860                                 
861                                 if (!userSpecified)
862                                         this.credentials = credentials;
863                                 
864                                 return proxy;
865                         }
866                         
867                         public bool IsBypassed (Uri targetUri)
868                         {
869                                 if (targetUri == null)
870                                         throw new ArgumentNullException ("targetUri");
871                                 
872                                 return GetProxy (targetUri) == targetUri;
873                         }
874                 }
875                 
876                 static CFWebProxy defaultWebProxy;
877                 public static IWebProxy GetDefaultProxy ()
878                 {
879                         if (defaultWebProxy == null)
880                                 defaultWebProxy = new CFWebProxy ();
881                         
882                         return defaultWebProxy;
883                 }
884         }
885 }