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