Migrate a large number of AMI events over to Stasis-Core
authorMatthew Jordan <mjordan@digium.com>
Fri, 24 May 2013 20:44:07 +0000 (20:44 +0000)
committerMatthew Jordan <mjordan@digium.com>
Fri, 24 May 2013 20:44:07 +0000 (20:44 +0000)
This patch moves a number of AMI events over to the Stasis-Core message bus.
This includes:
 * ChanSpyStart/Stop
 * MonitorStart/Stop
 * MusicOnHoldStart/Stop
 * FullyBooted/Reload
 * All Voicemail/MWI related events

In addition, it adds some Stasis-Core and AMI support for generic AMI messages,
refactors the message router in AMI to use a single router with topic
forwarding for the topics that AMI cares about, and refactors MWI message
types and topics to be more name compliant.

Review: https://reviewboard.asterisk.org/r/2532

(closes issue ASTERISK-21462)

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

37 files changed:
CHANGES
apps/app_chanspy.c
apps/app_fax.c
apps/app_minivm.c
apps/app_voicemail.c
channels/chan_dahdi.c
channels/chan_iax2.c
channels/chan_mgcp.c
channels/chan_sip.c
channels/chan_skinny.c
channels/chan_unistim.c
channels/sig_pri.c
include/asterisk/_private.h
include/asterisk/app.h
include/asterisk/json.h
include/asterisk/manager.h
include/asterisk/stasis_channels.h
main/app.c
main/asterisk.c
main/cdr.c
main/cli.c
main/dnsmgr.c
main/enum.c
main/json.c
main/loader.c
main/manager.c
main/manager_channels.c
main/manager_mwi.c [new file with mode: 0644]
main/pbx.c
main/stasis_channels.c
res/parking/parking_manager.c
res/res_fax.c
res/res_jabber.c
res/res_monitor.c
res/res_musiconhold.c
res/res_sip_mwi.c
res/res_xmpp.c

diff --git a/CHANGES b/CHANGES
index ec85f5d..273a470 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -72,6 +72,26 @@ AMI (Asterisk Manager Interface)
    event, the various ChanVariable fields will contain a suffix that specifies
    which channel they correspond to.
 
+ * All "Reload" events have been consolidated into a single event type. This
+   event will always contain a Module field specifying the name of the module
+   and a Status field denoting the result of the reload. All modules now issue
+   this event when being reloaded.
+
+ * The "ModuleLoadReport" event has been removed. Most AMI connections would
+   fail to receive this event due to being connected after modules have loaded.
+   AMI connections that want to know when Asterisk is ready should listen for
+   the "FullyBooted" event.
+
+ * app_fax now sends the same send fax/receive fax events as res_fax. The
+   "FaxSent" event is now the "SendFAX" event, and the "FaxReceived" event is
+   now the "ReceiveFAX" event.
+
+ * The MusicOnHold event is now two events: MusicOnHoldStart and
+   MusicOnHoldStop. The sub type field has been removed.
+
+ * The JabberEvent event has been removed. It is not AMI's purpose to be a
+   carrier for another protocol.
+
  * The AMI 'Status' response event to the AMI Status action replaces the
    BridgedChannel and BridgedUniqueid headers with the BridgeID header to
    indicate what bridge the channel is currently in.
index 8e45907..4e241b9 100644 (file)
@@ -55,6 +55,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/lock.h"
 #include "asterisk/options.h"
 #include "asterisk/autochan.h"
+#include "asterisk/stasis_channels.h"
+#include "asterisk/json.h"
 
 #define AST_NAME_STRLEN 256
 #define NUM_SPYGROUPS 128
@@ -188,6 +190,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                </description>
                <see-also>
                        <ref type="application">ExtenSpy</ref>
+                       <ref type="managerEvent">ChanSpyStart</ref>
+                       <ref type="managerEvent">ChanSpyStop</ref>
                </see-also>
        </application>
        <application name="ExtenSpy" language="en_US">
@@ -322,9 +326,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                </description>
                <see-also>
                        <ref type="application">ChanSpy</ref>
+                       <ref type="managerEvent">ChanSpyStart</ref>
+                       <ref type="managerEvent">ChanSpyStop</ref>
                </see-also>
        </application>
-       
        <application name="DAHDIScan" language="en_US">
                <synopsis>
                        Scan DAHDI channels to monitor calls.
@@ -338,6 +343,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        <para>Allows a call center manager to monitor DAHDI channels in a
                        convenient way.  Use <literal>#</literal> to select the next channel and use <literal>*</literal> to exit.</para>
                </description>
+               <see-also>
+                       <ref type="managerEvent">ChanSpyStart</ref>
+                       <ref type="managerEvent">ChanSpyStop</ref>
+               </see-also>
        </application>
  ***/
 
@@ -512,6 +521,68 @@ static void change_spy_mode(const char digit, struct ast_flags *flags)
        }
 }
 
+static int pack_channel_into_message(struct ast_channel *chan, const char *role,
+                                                                        struct ast_multi_channel_blob *payload)
+{
+       RAII_VAR(struct ast_channel_snapshot *, snapshot,
+                       ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan)),
+                       ao2_cleanup);
+
+       if (!snapshot) {
+               return -1;
+       }
+       ast_multi_channel_blob_add_channel(payload, role, snapshot);
+       return 0;
+}
+
+/*! \internal
+ * \brief Publish the chanspy message over Stasis-Core
+ * \param spyer The channel doing the spying
+ * \param spyee Who is being spied upon
+ * \start start If non-zero, the spying is starting. Otherwise, the spyer is
+ * finishing
+ */
+static void publish_chanspy_message(struct ast_channel *spyer,
+                                                                       struct ast_channel *spyee,
+                                                                       int start)
+{
+       RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
+       RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
+       RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
+
+       if (!spyer) {
+               ast_log(AST_LOG_WARNING, "Attempt to publish ChanSpy message for NULL spyer channel\n");
+               return;
+       }
+       blob = ast_json_null();
+       if (!blob) {
+               return;
+       }
+
+       payload = ast_multi_channel_blob_create(blob);
+       if (!payload) {
+               return;
+       }
+
+       if (pack_channel_into_message(spyer, "spyer_channel", payload)) {
+               return;
+       }
+
+       if (spyee) {
+               if (pack_channel_into_message(spyee, "spyee_channel", payload)) {
+                       return;
+               }
+       }
+
+       message = stasis_message_create(
+                       start ? ast_channel_chanspy_start_type(): ast_channel_chanspy_stop_type(),
+                                       payload);
+       if (!message) {
+               return;
+       }
+       stasis_publish(ast_channel_topic(spyer), message);
+}
+
 static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_autochan,
        int *volfactor, int fd, struct spy_dtmf_options *user_options, struct ast_flags *flags,
        char *exitcontext)
@@ -524,38 +595,22 @@ static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_auto
        struct ast_silence_generator *silgen = NULL;
        struct ast_autochan *spyee_bridge_autochan = NULL;
        const char *spyer_name;
-       struct ast_channel *chans[] = { chan, spyee_autochan->chan };
-
-       ast_channel_lock(chan);
-       spyer_name = ast_strdupa(ast_channel_name(chan));
-       ast_channel_unlock(chan);
-
-       /* We now hold the channel lock on spyee */
 
        if (ast_check_hangup(chan) || ast_check_hangup(spyee_autochan->chan) ||
                        ast_test_flag(ast_channel_flags(spyee_autochan->chan), AST_FLAG_ZOMBIE)) {
                return 0;
        }
 
+       ast_channel_lock(chan);
+       spyer_name = ast_strdupa(ast_channel_name(chan));
+       ast_channel_unlock(chan);
+
        ast_channel_lock(spyee_autochan->chan);
        name = ast_strdupa(ast_channel_name(spyee_autochan->chan));
        ast_channel_unlock(spyee_autochan->chan);
 
        ast_verb(2, "Spying on channel %s\n", name);
-       /*** DOCUMENTATION
-               <managerEventInstance>
-                       <synopsis>Raised when a channel has started spying on another channel.</synopsis>
-                       <see-also>
-                               <ref type="application">ChanSpy</ref>
-                               <ref type="application">ExtenSpy</ref>
-                               <ref type="managerEvent">ChanSpyStop</ref>
-                       </see-also>
-               </managerEventInstance>
-       ***/
-       ast_manager_event_multichan(EVENT_FLAG_CALL, "ChanSpyStart", 2, chans,
-                       "SpyerChannel: %s\r\n"
-                       "SpyeeChannel: %s\r\n",
-                       spyer_name, name);
+       publish_chanspy_message(chan, spyee_autochan->chan, 1);
 
        memset(&csth, 0, sizeof(csth));
        ast_copy_flags(&csth.flags, flags, AST_FLAGS_ALL);
@@ -740,15 +795,7 @@ static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_auto
        }
 
        ast_verb(2, "Done Spying on channel %s\n", name);
-       /*** DOCUMENTATION
-               <managerEventInstance>
-                       <synopsis>Raised when a channel has stopped spying on another channel.</synopsis>
-                       <see-also>
-                               <ref type="managerEvent">ChanSpyStart</ref>
-                       </see-also>
-               </managerEventInstance>
-       ***/
-       ast_manager_event(chan, EVENT_FLAG_CALL, "ChanSpyStop", "SpyeeChannel: %s\r\n", name);
+       publish_chanspy_message(chan, NULL, 0);
 
        return running;
 }
index adee8f4..a7b9e53 100644 (file)
@@ -43,7 +43,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/app.h"
 #include "asterisk/dsp.h"
 #include "asterisk/module.h"
-#include "asterisk/manager.h"
+#include "asterisk/stasis.h"
+#include "asterisk/stasis_channels.h"
 
 /*** DOCUMENTATION
        <application name="SendFAX" language="en_US" module="app_fax">
@@ -202,6 +203,9 @@ static int t38_tx_packet_handler(t38_core_state_t *s, void *user_data, const uin
 
 static void phase_e_handler(t30_state_t *f, void *user_data, int result)
 {
+       RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
+       RAII_VAR(struct ast_json *, json_filenames, NULL, ast_json_unref);
+       RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
        const char *local_ident;
        const char *far_ident;
        char buf[20];
@@ -251,32 +255,24 @@ static void phase_e_handler(t30_state_t *f, void *user_data, int result)
        ast_debug(1, "  Image resolution:  %d x %d\n", stat.x_resolution, stat.y_resolution);
        ast_debug(1, "  Transfer Rate:     %d\n", stat.bit_rate);
 
-       ast_manager_event(s->chan, EVENT_FLAG_CALL,
-               s->direction ? "FaxSent" : "FaxReceived",
-               "Channel: %s\r\n"
-               "Exten: %s\r\n"
-               "CallerID: %s\r\n"
-               "CallerIDName: %s\r\n"
-               "ConnectedLineNum: %s\r\n"
-               "ConnectedLineName: %s\r\n"
-               "RemoteStationID: %s\r\n"
-               "LocalStationID: %s\r\n"
-               "PagesTransferred: %d\r\n"
-               "Resolution: %d\r\n"
-               "TransferRate: %d\r\n"
-               "FileName: %s\r\n",
-               ast_channel_name(s->chan),
-               ast_channel_exten(s->chan),
-               S_COR(ast_channel_caller(s->chan)->id.number.valid, ast_channel_caller(s->chan)->id.number.str, ""),
-               S_COR(ast_channel_caller(s->chan)->id.name.valid, ast_channel_caller(s->chan)->id.name.str, ""),
-               S_COR(ast_channel_connected(s->chan)->id.number.valid, ast_channel_connected(s->chan)->id.number.str, ""),
-               S_COR(ast_channel_connected(s->chan)->id.name.valid, ast_channel_connected(s->chan)->id.name.str, ""),
-               far_ident,
-               local_ident,
-               pages_transferred,
-               stat.y_resolution,
-               stat.bit_rate,
-               s->file_name);
+       json_filenames = ast_json_pack("[s]", s->file_name);
+       if (!json_filenames) {
+               return;
+       }
+       ast_json_ref(json_filenames);
+       json_object = ast_json_pack("{s: s, s: s, s: s, s: i, s: i, s: i, s: o}",
+                       "type", s->direction ? "send" : "receive",
+                       "remote_station_id", far_ident,
+                       "local_station_id", local_ident,
+                       "fax_pages", pages_transferred,
+                       "fax_resolution", stat.y_resolution,
+                       "fax_bitrate", stat.bit_rate,
+                       "filenames", json_filenames);
+       message = ast_channel_cached_blob_create(s->chan, ast_channel_fax_type(), json_object);
+       if (!message) {
+               return;
+       }
+       stasis_publish(ast_channel_topic(s->chan), message);
 }
 
 /* === Helper functions to configure fax === */
index 53c5f09..ba6d6e5 100644 (file)
@@ -166,14 +166,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/say.h"
 #include "asterisk/module.h"
 #include "asterisk/app.h"
-#include "asterisk/manager.h"
 #include "asterisk/dsp.h"
 #include "asterisk/localtime.h"
 #include "asterisk/cli.h"
 #include "asterisk/utils.h"
 #include "asterisk/linkedlists.h"
 #include "asterisk/callerid.h"
-#include "asterisk/event.h"
+#include "asterisk/stasis.h"
+#include "asterisk/stasis_channels.h"
+#include "asterisk/json.h"
 
 /*** DOCUMENTATION
 <application name="MinivmRecord" language="en_US">
@@ -495,7 +496,23 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                <ref type="function">MINIVMCOUNTER</ref>
        </see-also>
 </function>
-
+       <managerEvent language="en_US" name="MiniVoiceMail">
+               <managerEventInstance class="EVENT_FLAG_CALL">
+                       <synopsis>Raised when a notification is sent out by a MiniVoiceMail application</synopsis>
+                       <syntax>
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
+                               <parameter name="Action">
+                                       <para>What action was taken. Currently, this will always be <literal>SentNotification</literal></para>
+                               </parameter>
+                               <parameter name="Mailbox">
+                                       <para>The mailbox that the notification was about, specified as <literal>mailbox</literal>@<literal>context</literal></para>
+                               </parameter>
+                               <parameter name="Counter">
+                                       <para>A message counter derived from the <literal>MVM_COUNTER</literal> channel variable.</para>
+                               </parameter>
+                       </syntax>
+               </managerEventInstance>
+       </managerEvent>
 ***/
 
 #ifndef TRUE
@@ -1761,6 +1778,9 @@ static void run_externnotify(struct ast_channel *chan, struct minivm_account *vm
  * \brief Send message to voicemail account owner */
 static int notify_new_message(struct ast_channel *chan, const char *templatename, struct minivm_account *vmu, const char *filename, long duration, const char *format, char *cidnum, char *cidname)
 {
+       RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
+       RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_mwi_state *, mwi_state, NULL, ao2_cleanup);
        char *stringp;
        struct minivm_template *etemplate;
        char *messageformat;
@@ -1826,8 +1846,26 @@ static int notify_new_message(struct ast_channel *chan, const char *templatename
                res = sendmail(etemplate, vmu, cidnum, cidname, filename, messageformat, duration, etemplate->attachment, MVM_MESSAGE_PAGE, counter);
        }
 
-       ast_manager_event(chan, EVENT_FLAG_CALL, "MiniVoiceMail", "Action: SentNotification\rn\nMailbox: %s@%s\r\nCounter: %s\r\n", vmu->username, vmu->domain, counter);
+       mwi_state = ast_mwi_create(vmu->username, vmu->domain);
+       if (!mwi_state) {
+               goto notify_cleanup;
+       }
+       mwi_state->snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan));
+
+       json_object = ast_json_pack("{s: s, s: s}",
+                       "Event", "MiniVoiceMail"
+                       "Action", "SentNotification",
+                       "Counter", counter);
+       if (!json_object) {
+               goto notify_cleanup;
+       }
+       message = ast_mwi_blob_create(mwi_state, ast_mwi_vm_app_type(), json_object);
+       if (!message) {
+               goto notify_cleanup;
+       }
+       stasis_publish(ast_mwi_topic(mwi_state->uniqueid), message);
 
+notify_cleanup:
        run_externnotify(chan, vmu);            /* Run external notification */
 
        if (etemplate->locale) {
@@ -2011,7 +2049,7 @@ static int leave_voicemail(struct ast_channel *chan, char *username, struct leav
 
 /*!\internal
  * \brief Queue a message waiting event */
-static void queue_mwi_event(const char *mbx, const char *ctx, int urgent, int new, int old)
+static void queue_mwi_event(const char *channel_id, const char *mbx, const char *ctx, int urgent, int new, int old)
 {
        char *mailbox, *context;
 
@@ -2021,7 +2059,7 @@ static void queue_mwi_event(const char *mbx, const char *ctx, int urgent, int ne
                context = "default";
        }
 
-       stasis_publish_mwi_state(mailbox, context, new + urgent, old);
+       ast_publish_mwi_state_channel(mailbox, context, new + urgent, old, channel_id);
 }
 
 /*!\internal
@@ -2056,7 +2094,7 @@ static int minivm_mwi_exec(struct ast_channel *chan, const char *data)
                ast_log(LOG_ERROR, "Need mailbox@context as argument. Sorry. Argument 0 %s\n", argv[0]);
                return -1;
        }
-       queue_mwi_event(mailbox, domain, atoi(argv[1]), atoi(argv[2]), atoi(argv[3]));
+       queue_mwi_event(ast_channel_uniqueid(chan), mailbox, domain, atoi(argv[1]), atoi(argv[2]), atoi(argv[3]));
 
        return res;
 }
@@ -2078,7 +2116,6 @@ static int minivm_notify_exec(struct ast_channel *chan, const char *data)
        const char *filename;
        const char *format;
        const char *duration_string;
-
        if (ast_strlen_zero(data))  {
                ast_log(LOG_ERROR, "Minivm needs at least an account argument \n");
                return -1;
index b3ceeeb..90458bb 100644 (file)
@@ -7741,7 +7741,7 @@ static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu,
        return cmd;
 }
 
-static void queue_mwi_event(const char *box, int urgent, int new, int old)
+static void queue_mwi_event(const char *channel_id, const char *box, int urgent, int new, int old)
 {
        char *mailbox, *context;
 
@@ -7752,7 +7752,7 @@ static void queue_mwi_event(const char *box, int urgent, int new, int old)
                context = "default";
        }
 
-       stasis_publish_mwi_state(mailbox, context, new + urgent, old);
+       ast_publish_mwi_state_channel(mailbox, context, new + urgent, old, channel_id);
 }
 
 /*!
@@ -7842,32 +7842,7 @@ static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu,
        if (ast_app_has_voicemail(ext_context, NULL)) 
                ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
 
-       queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
-
-       /*** DOCUMENTATION
-               <managerEventInstance>
-                       <synopsis>Raised when a new message has been left in a voicemail mailbox.</synopsis>
-                       <syntax>
-                               <parameter name="Mailbox">
-                                       <para>The mailbox with the new message, specified as <emphasis>mailbox</emphasis>@<emphasis>context</emphasis></para>
-                               </parameter>
-                               <parameter name="Waiting">
-                                       <para>Whether or not the mailbox has access to a voicemail application.</para>
-                               </parameter>
-                               <parameter name="New">
-                                       <para>The number of new messages.</para>
-                               </parameter>
-                               <parameter name="Old">
-                                       <para>The number of old messages.</para>
-                               </parameter>
-                       </syntax>
-               </managerEventInstance>
-       ***/
-       ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting",
-                       "Mailbox: %s@%s\r\n"
-                       "Waiting: %d\r\n"
-                       "New: %d\r\n"
-                       "Old: %d\r\n", vmu->mailbox, vmu->context, ast_app_has_voicemail(ext_context, NULL), newmsgs, oldmsgs);
+       queue_mwi_event(ast_channel_uniqueid(chan), ext_context, urgentmsgs, newmsgs, oldmsgs);
        run_externnotify(vmu->context, vmu->mailbox, flag);
 
 #ifdef IMAP_STORAGE
@@ -11538,16 +11513,10 @@ out:
        if (valid) {
                int new = 0, old = 0, urgent = 0;
                snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
-               /*** DOCUMENTATION
-                       <managerEventInstance>
-                               <synopsis>Raised when a user has finished listening to their messages.</synopsis>
-                       </managerEventInstance>
-               ***/
-               ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
                /* Urgent flag not passwd to externnotify here */
                run_externnotify(vmu->context, vmu->mailbox, NULL);
                ast_app_inboxcount2(ext_context, &urgent, &new, &old);
-               queue_mwi_event(ext_context, urgent, new, old);
+               queue_mwi_event(ast_channel_uniqueid(chan), ext_context, urgent, new, old);
        }
 #ifdef IMAP_STORAGE
        /* expunge message - use UID Expunge if supported on IMAP server*/
@@ -11766,7 +11735,7 @@ static int append_mailbox(const char *context, const char *box, const char *data
        strcat(mailbox_full, context);
 
        inboxcount2(mailbox_full, &urgent, &new, &old);
-       queue_mwi_event(mailbox_full, urgent, new, old);
+       queue_mwi_event(NULL, mailbox_full, urgent, new, old);
 
        return 0;
 }
@@ -12502,7 +12471,7 @@ static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
                mwi_sub->old_urgent = urgent;
                mwi_sub->old_new = new;
                mwi_sub->old_old = old;
-               queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
+               queue_mwi_event(NULL, mwi_sub->mailbox, urgent, new, old);
                run_externnotify(NULL, mwi_sub->mailbox, NULL);
        }
 }
@@ -12647,7 +12616,7 @@ static void mwi_event_cb(void *userdata, struct stasis_subscription *sub, struct
        }
 
        change = stasis_message_data(msg);
