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