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