1 /*
2  * Copyright (C) 2011 VoIP Embedded, Inc.
3  *
4  * This file is part of Kamailio, a free SIP server.
5  *
6  * Kamailio is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version
10  *
11  * Kamailio is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21 
22 #include <string.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <stdarg.h>
26 
27 #include "../../core/ver.h"
28 #include "../../core/trim.h"
29 #include "../../core/sr_module.h"
30 #include "../../core/nonsip_hooks.h"
31 #include "../../core/kemi.h"
32 #include "../../modules/xhttp/api.h"
33 #include "xhttp_rpc.h"
34 #include "xhttp_rpc_fnc.h"
35 
36 /** @addtogroup xhttp_rpc
37  * @ingroup modules
38  * @{
39  *
40  * <h1>Overview of Operation</h1>
41  * This module provides a web interface for RPC management interface.
42  * It is built on top of the xhttp API module.
43  */
44 
45 /** @file
46  *
47  * This is the main file of xhttp_rpc module which contains all the functions
48  * related to http processing, as well as the module interface.
49  */
50 
51 MODULE_VERSION
52 
53 str XHTTP_RPC_REASON_OK = str_init("OK");
54 str XHTTP_RPC_CONTENT_TYPE_TEXT_HTML = str_init("text/html");
55 
56 
57 xhttp_rpc_mod_cmds_t *xhttp_rpc_mod_cmds = NULL;
58 int xhttp_rpc_mod_cmds_size = 0;
59 
60 /* FIXME: this should be initialized in ../../core/ver.c */
61 int full_version_len;
62 int ver_name_len;
63 
64 static int mod_init(void);
65 static int child_init(int rank);
66 static int xhttp_rpc_dispatch(sip_msg_t* msg, char* s1, char* s2);
67 
68 
69 /** The context of the xhttp_rpc request being processed.
70  *
71  * This is a global variable that records the context of the xhttp_rpc request
72  * being currently processed.
73  * @sa rpc_ctx
74  */
75 static rpc_ctx_t ctx;
76 
77 static xhttp_api_t xhttp_api;
78 
79 /** Pointers to the functions that implement the RPC interface
80  * of xhttp_rpc module
81  */
82 static rpc_t func_param;
83 
84 str xhttp_rpc_root = str_init("rpc");
85 int buf_size = 0;
86 char error_buf[ERROR_REASON_BUF_LEN];
87 
88 static cmd_export_t cmds[] = {
89 	{"dispatch_xhttp_rpc",(cmd_function)xhttp_rpc_dispatch,0,0,0,
90 			REQUEST_ROUTE|EVENT_ROUTE},
91 	{0, 0, 0, 0, 0, 0}
92 };
93 
94 static param_export_t params[] = {
95 	{"xhttp_rpc_root",	PARAM_STR,	&xhttp_rpc_root},
96 	{"xhttp_rpc_buf_size",	INT_PARAM,	&buf_size},
97 	{0, 0, 0}
98 };
99 
100 /** module exports */
101 struct module_exports exports= {
102 	"xhttp_rpc",		/* module name */
103 	DEFAULT_DLFLAGS,	/* dlopen flags */
104 	cmds,				/* cmd (cfg function) exports */
105 	params,				/* param exports */
106 	0,					/* RPC method exports */
107 	0,					/* pv exports */
108 	0,					/* response handling function */
109 	mod_init,			/* module init function */
110 	child_init,			/* per-child init function */
111 	0					/* module destroy function */
112 };
113 
114 
115 /** Implementation of rpc_fault function required by the management API.
116  *
117  * This function will be called whenever a management function
118  * indicates that an error ocurred while it was processing the request. The
119  * function takes the reply code and reason phrase as parameters, these will
120  * be put in the body of the reply.
121  *
122  * @param ctx A pointer to the context structure of the request being
123  *            processed.
124  * @param code Reason code.
125  * @param fmt Formatting string used to build the reason phrase.
126  */
rpc_fault(rpc_ctx_t * ctx,int code,char * fmt,...)127 static void rpc_fault(rpc_ctx_t* ctx, int code, char* fmt, ...)
128 {
129 	va_list ap;
130 	struct xhttp_rpc_reply *reply = &ctx->reply;
131 
132 	reply->code = code;
133 	va_start(ap, fmt);
134 	vsnprintf(error_buf, ERROR_REASON_BUF_LEN, fmt, ap);
135 	va_end(ap);
136 	reply->reason.len = strlen(error_buf);
137 	reply->reason.s = error_buf;
138 	/* reset body so we can print the error */
139 	reply->body.len = 0;
140 
141 	return;
142 }
143 
144 
145 /**
146  */
free_data_struct(struct rpc_data_struct * rpc_d)147 static void free_data_struct(struct rpc_data_struct *rpc_d)
148 {
149 	struct rpc_data_struct *ds;
150 
151 	if (!rpc_d) {
152 		LM_ERR("Atempting to free NULL rpc_data_struct\n");
153 		return;
154 	}
155 	while (rpc_d) {
156 		ds = rpc_d->next;
157 		pkg_free(rpc_d);
158 		rpc_d = ds;
159 	}
160 	return;
161 }
162 
163 
164 /**
165  */
new_data_struct(rpc_ctx_t * ctx)166 static struct rpc_data_struct *new_data_struct(rpc_ctx_t* ctx)
167 {
168 	struct rpc_data_struct *ds;
169 
170 	if (!ctx) return NULL;
171 	ds = (struct rpc_data_struct*)pkg_malloc(sizeof(struct rpc_data_struct));
172 	if (!ds) {
173 		PKG_MEM_ERROR;
174 		rpc_fault(ctx, 500, "Internal Server Error (oom)");
175 		return NULL;
176 	}
177 	memset(ds, 0, sizeof(struct rpc_data_struct));
178 	ds->ctx = ctx;
179 
180 	return ds;
181 }
182 
183 
184 /** Initialize xhttp_rpc reply data structure.
185  *
186  * This function initializes the data structure that contains all data related
187  * to the xhttp_rpc reply being created. The function must be called before any
188  * other function that adds data to the reply.
189  * @param ctx rpc_ctx_t structure to be initialized.
190  * @return 0 on success, a negative number on error.
191  */
init_xhttp_rpc_reply(rpc_ctx_t * ctx)192 static int init_xhttp_rpc_reply(rpc_ctx_t *ctx)
193 {
194 	struct xhttp_rpc_reply *reply = &ctx->reply;
195 
196 	reply->code = 200;
197 	reply->reason = XHTTP_RPC_REASON_OK;
198 	reply->buf.s = pkg_malloc(buf_size);
199 	if (!reply->buf.s) {
200 		PKG_MEM_ERROR;
201 		rpc_fault(ctx, 500, "Internal Server Error (No memory left)");
202 		return -1;
203 	}
204 	reply->buf.len = buf_size;
205 	reply->body.s = reply->buf.s;
206 	reply->body.len = 0;
207 	return 0;
208 }
209 
210 
211 /** Implementation of rpc_send function required by the management API.
212  *
213  * This is the function that will be called whenever a management function
214  * asks the management interface to send the reply to the client.
215  * The SIP/HTTP reply sent to
216  * the client will be always 200 OK, if an error ocurred on the server then it
217  * will be indicated in the html document in body.
218  *
219  * @param ctx A pointer to the context structure of the xhttp_rpc request that
220  *            generated the reply.
221  * @return 1 if the reply was already sent, 0 on success, a negative number on
222  *            error
223  */
rpc_send(rpc_ctx_t * ctx)224 static int rpc_send(rpc_ctx_t* ctx)
225 {
226 	struct xhttp_rpc_reply* reply;
227 
228 	if (ctx->reply_sent) return 1;
229 
230 	reply = &ctx->reply;
231 
232 	if (0!=xhttp_rpc_build_page(ctx)){
233 		rpc_fault(ctx, 500, "Internal Server Error");
234 	}
235 
236 	ctx->reply_sent = 1;
237 	if (reply->body.len)
238 		xhttp_api.reply(ctx->msg, reply->code, &reply->reason,
239 			&XHTTP_RPC_CONTENT_TYPE_TEXT_HTML, &reply->body);
240 	else
241 		xhttp_api.reply(ctx->msg, reply->code, &reply->reason,
242 			&XHTTP_RPC_CONTENT_TYPE_TEXT_HTML, &reply->reason);
243 
244 	if (reply->buf.s) {
245 		pkg_free(reply->buf.s);
246 		reply->buf.s = NULL;
247 		reply->buf.len = 0;
248 	}
249 	if (ctx->arg.s) {
250 		pkg_free(ctx->arg.s);
251 		ctx->arg.s = NULL;
252 		ctx->arg.len = 0;
253 	}
254 	if (ctx->data_structs) {
255 		free_data_struct(ctx->data_structs);
256 		ctx->data_structs = NULL;
257 	}
258 
259 	return 0;
260 }
261 
262 
263 /** Converts the variables provided in parameter ap according to formatting
264  * string provided in parameter fmt into HTML format.
265  *
266  * This function takes the parameters provided in ap parameter and creates
267  * HTML formatted parameters that will be put in the html document.
268  * The format of input parameters is described in formatting string
269  * fmt which follows the syntax of the management API. In the case of
270  * an error the function will generate an error reply in err_reply parameter
271  * instead.
272  * @param ctx An error reply document will be generated here if the
273  *                  function encounters a problem while processing input
274  *                  parameters.
275  * @param fmt Formatting string of the management API.
276  * @param ap A pointer to the array of input parameters.
277  *
278  */
print_value(rpc_ctx_t * ctx,char fmt,va_list * ap,str * id)279 static int print_value(rpc_ctx_t* ctx, char fmt, va_list* ap, str *id)
280 
281 {
282 	str body;
283 	str *sp;
284 	char buf[PRINT_VALUE_BUF_LEN];
285 	time_t dt;
286 	struct tm t;
287 
288 	switch(fmt) {
289 	case 'd':
290 		body.s = sint2str(va_arg(*ap, int), &body.len);
291 		break;
292 	case 'f':
293 		body.s = buf;
294 		body.len = snprintf(buf, PRINT_VALUE_BUF_LEN,
295 				"%f", va_arg(*ap, double));
296 		if (body.len < 0) {
297 			LM_ERR("Error while converting double\n");
298 			return -1;
299 		}
300 		break;
301 	case 'b':
302 		body.len = 1;
303 		body.s = ((va_arg(*ap, int)==0)?"0":"1");
304 		break;
305 	case 't':
306 		body.s = buf;
307 		body.len = sizeof("19980717T14:08:55") - 1;
308 		dt = va_arg(*ap, time_t);
309 		gmtime_r(&dt, &t);
310 		if (strftime(buf, PRINT_VALUE_BUF_LEN,
311 				"%Y%m%dT%H:%M:%S", &t) == 0) {
312 			LM_ERR("Error while converting time\n");
313 			return -1;
314 		}
315 		break;
316 	case 's':
317 		body.s = va_arg(*ap, char*);
318 		body.len = strlen(body.s);
319 		break;
320 	case 'S':
321 		sp = va_arg(*ap, str*);
322 		body = *sp;
323 		break;
324 	default:
325 		body.len = 0;
326 		body.s = NULL;
327 		LM_ERR("Invalid formatting character [%c]\n", fmt);
328 		return -1;
329 	}
330 	if (0!=xhttp_rpc_build_content(ctx, &body, id)) {
331 		rpc_fault(ctx, 500, "Internal Server Error");
332 		return -1;
333 	}
334 	return 0;
335 }
336 
337 
338 /** Implementation of rpc_add function required by the management API.
339  *
340  * This function will be called when an RPC management function calls
341  * rpc->add to add a parameter to the xhttp_rpc reply being generated.
342  */
rpc_add(rpc_ctx_t * ctx,char * fmt,...)343 static int rpc_add(rpc_ctx_t* ctx, char* fmt, ...)
344 {
345 	void **void_ptr;
346 	struct rpc_data_struct *ds;
347 	va_list ap;
348 
349 	if (0!=xhttp_rpc_build_content(ctx, NULL, NULL)) {
350 		rpc_fault(ctx, 500, "Internal Server Error");
351 		return -1;
352 	}
353 	va_start(ap, fmt);
354 	while(*fmt) {
355 		if (*fmt == '{' || *fmt == '[') {
356 			void_ptr = va_arg(ap, void**);
357 			ds = new_data_struct(ctx);
358 			if (!ds) goto err;
359 			if (ctx->data_structs) free_data_struct(ctx->data_structs);
360 			ctx->data_structs = ds;
361 			*void_ptr = ds;
362 		} else {
363 			if (print_value(ctx, *fmt, &ap, NULL) < 0) goto err;
364 		}
365 		fmt++;
366 	}
367 	va_end(ap);
368 	return 0;
369 err:
370 	va_end(ap);
371 	return -1;
372 }
373 
374 
375 /** Implementation of rpc->scan function required by the management API.
376  *
377  * This is the function that will be called whenever a management function
378  * calls rpc->scan to get the value of parameter from the xhttp_rpc
379  * request. This function will extract the current parameter from the xhttp_rpc
380  * URL and attempts to convert it to the type requested by the management
381  * function that called it.
382  */
rpc_scan(rpc_ctx_t * ctx,char * fmt,...)383 static int rpc_scan(rpc_ctx_t* ctx, char* fmt, ...)
384 {
385 	int *int_ptr;
386 	char **char_ptr;
387 	double *double_ptr;
388 	str *str_ptr;
389 
390 	str arg;
391 
392 	int mandatory_param = 1;
393 	int modifiers = 0;
394 	char* orig_fmt;
395 	va_list ap;
396 
397 	orig_fmt=fmt;
398 	va_start(ap, fmt);
399 	while(*fmt) {
400 		switch(*fmt) {
401 		case '*': /* start of optional parameters */
402 			mandatory_param = 0;
403 			modifiers++;
404 			fmt++;
405 			continue;
406 			break;
407 		case '.': /* autoconvert */
408 			modifiers++;
409 			fmt++;
410 			continue;
411 			break;
412 		case 'b': /* Bool */
413 		case 't': /* Date and time */
414 		case 'd': /* Integer */
415 			xhttp_rpc_get_next_arg(ctx, &arg);
416 			if (arg.len==0)
417 				goto read_error;
418 			int_ptr = va_arg(ap, int*);
419 			*int_ptr = strtol(arg.s, 0, 0);
420 			break;
421 		case 'f': /* double */
422 			xhttp_rpc_get_next_arg(ctx, &arg);
423 			if (arg.len==0)
424 				goto read_error;
425 			double_ptr = va_arg(ap, double*);
426 			*double_ptr = strtod(arg.s, 0);
427 			break;
428 		case 's': /* zero terminated string */
429 			xhttp_rpc_get_next_arg(ctx, &arg);
430 			if (arg.len==0)
431 				goto read_error;
432 			char_ptr = va_arg(ap, char**);
433 			*char_ptr = arg.s;
434 			break;
435 		case 'S': /* str structure */
436 			xhttp_rpc_get_next_arg(ctx, &arg);
437 			if (arg.len==0)
438 				goto read_error;
439 			str_ptr = va_arg(ap, str*);
440 			*str_ptr = arg;
441 			break;
442 		case '{':
443 			xhttp_rpc_get_next_arg(ctx, &arg);
444 			if (arg.len==0)
445 				goto read_error;
446 			LM_ERR("Unsupported param type [{]\n");
447 			rpc_fault(ctx, 500, "Unsupported param type [{]");
448 			goto error;
449 			break;
450 		default:
451 			LM_ERR("Invalid param type in formatting string: [%c]\n", *fmt);
452 			rpc_fault(ctx, 500,
453 				"Internal Server Error (inval formatting str)");
454 			goto error;
455 		}
456 		fmt++;
457 	}
458 	va_end(ap);
459 	return (int)(fmt-orig_fmt)-modifiers;
460 read_error:
461 	if (mandatory_param) rpc_fault(ctx, 400, "Invalid parameter value");
462 error:
463 	va_end(ap);
464 	return -((int)(fmt-orig_fmt)-modifiers);
465 }
466 
467 
468 /** Implementation of rpc_rpl_printf function required by the management API.
469  *
470  * This function will be called whenever an RPC management function calls
471  * rpc-printf to add a parameter to the xhttp_rpc reply being constructed.
472  */
rpc_rpl_printf(rpc_ctx_t * ctx,char * fmt,...)473 static int rpc_rpl_printf(rpc_ctx_t* ctx, char* fmt, ...)
474 {
475 	int n, size;
476 	char *p;
477 	va_list ap;
478 
479 	if (0!=xhttp_rpc_build_content(ctx, NULL, NULL)) {
480 		rpc_fault(ctx, 500, "Internal Server Error");
481 		return -1;
482 	}
483 
484 	p = ctx->reply.body.s + ctx->reply.body.len;
485 	size = ctx->reply.buf.len - ctx->reply.body.len;
486 	va_start(ap, fmt);
487 	n = vsnprintf(p, size, fmt, ap);
488 	va_end(ap);
489 	if (n > -1 && n < size) {
490 		ctx->reply.body.len += n;
491 		p += n;
492 	} else {
493 		LM_ERR("oom\n");
494 		rpc_fault(ctx, 500, "Internal Server Error (oom)");
495 		return -1;
496 	}
497 	if (0!=xhttp_rpc_insert_break(ctx)) {
498 		LM_ERR("oom\n");
499 		rpc_fault(ctx, 500, "Internal Server Error (oom)");
500 		return -1;
501 	}
502 
503 	return 0;
504 }
505 
506 
507 /** Adds a new member to structure.
508  */
rpc_struct_add(struct rpc_data_struct * rpc_s,char * fmt,...)509 static int rpc_struct_add(struct rpc_data_struct* rpc_s, char* fmt, ...)
510 {
511 	va_list ap;
512 	void **void_ptr;
513 	str member_name;
514 	rpc_ctx_t *ctx = rpc_s->ctx;
515 	struct rpc_data_struct *ds, *s;
516 
517 	if (!ctx) {
518 		LM_ERR("Invalid context\n");
519 		return -1;
520 	}
521 	if (!ctx->data_structs) {
522 		LM_ERR("Invalid structs\n");
523 		return -1;
524 	}
525 	s = ds = ctx->data_structs;
526 	ctx->struc_depth = 0;
527 	while (s) {
528 		if (s == rpc_s) {
529 			if (s->next) {
530 				free_data_struct(s->next);
531 				s->next = NULL;
532 			}
533 			break;
534 		}
535 		ctx->struc_depth++;
536 		ds = s;
537 		s = s->next;
538 	}
539 	if (!s)
540 		s = ds;
541 	va_start(ap, fmt);
542 	while(*fmt) {
543 		member_name.s = va_arg(ap, char*);
544 		member_name.len = (member_name.s?strlen(member_name.s):0);
545 		if (*fmt == '{' || *fmt == '[') {
546 			void_ptr = va_arg(ap, void**);
547 			ds = new_data_struct(ctx);
548 			if (!ds) goto err;
549 			s->next = ds;
550 			*void_ptr = ds;
551 			if (0!=xhttp_rpc_build_content(ctx, NULL, &member_name))
552 				goto err;
553 		} else {
554 			if (print_value(ctx, *fmt, &ap, &member_name) < 0) goto err;
555 		}
556 		fmt++;
557 	}
558 	va_end(ap);
559 	return 0;
560 err:
561 	va_end(ap);
562 	return -1;
563 }
564 
565 
566 /** Adds a new member to array.
567  */
rpc_array_add(struct rpc_data_struct * rpc_s,char * fmt,...)568 static int rpc_array_add(struct rpc_data_struct* rpc_s, char* fmt, ...)
569 {
570 	va_list ap;
571 	void **void_ptr;
572 	rpc_ctx_t *ctx = rpc_s->ctx;
573 	struct rpc_data_struct *ds, *s;
574 
575 	if (!ctx) {
576 		LM_ERR("Invalid context\n");
577 		return -1;
578 	}
579 	if (!ctx->data_structs) {
580 		LM_ERR("Invalid structs\n");
581 		return -1;
582 	}
583 	s = ds = ctx->data_structs;
584 	ctx->struc_depth = 0;
585 	while (s) {
586 		if (s == rpc_s) {
587 			if (s->next) {
588 				free_data_struct(s->next);
589 				s->next = NULL;
590 			}
591 			break;
592 		}
593 		ctx->struc_depth++;
594 		ds = s;
595 		s = s->next;
596 	}
597 	if (!s)
598 		s = ds;
599 	va_start(ap, fmt);
600 	while(*fmt) {
601 		if (*fmt == '{' || *fmt == '[') {
602 			void_ptr = va_arg(ap, void**);
603 			ds = new_data_struct(ctx);
604 			if (!ds) goto err;
605 			s->next = ds;
606 			*void_ptr = ds;
607 			if (0!=xhttp_rpc_build_content(ctx, NULL, NULL))
608 				goto err;
609 		} else {
610 			if (print_value(ctx, *fmt, &ap, NULL) < 0) goto err;
611 		}
612 		fmt++;
613 	}
614 	va_end(ap);
615 	return 0;
616 err:
617 	va_end(ap);
618 	return -1;
619 }
620 
621 
rpc_struct_scan(struct rpc_data_struct * rpc_s,char * fmt,...)622 static int rpc_struct_scan(struct rpc_data_struct* rpc_s, char* fmt, ...)
623 {
624 	LM_ERR("Not implemented\n");
625 	return -1;
626 }
627 
628 
629 /** Create a new member from formatting string and add it to a structure.
630  */
rpc_struct_printf(struct rpc_data_struct * rpc_s,char * member_name,char * fmt,...)631 static int rpc_struct_printf(struct rpc_data_struct* rpc_s, char* member_name, char* fmt, ...)
632 {
633 	va_list ap;
634 	char buf[PRINT_VALUE_BUF_LEN];
635 	int len;
636 	str _name,_body;
637 	rpc_ctx_t *ctx = rpc_s->ctx;
638 
639 	if (!ctx) {
640 		LM_ERR("Invalid context\n");
641 		return -1;
642 	}
643 
644 	va_start(ap, fmt);
645 	len=vsnprintf(buf, PRINT_VALUE_BUF_LEN, fmt, ap);
646 	va_end(ap);
647 	if ((len<0) || (len>PRINT_VALUE_BUF_LEN)){
648 		LM_ERR("buffer size exceeded [%d]\n", PRINT_VALUE_BUF_LEN);
649 		return -1;
650 	}
651 
652 	_name.s = member_name;
653 	_name.len = strlen(member_name);
654 	_body.s = buf;
655 	_body.len = len;
656 	if (0!=xhttp_rpc_build_content(ctx, &_body, &_name)) return -1;
657 
658 	return 0;
659 }
660 
661 
662 /** Returns the RPC capabilities supported by the xmlrpc driver.
663  */
rpc_capabilities(rpc_ctx_t * ctx)664 static rpc_capabilities_t rpc_capabilities(rpc_ctx_t* ctx)
665 {
666 	/* No support for async commands.
667 	 */
668 	return 0;
669 }
670 
671 
672 /** Returns a new "delayed reply" context.
673  * Creates a new delayed reply context in shm and returns it.
674  * @return 0 - not supported, already replied, or no more memory;
675  *         !=0 pointer to the special delayed ctx.
676  * Note1: one should use the returned ctx reply context to build a reply and
677  *  when finished call rpc_delayed_ctx_close().
678  * Note2: adding pieces to the reply in different processes is not supported.
679  */
rpc_delayed_ctx_new(rpc_ctx_t * ctx)680 static struct rpc_delayed_ctx* rpc_delayed_ctx_new(rpc_ctx_t* ctx)
681 {
682 	return NULL;
683 }
684 
685 
686 /** Closes a "delayed reply" context and sends the reply.
687  * If no reply has been sent the reply will be built and sent automatically.
688  * See the notes from rpc_new_delayed_ctx()
689  */
rpc_delayed_ctx_close(struct rpc_delayed_ctx * dctx)690 static void rpc_delayed_ctx_close(struct rpc_delayed_ctx* dctx)
691 {
692 	return;
693 }
694 
695 
mod_init(void)696 static int mod_init(void)
697 {
698 	int i;
699 
700 	/* bind the XHTTP API */
701 	if (xhttp_load_api(&xhttp_api) < 0) {
702 		LM_ERR("cannot bind to XHTTP API\n");
703 		return -1;
704 	}
705 
706 	/* Check xhttp_rpc_buf_size param */
707 	if (buf_size == 0)
708 		buf_size = pkg_mem_size/3;
709 
710 	/* Check xhttp_rpc_root param */
711 	for(i=0;i<xhttp_rpc_root.len;i++){
712 		if ( !isalnum(xhttp_rpc_root.s[i]) && xhttp_rpc_root.s[i]!='_') {
713 			LM_ERR("bad xhttp_rpc_root param [%.*s], char [%c] "
714 				"- use only alphanumerical chars\n",
715 				xhttp_rpc_root.len, xhttp_rpc_root.s,
716 				xhttp_rpc_root.s[i]);
717 			return -1;
718 		}
719 	}
720 
721 	memset(&func_param, 0, sizeof(func_param));
722 	func_param.send = (rpc_send_f)rpc_send;
723 	func_param.fault = (rpc_fault_f)rpc_fault;
724 	func_param.add = (rpc_add_f)rpc_add;
725 	func_param.scan = (rpc_scan_f)rpc_scan;
726 	func_param.rpl_printf = (rpc_rpl_printf_f)rpc_rpl_printf;
727 	func_param.struct_add = (rpc_struct_add_f)rpc_struct_add;
728 	func_param.array_add = (rpc_array_add_f)rpc_array_add;
729 	func_param.struct_scan = (rpc_struct_scan_f)rpc_struct_scan;
730 	func_param.struct_printf = (rpc_struct_printf_f)rpc_struct_printf;
731 	func_param.capabilities = (rpc_capabilities_f)rpc_capabilities;
732 	func_param.delayed_ctx_new = (rpc_delayed_ctx_new_f)rpc_delayed_ctx_new;
733 	func_param.delayed_ctx_close =
734 		(rpc_delayed_ctx_close_f)rpc_delayed_ctx_close;
735 
736 	return 0;
737 }
738 
child_init(int rank)739 static int child_init(int rank)
740 {
741 	int i, j;
742 	int len;
743 	xhttp_rpc_mod_cmds_t *cmds;
744 	/* rpc_export_t *rpc_e; */
745 
746 	if(rank==PROC_MAIN || rank==PROC_TCP_MAIN)
747 		return 0; /* do nothing for the main process */
748 
749 	if (rank==PROC_INIT)
750 	{
751 		/* building a cache of rpc module commands */
752 		xhttp_rpc_mod_cmds =
753 			(xhttp_rpc_mod_cmds_t*)pkg_malloc(sizeof(xhttp_rpc_mod_cmds_t));
754 		if (xhttp_rpc_mod_cmds==NULL){
755 			PKG_MEM_ERROR;
756 			return -1;
757 		}
758 		xhttp_rpc_mod_cmds->rpc_e_index = 0;
759 		xhttp_rpc_mod_cmds->mod.s = NULL;
760 		xhttp_rpc_mod_cmds->mod.len = 0;
761 		xhttp_rpc_mod_cmds->size = 0;
762 		xhttp_rpc_mod_cmds_size = 1;
763 		cmds = xhttp_rpc_mod_cmds;
764 		for(i=0; i<rpc_sarray_crt_size; i++){
765 			len = strlen(rpc_sarray[i]->name);
766 			j = 0;
767 			while (j<len && rpc_sarray[i]->name[j]!='.')
768 				j++;
769 			if (j==len) {
770 				LM_DBG("dropping invalid command format [%.*s]\n",
771 						len, rpc_sarray[i]->name);
772 			} else {
773 				if (cmds->mod.len==0) {
774 					/* this is the first module */
775 					cmds->rpc_e_index = i;
776 					cmds->mod.s = (char*)&rpc_sarray[i]->name[0];
777 					cmds->mod.len = j;
778 					cmds->size++;
779 				} else if (cmds->mod.len==j &&
780 					strncmp(cmds->mod.s,
781 						(char*)&rpc_sarray[i]->name[0],
782 						j)==0){
783 					cmds->size++;
784 				} else {
785 					cmds = (xhttp_rpc_mod_cmds_t*)
786 						pkg_realloc(xhttp_rpc_mod_cmds,
787 							(xhttp_rpc_mod_cmds_size+1)*
788 							sizeof(xhttp_rpc_mod_cmds_t));
789 					if (cmds==NULL){
790 						LM_ERR("oom\n");
791 						return -1;
792 					}
793 					xhttp_rpc_mod_cmds = cmds;
794 					cmds = &xhttp_rpc_mod_cmds[xhttp_rpc_mod_cmds_size];
795 					cmds->rpc_e_index = i;
796 					cmds->mod.s = (char*)&rpc_sarray[i]->name[0];
797 					cmds->mod.len = j;
798 					xhttp_rpc_mod_cmds_size++;
799 					cmds->size = 1;
800 				}
801 			}
802 		}
803 		/*
804 		for(i=0; i<xhttp_rpc_mod_cmds_size; i++){
805 			for (j=0; j<xhttp_rpc_mod_cmds[i].size; j++){
806 				rpc_e = rpc_sarray[xhttp_rpc_mod_cmds[i].rpc_e_index+j];
807 				LM_DBG("[%p] => [%p]->[%.*s] [%p]->[%s]\n",
808 					rpc_e,
809 					xhttp_rpc_mod_cmds[i].mod.s,
810 					xhttp_rpc_mod_cmds[i].mod.len,
811 					xhttp_rpc_mod_cmds[i].mod.s,
812 					rpc_e->name,
813 					rpc_e->name);
814 			}
815 		}
816 		*/
817 	}
818 
819 	full_version_len = strlen(full_version);
820 	ver_name_len = strlen(ver_name);
821 	return 0;
822 }
823 
824 
ki_xhttp_rpc_dispatch(sip_msg_t * msg)825 static int ki_xhttp_rpc_dispatch(sip_msg_t* msg)
826 {
827 	rpc_export_t* rpc_e;
828 	str arg = {NULL, 0};
829 	int ret = 0;
830 	int i;
831 
832 	if(!IS_HTTP(msg)) {
833 		LM_DBG("Got non HTTP msg\n");
834 		return NONSIP_MSG_PASS;
835 	}
836 
837 	/* Init xhttp_rpc context */
838 	if (ctx.reply.buf.s) LM_ERR("Unexpected buf value [%p][%d]\n",
839 				ctx.reply.buf.s, ctx.reply.buf.len);
840 	memset(&ctx, 0, sizeof(rpc_ctx_t));
841 	ctx.msg = msg;
842 	ctx.mod = ctx.cmd = -1;
843 	if (init_xhttp_rpc_reply(&ctx) < 0) goto send_reply;
844 
845 	/* Extract arguments from url */
846 	if (0!=xhttp_rpc_parse_url(&msg->first_line.u.request.uri,
847 				&ctx.mod, &ctx.cmd, &arg)){
848 		rpc_fault(&ctx, 500, "Bad URL");
849 		goto send_reply;
850 	}
851 
852 	if (arg.s) {
853 		if (arg.len) {
854 			/* Unescape args */
855 			ctx.arg.s = pkg_malloc((arg.len+1)*sizeof(char));
856 			if (ctx.arg.s==NULL){
857 				PKG_MEM_ERROR;
858 				rpc_fault(&ctx, 500, "Internal Server Error (oom)");
859 				goto send_reply;
860 			}
861 			for(i=0;i<arg.len;i++) if (arg.s[i]=='+') arg.s[i]=' ';
862 			if (0>un_escape(&arg, &ctx.arg)) {
863 				LM_ERR("unable to escape [%.*s]\n", arg.len, arg.s);
864 				rpc_fault(&ctx, 500, "Bad arg in URL");
865 				goto send_reply;
866 			}
867 			ctx.arg.s[ctx.arg.len] = '\0';
868 			ctx.arg.len++;
869 			ctx.arg2scan = ctx.arg;
870 		}
871 		ctx.arg_received = 1;
872 	} else {
873 		goto send_reply;
874 	}
875 
876 	/*
877 	rpc_e=find_rpc_export((char*)rpc_sarray[xhttp_rpc_mod_cmds[ctx.mod].rpc_e_index+ctx.cmd]->name, 0);
878 	if ((rpc_e==NULL) || (rpc_e->function==NULL)){
879 		LM_ERR("Unable to find rpc command [%s]\n",
880 		rpc_sarray[xhttp_rpc_mod_cmds[ctx.mod].rpc_e_index+ctx.cmd]->name);
881 		rpc_fault(&ctx, 500, "Method not found");
882 		goto send_reply;
883 	}
884 	*/
885 	rpc_e=rpc_sarray[xhttp_rpc_mod_cmds[ctx.mod].rpc_e_index+ctx.cmd];
886 	rpc_e->function(&func_param, &ctx);
887 
888 send_reply:
889 	if (!ctx.reply_sent) {
890 		ret = rpc_send(&ctx);
891 	}
892 	if (ret < 0) return -1;
893 	return 0;
894 }
895 
xhttp_rpc_dispatch(sip_msg_t * msg,char * s1,char * s2)896 static int xhttp_rpc_dispatch(sip_msg_t* msg, char* s1, char* s2)
897 {
898 	return ki_xhttp_rpc_dispatch(msg);
899 }
900 
901 /**
902  *
903  */
904 /* clang-format off */
905 static sr_kemi_t sr_kemi_xhttp_rpc_exports[] = {
906 	{ str_init("xhttp_rpc"), str_init("dispatch"),
907 		SR_KEMIP_INT, ki_xhttp_rpc_dispatch,
908 		{ SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
909 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
910 	},
911 
912 	{ {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
913 };
914 /* clang-format on */
915 
916 /**
917  *
918  */
mod_register(char * path,int * dlflags,void * p1,void * p2)919 int mod_register(char *path, int *dlflags, void *p1, void *p2)
920 {
921 	sr_kemi_modules_add(sr_kemi_xhttp_rpc_exports);
922 	return 0;
923 }
924 
925 /** @} */
926