--- chan_mgcp.c	2008-03-21 10:11:27.000000000 +0200
+++ chan_mgcp.c.patched	2008-03-28 16:17:58.000000000 +0200
@@ -162,6 +162,9 @@
 static int dtmfmode = 0;
 static int nat = 0;
 
+static int ncs = 0;
+static int hidecallerid = 0;
+
 static ast_group_t cur_callergroup = 0;
 static ast_group_t cur_pickupgroup = 0;
 
@@ -342,6 +345,7 @@
 	int hidecallerid;
 	int dtmfmode;
 	int amaflags;
+	int ncs;
 	int type;
 	int slowsequence;			/*!< MS: Sequence the endpoint as a whole */
 	int group;
@@ -396,7 +400,7 @@
 	int delme;                 /*!< needed for reload */
 	struct mgcp_response *responses;
 	struct mgcp_gateway *next;
-} *gateways;
+} *gateways = NULL, *gatetemplate = NULL;
 
 AST_MUTEX_DEFINE_STATIC(mgcp_reload_lock);
 static int mgcp_reloading = 0;
@@ -918,6 +922,7 @@
 {
 	struct mgcp_subchannel *sub = ast->tech_pvt;
 	struct mgcp_endpoint *p = sub->parent;
+	int unhidecallerid = 0;
 
 	if (option_debug) {
 		ast_log(LOG_DEBUG, "mgcp_hangup(%s)\n", ast->name);
@@ -991,7 +996,11 @@
 	ast_module_unref(ast_module_info->self);
 
 	if ((p->hookstate == MGCP_ONHOOK) && (!sub->next->rtp)) {
+
+		if (p->hidecallerid) unhidecallerid = 1;
+		    
 		p->hidecallerid = 0;
+
 		if (p->hascallwaiting && !p->callwaiting) {
 			if (option_verbose > 2)
 				ast_verbose(VERBOSE_PREFIX_3 "Enabling call waiting on %s\n", ast->name);
@@ -1010,6 +1019,8 @@
 			}
 			transmit_notify_request(sub, "L/vmwi(-)");
 		}
+
+		if (unhidecallerid) p->hidecallerid = 1;
 	}
 	ast_mutex_unlock(&sub->lock);
 	return 0;
@@ -1427,14 +1438,14 @@
 #ifdef DLINK_BUGGY_FIRMWARE	
 		transmit_notify_request(sub, "rt");
 #else
-		transmit_notify_request(sub, "G/rt");
+		transmit_notify_request(sub, sub->parent->ncs ? "L/rt" : "G/rt");
 #endif		
 		break;
 	case AST_CONTROL_BUSY:
 		transmit_notify_request(sub, "L/bz");
 		break;
 	case AST_CONTROL_CONGESTION:
-		transmit_notify_request(sub, "G/cg");
+		transmit_notify_request(sub, sub->parent->ncs ? "L/cg" : "G/cg");
 		break;
 	case AST_CONTROL_HOLD:
 		ast_moh_start(ast, data, NULL);
@@ -1564,7 +1575,7 @@
 	return "";
 }
 
-static char *__get_header(struct mgcp_request *req, char *name, int *start)
+static char *__get_header(struct mgcp_request *req, char *name, int *start, char *def)
 {
 	int x;
 	int len = strlen(name);
@@ -1580,13 +1591,19 @@
 		}
 	}
 	/* Don't return NULL, so get_header is always a valid pointer */
-	return "";
+	return def;
 }
 
 static char *get_header(struct mgcp_request *req, char *name)
 {
 	int start = 0;
-	return __get_header(req, name, &start);
+	return __get_header(req, name, &start, "");
+}
+
+static char *get_header_default(struct mgcp_request *req, char *name, char *def)
+{
+	int start = 0;
+	return __get_header(req, name, &start, def);
 }
 
 /*! \brief get_csv: (SC:) get comma separated value */
@@ -1611,7 +1628,50 @@
 	return s;
 }
 
