conference with control over DI

git-svn-id: http://svn.berlios.de/svnroot/repos/sems/trunk@371 8eb893ce-cfd4-0310-b710-fb5ebe64c474
sayer/1.4-spce2.6
Stefan Sayer 19 years ago
parent c2c5015521
commit 8b427ea9d2

@ -0,0 +1,9 @@
plug_in_name = webconference
module_ldflags =
module_cflags =
extra_install = $(plug_in_name)_audio
COREPATH ?=../../core
include $(COREPATH)/plug-in/Makefile.app_module

@ -0,0 +1,78 @@
webconference : conference with dial-in and dial-out over DI (xmlrpc)
This conference module can do dial-in, dial-out and mixed
dial-in/dial-out conferences. It can be controlled over DI functions
from other modules, and, for example, using the xmlrpc2di module,
the conference rooms can be controlled via XMLRPC.
It implements conference rooms with dial-in and conference room entry
via DTMF, and authenticated dial-out.
Participants can be listed, kicked out, muted and unmuted over DI
(using xmlrpc2di over XMLRPC).
implemented DI functions:
----
roomCreate(string room):
int code, string result, string adminpin
code/result:
0 OK
1 room already open
----
roomInfo(string room, string adminpin):
int code, string result, list<participant> participants
participant: string call_tag, string number, int status, string reason, int muted
status:
0 Disconnected
1 Connecting
2 Ringing
3 Connected
4 Disconnecting
reason: e.g. "200 OK", "606 Declined", ...
code/result:
0 OK
1 wrong adminpin
----
dialout(string room, string adminpin, string callee,
string from_user, string domain,
string auth_user, string auth_realm, string auth_pwd) :
int code, string result, string callid
code/result:
0 OK
1 wrong adminpin
2 failed
----
kickout(string room, string adminpin, string call_tag) :
int code, string result
code/result:
0 OK
1 wrong adminpin
2 call does not exist in room
----
mute(string room, string adminpin, string call_tag) :
int code, string result
code/result:
0 OK
1 wrong adminpin
2 call does not exist in room
----
unmute(string room, string adminpin, string call_tag) :
int code, string result
code/result:
0 OK
1 wrong adminpin
2 call does not exist in room
----
serverInfo():
string name,
int load,
int cpu
----

@ -0,0 +1,114 @@
#include "RoomInfo.h"
#include <string.h>
#include "log.h"
void ConferenceRoomParticipant::updateAccess(const struct timeval& now) {
memcpy(&last_access_time, &now, sizeof(struct timeval));
}
bool ConferenceRoomParticipant::expired(const struct timeval& now) {
if (Finished != status)
return false;
struct timeval diff;
timersub(&now,&last_access_time,&diff);
return (diff.tv_sec > 0) &&
(unsigned int)diff.tv_sec > PARTICIPANT_EXPIRED_DELAY;
}
AmArgArray* ConferenceRoomParticipant::asArgArray() {
AmArgArray* res = new AmArgArray();
res->push(AmArg(localtag.c_str()));
res->push(AmArg(number.c_str()));
res->push(AmArg((int)status));
res->push(AmArg(last_reason.c_str()));
res->push(AmArg((int)muted));
return res;
}
void ConferenceRoomParticipant::setMuted(int mute) {
muted = mute;
}
void ConferenceRoomParticipant::updateStatus(ConferenceRoomParticipant::ParticipantStatus
new_status,
const string& reason) {
status = new_status;
last_reason = reason;
struct timeval now;
gettimeofday(&now, NULL);
updateAccess(now);
}
void ConferenceRoom::cleanExpired() {
struct timeval now;
gettimeofday(&now, NULL);
list<ConferenceRoomParticipant>::iterator it=participants.begin();
while (it != participants.end()) {
if (it->expired(now)) {
participants.erase(it);
it=participants.begin();
} else
it++;
}
}
AmArgArray* ConferenceRoom::asArgArray() {
cleanExpired();
AmArgArray* res = new AmArgArray();
for (list<ConferenceRoomParticipant>::iterator it=participants.begin();
it != participants.end(); it++) {
AmArg r;
r.setBorrowedPointer(it->asArgArray());
res->push(r);
}
return res;
}
void ConferenceRoom::newParticipant(const string& localtag,
const string& number) {
participants.push_back(ConferenceRoomParticipant());
participants.back().localtag = localtag;
participants.back().number = number;
}
bool ConferenceRoom::hasParticipant(const string& localtag) {
bool res = false;
for (list<ConferenceRoomParticipant>::iterator it =participants.begin();
it != participants.end();it++)
if (it->localtag == localtag) {
res = true;
break;
}
return res;
}
void ConferenceRoom::setMuted(const string& localtag, int mute) {
for (list<ConferenceRoomParticipant>::iterator it =participants.begin();
it != participants.end();it++)
if (it->localtag == localtag) {
it->setMuted(mute);
break;
}
}
bool ConferenceRoom::updateStatus(const string& part_tag,
ConferenceRoomParticipant::ParticipantStatus newstatus,
const string& reason) {
cleanExpired();
bool res = false;
list<ConferenceRoomParticipant>::iterator it=participants.begin();
while (it != participants.end()) {
if (it->localtag == part_tag) {
it->updateStatus(newstatus, reason);
res = true;
}
it++;
}
return res;
}

