SOAP, SRW codecs and HTTP transport for YAZ using libxml2.
[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.1 2003-02-12 15:06:44 adam Exp $
6  */
7
8 #include <yaz/soap.h>
9
10 static const char *soap_v1_1 = "http://schemas.xmlsoap.org/soap/envelope/";
11 static const char *soap_v1_2 = "http://www.w3.org/2001/06/soap-envelope";
12
13 int z_soap_error(ODR o, Z_SOAP *p,
14                  const char *fault_code, const char *fault_string,
15                  const char *details)
16 {
17     p->which = Z_SOAP_error;
18     p->u.soap_error = odr_malloc(o, sizeof(*p->u.soap_error));
19     p->u.soap_error->fault_code = odr_strdup(o, fault_code);
20     p->u.soap_error->fault_string = odr_strdup(o, fault_string);
21     if (details)
22         p->u.soap_error->details = odr_strdup(o, details);
23     else
24         p->u.soap_error->details = 0;
25     return -1;
26 }
27
28 int z_soap_codec(ODR o, Z_SOAP **pp, 
29                  char **content_buf, int *content_len,
30                  Z_SOAP_Handler *handlers)
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 = 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         /* check that root node is Envelope */
50         ptr = xmlDocGetRootElement(doc);
51         if (!ptr || ptr->type != XML_ELEMENT_NODE ||
52             strcmp(ptr->name, "Envelope"))
53         {
54             xmlFreeDoc(doc);
55             return z_soap_error(o, p, "SOAP-ENV:Client",
56                                 "No Envelope element", 0);
57         }
58         else
59         {
60             /* determine SOAP version */
61             const char * ns_envelope = ptr->ns->href;
62             if (!strcmp(ns_envelope, soap_v1_1))
63                 p->ns = soap_v1_1;
64             else if (!strcmp(ns_envelope, soap_v1_2))
65                 p->ns = soap_v1_2;
66             else
67             {
68                 xmlFreeDoc(doc);
69                 return z_soap_error(o, p, "SOAP-ENV:Client",
70                                     "Bad SOAP version", 0);
71             }
72         }
73         ptr = ptr->children;
74         while(ptr && ptr->type == XML_TEXT_NODE)
75             ptr = ptr->next;
76         if (ptr && ptr->type == XML_ELEMENT_NODE &&
77             !strcmp(ptr->ns->href, p->ns) &&
78             !strcmp(ptr->name, "Header"))
79         {
80             ptr = ptr->next;
81             while(ptr && ptr->type == XML_TEXT_NODE)
82                 ptr = ptr->next;
83         }
84         /* check that Body is present */
85         if (!ptr || ptr->type != XML_ELEMENT_NODE || 
86             strcmp(ptr->name, "Body"))
87         {
88             xmlFreeDoc(doc);
89             return z_soap_error(o, p, "SOAP-ENV:Client",
90                                 "SOAP Body element not found", 0);
91         }
92         if (strcmp(ptr->ns->href, p->ns))
93         {
94             xmlFreeDoc(doc);
95             return z_soap_error(o, p, "SOAP-ENV:Client",
96                                 "SOAP bad NS for Body element", 0);
97         }
98         pptr = ptr;
99         ptr = ptr->children;
100         while (ptr && ptr->type == XML_TEXT_NODE)
101             ptr = ptr->next;
102         if (!ptr || ptr->type != XML_ELEMENT_NODE)
103         {
104             xmlFreeDoc(doc);
105             return z_soap_error(o, p, "SOAP-ENV:Client",
106                                 "SOAP No content for Body", 0);
107         }
108         /* check for fault package */
109         if (!strcmp(ptr->ns->href, p->ns)
110             && !strcmp(ptr->name, "Fault") && ptr->children)
111         {
112             ptr = ptr->children;
113
114             p->which = Z_SOAP_fault;
115             p->u.fault = odr_malloc(o, sizeof(*p->u.fault));
116             p->u.fault->fault_code = 0;
117             p->u.fault->fault_string = 0;
118             p->u.fault->details = 0;
119             while (ptr)
120             {
121                 if (ptr->children && ptr->children->type == XML_TEXT_NODE)
122                 {
123                     if (!strcmp(ptr->name, "faultcode"))
124                         p->u.fault->fault_code =
125                             odr_strdup(o, ptr->children->content);
126                     if (!strcmp(ptr->name, "faultstring"))
127                         p->u.fault->fault_string =
128                             odr_strdup(o, ptr->children->content);
129                     if (!strcmp(ptr->name, "details"))
130                         p->u.fault->details =
131                             odr_strdup(o, ptr->children->content);
132                 }
133                 ptr = ptr->next;
134             }
135             ret = 0;
136         }
137         else
138         {
139             for (i = 0; handlers[i].ns; i++)
140                 if (!strcmp(ptr->ns->href, handlers[i].ns))
141                     break;
142             if (handlers[i].ns)
143             {
144                 void *handler_data = 0;
145                 ret = (*handlers[i].f)(o, pptr, &handler_data,
146                                        handlers[i].client_data,
147                                        handlers[i].ns);
148                 if (ret || !handler_data)
149                     z_soap_error(o, p, "SOAP-ENV:Client",
150                                  "SOAP Handler returned error", 0);
151                 else
152                 {
153                     p->which = Z_SOAP_generic;
154                     p->u.generic = odr_malloc(o, sizeof(*p->u.generic));
155                     p->u.generic->no = i;
156                     p->u.generic->ns = handlers[i].ns;
157                     p->u.generic->p = handler_data;
158                 }
159             }
160             else
161             {
162                 ret = z_soap_error(o, p, "SOAP-ENV:Client", 
163                                    "No handler for NS", 0);
164             }
165         }
166         xmlFreeDoc(doc);
167         return ret;
168     }
169     else if (o->direction == ODR_ENCODE)
170     {
171         Z_SOAP *p = *pp;
172         xmlNsPtr ns_env;
173         xmlNodePtr envelope_ptr, body_ptr;
174         xmlChar *buf_out;
175         int len_out;
176
177         xmlDocPtr doc = xmlNewDoc("1.0");
178
179         envelope_ptr = xmlNewNode(0, "Envelope");
180         ns_env = xmlNewNs(envelope_ptr, p->ns, "SOAP-ENV");
181         body_ptr = xmlNewChild(envelope_ptr, ns_env, "Body", 0);
182         xmlDocSetRootElement(doc, envelope_ptr);
183
184         if (p->which == Z_SOAP_fault || p->which == Z_SOAP_error)
185         {
186             Z_SOAP_Fault *f = p->u.fault;
187             xmlNodePtr fault_ptr = xmlNewChild(body_ptr, ns_env, "Fault", 0);
188             xmlNewChild(fault_ptr, ns_env, "faultcode",  f->fault_code);
189             xmlNewChild(fault_ptr, ns_env, "faultstring", f->fault_string);
190             if (f->details)
191                 xmlNewChild(fault_ptr, ns_env, "details", f->details);
192         }
193         else if (p->which == Z_SOAP_generic)
194         {
195             int ret, no = p->u.generic->no;
196             
197             ret = (*handlers[no].f)(o, body_ptr, &p->u.generic->p,
198                                     handlers[no].client_data,
199                                     handlers[no].ns);
200             if (ret)
201                 return ret;
202         }
203         xmlDocDumpMemory(doc, &buf_out, &len_out);
204         *content_buf = odr_malloc(o, len_out);
205         *content_len = len_out;
206         memcpy(*content_buf, buf_out, len_out);
207         xmlFree(buf_out);
208         xmlFreeDoc(doc);
209         return 0;
210     }
211     return 0;
212 }