1 /*
2  * Copyright (C) 2006-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 "AmB2ABSession.h"
27 #include "AmSessionContainer.h"
28 #include "AmConfig.h"
29 #include "AmMediaProcessor.h"
30 #include "ampi/MonitoringAPI.h"
31 
32 #include <assert.h>
33 
34 AmB2ABSession::AmB2ABSession()
35   : AmSession(), connector(NULL)
36 {
37 }
38 
39 AmB2ABSession::AmB2ABSession(const string& other_local_tag)
40   : AmSession(),
41     other_id(other_local_tag)
42 {}
43 
44 
45 AmB2ABSession::~AmB2ABSession()
46 {
47 }
48 
49 void AmB2ABSession::clear_other()
50 {
51 #if __GNUC__ < 3
52   string cleared ("");
53   other_id.assign (cleared, 0, 0);
54 #else
55   other_id.clear();
56 #endif
57 }
58 
59 void AmB2ABSession::process(AmEvent* event)
60 {
61   B2ABEvent* b2b_e = dynamic_cast<B2ABEvent*>(event);
62   if(b2b_e){
63     onB2ABEvent(b2b_e);
64     return;
65   }
66 
67   AmSession::process(event);
68 }
69 
70 void AmB2ABSession::onB2ABEvent(B2ABEvent* ev)
71 {
72   switch(ev->event_id){
73 
74   case B2ABTerminateLeg:
75     terminateLeg();
76     break;
77   }
78 }
79 
80 void AmB2ABSession::relayEvent(AmEvent* ev)
81 {
82   DBG("AmB2ABSession::relayEvent: id=%s\n",
83       other_id.c_str());
84 
85   if(!other_id.empty())
86     AmSessionContainer::instance()->postEvent(other_id,ev);
read(unsigned int user_ts,unsigned int size)87 }
88 
89 void AmB2ABSession::connectSession()
90 {
91   if (!connector) {
92     DBG("error - trying to connect session, but no connector!\n");
93     return;
94   }
95   connector->connectSession(this);
96   AmMediaProcessor::instance()->addSession(this, callgroup);
97 }
98 
99 void AmB2ABSession::disconnectSession()
100 {
101   if (!connector)
102     return;
103 
104   connector->disconnectSession(this);
105 }
106 
107 void AmB2ABSession::onBye(const AmSipRequest& req) {
108   terminateOtherLeg();
109   disconnectSession();
110   setStopped();
111 }
112 
113 void AmB2ABSession::terminateLeg()
114 {
115   dlg->bye();
116   disconnectSession();
117   setStopped();
118 }
119 
120 void AmB2ABSession::terminateOtherLeg()
121 {
122   relayEvent(new B2ABEvent(B2ABTerminateLeg));
123   clear_other();
124 }
125 
126 AmB2ABCallerSession::AmB2ABCallerSession()
127   : AmB2ABSession(),
128     callee_status(None)
129 {
130   // owned by us
131   connector = new AmSessionAudioConnector();
132 }
133 
134 AmB2ABCallerSession::~AmB2ABCallerSession()
135 {
136   delete connector;
137 }
138 
139 void AmB2ABCallerSession::onBeforeDestroy() {
140   DBG("Waiting for release from callee session...\n");
141   connector->waitReleased();
142   DBG("OK, got release from callee session.\n");
143 }
144 
145 void AmB2ABCallerSession::terminateOtherLeg()
146 {
147   if (callee_status != None)
148     AmB2ABSession::terminateOtherLeg();
149 
150   callee_status = None;
151 }
152 
153 void AmB2ABCallerSession::onB2ABEvent(B2ABEvent* ev)
154 {
155 
156   switch(ev->event_id) {
157   case B2ABConnectAudio: {
158     callee_status = Connected;
159 
160     DBG("ConnectAudio event received from other leg\n");
161     B2ABConnectAudioEvent* ca =
162       dynamic_cast<B2ABConnectAudioEvent*>(ev);
163     if (!ca)
164       return;
165 
166     connectSession();
167 
168     return;
169   } break;
170 
171   case B2ABConnectEarlyAudio: {
172     callee_status = Early;
173 
174     DBG("ConnectEarlyAudio event received from other leg\n");
175     B2ABConnectEarlyAudioEvent* ca =
176       dynamic_cast<B2ABConnectEarlyAudioEvent*>(ev);
177     if (!ca)
178       return;
179 
180     connectSession();
181 
182     return;
183   } break;
184 
185   case B2ABConnectOtherLegException:
186   case B2ABConnectOtherLegFailed: {
187     WARN("looks like callee leg could not be created. terminating our leg.\n");
188     terminateLeg();
189     callee_status = None;
190     return;
191   } break;
192 
193   case B2ABOtherLegRinging: {
194     DBG("callee_status set to Ringing.\n");
195     callee_status = Ringing;
196     return;
197   } break;
198 
199   }
200 
201   AmB2ABSession::onB2ABEvent(ev);
202 }
203 
204 void AmB2ABCallerSession::connectCallee(const string& remote_party,
205 					const string& remote_uri,
206 					const string& local_party,
207 					const string& local_uri,
208 					const string& headers)
209 {
210   if(callee_status != None)
211     terminateOtherLeg();
212 
213   B2ABConnectLegEvent* ev = new B2ABConnectLegEvent(remote_party,remote_uri,
214 						    local_party,local_uri,
215 						    getLocalTag(), // callgroup
216 						    headers);  // extra headers
217 
218   relayEvent(ev);
219   callee_status = NoReply;
220 }
221 
222 void AmB2ABCallerSession::relayEvent(AmEvent* ev)
223 {
224   if(other_id.empty()){
225     B2ABConnectLegEvent* co_ev  = dynamic_cast<B2ABConnectLegEvent*>(ev);
226     if( co_ev ) {
227       setupCalleeSession(createCalleeSession(), co_ev);
228       if (other_id.length()) {
229 	MONITORING_LOG(getLocalTag().c_str(), "b2b_leg", other_id.c_str());
230       }
231     }
232   }
233 
234   AmB2ABSession::relayEvent(ev);
235 }
236 
237 void AmB2ABCallerSession::setupCalleeSession(AmB2ABCalleeSession* callee_session,
238 					     B2ABConnectLegEvent* ev)
239 {
240 
241   if (NULL == callee_session)
242     return;
243 
244   other_id = AmSession::getNewId();
245   //  return;
246   assert(callee_session);
247 
248   AmSipDialog* callee_dlg = callee_session->dlg;
249 
250   callee_dlg->setCallid(AmSession::getNewId());
251   callee_dlg->setLocalTag(other_id);
252 
253 
254   MONITORING_LOG(other_id.c_str(),
255 		 "dir",  "out");
256 
257   callee_session->start();
258 
259   AmSessionContainer* sess_cont = AmSessionContainer::instance();
260   sess_cont->addSession(other_id,callee_session);
261 }
262 
263 AmB2ABCalleeSession* AmB2ABCallerSession::createCalleeSession()
264 {
265   return new AmB2ABCalleeSession(getLocalTag(), connector);
266 }
267 
268 AmB2ABCalleeSession::AmB2ABCalleeSession(const string& other_local_tag,
269 					 AmSessionAudioConnector* callers_connector)
270   : AmB2ABSession(other_local_tag),
271     is_connected(false)
272 {
273   connector=callers_connector;
274   connector->block();
275 }
276 
277 AmB2ABCalleeSession::~AmB2ABCalleeSession()
278 {
279 }
280 
281 void AmB2ABCalleeSession::onBeforeDestroy() {
282   DBG("releasing caller session.\n");
283   connector->release();
284   // now caller session is released
285 }
286 
287 
288 void AmB2ABCalleeSession::onB2ABEvent(B2ABEvent* ev)
289 {
290   if(ev->event_id == B2ABConnectLeg){
291 
292     try {
293       B2ABConnectLegEvent* co_ev = dynamic_cast<B2ABConnectLegEvent*>(ev);
294       assert(co_ev);
295 
296       MONITORING_LOG4(getLocalTag().c_str(),
297 		      "b2b_leg", other_id.c_str(),
298 		      "from",    co_ev->local_party.c_str(),
299 		      "to",      co_ev->remote_party.c_str(),
300 		      "ruri",    co_ev->remote_uri.c_str());
301 
302       dlg->setLocalParty(co_ev->local_party);
303       dlg->setLocalUri(co_ev->local_uri);
304 
305       dlg->setRemoteParty(co_ev->remote_party);
306       dlg->setRemoteUri(co_ev->remote_uri);
307 
308       setCallgroup(co_ev->callgroup);
309 
310       //setNegotiateOnReply(true);
311       if (sendInvite(co_ev->headers)) {
312 	throw string("INVITE could not be sent\n");
313       }
314 
315       return;
316     }
317     catch(const AmSession::Exception& e){
318       ERROR("%i %s\n",e.code,e.reason.c_str());
319       relayEvent(new B2ABConnectOtherLegExceptionEvent(e.code,e.reason));
320       setStopped();
321     }
322     catch(const string& err){
323       ERROR("startSession: %s\n",err.c_str());
324       relayEvent(new B2ABConnectOtherLegExceptionEvent(500,err));
325       setStopped();
326     }
327     catch(...){
328       ERROR("unexpected exception\n");
329       relayEvent(new B2ABConnectOtherLegExceptionEvent(500,"unexpected exception"));
330       setStopped();
331     }
332   }
333 
334   AmB2ABSession::onB2ABEvent(ev);
335 }
336 
337 void AmB2ABCalleeSession::onEarlySessionStart() {
338   DBG("onEarlySessionStart of callee session\n");
339   connectSession();
340   is_connected = true;
341   relayEvent(new B2ABConnectEarlyAudioEvent());
342 }
343 
344 
345 void AmB2ABCalleeSession::onSessionStart() {
346   DBG("onSessionStart of callee session\n");
347   if (!is_connected) {
348     is_connected = true;
349     DBG("call connectSession\n");
350     connectSession();
351   }
352   relayEvent(new B2ABConnectAudioEvent());
353 }
354 
355 void AmB2ABCalleeSession::onSipReply(const AmSipRequest& req, const AmSipReply& rep,
356 				     AmBasicSipDialog::Status old_dlg_status) {
357   AmB2ABSession::onSipReply(req, rep, old_dlg_status);
358   AmSipDialog::Status status = dlg->getStatus();
359 
360   if ((old_dlg_status == AmSipDialog::Trying) ||
361       (old_dlg_status == AmSipDialog::Proceeding) ||
362       (old_dlg_status == AmSipDialog::Early)) {
363 
364     if (status == AmSipDialog::Disconnected) {
365 
366       DBG("callee session creation failed. notifying caller session.\n");
367       DBG("this happened with reply: %d.\n", rep.code);
368       relayEvent(new B2ABConnectOtherLegFailedEvent(rep.code, rep.reason));
369 
370     } else if (rep.code == 180) {
371       relayEvent(new B2ABOtherLegRingingEvent());
372     }
373   }
374 }
375 
376 // ----------------------- SessionAudioConnector -----------------
377 
378 void AmSessionAudioConnector::connectSession(AmSession* sess)
379 {
380     const string& tag = sess->getLocalTag();
381 
382     tag_mut.lock();
383 
384     if (connected[0] && tag_sess[0] == tag) {
385       // re-connect to position 0
386       sess->setInOut(&audio_connectors[0],&audio_connectors[1]);
387     } else if (connected[1] && tag_sess[1] == tag) {
388       // re-connect to position 1
389       sess->setInOut(&audio_connectors[1],&audio_connectors[0]);
390     } else if(!connected[0]){
391       // connect to empty position 0
392       connected[0] = true;
393       tag_sess[0] = "";
394       tag_sess[0].append(tag);
395       sess->setInOut(&audio_connectors[0],&audio_connectors[1]);
396     }
397     else if(!connected[1]){
398       // connect to empty position 1
399       connected[1] = true;
400       tag_sess[1] = "";
401       tag_sess[1].append(tag);
402       sess->setInOut(&audio_connectors[1],&audio_connectors[0]);
403     }
404     else {
405 	ERROR("connector full!\n");
406     }
407 
408     tag_mut.unlock();
409 }
410 
411 bool AmSessionAudioConnector::disconnectSession(AmSession* sess)
412 {
413   bool res = true;
414 
415   const string& tag = sess->getLocalTag();
416 
417   tag_mut.lock();
418   if (connected[0] && (tag_sess[0] == tag)) {
419     tag_sess[0].clear();
420     connected[0] = false;
421     sess->setInOut(NULL, NULL);
422     res = connected[1];
423   } else if (connected[1] && (tag_sess[1] == tag)) {
424     tag_sess[1].clear();
425     connected[1] = false;
426     sess->setInOut(NULL, NULL);
427     res = connected[0];
428   } else {
429     DBG("disconnecting from wrong AmSessionAudioConnector\n");
430   }
431   tag_mut.unlock();
432 
433   return res;
434 }
435 
436 /* mark as in use by not owning entity */
437 void AmSessionAudioConnector::block() {
438   released.set(false);
439 }
440 
441 /* mark as released by not owning entity */
442 void AmSessionAudioConnector::release() {
443   released.set(true);
444 }
445 
446 /* wait until released  */
447 void AmSessionAudioConnector::waitReleased() {
448   released.wait_for();
449 }
450 
451