Make cql_transform_rpn2cql_{stream,wrbuf} thread-safe
[yaz-moved-to-github.git] / src / soap.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) Index Data
3  * See the file LICENSE for details.
4  */
5 /**
6  * \file soap.c
7  * \brief Implements SOAP
8  *
9  * This implements encoding and decoding of SOAP packages using
10  * Libxml2.
11  */
12 #if HAVE_CONFIG_H
13 #include <config.h>
14 #endif
15
16 #include <yaz/soap.h>
17 #include <yaz/match_glob.h>
18
19 #if YAZ_HAVE_XML2
20 #include <libxml/parser.h>
21 #include <libxml/tree.h>
22
23 static const char *soap_v1_1 = "http://schemas.xmlsoap.org/soap/envelope/";
24 static const char *soap_v1_2 = "http://www.w3.org/2001/06/soap-envelope";
25
26 int z_soap_codec_enc_xsl(ODR o, Z_SOAP **pp,
27                          char **content_buf, int *content_len,
28                          Z_SOAP_Handler *handlers,
29                          const char *encoding,
30                          const char *stylesheet)
31 {
32     if (o->direction == ODR_DECODE)
33     {
34         Z_SOAP *p;
35         xmlNodePtr ptr, pptr;
36         xmlDocPtr doc;
37         int i, ret;
38
39         if (!content_buf || !*content_buf || !content_len)
40             return -1;
41
42         *pp = p = (Z_SOAP *) odr_malloc(o, sizeof(*p));
43         p->ns = soap_v1_1;
44
45         doc = xmlParseMemory(*content_buf, *content_len);
46         if (!doc)
47             return z_soap_error(o, p, "SOAP-ENV:Client",
48                                 "Bad XML Document", 0);
49
50         ptr = xmlDocGetRootElement(doc);
51         if (!ptr || ptr->type != XML_ELEMENT_NODE || !ptr->ns)
52         {
53             xmlFreeDoc(doc);
54             return z_soap_error(o, p, "SOAP-ENV:Client",
55                                 "No Envelope element", 0);
56         }
57         /* check for SRU root node match */
58         for (i = 0; handlers[i].ns; i++)
59         {
60             const char *hns = handlers[i].ns;
61             if (strchr(hns, ':'))
62             {
63                 if (yaz_match_glob(hns, (const char *) ptr->ns->href))
64                     break;
65             }
66             else
67             {
68                 if (yaz_match_glob(hns, (const char *) ptr->name))
69                     break;
70             }
71         }
72         if (handlers[i].ns)
73         {
74             void *handler_data = 0;
75             xmlNode p_top_tmp; /* pseudo parent node needed */
76
77             p_top_tmp.children = ptr;
78             ret = (*handlers[i].f)(o, &p_top_tmp, &handler_data,
79                                    handlers[i].client_data,
80                                    (const char *)ptr->ns->href);
81
82             if (ret || !handler_data)
83                 z_soap_error(o, p, "SOAP-ENV:Client",
84                              "SOAP Handler returned error", 0);
85             else
86             {
87                 p->which = Z_SOAP_generic;
88                 p->u.generic = (Z_SOAP_Generic *)
89                     odr_malloc(o, sizeof(*p->u.generic));
90                 p->u.generic->no = i;
91                 p->u.generic->ns = handlers[i].ns;
92                 p->u.generic->p = handler_data;
93             }
94             xmlFreeDoc(doc);
95             return ret;
96         }
97         /* OK: assume SOAP */
98         if (xmlStrcmp(ptr->name, BAD_CAST "Envelope"))
99         {
100             xmlFreeDoc(doc);
101             return z_soap_error(o, p, "SOAP-ENV:Client",
102                                 "No Envelope element", 0);
103         }
104         else
105         {
106             /* determine SOAP version */
107             const char * ns_envelope = (const char *) ptr->ns->href;
108             if (!strcmp(ns_envelope, soap_v1_1))
109                 p->ns = soap_v1_1;
110             else if (!strcmp(ns_envelope, soap_v1_2))
111                 p->ns = soap_v1_2;
112             else
113             {
114                 xmlFreeDoc(doc);
115                 return z_soap_error(o, p, "SOAP-ENV:Client",
116                                     "Bad SOAP version", 0);
117             }
118         }
119         ptr = ptr->children;
120         while(ptr && ptr->type == XML_TEXT_NODE)
121             ptr = ptr->next;
122         if (ptr && ptr->type == XML_ELEMENT_NODE &&
123             !xmlStrcmp(ptr->ns->href, BAD_CAST p->ns) &&
124             !xmlStrcmp(ptr->name, BAD_CAST "Header"))
125         {
126             ptr = ptr->next;
127             while(ptr && ptr->type == XML_TEXT_NODE)
128                 ptr = ptr->next;
129         }
130         /* check that Body is present */
131         if (!ptr || ptr->type != XML_ELEMENT_NODE ||
132             xmlStrcmp(ptr->name, BAD_CAST "Body"))
133         {
134             xmlFreeDoc(doc);
135             return z_soap_error(o, p, "SOAP-ENV:Client",
136                                 "SOAP Body element not found", 0);
137         }
138         if (xmlStrcmp(ptr->ns->href, BAD_CAST p->ns))
139         {
140             xmlFreeDoc(doc);
141             return z_soap_error(o, p, "SOAP-ENV:Client",
142                                 "SOAP bad NS for Body element", 0);
143         }
144         pptr = ptr;
145         ptr = ptr->children;
146         while (ptr && ptr->type == XML_TEXT_NODE)
147             ptr = ptr->next;
148         if (!ptr || ptr->type != XML_ELEMENT_NODE)
149         {
150             xmlFreeDoc(doc);
151             return z_soap_error(o, p, "SOAP-ENV:Client",
152                                 "SOAP No content for Body", 0);
153         }
154         if (!ptr->ns)
155         {
156             xmlFreeDoc(doc);
157             return z_soap_error(o, p, "SOAP-ENV:Client",
158                                 "SOAP No namespace for content", 0);
159         }
160         /* check for fault package */
161         if (!xmlStrcmp(ptr->ns->href, BAD_CAST p->ns)
162             && !xmlStrcmp(ptr->name, BAD_CAST "Fault") && ptr->children)
163         {
164             ptr = ptr->children;
165
166             p->which = Z_SOAP_fault;
167             p->u.fault = (Z_SOAP_Fault *) odr_malloc(o, sizeof(*p->u.fault));
168             p->u.fault->fault_code = 0;
169             p->u.fault->fault_string = 0;
170             p->u.fault->details = 0;
171             while (ptr)
172             {
173                 if (ptr->children && ptr->children->type == XML_TEXT_NODE)
174                 {
175                     if (!xmlStrcmp(ptr->name, BAD_CAST "faultcode"))
176                         p->u.fault->fault_code =
177                             odr_strdup(o, (const char *)
178                                        ptr->children->content);
179                     if (!xmlStrcmp(ptr->name, BAD_CAST "faultstring"))
180                         p->u.fault->fault_string =
181                             odr_strdup(o, (const char *)
182                                        ptr->children->content);
183                     if (!xmlStrcmp(ptr->name, BAD_CAST "details"))
184                         p->u.fault->details =
185                             odr_strdup(o, (const char *)
186                                        ptr->children->content);
187                 }
188                 ptr = ptr->next;
189             }
190             ret = 0;
191         }
192         else
193         {
194             const char *ns = (const char *) ptr->ns->href;
195             for (i = 0; handlers[i].ns; i++)
196             {
197                 if (strchr(handlers[i].ns, ':') &&
198                     yaz_match_glob(handlers[i].ns, ns))
199                     break;
200             }
201             if (handlers[i].ns)
202             {
203                 void *handler_data = 0;
204                 ret = (*handlers[i].f)(o, pptr, &handler_data,
205                                        handlers[i].client_data, ns);
206                 if (ret || !handler_data)
207                     z_soap_error(o, p, "SOAP-ENV:Client",
208                                  "SOAP Handler returned error", 0);
209                 else
210                 {
211                     p->which = Z_SOAP_generic;
212                     p->u.generic = (Z_SOAP_Generic *)
213                         odr_malloc(o, sizeof(*p->u.generic));
214                     p->u.generic->no = i;
215                     p->u.generic->ns = handlers[i].ns;
216                     p->u.generic->p = handler_data;
217                 }
218             }
219             else
220             {
221                 ret = z_soap_error(o, p, "SOAP-ENV:Client",
222                                    "No handler for NS", ns);
223             }
224         }
225         xmlFreeDoc(doc);
226         return ret;
227     }
228     else if (o->direction == ODR_ENCODE)
229     {
230         Z_SOAP *p = *pp;
231         xmlNsPtr ns_env;
232         xmlNodePtr envelope_ptr, body_ptr;
233
234         xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
235
236         envelope_ptr = xmlNewNode(0, BAD_CAST "Envelope");
237         ns_env = xmlNewNs(envelope_ptr, BAD_CAST p->ns,
238                           BAD_CAST "SOAP-ENV");
239         xmlSetNs(envelope_ptr, ns_env);
240
241         body_ptr = xmlNewChild(envelope_ptr, ns_env, BAD_CAST "Body",
242                                0);
243         xmlDocSetRootElement(doc, envelope_ptr);
244
245         if (p->which == Z_SOAP_fault || p->which == Z_SOAP_error)
246         {
247             Z_SOAP_Fault *f = p->u.fault;
248             xmlNodePtr fault_ptr = xmlNewChild(body_ptr, ns_env,
249                                                BAD_CAST "Fault", 0);
250             xmlNewChild(fault_ptr, ns_env, BAD_CAST "faultcode",
251                         BAD_CAST f->fault_code);
252             xmlNewChild(fault_ptr, ns_env, BAD_CAST "faultstring",
253                         BAD_CAST f->fault_string);
254             if (f->details)
255                 xmlNewChild(fault_ptr, ns_env, BAD_CAST "details",
256                             BAD_CAST f->details);
257         }
258         else if (p->which == Z_SOAP_generic)
259         {
260             int ret, no = p->u.generic->no;
261
262             ret = (*handlers[no].f)(o, body_ptr, &p->u.generic->p,
263                                     handlers[no].client_data,
264                                     handlers[no].ns);
265             if (ret)
266             {
267                 xmlFreeDoc(doc);
268                 return ret;
269             }
270         }
271         if (p->which == Z_SOAP_generic && !strcmp(p->ns, "SRU"))
272         {
273             xmlDocSetRootElement(doc, body_ptr->children);
274             body_ptr->children = 0;
275             xmlFreeNode(envelope_ptr);
276         }
277         if (stylesheet)
278         {
279             char *content = (char *) odr_malloc(o, strlen(stylesheet) + 40);
280
281             xmlNodePtr pi, ptr = xmlDocGetRootElement(doc);
282             sprintf(content, "type=\"text/xsl\" href=\"%s\"", stylesheet);
283             pi = xmlNewPI(BAD_CAST "xml-stylesheet",
284                           BAD_CAST content);
285             xmlAddPrevSibling(ptr, pi);
286         }
287         if (1)
288         {
289             xmlChar *buf_out;
290             int len_out;
291             if (encoding)
292                 xmlDocDumpMemoryEnc(doc, &buf_out, &len_out, encoding);
293             else
294                 xmlDocDumpMemory(doc, &buf_out, &len_out);
295             *content_buf = (char *) odr_malloc(o, len_out);
296             *content_len = len_out;
297             memcpy(*content_buf, buf_out, len_out);
298             xmlFree(buf_out);
299         }
300         xmlFreeDoc(doc);
301         return 0;
302     }
303     return 0;
304 }
305 #else
306 int z_soap_codec_enc_xsl(ODR o, Z_SOAP **pp,
307                          char **content_buf, int *content_len,
308                          Z_SOAP_Handler *handlers, const char *encoding,
309                          const char *stylesheet)
310 {
311     static char *err_xml =
312         "<?xml version=\"1.0\"?>\n"
313         "<SOAP-ENV:Envelope"
314         " xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
315         "\t<SOAP-ENV:Body>\n"
316         "\t\t<SOAP-ENV:Fault>\n"
317         "\t\t\t<faultcode>SOAP-ENV:Server</faultcode>\n"
318         "\t\t\t<faultstring>HTTP error</faultstring>\n"
319         "\t\t\t<detail>SOAP not supported in this YAZ configuration</detail>\n"
320         "\t\t</SOAP-ENV:Fault>\n"
321         "\t</SOAP-ENV:Body>\n"
322         "</SOAP-ENV:Envelope>\n";
323     if (o->direction == ODR_ENCODE)
324     {
325         *content_buf = err_xml;
326         *content_len = strlen(err_xml);
327     }
328     return -1;
329 }
330 #endif
331 int z_soap_codec_enc(ODR o, Z_SOAP **pp,
332                      char **content_buf, int *content_len,
333                      Z_SOAP_Handler *handlers,
334                      const char *encoding)
335 {
336     return z_soap_codec_enc_xsl(o, pp, content_buf, content_len, handlers,
337                                 encoding, 0);
338 }
339
340 int z_soap_codec(ODR o, Z_SOAP **pp,
341                  char **content_buf, int *content_len,
342                  Z_SOAP_Handler *handlers)
343 {
344     return z_soap_codec_enc(o, pp, content_buf, content_len, handlers, 0);
345 }
346
347 int z_soap_error(ODR o, Z_SOAP *p,
348                  const char *fault_code, const char *fault_string,
349                  const char *details)
350 {
351     p->which = Z_SOAP_error;
352     p->u.soap_error = (Z_SOAP_Fault *)
353         odr_malloc(o, sizeof(*p->u.soap_error));
354     p->u.soap_error->fault_code = odr_strdup(o, fault_code);
355     p->u.soap_error->fault_string = odr_strdup(o, fault_string);
356     if (details)
357         p->u.soap_error->details = odr_strdup(o, details);
358     else
359         p->u.soap_error->details = 0;
360     return -1;
361 }
362
363 /*
364  * Local variables:
365  * c-basic-offset: 4
366  * c-file-style: "Stroustrup"
367  * indent-tabs-mode: nil
368  * End:
369  * vim: shiftwidth=4 tabstop=8 expandtab
370  */
371