1 /*
2  * Copyright (C) 2008 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. This program is released under
10  * the GPL with the additional exemption that compiling, linking,
11  * and/or using OpenSSL is allowed.
12  *
13  * For a license to use the SEMS software under conditions
14  * other than those described here, or to purchase support for this
15  * software, please contact iptel.org by e-mail at the following addresses:
16  *    info@iptel.org
17  *
18  * SEMS is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27 #include "ModDlg.h"
28 #include "log.h"
29 #include "AmUtils.h"
30 
31 #include "DSMSession.h"
32 #include "AmSession.h"
33 #include "AmB2BSession.h"
34 #include <string.h>
35 #include "AmSipHeaders.h"
36 
37 #include "AmUAC.h"
38 #include "ampi/UACAuthAPI.h"
39 
40 SC_EXPORT(MOD_CLS_NAME);
41 
42 
MOD_ACTIONEXPORT_BEGIN(MOD_CLS_NAME)43 MOD_ACTIONEXPORT_BEGIN(MOD_CLS_NAME) {
44 
45   DEF_CMD("dlg.reply", DLGReplyAction);
46   DEF_CMD("dlg.replyRequest", DLGReplyRequestAction);
47   DEF_CMD("dlg.acceptInvite", DLGAcceptInviteAction);
48   DEF_CMD("dlg.bye", DLGByeAction);
49   DEF_CMD("dlg.connectCalleeRelayed", DLGConnectCalleeRelayedAction);
50   DEF_CMD("dlg.dialout", DLGDialoutAction);
51 
52   DEF_CMD("dlg.getRequestBody", DLGGetRequestBodyAction)
53   DEF_CMD("dlg.getReplyBody", DLGGetReplyBodyAction)
54 
55   DEF_CMD("dlg.getOtherId", DLGGetOtherIdAction)
56   DEF_CMD("dlg.getRtpRelayMode", DLGGetRtpRelayModeAction)
57 
58   DEF_CMD("dlg.refer", DLGReferAction);
59   DEF_CMD("dlg.info", DLGInfoAction);
60   DEF_CMD("dlg.relayError", DLGB2BRelayErrorAction);
61   DEF_CMD("dlg.relayReply", DLGB2BRelayErrorAction); // alias
62 
63   DEF_CMD("dlg.addReplyBodyPart", DLGAddReplyBodyPartAction);
64   DEF_CMD("dlg.deleteReplyBodyPart", DLGDeleteReplyBodyPartAction);
65 
66 } MOD_ACTIONEXPORT_END;
67 
68 //MOD_CONDITIONEXPORT_NONE(MOD_CLS_NAME);
69 
70 
MOD_CONDITIONEXPORT_BEGIN(MOD_CLS_NAME)71 MOD_CONDITIONEXPORT_BEGIN(MOD_CLS_NAME) {
72   if (cmd == "dlg.replyHasContentType")
73     return new DLGReplyHasContentTypeCondition(params, false);
74 
75   if (cmd == "dlg.requestHasContentType")
76     return new DLGRequestHasContentTypeCondition(params, false);
77 
78 } MOD_CONDITIONEXPORT_END;
79 
onInvite(const AmSipRequest & req,DSMSession * sess)80 bool DLGModule::onInvite(const AmSipRequest& req, DSMSession* sess) {
81   // save inivital invite to last_req
82   // todo: save this in avar
83  sess->last_req.reset(new AmSipRequest(req));
84  return true;
85 }
86 
replaceLineEnds(string input)87 string replaceLineEnds(string input)
88 {
89   string result;
90   size_t last = 0;
91   size_t pos;
92   while ((pos = input.find("\\r\\n", last)) != string::npos) {
93     result += input.substr(last, pos-last);
94     result += "\r\n";
95     last = pos + 4;
96   }
97   if (!input.substr(last).empty()) {
98     result += input.substr(last);
99     result += "\r\n";
100   }
101   return result;
102 }
103 
104 // todo: convert errors to exceptions
replyRequest(DSMSession * sc_sess,AmSession * sess,EventParamT * event_params,const string & par1,const string & par2,const AmSipRequest & req)105 void replyRequest(DSMSession* sc_sess, AmSession* sess,
106 		  EventParamT* event_params,
107 		  const string& par1, const string& par2,
108 		  const AmSipRequest& req) {
109   string code = resolveVars(par1, sess, sc_sess, event_params);
110   string reason = resolveVars(par2, sess, sc_sess, event_params);
111   string hdrs = replaceLineEnds(resolveVars("$dlg.reply.hdrs", sess, sc_sess, event_params));
112   unsigned int code_i;
113   if (str2i(code, code_i)) {
114     ERROR("decoding reply code '%s'\n", code.c_str());
115     sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
116     return;
117   }
118 
119   DBG("replying with %i %s, hdrs='%s'\n", code_i, reason.c_str(), hdrs.c_str());
120 
121   if (sess->dlg->reply(req, code_i, reason, NULL, hdrs)) {
122     sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
123     sc_sess->SET_STRERROR("error sending reply");
124   } else
125     sc_sess->CLR_ERRNO;
126 }
127 
128 CONST_ACTION_2P(DLGReplyAction, ',', true);
EXEC_ACTION_START(DLGReplyAction)129 EXEC_ACTION_START(DLGReplyAction) {
130 
131   if (!sc_sess->last_req.get()) {
132     ERROR("no last request to reply\n");
133     sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
134     sc_sess->SET_STRERROR("no last request to reply");
135     return false;
136   }
137 
138   replyRequest(sc_sess, sess, event_params, par1, par2, *sc_sess->last_req.get());
139 } EXEC_ACTION_END;
140 
141 // todo (?) move replyRequest to core module (?)
142 CONST_ACTION_2P(DLGReplyRequestAction, ',', true);
EXEC_ACTION_START(DLGReplyRequestAction)143 EXEC_ACTION_START(DLGReplyRequestAction) {
144   DSMSipRequest* sip_req;
145 
146   AVarMapT::iterator it = sc_sess->avar.find(DSM_AVAR_REQUEST);
147   if (it == sc_sess->avar.end() ||
148       !isArgAObject(it->second) ||
149       !(sip_req = dynamic_cast<DSMSipRequest*>(it->second.asObject()))) {
150     throw DSMException("dlg", "cause", "no request");
151   }
152 
153   replyRequest(sc_sess, sess, event_params, par1, par2, *sip_req->req);
154 } EXEC_ACTION_END;
155 
156 CONST_ACTION_2P(DLGAcceptInviteAction, ',', true);
EXEC_ACTION_START(DLGAcceptInviteAction)157 EXEC_ACTION_START(DLGAcceptInviteAction) {
158   // defaults to 200 OK
159   unsigned int code_i=200;
160   string reason = "OK";
161   string code = resolveVars(par1, sess, sc_sess, event_params);
162   string hdrs = replaceLineEnds(resolveVars("$dlg.reply.hdrs", sess, sc_sess, event_params));
163 
164   if (code.length()) {
165     reason = resolveVars(par2, sess, sc_sess, event_params);
166     if (str2i(code, code_i)) {
167       ERROR("decoding reply code '%s'\n", code.c_str());
168       sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
169       sc_sess->SET_STRERROR("decoding reply code '"+
170 			    code+"%s'\n");
171       return false;
172     }
173   }
174 
175   DBG("replying with %i %s, hdrs='%s'\n", code_i, reason.c_str(), hdrs.c_str());
176 
177   if (!sc_sess->last_req.get()) {
178     ERROR("no last request to reply\n");
179     sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
180     sc_sess->SET_STRERROR("no last request to reply");
181     return false;
182   }
183 
184   try {
185     AmMimeBody sdp_body;
186     if(sess->dlg->reply(*sc_sess->last_req.get(),code_i, reason,
187 		       sdp_body.addPart(SIP_APPLICATION_SDP), hdrs) != 0)
188       throw AmSession::Exception(500,"could not send response");
189 
190   }catch(const AmSession::Exception& e){
191 
192     ERROR("%i %s\n",e.code,e.reason.c_str());
193     sess->setStopped();
194     sess->dlg->reply(*sc_sess->last_req.get(),e.code,e.reason);
195 
196     sc_sess->SET_ERRNO(DSM_ERRNO_DLG);
197     sc_sess->SET_STRERROR("Error accepting call: "+ int2str(e.code) + " "+ e.reason);
198   }
199 } EXEC_ACTION_END;
200 
EXEC_ACTION_START(DLGByeAction)201 EXEC_ACTION_START(DLGByeAction) {
202   string hdrs = replaceLineEnds(resolveVars(arg, sess, sc_sess, event_params));
203 
204   if (sess->dlg->bye(hdrs)) {
205     sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
206     sc_sess->SET_STRERROR("Error sending bye");
207   } else {
208     sc_sess->SET_ERRNO(DSM_ERRNO_OK);
209   }
210 } EXEC_ACTION_END;
211 
212 
213 CONST_ACTION_2P(DLGConnectCalleeRelayedAction,',', false);
EXEC_ACTION_START(DLGConnectCalleeRelayedAction)214 EXEC_ACTION_START(DLGConnectCalleeRelayedAction) {
215   string remote_party = resolveVars(par1, sess, sc_sess, event_params);
216   string remote_uri = resolveVars(par2, sess, sc_sess, event_params);
217 
218   // if (sc_sess->last_req.get()) {
219   //   sc_sess->B2BaddReceivedRequest(*sc_sess->last_req.get());
220   // } else {
221   //   WARN("internal error: initial INVITE request missing.\n");
222   // }
223   // AmB2BSession* b2b_sess = dynamic_cast<AmB2BSession*>(sess);
224   // if (b2b_sess)
225   //   b2b_sess->set_sip_relay_only(true);
226   // else
227   //   ERROR("getting B2B session.\n");
228 
229   sc_sess->B2BconnectCallee(remote_party, remote_uri, true);
230 } EXEC_ACTION_END;
231 
EXEC_ACTION_START(DLGDialoutAction)232 EXEC_ACTION_START(DLGDialoutAction) {
233   string arrayname = resolveVars(arg, sess, sc_sess, event_params);
234 
235 #define GET_VARIABLE_MANDATORY(varname_suffix, outvar)			\
236   it = sc_sess->var.find(arrayname+varname_suffix); \
237   if (it == sc_sess->var.end()) {					\
238     WARN("%s", std::string("need " + arrayname + varname_suffix " set for dlg.dialoutSimple("+arrayname+")").c_str()); \
239     sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);				\
240     return false;							\
241   }									\
242   outvar = it->second;
243 
244 #define GET_VARIABLE_OPTIONAL(varname_suffix, outvar) \
245   it = sc_sess->var.find(arrayname+varname_suffix);  \
246   if (it != sc_sess->var.end())		      \
247     outvar = it->second;
248 
249   map<string, string>::iterator it;
250 
251   string v_from;
252   GET_VARIABLE_MANDATORY("_caller", v_from);
253   string v_to;
254   GET_VARIABLE_MANDATORY("_callee", v_to);
255   string v_domain;
256   GET_VARIABLE_MANDATORY("_domain", v_domain);
257   string app_name;
258   GET_VARIABLE_MANDATORY("_app", app_name);
259 
260   string user = v_from;
261   string r_uri = "sip:"+v_to+"@"+v_domain;
262 
263   GET_VARIABLE_OPTIONAL("_r_uri", r_uri);
264 
265   string from = "<sip:"+v_from+"@"+v_domain+">";
266   GET_VARIABLE_OPTIONAL("_from", from);
267 
268   string from_uri = "sip:"+v_from+"@"+v_domain;
269   GET_VARIABLE_OPTIONAL("_from_uri", from_uri);
270 
271   string to = "<sip:"+v_to+"@"+v_domain+">";
272   GET_VARIABLE_OPTIONAL("_to", to);
273 
274   string auth_user;
275   GET_VARIABLE_OPTIONAL("_auth_user", auth_user);
276 
277   string auth_pwd;
278   GET_VARIABLE_OPTIONAL("_auth_pwd", auth_pwd);
279 
280   string ltag;
281   GET_VARIABLE_OPTIONAL("_ltag", ltag);
282 
283   string hdrs;
284   GET_VARIABLE_OPTIONAL("_hdrs", hdrs);
285 
286   if (hdrs.length()) {
287     size_t crpos;
288     while ((crpos=hdrs.find("\\r\\n")) != string::npos) {
289       hdrs.replace(crpos, 4, "\r\n");
290     }
291   }
292 
293 #undef GET_VARIABLE_MANDATORY
294 #undef GET_VARIABLE_OPTIONAL
295 
296   DBG("placing UAC call: user <%s>, app <%s>, ruri <%s>, from <%s> "
297       "from_uri <%s>, to <%s>, ltag <%s>, hdrs <%s>, auth_user <%s>, auth_pwd <not shown>\n",
298       user.c_str(), app_name.c_str(), r_uri.c_str(), from.c_str(),
299       from_uri.c_str(), to.c_str(), ltag.c_str(), hdrs.c_str(), auth_user.c_str());
300 
301   AmArg* sess_params = new AmArg();
302   bool has_auth = false;
303   if (!auth_user.empty() && !auth_pwd.empty()) {
304     AmArg auth_param;
305     auth_param.setBorrowedPointer(new UACAuthCred("", auth_user,auth_pwd));
306     sess_params->push(auth_param);
307     has_auth = true;
308   }
309   AmArg var_struct;
310   string varprefix = arrayname+"_var.";
311   bool has_vars = false;
312   map<string, string>::iterator lb = sc_sess->var.lower_bound(varprefix);
313   while (lb != sc_sess->var.end()) {
314     if ((lb->first.length() < varprefix.length()) ||
315 	strncmp(lb->first.c_str(), varprefix.c_str(),varprefix.length()))
316       break;
317     string varname = lb->first.substr(varprefix.length());
318     if (!has_auth) // sess_params is variable struct
319       (*sess_params)[varname] = lb->second;
320     else // variable struct is in sess_params array
321       var_struct[varname] = lb->second;
322 
323     lb++;
324     has_vars = true;
325   }
326 
327   if (has_vars && has_auth)
328       sess_params->push(var_struct);
329 
330   DBG("sess_params: '%s'\n", AmArg::print(*sess_params).c_str());
331 
332   string new_sess_tag = AmUAC::dialout(user, app_name, r_uri, from, from_uri, to, ltag, hdrs, sess_params);
333 
334   if (!new_sess_tag.empty()) {
335     sc_sess->var[arrayname + "_ltag"] = new_sess_tag;
336   } else {
337     sc_sess->var[arrayname + "_ltag"] = "";
338     sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
339   }
340 
341 } EXEC_ACTION_END;
342 
MATCH_CONDITION_START(DLGReplyHasContentTypeCondition)343 MATCH_CONDITION_START(DLGReplyHasContentTypeCondition) {
344   AVarMapT::iterator it = sc_sess->avar.find(DSM_AVAR_REPLY);
345   if (it == sc_sess->avar.end()) {
346     ERROR("DSM script error: dlg.replyHasContentType condition used for "
347 	  "other event than sipReply event\n");
348     return false;
349   }
350 
351   DSMSipReply* dsm_reply = NULL;
352   if (!isArgAObject(sc_sess->avar[DSM_AVAR_REPLY]) ||
353       (NULL ==
354        (dsm_reply = dynamic_cast<DSMSipReply*>(sc_sess->avar[DSM_AVAR_REPLY].asObject())))) {
355     ERROR("internal: DSM could not get DSMSipReply\n");
356     return false;
357   }
358 
359   bool res = dsm_reply->reply->body.hasContentType(arg);
360 
361   DBG("checking for content_type '%s': %s\n", arg.c_str(), res?"has it":"doesn't have it");
362   return res;
363 } MATCH_CONDITION_END;
364 
MATCH_CONDITION_START(DLGRequestHasContentTypeCondition)365 MATCH_CONDITION_START(DLGRequestHasContentTypeCondition) {
366   AVarMapT::iterator it = sc_sess->avar.find(DSM_AVAR_REQUEST);
367   if (it == sc_sess->avar.end()) {
368     ERROR("DSM script error: dlg.requestHasContentType condition used for "
369 	  "other event than sipRequest event\n");
370     return false;
371   }
372 
373   DSMSipRequest* dsm_req = NULL;
374   if (!isArgAObject(sc_sess->avar[DSM_AVAR_REQUEST]) ||
375       (NULL ==
376        (dsm_req = dynamic_cast<DSMSipRequest*>(sc_sess->avar[DSM_AVAR_REQUEST].asObject())))) {
377     ERROR("internal: DSM could not get DSMSipRequest\n");
378     return false;
379   }
380 
381   bool res = dsm_req->req->body.hasContentType(arg);
382 
383   DBG("checking for content_type '%s': %s\n", arg.c_str(), res?"has it":"doesn't have it");
384   return res;
385 } MATCH_CONDITION_END;
386 
387 CONST_ACTION_2P(DLGGetRequestBodyAction, ',', false);
EXEC_ACTION_START(DLGGetRequestBodyAction)388 EXEC_ACTION_START(DLGGetRequestBodyAction) {
389   DSMSipRequest* sip_req;
390 
391   AVarMapT::iterator it = sc_sess->avar.find(DSM_AVAR_REQUEST);
392   if (it == sc_sess->avar.end() ||
393       !isArgAObject(it->second) ||
394       !(sip_req = dynamic_cast<DSMSipRequest*>(it->second.asObject()))) {
395     throw DSMException("dlg", "cause", "no request");
396   }
397 
398   string content_type = resolveVars(par1, sess, sc_sess, event_params);
399   string dstvar = resolveVars(par2, sess, sc_sess, event_params);
400 
401   const AmMimeBody* msg_body = sip_req->req->body.hasContentType(content_type);
402   if (NULL == msg_body) {
403     DBG("body with content_type %s not found\n", content_type.c_str());
404     sc_sess->var.erase(dstvar);
405   } else {
406     sc_sess->var[dstvar] = string((const char*)msg_body->getPayload());
407     DBG("set $%s='%s'\n", dstvar.c_str(), sc_sess->var[dstvar].c_str());
408   }
409 } EXEC_ACTION_END;
410 
411 CONST_ACTION_2P(DLGGetReplyBodyAction, ',', false);
EXEC_ACTION_START(DLGGetReplyBodyAction)412 EXEC_ACTION_START(DLGGetReplyBodyAction) {
413   DSMSipReply* sip_req;
414 
415   AVarMapT::iterator it = sc_sess->avar.find(DSM_AVAR_REPLY);
416   if (it == sc_sess->avar.end() ||
417       !isArgAObject(it->second) ||
418       !(sip_req = dynamic_cast<DSMSipReply*>(it->second.asObject()))) {
419     throw DSMException("dlg", "cause", "no reply");
420   }
421 
422   string content_type = resolveVars(par1, sess, sc_sess, event_params);
423   string dstvar = resolveVars(par2, sess, sc_sess, event_params);
424 
425   const AmMimeBody* msg_body = sip_req->reply->body.hasContentType(content_type);
426   if (NULL == msg_body) {
427     DBG("body with content_type %s not found\n", content_type.c_str());
428     sc_sess->var.erase(dstvar);
429   } else {
430     sc_sess->var[dstvar] = string((const char*)msg_body->getPayload());
431     DBG("set $%s='%s'\n", dstvar.c_str(), sc_sess->var[dstvar].c_str());
432   }
433 } EXEC_ACTION_END;
434 
EXEC_ACTION_START(DLGGetOtherIdAction)435 EXEC_ACTION_START(DLGGetOtherIdAction) {
436   string varname = arg;
437   AmB2BSession* b2b_sess = dynamic_cast<AmB2BSession*>(sess);
438   if (NULL == b2b_sess) {
439     DBG("script writer error: dlg.getOtherId used without B2B session object.\n");
440     EXEC_ACTION_STOP;
441   }
442 
443   if (varname.size() && varname[0] == '$')
444     varname.erase(0, 1);
445   sc_sess->var[varname] = b2b_sess->getOtherId();
446 } EXEC_ACTION_END;
447 
EXEC_ACTION_START(DLGGetRtpRelayModeAction)448 EXEC_ACTION_START(DLGGetRtpRelayModeAction) {
449   string varname = arg;
450   AmB2BSession* b2b_sess = dynamic_cast<AmB2BSession*>(sess);
451   if (NULL == b2b_sess) {
452     DBG("script writer error: dlg.getOtherId used without B2B session object.\n");
453     EXEC_ACTION_STOP;
454   }
455 
456   if (varname.size() && varname[0] == '$')
457     varname.erase(0, 1);
458   switch (b2b_sess->getRtpRelayMode()) {
459   case AmB2BSession::RTP_Direct: sc_sess->var[varname] = "RTP_Direct"; break;
460   case AmB2BSession::RTP_Relay: sc_sess->var[varname] = "RTP_Relay"; break;
461   case AmB2BSession::RTP_Transcoding: sc_sess->var[varname] = "RTP_Transcoding"; break;
462   default: sc_sess->var[varname] = "Unknown"; break;
463   }
464 
465   DBG("get RTP relay mode: %s='%s'\n", varname.c_str(), sc_sess->var[varname].c_str());
466 } EXEC_ACTION_END;
467 
468 CONST_ACTION_2P(DLGReferAction, ',', true);
EXEC_ACTION_START(DLGReferAction)469 EXEC_ACTION_START(DLGReferAction) {
470 
471   AmSession* b2b_sess = dynamic_cast<AmSession*>(sess);
472   if (NULL == b2b_sess) {
473     throw DSMException("sbc", "type", "param", "cause",
474 		       "dlg.refer used on non-session");
475   }
476 
477   string refer_to = resolveVars(par1, sess, sc_sess, event_params);
478   string expires_s = resolveVars(par2, sess, sc_sess, event_params);
479 
480   int expires = -1;
481   if (!expires_s.empty()) {
482     if (!str2int(expires_s, expires)) {
483       throw DSMException("sbc", "type", "param", "cause",
484 			 "expires "+expires_s+" not valid");
485     }
486   }
487 
488   if (NULL == b2b_sess->dlg) {
489       throw DSMException("sbc", "type", "param", "cause",
490 			 "call doesn't have SIP dialog (OOPS!)");
491   }
492 
493   if (b2b_sess->dlg->refer(refer_to, expires)) {
494     sc_sess->SET_ERRNO(DSM_ERRNO_DLG);
495     sc_sess->SET_STRERROR("sending REFER failed");
496   } else {
497     sc_sess->CLR_ERRNO;
498   }
499 } EXEC_ACTION_END;
500 
501 CONST_ACTION_2P(DLGInfoAction, ',', true);
EXEC_ACTION_START(DLGInfoAction)502 EXEC_ACTION_START(DLGInfoAction) {
503 
504   AmSession* b2b_sess = dynamic_cast<AmSession*>(sess);
505   if (NULL == b2b_sess) {
506     throw DSMException("sbc", "type", "param", "cause",
507 		       "dlg.info used on non-session");
508   }
509 
510   string content_type = resolveVars(par1, sess, sc_sess, event_params);
511   string body_str = resolveVars(par2, sess, sc_sess, event_params);
512 
513   if (NULL == b2b_sess->dlg) {
514     throw DSMException("sbc", "type", "param", "cause",
515 		       "call doesn't have SIP dialog (OOPS!)");
516   }
517 
518   string body_crlf = body_str;
519   AmMimeBody *body = new AmMimeBody();
520   if (!content_type.empty()) {
521     DBG("body_crlf is '%s'\n", body_crlf.c_str());
522     while (true) {
523       size_t p = body_crlf.find("\\r\\n");
524       if (p==string::npos)
525 	break;
526       body_crlf.replace(p, 4, "\r\n");
527     }
528     DBG("-> body_crlf is '%s'\n", body_crlf.c_str());
529     if (body->parse(content_type,
530 		    reinterpret_cast<const unsigned char*>(body_crlf.c_str()),
531 		    body_crlf.length())) {
532       throw DSMException("sbc", "type", "param", "cause",
533 			 "parsing of INFO body failed");
534     }
535   }
536 
537   if (b2b_sess->dlg->info("", body)) {
538     sc_sess->SET_ERRNO(DSM_ERRNO_DLG);
539     sc_sess->SET_STRERROR("sending INFO failed");
540   } else {
541     sc_sess->CLR_ERRNO;
542   }
543 
544 } EXEC_ACTION_END;
545 
546 #define GET_B2B_SESSION(action)						\
547   AmB2BSession* b2b_sess = dynamic_cast<AmB2BSession*>(sess);		\
548   if (NULL == b2b_sess) {						\
549     throw DSMException("sbc", "type", "param", "cause",			\
550 		       #action " used on non-b2b-session");		\
551   }
552 
553 CONST_ACTION_2P(DLGB2BRelayErrorAction, ',', false);
EXEC_ACTION_START(DLGB2BRelayErrorAction)554 EXEC_ACTION_START(DLGB2BRelayErrorAction) {
555   DSMSipRequest* sip_req;
556 
557   AVarMapT::iterator it = sc_sess->avar.find(DSM_AVAR_REQUEST);
558   if (it == sc_sess->avar.end() ||
559       !isArgAObject(it->second) ||
560       !(sip_req = dynamic_cast<DSMSipRequest*>(it->second.asObject()))) {
561     throw DSMException("dlg", "cause", "no request");
562   }
563   GET_B2B_SESSION(dlg.relayError);
564 
565   string code = resolveVars(par1, sess, sc_sess, event_params);
566   string reason = resolveVars(par2, sess, sc_sess, event_params);
567   unsigned int code_i;
568   if (str2i(code, code_i)) {
569     ERROR("decoding reply code '%s'\n", code.c_str());
570     sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
571     EXEC_ACTION_STOP;
572   }
573 
574   b2b_sess->relayError(sip_req->req->method, sip_req->req->cseq, true, code_i, reason.c_str());
575 } EXEC_ACTION_END;
576 
577 CONST_ACTION_2P(DLGAddReplyBodyPartAction, ',', false);
EXEC_ACTION_START(DLGAddReplyBodyPartAction)578 EXEC_ACTION_START(DLGAddReplyBodyPartAction) {
579   DSMMutableSipReply* sip_reply;
580 
581   AVarMapT::iterator it = sc_sess->avar.find(DSM_AVAR_REPLY);
582   if (it == sc_sess->avar.end() ||
583       !isArgAObject(it->second) ||
584       !(sip_reply = dynamic_cast<DSMMutableSipReply*>(it->second.asObject()))) {
585     throw DSMException("dlg", "cause", "no reply");
586   }
587 
588   string content_type = resolveVars(par1, sess, sc_sess, event_params);
589   string body_part = resolveVars(par2, sess, sc_sess, event_params);
590 
591   AmMimeBody* new_part;
592 
593   new_part = sip_reply->mutable_reply->body.addPart(content_type);
594   new_part->setPayload((const unsigned char*)body_part.c_str(),
595 		       body_part.length());
596   DBG("added to reply body part %s='%s'\n",
597       content_type.c_str(), body_part.c_str());
598 } EXEC_ACTION_END;
599 
EXEC_ACTION_START(DLGDeleteReplyBodyPartAction)600 EXEC_ACTION_START(DLGDeleteReplyBodyPartAction) {
601   DSMMutableSipReply* sip_reply;
602 
603   AVarMapT::iterator it = sc_sess->avar.find(DSM_AVAR_REPLY);
604   if (it == sc_sess->avar.end() ||
605       !isArgAObject(it->second) ||
606       !(sip_reply = dynamic_cast<DSMMutableSipReply*>(it->second.asObject()))) {
607     throw DSMException("dlg", "cause", "no reply");
608   }
609 
610   if (sip_reply->mutable_reply->body.deletePart(arg)) {
611     DBG("failed to delete reply body part '%s'\n", arg.c_str());
612   } else {
613     DBG("deleted reply body part '%s'\n", arg.c_str());
614   }
615 } EXEC_ACTION_END;
616