Merge pull request #487 from mayerwin/patch-1
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel.Channels / StandardBindingImporter.cs
1 //
2 // StandardBindingImporter.cs
3 //
4 // Author:
5 //       Martin Baulig <martin.baulig@xamarin.com>
6 //
7 // Copyright (c) 2012 Xamarin Inc. (http://www.xamarin.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to deal
11 // in the Software without restriction, including without limitation the rights
12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 // copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
15 //
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
18 //
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 // THE SOFTWARE.
26
27 using System;
28 using System.Net;
29 using System.Xml;
30 using System.Xml.Schema;
31 using System.Collections.Generic;
32 using System.ServiceModel;
33 using System.ServiceModel.Channels;
34 using System.ServiceModel.Description;
35
36 using WS = System.Web.Services.Description;
37 using QName = System.Xml.XmlQualifiedName;
38
39 namespace System.ServiceModel.Channels {
40
41         public class StandardBindingImporter : IWsdlImportExtension {
42                 #region IWsdlImportExtension implementation
43
44                 public void BeforeImport (WS.ServiceDescriptionCollection wsdlDocuments, XmlSchemaSet xmlSchemas,
45                                           ICollection<XmlElement> policy)
46                 {
47                 }
48
49                 public void ImportContract (WsdlImporter importer, WsdlContractConversionContext contractContext)
50                 {
51                 }
52
53                 WS.Port LookupPort (WsdlImporter importer, QName name)
54                 {
55                         foreach (WS.ServiceDescription doc in importer.WsdlDocuments) {
56                                 foreach (WS.Service service in doc.Services) {
57                                         foreach (WS.Port port in service.Ports) {
58                                                 if (!name.Namespace.Equals (port.Binding.Namespace))
59                                                         continue;
60                                                 if (!name.Name.Equals (port.Binding.Name))
61                                                         continue;
62                                                 return port;
63                                         }
64                                 }
65                         }
66
67                         return null;
68                 }
69
70                 public void ImportEndpoint (WsdlImporter importer, WsdlEndpointConversionContext context)
71                 {
72                         var custom = context.Endpoint.Binding as CustomBinding;
73                         if (custom == null)
74                                 return;
75
76                         var soapHttp = GetHttpSoapBinding (context.WsdlBinding);
77                         if (soapHttp != null) {
78                                 ImportBasicHttpBinding (importer, context, custom, soapHttp);
79                                 return;
80                         }
81
82                         var soapTcp = GetTcpSoapBinding (context.WsdlBinding);
83                         if (soapTcp != null) {
84                                 ImportNetTcpBinding (importer, context, custom, soapTcp);
85                                 return;
86                         }
87                 }
88
89                 internal static WS.SoapBinding GetHttpSoapBinding (WS.Binding binding)
90                 {
91                         WS.SoapBinding soap = null;
92                         foreach (var extension in binding.Extensions) {
93                                 var check = extension as WS.SoapBinding;
94                                 if (check != null) {
95                                         soap = check;
96                                         break;
97                                 }
98                         }
99                         
100                         if (soap == null)
101                                 return null;
102                         if (soap.Transport != WS.SoapBinding.HttpTransport)
103                                 return null;
104                         if (soap.Style != WS.SoapBindingStyle.Document)
105                                 return null;
106                         return soap;
107                 }
108
109                 const string TcpTransport = "http://schemas.microsoft.com/soap/tcp";
110                 
111                 internal static WS.Soap12Binding GetTcpSoapBinding (WS.Binding binding)
112                 {
113                         WS.Soap12Binding soap = null;
114                         foreach (var extension in binding.Extensions) {
115                                 var check = extension as WS.Soap12Binding;
116                                 if (check != null) {
117                                         soap = check;
118                                         break;
119                                 }
120                         }
121                         
122                         if (soap == null)
123                                 return null;
124                         if (soap.Transport != TcpTransport)
125                                 return null;
126                         if (soap.Style != WS.SoapBindingStyle.Document)
127                                 return null;
128                         return soap;
129                 }
130
131                 bool ImportBasicHttpBinding (
132                         WsdlImporter importer, WsdlEndpointConversionContext context,
133                         CustomBinding custom, WS.SoapBinding soap)
134                 {
135                         TransportBindingElement transportElement = null;
136                         MtomMessageEncodingBindingElement mtomElement = null;
137                         TextMessageEncodingBindingElement textElement = null;
138                         bool foundUnknownElement = false;
139
140                         foreach (var element in custom.Elements) {
141                                 if (element is TransportBindingElement)
142                                         transportElement = (TransportBindingElement)element;
143                                 else if (element is MtomMessageEncodingBindingElement)
144                                         mtomElement = (MtomMessageEncodingBindingElement)element;
145                                 else if (element is TextMessageEncodingBindingElement)
146                                         textElement = (TextMessageEncodingBindingElement)element;
147                                 else {
148                                         importer.AddWarning (
149                                                 "Found unknown binding element `{0}' while attempting " +
150                                                 "to import binding `{0}'.", element.GetType (),
151                                                 custom.Name);
152                                         foundUnknownElement = true;
153                                 }
154                         }
155
156                         if (foundUnknownElement)
157                                 return false;
158
159                         if ((mtomElement != null) && (textElement != null)) {
160                                 // FIXME: Should never happen
161                                 importer.AddWarning (
162                                         "Found both MtomMessageEncodingBindingElement and " +
163                                         "TextMessageEncodingBindingElement while attempting to " +
164                                         "import binding `{0}'.", custom.Name);
165                                 return false;
166                         }
167
168                         BasicHttpBinding httpBinding;
169                         AuthenticationSchemes authScheme;
170
171                         /*
172                          * FIXME: Maybe make the BasicHttpBinding use the transport element
173                          * that we created with the TransportBindingElementImporter ?
174                          * 
175                          * There seems to be no public API to do that, so maybe add a private .ctor ?
176                          * 
177                          */
178
179                         var httpsTransport = transportElement as HttpsTransportBindingElement;
180                         var httpTransport = transportElement as HttpTransportBindingElement;
181
182                         if (httpsTransport != null) {
183                                 httpBinding = new BasicHttpBinding (BasicHttpSecurityMode.Transport);
184                                 authScheme = httpsTransport.AuthenticationScheme;
185                         } else if (httpTransport != null) {
186                                 authScheme = httpTransport.AuthenticationScheme;
187                                 if ((authScheme != AuthenticationSchemes.None) &&
188                                         (authScheme != AuthenticationSchemes.Anonymous))
189                                         httpBinding = new BasicHttpBinding (
190                                                 BasicHttpSecurityMode.TransportCredentialOnly);
191                                 else
192                                         httpBinding = new BasicHttpBinding ();
193                         } else {
194                                 httpBinding = new BasicHttpBinding ();
195                                 authScheme = AuthenticationSchemes.Anonymous;
196                         }
197
198                         if (mtomElement != null)
199                                 httpBinding.MessageEncoding = WSMessageEncoding.Mtom;
200                         else if (textElement != null)
201                                 httpBinding.MessageEncoding = WSMessageEncoding.Text;
202                         else {
203                                 importer.AddWarning (
204                                         "Found neither MtomMessageEncodingBindingElement nor " +
205                                         "TextMessageEncodingBindingElement while attempting to " +
206                                         "import binding `{0}'.", custom.Name);
207                                 return false;
208                         }
209
210                         httpBinding.Name = context.Endpoint.Binding.Name;
211                         httpBinding.Namespace = context.Endpoint.Binding.Namespace;
212
213                         switch (authScheme) {
214                         case AuthenticationSchemes.None:
215                         case AuthenticationSchemes.Anonymous:
216                                 httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
217                                 break;
218                         case AuthenticationSchemes.Basic:
219                                 httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
220                                 break;
221                         case AuthenticationSchemes.Digest:
222                                 httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Digest;
223                                 break;
224                         case AuthenticationSchemes.Ntlm:
225                                 httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;
226                                 break;
227                         case AuthenticationSchemes.Negotiate:
228                                 httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
229                                 break;
230                         default:
231                                 importer.AddWarning ("Invalid auth scheme: {0}", authScheme);
232                                 return false;
233                         }
234
235                         if ((httpsTransport != null) && httpsTransport.RequireClientCertificate) {
236                                 if (httpBinding.Security.Transport.ClientCredentialType != HttpClientCredentialType.None) {
237                                         importer.AddWarning ("Cannot use both client certificate and explicit auth type.");
238                                         return false;
239                                 }
240                                 httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
241                         }
242
243                         context.Endpoint.Binding = httpBinding;
244                         return true;
245                 }
246
247                 bool ImportNetTcpBinding (
248                         WsdlImporter importer, WsdlEndpointConversionContext context,
249                         CustomBinding custom, WS.Soap12Binding soap)
250                 {
251                         TcpTransportBindingElement transportElement = null;
252                         BinaryMessageEncodingBindingElement binaryElement = null;
253                         TransactionFlowBindingElement transactionFlowElement = null;
254                         WindowsStreamSecurityBindingElement windowsStreamElement = null;
255                         SslStreamSecurityBindingElement sslStreamElement = null;
256                         bool foundUnknownElement = false;
257                         
258                         foreach (var element in custom.Elements) {
259                                 if (element is TcpTransportBindingElement)
260                                         transportElement = (TcpTransportBindingElement)element;
261                                 else if (element is BinaryMessageEncodingBindingElement)
262                                         binaryElement = (BinaryMessageEncodingBindingElement)element;
263                                 else if (element is TransactionFlowBindingElement)
264                                         transactionFlowElement = (TransactionFlowBindingElement)element;
265                                 else if (element is WindowsStreamSecurityBindingElement)
266                                         windowsStreamElement = (WindowsStreamSecurityBindingElement)element;
267                                 else if (element is SslStreamSecurityBindingElement)
268                                         sslStreamElement = (SslStreamSecurityBindingElement)element;
269                                 else {
270                                         importer.AddWarning (
271                                                 "Found unknown binding element `{0}' while importing " +
272                                                 "binding `{1}'.", element.GetType (), custom.Name);
273                                         foundUnknownElement = true;
274                                 }
275                         }
276
277                         if (foundUnknownElement)
278                                 return false;
279
280                         if (transportElement == null) {
281                                 importer.AddWarning (
282                                         "Missing TcpTransportBindingElement while importing " +
283                                         "binding `{0}'.", custom.Name);
284                                 return false;
285                         }
286                         if (binaryElement == null) {
287                                 importer.AddWarning (
288                                         "Missing BinaryMessageEncodingBindingElement while importing " +
289                                         "binding `{0}'.", custom.Name);
290                                 return false;
291                         }
292
293                         if ((windowsStreamElement != null) && (sslStreamElement != null)) {
294                                 importer.AddWarning (
295                                         "Found both WindowsStreamSecurityBindingElement and " +
296                                         "SslStreamSecurityBindingElement while importing binding `{0}.",
297                                         custom.Name);
298                                 return false;
299                         }
300
301                         NetTcpSecurity security;
302                         if (windowsStreamElement != null) {
303                                 security = new NetTcpSecurity (SecurityMode.Transport);
304                                 security.Transport.ProtectionLevel = windowsStreamElement.ProtectionLevel;
305                         } else if (sslStreamElement != null) {
306                                 security = new NetTcpSecurity (SecurityMode.TransportWithMessageCredential);
307                         } else {
308                                 security = new NetTcpSecurity (SecurityMode.None);
309                         }
310
311                         var netTcp = new NetTcpBinding (transportElement, security, false);
312
313                         netTcp.Name = context.Endpoint.Binding.Name;
314                         netTcp.Namespace = context.Endpoint.Binding.Namespace;
315
316                         context.Endpoint.Binding = netTcp;
317                         return true;
318                 }
319
320                 #endregion
321         }
322 }
323