@ -0,0 +1,78 @@
#ifndef ROOM_INFO_H
#define ROOM_INFO_H
#include <string>
using std::string;
#include <list>
using std::list;
#include <sys/time.h>
#include "AmArg.h"
#include "AmThread.h"
#define PARTICIPANT_EXPIRED_DELAY 10
struct ConferenceRoomParticipant {
enum ParticipantStatus {
Disconnected = 0,
Connecting,
Ringing,
Connected,
Disconnecting,
Finished
};
string localtag;
string number;
ParticipantStatus status;
string last_reason;
int muted;
struct timeval last_access_time;
ConferenceRoomParticipant()
: status(Disconnected), muted(0) { }
~ConferenceRoomParticipant() { }
inline void updateAccess(const struct timeval& now);
inline bool expired(const struct timeval& now);
inline void updateStatus(ParticipantStatus new_status,
const string& reason);
inline void setMuted(int mute);
AmArgArray* asArgArray();
};
struct ConferenceRoom {
string adminpin;
list<ConferenceRoomParticipant> participants;
ConferenceRoom() { }
~ConferenceRoom() { }
void cleanExpired();
AmArgArray* asArgArray();
void newParticipant(const string& localtag, const string& number);
bool updateStatus(const string& part_tag,
ConferenceRoomParticipant::ParticipantStatus newstatus,
const string& reason);
bool hasParticipant(const string& localtag);
void setMuted(const string& localtag, int mute);
};
#endif

