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, &params);
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, &params);
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, &params);
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, &params);
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, &params);
315 
316     delete sip_req;
317     avar.erase(DSM_AVAR_REQUEST);
318 
319     if (checkParam(DSM_PROCESSED, DSM_TRUE, &params)) {
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, &params);
349 
350     delete dsm_reply;
351     avar.erase(DSM_AVAR_REPLY);
352 
353     if (checkParam(DSM_PROCESSED, DSM_TRUE, &params)) {
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, &params);
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, &params);
388 
389   delete dsm_reply;
390   avar.erase(DSM_AVAR_REPLY);
391 
392   if (checkParam(DSM_PROCESSED, DSM_TRUE, &params)) {
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, &params);
405 
406   if (checkParam(DSM_PROCESSED, DSM_TRUE, &params)) {
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, &params);
418 
419   if (checkParam(DSM_PROCESSED, DSM_TRUE, &params)) {
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, &params);
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, &params);
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, &params);
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, &params);
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, &params);
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, &params);
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, &params);
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, &params);
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, &params);
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, &params);
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, &params);
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, &params);
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, &params);
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, &params);
844 
845   avar.erase(DSM_AVAR_REQUEST);
846 
847   return checkParam(DSM_PROCESSED, DSM_TRUE, &params);
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, &params);
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