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