1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the envied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 **/
19 
20 #include "common.h"
21 #include "log.h"
22 #include "zbxjson.h"
23 #include "zbxhttp.h"
24 #include "zbxembed.h"
25 #include "httprequest.h"
26 #include "embed.h"
27 #include "duktape.h"
28 #include "zbxalgo.h"
29 
30 #ifdef HAVE_LIBCURL
31 
32 #define ZBX_HTTPAUTH_NONE		CURLAUTH_NONE
33 #define ZBX_HTTPAUTH_BASIC		CURLAUTH_BASIC
34 #define ZBX_HTTPAUTH_DIGEST		CURLAUTH_DIGEST
35 #if LIBCURL_VERSION_NUM >= 0x072600
36 #	define ZBX_HTTPAUTH_NEGOTIATE	CURLAUTH_NEGOTIATE
37 #else
38 #	define ZBX_HTTPAUTH_NEGOTIATE	CURLAUTH_GSSNEGOTIATE
39 #endif
40 #define ZBX_HTTPAUTH_NTLM		CURLAUTH_NTLM
41 
42 extern char	*CONFIG_SOURCE_IP;
43 
44 typedef struct
45 {
46 	CURL			*handle;
47 	struct curl_slist	*headers;
48 	char			*data;
49 	char			*headers_in;
50 	size_t			data_alloc;
51 	size_t			data_offset;
52 	size_t			headers_in_alloc;
53 	size_t			headers_in_offset;
54 	unsigned char		custom_header;
55 }
56 zbx_es_httprequest_t;
57 
58 /* ZBX_CURL_SETOPT() macro is a code snippet to make code shorter and facilitate resource deallocation */
59 /* in case of error. Be careful with using ZBX_CURL_SETOPT(), duk_push_error_object() and duk_error()  */
60 /* in functions - it is easy to get memory leaks because duk_error() causes longjmp().                 */
61 /* Note that the caller of ZBX_CURL_SETOPT() must define variable 'int err_index' and label 'out'.     */
62 #define ZBX_CURL_SETOPT(ctx, handle, opt, value, err)							\
63 	if (CURLE_OK != (err = curl_easy_setopt(handle, opt, value)))					\
64 	{												\
65 		err_index = duk_push_error_object(ctx, DUK_RET_EVAL_ERROR,					\
66 				"cannot set cURL option " #opt ": %s.", curl_easy_strerror(err));	\
67 		goto out;										\
68 	}
69 
curl_write_cb(void * ptr,size_t size,size_t nmemb,void * userdata)70 static size_t	curl_write_cb(void *ptr, size_t size, size_t nmemb, void *userdata)
71 {
72 	size_t			r_size = size * nmemb;
73 	zbx_es_httprequest_t	*request = (zbx_es_httprequest_t *)userdata;
74 
75 	zbx_strncpy_alloc(&request->data, &request->data_alloc, &request->data_offset, (const char *)ptr, r_size);
76 
77 	return r_size;
78 }
79 
curl_header_cb(void * ptr,size_t size,size_t nmemb,void * userdata)80 static size_t	curl_header_cb(void *ptr, size_t size, size_t nmemb, void *userdata)
81 {
82 	size_t			r_size = size * nmemb;
83 	zbx_es_httprequest_t	*request = (zbx_es_httprequest_t *)userdata;
84 
85 	zbx_strncpy_alloc(&request->headers_in, &request->headers_in_alloc, &request->headers_in_offset, (const char *)ptr, r_size);
86 
87 	return r_size;
88 }
89 
90 /******************************************************************************
91  *                                                                            *
92  * Function: es_httprequest                                                   *
93  *                                                                            *
94  * Purpose: return backing C structure embedded in CurlHttpRequest object     *
95  *                                                                            *
96  ******************************************************************************/
es_httprequest(duk_context * ctx)97 static zbx_es_httprequest_t *es_httprequest(duk_context *ctx)
98 {
99 	zbx_es_httprequest_t	*request;
100 
101 	duk_push_this(ctx);
102 	duk_get_prop_string(ctx, -1, "\xff""\xff""d");
103 	request = (zbx_es_httprequest_t *)duk_to_pointer(ctx, -1);
104 	duk_pop(ctx);
105 
106 	return request;
107 }
108 
109 /******************************************************************************
110  *                                                                            *
111  * Function: es_httprequest_dtor                                              *
112  *                                                                            *
113  * Purpose: CurlHttpRequest destructor                                        *
114  *                                                                            *
115  ******************************************************************************/
es_httprequest_dtor(duk_context * ctx)116 static duk_ret_t	es_httprequest_dtor(duk_context *ctx)
117 {
118 	zbx_es_httprequest_t	*request;
119 
120 	duk_get_prop_string(ctx, 0, "\xff""\xff""d");
121 	request = (zbx_es_httprequest_t *)duk_to_pointer(ctx, -1);
122 	if (NULL != request)
123 	{
124 		if (NULL != request->headers)
125 			curl_slist_free_all(request->headers);
126 		if (NULL != request->handle)
127 			curl_easy_cleanup(request->handle);
128 		zbx_free(request->data);
129 		zbx_free(request->headers_in);
130 		zbx_free(request);
131 
132 		duk_push_pointer(ctx, NULL);
133 		duk_put_prop_string(ctx, 0, "\xff""\xff""d");
134 	}
135 
136 	return 0;
137 }
138 
139 /******************************************************************************
140  *                                                                            *
141  * Function: es_httprequest_ctor                                              *
142  *                                                                            *
143  * Purpose: CurlHttpRequest constructor                                       *
144  *                                                                            *
145  ******************************************************************************/
es_httprequest_ctor(duk_context * ctx)146 static duk_ret_t	es_httprequest_ctor(duk_context *ctx)
147 {
148 	zbx_es_httprequest_t	*request;
149 	CURLcode		err;
150 	zbx_es_env_t		*env;
151 	int			err_index = -1;
152 
153 	if (!duk_is_constructor_call(ctx))
154 		return DUK_RET_TYPE_ERROR;
155 
156 	duk_push_global_stash(ctx);
157 
158 	if (1 != duk_get_prop_string(ctx, -1, "\xff""\xff""zbx_env"))
159 		return duk_error(ctx, DUK_RET_TYPE_ERROR, "cannot access internal environment");
160 
161 	env = (zbx_es_env_t *)duk_to_pointer(ctx, -1);
162 	duk_pop(ctx);
163 
164 	duk_push_this(ctx);
165 
166 	request = (zbx_es_httprequest_t *)zbx_malloc(NULL, sizeof(zbx_es_httprequest_t));
167 	memset(request, 0, sizeof(zbx_es_httprequest_t));
168 
169 	if (NULL == (request->handle = curl_easy_init()))
170 	{
171 		err_index = duk_push_error_object(ctx, DUK_RET_EVAL_ERROR, "cannot initialize cURL library");
172 		goto out;
173 	}
174 
175 	ZBX_CURL_SETOPT(ctx, request->handle, CURLOPT_COOKIEFILE, "", err);
176 	ZBX_CURL_SETOPT(ctx, request->handle, CURLOPT_FOLLOWLOCATION, 1L, err);
177 	ZBX_CURL_SETOPT(ctx, request->handle, CURLOPT_WRITEFUNCTION, curl_write_cb, err);
178 	ZBX_CURL_SETOPT(ctx, request->handle, CURLOPT_WRITEDATA, request, err);
179 	ZBX_CURL_SETOPT(ctx, request->handle, CURLOPT_PRIVATE, request, err);
180 	ZBX_CURL_SETOPT(ctx, request->handle, CURLOPT_SSL_VERIFYPEER, 0L, err);
181 	ZBX_CURL_SETOPT(ctx, request->handle, CURLOPT_TIMEOUT, (long)env->timeout, err);
182 	ZBX_CURL_SETOPT(ctx, request->handle, CURLOPT_SSL_VERIFYHOST, 0L, err);
183 	ZBX_CURL_SETOPT(ctx, request->handle, CURLOPT_HEADERFUNCTION, curl_header_cb, err);
184 	ZBX_CURL_SETOPT(ctx, request->handle, CURLOPT_HEADERDATA, request, err);
185 	ZBX_CURL_SETOPT(ctx, request->handle, CURLOPT_INTERFACE, CONFIG_SOURCE_IP, err);
186 
187 	duk_push_pointer(ctx, request);
188 	duk_put_prop_string(ctx, -2, "\xff""\xff""d");
189 
190 	duk_push_c_function(ctx, es_httprequest_dtor, 1);
191 	duk_set_finalizer(ctx, -2);
192 out:
193 	if (-1 != err_index)
194 	{
195 		if (NULL != request->handle)
196 			curl_easy_cleanup(request->handle);
197 		zbx_free(request);
198 
199 		return duk_throw(ctx);
200 	}
201 
202 	return 0;
203 }
204 
205 /******************************************************************************
206  *                                                                            *
207  * Function: es_httprequest_add_header                                        *
208  *                                                                            *
209  * Purpose: CurlHttpRequest.SetHeader method                                  *
210  *                                                                            *
211  ******************************************************************************/
es_httprequest_add_header(duk_context * ctx)212 static duk_ret_t	es_httprequest_add_header(duk_context *ctx)
213 {
214 	zbx_es_httprequest_t	*request;
215 	CURLcode		err;
216 	char			*utf8 = NULL;
217 	int			err_index = -1;
218 
219 	if (NULL == (request = es_httprequest(ctx)))
220 		return duk_error(ctx, DUK_RET_EVAL_ERROR, "internal scripting error: null object");
221 
222 	if (SUCCEED != zbx_cesu8_to_utf8(duk_to_string(ctx, 0), &utf8))
223 	{
224 		err_index = duk_push_error_object(ctx, DUK_RET_TYPE_ERROR, "cannot convert header to utf8");
225 		goto out;
226 	}
227 
228 	request->headers = curl_slist_append(request->headers, utf8);
229 	ZBX_CURL_SETOPT(ctx, request->handle, CURLOPT_HTTPHEADER, request->headers, err);
230 	request->custom_header = 1;
231 out:
232 	zbx_free(utf8);
233 
234 	if (-1 != err_index)
235 		return duk_throw(ctx);
236 
237 	return 0;
238 }
239 
240 /******************************************************************************
241  *                                                                            *
242  * Function: es_httprequest_clear_header                                      *
243  *                                                                            *
244  * Purpose: CurlHttpRequest.ClearHeader method                                *
245  *                                                                            *
246  ******************************************************************************/
es_httprequest_clear_header(duk_context * ctx)247 static duk_ret_t	es_httprequest_clear_header(duk_context *ctx)
248 {
249 	zbx_es_httprequest_t	*request;
250 
251 	if (NULL == (request = es_httprequest(ctx)))
252 		return duk_error(ctx, DUK_RET_EVAL_ERROR, "internal scripting error: null object");
253 
254 	curl_slist_free_all(request->headers);
255 	request->headers = NULL;
256 	request->custom_header = 0;
257 
258 	return 0;
259 }
260 
261 /******************************************************************************
262  *                                                                            *
263  * Function: es_httprequest_query                                             *
264  *                                                                            *
265  * Purpose: CurlHttpRequest HTTP request implementation                       *
266  *                                                                            *
267  * Parameters: ctx          - [IN] the scripting engine context               *
268  *             http_request - [IN] the HTTP request (GET, PUT, POST, DELETE)  *
269  *                                                                            *
270  ******************************************************************************/
es_httprequest_query(duk_context * ctx,const char * http_request)271 static duk_ret_t	es_httprequest_query(duk_context *ctx, const char *http_request)
272 {
273 	zbx_es_httprequest_t	*request;
274 	char			*url = NULL, *contents = NULL;
275 	CURLcode		err;
276 	int			err_index = -1;
277 
278 	if (SUCCEED != zbx_cesu8_to_utf8(duk_to_string(ctx, 0), &url))
279 	{
280 		err_index = duk_push_error_object(ctx, DUK_RET_TYPE_ERROR, "cannot convert URL to utf8");
281 		goto out;
282 	}
283 
284 	if (0 == duk_is_null_or_undefined(ctx, 1))
285 	{
286 		if (SUCCEED != zbx_cesu8_to_utf8(duk_to_string(ctx, 1), &contents))
287 		{
288 			err_index = duk_push_error_object(ctx, DUK_RET_TYPE_ERROR,
289 					"cannot convert request contents to utf8");
290 			goto out;
291 		}
292 	}
293 
294 	if (NULL == (request = es_httprequest(ctx)))
295 	{
296 		err_index = duk_push_error_object(ctx, DUK_RET_EVAL_ERROR, "internal scripting error: null object");
297 		goto out;
298 	}
299 
300 	ZBX_CURL_SETOPT(ctx, request->handle, CURLOPT_URL, url, err);
301 
302 	if (0 == request->custom_header)
303 	{
304 		struct zbx_json_parse	jp;
305 
306 		if (NULL != request->headers)
307 		{
308 			curl_slist_free_all(request->headers);
309 			request->headers = NULL;
310 		}
311 
312 		if (NULL != contents)
313 		{
314 			if (SUCCEED == zbx_json_open(contents, &jp))
315 				request->headers = curl_slist_append(NULL, "Content-Type: application/json");
316 			else
317 				request->headers = curl_slist_append(NULL, "Content-Type: text/plain");
318 		}
319 	}
320 
321 	ZBX_CURL_SETOPT(ctx, request->handle, CURLOPT_HTTPHEADER, request->headers, err);
322 	ZBX_CURL_SETOPT(ctx, request->handle, CURLOPT_CUSTOMREQUEST, http_request, err);
323 	ZBX_CURL_SETOPT(ctx, request->handle, CURLOPT_POSTFIELDS, ZBX_NULL2EMPTY_STR(contents), err);
324 
325 	request->data_offset = 0;
326 	request->headers_in_offset = 0;
327 
328 	if (CURLE_OK != (err = curl_easy_perform(request->handle)))
329 	{
330 		err_index = duk_push_error_object(ctx, DUK_RET_EVAL_ERROR, "cannot get URL: %s.",
331 				curl_easy_strerror(err));
332 		goto out;
333 	}
334 out:
335 	zbx_free(url);
336 	zbx_free(contents);
337 
338 	if (-1 != err_index)
339 		return duk_throw(ctx);
340 
341 	duk_push_string(ctx, request->data);
342 
343 	return 1;
344 }
345 
346 /******************************************************************************
347  *                                                                            *
348  * Function: es_httprequest_get                                               *
349  *                                                                            *
350  * Purpose: CurlHttpRequest.Get method                                        *
351  *                                                                            *
352  ******************************************************************************/
es_httprequest_get(duk_context * ctx)353 static duk_ret_t	es_httprequest_get(duk_context *ctx)
354 {
355 	return es_httprequest_query(ctx, "GET");
356 }
357 
358 /******************************************************************************
359  *                                                                            *
360  * Function: es_httprequest_put                                               *
361  *                                                                            *
362  * Purpose: CurlHttpRequest.Put method                                        *
363  *                                                                            *
364  ******************************************************************************/
es_httprequest_put(duk_context * ctx)365 static duk_ret_t	es_httprequest_put(duk_context *ctx)
366 {
367 	return es_httprequest_query(ctx, "PUT");
368 }
369 
370 /******************************************************************************
371  *                                                                            *
372  * Function: es_httprequest_post                                              *
373  *                                                                            *
374  * Purpose: CurlHttpRequest.Post method                                       *
375  *                                                                            *
376  ******************************************************************************/
es_httprequest_post(duk_context * ctx)377 static duk_ret_t	es_httprequest_post(duk_context *ctx)
378 {
379 	return es_httprequest_query(ctx, "POST");
380 }
381 
382 /******************************************************************************
383  *                                                                            *
384  * Function: es_httprequest_delete                                            *
385  *                                                                            *
386  * Purpose: CurlHttpRequest.Delete method                                     *
387  *                                                                            *
388  ******************************************************************************/
es_httprequest_delete(duk_context * ctx)389 static duk_ret_t	es_httprequest_delete(duk_context *ctx)
390 {
391 	return es_httprequest_query(ctx, "DELETE");
392 }
393 
394 /******************************************************************************
395  *                                                                            *
396  * Function: es_httprequest_set_proxy                                         *
397  *                                                                            *
398  * Purpose: CurlHttpRequest.SetProxy method                                   *
399  *                                                                            *
400  ******************************************************************************/
es_httprequest_set_proxy(duk_context * ctx)401 static duk_ret_t	es_httprequest_set_proxy(duk_context *ctx)
402 {
403 	zbx_es_httprequest_t	*request;
404 	CURLcode		err;
405 	int			err_index = -1;
406 
407 	if (NULL == (request = es_httprequest(ctx)))
408 		return duk_error(ctx, DUK_RET_EVAL_ERROR, "internal scripting error: null object");
409 
410 	ZBX_CURL_SETOPT(ctx, request->handle, CURLOPT_PROXY, duk_to_string(ctx, 0), err);
411 out:
412 	if (-1 != err_index)
413 		return duk_throw(ctx);
414 
415 	return 0;
416 }
417 
418 /******************************************************************************
419  *                                                                            *
420  * Function: es_httprequest_status                                            *
421  *                                                                            *
422  * Purpose: CurlHttpRequest.Status method                                     *
423  *                                                                            *
424  ******************************************************************************/
es_httprequest_status(duk_context * ctx)425 static duk_ret_t	es_httprequest_status(duk_context *ctx)
426 {
427 	zbx_es_httprequest_t	*request;
428 	long			response_code;
429 	CURLcode		err;
430 
431 	if (NULL == (request = es_httprequest(ctx)))
432 		return duk_error(ctx, DUK_RET_EVAL_ERROR, "internal scripting error: null object");
433 
434 	if (CURLE_OK != (err = curl_easy_getinfo(request->handle, CURLINFO_RESPONSE_CODE, &response_code)))
435 		return duk_error(ctx, DUK_RET_EVAL_ERROR, "cannot obtain request status: %s", curl_easy_strerror(err));
436 
437 	duk_push_number(ctx, (duk_double_t)response_code);
438 
439 	return 1;
440 }
441 
442 /******************************************************************************
443  *                                                                            *
444  * Function: parse_header                                                     *
445  *                                                                            *
446  * Purpose: retrieves value of a header                                       *
447  *                                                                            *
448  * Parameters: header    - [IN] the http header to extract value from         *
449  *             value_out - [OUT] the value                                    *
450  *                                                                            *
451  ******************************************************************************/
parse_header(char * header,char ** value_out)452 static int	parse_header(char *header, char **value_out)
453 {
454 	char *value;
455 
456 	if (NULL == (value = strchr(header, ':')))
457 		return FAIL;
458 
459 	*value++ = '\0';
460 	while (' ' == *value || '\t' == *value)
461 		value++;
462 
463 	*value_out = value;
464 
465 	return SUCCEED;
466 }
467 
468 /******************************************************************************
469  *                                                                            *
470  * Function: es_obj_put_http_header                                           *
471  *                                                                            *
472  * Purpose: puts http header <field>: <value> as object property/value        *
473  *                                                                            *
474  * Parameters: ctx    - [IN] the duktape context                              *
475  *             idx    - [IN] the object index on duktape stack                *
476  *             header - [IN] the http header to parse and put                 *
477  *                                                                            *
478  ******************************************************************************/
es_put_header(duk_context * ctx,int idx,char * header)479 static void	es_put_header(duk_context *ctx, int idx, char *header)
480 {
481 	char	*value;
482 
483 	if (FAIL == parse_header(header, &value))
484 		return;
485 
486 	duk_push_string(ctx, value);
487 
488 	/* duk_put_prop_string() throws error on failure, no need to check return code */
489 	(void)duk_put_prop_string(ctx, idx, header);
490 }
491 
492 /******************************************************************************
493  *                                                                            *
494  * Function: get_headers_as_strings                                           *
495  *                                                                            *
496  * Purpose: retrieve headers from request in form of strings                  *
497  *                                                                            *
498  * Parameters: ctx     - [IN] the duktape context                             *
499  *             request - [IN] the request to retrieve headers from            *
500  *                                                                            *
501  ******************************************************************************/
get_headers_as_strings(duk_context * ctx,zbx_es_httprequest_t * request)502 static duk_ret_t	get_headers_as_strings(duk_context *ctx, zbx_es_httprequest_t *request)
503 {
504 	char			*ptr, *header;
505 	duk_idx_t		idx;
506 
507 	idx = duk_push_object(ctx);
508 
509 	if (0 == request->headers_in_offset)
510 		return 1;
511 
512 	for (ptr = request->headers_in; NULL != (header = zbx_http_parse_header(&ptr)); )
513 	{
514 		es_put_header(ctx, idx, header);
515 		zbx_free(header);
516 	}
517 
518 	return 1;
519 }
520 
521 typedef struct
522 {
523 	char			*name;
524 	zbx_vector_str_t	values;
525 }
526 zbx_cached_header_t;
527 
cached_headers_free(zbx_cached_header_t * header)528 static void	cached_headers_free(zbx_cached_header_t *header)
529 {
530 	zbx_vector_str_clear_ext(&header->values, zbx_str_free);
531 	zbx_vector_str_destroy(&header->values);
532 	zbx_free(header->name);
533 	zbx_free(header);
534 }
535 
536 /******************************************************************************
537  *                                                                            *
538  * Function: get_headers_as_arrays                                            *
539  *                                                                            *
540  * Purpose: retrieve headers from request in form of arrays                   *
541  *                                                                            *
542  * Parameters: ctx     - [IN] the duktape context                             *
543  *             request - [IN] the request to retrieve headers from            *
544  *                                                                            *
545  ******************************************************************************/
get_headers_as_arrays(duk_context * ctx,zbx_es_httprequest_t * request)546 static duk_ret_t	get_headers_as_arrays(duk_context *ctx, zbx_es_httprequest_t *request)
547 {
548 	char			*ptr, *header;
549 	zbx_vector_ptr_t	headers;
550 	duk_idx_t		idx;
551 	int			i, j;
552 
553 	zbx_vector_ptr_create(&headers);
554 
555 	idx = duk_push_object(ctx);
556 
557 	if (0 == request->headers_in_offset)
558 		goto out;
559 
560 	for (ptr = request->headers_in; NULL != (header = zbx_http_parse_header(&ptr)); )
561 	{
562 		char			*value;
563 		zbx_cached_header_t	*existing_header = NULL;
564 
565 		if (FAIL == parse_header(header, &value))
566 		{
567 			zbx_free(header);
568 			continue;
569 		}
570 
571 		for (j = 0; j < headers.values_num; j++)
572 		{
573 			zbx_cached_header_t *h = (zbx_cached_header_t*)headers.values[j];
574 
575 			if (0 == strcmp(header, h->name))
576 			{
577 				existing_header = h;
578 				zbx_vector_str_append(&existing_header->values, zbx_strdup(NULL, value));
579 				zbx_free(header);
580 
581 				break;
582 			}
583 		}
584 
585 		if (NULL == existing_header)
586 		{
587 			zbx_cached_header_t	*cached_header;
588 
589 			cached_header = zbx_malloc(NULL, sizeof(zbx_cached_header_t));
590 
591 			cached_header->name = header;
592 			zbx_vector_str_create(&cached_header->values);
593 			zbx_vector_str_append(&cached_header->values, zbx_strdup(NULL, value));
594 			zbx_vector_ptr_append(&headers, cached_header);
595 		}
596 	}
597 
598 	for (i = 0; i < headers.values_num; i++) {
599 		zbx_cached_header_t	*h = (zbx_cached_header_t*)headers.values[i];
600 		duk_idx_t		arr_idx;
601 		int			j;
602 
603 		arr_idx = duk_push_array(ctx);
604 
605 		for (j = 0; j < h->values.values_num; j++)
606 		{
607 			duk_push_string(ctx, h->values.values[j]);
608 			duk_put_prop_index(ctx, arr_idx, (duk_uarridx_t)j);
609 		}
610 
611 		(void)duk_put_prop_string(ctx, idx, h->name);
612 	}
613 
614 out:
615 	zbx_vector_ptr_clear_ext(&headers, (zbx_mem_free_func_t)cached_headers_free);
616 	zbx_vector_ptr_destroy(&headers);
617 	return 1;
618 }
619 
620 /******************************************************************************
621  *                                                                            *
622  * Function: es_httprequest_get_headers                                       *
623  *                                                                            *
624  * Purpose: CurlHttpRequest.GetHeaders method                                 *
625  *                                                                            *
626  ******************************************************************************/
es_httprequest_get_headers(duk_context * ctx)627 static duk_ret_t	es_httprequest_get_headers(duk_context *ctx)
628 {
629 	zbx_es_httprequest_t	*request;
630 
631 	if (NULL == (request = es_httprequest(ctx)))
632 		return duk_error(ctx, DUK_RET_EVAL_ERROR, "internal scripting error: null object");
633 
634 	if (0 == duk_is_null_or_undefined(ctx, 0))
635 	{
636 		duk_bool_t	as_array;
637 
638 		as_array = duk_to_boolean(ctx, 0);
639 
640 		if (0 != as_array)
641 			return get_headers_as_arrays(ctx, request);
642 	}
643 
644 	return get_headers_as_strings(ctx, request);
645 }
646 
647 /******************************************************************************
648  *                                                                            *
649  * Function: es_httprequest_set_httpauth                                      *
650  *                                                                            *
651  * Purpose: CurlHttpRequest.SetHttpAuth method                                *
652  *                                                                            *
653  ******************************************************************************/
es_httprequest_set_httpauth(duk_context * ctx)654 static duk_ret_t	es_httprequest_set_httpauth(duk_context *ctx)
655 {
656 	zbx_es_httprequest_t	*request;
657 	char			*username = NULL, *password = NULL;
658 	int			err_index = -1, mask;
659 	CURLcode		err;
660 
661 	if (NULL == (request = es_httprequest(ctx)))
662 		return duk_error(ctx, DUK_RET_EVAL_ERROR, "internal scripting error: null object");
663 
664 	mask = duk_to_int32(ctx, 0);
665 
666 	if (0 != (mask & ~(ZBX_HTTPAUTH_BASIC | ZBX_HTTPAUTH_DIGEST | ZBX_HTTPAUTH_NEGOTIATE | ZBX_HTTPAUTH_NTLM)))
667 		return duk_error(ctx, DUK_RET_EVAL_ERROR, "invalid HTTP authentication mask");
668 
669 	if (0 == duk_is_null_or_undefined(ctx, 1))
670 	{
671 		if (SUCCEED != zbx_cesu8_to_utf8(duk_to_string(ctx, 1), &username))
672 		{
673 			err_index = duk_push_error_object(ctx, DUK_RET_TYPE_ERROR, "cannot convert username to utf8");
674 			goto out;
675 		}
676 	}
677 
678 	if (0 == duk_is_null_or_undefined(ctx, 2))
679 	{
680 		if (SUCCEED != zbx_cesu8_to_utf8(duk_to_string(ctx, 2), &password))
681 		{
682 			err_index = duk_push_error_object(ctx, DUK_RET_TYPE_ERROR, "cannot convert username to utf8");
683 			goto out;
684 		}
685 	}
686 
687 	ZBX_CURL_SETOPT(ctx, request->handle, CURLOPT_HTTPAUTH, mask, err);
688 
689 	if (NULL != username)
690 		ZBX_CURL_SETOPT(ctx, request->handle, CURLOPT_USERNAME, username, err);
691 
692 	if (NULL != password)
693 		ZBX_CURL_SETOPT(ctx, request->handle, CURLOPT_PASSWORD, password, err);
694 
695 out:
696 	zbx_free(password);
697 	zbx_free(username);
698 
699 	if (-1 != err_index)
700 		return duk_throw(ctx);
701 
702 	return 0;
703 }
704 
705 static const duk_function_list_entry	curlhttprequest_methods[] = {
706 	{"AddHeader", es_httprequest_add_header, 1},
707 	{"ClearHeader", es_httprequest_clear_header, 0},
708 	{"Get", es_httprequest_get, 2},
709 	{"Put", es_httprequest_put, 2},
710 	{"Post", es_httprequest_post, 2},
711 	{"Delete", es_httprequest_delete, 2},
712 	{"Status", es_httprequest_status, 0},
713 	{"SetProxy", es_httprequest_set_proxy, 1},
714 	{"GetHeaders", es_httprequest_get_headers, 1},
715 	{"SetHttpAuth", es_httprequest_set_httpauth, 3},
716 	{NULL, NULL, 0}
717 };
718 
719 static const duk_function_list_entry	httprequest_methods[] = {
720 	{"addHeader", es_httprequest_add_header, 1},
721 	{"clearHeader", es_httprequest_clear_header, 0},
722 	{"get", es_httprequest_get, 2},
723 	{"put", es_httprequest_put, 2},
724 	{"post", es_httprequest_post, 2},
725 	{"delete", es_httprequest_delete, 2},
726 	{"getStatus", es_httprequest_status, 0},
727 	{"setProxy", es_httprequest_set_proxy, 1},
728 	{"getHeaders", es_httprequest_get_headers, 1},
729 	{"setHttpAuth", es_httprequest_set_httpauth, 3},
730 	{NULL, NULL, 0}
731 };
732 
733 #else
734 
es_httprequest_ctor(duk_context * ctx)735 static duk_ret_t	es_httprequest_ctor(duk_context *ctx)
736 {
737 	if (!duk_is_constructor_call(ctx))
738 		return DUK_RET_EVAL_ERROR;
739 
740 	return duk_error(ctx, DUK_RET_EVAL_ERROR, "missing cURL library");
741 }
742 
743 static const duk_function_list_entry	httprequest_methods[] = {
744 	{NULL, NULL, 0}
745 };
746 
747 static const duk_function_list_entry	*curlhttprequest_methods = httprequest_methods;
748 #endif
749 
es_httprequest_create_prototype(duk_context * ctx,const char * obj_name,const duk_function_list_entry * methods)750 static int	es_httprequest_create_prototype(duk_context *ctx, const char *obj_name,
751 		const duk_function_list_entry *methods)
752 {
753 	duk_push_c_function(ctx, es_httprequest_ctor, 0);
754 	duk_push_object(ctx);
755 
756 	duk_put_function_list(ctx, -1, methods);
757 
758 	if (1 != duk_put_prop_string(ctx, -2, "prototype"))
759 		return FAIL;
760 
761 	if (1 != duk_put_global_string(ctx, obj_name))
762 		return FAIL;
763 
764 	return SUCCEED;
765 }
766 
zbx_es_init_httprequest(zbx_es_t * es,char ** error)767 int	zbx_es_init_httprequest(zbx_es_t *es, char **error)
768 {
769 	if (0 != setjmp(es->env->loc))
770 	{
771 		*error = zbx_strdup(*error, es->env->error);
772 		return FAIL;
773 	}
774 
775 	if (FAIL == es_httprequest_create_prototype(es->env->ctx, "CurlHttpRequest", curlhttprequest_methods) ||
776 			FAIL == es_httprequest_create_prototype(es->env->ctx, "HttpRequest", httprequest_methods))
777 	{
778 		*error = zbx_strdup(*error, duk_safe_to_string(es->env->ctx, -1));
779 		duk_pop(es->env->ctx);
780 		return FAIL;
781 	}
782 
783 #ifdef HAVE_LIBCURL
784 	duk_push_number(es->env->ctx, ZBX_HTTPAUTH_NONE);
785 	duk_put_global_string(es->env->ctx, "HTTPAUTH_NONE");
786 	duk_push_number(es->env->ctx, ZBX_HTTPAUTH_BASIC);
787 	duk_put_global_string(es->env->ctx, "HTTPAUTH_BASIC");
788 	duk_push_number(es->env->ctx, ZBX_HTTPAUTH_DIGEST);
789 	duk_put_global_string(es->env->ctx, "HTTPAUTH_DIGEST");
790 	duk_push_number(es->env->ctx, ZBX_HTTPAUTH_NEGOTIATE);
791 	duk_put_global_string(es->env->ctx, "HTTPAUTH_NEGOTIATE");
792 	duk_push_number(es->env->ctx, ZBX_HTTPAUTH_NTLM);
793 	duk_put_global_string(es->env->ctx, "HTTPAUTH_NTLM");
794 #endif
795 
796 	return SUCCEED;
797 }
798