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