-static struct mgcp_subchannel *find_subchannel_and_lock(char *name, int msgid, struct sockaddr_in *sin)
+static struct mgcp_gateway *clone_gateway(struct mgcp_gateway *gw, char *at)
+{
+    struct mgcp_gateway *gw_new;
+    struct mgcp_endpoint *e, *e_new;
+    struct mgcp_subchannel *sub, *sub_new;
+
+    if ((gw_new = malloc(sizeof(struct mgcp_gateway)))) {
+	memcpy(gw_new, gw, sizeof (struct mgcp_gateway));
+        strncpy(gw_new->name, at, 79);
+        gw_new->endpoints = NULL;
+	gw_new->dynamic = 1;
+	for (e = gw->endpoints; e; e = e->next) {
+	    if ((e_new = malloc(sizeof(struct mgcp_endpoint)))) {
+		if (mgcpdebug)
+		    ast_verbose("Cloning endpoint %s@%s\n", e_new->name, gw_new->name);
+		    e_new->parent = gw_new;
+		    e_new->sub = NULL;
+		    e_new->next = gw_new->endpoints;
+		    gw_new->endpoints = e_new;
+		    for (sub = e->sub; sub; sub = sub->next) {
+			if ((sub_new = malloc(sizeof(struct mgcp_subchannel)))) {
+			    memcpy(sub_new, sub, sizeof(struct mgcp_subchannel));
+			    if (mgcpdebug)
+				ast_verbose("Cloning subchannel %d of endpoint %s@%s\n", sub_new->id, e_new->name, gw_new->name);
+			    sub_new->parent = e_new;
+			    sub_new->next = e_new->sub;
+			    e_new->sub = sub_new;
+			    }
+                            //fix circular reference
+			    if (sub->next == e->sub) {
+				while(sub_new->next){
+				    sub_new = sub_new->next;
+				}
+				sub_new->next = e_new->sub;
+				break;
+                           }
+                       }
+                   }
+           }
+   }
+   return gw_new;
+}
+
+static struct mgcp_subchannel *find_subchannel_and_lock(char *name, int msgid, struct sockaddr_in *sin, struct mgcp_gateway *gt)
 {
 	struct mgcp_endpoint *p = NULL;
 	struct mgcp_subchannel *sub = NULL;
@@ -1709,7 +1769,7 @@
 				p = p->next;
 			}
 			if (sub && found) {
-				ast_mutex_lock(&sub->lock);
+				 ast_mutex_lock(&sub->lock);
 				break;
 			}
 		}
@@ -1718,10 +1778,20 @@
 	ast_mutex_unlock(&gatelock);
 	if (!sub) {
 		if (name) {
-			if (g)
-				ast_log(LOG_NOTICE, "Endpoint '%s' not found on gateway '%s'\n", tmp, at);
-			else
-				ast_log(LOG_NOTICE, "Gateway '%s' (and thus its endpoint '%s') does not exist\n", at, tmp);
+			if (g) {
+			    ast_log(LOG_NOTICE, "Endpoint '%s' not found on gateway '%s'\n", tmp, at);
+			 } else {
+				if (gt && (g = clone_gateway(gt, at))) {
+				    ast_mutex_lock(&gatelock);
+				    g->next = gateways;
+				    gateways = g;
+				    ast_mutex_unlock(&gatelock);
+				    ast_log(LOG_NOTICE, "Gateway '%s' dynamically allocated\n", at);
+				    return find_subchannel_and_lock(name, msgid, sin, NULL);
+				    } else {
+					ast_log(LOG_NOTICE, "Gateway '%s' (and thus its endpoint '%s') does not exist\n", at, tmp);
+				    }
+			    }
 		} 
 	}
 	return sub;
@@ -1986,9 +2056,11 @@
 	req->header[req->headers] = req->data + req->len;
 	/* check if we need brackets around the gw name */
 	if (p->parent->isnamedottedip)
-		snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s %d %s@[%s] MGCP 1.0\r\n", verb, oseq, p->name, p->parent->name);
+//		snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s %d %s@[%s] MGCP 1.0\r\n", verb, oseq, p->name, p->parent->name);
+		snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s %d %s@[%s] MGCP 1.0%s\r\n", verb, oseq, p->name, p->parent->name, p->ncs ? " NCS 1.0" : "");
 	else
-		snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s %d %s@%s MGCP 1.0\r\n", verb, oseq, p->name, p->parent->name);
+//		snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s %d %s@%s MGCP 1.0\r\n", verb, oseq, p->name, p->parent->name);
+		snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s %d %s@%s MGCP 1.0%s\r\n", verb, oseq, p->name, p->parent->name, p->ncs ? " NCS 1.0" : "");
 	req->len += strlen(req->header[req->headers]);
 	if (req->headers < MGCP_MAX_HEADERS)
 		req->headers++;
