test: Insert dmStatus to DummyOperation response
[libisds.git] / test / simline / service.c
blob6efbcb65a433bb0090aca8b8310501ab1cfafde7
1 #include "../test-tools.h"
2 #include "http.h"
3 #include <string.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(). */
13 typedef enum {
14 MESSAGE_NS_1,
15 MESSAGE_NS_UNSIGNED,
16 MESSAGE_NS_SIGNED_INCOMING,
17 MESSAGE_NS_SIGNED_OUTGOING,
18 MESSAGE_NS_SIGNED_DELIVERY
19 } message_ns_type;
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"
32 struct service {
33 const char *end_point;
34 const xmlChar *name;
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) \
41 { \
42 xmlNodePtr node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
43 (xmlChar *) (string)); \
44 if (NULL == node) { \
45 error = -1; \
46 goto leave; \
47 } \
50 #define INSERT_STRING(parent, element, string) \
51 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
53 #define INSERT_ELEMENT(child, parent, element) \
54 { \
55 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
56 if (NULL == (child)) { \
57 error = -1; \
58 goto leave; \
59 } \
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) {
72 int error = 0;
73 xmlNodePtr status;
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);
80 leave:
81 return error;
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;
111 switch(message_ns) {
112 case MESSAGE_NS_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;
122 default:
123 return -1;
126 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
127 return -1;
128 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
129 return -1;
130 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
131 return -1;
132 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
133 return -1;
134 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
135 return -1;
136 return 0;
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");
160 return;
163 request_doc = xmlParseMemory(request, request_length);
164 if (NULL == request_doc) {
165 http_send_response_400(socket, "Client sent invalid XML document");
166 return;
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");
173 return;
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");
181 return;
184 /* Get SOAP Body */
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");
191 return;
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");
199 return;
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");
207 return;
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");
215 return;
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");
224 return;
227 /* Build SOAP response envelope */
228 response_doc = xmlNewDoc(BAD_CAST "1.0");
229 if (!response_doc) {
230 http_send_response_500(socket, "Could not build SOAP response document");
231 goto leave;
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");
236 goto leave;
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");
244 goto leave;
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");
252 goto leave;
254 /* Append ISDS response element */
255 if (-1 == test_asprintf(&response_name, "%s%s", isds_request->name,
256 "Response")) {
257 http_send_response_500(socket,
258 "Could not buld ISDS resposne element name");
259 goto leave;
261 isds_response = xmlNewChild(response_soap_body, NULL,
262 BAD_CAST response_name, NULL);
263 free(response_name);
264 if (NULL == isds_response) {
265 http_send_response_500(socket,
266 "Could not add ISDS response element to SOAP response body");
267 goto leave;
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");
272 goto leave;
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)) {
280 service_handled = 1;
281 if (!services[i].function(socket, request_doc, xpath_ctx, isds_request,
282 response_doc, isds_response)) {
283 service_passed = 1;
284 } else {
285 http_send_response_500(socket,
286 "Internal server error while processing ISDS request");
288 break;
292 /* Send response */
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");
299 goto leave;
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
303 * elements. */
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");
307 goto leave;
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");
315 goto leave;
318 http_send_response_200(socket, http_response_body->content,
319 http_response_body->use, soap_mime_type);
322 leave:
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");