diff -uNr asterisk-1.6.0.5.orig/channels/chan_sip.c asterisk-1.6.0.5/channels/chan_sip.c --- asterisk-1.6.0.5.orig/channels/chan_sip.c 2008-12-03 16:13:27.000000000 +0200 +++ asterisk-1.6.0.5/channels/chan_sip.c 2009-05-02 14:09:31.000000000 +0200 @@ -310,7 +310,8 @@ DIALOG_INFO_XML, CPIM_PIDF_XML, PIDF_XML, - MWI_NOTIFICATION + MWI_NOTIFICATION, + DIALOG_RLMI_XML }; /*! \brief Subscription types that we support. We support @@ -330,7 +331,8 @@ { 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 */ - { MWI_NOTIFICATION, "message-summary", "application/simple-message-summary", "mwi" } /* RFC 3842: Mailbox notification */ + { MWI_NOTIFICATION, "message-summary", "application/simple-message-summary", "mwi" }, /* RFC 3842: Mailbox notification */ + { DIALOG_RLMI_XML, "dialog", "multipart/related;boundary=UniqueAsteriskRLMIBoundary;type=application/rlmi+xml", "application/rlmi+xml" } /* RFC 4662 */ }; @@ -565,7 +567,7 @@ /* RFC3329: Security agreement mechanism */ { SIP_OPT_SEC_AGREE, NOT_SUPPORTED, "sec_agree" }, /* SIMPLE events: RFC4662 */ - { SIP_OPT_EVENTLIST, NOT_SUPPORTED, "eventlist" }, + { SIP_OPT_EVENTLIST, SUPPORTED, "eventlist" }, /* GRUU: Globally Routable User Agent URI's */ { SIP_OPT_GRUU, NOT_SUPPORTED, "gruu" }, /* RFC4538: Target-dialog */ @@ -1130,6 +1132,32 @@ }; +struct sip_evcalls { + int stateid; /*!< SUBSCRIBE: ID for devicestate subscriptions */ + int laststate; /*!< SUBSCRIBE: Last known extension state */ + int dialogver; /*!< SUBSCRIBE: Version for subscription dialog-info */ + AST_DECLARE_STRING_FIELDS( + AST_STRING_FIELD(exten); /*!< Exten to watch */ + AST_STRING_FIELD(name); /*!< Name of user to program */ + AST_STRING_FIELD(uri); /*!< Original requested URI */ + AST_STRING_FIELD(contentid); /*!< Dialogue Content ID */ + ); + AST_LIST_ENTRY(sip_evcalls) list; /*!< List mechanics*/ +}; + +AST_LIST_HEAD_STATIC(calllist, sip_evcalls); /*!< List of calls */ + +struct sip_eventlist { + ast_mutex_t lock; /*!< Dialog private lock */ + AST_DECLARE_STRING_FIELDS( + AST_STRING_FIELD(exten); /*!< Extension to notify */ + AST_STRING_FIELD(supported); /*!< Supported formats */ + AST_STRING_FIELD(uri); /*!< Supported formats */ + ); + unsigned long version; /*! chanvars); p->chanvars = NULL; } + if (p->eventlist) { + struct sip_evcalls *evsub = NULL; + AST_LIST_LOCK(&p->eventlist->calls); + while ((evsub = AST_LIST_REMOVE_HEAD(&p->eventlist->calls, list))) + free(evsub); + AST_LIST_UNLOCK(&p->eventlist->calls); + free(p->eventlist); + } ast_mutex_destroy(&p->pvt_lock); ast_string_field_free_memory(p); @@ -7536,6 +7573,14 @@ char contact[SIPBUFSIZE]; snprintf(contact, sizeof(contact), "%s;expires=%d", p->our_contact, p->expiry); add_header(resp, "Contact", contact); /* Not when we unregister */ + if (!p->sipoptions) { + const char *supported = get_header(req, "Supported"); + if (!ast_strlen_zero(supported)) { + parse_sip_options(p, supported); + if (p->sipoptions == SIP_OPT_EVENTLIST) + add_header(resp, "Require", "eventlist"); /* Require Eventlist */ + } + } } } else if (msg[0] != '4' && !ast_strlen_zero(p->our_contact)) { add_header(resp, "Contact", p->our_contact); @@ -9019,7 +9064,7 @@ /*! \brief Used in the SUBSCRIBE notification subsystem */ static int transmit_state_notify(struct sip_pvt *p, int state, int full, int timeout) { - struct ast_str *tmp = ast_str_alloca(4000); + struct ast_str *tmp = ast_str_alloca(16000); char from[256], to[256]; char *c, *mfrom, *mto; struct sip_request req; @@ -9029,6 +9074,7 @@ enum state { NOTIFY_OPEN, NOTIFY_INUSE, NOTIFY_CLOSED } local_state = NOTIFY_OPEN; char *pidfstate = "--"; char *pidfnote= "Ready"; + struct sip_eventlist *evl=p->eventlist; memset(from, 0, sizeof(from)); memset(to, 0, sizeof(to)); @@ -9135,12 +9181,140 @@ add_header(&req, "Subscription-State", "terminated;reason=noresource"); break; default: - if (p->expiry) + if ((p->expiry) || (p->eventlist)) add_header(&req, "Subscription-State", "active"); else /* Expired */ add_header(&req, "Subscription-State", "terminated;reason=timeout"); } switch (p->subscribed) { + case DIALOG_RLMI_XML: /* RFC 4662 */ + if (!evl) + break; + + struct sip_evcalls *newcall = NULL; + + add_header(&req, "Require", "eventlist"); + + struct ast_config *blflist_config = ast_load_realtime_multientry("blflist", "exten LIKE", "%", "peer", p->exten, NULL); + const char *blfpeer=NULL; + if (!blflist_config) { + ast_log(LOG_ERROR, "no blflist defined in your config (extconfig.conf).\n"); + } else { + const char *blfexten; + char slist[4000],diapart[4000],dialist[4000]; + char *lst = slist, *dp = diapart, *dl = dialist; + size_t listbytes = sizeof(slist), dpbytes = sizeof(diapart), dlbytes = sizeof(dialist); + + ast_build_string(&lst, &listbytes, ""); + ast_build_string(&lst, &listbytes, "\n",mto,evl->version++); + while ((blfpeer = ast_category_browse(blflist_config, blfpeer))) { + blfexten=ast_variable_retrieve(blflist_config, blfpeer, "exten"); + + AST_LIST_LOCK(&evl->calls); + AST_LIST_TRAVERSE_SAFE_BEGIN(&evl->calls, newcall, list) { + if ((newcall) && (!strcasecmp(newcall->exten, blfexten))) + break; + } + AST_LIST_TRAVERSE_SAFE_END + + if (!newcall) { + newcall=ast_calloc(1,sizeof(struct sip_evcalls)); + ast_string_field_init(newcall, 512); + ast_string_field_set(newcall, exten, blfexten); + ast_string_field_build(newcall, uri, "%s@%s", blfexten, p->fromdomain); + ast_string_field_set(newcall, name, ast_variable_retrieve(blflist_config, blfpeer, "name")); + AST_LIST_INSERT_TAIL(&evl->calls, newcall, list); + newcall->stateid = ast_extension_state_add(p->context, newcall->exten, cb_extensionstate, p); + } + + ast_string_field_build(newcall, contentid, "%08lx", ast_random()); + + + if (!strcasecmp(evl->exten,blfexten)) { + ast_log(LOG_WARNING,"Found target exten for update %s set state info/delete\n",evl->exten); + newcall->laststate=state; + } + switch (newcall->laststate) { + case (AST_EXTENSION_RINGING | AST_EXTENSION_INUSE): + statestring = (global_notifyringing) ? "early" : "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 = "terminated"; + local_state = NOTIFY_CLOSED; + pidfstate = "away"; + pidfnote = "Unavailable"; + break; + case AST_EXTENSION_ONHOLD: + statestring = "confirmed"; + local_state = NOTIFY_INUSE; + pidfstate = "busy"; + pidfnote = "On Hold"; + break; + case AST_EXTENSION_NOT_INUSE: + default: + statestring = "terminated"; + local_state = NOTIFY_OPEN; + pidfstate = "--"; + pidfnote= "Ready"; + /* Default setting */ + break; + } + + ast_build_string(&lst, &listbytes, "%s",newcall->uri,newcall->name); + ast_build_string(&lst, &listbytes, "\n",ast_random(),newcall->contentid); + + ast_build_string(&dp, &dpbytes, ""); + ast_build_string(&dp, &dpbytes, "", newcall->dialogver++, full ? "full":"partial", newcall->uri); + ast_build_string(&dp, &dpbytes, "", newcall->exten); + ast_build_string(&dp, &dpbytes, "%s", statestring); + if (state == AST_EXTENSION_ONHOLD) { + ast_build_string(&dp, &dpbytes, "", newcall->uri); + } + ast_build_string(&dp, &dpbytes, "sip:%s",newcall->name,newcall->uri); + ast_build_string(&dp, &dpbytes, "\n"); + + ast_build_string(&dl, &dlbytes, "Content-Type:application/dialog-info+xml\n"); + ast_build_string(&dl, &dlbytes, "Content-Length:%i\n",strlen(diapart)); + ast_build_string(&dl, &dlbytes, "Content-ID:<%s>\n\n",newcall->contentid); + ast_build_string(&dl, &dlbytes, diapart); + ast_build_string(&dl, &dlbytes, "--UniqueAsteriskRLMIBoundary\n"); + dp=diapart; + dpbytes=sizeof(diapart); + + AST_LIST_UNLOCK(&evl->calls); + } + ast_build_string(&lst, &listbytes, "\n"); + + ast_build_string(&t, &maxbytes, "--UniqueAsteriskRLMIBoundary\n"); + ast_build_string(&t, &maxbytes, "Content-Type:application/rlmi+xml\n"); + ast_build_string(&t, &maxbytes, "Content-Length:%i\n",strlen(slist)); + ast_build_string(&t, &maxbytes, "Content-ID:<%08lx>\n\n",ast_random()); + ast_build_string(&t, &maxbytes, slist); + ast_build_string(&t, &maxbytes, "--UniqueAsteriskRLMIBoundary\n"); + ast_build_string(&t, &maxbytes, dialist); + } + break; case XPIDF_XML: case CPIM_PIDF_XML: ast_str_append(&tmp, 0, @@ -10457,6 +10631,17 @@ sip_pvt_lock(p); + if ((p->eventlist) && (p->subscribed != NONE)) { + ast_mutex_lock(&p->eventlist->lock); + ast_string_field_set(p->eventlist, exten, exten); + transmit_state_notify(p, state, 1, FALSE); + ast_mutex_unlock(&p->eventlist->lock); + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_1 "Extension Changed %s new state %s for Notify User %s\n", exten, ast_extension_state2str(state), p->username); + ast_mutex_unlock(&p->lock); + return 0; + } + switch(state) { case AST_EXTENSION_DEACTIVATED: /* Retry after a while */ case AST_EXTENSION_REMOVED: /* Extension is gone */ @@ -13375,7 +13560,10 @@ struct sip_pvt *cur = __cur; struct __show_chan_arg *arg = __arg; const struct sockaddr_in *dst = sip_real_dst(cur); + struct sip_eventlist *evl; + struct sip_evcalls *newcall; + evl = cur->eventlist; /* XXX indentation preserved to reduce diff. Will be fixed later */ if (cur->subscribed == NONE && !arg->subscriptions) { /* set if SIP transfer in progress */ @@ -13397,16 +13585,35 @@ struct ast_str *mailbox_str = ast_str_alloca(512); if (cur->subscribed == MWI_NOTIFICATION && cur->relatedpeer) peer_mailboxes_to_str(&mailbox_str, cur->relatedpeer); - ast_cli(arg->fd, FORMAT3, ast_inet_ntoa(dst->sin_addr), - S_OR(cur->username, S_OR(cur->cid_num, "(None)")), - cur->callid, - /* the 'complete' exten/context is hidden in the refer_to field for subscriptions */ - cur->subscribed == MWI_NOTIFICATION ? "--" : cur->subscribeuri, - cur->subscribed == MWI_NOTIFICATION ? "" : ast_extension_state2str(cur->laststate), - subscription_type2str(cur->subscribed), - cur->subscribed == MWI_NOTIFICATION ? S_OR(mailbox_str->str, "") : "" -); - arg->numchans++; + if (! evl) { + ast_cli(arg->fd, FORMAT3, ast_inet_ntoa(dst->sin_addr), + S_OR(cur->username, S_OR(cur->cid_num, "(None)")), + cur->callid, + /* the 'complete' exten/context is hidden in the refer_to field for subscriptions */ + cur->subscribed == MWI_NOTIFICATION ? "--" : cur->subscribeuri, + cur->subscribed == MWI_NOTIFICATION ? "" : ast_extension_state2str(cur->laststate), + subscription_type2str(cur->subscribed), + cur->subscribed == MWI_NOTIFICATION ? S_OR(mailbox_str->str, "") : ""); + arg->numchans++; + } else { + ast_mutex_lock(&evl->lock); + AST_LIST_LOCK(&evl->calls); + AST_LIST_TRAVERSE(&evl->calls, newcall, list) { + if (newcall) { + ast_cli(arg->fd, FORMAT3, ast_inet_ntoa(dst->sin_addr), + S_OR(cur->username, S_OR(cur->cid_num, "(None)")), + cur->callid, + /* the 'complete' exten/context is hidden in the refer_to field for subscriptions */ + cur->subscribed == MWI_NOTIFICATION ? "--" : newcall->exten, + cur->subscribed == MWI_NOTIFICATION ? "" : ast_extension_state2str(newcall->laststate), + subscription_type2str(cur->subscribed), + cur->subscribed == MWI_NOTIFICATION ? S_OR(mailbox_str->str, "") : ""); + arg->numchans++; + } + } + AST_LIST_UNLOCK(&evl->calls); + ast_mutex_unlock(&evl->lock); + } } return 0; /* don't care, we scan all channels */ @@ -18187,7 +18394,17 @@ /* Polycom phones only handle xpidf+xml, even if they say they can handle pidf+xml as well */ - if (strstr(p->useragent, "Polycom")) { + if (strstr(accept, "application/rlmi+xml")) { + if (!p->eventlist) { + p->eventlist=ast_calloc(1,sizeof(struct sip_eventlist)); + ast_mutex_init(&p->eventlist->lock); + p->eventlist->version=0; + ast_string_field_init(p->eventlist,512); + AST_LIST_HEAD_INIT(&p->eventlist->calls); + ast_string_field_set(p->eventlist, supported, accept); + } + p->subscribed = DIALOG_RLMI_XML; + } else if (strstr(p->useragent, "Polycom")) { p->subscribed = XPIDF_XML; } else if (strstr(accept, "application/pidf+xml")) { p->subscribed = PIDF_XML; /* RFC 3863 format */