-       if (change->topic == stasis_mwi_topic_all()) {
+       if (change->topic == ast_mwi_topic_all()) {
                return;
        }
 
@@ -12668,10 +12637,10 @@ static int dump_cache(void *obj, void *arg, int flags)
 static void start_poll_thread(void)
 {
        int errcode;
-       mwi_sub_sub = stasis_subscribe(stasis_mwi_topic_all(), mwi_event_cb, NULL);
+       mwi_sub_sub = stasis_subscribe(ast_mwi_topic_all(), mwi_event_cb, NULL);
 
        if (mwi_sub_sub) {
-               struct ao2_container *cached = stasis_cache_dump(stasis_mwi_topic_cached(), stasis_subscription_change_type());
+               struct ao2_container *cached = stasis_cache_dump(ast_mwi_topic_cached(), stasis_subscription_change_type());
                if (cached) {
                        ao2_callback(cached, OBJ_MULTIPLE | OBJ_NODATA, dump_cache, NULL);
                }
@@ -15263,7 +15232,7 @@ static void notify_new_state(struct ast_vm_user *vmu)
        snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
        run_externnotify(vmu->context, vmu->mailbox, NULL);
        ast_app_inboxcount2(ext_context, &urgent, &new, &old);
-       queue_mwi_event(ext_context, urgent, new, old);
+       queue_mwi_event(NULL, ext_context, urgent, new, old);
 }
 
 static int vm_msg_forward(const char *from_mailbox,
index 1f9ee5a..03279a0 100644 (file)
@@ -3762,7 +3762,7 @@ static void notify_message(char *mailbox_full, int thereornot)
        if (ast_strlen_zero(context))
                context = "default";
 
-       stasis_publish_mwi_state(mailbox, context, thereornot, thereornot);
+       ast_publish_mwi_state(mailbox, context, thereornot, thereornot);
 
        if (!ast_strlen_zero(mailbox) && !ast_strlen_zero(mwimonitornotify)) {
                snprintf(s, sizeof(s), "%s %s %d", mwimonitornotify, mailbox, thereornot);
@@ -5427,10 +5427,10 @@ static int has_voicemail(struct dahdi_pvt *p)
        }
 
        ast_str_set(&uniqueid, 0, "%s@%s", mailbox, context);
-       mwi_message = stasis_cache_get(stasis_mwi_topic_cached(), stasis_mwi_state_type(), ast_str_buffer(uniqueid));
+       mwi_message = stasis_cache_get(ast_mwi_topic_cached(), ast_mwi_state_type(), ast_str_buffer(uniqueid));
 
        if (mwi_message) {
-               struct stasis_mwi_state *mwi_state = stasis_message_data(mwi_message);
+               struct ast_mwi_state *mwi_state = stasis_message_data(mwi_message);
                new_msgs = mwi_state->new_msgs;
        } else {
                new_msgs = ast_app_has_voicemail(p->mailbox, NULL);
@@ -13235,7 +13235,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
 
                        ast_str_set(&uniqueid, 0, "%s@%s", mailbox, context);
 
-                       mailbox_specific_topic = stasis_mwi_topic(ast_str_buffer(uniqueid));
+                       mailbox_specific_topic = ast_mwi_topic(ast_str_buffer(uniqueid));
                        if (mailbox_specific_topic) {
                                tmp->mwi_event_sub = stasis_subscribe(mailbox_specific_topic, mwi_event_cb, NULL);
                        }
index 49ce66c..7f98159 100644 (file)
@@ -8772,10 +8772,10 @@ static int update_registry(struct sockaddr_in *sin, int callno, char *devtype, i
                        }
 
                        ast_str_set(&uniqueid, 0, "%s@%s", mailbox, context);
-                       msg = stasis_cache_get(stasis_mwi_topic_cached(), stasis_mwi_state_type(), ast_str_buffer(uniqueid));
+                       msg = stasis_cache_get(ast_mwi_topic_cached(), ast_mwi_state_type(), ast_str_buffer(uniqueid));
 
                        if (msg) {
-                               struct stasis_mwi_state *mwi_state = stasis_message_data(msg);
+                               struct ast_mwi_state *mwi_state = stasis_message_data(msg);
                                new = mwi_state->new_msgs;
                                old = mwi_state->old_msgs;
                        } else { /* Fall back on checking the mailbox directly */
@@ -12555,7 +12555,7 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, st
 
                ast_str_set(&uniqueid, 0, "%s@%s", mailbox, context);
 
-               mailbox_specific_topic = stasis_mwi_topic(ast_str_buffer(uniqueid));
+               mailbox_specific_topic = ast_mwi_topic(ast_str_buffer(uniqueid));
                if (mailbox_specific_topic) {
                        peer->mwi_event_sub = stasis_subscribe(mailbox_specific_topic, mwi_event_cb, NULL);
                }
index 1f08307..c6b4410 100644 (file)
@@ -507,10 +507,10 @@ static int has_voicemail(struct mgcp_endpoint *p)
 
        ast_str_set(&uniqueid, 0, "%s@%s", mbox, cntx);
 
-       msg = stasis_cache_get(stasis_mwi_topic_cached(), stasis_mwi_state_type(), ast_str_buffer(uniqueid));
+       msg = stasis_cache_get(ast_mwi_topic_cached(), ast_mwi_state_type(), ast_str_buffer(uniqueid));
 
        if (msg) {
-               struct stasis_mwi_state *mwi_state = stasis_message_data(msg);
+               struct ast_mwi_state *mwi_state = stasis_message_data(msg);
                new_msgs = mwi_state->new_msgs;
        } else {
                new_msgs = ast_app_has_voicemail(p->mailbox, NULL);
@@ -4169,7 +4169,7 @@ static struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v)
                                        ast_str_reset(uniqueid);
                                        ast_str_set(&uniqueid, 0, "%s@%s", mbox, cntx);
 
-                                       mailbox_specific_topic = stasis_mwi_topic(ast_str_buffer(uniqueid));
+                                       mailbox_specific_topic = ast_mwi_topic(ast_str_buffer(uniqueid));
                                        if (mailbox_specific_topic) {
                                                e->mwi_event_sub = stasis_subscribe(mailbox_specific_topic, mwi_event_cb, NULL);
                                        }
index f7a528b..77cd9c2 100644 (file)
@@ -16724,7 +16724,7 @@ static void mwi_event_cb(void *userdata, struct stasis_subscription *sub, struct
                ao2_cleanup(peer);
                return;
        }
-       if (stasis_mwi_state_type() == stasis_message_type(msg)) {
+       if (ast_mwi_state_type() == stasis_message_type(msg)) {
                sip_send_mwi_to_peer(peer, 0);
        }
 }
@@ -24767,7 +24767,7 @@ static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, str
                        char *old = strsep(&c, " ");
                        char *new = strsep(&old, "/");
 
-                       stasis_publish_mwi_state(mailbox, "SIP_Remote", atoi(new), atoi(old));
+                       ast_publish_mwi_state(mailbox, "SIP_Remote", atoi(new), atoi(old));
 
                        transmit_response(p, "200 OK", req);
                } else {
@@ -27420,7 +27420,7 @@ static void add_peer_mwi_subs(struct sip_peer *peer)
                ast_str_reset(uniqueid);
                ast_str_set(&uniqueid, 0, "%s@%s", mailbox->mailbox, S_OR(mailbox->context, "default"));
 
-               mailbox_specific_topic = stasis_mwi_topic(ast_str_buffer(uniqueid));
+               mailbox_specific_topic = ast_mwi_topic(ast_str_buffer(uniqueid));
                if (mailbox_specific_topic) {
                        ao2_ref(peer, +1);
                        mailbox->event_sub = stasis_subscribe(mailbox_specific_topic, mwi_event_cb, peer);
@@ -28630,12 +28630,12 @@ static int get_cached_mwi(struct sip_peer *peer, int *new, int *old)
        in_cache = 0;
        AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) {
                RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
-               struct stasis_mwi_state *mwi_state;
+               struct ast_mwi_state *mwi_state;
 
                ast_str_reset(uniqueid);
                ast_str_set(&uniqueid, 0, "%s@%s", mailbox->mailbox, S_OR(mailbox->context, "default"));
 
-               msg = stasis_cache_get(stasis_mwi_topic_cached(), stasis_mwi_state_type(), ast_str_buffer(uniqueid));
+               msg = stasis_cache_get(ast_mwi_topic_cached(), ast_mwi_state_type(), ast_str_buffer(uniqueid));
                if (!msg) {
                        continue;
                }
index cd194d5..f139804 100644 (file)
@@ -3535,8 +3535,8 @@ static void mwi_event_cb(void *userdata, struct stasis_subscription *sub, struct
                return;
        }
 
-       if (msg && stasis_mwi_state_type() == stasis_message_type(msg)) {
-               struct stasis_mwi_state *mwi_state = stasis_message_data(msg);
+       if (msg && ast_mwi_state_type() == stasis_message_type(msg)) {
+               struct ast_mwi_state *mwi_state = stasis_message_data(msg);
                l->newmsgs = mwi_state->new_msgs;
        }
 
@@ -8214,7 +8214,7 @@ static struct skinny_line *config_line(const char *lname, struct ast_variable *v
 
                ast_str_set(&uniqueid, 0, "%s@%s", cfg_mailbox, cfg_context);
 
-               mailbox_specific_topic = stasis_mwi_topic(ast_str_buffer(uniqueid));
+               mailbox_specific_topic = ast_mwi_topic(ast_str_buffer(uniqueid));
                if (mailbox_specific_topic) {
                        l->mwi_event_sub = stasis_subscribe(mailbox_specific_topic, mwi_event_cb, l);
                }
index 88332ec..42a71ba 100644 (file)
@@ -5508,10 +5508,10 @@ static int unistim_send_mwi_to_peer(struct unistim_line *peer, unsigned int tick
 
        ast_str_set(&uniqueid, 0, "%s@%s", mailbox, context);
 
-       msg = stasis_cache_get(stasis_mwi_topic_cached(), stasis_mwi_state_type(), ast_str_buffer(uniqueid));
+       msg = stasis_cache_get(ast_mwi_topic_cached(), ast_mwi_state_type(), ast_str_buffer(uniqueid));
 
        if (msg) {
-               struct stasis_mwi_state *mwi_state = stasis_message_data(msg);
+               struct ast_mwi_state *mwi_state = stasis_message_data(msg);
                new = mwi_state->new_msgs;
        } else { /* Fall back on checking the mailbox directly */
                new = ast_app_has_voicemail(peer->mailbox, "INBOX");
index 353b7f3..db0c920 100644 (file)
@@ -8768,9 +8768,9 @@ static void sig_pri_mwi_event_cb(void *userdata, struct stasis_subscription *sub
        const char *mbox_number;
        int num_messages;
        int idx;
-       struct stasis_mwi_state *mwi_state;
+       struct ast_mwi_state *mwi_state;
 
-       if (stasis_mwi_state_type() != stasis_message_type(msg)) {
+       if (ast_mwi_state_type() != stasis_message_type(msg)) {
                return;
        }
 
@@ -8816,7 +8816,7 @@ static void sig_pri_mwi_cache_update(struct sig_pri_span *pri)
 {
        int idx;
        struct ast_str *uniqueid = ast_str_alloca(AST_MAX_MAILBOX_UNIQUEID);
-       struct stasis_mwi_state *mwi_state;
+       struct ast_mwi_state *mwi_state;
 
        for (idx = 0; idx < ARRAY_LEN(pri->mbox); ++idx) {
                RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
@@ -8828,7 +8828,7 @@ static void sig_pri_mwi_cache_update(struct sig_pri_span *pri)
                ast_str_reset(uniqueid);
                ast_str_set(&uniqueid, 0, "%s@%s", pri->mbox[idx].number, pri->mbox[idx].context);
 
-               msg = stasis_cache_get(stasis_mwi_topic_cached(), stasis_mwi_state_type(), ast_str_buffer(uniqueid));
+               msg = stasis_cache_get(ast_mwi_topic_cached(), ast_mwi_state_type(), ast_str_buffer(uniqueid));
                if (!msg) {
                        /* No cached event for this mailbox. */
                        continue;
@@ -9002,7 +9002,7 @@ int sig_pri_start_pri(struct sig_pri_span *pri)
                ast_str_set(&mwi_description, -1, "%s span %d[%d] MWI mailbox %s@%s",
                        sig_pri_cc_type_name, pri->span, i, mbox_number, mbox_context);
 
-               mailbox_specific_topic = stasis_mwi_topic(ast_str_buffer(uniqueid));
+               mailbox_specific_topic = ast_mwi_topic(ast_str_buffer(uniqueid));
                if (mailbox_specific_topic) {
                        pri->mbox[i].sub = stasis_subscribe(mailbox_specific_topic, sig_pri_mwi_event_cb, pri);
                }
index 3fe35e5..1d08449 100644 (file)
@@ -52,6 +52,20 @@ void ast_msg_shutdown(void);        /*!< Provided by message.c */
 int aco_init(void);             /*!< Provided by config_options.c */
 
 /*!
+ * \since 12
+ * \brief Possible return types for \ref ast_module_reload
+ */
+enum ast_module_reload_result {
+       AST_MODULE_RELOAD_SUCCESS = 0,      /*!< The module was reloaded succesfully */
+       AST_MODULE_RELOAD_QUEUED,           /*!< The module reload request was queued */
+       AST_MODULE_RELOAD_NOT_FOUND,        /*!< The requested module was not found */
+       AST_MODULE_RELOAD_ERROR,            /*!< An error occurred while reloading the module */
+       AST_MODULE_RELOAD_IN_PROGRESS,      /*!< A module reload request is already in progress */
+       AST_MODULE_RELOAD_UNINITIALIZED,    /*!< The module has not been initialized */
+       AST_MODULE_RELOAD_NOT_IMPLEMENTED,  /*!< This module doesn't support reloading */
+};
+
+/*!
  * \brief Initialize the bridging system.
  * \since 12.0.0
  *
@@ -78,13 +92,10 @@ int ast_local_init(void);
  *
  * \note Modules are reloaded using their reload() functions, not unloading
  * them and loading them again.
- * 
- * \return 0 if the specified module was not found.
- * \retval 1 if the module was found but cannot be reloaded.
- * \retval -1 if a reload operation is already in progress.
- * \retval 2 if the specfied module was found and reloaded.
+ *
+ * \retval The \ref ast_module_reload_result status of the module load request
  */
-int ast_module_reload(const char *name);
+enum ast_module_reload_result ast_module_reload(const char *name);
 
 /*!
  * \brief Process reload requests received during startup.
index 6cfb380..85c2aef 100644 (file)
@@ -1104,8 +1104,8 @@ void ast_safe_fork_cleanup(void);
 int ast_app_parse_timelen(const char *timestr, int *result, enum ast_timelen defunit);
 
 /*!
+ * \since 12
  * \brief Publish a MWI state update via stasis
- * \param[in] uniqueid A unique identifier for this mailbox (usually mailbox@context)
  * \param[in] mailbox The number identifying this mailbox
  * \param[in] context The context this mailbox resides in
  * \param[in] new_msgs The number of new messages in this mailbox
@@ -1114,26 +1114,44 @@ int ast_app_parse_timelen(const char *timestr, int *result, enum ast_timelen def
  * \retval -1 Failure
  * \since 12
  */
-#define stasis_publish_mwi_state(mailbox, context, new_msgs, old_msgs) \
-       stasis_publish_mwi_state_full(mailbox, context, new_msgs, old_msgs, NULL)
+#define ast_publish_mwi_state(mailbox, context, new_msgs, old_msgs) \
+       ast_publish_mwi_state_full(mailbox, context, new_msgs, old_msgs, NULL, NULL)
+
+/*!
+ * \since 12
+ * \brief Publish a MWI state update associated with some channel
+ * \param[in] mailbox The number identifying this mailbox
+ * \param[in] context The context this mailbox resides in
+ * \param[in] new_msgs The number of new messages in this mailbox
+ * \param[in] old_msgs The number of old messages in this mailbox
+ * \param[in] channel_id A unique identifier for a channel associated with this
+ * change in mailbox state
+ * \retval 0 Success
+ * \retval -1 Failure
+ * \since 12
+ */
+#define ast_publish_mwi_state_channel(mailbox, context, new_msgs, old_msgs, channel_id) \
+       ast_publish_mwi_state_full(mailbox, context, new_msgs, old_msgs, channel_id, NULL)
 
 /*!
- * \brief Publish a MWI state update via stasis with EID
- * \param[in] uniqueid A unique identifier for this mailbox (usually mailbox@context)
+ * \since 12
+ * \brief Publish a MWI state update via stasis with all parameters
  * \param[in] mailbox The number identifying this mailbox
  * \param[in] context The context this mailbox resides in
  * \param[in] new_msgs The number of new messages in this mailbox
  * \param[in] old_msgs The number of old messages in this mailbox
+ * \param[in] channel_id A unique identifier for a channel associated with this
  * \param[in] eid The EID of the server that originally published the message
  * \retval 0 Success
  * \retval -1 Failure
  * \since 12
  */
-int stasis_publish_mwi_state_full(
+int ast_publish_mwi_state_full(
                        const char *mailbox,
                        const char *context,
                        int new_msgs,
                        int old_msgs,
+                       const char *channel_id,
                        struct ast_eid *eid);
 
 /*! \addtogroup StasisTopicsAndMessages
@@ -1144,49 +1162,103 @@ int stasis_publish_mwi_state_full(
  * \brief The structure that contains MWI state
  * \since 12
  */
-struct stasis_mwi_state {
+struct ast_mwi_state {
        AST_DECLARE_STRING_FIELDS(
-               AST_STRING_FIELD(uniqueid);     /*!< Unique identifier for this mailbox/context */
-               AST_STRING_FIELD(mailbox);      /*!< Mailbox for this event */
-               AST_STRING_FIELD(context);      /*!< Context that this mailbox belongs to */
+               AST_STRING_FIELD(uniqueid);  /*!< Unique identifier for this mailbox/context */
+               AST_STRING_FIELD(mailbox);   /*!< Mailbox for this event */
+               AST_STRING_FIELD(context);   /*!< Context that this mailbox belongs to */
        );
-       int new_msgs;                           /*!< The current number of new messages for this mailbox */
-       int old_msgs;                           /*!< The current number of old messages for this mailbox */
-       struct ast_eid eid;                     /*!< The EID of the server where this message originated */
+       int new_msgs;                    /*!< The current number of new messages for this mailbox */
+       int old_msgs;                    /*!< The current number of old messages for this mailbox */
+       /*! If applicable, a snapshot of the channel that caused this MWI change */
+       struct ast_channel_snapshot *snapshot;
+       struct ast_eid eid;              /*!< The EID of the server where this message originated */
 };
 
 /*!
- * \brief Get the Stasis topic for MWI messages
+ * \brief Object that represents an MWI update with some additional application
+ * defined data
+ */
+struct ast_mwi_blob {
+       struct ast_mwi_state *mwi_state;    /*!< MWI state */
+       struct ast_json *blob;              /*!< JSON blob of data */
+};
+
+/*!
+ * \since 12
+ * \brief Create a \ref ast_mwi_state object
+ *
+ * \retval \ref ast_mwi_state object on success
+ * \retval NULL on error
+ */
+struct ast_mwi_state *ast_mwi_create(const char *mailbox, const char *context);
+
+/*!
+ * \since 12
+ * \brief Creates a \ref ast_mwi_blob message.
+ *
+ * The \a blob JSON object requires a \c "type" field describing the blob. It
+ * should also be treated as immutable and not modified after it is put into the
+ * message.
+ *
+ * \param mwi_state MWI state associated with the update
+ * \param message_type The type of message to create
+ * \param blob JSON object representing the data.
+ * \return \ref ast_mwi_blob message.
+ * \return \c NULL on error
+ */
+struct stasis_message *ast_mwi_blob_create(struct ast_mwi_state *mwi_state,
+                                          struct stasis_message_type *message_type,
+                                          struct ast_json *blob);
+
+/*!
+ * \brief Get the \ref stasis topic for MWI messages
  * \retval The topic structure for MWI messages
  * \retval NULL if it has not been allocated
  * \since 12
  */
-struct stasis_topic *stasis_mwi_topic_all(void);
+struct stasis_topic *ast_mwi_topic_all(void);
 
 /*!
- * \brief Get the Stasis topic for MWI messages on a unique ID
+ * \brief Get the \ref stasis topic for MWI messages on a unique ID
  * \param uniqueid The unique id for which to get the topic
  * \retval The topic structure for MWI messages for a given uniqueid
  * \retval NULL if it failed to be found or allocated
  * \since 12
  */
-struct stasis_topic *stasis_mwi_topic(const char *uniqueid);
+struct stasis_topic *ast_mwi_topic(const char *uniqueid);
 
 /*!
- * \brief Get the Stasis caching topic for MWI messages
+ * \brief Get the \ref stasis caching topic for MWI messages
  * \retval The caching topic structure for MWI messages
  * \retval NULL if it has not been allocated
  * \since 12
  */
-struct stasis_caching_topic *stasis_mwi_topic_cached(void);
+struct stasis_caching_topic *ast_mwi_topic_cached(void);
 
 /*!
- * \brief Get the Stasis message type for MWI messages
+ * \brief Get the \ref stasis message type for MWI messages
  * \retval The message type structure for MWI messages
- * \retval NULL if it has not been allocated
+ * \retval NULL on error
+ * \since 12
+ */
+struct stasis_message_type *ast_mwi_state_type(void);
+
+/*!
+ * \brief Get the \ref stasis message type for voicemail application specific messages
+ *
+ * This message type exists for those messages a voicemail application may wish to send
+ * that have no logical relationship with other voicemail applications. Voicemail apps
+ * that use this message type must pass a \ref ast_mwi_blob. Any extraneous information
+ * in the JSON blob must be packed as key/value pair tuples of strings.
+ *
+ * At least one key/value tuple must have a key value of "Event".
+ *
+ * \retval The \ref stasis_message_type for voicemail application specific messages
+ * \retval NULL on error
  * \since 12
  */
-struct stasis_message_type *stasis_mwi_state_type(void);
+struct stasis_message_type *ast_mwi_vm_app_type(void);
 
 /*! @} */
 
index 978d639..baf8cf6 100644 (file)
@@ -125,6 +125,8 @@ struct ast_json *ast_json_ref(struct ast_json *value);
 /*!
  * \brief Decrease refcount on \a value. If refcount reaches zero, \a value is freed.
  * \since 12.0.0
+ *
+ * \note It is safe to pass \c NULL to this function.
  */
 void ast_json_unref(struct ast_json *value);
 
@@ -602,6 +604,15 @@ struct ast_json_iter *ast_json_object_iter_next(struct ast_json *object, struct
 const char *ast_json_object_iter_key(struct ast_json_iter *iter);
 
 /*!
+ * \brief Retrieve the iterator object for a particular key
+ * \since 12.0.0
+ *
+ * \param key Key of the field the \c ast_json_iter points to
+ * \return \ref ast_json_iter object that points to \a key
+ */
+struct ast_json_iter *ast_json_object_key_to_iter(const char *key);
+
+/*!
  * \brief Get the value from an iterator.
  * \since 12.0.0
  *
@@ -628,6 +639,23 @@ struct ast_json *ast_json_object_iter_value(struct ast_json_iter *iter);
  */
 int ast_json_object_iter_set(struct ast_json *object, struct ast_json_iter *iter, struct ast_json *value);
 
+/*!
+ * \brief Iterate over key/value pairs
+ *
+ * \note This is a reproduction of the jansson library's \ref json_object_foreach
+ * using the equivalent ast_* wrapper functions. This creates a for loop using the various
+ * iteration function calls.
+ *
+ * \param object The \ref ast_json object that contains key/value tuples to iterate over
+ * \param key A \c const char pointer key for the key/value tuple
+ * \param value A \ref ast_json object for the key/value tuple
+ */
+#define ast_json_object_foreach(object, key, value) \
+       for (key = ast_json_object_iter_key(ast_json_object_iter(object)); \
+               key && (value = ast_json_object_iter_value(ast_json_object_key_to_iter(key))); \
+               key = ast_json_object_iter_key(ast_json_object_iter_next(object, ast_json_object_key_to_iter(key))))
+
+
 /*!@}*/
 
 /*!@{*/
index 4e9b8d1..6b1402b 100644 (file)
@@ -330,7 +330,7 @@ struct ast_channel_snapshot;
  * \retval NULL on error
  * \retval ast_str* on success (must be ast_freed by caller)
  */
-struct ast_str *ast_manager_build_channel_state_string_suffix(
+struct ast_str *ast_manager_build_channel_state_string_prefix(
                const struct ast_channel_snapshot *snapshot,
                const char *suffix);
 
@@ -351,6 +351,32 @@ struct ast_str *ast_manager_build_channel_state_string(
 struct ast_bridge_snapshot;
 
 /*!
+ * \since 12
+ * \brief Callback used to determine whether a key should be skipped when converting a
+ *  JSON object to a manager blob
+ * \param key Key from JSON blob to be evaluated
+ * \retval non-zero if the key should be excluded
+ * \retval zero if the key should not be excluded
+ */
+typedef int (*key_exclusion_cb)(const char *key);
+
+struct ast_json;
+
+/*!
+ * \since 12
+ * \brief Convert a JSON object into an AMI compatible string
+ *
+ * \param blob The JSON blob containing key/value pairs to convert
+ * \param exclusion_cb A \ref key_exclusion_cb pointer to a function that will exclude
+ * keys from the final AMI string
+ *
+ * \retval A malloc'd \ref ast_str object. Callers of this function should free
+ * the returned \ref ast_str object
+ * \retval NULL on error
+ */
+struct ast_str *ast_manager_str_from_json_object(struct ast_json *blob, key_exclusion_cb exclusion_cb);
+
+/*!
  * \brief Generate the AMI message body from a bridge snapshot
  * \since 12
  *
@@ -398,13 +424,21 @@ ast_manager_event_blob_create(
 
 /*!
  * \brief Initialize support for AMI channel events.
- * \return 0 on success.
- * \return non-zero on error.
+ * \retval 0 on success.
+ * \retval non-zero on error.
  * \since 12
  */
 int manager_channels_init(void);
 
 /*!
+ * \since 12
+ * \brief Initialize support for AMI MWI events.
+ * \retval 0 on success
+ * \retval non-zero on error
+ */
+int manager_mwi_init(void);
+
+/*!
  * \brief Initialize support for AMI channel events.
  * \return 0 on success.
  * \return non-zero on error.
@@ -412,4 +446,54 @@ int manager_channels_init(void);
  */
 int manager_bridging_init(void);
 
+/*!
+ * \since 12
+ * \brief Get the \ref stasis_message_type for generic messages
+ *
+ * A generic AMI message expects a JSON only payload. The payload must have the following
+ * structure:
+ * {type: s, class_type: i, event: [ {s: s}, ...] }
+ *
+ * - type is the AMI event type
+ * - class_type is the class authorization type for the event
+ * - event is a list of key/value tuples to be sent out in the message
+ *
+ * \retval A \ref stasis_message_type for AMI messages
+ */
+struct stasis_message_type *ast_manager_get_generic_type(void);
+
+/*!
+ * \since 12
+ * \brief Get the \ref stasis topic for AMI
+ *
+ * \retval The \ref stasis topic for AMI
+ * \retval NULL on error
+ */
+struct stasis_topic *ast_manager_get_topic(void);
+
+struct ast_json;
+
+/*!
+ * \since 12
+ * \brief Publish a generic \ref stasis_message_type to the \ref stasis_topic for AMI
+ *
+ * Publishes a message to the \ref stasis message bus solely for the consumption of AMI.
+ * The message will be of the type provided by \ref ast_manager_get_type, and will be
+ * published to the topic provided by \ref ast_manager_get_topic. As such, the JSON must
+ * be constructed as defined by the \ref ast_manager_get_type message.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_manager_publish_message(struct ast_json *json);
+
+/*!
+ * \since 12
+ * \brief Get the \ref stasis_message_router for AMI
+ *
+ * \retval The \ref stasis_message_router for AMI
+ * \retval NULL on error
+ */
+struct stasis_message_router *ast_manager_get_message_router(void);
+
 #endif /* _ASTERISK_MANAGER_H */
index dace99a..7c214d5 100644 (file)
@@ -125,14 +125,15 @@ struct ast_channel_snapshot *ast_channel_snapshot_create(
 
 /*!
  * \since 12
- * \brief Get the most recent snapshot for channel with the given \a uniqueid.
+ * \brief Obtain the latest \ref ast_channel_snapshot from the \ref stasis cache. This is
+ * an ao2 object, so use \ref ao2_cleanup() to deallocate.
  *
- * \param uniqueid Uniqueid of the snapshot to fetch.
- * \return Most recent channel snapshot
- * \return \c NULL on error
+ * \param unique_id The channel's unique ID
+ *
+ * \retval A \ref ast_channel_snapshot on success
+ * \retval NULL on error
  */
-struct ast_channel_snapshot *ast_channel_snapshot_get_latest(
-       const char *uniqueid);
+struct ast_channel_snapshot *ast_channel_snapshot_get_latest(const char *uniqueid);
 
 /*!
  * \since 12
@@ -154,6 +155,27 @@ struct stasis_message *ast_channel_blob_create(struct ast_channel *chan,
 
 /*!
  * \since 12
+ * \brief Creates a \ref ast_channel_blob message using the current cached
+ * \ref ast_channel_snapshot for the passed in \ref ast_channel
+ *
+ * The given \a blob should be treated as immutable and not modified after it is
+ * put into the message.
+ *
+ * \param chan Channel blob is associated with, or \c NULL for global/all channels.
+ * \param type Message type for this blob.
+ * \param blob JSON object representing the data, or \c NULL for no data. If
+ *             \c NULL, ast_json_null() is put into the object.
+ *
+ * \param chan Channel blob is associated with
+ * \param blob JSON object representing the data.
+ * \return \ref ast_channel_blob message.
+ * \return \c NULL on error
+ */
+struct stasis_message *ast_channel_cached_blob_create(struct ast_channel *chan,
+       struct stasis_message_type *type, struct ast_json *blob);
+
+/*!
+ * \since 12
  * \brief Create a \ref ast_channel_blob message, pulling channel state from
  *        the cache.
  *
@@ -319,6 +341,70 @@ struct stasis_message_type *ast_channel_dtmf_end_type(void);
 
 /*!
  * \since 12
+ * \brief Message type for when a channel starts spying on another channel
+ *
+ * \retval A stasis message type
+ */
+struct stasis_message_type *ast_channel_chanspy_start_type(void);
+
+/*!
+ * \since 12
+ * \brief Message type for when a channel stops spying on another channel
+ *
+ * \retval A stasis message type
+ */
+struct stasis_message_type *ast_channel_chanspy_stop_type(void);
+
+/*!
+ * \since 12
+ * \brief Message type for a fax operation
+ *
+ * \retval A stasis message type
+ */
+struct stasis_message_type *ast_channel_fax_type(void);
+
+/*!
+ * \since 12
+ * \brief Message type for hangup handler related actions
+ *
+ * \retval A stasis message type
+ */
+struct stasis_message_type *ast_channel_hangup_handler_type(void);
+
+/*!
+ * \since 12
+ * \brief Message type for starting monitor on a channel
+ *
+ * \retval A stasis message type
+ */
+struct stasis_message_type *ast_channel_monitor_start_type(void);
+
+/*!
+ * \since 12
+ * \brief Message type for stopping monitor on a channel
+ *
+ * \retval A stasis message type
+ */
+struct stasis_message_type *ast_channel_monitor_stop_type(void);
+
+/*!
+ * \since 12
+ * \brief Message type for starting music on hold on a channel
+ *
+ * \retval A stasis message type
+ */
+struct stasis_message_type *ast_channel_moh_start_type(void);
+
+/*!
+ * \since 12
+ * \brief Message type for stopping music on hold on a channel
+ *
+ * \retval A stasis message type
+ */
+struct stasis_message_type *ast_channel_moh_stop_type(void);
+
+/*!
+ * \since 12
  * \brief Publish in the \ref ast_channel_topic or \ref ast_channel_topic_all
  * topics a stasis message for the channels involved in a dial operation.
  *
index 3001450..9fa501f 100644 (file)
@@ -68,6 +68,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/module.h"
 #include "asterisk/astobj2.h"
 #include "asterisk/stasis.h"
+#include "asterisk/stasis_channels.h"
+#include "asterisk/json.h"
 
 #define MWI_TOPIC_BUCKETS 57
 
@@ -82,11 +84,22 @@ struct zombie {
 
 static AST_LIST_HEAD_STATIC(zombies, zombie);
 
+/*
+ * @{ \brief Define \ref stasis topic objects for MWI
+ */
 static struct stasis_topic *mwi_topic_all;
 static struct stasis_caching_topic *mwi_topic_cached;
 static struct stasis_topic_pool *mwi_topic_pool;
+/* @} */
+
+/*
+ * @{ \brief Define \ref stasis message types for MWI
+ */
+STASIS_MESSAGE_TYPE_DEFN(ast_mwi_state_type);
+STASIS_MESSAGE_TYPE_DEFN(ast_mwi_vm_app_type);
+/* @} */
+
 
-STASIS_MESSAGE_TYPE_DEFN(stasis_mwi_state_type);
 
 static void *shaun_of_the_dead(void *data)
 {
@@ -2657,61 +2670,95 @@ int ast_app_parse_timelen(const char *timestr, int *result, enum ast_timelen uni
 
 static void mwi_state_dtor(void *obj)
 {
-       struct stasis_mwi_state *mwi_state = obj;
+       struct ast_mwi_state *mwi_state = obj;
        ast_string_field_free_memory(mwi_state);
+       ao2_cleanup(mwi_state->snapshot);
+       mwi_state->snapshot = NULL;
 }
 
-struct stasis_topic *stasis_mwi_topic_all(void)
+struct stasis_topic *ast_mwi_topic_all(void)
 {
        return mwi_topic_all;
 }
 
-struct stasis_caching_topic *stasis_mwi_topic_cached(void)
+struct stasis_caching_topic *ast_mwi_topic_cached(void)
 {
        return mwi_topic_cached;
 }
 
-struct stasis_topic *stasis_mwi_topic(const char *uniqueid)
+struct stasis_topic *ast_mwi_topic(const char *uniqueid)
 {
        return stasis_topic_pool_get_topic(mwi_topic_pool, uniqueid);
 }
 
-int stasis_publish_mwi_state_full(
+struct ast_mwi_state *ast_mwi_create(const char *mailbox, const char *context)
+{
+       RAII_VAR(struct ast_mwi_state *, mwi_state, NULL, ao2_cleanup);
+       struct ast_str *uniqueid = ast_str_alloca(AST_MAX_MAILBOX_UNIQUEID);
+
+       ast_assert(!ast_strlen_zero(mailbox));
+       ast_assert(!ast_strlen_zero(context));
+
+       mwi_state = ao2_alloc(sizeof(*mwi_state), mwi_state_dtor);
+       if (!mwi_state) {
+               return NULL;
+       }
+
+       if (ast_string_field_init(mwi_state, 256)) {
+               return NULL;
+       }
+       ast_str_set(&uniqueid, 0, "%s@%s", mailbox, context);
+       ast_string_field_set(mwi_state, uniqueid, ast_str_buffer(uniqueid));
+       ast_string_field_set(mwi_state, mailbox, mailbox);
+       ast_string_field_set(mwi_state, context, context);
+
+       ao2_ref(mwi_state, +1);
+       return mwi_state;
+}
+
+
+int ast_publish_mwi_state_full(
                        const char *mailbox,
                        const char *context,
                        int new_msgs,
                        int old_msgs,
+                       const char *channel_id,
                        struct ast_eid *eid)
 {
-       RAII_VAR(struct stasis_mwi_state *, mwi_state, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_mwi_state *, mwi_state, NULL, ao2_cleanup);
        RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
        struct ast_str *uniqueid = ast_str_alloca(AST_MAX_MAILBOX_UNIQUEID);
        struct stasis_topic *mailbox_specific_topic;
 
-       ast_assert(!ast_strlen_zero(mailbox));
-       ast_assert(!ast_strlen_zero(context));
-
-       ast_str_set(&uniqueid, 0, "%s@%s", mailbox, context);
-
-       mwi_state = ao2_alloc(sizeof(*mwi_state), mwi_state_dtor);
-       if (ast_string_field_init(mwi_state, 256)) {
+       mwi_state = ast_mwi_create(mailbox, context);
+       if (!mwi_state) {
                return -1;
        }
 
-       ast_string_field_set(mwi_state, uniqueid, ast_str_buffer(uniqueid));
-       ast_string_field_set(mwi_state, mailbox, mailbox);
-       ast_string_field_set(mwi_state, context, context);
        mwi_state->new_msgs = new_msgs;
        mwi_state->old_msgs = old_msgs;
+
+       if (!ast_strlen_zero(channel_id)) {
+               RAII_VAR(struct stasis_message *, chan_message,
+                       stasis_cache_get(ast_channel_topic_all_cached(),
+                                       ast_channel_snapshot_type(),
+                                       channel_id),
+                       ao2_cleanup);
+               if (chan_message) {
+                       mwi_state->snapshot = stasis_message_data(chan_message);
+                       ao2_ref(mwi_state->snapshot, +1);
+               }
+       }
+
        if (eid) {
                mwi_state->eid = *eid;
        } else {
                ast_set_default_eid(&mwi_state->eid);
        }
 
-       message = stasis_message_create(stasis_mwi_state_type(), mwi_state);
+       message = stasis_message_create(ast_mwi_state_type(), mwi_state);
 
-       mailbox_specific_topic = stasis_mwi_topic(ast_str_buffer(uniqueid));
+       mailbox_specific_topic = ast_mwi_topic(ast_str_buffer(uniqueid));
        if (!mailbox_specific_topic) {
                return -1;
        }
@@ -2723,8 +2770,8 @@ int stasis_publish_mwi_state_full(
 
 static const char *mwi_state_get_id(struct stasis_message *message)
 {
-       if (stasis_mwi_state_type() == stasis_message_type(message)) {
-               struct stasis_mwi_state *mwi_state = stasis_message_data(message);
+       if (ast_mwi_state_type() == stasis_message_type(message)) {
+               struct ast_mwi_state *mwi_state = stasis_message_data(message);
                return mwi_state->uniqueid;
        } else if (stasis_subscription_change_type() == stasis_message_type(message)) {
                struct stasis_subscription_change *change = stasis_message_data(message);
@@ -2734,19 +2781,58 @@ static const char *mwi_state_get_id(struct stasis_message *message)
        return NULL;
 }
 
+static void mwi_blob_dtor(void *obj)
+{
+       struct ast_mwi_blob *mwi_blob = obj;
+
+       ao2_cleanup(mwi_blob->mwi_state);
+       ast_json_unref(mwi_blob->blob);
+}
+
+struct stasis_message *ast_mwi_blob_create(struct ast_mwi_state *mwi_state,
+                                              struct stasis_message_type *message_type,
+                                              struct ast_json *blob)
+{
+       RAII_VAR(struct ast_mwi_blob *, obj, NULL, ao2_cleanup);
+       RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
+
+       ast_assert(blob != NULL);
+
+       obj = ao2_alloc(sizeof(*obj), mwi_blob_dtor);
+       if (!obj) {
+               return NULL;
+       }
+
+       obj->mwi_state = mwi_state;
+       ao2_ref(obj->mwi_state, +1);
+       obj->blob = ast_json_ref(blob);
+
+       msg = stasis_message_create(message_type, obj);
+       if (!msg) {
+               return NULL;
+       }
+
+       ao2_ref(msg, +1);
+       return msg;
+}
+
 static void app_exit(void)
 {
        ao2_cleanup(mwi_topic_all);
        mwi_topic_all = NULL;
        mwi_topic_cached = stasis_caching_unsubscribe_and_join(mwi_topic_cached);
-       STASIS_MESSAGE_TYPE_CLEANUP(stasis_mwi_state_type);
+       STASIS_MESSAGE_TYPE_CLEANUP(ast_mwi_state_type);
+       STASIS_MESSAGE_TYPE_CLEANUP(ast_mwi_vm_app_type);
        ao2_cleanup(mwi_topic_pool);
        mwi_topic_pool = NULL;
 }
 
 int app_init(void)
 {
-       if (STASIS_MESSAGE_TYPE_INIT(stasis_mwi_state_type) != 0) {
+       if (STASIS_MESSAGE_TYPE_INIT(ast_mwi_state_type) != 0) {
+               return -1;
+       }
+       if (STASIS_MESSAGE_TYPE_INIT(ast_mwi_vm_app_type) != 0) {
                return -1;
        }
        mwi_topic_all = stasis_topic_create("stasis_mwi_topic");
index d8062d3..9407338 100644 (file)
@@ -242,12 +242,44 @@ int daemon(int, int);  /* defined in libresolv of all places */
 #include "asterisk/sorcery.h"
 #include "asterisk/stasis.h"
 #include "asterisk/json.h"
-#include "asterisk/security_events.h"
 #include "asterisk/stasis_endpoints.h"
 
 #include "../defaults.h"
 
 /*** DOCUMENTATION
+       <managerEvent language="en_US" name="FullyBooted">
+               <managerEventInstance class="EVENT_FLAG_SYSTEM">
+                       <synopsis>Raised when all Asterisk initialization procedures have finished.</synopsis>
+                       <syntax>
+                               <parameter name="Status">
+                                       <para>Informational message</para>
+                               </parameter>
+                       </syntax>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="Shutdown">
+               <managerEventInstance class="EVENT_FLAG_SYSTEM">
+                       <synopsis>Raised when Asterisk is shutdown or restarted.</synopsis>
+                       <syntax>
+                               <parameter name="Shutdown">
+                                       <para>Whether the shutdown is proceeding cleanly (all channels
+                                       were hungup successfully) or uncleanly (channels will be
+                                       terminated)</para>
+                                       <enumlist>
+                                               <enum name="Uncleanly"/>
+                                               <enum name="Cleanly"/>
+                                       </enumlist>
+                               </parameter>
+                               <parameter name="Restart">
+                                       <para>Whether or not a restart will occur.</para>
+                                       <enumlist>
+                                               <enum name="True"/>
+                                               <enum name="False"/>
+                                       </enumlist>
+                               </parameter>
+                       </syntax>
+               </managerEventInstance>
+       </managerEvent>
  ***/
 
 #ifndef AF_LOCAL
@@ -425,6 +457,9 @@ struct file_version {
        char *version;
 };
 
+/*! \brief The \ref stasis topic for system level changes */
+static struct stasis_topic *system_topic;
+
 static AST_RWLIST_HEAD_STATIC(file_versions, file_version);
 
 void ast_register_file_version(const char *file, const char *version)
@@ -1067,7 +1102,7 @@ struct stasis_topic *ast_system_topic(void)
 /*! \brief Cleanup the \ref stasis system level items */
 static void stasis_system_topic_cleanup(void)
 {
-       ao2_ref(system_topic, -1);
+       ao2_cleanup(system_topic);
        system_topic = NULL;
        STASIS_MESSAGE_TYPE_CLEANUP(ast_network_change_type);
 }
@@ -1085,9 +1120,54 @@ static int stasis_system_topic_init(void)
        if (STASIS_MESSAGE_TYPE_INIT(ast_network_change_type) != 0) {
                return -1;
        }
+
        return 0;
 }
 
+/*!
+ * \brief Publish a \ref system_status_type message over \ref stasis
+ *
+ * \param payload The JSON payload to send with the message
+ */
+static void publish_system_message(const char *message_type, struct ast_json *obj)
+{
+       RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_json_payload *, payload, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_json *, event_info, NULL, ast_json_unref);
+
+       if (!obj) {
+               return;
+       }
+
+       event_info = ast_json_pack("{s: s, s: i, s: o}",
+                       "type", message_type,
+                       "class_type", EVENT_FLAG_SYSTEM,
+                       "event", obj);
+       if (!event_info) {
+               return;
+       }
+
+       payload = ast_json_payload_create(event_info);
+       if (!payload) {
+               return;
+       }
+
+       message = stasis_message_create(ast_manager_get_generic_type(), payload);
+       if (!message) {
+               return;
+       }
+       stasis_publish(ast_manager_get_topic(), message);
+}
+
+static void publish_fully_booted(void)
+{
+       RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
+
+       json_object = ast_json_pack("{s: s}",
+                       "Status", "Fully Booted");
+       publish_system_message("FullyBooted", json_object);
+}
+
 static void ast_run_atexits(void)
 {
        struct ast_atexit *ae;
@@ -1897,6 +1977,7 @@ static int can_safely_quit(shutdown_nice_t niceness, int restart)
 static void really_quit(int num, shutdown_nice_t niceness, int restart)
 {
        int active_channels;
+       RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
 
        if (niceness >= SHUTDOWN_NICE) {
                ast_module_shutdown();
@@ -1925,33 +2006,10 @@ static void really_quit(int num, shutdown_nice_t niceness, int restart)
                }
        }
        active_channels = ast_active_channels();
-       /* The manager event for shutdown must happen prior to ast_run_atexits, as
-        * the manager interface will dispose of its sessions as part of its
-        * shutdown.
-        */
-       /*** DOCUMENTATION
-               <managerEventInstance>
-                       <synopsis>Raised when Asterisk is shutdown or restarted.</synopsis>
-                       <syntax>
-                               <parameter name="Shutdown">
-                                       <enumlist>
-                                               <enum name="Uncleanly"/>
-                                               <enum name="Cleanly"/>
-                                       </enumlist>
-                               </parameter>
-                               <parameter name="Restart">
-                                       <enumlist>
-                                               <enum name="True"/>
-                                               <enum name="False"/>
-                                       </enumlist>
-                               </parameter>
-                       </syntax>
-               </managerEventInstance>
-       ***/
-       manager_event(EVENT_FLAG_SYSTEM, "Shutdown", "Shutdown: %s\r\n"
-               "Restart: %s\r\n",
-               active_channels ? "Uncleanly" : "Cleanly",
-               restart ? "True" : "False");
+       json_object = ast_json_pack("{s: s, s: s}",
+                       "Shutdown", active_channels ? "Uncleanly" : "Cleanly",
+                       "Restart", restart ? "True" : "False");
+       publish_system_message("Shutdown", json_object);
        ast_verb(0, "Asterisk %s ending (%d).\n",
                active_channels ? "uncleanly" : "cleanly", num);
 
@@ -4226,13 +4284,13 @@ int main(int argc, char *argv[])
 
        aco_init();
 
-       if (devstate_init()) {
-               printf("Device state core initialization failed.\n%s", term_quit());
+       if (app_init()) {
+               printf("App core initialization failed.\n%s", term_quit());
                exit(1);
        }
 
-       if (app_init()) {
-               printf("App core initialization failed.\n%s", term_quit());
+       if (devstate_init()) {
+               printf("Device state core initialization failed.\n%s", term_quit());
                exit(1);
        }
 
@@ -4264,12 +4322,6 @@ int main(int argc, char *argv[])
                exit(1);
        }
 
-       if (ast_security_stasis_init()) {               /* Initialize Security Stasis Topic and Events */
-               ast_security_stasis_cleanup();
-               printf("%s", term_quit());
-               exit(1);
-       }
-
        if (ast_named_acl_init()) { /* Initialize the Named ACL system */
                printf("%s", term_quit());
                exit(1);
@@ -4374,12 +4426,7 @@ int main(int argc, char *argv[])
        }
 
        ast_set_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED);
-       /*** DOCUMENTATION
-               <managerEventInstance>
-                       <synopsis>Raised when all Asterisk initialization procedures have finished.</synopsis>
-               </managerEventInstance>
-       ***/
-       manager_event(EVENT_FLAG_SYSTEM, "FullyBooted", "Status: Fully Booted\r\n");
+       publish_fully_booted();
 
        ast_process_pending_reloads();
 
index ff7cef2..a056067 100644 (file)
@@ -1674,7 +1674,6 @@ static void do_reload(int reload)
 
        ast_mutex_unlock(&cdr_batch_lock);
        ast_config_destroy(config);
-       manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: CDR\r\nMessage: CDR subsystem reload requested\r\n");
 }
 
 static void cdr_engine_shutdown(void)
index 22232ac..683ae9c 100644 (file)
@@ -303,14 +303,30 @@ static char *handle_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args
                return CLI_SUCCESS;
        }
        for (x = e->args; x < a->argc; x++) {
-               int res = ast_module_reload(a->argv[x]);
-               /* XXX reload has multiple error returns, including -1 on error and 2 on success */
+               enum ast_module_reload_result res = ast_module_reload(a->argv[x]);
                switch (res) {
-               case 0:
+               case AST_MODULE_RELOAD_NOT_FOUND:
                        ast_cli(a->fd, "No such module '%s'\n", a->argv[x]);
                        break;
-               case 1:
-                       ast_cli(a->fd, "Module '%s' does not support reload\n", a->argv[x]);
+               case AST_MODULE_RELOAD_NOT_IMPLEMENTED:
+                       ast_cli(a->fd, "The module '%s' does not support reloads\n", a->argv[x]);
+                       break;
+               case AST_MODULE_RELOAD_QUEUED:
+                       ast_cli(a->fd, "Asterisk cannot reload a module yet; request queued\n");
+                       break;
+               case AST_MODULE_RELOAD_ERROR:
+                       ast_cli(a->fd, "The module '%s' reported a reload failure\n", a->argv[x]);
+                       break;
+               case AST_MODULE_RELOAD_IN_PROGRESS:
+                       ast_cli(a->fd, "A module reload request is already in progress; please be patient\n");
+                       break;
+               case AST_MODULE_RELOAD_UNINITIALIZED:
+                       ast_cli(a->fd, "The module '%s' was not properly initialized. Before reloading"
+                                       " the module, you must run \"module load %s\" and fix whatever is"
+                                       " preventing the module from being initialized.\n", a->argv[x], a->argv[x]);
+                       break;
+               case AST_MODULE_RELOAD_SUCCESS:
+                       ast_cli(a->fd, "Module '%s' reloaded successfully.\n", a->argv[x]);
                        break;
                }
        }
index bfba471..d642cd6 100644 (file)
@@ -514,7 +514,6 @@ static int do_reload(int loading)
        }
 
        ast_mutex_unlock(&refresh_lock);
-       manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: DNSmgr\r\nStatus: %s\r/nMessage: DNSmgr reload Requested\r\n", enabled ? "Enabled" : "Disabled");
 
        return 0;
 }
index d097288..7528092 100644 (file)
@@ -1007,7 +1007,6 @@ static int private_enum_init(int reload)
                ast_config_destroy(cfg);
        }
        ast_mutex_unlock(&enumlock);
-       manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: Enum\r\nStatus: Enabled\r\nMessage: ENUM reload Requested\r\n");
        return 0;
 }
 
index 5b69ccb..70830bd 100644 (file)
@@ -78,6 +78,9 @@ struct ast_json *ast_json_ref(struct ast_json *json)
 
 void ast_json_unref(struct ast_json *json)
 {
+       if (!json) {
+               return;
+       }
        json_decref((json_t *)json);
 }
 
@@ -327,6 +330,10 @@ const char *ast_json_object_iter_key(struct ast_json_iter *iter)
 {
        return json_object_iter_key(iter);
 }
+struct ast_json_iter *ast_json_object_key_to_iter(const char *key)
+{
+       return (struct ast_json_iter *)json_object_key_to_iter(key);
+}
 struct ast_json *ast_json_object_iter_value(struct ast_json_iter *iter)
 {
        return (struct ast_json *)json_object_iter_value(iter);
index 3bcf37c..7e5a5ae 100644 (file)
@@ -63,6 +63,30 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/utils.h"
 
 /*** DOCUMENTATION
+       <managerEvent language="en_US" name="Reload">
+               <managerEventInstance class="EVENT_FLAG_SYSTEM">
+                       <synopsis>Raised when a module has been reloaded in Asterisk.</synopsis>
+                       <syntax>
+                               <parameter name="Module">
+                                       <para>The name of the module that was reloaded, or
+                                       <literal>All</literal> if all modules were reloaded</para>
+                               </parameter>
+                               <parameter name="Status">
+                                       <para>The numeric status code denoting the success or failure
+                                       of the reload request.</para>
+                                       <enumlist>
+                                               <enum name="0"><para>Success</para></enum>
+                                               <enum name="1"><para>Request queued</para></enum>
+                                               <enum name="2"><para>Module not found</para></enum>
+                                               <enum name="3"><para>Error</para></enum>
+                                               <enum name="4"><para>Reload already in progress</para></enum>
+                                               <enum name="5"><para>Module uninitialized</para></enum>
+                                               <enum name="6"><para>Reload not supported</para></enum>
+                                       </enumlist>
+                               </parameter>
+                       </syntax>
+               </managerEventInstance>
+       </managerEvent>
  ***/
 
 #ifndef RTLD_NOW
@@ -709,22 +733,63 @@ static void queue_reload_request(const char *module)
        AST_LIST_UNLOCK(&reload_queue);
 }
 
-int ast_module_reload(const char *name)
+/*!
+ * \since 12
+ * \internal
+ * \brief Publish a \ref stasis message regarding the reload result
+ */
+static void publish_reload_message(const char *name, enum ast_module_reload_result result)
+{
+       RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_json_payload *, payload, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
+       RAII_VAR(struct ast_json *, event_object, NULL, ast_json_unref);
+       char res_buffer[8];
+
+       snprintf(res_buffer, sizeof(res_buffer), "%d", result);
+       event_object = ast_json_pack("{s: s, s: s}",
+                       "Module", S_OR(name, "All"),
+                       "Status", res_buffer);
+       json_object = ast_json_pack("{s: s, s: i, s: o}",
+                       "type", "Reload",
+                       "class_type", EVENT_FLAG_SYSTEM,
+                       "event", event_object);
+
+       if (!json_object) {
+               return;
+       }
+
+       payload = ast_json_payload_create(json_object);
+       if (!payload) {
+               return;
+       }
+
+       message = stasis_message_create(ast_manager_get_generic_type(), payload);
+       if (!message) {
+               return;
+       }
+
+       stasis_publish(ast_manager_get_topic(), message);
+}
+
+enum ast_module_reload_result ast_module_reload(const char *name)
 {
        struct ast_module *cur;
-       int res = 0; /* return value. 0 = not found, others, see below */
+       enum ast_module_reload_result res = AST_MODULE_RELOAD_NOT_FOUND;
        int i;
 
        /* If we aren't fully booted, we just pretend we reloaded but we queue this
           up to run once we are booted up. */
        if (!ast_fully_booted) {
                queue_reload_request(name);
-               return 0;
+               res = AST_MODULE_RELOAD_QUEUED;
+               goto module_reload_exit;
        }
 
        if (ast_mutex_trylock(&reloadlock)) {
                ast_verbose("The previous reload command didn't finish yet\n");
-               return -1;      /* reload already in progress */
+               res = AST_MODULE_RELOAD_IN_PROGRESS;
+               goto module_reload_exit;
        }
        ast_lastreloadtime = ast_tvnow();
 
@@ -740,26 +805,26 @@ int ast_module_reload(const char *name)
                if (res != AST_LOCK_SUCCESS) {
                        ast_verbose("Cannot grab lock on %s\n", ast_config_AST_CONFIG_DIR);
                        ast_mutex_unlock(&reloadlock);
-                       return -1;
+                       res = AST_MODULE_RELOAD_ERROR;
+                       goto module_reload_exit;
                }
        }
 
        /* Call "predefined" reload here first */
        for (i = 0; reload_classes[i].name; i++) {
                if (!name || !strcasecmp(name, reload_classes[i].name)) {
-                       if (!reload_classes[i].reload_fn()) {
-                               ast_test_suite_event_notify("MODULE_RELOAD", "Message: %s", name);
+                       if (reload_classes[i].reload_fn() == AST_MODULE_LOAD_SUCCESS) {
+                               res = AST_MODULE_RELOAD_SUCCESS;
                        }
-                       res = 2;        /* found and reloaded */
                }
        }
 
-       if (name && res) {
+       if (name && res == AST_MODULE_RELOAD_SUCCESS) {
                if (ast_opt_lock_confdir) {
                        ast_unlock_path(ast_config_AST_CONFIG_DIR);
                }
                ast_mutex_unlock(&reloadlock);
-               return res;
+               goto module_reload_exit;
        }
 
        AST_LIST_LOCK(&module_list);
@@ -770,28 +835,30 @@ int ast_module_reload(const char *name)
                        continue;
 
                if (!cur->flags.running || cur->flags.declined) {
-                       if (!name)
+                       if (res == AST_MODULE_RELOAD_NOT_FOUND) {
+                               res = AST_MODULE_RELOAD_UNINITIALIZED;
+                       }
+                       if (!name) {
                                continue;
-                       ast_log(LOG_NOTICE, "The module '%s' was not properly initialized.  "
-                               "Before reloading the module, you must run \"module load %s\" "
-                               "and fix whatever is preventing the module from being initialized.\n",
-                               name, name);
-                       res = 2; /* Don't report that the module was not found */
+                       }
                        break;
                }
 
                if (!info->reload) {    /* cannot be reloaded */
-                       /* Nothing to reload, so reload is successful */
-                       ast_test_suite_event_notify("MODULE_RELOAD", "Message: %s", cur->resource);
-                       if (res < 1)    /* store result if possible */
-                               res = 1;        /* 1 = no reload() method */
-                       continue;
+                       if (res == AST_MODULE_RELOAD_NOT_FOUND) {
+                               res = AST_MODULE_RELOAD_NOT_IMPLEMENTED;
+                       }
+                       if (!name) {
+                               continue;
+                       }
+                       break;
                }
-
-               res = 2;
                ast_verb(3, "Reloading module '%s' (%s)\n", cur->resource, info->description);
-               if (!info->reload()) {
-                       ast_test_suite_event_notify("MODULE_RELOAD", "Message: %s", cur->resource);
+               if (info->reload() == AST_MODULE_LOAD_SUCCESS) {
+                       res = AST_MODULE_RELOAD_SUCCESS;
+               }
+               if (name) {
+                       break;
                }
        }
        AST_LIST_UNLOCK(&module_list);
@@ -801,6 +868,8 @@ int ast_module_reload(const char *name)
        }
        ast_mutex_unlock(&reloadlock);
 
+module_reload_exit:
+       publish_reload_message(name, res);
        return res;
 }
 
@@ -1212,25 +1281,6 @@ done:
        }
 
        AST_LIST_UNLOCK(&module_list);
-
-       /* Tell manager clients that are aggressive at logging in that we're done
-          loading modules. If there's a DNS problem in chan_sip, we might not
-          even reach this */
-       /*** DOCUMENTATION
-               <managerEventInstance>
-                       <synopsis>Raised when all dynamic modules have finished their initial loading.</synopsis>
-                       <syntax>
-                               <parameter name="ModuleSelection">
-                                       <enumlist>
-                                               <enum name="Preload"/>
-                                               <enum name="All"/>
-                                       </enumlist>
-                               </parameter>
-                       </syntax>
-               </managerEventInstance>
-       ***/
-       manager_event(EVENT_FLAG_SYSTEM, "ModuleLoadReport", "ModuleLoadStatus: Done\r\nModuleSelection: %s\r\nModuleCount: %d\r\n", preload_only ? "Preload" : "All", modulecount);
-
        return res;
 }
 
index c28e616..96fbdae 100644 (file)
@@ -92,6 +92,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/stringfields.h"
 #include "asterisk/presencestate.h"
 #include "asterisk/stasis.h"
+#include "asterisk/stasis_message_router.h"
 #include "asterisk/test.h"
 #include "asterisk/json.h"
 #include "asterisk/bridging.h"
@@ -1062,6 +1063,12 @@ static int block_sockets;
 static int unauth_sessions = 0;
 static struct stasis_subscription *acl_change_sub;
 
+/*! \brief A \ref stasis_topic that all topics AMI cares about will be forwarded to */
+static struct stasis_topic *manager_topic;
+
+/*! \brief The \ref stasis_message_router for all \ref stasis messages */
+static struct stasis_message_router *stasis_router;
+
 #define MGR_SHOW_TERMINAL_WIDTH 80
 
 #define MAX_VARS 128
@@ -1226,6 +1233,12 @@ AO2_GLOBAL_OBJ_STATIC(event_docs);
 static enum add_filter_result manager_add_filter(const char *filter_pattern, struct ao2_container *whitefilters, struct ao2_container *blackfilters);
 
 /*!
+ * @{ \brief Define AMI message types.
+ */
+STASIS_MESSAGE_TYPE_DEFN(ast_manager_get_generic_type);
+/*! @} */
+
+/*!
  * \internal
  * \brief Find a registered action object.
  *
@@ -1249,6 +1262,89 @@ static struct manager_action *action_find(const char *name)
        return act;
 }
 
+struct stasis_topic *ast_manager_get_topic(void)
+{
+       return manager_topic;
+}
+
+struct stasis_message_router *ast_manager_get_message_router(void)
+{
+       return stasis_router;
+}
+
+struct ast_str *ast_manager_str_from_json_object(struct ast_json *blob, key_exclusion_cb exclusion_cb)
+{
+       struct ast_str *output_str = ast_str_create(32);
+       struct ast_json *value;
+       const char *key;
+       if (!output_str) {
+               return NULL;
+       }
+
+       ast_json_object_foreach(blob, key, value) {
+               if (exclusion_cb && exclusion_cb(key)) {
+                       continue;
+               }
+               ast_str_append(&output_str, 0, "%s: %s\r\n", key, ast_json_string_get(value));
+               if (!output_str) {
+                       return NULL;
+               }
+       }
+
+       return output_str;
+}
+
+static void manager_generic_msg_cb(void *data, struct stasis_subscription *sub,
+                                   struct stasis_topic *topic,
+                                   struct stasis_message *message)
+{
+       struct ast_json_payload *payload = stasis_message_data(message);
+       int class_type = ast_json_integer_get(ast_json_object_get(payload->json, "class_type"));
+       const char *type = ast_json_string_get(ast_json_object_get(payload->json, "type"));
+       struct ast_json *event = ast_json_object_get(payload->json, "event");
+       RAII_VAR(struct ast_str *, event_buffer, NULL, ast_free);
+
+       event_buffer = ast_manager_str_from_json_object(event, NULL);
+       if (!event_buffer) {
+               ast_log(AST_LOG_WARNING, "Error while creating payload for event %s\n", type);
+               return;
+       }
+       manager_event(class_type, type, "%s", ast_str_buffer(event_buffer));
+}
+
+int ast_manager_publish_message(struct ast_json *obj)
+{
+       RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_json_payload *, payload, NULL, ao2_cleanup);
+       struct ast_json *type = ast_json_object_get(obj, "type");
+       struct ast_json *class_type = ast_json_object_get(obj, "class_type");
+       struct ast_json *event = ast_json_object_get(obj, "event");
+
+       if (!type) {
+               ast_log(AST_LOG_ERROR, "Attempt to send generic manager event without type field\n");
+               return -1;
+       }
+       if (!class_type) {
+               ast_log(AST_LOG_ERROR, "Attempt to send generic manager event without class type field\n");
+               return -1;
+       }
+       if (!event) {
+               ast_log(AST_LOG_ERROR, "Attempt to send generic manager event without event payload\n");
+               return -1;
+       }
+
+       payload = ast_json_payload_create(obj);
+       if (!payload) {
+               return -1;
+       }
+       message = stasis_message_create(ast_manager_get_generic_type(), payload);
+       if (!message) {
+               return -1;
+       }
+       stasis_publish(ast_manager_get_topic(), message);
+       return 0;
+}
+
 /*! \brief Add a custom hook to be called when an event is fired */
 void ast_manager_register_hook(struct manager_custom_hook *hook)
 {
@@ -5034,24 +5130,29 @@ static int action_corestatus(struct mansession *s, const struct message *m)
 static int action_reload(struct mansession *s, const struct message *m)
 {
        const char *module = astman_get_header(m, "Module");
-       int res = ast_module_reload(S_OR(module, NULL));
+       enum ast_module_reload_result res = ast_module_reload(S_OR(module, NULL));
 
        switch (res) {
-       case -1:
-               astman_send_error(s, m, "A reload is in progress");
-               break;
-       case 0:
+       case AST_MODULE_RELOAD_NOT_FOUND:
                astman_send_error(s, m, "No such module");
                break;
-       case 1:
+       case AST_MODULE_RELOAD_NOT_IMPLEMENTED:
                astman_send_error(s, m, "Module does not support reload");
                break;
-       case 2:
-               astman_send_ack(s, m, "Module Reloaded");
-               break;
-       default:
+       case AST_MODULE_RELOAD_ERROR:
                astman_send_error(s, m, "An unknown error occurred");
                break;
+       case AST_MODULE_RELOAD_IN_PROGRESS:
+               astman_send_error(s, m, "A reload is in progress");
+               break;
+       case AST_MODULE_RELOAD_UNINITIALIZED:
+               astman_send_error(s, m, "Module not initialized");
+               break;
+       case AST_MODULE_RELOAD_QUEUED:
+       case AST_MODULE_RELOAD_SUCCESS:
+               /* Treat a queued request as success */
+               astman_send_ack(s, m, "Module Reloaded");
+               break;
        }
        return 0;
 }
@@ -7526,6 +7627,14 @@ static void manager_shutdown(void)
        ao2_t_global_obj_release(event_docs, "Dispose of event_docs");
 #endif
 
+       if (stasis_router) {
+               stasis_message_router_unsubscribe_and_join(stasis_router);
+               stasis_router = NULL;
+       }
+       ao2_cleanup(manager_topic);
+       manager_topic = NULL;
+       STASIS_MESSAGE_TYPE_CLEANUP(ast_manager_get_generic_type);
+
        ast_tcptls_server_stop(&ami_desc);
        ast_tcptls_server_stop(&amis_desc);
 
@@ -7552,6 +7661,31 @@ static void manager_shutdown(void)
        }
 }
 
+
+/*! \brief Initialize all \ref stasis topics and routers used by the various
+ * sub-components of AMI
+ */
+static int manager_subscriptions_init(void)
+{
+       STASIS_MESSAGE_TYPE_INIT(ast_manager_get_generic_type);
+       manager_topic = stasis_topic_create("manager_topic");
+       if (!manager_topic) {
+               return -1;
+       }
+       stasis_router = stasis_message_router_create(manager_topic);
+       if (!stasis_router) {
+               return -1;
+       }
+
+       if (stasis_message_router_add(stasis_router,
+                                        ast_manager_get_generic_type(),
+                                        manager_generic_msg_cb,
+                                        NULL)) {
+               return -1;
+       }
+       return 0;
+}
+
 static int __init_manager(int reload, int by_external_config)
 {
        struct ast_config *ucfg = NULL, *cfg = NULL;
@@ -7573,8 +7707,19 @@ static int __init_manager(int reload, int by_external_config)
 
        manager_enabled = 0;
 
-       if (manager_channels_init()) {
-               return -1;
+       if (!reload) {
+               if (manager_subscriptions_init()) {
+                       ast_log(AST_LOG_ERROR, "Failed to initialize manager subscriptions\n");
+                       return -1;
+               }
+               if (manager_channels_init()) {
+                       ast_log(AST_LOG_ERROR, "Failed to initialize manager channel handling\n");
+                       return -1;
+               }
+               if (manager_mwi_init()) {
+                       ast_log(AST_LOG_ERROR, "Failed to initialize manager MWI handling\n");
+                       return -1;
+               }
        }
 
        if (manager_bridging_init()) {
@@ -8025,8 +8170,6 @@ static int __init_manager(int reload, int by_external_config)
                httptimeout = newhttptimeout;
        }
 
-       manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: Manager\r\nStatus: %s\r\nMessage: Manager reload Requested\r\n", manager_enabled ? "Enabled" : "Disabled");
-
        ast_tcptls_server_start(&ami_desc);
        if (tls_was_enabled && !ami_tls_cfg.enabled) {
                ast_tcptls_server_stop(&amis_desc);
index fb579dd..f3c72ec 100644 (file)
@@ -37,8 +37,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/pbx.h"
 #include "asterisk/stasis_channels.h"
 
-static struct stasis_message_router *channel_state_router;
-
 /*** DOCUMENTATION
        <managerEvent language="en_US" name="Newchannel">
                <managerEventInstance class="EVENT_FLAG_CALL">
@@ -160,12 +158,12 @@ static struct stasis_message_router *channel_state_router;
                        <synopsis>Raised when a dial action has started.</synopsis>
                        <syntax>
                                <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
-                               <parameter name="ChannelDest">
+                               <parameter name="DestChannel">
                                </parameter>
-                               <parameter name="ChannelStateDest">
-                                       <para>A numeric code for the channel's current state, related to ChannelStateDescDest</para>
+                               <parameter name="DestChannelState">
+                                       <para>A numeric code for the channel's current state, related to DestChannelStateDesc</para>
                                </parameter>
-                               <parameter name="ChannelStateDescDest">
+                               <parameter name="DestChannelStateDesc">
                                        <enumlist>
                                                <enum name="Down"/>
                                                <enum name="Rsrvd"/>
@@ -180,23 +178,23 @@ static struct stasis_message_router *channel_state_router;
                                                <enum name="Unknown"/>
                                        </enumlist>
                                </parameter>
-                               <parameter name="CallerIDNumDest">
+                               <parameter name="DestCallerIDNum">
                                </parameter>
-                               <parameter name="CallerIDNameDest">
+                               <parameter name="DestCallerIDName">
                                </parameter>
-                               <parameter name="ConnectedLineNumDest">
+                               <parameter name="DestConnectedLineNum">
                                </parameter>
-                               <parameter name="ConnectedLineNameDest">
+                               <parameter name="DestConnectedLineName">
                                </parameter>
-                               <parameter name="AccountCodeDest">
+                               <parameter name="DestAccountCode">
                                </parameter>
-                               <parameter name="ContextDest">
+                               <parameter name="DestContext">
                                </parameter>
-                               <parameter name="ExtenDest">
+                               <parameter name="DestExten">
                                </parameter>
-                               <parameter name="PriorityDest">
+                               <parameter name="DestPriority">
                                </parameter>
-                               <parameter name="UniqueidDest">
+                               <parameter name="DestUniqueid">
                                </parameter>
                                <parameter name="DialString">
                                        <para>The non-technology specific device being dialed.</para>
@@ -230,11 +228,270 @@ static struct stasis_message_router *channel_state_router;
                        </see-also>
                </managerEventInstance>
        </managerEvent>
- ***/
+       <managerEvent language="en_US" name="ChanSpyStart">
+               <managerEventInstance class="EVENT_FLAG_CALL">
+                       <synopsis>Raised when one channel begins spying on another channel.</synopsis>
+                       <syntax>
+                               <parameter name="SpyerChannel">
+                                       <para>The channel performing the spying.</para>
+                               </parameter>
+                               <parameter name="SpyerChannelState">
+                                       <para>A numeric code for the channel's current state, related to SpyerChannelStateDesc</para>
+                               </parameter>
+                               <parameter name="SpyerChannelStateDesc">
+                                       <enumlist>
+                                               <enum name="Down"/>
+                                               <enum name="Rsrvd"/>
+                                               <enum name="OffHook"/>
+                                               <enum name="Dialing"/>
+                                               <enum name="Ring"/>
+                                               <enum name="Ringing"/>
+                                               <enum name="Up"/>
+                                               <enum name="Busy"/>
+                                               <enum name="Dialing Offhook"/>
+                                               <enum name="Pre-ring"/>
+                                               <enum name="Unknown"/>
+                                       </enumlist>
+                               </parameter>
+                               <parameter name="SpyerCallerIDNum">
+                               </parameter>
+                               <parameter name="SpyerCallerIDName">
+                               </parameter>
+                               <parameter name="SpyerConnectedLineNum">
+                               </parameter>
+                               <parameter name="SpyerConnectedLineName">
+                               </parameter>
+                               <parameter name="SpyerAccountCode">
+                               </parameter>
+                               <parameter name="SpyerContext">
+                               </parameter>
+                               <parameter name="SpyerExten">
+                               </parameter>
+                               <parameter name="SpyerPriority">
+                               </parameter>
+                               <parameter name="SpyerUniqueid">
+                               </parameter>
+                               <parameter name="SpyeeChannel">
+                                       <para>The channel being spied upon.</para>
+                               </parameter>
+                               <parameter name="SpyeeChannelState">
+                                       <para>A numeric code for the channel's current state, related to SpyeeChannelStateDesc</para>
+                               </parameter>
+                               <parameter name="SpyeeChannelStateDesc">
+                                       <enumlist>
+                                               <enum name="Down"/>
+                                               <enum name="Rsrvd"/>
+                                               <enum name="OffHook"/>
+                                               <enum name="Dialing"/>
+                                               <enum name="Ring"/>
+                                               <enum name="Ringing"/>
+                                               <enum name="Up"/>
+                                               <enum name="Busy"/>
+                                               <enum name="Dialing Offhook"/>
+                                               <enum name="Pre-ring"/>
+                                               <enum name="Unknown"/>
+                                       </enumlist>
+                               </parameter>
+                               <parameter name="SpyeeCallerIDNum">
+                               </parameter>
+                               <parameter name="SpyeeCallerIDName">
+                               </parameter>
+                               <parameter name="SpyeeConnectedLineNum">
+                               </parameter>
+                               <parameter name="SpyeeConnectedLineName">
+                               </parameter>
+                               <parameter name="SpyeeAccountCode">
+                               </parameter>
+                               <parameter name="SpyeeContext">
+                               </parameter>
+                               <parameter name="SpyeeExten">
+                               </parameter>
+                               <parameter name="SpyeePriority">
+                               </parameter>
+                               <parameter name="SpyeeUniqueid">
+                               </parameter>
+                       </syntax>
+                       <see-also>
+                               <ref type="application">ChanSpyStop</ref>
+                       </see-also>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="ChanSpyStop">
+               <managerEventInstance class="EVENT_FLAG_CALL">
+                       <synopsis>Raised when a channel has stopped spying.</synopsis>
+                       <syntax>
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='ChanSpyStart']/managerEventInstance/syntax/parameter[contains(@name, 'Spyer')])" />
+                       </syntax>
+                       <see-also>
+                               <ref type="application">ChanSpyStart</ref>
+                       </see-also>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="HangupHandlerRun">
+               <managerEventInstance class="EVENT_FLAG_DIALPLAN">
+                       <synopsis>Raised when a hangup handler is about to be called.</synopsis>
+                       <syntax>
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
+                               <parameter name="Handler">
+                                       <para>Hangup handler parameter string passed to the Gosub application.</para>
+                               </parameter>
+                       </syntax>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="HangupHandlerPop">
+               <managerEventInstance class="EVENT_FLAG_DIALPLAN">
+                       <synopsis>
+                               Raised when a hangup handler is removed from the handler stack
+                               by the CHANNEL() function.
+                       </synopsis>
+                       <syntax>
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='HangupHandlerRun']/managerEventInstance/syntax/parameter)" />
+                       </syntax>
+                       <see-also>
+                               <ref type="managerEvent">HangupHandlerPush</ref>
+                               <ref type="function">CHANNEL</ref>
+                       </see-also>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="HangupHandlerPush">
+               <managerEventInstance class="EVENT_FLAG_DIALPLAN">
+                       <synopsis>
+                               Raised when a hangup handler is added to the handler stack by
+                               the CHANNEL() function.
+                       </synopsis>
+                       <syntax>
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='HangupHandlerRun']/managerEventInstance/syntax/parameter)" />
+                       </syntax>
+                       <see-also>
+                               <ref type="managerEvent">HangupHandlerPop</ref>
+                               <ref type="function">CHANNEL</ref>
+                       </see-also>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="FAXStatus">
+               <managerEventInstance class="EVENT_FLAG_CALL">
+                       <synopsis>
+                               Raised periodically during a fax transmission.
+                       </synopsis>
+                       <syntax>
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
+                               <parameter name="Operation">
+                                       <enumlist>
+                                               <enum name="gateway"/>
+                                               <enum name="receive"/>
+                                               <enum name="send"/>
+                                       </enumlist>
+                               </parameter>
+                               <parameter name="Status">
+                                       <para>A text message describing the current status of the fax</para>
+                               </parameter>
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='ReceiveFAX']/managerEventInstance/syntax/parameter[@name='LocalStationID'])" />
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='ReceiveFAX']/managerEventInstance/syntax/parameter[@name='FileName'])" />
+                       </syntax>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="ReceiveFAX">
+               <managerEventInstance class="EVENT_FLAG_CALL">
+                       <synopsis>
+                               Raised when a receive fax operation has completed.
+                       </synopsis>
+                       <syntax>
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
+                               <parameter name="LocalStationID">
+                                       <para>The value of the <variable>LOCALSTATIONID</variable> channel variable</para>
+                               </parameter>
+                               <parameter name="RemoteStationID">
+                                       <para>The value of the <variable>REMOTESTATIONID</variable> channel variable</para>
+                               </parameter>
+                               <parameter name="PagesTransferred">
+                                       <para>The number of pages that have been transferred</para>
+                               </parameter>
+                               <parameter name="Resolution">
+                                       <para>The negotiated resolution</para>
+                               </parameter>
+                               <parameter name="TransferRate">
+                                       <para>The negotiated transfer rate</para>
+                               </parameter>
+                               <parameter name="FileName" multiple="yes">
+                                       <para>The files being affected by the fax operation</para>
+                               </parameter>
+                       </syntax>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="SendFAX">
+               <managerEventInstance class="EVENT_FLAG_CALL">
+                       <synopsis>
+                               Raised when a send fax operation has completed.
+                       </synopsis>
+                       <syntax>
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='ReceiveFAX']/managerEventInstance/syntax/parameter)" />
+                       </syntax>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="MusicOnHoldStart">
+               <managerEventInstance class="EVENT_FLAG_CALL">
+                       <synopsis>Raised when music on hold has started on a channel.</synopsis>
+                       <syntax>
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
+                               <parameter name="Class">
+                                       <para>The class of music being played on the channel</para>
+                               </parameter>
+                       </syntax>
+                       <see-also>
+                               <ref type="managerEvent">MusicOnHoldStop</ref>
+                               <ref type="application">MusicOnHold</ref>
+                       </see-also>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="MusicOnHoldStop">
+               <managerEventInstance class="EVENT_FLAG_CALL">
+                       <synopsis>Raised when music on hold has stopped on a channel.</synopsis>
+                       <syntax>
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
+                       </syntax>
+                       <see-also>
+                               <ref type="managerEvent">MusicOnHoldStart</ref>
+                               <ref type="application">StopMusicOnHold</ref>
+                       </see-also>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="MonitorStart">
+               <managerEventInstance class="EVENT_FLAG_CALL">
+                       <synopsis>Raised when monitoring has started on a channel.</synopsis>
+                       <syntax>
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
+                       </syntax>
+                       <see-also>
+                               <ref type="managerEvent">MonitorStop</ref>
+                               <ref type="application">Monitor</ref>
+                               <ref type="manager">Monitor</ref>
+                       </see-also>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="MonitorStop">
+               <managerEventInstance class="EVENT_FLAG_CALL">
+               <synopsis>Raised when monitoring has stopped on a channel.</synopsis>
+               <syntax>
+                       <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
+               </syntax>
+               <see-also>
+                       <ref type="managerEvent">MonitorStart</ref>
+                       <ref type="application">StopMonitor</ref>
+                       <ref type="manager">StopMonitor</ref>
+               </see-also>
+               </managerEventInstance>
+       </managerEvent>
+***/
+
+/*! \brief The \ref stasis subscription returned by the forwarding of the channel topic
+ * to the manager topic
+ */
+static struct stasis_subscription *topic_forwarder;
 
-struct ast_str *ast_manager_build_channel_state_string_suffix(
+struct ast_str *ast_manager_build_channel_state_string_prefix(
                const struct ast_channel_snapshot *snapshot,
-               const char *suffix)
+               const char *prefix)
 {
        struct ast_str *out = ast_str_create(1024);
        int res = 0;
@@ -242,30 +499,30 @@ struct ast_str *ast_manager_build_channel_state_string_suffix(
                return NULL;
        }
        res = ast_str_set(&out, 0,
-               "Channel%s: %s\r\n"
-               "ChannelState%s: %d\r\n"
-               "ChannelStateDesc%s: %s\r\n"
-               "CallerIDNum%s: %s\r\n"
-               "CallerIDName%s: %s\r\n"
-               "ConnectedLineNum%s: %s\r\n"
-               "ConnectedLineName%s: %s\r\n"
-               "AccountCode%s: %s\r\n"
-               "Context%s: %s\r\n"
-               "Exten%s: %s\r\n"
-               "Priority%s: %d\r\n"
-               "Uniqueid%s: %s\r\n",
-               suffix, snapshot->name,
-               suffix, snapshot->state,
-               suffix, ast_state2str(snapshot->state),
-               suffix, S_OR(snapshot->caller_number, "<unknown>"),
-               suffix, S_OR(snapshot->caller_name, "<unknown>"),
-               suffix, S_OR(snapshot->connected_number, "<unknown>"),
-               suffix, S_OR(snapshot->connected_name, "<unknown>"),
-               suffix, snapshot->accountcode,
-               suffix, snapshot->context,
-               suffix, snapshot->exten,
-               suffix, snapshot->priority,
-               suffix, snapshot->uniqueid);
+               "%sChannel: %s\r\n"
+               "%sChannelState: %d\r\n"
+               "%sChannelStateDesc: %s\r\n"
+               "%sCallerIDNum: %s\r\n"
+               "%sCallerIDName: %s\r\n"
+               "%sConnectedLineNum: %s\r\n"
+               "%sConnectedLineName: %s\r\n"
+               "%sAccountCode: %s\r\n"
+               "%sContext: %s\r\n"
+               "%sExten: %s\r\n"
+               "%sPriority: %d\r\n"
+               "%sUniqueid: %s\r\n",
+               prefix, snapshot->name,
+               prefix, snapshot->state,
+               prefix, ast_state2str(snapshot->state),
+               prefix, S_OR(snapshot->caller_number, "<unknown>"),
+               prefix, S_OR(snapshot->caller_name, "<unknown>"),
+               prefix, S_OR(snapshot->connected_number, "<unknown>"),
+               prefix, S_OR(snapshot->connected_name, "<unknown>"),
+               prefix, snapshot->accountcode,
+               prefix, snapshot->context,
+               prefix, snapshot->exten,
+               prefix, snapshot->priority,
+               prefix, snapshot->uniqueid);
 
        if (!res) {
                return NULL;
@@ -274,8 +531,8 @@ struct ast_str *ast_manager_build_channel_state_string_suffix(
        if (snapshot->manager_vars) {
                struct ast_var_t *var;
                AST_LIST_TRAVERSE(snapshot->manager_vars, var, entries) {
-                       ast_str_append(&out, 0, "ChanVariable%s: %s=%s\r\n",
-                                      suffix,
+                       ast_str_append(&out, 0, "%sChanVariable: %s=%s\r\n",
+                                      prefix,
                                       var->name, var->value);
                }
        }
@@ -286,7 +543,7 @@ struct ast_str *ast_manager_build_channel_state_string_suffix(
 struct ast_str *ast_manager_build_channel_state_string(
                const struct ast_channel_snapshot *snapshot)
 {
-       return ast_manager_build_channel_state_string_suffix(snapshot, "");
+       return ast_manager_build_channel_state_string_prefix(snapshot, "");
 }
 
 /*! \brief Typedef for callbacks that get called on channel snapshot updates */
@@ -477,38 +734,6 @@ static void channel_varset_cb(void *data, struct stasis_subscription *sub,
                      variable, value);
 }
 
-/*!
- * \brief Callback used to determine whether a key should be skipped when converting a JSON object to a manager blob
- * \param key Key from JSON blob to be evaluated
- * \retval non-zero if the key should be excluded
- * \retval zero if the key should not be excluded
- */
-typedef int (*key_exclusion_cb)(const char *key);
-
-static struct ast_str *manager_str_from_json_object(struct ast_json *blob, key_exclusion_cb exclusion_cb)
-{
-       struct ast_str *output_str = ast_str_create(32);
-       struct ast_json_iter *blob_iter = ast_json_object_iter(blob);
-       if (!output_str || !blob_iter) {
-               return NULL;
-       }
-
-       do {
-               const char *key = ast_json_object_iter_key(blob_iter);
-               const char *value = ast_json_string_get(ast_json_object_iter_value(blob_iter));
-               if (exclusion_cb && exclusion_cb(key)) {
-                       continue;
-               }
-
-               ast_str_append(&output_str, 0, "%s: %s\r\n", key, value);
-               if (!output_str) {
-                       return NULL;
-               }
-       } while ((blob_iter = ast_json_object_iter_next(blob, blob_iter)));
-
-       return output_str;
-}
-
 static int userevent_exclusion_cb(const char *key)
 {
        if (!strcmp("type", key)) {
@@ -529,7 +754,7 @@ static void channel_user_event_cb(void *data, struct stasis_subscription *sub,
        const char *eventname;
 
        eventname = ast_json_string_get(ast_json_object_get(obj->blob, "eventname"));
-       body = manager_str_from_json_object(obj->blob, userevent_exclusion_cb);
+       body = ast_manager_str_from_json_object(obj->blob, userevent_exclusion_cb);
        channel_event_string = ast_manager_build_channel_state_string(obj->snapshot);
 
        if (!channel_event_string || !body) {
@@ -557,6 +782,20 @@ static void channel_user_event_cb(void *data, struct stasis_subscription *sub,
                      ast_str_buffer(channel_event_string), eventname, ast_str_buffer(body));
 }
 
+static void publish_basic_channel_event(const char *event, int class, struct ast_channel_snapshot *snapshot)
+{
+       RAII_VAR(struct ast_str *, channel_event_string, NULL, ast_free);
+
+       channel_event_string = ast_manager_build_channel_state_string(snapshot);
+       if (!channel_event_string) {
+               return;
+       }
+
+       manager_event(class, event,
+               "%s",
+               ast_str_buffer(channel_event_string));
+}
+
 static void channel_hangup_request_cb(void *data,
        struct stasis_subscription *sub, struct stasis_topic *topic,
        struct stasis_message *message)
@@ -597,6 +836,64 @@ static void channel_hangup_request_cb(void *data,
                      ast_str_buffer(extra));
 }
 
+static void channel_chanspy_stop_cb(void *data, struct stasis_subscription *sub,
+               struct stasis_topic *topic, struct stasis_message *message)
+{
+       RAII_VAR(struct ast_str *, spyer_channel_string, NULL, ast_free);
+       RAII_VAR(struct ast_channel_snapshot *, spyer, NULL, ao2_cleanup);
+       struct ast_multi_channel_blob *payload = stasis_message_data(message);
+
+       spyer = ast_multi_channel_blob_get_channel(payload, "spyer_channel");
+       if (!spyer) {
+               ast_log(AST_LOG_WARNING, "Received ChanSpy Start event with no spyer channel!\n");
+               return;
+       }
+
+       spyer_channel_string = ast_manager_build_channel_state_string_prefix(spyer, "Spyer");
+       if (!spyer_channel_string) {
+               return;
+       }
+
+       manager_event(EVENT_FLAG_CALL, "ChanSpyStop",
+                     "%s",
+                     ast_str_buffer(spyer_channel_string));
+}
+
+static void channel_chanspy_start_cb(void *data, struct stasis_subscription *sub,
+               struct stasis_topic *topic, struct stasis_message *message)
+{
+       RAII_VAR(struct ast_str *, spyer_channel_string, NULL, ast_free);
+       RAII_VAR(struct ast_str *, spyee_channel_string, NULL, ast_free);
+       RAII_VAR(struct ast_channel_snapshot *, spyer, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_channel_snapshot *, spyee, NULL, ao2_cleanup);
+       struct ast_multi_channel_blob *payload = stasis_message_data(message);
+
+       spyer = ast_multi_channel_blob_get_channel(payload, "spyer_channel");
+       if (!spyer) {
+               ast_log(AST_LOG_WARNING, "Received ChanSpy Start event with no spyer channel!\n");
+               return;
+       }
+       spyee = ast_multi_channel_blob_get_channel(payload, "spyee_channel");
+       if (!spyee) {
+               ast_log(AST_LOG_WARNING, "Received ChanSpy Start event with no spyee channel!\n");
+               return;
+       }
+
+       spyer_channel_string = ast_manager_build_channel_state_string_prefix(spyer, "Spyer");
+       if (!spyer_channel_string) {
+               return;
+       }
+       spyee_channel_string = ast_manager_build_channel_state_string_prefix(spyee, "Spyee");
+       if (!spyee_channel_string) {
+               return;
+       }
+
+       manager_event(EVENT_FLAG_CALL, "ChanSpyStart",
+                     "%s%s",
+                     ast_str_buffer(spyer_channel_string),
+                     ast_str_buffer(spyee_channel_string));
+}
+
 static void channel_dtmf_begin_cb(void *data, struct stasis_subscription *sub,
        struct stasis_topic *topic, struct stasis_message *message)
 {
@@ -685,6 +982,154 @@ static void channel_dtmf_end_cb(void *data, struct stasis_subscription *sub,
                digit, duration_ms, direction);
 }
 
+static void channel_hangup_handler_cb(void *data, struct stasis_subscription *sub,
+               struct stasis_topic *topic, struct stasis_message *message)
+{
+       RAII_VAR(struct ast_str *, channel_event_string, NULL, ast_free);
+       struct ast_channel_blob *payload = stasis_message_data(message);
+       const char *action = ast_json_string_get(ast_json_object_get(payload->blob, "type"));
+       const char *handler = ast_json_string_get(ast_json_object_get(payload->blob, "handler"));
+       const char *event;
+
+       channel_event_string = ast_manager_build_channel_state_string(payload->snapshot);
+
+       if (!channel_event_string) {
+               return;
+       }
+
+       if (!strcmp(action, "type")) {
+               event = "HangupHandlerRun";
+       } else if (!strcmp(action, "type")) {
+               event = "HangupHandlerPop";
+       } else if (!strcmp(action, "type")) {
+               event = "HangupHandlerPush";
+       } else {
+               return;
+       }
+       manager_event(EVENT_FLAG_DIALPLAN, event,
+               "%s"
+               "Handler: %s\r\n",
+               ast_str_buffer(channel_event_string),
+               handler);
+}
+
+static void channel_fax_cb(void *data, struct stasis_subscription *sub,
+               struct stasis_topic *topic, struct stasis_message *message)
+{
+       RAII_VAR(struct ast_str *, channel_event_string, NULL, ast_free);
+       RAII_VAR(struct ast_str *, event_buffer, ast_str_create(256), ast_free);
+       struct ast_channel_blob *payload = stasis_message_data(message);
+       const char *type = ast_json_string_get(ast_json_object_get(payload->blob, "type"));
+       struct ast_json *operation = ast_json_object_get(payload->blob, "operation");
+       struct ast_json *status = ast_json_object_get(payload->blob, "status");
+       struct ast_json *local_station_id = ast_json_object_get(payload->blob, "local_station_id");
+       struct ast_json *remote_station_id = ast_json_object_get(payload->blob, "remote_station_id");
+       struct ast_json *fax_pages = ast_json_object_get(payload->blob, "fax_pages");
+       struct ast_json *fax_resolution = ast_json_object_get(payload->blob, "fax_resolution");
+       struct ast_json *fax_bitrate = ast_json_object_get(payload->blob, "fax_bitrate");
+       struct ast_json *filenames = ast_json_object_get(payload->blob, "filenames");
+       const char *event;
+       size_t array_len;
+       size_t i;
+
+       if (!event_buffer) {
+               return;
+       }
+
+       channel_event_string = ast_manager_build_channel_state_string(payload->snapshot);
+       if (!channel_event_string) {
+               return;
+       }
+
+       if (!strcmp(type, "status")) {
+               event = "FAXStatus";
+       } else if (!strcmp(type, "receive")) {
+               event = "ReceiveFAX";
+       } else if (!strcmp(type, "send")) {
+               event = "SendFAX";
+       } else {
+               return;
+       }
+
+       if (operation) {
+               ast_str_append(&event_buffer, 0, "Operation: %s\r\n", ast_json_string_get(operation));
+       }
+       if (status) {
+               ast_str_append(&event_buffer, 0, "Status: %s\r\n", ast_json_string_get(status));
+       }
+       if (local_station_id) {
+               ast_str_append(&event_buffer, 0, "LocalStationID: %s\r\n", ast_json_string_get(local_station_id));
+       }
+       if (remote_station_id) {
+               ast_str_append(&event_buffer, 0, "RemoteStationID: %s\r\n", ast_json_string_get(remote_station_id));
+       }
+       if (fax_pages) {
+               ast_str_append(&event_buffer, 0, "PagesTransferred: %s\r\n", ast_json_string_get(fax_pages));
+       }
+       if (fax_resolution) {
+               ast_str_append(&event_buffer, 0, "Resolution: %s\r\n", ast_json_string_get(fax_resolution));
+       }
+       if (fax_bitrate) {
+               ast_str_append(&event_buffer, 0, "TransferRate: %s\r\n", ast_json_string_get(fax_bitrate));
+       }
+       if (filenames) {
+               array_len = ast_json_array_size(filenames);
+               for (i = 0; i < array_len; i++) {
+                       ast_str_append(&event_buffer, 0, "FileName: %s\r\n", ast_json_string_get(ast_json_array_get(filenames, i)));
+               }
+       }
+
+       manager_event(EVENT_FLAG_CALL, event,
+               "%s"
+               "%s",
+               ast_str_buffer(channel_event_string),
+               ast_str_buffer(event_buffer));
+}
+
+static void channel_moh_start_cb(void *data, struct stasis_subscription *sub,
+               struct stasis_topic *topic, struct stasis_message *message)
+{
+       struct ast_channel_blob *payload = stasis_message_data(message);
+       struct ast_json *blob = payload->blob;
+       RAII_VAR(struct ast_str *, channel_event_string, NULL, ast_free);
+
+       channel_event_string = ast_manager_build_channel_state_string(payload->snapshot);
+       if (!channel_event_string) {
+               return;
+       }
+
+       manager_event(EVENT_FLAG_CALL, "MusicOnHoldStart",
+               "%s"
+               "Class: %s\r\n",
+               ast_str_buffer(channel_event_string),
+               ast_json_string_get(ast_json_object_get(blob, "class")));
+
+}
+
+static void channel_moh_stop_cb(void *data, struct stasis_subscription *sub,
+               struct stasis_topic *topic, struct stasis_message *message)
+{
+       struct ast_channel_blob *payload = stasis_message_data(message);
+
+       publish_basic_channel_event("MusicOnHoldStop", EVENT_FLAG_CALL, payload->snapshot);
+}
+
+static void channel_monitor_start_cb(void *data, struct stasis_subscription *sub,
+               struct stasis_topic *topic, struct stasis_message *message)
+{
+       struct ast_channel_blob *payload = stasis_message_data(message);
+
+       publish_basic_channel_event("MonitorStart", EVENT_FLAG_CALL, payload->snapshot);
+}
+
+static void channel_monitor_stop_cb(void *data, struct stasis_subscription *sub,
+               struct stasis_topic *topic, struct stasis_message *message)
+{
+       struct ast_channel_blob *payload = stasis_message_data(message);
+
+       publish_basic_channel_event("MonitorStop", EVENT_FLAG_CALL, payload->snapshot);
+}
+
 /*!
  * \brief Callback processing messages for channel dialing
  */
@@ -704,7 +1149,7 @@ static void channel_dial_cb(void *data, struct stasis_subscription *sub,
 
        /* Peer is required - otherwise, who are we dialing? */
        ast_assert(peer != NULL);
-       peer_event_string = ast_manager_build_channel_state_string_suffix(peer, "Dest");
+       peer_event_string = ast_manager_build_channel_state_string_prefix(peer, "Dest");
        if (!peer_event_string) {
                return;
        }
@@ -737,63 +1182,112 @@ static void channel_dial_cb(void *data, struct stasis_subscription *sub,
 
 static void manager_channels_shutdown(void)
 {
-       stasis_message_router_unsubscribe_and_join(channel_state_router);
-       channel_state_router = NULL;
+       stasis_unsubscribe(topic_forwarder);
+       topic_forwarder = NULL;
 }
 
 int manager_channels_init(void)
 {
        int ret = 0;
+       struct stasis_topic *manager_topic;
+       struct stasis_topic *channel_topic;
+       struct stasis_message_router *message_router;
 
-       if (channel_state_router) {
-               /* Already initialized */
-               return 0;
+       manager_topic = ast_manager_get_topic();
+       if (!manager_topic) {
+               return -1;
+       }
+       message_router = ast_manager_get_message_router();
+       if (!message_router) {
+               return -1;
+       }
+       channel_topic = stasis_caching_get_topic(ast_channel_topic_all_cached());
+       if (!channel_topic) {
+               return -1;
        }
 
-       ast_register_atexit(manager_channels_shutdown);
-
-       channel_state_router = stasis_message_router_create(
-               stasis_caching_get_topic(ast_channel_topic_all_cached()));
-
-       if (!channel_state_router) {
+       topic_forwarder = stasis_forward_all(channel_topic, manager_topic);
+       if (!topic_forwarder) {
                return -1;
        }
 
-       ret |= stasis_message_router_add(channel_state_router,
+       ast_register_atexit(manager_channels_shutdown);
+
+       ret |= stasis_message_router_add(message_router,
                                         stasis_cache_update_type(),
                                         channel_snapshot_update,
                                         NULL);
 
-       ret |= stasis_message_router_add(channel_state_router,
+       ret |= stasis_message_router_add(message_router,
                                         ast_channel_varset_type(),
                                         channel_varset_cb,
                                         NULL);
 
-       ret |= stasis_message_router_add(channel_state_router,
+       ret |= stasis_message_router_add(message_router,
                                         ast_channel_user_event_type(),
                                         channel_user_event_cb,
                                         NULL);
 
-       ret |= stasis_message_router_add(channel_state_router,
+       ret |= stasis_message_router_add(message_router,
                                         ast_channel_dtmf_begin_type(),
                                         channel_dtmf_begin_cb,
                                         NULL);
 
-       ret |= stasis_message_router_add(channel_state_router,
+       ret |= stasis_message_router_add(message_router,
                                         ast_channel_dtmf_end_type(),
                                         channel_dtmf_end_cb,
                                         NULL);
 
-       ret |= stasis_message_router_add(channel_state_router,
+       ret |= stasis_message_router_add(message_router,
                                         ast_channel_hangup_request_type(),
                                         channel_hangup_request_cb,
                                         NULL);
 
-       ret |= stasis_message_router_add(channel_state_router,
+       ret |= stasis_message_router_add(message_router,
                                         ast_channel_dial_type(),
                                         channel_dial_cb,
                                         NULL);
 
+       ret |= stasis_message_router_add(message_router,
+                                        ast_channel_fax_type(),
+                                        channel_fax_cb,
+                                        NULL);
+
+       ret |= stasis_message_router_add(message_router,
+                                        ast_channel_chanspy_start_type(),
+                                        channel_chanspy_start_cb,
+                                        NULL);
+
+       ret |= stasis_message_router_add(message_router,
+                                        ast_channel_chanspy_stop_type(),
+                                        channel_chanspy_stop_cb,
+                                        NULL);
+
+       ret |= stasis_message_router_add(message_router,
+                                        ast_channel_hangup_handler_type(),
+                                        channel_hangup_handler_cb,
+                                        NULL);
+
+       ret |= stasis_message_router_add(message_router,
+                                        ast_channel_moh_start_type(),
+                                        channel_moh_start_cb,
+                                        NULL);
+
+       ret |= stasis_message_router_add(message_router,
+                                        ast_channel_moh_stop_type(),
+                                        channel_moh_stop_cb,
+                                        NULL);
+
+       ret |= stasis_message_router_add(message_router,
+                                        ast_channel_monitor_start_type(),
+                                        channel_monitor_start_cb,
+                                        NULL);
+
+       ret |= stasis_message_router_add(message_router,
+                                        ast_channel_monitor_stop_type(),
+                                        channel_monitor_stop_cb,
+                                        NULL);
+
        /* If somehow we failed to add any routes, just shut down the whole
         * thing and fail it.
         */
diff --git a/main/manager_mwi.c b/main/manager_mwi.c
new file mode 100644 (file)
index 0000000..ac62908
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Matt Jordan <mjordan@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief The Asterisk Management Interface - AMI (MWI event handling)
+ *
+ * \author Matt Jordan <mjordan@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/manager.h"
+#include "asterisk/app.h"
+#include "asterisk/channel.h"
+#include "asterisk/stasis_message_router.h"
+#include "asterisk/stasis.h"
+
+struct stasis_message_router *mwi_state_router;
+
+/*** DOCUMENTATION
+ ***/
+
+/*! \brief The \ref stasis subscription returned by the forwarding of the MWI topic
+ * to the manager topic
+ */
+static struct stasis_subscription *topic_forwarder;
+
+/*! \brief Callback function used by \ref mwi_app_event_cb to weed out "Event" keys */
+static int exclude_event_cb(const char *key)
+{
+       if (!strcmp(key, "Event")) {
+               return -1;
+       }
+       return 0;
+}
+
+/*! \brief Generic MWI event callback used for one-off events from voicemail modules */
+static void mwi_app_event_cb(void *data, struct stasis_subscription *sub,
+                                   struct stasis_topic *topic,
+                                   struct stasis_message *message)
+{
+       struct ast_mwi_blob *payload = stasis_message_data(message);
+       RAII_VAR(struct ast_str *, channel_event_string, NULL, ast_free);
+       RAII_VAR(struct ast_str *, event_buffer, NULL, ast_free);
+       struct ast_json *event_json = ast_json_object_get(payload->blob, "Event");
+
+       if (!event_json) {
+               return;
+       }
+
+       if (payload->mwi_state && payload->mwi_state->snapshot) {
+               channel_event_string = ast_manager_build_channel_state_string(payload->mwi_state->snapshot);
+       }
+
+       event_buffer = ast_manager_str_from_json_object(payload->blob, exclude_event_cb);
+       if (!event_buffer) {
+               ast_log(AST_LOG_WARNING, "Failed to create payload for event %s\n", ast_json_string_get(event_json));
+               return;
+       }
+
+       manager_event(EVENT_FLAG_CALL, ast_json_string_get(event_json),
+                       "Mailbox: %s\r\n"
+                       "%s"
+                       "%s",
+                       payload->mwi_state ? payload->mwi_state->uniqueid : "Unknown",
+                       ast_str_buffer(event_buffer),
+                       channel_event_string ? ast_str_buffer(channel_event_string) : "");
+}
+
+static void mwi_update_cb(void *data, struct stasis_subscription *sub,
+                                   struct stasis_topic *topic,
+                                   struct stasis_message *message)
+{
+       struct ast_mwi_state *mwi_state;
+       RAII_VAR(struct ast_str *, channel_event_string, NULL, ast_free);
+
+       if (ast_mwi_state_type() != stasis_message_type(message)) {
+               return;
+       }
+
+       mwi_state = stasis_message_data(message);
+       if (!mwi_state) {
+               return;
+       }
+
+       if (mwi_state->snapshot) {
+               channel_event_string = ast_manager_build_channel_state_string(mwi_state->snapshot);
+       }
+
+       /*** DOCUMENTATION
+               <managerEventInstance>
+                       <synopsis>Raised when the state of messages in a voicemail mailbox
+                       has changed or when a channel has finished interacting with a
+                       mailbox.</synopsis>
+                       <syntax>
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
+                               <parameter name="Mailbox">
+                                       <para>The mailbox with the new message, specified as <literal>mailbox</literal>@<literal>context</literal></para>
+                               </parameter>
+                               <parameter name="Waiting">
+                                       <para>Whether or not the mailbox has messages waiting for it.</para>
+                               </parameter>
+                               <parameter name="New">
+                                       <para>The number of new messages.</para>
+                               </parameter>
+                               <parameter name="Old">
+                                       <para>The number of old messages.</para>
+                               </parameter>
+                       </syntax>
+                       <description>
+                               <note><para>The Channel related parameters are only present if a
+                               channel was involved in the manipulation of a mailbox. If no
+                               channel is involved, the parameters are not included with the
+                               event.</para>
+                               </note>
+                       </description>
+               </managerEventInstance>
+       ***/
+       manager_event(EVENT_FLAG_CALL, "MessageWaiting",
+                       "%s"
+                       "Mailbox: %s\r\n"
+                       "Waiting: %d\r\n"
+                       "New: %d\r\n"
+                       "Old: %d\r\n",
+                       AS_OR(channel_event_string, ""),
+                       mwi_state->uniqueid,
+                       ast_app_has_voicemail(mwi_state->uniqueid, NULL),
+                       mwi_state->new_msgs,
+                       mwi_state->old_msgs);
+}
+
+static void manager_mwi_shutdown(void)
+{
+       stasis_unsubscribe(topic_forwarder);
+       topic_forwarder = NULL;
+}
+
+int manager_mwi_init(void)
+{
+       int ret = 0;
+       struct stasis_topic *manager_topic;
+       struct stasis_topic *mwi_topic;
+       struct stasis_message_router *message_router;
+
+       manager_topic = ast_manager_get_topic();
+       if (!manager_topic) {
+               return -1;
+       }
+       message_router = ast_manager_get_message_router();
+       if (!message_router) {
+               return -1;
+       }
+       mwi_topic = ast_mwi_topic_all();
+       if (!mwi_topic) {
+               return -1;
+       }
+
+       topic_forwarder = stasis_forward_all(mwi_topic, manager_topic);
+       if (!topic_forwarder) {
+               return -1;
+       }
+
+       ast_register_atexit(manager_mwi_shutdown);
+
+       ret |= stasis_message_router_add(message_router,
+                                        ast_mwi_state_type(),
+                                        mwi_update_cb,
+                                        NULL);
+
+       ret |= stasis_message_router_add(message_router,
+                                        ast_mwi_vm_app_type(),
+                                        mwi_app_event_cb,
+                                        NULL);
+
+       /* If somehow we failed to add any routes, just shut down the whole
+        * thing and fail it.
+        */
+       if (ret) {
+               manager_mwi_shutdown();
+               return -1;
+       }
+
+       return 0;
+}
index 8408048..1c26a9c 100644 (file)
@@ -5769,6 +5769,30 @@ void ast_pbx_h_exten_run(struct ast_channel *chan, const char *context)
        ast_channel_unlock(chan);
 }
 
+/*!
+ * \internal
+ * \brief Publish a hangup handler related message to \ref stasis
+ */
+static void publish_hangup_handler_message(const char *action, struct ast_channel *chan, const char *handler)
+{
+       RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
+       RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
+
+       blob = ast_json_pack("{s: s, s: s}",
+                       "type", action,
+                       "handler", S_OR(handler, ""));
+       if (!blob) {
+               return;
+       }
+
+       message = ast_channel_blob_create(chan, ast_channel_hangup_handler_type(), blob);
+       if (!message) {
+               return;
+       }
+
+       stasis_publish(ast_channel_topic(chan), message);
+}
+
 int ast_pbx_hangup_handler_run(struct ast_channel *chan)
 {
        struct ast_hangup_handler_list *handlers;
@@ -5798,23 +5822,7 @@ int ast_pbx_hangup_handler_run(struct ast_channel *chan)
                        break;
                }
 
-               /*** DOCUMENTATION
-                       <managerEventInstance>
-                               <synopsis>Raised when a hangup handler is about to be called.</synopsis>
-                               <syntax>
-                                       <parameter name="Handler">
-                                               <para>Hangup handler parameter string passed to the Gosub application.</para>
-                                       </parameter>
-                               </syntax>
-                       </managerEventInstance>
-               ***/
-               manager_event(EVENT_FLAG_DIALPLAN, "HangupHandlerRun",
-                       "Channel: %s\r\n"
-                       "Uniqueid: %s\r\n"
-                       "Handler: %s\r\n",
-                       ast_channel_name(chan),
-                       ast_channel_uniqueid(chan),
-                       h_handler->args);
+               publish_hangup_handler_message("run", chan, h_handler->args);
                ast_channel_unlock(chan);
 
                ast_app_exec_sub(NULL, chan, h_handler->args, 1);
@@ -5859,30 +5867,7 @@ int ast_pbx_hangup_handler_pop(struct ast_channel *chan)
        handlers = ast_channel_hangup_handlers(chan);
        h_handler = AST_LIST_REMOVE_HEAD(handlers, node);
        if (h_handler) {
-               /*** DOCUMENTATION
-                       <managerEventInstance>
-                               <synopsis>
-                                       Raised when a hangup handler is removed from the handler
-                                       stack by the CHANNEL() function.
-                               </synopsis>
-                               <syntax>
-                                       <parameter name="Handler">
-                                               <para>Hangup handler parameter string passed to the Gosub application.</para>
-                                       </parameter>
-                               </syntax>
-                               <see-also>
-                                       <ref type="managerEvent">HangupHandlerPush</ref>
-                                       <ref type="function">CHANNEL</ref>
-                               </see-also>
-                       </managerEventInstance>
-               ***/
-               manager_event(EVENT_FLAG_DIALPLAN, "HangupHandlerPop",
-                       "Channel: %s\r\n"
-                       "Uniqueid: %s\r\n"
-                       "Handler: %s\r\n",
-                       ast_channel_name(chan),
-                       ast_channel_uniqueid(chan),
-                       h_handler->args);
+               publish_hangup_handler_message("pop", chan, h_handler->args);
        }
        ast_channel_unlock(chan);
        if (h_handler) {
@@ -5918,32 +5903,7 @@ void ast_pbx_hangup_handler_push(struct ast_channel *chan, const char *handler)
 
        handlers = ast_channel_hangup_handlers(chan);
        AST_LIST_INSERT_HEAD(handlers, h_handler, node);
-
-       /*** DOCUMENTATION
-               <managerEventInstance>
-                       <synopsis>
-                               Raised when a hangup handler is added to the handler
-                               stack by the CHANNEL() function.
-                       </synopsis>
-                       <syntax>
-                               <parameter name="Handler">
-                                       <para>Hangup handler parameter string passed to the Gosub application.</para>
-                               </parameter>
-                       </syntax>
-                       <see-also>
-                               <ref type="managerEvent">HangupHandlerPop</ref>
-                               <ref type="function">CHANNEL</ref>
-                       </see-also>
-               </managerEventInstance>
-       ***/
-       manager_event(EVENT_FLAG_DIALPLAN, "HangupHandlerPush",
-               "Channel: %s\r\n"
-               "Uniqueid: %s\r\n"
-               "Handler: %s\r\n",
-               ast_channel_name(chan),
-               ast_channel_uniqueid(chan),
-               h_handler->args);
-
+       publish_hangup_handler_message("push", chan, h_handler->args);
        ast_channel_unlock(chan);
 }
 
index f8c9be3..d3c543a 100644 (file)
@@ -48,6 +48,14 @@ STASIS_MESSAGE_TYPE_DEFN(ast_channel_user_event_type);
 STASIS_MESSAGE_TYPE_DEFN(ast_channel_hangup_request_type);
 STASIS_MESSAGE_TYPE_DEFN(ast_channel_dtmf_begin_type);
 STASIS_MESSAGE_TYPE_DEFN(ast_channel_dtmf_end_type);
+STASIS_MESSAGE_TYPE_DEFN(ast_channel_chanspy_start_type);
+STASIS_MESSAGE_TYPE_DEFN(ast_channel_chanspy_stop_type);
+STASIS_MESSAGE_TYPE_DEFN(ast_channel_fax_type);
+STASIS_MESSAGE_TYPE_DEFN(ast_channel_hangup_handler_type);
+STASIS_MESSAGE_TYPE_DEFN(ast_channel_moh_start_type);
+STASIS_MESSAGE_TYPE_DEFN(ast_channel_moh_stop_type);
+STASIS_MESSAGE_TYPE_DEFN(ast_channel_monitor_start_type);
+STASIS_MESSAGE_TYPE_DEFN(ast_channel_monitor_stop_type);
 /*! @} */
 
 /*! \brief Topic for all channels */
@@ -150,28 +158,6 @@ struct ast_channel_snapshot *ast_channel_snapshot_create(struct ast_channel *cha
        return snapshot;
 }
 
-struct ast_channel_snapshot *ast_channel_snapshot_get_latest(
-       const char *uniqueid)
-{
-        RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
-       struct ast_channel_snapshot *snapshot;
-
-       msg = stasis_cache_get(ast_channel_topic_all_cached(),
-               ast_channel_snapshot_type(), uniqueid);
-
-       if (!msg) {
-               return NULL;
-       }
-
-       snapshot = stasis_message_data(msg);
-       if (!snapshot) {
-               return NULL;
-       }
-
-       ao2_ref(snapshot, +1);
-       return snapshot;
-}
-
 static void publish_message_for_channel_topics(struct stasis_message *message, struct ast_channel *chan)
 {
        if (chan) {
@@ -230,12 +216,13 @@ void ast_channel_publish_dial(struct ast_channel *caller, struct ast_channel *pe
        publish_message_for_channel_topics(msg, caller);
 }
 
-static struct stasis_message *channel_blob_create(
-       struct ast_channel_snapshot *snapshot,
-       struct stasis_message_type *type, struct ast_json *blob)
+static struct stasis_message *create_channel_blob_message(struct ast_channel_snapshot *snapshot,
+               struct stasis_message_type *type,
+               struct ast_json *blob)
+
 {
-       RAII_VAR(struct ast_channel_blob *, obj, NULL, ao2_cleanup);
        RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_channel_blob *, obj, NULL, ao2_cleanup);
 
        if (blob == NULL) {
                blob = ast_json_null();
@@ -247,10 +234,9 @@ static struct stasis_message *channel_blob_create(
        }
 
        if (snapshot) {
-               ao2_ref(snapshot, +1);
                obj->snapshot = snapshot;
+               ao2_ref(obj->snapshot, +1);
        }
-
        obj->blob = ast_json_ref(blob);
 
        msg = stasis_message_create(type, obj);
@@ -262,33 +248,27 @@ static struct stasis_message *channel_blob_create(
        return msg;
 }
 
-struct stasis_message *ast_channel_blob_create(struct ast_channel *chan,
-       struct stasis_message_type *type, struct ast_json *blob)
+struct stasis_message *ast_channel_cached_blob_create(struct ast_channel *chan,
+                                              struct stasis_message_type *type,
+                                              struct ast_json *blob)
 {
-       RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
-
-       if (chan != NULL) {
-               snapshot = ast_channel_snapshot_create(chan);
-               if (snapshot == NULL) {
-                       return NULL;
-               }
-       }
+       RAII_VAR(struct ast_channel_snapshot *, snapshot,
+                       ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan)),
+                       ao2_cleanup);
 
-       return channel_blob_create(snapshot, type, blob);
+       return create_channel_blob_message(snapshot, type, blob);
 }
 
-struct stasis_message *ast_channel_blob_create_from_cache(
-       const char *uniqueid, struct stasis_message_type *type,
-       struct ast_json *blob)
+struct stasis_message *ast_channel_blob_create(struct ast_channel *chan,
+       struct stasis_message_type *type, struct ast_json *blob)
 {
        RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
 
-       snapshot = ast_channel_snapshot_get_latest(uniqueid);
-       if (snapshot == NULL) {
-               return NULL;
+       if (chan) {
+               snapshot = ast_channel_snapshot_create(chan);
        }
 
-       return channel_blob_create(snapshot, type, blob);
+       return create_channel_blob_message(snapshot, type, blob);
 }
 
 /*! \brief A channel snapshot wrapper object used in \ref ast_multi_channel_blob objects */
@@ -362,6 +342,28 @@ struct ast_multi_channel_blob *ast_multi_channel_blob_create(struct ast_json *bl
        return obj;
 }
 
+struct ast_channel_snapshot *ast_channel_snapshot_get_latest(const char *uniqueid)
+{
+       RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
+       struct ast_channel_snapshot *snapshot;
+
+       ast_assert(!ast_strlen_zero(uniqueid));
+
+       message = stasis_cache_get(ast_channel_topic_all_cached(),
+                       ast_channel_snapshot_type(),
+                       uniqueid);
+       if (!message) {
+               return NULL;
+       }
+
+       snapshot = stasis_message_data(message);
+       if (!snapshot) {
+               return NULL;
+       }
+       ao2_ref(snapshot, +1);
+       return snapshot;
+}
+
 static void channel_role_snapshot_dtor(void *obj)
 {
        struct channel_role_snapshot *role_snapshot = obj;
@@ -459,7 +461,6 @@ void ast_channel_publish_snapshot(struct ast_channel *chan)
        stasis_publish(ast_channel_topic(chan), message);
 }
 
-
 void ast_channel_publish_varset(struct ast_channel *chan, const char *name, const char *value)
 {
        RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
@@ -584,6 +585,14 @@ void ast_stasis_channels_shutdown(void)
        STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_hangup_request_type);
        STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_dtmf_begin_type);
        STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_dtmf_end_type);
+       STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_chanspy_start_type);
+       STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_chanspy_stop_type);
+       STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_fax_type);
+       STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_hangup_handler_type);
+       STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_moh_start_type);
+       STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_moh_stop_type);
+       STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_monitor_start_type);
+       STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_monitor_stop_type);
 }
 
 void ast_stasis_channels_init(void)
@@ -595,7 +604,14 @@ void ast_stasis_channels_init(void)
        STASIS_MESSAGE_TYPE_INIT(ast_channel_hangup_request_type);
        STASIS_MESSAGE_TYPE_INIT(ast_channel_dtmf_begin_type);
        STASIS_MESSAGE_TYPE_INIT(ast_channel_dtmf_end_type);
-
+       STASIS_MESSAGE_TYPE_INIT(ast_channel_chanspy_start_type);
+       STASIS_MESSAGE_TYPE_INIT(ast_channel_chanspy_stop_type);
+       STASIS_MESSAGE_TYPE_INIT(ast_channel_fax_type);
+       STASIS_MESSAGE_TYPE_INIT(ast_channel_hangup_handler_type);
+       STASIS_MESSAGE_TYPE_INIT(ast_channel_moh_start_type);
+       STASIS_MESSAGE_TYPE_INIT(ast_channel_moh_stop_type);
+       STASIS_MESSAGE_TYPE_INIT(ast_channel_monitor_start_type);
+       STASIS_MESSAGE_TYPE_INIT(ast_channel_monitor_stop_type);
        channel_topic_all = stasis_topic_create("ast_channel_topic_all");
        channel_topic_all_cached = stasis_caching_topic_create(channel_topic_all, channel_snapshot_get_id);
 }
index d6a0557..697468e 100644 (file)
@@ -277,14 +277,14 @@ static struct ast_str *manager_build_parked_call_string(const struct ast_parked_
                return NULL;
        }
 
-       parkee_string = ast_manager_build_channel_state_string_suffix(payload->parkee, "Parkee");
+       parkee_string = ast_manager_build_channel_state_string_prefix(payload->parkee, "Parkee");
 
        if (payload->parker) {
-               parker_string = ast_manager_build_channel_state_string_suffix(payload->parker, "Parker");
+               parker_string = ast_manager_build_channel_state_string_prefix(payload->parker, "Parker");
        }
 
        if (payload->retriever) {
-               retriever_string = ast_manager_build_channel_state_string_suffix(payload->retriever, "Retriever");
+               retriever_string = ast_manager_build_channel_state_string_prefix(payload->retriever, "Retriever");
        }
 
        ast_str_set(&out, 0,
index 9afad4a..4b8c9d7 100644 (file)
@@ -83,11 +83,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
 #include "asterisk/pbx.h"
-#include "asterisk/manager.h"
 #include "asterisk/dsp.h"
 #include "asterisk/indications.h"
 #include "asterisk/ast_version.h"
 #include "asterisk/translate.h"
+#include "asterisk/stasis.h"
+#include "asterisk/stasis_channels.h"
 
 /*** DOCUMENTATION
        <application name="ReceiveFAX" language="en_US" module="res_fax">
@@ -384,12 +385,6 @@ AST_APP_OPTIONS(fax_exec_options, BEGIN_OPTIONS
        AST_APP_OPTION('z', OPT_REQUEST_T38),
 END_OPTIONS);
 
-struct manager_event_info {
-       char context[AST_MAX_CONTEXT];
-       char exten[AST_MAX_EXTENSION];
-       char cid[128];
-};
-
 static void debug_check_frame_for_silence(struct ast_fax_session *s, unsigned int c2s, struct ast_frame *frame)
 {
        struct debug_info_history *history = c2s ? &s->debug_info->c2s : &s->debug_info->s2c;
@@ -1091,13 +1086,39 @@ static struct ast_fax_session *fax_session_new(struct ast_fax_session_details *d
        return s;
 }
 
-static void get_manager_event_info(struct ast_channel *chan, struct manager_event_info *info)
+/*!
+ * \internal
+ * \brief Convert the filenames in a fax session into a JSON array
+ * \retval NULL on error
+ * \retval A \ref ast_json array on success
+ */
+static struct ast_json *generate_filenames_json(struct ast_fax_session_details *details)
 {
-       pbx_substitute_variables_helper(chan, "${CONTEXT}", info->context, sizeof(info->context));
-       pbx_substitute_variables_helper(chan, "${EXTEN}", info->exten, sizeof(info->exten));
-       pbx_substitute_variables_helper(chan, "${CALLERID(num)}", info->cid, sizeof(info->cid));
-}
+       RAII_VAR(struct ast_json *, json_array, ast_json_array_create(), ast_json_unref);
+       struct ast_fax_document *doc;
+
+       if (!details || !json_array) {
+               return NULL;
+       }
+
+       /* don't process empty lists */
+       if (AST_LIST_EMPTY(&details->documents)) {
+               return NULL;
+       }
+
+       AST_LIST_TRAVERSE(&details->documents, doc, next) {
+               struct ast_json *entry = ast_json_string_create(doc->filename);
+               if (!entry) {
+                       return NULL;
+               }
+               if (ast_json_array_append(json_array, entry)) {
+                       return NULL;
+               }
+       }
 
+       ast_json_ref(json_array);
+       return json_array;
+}
 
 /* \brief Generate a string of filenames using the given prefix and separator.
  * \param details the fax session details
@@ -1149,39 +1170,39 @@ static char *generate_filenames_string(struct ast_fax_session_details *details,
 /*! \brief send a FAX status manager event */
 static int report_fax_status(struct ast_channel *chan, struct ast_fax_session_details *details, const char *status)
 {
-       char *filenames = generate_filenames_string(details, "FileName: ", "\r\n");
+       RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
+       RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
+       RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
+       struct ast_json *json_filenames = NULL;
 
-       ast_channel_lock(chan);
-       if (details->option.statusevents) {
-               struct manager_event_info info;
-
-               get_manager_event_info(chan, &info);
-               manager_event(EVENT_FLAG_CALL,
-                             "FAXStatus",
-                             "Operation: %s\r\n"
-                             "Status: %s\r\n"
-                             "Channel: %s\r\n"
-                             "Context: %s\r\n"
-                             "Exten: %s\r\n"
-                             "CallerID: %s\r\n"
-                             "LocalStationID: %s\r\n"
-                             "%s%s",
-                             (details->caps & AST_FAX_TECH_GATEWAY) ? "gateway" : (details->caps & AST_FAX_TECH_RECEIVE) ? "receive" : "send",
-                             status,
-                             ast_channel_name(chan),
-                             info.context,
-                             info.exten,
-                             info.cid,
-                             details->localstationid,
-                             S_OR(filenames, ""),
-                             filenames ? "\r\n" : "");
+       if (!details->option.statusevents) {
+               return 0;
        }
-       ast_channel_unlock(chan);
 
-       if (filenames) {
-               ast_free(filenames);
+       json_filenames = generate_filenames_json(details);
+       if (!json_filenames) {
+               return -1;
+       }
+
+       json_object = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: o}",
+                       "type", "status",
+                       "operation", (details->caps & AST_FAX_TECH_GATEWAY) ? "gateway" : (details->caps & AST_FAX_TECH_RECEIVE) ? "receive" : "send",
+                       "status", status,
+                       "local_station_id", details->localstationid,
+                       "filenames", json_filenames);
+       if (!json_object) {
+               return -1;
        }
 
+       {
+               SCOPED_CHANNELLOCK(lock, chan);
+
+               message = ast_channel_cached_blob_create(chan, ast_channel_fax_type(), json_object);
+               if (!message) {
+                       return -1;
+               }
+               stasis_publish(ast_channel_topic(chan), message);
+       }
        return 0;
 }
 
@@ -1738,13 +1759,53 @@ static int receivefax_t38_init(struct ast_channel *chan, struct ast_fax_session_
        return 0;
 }
 
+/*! \brief Report on the final state of a receive fax operation
+ * \note This will lock the \ref ast_channel
+ */
+static int report_receive_fax_status(struct ast_channel *chan, const char *filename)
+{
+       RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
+       RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_json *, json_array, ast_json_array_create(), ast_json_unref);
+       struct ast_json *json_filename = ast_json_string_create(filename);
+
+       if (!json_array || !json_filename) {
+               return -1;
+       }
+       ast_json_array_append(json_array, json_filename);
+
+       {
+               SCOPED_CHANNELLOCK(lock, chan);
+
+               json_object = ast_json_pack("s: s, s: s, s: s, s: s, s: s, s: s, s: s, s: o",
+                               "type", "receive"
+                               "remote_station_id", S_OR(pbx_builtin_getvar_helper(chan, "REMOTESTATIONID"), ""),
+                               "local_station_id", S_OR(pbx_builtin_getvar_helper(chan, "LOCALSTATIONID"), ""),
+                               "fax_pages", S_OR(pbx_builtin_getvar_helper(chan, "FAXPAGES"), ""),
+                               "fax_resolution", S_OR(pbx_builtin_getvar_helper(chan, "FAXRESOLUTION"), ""),
+                               "fax_bitrate", S_OR(pbx_builtin_getvar_helper(chan, "FAXBITRATE"), ""),
+                               "filenames", json_array);
+               if (!json_object) {
+                       return -1;
+               }
+
+               message = ast_channel_cached_blob_create(chan, ast_channel_fax_type(), json_object);
+               if (!message) {
+                       return -1;
+               }
+
+               stasis_publish(ast_channel_topic(chan), message);
+       }
+       return 0;
+}
+
 /*! \brief initiate a receive FAX session */
 static int receivefax_exec(struct ast_channel *chan, const char *data)
 {
        char *parse, modems[128] = "";
        int channel_alive;
-       struct ast_fax_session_details *details;
-       struct ast_fax_session *s;
+       RAII_VAR(struct ast_fax_session_details *, details, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_fax_session *, s, NULL, ao2_cleanup);
        struct ast_fax_tech_token *token = NULL;
        struct ast_fax_document *doc;
        AST_DECLARE_APP_ARGS(args,
@@ -1752,7 +1813,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                AST_APP_ARG(options);
        );
        struct ast_flags opts = { 0, };
-       struct manager_event_info info;
        enum ast_t38_state t38state;
 
        /* initialize output channel variables */
@@ -1780,7 +1840,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, resultstr, "can't receive a fax on a channel with a T.38 gateway");
                set_channel_variables(chan, details);
                ast_log(LOG_ERROR, "executing ReceiveFAX on a channel with a T.38 Gateway is not supported\n");
-               ao2_ref(details, -1);
                return -1;
        }
 
@@ -1789,7 +1848,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, resultstr, "maxrate is less than minrate");
                set_channel_variables(chan, details);
                ast_log(LOG_ERROR, "maxrate %d is less than minrate %d\n", details->maxrate, details->minrate);
-               ao2_ref(details, -1);
                return -1;
        }
 
@@ -1799,7 +1857,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, error, "INVALID_ARGUMENTS");
                ast_string_field_set(details, resultstr, "incompatible 'modems' and 'minrate' settings");
                set_channel_variables(chan, details);
-               ao2_ref(details, -1);
                return -1;
        }
 
@@ -1809,7 +1866,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, error, "INVALID_ARGUMENTS");
                ast_string_field_set(details, resultstr, "incompatible 'modems' and 'maxrate' settings");
                set_channel_variables(chan, details);
-               ao2_ref(details, -1);
                return -1;
        }
 
@@ -1818,7 +1874,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, resultstr, "invalid arguments");
                set_channel_variables(chan, details);
                ast_log(LOG_WARNING, "%s requires an argument (filename[,options])\n", app_receivefax);
-               ao2_ref(details, -1);
                return -1;
        }
        parse = ast_strdupa(data);
@@ -1829,7 +1884,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, error, "INVALID_ARGUMENTS");
                ast_string_field_set(details, resultstr, "invalid arguments");
                set_channel_variables(chan, details);
-               ao2_ref(details, -1);
                return -1;
        }
        if (ast_strlen_zero(args.filename)) {
@@ -1837,7 +1891,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, resultstr, "invalid arguments");
                set_channel_variables(chan, details);
                ast_log(LOG_WARNING, "%s requires an argument (filename[,options])\n", app_receivefax);
-               ao2_ref(details, -1);
                return -1;
        }
 
@@ -1847,7 +1900,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, resultstr, "invalid arguments");
                set_channel_variables(chan, details);
                ast_log(LOG_WARNING, "%s does not support polling\n", app_receivefax);
-               ao2_ref(details, -1);
                return -1;
        }
 
@@ -1861,7 +1913,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, resultstr, "error allocating memory");
                set_channel_variables(chan, details);
                ast_log(LOG_ERROR, "System cannot provide memory for session requirements.\n");
-               ao2_ref(details, -1);
                return -1;
        }
 
@@ -1894,7 +1945,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, resultstr, "error reserving fax session");
                set_channel_variables(chan, details);
                ast_log(LOG_ERROR, "Unable to reserve FAX session.\n");
