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