dialplan: exported dp_match() and dp_replace() to kemi framework
[sip-router] / src / modules / dialplan / dialplan.c
1 /*
2  * Copyright (C)  2007-2008 Voice Sistem SRL
3  *
4  * Copyright (C)  2008 Juha Heinanen
5  *
6  * Copyright (C)  2014 Olle E. Johansson, Edvina AB
7  *
8  * This file is part of Kamailio, a free SIP server.
9  *
10  * Kamailio is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version
14  *
15  * Kamailio is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
23  *
24  */
25
26 /*!
27  * \file
28  * \brief Kamailio dialplan :: Module interface
29  * \ingroup dialplan
30  * Module: \ref dialplan
31  */
32
33 /*! \defgroup dialplan Kamailio dialplan transformations module
34  *
35  */
36
37
38
39
40 #include <string.h>
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <math.h>
44 #include "../../core/sr_module.h"
45 #include "../../lib/srdb1/db.h"
46 #include "../../core/dprint.h"
47 #include "../../core/error.h"
48 #include "../../core/ut.h"
49 #include "../../core/action.h"
50 #include "../../core/pvar.h"
51 #include "../../core/dset.h"
52 #include "../../core/mod_fix.h"
53 #include "../../core/mem/mem.h"
54 #include "../../core/parser/parse_to.h"
55 #include "../../core/rpc.h"
56 #include "../../core/rpc_lookup.h"
57 #include "../../core/lvalue.h"
58 #include "../../core/kemi.h"
59 #include "dialplan.h"
60 #include "dp_db.h"
61
62 MODULE_VERSION
63
64 #define DEFAULT_PARAM    "$rU"
65
66 static int mod_init(void);
67 static int child_init(int rank);
68 static void mod_destroy();
69
70 static int dialplan_init_rpc(void);
71
72 static int dp_translate_f(struct sip_msg* msg, char* str1, char* str2);
73 static int dp_trans_fixup(void ** param, int param_no);
74 static int dp_reload_f(struct sip_msg* msg);
75 static int w_dp_replace(sip_msg_t* msg, char* pid, char* psrc, char* pdst);
76 static int w_dp_match(sip_msg_t* msg, char* pid, char* psrc);
77
78 int dp_replace_fixup(void** param, int param_no);
79 int dp_replace_fixup_free(void** param, int param_no);
80
81 str attr_pvar_s = STR_NULL;
82 pv_spec_t *attr_pvar = NULL;
83
84 str default_param_s = str_init(DEFAULT_PARAM);
85 dp_param_p default_par2 = NULL;
86
87 int dp_fetch_rows = 1000;
88 int dp_match_dynamic = 0;
89 int dp_append_branch = 1;
90
91 static param_export_t mod_params[]={
92         { "db_url",                     PARAM_STR,      &dp_db_url },
93         { "table_name",         PARAM_STR,      &dp_table_name },
94         { "dpid_col",           PARAM_STR,      &dpid_column },
95         { "pr_col",                     PARAM_STR,      &pr_column },
96         { "match_op_col",       PARAM_STR,      &match_op_column },
97         { "match_exp_col",      PARAM_STR,      &match_exp_column },
98         { "match_len_col",      PARAM_STR,      &match_len_column },
99         { "subst_exp_col",      PARAM_STR,      &subst_exp_column },
100         { "repl_exp_col",       PARAM_STR,      &repl_exp_column },
101         { "attrs_col",          PARAM_STR,      &attrs_column },
102         { "attrs_pvar",     PARAM_STR,  &attr_pvar_s },
103         { "fetch_rows",         PARAM_INT,      &dp_fetch_rows },
104         { "match_dynamic",      PARAM_INT,      &dp_match_dynamic },
105         { "append_branch",      PARAM_INT,      &dp_append_branch },
106         {0,0,0}
107 };
108
109 static cmd_export_t cmds[]={
110         {"dp_translate",(cmd_function)dp_translate_f,   2,      dp_trans_fixup,  0,
111                 ANY_ROUTE},
112         {"dp_translate",(cmd_function)dp_translate_f,   1,      dp_trans_fixup,  0,
113                 ANY_ROUTE},
114         {"dp_reload",(cmd_function)dp_reload_f, 0, 0,  0,
115                 ANY_ROUTE},
116         {"dp_match",(cmd_function)w_dp_match,   2,      fixup_igp_spve,
117                 fixup_free_igp_spve, ANY_ROUTE},
118         {"dp_replace",(cmd_function)w_dp_replace,       2,      dp_replace_fixup,
119                 dp_replace_fixup_free, ANY_ROUTE},
120         {0,0,0,0,0,0}
121 };
122
123 struct module_exports exports= {
124         "dialplan",     /* module's name */
125         DEFAULT_DLFLAGS, /* dlopen flags */
126         cmds,               /* exported functions */
127         mod_params,     /* param exports */
128         0,                              /* exported statistics */
129         0,                              /* exported MI functions */
130         0,                              /* exported pseudo-variables */
131         0,                              /* additional processes */
132         mod_init,               /* module initialization function */
133         0,                              /* reply processing function */
134         mod_destroy,
135         child_init              /* per-child init function */
136 };
137
138
139 static int mod_init(void)
140 {
141         if(dialplan_init_rpc()!=0)
142         {
143                 LM_ERR("failed to register RPC commands\n");
144                 return -1;
145         }
146
147         LM_DBG("db_url=%s/%d/%p\n", ZSW(dp_db_url.s), dp_db_url.len,dp_db_url.s);
148
149         if(attr_pvar_s.s && attr_pvar_s.len>0) {
150                 attr_pvar = pv_cache_get(&attr_pvar_s);
151                 if( (attr_pvar==NULL) ||
152                                 ((attr_pvar->type != PVT_AVP) &&
153                                  (attr_pvar->type != PVT_XAVP) &&
154                                  (attr_pvar->type!=PVT_SCRIPTVAR))) {
155                         LM_ERR("invalid pvar name\n");
156                         return -1;
157                 }
158         }
159
160         default_par2 = (dp_param_p)shm_malloc(sizeof(dp_param_t));
161         if(default_par2 == NULL){
162                 LM_ERR("no shm more memory\n");
163                 return -1;
164         }
165         memset(default_par2, 0, sizeof(dp_param_t));
166
167         /* emulate "$rU/$rU" as second parameter for dp_translate() */
168         default_param_s.len = strlen(default_param_s.s);
169         default_par2->v.sp[0] = pv_cache_get(&default_param_s);
170         if (default_par2->v.sp[0]==NULL) {
171                 LM_ERR("input pv is invalid\n");
172                 return -1;
173         }
174
175         default_param_s.len = strlen(default_param_s.s);
176         default_par2->v.sp[1] = pv_cache_get(&default_param_s);
177         if (default_par2->v.sp[1]==NULL) {
178                 LM_ERR("output pv is invalid\n");
179                 return -1;
180         }
181
182         if(dp_fetch_rows<=0)
183                 dp_fetch_rows = 1000;
184
185         if(init_data() != 0) {
186                 LM_ERR("could not initialize data\n");
187                 return -1;
188         }
189
190         return 0;
191 }
192
193
194 static int child_init(int rank)
195 {
196         return 0;
197 }
198
199
200 static void mod_destroy(void)
201 {
202         /*destroy shared memory*/
203         if(default_par2){
204                 shm_free(default_par2);
205                 default_par2 = NULL;
206         }
207         destroy_data();
208 }
209
210
211 static int dp_get_ivalue(struct sip_msg* msg, dp_param_p dp, int *val)
212 {
213         pv_value_t value;
214
215         if(dp->type==DP_VAL_INT) {
216                 *val = dp->v.id;
217                 LM_DBG("dpid is %d from constant argument\n", *val);
218                 return 0;
219         }
220
221         LM_DBG("searching %d\n",dp->v.sp[0]->type);
222
223         if( pv_get_spec_value( msg, dp->v.sp[0], &value)!=0
224                         || value.flags&(PV_VAL_NULL|PV_VAL_EMPTY) || !(value.flags&PV_VAL_INT)) {
225                 LM_ERR("no AVP, XAVP or SCRIPTVAR found (error in scripts)\n");
226                 return -1;
227         }
228         *val = value.ri;
229         LM_DBG("dpid is %d from pv argument\n", *val);
230         return 0;
231 }
232
233
234 static int dp_get_svalue(struct sip_msg * msg, pv_spec_t *spec, str* val)
235 {
236         pv_value_t value;
237
238         LM_DBG("searching %d \n", spec->type);
239
240         if ( pv_get_spec_value(msg,spec,&value)!=0 || value.flags&PV_VAL_NULL
241                         || value.flags&PV_VAL_EMPTY || !(value.flags&PV_VAL_STR)){
242                 LM_ERR("no AVP, XAVP or SCRIPTVAR found (error in scripts)\n");
243                 return -1;
244         }
245
246         *val = value.rs;
247         return 0;
248 }
249
250
251 static int dp_update(struct sip_msg * msg, pv_spec_t * dest,
252                 str * repl, str * attrs)
253 {
254         int no_change;
255         pv_value_t val;
256
257         memset(&val, 0, sizeof(pv_value_t));
258         val.flags = PV_VAL_STR;
259
260         no_change = (dest==NULL) || (dest->type == PVT_NONE)
261                                                 || (!repl->s) || (!repl->len);
262
263         if (no_change)
264                 goto set_attr_pvar;
265
266         val.rs = *repl;
267
268         if(dest->setf) {
269                 if(dest->setf(msg, &dest->pvp, (int)EQ_T, &val)<0) {
270                         LM_ERR("setting dst pseudo-variable failed\n");
271                         return -1;
272                 }
273         } else {
274                 LM_WARN("target variable is read only - skipping setting its value\n");
275         }
276
277         if(dp_append_branch!=0) {
278                 if(is_route_type(FAILURE_ROUTE)
279                                 && (dest->type == PVT_RURI
280                                                 || dest->type == PVT_RURI_USERNAME)) {
281                         if(append_branch(msg, 0, 0, 0, Q_UNSPECIFIED, 0, 0, 0, 0, 0, 0)
282                                         != 1) {
283                                 LM_ERR("append branch action failed\n");
284                                 return -1;
285                         }
286                 }
287         }
288
289 set_attr_pvar:
290
291         if(attr_pvar==NULL || attrs==NULL)
292                 return 0;
293
294         val.rs = *attrs;
295         if(attr_pvar->setf(msg, &attr_pvar->pvp, (int)EQ_T, &val)<0)
296         {
297                 LM_ERR("setting attr pseudo-variable failed\n");
298                 return -1;
299         }
300
301         return 0;
302 }
303
304
305 static int dp_translate_f(struct sip_msg* msg, char* str1, char* str2)
306 {
307         int dpid;
308         str input, output;
309         dpl_id_p idp;
310         dp_param_p id_par, repl_par;
311         str attrs, *outattrs;
312
313         if(!msg)
314                 return -1;
315
316         /*verify first param's value*/
317         id_par = (dp_param_p) str1;
318         if (dp_get_ivalue(msg, id_par, &dpid) != 0){
319                 LM_ERR("no dpid value\n");
320                 return -1;
321         }
322
323         if ((idp = select_dpid(dpid)) ==0 ){
324                 LM_DBG("no information available for dpid %i\n", dpid);
325                 return -2;
326         }
327
328         repl_par = (str2!=NULL)? ((dp_param_p)str2):default_par2;
329         if (dp_get_svalue(msg, repl_par->v.sp[0], &input)!=0){
330                 LM_ERR("invalid param 2\n");
331                 return -1;
332         }
333
334         LM_DBG("input is %.*s\n", input.len, input.s);
335
336         outattrs = (!attr_pvar)?NULL:&attrs;
337         if (dp_translate_helper(msg, &input, &output, idp, outattrs)!=0) {
338                 LM_DBG("could not translate %.*s "
339                                 "with dpid %i\n", input.len, input.s, idp->dp_id);
340                 return -1;
341         }
342         LM_DBG("input %.*s with dpid %i => output %.*s\n",
343                         input.len, input.s, idp->dp_id, output.len, output.s);
344
345         /* set the output */
346         if (dp_update(msg, repl_par->v.sp[1], &output, outattrs) !=0){
347                 LM_ERR("cannot set the output\n");
348                 return -1;
349         }
350
351         return 1;
352
353 }
354
355 #define verify_par_type(_par_no, _spec, _ret) \
356         do{\
357                 if( ((_par_no == 1) \
358                                         && (_spec->type != PVT_AVP) && (_spec->type != PVT_XAVP) && \
359                                         (_spec->type!=PVT_SCRIPTVAR) )\
360                                 ||((_par_no == 2) \
361                                         && (_spec->type != PVT_AVP) && (_spec->type != PVT_XAVP) && \
362                                         (_spec->type!=PVT_SCRIPTVAR) \
363                                         && (_spec->type!=PVT_RURI) && (_spec->type!=PVT_RURI_USERNAME))){\
364                         \
365                         LM_ERR("Unsupported Parameter TYPE[%d]\n", _spec->type);\
366                         _ret = E_UNSPEC; \
367                         goto error; \
368                 }\
369         }while(0);
370
371
372 /* first param: DPID: type: INT, AVP, XAVP, SVAR
373  * second param: SRC type: any psedo variable type
374  * second param: DST type: RURI, RURI_USERNAME, AVP, XAVP, SVAR, N/A
375  * default value for the second param: $ru.user/$ru.user
376  */
377 static int dp_trans_fixup(void ** param, int param_no){
378
379         int dpid;
380         dp_param_p dp_par= NULL;
381         char *p, *s=NULL;
382         str lstr;
383         int ret = E_INVALID_PARAMS;
384
385         if(param_no!=1 && param_no!=2)
386                 return 0;
387
388         p = (char*)*param;
389         if(!p || (*p == '\0')){
390                 LM_DBG("null param %i\n", param_no);
391                 return E_CFG;
392         }
393
394         LM_DBG("param_no is %i\n", param_no);
395
396         dp_par = (dp_param_p)pkg_malloc(sizeof(dp_param_t));
397         if(dp_par == NULL){
398                 LM_ERR("no more pkg memory\n");
399                 return E_OUT_OF_MEM;
400         }
401         memset(dp_par, 0, sizeof(dp_param_t));
402
403         if(param_no == 1) {
404                 if(*p != '$') {
405                         dp_par->type = DP_VAL_INT;
406                         lstr.s = *param; lstr.len = strlen(*param);
407                         if(str2sint(&lstr, &dpid) != 0) {
408                                 LM_ERR("bad number <%s>\n",(char *)(*param));
409                                 ret = E_CFG;
410                                 goto error;
411                         }
412
413                         dp_par->type = DP_VAL_INT;
414                         dp_par->v.id = dpid;
415                 }else{
416                         lstr.s = p; lstr.len = strlen(p);
417                         dp_par->v.sp[0] = pv_cache_get(&lstr);
418                         if (dp_par->v.sp[0]==NULL) {
419                                 goto error;
420                         }
421
422                         verify_par_type(param_no, dp_par->v.sp[0], ret);
423                         dp_par->type = DP_VAL_SPEC;
424                 }
425         } else {
426
427                 if (((s = strchr(p, '/')) != 0) && (*(s+1)=='\0'))
428                         goto error;
429
430                 if (s != 0) {
431                         *s = '\0'; s++;
432                 }
433
434                 lstr.s = p; lstr.len = strlen(p);
435                 dp_par->v.sp[0] = pv_cache_get(&lstr);
436                 if(dp_par->v.sp[0]==NULL) {
437                         goto error;
438                 }
439
440                 if (s != 0) {
441                         lstr.s = s; lstr.len = strlen(s);
442                         dp_par->v.sp[1] = pv_cache_get(&lstr);
443                         if (dp_par->v.sp[1]==NULL) {
444                                 goto error;
445                         }
446                         verify_par_type(param_no, dp_par->v.sp[1], ret);
447                 }
448
449                 dp_par->type = DP_VAL_SPEC;
450
451         }
452
453         *param = (void *)dp_par;
454
455         return 0;
456
457 error:
458         LM_ERR("failed to parse param %i\n", param_no);
459         if(dp_par) pkg_free(dp_par);
460
461         return ret;
462 }
463
464 static int dp_replace_helper(sip_msg_t *msg, int dpid, str *input,
465                 pv_spec_t *pvd)
466 {
467         dpl_id_p idp;
468         str output = STR_NULL;
469         str attrs = STR_NULL;
470         str *outattrs = NULL;
471
472         if ((idp = select_dpid(dpid)) ==0) {
473                 LM_DBG("no information available for dpid %i\n", dpid);
474                 return -2;
475         }
476
477         outattrs = (!attr_pvar)?NULL:&attrs;
478         if (dp_translate_helper(msg, input, &output, idp, outattrs)!=0) {
479                 LM_DBG("could not translate %.*s "
480                                 "with dpid %i\n", input->len, input->s, idp->dp_id);
481                 return -1;
482         }
483         LM_DBG("input %.*s with dpid %i => output %.*s\n",
484                         input->len, input->s, idp->dp_id, output.len, output.s);
485
486         /* set the output */
487         if (dp_update(msg, pvd, &output, outattrs) !=0){
488                 LM_ERR("cannot set the output\n");
489                 return -1;
490         }
491
492         return 1;
493 }
494
495 static int w_dp_replace(sip_msg_t* msg, char* pid, char* psrc, char* pdst)
496 {
497         int dpid = 1;
498         str src = STR_NULL;
499         pv_spec_t *pvd = NULL;
500
501         if(fixup_get_ivalue(msg, (gparam_t*)pid, &dpid)<0) {
502                 LM_ERR("failed to get dialplan id value\n");
503                 return -1;
504         }
505         if(fixup_get_svalue(msg, (gparam_t*)psrc, &src)<0) {
506                 LM_ERR("failed to get src value\n");
507                 return -1;
508         }
509         pvd = (pv_spec_t*)pdst;
510
511         return dp_replace_helper(msg, dpid, &src, pvd);
512 }
513
514 static int ki_dp_replace(sip_msg_t* msg, int dpid, str* src, str* dst)
515 {
516         pv_spec_t *pvd = NULL;
517
518         pvd = pv_cache_get(dst);
519         if(pvd==NULL) {
520                 LM_ERR("cannot get pv spec for [%.*s]\n", dst->len, dst->s);
521                 return -1;
522         }
523
524         return dp_replace_helper(msg, dpid, src, pvd);
525 }
526
527 static int w_dp_match(sip_msg_t* msg, char* pid, char* psrc)
528 {
529         int dpid = 1;
530         str src = STR_NULL;
531
532         if(fixup_get_ivalue(msg, (gparam_t*)pid, &dpid)<0) {
533                 LM_ERR("failed to get dialplan id value\n");
534                 return -1;
535         }
536         if(fixup_get_svalue(msg, (gparam_t*)psrc, &src)<0) {
537                 LM_ERR("failed to get src value\n");
538                 return -1;
539         }
540
541         return dp_replace_helper(msg, dpid, &src, NULL);
542 }
543
544 static int ki_dp_match(sip_msg_t* msg, int dpid, str* src)
545 {
546         return dp_replace_helper(msg, dpid, src, NULL);
547 }
548
549 int dp_replace_fixup(void** param, int param_no)
550 {
551         if (param_no == 1)
552                 return fixup_igp_null(param, param_no);
553         else if (param_no == 2)
554                 return fixup_spve_all(param, param_no);
555         else if (param_no == 3)
556                 return fixup_pvar_all(param, param_no);
557         return E_UNSPEC;
558 }
559
560
561 int dp_replace_fixup_free(void** param, int param_no)
562 {
563         if (param_no == 1)
564                 return fixup_free_igp_null(param, param_no);
565         else if (param_no == 2)
566                 return fixup_free_spve_all(param, param_no);
567         else if (param_no == 3)
568                 return fixup_free_pvar_all(param, param_no);
569         return E_UNSPEC;
570 }
571
572 /**
573  * trigger reload of dialplan db records from config file
574  */
575 static int dp_reload_f(struct sip_msg* msg)
576 {
577         if (dp_connect_db() < 0) {
578                 LM_ERR("failed to reload rules fron database (db connect)\n");
579                 return -1;
580         }
581
582         if(dp_load_db() != 0){
583                 LM_ERR("failed to reload rules fron database (db load)\n");
584                 dp_disconnect_db();
585                 return -1;
586         }
587
588         dp_disconnect_db();
589
590         LM_DBG("reloaded dialplan\n");
591         return 1;
592 }
593
594
595 static const char* dialplan_rpc_reload_doc[2] = {
596         "Reload dialplan table from database",
597         0
598 };
599
600
601 /*
602  * RPC command to reload dialplan table
603  */
604 static void dialplan_rpc_reload(rpc_t* rpc, void* ctx)
605 {
606         if (dp_connect_db() < 0) {
607                 LM_ERR("failed to reload rules fron database (db connect)\n");
608                 rpc->fault(ctx, 500, "DB Connection Error");
609                 return;
610         }
611
612         if(dp_load_db() != 0){
613                 LM_ERR("failed to reload rules fron database (db load)\n");
614                 dp_disconnect_db();
615                 rpc->fault(ctx, 500, "Dialplan Reload Failed");
616                 return;
617         }
618
619         dp_disconnect_db();
620         return;
621 }
622
623
624
625 static const char* dialplan_rpc_translate_doc[2] = {
626         "Perform dialplan translation",
627         0
628 };
629
630
631 /*
632  * RPC command to perform dialplan translation
633  */
634 static void dialplan_rpc_translate(rpc_t* rpc, void* ctx)
635 {
636         dpl_id_p idp;
637         str input;
638         int dpid;
639         str attrs  = {"", 0};
640         str output = {0, 0};
641         void* th;
642
643         if (rpc->scan(ctx, "dS", &dpid, &input) < 2)
644         {
645                 rpc->fault(ctx, 500, "Invalid parameters");
646                 return;
647         }
648
649         if ((idp = select_dpid(dpid)) == 0 ){
650                 LM_ERR("no information available for dpid %i\n", dpid);
651                 rpc->fault(ctx, 500, "Dialplan ID not matched");
652                 return;
653         }
654
655         if(input.s == NULL || input.len== 0)    {
656                 LM_ERR("empty input parameter\n");
657                 rpc->fault(ctx, 500, "Empty input parameter");
658                 return;
659         }
660
661         LM_DBG("trying to translate %.*s with dpid %i\n",
662                         input.len, input.s, idp->dp_id);
663         if (dp_translate_helper(NULL, &input, &output, idp, &attrs)!=0){
664                 LM_DBG("could not translate %.*s with dpid %i\n",
665                                 input.len, input.s, idp->dp_id);
666                 rpc->fault(ctx, 500, "No translation");
667                 return;
668         }
669         LM_DBG("input %.*s with dpid %i => output %.*s\n",
670                         input.len, input.s, idp->dp_id, output.len, output.s);
671
672         if (rpc->add(ctx, "{", &th) < 0)
673         {
674                 rpc->fault(ctx, 500, "Internal error creating rpc");
675                 return;
676         }
677         if(rpc->struct_add(th, "SS",
678                                 "Output", &output,
679                                 "Attributes", &attrs)<0)
680         {
681                 rpc->fault(ctx, 500, "Internal error creating rpc");
682                 return;
683         }
684
685         return;
686 }
687
688 /*
689  * RPC command to dump dialplan
690  */
691 static void dialplan_rpc_dump(rpc_t* rpc, void* ctx)
692 {
693         dpl_id_p idp;
694         dpl_index_p indexp;
695         dpl_node_p rulep;
696         int dpid;
697         void* th;
698         void* ih;
699         void* sh;
700
701         if (rpc->scan(ctx, "d", &dpid) < 1)
702         {
703                 rpc->fault(ctx, 500, "Missing parameter");
704                 return;
705         }
706
707         if ((idp = select_dpid(dpid)) == 0 ) {
708                 LM_ERR("no information available for dpid %i\n", dpid);
709                 rpc->fault(ctx, 500, "Dialplan ID not matched");
710                 return;
711         }
712
713         LM_DBG("trying to dump dpid %i\n", idp->dp_id);
714
715         /* add entry node */
716         if (rpc->add(ctx, "{", &th) < 0)
717         {
718                 rpc->fault(ctx, 500, "Internal error root reply");
719                 return;
720         }
721
722         if(rpc->struct_add(th, "d[",
723                                 "DPID",  dpid,
724                                 "ENTRIES", &ih)<0)
725         {
726                 rpc->fault(ctx, 500, "Internal error sets structure");
727                 return;
728         }
729
730         for(indexp=idp->first_index; indexp!=NULL;indexp=indexp->next) {
731                 LM_DBG("INDEX LEN: %i\n", indexp->len);
732                 for(rulep = indexp->first_rule; rulep!= NULL;rulep = rulep->next) {
733                         LM_DBG("DPID: %i PRIO : %i\n", rulep->dpid, rulep->pr);
734                         if (rpc->struct_add(ih, "{","ENTRY", &sh) < 0)
735                         {
736                                 rpc->fault(ctx, 500, "Internal error root reply");
737                                 return;
738                         }
739
740                         if (rpc->struct_add(sh, "dd", "PRIO", rulep->pr,
741                                 "MATCHOP", rulep->matchop)<0)
742                         {
743                                 rpc->fault(ctx, 500, "Internal error adding prio");
744                                 return;
745                         }
746                         if (rpc->struct_add(sh, "s", "MATCHEXP", rulep->match_exp) < 0 )
747                         {
748                                 rpc->fault(ctx, 500, "Internal error adding match exp");
749                                 return;
750                         }
751                         if (rpc->struct_add(sh, "d", "MATCHLEN", rulep->matchlen) < 0 )
752                         {
753                                 rpc->fault(ctx, 500, "Internal error adding expression data and attribute");
754                                 return;
755                         }
756                         if (rpc->struct_add(sh, "s", "SUBSTEXP", rulep->subst_exp) < 0 )
757                         {
758                                 rpc->fault(ctx, 500, "Internal error adding subst exp");
759                                 return;
760                         }
761                         if (rpc->struct_add(sh, "s", "REPLEXP", rulep->repl_exp) < 0 )
762                         {
763                                 rpc->fault(ctx, 500, "Internal error adding replace exp ");
764                                 return;
765                         }
766                         if (rpc->struct_add(sh, "s", "ATTRS", rulep->attrs) < 0 )
767                         {
768                                 rpc->fault(ctx, 500, "Internal error adding attribute");
769                                 return;
770                         }
771                 }
772         }
773
774         return;
775 }
776
777 static const char* dialplan_rpc_dump_doc[2] = {
778         "Dump dialplan content",
779         0
780 };
781
782
783 rpc_export_t dialplan_rpc_list[] = {
784         {"dialplan.reload", dialplan_rpc_reload,
785                 dialplan_rpc_reload_doc, 0},
786         {"dialplan.translate",   dialplan_rpc_translate,
787                 dialplan_rpc_translate_doc, 0},
788         {"dialplan.dump",   dialplan_rpc_dump,
789                 dialplan_rpc_dump_doc, 0},
790         {0, 0, 0, 0}
791 };
792
793 static int dialplan_init_rpc(void)
794 {
795         if (rpc_register_array(dialplan_rpc_list)!=0)
796         {
797                 LM_ERR("failed to register RPC commands\n");
798                 return -1;
799         }
800         return 0;
801 }
802
803 /**
804  *
805  */
806 /* clang-format off */
807 static sr_kemi_t sr_kemi_dialplan_exports[] = {
808         { str_init("dialplan"), str_init("dp_match"),
809                 SR_KEMIP_INT, ki_dp_match,
810                 { SR_KEMIP_INT, SR_KEMIP_STR, SR_KEMIP_NONE,
811                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
812         },
813         { str_init("dialplan"), str_init("dp_replace"),
814                 SR_KEMIP_INT, ki_dp_replace,
815                 { SR_KEMIP_INT, SR_KEMIP_STR, SR_KEMIP_STR,
816                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
817         },
818
819         { {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
820 };
821 /* clang-format on */
822
823 /**
824  *
825  */
826 int mod_register(char *path, int *dlflags, void *p1, void *p2)
827 {
828         sr_kemi_modules_add(sr_kemi_dialplan_exports);
829         return 0;
830 }