Index: pbx.c
===================================================================
RCS file: /usr/cvsroot/asterisk/pbx.c,v
retrieving revision 1.266
diff -u -r1.266 pbx.c
--- pbx.c	22 Aug 2005 18:47:19 -0000	1.266
+++ pbx.c	23 Aug 2005 16:41:52 -0000
@@ -1738,7 +1738,7 @@
 	char *cur, *rest;
 	int res = -1;
 	int allunavailable = 1, allbusy = 1, allfree = 1;
-	int busy = 0;
+	int busy = 0, inuse = 0, ring = 0;
 
 	if (!e)
 		return -1;
@@ -1749,7 +1749,7 @@
 	do {
 		rest = strchr(cur, '&');
 		if (rest) {
-	    		*rest = 0;
+			*rest = 0;
 			rest++;
 		}
 	
@@ -1760,7 +1760,15 @@
 			allbusy = 0;
 			break;
 		case AST_DEVICE_INUSE:
-			return AST_EXTENSION_INUSE;
+			inuse = 1;
+			allunavailable = 0;
+			allfree = 0;
+			break;
+		case AST_DEVICE_RINGING:
+			ring = 1;
+			allunavailable = 0;
+			allfree = 0;
+			break;
 		case AST_DEVICE_BUSY:
 			allunavailable = 0;
 			allfree = 0;
@@ -1779,7 +1787,13 @@
 		cur = rest;
 	} while (cur);
 
-	if (allfree)			
+	if (!inuse && ring)
+		return AST_EXTENSION_RINGING;
+	if (inuse && ring)
+		return (AST_EXTENSION_INUSE | AST_EXTENSION_RINGING);
+	if (inuse)
+		return AST_EXTENSION_INUSE;
+	if (allfree)
 		return AST_EXTENSION_NOT_INUSE;
 	if (allbusy)		
 		return AST_EXTENSION_BUSY;
Index: channels/chan_sip.c
===================================================================
RCS file: /usr/cvsroot/asterisk/channels/chan_sip.c,v
retrieving revision 1.813
diff -u -r1.813 chan_sip.c
--- channels/chan_sip.c	23 Aug 2005 14:41:51 -0000	1.813
+++ channels/chan_sip.c	23 Aug 2005 16:41:56 -0000
@@ -281,6 +281,7 @@
 
 #define DEFAULT_CONTEXT "default"
 static char default_context[AST_MAX_CONTEXT] = DEFAULT_CONTEXT;
+static char default_subscribecontext[AST_MAX_CONTEXT];
 
 #define DEFAULT_VMEXTEN "asterisk"
 static char global_vmexten[AST_MAX_EXTENSION] = DEFAULT_VMEXTEN;
@@ -295,6 +296,7 @@
 #define DEFAULT_NOTIFYMIME "application/simple-message-summary"
 static char default_notifymime[AST_MAX_EXTENSION] = DEFAULT_NOTIFYMIME;
 
+static int global_notifyringing = 1;	/* Send notifications on ringing */
 
 static int default_qualify = 0;		/* Default Qualify= setting */
 
@@ -541,6 +543,7 @@
 	char from[256];				/* The From: header */
 	char useragent[256];			/* User agent in SIP request */
 	char context[AST_MAX_CONTEXT];		/* Context for this call */
+	char subscribecontext[AST_MAX_CONTEXT];	/* Subscribecontext */
 	char fromdomain[MAXHOSTNAMELEN];	/* Domain to show in the from field */
 	char fromuser[AST_MAX_EXTENSION];	/* User to show in the user field */
 	char fromname[AST_MAX_EXTENSION];	/* Name to show in the user field */
@@ -587,8 +590,10 @@
 	int rtpholdtimeout;			/* RTP timeout when on hold */
 	int rtpkeepalive;			/* Send RTP packets for keepalive */
 
-	int subscribed;				/* Is this call a subscription?  */
+	/* Is this call a subscription?  */
+	enum subscriptionstate { NONE, XPIDF_XML, DIALOG_INFO_XML, CPIM_PIDF_XML, PIDF_XML,	} subscribed;
 	int stateid;
+	int laststate;                          /* Last known state */
 	int dialogver;
 	
 	struct ast_dsp *vad;			/* Voice Activation Detection dsp */
@@ -628,6 +633,7 @@
 	char secret[80];		/* Password */
 	char md5secret[80];		/* Password in md5 */
 	char context[AST_MAX_CONTEXT];	/* Default context for incoming calls */
