1 ///
2 /// MonoWSDL.cs -- a WSDL to proxy code generator.
3 ///
4 /// Author: Erik LeBel (eriklebel@yahoo.ca)
5 /// 		Lluis Sanchez (lluis@novell.com)
6 ///
7 /// Copyright (C) 2003, Erik LeBel,
8 ///
9 
10 
11 using System;
12 using System.Xml;
13 using System.Xml.Serialization;
14 using System.Xml.Schema;
15 using System.Collections;
16 using System.Collections.Specialized;
17 using System.CodeDom;
18 using System.CodeDom.Compiler;
19 using System.IO;
20 using System.Net;
21 using System.Web.Services.Description;
22 using System.Web.Services.Discovery;
23 using System.Web.Services;
24 
25 using Microsoft.CSharp;
26 
27 namespace Mono.WebServices
28 {
29 	public class Driver
30 	{
31 		string ProductId = "Web Services Description Language Utility\nMono Framework v" + Environment.Version;
32 		const string UsageMessage =
33 			"wsdl [options] {path | URL} {path | URL} ...\n\n"
34 			+ "   -d, -domain:domain           Domain of username for server authentication.\n"
35 			+ "   -l, -language:language       Language of generated code. Allowed CS (default)\n"
36 			+ "                                and VB. You can also specify the fully qualified\n"
37 			+ "                                name of a class that implements the\n"
38 			+ "                                System.CodeDom.Compiler.CodeDomProvider Class.\n"
39 			+ "   -n, -namespace:ns            The namespace of the generated code, default\n"
40 			+ "                                namespace if none.\n"
41 			+ "   -nologo                      Surpress the startup logo.\n"
42 			+ "   -o, -out:filename            The target file for generated code.\n"
43 			+ "   -p, -password:pwd            Password used to contact the server.\n"
44 			+ "   -protocol:protocol           Protocol to implement. Allowed: Soap (default),\n"
45 			+ "                                HttpGet or HttpPost.\n"
46 			+ "   -fields                      Generate fields instead of properties in data\n"
47 			+ "                                classes.\n"
48 			+ "   -server                      Generate server instead of client proxy code.\n"
49 			+ "   -u, -username:username       Username used to contact the server.\n"
50 			+ "   -proxy:url                   Address of the proxy.\n"
51 			+ "   -pu, -proxyusername:username Username used to contact the proxy.\n"
52 			+ "   -pp, -proxypassword:pwd      Password used to contact the proxy.\n"
53 			+ "   -pd, -proxydomain:domain     Domain of username for proxy authentication.\n"
54 			+ "   -urlkey, -appsettingurlkey:key Configuration key that contains the default\n"
55 			+ "                                url for the generated WS proxy.\n"
56 			+ "   -baseurl, -appsettingbaseurl:url Base url to use when constructing the\n"
57 			+ "                                service url.\n"
58 			+ "   -sample:[binding/]operation  Display a sample SOAP request and response.\n"
59 			+ "   -?                           Display this message\n"
60 			+ "\n"
61 			+ "Options can be of the forms  -option, --option or /option\n";
62 
63 		ArrayList descriptions = new ArrayList ();
64 		ArrayList schemas = new ArrayList ();
65 
66 		bool noLogo;
67 		bool help;
68 		string sampleSoap;
69 
70 		string proxyAddress;
71 		string proxyDomain;
72 		string proxyPassword;
73 		string proxyUsername;
74 		string username;
75 		string password;
76 		string domain;
77 
78 		string applicationSignature;
79 		string appSettingURLKey;
80 		string appSettingBaseURL;
81 		string language = "CS";
82 		string ns;
83 		string outFilename;
84 		string protocol = "Soap";
85 		ServiceDescriptionImportStyle style;
86 		CodeGenerationOptions options = CodeGenerationOptions.GenerateProperties | CodeGenerationOptions.GenerateNewAsync;
87 		bool verbose;
88 
89 		StringCollection urls = new StringCollection ();
90 
91 		///
92 		/// <summary>
93 		///	Application entry point.
94 		/// </summary>
95 		///
Main(string[] args)96 		public static int Main(string[] args)
97 		{
98 			Driver d = new Driver();
99 			return d.Run(args);
100 		}
101 
Driver()102 		Driver()
103 		{
104 			applicationSignature = ProductId;
105 		}
106 
Run(string[] args)107 		int Run (string[] args)
108 		{
109 			try
110 			{
111 				// parse command line arguments
112 				foreach (string argument in args)
113 					ImportArgument(argument);
114 
115 				if (noLogo == false)
116 					Console.WriteLine(ProductId);
117 
118 				if (help || urls.Count == 0)
119 				{
120 					Console.WriteLine(UsageMessage);
121 					return 0;
122 				}
123 
124 				CodeCompileUnit codeUnit = new CodeCompileUnit();
125 				CodeNamespace proxyCode = GetCodeNamespace();
126 				codeUnit.Namespaces.Add (proxyCode);
127 
128 				WebReferenceCollection references = new WebReferenceCollection ();
129 
130 				DiscoveryClientProtocol dcc = CreateClient ();
131 
132 				foreach (string murl in urls)
133 				{
134 
135 					string url = murl;
136 					if (!url.StartsWith ("http://") && !url.StartsWith ("https://") && !url.StartsWith ("file://"))
137 						url = new Uri (Path.GetFullPath (url)).ToString ();
138 
139 					dcc.DiscoverAny (url);
140 					dcc.ResolveAll ();
141 
142 				}
143 
144 				WebReference reference = new WebReference (dcc.Documents, proxyCode, protocol, appSettingURLKey, appSettingBaseURL);
145 				references.Add (reference);
146 
147 				if (sampleSoap != null)
148 					ConsoleSampleGenerator.Generate (descriptions, schemas, sampleSoap, protocol);
149 
150 				if (sampleSoap != null)
151 					return 0;
152 
153 				// generate the code
154 				GenerateCode (references, codeUnit);
155 				return 0;
156 			}
157 			catch (Exception exception)
158 			{
159 				Console.WriteLine("Error: {0}", exception.Message);
160 
161 				// Supress this except for when debug is enabled
162 				Console.WriteLine("Stack:\n {0}", exception.StackTrace);
163 				return 2;
164 			}
165 		}
166 
167 		///
168 		/// <summary>
169 		///	Generate code for the specified ServiceDescription.
170 		/// </summary>
171 		///
GenerateCode(WebReferenceCollection references, CodeCompileUnit codeUnit)172 		public bool GenerateCode (WebReferenceCollection references, CodeCompileUnit codeUnit)
173 		{
174 			bool hasWarnings = false;
175 
176 			CodeDomProvider provider = GetProvider();
177 
178 			StringCollection validationWarnings;
179 			WebReferenceOptions opts = new WebReferenceOptions ();
180 			opts.CodeGenerationOptions = options;
181 			opts.Style = style;
182 			opts.Verbose = verbose;
183 			validationWarnings = ServiceDescriptionImporter.GenerateWebReferences (references, provider, codeUnit, opts);
184 
185 			for (int n=0; n<references.Count; n++)
186 			{
187 				WebReference wr  = references [n];
188 
189 				BasicProfileViolationCollection violations = new BasicProfileViolationCollection ();
190 				if (String.Compare (protocol, "SOAP", StringComparison.OrdinalIgnoreCase) == 0 && !WebServicesInteroperability.CheckConformance (WsiProfiles.BasicProfile1_1, wr, violations)) {
191 					wr.Warnings |= ServiceDescriptionImportWarnings.WsiConformance;
192 				}
193 
194 				if (wr.Warnings != 0)
195 				{
196 					if (!hasWarnings) {
197 						WriteText ("", 0, 0);
198 						WriteText ("There were some warnings while generating the code:", 0, 0);
199 					}
200 
201 					WriteText ("", 0, 0);
202 					WriteText (urls[n], 2, 2);
203 
204 					if ((wr.Warnings & ServiceDescriptionImportWarnings.WsiConformance) > 0) {
205 						WriteText ("- This web reference does not conform to WS-I Basic Profile v1.1", 4, 6);
206 						foreach (BasicProfileViolation vio in violations) {
207 							WriteText (vio.NormativeStatement + ": " + vio.Details, 8, 8);
208 							foreach (string ele in vio.Elements)
209 								WriteText ("* " + ele, 10, 12);
210 						}
211 					}
212 
213 					if ((wr.Warnings & ServiceDescriptionImportWarnings.NoCodeGenerated) > 0)
214 						WriteText ("- WARNING: No proxy class was generated", 4, 6);
215 					if ((wr.Warnings & ServiceDescriptionImportWarnings.NoMethodsGenerated) > 0)
216 						WriteText ("- WARNING: The proxy class generated includes no methods", 4, 6);
217 					if ((wr.Warnings & ServiceDescriptionImportWarnings.OptionalExtensionsIgnored) > 0)
218 						WriteText ("- WARNING: At least one optional extension has been ignored", 4, 6);
219 					if ((wr.Warnings & ServiceDescriptionImportWarnings.RequiredExtensionsIgnored) > 0)
220 						WriteText ("- WARNING: At least one necessary extension has been ignored", 4, 6);
221 					if ((wr.Warnings & ServiceDescriptionImportWarnings.UnsupportedBindingsIgnored) > 0)
222 						WriteText ("- WARNING: At least one binding is of an unsupported type and has been ignored", 4, 6);
223 					if ((wr.Warnings & ServiceDescriptionImportWarnings.UnsupportedOperationsIgnored) > 0)
224 						WriteText ("- WARNING: At least one operation is of an unsupported type and has been ignored", 4, 6);
225 
226 					hasWarnings = true;
227 				}
228 			}
229 
230 			if (hasWarnings) WriteText ("",0,0);
231 
232 			string filename = outFilename;
233 			bool hasBindings = false;
234 
235 			foreach (object doc in references[0].Documents.Values)
236 			{
237 				ServiceDescription desc = doc as ServiceDescription;
238 				if (desc == null) continue;
239 
240 				if (desc.Services.Count > 0 && filename == null)
241 					filename = desc.Services[0].Name + "." + provider.FileExtension;
242 
243 				if (desc.Bindings.Count > 0 || desc.Services.Count > 0)
244 					hasBindings = true;
245 			}
246 
247 			if (filename == null)
248 				filename = "output." + provider.FileExtension;
249 
250 			if (hasBindings) {
251 				WriteText ("Writing file '" + filename + "'", 0, 0);
252 				StreamWriter writer = new StreamWriter(filename);
253 
254 				CodeGeneratorOptions compilerOptions = new CodeGeneratorOptions();
255 				provider.GenerateCodeFromCompileUnit (codeUnit, writer, compilerOptions);
256 				writer.Close();
257 			}
258 
259 			return hasWarnings;
260 		}
261 
262 		///
263 		/// <summary>
264 		///	Create the CodeNamespace with the generator's signature commented in.
265 		/// </summary>
266 		///
GetCodeNamespace()267 		CodeNamespace GetCodeNamespace()
268 		{
269 			CodeNamespace codeNamespace = new CodeNamespace(ns);
270 
271 			if (applicationSignature != null)
272 			{
273 				codeNamespace.Comments.Add(new CodeCommentStatement("\n This source code was auto-generated by " + applicationSignature + "\n"));
274 			}
275 
276 			return codeNamespace;
277 		}
278 
279 		///
280 		/// <summary/>
281 		///
WriteCodeUnit(CodeCompileUnit codeUnit, string serviceName)282 		void WriteCodeUnit(CodeCompileUnit codeUnit, string serviceName)
283 		{
284 			CodeDomProvider provider = GetProvider();
285 			ICodeGenerator generator = provider.CreateGenerator();
286 			CodeGeneratorOptions options = new CodeGeneratorOptions();
287 
288 			string filename;
289 			if (outFilename != null)
290 				filename = outFilename;
291 			else
292 				filename = serviceName	+ "." + provider.FileExtension;
293 
294 			Console.WriteLine ("Writing file '{0}'", filename);
295 			StreamWriter writer = new StreamWriter(filename);
296 			generator.GenerateCodeFromCompileUnit(codeUnit, writer, options);
297 			writer.Close();
298 		}
299 
300 		///
301 		/// <summary>
302 		///	Fetch the Code Provider for the language specified by the 'language' members.
303 		/// </summary>
304 		///
GetProvider()305 		private CodeDomProvider GetProvider()
306 		{
307 			CodeDomProvider provider;
308 			Type type;
309 
310 			switch (language.ToUpper ()) {
311 			case "CS":
312 				provider = new CSharpCodeProvider ();
313 				break;
314 			case "VB":
315 				provider = new Microsoft.VisualBasic.VBCodeProvider ();
316 				break;
317 			case "BOO":
318 				type = Type.GetType("Boo.Lang.CodeDom.BooCodeProvider, Boo.Lang.CodeDom, Version=1.0.0.0, Culture=neutral, PublicKeyToken=32c39770e9a21a67");
319 				if (type != null){
320 					return (CodeDomProvider) Activator.CreateInstance (type);
321 				}
322 				throw new Exception ("Boo.Lang.CodeDom.BooCodeProvider not available");
323 
324 			default:
325 				type = Type.GetType(language);
326 				if (type != null) {
327 					return (CodeDomProvider) Activator.CreateInstance (type);
328 				}
329 				throw new Exception ("Unknown language");
330 			}
331 			return provider;
332 		}
333 
334 
335 
336 		///
337 		/// <summary>
338 		///	Interperet the command-line arguments and configure the relavent components.
339 		/// </summary>
340 		///
ImportArgument(string argument)341 		void ImportArgument(string argument)
342 		{
343 			string optionValuePair;
344 
345 			if (argument.StartsWith("--"))
346 			{
347 				optionValuePair = argument.Substring(2);
348 			}
349 			else if (argument.StartsWith("/") || argument.StartsWith("-"))
350 			{
351 				optionValuePair = argument.Substring(1);
352 			}
353 			else
354 			{
355 				urls.Add (argument);
356 				return;
357 			}
358 
359 			string option;
360 			string value;
361 
362 			int indexOfEquals = optionValuePair.IndexOf(':');
363 			if (indexOfEquals > 0)
364 			{
365 				option = optionValuePair.Substring(0, indexOfEquals);
366 				value = optionValuePair.Substring(indexOfEquals + 1);
367 			}
368 			else
369 			{
370 				option = optionValuePair;
371 				value = null;
372 			}
373 
374 			switch (option)
375 			{
376 				case "appsettingurlkey":
377 				case "urlkey":
378 				    appSettingURLKey = value;
379 				    break;
380 
381 				case "appsettingbaseurl":
382 				case "baseurl":
383 				    appSettingBaseURL = value;
384 				    break;
385 
386 				case "d":
387 				case "domain":
388 				    domain = value;
389 				    break;
390 
391 				case "l":
392 				case "language":
393 				    language = value;
394 				    break;
395 
396 				case "n":
397 				case "namespace":
398 				    ns = value;
399 				    break;
400 
401 				case "nologo":
402 				    noLogo = true;
403 				    break;
404 
405 				case "o":
406 				case "out":
407 				    outFilename = value;
408 				    break;
409 
410 				case "p":
411 				case "password":
412 				    password = value;
413 				    break;
414 
415 				case "protocol":
416 				    protocol = value;
417 				    break;
418 
419 				case "proxy":
420 				    proxyAddress = value;
421 				    break;
422 
423 				case "proxydomain":
424 				case "pd":
425 				    proxyDomain = value;
426 				    break;
427 
428 				case "proxypassword":
429 				case "pp":
430 				    proxyPassword = value;
431 				    break;
432 
433 				case "proxyusername":
434 				case "pu":
435 				    proxyUsername = value;
436 				    break;
437 
438 				case "server":
439 				    style = ServiceDescriptionImportStyle.Server;
440 				    break;
441 
442 				case "u":
443 				case "username":
444 				    username = value;
445 				    break;
446 
447 				case "verbose":
448 					verbose = true;
449 					break;
450 
451 				case "fields":
452 					options &= ~CodeGenerationOptions.GenerateProperties;
453 					break;
454 
455 				case "sample":
456 					sampleSoap = value;
457 					break;
458 
459 				case "?":
460 				    help = true;
461 				    break;
462 
463 				default:
464 					if (argument.StartsWith ("/") && argument.IndexOfAny (Path.InvalidPathChars) == -1) {
465 						urls.Add (argument);
466 						break;
467 					}
468 					else
469 					    throw new Exception("Unknown option " + option);
470 			}
471 		}
472 
CreateClient()473 		DiscoveryClientProtocol CreateClient ()
474 		{
475 			DiscoveryClientProtocol dcc = new DiscoveryClientProtocol ();
476 
477 			if (username != null || password != null || domain != null)
478 			{
479 				NetworkCredential credentials = new NetworkCredential();
480 
481 				if (username != null)
482 					credentials.UserName = username;
483 
484 				if (password != null)
485 					credentials.Password = password;
486 
487 				if (domain != null)
488 					credentials.Domain = domain;
489 
490 				dcc.Credentials = credentials;
491 			}
492 
493 			if (proxyAddress != null)
494 			{
495 				WebProxy proxy = new WebProxy (proxyAddress);
496 				if (proxyUsername != null || proxyPassword != null || proxyDomain != null)
497 				{
498 					NetworkCredential credentials = new NetworkCredential();
499 
500 					if (proxyUsername != null)
501 						credentials.UserName = proxyUsername;
502 
503 					if (proxyPassword != null)
504 						credentials.Password = proxyPassword;
505 
506 					if (proxyDomain != null)
507 						credentials.Domain = proxyDomain;
508 
509 					proxy.Credentials = credentials;
510 				}
511 			}
512 
513 			return dcc;
514 		}
515 
WriteText(string text, int initialLeftMargin, int leftMargin)516 		static void WriteText (string text, int initialLeftMargin, int leftMargin)
517 		{
518 			int n = 0;
519 			int margin = initialLeftMargin;
520 			int maxCols = 80;
521 
522 			if (text == "") {
523 				Console.WriteLine ();
524 				return;
525 			}
526 
527 			while (n < text.Length)
528 			{
529 				int col = margin;
530 				int lastWhite = -1;
531 				int sn = n;
532 				while (col < maxCols && n < text.Length) {
533 					if (char.IsWhiteSpace (text[n]))
534 						lastWhite = n;
535 					col++;
536 					n++;
537 				}
538 
539 				if (lastWhite == -1 || col < maxCols)
540 					lastWhite = n;
541 				else if (col >= maxCols)
542 					n = lastWhite + 1;
543 
544 				Console.WriteLine (new String (' ', margin) + text.Substring (sn, lastWhite - sn));
545 				margin = leftMargin;
546 			}
547 		}
548 	}
549 }
550 
551