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