-               ao2_ref(details, -1);
                return -1;
        }
 
@@ -1905,8 +1955,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                        set_channel_variables(chan, details);
                        ast_log(LOG_WARNING, "Channel '%s' failed answer attempt.\n", ast_channel_name(chan));
                        fax_session_release(s, token);
-                       ao2_ref(s, -1);
-                       ao2_ref(details, -1);
                        return -1;
                }
        }
@@ -1917,8 +1965,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                        ast_string_field_set(details, resultstr, "error negotiating T.38");
                        set_channel_variables(chan, details);
                        fax_session_release(s, token);
-                       ao2_ref(s, -1);
-                       ao2_ref(details, -1);
                        return -1;
                }
        } else {
@@ -1931,8 +1977,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                        ast_string_field_set(details, resultstr, "error negotiating T.38");
                        set_channel_variables(chan, details);
                        fax_session_release(s, token);
-                       ao2_ref(s, -1);
-                       ao2_ref(details, -1);
                        ast_log(LOG_ERROR, "error initializing channel '%s' in T.38 mode\n", ast_channel_name(chan));
                        return -1;
                }
@@ -1948,36 +1992,9 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                }
        }
 
-       /* send out the AMI completion event */
-       ast_channel_lock(chan);
-
-       get_manager_event_info(chan, &info);
-       manager_event(EVENT_FLAG_CALL,
-                     "ReceiveFAX",
-                     "Channel: %s\r\n"
-                     "Context: %s\r\n"
-                     "Exten: %s\r\n"
-                     "CallerID: %s\r\n"
-                     "RemoteStationID: %s\r\n"
-                     "LocalStationID: %s\r\n"
-                     "PagesTransferred: %s\r\n"
-                     "Resolution: %s\r\n"
-                     "TransferRate: %s\r\n"
-                     "FileName: %s\r\n",
-                     ast_channel_name(chan),
-                     info.context,
-                     info.exten,
-                     info.cid,
-                     S_OR(pbx_builtin_getvar_helper(chan, "REMOTESTATIONID"), ""),
-                     S_OR(pbx_builtin_getvar_helper(chan, "LOCALSTATIONID"), ""),
-                     S_OR(pbx_builtin_getvar_helper(chan, "FAXPAGES"), ""),
-                     S_OR(pbx_builtin_getvar_helper(chan, "FAXRESOLUTION"), ""),
-                     S_OR(pbx_builtin_getvar_helper(chan, "FAXBITRATE"), ""),
-                     args.filename);
-       ast_channel_unlock(chan);
-
-       ao2_ref(s, -1);
-       ao2_ref(details, -1);
+       if (report_receive_fax_status(chan, args.filename)) {
+               ast_log(AST_LOG_ERROR, "Error publishing ReceiveFax status message\n");
+       }
 
        /* If the channel hungup return -1; otherwise, return 0 to continue in the dialplan */
        return (!channel_alive) ? -1 : 0;
