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