Branch data Line data Source code
1 : : /* zxidcurl.c - libcurl interface for making SOAP calls and getting metadata
2 : : * Copyright (c) 2010 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
3 : : * Copyright (c) 2006-2008 Symlabs (symlabs@symlabs.com), All Rights Reserved.
4 : : * Author: Sampo Kellomaki (sampo@iki.fi)
5 : : * This is confidential unpublished proprietary source code of the author.
6 : : * NO WARRANTY, not even implied warranties. Contains trade secrets.
7 : : * Distribution prohibited unless authorized in writing.
8 : : * Licensed under Apache License 2.0, see file COPYING.
9 : : * $Id: zxidcurl.c,v 1.9 2009-11-24 23:53:40 sampo Exp $
10 : : *
11 : : * 12.8.2006, created --Sampo
12 : : * 4.10.2007, fixed missing Content-length header found by Damien Laniel --Sampo
13 : : * 4.10.2008, added documentation --Sampo
14 : : * 1.2.2010, removed arbitrary limit on SOAP response size --Sampo
15 : : *
16 : : * See also: http://hoohoo.ncsa.uiuc.edu/cgi/interface.html (CGI specification)
17 : : * http://curl.haxx.se/libcurl/
18 : : */
19 : :
20 : : #include "platform.h" /* needed on Win32 for pthread_mutex_lock() et al. */
21 : : #include <string.h>
22 : :
23 : : #ifdef USE_CURL
24 : : #include <curl/curl.h>
25 : :
26 : : #if LIBCURL_VERSION_NUM < 0x070c00
27 : : #define CURL_EASY_STRERR(ec) "error-not-avail-in-curl-before-0x070c00"
28 : : #else
29 : : #define CURL_EASY_STRERR(ec) curl_easy_strerror(ec)
30 : : #endif
31 : :
32 : : #endif
33 : :
34 : : #include "errmac.h"
35 : : #include "zxid.h"
36 : : #include "zxidutil.h"
37 : : #include "zxidconf.h"
38 : : #include "c/zx-ns.h"
39 : : #include "c/zx-data.h"
40 : :
41 : : /* ============== CoT and Metadata of Others ============== */
42 : :
43 : : /*() Call back used by Curl to move received data to application buffer.
44 : : * Internal. Do not use directly. */
45 : :
46 : : /* Called by: */
47 : : size_t zxid_curl_write_data(void *buffer, size_t size, size_t nmemb, void *userp)
48 : 1676 : {
49 : 1676 : int len = size * nmemb;
50 : : #if 1
51 : 1676 : struct zxid_curl_ctx* rc = (struct zxid_curl_ctx*)userp;
52 : 1676 : int old_len, new_len, in_buf = rc->p - rc->buf;
53 [ + + ]: 1676 : if (rc->p + len > rc->lim) {
54 : 193 : old_len = rc->lim-rc->buf;
55 : 193 : new_len = MIN(MAX(old_len + old_len, in_buf + len), ZXID_MAX_CURL_BUF);
56 [ - + ]: 193 : if (new_len == ZXID_MAX_CURL_BUF) {
57 : 0 : ERR("Too large HTTP response. Response length at least %d. Maximum allowed length (ZXID_MAX_CURL_BUF): %d", in_buf + len, ZXID_MAX_CURL_BUF);
58 : 0 : return -1; /* Signal error */
59 : : }
60 [ + + - + ]: 193 : D("Reallocating curl buffer from %d to %d in_buf=%d len=%d", old_len, new_len, in_buf, len);
61 [ - + # # : 193 : REALLOCN(rc->buf, new_len+1);
+ - - + #
# # # #
# ]
62 : 193 : rc->p = rc->buf + in_buf;
63 : 193 : rc->lim = rc->buf + new_len;
64 : : }
65 : 1676 : memcpy(rc->p, buffer, len);
66 : 1676 : rc->p += len;
67 [ - + ]: 1676 : if (zx_debug & CURL_INOUT) {
68 : 0 : INFO("RECV(%.*s) %d chars", len, (char*)buffer, len);
69 : 0 : D_XML_BLOB(0, "RECV", len, (char*)buffer);
70 : : }
71 : : #else
72 : : int fd = (int)userp;
73 : : write_all_fd(fd, buffer, len);
74 : : #endif
75 : 1676 : return len;
76 : : }
77 : :
78 : : /*() Call back used by Curl to move data from application buffer to Curl
79 : : * internal send buffer. Internal. Do not use directly. */
80 : :
81 : : /* Called by: */
82 : : size_t zxid_curl_read_data(void *buffer, size_t size, size_t nmemb, void *userp)
83 : 2082 : {
84 : 2082 : int len = size*nmemb;
85 : 2082 : struct zxid_curl_ctx* wc = (struct zxid_curl_ctx*)userp;
86 [ + + ]: 2082 : if (len > (wc->lim - wc->p))
87 : 288 : len = wc->lim - wc->p;
88 : 2082 : memcpy(buffer, wc->p, len);
89 : 2082 : wc->p += len;
90 [ - + ]: 2082 : if (zx_debug & CURL_INOUT) {
91 : 0 : INFO("SEND(%.*s) %d chars", len, (char*)buffer, len);
92 : 0 : D_XML_BLOB(0, "SEND", len, (char*)buffer);
93 : : }
94 : 2082 : return len;
95 : : }
96 : :
97 : : /*() Send HTTP request for metadata using Well Known Location (WKL) method
98 : : * and wait for response. Send the message to the server using Curl. Return
99 : : * the metadata as parsed XML for the entity.
100 : : * This call will block while the HTTP request-response is happening.
101 : : *
102 : : * cf:: ZXID configuration object, also used for memory allocation
103 : : * url:: Where the request will be sent, i.e. the WKL
104 : : * return:: XML data structure representing the entity, or 0 upon failure
105 : : *
106 : : * The underlying HTTP client is libcurl. While libcurl is documented to
107 : : * be "entirely thread safe", one limitation is that chrl handle can not
108 : : * be shared between threads. Since we keep the curl handle a part
109 : : * of the configuration object, which may be shared between threads,
110 : : * we need to take a lock for duration of the curl operation. Thus any
111 : : * given configuration object can have only one HTTP request active
112 : : * at a time. If you need more parallelism, you need more configuration
113 : : * objects.
114 : : */
115 : :
116 : : /* Called by: opt x3, zxid_addmd, zxid_get_meta_ss */
117 : : zxid_entity* zxid_get_meta(zxid_conf* cf, const char* url)
118 : 155 : {
119 : : #ifdef USE_CURL
120 : : zxid_entity* ent;
121 : : CURLcode res;
122 : : struct zxid_curl_ctx rc;
123 : : #if 1
124 : 155 : rc.buf = rc.p = ZX_ALLOC(cf->ctx, ZXID_INIT_MD_BUF+1);
125 : 155 : rc.lim = rc.buf + ZXID_INIT_MD_BUF;
126 [ - + # # ]: 155 : LOCK(cf->curl_mx, "curl get_meta");
127 : 155 : curl_easy_setopt(cf->curl, CURLOPT_WRITEDATA, &rc);
128 : : #else
129 : : {
130 : : /* TEST CODE (usually disabled) */
131 : : int fd = open_fd_from_path(O_CREAT | O_WRONLY | O_TRUNC, 0666, "get_meta TEST", 1,
132 : : "%s" ZXID_COT_DIR "test", cf->path);
133 : : if (fd == -1) {
134 : : perror("open meta to write");
135 : : /*UNLOCK(cf->curl_mx, "curl get_meta");*/
136 : : ERR("Failed to open file(%s) for writing metadata", url);
137 : : return 0;
138 : : }
139 : : curl_easy_setopt(cf->curl, CURLOPT_WRITEDATA, fd);
140 : : }
141 : : #endif
142 : 155 : curl_easy_setopt(cf->curl, CURLOPT_WRITEFUNCTION, zxid_curl_write_data);
143 : 155 : curl_easy_setopt(cf->curl, CURLOPT_NOPROGRESS, 1);
144 : 155 : curl_easy_setopt(cf->curl, CURLOPT_FOLLOWLOCATION, 1);
145 : 155 : curl_easy_setopt(cf->curl, CURLOPT_MAXREDIRS, 110);
146 : 155 : curl_easy_setopt(cf->curl, CURLOPT_SSL_VERIFYPEER, 0);
147 : 155 : curl_easy_setopt(cf->curl, CURLOPT_SSL_VERIFYHOST, 0);
148 : 155 : curl_easy_setopt(cf->curl, CURLOPT_URL, url);
149 : :
150 [ + + - + ]: 155 : D("get_meta: url(%s)", url);
151 [ + - ]: 155 : if (cf->log_level>1)
152 : 155 : zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "W", "GETMD", url, 0);
153 : 155 : res = curl_easy_perform(cf->curl);
154 [ - + # # ]: 155 : UNLOCK(cf->curl_mx, "curl get_meta");
155 : 155 : rc.lim = rc.p;
156 : 155 : rc.p[1] = 0;
157 : 155 : rc.p = rc.buf;
158 [ + + ]: 155 : if (rc.lim - rc.p < 300) {
159 : 153 : ERR("Metadata response too short (%d chars, min 300 required). url(%s) CURLcode(%d) CURLerr(%s) buf(%.*s)", rc.lim-rc.buf, url, res, CURL_EASY_STRERR(res), rc.lim-rc.buf, rc.buf);
160 : 153 : ZX_FREE(cf->ctx, rc.buf);
161 : 153 : return 0;
162 : : }
163 : :
164 : 2 : ent = zxid_parse_meta(cf, &rc.p, rc.lim);
165 [ - + ]: 2 : if (!ent) {
166 : 0 : ERR("Failed to parse metadata response url(%s) CURLcode(%d) CURLerr(%s) buf(%.*s)", url, res, CURL_EASY_STRERR(res), rc.lim-rc.buf, rc.buf);
167 : 0 : ZX_FREE(cf->ctx, rc.buf);
168 : 0 : return 0;
169 : : }
170 [ + - ]: 2 : if (cf->log_level>0)
171 : 2 : zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "W", "GOTMD", url, 0);
172 : 2 : return ent;
173 : : #else
174 : : ERR("This copy of zxid was compiled to NOT use libcurl. As such metadata fetching and web service calling are not supported. Add -DUSE_CURL and recompile. %d", 0);
175 : : return 0;
176 : : #endif
177 : : }
178 : :
179 : : /*() Wrapper for zxid_get_meta() so you can provide the URL as ~zx_str~. */
180 : : /* Called by: zxid_get_ent_ss */
181 : : zxid_entity* zxid_get_meta_ss(zxid_conf* cf, struct zx_str* url)
182 : 153 : {
183 : 153 : return zxid_get_meta(cf, zx_str_to_c(cf->ctx, url));
184 : : }
185 : :
186 : : /* ============== SOAP Call ============= */
187 : :
188 : : /*() HTTP client for POST method.
189 : : * This method is just a wrapper around underlying libcurl HTTP client.
190 : : *
191 : : * cf:: ZXID configuration object
192 : : * url_len:: Length of the URL. If -1 is passed, strlen(url) is used
193 : : * url:: URL for POST
194 : : * len:: Length of the data. If -1 is passed, strlen(data) is used
195 : : * data:: HTTP body for the POST
196 : : * returns:: HTTP body of the response */
197 : :
198 : : /* Called by: zxid_soap_call_raw */
199 : : struct zx_str* zxid_http_post_raw(zxid_conf* cf, int url_len, const char* url, int len, const char* data)
200 : 144 : {
201 : : #ifdef USE_CURL
202 : : struct zx_str* ret;
203 : : CURLcode res;
204 : : struct zxid_curl_ctx rc;
205 : : struct zxid_curl_ctx wc;
206 : : struct curl_slist content_type;
207 : : struct curl_slist SOAPaction;
208 : : char* urli;
209 : 144 : rc.buf = rc.p = ZX_ALLOC(cf->ctx, ZXID_INIT_SOAP_BUF+1);
210 : 144 : rc.lim = rc.buf + ZXID_INIT_SOAP_BUF;
211 [ - + # # ]: 144 : LOCK(cf->curl_mx, "curl soap");
212 : 144 : curl_easy_setopt(cf->curl, CURLOPT_WRITEDATA, &rc);
213 : 144 : curl_easy_setopt(cf->curl, CURLOPT_WRITEFUNCTION, zxid_curl_write_data);
214 : 144 : curl_easy_setopt(cf->curl, CURLOPT_NOPROGRESS, 1);
215 : 144 : curl_easy_setopt(cf->curl, CURLOPT_FOLLOWLOCATION, 1);
216 : 144 : curl_easy_setopt(cf->curl, CURLOPT_MAXREDIRS, 110);
217 : 144 : curl_easy_setopt(cf->curl, CURLOPT_SSL_VERIFYPEER, 0); /* *** arrange verification */
218 : 144 : curl_easy_setopt(cf->curl, CURLOPT_SSL_VERIFYHOST, 0); /* *** arrange verification */
219 : : //curl_easy_setopt(cf->curl, CURLOPT_CERTINFO, 1);
220 : :
221 [ + + ]: 144 : if (url_len == -1)
222 : 1 : url_len = strlen(url);
223 : 144 : urli = ZX_ALLOC(cf->ctx, url_len+1);
224 : 144 : memcpy(urli, url, url_len);
225 : 144 : urli[url_len] = 0;
226 : : DD("urli(%s) len=%d", urli, len);
227 : 144 : curl_easy_setopt(cf->curl, CURLOPT_URL, urli);
228 : :
229 [ + + ]: 144 : if (len == -1)
230 : 1 : len = strlen(data);
231 : 144 : wc.buf = wc.p = (char*)data;
232 : 144 : wc.lim = (char*)data + len;
233 : :
234 : 144 : curl_easy_setopt(cf->curl, CURLOPT_POST, 1);
235 : 144 : curl_easy_setopt(cf->curl, CURLOPT_POSTFIELDSIZE, len);
236 : 144 : curl_easy_setopt(cf->curl, CURLOPT_READDATA, &wc);
237 : 144 : curl_easy_setopt(cf->curl, CURLOPT_READFUNCTION, zxid_curl_read_data);
238 : :
239 : 144 : ZERO(&content_type, sizeof(content_type));
240 : 144 : content_type.data = "Content-Type: text/xml";
241 : 144 : ZERO(&SOAPaction, sizeof(SOAPaction));
242 : : #if 1
243 : 144 : SOAPaction.data = "SOAPAction: \"\""; /* Empty SOAPAction is the ID-WSF (and SAML?) standard */
244 : : #else
245 : : /* Evil stuff: some implementations, especially Apache AXIS, are very
246 : : * picky about SOAPAction header. */
247 : : //SOAPaction.data = "SOAPAction: \"http://ws.apache.org/axis2/TestPolicyPortType/authRequestRequest\"";
248 : : SOAPaction.data = "SOAPAction: \"authRequest\"";
249 : : #endif
250 : 144 : SOAPaction.next = &content_type; //curl_slist_append(3)
251 : 144 : curl_easy_setopt(cf->curl, CURLOPT_HTTPHEADER, &SOAPaction);
252 : :
253 [ + + - + ]: 144 : D("----------- url(%s) -----------", urli);
254 : : DD("SOAP_CALL post(%.*s) len=%d\n", len, data, len);
255 : 144 : D_XML_BLOB(cf, "SOAPCALL POST", len, data);
256 : 144 : res = curl_easy_perform(cf->curl); /* <========= Actual call, blocks. */
257 [ + - + ]: 144 : switch (res) {
258 : 142 : case 0: break;
259 : : case CURLE_SSL_CONNECT_ERROR:
260 : 0 : ERR("Is the URL(%s) really an https url? Check that certificate of the server is valid and that certification authority is known to the client. CURLcode(%d) CURLerr(%s)", urli, res, CURL_EASY_STRERR(res));
261 : : DD("buf(%.*s)", rc.lim-rc.buf, rc.buf);
262 : : #if 0
263 : : struct curl_certinfo* ci;
264 : : res = curl_easy_getinfo(cf->curl, CURLINFO_CERTINFO, &ci); /* CURLINFO_SSL_VERIFYRESULT */
265 : : if (!res && ci) {
266 : : int i;
267 : : struct curl_slist *slist;
268 : : D("%d certs", ci->num_of_certs);
269 : : for (i = 0; i < ci->num_of_certs; ++i)
270 : : for (slist = ci->certinfo[i]; slist; slist = slist->next)
271 : : D("%d: %s", i, slist->data);
272 : : }
273 : : #endif
274 : 0 : break;
275 : : default:
276 : 2 : ERR("Failed post to url(%s) CURLcode(%d) CURLerr(%s)", urli, res, CURL_EASY_STRERR(res));
277 : : DD("buf(%.*s)", rc.lim-rc.buf, rc.buf);
278 : : }
279 [ - + # # ]: 144 : UNLOCK(cf->curl_mx, "curl soap");
280 : 144 : ZX_FREE(cf->ctx, urli);
281 : 144 : rc.lim = rc.p;
282 : 144 : rc.p[0] = 0;
283 : :
284 : : DD("SOAP_CALL got(%s)", rc.buf);
285 : 144 : D_XML_BLOB(cf, "SOAPCALL GOT", -2, rc.buf);
286 : :
287 : 144 : ret = zx_ref_len_str(cf->ctx, rc.lim - rc.buf, rc.buf);
288 : 144 : return ret;
289 : : #else
290 : : ERR("This copy of zxid was compiled to NOT use libcurl. SOAP calls (such as Artifact profile and WSC) are not supported. Add -DUSE_CURL (make ENA_CURL=1) and recompile. %d", 0);
291 : : return 0;
292 : : #endif
293 : : }
294 : :
295 : : /*(i) Send SOAP request and wait for response. Send the message to the
296 : : * server using Curl. Return the parsed XML response data structure.
297 : : * This call will block while the HTTP request-response is happening.
298 : : *
299 : : * cf:: ZXID configuration object, also used for memory allocation
300 : : * url:: Where the request will be sent
301 : : * env:: SOAP enevlope to be serialized and sent
302 : : * ret_enve:: result parameter allows upper layers to see the message as string
303 : : * return:: XML data structure representing the response, or 0 upon failure
304 : : *
305 : : * The underlying HTTP client is libcurl. While libcurl is documented to
306 : : * be "entirely thread safe", one limitation is that chrl handle can not
307 : : * be shared between threads. Since we keep the curl handle a part
308 : : * of the configuration object, which may be shared between threads,
309 : : * we need to take a lock for duration of the curl operation. Thus any
310 : : * given configuration object can have only one HTTP request active
311 : : * at a time. If you need more parallelism, you need more configuration
312 : : * objects.
313 : : */
314 : :
315 : : /* Called by: zxid_soap_call_hdr_body, zxid_wsc_call */
316 : : struct zx_root_s* zxid_soap_call_raw(zxid_conf* cf, struct zx_str* url, struct zx_e_Envelope_s* env, char** ret_enve)
317 : 143 : {
318 : : #ifdef USE_CURL
319 : : struct zx_root_s* r;
320 : : struct zx_str* ret;
321 : : struct zx_str* ss;
322 : :
323 : 143 : ss = zx_easy_enc_elem_opt(cf, &env->gg);
324 : : DD("ss(%.*s) len=%d", ss->len, ss->s, ss->len);
325 : 143 : ret = zxid_http_post_raw(cf, url->len, url->s, ss->len, ss->s);
326 : 143 : zx_str_free(cf->ctx, ss);
327 [ + + ]: 143 : if (ret_enve)
328 [ + - ]: 19 : *ret_enve = ret?ret->s:0;
329 [ - + ]: 143 : if (!ret)
330 : 0 : return 0;
331 : :
332 : 143 : r = zx_dec_zx_root(cf->ctx, ret->len, ret->s, "soap_call");
333 [ + - + + : 143 : if (!r || !r->Envelope || !r->Envelope->Body) {
- + ]
334 : 2 : ERR("Failed to parse SOAP response url(%.*s)", url->len, url->s);
335 : 2 : D_XML_BLOB(cf, "BAD SOAP RESPONSE", ret->len, ret->s);
336 : 2 : ZX_FREE(cf->ctx, ret);
337 : 2 : return 0;
338 : : }
339 : 141 : return r;
340 : : #else
341 : : ERR("This copy of zxid was compiled to NOT use libcurl. SOAP calls (such as Artifact profile and WSC) are not supported. Add -DUSE_CURL (make ENA_CURL=1) and recompile. %d", 0);
342 : : return 0;
343 : : #endif
344 : : }
345 : :
346 : : /*() Encode XML data structure representing SOAP envelope (request)
347 : : * and send the message to the server using Curl. Return the parsed
348 : : * XML response data structure. This call will block while the HTTP
349 : : * request-response is happening. To be called from SSO world.
350 : : * Wrapper for zxid_soap_call_raw().
351 : : *
352 : : * cf:: ZXID configuration object, also used for memory allocation
353 : : * url:: The endpoint where the request will be sent
354 : : * hdr:: XML data structure representing the SOAP headers. Possibly 0 if no headers are desired
355 : : * body:: XML data structure representing the SOAP body
356 : : * return:: XML data structure representing the response */
357 : :
358 : : /* Called by: zxid_as_call_ses, zxid_az_soap, zxid_idp_soap, zxid_soap_call_body, zxid_sp_deref_art, zxid_sp_soap */
359 : : struct zx_root_s* zxid_soap_call_hdr_body(zxid_conf* cf, struct zx_str* url, struct zx_e_Header_s* hdr, struct zx_e_Body_s* body)
360 : 106 : {
361 : : struct zx_root_s* r;
362 : 106 : struct zx_e_Envelope_s* env = zx_NEW_e_Envelope(cf->ctx,0);
363 : 106 : env->Header = hdr;
364 : 106 : env->Body = body;
365 : 106 : zx_add_kid(&env->gg, &body->gg);
366 [ + + ]: 106 : if (hdr)
367 : 89 : zx_add_kid(&env->gg, &hdr->gg);
368 : 106 : r = zxid_soap_call_raw(cf, url, env, 0);
369 : 106 : return r;
370 : : }
371 : :
372 : : /*() Encode XML data structure representing SOAP envelope (request)
373 : : * and send the message to the server using Curl. Return the parsed
374 : : * XML response data structure. This call will block while the HTTP
375 : : * request-response is happening. To be called from SSO world.
376 : : * Wrapper for zxid_soap_call_raw().
377 : : *
378 : : * cf:: ZXID configuration object, also used for memory allocation
379 : : * url:: The endpoint where the request will be sent
380 : : * body:: XML data structure representing the SOAP body
381 : : * return:: XML data structure representing the response */
382 : :
383 : : /* Called by: */
384 : : struct zx_root_s* zxid_soap_call_body(zxid_conf* cf, struct zx_str* url, struct zx_e_Body_s* body)
385 : 0 : {
386 : : /*return zxid_soap_call_hdr_body(cf, url, zx_NEW_e_Header(cf->ctx,0), body);*/
387 : 0 : return zxid_soap_call_hdr_body(cf, url, 0, body);
388 : : }
389 : :
390 : : /*() Emit to stdout XML data structure representing SOAP envelope (request).
391 : : * Typically used in CGI environment, e.g. by the IdP and Discovery.
392 : : * Optionally logs the issued message to local audit trail.
393 : : *
394 : : * cf:: ZXID configuration object, also used for memory allocation
395 : : * body:: XML data structure representing the request
396 : : * return:: 0 if fail, ZXID_REDIR_OK if success. */
397 : :
398 : : /* Called by: zxid_idp_soap_dispatch x2, zxid_sp_soap_dispatch x7 */
399 : : int zxid_soap_cgi_resp_body(zxid_conf* cf, zxid_ses* ses, struct zx_e_Body_s* body)
400 : 135 : {
401 : 135 : struct zx_e_Envelope_s* env = zx_NEW_e_Envelope(cf->ctx,0);
402 : : struct zx_str* ss;
403 : : struct zx_str* logpath;
404 : 135 : env->Body = body;
405 : 135 : zx_add_kid(&env->gg, &body->gg);
406 : 135 : env->Header = zx_NEW_e_Header(cf->ctx, &env->gg);
407 : 135 : zxid_wsf_decor(cf, ses, env, 1);
408 : 135 : ss = zx_easy_enc_elem_opt(cf, &env->gg);
409 : :
410 [ + - ]: 135 : if (cf->log_issue_msg) {
411 : 135 : logpath = zxlog_path(cf, ses->issuer, ss, ZXLOG_ISSUE_DIR, ZXLOG_WIR_KIND, 1);
412 [ + + ]: 135 : if (logpath) {
413 [ - + ]: 117 : if (zxlog_dup_check(cf, logpath, "cgi_resp")) {
414 : 0 : ERR("Duplicate wire msg(%.*s) (Simple Sign)", ss->len, ss->s);
415 : : #if 0
416 : : if (cf->dup_msg_fatal) {
417 : : ERR("FATAL (by configuration): Duplicate wire msg(%.*s) (cgi_resp)", ss->len, ss->s);
418 : : zxlog_blob(cf, 1, logpath, ss, "cgi_resp dup");
419 : : zx_str_free(cf->ctx, logpath);
420 : : return 0;
421 : : }
422 : : #endif
423 : : }
424 : 117 : zxlog_blob(cf, 1, logpath, ss, "cgi_resp");
425 : 117 : zxlogwsp(cf, ses, "K", "CGIRESP", 0, "logpath(%.*s)", logpath->len, logpath->s);
426 : 117 : zx_str_free(cf->ctx, logpath);
427 : : }
428 : : }
429 : :
430 [ - + ]: 135 : if (zx_debug & ZXID_INOUT) INFO("SOAP_RESP(%.*s)", ss->len, ss->s);
431 : 135 : printf("CONTENT-TYPE: text/xml" CRLF "CONTENT-LENGTH: %d" CRLF2 "%.*s", ss->len, ss->len, ss->s);
432 : 135 : return ZXID_REDIR_OK;
433 : : }
434 : :
435 : : /* EOF -- zxidcurl.c */
|