doc: Update list of user web service according to spec. 2009-10-30
[libisds.git] / src / validator.c
blob9aae3a6e74de499018f51f05ec28a7d55d4867d2
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 allocated status reference number. Returned
17 * NULL means no referce was delivered by server. Use NULL if you don't care.
18 * */
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_SUPPLEMENTARY:
42 status_code_expr = BAD_CAST
43 "/*/isds:dbStatus/isds:dbStatusCode/text()";
44 status_message_expr = BAD_CAST
45 "/*/isds:dbStatus/isds:dbStatusMessage/text()";
46 break;
47 default:
48 err = IE_NOTSUP;
49 goto leave;
52 xpath_ctx = xmlXPathNewContext(response);
53 if (!xpath_ctx) {
54 err = IE_ERROR;
55 goto leave;
57 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
58 err = IE_ERROR;
59 goto leave;
62 /* Get status code */
63 result = xmlXPathEvalExpression(status_code_expr, xpath_ctx);
64 if (!result) {
65 err = IE_ERROR;
66 goto leave;
68 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
69 isds_log_message(context, _("ISDS response is missing StatusCode"));
70 err = IE_ISDS;
71 goto leave;
73 *code = xmlXPathCastNodeSetToString(result->nodesetval);
74 if (!code) {
75 err = IE_ERROR;
76 goto leave;
79 if (message) {
80 /* Get status message */
81 xmlXPathFreeObject(result);
82 result = xmlXPathEvalExpression(status_message_expr, xpath_ctx);
83 if (!result) {
84 err = IE_ERROR;
85 goto leave;
87 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
88 /* E.g. CreateMessageResponse with dmStatusCode 9005 has empty
89 * message */
90 *message = NULL;
91 } else {
92 *message = xmlXPathCastNodeSetToString(result->nodesetval);
93 if (!message) {
94 err = IE_ERROR;
95 goto leave;
100 if (refnumber) {
101 /* Get status reference number */
102 xmlXPathFreeObject(result);
103 result = xmlXPathEvalExpression(
104 BAD_CAST "/*/isds:dbStatus/isds:dbStatusRefNumber/text()",
105 xpath_ctx);
106 if (!result) {
107 err = IE_ERROR;
108 goto leave;
110 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
111 *refnumber = NULL;
112 } else {
113 *refnumber = xmlXPathCastNodeSetToString(result->nodesetval);
114 if (!message) {
115 err = IE_ERROR;
116 goto leave;
120 leave:
121 xmlXPathFreeObject(result);
122 xmlXPathFreeContext(xpath_ctx);
123 return err;
127 /* Send @request to ISDS and return ISDS @response as XML document.
128 * Be ware the @response can be invalid (in sense of XML Schema).
129 * (And it is because current ISDS server does not follow its own
130 * specification. Please appology my government, its herd of imcompetent
131 * creatures.)
132 * @context is ISDS session context,
133 * @service identifies ISDS web service
134 * @request is tree with ISDS message, can be NULL
135 * @response is automatically allocated response from server as XML Document
136 * @raw_response is automatically allocated bitstream with response body. Use
137 * NULL if you don't care
138 * @raw_response_length is size of @raw_response in bytes
139 * In case of error, @response and @raw_response will be dealocated.
140 * */
141 _hidden isds_error isds(struct isds_ctx *context, const isds_service service,
142 const xmlNodePtr request, xmlDocPtr *response,
143 void **raw_response, size_t *raw_response_length) {
144 isds_error err = IE_SUCCESS;
145 xmlNodePtr response_body = NULL, isds_node;
146 char *file = NULL;
148 if (!context) return IE_INVALID_CONTEXT;
149 if (!response) return IE_INVAL;
150 if (!raw_response_length && raw_response) return IE_INVAL;
152 switch (service) {
153 case SERVICE_DM_OPERATIONS: file = "dz"; break;
154 case SERVICE_DM_INFO: file = "dx"; break;
155 case SERVICE_DB_SEARCH: file = "df"; break;
156 case SERVICE_DB_SUPPLEMENTARY: file = "DsManage"; break;
157 default: return (IE_INVAL);
160 err = soap(context, file, request, &response_body,
161 raw_response, raw_response_length);
163 if (err) goto leave;
165 if (!response_body) {
166 isds_log_message(context, _("SOAP returned empty body"));
167 err = IE_ISDS;
170 /* Find ISDS element */
171 for (isds_node = response_body; isds_node; isds_node = isds_node->next) {
172 if (isds_node->type == XML_ELEMENT_NODE &&
173 isds_node->ns &&
174 !xmlStrcmp(isds_node->ns->href, BAD_CAST ISDS_NS))
175 break;
177 if (!isds_node) {
178 isds_log_message(context,
179 _("SOAP response does not contain ISDS elemement"));
180 err = IE_ISDS;
181 goto leave;
184 /* Destroy other nodes */
185 if (isds_node == response_body)
186 response_body = response_body->next;
187 xmlUnlinkNode(isds_node);
188 xmlFreeNodeList(response_body);
189 response_body = NULL;
191 /* TODO: validate the response */
193 /* Build XML document */
194 *response = xmlNewDoc(BAD_CAST "1.0");
195 if (!*response) {
196 isds_log_message(context, _("Could not build ISDS response document"));
197 err = IE_ERROR;
198 goto leave;
200 xmlDocSetRootElement(*response, isds_node);
202 leave:
203 if (err) {
204 xmlFreeDoc(*response);
205 if (raw_response) zfree(*raw_response);
207 xmlFreeNodeList(response_body);
209 return err;
212 /* Walk through list of isds_documents and check for their types and
213 * references.
214 * @context is session context
215 * @documents is list of isds_document to check
216 * @returns IE_SUCCESS if structure is valid, otherwise context' message will
217 * be filled with explanation of found problem. */
218 _hidden isds_error check_documents_hierarchy(struct isds_ctx *context,
219 const struct isds_list *documents) {
221 const struct isds_list *item;
222 const struct isds_document *document;
223 _Bool main_exists = 0;
225 if (!context) return IE_INVALID_CONTEXT;
226 if (!documents) return IE_INVAL;
228 for (item = documents; item; item = item->next) {
229 document = (const struct isds_document *) item->data;
230 if (!document) continue;
232 /* Only one document can be main */
233 if (document->dmFileMetaType == FILEMETATYPE_MAIN) {
234 if (main_exists) {
235 isds_log_message(context,
236 _("List contains more main documents"));
237 return IE_ERROR;
239 main_exists = 1;
242 /* All document identifiers should be unique */
243 if (document->dmFileGuid) {
244 if (isds_find_document_by_id(documents, document->dmFileGuid) !=
245 document) {
246 isds_printf_message(context, _("List contains more documents "
247 "with the same ID `%s'"), document->dmFileGuid);
248 return IE_ERROR;
252 /* All document references should point to existing document ID */
253 /* ???: Should we forbid self-refencing? */
254 if (document->dmUpFileGuid) {
255 if (!isds_find_document_by_id(documents,
256 document->dmUpFileGuid)) {
257 isds_printf_message(context, _("List contains documents "
258 "referencing to unexisting document ID `%s'"),
259 document->dmUpFileGuid);
260 return IE_ERROR;
265 if (!main_exists) {
266 isds_log_message(context, _("List does not contain main document"));
267 return IE_ERROR;
270 return IE_SUCCESS;
274 /* Check for message ID length
275 * @context is session context
276 * @message_id checked message ID
277 * @return IE_SUCCESS or appropriate error code and fill context' message */
278 isds_error validate_message_id_length(struct isds_ctx *context,
279 const xmlChar *message_id) {
280 if (!context) return IE_INVALID_CONTEXT;
281 if (!message_id) return IE_INVAL;
283 const int length = xmlUTF8Strlen(message_id);
285 if (length == -1) {
286 char *message_id_locale = utf82locale((char*) message_id);
287 isds_printf_message(context,
288 _("Could not check message ID length: %s"),
289 message_id_locale);
290 free(message_id_locale);
291 return IE_ERROR;
294 if (length >= 20) {
295 char *message_id_locale = utf82locale((char*) message_id);
296 isds_printf_message(context,
297 _("Message ID must not be longer than 20 characters: %s"),
298 message_id_locale);
299 free(message_id_locale);
300 return IE_INVAL;
303 return IE_SUCCESS;