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