--- chan_mgcp.c 2009-07-24 19:16:14.000000000 -0300 +++ chan_mgcp.c 2009-07-24 21:12:55.000000000 -0300 @@ -310,6 +310,16 @@ #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]; @@ -374,6 +384,7 @@ /* message go the the endpoint and not the channel so they stay here */ struct mgcp_endpoint *next; struct mgcp_gateway *parent; + struct mgcp_digitmap *digitmap; /*!< if NULL default_digitmap will be used */ }; static struct mgcp_gateway { @@ -446,7 +457,8 @@ 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 const struct ast_channel_tech mgcp_tech = { .type = "MGCP", @@ -1110,6 +1122,39 @@ 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; @@ -1207,6 +1252,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"), }; @@ -2619,7 +2665,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 */ @@ -2633,7 +2680,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"); @@ -2651,7 +2705,12 @@ 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; @@ -3360,7 +3419,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); @@ -3568,11 +3627,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] == '#'))) { + (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"; @@ -3586,8 +3647,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 */ @@ -3781,6 +3846,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; @@ -3995,6 +4062,8 @@ struct mgcp_gateway *gw; struct mgcp_endpoint *e; struct mgcp_subchannel *sub; + struct ast_variable *cur_digitmap = NULL; + /*char txident[80];*/ int i=0, y=0; int gw_reload = 0; @@ -4136,6 +4205,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; @@ -4209,6 +4280,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; @@ -4318,6 +4395,11 @@ 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); + if (!ep_reload) { e->onhooktime = time(NULL); /* ASSUME we're onhook */ @@ -4505,6 +4587,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 */ + ast_mutex_destroy(&e->lock); ast_mutex_destroy(&e->rqnt_queue_lock); ast_mutex_destroy(&e->cmd_queue_lock); @@ -4564,6 +4649,64 @@ ast_mutex_unlock(&gatelock); } + +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) { struct ast_config *cfg; @@ -4575,7 +4718,9 @@ 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"); return 0; @@ -4598,6 +4743,111 @@ /* 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) { @@ -4650,7 +4900,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; } @@ -4670,7 +4928,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) { @@ -4825,6 +5083,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)) { @@ -4857,6 +5116,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) @@ -4864,6 +5126,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");