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