@@ -2223,14 +2240,53 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det
        return 0;
 }
 
+/*!
+ * \brief Report on the status of a completed fax send attempt
+ * \note This will lock the \ref ast_channel
+ */
+static int report_send_fax_status(struct ast_channel *chan, struct ast_fax_session_details *details)
+{
+       RAII_VAR(struct ast_json *, json_obj, NULL, ao2_cleanup);
+       RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
+       struct ast_json *json_filenames;
+
+       json_filenames = generate_filenames_json(details);
+       if (!json_filenames) {
+               return -1;
+       }
+
+       {
+               SCOPED_CHANNELLOCK(lock, chan);
+               json_obj = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: s, s: s, s: o}",
+                               "type", "send"
+                               "remote_station_id", S_OR(pbx_builtin_getvar_helper(chan, "REMOTESTATIONID"), ""),
+                               "local_station_id", S_OR(pbx_builtin_getvar_helper(chan, "LOCALSTATIONID"), ""),
+                               "fax_pages", S_OR(pbx_builtin_getvar_helper(chan, "FAXPAGES"), ""),
+                               "fax_resolution", S_OR(pbx_builtin_getvar_helper(chan, "FAXRESOLUTION"), ""),
+                               "fax_bitrate", S_OR(pbx_builtin_getvar_helper(chan, "FAXBITRATE"), ""),
+                               "filenames", json_filenames);
+               if (!json_obj) {
+                       return -1;
+               }
+
+               message = ast_channel_cached_blob_create(chan, ast_channel_fax_type(), json_obj);
+               if (!message) {
+                       return -1;
+               }
+               stasis_publish(ast_channel_topic(chan), message);
+       }
+       return 0;
+}
+
+
 
 /*! \brief initiate a send FAX session */
 static int sendfax_exec(struct ast_channel *chan, const char *data)
 {
        char *parse, *filenames, *c, modems[128] = "";
        int channel_alive, file_count;
-       struct ast_fax_session_details *details;
-       struct ast_fax_session *s;
+       RAII_VAR(struct ast_fax_session_details *, details, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_fax_session *, s, NULL, ao2_cleanup);
        struct ast_fax_tech_token *token = NULL;
        struct ast_fax_document *doc;
        AST_DECLARE_APP_ARGS(args,
@@ -2238,7 +2294,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                AST_APP_ARG(options);
        );
        struct ast_flags opts = { 0, };
-       struct manager_event_info info;
        enum ast_t38_state t38state;
 
        /* initialize output channel variables */
@@ -2266,7 +2321,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, resultstr, "can't send a fax on a channel with a T.38 gateway");
                set_channel_variables(chan, details);
                ast_log(LOG_ERROR, "executing SendFAX on a channel with a T.38 Gateway is not supported\n");
