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