1 /*
2 * Copyright (C) 2007 iptego GmbH
3 *
4 * This file is part of SEMS, a free SIP media server.
5 *
6 * SEMS is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * For a license to use the sems software under conditions
12 * other than those described here, or to purchase support for this
13 * software, please contact iptel.org by e-mail at the following addresses:
14 * info@iptel.org
15 *
16 * SEMS is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26 #include "PinAuthConference.h"
27 #include "AmConferenceStatus.h"
28 #include "AmUtils.h"
29 #include "log.h"
30
31 #define APP_NAME "pinauthconference"
32
33 EXPORT_SESSION_FACTORY(PinAuthConferenceFactory,APP_NAME);
34
PinAuthConferenceFactory(const string & _app_name)35 PinAuthConferenceFactory::PinAuthConferenceFactory(const string& _app_name)
36 : AmSessionFactory(_app_name)
37 {
38 }
39
40 string PinAuthConferenceFactory::DigitsDir;
41 PlayoutType PinAuthConferenceFactory::m_PlayoutType = ADAPTIVE_PLAYOUT;
42
onLoad()43 int PinAuthConferenceFactory::onLoad()
44 {
45 AmConfigReader cfg;
46 if(cfg.loadFile(AmConfig::ModConfigPath + string(APP_NAME)+ ".conf"))
47 return -1;
48
49 // get application specific global parameters
50 configureModule(cfg);
51
52 // get prompts
53 AM_PROMPT_START;
54 AM_PROMPT_ADD(FIRST_PARTICIPANT, ANNOUNCE_PATH "first_paricipant.wav");
55 AM_PROMPT_ADD(JOIN_SOUND, ANNOUNCE_PATH "beep.wav");
56 AM_PROMPT_ADD(DROP_SOUND, ANNOUNCE_PATH "beep.wav");
57 AM_PROMPT_ADD(ENTER_PIN, ANNOUNCE_PATH "enter_pin.wav");
58 AM_PROMPT_ADD(WRONG_PIN, ANNOUNCE_PATH "wrong_pin.wav");
59 AM_PROMPT_ADD(ENTERING_CONFERENCE, ANNOUNCE_PATH "entering_conference.wav");
60 AM_PROMPT_END(prompts, cfg, APP_NAME);
61
62 DigitsDir = cfg.getParameter("digits_dir");
63 if (DigitsDir.length() && DigitsDir[DigitsDir.length()-1]!='/')
64 DigitsDir+='/';
65
66 if (!DigitsDir.length()) {
67 WARN("No digits_dir specified in configuration.\n");
68 }
69 for (int i=0;i<10;i++)
70 prompts.setPrompt(int2str(i), DigitsDir+int2str(i)+".wav", APP_NAME);
71
72 string playout_type = cfg.getParameter("playout_type");
73 if (playout_type == "simple") {
74 m_PlayoutType = SIMPLE_PLAYOUT;
75 DBG("Using simple (fifo) buffer as playout technique.\n");
76 } else if (playout_type == "adaptive_jb") {
77 m_PlayoutType = JB_PLAYOUT;
78 DBG("Using adaptive jitter buffer as playout technique.\n");
79 } else {
80 DBG("Using adaptive playout buffer as playout technique.\n");
81 }
82
83 return 0;
84 }
85
86 // incoming calls
onInvite(const AmSipRequest &,const string & app_name,const map<string,string> & app_params)87 AmSession* PinAuthConferenceFactory::onInvite(const AmSipRequest&, const string& app_name,
88 const map<string,string>& app_params)
89 {
90 return new PinAuthConferenceDialog(prompts);
91 }
92
93 // outgoing calls
onInvite(const AmSipRequest & req,const string & app_name,AmArg & session_params)94 AmSession* PinAuthConferenceFactory::onInvite(const AmSipRequest& req, const string& app_name,
95 AmArg& session_params)
96 {
97 return new PinAuthConferenceDialog(prompts);
98 }
99
PinAuthConferenceDialog(AmPromptCollection & prompts)100 PinAuthConferenceDialog::PinAuthConferenceDialog(AmPromptCollection& prompts)
101 : play_list(this), separator(this, 0), prompts(prompts), state(None)
102 {
103 // set configured playout type
104 RTPStream()->setPlayoutType(PinAuthConferenceFactory::m_PlayoutType);
105 }
106
~PinAuthConferenceDialog()107 PinAuthConferenceDialog::~PinAuthConferenceDialog()
108 {
109 play_list.flush();
110 prompts.cleanup((long)this);
111 }
112
connectConference(const string & room)113 void PinAuthConferenceDialog::connectConference(const string& room) {
114 // set the conference id ('conference room')
115 conf_id = room;
116
117 // disconnect in/out for safety
118 setInOut(NULL, NULL);
119
120 // we need to be in the same callgroup as the other
121 // people in the conference (important if we have multiple
122 // MediaProcessor threads
123 changeCallgroup(conf_id);
124
125 // get a channel from the status
126 channel.reset(AmConferenceStatus::getChannel(conf_id,getLocalTag(),RTPStream()->getSampleRate()));
127
128 // clear the playlist
129 play_list.flush();
130
131 // add the channel to our playlist
132 play_list.addToPlaylist(new AmPlaylistItem(channel.get(),
133 channel.get()));
134
135 // set the playlist as input and output
136 setInOut(&play_list,&play_list);
137 }
138
onSessionStart()139 void PinAuthConferenceDialog::onSessionStart()
140 {
141 state = EnteringPin;
142
143 prompts.addToPlaylist(ENTER_PIN, (long)this, play_list);
144
145 // set the playlist as input and output
146 setInOut(&play_list,&play_list);
147
148 AmSession::onSessionStart();
149 }
150
onBye(const AmSipRequest & req)151 void PinAuthConferenceDialog::onBye(const AmSipRequest& req)
152 {
153 play_list.flush();
154 setInOut(NULL,NULL);
155 channel.reset(NULL);
156 setStopped();
157 }
158
process(AmEvent * ev)159 void PinAuthConferenceDialog::process(AmEvent* ev)
160 {
161 // check conference events
162 ConferenceEvent* ce = dynamic_cast<ConferenceEvent*>(ev);
163 if(ce && (conf_id == ce->conf_id)){
164 switch(ce->event_id){
165
166 case ConfNewParticipant: {
167 DBG("########## new participant #########\n");
168 if(ce->participants == 1){
169 prompts.addToPlaylist(FIRST_PARTICIPANT, (long)this, play_list, true);
170 } else {
171 prompts.addToPlaylist(JOIN_SOUND, (long)this, play_list, true);
172 }
173 } break;
174
175 case ConfParticipantLeft: {
176 DBG("########## participant left ########\n");
177 prompts.addToPlaylist(DROP_SOUND, (long)this, play_list, true);
178 } break;
179
180 default:
181 break;
182 }
183 return;
184 }
185
186 // our item will fire this event
187 AmPlaylistSeparatorEvent* sep_ev = dynamic_cast<AmPlaylistSeparatorEvent*>(ev);
188 if (NULL != sep_ev) {
189 // don't care for the id here
190 if (EnteringConference == state) {
191 state = InConference;
192 connectConference(pin_str);
193 DBG("connectConference. **********************\n");
194 }
195 }
196 // audio events
197 AmAudioEvent* audio_ev = dynamic_cast<AmAudioEvent*>(ev);
198 if (audio_ev &&
199 audio_ev->event_id == AmAudioEvent::noAudio) {
200 DBG("received noAudio event. **********************\n");
201 return;
202 }
203
204 AmSession::process(ev);
205 }
206
onDtmf(int event,int duration)207 void PinAuthConferenceDialog::onDtmf(int event, int duration)
208 {
209 DBG("PinAuthConferenceDialog::onDtmf: event %d duration %d\n",
210 event, duration);
211
212 if (EnteringPin == state) {
213 // not yet in conference
214 if (event<10) {
215 pin_str += int2str(event);
216 DBG("added '%s': PIN is now '%s'.\n",
217 int2str(event).c_str(), pin_str.c_str());
218 } else if (event==10 || event==11) {
219 // pound and star key
220 // if required add checking of pin here...
221 if (!pin_str.length()) {
222
223 prompts.addToPlaylist(WRONG_PIN, (long)this, play_list, true);
224 } else {
225 state = EnteringConference;
226 setInOut(NULL, NULL);
227 play_list.flush();
228 for (size_t i=0;i<pin_str.length();i++) {
229 string num = "";
230 num[0] = pin_str[i];
231 DBG("adding '%s' to playlist.\n", num.c_str());
232
233 prompts.addToPlaylist(num,
234 (long)this, play_list);
235 }
236
237 setInOut(&play_list,&play_list);
238 prompts.addToPlaylist(ENTERING_CONFERENCE,
239 (long)this, play_list);
240 play_list.addToPlaylist(new AmPlaylistItem(&separator, NULL));
241 }
242 }
243 }
244 }
245
246