-               ao2_ref(details, -1);
                return -1;
        }
 
@@ -2275,7 +2329,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, resultstr, "maxrate is less than minrate");
                set_channel_variables(chan, details);
                ast_log(LOG_ERROR, "maxrate %d is less than minrate %d\n", details->maxrate, details->minrate);
-               ao2_ref(details, -1);
                return -1;
        }
 
@@ -2285,7 +2338,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, error, "INVALID_ARGUMENTS");
                ast_string_field_set(details, resultstr, "incompatible 'modems' and 'minrate' settings");
                set_channel_variables(chan, details);
-               ao2_ref(details, -1);
                return -1;
        }
 
@@ -2295,7 +2347,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, error, "INVALID_ARGUMENTS");
                ast_string_field_set(details, resultstr, "incompatible 'modems' and 'maxrate' settings");
                set_channel_variables(chan, details);
-               ao2_ref(details, -1);
                return -1;
        }
 
@@ -2304,7 +2355,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, resultstr, "invalid arguments");
                set_channel_variables(chan, details);
                ast_log(LOG_WARNING, "%s requires an argument (filename[&filename[&filename]][,options])\n", app_sendfax);
-               ao2_ref(details, -1);
                return -1;
        }
        parse = ast_strdupa(data);
@@ -2316,7 +2366,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, error, "INVALID_ARGUMENTS");
                ast_string_field_set(details, resultstr, "invalid arguments");
                set_channel_variables(chan, details);
