1 #include "../test-tools.h"
4 #include <libxml/parser.h>
5 #include <libxml/xpath.h>
6 #include <libxml/xpathInternals.h>
7 #include <libxml/xmlsave.h>
9 static const char *soap_mime_type
= "text/xml"; /* SOAP/1.1 requires text/xml */
11 /* Used to choose proper name space for message elements.
12 * See _isds_register_namespaces(). */
16 MESSAGE_NS_SIGNED_INCOMING
,
17 MESSAGE_NS_SIGNED_OUTGOING
,
18 MESSAGE_NS_SIGNED_DELIVERY
21 #define SOAP_NS "http://schemas.xmlsoap.org/soap/envelope/"
22 #define SOAP2_NS "http://www.w3.org/2003/05/soap-envelope"
23 #define ISDS1_NS "http://isds.czechpoint.cz"
24 #define ISDS_NS "http://isds.czechpoint.cz/v20"
25 #define SISDS_INCOMING_NS "http://isds.czechpoint.cz/v20/message"
26 #define SISDS_OUTGOING_NS "http://isds.czechpoint.cz/v20/SentMessage"
27 #define SISDS_DELIVERY_NS "http://isds.czechpoint.cz/v20/delivery"
28 #define SCHEMA_NS "http://www.w3.org/2001/XMLSchema"
29 #define DEPOSIT_NS "urn:uschovnaWSDL"
33 const char *end_point
;
35 int (*function
) (int, xmlDocPtr
, xmlXPathContextPtr
, xmlNodePtr
,
36 xmlDocPtr
, xmlNodePtr
);
39 /* Following INSERT_* macros expect @error and leave label */
40 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
42 xmlNodePtr node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
43 (xmlChar *) (string)); \
50 #define INSERT_STRING(parent, element, string) \
51 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
53 #define INSERT_ELEMENT(child, parent, element) \
55 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
56 if (NULL == (child)) { \
62 /* Insert dmStatus or similar subtree
63 * @parent is element to insert to
64 * @dm is true for dmStatus, otherwise dbStatus
65 * @code is stautus code as string
66 * @message is UTF-8 encoded message
67 * @db_ref_number is optinal reference number propagated if not @dm
68 * @return 0 on success, otherwise non-0. */
69 static int insert_isds_status(xmlNodePtr parent
, _Bool dm
,
70 const xmlChar
* code
, const xmlChar
*message
,
71 const xmlChar
*db_ref_number
) {
74 INSERT_ELEMENT(status
, parent
, (dm
) ? "dmStatus" : "dbStatus");
75 INSERT_STRING(status
, (dm
) ? "dmStatusCode" : "dbStatusCode", "0000");
76 INSERT_STRING(status
, (dm
) ? "dmStatusMessage" : "dbStatusMessage", "Success");
77 if (!dm
&& NULL
!= db_ref_number
) {
78 INSERT_STRING(status
, "dbStatusRefNumber", db_ref_number
);
85 /* Parse and respond to DummyOperation */
86 static int service_DummyOperation(int socket
, const xmlDocPtr soap_request
,
87 xmlXPathContextPtr xpath_ctx
, xmlNodePtr isds_request
,
88 xmlDocPtr soap_response
, xmlNodePtr isds_response
) {
89 return insert_isds_status(isds_response
, 1, BAD_CAST
"0000",
90 BAD_CAST
"Success", NULL
);
94 /* List of implemented services */
95 static struct service services
[] = {
96 { "DS/dz", BAD_CAST
"DummyOperation", service_DummyOperation
},
100 /* Makes known all relevant namespaces to given XPath context
101 * @xpath_ctx is XPath context
102 * @message_ns selects proper message name space. Unsigned and signed
103 * messages and delivery info's differ in prefix and URI.
104 * @return 0 in success, otherwise not 0. */
105 static int register_namespaces(xmlXPathContextPtr xpath_ctx
,
106 const message_ns_type message_ns
) {
107 const xmlChar
*message_namespace
= NULL
;
109 if (!xpath_ctx
) return -1;
113 message_namespace
= BAD_CAST ISDS1_NS
; break;
114 case MESSAGE_NS_UNSIGNED
:
115 message_namespace
= BAD_CAST ISDS_NS
; break;
116 case MESSAGE_NS_SIGNED_INCOMING
:
117 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
118 case MESSAGE_NS_SIGNED_OUTGOING
:
119 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
120 case MESSAGE_NS_SIGNED_DELIVERY
:
121 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
126 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
128 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", BAD_CAST ISDS_NS
))
130 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
132 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
134 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))
140 /* Parse soap request, pass it to service endpoint and respond to it.
141 * It sends final HTTP response. */
142 void soap(int socket
, const void *request
, size_t request_length
,
143 const char *end_point
) {
144 xmlDocPtr request_doc
= NULL
;
145 xmlXPathContextPtr xpath_ctx
= NULL
;
146 xmlXPathObjectPtr request_soap_body
= NULL
;
147 xmlNodePtr isds_request
= NULL
; /* pointer only */
148 _Bool service_handled
= 0, service_passed
= 0;
149 xmlDocPtr response_doc
= NULL
;
150 xmlNodePtr response_soap_envelope
= NULL
, response_soap_body
= NULL
,
151 isds_response
= NULL
;
152 xmlNsPtr soap_ns
= NULL
, isds_ns
= NULL
;
153 char *response_name
= NULL
;
154 xmlBufferPtr http_response_body
= NULL
;
155 xmlSaveCtxtPtr save_ctx
= NULL
;
158 if (NULL
== request
|| request_length
== 0) {
159 http_send_response_400(socket
, "Client sent empty body");
163 request_doc
= xmlParseMemory(request
, request_length
);
164 if (NULL
== request_doc
) {
165 http_send_response_400(socket
, "Client sent invalid XML document");
169 xpath_ctx
= xmlXPathNewContext(request_doc
);
170 if (NULL
== xpath_ctx
) {
171 xmlFreeDoc(request_doc
);
172 http_send_response_500(socket
, "Could not create XPath context");
176 if (register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
177 xmlXPathFreeContext(xpath_ctx
);
178 xmlFreeDoc(request_doc
);
179 http_send_response_500(socket
,
180 "Could not register name spaces to the XPath context");
185 request_soap_body
= xmlXPathEvalExpression(
186 BAD_CAST
"/soap:Envelope/soap:Body", xpath_ctx
);
187 if (NULL
== request_soap_body
) {
188 xmlXPathFreeContext(xpath_ctx
);
189 xmlFreeDoc(request_doc
);
190 http_send_response_400(socket
, "Client sent invalid SOAP request");
193 if (xmlXPathNodeSetIsEmpty(request_soap_body
->nodesetval
)) {
194 xmlXPathFreeObject(request_soap_body
);
195 xmlXPathFreeContext(xpath_ctx
);
196 xmlFreeDoc(request_doc
);
197 http_send_response_400(socket
,
198 "SOAP request does not contain SOAP Body element");
201 if (request_soap_body
->nodesetval
->nodeNr
> 1) {
202 xmlXPathFreeObject(request_soap_body
);
203 xmlXPathFreeContext(xpath_ctx
);
204 xmlFreeDoc(request_doc
);
205 http_send_response_400(socket
,
206 "SOAP response has more than one Body element");
209 isds_request
= request_soap_body
->nodesetval
->nodeTab
[0]->children
;
210 if (isds_request
->next
!= NULL
) {
211 xmlXPathFreeObject(request_soap_body
);
212 xmlXPathFreeContext(xpath_ctx
);
213 xmlFreeDoc(request_doc
);
214 http_send_response_400(socket
, "SOAP body has more than one child");
217 if (isds_request
->type
!= XML_ELEMENT_NODE
|| isds_request
->ns
== NULL
||
218 xmlStrcmp(isds_request
->ns
->href
, BAD_CAST ISDS_NS
)) {
219 xmlXPathFreeObject(request_soap_body
);
220 xmlXPathFreeContext(xpath_ctx
);
221 xmlFreeDoc(request_doc
);
222 http_send_response_400(socket
,
223 "SOAP body does not contain an ISDS elment");
227 /* Build SOAP response envelope */
228 response_doc
= xmlNewDoc(BAD_CAST
"1.0");
230 http_send_response_500(socket
, "Could not build SOAP response document");
233 response_soap_envelope
= xmlNewNode(NULL
, BAD_CAST
"Envelope");
234 if (!response_soap_envelope
) {
235 http_send_response_500(socket
, "Could not build SOAP response envelope");
238 xmlDocSetRootElement(response_doc
, response_soap_envelope
);
239 /* Only this way we get namespace definition as @xmlns:soap,
240 * otherwise we get namespace prefix without definition */
241 soap_ns
= xmlNewNs(response_soap_envelope
, BAD_CAST SOAP_NS
, NULL
);
242 if(NULL
== soap_ns
) {
243 http_send_response_500(socket
, "Could not create SOAP name space");
246 xmlSetNs(response_soap_envelope
, soap_ns
);
247 response_soap_body
= xmlNewChild(response_soap_envelope
, NULL
,
248 BAD_CAST
"Body", NULL
);
249 if (!response_soap_body
) {
250 http_send_response_500(socket
,
251 "Could not add Body to SOAP response envelope");
254 /* Append ISDS response element */
255 if (-1 == test_asprintf(&response_name
, "%s%s", isds_request
->name
,
257 http_send_response_500(socket
,
258 "Could not buld ISDS resposne element name");
261 isds_response
= xmlNewChild(response_soap_body
, NULL
,
262 BAD_CAST response_name
, NULL
);
264 if (NULL
== isds_response
) {
265 http_send_response_500(socket
,
266 "Could not add ISDS response element to SOAP response body");
269 isds_ns
= xmlNewNs(isds_response
, BAD_CAST ISDS_NS
, NULL
);
270 if(NULL
== isds_ns
) {
271 http_send_response_500(socket
, "Could not create ISDS name space");
274 xmlSetNs(isds_response
, isds_ns
);
276 /* Dispatch request to service */
277 for (int i
= 0; i
< sizeof(services
)/sizeof(services
[0]); i
++) {
278 if (!strcmp(services
[i
].end_point
, end_point
) &&
279 !xmlStrcmp(services
[i
].name
, isds_request
->name
)) {
281 if (!services
[i
].function(socket
, request_doc
, xpath_ctx
, isds_request
,
282 response_doc
, isds_response
)) {
285 http_send_response_500(socket
,
286 "Internal server error while processing ISDS request");
293 if (service_passed
) {
294 /* Serialize the SOAP response */
295 http_response_body
= xmlBufferCreate();
296 if (NULL
== http_response_body
) {
297 http_send_response_500(socket
,
298 "Could not create xmlBuffer for response serialization");
301 /* Last argument 1 means format the XML tree. This is pretty but it breaks
302 * XML document transport as it adds text nodes (indentiation) between
304 save_ctx
= xmlSaveToBuffer(http_response_body
, "UTF-8", 0);
305 if (NULL
== save_ctx
) {
306 http_send_response_500(socket
, "Could not create XML serializer");
309 /* XXX: According LibXML documentation, this function does not return
310 * meaningful value yet */
311 xmlSaveDoc(save_ctx
, response_doc
);
312 if (-1 == xmlSaveFlush(save_ctx
)) {
313 http_send_response_500(socket
,
314 "Could not serialize SOAP response");
318 http_send_response_200(socket
, http_response_body
->content
,
319 http_response_body
->use
, soap_mime_type
);
323 xmlSaveClose(save_ctx
);
324 xmlBufferFree(http_response_body
);
326 xmlFreeDoc(response_doc
);
328 xmlXPathFreeObject(request_soap_body
);
329 xmlXPathFreeContext(xpath_ctx
);
330 xmlFreeDoc(request_doc
);
332 if (!service_handled
) {
333 http_send_response_500(socket
,
334 "Requested ISDS service not implemented");