]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
Prevent exhaustion of system resources through exploitation of event cache
authorMatthew Jordan <mjordan@digium.com>
Wed, 2 Jan 2013 16:54:20 +0000 (16:54 +0000)
committerMatthew Jordan <mjordan@digium.com>
Wed, 2 Jan 2013 16:54:20 +0000 (16:54 +0000)
Asterisk maintains an internal cache for devices in the event subsystem. The
device state cache holds the state of each device known to Asterisk, such that
consumers of device state information can query for the last known state for
a particular device, even if it is not part of an active call. The concept of
a device in Asterisk can include entities that do not have a physical
representation. One way that this occurred was when anonymous calls are allowed
in Asterisk. A device was automatically created and stored in the cache for
each anonymous call that occurred; this was possible in the SIP and IAX2
channel drivers and through channel drivers that utilized the
res_jabber/res_xmpp resource modules (Gtalk, Jingle, and Motif). These devices
are never removed from the system, allowing anonymous calls to potentially
exhaust a system's resources.

This patch changes the event cache subsystem and device state management to
no longer cache devices that are not associated with a physical entity.

(issue ASTERISK-20175)
Reported by: Russell Bryant, Leif Madsen, Joshua Colp
Tested by: kmoore
patches:
  event-cachability-3.diff uploaded by jcolp (license 5000)

git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.8@378303 65c4cc65-6c06-0410-ace0-fbb531ad65f3

18 files changed:
apps/app_confbridge.c
apps/app_meetme.c
channels/chan_agent.c
channels/chan_dahdi.c
channels/chan_iax2.c
channels/chan_local.c
channels/chan_sip.c
channels/chan_skinny.c
funcs/func_devstate.c
include/asterisk/channel.h
include/asterisk/devicestate.h
include/asterisk/event_defs.h
main/channel.c
main/devicestate.c
main/event.c
main/features.c
res/res_calendar.c
res/res_jabber.c

index 18cfb746c71c3cbef8b2a14d1f8cc3331d73722e..a4eea43b8f01bef69f2d4b75207fa39b6987cdc0 100644 (file)
@@ -486,7 +486,7 @@ static struct conference_bridge *join_conference_bridge(const char *name, struct
 
        /* Set the device state for this conference */
        if (conference_bridge->users == 1) {
-               ast_devstate_changed(AST_DEVICE_INUSE, "confbridge:%s", conference_bridge->name);
+               ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "confbridge:%s", conference_bridge->name);
        }
 
        /* If the caller is a marked user or is waiting for a marked user to enter pass 'em off, otherwise pass them off to do regular joining stuff */
@@ -568,7 +568,7 @@ static void  leave_conference_bridge(struct conference_bridge *conference_bridge
                }
        } else {
                /* Set device state to "not in use" */
-               ast_devstate_changed(AST_DEVICE_NOT_INUSE, "confbridge:%s", conference_bridge->name);
+               ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "confbridge:%s", conference_bridge->name);
 
                ao2_unlink(conference_bridges, conference_bridge);
        }
index 64e788359b7e89f146a5d50e98f6a1ffc1d170e6..1f2286e09cd210cf8743e75cec3cc7fd9b7f3cbe 100644 (file)
@@ -2576,7 +2576,7 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struc
 
        /* This device changed state now - if this is the first user */
        if (conf->users == 1)
-               ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
+               ast_devstate_changed(AST_DEVICE_INUSE, (conf->isdynamic ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), "meetme:%s", conf->confno);
 
        ast_mutex_unlock(&conf->playlock);
 
@@ -3884,7 +3884,7 @@ bailoutandtrynormal:
 
                /* Change any states */
                if (!conf->users) {
-                       ast_devstate_changed(AST_DEVICE_NOT_INUSE, "meetme:%s", conf->confno);
+                       ast_devstate_changed(AST_DEVICE_NOT_INUSE, (conf->isdynamic ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), "meetme:%s", conf->confno);
                }
 
                /* Return the number of seconds the user was in the conf */
@@ -5303,8 +5303,8 @@ static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk
                                || trunk_ref == exclude)
                                continue;
                        trunk_ref->state = state;
-                       ast_devstate_changed(sla_state_to_devstate(state), 
-                               "SLA:%s_%s", station->name, trunk->name);
+                       ast_devstate_changed(sla_state_to_devstate(state), AST_DEVSTATE_CACHABLE,
+                                            "SLA:%s_%s", station->name, trunk->name);
                        break;
                }
        }
@@ -5802,8 +5802,8 @@ static void sla_handle_hold_event(struct sla_event *event)
 {
        ast_atomic_fetchadd_int((int *) &event->trunk_ref->trunk->hold_stations, 1);
        event->trunk_ref->state = SLA_TRUNK_STATE_ONHOLD_BYME;
-       ast_devstate_changed(AST_DEVICE_ONHOLD, "SLA:%s_%s", 
-               event->station->name, event->trunk_ref->trunk->name);
+       ast_devstate_changed(AST_DEVICE_ONHOLD, AST_DEVSTATE_CACHABLE, "SLA:%s_%s",
+                            event->station->name, event->trunk_ref->trunk->name);
        sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_ONHOLD, 
                INACTIVE_TRUNK_REFS, event->trunk_ref);
 
@@ -6312,8 +6312,8 @@ static int sla_station_exec(struct ast_channel *chan, const char *data)
                        sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
                else {
                        trunk_ref->state = SLA_TRUNK_STATE_UP;
-                       ast_devstate_changed(AST_DEVICE_INUSE, 
-                               "SLA:%s_%s", station->name, trunk_ref->trunk->name);
+                       ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE,
+                                            "SLA:%s_%s", station->name, trunk_ref->trunk->name);
                }
        } else if (trunk_ref->state == SLA_TRUNK_STATE_RINGING) {
                struct sla_ringing_trunk *ringing_trunk;
index 06c64b26faccb68599a41446748215eaaf847136..92aa796817a0e2dc1d4436950d5fde692918a108 100644 (file)
@@ -618,7 +618,7 @@ static struct ast_frame *agent_read(struct ast_channel *ast)
                if (p->chan) {
                        p->chan->_bridge = NULL;
                        p->chan = NULL;
-                       ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent);
+                       ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent);
                        p->acknowledged = 0;
                }
        } else {
@@ -875,7 +875,7 @@ static int agent_call(struct ast_channel *ast, char *dest, int timeout)
        } else {
                /* Agent hung-up */
                p->chan = NULL;
-               ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent);
+               ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent);
        }
 
        if (!res) {
@@ -994,7 +994,7 @@ static int agent_hangup(struct ast_channel *ast)
        if (!p->loginstart) {
                p->logincallerid[0] = '\0';
        } else {
-               ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Agent/%s", p->agent);
+               ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent);
        }
 
        if (p->abouttograb) {
@@ -2138,7 +2138,7 @@ static int login_exec(struct ast_channel *chan, const char *data)
                                                }
                                                ast_mutex_unlock(&p->lock);
                                                AST_LIST_UNLOCK(&agents);