+	char subscribecontext[AST_MAX_CONTEXT];	/* Default context for subscriptions */
 	char cid_num[80];		/* Caller ID num */
 	char cid_name[80];		/* Caller ID name */
 	char accountcode[AST_MAX_ACCOUNT_CODE];	/* Account code */
@@ -659,6 +665,7 @@
 	char md5secret[80];		/* Password in MD5 */
 	struct sip_auth *auth;		/* Realm authentication list */
 	char context[AST_MAX_CONTEXT];	/* Default context for incoming calls */
+	char subscribecontext[AST_MAX_CONTEXT];	/* Default context for subscriptions */
 	char username[80];		/* Temporary username until registration */ 
 	char accountcode[AST_MAX_ACCOUNT_CODE];	/* Account code */
 	int amaflags;			/* AMA Flags (for billing) */
@@ -2898,6 +2905,7 @@
 	p->method = intended_method;
 	p->initid = -1;
 	p->autokillid = -1;
+	p->subscribed = NONE;
 	p->stateid = -1;
 	p->prefs = prefs;
 #ifdef OSP_SUPPORT
@@ -3811,7 +3819,7 @@
 	return 0;
 }
 
-/*--- reqprep: Initialize a SIP request packet ---*/
+/*--- reqprep: Initialize a SIP request response packet ---*/
 static int reqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, int seqno, int newbranch)
 {
 	struct sip_request *orig = &p->initreq;
@@ -4555,65 +4563,138 @@
 	char *t = tmp, *c, *a, *mfrom, *mto;
 	size_t maxbytes = sizeof(tmp);
 	struct sip_request req;
+	char hint[AST_MAX_EXTENSION];
+	char *statestring = "terminated";
+	enum state { NOTIFY_OPEN, NOTIFY_INUSE, NOTIFY_CLOSED, } local_state = NOTIFY_OPEN;
+	char *pidfstate = "--";
+	char *pidfnote= "Ready";
+
+	switch (state) {
+	case (AST_EXTENSION_RINGING | AST_EXTENSION_INUSE):
+		if (global_notifyringing)
+			statestring = "early";
+		else
+			statestring = "confirmed";
+		local_state = NOTIFY_INUSE;
+		pidfstate = "busy";
+		pidfnote = "Ringing";
+		break;
+	case AST_EXTENSION_RINGING:
+		statestring = "early";
+		local_state = NOTIFY_INUSE;
+		pidfstate = "busy";
+		pidfnote = "Ringing";
+		break;
+	case AST_EXTENSION_INUSE:
+		statestring = "confirmed";
+		local_state = NOTIFY_INUSE;
+		pidfstate = "busy";
+		pidfnote = "On the phone";
+		break;
+	case AST_EXTENSION_BUSY:
+		statestring = "confirmed";
+		local_state = NOTIFY_CLOSED;
+		pidfstate = "busy";
+		pidfnote = "On the phone";
+		break;
+	case AST_EXTENSION_UNAVAILABLE:
+		statestring = "confirmed";
+		local_state = NOTIFY_CLOSED;
+		pidfstate = "away";
+		pidfnote = "Unavailable";
+		break;
+	case AST_EXTENSION_NOT_INUSE:
+	default:
+		/* Default setting */
+		break;
+	}
+	
+	/* Check which device/devices we are watching  and if they are registered */
+	if (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, p->context, p->exten)) {
+		/* If they are not registered, we will override notification and show no availability */
+		if (ast_device_state(hint) == AST_DEVICE_UNAVAILABLE) {
+			local_state = NOTIFY_CLOSED;
+			pidfstate = "away";
+			pidfnote = "Not online";
+		}
+	}
 
 	memset(from, 0, sizeof(from));
-	memset(to, 0, sizeof(to));
 	ast_copy_string(from, get_header(&p->initreq, "From"), sizeof(from));
-
 	c = get_in_brackets(from);
 	if (strncmp(c, "sip:", 4)) {
 		ast_log(LOG_WARNING, "Huh?  Not a SIP header (%s)?\n", c);
 		return -1;
 	}
-	if ((a = strchr(c, ';'))) {
+	if ((a = strchr(c, ';')))
 		*a = '\0';
-	}
 	mfrom = c;
 