@ -0,0 +1,627 @@
/*
* $Id: WebConference.cpp 288 2007-03-28 16:32:02Z sayer $
*
* Copyright (C) 2007 iptego GmbH
*
* 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.
*
* 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 "WebConference.h"
#include "AmConferenceStatus.h"
#include "AmUtils.h"
#include "log.h"
#include "AmUAC.h"
#include "AmPlugIn.h"
#include "AmSessionContainer.h"
#include <stdlib.h>
#define APP_NAME "webconference"
EXPORT_SESSION_FACTORY(WebConferenceFactory,APP_NAME);
EXPORT_PLUGIN_CLASS_FACTORY(WebConferenceFactory,APP_NAME);
WebConferenceFactory::WebConferenceFactory(const string& _app_name)
: AmSessionFactory(_app_name),
AmDynInvokeFactory(_app_name),
configured(false)
{
if (NULL == _instance) {
_instance = this;
}
}
WebConferenceFactory* WebConferenceFactory::_instance=0;
string WebConferenceFactory::DigitsDir;
PlayoutType WebConferenceFactory::m_PlayoutType = ADAPTIVE_PLAYOUT;
int WebConferenceFactory::onLoad()
{
// only execute this once
if (configured)
return 0;
configured = true;
AmConfigReader cfg;
if(cfg.loadFile(AmConfig::ModConfigPath + string(APP_NAME)+ ".conf"))
return -1;
// get application specific global parameters
configureModule(cfg);
// get prompts
AM_PROMPT_START;
AM_PROMPT_ADD(FIRST_PARTICIPANT, ANNOUNCE_PATH "first_paricipant.wav");
AM_PROMPT_ADD(JOIN_SOUND, ANNOUNCE_PATH "beep.wav");
AM_PROMPT_ADD(DROP_SOUND, ANNOUNCE_PATH "beep.wav");
AM_PROMPT_ADD(ENTER_PIN, ANNOUNCE_PATH "enter_pin.wav");
AM_PROMPT_ADD(WRONG_PIN, ANNOUNCE_PATH "wrong_pin.wav");
AM_PROMPT_ADD(ENTERING_CONFERENCE, ANNOUNCE_PATH "entering_conference.wav");
AM_PROMPT_END(prompts, cfg, APP_NAME);
DigitsDir = cfg.getParameter("digits_dir");
if (DigitsDir.length() && DigitsDir[DigitsDir.length()-1]!='/')
DigitsDir+='/';
if (!DigitsDir.length()) {
WARN("No digits_dir specified in configuration.\n");
}
for (int i=0;i<10;i++)
prompts.setPrompt(int2str(i), DigitsDir+int2str(i)+".wav", APP_NAME);
string playout_type = cfg.getParameter("playout_type");
if (playout_type == "simple") {
m_PlayoutType = SIMPLE_PLAYOUT;
DBG("Using simple (fifo) buffer as playout technique.\n");
} else if (playout_type == "adaptive_jb") {
m_PlayoutType = JB_PLAYOUT;
DBG("Using adaptive jitter buffer as playout technique.\n");
} else {
DBG("Using adaptive playout buffer as playout technique.\n");
}
return 0;
}
void WebConferenceFactory::newParticipant(const string& conf_id,
const string& localtag,
const string& number) {
DBG("newparticipant ------------------- %s %s %s\n", conf_id.c_str(),
localtag.c_str(),
number.c_str());
rooms_mut.lock();
rooms[conf_id].newParticipant(localtag, number);
rooms_mut.unlock();
}
void WebConferenceFactory::updateStatus(const string& conf_id,
const string& localtag,
ConferenceRoomParticipant::ParticipantStatus status,
const string& reason) {
rooms_mut.lock();
rooms[conf_id].updateStatus(localtag, status, reason);
rooms_mut.unlock();
}
ConferenceRoom* WebConferenceFactory::getRoom(const string& room,
const string& adminpin) {
ConferenceRoom* res = NULL;
map<string, ConferenceRoom>::iterator it = rooms.find(room);
if (it == rooms.end()) {
// (re)open room
rooms[room] = ConferenceRoom();
rooms[room].adminpin = adminpin;
res = &rooms[room];
} else {
if (!it->second.adminpin.empty() &&
(it->second.adminpin != adminpin)) {
// wrong pin
} else {
// update adminpin if room was created by dialin
if (it->second.adminpin.empty())
it->second.adminpin = adminpin;
res = &it->second;
}
}
return res;
}
// incoming calls
AmSession* WebConferenceFactory::onInvite(const AmSipRequest& req)
{
DBG("ONINVITE ----------------- INCOMING ---------------------\n");
return new WebConferenceDialog(prompts, getInstance(), NULL);
}
// outgoing calls - req is INVITE
AmSession* WebConferenceFactory::onInvite(const AmSipRequest& req,
AmArg& session_params)
{
DBG("ONINVITE ----------------- OUTGOING ---------------------\n");
UACAuthCred* cred = NULL;
if (session_params.getType() == AmArg::AObject) {
ArgObject* cred_obj = session_params.asObject();
if (cred_obj)
cred = dynamic_cast<UACAuthCred*>(cred_obj);
}
AmSession* s = new WebConferenceDialog(prompts, getInstance(), cred);
AmSessionEventHandlerFactory* uac_auth_f =
AmPlugIn::instance()->getFactory4Seh("uac_auth");
if (uac_auth_f != NULL) {
DBG("UAC Auth enabled for new announcement session.\n");
AmSessionEventHandler* h = uac_auth_f->getHandler(s);
if (h != NULL )
s->addHandler(h);
} else {
ERROR("uac_auth interface not accessible. Load uac_auth for authenticated dialout.\n");
}
return s;
}
void WebConferenceFactory::invoke(const string& method,
const AmArgArray& args,
AmArgArray& ret)
{
if(method == "roomCreate"){
roomCreate(args, ret);
} else if(method == "roomInfo"){
roomInfo(args, ret);
} else if(method == "dialout"){
dialout(args, ret);
} else if(method == "mute"){
mute(args, ret);
} else if(method == "unmute"){
unmute(args, ret);
} else if(method == "kickout"){
kickout(args, ret);
} else if(method == "serverInfo"){
serverInfo(args, ret);
} else if(method == "help"){
ret.push("help text goes here");
} else
throw AmDynInvoke::NotImplemented(method);
}
string WebConferenceFactory::getRandomPin() {
string res;
for (int i=0;i<6;i++)
res+=(char)('0'+random()%10);
return res;
}
void WebConferenceFactory::roomCreate(const AmArgArray& args, AmArgArray& ret) {
string room = args.get(0).asCStr();
rooms_mut.lock();
map<string, ConferenceRoom>::iterator it = rooms.find(room);
if (it == rooms.end()) {
rooms[room] = ConferenceRoom();
rooms[room].adminpin = getRandomPin();
ret.push(0);
ret.push("OK");
ret.push(rooms[room].adminpin.c_str());
} else {
ret.push(1);
ret.push("room already opened");
}
rooms_mut.unlock();
}
void WebConferenceFactory::roomInfo(const AmArgArray& args, AmArgArray& ret) {
string room = args.get(0).asCStr();
string adminpin = args.get(1).asCStr();;
rooms_mut.lock();
ConferenceRoom* r = getRoom(room, adminpin);
if (NULL == r) {
ret.push(1);
ret.push("wrong adminpin");
// for consistency, add an empty array
AmArgArray* a = new AmArgArray();
AmArg res;
res.setBorrowedPointer(a);
ret.push(res);
} else {
ret.push(0);
ret.push("OK");
AmArg res;
res.setBorrowedPointer(r->asArgArray());
ret.push(res);
}
rooms_mut.unlock();
// if (checkAdminpin(room, adminpin)) {
// ret.push(0);
// ret.push("OK");
// AmArg r;
// r.setBorrowedPointer(it->second.asArgArray());
// ret.push(r);
// } else {
// ret.push(1);
// ret.push("wrong adminpin");
// // for consistency, add an empty array
// AmArgArray* a = new AmArgArray();
// AmArg r;
// r.setBorrowedPointer(a);
// ret.push(r);
// }
// rooms_mut.lock();
// map<string, ConferenceRoom>::iterator it = rooms.find(room);
// if (it == rooms.end()) {
// // (re)open room
// rooms[room] = ConferenceRoom();
// rooms[room].adminpin = adminpin;
// ret.push(0);
// ret.push("OK");
// // add empty paricipants list
// AmArgArray* a = new AmArgArray();
// AmArg r;
// r.setBorrowedPointer(a);
// ret.push(r);
// } else {
// if (!it->second.adminpin.empty() &&
// (it->second.adminpin != adminpin)) {
// ret.push(1);
// ret.push("wrong adminpin");
// } else {
// // update adminpin if room was created by dialin
// if (it->second.adminpin.empty())
// it->second.adminpin = adminpin;
// // return room info
// ret.push(0);
// ret.push("OK");
// AmArg r;
// r.setBorrowedPointer(it->second.asArgArray());
// ret.push(r);
// }
// }
// rooms_mut.unlock();
}
void WebConferenceFactory::dialout(const AmArgArray& args, AmArgArray& ret) {
string room = args.get(0).asCStr();
string adminpin = args.get(1).asCStr();
string callee = args.get(2).asCStr();
string from_user = args.get(3).asCStr();
string domain = args.get(4).asCStr();
string auth_user = args.get(5).asCStr();
string auth_realm = args.get(6).asCStr();
string auth_pwd = args.get(7).asCStr();
string from = "sip:" + from_user + "@" + domain;
string to = "sip:" + callee + "@" + domain;
// check adminpin
rooms_mut.lock();
ConferenceRoom* r = getRoom(room, adminpin);
rooms_mut.unlock();
if (NULL == r) {
ret.push(1);
ret.push("wrong adminpin");
ret.push("");
return;
}
DBG("dialout webconference room '%s', from '%s', to '%s'",
room.c_str(), from.c_str(), to.c_str());
AmArg* a = new AmArg();
a->setBorrowedPointer(new UACAuthCred(auth_realm, auth_user, auth_pwd));
AmSession* s = AmUAC::dialout(room.c_str(), APP_NAME, to,
"<" + from + ">", from, "<" + to + ">",
string(""), // callid
a);
if (s) {
string localtag = s->getLocalTag();
ret.push(0);
ret.push("OK");
ret.push(localtag.c_str());
newParticipant(room, localtag, to);
updateStatus(room, localtag,
ConferenceRoomParticipant::Connecting,
"INVITE");
}
else {
ret.push(1);
ret.push("internal error");
ret.push("");
}
}
void WebConferenceFactory::postConfEvent(const AmArgArray& args, AmArgArray& ret,
int id, int mute) {
string room = args.get(0).asCStr();
string adminpin = args.get(1).asCStr();
string call_tag = args.get(2).asCStr();
// check adminpin
rooms_mut.lock();
ConferenceRoom* r = getRoom(room, adminpin);
if (NULL == r) {
ret.push(1);
ret.push("wrong adminpin");
rooms_mut.unlock();
return;
}
bool p_exists = r->hasParticipant(call_tag);
if (p_exists && (mute >= 0))
r->setMuted(call_tag, mute);
rooms_mut.unlock();
if (p_exists) {
AmSessionContainer::instance()->postEvent(call_tag,
new WebConferenceEvent(id));
ret.push(0);
ret.push("OK");
} else {
ret.push(2);
ret.push("call does not exist");
}
}
void WebConferenceFactory::kickout(const AmArgArray& args, AmArgArray& ret) {
postConfEvent(args, ret, WebConferenceEvent::Kick, -1);
}
void WebConferenceFactory::mute(const AmArgArray& args, AmArgArray& ret) {
postConfEvent(args, ret, WebConferenceEvent::Mute, 1);
}
void WebConferenceFactory::unmute(const AmArgArray& args, AmArgArray& ret) {
postConfEvent(args, ret, WebConferenceEvent::Unmute, 0);
}
void WebConferenceFactory::serverInfo(const AmArgArray& args, AmArgArray& ret) {
ret.push("Not yet implemented");
}
WebConferenceDialog::WebConferenceDialog(AmPromptCollection& prompts,
WebConferenceFactory* my_f,
UACAuthCred* cred)
: play_list(this), separator(this, 0), prompts(prompts), state(None),
factory(my_f), cred(cred)
{
is_dialout = (cred != NULL);
// set configured playout type
rtp_str.setPlayoutType(WebConferenceFactory::m_PlayoutType);
}
WebConferenceDialog::~WebConferenceDialog()
{
prompts.cleanup((long)this);
if (InConference == state) {
factory->updateStatus(conf_id,
getLocalTag(),
ConferenceRoomParticipant::Finished,
"out");
}
}
void WebConferenceDialog::connectConference(const string& room) {
// set the conference id ('conference room')
conf_id = room;
// disconnect in/out for safety
setInOut(NULL, NULL);
// we need to be in the same callgroup as the other
// people in the conference (important if we have multiple
// MediaProcessor threads
changeCallgroup(conf_id);
// get a channel from the status
channel.reset(AmConferenceStatus::getChannel(conf_id,getLocalTag()));
// clear the playlist
play_list.close();
// add the channel to our playlist
play_list.addToPlaylist(new AmPlaylistItem(channel.get(),
channel.get()));
// set the playlist as input and output
setInOut(&play_list,&play_list);
}
void WebConferenceDialog::onSessionStart(const AmSipRequest& req) {
state = EnteringPin;
prompts.addToPlaylist(ENTER_PIN, (long)this, play_list);
// set the playlist as input and output
setInOut(&play_list,&play_list);
}
void WebConferenceDialog::onSessionStart(const AmSipReply& rep) {
state = InConference;
connectConference(dlg.user);
}
void WebConferenceDialog::onSipReply(const AmSipReply& reply) {
DBG("SIP REPLY ----------------------\n");
AmSession::onSipReply(reply);
if (is_dialout) {
DBG("is_dialout");
// map AmSipDialog state to WebConferenceState
ConferenceRoomParticipant::ParticipantStatus rep_st = ConferenceRoomParticipant::Connecting;
switch (dlg.getStatus()) {
case AmSipDialog::Pending: {
rep_st = ConferenceRoomParticipant::Connecting;
if (reply.code == 180)
rep_st = ConferenceRoomParticipant::Ringing;
} break;
case AmSipDialog::Connected:
rep_st = ConferenceRoomParticipant::Connected; break;
case AmSipDialog::Disconnecting:
rep_st = ConferenceRoomParticipant::Disconnecting; break;
}
DBG("is dialout: updateing status\n");
factory->updateStatus(dlg.user, getLocalTag(),
rep_st, int2str(reply.code) + " " + reply.reason);
}
}
void WebConferenceDialog::onBye(const AmSipRequest& req)
{
if (InConference == state) {
factory->updateStatus(conf_id,
getLocalTag(),
ConferenceRoomParticipant::Disconnecting,
req.method);
}
disconnectConference();
}
void WebConferenceDialog::disconnectConference() {
play_list.close();
setInOut(NULL,NULL);
channel.reset(NULL);
setStopped();
}
void WebConferenceDialog::process(AmEvent* ev)
{
// check conference events
ConferenceEvent* ce = dynamic_cast<ConferenceEvent*>(ev);
if(ce && (conf_id == ce->conf_id)){
switch(ce->event_id){
case ConfNewParticipant: {
DBG("########## new participant #########\n");
if(ce->participants == 1){
prompts.addToPlaylist(FIRST_PARTICIPANT, (long)this, play_list, true);
} else {
prompts.addToPlaylist(JOIN_SOUND, (long)this, play_list, true);
}
} break;
case ConfParticipantLeft: {
DBG("########## participant left ########\n");
prompts.addToPlaylist(DROP_SOUND, (long)this, play_list, true);
} break;
default:
break;
}
return;
}
// our item will fire this event
AmPlaylistSeparatorEvent* sep_ev = dynamic_cast<AmPlaylistSeparatorEvent*>(ev);
if (NULL != sep_ev) {
// don't care for the id here
if (EnteringConference == state) {
state = InConference;
DBG("connectConference. **********************\n");
connectConference(pin_str);
factory->newParticipant(pin_str,
getLocalTag(),
dlg.remote_party);
factory->updateStatus(pin_str,
getLocalTag(),
ConferenceRoomParticipant::Connected,
"200 OK");
}
}
// audio events
AmAudioEvent* audio_ev = dynamic_cast<AmAudioEvent*>(ev);
if (audio_ev &&
audio_ev->event_id == AmAudioEvent::noAudio) {
DBG("received noAudio event. **********************\n");
return;
}
WebConferenceEvent* webconf_ev = dynamic_cast<WebConferenceEvent*>(ev);
if (NULL != webconf_ev) {
if (InConference == state) {
switch(webconf_ev->event_id) {
case WebConferenceEvent::Kick: {
dlg.bye();
disconnectConference();
factory->updateStatus(conf_id,
getLocalTag(),
ConferenceRoomParticipant::Disconnecting,
"disconnect");
} break;
case WebConferenceEvent::Mute: { setInOut(NULL, &play_list); } break;
case WebConferenceEvent::Unmute:{ setInOut(&play_list, &play_list); } break;
default: { WARN("ignoring unknown webconference event %d\n", webconf_ev->event_id); } break;
}
}
return;
}
AmSession::process(ev);
}
void WebConferenceDialog::onDtmf(int event, int duration)
{
DBG("WebConferenceDialog::onDtmf: event %d duration %d\n",
event, duration);
if (EnteringPin == state) {
// not yet in conference
if (event<10) {
pin_str += int2str(event);
DBG("added '%s': PIN is now '%s'.\n",
int2str(event).c_str(), pin_str.c_str());
} else if (event==10 || event==11) {
// pound and star key
// if required add checking of pin here...
if (!pin_str.length()) {
prompts.addToPlaylist(WRONG_PIN, (long)this, play_list, true);
} else {
state = EnteringConference;
setInOut(NULL, NULL);
play_list.close();
for (size_t i=0;i<pin_str.length();i++) {
string num = "";
num[0] = pin_str[i];
DBG("adding '%s' to playlist.\n", num.c_str());
prompts.addToPlaylist(num,
(long)this, play_list);
}
setInOut(&play_list,&play_list);
prompts.addToPlaylist(ENTERING_CONFERENCE,
(long)this, play_list);
play_list.addToPlaylist(new AmPlaylistItem(&separator, NULL));
}
}
}
}

@ -0,0 +1,176 @@
/*
* $Id: PinAuthConference.h 288 2007-03-28 16:32:02Z sayer $
*
* Copyright (C) 2007 iptego GmbH
*
* 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.
*
* 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 _PINAUTHCONFERENCE_H_
#define _PINAUTHCONFERENCE_H_
#include "AmApi.h"
#include "AmSession.h"
#include "AmAudio.h"
#include "AmConferenceChannel.h"
#include "AmPlaylist.h"
#include "AmPromptCollection.h"
#include "ampi/UACAuthAPI.h"
#include "RoomInfo.h"
#include <map>
#include <string>
using std::map;
using std::string;
class ConferenceStatus;
class ConferenceStatusContainer;
// configuration parameter names
#define ENTERING_CONFERENCE "entering_conference"
#define FIRST_PARTICIPANT "first_participant"
#define JOIN_SOUND "join_sound"
#define DROP_SOUND "drop_sound"
#define ENTER_PIN "enter_pin"
#define WRONG_PIN "wrong_pin"
// default path for files
#define ANNOUNCE_PATH "../apps/examples/webconference/"
class WebConferenceEvent : public AmEvent
{
public:
WebConferenceEvent(int id) : AmEvent(id) { }
enum { Kick,
Mute,
Unmute
};
};
class WebConferenceFactory
: public AmSessionFactory,
public AmDynInvokeFactory,
public AmDynInvoke
{
AmPromptCollection prompts;
map<string, ConferenceRoom> rooms;
AmMutex rooms_mut;
// for DI
static WebConferenceFactory* _instance;
bool configured;
string getRandomPin();
/** returns NULL if adminpin wrong */
ConferenceRoom* getRoom(const string& room,
const string& adminpin);
void postConfEvent(const AmArgArray& args, AmArgArray& ret,
int id, int mute);
public:
static string DigitsDir;
static PlayoutType m_PlayoutType;
WebConferenceFactory(const string& _app_name);
AmSession* onInvite(const AmSipRequest&);
AmSession* onInvite(const AmSipRequest& req,
AmArg& session_params);
int onLoad();
inline void newParticipant(const string& conf_id,
const string& localtag,
const string& number);
inline void updateStatus(const string& conf_id,
const string& localtag,
ConferenceRoomParticipant::ParticipantStatus status,
const string& reason);
// DI API
WebConferenceFactory* getInstance(){
return _instance;
}
void invoke(const string& method, const AmArgArray& args, AmArgArray& ret);
// DI functions
void roomCreate(const AmArgArray& args, AmArgArray& ret);
void roomInfo(const AmArgArray& args, AmArgArray& ret);
void dialout(const AmArgArray& args, AmArgArray& ret);
void kickout(const AmArgArray& args, AmArgArray& ret);
void mute(const AmArgArray& args, AmArgArray& ret);
void unmute(const AmArgArray& args, AmArgArray& ret);
void serverInfo(const AmArgArray& args, AmArgArray& ret);
};
class WebConferenceDialog
: public AmSession,
public CredentialHolder
{
public:
enum WebConferenceState {
None,
EnteringPin,
EnteringConference,
InConference
};
private:
AmPlaylist play_list;
AmPlaylistSeparator separator;
AmPromptCollection& prompts;
// our connection to the conference
auto_ptr<AmConferenceChannel> channel;
string conf_id;
string pin_str;
void connectConference(const string& room);
void disconnectConference();
WebConferenceState state;
WebConferenceFactory* factory;
bool is_dialout;
UACAuthCred* cred;
public:
WebConferenceDialog(AmPromptCollection& prompts,
WebConferenceFactory* my_f,
UACAuthCred* cred);
~WebConferenceDialog();
void process(AmEvent* ev);
void onSipReply(const AmSipReply& reply);
void onSessionStart(const AmSipRequest& req);
void onSessionStart(const AmSipReply& rep);
void onDtmf(int event, int duration);
void onBye(const AmSipRequest& req);
UACAuthCred* getCredentials() { return cred; }
};
#endif
// Local Variables:
// mode:C++
// End:

@ -0,0 +1,12 @@
# configure the various announcements here
first_participant=/usr/local/lib/sems/audio/webconference/first_participant.wav
join_sound=/usr/local/lib/sems/audio/webconference/beep.wav
drop_sound=/usr/local/lib/sems/audio/webconference/beep.wav
enter_pin=/usr/local/lib/sems/audio/webconference/pin_prompt.wav
wrong_pin=/usr/local/lib/sems/audio/webconference/wrong_pin.wav
entering_conference=/usr/local/lib/sems/audio/webconference/entering_conference.wav
# digits to play the room number
# must contain the files 0.wav, 1.wav, ..., 9.wav
digits_dir=/usr/local/lib/sems/audio/webconference/

Binary file not shown.
Loading…
Cancel
Save