-                                               ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Agent/%s", p->agent);
+                                               ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent);
                                                while (res >= 0) {
                                                        ast_mutex_lock(&p->lock);
                                                        if (p->deferlogoff && p->chan) {
@@ -2159,7 +2159,7 @@ static int login_exec(struct ast_channel *chan, const char *data)
                                                                if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) {
                                                                        ast_debug(1, "Wrapup time for %s expired!\n", p->agent);
                                                                        p->lastdisc = ast_tv(0, 0);
-                                                                       ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Agent/%s", p->agent);
+                                                                       ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent);
                                                                        if (p->ackcall) {
                                                                                check_beep(p, 0);
                                                                        } else {
@@ -2219,7 +2219,7 @@ static int login_exec(struct ast_channel *chan, const char *data)
                                                ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGOFF", "%s|%ld", chan->name, logintime);
                                                ast_verb(2, "Agent '%s' logged out\n", p->agent);
                                                /* If there is no owner, go ahead and kill it now */
-                                               ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent);
+                                               ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent);
                                                if (p->dead && !p->owner) {
                                                        ast_mutex_destroy(&p->lock);
                                                        ast_cond_destroy(&p->app_complete_cond);
index ccd8f3b1d23483387e23f6570c8ae2b8d1b003f8..a95a8ce3ac1da34f5541a1aefa4a97bb283141de 100644 (file)
@@ -3364,7 +3364,7 @@ static void dahdi_pri_update_span_devstate(struct sig_pri_span *pri)
        }
        if (pri->congestion_devstate != new_state) {
                pri->congestion_devstate = new_state;
-               ast_devstate_changed(AST_DEVICE_UNKNOWN, "DAHDI/I%d/congestion", pri->span);
+               ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_NOT_CACHABLE, "DAHDI/I%d/congestion", pri->span);
        }
 #if defined(THRESHOLD_DEVSTATE_PLACEHOLDER)
        /* Update the span threshold device state and report any change. */
@@ -3380,7 +3380,7 @@ static void dahdi_pri_update_span_devstate(struct sig_pri_span *pri)
        }
        if (pri->threshold_devstate != new_state) {
                pri->threshold_devstate = new_state;
-               ast_devstate_changed(AST_DEVICE_UNKNOWN, "DAHDI/I%d/threshold", pri->span);
+               ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_NOT_CACHABLE, "DAHDI/I%d/threshold", pri->span);
        }
 #endif /* defined(THRESHOLD_DEVSTATE_PLACEHOLDER) */
 }
@@ -9804,7 +9804,8 @@ static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpb
        if (dashptr) {
                *dashptr = '\0';
        }
-       ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, device_name);
+       tmp->flags |= AST_FLAG_DISABLE_DEVSTATE_CACHE;
+       ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, AST_DEVSTATE_NOT_CACHABLE, device_name);
 
        for (v = i->vars ; v ; v = v->next)
                pbx_builtin_setvar_helper(tmp, v->name, v->value);
index d7eeb245a3188bb8069eaef6a6e6a69ce10f4a2d..7a17c26ed631a9348783088d1afc58aac034caab 100644 (file)
@@ -5729,7 +5729,7 @@ static int iax2_getpeertrunk(struct sockaddr_in sin)
 }
 
 /*! \brief  Create new call, interface with the PBX core */
-static struct ast_channel *ast_iax2_new(int callno, int state, format_t capability, const char *linkedid)
+static struct ast_channel *ast_iax2_new(int callno, int state, format_t capability, const char *linkedid, unsigned int cachable)
 {
        struct ast_channel *tmp;
        struct chan_iax2_pvt *i;
@@ -5798,6 +5798,10 @@ static struct ast_channel *ast_iax2_new(int callno, int state, format_t capabili
        i->owner = tmp;
        i->capability = capability;
 
+       if (!cachable) {
+               tmp->flags |= AST_FLAG_DISABLE_DEVSTATE_CACHE;
+       }
+
        /* Set inherited variables */
        if (i->vars) {
                for (v = i->vars ; v ; v = v->next)
@@ -8084,7 +8088,7 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
                /* if challenge has been sent, but no challenge response if given, reject. */
                goto return_unref;
        }
-       ast_devstate_changed(AST_DEVICE_UNKNOWN, "IAX2/%s", p->name); /* Activate notification */
+       ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "IAX2/%s", p->name); /* Activate notification */
 
        /* either Authentication has taken place, or a REGAUTH must be sent before verifying registration */
        res = 0;
@@ -8638,7 +8642,7 @@ static void __expire_registry(const void *data)
        if (!ast_test_flag64(peer, IAX_TEMPONLY))
                ast_db_del("IAX/Registry", peer->name);
        register_peer_exten(peer, 0);
-       ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "IAX2/%s", peer->name); /* Activate notification */
+       ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "IAX2/%s", peer->name); /* Activate notification */
        if (iax2_regfunk)
                iax2_regfunk(peer->name, 0);
 
@@ -8693,7 +8697,7 @@ static void reg_source_db(struct iax2_peer *p)
                }
        }
 