@@ -2149,7 +2221,7 @@
 		ast_rtp_get_peer(rtp, &sub->tmpdest);
 		return 0;
 	}
-	snprintf(local, sizeof(local), "p:20");
+	snprintf(local, sizeof(local), "p:20, s:off, e:on");
 	for (x=1;x<= AST_FORMAT_MAX_AUDIO; x <<= 1) {
 		if (p->capability & x) {
 			snprintf(tmp, sizeof(tmp), ", a:%s", ast_rtp_lookup_mime_subtype(1, x, 0));
@@ -2179,7 +2251,7 @@
 	int x;
 	struct mgcp_endpoint *p = sub->parent;
 
-	snprintf(local, sizeof(local), "p:20");
+	snprintf(local, sizeof(local), "p:20, s:off, e:on");
 	for (x=1;x<= AST_FORMAT_MAX_AUDIO; x <<= 1) {
 		if (p->capability & x) {
 			snprintf(tmp, sizeof(tmp), ", a:%s", ast_rtp_lookup_mime_subtype(1, x, 0));
@@ -2221,7 +2293,8 @@
 		add_header(&resp, "R", "L/hd(N)");
 		break;
 	case MGCP_OFFHOOK:
-		add_header_offhook(sub, &resp);
+		//add_header_offhook(sub, &resp);
+		add_header(&resp, "R", (sub->rtp && (p->dtmfmode & MGCP_DTMF_INBAND)) ? "L/hu(N),L/hf(N)" : (p->ncs ? "L/hu(N), L/hf(N), L/[0-9#*](N)" : "L/hu(N),L/hf(N),D/[0-9#*](N)"));
 		break;
 	}
 	if (!ast_strlen_zero(tone)) {
@@ -2264,7 +2337,8 @@
 		add_header(&resp, "R", "L/hd(N)");
 		break;
 	case MGCP_OFFHOOK:
-		add_header_offhook(sub, &resp);
+		//add_header_offhook(sub, &resp);
+		add_header(&resp, "R", (sub->rtp && (p->dtmfmode & MGCP_DTMF_INBAND)) ? "L/hu(N),L/hf(N)" : (p->ncs ? "L/hu(N), L/hf(N), L/[0-9#*](N)" : "L/hu(N),L/hf(N),D/[0-9#*](N)"));
 		break;
 	}
 	if (!ast_strlen_zero(tone2)) {
@@ -2305,7 +2379,8 @@
 		add_header(&resp, "R", "L/hd(N)");
 		break;
 	case MGCP_OFFHOOK:
-		add_header_offhook(sub, &resp);
+		//add_header_offhook(sub, &resp);
+		add_header(&resp, "R", (sub->rtp && (p->dtmfmode & MGCP_DTMF_INBAND)) ? "L/hu(N),L/hf(N)" : (p->ncs ? "L/hu(N), L/hf(N), L/[0-9#*](N)" : "L/hu(N),L/hf(N),D/[0-9#*](N)"));
 		break;
 	}
 	/* fill in new fields */
@@ -2492,6 +2567,10 @@
 			break;
 		}
 		if (sub) {
+			if (!sub->cxident[0] && (req->cmd == MGCP_CMD_CRCX)) {
+			    ast_log(LOG_NOTICE, "DLCX for all connections on %s due to error %d\n", gw->name, result);
+			    transmit_connection_del(sub);
+			}
 			if (sub->owner) {
 				ast_log(LOG_NOTICE, "Terminating on result %d from %s@%s-%d\n", 
 					result, p->name, p->parent->name, sub ? sub->id:-1);
@@ -2515,6 +2594,14 @@
 	}
 
 	if (resp) {
+               /* responseAck: */
+		if ((req->cmd == MGCP_CMD_CRCX || req->cmd == MGCP_CMD_MDCX)) {
+            	    if ((c = get_header_default(resp, "K", NULL))) {
+			if (c == '\0') {
+			    transmit_response(sub, "000", resp, "OK");
+			}
+		    }
+		}
 		if (req->cmd == MGCP_CMD_CRCX) {
 			if ((c = get_header(resp, "I"))) {
 				if (!ast_strlen_zero(c) && sub) {
@@ -2720,7 +2807,7 @@
 						ast_log(LOG_WARNING, "PBX exited non-zero\n");
 						/*res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);*/
 						/*transmit_notify_request(p, "nbz", 1);*/
-						transmit_notify_request(sub, "G/cg");
+						transmit_notify_request(sub, p->ncs ? "L/cg" : "G/cg");
 					}
 					return NULL;
 				}
@@ -2732,7 +2819,7 @@
 		} else if (res == 0) {
 			ast_log(LOG_DEBUG, "not enough digits (and no ambiguous match)...\n");
 			/*res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);*/
-			transmit_notify_request(sub, "G/cg");
+			transmit_notify_request(sub, p->ncs ? "L/cg" : "G/cg");
 			/*zt_wait_event(p->subs[index].zfd);*/
 			ast_hangup(chan);
 			memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf));
@@ -2756,7 +2843,7 @@
 			if (ast_pickup_call(chan)) {
 				ast_log(LOG_WARNING, "No call pickup possible...\n");
 				/*res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);*/
-				transmit_notify_request(sub, "G/cg");
+				transmit_notify_request(sub, p->ncs ? "L/cg" : "G/cg");
 			}
 			memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf));
 			ast_hangup(chan);
@@ -3002,12 +3089,12 @@
 #ifdef DLINK_BUGGY_FIRMWARE	
 				transmit_notify_request(sub, "rt");
 #else
-				transmit_notify_request(sub, "G/rt");
+				transmit_notify_request(sub, p->ncs ? "L/rt" : "G/rt");
 #endif		
 				c = mgcp_new(sub, AST_STATE_RING);
 				if (!c) {
 					ast_log(LOG_WARNING, "Unable to start PBX on channel %s@%s\n", p->name, p->parent->name);
-					transmit_notify_request(sub, "G/cg");
+					transmit_notify_request(sub, p->ncs ? "L/cg" : "G/cg");
 					ast_hangup(c);
 				}
 			} else {
@@ -3056,6 +3143,7 @@
 	struct mgcp_endpoint *p = sub->parent;
 	struct mgcp_gateway *g = NULL;
 	int res;
+	int unhidecallerid = 0;
 
 	if (mgcpdebug) {
 		ast_verbose("Handling request '%s' on %s@%s\n", req->verb, p->name, p->parent->name);
@@ -3259,6 +3347,9 @@
 				}
 			}
 			if ((p->hookstate == MGCP_ONHOOK) && (!sub->rtp) && (!sub->next->rtp)) {
+			
+				if (p->hidecallerid) unhidecallerid = 1;
+			
 				p->hidecallerid = 0;
 				if (p->hascallwaiting && !p->callwaiting) {
 					if (option_verbose > 2)
@@ -3276,6 +3367,8 @@
 					}
 					transmit_notify_request(sub, "L/vmwi(-)");
 				}
