--- chan_mgcp.c 2009-12-11 19:17:54.000000000 -0300 +++ chan_mgcp.c 2009-12-14 15:56:04.000000000 -0300 @@ -313,6 +313,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]; @@ -378,6 +386,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 { @@ -449,6 +458,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); @@ -1060,6 +1071,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; @@ -1153,6 +1196,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"), }; @@ -2546,6 +2590,7 @@ { 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 @@ -2560,7 +2605,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 { ast_debug(1, "We don't want more digits if we will end the call\n"); add_header(resp, "R", "L/hu(N),L/hf(N)"); @@ -2576,7 +2628,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; @@ -3279,7 +3334,7 @@ struct ast_frame f = { 0, }; struct mgcp_endpoint *p = sub->parent; struct mgcp_gateway *g = NULL; - int res; + int res, i, j; ast_debug(1, "Handling request '%s' on %s@%s\n", req->verb, p->name, p->parent->name); /* Clear out potential response */ @@ -3483,11 +3538,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.integer = ev[0]; f.src = "mgcp"; @@ -3501,8 +3558,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 */ @@ -3688,6 +3749,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); } } @@ -3905,6 +3968,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; @@ -4052,7 +4116,10 @@ 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 */ for (e = gw->endpoints; e; e = e->next) { if (!strcasecmp(v->value, e->name)) { @@ -4126,6 +4193,12 @@ e->transfer = transfer; e->threewaycalling = threewaycalling; e->onhooktime = time(NULL); + + if(ep_reload && e->digitmap && !e->digitmap->name) + free_digitmap(e->digitmap); /* free anonymous digitmap */ + + e->digitmap = get_digitmap_from_variable(cur_digitmap); + /* ASSUME we're onhook */ e->hookstate = MGCP_ONHOOK; e->chanvars = copy_vars(chanvars); @@ -4227,6 +4300,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 */ @@ -4429,6 +4506,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; @@ -4493,6 +4573,8 @@ ast_mutex_unlock(&gatelock); } + + static struct ast_variable *add_var(const char *buf, struct ast_variable *list) { struct ast_variable *tmpvar = NULL; @@ -4524,6 +4606,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) { @@ -4536,6 +4671,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"); @@ -4560,6 +4697,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; + for (v = ast_variable_browse(cfg, "general"); v; v = v->next) { /* handle jb conf */ if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) { @@ -4612,7 +4855,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); } + } /* mark existing entries for deletion */ @@ -4626,7 +4877,7 @@ ast_mutex_unlock(&gatelock); for (cat = ast_category_browse(cfg, NULL); cat; cat = ast_category_browse(cfg, cat)) { - if (strcasecmp(cat, "general")) { + if (strcasecmp(cat, "general") && strcasecmp(cat, "digitmaps")) { ast_mutex_lock(&gatelock); if ((g = build_gateway(cat, ast_variable_browse(cfg, cat)))) { ast_verb(3, "Added gateway '%s'\n", g->name); @@ -4776,6 +5027,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)) { @@ -4808,6 +5060,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) { @@ -4816,6 +5071,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");