i18n: Fix message typos
[libisds.git] / src / validator.c
blob06f161fe81e95f21301325c52bb25c21cc8ed985
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, MESSAGE_NS_UNSIGNED)) {
59 err = IE_ERROR;
60 goto leave;
63 /* Get status code */
64 result = xmlXPathEvalExpression(status_code_expr, xpath_ctx);
65 if (!result) {
66 err = IE_ERROR;
67 goto leave;
69 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
70 isds_log_message(context,
71 _("ISDS response is missing StatusCode element"));
72 err = IE_ISDS;
73 goto leave;
75 *code = xmlXPathCastNodeSetToString(result->nodesetval);
76 if (!code) {
77 err = IE_ERROR;
78 goto leave;
81 if (message) {
82 /* Get status message */
83 xmlXPathFreeObject(result);
84 result = xmlXPathEvalExpression(status_message_expr, xpath_ctx);
85 if (!result) {
86 err = IE_ERROR;
87 goto leave;
89 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
90 /* E.g. CreateMessageResponse with dmStatusCode 9005 has empty
91 * message */
92 *message = NULL;
93 } else {
94 *message = xmlXPathCastNodeSetToString(result->nodesetval);
95 if (!message) {
96 err = IE_ERROR;
97 goto leave;
102 if (refnumber) {
103 /* Get reference number of client request */
104 zfree(*refnumber);
105 xmlXPathFreeObject(result);
106 result = xmlXPathEvalExpression(
107 BAD_CAST "/*/isds:dbStatus/isds:dbStatusRefNumber/text()",
108 xpath_ctx);
109 if (!result) {
110 err = IE_ERROR;
111 goto leave;
113 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
114 *refnumber = NULL;
115 } else {
116 *refnumber = xmlXPathCastNodeSetToString(result->nodesetval);
117 if (!message) {
118 err = IE_ERROR;
119 goto leave;
123 leave:
124 xmlXPathFreeObject(result);
125 xmlXPathFreeContext(xpath_ctx);
126 return err;
130 /* Send @request to ISDS and return ISDS @response as XML document.
131 * Be ware the @response can be invalid (in sense of XML Schema).
132 * (And it is because current ISDS server does not follow its own
133 * specification. Please appology my government, its herd of imcompetent
134 * creatures.)
135 * @context is ISDS session context,
136 * @service identifies ISDS web service
137 * @request is tree with ISDS message, can be NULL
138 * @response is automatically allocated response from server as XML Document
139 * @raw_response is automatically allocated bitstream with response body. Use
140 * NULL if you don't care
141 * @raw_response_length is size of @raw_response in bytes
142 * In case of error, @response and @raw_response will be dealocated.
143 * */
144 _hidden isds_error isds(struct isds_ctx *context, const isds_service service,
145 const xmlNodePtr request, xmlDocPtr *response,
146 void **raw_response, size_t *raw_response_length) {
147 isds_error err = IE_SUCCESS;
148 xmlNodePtr response_body = NULL, isds_node;
149 char *file = NULL;
151 if (!context) return IE_INVALID_CONTEXT;
152 if (!response) return IE_INVAL;
153 if (!raw_response_length && raw_response) return IE_INVAL;
155 switch (service) {
156 case SERVICE_DM_OPERATIONS: file = "dz"; break;
157 case SERVICE_DM_INFO: file = "dx"; break;
158 case SERVICE_DB_SEARCH: file = "df"; break;
159 case SERVICE_DB_ACCESS: file = "DsManage"; break;
160 case SERVICE_DB_MANIPULATION: file = "DsManage"; break;
161 default: return (IE_INVAL);
164 err = soap(context, file, request, &response_body,
165 raw_response, raw_response_length);
167 if (err) goto leave;
169 if (!response_body) {
170 isds_log_message(context, _("SOAP returned empty body"));
171 err = IE_ISDS;
174 /* Find ISDS element */
175 for (isds_node = response_body; isds_node; isds_node = isds_node->next) {
176 if (isds_node->type == XML_ELEMENT_NODE &&
177 isds_node->ns &&
178 !xmlStrcmp(isds_node->ns->href, BAD_CAST ISDS_NS))
179 break;
181 if (!isds_node) {
182 isds_log_message(context,
183 _("SOAP response does not contain ISDS element"));
184 err = IE_ISDS;
185 goto leave;
188 /* Destroy other nodes */
189 if (isds_node == response_body)
190 response_body = response_body->next;
191 xmlUnlinkNode(isds_node);
192 xmlFreeNodeList(response_body);
193 response_body = NULL;
195 /* TODO: validate the response */
197 /* Build XML document */
198 *response = xmlNewDoc(BAD_CAST "1.0");
199 if (!*response) {
200 isds_log_message(context, _("Could not build ISDS response document"));
201 err = IE_ERROR;
202 goto leave;
204 xmlDocSetRootElement(*response, isds_node);
206 leave:
207 if (err) {
208 xmlFreeDoc(*response);
209 if (raw_response) zfree(*raw_response);
211 xmlFreeNodeList(response_body);
213 return err;
216 /* Walk through list of isds_documents and check for their types and
217 * references.
218 * @context is session context
219 * @documents is list of isds_document to check
220 * @returns IE_SUCCESS if structure is valid, otherwise context' message will
221 * be filled with explanation of found problem. */
222 _hidden isds_error check_documents_hierarchy(struct isds_ctx *context,
223 const struct isds_list *documents) {
225 const struct isds_list *item;
226 const struct isds_document *document;
227 _Bool main_exists = 0;
229 if (!context) return IE_INVALID_CONTEXT;
230 if (!documents) return IE_INVAL;
232 for (item = documents; item; item = item->next) {
233 document = (const struct isds_document *) item->data;
234 if (!document) continue;
236 /* Only one document can be main */
237 if (document->dmFileMetaType == FILEMETATYPE_MAIN) {
238 if (main_exists) {
239 isds_log_message(context,
240 _("List contains more main documents"));
241 return IE_ERROR;
243 main_exists = 1;
246 /* All document identifiers should be unique */
247 if (document->dmFileGuid) {
248 if (isds_find_document_by_id(documents, document->dmFileGuid) !=
249 document) {
250 isds_printf_message(context, _("List contains more documents "
251 "with the same ID `%s'"), document->dmFileGuid);
252 return IE_ERROR;
256 /* All document references should point to existing document ID */
257 /* ???: Should we forbid self-refencing? */
258 if (document->dmUpFileGuid) {
259 if (!isds_find_document_by_id(documents,
260 document->dmUpFileGuid)) {
261 isds_printf_message(context, _("List contains documents "
262 "referencing to unexisting document ID `%s'"),
263 document->dmUpFileGuid);
264 return IE_ERROR;
269 if (!main_exists) {
270 isds_log_message(context, _("List does not contain main document"));
271 return IE_ERROR;
274 return IE_SUCCESS;
278 /* Check for message ID length
279 * @context is session context
280 * @message_id checked message ID
281 * @return IE_SUCCESS or appropriate error code and fill context' message */
282 isds_error validate_message_id_length(struct isds_ctx *context,
283 const xmlChar *message_id) {
284 if (!context) return IE_INVALID_CONTEXT;
285 if (!message_id) return IE_INVAL;
287 const int length = xmlUTF8Strlen(message_id);
289 if (length == -1) {
290 char *message_id_locale = utf82locale((char*) message_id);
291 isds_printf_message(context,
292 _("Could not check message ID length: %s"),
293 message_id_locale);
294 free(message_id_locale);
295 return IE_ERROR;
298 if (length >= 20) {
299 char *message_id_locale = utf82locale((char*) message_id);
300 isds_printf_message(context,
301 _("Message ID must not be longer than 20 characters: %s"),
302 message_id_locale);
303 free(message_id_locale);
304 return IE_INVAL;
307 return IE_SUCCESS;