diff --git a/CHANGES b/CHANGES
index cc9cad3da119f65a069db44e00445f01107a5115..334c478133027840f5be27ab2014c8cadd97977d 100755
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,4 @@
+ -- Add "hint" support
  -- Improve call forwarding using new "Local" channel driver.
  -- Add "Local" channel
  -- Substantial SIP enhancements including retransmissions
diff --git a/CREDITS b/CREDITS
index a3ad07c350906c422d8ab8f37fdd51107dded596..4ee9df741a2e2c20d0b54696793012718f494892 100755
--- a/CREDITS
+++ b/CREDITS
@@ -24,6 +24,7 @@ PhoneJack and Linejack card to the project.  (http://www.quicknet.net)
 === MISCELLANEOUS PATCHES ===
 James Golovich - Innumerable contributions
        You can find him and asterisk-perl at http://asterisk.gnuinter.net
+Andre Bierwirth - Extension hints and status
 Oliver Daudey - ISDN4Linux fixes
 Pauline Middelink - ISDN4Linux patches and some general patches.
        She can be found at http://www.polyware.nl/~middelink/En/
diff --git a/channel.c b/channel.c
index ed9bd25113f1f204ab227d44f92641c1e0ef8a20..56528b8128cbb37b96890c5738262da41355774e 100755
--- a/channel.c
+++ b/channel.c
@@ -1496,8 +1496,6 @@ int ast_device_state(char *device)
 		}
 		chanls = chanls->next;
 	}