-               ao2_ref(details, -1);
                return -1;
        }
        if (ast_strlen_zero(args.filenames)) {
@@ -2324,7 +2373,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, resultstr, "invalid arguments");
                set_channel_variables(chan, details);
                ast_log(LOG_WARNING, "%s requires an argument (filename[&filename[&filename]],options])\n", app_sendfax);
-               ao2_ref(details, -1);
                return -1;
        }
 
@@ -2334,7 +2382,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, resultstr, "invalid arguments");
                set_channel_variables(chan, details);
                ast_log(LOG_WARNING, "%s does not support polling\n", app_sendfax);
-               ao2_ref(details, -1);
                return -1;
        }
 
@@ -2348,7 +2395,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                        ast_string_field_set(details, resultstr, "error reading file");
                        set_channel_variables(chan, details);
                        ast_log(LOG_ERROR, "access failure.  Verify '%s' exists and check permissions.\n", args.filenames);
-                       ao2_ref(details, -1);
                        return -1;
                }
 
@@ -2357,7 +2403,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                        ast_string_field_set(details, resultstr, "error allocating memory");
                        set_channel_variables(chan, details);
                        ast_log(LOG_ERROR, "System cannot provide memory for session requirements.\n");
-                       ao2_ref(details, -1);
                        return -1;
                }
 