-       ast_devstate_changed(AST_DEVICE_UNKNOWN, "IAX2/%s", p->name); /* Activate notification */
+       ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "IAX2/%s", p->name); /* Activate notification */
 
        p->expire = iax2_sched_add(sched, (p->expiry + 10) * 1000, expire_registry, peer_ref(p));
        if (p->expire == -1) {
@@ -8770,14 +8774,14 @@ static int update_registry(struct sockaddr_in *sin, int callno, char *devtype, i
                                            ast_test_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED) ? "AUTHENTICATED" : "UNAUTHENTICATED", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
                        manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Registered\r\n", p->name);
                        register_peer_exten(p, 1);
-                       ast_devstate_changed(AST_DEVICE_UNKNOWN, "IAX2/%s", p->name); /* Activate notification */
+                       ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "IAX2/%s", p->name); /* Activate notification */
                } else if (!ast_test_flag64(p, IAX_TEMPONLY)) {
                        ast_verb(3, "Unregistered IAX2 '%s' (%s)\n", p->name,
                                            ast_test_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED) ? "AUTHENTICATED" : "UNAUTHENTICATED");
                        manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Unregistered\r\n", p->name);
                        register_peer_exten(p, 0);
                        ast_db_del("IAX/Registry", p->name);
-                       ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "IAX2/%s", p->name); /* Activate notification */
+                       ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "IAX2/%s", p->name); /* Activate notification */
                }
                /* Update the host */
                /* Verify that the host is really there */
@@ -10291,7 +10295,8 @@ static int socket_process(struct iax2_thread *thread)
                    (f.frametype == AST_FRAME_IAX)) {
                        if (ast_test_flag64(iaxs[fr->callno], IAX_DELAYPBXSTART)) {
                                ast_clear_flag64(iaxs[fr->callno], IAX_DELAYPBXSTART);
-                               if (!ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->chosenformat, NULL)) {
+                               if (!ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->chosenformat, NULL,
+                                                 ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_AUTHENTICATED))) {
                                        ast_variables_destroy(ies.vars);
                                        ast_mutex_unlock(&iaxsl[fr->callno]);
                                        return 1;
@@ -10924,13 +10929,13 @@ static int socket_process(struct iax2_thread *thread)
                                                if (iaxs[fr->callno]->pingtime <= peer->maxms) {
                                                        ast_log(LOG_NOTICE, "Peer '%s' is now REACHABLE! Time: %d\n", peer->name, iaxs[fr->callno]->pingtime);
                                                        manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Reachable\r\nTime: %d\r\n", peer->name, iaxs[fr->callno]->pingtime); 
-                                                       ast_devstate_changed(AST_DEVICE_NOT_INUSE, "IAX2/%s", peer->name); /* Activate notification */
+                                                       ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "IAX2/%s", peer->name); /* Activate notification */
                                                }
                                        } else if ((peer->historicms > 0) && (peer->historicms <= peer->maxms)) {
                                                if (iaxs[fr->callno]->pingtime > peer->maxms) {
                                                        ast_log(LOG_NOTICE, "Peer '%s' is now TOO LAGGED (%d ms)!\n", peer->name, iaxs[fr->callno]->pingtime);
                                                        manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Lagged\r\nTime: %d\r\n", peer->name, iaxs[fr->callno]->pingtime); 
-                                                       ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "IAX2/%s", peer->name); /* Activate notification */
+                                                       ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "IAX2/%s", peer->name); /* Activate notification */
                                                }
                                        }
                                        peer->lastms = iaxs[fr->callno]->pingtime;
@@ -11172,7 +11177,7 @@ static int socket_process(struct iax2_thread *thread)
                                                                                        using_prefs);
 
                                                        ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
-                                                       if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, format, NULL)))
+                                                       if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, format, NULL, 1)))
                                                                iax2_destroy(fr->callno);
                                                        else if (ies.vars) {
                                                                struct ast_datastore *variablestore;
@@ -11243,7 +11248,7 @@ immediatedial:
                                                        ast_getformatname_multiple(tmp, sizeof(tmp), iaxs[fr->callno]->peerformat));
                                                ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
                                                send_command(iaxs[fr->callno], AST_FRAME_CONTROL, AST_CONTROL_PROGRESS, 0, NULL, 0, -1);
-                                               if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->peerformat, NULL)))
+                                               if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->peerformat, NULL, 1)))
                                                        iax2_destroy(fr->callno);
                                                else if (ies.vars) {
                                                        struct ast_datastore *variablestore;
@@ -11996,7 +12001,7 @@ static void __iax2_poke_noanswer(const void *data)
        if (peer->lastms > -1) {
                ast_log(LOG_NOTICE, "Peer '%s' is now UNREACHABLE! Time: %d\n", peer->name, peer->lastms);
                manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Unreachable\r\nTime: %d\r\n", peer->name, peer->lastms);
-               ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "IAX2/%s", peer->name); /* Activate notification */
+               ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "IAX2/%s", peer->name); /* Activate notification */
        }
        if ((callno = peer->callno) > 0) {
                ast_mutex_lock(&iaxsl[callno]);
@@ -12164,7 +12169,7 @@ static struct ast_channel *iax2_request(const char *type, format_t format, const
        if (cai.found)
                ast_string_field_set(iaxs[callno], host, pds.peer);
 
-       c = ast_iax2_new(callno, AST_STATE_DOWN, cai.capability, requestor ? requestor->linkedid : NULL);
+       c = ast_iax2_new(callno, AST_STATE_DOWN, cai.capability, requestor ? requestor->linkedid : NULL, cai.found);
 
        ast_mutex_unlock(&iaxsl[callno]);
 
index 459bd5b12d114135ca4cd3b274940e440a690b2d..2d283eb719e5d011ffa7f0f0ce557e44c8ada039 100644 (file)
@@ -1229,6 +1229,9 @@ static struct ast_channel *local_new(struct local_pvt *p, int state, const char
        tmp->tech_pvt = p;
        tmp2->tech_pvt = p;
 
+       tmp->flags |= AST_FLAG_DISABLE_DEVSTATE_CACHE;
+       tmp2->flags |= AST_FLAG_DISABLE_DEVSTATE_CACHE;
+
        p->owner = tmp;
        p->chan = tmp2;
 
index 7e51ed513965000a7699a7e15b181f1b1cab2dbe..4687f87b99cd23b73deed54914a729908979170b 100644 (file)
@@ -6418,7 +6418,7 @@ static int update_call_counter(struct sip_pvt *fup, int event)
        }
 
        if (p) {
-               ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", p->name);
+               ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "SIP/%s", p->name);
                unref_peer(p, "update_call_counter: unref_peer from call counter");
        }
        return 0;
@@ -7609,6 +7609,9 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit
        if (i->rtp)
                ast_jb_configure(tmp, &global_jbconf);
 
+       if (!i->relatedpeer) {
+               tmp->flags |= AST_FLAG_DISABLE_DEVSTATE_CACHE;
+       }
        /* Set channel variables for this call from configuration */
        for (v = i->chanvars ; v ; v = v->next) {
                char valuebuf[1024];
@@ -14221,7 +14224,7 @@ static int expire_register(const void *data)
 
        manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name);
        register_peer_exten(peer, FALSE);       /* Remove regexten */
-       ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name);
+       ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "SIP/%s", peer->name);
 
        /* Do we need to release this peer from memory?
                Only for realtime peers and autocreated peers
@@ -15003,8 +15006,9 @@ static void sip_peer_hold(struct sip_pvt *p, int hold)
        ast_atomic_fetchadd_int(&p->relatedpeer->onHold, (hold ? +1 : -1));
 
        /* Request device state update */
