--- chan_mgcp.c 2009-11-03 16:01:16.000000000 -0200 +++ chan_mgcp.c 2009-11-03 16:32:45.000000000 -0200 @@ -311,6 +311,14 @@ #define TYPE_TRUNK 1 #define TYPE_LINE 2 +struct mgcp_digitmap { + char *name; /*!< only for digitmaps defined under the [digitmaps] section and "overlap" special digitmap */ + char *digitmap; /*!< if NULL, then overlap mode is used */ + struct mgcp_digitmap *next; + +} *named_digitmaps = NULL, /*!< digitmaps capable of being referenced, first one will be always "overlap" */ + *default_digitmap = NULL; + struct mgcp_endpoint { ast_mutex_t lock; char name[80]; @@ -376,6 +384,7 @@ struct ast_variable *chanvars; /*!< Variables to set for channel created by user */ struct mgcp_endpoint *next; struct mgcp_gateway *parent; + struct mgcp_digitmap *digitmap; /*!< if NULL default_digitmap will be used */ }; static struct mgcp_gateway { @@ -447,6 +456,8 @@ static struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v); static int mgcp_alloc_pktcgate(struct mgcp_subchannel *sub); static int acf_channel_read(struct ast_channel *chan, const char *funcname, char *preparse, char *buf, size_t buflen); +static struct mgcp_digitmap *get_digitmap_from_variable(const struct ast_variable *var); +static void free_digitmap(struct mgcp_digitmap *dm); static struct ast_variable *add_var(const char *buf, struct ast_variable *list); static struct ast_variable *copy_vars(struct ast_variable *src); @@ -1118,6 +1129,38 @@ return CLI_SUCCESS; } +static char *handle_mgcp_show_digitmaps(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + struct mgcp_digitmap *dm; + char * ov = ""; + + switch (cmd) { + case CLI_INIT: + e->command = "mgcp show digitmaps"; + e->usage = + "Usage: mgcp show digitmaps\n" + " Lists all defined named Digitmaps.\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc != 3) + return CLI_SHOWUSAGE; + + ast_cli(a->fd, "MGCP Digitmaps (digitmaps defined in endpoints are not shown):\n\n"); + dm = named_digitmaps; + while(dm) { + ast_cli(a->fd, "%14s: %s\n", dm->name, dm->digitmap && dm->digitmap[0] != '\0' ? dm->digitmap : ov); + dm = dm->next; + } + + dm = default_digitmap; + ast_cli(a->fd, "%14s: %s\n\n", "", dm ? (dm->name ? dm->name : (dm->digitmap && dm->digitmap[0] != '\0' ? dm->digitmap : ov)) : ov); + + return CLI_SUCCESS; +} + static char *handle_mgcp_audit_endpoint(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct mgcp_gateway *mg; @@ -1215,6 +1258,7 @@ AST_CLI_DEFINE(handle_mgcp_audit_endpoint, "Audit specified MGCP endpoint"), AST_CLI_DEFINE(handle_mgcp_show_endpoints, "List defined MGCP endpoints"), AST_CLI_DEFINE(handle_mgcp_set_debug, "Enable/Disable MGCP debugging"), + AST_CLI_DEFINE(handle_mgcp_show_digitmaps, "List defined named and default Digitmaps"), AST_CLI_DEFINE(mgcp_reload, "Reload MGCP configuration"), }; @@ -2637,7 +2681,8 @@ { struct mgcp_endpoint *p = sub->parent; char tone_indicate_end = 0; - + struct mgcp_digitmap *dm; + /* We also should check the tone to indicate, because it have no sense to request notify D/[0-9#*] (dtmf keys) if we are sending congestion tone for example G/cg */ @@ -2651,7 +2696,14 @@ add_header(resp, "R", "L/hu(N),L/hf(N)"); } else if (!tone_indicate_end){ - add_header(resp, "R", (p->ncs ? "L/hu(N),L/hf(N),L/[0-9#*](N)" : "L/hu(N),L/hf(N),D/[0-9#*](N)")); + dm = sub->parent->digitmap ? sub->parent->digitmap : default_digitmap; + if(dm && dm->digitmap && dm->digitmap[0] != '\0') { + add_header(resp, "D", dm->digitmap); + add_header(resp, "R", "L/hu(N),L/hf(N),L/[0-9#*](D)"); + } else { + /* overlap mode (no digit collection at the endpoint) */ + add_header(resp, "R", "L/hu(N),L/hf(N),L/[0-9#*](N)"); + } } else { if (mgcpdebug) { ast_verbose("We don't want more digits if we will end the call\n"); @@ -2669,7 +2721,10 @@ reqprep(&resp, p, "AUEP"); /* removed unknown param VS */ /*add_header(&resp, "F", "A,R,D,S,X,N,I,T,O,ES,E,MD,M");*/ - add_header(&resp, "F", "A"); + if(p->ncs) + add_header(&resp, "F", "A,R,D,S,X,N,I,T,O,ES,VS,E,MD"); + else + add_header(&resp, "F", "A"); /* fill in new fields */ resp.cmd = MGCP_CMD_AUEP; resp.trid = oseq; @@ -3378,7 +3433,7 @@ struct ast_frame f = { 0, }; struct mgcp_endpoint *p = sub->parent; struct mgcp_gateway *g = NULL; - int res; + int res, i, j; if (mgcpdebug) { ast_verbose("Handling request '%s' on %s@%s\n", req->verb, p->name, p->parent->name); @@ -3586,11 +3641,13 @@ transmit_notify_request(sub, "L/vmwi(-)"); } } - } else if ((strlen(ev) == 1) && + } else if ((strlen(ev) >= 1) && (((ev[0] >= '0') && (ev[0] <= '9')) || ((ev[0] >= 'A') && (ev[0] <= 'D')) || (ev[0] == '*') || (ev[0] == '#'))) { if (sub && sub->owner && (sub->owner->_state >= AST_STATE_UP)) { + /* In this section we will only recive single digits in overlap mode, because endpoint + digit collection according a digitmap will not be used when the call is up */ f.frametype = AST_FRAME_DTMF; f.subclass = ev[0]; f.src = "mgcp"; @@ -3604,8 +3661,12 @@ memset(p->curtone, 0, sizeof(p->curtone)); } } else { - p->dtmf_buf[strlen(p->dtmf_buf)] = ev[0]; - p->dtmf_buf[strlen(p->dtmf_buf)] = '\0'; + for(i=0, j = strlen(p->dtmf_buf); ev[i] != '\0' && j < sizeof(p->dtmf_buf); ++i) + if(ev[i] != ',') { + p->dtmf_buf[j] = ev[i]; + ++j; + } + p->dtmf_buf[j] = '\0'; } } else if (!strcasecmp(ev, "T")) { /* Digit timeout -- unimportant */ @@ -3799,6 +3860,8 @@ ast_mutex_destroy(&e->lock); ast_mutex_destroy(&e->rqnt_queue_lock); ast_mutex_destroy(&e->cmd_queue_lock); + if(e->digitmap && !e->digitmap->name) + free_digitmap(e->digitmap); /* free anonymous digitmap */ free(e); } e = enext; @@ -4014,6 +4077,7 @@ struct mgcp_endpoint *e; struct mgcp_subchannel *sub; struct ast_variable *chanvars = NULL; + struct ast_variable *cur_digitmap = NULL; /*char txident[80];*/ int i=0, y=0; @@ -4164,6 +4228,8 @@ transfer = ast_true(v->value); } else if (!strcasecmp(v->name, "threewaycalling")) { threewaycalling = ast_true(v->value); + } else if( !strcasecmp(v->name, "digitmap")) { + cur_digitmap = v; } else if (!strcasecmp(v->name, "wcardep")) { /* locate existing endpoint */ e = gw->endpoints; @@ -4237,6 +4303,12 @@ e->slowsequence = slowsequence; e->transfer = transfer; e->threewaycalling = threewaycalling; + + if(ep_reload && e->digitmap && !e->digitmap->name) + free_digitmap(e->digitmap); /* free anonymous digitmap */ + + e->digitmap = get_digitmap_from_variable(cur_digitmap); + e->onhooktime = time(NULL); /* ASSUME we're onhook */ e->hookstate = MGCP_ONHOOK; @@ -4348,6 +4420,10 @@ e->transfer = transfer; e->threewaycalling = threewaycalling; + if(ep_reload && e->digitmap && !e->digitmap->name) + free_digitmap(e->digitmap); /* free anonymous digitmap */ + e->digitmap = get_digitmap_from_variable(cur_digitmap); + /* If we already have a valid chanvars, it's not a new endpoint (it's a reload), so first, free previous mem */ @@ -4553,6 +4629,9 @@ if (e->mwi_event_sub) ast_event_unsubscribe(e->mwi_event_sub); + if (e->digitmap && !e->digitmap->name) + free_digitmap(e->digitmap); /* free anonymous digitmap */ + if (e->chanvars) { ast_variables_destroy(e->chanvars); e->chanvars = NULL; @@ -4617,6 +4696,8 @@ ast_mutex_unlock(&gatelock); } + + static struct ast_variable *add_var(const char *buf, struct ast_variable *list) { struct ast_variable *tmpvar = NULL; @@ -4648,6 +4729,59 @@ return res; } +void static free_digitmap(struct mgcp_digitmap *dm) { + if(dm->name) + ast_free(dm->name); + if(dm->digitmap) + ast_free(dm->digitmap); + ast_free(dm); +} + + +static struct mgcp_digitmap *get_digitmap_from_variable(const struct ast_variable *var) { + struct mgcp_digitmap *dm; + int len; + + if(var == NULL) + return NULL; + + if(var->value == NULL || var->value[0] == '\0') { + /* No digitmap == overlap digitmap */ + return named_digitmaps; /* overlap digitmap is always first */ + } + + /* Named digitmaps are referenced using the "object" declaration form + at configuration files ('=>' usage), or by prefixing the name with + an '@'. '@' is needed because realtime modules doesn't support + declaring a variable as an "object". + */ + if(var->object || var->value[0] == '@') { + dm = named_digitmaps; + + while(dm) { + if (!strcmp(dm->name, &var->value[var->value[0] == '@'])) + return dm; + + dm = dm->next; + } + // Not found + ast_log(LOG_WARNING, "Unexistant digitmap '%s' referenced, using overlap\n", &var->value[var->value[0] == '@']); + + return named_digitmaps; + } + + /* Must construct Anonymous Digitmap */ + dm = ast_malloc(sizeof(struct mgcp_digitmap)); + dm->name = NULL; + + len = strlen(var->value)+1; + dm->digitmap = ast_malloc(len); + ast_copy_string(dm->digitmap, var->value, len); + + dm->next=NULL; + + return dm; +} static int reload_config(int reload) { @@ -4660,6 +4794,8 @@ struct hostent *hp; int format; struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; + struct mgcp_digitmap *dm, *dm_prev, *added_or_modified_dm_list = NULL; + int tmp; if (gethostname(ourhost, sizeof(ourhost)-1)) { ast_log(LOG_WARNING, "Unable to get hostname, MGCP disabled\n"); @@ -4684,6 +4820,112 @@ /* Copy the default jb config over global_jbconf */ memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf)); + + if(!reload) { + /* Create the default digitmap (== overlap mode) */ + dm = ast_malloc(sizeof(struct mgcp_digitmap)); + dm->name = strcpy(ast_malloc(7+1), "overlap"); + dm->digitmap = NULL; + dm->next = NULL; + added_or_modified_dm_list = dm; + + default_digitmap = dm; + } else { + /* Overlap digitmap is always preserved among reloads, move it from old to new list */ + added_or_modified_dm_list = named_digitmaps; + named_digitmaps=named_digitmaps->next; + added_or_modified_dm_list->next = NULL; + } + + /* (re)load named digitmaps */ + v = ast_variable_browse(cfg, "digitmaps"); + while(v) { + if(v->object || v->value[0] == '@') { + ast_log(LOG_WARNING, "Named digitmap '%s' references another named digitmap, ignoring definition\n", v->name); + continue; + } + + if(reload) { + /* Search for modified digitmaps */ + dm_prev = NULL; + dm = named_digitmaps; + while(dm) { + if (!strcmp(dm->name, v->name)) { + /* Update existing dm to preserve references from endpoints/default dm */ + if(dm->digitmap) + ast_free(dm->digitmap); + + tmp = strlen(v->value)+1; + dm->digitmap = ast_malloc(tmp); + ast_copy_string(dm->digitmap, v->value, tmp); + + /* Move from the old to the new list */ + if(dm_prev) + dm_prev->next = dm->next; + else + named_digitmaps = dm->next; + + /* Insert after first element (overlap dm) */ + dm->next = added_or_modified_dm_list->next; + added_or_modified_dm_list->next = dm; + + goto next_named_digitmap_definition; + } + dm_prev = dm; + dm = dm->next; + } + } + + /* Freshly new digitmap */ + dm = ast_malloc(sizeof(struct mgcp_digitmap)); + tmp = strlen(v->name)+1; + dm->name = ast_malloc(tmp); + ast_copy_string(dm->name, v->name, tmp); + + tmp = strlen(v->value)+1; + dm->digitmap = ast_malloc(tmp); + ast_copy_string(dm->digitmap, v->value, tmp); + + /* Insert after first element (overlap dm) */ + dm->next = added_or_modified_dm_list->next; + added_or_modified_dm_list->next = dm; + + next_named_digitmap_definition: + v = v->next; + } + + if(reload) { + /* Break references to deleted named digitmaps and free them */ + dm = named_digitmaps; /* old list with deleted entries */ + + ast_mutex_lock(&gatelock); + while(dm) { + /* Break the reference from default_digitmap if named digitmap was deleted */ + if(default_digitmap == dm) + default_digitmap = NULL; + + /* Break the endpoint references to the deleted named digitmap */ + g = gateways; + while (g) { + e = g->endpoints; + while (e) { + if(e->digitmap == dm) + e->digitmap = NULL; + e = e->next; + } + g = g->next; + } + + /* Now is safe to delete it */ + dm_prev = dm; + dm = dm->next; + free_digitmap(dm_prev); + } + ast_mutex_unlock(&gatelock); + } + + named_digitmaps = added_or_modified_dm_list; + v = ast_variable_browse(cfg, "general"); while (v) { /* handle jb conf */ @@ -4735,7 +4977,15 @@ gendigittimeout = atoi(v->value); } else if (!strcasecmp(v->name, "matchdigittimeout")) { matchdigittimeout = atoi(v->value); + } else if (!strcasecmp(v->name, "default_digitmap")) { + /* Default digitmap for all the lines without a + digitmap defined or referenced */ + if(default_digitmap && !default_digitmap->name) + free_digitmap(default_digitmap); + + default_digitmap = get_digitmap_from_variable(v); } + v = v->next; } @@ -4755,7 +5005,7 @@ cat = ast_category_browse(cfg, NULL); while(cat) { - if (strcasecmp(cat, "general")) { + if (strcasecmp(cat, "general") && strcasecmp(cat, "digitmaps")) { ast_mutex_lock(&gatelock); g = build_gateway(cat, ast_variable_browse(cfg, cat)); if (g) { @@ -4910,6 +5160,7 @@ { struct mgcp_endpoint *e; struct mgcp_gateway *g; + struct mgcp_digitmap *dm, *dm2; /* Check to see if we're reloading */ if (ast_mutex_trylock(&mgcp_reload_lock)) { @@ -4942,6 +5193,9 @@ } if (!ast_mutex_lock(&gatelock)) { + if(default_digitmap && !default_digitmap->name) + free_digitmap(default_digitmap); + for (g = gateways; g; g = g->next) { g->delme = 1; for (e = g->endpoints; e; e = e->next) @@ -4949,6 +5203,16 @@ } prune_gateways(); + + /* Now is safe to free all the named digitmaps (the only ones + that can have a refcount > 1) */ + dm = named_digitmaps; + while(dm) { + dm2 = dm->next; + free_digitmap(dm); + dm = dm2; + } + ast_mutex_unlock(&gatelock); } else { ast_log(LOG_WARNING, "Unable to lock the gateways list.\n");