-	reqprep(&req, p, SIP_NOTIFY, 0, 1);
-
-	if (p->subscribed == 1) {
-		ast_copy_string(to, get_header(&p->initreq, "To"), sizeof(to));
+	memset(to, 0, sizeof(to));
+	ast_copy_string(to, get_header(&p->initreq, "To"), sizeof(to));
+	c = get_in_brackets(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;
 
-		c = get_in_brackets(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;
+	reqprep(&req, p, SIP_NOTIFY, 0, 1);
 
+	switch (p->subscribed) {
+	case XPIDF_XML:
+	case CPIM_PIDF_XML:
 		add_header(&req, "Event", "presence");
 		add_header(&req, "Subscription-State", "active");
 		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;
-
 		ast_build_string(&t, &maxbytes, "\n");
 		ast_build_string(&t, &maxbytes, "\n");
 		ast_build_string(&t, &maxbytes, "\n");
 		ast_build_string(&t, &maxbytes, "\n", mfrom);
 		ast_build_string(&t, &maxbytes, "\n", p->exten);
 		ast_build_string(&t, &maxbytes, "\n", mto);
-		ast_build_string(&t, &maxbytes, "\n", !state ? "open" : (state==1) ? "inuse" : "closed");
-		ast_build_string(&t, &maxbytes, "\n", !state ? "online" : (state==1) ? "onthephone" : "offline");
+		ast_build_string(&t, &maxbytes, "\n", (local_state ==  NOTIFY_OPEN) ? "open" : (local_state == NOTIFY_INUSE) ? "inuse" : "closed");
+		ast_build_string(&t, &maxbytes, "\n", (local_state == NOTIFY_OPEN) ? "online" : (local_state == NOTIFY_INUSE) ? "onthephone" : "offline");
 		ast_build_string(&t, &maxbytes, "\n\n\n");
-	} else {
+		break;
+	case PIDF_XML: /* Eyebeam supports this format */
+		add_header(&req, "Event", "presence");
+		add_header(&req, "Subscription-State", "active");
+		add_header(&req, "Content-Type", "application/pidf+xml");
+		ast_build_string(&t, &maxbytes, "\n");
+		ast_build_string(&t, &maxbytes, "\n", mfrom);
+		ast_build_string(&t, &maxbytes, "\n");
+		if (pidfstate[0] != '-')
+			ast_build_string(&t, &maxbytes, "%s\n", pidfstate);
+		ast_build_string(&t, &maxbytes, "\n");
+		ast_build_string(&t, &maxbytes, "%s\n", pidfnote); /* Note */
+		ast_build_string(&t, &maxbytes, "\n", p->exten); /* Tuple start */
+		ast_build_string(&t, &maxbytes, "%s\n", mto);
+		if (pidfstate[0] == 'b') /* Busy? Still open ... */
+			ast_build_string(&t, &maxbytes, "open\n");
+		else
+			ast_build_string(&t, &maxbytes, "%s\n", (local_state != NOTIFY_CLOSED) ? "open" : "closed");
+		ast_build_string(&t, &maxbytes, "\n\n");
+		break;
+	case DIALOG_INFO_XML: /* SNOM subscribes in this format */
 		add_header(&req, "Event", "dialog");
 		add_header(&req, "Content-Type", "application/dialog-info+xml");
 		ast_build_string(&t, &maxbytes, "\n");
-		ast_build_string(&t, &maxbytes, "\n", p->dialogver++, full ? "full":"partial", mfrom);
-		ast_build_string(&t, &maxbytes, "\n\n");	
+		ast_build_string(&t, &maxbytes, "\n", p->dialogver++, full ? "full":"partial", mto);
+		if ((state & AST_EXTENSION_RINGING) && global_notifyringing)
+			ast_build_string(&t, &maxbytes, "\n");
+		break;
+	case NONE:
+	default:
+		break;
 	}
+
 	if (t > tmp + sizeof(tmp))
 		ast_log(LOG_WARNING, "Buffer overflow detected!!  (Please file a bug report)\n");
 
@@ -5537,7 +5618,7 @@
 #endif
 		)
 		return 0;
-	if (sipmethod == SIP_REGISTER) {
+	if (sipmethod == SIP_REGISTER || sipmethod == SIP_SUBSCRIBE) {
 		/* On a REGISTER, we have to use 401 and its family of headers instead of 407 and its family
 		   of headers -- GO SIP!  Whoo hoo!  Two things that do the same thing but are used in
 		   different circumstances! What a surprise. */
@@ -5721,10 +5802,13 @@
 	return res;
 }
 
-/*--- cb_extensionstate: Part of thte SUBSCRIBE support subsystem ---*/
+/*--- cb_extensionstate: Callback for the devicestate notification (SUBSCRIBE) support subsystem ---*/
+/*	If you add an "hint" priority to the extension in the dial plan,
+	you will get notifications on device state changes */
 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;
@@ -5732,6 +5816,7 @@
 	}
  
 	transmit_state_notify(p, state, 1);
+	p->laststate = state;
 
 	if (option_debug > 1)
 		ast_verbose(VERBOSE_PREFIX_1 "Extension Changed %s new state %d for Notify User %s\n", exten, state, p->username);
@@ -6362,6 +6447,7 @@
 				ast_copy_string(p->cid_name, user->cid_name, sizeof(p->cid_name));
 			ast_copy_string(p->username, user->name, sizeof(p->username));
 			ast_copy_string(p->peersecret, user->secret, sizeof(p->peersecret));
+			ast_copy_string(p->subscribecontext, user->subscribecontext, sizeof(p->subscribecontext));
 			ast_copy_string(p->peermd5secret, user->md5secret, sizeof(p->peermd5secret));
 			ast_copy_string(p->accountcode, user->accountcode, sizeof(p->accountcode));
 			ast_copy_string(p->language, user->language, sizeof(p->language));
@@ -6424,6 +6510,7 @@
 			}
 			ast_copy_string(p->peersecret, peer->secret, sizeof(p->peersecret));
 			p->peersecret[sizeof(p->peersecret)-1] = '\0';
+			ast_copy_string(p->subscribecontext, peer->subscribecontext, sizeof(p->subscribecontext));
 			ast_copy_string(p->peermd5secret, peer->md5secret, sizeof(p->peermd5secret));
 			p->peermd5secret[sizeof(p->peermd5secret)-1] = '\0';
 			p->callingpres = peer->callingpres;
@@ -6530,16 +6617,28 @@
 
                 
 /*--- receive_message: Receive SIP MESSAGE method messages ---*/
-/*   we handle messages within current calls currently */
+/*	We only handle messages within current calls currently */
+/*	Reference: RFC 3428 */
 static void receive_message(struct sip_pvt *p, struct sip_request *req)
 {
 	char buf[1024];
 	struct ast_frame f;
+	char *content_type;
+
+	content_type = get_header(req, "Content-Type");
+	if (strcmp(content_type, "text/plain")) { /* No text/plain attachment */
+		transmit_response(p, "415 Unsupported Media Type", req); /* Good enough, or? */
+		ast_set_flag(p, SIP_NEEDDESTROY);
+		return;
+	}
 
 	if (get_msg_text(buf, sizeof(buf), req)) {
 		ast_log(LOG_WARNING, "Unable to retrieve text from %s\n", p->callid);
+		transmit_response(p, "202 Accepted", req);
+		ast_set_flag(p, SIP_NEEDDESTROY);
 		return;
 	}
+
 	if (p->owner) {
 		if (sip_debug_test_pvt(p))
 			ast_verbose("Message received: '%s'\n", buf);
@@ -6550,7 +6649,13 @@
 		f.data = buf;
 		f.datalen = strlen(buf);
 		ast_queue_frame(p->owner, &f);
+		transmit_response(p, "202 Accepted", req); /* We respond 202 accepted, since we relay the message */
+	} else { /* Message outside of a call, we do not support that */
+		ast_log(LOG_WARNING,"Received message to %s from %s, dropped it...\n  Content-Type:%s\n  Message: %s\n", get_header(req,"To"), get_header(req,"From"), content_type, buf);
+		transmit_response(p, "405 Method Not Allowed", req); /* Good enough, or? */
 	}
+	ast_set_flag(p, SIP_NEEDDESTROY);
+	return;
 }
 
 /*--- sip_show_inuse: CLI Command to show calls within limits set by 
@@ -7512,7 +7617,7 @@
 
 static int __sip_show_channels(int fd, int argc, char *argv[], int subscriptions)
 {
-#define FORMAT3 "%-15.15s  %-10.10s  %-21.21s  %-15.15s\n"
+#define FORMAT3 "%-15.15s  %-10.10s  %-21.21s  %-15.15s %10s\n"
 #define FORMAT2 "%-15.15s  %-10.10s  %-11.11s  %-11.11s  %-4.4s   %-7.7s	%s \n"
 #define FORMAT  "%-15.15s  %-10.10s  %-11.11s  %5.5d/%5.5d %-4.4s  %-7.7s%s %s\n"
 	struct sip_pvt *cur;
@@ -7525,9 +7630,9 @@
 	if (!subscriptions)
 		ast_cli(fd, FORMAT2, "Peer", "User/ANR", "Call ID", "Seq (Tx/Rx)", "Format", "Hold", "Last Msg");
 	else
-        	ast_cli(fd, FORMAT3, "Peer", "User", "Call ID", "URI");
+		ast_cli(fd, FORMAT3, "Peer", "User", "Call ID", "Extension", "Last state");
 	while (cur) {
-		if (!cur->subscribed && !subscriptions) {
+		if (cur->subscribed == NONE && !subscriptions) {
 			ast_cli(fd, FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), cur->sa.sin_addr), 
 				ast_strlen_zero(cur->username) ? ( ast_strlen_zero(cur->cid_num) ? "(None)" : cur->cid_num ) : cur->username, 
 				cur->callid, 
@@ -7538,19 +7643,19 @@
 				cur->lastmsg );
 			numchans++;
 		}
-		if (cur->subscribed && subscriptions) {
-                	ast_cli(fd, FORMAT3, ast_inet_ntoa(iabuf, sizeof(iabuf), cur->sa.sin_addr),
-				ast_strlen_zero(cur->username) ? ( ast_strlen_zero(cur->cid_num) ? "(None)" : cur->cid_num ) : cur->username, 
-                        	cur->callid, cur->uri);
-
-                }
+		if (cur->subscribed != NONE && subscriptions) {
+			ast_cli(fd, FORMAT3, ast_inet_ntoa(iabuf, sizeof(iabuf), cur->sa.sin_addr),
+			        ast_strlen_zero(cur->username) ? ( ast_strlen_zero(cur->cid_num) ? "(None)" : cur->cid_num ) : cur->username, 
+			        cur->callid, cur->exten, ast_state2str(cur->laststate));
+			numchans++;
+		}
 		cur = cur->next;
 	}
 	ast_mutex_unlock(&iflock);
 	if (!subscriptions)
-		ast_cli(fd, "%d active SIP channel(s)\n", numchans);
+		ast_cli(fd, "%d active SIP channel%s\n", numchans, (numchans != 1) ? "s" : "");
 	else
-		ast_cli(fd, "%d active SIP subscriptions(s)\n", numchans);
+		ast_cli(fd, "%d active SIP subscription%s\n", numchans, (numchans != 1) ? "s" : "");
 	return RESULT_SUCCESS;
 #undef FORMAT
 #undef FORMAT2
@@ -7712,7 +7817,7 @@
 	while(cur) {
 		if (!strncasecmp(cur->callid, argv[3],len)) {
 			ast_cli(fd,"\n");
-			if (cur->subscribed)
+			if (cur->subscribed != NONE)
 				ast_cli(fd, "  * Subscription\n");
 			else
 				ast_cli(fd, "  * SIP Call\n");
@@ -7782,7 +7887,7 @@
 	while(cur) {
 		if (!strncasecmp(cur->callid, argv[3], len)) {
 			ast_cli(fd,"\n");
-			if (cur->subscribed)
+			if (cur->subscribed != NONE)
 				ast_cli(fd, "  * Subscription\n");
 			else
 				ast_cli(fd, "  * SIP Call\n");
@@ -8876,7 +8981,7 @@
 					ast_log(LOG_WARNING, "Notify answer on an owned channel?\n");
 					ast_queue_hangup(p->owner);
 				} else {
-					if (!p->subscribed) {
+					if (p->subscribed == NONE) {
 						ast_set_flag(p, SIP_NEEDDESTROY); 
 					}
 				}
@@ -8898,6 +9003,7 @@
 						time(&p->ospstart);
 #endif
 						ast_queue_control(p->owner, AST_CONTROL_ANSWER);
+						ast_setstate(p->owner, AST_STATE_UP);
 					} else {
 						struct ast_frame af = { AST_FRAME_NULL, };
 						ast_queue_frame(p->owner, &af);
@@ -8906,6 +9012,7 @@
 						  by sending CANCEL */
 					ast_set_flag(p, SIP_PENDINGBYE);	
 				p->authtries = 0;
+				ast_device_state_changed("SIP/%s", p->peername);
 				/* If I understand this right, the branch is different for a non-200 ACK only */
 				transmit_request(p, SIP_ACK, seqno, 0, 1);
 				check_pendings(p);
@@ -9668,16 +9775,12 @@
 /*--- handle_request_message: Handle incoming MESSAGE request ---*/
 static int handle_request_message(struct sip_pvt *p, struct sip_request *req, int debug, int ignore)
 {
-	if (p->lastinvite) {
-		if (!ignore) {
-			if (debug)
-				ast_verbose("Receiving message!\n");
-			receive_message(p, req);
-		}
-		transmit_response(p, "200 OK", req);
+	if (!ignore) {
+		if (debug)
+			ast_verbose("Receiving message!\n");
+		receive_message(p, req);
 	} else {
-		transmit_response(p, "405 Method Not Allowed", req);
-		ast_set_flag(p, SIP_NEEDDESTROY);
+		transmit_response(p, "202 Accepted", req);
 	}
 	return 1;
 }
@@ -9713,7 +9816,9 @@
 			return 0;
 		}
 		/* Initialize the context if it hasn't been already */
-		if (ast_strlen_zero(p->context))
+		if (p->subscribecontext && !ast_strlen_zero(p->subscribecontext))
+			ast_copy_string(p->context, p->subscribecontext, sizeof(p->context));
+		else if (ast_strlen_zero(p->context))
 			strcpy(p->context, default_context);
 		/* Get destination right away */
 		gotdest = get_destination(p, NULL);
@@ -9722,15 +9827,36 @@
 			if (gotdest < 0)
 				transmit_response(p, "404 Not Found", req);
 			else
-				transmit_response(p, "484 Address Incomplete", req);
+				transmit_response(p, "484 Address Incomplete", req);	/* Overlap dialing on SUBSCRIBE?? */
 			ast_set_flag(p, SIP_NEEDDESTROY);	
 		} else {
+			char *event = get_header(req, "Event");	/* Get Event package name */
+			char *accept = get_header(req, "Accept");
+
 			/* Initialize tag */	
 			p->tag = rand();
-			if (!strcmp(get_header(req, "Accept"), "application/dialog-info+xml"))
-				p->subscribed = 2;
-			else if (!strcmp(get_header(req, "Accept"), "application/simple-message-summary")) {
-				/* Looks like they actually want a mailbox */
+			if (!strcmp(event, "presence") || !strcmp(event, "dialog")) { /* Presence, RFC 3842 */
+ 				/* Header from Xten Eye-beam Accept: multipart/related, application/rlmi+xml, application/pidf+xml, application/xpidf+xml */
+ 				if (strstr(accept, "application/dialog-info+xml")) {
+ 					p->subscribed = DIALOG_INFO_XML;
+ 					/* IETF draft: draft-ietf-sipping-dialog-package-05.txt */
+ 					/* Should not be used for SUBSCRIBE, but anyway */
+ 				} else if (strstr(accept, "application/cpim-pidf+xml")) {
+ 					p->subscribed = CPIM_PIDF_XML;    /* RFC 3863 format */
+ 				} else if (strstr(accept, "application/pidf+xml")) {
+ 					p->subscribed = PIDF_XML;         /* RFC 3863 format */
+ 				} else if (strstr(accept, "application/xpidf+xml")) {
+ 					p->subscribed = XPIDF_XML;        /* Early pre-RFC 3863 format with MSN additions (Microsoft Messenger) */
+ 				} else if (strstr(p->useragent, "Polycom")) {
+ 					p->subscribed = XPIDF_XML;        /*  Polycoms subscribe for "event: dialog" but don't include an "accept:" header */
+				} else {
+ 					/* Can't find a format for events that we know about */
+ 					transmit_response(p, "489 Bad Event", req);
+ 					ast_set_flag(p, SIP_NEEDDESTROY);	
+ 					return 0;
+ 				}
+ 			} else if (!strcmp(event, "message-summary") && !strcmp(accept, "application/simple-message-summary")) {
+				/* Looks like they actually want a mailbox status */
 
 				/* At this point, we should check if they subscribe to a mailbox that
 				  has the same extension as the peer or the mailbox id. If we configure
@@ -9756,9 +9882,14 @@
 					ast_set_flag(p, SIP_NEEDDESTROY);	
 				}
 				return 0;
-			} else
-				p->subscribed = 1;
-			if (p->subscribed)
+			} else { /* At this point, Asterisk does not understand the specified event */
+				transmit_response(p, "489 Bad Event", req);
+				if (option_debug > 1)
+					ast_log(LOG_DEBUG, "Received SIP subscribe for unknown event package: %s\n", event);
+				ast_set_flag(p, SIP_NEEDDESTROY);	
+				return 0;
+			}
+			if (p->subscribed != NONE)
 				p->stateid = ast_extension_state_add(p->context, p->exten, cb_extensionstate, p);
 		}
 	} else 
