8a10195ef012153e0b3ea7d73f8f65c08c332c79
[yaz-moved-to-github.git] / src / soap.c
1 /*
2  * Copyright (c) 2002-2004, Index Data.
3  * See the file LICENSE for details.
4  *
5  * $Id: soap.c,v 1.9 2004-10-15 00:19:00 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         /* check that root node is Envelope */
48         ptr = xmlDocGetRootElement(doc);
49         if (!ptr || ptr->type != XML_ELEMENT_NODE ||
50             strcmp(ptr->name, "Envelope") || !ptr->ns)
51         {
52             xmlFreeDoc(doc);
53             return z_soap_error(o, p, "SOAP-ENV:Client",
54                                 "No Envelope element", 0);
55         }
56         else
57         {
58             /* determine SOAP version */
59             const char * ns_envelope = ptr->ns->href;
60             if (!strcmp(ns_envelope, soap_v1_1))
61                 p->ns = soap_v1_1;
62             else if (!strcmp(ns_envelope, soap_v1_2))
63                 p->ns = soap_v1_2;
64             else
65             {
66                 xmlFreeDoc(doc);
67                 return z_soap_error(o, p, "SOAP-ENV:Client",
68                                     "Bad SOAP version", 0);
69             }
70         }
71         ptr = ptr->children;
72         while(ptr && ptr->type == XML_TEXT_NODE)
73             ptr = ptr->next;
74         if (ptr && ptr->type == XML_ELEMENT_NODE &&
75             !strcmp(ptr->ns->href, p->ns) &&
76             !strcmp(ptr->name, "Header"))
77         {
78             ptr = ptr->next;
79             while(ptr && ptr->type == XML_TEXT_NODE)
80                 ptr = ptr->next;
81         }
82         /* check that Body is present */
83         if (!ptr || ptr->type != XML_ELEMENT_NODE || 
84             strcmp(ptr->name, "Body"))
85         {
86             xmlFreeDoc(doc);
87             return z_soap_error(o, p, "SOAP-ENV:Client",
88                                 "SOAP Body element not found", 0);
89         }
90         if (strcmp(ptr->ns->href, p->ns))
91         {
92             xmlFreeDoc(doc);
93             return z_soap_error(o, p, "SOAP-ENV:Client",
94                                 "SOAP bad NS for Body element", 0);
95         }
96         pptr = ptr;
97         ptr = ptr->children;
98         while (ptr && ptr->type == XML_TEXT_NODE)
99             ptr = ptr->next;
100         if (!ptr || ptr->type != XML_ELEMENT_NODE)
101         {
102             xmlFreeDoc(doc);
103             return z_soap_error(o, p, "SOAP-ENV:Client",
104                                 "SOAP No content for Body", 0);
105         }
106         if (!ptr->ns)
107         {
108             xmlFreeDoc(doc);
109             return z_soap_error(o, p, "SOAP-ENV:Client",
110                                 "SOAP No namespace for content", 0);
111         }
112         /* check for fault package */
113         if (!strcmp(ptr->ns->href, p->ns)
114             && !strcmp(ptr->name, "Fault") && ptr->children)
115         {
116             ptr = ptr->children;
117
118             p->which = Z_SOAP_fault;
119             p->u.fault = (Z_SOAP_Fault *) odr_malloc(o, sizeof(*p->u.fault));
120             p->u.fault->fault_code = 0;
121             p->u.fault->fault_string = 0;
122             p->u.fault->details = 0;
123             while (ptr)
124             {
125                 if (ptr->children && ptr->children->type == XML_TEXT_NODE)
126                 {
127                     if (!strcmp(ptr->name, "faultcode"))
128                         p->u.fault->fault_code =
129                             odr_strdup(o, ptr->children->content);
130                     if (!strcmp(ptr->name, "faultstring"))
131                         p->u.fault->fault_string =
132                             odr_strdup(o, ptr->children->content);
133                     if (!strcmp(ptr->name, "details"))
134                         p->u.fault->details =
135                             odr_strdup(o, ptr->children->content);
136                 }
137                 ptr = ptr->next;
138             }
139             ret = 0;
140         }
141         else
142         {
143             for (i = 0; handlers[i].ns; i++)
144                 if (!strcmp(ptr->ns->href, handlers[i].ns))
145                     break;
146             if (handlers[i].ns)
147             {
148                 void *handler_data = 0;
149                 ret = (*handlers[i].f)(o, pptr, &handler_data,
150                                        handlers[i].client_data,
151                                        handlers[i].ns);
152                 if (ret || !handler_data)
153                     z_soap_error(o, p, "SOAP-ENV:Client",
154                                  "SOAP Handler returned error", 0);
155                 else
156                 {
157                     p->which = Z_SOAP_generic;
158                     p->u.generic = (Z_SOAP_Generic *)
159                         odr_malloc(o, sizeof(*p->u.generic));
160                     p->u.generic->no = i;
161                     p->u.generic->ns = handlers[i].ns;
162                     p->u.generic->p = handler_data;
163                 }
164             }
165             else
166             {
167                 ret = z_soap_error(o, p, "SOAP-ENV:Client", 
168                                    "No handler for NS", ptr->ns->href);
169             }
170         }
171         xmlFreeDoc(doc);
172         return ret;
173     }
174     else if (o->direction == ODR_ENCODE)
175     {
176         Z_SOAP *p = *pp;
177         xmlNsPtr ns_env;
178         xmlNodePtr envelope_ptr, body_ptr;
179
180         xmlDocPtr doc = xmlNewDoc("1.0");
181
182         envelope_ptr = xmlNewNode(0, "Envelope");
183         ns_env = xmlNewNs(envelope_ptr, p->ns, "SOAP-ENV");
184         xmlSetNs(envelope_ptr, ns_env);
185
186         body_ptr = xmlNewChild(envelope_ptr, ns_env, "Body", 0);
187         xmlDocSetRootElement(doc, envelope_ptr);
188
189         if (p->which == Z_SOAP_fault || p->which == Z_SOAP_error)
190         {
191             Z_SOAP_Fault *f = p->u.fault;
192             xmlNodePtr fault_ptr = xmlNewChild(body_ptr, ns_env, "Fault", 0);
193             xmlNewChild(fault_ptr, ns_env, "faultcode",  f->fault_code);
194             xmlNewChild(fault_ptr, ns_env, "faultstring", f->fault_string);
195             if (f->details)
196                 xmlNewChild(fault_ptr, ns_env, "details", f->details);
197         }
198         else if (p->which == Z_SOAP_generic)
199         {
200             int ret, no = p->u.generic->no;
201             
202             ret = (*handlers[no].f)(o, body_ptr, &p->u.generic->p,
203                                     handlers[no].client_data,
204                                     handlers[no].ns);
205             if (ret)
206             {
207                 xmlFreeDoc(doc);
208                 return ret;
209             }
210         }
211         if (p->which == Z_SOAP_generic && !strcmp(p->ns, "SRU"))
212         {
213             xmlDocSetRootElement(doc, body_ptr->children);
214             body_ptr->children = 0;
215             xmlFreeNode(envelope_ptr);
216         }
217         if (stylesheet)
218         {
219             char *content = odr_malloc(o, strlen(stylesheet) + 40);
220             
221             xmlNodePtr pi, ptr = xmlDocGetRootElement(doc);
222             sprintf(content, "type=\"text/xsl\" href=\"%s\"", stylesheet);
223             pi = xmlNewPI("xml-stylesheet", content);
224             xmlAddPrevSibling(ptr, pi);
225         }
226         if (1)
227         {
228             xmlChar *buf_out;
229             int len_out;
230             if (encoding)
231                 xmlDocDumpMemoryEnc(doc, &buf_out, &len_out, encoding);
232             else
233                 xmlDocDumpMemory(doc, &buf_out, &len_out);
234             *content_buf = (char *) odr_malloc(o, len_out);
235             *content_len = len_out;
236             memcpy(*content_buf, buf_out, len_out);
237             xmlFree(buf_out);
238         }
239         xmlFreeDoc(doc);
240         return 0;
241     }
242     return 0;
243 }
244 #else
245 int z_soap_codec_enc_xsl(ODR o, Z_SOAP **pp, 
246                          char **content_buf, int *content_len,
247                          Z_SOAP_Handler *handlers, const char *encoding,
248                          const char *stylesheet)
249 {
250     static char *err_xml =
251         "<?xml version=\"1.0\"?>\n"
252         "<SOAP-ENV:Envelope"
253         " xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
254         "\t<SOAP-ENV:Body>\n"
255         "\t\t<SOAP-ENV:Fault>\n"
256         "\t\t\t<faultcode>SOAP-ENV:Server</faultcode>\n"
257         "\t\t\t<faultstring>HTTP error</faultstring>\n"
258         "\t\t\t<detail>SOAP not supported in this YAZ configuration</detail>\n"
259         "\t\t</SOAP-ENV:Fault>\n"
260         "\t</SOAP-ENV:Body>\n"
261         "</SOAP-ENV:Envelope>\n";
262     if (o->direction == ODR_ENCODE)
263     {
264         *content_buf = err_xml;
265         *content_len = strlen(err_xml);
266     }
267     return -1;
268 }
269 #endif
270 int z_soap_codec_enc(ODR o, Z_SOAP **pp, 
271                      char **content_buf, int *content_len,
272                      Z_SOAP_Handler *handlers,
273                      const char *encoding)
274 {
275     return z_soap_codec_enc_xsl(o, pp, content_buf, content_len, handlers,
276                                 encoding, 0);
277 }
278
279 int z_soap_codec(ODR o, Z_SOAP **pp, 
280                  char **content_buf, int *content_len,
281                  Z_SOAP_Handler *handlers)
282 {
283     return z_soap_codec_enc(o, pp, content_buf, content_len, handlers, 0);
284 }
285
286 int z_soap_error(ODR o, Z_SOAP *p,
287                  const char *fault_code, const char *fault_string,
288                  const char *details)
289 {
290     p->which = Z_SOAP_error;
291     p->u.soap_error = (Z_SOAP_Fault *) 
292         odr_malloc(o, sizeof(*p->u.soap_error));
293     p->u.soap_error->fault_code = odr_strdup(o, fault_code);
294     p->u.soap_error->fault_string = odr_strdup(o, fault_string);
295     if (details)
296         p->u.soap_error->details = odr_strdup(o, details);
297     else
298         p->u.soap_error->details = 0;
299     return -1;
300 }
301