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