-       ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", p->relatedpeer->name);
-       
+       ast_devstate_changed(AST_DEVICE_UNKNOWN, (p->owner->flags & AST_FLAG_DISABLE_DEVSTATE_CACHE ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE),
+                            "SIP/%s", p->relatedpeer->name);
+
        return;
 }
 
@@ -15422,7 +15426,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock
                } else {
                        update_peer_lastmsgssent(peer, -1, 0);
                }
-               ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name);
+               ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "SIP/%s", peer->name);
        }
        if (res < 0) {
                switch (res) {
@@ -21414,7 +21418,7 @@ static void handle_response_peerpoke(struct sip_pvt *p, int resp, struct sip_req
 
                ast_log(LOG_NOTICE, "Peer '%s' is now %s. (%dms / %dms)\n",
                        peer->name, s, pingtime, peer->maxms);
-               ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name);
+               ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "SIP/%s", peer->name);
                if (sip_cfg.peer_rtupdate) {
                        ast_update_realtime(ast_check_realtime("sipregs") ? "sipregs" : "sippeers", "name", peer->name, "lastms", str_lastms, SENTINEL);
                }
@@ -26870,7 +26874,7 @@ static int sip_poke_noanswer(const void *data)
        /* Don't send a devstate change if nothing changed. */
        if (peer->lastms > -1) {
                peer->lastms = -1;
-               ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name);
+               ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "SIP/%s", peer->name);
        }
 
        /* Try again quickly */
index a92bd48bc897146cbc974195100569bcfd21e6f0..0b16a34edef5b33ac72e7e3f78c397f0637455c9 100644 (file)
@@ -1925,7 +1925,7 @@ static int skinny_register(struct skinny_req *req, struct skinnysession *s)
                                        register_exten(l);
                                        /* initialize MWI on line and device */
                                        mwi_event_cb(0, l);
-                                       ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
+                                       ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s@%s", l->name, d->name);
                                }
                                --instance;
                        }
@@ -1963,7 +1963,7 @@ static int skinny_unregister(struct skinny_req *req, struct skinnysession *s)
                                l->instance = 0;
                                manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: Skinny\r\nPeer: Skinny/%s@%s\r\nPeerStatus: Unregistered\r\n", l->name, d->name);
                                unregister_exten(l);
-                               ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Skinny/%s@%s", l->name, d->name);
+                               ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "Skinny/%s@%s", l->name, d->name);
                        }
                }
        }
@@ -5321,7 +5321,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
                        ast_verb(1, "RECEIVED UNKNOWN STIMULUS:  %d(%d/%d)\n", event, instance, callreference);
                break;
        }
-       ast_devstate_changed(AST_DEVICE_UNKNOWN, "Skinny/%s@%s", l->name, d->name);
+       ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "Skinny/%s@%s", l->name, d->name);
 
        return 1;
 }
@@ -5372,7 +5372,7 @@ static int handle_offhook_message(struct skinny_req *req, struct skinnysession *
        transmit_ringer_mode(d, SKINNY_RING_OFF);
        l->hookstate = SKINNY_OFFHOOK;
 
-       ast_devstate_changed(AST_DEVICE_INUSE, "Skinny/%s@%s", l->name, d->name);
+       ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s@%s", l->name, d->name);
 
        if (sub && sub->onhold) {
                return 1;
@@ -5448,7 +5448,7 @@ static int handle_onhook_message(struct skinny_req *req, struct skinnysession *s
                return 0;
        }
 
-       ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
+       ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s@%s", l->name, d->name);
 
        if (sub->onhold) {
                return 0;
@@ -5834,7 +5834,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
                return 0;
        }
 
-       ast_devstate_changed(AST_DEVICE_INUSE, "Skinny/%s@%s", l->name, d->name);
+       ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s@%s", l->name, d->name);
 
        switch(event) {
        case SOFTKEY_NONE:
@@ -6049,7 +6049,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
                                transmit_callstate(d, l->instance, sub->callid, l->hookstate);
                        }
 
-                       ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
+                       ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s@%s", l->name, d->name);
                        if (skinnydebug)
                                ast_verb(1, "Skinny %s@%s went on hook\n", l->name, d->name);
                        if (l->transfer && sub->xferor && sub->owner->_state >= AST_STATE_RING) {
@@ -6073,7 +6073,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
                                }
                        }
                        if ((l->hookstate == SKINNY_ONHOOK) && (AST_LIST_NEXT(sub, list) && !AST_LIST_NEXT(sub, list)->rtp)) {
-                               ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
+                               ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s@%s", l->name, d->name);
                        }
                }
                break;
index e1f34387a116f8d87d2217a5d67edc271a3175dc..6472610c8a70a0b2bfdd5451af2ada658f052d9e 100644 (file)
@@ -132,7 +132,7 @@ static int devstate_write(struct ast_channel *chan, const char *cmd, char *data,
 
        ast_db_put(astdb_family, data, value);
 
-       ast_devstate_changed(state_val, "Custom:%s", data);
+       ast_devstate_changed(state_val, AST_DEVSTATE_CACHABLE, "Custom:%s", data);
 
        return 0;
 }
@@ -295,7 +295,7 @@ static char *handle_cli_devstate_change(struct ast_cli_entry *e, int cmd, struct
 
        ast_db_put(astdb_family, dev, state);
 
-       ast_devstate_changed(state_val, "Custom:%s", dev);
+       ast_devstate_changed(state_val, AST_DEVSTATE_CACHABLE, "Custom:%s", dev);
 
        return CLI_SUCCESS;
 }
