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