@@ -9772,9 +9903,10 @@
 			ast_set_flag(p, SIP_NEEDDESTROY);	
 			return 0;
 		}
+		/* TODO: Do we need this any longer? And what exactly to remove? */
 		/* The next line can be removed if the SNOM200 Expires bug is fixed */
-		if (p->subscribed == 1) {  
-			if (p->expiry>max_expiry)
+		if (p->subscribed == DIALOG_INFO_XML) {  
+			if (p->expiry > max_expiry)
 				p->expiry = max_expiry;
 		}
 		/* Go ahead and free RTP port */
@@ -9786,9 +9918,11 @@
 			ast_rtp_destroy(p->vrtp);
 			p->vrtp = NULL;
 		}
+		if (sipdebug || option_debug > 1)
+			ast_log(LOG_DEBUG, "Adding subscription for extension %s context %s for peer %s\n", p->exten, p->context, p->username);
 		transmit_response(p, "200 OK", req);
-		sip_scheddestroy(p, (p->expiry+10)*1000);
-		transmit_state_notify(p, ast_extension_state(NULL, p->context, p->exten),1);
+		sip_scheddestroy(p, (p->expiry + 10) * 1000); /* Set timer for destruction of call at expiration */
+		transmit_state_notify(p, ast_extension_state(NULL, p->context, p->exten),1); /* Send first notification */
 	}
 	return 1;
 }
