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