@@ -2402,7 +2447,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, resultstr, "error reserving fax session");
                set_channel_variables(chan, details);
                ast_log(LOG_ERROR, "Unable to reserve FAX session.\n");
-               ao2_ref(details, -1);
                return -1;
        }
 
@@ -2413,8 +2457,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                        set_channel_variables(chan, details);
                        ast_log(LOG_WARNING, "Channel '%s' failed answer attempt.\n", ast_channel_name(chan));
                        fax_session_release(s, token);
-                       ao2_ref(s, -1);
-                       ao2_ref(details, -1);
                        return -1;
                }
        }
@@ -2425,8 +2467,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                        ast_string_field_set(details, resultstr, "error negotiating T.38");
                        set_channel_variables(chan, details);
                        fax_session_release(s, token);
-                       ao2_ref(s, -1);
-                       ao2_ref(details, -1);
                        return -1;
                }
        } else {
@@ -2439,8 +2479,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                        ast_string_field_set(details, resultstr, "error negotiating T.38");
                        set_channel_variables(chan, details);
                        fax_session_release(s, token);
-                       ao2_ref(s, -1);
-                       ao2_ref(details, -1);
                        ast_log(LOG_ERROR, "error initializing channel '%s' in T.38 mode\n", ast_channel_name(chan));
                        return -1;
                }
