--- /dev/null
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is
+ * Anthony Minessale II <anthmct@yahoo.com>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Anthony Minessale II <anthmct@yahoo.com>
+ *
+ *
+ * mod_xmpp_event.c -- XMPP Event Logger
+ *
+ */
+#include <switch.h>
+#include <iksemel.h>
+
+static const char modname[] = "mod_xmpp_event";
+
+static int RUNNING = 0;
+static iksfilter *my_filter;
+static int opt_timeout = 30;
+static int opt_use_tls = 0;
+
+/* stuff we keep per session */
+struct session {
+ iksparser *parser;
+ iksid *acc;
+ char *pass;
+ int features;
+ int authorized;
+ int counter;
+ int job_done;
+};
+
+static struct {
+ char *jid;
+ char *passwd;
+ char *target_jid;
+ int debug;
+ struct session session;
+} globals;
+
+static void event_handler (switch_event *event)
+{
+ char buf[1024];
+ iks *msg;
+
+ if (!globals.session.authorized) {
+ return;
+ }
+
+ switch(event->event_id) {
+ default:
+ switch_event_serialize(event, buf, sizeof(buf), NULL);
+ //switch_console_printf(SWITCH_CHANNEL_CONSOLE, "\nEVENT\n--------------------------------\n%s\n", buf);
+ msg = iks_make_msg(IKS_TYPE_NONE, globals.target_jid, buf);
+ iks_send(globals.session.parser, msg);
+ break;
+ }
+}
+
+
+
+SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_jid, globals.jid)
+ SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_target_jid, globals.target_jid)
+ SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_passwd, globals.passwd)
+
+
+ static switch_status load_config(void)
+{
+ switch_config cfg;
+ switch_status status = SWITCH_STATUS_FALSE;
+ char *var, *val;
+ char *cf = "xmpp_event.conf";
+ int count = 0;
+
+ memset(&globals, 0, sizeof(globals));
+
+ if (!switch_config_open_file(&cfg, cf)) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "open of %s failed\n", cf);
+ return SWITCH_STATUS_TERM;
+ }
+
+ while (switch_config_next_pair(&cfg, &var, &val)) {
+ if (!strcasecmp(cfg.category, "settings")) {
+ if (!strcmp(var, "jid")) {
+ set_global_jid(val);
+ count++;
+ } else if (!strcmp(var, "target_jid")) {
+ set_global_target_jid(val);
+ count++;
+ } else if (!strcmp(var, "passwd")) {
+ set_global_passwd(val);
+ count++;
+ } else if (!strcmp(var, "debug")) {
+ globals.debug = atoi(val);
+ }
+ }
+ }
+
+ switch_config_close_file(&cfg);
+
+ /* TBD use config to pick what events to bind to */
+ if (switch_event_bind((char *)modname, SWITCH_EVENT_ALL, SWITCH_EVENT_SUBCLASS_ANY, event_handler, NULL) != SWITCH_STATUS_SUCCESS) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Couldn't bind!\n");
+ return SWITCH_STATUS_GENERR;
+ }
+
+ if (count == 3) {
+ status = SWITCH_STATUS_SUCCESS;
+ }
+
+ return status;
+
+}
+
+int
+on_result (struct session *sess, ikspak *pak)
+{
+
+ return IKS_FILTER_EAT;
+}
+
+int
+on_stream (struct session *sess, int type, iks *node)
+{
+ sess->counter = opt_timeout;
+ //iks *x;
+
+ switch (type) {
+ case IKS_NODE_START:
+ if (opt_use_tls && !iks_is_secure (sess->parser)) {
+ iks_start_tls (sess->parser);
+ }
+ break;
+ case IKS_NODE_NORMAL:
+ if (strcmp ("stream:features", iks_name (node)) == 0) {
+ sess->features = iks_stream_features (node);
+ if (opt_use_tls && !iks_is_secure (sess->parser)) break;
+ if (sess->authorized) {
+ iks *t;
+ if (sess->features & IKS_STREAM_BIND) {
+ t = iks_make_resource_bind (sess->acc);
+ iks_send (sess->parser, t);
+ iks_delete (t);
+ }
+ if (sess->features & IKS_STREAM_SESSION) {
+ t = iks_make_session ();
+ iks_insert_attrib (t, "id", "auth");
+ iks_send (sess->parser, t);
+ iks_delete (t);
+ }
+ } else {
+ if (sess->features & IKS_STREAM_SASL_MD5)
+ iks_start_sasl (sess->parser, IKS_SASL_DIGEST_MD5, sess->acc->user, sess->pass);
+ else if (sess->features & IKS_STREAM_SASL_PLAIN)
+ iks_start_sasl (sess->parser, IKS_SASL_PLAIN, sess->acc->user, sess->pass);
+ }
+ } else if (strcmp ("failure", iks_name (node)) == 0) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "sasl authentication failed\n");
+ } else if (strcmp ("success", iks_name (node)) == 0) {
+ sess->authorized = 1;
+ iks_send_header (sess->parser, sess->acc->server);
+ } else {
+ ikspak *pak;
+
+ pak = iks_packet (node);
+ iks_filter_packet (my_filter, pak);
+ if (sess->job_done == 1) return IKS_HOOK;
+ }
+ break;
+#if 0
+ case IKS_NODE_STOP:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "server disconnected\n");
+ break;
+
+ case IKS_NODE_ERROR:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "stream error\n");
+ break;
+#endif
+
+ }
+
+ if (node) iks_delete (node);
+ return IKS_OK;
+}
+
+int
+on_error (void *user_data, ikspak *pak)
+{
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "authorization failed\n");
+ return IKS_FILTER_EAT;
+}
+
+void
+on_log (struct session *sess, const char *data, size_t size, int is_incoming)
+{
+ if (iks_is_secure (sess->parser)) fprintf (stderr, "Sec");
+ if (is_incoming) fprintf (stderr, "RECV"); else fprintf (stderr, "SEND");
+ fprintf (stderr, "[%s]\n", data);
+}
+
+void
+j_setup_filter (struct session *sess)
+{
+ if (my_filter) iks_filter_delete (my_filter);
+ my_filter = iks_filter_new ();
+ iks_filter_add_rule (my_filter, (iksFilterHook *) on_result, sess,
+ IKS_RULE_TYPE, IKS_PAK_IQ,
+ IKS_RULE_SUBTYPE, IKS_TYPE_RESULT,
+ IKS_RULE_ID, "auth",
+ IKS_RULE_DONE);
+ iks_filter_add_rule (my_filter, on_error, sess,
+ IKS_RULE_TYPE, IKS_PAK_IQ,
+ IKS_RULE_SUBTYPE, IKS_TYPE_ERROR,
+ IKS_RULE_ID, "auth",
+ IKS_RULE_DONE);
+}
+
+static void j_connect (char *jabber_id, char *pass)
+{
+
+ int e;
+
+ memset (&globals.session, 0, sizeof (globals.session));
+ globals.session.parser = iks_stream_new (IKS_NS_CLIENT, &globals.session, (iksStreamHook *) on_stream);
+ if (globals.debug) iks_set_log_hook (globals.session.parser, (iksLogHook *) on_log);
+ globals.session.acc = iks_id_new (iks_parser_stack (globals.session.parser), jabber_id);
+ if (NULL == globals.session.acc->resource) {
+ /* user gave no resource name, use the default */
+ char *tmp;
+ tmp = iks_malloc (strlen (globals.session.acc->user) + strlen (globals.session.acc->server) + 9 + 3);
+ sprintf (tmp, "%s@%s/%s", globals.session.acc->user, globals.session.acc->server, modname);
+ globals.session.acc = iks_id_new (iks_parser_stack (globals.session.parser), tmp);
+ iks_free (tmp);
+ }
+ globals.session.pass = pass;
+
+ j_setup_filter (&globals.session);
+
+ e = iks_connect_tcp (globals.session.parser, globals.session.acc->server, IKS_JABBER_PORT);
+ switch (e) {
+ case IKS_OK:
+ break;
+ case IKS_NET_NODNS:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "hostname lookup failed\n");
+ case IKS_NET_NOCONN:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "connection failed\n");
+ default:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "io error\n");
+ }
+
+ globals.session.counter = opt_timeout;
+ while (RUNNING == 1) {
+ e = iks_recv (globals.session.parser, 1);
+ if(globals.session.job_done) break;
+ if (IKS_HOOK == e) break;
+ if (!globals.session.authorized) {
+ if (IKS_NET_TLSFAIL == e) switch_console_printf(SWITCH_CHANNEL_CONSOLE, "tls handshake failed\n");
+ if (IKS_OK != e) switch_console_printf(SWITCH_CHANNEL_CONSOLE, "io error\n");
+ globals.session.counter--;
+ if (globals.session.counter == 0) switch_console_printf(SWITCH_CHANNEL_CONSOLE, "network timeout\n");
+ }
+ }
+ iks_disconnect(globals.session.parser);
+ iks_parser_delete (globals.session.parser);
+ globals.session.authorized = 0;
+ RUNNING = 0;
+
+}
+
+static switch_loadable_module_interface xmpp_event_module_interface = {
+ /*.module_name*/ modname,
+ /*.endpoint_interface*/ NULL,
+ /*.timer_interface*/ NULL,
+ /*.dialplan_interface*/ NULL,
+ /*.codec_interface*/ NULL,
+ /*.application_interface*/ NULL
+};
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_load(switch_loadable_module_interface **interface, char *filename) {
+ /* connect my internal structure to the blank pointer passed to me */
+ *interface = &xmpp_event_module_interface;
+
+ if (load_config() != SWITCH_STATUS_SUCCESS) {
+ return SWITCH_STATUS_FALSE;
+ }
+
+
+
+ /* indicate that the module should continue to be loaded */
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_shutdown(void)
+{
+ RUNNING = -1;
+
+ while (RUNNING) {
+ switch_yield(1000);
+ }
+ return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_runtime(void)
+{
+ RUNNING = 1;
+ j_connect(globals.jid, globals.passwd);
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "disconnecting client\n");
+ return SWITCH_STATUS_TERM;
+}