Skip to content
Newer
Older
100644 810 lines (702 sloc) 30.5 KB
08cee94 @kalski added soap12 capability
kalski authored
1 %%%-------------------------------------------------------------------
2 %%% Created : 29 Nov 2006 by Torbjorn Tornkvist <tobbe@tornkvist.org>
3 %%% Author : Willem de Jong (w.a.de.jong@gmail.com).
4 %%% Desc. : Common SOAP code.
5 %%%-------------------------------------------------------------------
6
7 %%% modified (WdJ, May 2007): deal with imports in the WSDL.
8 %%% modified (WdJ, August 2007): the WSDL can contain more than 1 schema
9 %%% copied from yaws_soap_lib (Kaloyan Dimitrov, February 2012): to be used for soap12 calls
10
11 -module(yaws_soap12_lib).
12
13 -export([initModel/1, initModel/2,
14 initModelFile/1,
15 config_file_xsd/0,
16 call/3, call/4, call/5, call/6,
17 call_attach/4, call_attach/5, call_attach/8,
18 write_hrl/2, write_hrl/3,
19 findHeader/2,
20 parseMessage/2,
21 makeFault/2,
22 is_wsdl/1, wsdl_model/1, wsdl_op_service/1,
23 wsdl_op_port/1, wsdl_op_operation/1,
24 wsdl_op_binding/1, wsdl_op_address/1,
25 wsdl_op_action/1, wsdl_operations/1,
26 get_operation/2
27 ]).
28
29
30 %%% For testing...
31 -export([qtest/0]).
32
33
34 -include("../include/yaws_soap.hrl").
35 -include("../include/soap-envelope.hrl").
36 -include("../include/wsdl11soap12.hrl").
37
38 -define(HTTP_REQ_TIMEOUT, 20000).
39
40 %%-define(dbg(X,Y),
41 %% error_logger:info_msg("*dbg ~p(~p): " X,
42 %% [?MODULE, ?LINE | Y])).
43 -define(dbg(X,Y), true).
44
45
46 -record(yaws_soap_config, {atts, xsd_path, user_module, wsdl_file, add_files}).
47 -record(xsd_file, {atts, name, prefix, import_specs}).
48 -record(import_specs, {atts, namespace, prefix, location}).
49
50 -define(DefaultPrefix, "p").
51
52
53 %%%
54 %%% Writes the header file (record definitions) for a WSDL file
55 %%%
56 write_hrl(WsdlURL, Output) when is_list(WsdlURL) ->
57 write_hrl(initModel(WsdlURL), Output);
58 write_hrl(#wsdl{model = Model}, Output) when is_list(Output) ->
59 erlsom:write_hrl(Model, Output).
60
61 write_hrl(WsdlURL, Output, PrefixOrOptions)
62 when is_list(WsdlURL),is_list(PrefixOrOptions) ->
63 write_hrl(initModel(WsdlURL, PrefixOrOptions), Output).
64
65
66
67 %%% For testing only...
68 qtest() ->
69 call("http://www.webservicex.net/WeatherForecast.asmx?WSDL",
70 "GetWeatherByPlaceName",
71 ["Boston"]).
72
73 %%% --------------------------------------------------------------------
74 %%% Access functions
75 %%% --------------------------------------------------------------------
76 is_wsdl(Wsdl) when is_record(Wsdl,wsdl) -> true;
77 is_wsdl(_) -> false.
78
79 wsdl_operations(#wsdl{operations = Ops}) -> Ops.
80
81 wsdl_model(#wsdl{model = Model}) -> Model.
82
83 wsdl_op_service(#operation{service = Service}) -> Service.
84
85 wsdl_op_port(#operation{port = Port}) -> Port.
86
87 wsdl_op_operation(#operation{operation = Op}) -> Op.
88
89 wsdl_op_binding(#operation{binding = Binding}) -> Binding.
90
91 wsdl_op_address(#operation{address = Address}) -> Address.
92
93 wsdl_op_action(#operation{action = Action}) -> Action.
94
95
96 %%% --------------------------------------------------------------------
97 %%% For Quick deployment
98 %%% --------------------------------------------------------------------
99 call(WsdlURL, Operation, ListOfData) when is_list(WsdlURL) ->
100 Wsdl = initModel(WsdlURL, ?DefaultPrefix),
101 call(Wsdl, Operation, ListOfData);
102 call(Wsdl, Operation, ListOfData) when is_record(Wsdl, wsdl) ->
103 case get_operation(Wsdl#wsdl.operations, Operation) of
104 {ok, Op} ->
105 Msg = mk_msg(?DefaultPrefix, Operation, ListOfData),
106 call(Wsdl, Operation, Op#operation.port,
107 Op#operation.service, [], Msg);
108 Else ->
109 Else
110 end.
111
112 %%% --------------------------------------------------------------------
113 %%% Takes http headers
114 %%% --------------------------------------------------------------------
115 call(WsdlURL, Operation, ListOfData, http_headers, HttpHeaders) when is_list(WsdlURL) ->
116 Wsdl = initModel(WsdlURL, ?DefaultPrefix),
117 call(Wsdl, Operation, ListOfData, http_headers, HttpHeaders);
118 call(Wsdl, Operation, ListOfData, http_headers, HttpHeaders) when is_record(Wsdl, wsdl) ->
119 case get_operation(Wsdl#wsdl.operations, Operation) of
120 {ok, Op} ->
121 Msg = mk_msg(?DefaultPrefix, Operation, ListOfData),
122 call(Wsdl, Operation, Op#operation.port,
123 Op#operation.service, [], Msg, http_headers, HttpHeaders);
124 Else ->
125 Else
126 end;
127
128 %%% --------------------------------------------------------------------
129 %%% With additional specified prefix
130 %%% --------------------------------------------------------------------
131 call(WsdlURL, Operation, ListOfData, prefix, Prefix) when is_list(WsdlURL) ->
132 Wsdl = initModel(WsdlURL, Prefix),
133 call(Wsdl, Operation, ListOfData, prefix, Prefix );
134 call(Wsdl, Operation, ListOfData, prefix, Prefix) when is_record(Wsdl, wsdl) ->
135 case get_operation(Wsdl#wsdl.operations, Operation) of
136 {ok, Op} ->
137 Msg = mk_msg(Prefix, Operation, ListOfData),
138 call(Wsdl, Operation, Op#operation.port,
139 Op#operation.service, [], Msg);
140 Else ->
141 Else
142 end.
143
144
145 %%% --------------------------------------------------------------------
146 %%% Takes the actual records for the Header and Body message.
147 %%% --------------------------------------------------------------------
148 call(WsdlURL, Operation, Header, Msg) when is_list(WsdlURL) ->
149 Wsdl = initModel(WsdlURL, ?DefaultPrefix),
150 call(Wsdl, Operation, Header, Msg);
151 call(Wsdl, Operation, Header, Msg) when is_record(Wsdl, wsdl) ->
152 case get_operation(Wsdl#wsdl.operations, Operation) of
153 {ok, Op} ->
154 call(Wsdl, Operation, Op#operation.port, Op#operation.service,
155 Header, Msg);
156 Else ->
157 Else
158 end.
159
160
161 mk_msg(_Prefix, _Operation, ListOfData) ->
162 ListOfData. % rest of record data
163
164 get_operation([#operation{operation = X} = Op|_], X) ->
165 {ok, Op};
166 get_operation([_|T], Op) ->
167 get_operation(T, Op);
168 get_operation([], _Op) ->
169 {error, "operation not found"}.
170
171
172 %%% --------------------------------------------------------------------
173 %%% Make a SOAP request (no attachments)
174 %%% --------------------------------------------------------------------
175 call(Wsdl, Operation, Port, Service, Headers, Message) ->
176 call_attach(Wsdl, Operation, Port, Service, Headers, Message, [], []).
177
178 %%% --------------------------------------------------------------------
179 %%% Make a SOAP request (with http headers)
180 %%% --------------------------------------------------------------------
181 call(Wsdl, Operation, Port, Service, Headers, Message, http_headers, HttpHeaders) ->
182 call_attach(Wsdl, Operation, Port, Service, Headers, Message, [], HttpHeaders).
183
184
185 %%% --------------------------------------------------------------------
186 %%% For Quick deployment (with attachments)
187 %%% --------------------------------------------------------------------
188 call_attach(WsdlURL, Operation, ListOfData, Attachments)
189 when is_list(WsdlURL) ->
190 Wsdl = initModel(WsdlURL, ?DefaultPrefix),
191 call_attach(Wsdl, Operation, ListOfData, Attachments);
192 call_attach(Wsdl, Operation, ListOfData, Attachments)
193 when is_record(Wsdl, wsdl) ->
194 case get_operation(Wsdl#wsdl.operations, Operation) of
195 {ok, Op} ->
196 Msg = mk_msg(?DefaultPrefix, Operation, ListOfData),
197 call_attach(Wsdl, Operation, Op#operation.port,
198 Op#operation.service, [], Msg, Attachments, []);
199 Else ->
200 Else
201 end.
202
203 %%% --------------------------------------------------------------------
204 %%% Takes the actual records for the Header and Body message
205 %%% (with attachments)
206 %%% --------------------------------------------------------------------
207 call_attach(WsdlURL, Operation, Header, Msg, Attachments)
208 when is_list(WsdlURL) ->
209 Wsdl = initModel(WsdlURL, ?DefaultPrefix),
210 call_attach(Wsdl, Operation, Header, Msg, Attachments);
211 call_attach(Wsdl, Operation, Header, Msg, Attachments)
212 when is_record(Wsdl, wsdl) ->
213 case get_operation(Wsdl#wsdl.operations, Operation) of
214 {ok, Op} ->
215 call_attach(Wsdl, Operation, Op#operation.port,
216 Op#operation.service,
217 Header, Msg, Attachments, []);
218 Else ->
219 Else
220 end.
221
222
223 %%% --------------------------------------------------------------------
224 %%% Make a SOAP request (with attachments)
225 %%% --------------------------------------------------------------------
226 call_attach(#wsdl{operations = Operations, model = Model},
227 Operation, Port, Service, Headers, Message, Attachments, HttpHeaders) ->
228 %% find the operation
229 case findOperation(Operation, Port, Service, Operations) of
230 #operation{address = URL, action=Action, operation = Operation} ->
231 %% Add the Soap envelope
232 Envelope = mk_envelope(Message, Headers),
233 %% Encode the message
234 case erlsom:write(Envelope, Model) of
235 {ok, XmlMessage} ->
236
237 {ContentType, Request} =
238 make_request_body(XmlMessage, Attachments, Operation),
239 HttpClientOptions = [],
240 ?dbg("+++ Request = ~p~n", [Request]),
241 HttpRes = http_request(URL, Action, Request,
242 HttpClientOptions, HttpHeaders,
243 ContentType),
244 ?dbg("+++ HttpRes = ~p~n", [HttpRes]),
245 case HttpRes of
246 {ok, _Code, _ReturnHeaders, Body} ->
247 parseMessage(Body, Model);
248 Error ->
249 %% in case of HTTP error: return
250 %% {error, description}
251 Error
252 end;
253 {error, EncodingError} ->
254 {error, {encoding_error, EncodingError}}
255 end;
256 false ->
257 {error, {unknown_operation, Operation}}
258 end.
259
260 %%%
261 %%% returns {ok, Header, Body} | {error, Error}
262 %%%
263 parseMessage(Message, #wsdl{model = Model}) ->
264 parseMessage(Message, Model);
265 %%
266 parseMessage(Message, Model) ->
267 Parsed = erlsom:scan(Message, Model),
268 case Parsed of
269 {ok, #'soap:Envelope'{'Body' = #'soap:Body'{choice = Body},
270 'Header' = undefined}, _} ->
271 {ok, undefined, Body};
272 {ok, #'soap:Envelope'{'Body' = #'soap:Body'{choice = Body},
273 'Header' = #'soap:Header'{choice = Header}}, _} ->
274 {ok, Header, Body};
275 {error, ErrorMessage} ->
276 {error, {decoding, ErrorMessage}}
277 end.
278
279
280 findOperation(_Operation, _Port, _Service, []) ->
281 false;
282 findOperation(Operation, Port, Service,
283 [Op = #operation{operation = Operation,
284 port = Port, service = Service} | _]) ->
285 Op;
286 findOperation(Operation, Port, Service, [#operation{} | Tail]) ->
287 findOperation(Operation, Port, Service, Tail).
288
289
290 mk_envelope(M, H) when is_tuple(M) -> mk_envelope([M], H);
291 mk_envelope(M, H) when is_tuple(H) -> mk_envelope(M, [H]);
292 %%
293 mk_envelope(Messages, []) when is_list(Messages) ->
294 #'soap:Envelope'{'Body' = #'soap:Body'{choice = Messages}};
295 mk_envelope(Messages, Headers) when is_list(Messages),is_list(Headers) ->
296 #'soap:Envelope'{'Body' = #'soap:Body'{choice = Messages},
297 'Header' = #'soap:Header'{choice = Headers}}.
298
299 %%% --------------------------------------------------------------------
300 %%% Parse a WSDL file and return a 'Model'
301 %%% --------------------------------------------------------------------
302 initModel(WsdlFile) ->
303 initModel(WsdlFile, ?DefaultPrefix).
304
305 %% PrefixOrOptions can be a property list that contains the options
306 %% for Erlsom, or a String. If it is a string, this is used as the
307 %% Erlsom 'prefix' option (and the other options are left unspecified).
308 initModel(WsdlFile, PrefixOrOptions) ->
309 Options = case is_string(PrefixOrOptions) of
310 no ->
311 %% It is an option list
312 %% Add the default prefix at the end - it will only be used
313 %% if no other prefix is specified
314 PrefixOrOptions ++ [{prefix, ?DefaultPrefix}];
315 _ ->
316 %% just the prefix
317 [{prefix, PrefixOrOptions}]
318 end,
319 PrivDir = priv_dir(),
320 initModel2(WsdlFile, Options, PrivDir, undefined, undefined).
321
322 initModelFile(ConfigFile) ->
323 {ok, ConfigSchema} = erlsom:compile_xsd(config_file_xsd()),
324 %% read (parse) the config file
325 {ok, Config, _} = erlsom:scan_file(ConfigFile, ConfigSchema),
326 #yaws_soap_config{xsd_path = XsdPath,
327 wsdl_file = Wsdl,
328 add_files = AddFiles} = Config,
329 #xsd_file{name = WsdlFile, prefix = Prefix, import_specs = Import} = Wsdl,
330 initModel2(WsdlFile, [{prefix, Prefix}], XsdPath, Import, AddFiles).
331
332 priv_dir() ->
333 case code:priv_dir(yaws) of
334 {error, bad_name} ->
335 filename:join([filename:dirname(code:which(yaws)),"..", "priv"]);
336 A ->
337 A
338 end.
339
340 initModel2(WsdlFile, ErlsomOptions, Path, Import, AddFiles) ->
341 WsdlName = filename:join([Path, "wsdl.xsd"]),
342 IncludeWsdl = {"http://schemas.xmlsoap.org/wsdl/", "wsdl", WsdlName},
343 {ok, WsdlModel} = erlsom:compile_xsd_file(
344 filename:join([Path, "wsdl11soap12.xsd"]),
345 [{prefix, "soap"},
346 {include_files, [IncludeWsdl]}]),
347 %% uncomment to generate the wsdl11soap12.hrl file
348 %% erlsom:write_hrl(WsdlModel, "/home/kalski/test/wsdl11soap12.hrl"),
349 %% add the xsd model (since xsd is also used in the wsdl)
350 WsdlModel2 = erlsom:add_xsd_model(WsdlModel),
351 Options = ErlsomOptions ++ makeOptions(Import),
352 %% parse Wsdl
353 {Model, Operations} = parseWsdls([WsdlFile], WsdlModel2,
354 Options, {undefined, []}),
355 %% TODO: add files as required
356 %% now compile envelope.xsd, and add Model
357 {ok, EnvelopeModel} = erlsom:compile_xsd_file(
358 filename:join([Path, "soap-envelope.xsd"]),
359 [{prefix, "soap"},
360 {include_files, [{"http://www.w3.org/XML/1998/namespace", undefined, filename:join([Path, "xml.xsd"])}]}]),
361 SoapModel = erlsom:add_model(EnvelopeModel, Model),
362 %% uncomment to generate the soap-envelope.hrl file
363 %% erlsom:write_hrl(EnvelopeModel, "/home/kalski/test/soap-envelope.hrl"),
364 SoapModel2 = addModels(AddFiles, SoapModel),
365 #wsdl{operations = Operations, model = SoapModel2}.
366
367
368 %%% --------------------------------------------------------------------
369 %%% Parse a list of WSDLs and import (recursively)
370 %%% Returns {Model, Operations}
371 %%% --------------------------------------------------------------------
372 parseWsdls([], _WsdlModel, _Options, Acc) ->
373 Acc;
374 parseWsdls([WsdlFile | Tail], WsdlModel, Options, {AccModel, AccOperations}) ->
375 WsdlFileNoSpaces = rmsp(WsdlFile),
376 {ok, WsdlFileContent} = get_url_file(WsdlFileNoSpaces),
377 {ok, ParsedWsdl, _} = erlsom:scan(WsdlFileContent, WsdlModel),
378 %% get the xsd elements from this model, and hand it over to erlsom_compile.
379 Xsds = getXsdsFromWsdl(ParsedWsdl),
380 %% Now we need to build a list: [{Namespace, Xsd, Prefix}, ...] for
381 %% all the Xsds in the WSDL.
382 %% This list is used when a schema includes one of the other schemas.
383 %% The AXIS java2wsdl tool generates wsdls that depend on this feature.
384 ImportList = makeImportList(Xsds, []),
385 Model2 = addSchemas(Xsds, AccModel, Options, ImportList),
386 Ports = getPorts(ParsedWsdl),
387 Operations = getOperations(ParsedWsdl, Ports),
388 Imports = getImports(filename:dirname(WsdlFileNoSpaces), ParsedWsdl),
389 Model3 = addSchemaFiles(Imports, Model2, Options, []),
390 Acc2 = {Model3, Operations ++ AccOperations},
391 %% process imports (recursively, so that imports in the imported files are
392 %% processed as well).
393 %% For the moment, the namespace is ignored on operations etc.
394 %% this makes it a bit easier to deal with imported wsdl's.
395 %% TODO uncomment if imports can be WSDL
396 %%Acc3 = parseWsdls(Imports, WsdlModel, Options, Acc2),
397 parseWsdls(Tail, WsdlModel, Options, Acc2).
398
399 %%% --------------------------------------------------------------------
400 %%% build a list: [{Namespace, Xsd}, ...] for all the Xsds in the WSDL.
401 %%% This list is used when a schema inlcudes one of the other schemas.
402 %%% The AXIS java2wsdl tool generates wsdls that depend on this feature.
403 makeImportList([], Acc) ->
404 Acc;
405 makeImportList([ Xsd | Tail], Acc) ->
406 makeImportList(Tail, [{erlsom_lib:getTargetNamespaceFromXsd(Xsd),
407 undefined, Xsd} | Acc]).
408
409
410 %%% --------------------------------------------------------------------
411 %%% compile each of the schemas, and add it to the model.
412 %%% Returns Model
413 %%% (TODO: using the same prefix for all XSDS makes no sense)
414 %%% --------------------------------------------------------------------
415 addSchemas([], AccModel, _Options, _ImportList) ->
416 AccModel;
417 addSchemas([Xsd| Tail], AccModel, Options, ImportList) ->
418 Model2 = case Xsd of
419 undefined ->
420 AccModel;
421 _ ->
422 {ok, Model} =
423 erlsom_compile:compile_parsed_xsd(
424 Xsd,
425 [{include_files, ImportList} |Options]),
426 case AccModel of
427 undefined -> Model;
428 _ -> erlsom:add_model(AccModel, Model)
429 end
430 end,
431 addSchemas(Tail, Model2, Options, ImportList).
432
433 %%% --------------------------------------------------------------------
434 %%% compile each of the schema files, and add it to the model.
435 %%% Returns Model
436 %%% (TODO: using the same prefix for all XSD files makes no sense)
437 %%% --------------------------------------------------------------------
438 addSchemaFiles([], AccModel, _Options, _ImportList) ->
439 AccModel;
440 addSchemaFiles([Xsd| Tail], AccModel, Options, ImportList) ->
441 Model2 = case Xsd of
442 undefined ->
443 AccModel;
444 _ ->
445 {ok, Model} =
446 erlsom:compile_xsd_file(
447 get_file_with_path(Xsd),
448 [{include_files, ImportList} |Options]),
449 case AccModel of
450 undefined -> Model;
451 _ -> erlsom:add_model(AccModel, Model)
452 end
453 end,
454 addSchemaFiles(Tail, Model2, Options, ImportList).
455
456 %%% --------------------------------------------------------------------
457 %%% Get a file from an URL spec.
458 %%% --------------------------------------------------------------------
459 get_url_file("http://"++_ = URL) ->
460 case httpc:request(URL) of
461 {ok,{{_HTTP,200,_OK}, _Headers, Body}} ->
462 {ok, Body};
463 {ok,{{_HTTP,RC,Emsg}, _Headers, _Body}} ->
464 error_logger:error_msg("~p: http-request got: ~p~n",
465 [?MODULE, {RC, Emsg}]),
466 {error, "failed to retrieve: "++URL};
467 {error, Reason} ->
468 error_logger:error_msg("~p: http-request failed: ~p~n",
469 [?MODULE, Reason]),
470 {error, "failed to retrieve: "++URL}
471 end;
472 get_url_file("file://"++Fname) ->
473 {ok, Bin} = file:read_file(Fname),
474 {ok, binary_to_list(Bin)};
475 %% added this, since this is what is used in many WSDLs (i.e.: just a filename).
476 get_url_file(Fname) ->
477 {ok, Bin} = file:read_file(Fname),
478 {ok, binary_to_list(Bin)}.
479
480
481 %%% --------------------------------------------------------------------
482 %%% Make a HTTP Request
483 %%% --------------------------------------------------------------------
484 http_request(URL, Action, Request, Options, Headers, ContentType) ->
485 case code:ensure_loaded(ibrowse) of
486 {module, ibrowse} ->
487 %% If ibrowse exist in the path then let's use it...
488 ibrowse_request(URL, Action, Request, Options,
489 Headers, ContentType);
490 _ ->
491 %% ...otherwise, let's use the OTP http client.
492 inets_request(URL, Action, Request, Options,
493 Headers, ContentType)
494 end.
495
496 inets_request(URL, Action, Request, Options, Headers, ContentType) ->
497 case Action of
498 undefined ->
499 NHeaders = Headers;
500 _ ->
501 NHeaders = [{"SOAPAction", Action} | Headers]
502 end,
503 NewHeaders = case proplists:get_value("Host", NHeaders) of
504 undefined ->
505 [{"Host", "localhost:8800"}|NHeaders];
506 _ ->
507 NHeaders
508 end,
509 NewOptions = [{cookies, enabled}|Options],
510 httpc:set_options(NewOptions),
511 case httpc:request(post,
512 {URL,NewHeaders,
513 ContentType,
514 Request},
515 [{timeout,?HTTP_REQ_TIMEOUT}],
516 [{sync, true}, {full_result, true},
517 {body_format, string}]) of
518 {ok,{{_HTTP,200,_OK},ResponseHeaders,ResponseBody}} ->
519 {ok, 200, ResponseHeaders, ResponseBody};
520 {ok,{{_HTTP,500,_Descr},ResponseHeaders,ResponseBody}} ->
521 {ok, 500, ResponseHeaders, ResponseBody};
522 {ok,{{_HTTP,ErrorCode,_Descr},ResponseHeaders,ResponseBody}} ->
523 {ok, ErrorCode, ResponseHeaders, ResponseBody};
524 Other ->
525 Other
526 end.
527
528 ibrowse_request(URL, Action, Request, Options, Headers, ContentType) ->
529 case start_ibrowse() of
530 ok ->
531 case Action of
532 undefined ->
533 NewHeaders = [{"Content-Type", ContentType} | Headers];
534 _ ->
535 NewHeaders = [{"Content-Type", ContentType}, {"SOAPAction", Action} | Headers]
536 end,
537 NewOptions = Options,
538 %%[{content_type, "text/xml; encoding=utf-8"} | Options],
539 case ibrowse:send_req(URL, NewHeaders, post, Request, NewOptions) of
540 {ok, Status, ResponseHeaders, ResponseBody} ->
541 {ok, list_to_integer(Status), ResponseHeaders,ResponseBody};
542 {error, Reason} ->
543 {error, Reason}
544 end;
545 error ->
546 {error, "could not start ibrowse"}
547 end.
548
549 start_ibrowse() ->
550 case ibrowse:start() of
551 {ok, _} -> ok;
552 {error, {already_started, _}} -> ok;
553 _ -> error
554 end.
555
556
557 rmsp(Str) -> string:strip(Str, left).
558
559
560 make_request_body(Content, [], Operation) ->
561 {"application/soap+xml;charset=UTF-8;action=\"" ++ Operation ++ "\"",
562 "<?xml version=\"1.0\" encoding=\"utf-8\"?>"++ Content};
563 make_request_body(Content, AttachedFiles, _Operation) ->
564 {"application/dime",
565 yaws_dime:encode("<?xml version=\"1.0\" encoding=\"utf-8\"?>" ++ Content,
566 AttachedFiles)}.
567
568 makeFault(FaultCode, FaultString) ->
569 try
570 "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">"
571 "<SOAP-ENV:Body>"
572 "<SOAP-ENV:Fault>"
573 "<faultcode>SOAP-ENV:" ++ FaultCode ++ "</faultcode>" ++
574 "<faultstring>" ++ FaultString ++ "</faultstring>" ++
575 "</SOAP-ENV:Fault>"
576 "</SOAP-ENV:Body>"
577 "</SOAP-ENV:Envelope>"
578 catch
579 _:_ ->
580 "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">"
581 "<SOAP-ENV:Body>"
582 "<SOAP-ENV:Fault>"
583 "<faultcode>SOAP-ENV:Server</faultcode>"
584 "<faultstring>Server error</faultstring>"
585 "</SOAP-ENV:Fault>"
586 "</SOAP-ENV:Body>"
587 "</SOAP-ENV:Envelope>"
588 end.
589
590 %% record http_header is not defined??
591 findHeader(Label, Headers) ->
592 findHeader0(yaws:to_lower(Label), Headers).
593
594 findHeader0(_Label, []) ->
595 undefined;
596 findHeader0(Label, [{_,_,Hdr,_,Val}|T]) ->
597 case {Label, yaws:to_lower(Hdr)} of
598 {X,X} -> Val;
599 _ -> findHeader0(Label, T)
600 end;
601 findHeader0(_Label, undefined) ->
602 undefined.
603
604
605 makeOptions(undefined) ->
606 [];
607 makeOptions(Import) ->
608 lists:map(fun makeOption/1, Import).
609
610 %% -record(import_specs, {atts, namespace, prefix, location}).
611 makeOption(#import_specs{namespace = Ns, prefix = Pf, location = Lc}) ->
612 {Ns, Pf, Lc}.
613
614
615 addModels(undefined, Model) ->
616 Model;
617 addModels(Import, Model) ->
618 lists:foldl(fun addModel/2, Model, Import).
619
620 %% -record(xsd_file, {atts, name, prefix, import_specs}).
621 addModel(undefined, Acc) ->
622 Acc;
623 addModel(#xsd_file{name = XsdFile, prefix = Prefix, import_specs = Import},
624 Acc) ->
625 Options = makeOptions(Import),
626 {ok, Model2} = erlsom:add_xsd_file(XsdFile, [{prefix, Prefix}|Options],Acc),
627 Model2.
628
629 %% returns [#port{}]
630 %% -record(port, {service, port, binding, address}).
631 getPorts(ParsedWsdl) ->
632 Services = getTopLevelElements(ParsedWsdl, 'wsdl:tService'),
633 getPortsFromServices(Services, []).
634
635 getPortsFromServices([], Acc) ->
636 Acc;
637 getPortsFromServices([Service|Tail], Acc) ->
638 getPortsFromServices(Tail, getPortsFromService(Service) ++ Acc).
639
640 getPortsFromService(#'wsdl:tService'{name = Name, port = Ports}) ->
641 getPortsInfo(Ports, Name, []).
642
643 getPortsInfo([], _Name, Acc) ->
644 Acc;
645
646 getPortsInfo([#'wsdl:tPort'{name = Name,
647 binding = Binding,
648 choice =
649 [#'soap:tAddress'{location = URL}]} | Tail],
650 ServiceName, Acc) ->
651 getPortsInfo(Tail, ServiceName, [#port{service = ServiceName,
652 port = Name,
653 binding = Binding,
654 address = URL}|Acc]);
655 %% non-soap bindings are ignored.
656 getPortsInfo([#'wsdl:tPort'{} | Tail], ServiceName, Acc) ->
657 getPortsInfo(Tail, ServiceName, Acc).
658
659
660 getTopLevelElements(#'wsdl:tDefinitions'{choice1 = TLElements}, Type) ->
661 getTopLevelElements(TLElements, Type, []).
662
663 getTopLevelElements([], _Type, Acc) ->
664 Acc;
665 getTopLevelElements([#'wsdl:anyTopLevelOptionalElement'{choice = Tuple}| Tail],
666 Type, Acc) ->
667 case element(1, Tuple) of
668 Type -> getTopLevelElements(Tail, Type, [Tuple|Acc]);
669 _ -> getTopLevelElements(Tail, Type, Acc)
670 end.
671
672 get_file_with_path(Url) ->
673 case Url of
674 "http://" ++ _ ->
675 undefined;
676 "file://" ++ FName ->
677 FName;
678 _ ->
679 Url
680 end.
681
682
683 getImports(WsdlDirname, Definitions) ->
684 Imports = getTopLevelElements(Definitions, 'wsdl:tImport'),
685 lists:map(fun(Import) ->
686 case WsdlDirname of
687 "http://" ++ _AbsDirname ->
688 WsdlDirname ++ "/" ++ Import#'wsdl:tImport'.location;
689 "file://" ++ _AbsDirname ->
690 WsdlDirname ++ "/" ++ Import#'wsdl:tImport'.location;
691 Fname ->
692 filename:join(Fname, Import#'wsdl:tImport'.location)
693 end
694 end, Imports).
695
696 %% returns [#operation{}]
697 getOperations(ParsedWsdl, Ports) ->
698 Bindings = getTopLevelElements(ParsedWsdl, 'wsdl:tBinding'),
699 getOperationsFromBindings(Bindings, Ports, []).
700
701 getOperationsFromBindings([], _Ports, Acc) ->
702 Acc;
703 getOperationsFromBindings([Binding|Tail], Ports, Acc) ->
704 getOperationsFromBindings(Tail, Ports,
705 getOperationsFromBinding(Binding, Ports) ++ Acc).
706
707 getOperationsFromBinding(#'wsdl:tBinding'{name = BindingName,
708 type = BindingType,
709 choice = _Choice,
710 operation = Operations}, Ports) ->
711 %% TODO: get soap info from Choice
712 getOperationsFromOperations(Operations, BindingName, BindingType,
713 Operations, Ports, []).
714
715 getOperationsFromOperation(BindingName, BindingType, Ports, Name, Action, Operations, Tail, Acc) ->
716 %% lookup Binding in Ports, and create a combined result
717 Ports2 = searchPorts(BindingName, Ports),
718 %% for each port, make an operation record
719 CombinedPorts = combinePorts(Ports2, Name, BindingName, Action),
720 getOperationsFromOperations(
721 Tail, BindingName, BindingType,
722 Operations, Ports, CombinedPorts ++ Acc).
723
724 getOperationsFromOperations([], _BindingName, _BindingType, _Operations, _Ports, Acc) ->
725 Acc;
726
727 getOperationsFromOperations([#'wsdl:tBindingOperation'{name = Name,
728 choice = Choice} | Tail],
729 BindingName, BindingType, Operations, Ports, Acc) ->
730 %% get SOAP action from Choice,
731 case Choice of
732 [#'soap:tOperation'{soapAction = Action}] ->
733 getOperationsFromOperation(BindingName, BindingType, Ports, Name, Action, Operations, Tail, Acc);
734 _ ->
735 getOperationsFromOperation(BindingName, BindingType, Ports, Name, undefined, Operations, Tail, Acc)
736 end.
737
738 combinePorts(Ports, Name, BindingName, Action) ->
739 combinePorts(Ports, Name, BindingName, Action, []).
740
741 combinePorts([], _Name, _BindingName, _Action, Acc) ->
742 Acc;
743 combinePorts([#port{service = Service,
744 port = PortName,
745 address = Address} | Tail],
746 Name, BindingName, Action, Acc) ->
747 combinePorts(Tail, Name, BindingName, Action,
748 [#operation{service = Service,
749 port = PortName, operation = Name,
750 binding = BindingName,
751 address = Address, action = Action} | Acc]).
752
753 searchPorts(BindingName, Ports) ->
754 searchPorts(BindingName, Ports, []).
755
756 searchPorts(_BindingName, [], Acc) ->
757 Acc;
758 searchPorts(BindingName, [Port | Tail], Acc) ->
759 PortBinding = erlsom_lib:localName(Port#port.binding),
760 case PortBinding of
761 BindingName ->
762 searchPorts(BindingName, Tail, [Port | Acc]);
763 _ ->
764 searchPorts(BindingName, Tail, Acc)
765 end.
766
767 %% copied from yaws/json.erl
768 is_string([]) -> yes;
769 is_string(List) -> is_string(List, non_unicode).
770
771 is_string([C|Rest], non_unicode) when C >= 0, C =< 255 -> is_string(Rest, non_unicode);
772 is_string([C|Rest], _) when C =< 65000 -> is_string(Rest, unicode);
773 is_string([], non_unicode) -> yes;
774 is_string([], unicode) -> unicode;
775 is_string(_, _) -> no.
776
777 getXsdsFromWsdl(Definitions) ->
778 case getTopLevelElements(Definitions, 'wsdl:tTypes') of
779 [#'wsdl:tTypes'{choice = Xsds}] -> Xsds;
780 [] -> []
781 end.
782
783 config_file_xsd() ->
784 "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
785 " <xs:element name=\"yaws_soap_config\">"
786 " <xs:complexType>"
787 " <xs:sequence>"
788 " <xs:element name=\"xsd_path\" type=\"xs:string\" minOccurs=\"0\"/>"
789 " <xs:element name=\"user_module\" type=\"xs:string\"/>"
790 " <xs:element name=\"wsdl_file\" type=\"xsd_file\"/>"
791 " <xs:element name=\"add_file\" type=\"xsd_file\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>"
792 " </xs:sequence>"
793 " </xs:complexType>"
794 " </xs:element>"
795 " <xs:complexType name=\"xsd_file\">"
796 " <xs:sequence>"
797 " <xs:element name=\"import_specs\" type=\"import_specs\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>"
798 " </xs:sequence>"
799 " <xs:attribute name=\"name\" type=\"string\" use=\"required\"/>"
800 " <xs:attribute name=\"prefix\" type=\"string\"/>"
801 " </xs:complexType>"
802 " <xs:complexType name=\"import_specs\">"
803 " <xs:attribute name=\"namespace\" type=\"string\" use=\"required\"/>"
804 " <xs:attribute name=\"prefix\" type=\"string\"/>"
805 " <xs:attribute name=\"location\" type=\"string\"/>"
806 " </xs:complexType>"
807 "</xs:schema>".
808
809
Something went wrong with that request. Please try again.