@@ -10392,13 +10526,27 @@
 	if (p) {
 		found++;
 		res = AST_DEVICE_UNAVAILABLE;
+		if (option_debug > 2) 
+			ast_log(LOG_DEBUG, "Checking device state for peer %s\n", dest);
 		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;
+			/* Peer is registred, or has default IP address and a valid registration */
+			/* Now check if we know anything about the state. The only way is by implementing
+			 * call control with incominglimit=X in sip.conf where X > 0 
+			 * Check if the device has incominglimit, and if qualify=on, if the device
+			 * is reachable */
+			if (p->incominglimit && (p->lastms == 0 || p->lastms <= p->maxms)) { /* Free for a call */
+				res = AST_DEVICE_NOT_INUSE;
+				if (p->inUse) /* On a call */
+					res = AST_DEVICE_BUSY;
+			} else { /* peer found and valid, state unknown */
+				res = AST_DEVICE_UNKNOWN;
+			}
 		}
 	}
 	if (!p && !found) {
+		if (option_debug > 2) 
+			ast_log(LOG_DEBUG, "Checking device state for DNS host %s\n", dest);
 		hp = ast_gethostbyname(host, &ahp);
 		if (hp)
 			res = AST_DEVICE_UNKNOWN;
@@ -10728,6 +10876,8 @@
 
 		if (!strcasecmp(v->name, "context")) {
 			ast_copy_string(user->context, v->value, sizeof(user->context));
+		} else if (!strcasecmp(v->name, "subscribecontext")) {
+			ast_copy_string(user->subscribecontext, v->value, sizeof(user->subscribecontext));
 		} else if (!strcasecmp(v->name, "setvar")) {
 			varname = ast_strdupa(v->value);
 			if (varname && (varval = strchr(varname,'='))) {
@@ -10812,6 +10962,7 @@
 		       SIP_DTMF | SIP_NAT | SIP_REINVITE | SIP_INSECURE_PORT | SIP_INSECURE_INVITE |
 		       SIP_PROG_INBAND | SIP_OSPAUTH);
 	strcpy(peer->context, default_context);
+	strcpy(peer->subscribecontext, default_subscribecontext);
 	strcpy(peer->language, default_language);
 	strcpy(peer->musicclass, global_musicclass);
 	peer->addr.sin_port = htons(DEFAULT_SIP_PORT);
@@ -10853,9 +11004,9 @@
 		/* Already in the list, remove it and it will be added back (or FREE'd)  */
 		found++;
  	} else {
-		peer = malloc(sizeof(struct sip_peer));
+		peer = malloc(sizeof(*peer));
 		if (peer) {
-			memset(peer, 0, sizeof(struct sip_peer));
+			memset(peer, 0, sizeof(*peer));
 			if (realtime)
 				rpeerobjs++;
 			else
@@ -10886,6 +11037,7 @@
 		peer->chanvars = NULL;
 	}
 	strcpy(peer->context, default_context);
+	strcpy(peer->subscribecontext, default_subscribecontext);
 	strcpy(peer->vmexten, global_vmexten);
 	strcpy(peer->language, default_language);
 	strcpy(peer->musicclass, global_musicclass);
@@ -10936,6 +11088,8 @@
 			ast_callerid_split(v->value, peer->cid_name, sizeof(peer->cid_name), peer->cid_num, sizeof(peer->cid_num));
 		} else if (!strcasecmp(v->name, "context")) {
 			ast_copy_string(peer->context, v->value, sizeof(peer->context));
+		} else if (!strcasecmp(v->name, "subscribecontext")) {
+			ast_copy_string(peer->subscribecontext, v->value, sizeof(peer->subscribecontext));
 		} else if (!strcasecmp(v->name, "fromdomain"))
 			ast_copy_string(peer->fromdomain, v->value, sizeof(peer->fromdomain));
 		else if (!strcasecmp(v->name, "usereqphone"))
@@ -11131,6 +11285,7 @@
 
 	/* Initialize some reasonable defaults at SIP reload */
 	ast_copy_string(default_context, DEFAULT_CONTEXT, sizeof(default_context));
+	default_subscribecontext[0] = '\0';
 	default_language[0] = '\0';
 	default_fromdomain[0] = '\0';
 	default_qualify = 0;
@@ -11140,6 +11295,7 @@
 	sipdebug = 0;
 	ast_copy_string(default_useragent, DEFAULT_USERAGENT, sizeof(default_useragent));
 	ast_copy_string(default_notifymime, DEFAULT_NOTIFYMIME, sizeof(default_notifymime));
+	global_notifyringing = 1;
 	ast_copy_string(global_realm, DEFAULT_REALM, sizeof(global_realm));
 	ast_copy_string(global_musicclass, "default", sizeof(global_musicclass));
 	ast_copy_string(default_callerid, DEFAULT_CALLERID, sizeof(default_callerid));
@@ -11234,6 +11390,8 @@
 			compactheaders = ast_true(v->value);
 		} else if (!strcasecmp(v->name, "notifymimetype")) {
 			ast_copy_string(default_notifymime, v->value, sizeof(default_notifymime));
+		} else if (!strcasecmp(v->name, "notifyringing")) {
+			global_notifyringing = ast_true(v->value);
 		} else if (!strcasecmp(v->name, "musicclass") || !strcasecmp(v->name, "musiconhold")) {
 			ast_copy_string(global_musicclass, v->value, sizeof(global_musicclass));
 		} else if (!strcasecmp(v->name, "language")) {
Index: configs/sip.conf.sample
===================================================================
RCS file: /usr/cvsroot/asterisk/configs/sip.conf.sample,v
retrieving revision 1.65
diff -u -r1.65 sip.conf.sample
--- configs/sip.conf.sample	23 Aug 2005 00:50:38 -0000	1.65
+++ configs/sip.conf.sample	23 Aug 2005 16:42:01 -0000
@@ -48,6 +48,9 @@
 ;maxexpirey=3600		; Max length of incoming registration we allow
 ;defaultexpirey=120		; Default length of incoming/outoing registration
 ;notifymimetype=text/plain	; Allow overriding of mime type in MWI NOTIFY
+;notifyringing=no       ; Sent ringing (yes) or inuse (no) as state in
+                        ; NOTIFY messages when observed extension is
+                        ; in state RINGING_AND_INUSE (defaults to "yes")
 ;checkmwi=10			; Default time between mailbox checks for peers
 ;vmexten=voicemail      ; dialplan extension to reach mailbox sets the 
 						; Message-Account in the MWI notify message 
Index: include/asterisk/pbx.h
===================================================================
RCS file: /usr/cvsroot/asterisk/include/asterisk/pbx.h,v
retrieving revision 1.49
diff -u -r1.49 pbx.h
--- include/asterisk/pbx.h	15 Jul 2005 23:24:51 -0000	1.49
+++ include/asterisk/pbx.h	23 Aug 2005 16:42:08 -0000
@@ -34,14 +34,16 @@
 #define PRIORITY_HINT	-1
 
 /*! Extension states */
-/*! No device INUSE or BUSY  */
-#define AST_EXTENSION_NOT_INUSE		0
+/*! No device INUSE, BUSY, UNAVAILABLE or RINGING  */
+#define AST_EXTENSION_NOT_INUSE   0
 /*! One or more devices INUSE */
-#define AST_EXTENSION_INUSE		1
+#define AST_EXTENSION_INUSE       (1 << 0)
 /*! All devices BUSY */
-#define AST_EXTENSION_BUSY		2
+#define AST_EXTENSION_BUSY        (1 << 1)
 /*! All devices UNAVAILABLE/UNREGISTERED */
-#define AST_EXTENSION_UNAVAILABLE 	3
+#define AST_EXTENSION_UNAVAILABLE (1 << 2)
+/*! One or more devices are RINGING */
+#define AST_EXTENSION_RINGING     (1 << 3)
 
 struct ast_context;
 struct ast_exten;