Remove "/DS" from server base URL
[libisds.git] / src / validator.c
blobb42d82f32880886cc5d58e41c82eee36ff278f1d
1 #include "isds_priv.h"
2 #include "utils.h"
3 #include "validator.h"
4 #include "soap.h"
7 /* Get ISDS status info from ISDS @response XML document.
8 * Be ware that different request families return differently encoded status
9 * (e.g. dmStatus, dbStatus)
10 * @context is ISDS context
11 * @service is ISDS web service identifier
12 * @response is ISDS response document
13 * @code is automatically allocated status code of the response
14 * @message is automatically allocated status message. Returned NULL means no
15 * message was delivered by server. Use NULL if you don't care.
16 * @refnumber is automatically reallocated request serial number assigned by
17 * ISDS. Returned *NULL means no number was delivered by server.
18 * Use NULL if you don't care. */
19 _hidden isds_error isds_response_status(struct isds_ctx *context,
20 const isds_service service, xmlDocPtr response,
21 xmlChar **code, xmlChar **message, xmlChar **refnumber) {
22 isds_error err = IE_SUCCESS;
23 xmlChar *status_code_expr = NULL, *status_message_expr = NULL;
24 xmlXPathContextPtr xpath_ctx = NULL;
25 xmlXPathObjectPtr result = NULL;
27 if (!response || !code) {
28 err = IE_INVAL;
29 goto leave;
32 switch (service) {
33 case SERVICE_DM_OPERATIONS:
34 case SERVICE_DM_INFO:
35 status_code_expr = BAD_CAST
36 "/*/isds:dmStatus/isds:dmStatusCode/text()";
37 status_message_expr = BAD_CAST
38 "/*/isds:dmStatus/isds:dmStatusMessage/text()";
39 break;
40 case SERVICE_DB_SEARCH:
41 case SERVICE_DB_ACCESS:
42 case SERVICE_DB_MANIPULATION:
43 status_code_expr = BAD_CAST
44 "/*/isds:dbStatus/isds:dbStatusCode/text()";
45 status_message_expr = BAD_CAST
46 "/*/isds:dbStatus/isds:dbStatusMessage/text()";
47 break;
48 default:
49 err = IE_NOTSUP;
50 goto leave;
53 xpath_ctx = xmlXPathNewContext(response);
54 if (!xpath_ctx) {
55 err = IE_ERROR;
56 goto leave;
58 if (register_namespaces(xpath_ctx,
59 (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) ?
60 MESSAGE_NS_1 : MESSAGE_NS_UNSIGNED)) {
61 err = IE_ERROR;
62 goto leave;
65 /* Get status code */
66 result = xmlXPathEvalExpression(status_code_expr, xpath_ctx);
67 if (!result) {
68 err = IE_ERROR;
69 goto leave;
71 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
72 isds_log_message(context,
73 (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) ?
74 _("ISDS1 response is missing StatusCode element") :
75 _("ISDS response is missing StatusCode element"));
76 err = IE_ISDS;
77 goto leave;
79 *code = xmlXPathCastNodeSetToString(result->nodesetval);
80 if (!code) {
81 err = IE_ERROR;
82 goto leave;
85 if (message) {
86 /* Get status message */
87 xmlXPathFreeObject(result);
88 result = xmlXPathEvalExpression(status_message_expr, xpath_ctx);
89 if (!result) {
90 err = IE_ERROR;
91 goto leave;
93 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
94 /* E.g. CreateMessageResponse with dmStatusCode 9005 has empty
95 * message */
96 *message = NULL;
97 } else {
98 *message = xmlXPathCastNodeSetToString(result->nodesetval);
99 if (!message) {
100 err = IE_ERROR;
101 goto leave;
106 if (refnumber) {
107 /* Get reference number of client request */
108 zfree(*refnumber);
109 xmlXPathFreeObject(result);
110 result = xmlXPathEvalExpression(
111 BAD_CAST "/*/isds:dbStatus/isds:dbStatusRefNumber/text()",
112 xpath_ctx);
113 if (!result) {
114 err = IE_ERROR;
115 goto leave;
117 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
118 *refnumber = NULL;
119 } else {
120 *refnumber = xmlXPathCastNodeSetToString(result->nodesetval);
121 if (!message) {
122 err = IE_ERROR;
123 goto leave;
127 leave:
128 xmlXPathFreeObject(result);
129 xmlXPathFreeContext(xpath_ctx);
130 return err;
134 /* Send @request to ISDS and return ISDS @response as XML document.
135 * Be ware the @response can be invalid (in sense of XML Schema).
136 * (And it is because current ISDS server does not follow its own
137 * specification. Please appology my government, its herd of imcompetent
138 * creatures.)
139 * @context is ISDS session context,
140 * @service identifies ISDS web service
141 * @request is tree with ISDS message, can be NULL
142 * @response is automatically allocated response from server as XML Document
143 * @raw_response is automatically allocated bitstream with response body. Use
144 * NULL if you don't care
145 * @raw_response_length is size of @raw_response in bytes
146 * In case of error, @response and @raw_response will be dealocated.
147 * */
148 _hidden isds_error isds(struct isds_ctx *context, const isds_service service,
149 const xmlNodePtr request, xmlDocPtr *response,
150 void **raw_response, size_t *raw_response_length) {
151 isds_error err = IE_SUCCESS;
152 xmlNodePtr response_body = NULL, isds_node;
153 char *file = NULL;
155 if (!context) return IE_INVALID_CONTEXT;
156 if (!response) return IE_INVAL;
157 if (!raw_response_length && raw_response) return IE_INVAL;
159 /* Effective ISDS URL is build from base URL and suffix.
160 * Other conenction types has specific stable URL. */
161 if (context->type == CTX_TYPE_ISDS) {
162 switch (service) {
163 case SERVICE_DM_OPERATIONS: file = "DS/dz"; break;
164 case SERVICE_DM_INFO: file = "DS/dx"; break;
165 case SERVICE_DB_SEARCH: file = "DS/df"; break;
166 case SERVICE_DB_ACCESS: file = "DS/DsManage"; break;
167 case SERVICE_DB_MANIPULATION: file = "DS/DsManage"; break;
168 default: return (IE_INVAL);
172 err = soap(context, file, request, &response_body,
173 raw_response, raw_response_length);
175 if (err) goto leave;
177 if (!response_body) {
178 isds_log_message(context, _("SOAP returned empty body"));
179 err = IE_ISDS;
182 /* Find ISDS element */
183 for (isds_node = response_body; isds_node; isds_node = isds_node->next) {
184 if (isds_node->type == XML_ELEMENT_NODE &&
185 isds_node->ns &&
186 !xmlStrcmp(isds_node->ns->href,
187 (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) ?
188 BAD_CAST ISDS1_NS : BAD_CAST ISDS_NS))
189 break;
191 if (!isds_node) {
192 isds_log_message(context,
193 (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) ?
194 _("SOAP response does not contain ISDS1 element") :
195 _("SOAP response does not contain ISDS element"));
196 err = IE_ISDS;
197 goto leave;
200 /* Destroy other nodes */
201 if (isds_node == response_body)
202 response_body = response_body->next;
203 xmlUnlinkNode(isds_node);
204 xmlFreeNodeList(response_body);
205 response_body = NULL;
207 /* TODO: validate the response */
209 /* Build XML document */
210 *response = xmlNewDoc(BAD_CAST "1.0");
211 if (!*response) {
212 isds_log_message(context,
213 (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) ?
214 _("Could not build ISDS1 response document") :
215 _("Could not build ISDS response document"));
216 err = IE_ERROR;
217 goto leave;
219 xmlDocSetRootElement(*response, isds_node);
221 leave:
222 if (err) {
223 xmlFreeDoc(*response);
224 if (raw_response) zfree(*raw_response);
226 xmlFreeNodeList(response_body);
228 return err;
232 /* Walk through list of isds_documents and check for their types and
233 * references.
234 * @context is session context
235 * @documents is list of isds_document to check
236 * @returns IE_SUCCESS if structure is valid, otherwise context' message will
237 * be filled with explanation of found problem. */
238 _hidden isds_error check_documents_hierarchy(struct isds_ctx *context,
239 const struct isds_list *documents) {
241 const struct isds_list *item;
242 const struct isds_document *document;
243 _Bool main_exists = 0;
245 if (!context) return IE_INVALID_CONTEXT;
246 if (!documents) return IE_INVAL;
248 for (item = documents; item; item = item->next) {
249 document = (const struct isds_document *) item->data;
250 if (!document) continue;
252 /* Only one document can be main */
253 if (document->dmFileMetaType == FILEMETATYPE_MAIN) {
254 if (main_exists) {
255 isds_log_message(context,
256 _("List contains more main documents"));
257 return IE_ERROR;
259 main_exists = 1;
262 /* All document identifiers should be unique */
263 if (document->dmFileGuid) {
264 if (isds_find_document_by_id(documents, document->dmFileGuid) !=
265 document) {
266 isds_printf_message(context, _("List contains more documents "
267 "with the same ID `%s'"), document->dmFileGuid);
268 return IE_ERROR;
272 /* All document references should point to existing document ID */
273 /* ???: Should we forbid self-refencing? */
274 if (document->dmUpFileGuid) {
275 if (!isds_find_document_by_id(documents,
276 document->dmUpFileGuid)) {
277 isds_printf_message(context, _("List contains documents "
278 "referencing to unexisting document ID `%s'"),
279 document->dmUpFileGuid);
280 return IE_ERROR;
285 if (!main_exists) {
286 isds_log_message(context, _("List does not contain main document"));
287 return IE_ERROR;
290 return IE_SUCCESS;
294 /* Check for message ID length
295 * @context is session context
296 * @message_id checked message ID
297 * @return IE_SUCCESS or appropriate error code and fill context' message */
298 isds_error validate_message_id_length(struct isds_ctx *context,
299 const xmlChar *message_id) {
300 if (!context) return IE_INVALID_CONTEXT;
301 if (!message_id) return IE_INVAL;
303 const int length = xmlUTF8Strlen(message_id);
305 if (length == -1) {
306 char *message_id_locale = utf82locale((char*) message_id);
307 isds_printf_message(context,
308 _("Could not check message ID length: %s"),
309 message_id_locale);
310 free(message_id_locale);
311 return IE_ERROR;
314 if (length >= 20) {
315 char *message_id_locale = utf82locale((char*) message_id);
316 isds_printf_message(context,
317 _("Message ID must not be longer than 20 characters: %s"),
318 message_id_locale);
319 free(message_id_locale);
320 return IE_INVAL;
323 return IE_SUCCESS;
327 /* Send @request to Czech POINT conversion deposit and return response
328 * as XML document.
329 * @context is Czech POINT session context,
330 * @request is tree with deposit message, can be NULL
331 * @response is automatically allocated response from server as XML Document
332 * In case of error, @response will be dealocated.
333 * */
334 _hidden isds_error czpdeposit(struct isds_ctx *context,
335 const xmlNodePtr request, xmlDocPtr *response) {
336 isds_error err = IE_SUCCESS;
337 xmlNodePtr response_body = NULL, deposit_node;
339 if (!context) return IE_INVALID_CONTEXT;
340 if (!response) return IE_INVAL;
342 err = soap(context, NULL, request, &response_body, NULL, NULL);
344 if (err) goto leave;
346 if (!response_body) {
347 isds_log_message(context, _("SOAP returned empty body"));
348 err = IE_ISDS;
351 /* Find deposit element */
352 for (deposit_node = response_body; deposit_node;
353 deposit_node = deposit_node->next) {
354 if (deposit_node->type == XML_ELEMENT_NODE &&
355 deposit_node->ns &&
356 !xmlStrcmp(deposit_node->ns->href, BAD_CAST DEPOSIT_NS))
357 break;
359 if (!deposit_node) {
360 isds_log_message(context,
361 _("SOAP response does not contain "
362 "Czech POINT deposit element"));
363 err = IE_ISDS;
364 goto leave;
367 /* Destroy other nodes */
368 if (deposit_node == response_body)
369 response_body = response_body->next;
370 xmlUnlinkNode(deposit_node);
371 xmlFreeNodeList(response_body);
372 response_body = NULL;
374 /* Build XML document */
375 *response = xmlNewDoc(BAD_CAST "1.0");
376 if (!*response) {
377 isds_log_message(context,
378 _("Could not build Czech POINT deposit response document"));
379 err = IE_ERROR;
380 goto leave;
382 xmlDocSetRootElement(*response, deposit_node);
384 leave:
385 if (err) {
386 xmlFreeDoc(*response);
388 xmlFreeNodeList(response_body);
390 return err;