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 24 Aug 2005 17:02:25 -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;
@@ -2428,6 +2442,12 @@
if (option_maxcalls) {
if (countcalls >= option_maxcalls) {
ast_log(LOG_NOTICE, "Maximum call limit of %d calls exceeded by '%s'!\n", option_maxcalls, c->name);
+ manager_event(EVENT_FLAG_CALL, "MaxcallsExceeded",
+ "Callcount: %d\r\n"
+ "Channel: %s\r\n",
+ countcalls,
+ c->name);
+
res = -1;
}
}
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 24 Aug 2005 17:02:25 -0000
@@ -37,11 +37,13 @@
/*! No device INUSE or BUSY */
#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, none are INUSE */
+#define AST_EXTENSION_RINGING (1 << 3)
struct ast_context;
struct ast_exten;
Index: channels/chan_sip.c
===================================================================
RCS file: /usr/cvsroot/asterisk/channels/chan_sip.c,v
retrieving revision 1.814
diff -u -r1.814 chan_sip.c
--- channels/chan_sip.c 23 Aug 2005 17:59:26 -0000 1.814
+++ channels/chan_sip.c 24 Aug 2005 17:02:28 -0000
@@ -135,6 +135,28 @@
bad things will happen.
*/
+enum subscriptiontype {
+ NONE,
+ XPIDF_XML,
+ DIALOG_INFO_XML,
+ CPIM_PIDF_XML,
+ PIDF_XML
+};
+
+static const struct cfsub_types {
+ enum subscriptiontype type;
+ const char *event;
+ const char *mediatype;
+ const char *text;
+} sub_types[] = {
+ { NONE, "-", "Unknown", "Unknown" },
+ /* IETF draft: draft-ietf-sipping-dialog-package-05.txt */
+ { DIALOG_INFO_XML, "dialog", "application/dialog-info+xml", "Dialog-info+xml" },
+ { CPIM_PIDF_XML, "presence", "application/cpim-pidf+xml", "cpim-pidf+xml" }, /* RFC 3863 */
+ { PIDF_XML, "presence", "application/pidf+xml", "pidf+xml" }, /* RFC 3863 */
+ { XPIDF_XML, "presence", "application/xpidf+xml", "xpidf+xml" } /* Pre-RFC 3863 with MS additions */
+};
+
enum sipmethod {
SIP_UNKNOWN,
SIP_RESPONSE,
@@ -269,7 +291,7 @@
/* SIP Methods we support */
-#define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY"
+#define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY"
/* SIP Extensions we support */
#define SUPPORTED_EXTENSIONS "replaces"
@@ -281,6 +303,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 +318,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 +565,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 */
@@ -586,9 +611,9 @@
int rtptimeout; /* RTP timeout time */
int rtpholdtimeout; /* RTP timeout when on hold */
int rtpkeepalive; /* Send RTP packets for keepalive */
-
- int subscribed; /* Is this call a subscription? */
+ enum subscriptiontype subscribed; /* Is this call a subscription? */
int stateid;
+ int laststate; /* Last known state */
int dialogver;
struct ast_dsp *vad; /* Voice Activation Detection dsp */
@@ -628,6 +653,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 +685,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) */
@@ -833,6 +860,7 @@
static void append_date(struct sip_request *req); /* Append date to SIP packet */
static int determine_firstline_parts(struct sip_request *req);
static void sip_dump_history(struct sip_pvt *dialog); /* Dump history to LOG_DEBUG at end of dialog, before destroying data */
+static struct cfsub_types *find_sub_type(enum subscriptiontype subtype);
/* Definition of this channel for channel registration */
static const struct ast_channel_tech sip_tech = {
@@ -2898,6 +2926,7 @@
p->method = intended_method;
p->initid = -1;
p->autokillid = -1;
+ p->subscribed = NONE;
p->stateid = -1;
p->prefs = prefs;
#ifdef OSP_SUPPORT
@@ -3800,7 +3829,7 @@
snprintf(tmp, sizeof(tmp), "%d", p->expiry);
add_header(resp, "Expires", tmp);
add_header(resp, "Contact", contact);
- } else {
+ } else if (p->our_contact[0]) {
add_header(resp, "Contact", p->our_contact);
}
if (p->maxforwards) {
@@ -3811,7 +3840,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;
@@ -3898,6 +3927,7 @@
return 0;
}
+/*--- __transmit_response: Base transmit response function */
static int __transmit_response(struct sip_pvt *p, char *msg, struct sip_request *req, int reliable)
{
struct sip_request resp;
@@ -4555,65 +4585,137 @@
char *t = tmp, *c, *a, *mfrom, *mto;
size_t maxbytes = sizeof(tmp);
struct sip_request req;
+ char hint[AST_MAX_EXTENSION];
+ char *statestring = "terminated";
+ struct cfsub_types *subscriptiontype;
+ 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;
+ }
+
+ subscriptiontype = find_sub_type(p->subscribed);
+
+ /* 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));
-
- 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;
+ 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;
- 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;
+ reqprep(&req, p, SIP_NOTIFY, 0, 1);
+
+ add_header(&req, "Event", subscriptiontype->event);
+ add_header(&req, "Content-Type", subscriptiontype->mediatype);
+ add_header(&req, "Subscription-State", "active");
+ switch (p->subscribed) {
+ case XPIDF_XML:
+ case CPIM_PIDF_XML:
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 {
- add_header(&req, "Event", "dialog");
- add_header(&req, "Content-Type", "application/dialog-info+xml");
+ break;
+ case PIDF_XML: /* Eyebeam supports this format */
+ 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 */
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 +5639,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 +5823,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 +5837,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 +6468,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 +6531,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 +6638,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 +6670,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
@@ -7468,6 +7594,7 @@
ast_cli(fd, " Reg. default duration: %d secs\n", default_expiry);
ast_cli(fd, " Outbound reg. timeout: %d secs\n", global_reg_timeout);
ast_cli(fd, " Outbound reg. attempts: %d\n", global_regattempts_max);
+ ast_cli(fd, " Notify ringing state: %s\n", global_notifyringing ? "Yes" : "No");
ast_cli(fd, "\nDefault Settings:\n");
ast_cli(fd, "-----------------\n");
ast_cli(fd, " Context: %s\n", default_context);
@@ -7495,6 +7622,34 @@
return RESULT_SUCCESS;
}
+/*--- sub_type2str: Show subscription type in string format */
+static char *sub_type2str(enum subscriptiontype subtype) {
+ int i;
+ char *res = NULL;
+
+ for (i = 1; (i < (sizeof(sub_types) / sizeof(sub_types[0]))) && !res; i++) {
+ if (sub_types[i].type == subtype) {
+ res = sub_types[i].text;
+ break;
+ }
+ }
+ return res;
+}
+
+/*--- find_sub_type: Find subscription type in array */
+static struct cfsub_types *find_sub_type(enum subscriptiontype subtype) {
+ int i;
+ struct cfsub_types *res = NULL;
+
+ for (i = 1; (i < (sizeof(sub_types) / sizeof(sub_types[0]))) && !res; i++) {
+ if (sub_types[i].type == subtype) {
+ res = &sub_types[i];
+ break;
+ }
+ }
+ return res;
+}
+
/* Forward declaration */
static int __sip_show_channels(int fd, int argc, char *argv[], int subscriptions);
@@ -7512,7 +7667,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 (%-10.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 +7680,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", "Type");
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 +7693,20 @@
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),
+ sub_type2str(cur->subscribed));
+ 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 +7868,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 +7938,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 +9032,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 +9054,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 +9063,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 +9826,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;
}
@@ -9688,6 +9842,20 @@
int res = 0;
struct ast_channel *c=NULL;
+ if (p->initreq.headers) {
+ /* We already have a dialog */
+ if (p->initreq.method != SIP_SUBSCRIBE) {
+ /* This is a SUBSCRIBE within another SIP dialog, which we do not support */
+ /* For transfers, this could happen, but since we haven't seen it happening, let us just refuse this */
+ transmit_response(p, "403 Forbidden (within dialog)", req);
+ /* Do not destroy session, since we will break the call if we do */
+ ast_log(LOG_DEBUG, "Got a subscription within the context of another call, can't handle that - %s (Method %s)\n", p->callid, sip_methods[p->initreq.method].text);
+ return 0;
+ } else {
+ if (debug)
+ ast_log(LOG_DEBUG, "Got a re-subscribe on existing subscription %s\n", p->callid);
+ }
+ }
if (!ignore) {
/* Use this as the basis */
if (debug)
@@ -9713,7 +9881,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 +9892,38 @@
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 */
+ p->tag = rand();
+
+ 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/pidf+xml")) {
+ p->subscribed = PIDF_XML; /* RFC 3863 format */
+ } else 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/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
@@ -9752,13 +9945,18 @@
transmit_response(p, "200 OK", req);
ast_set_flag(p, SIP_NEEDDESTROY);
} else {
- transmit_response(p, "403 Forbidden", req);
+ transmit_response(p, "404 Not found", req);
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,23 +9970,17 @@
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 */
- if (p->rtp && !p->owner) {
- ast_rtp_destroy(p->rtp);
- p->rtp = NULL;
- }
- if (p->vrtp && !p->owner) {
- 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 +10584,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 +10934,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 +11020,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 +11062,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 +11095,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 +11146,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 +11343,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;
@@ -11139,6 +11352,7 @@
externrefresh = 10;
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));
@@ -11233,6 +11447,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 24 Aug 2005 17:02:28 -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