@@ -341,7 +341,7 @@ static int load_module(void)
                if (dev_name <= (const char *) 1)
                        continue;
                ast_devstate_changed(ast_devstate_val(db_entry->data),
-                       "Custom:%s\n", dev_name);
+                       AST_DEVSTATE_CACHABLE, "Custom:%s\n", dev_name);
        }
        ast_db_freetree(db_tree);
        db_tree = NULL;
index d5aa046af37dd496edad599d1d1c5e5cec752f3d..f245b62d496b9f224cb6165aa24800c076440e34 100644 (file)
@@ -938,6 +938,12 @@ enum {
         *  some non-traditional dialplans (like AGI) to continue to function.
         */
        AST_FLAG_DISABLE_WORKAROUNDS = (1 << 20),
+       /*! Disable device state event caching.  This allows allows channel
+        * drivers to selectively prevent device state events from being cached
+        * by certain channels such as anonymous calls which have no persistent
+        * represenatation that can be tracked.
+        */
+       AST_FLAG_DISABLE_DEVSTATE_CACHE = (1 << 21),
 };
 
 /*! \brief ast_bridge_config flags */
index 66ca2bd1a3ecddb1fd4d0ac44741cc1aa26e43dd..86740bc2fd69b735906d1e9d71adcd4798924d66 100644 (file)
@@ -61,6 +61,14 @@ enum ast_device_state {
        AST_DEVICE_TOTAL,        /*/ Total num of device states, used for testing */
 };
 
+/*! \brief Device State Cachability
+ *  \note This is used to define the cachability of a device state when set.
+ */
+enum ast_devstate_cache {
+       AST_DEVSTATE_NOT_CACHABLE,  /*!< This device state is not cachable */
+       AST_DEVSTATE_CACHABLE,      /*!< This device state is cachable */
+};
+
 /*! \brief Devicestate provider call back */
 typedef enum ast_device_state (*ast_devstate_prov_cb_type)(const char *data);
 
@@ -129,6 +137,7 @@ enum ast_device_state ast_device_state(const char *device);
  * \brief Tells Asterisk the State for Device is changed
  *
  * \param state the new state of the device
+ * \param cachable whether this device state is cachable
  * \param fmt device name like a dial string with format parameters
  *
  * The new state of the device will be sent off to any subscribers
@@ -138,13 +147,14 @@ enum ast_device_state ast_device_state(const char *device);
  * \retval 0 on success
  * \retval -1 on failure
  */
-int ast_devstate_changed(enum ast_device_state state, const char *fmt, ...)
-       __attribute__((format(printf, 2, 3)));
+int ast_devstate_changed(enum ast_device_state state, enum ast_devstate_cache cachable, const char *fmt, ...)
+       __attribute__((format(printf, 3, 4)));
 
 /*!
  * \brief Tells Asterisk the State for Device is changed
  *
  * \param state the new state of the device
+ * \param cachable whether this device state is cachable
  * \param device device name like a dial string with format parameters
  *
  * The new state of the device will be sent off to any subscribers
@@ -154,7 +164,7 @@ int ast_devstate_changed(enum ast_device_state state, const char *fmt, ...)
  * \retval 0 on success
  * \retval -1 on failure
  */
-int ast_devstate_changed_literal(enum ast_device_state state, const char *device);
+int ast_devstate_changed_literal(enum ast_device_state state, enum ast_devstate_cache cachable, const char *device);
 
 /*!
  * \brief Tells Asterisk the State for Device is changed.
index 073d67bc6b9a14756fb36d8cd387e68a00533069..c385bcf79dfe7b511bf364b040abce6a3e355726 100644 (file)
@@ -283,8 +283,14 @@ enum ast_event_ie_type {
        AST_EVENT_IE_CHALLENGE           = 0x0032,
        AST_EVENT_IE_RESPONSE            = 0x0033,
        AST_EVENT_IE_EXPECTED_RESPONSE   = 0x0034,
+       /*!
+        * \brief Event non-cachability flag
+        * Used by: All events
+        * Payload type: UINT
+        */
+       AST_EVENT_IE_CACHABLE            = 0x0035,
        /*! \brief Must be the last IE value +1 */
-       AST_EVENT_IE_TOTAL               = 0x0035,
+       AST_EVENT_IE_TOTAL               = 0x0036,
 };
 
 /*!
index 00f3bbb342dfc0164bd499997cd98d795f92bffd..50ed55c1316a46c967c67491986ac46e37000451 100644 (file)
@@ -299,6 +299,7 @@ static void channel_data_add_flags(struct ast_data *tree,
        ast_data_add_bool(tree, "BRIDGE_HANGUP_RUN", ast_test_flag(chan, AST_FLAG_BRIDGE_HANGUP_RUN));
        ast_data_add_bool(tree, "BRIDGE_HANGUP_DONT", ast_test_flag(chan, AST_FLAG_BRIDGE_HANGUP_DONT));
        ast_data_add_bool(tree, "DISABLE_WORKAROUNDS", ast_test_flag(chan, AST_FLAG_DISABLE_WORKAROUNDS));
+       ast_data_add_bool(tree, "DISABLE_DEVSTATE_CACHE", ast_test_flag(chan, AST_FLAG_DISABLE_DEVSTATE_CACHE));
 }
 
 #if defined(KEEP_TILL_CHANNEL_PARTY_NUMBER_INFO_NEEDED)
@@ -2519,7 +2520,7 @@ static void ast_channel_destructor(void *obj)
                 * instance is dead, we don't know the state of all other possible
                 * instances.
                 */
-               ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, device_name);
+               ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, (chan->flags & AST_FLAG_DISABLE_DEVSTATE_CACHE ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), device_name);
        }
        ast_atomic_fetchadd_int(&chancount, -1);
 }
@@ -7058,7 +7059,7 @@ int ast_setstate(struct ast_channel *chan, enum ast_channel_state state)
        /* We have to pass AST_DEVICE_UNKNOWN here because it is entirely possible that the channel driver
         * for this channel is using the callback method for device state. If we pass in an actual state here
         * we override what they are saying the state is and things go amuck. */
