1 /*
2 * Copyright (C) 2008 iptego GmbH
3 * Copyright (C) 2012 Stefan Sayer
4 *
5 * This file is part of SEMS, a free SIP media server.
6 *
7 * SEMS is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version. This program is released under
11 * the GPL with the additional exemption that compiling, linking,
12 * and/or using OpenSSL is allowed.
13 *
14 * For a license to use the SEMS software under conditions
15 * other than those described here, or to purchase support for this
16 * software, please contact iptel.org by e-mail at the following addresses:
17 * info@iptel.org
18 *
19 * SEMS is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 */
28
29 #include "DSMCall.h"
30 #include "AmUtils.h"
31 #include "AmMediaProcessor.h"
32 #include "DSM.h"
33 #include "AmConferenceStatus.h"
34 #include "AmAdvancedAudio.h"
35 #include "AmRingTone.h"
36 #include "AmSipSubscription.h"
37
38 #include "../apps/jsonrpc/JsonRPCEvents.h" // todo!
39
DSMCall(const DSMScriptConfig & config,AmPromptCollection * prompts,DSMStateDiagramCollection & diags,const string & startDiagName,UACAuthCred * credentials)40 DSMCall::DSMCall(const DSMScriptConfig& config,
41 AmPromptCollection* prompts,
42 DSMStateDiagramCollection& diags,
43 const string& startDiagName,
44 UACAuthCred* credentials)
45 :
46 cred(credentials),
47 prompts(prompts), default_prompts(prompts), startDiagName(startDiagName),
48 playlist(this), run_invite_event(config.RunInviteEvent),
49 process_invite(true),
50 process_sessionstart(true), rec_file(NULL)
51 {
52 diags.addToEngine(&engine);
53 set_sip_relay_only(false);
54 }
55
~DSMCall()56 DSMCall::~DSMCall()
57 {
58 for (std::set<DSMDisposable*>::iterator it=
59 gc_trash.begin(); it != gc_trash.end(); it++)
60 delete *it;
61
62 for (vector<AmAudio*>::iterator it=
63 audiofiles.begin();it!=audiofiles.end();it++)
64 delete *it;
65
66 used_prompt_sets.insert(prompts);
67 for (set<AmPromptCollection*>::iterator it=
68 used_prompt_sets.begin(); it != used_prompt_sets.end(); it++)
69 (*it)->cleanup((long)this);
70 }
71
72 /** returns whether var exists && var==value*/
checkVar(const string & var_name,const string & var_val)73 bool DSMCall::checkVar(const string& var_name, const string& var_val) {
74 map<string, string>::iterator it = var.find(var_name);
75 return (it != var.end()) && (it->second == var_val);
76 }
77
getVar(const string & var_name)78 string DSMCall::getVar(const string& var_name) {
79 map<string, string>::iterator it = var.find(var_name);
80 if (it != var.end())
81 return it->second;
82 return "";
83 }
84
85 /** returns whether params, param exists && param==value*/
checkParam(const string & par_name,const string & par_val,map<string,string> * params)86 bool checkParam(const string& par_name, const string& par_val, map<string, string>* params) {
87 if (NULL == params)
88 return false;
89
90 map<string, string>::iterator it = params->find(par_name);
91 return (it != params->end()) && (it->second == par_val);
92 }
93
onStart()94 void DSMCall::onStart()
95 {
96 engine.init(this, this, startDiagName, DSMCondition::Start);
97 }
98
onInvite(const AmSipRequest & req)99 void DSMCall::onInvite(const AmSipRequest& req) {
100 // make B2B dialogs work in onInvite as well
101 invite_req = req;
102
103 if (!process_invite) {
104 // re-INVITEs
105 AmB2BCallerSession::onInvite(req);
106 return;
107 }
108 process_invite = false;
109
110 bool run_session_invite = engine.onInvite(req, this);
111
112 avar[DSM_AVAR_REQUEST] = AmArg((AmObject*)&req);
113
114 DBG("before runEvent(this, this, DSMCondition::Invite);\n");
115 AmSipDialog::Status old_st = dlg->getStatus();
116 engine.runEvent(this, this, DSMCondition::Invite, NULL);
117 avar.erase(DSM_AVAR_REQUEST);
118
119 if ( old_st != dlg->getStatus()
120 //checkVar(DSM_CONNECT_SESSION, DSM_CONNECT_SESSION_FALSE)
121 ) {
122 DBG("session choose to not connect media\n");
123 run_session_invite = false; // don't accept audio
124 }
125
126 if (run_session_invite)
127 AmB2BCallerSession::onInvite(req);
128 }
129
onOutgoingInvite(const string & headers)130 void DSMCall::onOutgoingInvite(const string& headers) {
131 if (!process_invite) {
132 // re-INVITE sent out
133 return;
134 }
135 process_invite = false;
136
137 // TODO: construct correct request of outgoing INVITE
138 AmSipRequest req;
139 req.hdrs = headers;
140
141 if (checkVar(DSM_CONNECT_SESSION, DSM_CONNECT_SESSION_FALSE)) {
142 DBG("session choose to not connect media\n");
143 // TODO: set flag to not connect RTP on session start
144 }
145
146 if (checkVar(DSM_ACCEPT_EARLY_SESSION, DSM_ACCEPT_EARLY_SESSION_FALSE)) {
147 DBG("session choose to not accept early session\n");
148 accept_early_session = false;
149 } else {
150 DBG("session choose to accept early session\n");
151 accept_early_session = true;
152 }
153 }
154
onRinging(const AmSipReply & reply)155 void DSMCall::onRinging(const AmSipReply& reply) {
156 map<string, string> params;
157 params["code"] = int2str(reply.code);
158 params["reason"] = reply.reason;
159 params["has_body"] = reply.body.empty() ?
160 "false" : "true";
161 engine.runEvent(this, this, DSMCondition::Ringing, ¶ms);
162 // todo: local ringbacktone
163 }
164
onEarlySessionStart()165 void DSMCall::onEarlySessionStart() {
166 engine.runEvent(this, this, DSMCondition::EarlySession, NULL);
167
168 if (checkVar(DSM_CONNECT_EARLY_SESSION, DSM_CONNECT_EARLY_SESSION_FALSE)) {
169 DBG("call does not connect early session\n");
170 } else {
171 if (!getInput())
172 setInput(&playlist);
173
174 if (!getOutput())
175 setOutput(&playlist);
176
177 AmB2BCallerSession::onEarlySessionStart();
178 }
179 }
180
onSessionStart()181 void DSMCall::onSessionStart()
182 {
183 if (process_sessionstart) {
184 process_sessionstart = false;
185
186 DBG("DSMCall::onSessionStart\n");
187 startSession();
188 }
189
190 AmB2BCallerSession::onSessionStart();
191 }
192
onSdpCompleted(const AmSdp & offer,const AmSdp & answer)193 int DSMCall::onSdpCompleted(const AmSdp& offer, const AmSdp& answer)
194 {
195 AmMimeBody* sdp_body = invite_req.body.hasContentType(SIP_APPLICATION_SDP);
196 if(!sdp_body) {
197 sdp_body = invite_req.body.addPart(SIP_APPLICATION_SDP);
198 }
199
200 if(sdp_body) {
201 string sdp_buf;
202 answer.print(sdp_buf);
203 sdp_body->setPayload((const unsigned char*)sdp_buf.c_str(),
204 sdp_buf.length());
205 }
206
207 return AmB2BCallerSession::onSdpCompleted(offer,answer);
208 }
209
getSdpOffer(AmSdp & offer)210 bool DSMCall::getSdpOffer(AmSdp& offer)
211 {
212 if (!AmB2BCallerSession::getSdpOffer(offer)) {
213 return false;
214 }
215
216 engine.processSdpOffer(offer);
217 return true;
218 }
219
getSdpAnswer(const AmSdp & offer,AmSdp & answer)220 bool DSMCall::getSdpAnswer(const AmSdp& offer, AmSdp& answer)
221 {
222 if (!AmB2BCallerSession::getSdpAnswer(offer, answer)) {
223 return false;
224 }
225
226 engine.processSdpAnswer(offer, answer);
227 return true;
228 }
229
startSession()230 void DSMCall::startSession(){
231
232 engine.runEvent(this, this, DSMCondition::SessionStart, NULL);
233 setReceiving(true);
234
235 if (!checkVar(DSM_CONNECT_SESSION, DSM_CONNECT_SESSION_FALSE)) {
236 if (!getInput())
237 setInput(&playlist);
238
239 setOutput(&playlist);
240 }
241 }
242
connectMedia()243 void DSMCall::connectMedia() {
244 if (!getInput())
245 setInput(&playlist);
246
247 setOutput(&playlist);
248 AmMediaProcessor::instance()->addSession(this, callgroup);
249 }
250
disconnectMedia()251 void DSMCall::disconnectMedia() {
252 AmMediaProcessor::instance()->removeSession(this);
253 }
254
mute()255 void DSMCall::mute() {
256 setMute(true);
257 }
258
unmute()259 void DSMCall::unmute() {
260 setMute(false);
261 }
262
263
onDtmf(int event,int duration_msec)264 void DSMCall::onDtmf(int event, int duration_msec) {
265 DBG("* Got DTMF key %d duration %d\n",
266 event, duration_msec);
267
268 map<string, string> params;
269 params["key"] = int2str(event);
270 params["duration"] = int2str(duration_msec);
271 engine.runEvent(this, this, DSMCondition::Key, ¶ms);
272 }
273
onBye(const AmSipRequest & req)274 void DSMCall::onBye(const AmSipRequest& req)
275 {
276 DBG("onBye\n");
277 map<string, string> params;
278 params["headers"] = req.hdrs;
279
280 engine.runEvent(this, this, DSMCondition::Hangup, ¶ms);
281
282 clearRtpReceiverRelay();
283 }
284
onCancel(const AmSipRequest & cancel)285 void DSMCall::onCancel(const AmSipRequest& cancel) {
286 DBG("onCancel\n");
287 if (dlg->getStatus() < AmSipDialog::Connected) {
288 //TODO: pass the cancel request as a parameter?
289 DBG("hangup event!!!\n");
290 map<string, string> params;
291 params["headers"] = cancel.hdrs;
292 engine.runEvent(this, this, DSMCondition::Hangup, ¶ms);
293 }
294 else {
295 DBG("ignoring onCancel event in established dialog\n");
296 }
297 }
298
onSipRequest(const AmSipRequest & req)299 void DSMCall::onSipRequest(const AmSipRequest& req) {
300
301 if (checkVar(DSM_ENABLE_REQUEST_EVENTS, DSM_TRUE)) {
302 map<string, string> params;
303 params["method"] = req.method;
304 params["r_uri"] = req.r_uri;
305 params["from"] = req.from;
306 params["to"] = req.to;
307 params["hdrs"] = req.hdrs;
308 params["cseq"] = int2str(req.cseq);
309
310 // pass AmSipRequest for use by mod_dlg
311 DSMSipRequest* sip_req = new DSMSipRequest(&req);
312 avar[DSM_AVAR_REQUEST] = AmArg(sip_req);
313
314 engine.runEvent(this, this, DSMCondition::SipRequest, ¶ms);
315
316 delete sip_req;
317 avar.erase(DSM_AVAR_REQUEST);
318
319 if (checkParam(DSM_PROCESSED, DSM_TRUE, ¶ms)) {
320 DBG("DSM script processed SIP request '%s', returning\n",
321 req.method.c_str());
322 return;
323 }
324 }
325
326 AmB2BCallerSession::onSipRequest(req);
327 }
328
onSipReply(const AmSipRequest & req,const AmSipReply & reply,AmBasicSipDialog::Status old_dlg_status)329 void DSMCall::onSipReply(const AmSipRequest& req,
330 const AmSipReply& reply,
331 AmBasicSipDialog::Status old_dlg_status)
332 {
333
334 if (checkVar(DSM_ENABLE_REPLY_EVENTS, DSM_TRUE)) {
335 map<string, string> params;
336 params["code"] = int2str(reply.code);
337 params["reason"] = reply.reason;
338 params["hdrs"] = reply.hdrs;
339 params["cseq"] = int2str(reply.cseq);
340
341 params["dlg_status"] = dlg->getStatusStr();
342 params["old_dlg_status"] = AmBasicSipDialog::getStatusStr(old_dlg_status);
343
344 // pass AmSipReply for use by mod_dlg (? sending ACK?)
345 DSMSipReply* dsm_reply = new DSMSipReply(&reply);
346 avar[DSM_AVAR_REPLY] = AmArg(dsm_reply);
347
348 engine.runEvent(this, this, DSMCondition::SipReply, ¶ms);
349
350 delete dsm_reply;
351 avar.erase(DSM_AVAR_REPLY);
352
353 if (checkParam(DSM_PROCESSED, DSM_TRUE, ¶ms)) {
354 DBG("DSM script processed SIP reply '%u %s', returning\n",
355 reply.code, reply.reason.c_str());
356 return;
357 }
358 }
359
360 AmB2BCallerSession::onSipReply(req, reply, old_dlg_status);
361
362 if ((old_dlg_status < AmSipDialog::Connected) &&
363 (dlg->getStatus() == AmSipDialog::Disconnected)) {
364 DBG("Outbound call failed with reply %d %s.\n",
365 reply.code, reply.reason.c_str());
366 map<string, string> params;
367 params["code"] = int2str(reply.code);
368 params["reason"] = reply.reason;
369 engine.runEvent(this, this, DSMCondition::FailedCall, ¶ms);
370 setStopped();
371 }
372 }
373
onRemoteDisappeared(const AmSipReply & reply)374 void DSMCall::onRemoteDisappeared(const AmSipReply& reply) {
375 map<string, string> params;
376 params["code"] = int2str(reply.code);
377 params["reason"] = reply.reason;
378 params["hdrs"] = reply.hdrs;
379 params["cseq"] = int2str(reply.cseq);
380
381 params["dlg_status"] = dlg->getStatusStr();
382
383 // pass AmSipReply for use by modules
384 DSMSipReply* dsm_reply = new DSMSipReply(&reply);
385 avar[DSM_AVAR_REPLY] = AmArg(dsm_reply);
386
387 engine.runEvent(this, this, DSMCondition::RemoteDisappeared, ¶ms);
388
389 delete dsm_reply;
390 avar.erase(DSM_AVAR_REPLY);
391
392 if (checkParam(DSM_PROCESSED, DSM_TRUE, ¶ms)) {
393 DBG("DSM script processed SIP onRemoteDisappeared reply '%u %s', returning\n",
394 reply.code, reply.reason.c_str());
395 return;
396 }
397
398 AmB2BCallerSession::onRemoteDisappeared(reply);
399 }
400
onSessionTimeout()401 void DSMCall::onSessionTimeout() {
402 map<string, string> params;
403
404 engine.runEvent(this, this, DSMCondition::SessionTimeout, ¶ms);
405
406 if (checkParam(DSM_PROCESSED, DSM_TRUE, ¶ms)) {
407 DBG("DSM script processed onSessionTimeout, returning\n");
408 return;
409 }
410
411 AmB2BCallerSession::onSessionTimeout();
412 }
413
onRtpTimeout()414 void DSMCall::onRtpTimeout() {
415 map<string, string> params;
416
417 engine.runEvent(this, this, DSMCondition::RtpTimeout, ¶ms);
418
419 if (checkParam(DSM_PROCESSED, DSM_TRUE, ¶ms)) {
420 DBG("DSM script processed onRtpTimeout, returning\n");
421 return;
422 }
423
424 AmB2BCallerSession::onRtpTimeout();
425 }
426
onNoAck(unsigned int cseq)427 void DSMCall::onNoAck(unsigned int cseq)
428 {
429 DBG("onNoAck\n");
430 map<string, string> params;
431 params["headers"] = "";
432 params["reason"] = "onNoAck";
433
434 engine.runEvent(this, this, DSMCondition::Hangup, ¶ms);
435 AmB2BCallerSession::onNoAck(cseq);
436 }
437
onSystemEvent(AmSystemEvent * ev)438 void DSMCall::onSystemEvent(AmSystemEvent* ev) {
439 map<string, string> params;
440 params["type"] = AmSystemEvent::getDescription(ev->sys_event);
441 engine.runEvent(this, this, DSMCondition::System, ¶ms);
442 if (params["processed"] != DSM_TRUE) {
443 AmB2BCallerSession::onSystemEvent(ev);
444 }
445 }
446
onBeforeDestroy()447 void DSMCall::onBeforeDestroy() {
448 map<string, string> params;
449 engine.runEvent(this, this, DSMCondition::BeforeDestroy, ¶ms);
450
451 engine.onBeforeDestroy(this, this);
452 }
453
454 #ifdef WITH_ZRTP
onZRTPProtocolEvent(zrtp_protocol_event_t event,zrtp_stream_t * stream_ctx)455 void DSMCall::onZRTPProtocolEvent(zrtp_protocol_event_t event, zrtp_stream_t *stream_ctx) {
456 DBG("DSMCall::onZRTPProtocolEvent: %s\n", zrtp_protocol_event_desc(event));
457
458 if (checkVar(DSM_ENABLE_ZRTP_EVENTS, DSM_TRUE)) {
459 map<string, string> params;
460 params["event"] = zrtp_protocol_event_desc(event);
461 params["event_id"] = int2str(event);
462 engine.runEvent(this, this, DSMCondition::ZRTPProtocolEvent, ¶ms);
463 }
464
465 }
466
onZRTPSecurityEvent(zrtp_security_event_t event,zrtp_stream_t * stream_ctx)467 void DSMCall::onZRTPSecurityEvent(zrtp_security_event_t event, zrtp_stream_t *stream_ctx) {
468 DBG("DSMCall::onZRTPSecurityEvent: %s\n", zrtp_security_event_desc(event));
469
470 if (checkVar(DSM_ENABLE_ZRTP_EVENTS, DSM_TRUE)) {
471 map<string, string> params;
472 params["event"] = zrtp_security_event_desc(event);
473 params["event_id"] = int2str(event);
474 engine.runEvent(this, this, DSMCondition::ZRTPSecurityEvent, ¶ms);
475 }
476
477 }
478 #endif
479
process(AmEvent * event)480 void DSMCall::process(AmEvent* event)
481 {
482
483 DBG("DSMCall::process\n");
484
485 if (event->event_id == DSM_EVENT_ID) {
486 DSMEvent* dsm_event = dynamic_cast<DSMEvent*>(event);
487 if (dsm_event) {
488 engine.runEvent(this, this, DSMCondition::DSMEvent, &dsm_event->params);
489 return;
490 }
491 }
492
493 AmAudioEvent* audio_event = dynamic_cast<AmAudioEvent*>(event);
494 if(audio_event &&
495 ((audio_event->event_id == AmAudioEvent::cleared) ||
496 (audio_event->event_id == AmAudioEvent::noAudio))){
497 map<string, string> params;
498 params["type"] = audio_event->event_id == AmAudioEvent::cleared?"cleared":"noAudio";
499 engine.runEvent(this, this, DSMCondition::NoAudio, ¶ms);
500 return;
501 }
502
503 AmPluginEvent* plugin_event = dynamic_cast<AmPluginEvent*>(event);
504 if(plugin_event && plugin_event->name == "timer_timeout") {
505 int timer_id = plugin_event->data.get(0).asInt();
506 map<string, string> params;
507 params["id"] = int2str(timer_id);
508 engine.runEvent(this, this, DSMCondition::Timer, ¶ms);
509 }
510
511 AmPlaylistSeparatorEvent* sep_ev = dynamic_cast<AmPlaylistSeparatorEvent*>(event);
512 if (sep_ev) {
513 map<string, string> params;
514 params["id"] = int2str(sep_ev->event_id);
515 engine.runEvent(this, this, DSMCondition::PlaylistSeparator, ¶ms);
516 }
517
518 ConferenceEvent * conf_ev = dynamic_cast<ConferenceEvent*>(event);
519 if (conf_ev) {
520 map<string, string> params;
521 params["type"] = "conference_event";
522 params["id"] = int2str(conf_ev->event_id);
523 engine.runEvent(this, this, DSMCondition::DSMEvent, ¶ms);
524 }
525
526 // todo: give modules the possibility to define/process events
527 JsonRpcEvent* jsonrpc_ev = dynamic_cast<JsonRpcEvent*>(event);
528 if (jsonrpc_ev) {
529 DBG("received jsonrpc event\n");
530
531 JsonRpcResponseEvent* resp_ev =
532 dynamic_cast<JsonRpcResponseEvent*>(jsonrpc_ev);
533 if (resp_ev) {
534 map<string, string> params;
535 params["ev_type"] = "JsonRpcResponse";
536 params["id"] = resp_ev->response.id;
537 params["is_error"] = resp_ev->response.is_error ?
538 "true":"false";
539
540 // decode result for easy use from script
541 varPrintArg(resp_ev->response.data, params, resp_ev->response.is_error ? "error": "result");
542
543 // decode udata for easy use from script
544 varPrintArg(resp_ev->udata, params, "udata");
545
546 // save reference to full parameters as avar
547 avar[DSM_AVAR_JSONRPCRESPONSEDATA] = AmArg(&resp_ev->response.data);
548 avar[DSM_AVAR_JSONRPCRESPONSEUDATA] = AmArg(&resp_ev->udata);
549
550 engine.runEvent(this, this, DSMCondition::JsonRpcResponse, ¶ms);
551
552 avar.erase(DSM_AVAR_JSONRPCRESPONSEUDATA);
553 avar.erase(DSM_AVAR_JSONRPCRESPONSEDATA);
554 return;
555 }
556
557 JsonRpcRequestEvent* req_ev =
558 dynamic_cast<JsonRpcRequestEvent*>(jsonrpc_ev);
559 if (req_ev) {
560 map<string, string> params;
561 params["ev_type"] = "JsonRpcRequest";
562 params["is_notify"] = req_ev->isNotification() ?
563 "true" : "false";
564 params["method"] = req_ev->method;
565 if (!req_ev->id.empty())
566 params["id"] = req_ev->id;
567
568 // decode request params result for easy use from script
569 varPrintArg(req_ev->params, params, "params");
570
571 // save reference to full parameters
572 avar[DSM_AVAR_JSONRPCREQUESTDATA] = AmArg(&req_ev->params);
573
574 engine.runEvent(this, this, DSMCondition::JsonRpcRequest, ¶ms);
575
576 avar.erase(DSM_AVAR_JSONRPCREQUESTDATA);
577 return;
578 }
579
580 }
581
582 if (event->event_id == E_SIP_SUBSCRIPTION) {
583 SIPSubscriptionEvent* sub_ev = dynamic_cast<SIPSubscriptionEvent*>(event);
584 if (sub_ev) {
585 DBG("DSM Call received SIP Subscription Event\n");
586 map<string, string> params;
587 params["status"] = sub_ev->getStatusText();
588 params["code"] = int2str(sub_ev->code);
589 params["reason"] = sub_ev->reason;
590 params["expires"] = int2str(sub_ev->expires);
591 params["has_body"] = sub_ev->notify_body.get()?"true":"false";
592 if (sub_ev->notify_body.get()) {
593 avar[DSM_AVAR_SIPSUBSCRIPTION_BODY] = AmArg(sub_ev->notify_body.get());
594 }
595 engine.runEvent(this, this, DSMCondition::SIPSubscription, ¶ms);
596 avar.erase(DSM_AVAR_SIPSUBSCRIPTION_BODY);
597 }
598 }
599
600 AmRtpTimeoutEvent* timeout_ev = dynamic_cast<AmRtpTimeoutEvent*>(event);
601 if (timeout_ev) {
602 map<string, string> params;
603 params["type"] = "rtp_timeout";
604 params["timeout_value"] = int2str(AmConfig::DeadRtpTime);
605 engine.runEvent(this, this, DSMCondition::RTPTimeout, ¶ms);
606 return;
607 }
608
609 if (event->event_id == E_B2B_APP) {
610 B2BEvent* b2b_ev = dynamic_cast<B2BEvent*>(event);
611 if(b2b_ev && b2b_ev->ev_type == B2BEvent::B2BApplication) {
612 engine.runEvent(this, this, DSMCondition::B2BEvent, &b2b_ev->params);
613 return;
614 }
615 }
616
617 AmB2BCallerSession::process(event);
618 }
619
getCredentials()620 inline UACAuthCred* DSMCall::getCredentials() {
621 return cred.get();
622 }
623
playPrompt(const string & name,bool loop,bool front)624 void DSMCall::playPrompt(const string& name, bool loop, bool front) {
625 DBG("playing prompt '%s'\n", name.c_str());
626 if (prompts->addToPlaylist(name, (long)this, playlist,
627 front, loop)) {
628 if ((var["prompts.default_fallback"] != "yes") ||
629 default_prompts->addToPlaylist(name, (long)this, playlist,
630 front, loop)) {
631 DBG("checked [%p]\n", default_prompts);
632 throw DSMException("prompt", "name", name);
633 } else {
634 used_prompt_sets.insert(default_prompts);
635 CLR_ERRNO;
636 }
637 } else {
638 CLR_ERRNO;
639 }
640 }
641
flushPlaylist()642 void DSMCall::flushPlaylist() {
643 DBG("flush playlist\n");
644 playlist.flush();
645 }
646
addToPlaylist(AmPlaylistItem * item,bool front)647 void DSMCall::addToPlaylist(AmPlaylistItem* item, bool front) {
648 DBG("add item to playlist\n");
649 if (front)
650 playlist.addToPlayListFront(item);
651 else
652 playlist.addToPlaylist(item);
653 }
654
playFile(const string & name,bool loop,bool front)655 void DSMCall::playFile(const string& name, bool loop, bool front) {
656 AmAudioFile* af = new AmAudioFile();
657 if(af->open(name,AmAudioFile::Read)) {
658 ERROR("audio file '%s' could not be opened for reading.\n",
659 name.c_str());
660 delete af;
661
662 throw DSMException("file", "path", name);
663
664 return;
665 }
666 if (loop)
667 af->loop.set(true);
668
669 if (front)
670 playlist.addToPlayListFront(new AmPlaylistItem(af, NULL));
671 else
672 playlist.addToPlaylist(new AmPlaylistItem(af, NULL));
673
674 audiofiles.push_back(af);
675 CLR_ERRNO;
676 }
677
playSilence(unsigned int length,bool front)678 void DSMCall::playSilence(unsigned int length, bool front) {
679 AmNullAudio* af = new AmNullAudio();
680 af->setReadLength(length);
681 if (front)
682 playlist.addToPlayListFront(new AmPlaylistItem(af, NULL));
683 else
684 playlist.addToPlaylist(new AmPlaylistItem(af, NULL));
685
686 audiofiles.push_back(af);
687 CLR_ERRNO;
688 }
689
playRingtone(int length,int on,int off,int f,int f2,bool front)690 void DSMCall::playRingtone(int length, int on, int off, int f, int f2, bool front) {
691 AmRingTone* af = new AmRingTone(length, on, off, f, f2);
692 if (front)
693 playlist.addToPlayListFront(new AmPlaylistItem(af, NULL));
694 else
695 playlist.addToPlaylist(new AmPlaylistItem(af, NULL));
696
697 audiofiles.push_back(af);
698 CLR_ERRNO;
699 }
700
recordFile(const string & name)701 void DSMCall::recordFile(const string& name) {
702 if (rec_file)
703 stopRecord();
704
705 DBG("start record to '%s'\n", name.c_str());
706 rec_file = new AmAudioFile();
707 if(rec_file->open(name,AmAudioFile::Write)) {
708 ERROR("audio file '%s' could not be opened for recording.\n",
709 name.c_str());
710 delete rec_file;
711 rec_file = NULL;
712 throw DSMException("file", "path", name);
713 return;
714 }
715 setInput(rec_file);
716 CLR_ERRNO;
717 }
718
getRecordLength()719 unsigned int DSMCall::getRecordLength() {
720 if (!rec_file) {
721 SET_ERRNO(DSM_ERRNO_SCRIPT);
722 SET_STRERROR("getRecordLength used while not recording.");
723 return 0;
724 }
725 CLR_ERRNO;
726 return rec_file->getLength();
727 }
728
getRecordDataSize()729 unsigned int DSMCall::getRecordDataSize() {
730 if (!rec_file) {
731 SET_ERRNO(DSM_ERRNO_SCRIPT);
732 SET_STRERROR("getRecordDataSize used while not recording.");
733 return 0;
734 }
735 CLR_ERRNO;
736 return rec_file->getDataSize();
737 }
738
stopRecord()739 void DSMCall::stopRecord() {
740 if (rec_file) {
741 setInput(&playlist);
742 rec_file->close();
743 delete rec_file;
744 rec_file = NULL;
745 CLR_ERRNO;
746 } else {
747 WARN("stopRecord: we are not recording\n");
748 SET_ERRNO(DSM_ERRNO_SCRIPT);
749 SET_STRERROR("stopRecord used while not recording.");
750 return;
751 }
752 }
753
setInOutPlaylist()754 void DSMCall::setInOutPlaylist() {
755 DBG("setting playlist as input and output\n");
756 setInOut(&playlist, &playlist);
757 }
758
setInputPlaylist()759 void DSMCall::setInputPlaylist() {
760 DBG("setting playlist as input\n");
761 setInput(&playlist);
762 }
763
setOutputPlaylist()764 void DSMCall::setOutputPlaylist() {
765 DBG("setting playlist as output\n");
766 setOutput(&playlist);
767 }
768
addPromptSet(const string & name,AmPromptCollection * prompt_set)769 void DSMCall::addPromptSet(const string& name,
770 AmPromptCollection* prompt_set) {
771 if (prompt_set) {
772 DBG("adding prompt set '%s'\n", name.c_str());
773 prompt_sets[name] = prompt_set;
774 CLR_ERRNO;
775 } else {
776 ERROR("trying to add NULL prompt set\n");
777 SET_ERRNO(DSM_ERRNO_INTERNAL);
778 SET_STRERROR("trying to add NULL prompt set\n");
779 }
780 }
781
setPromptSets(map<string,AmPromptCollection * > & new_prompt_sets)782 void DSMCall::setPromptSets(map<string, AmPromptCollection*>&
783 new_prompt_sets) {
784 prompt_sets = new_prompt_sets;
785 }
786
setPromptSet(const string & name)787 void DSMCall::setPromptSet(const string& name) {
788 map<string, AmPromptCollection*>::iterator it =
789 prompt_sets.find(name);
790
791 if (it == prompt_sets.end()) {
792 ERROR("prompt set %s unknown\n", name.c_str());
793 throw DSMException("prompt", "name", name);
794 return;
795 }
796
797 DBG("setting prompt set '%s'\n", name.c_str());
798 used_prompt_sets.insert(prompts);
799 prompts = it->second;
800 CLR_ERRNO;
801 }
802
803
addSeparator(const string & name,bool front)804 void DSMCall::addSeparator(const string& name, bool front) {
805 unsigned int id = 0;
806 if (str2i(name, id)) {
807 SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
808 SET_STRERROR("separator id '"+name+"' not a number");
809 return;
810 }
811
812 AmPlaylistSeparator* sep = new AmPlaylistSeparator(this, id);
813 if (front)
814 playlist.addToPlayListFront(new AmPlaylistItem(sep, sep));
815 else
816 playlist.addToPlaylist(new AmPlaylistItem(sep, sep));
817 // for garbage collector
818 audiofiles.push_back(sep);
819 CLR_ERRNO;
820 }
821
transferOwnership(DSMDisposable * d)822 void DSMCall::transferOwnership(DSMDisposable* d) {
823 if (d == NULL)
824 return;
825 gc_trash.insert(d);
826 }
827
releaseOwnership(DSMDisposable * d)828 void DSMCall::releaseOwnership(DSMDisposable* d) {
829 if (d == NULL)
830 return;
831 gc_trash.erase(d);
832 }
833
834 // AmB2BSession methods
onOtherBye(const AmSipRequest & req)835 bool DSMCall::onOtherBye(const AmSipRequest& req) {
836 DBG("* Got BYE from other leg\n");
837
838 DSMSipRequest sip_req(&req);
839 avar[DSM_AVAR_REQUEST] = AmArg((AmObject*)&sip_req);
840
841 map<string, string> params;
842 params["hdrs"] = req.hdrs; // todo: optimization - make this configurable
843 engine.runEvent(this, this, DSMCondition::B2BOtherBye, ¶ms);
844
845 avar.erase(DSM_AVAR_REQUEST);
846
847 return checkParam(DSM_PROCESSED, DSM_TRUE, ¶ms);
848 }
849
onOtherReply(const AmSipReply & reply)850 bool DSMCall::onOtherReply(const AmSipReply& reply) {
851 DBG("* Got reply from other leg: %u %s\n",
852 reply.code, reply.reason.c_str());
853
854 map<string, string> params;
855 params["code"] = int2str(reply.code);
856 params["reason"] = reply.reason;
857 params["hdrs"] = reply.hdrs; // todo: optimization - make this configurable
858
859 engine.runEvent(this, this, DSMCondition::B2BOtherReply, ¶ms);
860
861 return false;
862 }
863
B2BterminateOtherLeg()864 void DSMCall::B2BterminateOtherLeg() {
865 terminateOtherLeg();
866 }
867
B2BconnectCallee(const string & remote_party,const string & remote_uri,bool relayed_invite)868 void DSMCall::B2BconnectCallee(const string& remote_party,
869 const string& remote_uri,
870 bool relayed_invite) {
871 connectCallee(remote_party, remote_uri, relayed_invite);
872 }
873
newCalleeSession()874 AmB2BCalleeSession* DSMCall::newCalleeSession() {
875 DSMCallCalleeSession* s = new DSMCallCalleeSession(this);
876 s->dlg->setLocalParty(getVar(DSM_B2B_LOCAL_PARTY));
877 s->dlg->setLocalUri(getVar(DSM_B2B_LOCAL_URI));
878
879 string user = getVar(DSM_B2B_AUTH_USER);
880 string pwd = getVar(DSM_B2B_AUTH_PWD);
881 if (!user.empty() && !pwd.empty()) {
882 s->setCredentials("", user, pwd);
883
884 // adding auth handler
885 AmSessionEventHandlerFactory* uac_auth_f =
886 AmPlugIn::instance()->getFactory4Seh("uac_auth");
887 if (NULL == uac_auth_f) {
888 INFO("uac_auth module not loaded. uac auth NOT enabled for B2B b leg in DSM.\n");
889 } else {
890 AmSessionEventHandler* h = uac_auth_f->getHandler(s);
891
892 // we cannot use the generic AmSessionEventHandler hooks,
893 // because the hooks don't work in AmB2BSession
894 s->setAuthHandler(h);
895 DBG("uac auth enabled for DSM callee session.\n");
896 }
897 }
898
899 s->dlg->setCallid(getVar(DSM_B2B_CALLID));
900
901 return s;
902 }
903
B2BaddReceivedRequest(const AmSipRequest & req)904 void DSMCall::B2BaddReceivedRequest(const AmSipRequest& req) {
905 DBG("inserting request '%s' with CSeq %d in list of received requests\n",
906 req.method.c_str(), req.cseq);
907 recvd_req.insert(std::make_pair(req.cseq, req));
908 }
909
B2BsetRelayEarlyMediaSDP(bool enabled)910 void DSMCall::B2BsetRelayEarlyMediaSDP(bool enabled) {
911 set_sip_relay_early_media_sdp(enabled);
912 }
913
B2BsetHeaders(const string & hdr,bool replaceCRLF)914 void DSMCall::B2BsetHeaders(const string& hdr, bool replaceCRLF) {
915 if (!replaceCRLF) {
916 invite_req.hdrs = hdr;
917 } else {
918 string hdr_crlf = hdr;
919 DBG("hdr_crlf is '%s'\n", hdr_crlf.c_str());
920
921 while (true) {
922 size_t p = hdr_crlf.find("\\r\\n");
923 if (p==string::npos)
924 break;
925 hdr_crlf.replace(p, 4, "\r\n");
926 }
927 DBG("-> hdr_crlf is '%s'\n", hdr_crlf.c_str());
928 invite_req.hdrs += hdr_crlf;
929 }
930 // add \r\n if not in header
931 if (invite_req.hdrs.length()>2 &&
932 invite_req.hdrs.substr(invite_req.hdrs.length()-2) != "\r\n")
933 invite_req.hdrs+="\r\n";
934 }
935
B2BaddHeader(const string & hdr)936 void DSMCall::B2BaddHeader(const string& hdr) {
937 invite_req.hdrs +=hdr;
938 // add \r\n if not in header
939 if (invite_req.hdrs.length()>2 &&
940 invite_req.hdrs.substr(invite_req.hdrs.length()-2) != "\r\n")
941 invite_req.hdrs+="\r\n";
942 }
943
B2BclearHeaders()944 void DSMCall::B2BclearHeaders() {
945 invite_req.hdrs.clear();
946 }
947
B2BremoveHeader(const string & hdr)948 void DSMCall::B2BremoveHeader(const string& hdr) {
949 removeHeader(invite_req.hdrs, hdr);
950 }
951
952 /* --- B2B second leg -------------------------------------------------- */
953
DSMCallCalleeSession(const string & other_local_tag)954 DSMCallCalleeSession::DSMCallCalleeSession(const string& other_local_tag)
955 : AmB2BCalleeSession(other_local_tag) {
956 }
957
DSMCallCalleeSession(const AmB2BCallerSession * caller)958 DSMCallCalleeSession::DSMCallCalleeSession(const AmB2BCallerSession* caller)
959 : AmB2BCalleeSession(caller) {
960 }
961
setCredentials(const string & realm,const string & user,const string & pwd)962 void DSMCallCalleeSession::setCredentials(const string& realm,
963 const string& user,
964 const string& pwd) {
965 cred.reset(new UACAuthCred(realm, user, pwd));
966 }
967
getCredentials()968 UACAuthCred* DSMCallCalleeSession::getCredentials() {
969 return cred.get();
970 }
971
setAuthHandler(AmSessionEventHandler * h)972 void DSMCallCalleeSession::setAuthHandler(AmSessionEventHandler* h) {
973 auth.reset(h);
974 }
975
onSendRequest(AmSipRequest & req,int & flags)976 void DSMCallCalleeSession::onSendRequest(AmSipRequest& req, int& flags)
977 {
978 if (NULL != auth.get()) {
979 DBG("auth->onSendRequest cseq = %d\n", req.cseq);
980 auth->onSendRequest(req, flags);
981 }
982
983 AmB2BCalleeSession::onSendRequest(req, flags);
984 }
985
onSipReply(const AmSipRequest & req,const AmSipReply & reply,AmBasicSipDialog::Status old_dlg_status)986 void DSMCallCalleeSession::onSipReply(const AmSipRequest& req, const AmSipReply& reply,
987 AmBasicSipDialog::Status old_dlg_status)
988 {
989 // call event handlers where it is not done
990 TransMap::iterator t = relayed_req.find(reply.cseq);
991 bool fwd = t != relayed_req.end();
992 DBG("onSipReply: %i %s (fwd=%i)\n",reply.code,reply.reason.c_str(),fwd);
993 DBG("onSipReply: content-type = %s\n",reply.body.getCTStr().c_str());
994 if(fwd) {
995 CALL_EVENT_H(onSipReply, req, reply, old_dlg_status);
996 }
997
998
999 if (NULL == auth.get()) {
1000 AmB2BCalleeSession::onSipReply(req, reply, old_dlg_status);
1001 return;
1002 }
1003
1004 unsigned int cseq_before = dlg->cseq;
1005 if (!auth->onSipReply(req, reply, old_dlg_status)) {
1006 AmB2BCalleeSession::onSipReply(req, reply, old_dlg_status);
1007 } else {
1008 if (cseq_before != dlg->cseq) {
1009 DBG("uac_auth consumed reply with cseq %d and resent with cseq %d; "
1010 "updating relayed_req map\n", reply.cseq, cseq_before);
1011 updateUACTransCSeq(reply.cseq, cseq_before);
1012 }
1013 }
1014 }
1015