[System] Fixes SmtpClientTest.Deliver_Async
[mono.git] / mcs / class / System.Web.Services / System.Web.Services.Discovery / DiscoveryClientProtocol.cs
1 // 
2 // System.Web.Services.Protocols.DiscoveryClientProtocol.cs
3 //
4 // Author:
5 //   Dave Bettin (javabettin@yahoo.com)
6 //   Lluis Sanchez Gual (lluis@ximian.com)
7 //
8 // Copyright (C) Dave Bettin, 2002
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System.Collections;
33 using System.IO;
34 using System.Web.Services.Protocols;
35 using System.Xml;
36 using System.Xml.Schema;
37 using System.Xml.Serialization;
38 using System.Net;
39 using System.Text.RegularExpressions;
40 #if !MOBILE
41 using System.Web.Services.Description;
42 #endif
43
44 namespace System.Web.Services.Discovery {
45         public class DiscoveryClientProtocol : HttpWebClientProtocol {
46
47                 #region Fields
48
49                 private IList additionalInformation = new ArrayList ();
50                 private DiscoveryClientDocumentCollection documents = new DiscoveryClientDocumentCollection();
51                 private DiscoveryExceptionDictionary errors = new DiscoveryExceptionDictionary();
52                 private DiscoveryClientReferenceCollection references = new DiscoveryClientReferenceCollection();
53
54                 #endregion // Fields
55
56                 #region Constructors
57
58                 public DiscoveryClientProtocol () 
59                 {
60                 }
61                 
62                 #endregion // Constructors
63
64                 #region Properties
65
66                 public IList AdditionalInformation {
67                         get { return additionalInformation; }
68                 }
69                 
70                 public DiscoveryClientDocumentCollection Documents {
71                         get { return documents; }
72                 }
73                 
74                 public DiscoveryExceptionDictionary Errors {
75                         get { return errors; }
76                 }
77
78                 public DiscoveryClientReferenceCollection References {
79                         get { return references; }
80                 }               
81                 
82                 #endregion // Properties
83
84                 #region Methods
85
86                 public DiscoveryDocument Discover (string url)
87                 {
88                         Stream stream = Download (ref url);
89                         XmlTextReader reader = new XmlTextReader (url, stream);
90                         reader.XmlResolver = null;
91                         if (!DiscoveryDocument.CanRead (reader)) 
92                                 throw new InvalidOperationException ("The url '" + url + "' does not point to a valid discovery document");
93                                 
94                         DiscoveryDocument doc = DiscoveryDocument.Read (reader);
95                         reader.Close ();
96                         documents.Add (url, doc);
97                         AddDiscoReferences (doc);
98                         return doc;
99                 }
100
101                 public DiscoveryDocument DiscoverAny (string url)
102                 {
103                         try
104                         {
105                                 string contentType = null;
106                                 Stream stream = Download (ref url, ref contentType);
107         
108                                 if (contentType.IndexOf ("text/html") != -1)
109                                 {
110                                         // Look for an alternate url
111                                         
112                                         StreamReader sr = new StreamReader (stream);
113                                         string str = sr.ReadToEnd ();
114                                         
115                                         string rex = "link\\s*rel\\s*=\\s*[\"']?alternate[\"']?\\s*";
116                                         rex += "type\\s*=\\s*[\"']?text/xml[\"']?\\s*href\\s*=\\s*(?:\"(?<1>[^\"]*)\"|'(?<1>[^']*)'|(?<1>\\S+))";
117                                         Regex rob = new Regex (rex, RegexOptions.IgnoreCase);
118                                         Match m = rob.Match (str);
119                                         if (!m.Success) 
120                                                 throw new InvalidOperationException ("The HTML document does not contain Web service discovery information");
121                                         
122                                         if (url.StartsWith ("/"))
123                                         {
124                                                 Uri uri = new Uri (url);
125                                                 url = uri.GetLeftPart (UriPartial.Authority) + m.Groups[1];
126                                         }
127                                         else
128                                         {
129                                                 int i = url.LastIndexOf ('/');
130                                                 if (i == -1)
131                                                         throw new InvalidOperationException ("The HTML document does not contain Web service discovery information");
132
133                                                 Uri tmp = new Uri (url);
134                                                 tmp = new Uri (tmp, m.Groups [1].ToString ());
135                                                 url = tmp.ToString ();
136                                         }
137                                         stream = Download (ref url);
138                                 }
139                                 
140                                 XmlTextReader reader = new XmlTextReader (url, stream);
141                                 reader.XmlResolver = null;
142                                 reader.MoveToContent ();
143                                 DiscoveryDocument doc;
144                                 DiscoveryReference refe = null;
145                                 
146                                 if (DiscoveryDocument.CanRead (reader))
147                                 {
148                                         doc = DiscoveryDocument.Read (reader);
149                                         documents.Add (url, doc);
150                                         refe = new DiscoveryDocumentReference ();
151                                         AddDiscoReferences (doc);
152                                 }
153 #if !MOBILE
154                                 else if (ServiceDescription.CanRead (reader))
155                                 {
156                                         ServiceDescription wsdl = ServiceDescription.Read (reader);
157                                         documents.Add (url, wsdl);
158                                         doc = new DiscoveryDocument ();
159                                         refe = new ContractReference ();
160                                         doc.References.Add (refe);
161                                         refe.Url = url;
162                                         ((ContractReference)refe).ResolveInternal (this, wsdl);
163                                 }
164 #endif
165                                 else
166                                 {
167                                         XmlSchema schema = XmlSchema.Read (reader, null);
168                                         documents.Add (url, schema);
169                                         doc = new DiscoveryDocument ();
170                                         refe = new SchemaReference ();
171                                         refe.Url = url;
172                                         ((SchemaReference)refe).ResolveInternal (this, schema);
173                                         doc.References.Add (refe);
174                                 }
175                                 
176                                 refe.ClientProtocol = this;
177                                 refe.Url = url;
178                                 references.Add (url, refe);
179                                         
180                                 reader.Close ();
181                                 return doc;
182                         }
183                         catch (DiscoveryException ex) {
184                                 throw ex.Exception;
185                         }
186                 }
187                 
188                 void AddDiscoReferences (DiscoveryDocument doc)
189                 {
190                         foreach (DiscoveryReference re in doc.References)
191                         {
192                                 re.ClientProtocol = this;
193                                 references.Add (re.Url, re);
194                         }
195                         
196                         if (doc.AdditionalInfo != null) {
197                                 foreach (object info in doc.AdditionalInfo)
198                                         additionalInformation.Add (info);
199                         }
200                 }
201                 
202                 public Stream Download (ref string url)
203                 {
204                         string contentType = null;
205                         return Download (ref url, ref contentType);
206                 }
207                 
208                 public Stream Download (ref string url, ref string contentType)
209                 {
210                         if (url.StartsWith ("http://") || url.StartsWith ("https://"))
211                         {
212                                 WebRequest request = GetWebRequest (new Uri(url));
213                                 WebResponse resp = request.GetResponse ();
214                                 contentType = resp.ContentType;
215                                 return resp.GetResponseStream ();
216                         }
217                         else if (url.StartsWith ("file://"))
218                         {
219                                 WebRequest request = WebRequest.Create (new Uri (url));
220                                 WebResponse resp = request.GetResponse ();
221                                 contentType = resp.ContentType;
222                                 return resp.GetResponseStream ();
223                         }
224                         else
225                         {
226                                 string ext = Path.GetExtension (url).ToLower();
227                                 if (ext == ".wsdl" || ext == ".xsd")
228                                 {
229                                         contentType = "text/xml";
230                                         return new FileStream (url, FileMode.Open, FileAccess.Read);
231                                 }
232                                 else
233                                         throw new InvalidOperationException ("Unrecognized file type '" + url + "'. Extension must be one of .wsdl or .xsd");
234                         }
235                 }
236
237                 [System.Runtime.InteropServices.ComVisible (false)]
238                 [Obsolete ("This method will be removed from a future version. The method call is no longer required for resource discovery", false)]
239                 public void LoadExternals ()
240                 {
241                 }
242
243                 public DiscoveryClientResultCollection ReadAll (string topLevelFilename)
244                 {
245                         StreamReader sr = new StreamReader (topLevelFilename);
246                         XmlSerializer ser = new XmlSerializer (typeof (DiscoveryClientResultsFile));
247                         DiscoveryClientResultsFile resfile = (DiscoveryClientResultsFile) ser.Deserialize (sr);
248                         sr.Close ();
249                         
250                         string basePath = Path.GetDirectoryName (topLevelFilename);
251                         
252                         foreach (DiscoveryClientResult dcr in resfile.Results)
253                         {
254                                 Type type = Type.GetType (dcr.ReferenceTypeName);
255                                 DiscoveryReference dr = (DiscoveryReference) Activator.CreateInstance (type);
256                                 dr.Url = dcr.Url;
257                                 FileStream fs = new FileStream (Path.Combine (basePath, dcr.Filename), FileMode.Open, FileAccess.Read);
258                                 Documents.Add (dr.Url, dr.ReadDocument (fs));
259                                 fs.Close ();
260                                 References.Add (dr.Url, dr);
261                         }
262                         return resfile.Results;
263                 }
264
265                 public void ResolveAll ()
266                 {
267                         ArrayList list = new ArrayList (References.Values);
268                         foreach (DiscoveryReference re in list)
269                         {
270                                 try
271                                 {
272                                         if (re is DiscoveryDocumentReference)
273                                                 ((DiscoveryDocumentReference)re).ResolveAll ();
274                                         else
275                                                 re.Resolve ();
276                                 }
277                                 catch (DiscoveryException ex)
278                                 {
279                                         Errors [ex.Url] = ex.Exception; 
280                                 }
281                                 catch (Exception ex)
282                                 {
283                                         Errors [re.Url] = ex;   
284                                 }
285                         }
286                 }
287                 
288                 public void ResolveOneLevel ()
289                 {
290                         ArrayList list = new ArrayList (References.Values);
291                         foreach (DiscoveryReference re in list)
292                                 re.Resolve ();
293                 }
294                 
295                 public DiscoveryClientResultCollection WriteAll (string directory, string topLevelFilename)
296                 {
297                         DiscoveryClientResultsFile resfile = new DiscoveryClientResultsFile();
298                         
299                         foreach (DiscoveryReference re in References.Values)
300                         {
301                                 object doc = Documents [re.Url];
302                                 if (doc == null) continue;
303                                 
304                                 string fileName = FindValidName (resfile, re.DefaultFilename);
305                                 resfile.Results.Add (new DiscoveryClientResult (re.GetType(), re.Url, fileName));
306                                 
307                                 string filepath = Path.Combine (directory, fileName);
308                                 FileStream fs = new FileStream (filepath, FileMode.Create, FileAccess.Write);
309                                 re.WriteDocument (doc, fs);
310                                 fs.Close ();
311                         }
312                         
313                         StreamWriter sw = new StreamWriter (Path.Combine (directory, topLevelFilename));
314                         XmlSerializer ser = new XmlSerializer (typeof (DiscoveryClientResultsFile));
315                         ser.Serialize (sw, resfile);
316                         sw.Close ();
317                         return resfile.Results;
318                 }
319                 
320                 string FindValidName (DiscoveryClientResultsFile resfile, string baseName)
321                 {
322                         string name = baseName;
323                         int id = 0;
324                         bool found;
325                         do
326                         {
327                                 found = false;
328                                 foreach (DiscoveryClientResult res in resfile.Results)
329                                 {
330                                         if (name == res.Filename) {
331                                                 found = true; break;
332                                         }
333                                 }
334                                 if (found)
335                                         name = Path.GetFileNameWithoutExtension (baseName) + (++id) + Path.GetExtension (baseName);
336                         }
337                         while (found);
338                         
339                         return name;
340                 }
341                 
342                 #endregion // Methods
343                 
344                 #region Classes
345                 
346                 public sealed class DiscoveryClientResultsFile {
347                         
348                         #region Fields
349                         
350                         private DiscoveryClientResultCollection results;
351
352                         #endregion // Fields
353
354                         #region Contructors
355                         
356                         public DiscoveryClientResultsFile () 
357                         {
358                                 results = new DiscoveryClientResultCollection ();
359                         }
360                 
361                         #endregion // Constructors
362                         
363                         #region Properties
364                 
365                         public DiscoveryClientResultCollection Results {                                
366                                 get { return results; }
367                         }
368                         
369                         #endregion // Properties
370                 }
371                 
372                 #endregion // Classes
373         }
374                 
375         internal class DiscoveryException : Exception
376         {
377                 public string Url;
378                 public Exception Exception;
379                 
380                 public DiscoveryException (string url, Exception origin)
381                 {
382                         Url = url;
383                         Exception = origin;
384                 }
385         }
386 }