+				
+				 if (unhidecallerid) p->hidecallerid = 1;
 			}
 		} else if ((strlen(ev) == 1) && 
 				(((ev[0] >= '0') && (ev[0] <= '9')) ||
@@ -3377,8 +3470,12 @@
 	}
 
 	if (sscanf(req.verb, "%d", &result) && sscanf(req.identifier, "%d", &ident)) {
+	    if (result < 200) {
+		ast_log(LOG_DEBUG, "Ignoring provisional response on transaction %d\n", ident);
+		return 1;
+	    }
 		/* Try to find who this message is for, if it's important */
-		sub = find_subchannel_and_lock(NULL, ident, &sin);
+		sub = find_subchannel_and_lock(NULL, ident, &sin, gatetemplate);
 		if (sub) {
 			struct mgcp_gateway *gw = sub->parent->parent;
 			struct mgcp_message *cur, *prev;
@@ -3419,7 +3516,7 @@
 			return 1;
 		}
 		/* Process request, with iflock held */
-		sub = find_subchannel_and_lock(req.endpoint, 0, &sin);
+		sub = find_subchannel_and_lock(req.endpoint, 0, &sin, gatetemplate);
 		if (sub) {
 			/* look first to find a matching response in the queue */
 			if (!find_and_retrans(sub, &req))
@@ -3567,7 +3664,7 @@
 		ast_log(LOG_NOTICE, "MGCP Channels require an endpoint\n");
 		return NULL;
 	}
-	sub = find_subchannel_and_lock(tmp, 0, NULL);
+	sub = find_subchannel_and_lock(tmp, 0, NULL, gatetemplate);
 	if (!sub) {
 		ast_log(LOG_WARNING, "Unable to find MGCP endpoint '%s'\n", tmp);
 		*cause = AST_CAUSE_UNREGISTERED;
@@ -3692,6 +3789,8 @@
 					ast_log(LOG_WARNING, "'%s' is not a valid DTMF mode at line %d\n", v->value, v->lineno);
 			} else if (!strcasecmp(v->name, "nat")) {
 				nat = ast_true(v->value);
+			} else if (!strcasecmp(v->name, "ncs")) {
+				ncs = ast_true(v->value);
 			} else if (!strcasecmp(v->name, "callerid")) {
 				if (!strcasecmp(v->value, "asreceived")) {
 					cid_num[0] = '\0';
@@ -3720,6 +3819,8 @@
 				immediate = ast_true(v->value);
 			} else if (!strcasecmp(v->name, "cancallforward")) {
 				cancallforward = ast_true(v->value);
+			} else if (!strcasecmp(v->name, "hidecallerid")) {
+				hidecallerid = ast_true(v->value);
 			} else if (!strcasecmp(v->name, "singlepath")) {
 				singlepath = ast_true(v->value);
 			} else if (!strcasecmp(v->name, "canreinvite")) {
@@ -3780,6 +3881,7 @@
 					e->amaflags = amaflags;
 					e->capability = capability;
 					e->parent = gw;
+					e->hidecallerid = hidecallerid;
 					e->dtmfmode = dtmfmode;
 					if (!ep_reload && e->sub && e->sub->rtp)
 						e->dtmfmode |= MGCP_DTMF_INBAND;
@@ -3884,6 +3986,7 @@
 					e->amaflags = amaflags;
 					e->capability = capability;
 					e->dtmfmode = dtmfmode;
+					e->ncs = ncs;
 					e->adsi = adsi;
 					if (!strcasecmp(v->name, "trunk"))
 						e->type = TYPE_TRUNK;
@@ -3895,6 +3998,7 @@
 					e->pickupgroup=cur_pickupgroup;
 					e->callreturn = callreturn;
 					e->cancallforward = cancallforward;
+					e->hidecallerid = hidecallerid;
 					e->canreinvite = canreinvite;
 					e->singlepath = singlepath;
 					e->callwaiting = callwaiting;
@@ -4205,20 +4309,30 @@
 	cat = ast_category_browse(cfg, NULL);
 	while(cat) {
 		if (strcasecmp(cat, "general")) {
+		    if (!(strcasecmp(cat, "template"))) {
+			ast_mutex_lock(&gatelock);
+			gatetemplate = build_gateway(cat, ast_variable_browse(cfg, cat));
+			if (gatetemplate) {
+			    if (option_verbose > 2) {
+				ast_verbose(VERBOSE_PREFIX_3 "Added template gateway\n");
+			    }
+			}
+		    ast_mutex_unlock(&gatelock);
+		    } else {
 			ast_mutex_lock(&gatelock);
 			g = build_gateway(cat, ast_variable_browse(cfg, cat));
 			if (g) {
-				if (option_verbose > 2) {
-					ast_verbose(VERBOSE_PREFIX_3 "Added gateway '%s'\n", g->name);
-				}
-				g->next = gateways;
-				gateways = g;
+			    if (option_verbose > 2) {
+				ast_verbose(VERBOSE_PREFIX_3 "Added gateway '%s'\n", g->name);
+			    }
+			    g->next = gateways;
+			    gateways = g;
 			}
 			ast_mutex_unlock(&gatelock);
-
-			/* FS: process queue and IO */
-			if (monitor_thread == pthread_self()) {
-				if (sched) ast_sched_runq(sched);
+		    }
+		/* FS: process queue and IO */
+		if (monitor_thread == pthread_self()) {
+			if (sched) ast_sched_runq(sched);
 				if (io) ast_io_wait(io, 10);
 			}
 		}