@@ -2460,42 +2498,13 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
 
        if (!(filenames = generate_filenames_string(details, "FileName: ", "\r\n"))) {
                ast_log(LOG_ERROR, "Error generating SendFAX manager event\n");
-               ao2_ref(s, -1);
-               ao2_ref(details, -1);
                return (!channel_alive) ? -1 : 0;
        }
 
        /* send out the AMI completion event */
-       ast_channel_lock(chan);
-       get_manager_event_info(chan, &info);
-       manager_event(EVENT_FLAG_CALL,
-                     "SendFAX",
-                     "Channel: %s\r\n"
-                     "Context: %s\r\n"
-                     "Exten: %s\r\n"
-                     "CallerID: %s\r\n"
-                     "RemoteStationID: %s\r\n"
-                     "LocalStationID: %s\r\n"
-                     "PagesTransferred: %s\r\n"
-                     "Resolution: %s\r\n"
-                     "TransferRate: %s\r\n"
-                     "%s\r\n",
-                     ast_channel_name(chan),
-                     info.context,
-                     info.exten,
-                     info.cid,
-                     S_OR(pbx_builtin_getvar_helper(chan, "REMOTESTATIONID"), ""),
-                     S_OR(pbx_builtin_getvar_helper(chan, "LOCALSTATIONID"), ""),
-                     S_OR(pbx_builtin_getvar_helper(chan, "FAXPAGES"), ""),
-                     S_OR(pbx_builtin_getvar_helper(chan, "FAXRESOLUTION"), ""),
-                     S_OR(pbx_builtin_getvar_helper(chan, "FAXBITRATE"), ""),
-                     filenames);
-       ast_channel_unlock(chan);
-
-       ast_free(filenames);
-
-       ao2_ref(s, -1);
-       ao2_ref(details, -1);
+       if (report_send_fax_status(chan, details)) {
+               ast_log(AST_LOG_ERROR, "Error publishing SendFAX status message\n");
+       }
 
        /* If the channel hungup return -1; otherwise, return 0 to continue in the dialplan */
        return (!channel_alive) ? -1 : 0;
index 2070c80..7ca0bf8 100644 (file)
@@ -1536,10 +1536,6 @@ static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incom
 {
        struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
 
-       if (!ast_strlen_zero(xmpp)) {
-               manager_event(EVENT_FLAG_USER, "JabberEvent", "Account: %s\r\nPacket: %s\r\n", client->name, xmpp);
-       }
-
        if (client->debug) {
                if (is_incoming) {
                        ast_verbose("\nJABBER: %s INCOMING: %s\n", client->name, xmpp);
@@ -3247,9 +3243,9 @@ static void aji_mwi_cb(void *data, struct stasis_subscription *sub, struct stasi
        char oldmsgs[10];
        char newmsgs[10];
        struct aji_client *client = data;
-       struct stasis_mwi_state *mwi_state;
+       struct ast_mwi_state *mwi_state;
 
-       if (!stasis_subscription_is_subscribed(sub) || stasis_mwi_state_type() != stasis_message_type(msg)) {
+       if (!stasis_subscription_is_subscribed(sub) || ast_mwi_state_type() != stasis_message_type(msg)) {
                return;
        }
 
@@ -3308,7 +3304,7 @@ static int cached_devstate_cb(void *obj, void *arg, int flags)
 static void aji_init_event_distribution(struct aji_client *client)
 {
        if (!mwi_sub) {
-               mwi_sub = stasis_subscribe(stasis_mwi_topic_all(), aji_mwi_cb, client);
+               mwi_sub = stasis_subscribe(ast_mwi_topic_all(), aji_mwi_cb, client);
        }
        if (!device_state_sub) {
                RAII_VAR(struct ao2_container *, cached, NULL, ao2_cleanup);
@@ -3369,7 +3365,7 @@ static int aji_handle_pubsub_event(void *data, ikspak *pak)
                sscanf(iks_find_cdata(item_content, "OLDMSGS"), "%10d", &oldmsgs);
                sscanf(iks_find_cdata(item_content, "NEWMSGS"), "%10d", &newmsgs);
 
-               stasis_publish_mwi_state_full(item_id, context, newmsgs, oldmsgs, &pubsub_eid);
+               ast_publish_mwi_state_full(item_id, context, newmsgs, oldmsgs, NULL, &pubsub_eid);
 
                return IKS_FILTER_EAT;
        } else {
index 9aca24a..72911b5 100644 (file)
@@ -40,8 +40,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/file.h"
 #include "asterisk/pbx.h"
 #include "asterisk/module.h"
-#include "asterisk/manager.h"
 #include "asterisk/cli.h"
+#include "asterisk/manager.h"
+#include "asterisk/stasis.h"
+#include "asterisk/stasis_channels.h"
 #define AST_API_MODULE
 #include "asterisk/monitor.h"
 #include "asterisk/app.h"
@@ -291,6 +293,7 @@ int AST_OPTIONAL_API_NAME(ast_monitor_start)(struct ast_channel *chan, const cha
                                             const char *fname_base, int need_lock, int stream_action)
 {
        int res = 0;
+       RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
 
        LOCK_IF_NEEDED(chan, need_lock);
 
@@ -393,11 +396,12 @@ int AST_OPTIONAL_API_NAME(ast_monitor_start)(struct ast_channel *chan, const cha
                /* so we know this call has been monitored in case we need to bill for it or something */
                pbx_builtin_setvar_helper(chan, "__MONITORED","true");
 
-               ast_manager_event(chan, EVENT_FLAG_CALL, "MonitorStart",
-                                       "Channel: %s\r\n"
-                                               "Uniqueid: %s\r\n",
-                               ast_channel_name(chan),
-                                       ast_channel_uniqueid(chan));
+               message = ast_channel_cached_blob_create(chan,
+                               ast_channel_monitor_start_type(),
+                               NULL);
+               if (message) {
+                       stasis_publish(ast_channel_topic(chan), message);
+               }
        } else {
                ast_debug(1,"Cannot start monitoring %s, already monitored\n", ast_channel_name(chan));
                res = -1;
@@ -437,6 +441,7 @@ static const char *get_soxmix_format(const char *format)
 int AST_OPTIONAL_API_NAME(ast_monitor_stop)(struct ast_channel *chan, int need_lock)
 {
        int delfiles = 0;
+       RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
 
        LOCK_IF_NEEDED(chan, need_lock);
 
@@ -511,12 +516,12 @@ int AST_OPTIONAL_API_NAME(ast_monitor_stop)(struct ast_channel *chan, int need_l
                ast_free(ast_channel_monitor(chan));
                ast_channel_monitor_set(chan, NULL);
 
-               ast_manager_event(chan, EVENT_FLAG_CALL, "MonitorStop",
-                                       "Channel: %s\r\n"
-                               "Uniqueid: %s\r\n",
-                               ast_channel_name(chan),
-                               ast_channel_uniqueid(chan)
-                               );
+               message = ast_channel_cached_blob_create(chan,
+                               ast_channel_monitor_stop_type(),
+                               NULL);
+               if (message) {
+                       stasis_publish(ast_channel_topic(chan), message);
+               }
                pbx_builtin_setvar_helper(chan, "MONITORED", NULL);
        }
        pbx_builtin_setvar_helper(chan, "AUTO_MONITOR", NULL);
index 1306184..2ed7ea5 100644 (file)
@@ -67,7 +67,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/cli.h"
 #include "asterisk/stringfields.h"
 #include "asterisk/linkedlists.h"
-#include "asterisk/manager.h"
+#include "asterisk/stasis.h"
+#include "asterisk/stasis_channels.h"
 #include "asterisk/paths.h"
 #include "asterisk/astobj2.h"
 #include "asterisk/timing.h"
@@ -1373,6 +1374,8 @@ static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, con
        struct mohclass *mohclass = NULL;
        struct moh_files_state *state = ast_channel_music_state(chan);
        struct ast_variable *var = NULL;
+       RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
        int res;
        int realtime_possible = ast_check_realtime("musiconhold");
 
@@ -1567,14 +1570,6 @@ static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, con
                }
        }
 
-       ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
-               "State: Start\r\n"
-               "Channel: %s\r\n"
-               "UniqueID: %s\r\n"
-               "Class: %s\r\n",
-               ast_channel_name(chan), ast_channel_uniqueid(chan),
-               mohclass->name);
-
        ast_set_flag(ast_channel_flags(chan), AST_FLAG_MOH);
 
        if (mohclass->total_files) {
@@ -1583,6 +1578,20 @@ static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, con
                res = ast_activate_generator(chan, &mohgen, mohclass);
        }
 
+       json_object = ast_json_pack("{s: s}",
+                       "class", mohclass->name);
+       if (!json_object) {
+               mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
+               return -1;
+       }
+
+       message = ast_channel_cached_blob_create(chan,
+                       ast_channel_moh_start_type(),
+                       json_object);
+       if (message) {
+               stasis_publish(ast_channel_topic(chan), message);
+       }
+
        mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
 
        return res;
@@ -1590,6 +1599,7 @@ static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, con
 
 static void local_ast_moh_stop(struct ast_channel *chan)
 {
+       RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
        ast_clear_flag(ast_channel_flags(chan), AST_FLAG_MOH);
        ast_deactivate_generator(chan);
 
@@ -1601,11 +1611,10 @@ static void local_ast_moh_stop(struct ast_channel *chan)
                }
        }
 
-       ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
-               "State: Stop\r\n"
-               "Channel: %s\r\n"
-               "UniqueID: %s\r\n",
-               ast_channel_name(chan), ast_channel_uniqueid(chan));
+       message = ast_channel_cached_blob_create(chan, ast_channel_moh_stop_type(), NULL);
+       if (message) {
+               stasis_publish(ast_channel_topic(chan), message);
+       }
        ast_channel_unlock(chan);
 }
 
index 588662f..f18e564 100644 (file)
@@ -133,7 +133,7 @@ static struct mwi_stasis_subscription *mwi_stasis_subscription_alloc(const char
                return NULL;
        }
 
-       topic = stasis_mwi_topic(mailbox);
+       topic = ast_mwi_topic(mailbox);
 
        /* Safe strcpy */
        strcpy(mwi_stasis_sub->mailbox, mailbox);
@@ -237,9 +237,9 @@ static int get_message_count(void *obj, void *arg, int flags)
        RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
        struct mwi_stasis_subscription *mwi_stasis = obj;
        struct message_accumulator *counter = arg;
-       struct stasis_mwi_state *mwi_state;
+       struct ast_mwi_state *mwi_state;
 
-       msg = stasis_cache_get(stasis_mwi_topic_cached(), stasis_mwi_state_type(), mwi_stasis->mailbox);
+       msg = stasis_cache_get(ast_mwi_topic_cached(), ast_mwi_state_type(), mwi_stasis->mailbox);
        if (!msg) {
                return 0;
        }
@@ -604,7 +604,7 @@ static void mwi_stasis_cb(void *userdata, struct stasis_subscription *sub,
                return;
        }
 
-       if (stasis_mwi_state_type() == stasis_message_type(msg)) {
+       if (ast_mwi_state_type() == stasis_message_type(msg)) {
                struct ast_taskprocessor *serializer = mwi_sub->is_solicited ? ast_sip_subscription_get_serializer(mwi_sub->sip_sub) : NULL;
                ao2_ref(mwi_sub, +1);
                ast_sip_push_task(serializer, serialized_notify, mwi_sub);
index dea8372..89eb45d 100644 (file)
@@ -1324,9 +1324,9 @@ static void xmpp_pubsub_mwi_cb(void *data, struct stasis_subscription *sub, stru
        struct ast_xmpp_client *client = data;
        const char *mailbox, *context;
        char oldmsgs[10], newmsgs[10];
-       struct stasis_mwi_state *mwi_state;
+       struct ast_mwi_state *mwi_state;
 
-       if (!stasis_subscription_is_subscribed(sub) || stasis_mwi_state_type() != stasis_message_type(msg)) {
+       if (!stasis_subscription_is_subscribed(sub) || ast_mwi_state_type() != stasis_message_type(msg)) {
                return;
        }
 
@@ -1484,7 +1484,7 @@ static int xmpp_pubsub_handle_event(void *data, ikspak *pak)
                sscanf(iks_find_cdata(item_content, "OLDMSGS"), "%10d", &oldmsgs);
                sscanf(iks_find_cdata(item_content, "NEWMSGS"), "%10d", &newmsgs);
 
-               stasis_publish_mwi_state_full(item_id, context, newmsgs, oldmsgs, &pubsub_eid);
+               ast_publish_mwi_state_full(item_id, context, newmsgs, oldmsgs, NULL, &pubsub_eid);
 
                return IKS_FILTER_EAT;
        } else {
@@ -1596,7 +1596,7 @@ static void xmpp_init_event_distribution(struct ast_xmpp_client *client)
        xmpp_pubsub_unsubscribe(client, "device_state");
        xmpp_pubsub_unsubscribe(client, "message_waiting");
 
-       if (!(client->mwi_sub = stasis_subscribe(stasis_mwi_topic_all(), xmpp_pubsub_mwi_cb, client))) {
+       if (!(client->mwi_sub = stasis_subscribe(ast_mwi_topic_all(), xmpp_pubsub_mwi_cb, client))) {
                return;
        }
 
@@ -2546,10 +2546,6 @@ static void xmpp_log_hook(void *data, const char *xmpp, size_t size, int incomin
        RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
        struct ast_xmpp_client *client = data;
 
-       if (!ast_strlen_zero(xmpp)) {
-               manager_event(EVENT_FLAG_USER, "JabberEvent", "Account: %s\r\nPacket: %s\r\n", client->name, xmpp);
-       }
-
        if (!debug && (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) || !ast_test_flag(&clientcfg->flags, XMPP_DEBUG))) {
                return;
        }