-	if (!chanls)
-		ast_log(LOG_WARNING, "No channel type registered for '%s'\n", tech);
 	PTHREAD_MUTEX_UNLOCK(&chlock);
 	return AST_DEVICE_INVALID;
 }
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index e54c1bae3993f26333aef85276a54ff6cce40c2a..272a5a8a68af7076e0e069a7f74197c34cdbd2a3 100755
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -197,6 +197,10 @@ static struct sip_pvt {
 	int initid;							/* Auto-congest ID if appropriate */
 	int autokillid;						/* Auto-kill ID */
 
+	int subscribed;
+    	int stateid;
+	int dialogver;
+	
         int dtmfmode;
         struct ast_dsp *vad;
 	
@@ -718,10 +722,13 @@ static void __sip_destroy(struct sip_pvt *p, int lockowner)
 {
 	struct sip_pvt *cur, *prev = NULL;
 	struct sip_pkt *cp;
+	if (p->stateid > -1)
+		ast_extension_state_del(p->stateid, NULL);
 	if (p->initid > -1)
 		ast_sched_del(sched, p->initid);
 	if (p->autokillid > -1)
 		ast_sched_del(sched, p->autokillid);
+
 	if (p->rtp) {
 		ast_rtp_destroy(p->rtp);
 	}
@@ -1246,9 +1253,11 @@ static struct sip_pvt *sip_alloc(char *callid, struct sockaddr_in *sin, int useg
 	memset(p, 0, sizeof(struct sip_pvt));
 	p->initid = -1;
 	p->autokillid = -1;
+	p->stateid = -1;
 	p->rtp = ast_rtp_new(NULL, NULL);
 	p->branch = rand();	
 	p->tag = rand();
+	
 	/* Start with 101 instead of 1 */
 	p->ocseq = 101;
 	if (!p->rtp) {
@@ -1863,10 +1872,10 @@ static int respprep(struct sip_request *resp, struct sip_pvt *p, char *msg, stru
 		char contact[256];
 		char *c;
 		if ((c=getsipuri(ot))) {
-			snprintf(contact, sizeof(contact), "<%s@%s:%d>", c, inet_ntoa(p->ourip), ourport);
+			snprintf(contact, sizeof(contact), "<%s@%s:%d>;expires=%d", c, inet_ntoa(p->ourip), ourport, p->expirey);
 			free(c);
 		} else {
-			snprintf(contact, sizeof(contact), "<%s:%d>", inet_ntoa(p->ourip), ourport);
+			snprintf(contact, sizeof(contact), "<%s:%d>;expires=%d", inet_ntoa(p->ourip), ourport, p->expirey);
 		}
 		snprintf(tmp, sizeof(tmp), "%d", p->expirey);
 		add_header(resp, "Expires", tmp);
@@ -2284,6 +2293,92 @@ static int transmit_invite(struct sip_pvt *p, char *cmd, int sdp, char *auth, ch
 	return send_request(p, &req, 1, p->ocseq);
 }
 
+static int transmit_state_notify(struct sip_pvt *p, int state, int full)
+{
+	char tmp[2000];
+	char from[256], to[256];
+	char *t, *c, *a;
+	char *mfrom, *mto;
+	struct sip_request req;
+	char clen[20];
+	
+	strncpy(from, get_header(&p->initreq, "From"), sizeof(from)-1);
+
+	c = ditch_braces(from);
+	if (strncmp(c, "sip:", 4)) {
+		ast_log(LOG_WARNING, "Huh?  Not a SIP header (%s)?\n", c);
+		return -1;
+	}
+	if ((a = strchr(c, ';'))) {
+		*a = '\0';
+	}
+	mfrom = c;
+		
+	reqprep(&req, p, "NOTIFY", 1);
+
+	if (p->subscribed == 1) {
+    	    strncpy(to, get_header(&p->initreq, "To"), sizeof(to)-1);
+
+	    c = ditch_braces(to);
+	    if (strncmp(c, "sip:", 4)) {
+		ast_log(LOG_WARNING, "Huh?  Not a SIP header (%s)?\n", c);
+		return -1;
+	    }
+	    if ((a = strchr(c, ';'))) {
+		*a = '\0';
+	    }
+	    mto = c;
+
+	    add_header(&req, "Content-Type", "application/xpidf+xml");
+
+	    if ((state==AST_EXTENSION_UNAVAILABLE) || (state==AST_EXTENSION_BUSY))
+		state = 2;
+	    else if (state==AST_EXTENSION_INUSE)
+		state = 1;
+	    else
+		state = 0;
+	    
+	    t = tmp;		
+	    sprintf(t, "<?xml version=\"1.0\"?>\n");
+	    t = tmp + strlen(tmp);
+	    sprintf(t, "<!DOCTYPE presence PUBLIC \"-//IETF//DTD RFCxxxx XPIDF 1.0//EN\" \"xpidf.dtd\">\n");
+	    t = tmp + strlen(tmp);
+	    sprintf(t, "<presence>\n");
+	    t = tmp + strlen(tmp);
+	    sprintf(t, "<presentity uri=\"%s;method=SUBSCRIBE\" />\n", mfrom);
+	    t = tmp + strlen(tmp);
+	    sprintf(t, "<atom id=\"%s\">\n", p->exten);
+	    t = tmp + strlen(tmp);
+	    sprintf(t, "<address uri=\"%s;user=ip\" priority=\"0,800000\">\n", mto);
+	    t = tmp + strlen(tmp);
+	    sprintf(t, "<status status=\"%s\" />\n", !state ? "open" : (state==1) ? "inuse" : "closed");
+	    t = tmp + strlen(tmp);
+	    sprintf(t, "<msnsubstatus substatus=\"%s\" />\n", !state ? "online" : (state==1) ? "onthephone" : "offline");
+	    t = tmp + strlen(tmp);
+	    sprintf(t, "</address>\n</atom>\n</presence>\n");	    	
+	} else {
+    	    add_header(&req, "Event", "dialog");
+	    add_header(&req, "Content-Type", "application/dialog-info+xml");
+	
+	    t = tmp;		
+	    sprintf(t, "<?xml version=\"1.0\"?>\n");
+	    t = tmp + strlen(tmp);
+	    sprintf(t, "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" version=\"%d\" state=\"%s\" entity=\"%s\">\n", p->dialogver++, full ? "full":"partial", mfrom);
+	    t = tmp + strlen(tmp);
+	    sprintf(t, "<dialog id=\"%s\">\n", p->exten);
+	    t = tmp + strlen(tmp);
+	    sprintf(t, "<state>%s</state>\n", state ? "confirmed" : "terminated");
+	    t = tmp + strlen(tmp);
+	    sprintf(t, "</dialog>\n</dialog-info>\n");	
+	}
+
+	snprintf(clen, sizeof(clen), "%d", strlen(tmp));
+	add_header(&req, "Content-Length", clen);
+	add_line(&req, tmp);
+
+	return send_request(p, &req, 1, p->ocseq);
+}
+
 static int transmit_notify(struct sip_pvt *p, int newmsgs, int oldmsgs)
 {
 	struct sip_request req;
@@ -2307,8 +2402,7 @@ static int transmit_notify(struct sip_pvt *p, int newmsgs, int oldmsgs)
 		parse(&p->initreq);
 	}
 
-	p->lastinvite = p->ocseq;
-	return send_request(p, &req, 1, p->ocseq);
+	return send_request(p, &req, 0, p->ocseq);
 }
 
 static int transmit_register(struct sip_registry *r, char *cmd, char *auth);
@@ -2447,6 +2541,7 @@ static int expire_register(void *data)
 	struct sip_peer *p = data;
 	memset(&p->addr, 0, sizeof(p->addr));
 	p->expire = -1;
+	ast_device_state_changed("SIP/%s", p->name);
 	return 0;
 }
 
@@ -2478,14 +2573,14 @@ static int parse_contact(struct sip_pvt *pvt, struct sip_peer *p, struct sip_req
 		if (n) 
 			*n = '\0';
 	}
-	if (!strcasecmp(c, "*")) {
+	if (!strcasecmp(c, "*") || !expirey) {
 		/* This means remove all registrations and return OK */
 		memset(&p->addr, 0, sizeof(p->addr));
 		if (p->expire > -1)
 			ast_sched_del(sched, p->expire);
 		p->expire = -1;
 		if (option_verbose > 2)
-			ast_verbose(VERBOSE_PREFIX_3 "Unegistered SIP '%s'\n", p->username);
+			ast_verbose(VERBOSE_PREFIX_3 "Unregistered SIP '%s'\n", p->username);
 		return 0;
 	}
 	/* Make sure it's a SIP URL */
@@ -2755,6 +2850,22 @@ static int check_auth(struct sip_pvt *p, struct sip_request *req, char *randdata
 	return res;
 }
 
+static int cb_extensionstate(char *context, char* exten, int state, void *data)
+{
+    struct sip_pvt *p = data;
+    if (state == -1) {
+	sip_scheddestroy(p, 15000);
+	p->stateid = -1;
+	return 0;
+    }
+    
+    transmit_state_notify(p, state, 1);
+    
+    if (option_debug)
+        ast_verbose(VERBOSE_PREFIX_1 "Extension Changed %s new state %d for Notify User %s\n", exten, state, p->username);
+    return 0;
+}
+
 static int register_verify(struct sip_pvt *p, struct sockaddr_in *sin, struct sip_request *req, char *uri)
 {
 	int res = -1;
@@ -2801,6 +2912,9 @@ static int register_verify(struct sip_pvt *p, struct sockaddr_in *sin, struct si
 		peer = peer->next;
 	}
 	ast_pthread_mutex_unlock(&peerl.lock);
+	if (!res) {
+	    ast_device_state_changed("SIP/%s", peer->name);
+	}
 	if (res < 0)
 		transmit_response(p, "401 Unauthorized", &p->initreq);
 	return res;
@@ -2995,7 +3109,7 @@ static int check_via(struct sip_pvt *p, struct sip_request *req)
 	return 0;
 }
 
-static int check_user(struct sip_pvt *p, struct sip_request *req, char *cmd, char *uri)
+static int check_user(struct sip_pvt *p, struct sip_request *req, char *cmd, char *uri, int reliable)
 {
 	struct sip_user *user;
 	struct sip_peer *peer;
@@ -3031,7 +3145,7 @@ static int check_user(struct sip_pvt *p, struct sip_request *req, char *cmd, cha
 				ast_log(LOG_DEBUG, "Setting NAT on RTP to %d\n", p->nat);
 				ast_rtp_setnat(p->rtp, p->nat);
 			}
-			if (!(res = check_auth(p, req, p->randdata, sizeof(p->randdata), user->name, user->secret, cmd, uri, 1))) {
+			if (!(res = check_auth(p, req, p->randdata, sizeof(p->randdata), user->name, user->secret, cmd, uri, reliable))) {
 				sip_cancel_destroy(p);
 				strncpy(p->context, user->context, sizeof(p->context) - 1);
 				if (strlen(user->callerid) && strlen(p->callerid)) 
@@ -3229,6 +3343,7 @@ static int sip_show_channels(int fd, int argc, char *argv[])
 	cur = iflist;
 	ast_cli(fd, FORMAT2, "Peer", "Username", "Call ID", "Seq (Tx/Rx)", "Lag", "Jitter", "Format");
 	while (cur) {
+		if (!cur->subscribed) {
 			ast_cli(fd, FORMAT, inet_ntoa(cur->sa.sin_addr), 
 						strlen(cur->username) ? cur->username : "(None)", 
 						cur->callid, 
@@ -3236,8 +3351,9 @@ static int sip_show_channels(int fd, int argc, char *argv[])
 						0,
 						0,
 						cur->owner ? cur->owner->nativeformats : 0);
-		cur = cur->next;
 		numchans++;
+		}
+		cur = cur->next;
 	}
 	ast_pthread_mutex_unlock(&iflock);
 	ast_cli(fd, "%d active SIP channel(s)\n", numchans);
@@ -3525,6 +3641,7 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_
 		   Well, as long as it's not a 100 response...  since we might
 		   need to hang around for something more "difinitive" */
 		if (resp != 100) {
+			int statechanged = 0;
 			peer = p->peerpoke;
 			gettimeofday(&tv, NULL);
 			pingtime = (tv.tv_sec - peer->ps.tv_sec) * 1000 +
@@ -3532,14 +3649,23 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_
 			if (pingtime < 1)
 				pingtime = 1;
 			if ((peer->lastms < 0)  || (peer->lastms > peer->maxms)) {
-				if (pingtime <= peer->maxms)
-				ast_log(LOG_NOTICE, "Peer '%s' is now REACHABLE!\n", peer->name);
+				if (pingtime <= peer->maxms) {
+					ast_log(LOG_NOTICE, "Peer '%s' is now REACHABLE!\n", peer->name);
+					statechanged = 1;
+				}
 			} else if ((peer->lastms > 0) && (peer->lastms <= peer->maxms)) {
-				if (pingtime > peer->maxms)
+				if (pingtime > peer->maxms) {
 					ast_log(LOG_NOTICE, "Peer '%s' is now TOO LAGGED!\n", peer->name);
+					statechanged = 1;
+				}
 			}
+			if (!peer->lastms)
+			    statechanged = 1;
 			peer->lastms = pingtime;
 			peer->call = NULL;
+			if (statechanged)
+			    ast_device_state_changed("SIP/%s", peer->name);
+
 			if (peer->pokeexpire > -1)
 				ast_sched_del(sched, peer->pokeexpire);
 			if (!strcasecmp(msg, "INVITE"))
@@ -3547,7 +3673,7 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_
 			p->needdestroy = 1;
 			/* Try again eventually */
 			if ((peer->lastms < 0)  || (peer->lastms > peer->maxms))
-				peer->pokeexpire = ast_sched_add(sched, DEFAULT_FREQ_NOTOK, sip_poke_peer_s, peer);
+    				peer->pokeexpire = ast_sched_add(sched, DEFAULT_FREQ_NOTOK, sip_poke_peer_s, peer);
 			else
 				peer->pokeexpire = ast_sched_add(sched, DEFAULT_FREQ_OK, sip_poke_peer_s, peer);
 		}
@@ -3594,8 +3720,10 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_
 					ast_log(LOG_WARNING, "Notify answer on an owned channel?\n");
 					ast_queue_hangup(p->owner, 0);
 				} else {
-					sip_destroy(p);
-					p = NULL;
+					if (!p->subscribed) {
+					    sip_destroy(p);
+					    p = NULL;
+					}
 				}
 			} else if (!strcasecmp(msg, "INVITE")) {
 				if (strlen(get_header(req, "Content-Type")))
@@ -3699,13 +3827,18 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_
 				ast_log(LOG_NOTICE, "Dunno anything about a %d %s response from %s\n", resp, rest, p->owner ? p->owner->name : inet_ntoa(p->sa.sin_addr));
 		}
 	} else {
-		if (sipdebug)
-			ast_verbose("Message is %s\n", msg);
-		switch(resp) {
-		case 200:
-			if (!strcasecmp(msg, "INVITE") || !strcasecmp(msg, "REGISTER") )
-				transmit_request(p, "ACK", 0, 0);
-			break;
+		if (p->subscribed) {
+		    /* Acknowledge sequence number */
+		    __sip_ack(p, seqno, 0);
+		} else {
+			if (sipdebug)
+				ast_verbose("Message is %s\n", msg);
+			switch(resp) {
+			case 200:
+				if (!strcasecmp(msg, "INVITE") || !strcasecmp(msg, "REGISTER") )
+					transmit_request(p, "ACK", 0, 0);
+				break;
+			}
 		}
 	}
 	if (owner)
@@ -3922,7 +4055,7 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc
 			ast_verbose("Ignoring this request\n");
 		if (!p->lastinvite) {
 			/* Handle authentication if this is our first invite */
-			res = check_user(p, req, cmd, e);
+			res = check_user(p, req, cmd, e, 1);
 			if (res) {
 				if (res < 0) {
 					ast_log(LOG_NOTICE, "Failed to authenticate user %s\n", get_header(req, "From"));
@@ -4046,6 +4179,71 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc
 			ast_verbose("Receiving message!\n");
 		receive_message(p, req);
 		transmit_response(p, "200 OK", req);
+	} else if (!strcasecmp(cmd, "SUBSCRIBE")) {
+		if (!ignore) {
+			/* Use this as the basis */
+			if (sipdebug)
+				ast_verbose("Using latest SUBSCRIBE request as basis request\n");
+			/* This call is no longer outgoing if it ever was */
+			p->outgoing = 0;
+			copy_request(&p->initreq, req);
+			check_via(p, req);
+		} else if (sipdebug)
+			ast_verbose("Ignoring this request\n");
+			
+		if (!p->lastinvite) {
+			/* Handle authentication if this is our first subscribe */
+			res = check_user(p, req, cmd, e, 0);
+			if (res) {
+				if (res < 0) {
+					ast_log(LOG_NOTICE, "Failed to authenticate user %s for SUBSCRIBE\n", get_header(req, "From"));
+					sip_destroy(p);
+				}
+				return 0;
+			}
+			/* Initialize the context if it hasn't been already */
+			if (!strlen(p->context))
+				strncpy(p->context, context, sizeof(p->context) - 1);
+			if ((res = get_destination(p, NULL))) {
+				if (res < 0)
+					transmit_response(p, "404 Not Found", req);
+				else
+					transmit_response(p, "484 Address Incomplete", req);
+				sip_destroy(p);
+				p = NULL;
+				c = NULL;
+			} else {
+				/* Initialize tag */	
+				p->tag = rand();
+
+				if (!strcmp(get_header(req, "Accept"), "application/dialog-info+xml"))
+				    p->subscribed = 2;
+				else
+				    p->subscribed = 1;
+				
+				p->stateid = ast_extension_state_add(p->context, p->exten, cb_extensionstate, p);
+			}
+			
+		} else 
+			c = p->owner;
+
+		if (!ignore && p)
+			p->lastinvite = seqno;
+		if (p) {
+		    if (!(p->expirey = atoi(get_header(req, "Expires")))) {
+			transmit_response(p, "200 OK", req);
+			sip_destroy(p);	
+			return 0;
+		    }
+		    // The next line can be removed if the SNOM200 Expires bug is fixed
+		    if (p->subscribed == 1) {  
+			if (p->expirey>max_expirey)
+			    p->expirey = max_expirey;
+		    }
+		    transmit_response(p, "200 OK", req);
+		    sip_scheddestroy(p, (p->expirey+10)*1000);
+		    transmit_state_notify(p, ast_extension_state(NULL, p->context, p->exten),1);
+		}
 	} else if (!strcasecmp(cmd, "INFO")) {
 		if (sipdebug)
 			ast_verbose("Receiving DTMF!\n");
@@ -4276,6 +4474,7 @@ static int restart_monitor(void)
 static int sip_poke_noanswer(void *data)
 {
 	struct sip_peer *peer = data;
+	
 	peer->pokeexpire = -1;
 	if (peer->lastms > -1)
 		ast_log(LOG_NOTICE, "Peer '%s' is now UNREACHABLE!\n", peer->name);
@@ -4283,6 +4482,7 @@ static int sip_poke_noanswer(void *data)
 		sip_destroy(peer->call);
 	peer->call = NULL;
 	peer->lastms = -1;
+	ast_device_state_changed("SIP/%s", peer->name);
 	/* Try again quickly */
 	peer->pokeexpire = ast_sched_add(sched, DEFAULT_FREQ_NOTOK, sip_poke_peer_s, peer);
 	return 0;
@@ -4332,6 +4532,52 @@ static int sip_poke_peer(struct sip_peer *peer)
 	return 0;
 }
 
+static int sip_devicestate(void *data)
+{
+	char *ext, *host;
+	char tmp[256] = "";
+	char *dest = data;
+
+	struct hostent *hp;
+	struct sip_peer *p;
+	int found = 0;
+
+	int res = AST_DEVICE_INVALID;
+
+	strncpy(tmp, dest, sizeof(tmp) - 1);
+	host = strchr(tmp, '@');
+	if (host) {
+		*host = '\0';
+		host++;
+		ext = tmp;
+	} else {
+		host = tmp;
+		ext = NULL;
+	}
+
+	ast_pthread_mutex_lock(&peerl.lock);
+	p = peerl.peers;
+	while (p) {
+		if (!strcasecmp(p->name, host)) {
+			found++;
+			res = AST_DEVICE_UNAVAILABLE;
+			if ((p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) &&
+				(!p->maxms || ((p->lastms > -1)  && (p->lastms <= p->maxms)))) {
+				/* peer found and valid */
+				res = AST_DEVICE_UNKNOWN;
+				break;
+			}
+		}
+		p = p->next;
+	}
+	ast_pthread_mutex_unlock(&peerl.lock);
+	if (!p && !found) {
+		hp = gethostbyname(host);
+		if (hp)
+			res = AST_DEVICE_UNKNOWN;
+	}
+	return res;
+}
 
 static struct ast_channel *sip_request(char *type, int format, void *data)
 {
@@ -4841,7 +5087,7 @@ int load_module()
 	res = reload_config();
 	if (!res) {
 		/* Make sure we can register our sip channel type */
-		if (ast_channel_register(type, tdesc, capability, sip_request)) {
+		if (ast_channel_register_ex(type, tdesc, capability, sip_request, sip_devicestate)) {
 			ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
 			return -1;
 		}
@@ -4871,6 +5117,7 @@ int load_module()
 			sip_do_register(reg);
 
 		ast_pthread_mutex_unlock(&peerl.lock);
+		
 		/* And start the monitor for the first time */
 		restart_monitor();
 	}
@@ -4961,6 +5208,7 @@ int reload(void)
 int unload_module()
 {
 	struct sip_pvt *p, *pl;
+	
 	/* First, take us out of the channel loop */
 	ast_channel_unregister(type);
 	if (!ast_pthread_mutex_lock(&iflock)) {
diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h
index a616414e33f6d91935d96f9c450c7f1be64ef31d..de7c00ec6ea980b37961ed5613cf786ba7b880a6 100755
--- a/include/asterisk/pbx.h
+++ b/include/asterisk/pbx.h
@@ -48,7 +48,7 @@ struct ast_include;
 struct ast_ignorepat;
 struct ast_sw;
 
-typedef int (*ast_notify_cb_type)(char *context, char* id, int state, void *data);
+typedef int (*ast_state_cb_type)(char *context, char* id, int state, void *data);
 
 //! Data structure associated with an asterisk switch
 struct ast_switch {
@@ -215,12 +215,13 @@ int ast_extension_state(struct ast_channel *c, char *context, char *exten);
 
 //! Tells Asterisk the State for Device is changed
 /*!
- * \param device devicename like a dialstring
+ * \param fmt devicename like a dialstring with format parameters
  * Asterisk polls the new extensionstates and calls the registered
  * callbacks for the changed extensions
  * Returns 0 on success, -1 on failure
  */
-int ast_device_state_changed(char *device);
+int ast_device_state_changed(const char *fmt, ...)
+	__attribute__ ((format (printf, 1, 2)));
 
 //! Registers a state change callback
 /*!
@@ -232,7 +233,7 @@ int ast_device_state_changed(char *device);
  * Return -1 on failure, ID on success
  */ 
 int ast_extension_state_add(char *context, char *exten, 
-			    ast_notify_cb_type callback, void *data);
+			    ast_state_cb_type callback, void *data);
 
 //! Deletes a registered state change callback by ID
 /*!
@@ -240,7 +241,7 @@ int ast_extension_state_add(char *context, char *exten,
  * Removes the callback from list of callbacks
  * Return 0 on success, -1 on failure
  */
-int ast_extension_state_del(int id);
+int ast_extension_state_del(int id, ast_state_cb_type callback);
 
 //! If an extension exists, return non-zero
 /*!
diff --git a/manager.c b/manager.c
index 6491ed2a9df2c948a407138fd2a73c3f25e9d61f..dba21443bc37ffc01cb2419117494b48e57eea13 100755
--- a/manager.c
+++ b/manager.c
@@ -425,7 +425,24 @@ static int action_mailboxstatus(struct mansession *s, struct message *m)
 		return 0;
 	}
 	astman_send_ack(s, "Mailbox status will follow");
-	manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting:%d\r\n", mailbox, ast_app_has_voicemail(mailbox));
+	manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", mailbox, ast_app_has_voicemail(mailbox));
+	return 0;
+}
+
+static int action_extensionstate(struct mansession *s, struct message *m)
+{
+	char *exten = astman_get_header(m, "Exten");
+	char *context = astman_get_header(m, "Context");
+	int status;
+	if (!exten || !strlen(exten)) {
+		astman_send_error(s, "Extension not specified");
+		return 0;
+	}
+	if (!context || !strlen(context))
+		context = "default";
+	astman_send_ack(s, "Extension status will follow");
+	status = ast_extension_state(NULL, context, exten);
+	manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, status);
 	return 0;
 }
 
@@ -710,6 +727,7 @@ int init_manager(void)
 		ast_manager_register( "Originate", EVENT_FLAG_CALL, action_originate, "Originate Call" );
 		ast_manager_register( "MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox" );
 		ast_manager_register( "Command", EVENT_FLAG_COMMAND, action_command, "Execute Command" );
+		ast_manager_register( "ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status" );
 
 		ast_cli_register(&show_mancmds_cli);
 		ast_cli_register(&show_manconn_cli);
diff --git a/pbx.c b/pbx.c
index 011bd268777af2422da85eaeac3efd9c4c2543f5..ac1b00cc47d5f0684f385a2a90b9c792229f5f70 100755
--- a/pbx.c
+++ b/pbx.c
@@ -133,18 +133,18 @@ struct ast_app {
 };
 
 /* An extension state notify */
-struct ast_notify_cb {
+struct ast_state_cb {
     int id;
     void *data;
-    ast_notify_cb_type callback;
-    struct ast_notify_cb *next;
+    ast_state_cb_type callback;
+    struct ast_state_cb *next;
 };
 	    
-struct ast_notify {
+struct ast_hint {
     struct ast_exten *exten;
     int laststate; 
-    struct ast_notify_cb *callbacks;
-    struct ast_notify *next;
+    struct ast_state_cb *callbacks;
+    struct ast_hint *next;
 };
 
 
@@ -311,9 +311,10 @@ static pthread_mutex_t switchlock = AST_MUTEX_INITIALIZER;
 struct ast_switch *switches = NULL;
 
 /* Lock for extension state notifys */
-static pthread_mutex_t notifylock = AST_MUTEX_INITIALIZER;
-static int notifycnt = 0;
-struct ast_notify *notifys = NULL;
+static pthread_mutex_t hintlock = AST_MUTEX_INITIALIZER;
+static int stateid = 1;
+struct ast_hint *hints = NULL;
+struct ast_state_cb *statecbs = NULL;
 
 int pbx_exec(struct ast_channel *c, /* Channel */
 					struct ast_app *app,
@@ -1192,17 +1193,24 @@ int ast_extension_state(struct ast_channel *c, char *context, char *exten)
     return ast_extension_state2(e);    
 }
 
-int ast_device_state_changed(char *device) 
+int ast_device_state_changed(const char *fmt, ...) 
 {
-    struct ast_notify *list;
-    struct ast_notify_cb *cblist;
+    struct ast_hint *list;
+    struct ast_state_cb *cblist;
     char hint[AST_MAX_EXTENSION];
+    char device[AST_MAX_EXTENSION];
     char *cur, *rest;
     int state;
+    
+    va_list ap;
+
+    va_start(ap, fmt);
+    vsnprintf(device, sizeof(device)-1, fmt, ap);
+    va_end(ap);
         
-    pthread_mutex_lock(&notifylock);
+    pthread_mutex_lock(&hintlock);
 
-    list = notifys;
+    list = hints;
     
     while (list) {
 	
@@ -1219,11 +1227,20 @@ int ast_device_state_changed(char *device)
 	    // Found extension execute callbacks 
 		state = ast_extension_state2(list->exten);
 		if ((state != -1) && (state != list->laststate)) {
+		    // For general callbacks
+		    cblist = statecbs;
+		    while (cblist) {
+			cblist->callback(list->exten->parent->name, list->exten->exten, state, cblist->data);
+			cblist = cblist->next;
+		    }
+		    
+		    // For extension callbacks
     		    cblist = list->callbacks;
 		    while (cblist) {
 			cblist->callback(list->exten->parent->name, list->exten->exten, state, cblist->data);
 			cblist = cblist->next;
 		    }
+		    
 		    list->laststate = state;
 		}
 		break;
@@ -1234,24 +1251,60 @@ int ast_device_state_changed(char *device)
 	list = list->next;
     }
 
-    pthread_mutex_unlock(&notifylock);
+    pthread_mutex_unlock(&hintlock);
     return 1;
 }
 			
 int ast_extension_state_add(char *context, char *exten, 
-			    ast_notify_cb_type callback, void *data)
+			    ast_state_cb_type callback, void *data)
 {
-    struct ast_notify *list;
-    struct ast_notify_cb *cblist;
+    struct ast_hint *list;
+    struct ast_state_cb *cblist;
     struct ast_exten *e;
 
+    /* No context and extension add callback to statecbs list */
+    if (!context && !exten) {
+	pthread_mutex_lock(&hintlock);
+
+	cblist = statecbs;
+	while (cblist) {
+	    if (cblist->callback == callback) {
+		cblist->data = data;
+		pthread_mutex_unlock(&hintlock);
+	    }
+	    
+	    cblist = cblist->next;
+	}
+	
+	/* Now inserts the callback */
+	cblist = malloc(sizeof(struct ast_state_cb));
+	if (!cblist) {
+	    pthread_mutex_unlock(&hintlock);
+	    return -1;
+	}
+	memset(cblist, 0, sizeof(struct ast_state_cb));
+	cblist->id = 0;
+	cblist->callback = callback;
+	cblist->data = data;
+
+        cblist->next = statecbs;
+	statecbs = cblist;
+
+	pthread_mutex_unlock(&hintlock);
+	return 0;
+    }
+
+    if (!context || !exten)
+	return -1;
+
+    /* This callback type is for only one hint */
     e = ast_hint_extension(NULL, context, exten);    
     if (!e) {
         return -1;
     }
     
-    pthread_mutex_lock(&notifylock);
-    list = notifys;        
+    pthread_mutex_lock(&hintlock);
+    list = hints;        
     
     while (list) {
 	if (list->exten == e)
@@ -1260,135 +1313,188 @@ int ast_extension_state_add(char *context, char *exten,
     }
 
     if (!list) {
-	if (!e) {
-	    pthread_mutex_unlock(&notifylock);
-	    return -1;
-	}
-	list = malloc(sizeof(struct ast_notify));
-	if (!list) {
-	    pthread_mutex_unlock(&notifylock);
-	    return -1;
-	}
-	/* Initialize and insert new item */
-	memset(list, 0, sizeof(struct ast_notify));
-	list->exten = e;
-	list->laststate = ast_extension_state2(e);
-	list->next = notifys;
-	notifys = list;
+	pthread_mutex_unlock(&hintlock);
+	return -1;
     }
-    
+
     /* Now inserts the callback */
-    cblist = malloc(sizeof(struct ast_notify_cb));
+    cblist = malloc(sizeof(struct ast_state_cb));
     if (!cblist) {
-	pthread_mutex_unlock(&notifylock);
+	pthread_mutex_unlock(&hintlock);
 	return -1;
     }
-    memset(cblist, 0, sizeof(struct ast_notify_cb));
-    cblist->id = notifycnt++;
+    memset(cblist, 0, sizeof(struct ast_state_cb));
+    cblist->id = stateid++;
     cblist->callback = callback;
     cblist->data = data;
 
     cblist->next = list->callbacks;
     list->callbacks = cblist;
 
-    pthread_mutex_unlock(&notifylock);
+    pthread_mutex_unlock(&hintlock);
     return cblist->id;
 }
 
-static int ast_extension_state_clean(struct ast_exten *e)
+int ast_extension_state_del(int id, ast_state_cb_type callback)
+{
+    struct ast_hint *list;
+    struct ast_state_cb *cblist, *cbprev;
+    
+    if (!id && !callback)
+	return -1;
+            
+    pthread_mutex_lock(&hintlock);
+
+    /* id is zero is a callback without extension */
+    if (!id) {
+	cbprev = NULL;
+	cblist = statecbs;
+	while (cblist) {
+	    if (cblist->callback == callback) {
+		if (!cbprev)
+		    statecbs = cblist->next;
+		else
+		    cbprev->next = cblist->next;
+
+		free(cblist);
+
+	        pthread_mutex_unlock(&hintlock);
+		return 0;
+	    }
+	    cbprev = cblist;
+	    cblist = cblist->next;
+	}
+
+        pthread_mutex_lock(&hintlock);
+	return -1;
+    }
+
+    /* id greater zero is a callback with extension */
+    list = hints;
+    while (list) {
+	cblist = list->callbacks;
+	cbprev = NULL;
+	while (cblist) {
+	    if (cblist->id==id) {
+		if (!cbprev)
+		    list->callbacks = cblist->next;		
+		else
+		    cbprev->next = cblist->next;
+		
+		free(cblist);
+		
+		pthread_mutex_unlock(&hintlock);
+		return 0;		
+	    }		
+    	    cbprev = cblist;				
+	    cblist = cblist->next;
+	}
+	list = list->next;
+    }
+    
+    pthread_mutex_unlock(&hintlock);
+    return -1;
+}
+
+static int ast_add_hint(struct ast_exten *e)
+{
+    struct ast_hint *list;
+
+    if (!e) return -1;
+    
+    pthread_mutex_lock(&hintlock);
+    list = hints;        
+    
+    /* Search if hint exists, do nothing */
+    while (list) {
+	if (list->exten == e) {
+	    pthread_mutex_unlock(&hintlock);
+	    return -1;
+	}
+	list = list->next;    
+    }
+
+    list = malloc(sizeof(struct ast_hint));
+    if (!list) {
+	pthread_mutex_unlock(&hintlock);
+	return -1;
+    }
+    /* Initialize and insert new item */
+    memset(list, 0, sizeof(struct ast_hint));
+    list->exten = e;
+    list->laststate = ast_extension_state2(e);
+    list->next = hints;
+    hints = list;
+
+    pthread_mutex_unlock(&hintlock);
+    return 0;
+}
+
+static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
+{ 
+    struct ast_hint *list;
+
+    pthread_mutex_lock(&hintlock);
+    
+    list = hints;
+    
+    while(list) {
+	if (list->exten == oe) {
+	    list->exten = ne;
+	    pthread_mutex_unlock(&hintlock);	
+	    return 0;
+	}
+	list = list->next;
+    }
+    pthread_mutex_unlock(&hintlock);
+
+    return -1;
+}
+
+static int ast_remove_hint(struct ast_exten *e)
 {
     /* Cleanup the Notifys if hint is removed */
-    struct ast_notify *list, *prev = NULL;
-    struct ast_notify_cb *cblist, *cbprev;
+    struct ast_hint *list, *prev = NULL;
+    struct ast_state_cb *cblist, *cbprev;
+
+    if (!e) 
+	return -1;
 
-    pthread_mutex_lock(&notifylock);
+    pthread_mutex_lock(&hintlock);
 
-    list = notifys;    
+    list = hints;    
     while(list) {
 	if (list->exten==e) {
 	    cbprev = NULL;
 	    cblist = list->callbacks;
-	    while (cblist) {	    
+	    while (cblist) {
+		/* Notify with -1 and remove all callbacks */
 		cbprev = cblist;	    
 		cblist = cblist->next;
-		cblist->callback(list->exten->parent->name, list->exten->exten, -1, cblist->data);
+		cbprev->callback(list->exten->parent->name, list->exten->exten, -1, cbprev->data);
 		free(cbprev);
 	    }
 	    list->callbacks = NULL;
 
-	    if (!prev) {
-		notifys = list->next;
-		free(list);
-		list = notifys;
-	    } else {
+	    if (!prev)
+		hints = list->next;
+	    else
 		prev->next = list->next;
-		free(list);
-		list = prev->next;
-	    }
+
+	    free(list);
+	    
+	    pthread_mutex_unlock(&hintlock);
+	    return 0;
 	} else {
 	    prev = list;
     	    list = list->next;    
 	}
     }
 
-    pthread_mutex_unlock(&notifylock);
-    return 1;
+    pthread_mutex_unlock(&hintlock);
+    return -1;
 }
 
-int ast_extension_state_del(int id)
-{
-    struct ast_notify *list, *prev = NULL;
-    struct ast_notify_cb *cblist, *cbprev;
-    int res = -1;
-            
-    pthread_mutex_lock(&notifylock);
-
-    list = notifys;
-    while (list) {
-	cblist = list->callbacks;
-	cbprev = NULL;
-	while (cblist) {
-	    if (cblist->id==id) {
-		if (!cbprev) {
-		    list->callbacks = cblist->next;		
-		    free(cblist);
-		    cblist = list->callbacks;
-		} else {
-		    cbprev->next = cblist->next;
-		    free(cblist);
-		    cblist = cbprev->next;
-		}
-		
-		if (!list->callbacks) {
-		    if (!prev) {
-			notifys = list->next;
-			free(list);
-			list = notifys;
-		    } else {
-			prev->next = list->next;
-			free(list);
-			list = prev->next;
-		    }
-		}
-		res = 0;
-		break;
-	    } else {
-    		cbprev = cblist;				
-		cblist = cblist->next;
-	    }
-	}
-
-	// we can have only one item
-	if (cblist || !list)
-	    break;	    
-	    
-	prev = list;
-	list = list->next;
-    }
-    
-    pthread_mutex_unlock(&notifylock);
-    return res;
-}
 
 int ast_get_hint(char *hint, int maxlen, struct ast_channel *c, char *context, char *exten)
 {
@@ -1867,7 +1973,7 @@ int ast_context_remove_extension2(struct ast_context *con, char *extension, int
 					exten = peer->peer;
 					
 					if (!peer->priority==PRIORITY_HINT) 
-					    ast_extension_state_clean(peer);
+					    ast_remove_hint(peer);
 
 					peer->datad(peer->data);
 					free(peer);
@@ -1914,7 +2020,7 @@ int ast_context_remove_extension2(struct ast_context *con, char *extension, int
 
 						/* now, free whole priority extension */
 						if (peer->priority==PRIORITY_HINT)
-						    ast_extension_state_clean(peer);
+						    ast_remove_hint(peer);
 						peer->datad(peer->data);
 						free(peer);
 
@@ -3337,10 +3443,14 @@ int ast_add_extension2(struct ast_context *con,
 							tmp->next = e->next;
 							tmp->peer = e->peer;
 						}
+						if (tmp->priority == PRIORITY_HINT)
+						    ast_change_hint(e,tmp);
 						/* Destroy the old one */
 						e->datad(e->data);
 						free(e);
 						ast_pthread_mutex_unlock(&con->lock);
+						if (tmp->priority == PRIORITY_HINT)
+						    ast_change_hint(e, tmp);
 						/* And immediately return success. */
 						LOG;
 						return 0;
@@ -3373,6 +3483,9 @@ int ast_add_extension2(struct ast_context *con,
 					}
 					ast_pthread_mutex_unlock(&con->lock);
 					/* And immediately return success. */
+					if (tmp->priority == PRIORITY_HINT)
+					    ast_add_hint(tmp);
+					
 					LOG;
 					return 0;
 				}
@@ -3383,6 +3496,9 @@ int ast_add_extension2(struct ast_context *con,
 			   ep *must* be defined or we couldn't have gotten here. */
 			ep->peer = tmp;
 			ast_pthread_mutex_unlock(&con->lock);
+			if (tmp->priority == PRIORITY_HINT)
+			    ast_add_hint(tmp);
+			
 			/* And immediately return success. */
 			LOG;
 			return 0;
@@ -3399,6 +3515,9 @@ int ast_add_extension2(struct ast_context *con,
 				con->root = tmp;
 			}
 			ast_pthread_mutex_unlock(&con->lock);
+			if (tmp->priority == PRIORITY_HINT)
+			    ast_add_hint(tmp);
+
 			/* And immediately return success. */
 			LOG;
 			return 0;
@@ -3413,6 +3532,8 @@ int ast_add_extension2(struct ast_context *con,
 	else
 		con->root = tmp;
 	ast_pthread_mutex_unlock(&con->lock);
+	if (tmp->priority == PRIORITY_HINT)
+	    ast_add_hint(tmp);
 	LOG;
 	return 0;	
 }
@@ -3647,11 +3768,19 @@ int ast_pbx_outgoing_app(char *type, int format, void *data, int timeout, char *
 	return res;
 }
 
+static void destroy_exten(struct ast_exten *e)
+{
+	if (e->datad)
+		e->datad(e->data);
+	free(e);
+}
+
 void ast_context_destroy(struct ast_context *con, char *registrar)
 {
 	struct ast_context *tmp, *tmpl=NULL;
 	struct ast_include *tmpi, *tmpil= NULL;
 	struct ast_sw *sw, *swl= NULL;
+	struct ast_exten *e, *el, *en;
 	ast_pthread_mutex_lock(&conlock);
 	tmp = contexts;
 	while(tmp) {
@@ -3683,6 +3812,21 @@ void ast_context_destroy(struct ast_context *con, char *registrar)
 				free(swl);
 				swl = sw;
 			}
+			for (e = tmp->root; e; ) {
+			    if (e->priority == PRIORITY_HINT)
+				ast_remove_hint(e);
+			    e = e->next;
+			}
+			for (e = tmp->root; e;) {
+				for (en = e->peer; en;) {
+					el = en;
+					en = en->peer;
+					destroy_exten(el);
+				}
+				el = e;
+				e = e->next;
+				destroy_exten(el);
+			}
 			free(tmp);
 			if (!con) {
 				/* Might need to get another one -- restart */
diff --git a/pbx/pbx_config.c b/pbx/pbx_config.c
index b957f29e4248c67e07c7765b78b5c31ea06ec2b0..baed0f9fe92264972040aabd1ca6f393912fd5e9 100755
--- a/pbx/pbx_config.c
+++ b/pbx/pbx_config.c
@@ -1084,13 +1084,15 @@ static int handle_context_add_extension(int fd, int argc, char *argv[])
 		cidmatch = NULL;
 	}
 	prior       = strsep(&whole_exten,",");
-	if (!strcmp(prior, "hint")) {
-	    iprior = PRIORITY_HINT;
-	} else {
-	    iprior = atoi(prior);
+	if (prior) {
+    		if (!strcmp(prior, "hint")) {
+			iprior = PRIORITY_HINT;
+		} else {
+			iprior = atoi(prior);
+		}
 	}
 	app         = strsep(&whole_exten,",");
-	if ((start = strchr(app, '(')) && (end = strrchr(app, ')'))) {
+	if (app && (start = strchr(app, '(')) && (end = strrchr(app, ')'))) {
 		*start = *end = '\0';
 		app_data = start + 1;
 		for (start = app_data; *start; start++)