1 /*
2 * Copyright (C) 2002-2003 Fhg Fokus
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
28 #include "AmSipDialog.h"
29 #include "AmConfig.h"
30 #include "AmSession.h"
31 #include "AmUtils.h"
32 #include "AmSipHeaders.h"
33 #include "SipCtrlInterface.h"
34 #include "sems.h"
35
36 #include "sip/parse_route.h"
37 #include "sip/parse_uri.h"
38 #include "sip/parse_next_hop.h"
39
40 #include "AmB2BMedia.h" // just because of statistics
41
addTranscoderStats(string & hdrs)42 static void addTranscoderStats(string &hdrs)
43 {
44 // add transcoder statistics into request/reply headers
45 if (!AmConfig::TranscoderOutStatsHdr.empty()) {
46 string usage;
47 B2BMediaStatistics::instance()->reportCodecWriteUsage(usage);
48
49 hdrs += AmConfig::TranscoderOutStatsHdr + ": ";
50 hdrs += usage;
51 hdrs += CRLF;
52 }
53 if (!AmConfig::TranscoderInStatsHdr.empty()) {
54 string usage;
55 B2BMediaStatistics::instance()->reportCodecReadUsage(usage);
56
57 hdrs += AmConfig::TranscoderInStatsHdr + ": ";
58 hdrs += usage;
59 hdrs += CRLF;
60 }
61 }
62
AmSipDialog(AmSipDialogEventHandler * h)63 AmSipDialog::AmSipDialog(AmSipDialogEventHandler* h)
64 : AmBasicSipDialog(h),pending_invites(0),sdp_local(),
65 sdp_remote(),
66 early_session_started(false),session_started(false),
67 oa(this),
68 offeranswer_enabled(true), rel100(this,h)
69 {
70 }
71
~AmSipDialog()72 AmSipDialog::~AmSipDialog()
73 {
74 }
75
onRxReqSanity(const AmSipRequest & req)76 bool AmSipDialog::onRxReqSanity(const AmSipRequest& req)
77 {
78 if (req.method == SIP_METH_ACK) {
79 if(onRxReqStatus(req) && hdl)
80 hdl->onSipRequest(req);
81 return false;
82 }
83
84 if (req.method == SIP_METH_CANCEL) {
85
86 if (uas_trans.find(req.cseq) == uas_trans.end()) {
87 reply_error(req,481,SIP_REPLY_NOT_EXIST);
88 return false;
89 }
90
91 if(onRxReqStatus(req) && hdl)
92 hdl->onSipRequest(req);
93
94 return false;
95 }
96
97 if(!AmBasicSipDialog::onRxReqSanity(req))
98 return false;
99
100 if (req.method == SIP_METH_INVITE) {
101 bool pending = pending_invites;
102 if (offeranswer_enabled) {
103 // not sure this is needed here: could be in AmOfferAnswer as well
104 pending |= ((oa.getState() != AmOfferAnswer::OA_None) &&
105 (oa.getState() != AmOfferAnswer::OA_Completed));
106 }
107
108 if (pending) {
109 reply_error(req, 491, SIP_REPLY_PENDING,
110 SIP_HDR_COLSP(SIP_HDR_RETRY_AFTER)
111 + int2str(get_random() % 10) + CRLF);
112 return false;
113 }
114
115 pending_invites++;
116 }
117
118 return rel100.onRequestIn(req);
119 }
120
onRxReqStatus(const AmSipRequest & req)121 bool AmSipDialog::onRxReqStatus(const AmSipRequest& req)
122 {
123 switch(status){
124 case Disconnected:
125 if(req.method == SIP_METH_INVITE)
126 setStatus(Trying);
127 break;
128 case Connected:
129 if(req.method == SIP_METH_BYE)
130 setStatus(Disconnecting);
131 break;
132
133 case Trying:
134 case Proceeding:
135 case Early:
136 if(req.method == SIP_METH_BYE)
137 setStatus(Disconnecting);
138 else if(req.method == SIP_METH_CANCEL){
139 setStatus(Cancelling);
140 reply(req,200,"OK");
141 }
142 break;
143
144 default: break;
145 }
146
147 bool cont = true;
148 if (offeranswer_enabled) {
149 cont = (oa.onRequestIn(req) == 0);
150 }
151
152 return cont;
153 }
154
onSdpCompleted()155 int AmSipDialog::onSdpCompleted()
156 {
157 if(!hdl) return 0;
158
159 int ret = ((AmSipDialogEventHandler*)hdl)->
160 onSdpCompleted(oa.getLocalSdp(), oa.getRemoteSdp());
161
162 if(!ret) {
163 sdp_local = oa.getLocalSdp();
164 sdp_remote = oa.getRemoteSdp();
165
166 if((getStatus() == Early) && !early_session_started) {
167 ((AmSipDialogEventHandler*)hdl)->onEarlySessionStart();
168 early_session_started = true;
169 }
170
171 if((getStatus() == Connected) && !session_started) {
172 ((AmSipDialogEventHandler*)hdl)->onSessionStart();
173 session_started = true;
174 }
175 }
176 else {
177 oa.clear();
178 }
179
180 return ret;
181 }
182
getSdpOffer(AmSdp & offer)183 bool AmSipDialog::getSdpOffer(AmSdp& offer)
184 {
185 if(!hdl) return false;
186 return ((AmSipDialogEventHandler*)hdl)->getSdpOffer(offer);
187 }
188
getSdpAnswer(const AmSdp & offer,AmSdp & answer)189 bool AmSipDialog::getSdpAnswer(const AmSdp& offer, AmSdp& answer)
190 {
191 if(!hdl) return false;
192 return ((AmSipDialogEventHandler*)hdl)->getSdpAnswer(offer,answer);
193 }
194
getOAState()195 AmOfferAnswer::OAState AmSipDialog::getOAState() {
196 return oa.getState();
197 }
198
setOAState(AmOfferAnswer::OAState n_st)199 void AmSipDialog::setOAState(AmOfferAnswer::OAState n_st) {
200 oa.setState(n_st);
201 }
202
setRel100State(Am100rel::State rel100_state)203 void AmSipDialog::setRel100State(Am100rel::State rel100_state) {
204 DBG("setting 100rel state for '%s' to %i\n", local_tag.c_str(), rel100_state);
205 rel100.setState(rel100_state);
206 }
207
setOAEnabled(bool oa_enabled)208 void AmSipDialog::setOAEnabled(bool oa_enabled) {
209 DBG("%sabling offer_answer on SIP dialog '%s'\n",
210 oa_enabled?"en":"dis", local_tag.c_str());
211 offeranswer_enabled = oa_enabled;
212 }
213
onTxRequest(AmSipRequest & req,int & flags)214 int AmSipDialog::onTxRequest(AmSipRequest& req, int& flags)
215 {
216 rel100.onRequestOut(req);
217
218 if (offeranswer_enabled && oa.onRequestOut(req))
219 return -1;
220
221 if(AmBasicSipDialog::onTxRequest(req,flags) < 0)
222 return -1;
223
224 // add transcoder statistics into request headers
225 addTranscoderStats(req.hdrs);
226
227 if((req.method == SIP_METH_INVITE) && (status == Disconnected)){
228 setStatus(Trying);
229 }
230 else if((req.method == SIP_METH_BYE) && (status != Disconnecting)){
231 setStatus(Disconnecting);
232 }
233
234 if ((req.method == SIP_METH_BYE) || (req.method == SIP_METH_CANCEL)) {
235 flags |= SIP_FLAGS_NOCONTACT;
236 }
237
238 return 0;
239 }
240
241 // UAS behavior for locally sent replies
onTxReply(const AmSipRequest & req,AmSipReply & reply,int & flags)242 int AmSipDialog::onTxReply(const AmSipRequest& req, AmSipReply& reply, int& flags)
243 {
244 if (offeranswer_enabled) {
245 if(oa.onReplyOut(reply) < 0)
246 return -1;
247 }
248
249 rel100.onReplyOut(reply);
250
251 // update Dialog status
252 switch(status){
253
254 case Connected:
255 case Disconnected:
256 break;
257
258 case Cancelling:
259 if( (reply.cseq_method == SIP_METH_INVITE) &&
260 (reply.code < 200) ) {
261 // refuse local provisional replies
262 // when state is Cancelling
263 ERROR("refuse local provisional replies when state is Cancelling\n");
264 return -1;
265 }
266 // else continue with final
267 // reply processing
268 case Proceeding:
269 case Trying:
270 case Early:
271 if(reply.cseq_method == SIP_METH_INVITE){
272 if(reply.code < 200) {
273 setStatus(Early);
274 }
275 else if(reply.code < 300)
276 setStatus(Connected);
277 else
278 setStatus(Disconnected);
279 }
280 break;
281
282 case Disconnecting:
283 if(reply.cseq_method == SIP_METH_BYE){
284
285 // Only reason for refusing a BYE:
286 // authentication (NYI at this place)
287 // Also: we should not send provisionnal replies to a BYE
288 if(reply.code >= 200)
289 setStatus(Disconnected);
290 }
291 break;
292
293 default:
294 assert(0);
295 break;
296 }
297
298 // add transcoder statistics into reply headers
299 addTranscoderStats(reply.hdrs);
300
301 // target-refresh requests and their replies need to contain Contact (1xx
302 // replies only those establishing dialog, take care about them?)
303 if(reply.cseq_method != SIP_METH_INVITE &&
304 reply.cseq_method != SIP_METH_UPDATE) {
305
306 flags |= SIP_FLAGS_NOCONTACT;
307 }
308
309 return AmBasicSipDialog::onTxReply(req,reply,flags);
310 }
311
onReplyTxed(const AmSipRequest & req,const AmSipReply & reply)312 void AmSipDialog::onReplyTxed(const AmSipRequest& req, const AmSipReply& reply)
313 {
314 AmBasicSipDialog::onReplyTxed(req,reply);
315
316 if (offeranswer_enabled) {
317 oa.onReplySent(reply);
318 }
319
320 if (reply.code >= 200) {
321 if(reply.cseq_method == SIP_METH_INVITE)
322 pending_invites--;
323 }
324 }
325
onRequestTxed(const AmSipRequest & req)326 void AmSipDialog::onRequestTxed(const AmSipRequest& req)
327 {
328 AmBasicSipDialog::onRequestTxed(req);
329
330 if (offeranswer_enabled) {
331 oa.onRequestSent(req);
332 }
333 }
334
onRxReplySanity(const AmSipReply & reply)335 bool AmSipDialog::onRxReplySanity(const AmSipReply& reply)
336 {
337 if(!getRemoteTag().empty() && reply.to_tag != getRemoteTag()) {
338
339 if(status == Early) {
340 if(reply.code < 200 && !reply.to_tag.empty()) {
341 // Provision reply, such as 180, can come from a new UAS
342 return true;
343 }
344 } else if(reply.cseq_method == SIP_METH_INVITE
345 || reply.cseq_method == SIP_METH_CANCEL
346 || reply.cseq_method == SIP_METH_BYE) {
347 // do not drop in order to avoid avoid sessions hangs
348 DBG("[%s] reply for %s '%d %s' is not matched with dialog. but matched with transaction. process it",
349 local_tag.c_str(), reply.cseq_method.c_str(), reply.code,
350 reply.reason.c_str());
351 } else {
352 DBG("dropping reply (%u %s)\n", reply.code, reply.reason.c_str());
353 return false;
354 }
355 }
356
357 return true;
358 }
359
onRxReplyStatus(const AmSipReply & reply)360 bool AmSipDialog::onRxReplyStatus(const AmSipReply& reply)
361 {
362 // rfc3261 12.1
363 // Dialog established only by 101-199 or 2xx
364 // responses to INVITE
365
366 if(reply.cseq_method == SIP_METH_INVITE) {
367
368 switch(status){
369
370 case Trying:
371 case Proceeding:
372 if(reply.code < 200){
373 // Do not go to Early state if reply did not come from an UAS
374 if (((reply.code == 100) || (reply.code == 181) || (reply.code == 182))
375 || reply.to_tag.empty())
376 setStatus(Proceeding);
377 else {
378 setStatus(Early);
379 setRemoteTag(reply.to_tag);
380 setRouteSet(reply.route);
381 }
382 }
383 else if(reply.code < 300){
384 setStatus(Connected);
385 setRouteSet(reply.route);
386 if(reply.to_tag.empty()){
387 DBG("received 2xx reply without to-tag "
388 "(callid=%s): sending BYE\n",reply.callid.c_str());
389
390 send_200_ack(reply.cseq);
391 sendRequest(SIP_METH_BYE);
392 }
393 else {
394 setRemoteTag(reply.to_tag);
395 }
396 }
397
398 if(reply.code >= 300) {// error reply
399 setStatus(Disconnected);
400 setRemoteTag(reply.to_tag);
401 }
402 break;
403
404 case Early:
405 if(reply.code < 200){
406 if (!reply.to_tag.empty() && (reply.to_tag != getRemoteTag())) {
407 DBG("updating dialog based on reply (%u %s) in Early state\n",
408 reply.code, reply.reason.c_str());
409 setRemoteTag(reply.to_tag);
410 setRouteSet(reply.route);
411 break;
412 }
413 //DROP!!!
414 DBG("ignoring provisional reply (%u %s) in Early state\n",
415 reply.code, reply.reason.c_str());
416 }
417 else if(reply.code < 300){
418 setStatus(Connected);
419 setRouteSet(reply.route);
420 if(reply.to_tag.empty()){
421 DBG("received 2xx reply without to-tag "
422 "(callid=%s): sending BYE\n",reply.callid.c_str());
423
424 sendRequest(SIP_METH_BYE);
425 }
426 else {
427 setRemoteTag(reply.to_tag);
428 }
429 }
430 else { // error reply
431 setStatus(Disconnected);
432 setRemoteTag(reply.to_tag);
433 }
434 break;
435
436 case Cancelling:
437 if(reply.code >= 300){
438 // CANCEL accepted
439 DBG("CANCEL accepted, status -> Disconnected\n");
440 setStatus(Disconnected);
441 }
442 else if(reply.code < 300){
443 // CANCEL rejected
444 DBG("CANCEL rejected/too late - bye()\n");
445 setRemoteTag(reply.to_tag);
446 setStatus(Connected);
447 bye();
448 // if BYE could not be sent,
449 // there is nothing we can do anymore...
450 }
451 break;
452
453 //case Connected: // late 200...
454 // TODO: if reply.to_tag != getRemoteTag()
455 // -> ACK + BYE (+absorb answer)
456 default:
457 break;
458 }
459 }
460
461 if(status == Disconnecting){
462
463 DBG("?Disconnecting?: cseq_method = %s; code = %i\n",
464 reply.cseq_method.c_str(), reply.code);
465
466 if((reply.cseq_method == SIP_METH_BYE) && (reply.code >= 200)){
467 //TODO: support the auth case here (401/403)
468 setStatus(Disconnected);
469 }
470 }
471
472 if (offeranswer_enabled) {
473 oa.onReplyIn(reply);
474 }
475
476 bool cont = true;
477 if( (reply.code >= 200) && (reply.code < 300) &&
478 (reply.cseq_method == SIP_METH_INVITE) ) {
479
480 if(hdl) ((AmSipDialogEventHandler*)hdl)->onInvite2xx(reply);
481
482 } else {
483 cont = AmBasicSipDialog::onRxReplyStatus(reply);
484 }
485
486 return cont && rel100.onReplyIn(reply);
487 }
488
uasTimeout(AmSipTimeoutEvent * to_ev)489 void AmSipDialog::uasTimeout(AmSipTimeoutEvent* to_ev)
490 {
491 assert(to_ev);
492
493 switch(to_ev->type){
494 case AmSipTimeoutEvent::noACK:
495 DBG("Timeout: missing ACK\n");
496 if (offeranswer_enabled) {
497 oa.onNoAck(to_ev->cseq);
498 }
499 if(hdl) ((AmSipDialogEventHandler*)hdl)->onNoAck(to_ev->cseq);
500 break;
501
502 case AmSipTimeoutEvent::noPRACK:
503 DBG("Timeout: missing PRACK\n");
504 rel100.onTimeout(to_ev->req, to_ev->rpl);
505 break;
506
507 case AmSipTimeoutEvent::_noEv:
508 default:
509 break;
510 };
511
512 to_ev->processed = true;
513 }
514
getUACInvTransPending()515 bool AmSipDialog::getUACInvTransPending() {
516 for (TransMap::iterator it=uac_trans.begin();
517 it != uac_trans.end(); it++) {
518 if (it->second.method == SIP_METH_INVITE)
519 return true;
520 }
521 return false;
522 }
523
getUASPendingInv()524 AmSipRequest* AmSipDialog::getUASPendingInv()
525 {
526 for (TransMap::iterator it=uas_trans.begin();
527 it != uas_trans.end(); it++) {
528 if (it->second.method == SIP_METH_INVITE)
529 return &(it->second);
530 }
531 return NULL;
532 }
533
bye(const string & hdrs,int flags)534 int AmSipDialog::bye(const string& hdrs, int flags)
535 {
536 switch(status){
537
538 case Disconnecting:
539 case Connected: {
540 // collect INVITE UAC transactions
541 vector<unsigned int> ack_trans;
542 for (TransMap::iterator it=uac_trans.begin(); it != uac_trans.end(); it++) {
543 if (it->second.method == SIP_METH_INVITE){
544 ack_trans.push_back(it->second.cseq);
545 }
546 }
547 // finish any UAC transaction before sending BYE
548 for (vector<unsigned int>::iterator it=
549 ack_trans.begin(); it != ack_trans.end(); it++) {
550 send_200_ack(*it);
551 }
552
553 if (status != Disconnecting) {
554 setStatus(Disconnected);
555 return sendRequest(SIP_METH_BYE, NULL, hdrs, flags);
556 } else {
557 return 0;
558 }
559 }
560
561 case Trying:
562 case Proceeding:
563 case Early:
564 if(getUACInvTransPending())
565 return cancel(hdrs);
566 else {
567 for (TransMap::iterator it=uas_trans.begin();
568 it != uas_trans.end(); it++) {
569 if (it->second.method == SIP_METH_INVITE){
570 // let quit this call by sending final reply
571 return reply(it->second,
572 487,"Request terminated");
573 }
574 }
575
576 // missing AmSipRequest to be able
577 // to send the reply on behalf of the app.
578 ERROR("ignoring bye() in %s state: "
579 "no UAC transaction to cancel or UAS transaction to reply.\n",
580 getStatusStr());
581 setStatus(Disconnected);
582 }
583 return 0;
584
585 case Cancelling:
586 for (TransMap::iterator it=uas_trans.begin();
587 it != uas_trans.end(); it++) {
588 if (it->second.method == SIP_METH_INVITE){
589 // let's quit this call by sending final reply
590 return reply(it->second, 487,"Request terminated");
591 }
592 }
593
594 // missing AmSipRequest to be able
595 // to send the reply on behalf of the app.
596 DBG("ignoring bye() in %s state: no UAS transaction to reply",getStatusStr());
597 setStatus(Disconnected);
598 return 0;
599
600 default:
601 DBG("bye(): we are not connected "
602 "(status=%s). do nothing!\n",getStatusStr());
603 return 0;
604 }
605 }
606
reinvite(const string & hdrs,const AmMimeBody * body,int flags)607 int AmSipDialog::reinvite(const string& hdrs,
608 const AmMimeBody* body,
609 int flags)
610 {
611 if(getStatus() == Connected) {
612 return sendRequest(SIP_METH_INVITE, body, hdrs, flags);
613 }
614 else {
615 DBG("reinvite(): we are not connected "
616 "(status=%s). do nothing!\n",getStatusStr());
617 }
618
619 return -1;
620 }
621
invite(const string & hdrs,const AmMimeBody * body)622 int AmSipDialog::invite(const string& hdrs,
623 const AmMimeBody* body)
624 {
625 if(getStatus() == Disconnected) {
626 int res = sendRequest(SIP_METH_INVITE, body, hdrs);
627 DBG("TODO: is status already 'trying'? status=%s\n",getStatusStr());
628 //status = Trying;
629 return res;
630 }
631 else {
632 DBG("invite(): we are already connected "
633 "(status=%s). do nothing!\n",getStatusStr());
634 }
635
636 return -1;
637 }
638
update(const AmMimeBody * body,const string & hdrs)639 int AmSipDialog::update(const AmMimeBody* body,
640 const string &hdrs)
641 {
642 switch(getStatus()){
643 case Connected://if Connected, we should send a re-INVITE instead...
644 DBG("re-INVITE should be used instead (see RFC3311, section 5.1)\n");
645 case Trying:
646 case Proceeding:
647 case Early:
648 return sendRequest(SIP_METH_UPDATE, body, hdrs);
649
650 default:
651 case Cancelling:
652 case Disconnected:
653 case Disconnecting:
654 DBG("update(): dialog not connected "
655 "(status=%s). do nothing!\n",getStatusStr());
656 }
657
658 return -1;
659 }
660
refer(const string & refer_to,int expires,const string & referred_by,const string & extrahdrs)661 int AmSipDialog::refer(const string& refer_to,
662 int expires,
663 const string& referred_by,
664 const string& extrahdrs)
665 {
666 if(getStatus() == Connected) {
667 string hdrs = SIP_HDR_COLSP(SIP_HDR_REFER_TO) + refer_to + CRLF;
668 if (expires>=0)
669 hdrs+= SIP_HDR_COLSP(SIP_HDR_EXPIRES) + int2str(expires) + CRLF;
670 hdrs+= extrahdrs;
671 if (!referred_by.empty())
672 hdrs+= SIP_HDR_COLSP(SIP_HDR_REFERRED_BY) + referred_by + CRLF;
673
674 return sendRequest("REFER", NULL, hdrs);
675 }
676 else {
677 DBG("refer(): we are not Connected."
678 "(status=%s). do nothing!\n",getStatusStr());
679
680 return 0;
681 }
682 }
683
info(const string & hdrs,const AmMimeBody * body)684 int AmSipDialog::info(const string& hdrs, const AmMimeBody* body)
685 {
686 if(getStatus() == Connected) {
687 return sendRequest("INFO", body, hdrs);
688 } else {
689 DBG("info(): we are not Connected."
690 "(status=%s). do nothing!\n", getStatusStr());
691 return 0;
692 }
693 }
694
695 // proprietary
transfer(const string & target)696 int AmSipDialog::transfer(const string& target)
697 {
698 if(getStatus() == Connected){
699
700 setStatus(Disconnecting);
701
702 string hdrs = "";
703 AmSipDialog tmp_d(*this);
704
705 tmp_d.route = "";
706 // TODO!!!
707 //tmp_d.contact_uri = SIP_HDR_COLSP(SIP_HDR_CONTACT)
708 // "<" + tmp_d.remote_uri + ">" CRLF;
709 tmp_d.remote_uri = target;
710
711 string r_set;
712 if(!route.empty()){
713
714 hdrs = PARAM_HDR ": " "Transfer-RR=\"" + route + "\""+CRLF;
715 }
716
717 int ret = tmp_d.sendRequest("REFER",NULL,hdrs);
718 if(!ret){
719 uac_trans.insert(tmp_d.uac_trans.begin(),
720 tmp_d.uac_trans.end());
721 cseq = tmp_d.cseq;
722 }
723
724 return ret;
725 }
726
727 DBG("transfer(): we are not connected "
728 "(status=%i). do nothing!\n",status);
729
730 return 0;
731 }
732
prack(const AmSipReply & reply1xx,const AmMimeBody * body,const string & hdrs)733 int AmSipDialog::prack(const AmSipReply &reply1xx,
734 const AmMimeBody* body,
735 const string &hdrs)
736 {
737 switch(getStatus()) {
738 case Trying:
739 case Proceeding:
740 case Cancelling:
741 case Early:
742 case Connected:
743 break;
744 case Disconnected:
745 case Disconnecting:
746 ERROR("can not send PRACK while dialog is in state '%d'.\n", status);
747 return -1;
748 default:
749 ERROR("BUG: unexpected dialog state '%d'.\n", status);
750 return -1;
751 }
752 string h = hdrs +
753 SIP_HDR_COLSP(SIP_HDR_RACK) +
754 int2str(reply1xx.rseq) + " " +
755 int2str(reply1xx.cseq) + " " +
756 reply1xx.cseq_method + CRLF;
757 return sendRequest(SIP_METH_PRACK, body, h);
758 }
759
cancel()760 int AmSipDialog::cancel()
761 {
762 for(TransMap::reverse_iterator t = uac_trans.rbegin();
763 t != uac_trans.rend(); t++) {
764
765 if(t->second.method == SIP_METH_INVITE){
766
767 if(getStatus() != Cancelling){
768 setStatus(Cancelling);
769 return SipCtrlInterface::cancel(&t->second.tt, local_tag,
770 t->first, t->second.hdrs);
771 }
772 else {
773 ERROR("INVITE transaction has already been cancelled\n");
774 return -1;
775 }
776 }
777 }
778
779 ERROR("could not find INVITE transaction to cancel\n");
780 return -1;
781 }
782
cancel(const string & hdrs)783 int AmSipDialog::cancel(const string& hdrs)
784 {
785 for(TransMap::reverse_iterator t = uac_trans.rbegin();
786 t != uac_trans.rend(); t++) {
787
788 if(t->second.method == SIP_METH_INVITE){
789
790 if(getStatus() != Cancelling){
791 setStatus(Cancelling);
792 return SipCtrlInterface::cancel(&t->second.tt, local_tag,
793 t->first, hdrs);
794 }
795 else {
796 ERROR("INVITE transaction has already been cancelled\n");
797 return -1;
798 }
799 }
800 }
801
802 ERROR("could not find INVITE transaction to cancel\n");
803 return -1;
804 }
805
drop()806 int AmSipDialog::drop()
807 {
808 setStatus(Disconnected);
809 return 1;
810 }
811
send_200_ack(unsigned int inv_cseq,const AmMimeBody * body,const string & hdrs,int flags)812 int AmSipDialog::send_200_ack(unsigned int inv_cseq,
813 const AmMimeBody* body,
814 const string& hdrs,
815 int flags)
816 {
817 // TODO: implement missing pieces from RFC 3261:
818 // "The ACK MUST contain the same credentials as the INVITE. If
819 // the 2xx contains an offer (based on the rules above), the ACK MUST
820 // carry an answer in its body. If the offer in the 2xx response is not
821 // acceptable, the UAC core MUST generate a valid answer in the ACK and
822 // then send a BYE immediately."
823
824 TransMap::iterator inv_it = uac_trans.find(inv_cseq);
825 if (inv_it == uac_trans.end()) {
826 ERROR("trying to ACK a non-existing transaction (cseq=%i;local_tag=%s)\n",
827 inv_cseq,local_tag.c_str());
828 return -1;
829 }
830
831 AmSipRequest req;
832
833 req.method = SIP_METH_ACK;
834 req.r_uri = remote_uri;
835
836 req.from = SIP_HDR_COLSP(SIP_HDR_FROM) + local_party;
837 if(!ext_local_tag.empty())
838 req.from += ";tag=" + ext_local_tag;
839 else if(!local_tag.empty())
840 req.from += ";tag=" + local_tag;
841
842 req.to = SIP_HDR_COLSP(SIP_HDR_TO) + remote_party;
843 if(!remote_tag.empty())
844 req.to += ";tag=" + remote_tag;
845
846 req.cseq = inv_cseq;// should be the same as the INVITE
847 req.callid = callid;
848 req.contact = getContactHdr();
849
850 req.route = getRoute();
851
852 req.max_forwards = inv_it->second.max_forwards;
853
854 if(body != NULL)
855 req.body = *body;
856
857 if(onTxRequest(req,flags) < 0)
858 return -1;
859
860 if (!(flags&SIP_FLAGS_VERBATIM)) {
861 // add Signature
862 if (AmConfig::Signature.length())
863 req.hdrs += SIP_HDR_COLSP(SIP_HDR_USER_AGENT) + AmConfig::Signature + CRLF;
864 }
865
866 int res = SipCtrlInterface::send(req, local_tag,
867 remote_tag.empty() || !next_hop_1st_req ?
868 next_hop : "",
869 outbound_interface, 0, logger);
870 if (res)
871 return res;
872
873 onRequestTxed(req);
874 return 0;
875 }
876
877
878 /** EMACS **
879 * Local variables:
880 * mode: c++
881 * c-basic-offset: 2
882 * End:
883 */
884