diff --git a/apps/dsm/mods/mod_groups/CMakeLists.txt b/apps/dsm/mods/mod_groups/CMakeLists.txt new file mode 100644 index 00000000..58e57db0 --- /dev/null +++ b/apps/dsm/mods/mod_groups/CMakeLists.txt @@ -0,0 +1,7 @@ +set (mod_groups_SRCS +ModGroups.cpp +) + +SET(sems_dsm_module_name mod_groups) +INCLUDE(${CMAKE_SOURCE_DIR}/cmake/dsm.lib.rules.txt) + diff --git a/apps/dsm/mods/mod_groups/Makefile b/apps/dsm/mods/mod_groups/Makefile new file mode 100644 index 00000000..54b2dd06 --- /dev/null +++ b/apps/dsm/mods/mod_groups/Makefile @@ -0,0 +1,10 @@ +plug_in_name = mod_groups + +DSMPATH ?= ../.. + +module_ldflags = +module_cflags = -DMOD_NAME=\"$(plug_in_name)\" -I$(DSMPATH) + +COREPATH ?=$(DSMPATH)/../../core +lib_full_name = $(DSMPATH)/mods/lib/$(lib_name) +include $(DSMPATH)/mods/Makefile.dsm_module diff --git a/apps/dsm/mods/mod_groups/ModGroups.cpp b/apps/dsm/mods/mod_groups/ModGroups.cpp new file mode 100644 index 00000000..15a19f14 --- /dev/null +++ b/apps/dsm/mods/mod_groups/ModGroups.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2010 Stefan Sayer + * + * Development of this module was sponsored by TelTech Systems Inc + * + * This file is part of SEMS, a free SIP media server. + * + * SEMS is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. This program is released under + * the GPL with the additional exemption that compiling, linking, + * and/or using OpenSSL is allowed. + * + * For a license to use the SEMS software under conditions + * other than those described here, or to purchase support for this + * software, please contact iptel.org by e-mail at the following addresses: + * info@iptel.org + * + * SEMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ModGroups.h" +#include "log.h" +#include "AmUtils.h" + +#include "DSMSession.h" +#include "AmSession.h" +#include "AmSessionContainer.h" + +SC_EXPORT(MOD_CLS_NAME); + +AmMutex GroupsModule::groups_mut; +GroupMap GroupsModule::groups; +GroupMap GroupsModule::groups_rev; + + +MOD_ACTIONEXPORT_BEGIN(MOD_CLS_NAME) { + + DEF_CMD("groups.join", GroupsJoinAction); + DEF_CMD("groups.leave", GroupsLeaveAction); + DEF_CMD("groups.leaveAll", GroupsLeaveAllAction); + // DEF_CMD("groups.get", GroupsGetAction); + // DEF_CMD("groups.getMembers", GroupsGetMembersAction); + DEF_CMD("groups.postEvent", GroupsPostEventAction); + +} MOD_ACTIONEXPORT_END; + +MOD_CONDITIONEXPORT_NONE(MOD_CLS_NAME); + +void GroupsModule::onBeforeDestroy(DSMSession* sc_sess, AmSession* sess) { + leave_all_groups(sess->getLocalTag()); +} + +void GroupsModule::leave_all_groups(const string& ltag) { + GroupsModule::groups_mut.lock(); + + GroupMap::iterator it = GroupsModule::groups_rev.find(ltag); + if (it != GroupsModule::groups_rev.end()) { + set& active_groups = it->second; + for (set::iterator ag_it = + active_groups.begin(); ag_it != active_groups.end(); ag_it++) { + GroupsModule::groups[*ag_it].erase(ltag); + if (GroupsModule::groups[*ag_it].empty()) { + DBG("clearing empty group '%s'\n", ag_it->c_str()); + GroupsModule::groups.erase(*ag_it); + } + } + GroupsModule::groups_rev.erase(it); + } + + GroupsModule::groups_mut.unlock(); +} + +EXEC_ACTION_START(GroupsJoinAction) { + string groupname = resolveVars(arg, sess, sc_sess, event_params); + DBG("call '%s' joining group '%s'\n", + sess->getLocalTag().c_str(), groupname.c_str()); + + GroupsModule::groups_mut.lock(); + GroupsModule::groups[groupname].insert(sess->getLocalTag()); + GroupsModule::groups_rev[sess->getLocalTag()].insert(groupname); + GroupsModule::groups_mut.unlock(); +} EXEC_ACTION_END; + +EXEC_ACTION_START(GroupsLeaveAction) { + string groupname = resolveVars(arg, sess, sc_sess, event_params); + string ltag = sess->getLocalTag(); + DBG("call '%s' leaving group '%s'\n", + ltag.c_str(), groupname.c_str()); + + GroupsModule::groups_mut.lock(); + + GroupMap::iterator it = GroupsModule::groups.find(groupname); + if (it != GroupsModule::groups.end()) { + it->second.erase(ltag); + if (it->second.empty()) { + DBG("clearing empty group '%s'\n", groupname.c_str()); + GroupsModule::groups.erase(it); + } + } + + it = GroupsModule::groups_rev.find(ltag); + if (it != GroupsModule::groups_rev.end()) { + it->second.erase(groupname); + if (it->second.empty()) { + DBG("call '%s' in no group any more\n", ltag.c_str()); + GroupsModule::groups_rev.erase(it); + } + } + GroupsModule::groups_mut.unlock(); +} EXEC_ACTION_END; + +EXEC_ACTION_START(GroupsLeaveAllAction) { + string ltag = sess->getLocalTag(); + DBG("call '%s' leaving all groups\n", ltag.c_str()); + + GroupsModule::leave_all_groups(ltag); +} EXEC_ACTION_END; + +CONST_ACTION_2P(GroupsPostEventAction, ',', true); +EXEC_ACTION_START(GroupsPostEventAction) { + string groupname = resolveVars(par1, sess, sc_sess, event_params); + string var = resolveVars(par2, sess, sc_sess, event_params); + map ev_params; + + if (!var.empty()) { + if (var == "var") + ev_params = sc_sess->var; + else { + vector vars = explode(var, ";"); + for (vector::iterator it = + vars.begin(); it != vars.end(); it++) + ev_params[*it] = sc_sess->var[*it]; + } + } + + + DBG("posting event to group '%s'\n", groupname.c_str()); + GroupsModule::groups_mut.unlock(); + GroupMap::iterator grp = GroupsModule::groups.find(groupname); + bool posted = false; + if (grp != GroupsModule::groups.end()) { + for (set::iterator it = + grp->second.begin(); it != grp->second.end(); it++) { + if (*it == sess->getLocalTag()) + continue; // don't post to myself + + DSMEvent* ev = new DSMEvent(); + ev->params = ev_params; + if (!AmSessionContainer::instance()->postEvent(*it, ev)) { + DBG("could not post to call '%s'\n", it->c_str()); + } else { + DBG("posted event to call '%s'\n", it->c_str()); + posted = true; + } + } + } + GroupsModule::groups_mut.unlock(); + + if (posted) { + sc_sess->CLR_ERRNO; + } else { + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + sc_sess->SET_STRERROR("event could not be posted\n"); + } + +} EXEC_ACTION_END; diff --git a/apps/dsm/mods/mod_groups/ModGroups.h b/apps/dsm/mods/mod_groups/ModGroups.h new file mode 100644 index 00000000..4305e9ae --- /dev/null +++ b/apps/dsm/mods/mod_groups/ModGroups.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2010 Stefan Sayer + * + * Development of this module was sponsored by TelTech Systems Inc + * + * This file is part of SEMS, a free SIP media server. + * + * SEMS is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. This program is released under + * the GPL with the additional exemption that compiling, linking, + * and/or using OpenSSL is allowed. + * + * For a license to use the SEMS software under conditions + * other than those described here, or to purchase support for this + * software, please contact iptel.org by e-mail at the following addresses: + * info@iptel.org + * + * SEMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _MOD_MYSQL_H +#define _MOD_MYSQL_H +#include "DSMModule.h" +#include "DSMSession.h" +#include "AmThread.h" + +#define MOD_CLS_NAME GroupsModule + +#include +using std::set; +#include +using std::map; +#include +using std::string; + +typedef map > GroupMap; + +DECLARE_MODULE_BEGIN(MOD_CLS_NAME); + +static AmMutex groups_mut; +// group name ltags + +static GroupMap groups; +// ltags groups +static GroupMap groups_rev; + +static void leave_all_groups(const string& ltag); +void onBeforeDestroy(DSMSession* sc_sess, AmSession* sess); + +DECLARE_MODULE_END; + +DEF_ACTION_1P(GroupsJoinAction); +DEF_ACTION_1P(GroupsLeaveAction); +DEF_ACTION_1P(GroupsLeaveAllAction); +/* DEF_ACTION_1P(GroupsGetAction); */ +/* DEF_ACTION_1P(GroupsGetMembersAction); */ +DEF_ACTION_2P(GroupsPostEventAction); + +#endif diff --git a/doc/dsm/mods/Readme.mod_groups.txt b/doc/dsm/mods/Readme.mod_groups.txt new file mode 100644 index 00000000..63248a1f --- /dev/null +++ b/doc/dsm/mods/Readme.mod_groups.txt @@ -0,0 +1,56 @@ +mod_groups - Group event passing + + Copyright (C) 2010 Stefan Sayer + + Development of this module was sponsored by TelTech Systems Inc + +Overview +-------- + +The groups module implements a group event broadcast system. DSM calls can +join and leave groups, and post events to groups. Events are passed to all +members of the group that the event is posted to. Events can, just like the +`postEvent` function of DSM, contain either some variables, or all variables. +If a call ends, it is automatically removed from all groups it belongs to. + +Actions: + + groups.join(groupname) - Join a group + groups.leave(groupname) - Leave a group + groups.leaveAll() - Leave all groups + groups.postEvent(groupname, var1;var2) - post event to groupname with var1 and var2 + groups.postEvent(groupname, var) - post event to groupname with all variables + + +Example: + import(mod_groups); + initial state START + enter { + groups.join("yeah"); + repost(); + }; + + transition "in the call" START --> IN_CALL; + + state IN_CALL; + + transition "got key" IN_CALL - key / { + set($event="key_press"); + set($key=#key); + set($duration=#duration); + set($type=groups); + logVars(1); + groups.postEvent("yeah", event;key;duration;type); + } -> IN_CALL; + + transition "got group event" IN_CALL - event(#type==groups) / { + logParams(); + } -> IN_CALL; + + transition "hangup" IN_CALL - hangup / { + -- groups.leave("yeah"); + groups.leaveAll(); + stop(false); + } -> END; + + state END;