-       ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, name);
+       ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, (chan->flags & AST_FLAG_DISABLE_DEVSTATE_CACHE ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), name);
 
        /* setstate used to conditionally report Newchannel; this is no more */
        ast_manager_event(chan, EVENT_FLAG_CALL, "Newstate",
index 84eedc81c5d0b7f830a46ab09ac255b69b4222f2..aabf806e749b67c34c83e5e2b79ea5e23b1868b3 100644 (file)
@@ -174,6 +174,7 @@ static AST_RWLIST_HEAD_STATIC(devstate_provs, devstate_prov);
 
 struct state_change {
        AST_LIST_ENTRY(state_change) list;
+       enum ast_devstate_cache cachable;
        char device[1];
 };
 
@@ -191,6 +192,7 @@ struct devstate_change {
        AST_LIST_ENTRY(devstate_change) entry;
        uint32_t state;
        struct ast_eid eid;
+       enum ast_devstate_cache cachable;
        char device[1];
 };
 
@@ -424,7 +426,7 @@ static int getproviderstate(const char *provider, const char *address)
        return res;
 }
 
-static void devstate_event(const char *device, enum ast_device_state state)
+static void devstate_event(const char *device, enum ast_device_state state, int cachable)
 {
        struct ast_event *event;
        enum ast_event_type event_type;
@@ -440,18 +442,23 @@ static void devstate_event(const char *device, enum ast_device_state state)
        ast_debug(3, "device '%s' state '%d'\n", device, state);
 
        if (!(event = ast_event_new(event_type,
-                       AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
-                       AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, state,
-                       AST_EVENT_IE_END))) {
+                                   AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
+                                   AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, state,
+                                   AST_EVENT_IE_CACHABLE, AST_EVENT_IE_PLTYPE_UINT, cachable,
+                                   AST_EVENT_IE_END))) {
                return;
        }
 
-       ast_event_queue_and_cache(event);
+       if (cachable) {
+               ast_event_queue_and_cache(event);
+       } else {
+               ast_event_queue(event);
+       }
 }
 
 /*! Called by the state change thread to find out what the state is, and then
  *  to queue up the state change event */
-static void do_state_change(const char *device)
+static void do_state_change(const char *device, int cachable)
 {
        enum ast_device_state state;
 
@@ -459,10 +466,10 @@ static void do_state_change(const char *device)
 
        ast_debug(3, "Changing state for %s - state %d (%s)\n", device, state, ast_devstate2str(state));
 
-       devstate_event(device, state);
+       devstate_event(device, state, cachable);
 }
 
-int ast_devstate_changed_literal(enum ast_device_state state, const char *device)
+int ast_devstate_changed_literal(enum ast_device_state state, enum ast_devstate_cache cachable, const char *device)
 {
        struct state_change *change;
 
@@ -483,14 +490,15 @@ int ast_devstate_changed_literal(enum ast_device_state state, const char *device
         */
 
        if (state != AST_DEVICE_UNKNOWN) {
-               devstate_event(device, state);
+               devstate_event(device, state, cachable);
        } else if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) {
                /* we could not allocate a change struct, or */
                /* there is no background thread, so process the change now */
-               do_state_change(device);
+               do_state_change(device, cachable);
        } else {
                /* queue the change */
                strcpy(change->device, device);
+               change->cachable = cachable;
                AST_LIST_LOCK(&state_changes);
                AST_LIST_INSERT_TAIL(&state_changes, change, list);
                ast_cond_signal(&change_pending);
@@ -502,10 +510,10 @@ int ast_devstate_changed_literal(enum ast_device_state state, const char *device
 
 int ast_device_state_changed_literal(const char *dev)
 {
-       return ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, dev);
+       return ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, dev);
 }
 
-int ast_devstate_changed(enum ast_device_state state, const char *fmt, ...) 
+int ast_devstate_changed(enum ast_device_state state, enum ast_devstate_cache cachable, const char *fmt, ...)
 {
        char buf[AST_MAX_EXTENSION];
        va_list ap;
@@ -514,7 +522,7 @@ int ast_devstate_changed(enum ast_device_state state, const char *fmt, ...)
        vsnprintf(buf, sizeof(buf), fmt, ap);
        va_end(ap);
 
-       return ast_devstate_changed_literal(state, buf);
+       return ast_devstate_changed_literal(state, cachable, buf);
 }
 
 int ast_device_state_changed(const char *fmt, ...) 
@@ -526,7 +534,7 @@ int ast_device_state_changed(const char *fmt, ...)
        vsnprintf(buf, sizeof(buf), fmt, ap);
        va_end(ap);
 
-       return ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, buf);
+       return ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, buf);
 }
 
 /*! \brief Go through the dev state change queue and update changes in the dev state thread */
@@ -546,7 +554,7 @@ static void *do_devstate_changes(void *data)
                /* Process each state change */
                while ((current = next)) {
                        next = AST_LIST_NEXT(current, list);
-                       do_state_change(current->device);
+                       do_state_change(current->device, current->cachable);
                        ast_free(current);
                }
        }
@@ -590,7 +598,7 @@ static void devstate_cache_cb(const struct ast_event *event, void *data)
        collection->num_states++;
 }
 
-static void process_collection(const char *device, struct change_collection *collection)
+static void process_collection(const char *device, enum ast_devstate_cache cachable, struct change_collection *collection)
 {
        int i;
        struct ast_devstate_aggregate agg;
@@ -641,7 +649,11 @@ static void process_collection(const char *device, struct change_collection *col
                return;
        }
 
-       ast_event_queue_and_cache(event);
+       if (cachable) {
+               ast_event_queue_and_cache(event);
+       } else {
+               ast_event_queue(event);
+       }
 }
 
 static void handle_devstate_change(struct devstate_change *sc)
@@ -667,7 +679,7 @@ static void handle_devstate_change(struct devstate_change *sc)
        /* Populate the collection of device states from the cache */
        ast_event_dump_cache(tmp_sub);
 
-       process_collection(sc->device, &collection);
+       process_collection(sc->device, sc->cachable, &collection);
 
        ast_event_sub_destroy(tmp_sub);
 }
@@ -696,10 +708,12 @@ static void devstate_change_collector_cb(const struct ast_event *event, void *da
        const char *device;
        const struct ast_eid *eid;
        uint32_t state;
+       enum ast_devstate_cache cachable = AST_DEVSTATE_CACHABLE;
 
        device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
        eid = ast_event_get_ie_raw(event, AST_EVENT_IE_EID);
        state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
+       cachable = ast_event_get_ie_uint(event, AST_EVENT_IE_CACHABLE);
 
        if (ast_strlen_zero(device) || !eid) {
                ast_log(LOG_ERROR, "Invalid device state change event received\n");
@@ -712,6 +726,7 @@ static void devstate_change_collector_cb(const struct ast_event *event, void *da
        strcpy(sc->device, device);
        sc->eid = *eid;
        sc->state = state;
+       sc->cachable = cachable;
 
        ast_mutex_lock(&devstate_collector.lock);
        AST_LIST_INSERT_TAIL(&devstate_collector.devstate_change_q, sc, entry);
index 328dc1e7a45b1fc3217ae5aa56261acc990ca25f..85136c5e18cad9106b67efeb463d445d3fca451a 100644 (file)
@@ -268,6 +268,7 @@ static const struct ie_map {
        [AST_EVENT_IE_CHALLENGE]           = { AST_EVENT_IE_PLTYPE_STR,  "Challenge" },
        [AST_EVENT_IE_RESPONSE]            = { AST_EVENT_IE_PLTYPE_STR,  "Response" },
        [AST_EVENT_IE_EXPECTED_RESPONSE]   = { AST_EVENT_IE_PLTYPE_STR,  "ExpectedResponse" },
+       [AST_EVENT_IE_CACHABLE]            = { AST_EVENT_IE_PLTYPE_UINT,  "Cachable" },
 };
 
 const char *ast_event_get_type_name(const struct ast_event *event)
index 2f08a989c5894d9f37da110cef559656272c0049..00edb768d6165c02e7c816515a471b349eee4caa 100644 (file)
@@ -1094,7 +1094,7 @@ static void notify_metermaids(const char *exten, char *context, enum ast_device_
        ast_debug(4, "Notification of state change to metermaids %s@%s\n to state '%s'", 
                exten, context, ast_devstate2str(state));
 
-       ast_devstate_changed(state, "park:%s@%s", exten, context);
+       ast_devstate_changed(state, AST_DEVSTATE_CACHABLE, "park:%s@%s", exten, context);
 }
 
 /*! \brief metermaids callback from devicestate.c */
index 959c0493f4668e543f13d27efa2c7d85a975e670..7c8518b85ace6ab854e1237b34f92509c2a11e1f 100644 (file)
@@ -575,9 +575,9 @@ static struct ast_calendar_event *destroy_event(struct ast_calendar_event *event
         * but haven't hit the end event yet, go ahead and set the devicestate to the current busy status */
        if (event->bs_start_sched < 0 && event->bs_end_sched >= 0) {
                if (!calendar_is_busy(event->owner)) {
-                       ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Calendar:%s", event->owner->name);
+                       ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Calendar:%s", event->owner->name);
                } else {
-                       ast_devstate_changed(AST_DEVICE_BUSY, "Calendar:%s", event->owner->name);
+                       ast_devstate_changed(AST_DEVICE_BUSY, AST_DEVSTATE_CACHABLE, "Calendar:%s", event->owner->name);
                }
        }
 
@@ -818,9 +818,9 @@ static int calendar_devstate_change(const void *data)
        /* We can have overlapping events, so ignore the event->busy_state and check busy state
         * based on all events in the calendar */
        if (!calendar_is_busy(event->owner)) {
-               ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Calendar:%s", event->owner->name);
+               ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Calendar:%s", event->owner->name);
        } else {
-               ast_devstate_changed(AST_DEVICE_BUSY, "Calendar:%s", event->owner->name);
+               ast_devstate_changed(AST_DEVICE_BUSY, AST_DEVSTATE_CACHABLE, "Calendar:%s", event->owner->name);
        }
 
        event = ast_calendar_unref_event(event);
index 290cedca44a52aeaca6dab45641e78a60629add3..a00b8fe99cd2b61de2d29bb00eaa31b70c9d3784 100644 (file)
@@ -349,7 +349,7 @@ static char *aji_cli_create_leafnode(struct ast_cli_entry *e, int cmd,
 static void aji_create_affiliations(struct aji_client *client, const char *node);
 static iks* aji_pubsub_iq_create(struct aji_client *client, const char *type);
 static void aji_publish_device_state(struct aji_client *client, const char * device,
-       const char *device_state);
+                                    const char *device_state, unsigned int cachable);
 static int aji_handle_pubsub_error(void *data, ikspak *pak);
 static int aji_handle_pubsub_event(void *data, ikspak *pak);
 static void aji_pubsub_subscribe(struct aji_client *client, const char *node);
@@ -363,7 +363,7 @@ static void aji_publish_mwi(struct aji_client *client, const char *mailbox,
 static void aji_devstate_cb(const struct ast_event *ast_event, void *data);
 static void aji_mwi_cb(const struct ast_event *ast_event, void *data);
 static iks* aji_build_publish_skeleton(struct aji_client *client, const char *node,
-       const char *event_type);
+                                      const char *event_type, unsigned int cachable);
 /* No transports in this version */
 /*
 static int aji_create_transport(char *label, struct aji_client *client);
@@ -3207,6 +3207,7 @@ static void aji_devstate_cb(const struct ast_event *ast_event, void *data)
 {
        const char *device;
        const char *device_state;
+       unsigned int cachable;
        struct aji_client *client;
        if (ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(ast_event, AST_EVENT_IE_EID)))
        {
@@ -3218,7 +3219,8 @@ static void aji_devstate_cb(const struct ast_event *ast_event, void *data)
        client = ASTOBJ_REF((struct aji_client *) data);
        device = ast_event_get_ie_str(ast_event, AST_EVENT_IE_DEVICE);
        device_state = ast_devstate_str(ast_event_get_ie_uint(ast_event, AST_EVENT_IE_STATE));
-       aji_publish_device_state(client, device, device_state);
+       cachable = ast_event_get_ie_uint(ast_event, AST_EVENT_IE_CACHABLE);
+       aji_publish_device_state(client, device, device_state, cachable);
        ASTOBJ_UNREF(client, ast_aji_client_destroy);
 }
 
@@ -3258,11 +3260,13 @@ static void aji_init_event_distribution(struct aji_client *client)
  */
 static int aji_handle_pubsub_event(void *data, ikspak *pak)
 {
-       char *item_id, *device_state, *context;
+       char *item_id, *device_state, *context, *cachable_str;
        int oldmsgs, newmsgs;
        iks *item, *item_content;
        struct ast_eid pubsub_eid;
        struct ast_event *event;
+       unsigned int cachable = AST_DEVSTATE_CACHABLE;
+
        item = iks_find(iks_find(iks_find(pak->x, "event"), "items"), "item");
        if (!item) {
                ast_log(LOG_ERROR, "Could not parse incoming PubSub event\n");
@@ -3277,11 +3281,14 @@ static int aji_handle_pubsub_event(void *data, ikspak *pak)
        }
        if (!strcasecmp(iks_name(item_content), "state")) {
                device_state = iks_find_cdata(item, "state");
+               if ((cachable_str = iks_find_cdata(item, "cachable"))) {
+                       sscanf(cachable_str, "%30d", &cachable);
+               }
                if (!(event = ast_event_new(AST_EVENT_DEVICE_STATE_CHANGE,
-                       AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, item_id, AST_EVENT_IE_STATE,
-                       AST_EVENT_IE_PLTYPE_UINT, ast_devstate_val(device_state), AST_EVENT_IE_EID,
-                       AST_EVENT_IE_PLTYPE_RAW, &pubsub_eid, sizeof(pubsub_eid),
-                       AST_EVENT_IE_END))) {
+                                           AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, item_id, AST_EVENT_IE_STATE,
+                                           AST_EVENT_IE_PLTYPE_UINT, ast_devstate_val(device_state), AST_EVENT_IE_EID,
+                                           AST_EVENT_IE_PLTYPE_RAW, &pubsub_eid, sizeof(pubsub_eid),
+                                           AST_EVENT_IE_END))) {
                        return IKS_FILTER_EAT;
                }
        } else if (!strcasecmp(iks_name(item_content), "mailbox")) {
@@ -3301,7 +3308,13 @@ static int aji_handle_pubsub_event(void *data, ikspak *pak)
                        iks_name(item_content));
                return IKS_FILTER_EAT;
        }
-       ast_event_queue_and_cache(event);
+
+       if (cachable == AST_DEVSTATE_CACHABLE) {
+               ast_event_queue_and_cache(event);
+       } else {
+               ast_event_queue(event);
+       }
+
        return IKS_FILTER_EAT;
 }
 
@@ -3376,7 +3389,7 @@ static void aji_pubsub_subscribe(struct aji_client *client, const char *node)
  * \return iks *
  */
 static iks* aji_build_publish_skeleton(struct aji_client *client, const char *node,
-       const char *event_type)
+                                      const char *event_type, unsigned int cachable)
 {
        iks *request = aji_pubsub_iq_create(client, "set");
        iks *pubsub, *publish, *item;
@@ -3390,8 +3403,24 @@ static iks* aji_build_publish_skeleton(struct aji_client *client, const char *no
        }
        item = iks_insert(publish, "item");
        iks_insert_attrib(item, "id", node);
-       return item;
 
+       if (cachable == AST_DEVSTATE_NOT_CACHABLE) {
+               iks *options, *x, *field_form_type, *field_persist;
+
+               options = iks_insert(pubsub, "publish-options");
+               x = iks_insert(options, "x");
+               iks_insert_attrib(x, "xmlns", "jabber:x:data");
+               iks_insert_attrib(x, "type", "submit");
+               field_form_type = iks_insert(x, "field");
+               iks_insert_attrib(field_form_type, "var", "FORM_TYPE");
+               iks_insert_attrib(field_form_type, "type", "hidden");
+               iks_insert_cdata(iks_insert(field_form_type, "value"), "http://jabber.org/protocol/pubsub#publish-options", 0);
+               field_persist = iks_insert(x, "field");
+               iks_insert_attrib(field_persist, "var", "pubsub#persist_items");
+               iks_insert_cdata(iks_insert(field_persist, "value"), "0", 1);
+       }
+
+       return item;
 }
 
 /*!
@@ -3402,11 +3431,11 @@ static iks* aji_build_publish_skeleton(struct aji_client *client, const char *no
  * \return void
  */
 static void aji_publish_device_state(struct aji_client *client, const char *device,
-       const char *device_state)
+                                    const char *device_state, unsigned int cachable)
 {
-       iks *request = aji_build_publish_skeleton(client, device, "device_state");
+       iks *request = aji_build_publish_skeleton(client, device, "device_state", cachable);
        iks *state;
-       char eid_str[20];
+       char eid_str[20], cachable_str[2];
        if (ast_test_flag(&pubsubflags, AJI_PUBSUB_AUTOCREATE)) {
                if (ast_test_flag(&pubsubflags, AJI_XEP0248)) {
                        aji_create_pubsub_node(client, "leaf", device, "device_state");
@@ -3418,6 +3447,8 @@ static void aji_publish_device_state(struct aji_client *client, const char *devi
        state = iks_insert(request, "state");
        iks_insert_attrib(state, "xmlns", "http://asterisk.org");
        iks_insert_attrib(state, "eid", eid_str);
+       snprintf(cachable_str, sizeof(cachable_str), "%u", cachable);
+       iks_insert_attrib(state, "cachable", cachable_str);
        iks_insert_cdata(state, device_state, strlen(device_state));
        ast_aji_send(client, iks_root(request));
        iks_delete(request);
@@ -3437,7 +3468,7 @@ static void aji_publish_mwi(struct aji_client *client, const char *mailbox,
        char eid_str[20];
        iks *mailbox_node, *request;
        snprintf(full_mailbox, sizeof(full_mailbox), "%s@%s", mailbox, context);
-       request = aji_build_publish_skeleton(client, full_mailbox, "message_waiting");
+       request = aji_build_publish_skeleton(client, full_mailbox, "message_waiting", 1);
        ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
        mailbox_node = iks_insert(request, "mailbox");
        iks_insert_attrib(mailbox_node, "xmlns", "http://asterisk.org");