1 /*
2 * Copyright (C) 2012 Frafos 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
28 #include "AmSipSubscription.h"
29 #include "AmEventQueue.h"
30 #include "AmSipHeaders.h"
31 #include "AmAppTimer.h"
32 #include "AmUtils.h"
33 #include "jsonArg.h"
34
35 #include "AmSession.h" // getNewId()
36 #include "AmSessionContainer.h"
37
38 #include "sip/sip_timers.h"
39 #include "log.h"
40
41 #include <assert.h>
42
43 #define DEFAULT_SUB_EXPIRES 600
44
45 // TIMER N should first expire once transaction timer has hit
46 // in case we receive no reply to SUBSCRIBE.
47 #define RFC6665_TIMER_N_DURATION ((64 + 4)*T1_TIMER)/1000.0
48
49 #define SIP_HDR_SUBSCRIPTION_STATE "Subscription-State"
50 #define SIP_HDR_EVENT "Event"
51
52 const char* __timer_id_str[2] = {
53 "RFC6665_TIMER_N",
54 "SUBSCRIPTION_EXPIRE"
55 };
56
57 const char* __sub_state_str[] = {
58 "init",
59 "notify_wait",
60 "pending",
61 "active",
62 "terminated"
63 };
64
SingleSubscription(AmSipSubscription * subs,Role role,const string & event,const string & id)65 SingleSubscription::SingleSubscription(AmSipSubscription* subs, Role role,
66 const string& event, const string& id)
67 : sub_state(SubState_init), pending_subscribe(0), expires(0), timer_n(this,RFC6665_TIMER_N), timer_expires(this,SUBSCRIPTION_EXPIRE),
68 subs(subs), event(event),
69 id(id),role(role)
70 {
71 assert(subs);
72 }
73
dlg()74 AmBasicSipDialog* SingleSubscription::dlg()
75 {
76 return subs->dlg;
77 }
78
onTimer(int timer_id)79 void SingleSubscription::onTimer(int timer_id)
80 {
81 DBG("[%p] tag=%s;role=%s timer_id = %s\n",this,
82 dlg()->getLocalTag().c_str(),
83 role ? "Notifier" : "Subscriber",
84 __timer_id_str[timer_id]);
85
86 switch(timer_id){
87 case RFC6665_TIMER_N:
88 case SUBSCRIPTION_EXPIRE:
89 if(subs->ev_q) {
90 AmEvent* ev = new SingleSubTimeoutEvent(subs->dlg->getLocalTag(),
91 timer_id,this);
92 subs->ev_q->postEvent(ev);
93 }
94 return;
95 }
96 }
97
terminate()98 void SingleSubscription::terminate()
99 {
100 setState(SubState_terminated);
101 }
102
terminated()103 bool SingleSubscription::terminated()
104 {
105 return getState() == SubState_terminated;
106 }
107
108 SingleSubscription*
newSingleSubscription(SingleSubscription::Role role,const string & event,const string & id)109 AmSipSubscription::newSingleSubscription(SingleSubscription::Role role,
110 const string& event, const string& id)
111 {
112 return new SingleSubscription(this,role,event,id);
113 }
114
makeSubscription(const AmSipRequest & req,bool uac)115 SingleSubscription* AmSipSubscription::makeSubscription(const AmSipRequest& req,
116 bool uac)
117 {
118 SingleSubscription::Role role =
119 uac ?
120 SingleSubscription::Subscriber :
121 SingleSubscription::Notifier;
122
123 string event;
124 string id;
125
126 if(req.method == SIP_METH_SUBSCRIBE) {
127 // fetch Event-HF
128 event = getHeader(req.hdrs,SIP_HDR_EVENT,true);
129 id = get_header_param(event,"id");
130 event = strip_header_params(event);
131 }
132 else if(req.method == SIP_METH_REFER) {
133 //TODO: fetch Refer-Sub-HF (RFC 4488)
134 event = "refer";
135 id = int2str(req.cseq);
136 }
137 else {
138 DBG("subscription are only created by SUBSCRIBE or REFER requests\n");
139 // subscription are only created by SUBSCRIBE or REFER requests
140 // and we do not support unsolicited NOTIFYs
141 return NULL;
142 }
143
144 return newSingleSubscription(role,event,id);
145 }
146
~SingleSubscription()147 SingleSubscription::~SingleSubscription()
148 {
149 // just to be sure...
150 AmAppTimer::instance()->removeTimer(&timer_n);
151 // this one should still be active
152 AmAppTimer::instance()->removeTimer(&timer_expires);
153 }
154
requestFSM(const AmSipRequest & req)155 void SingleSubscription::requestFSM(const AmSipRequest& req)
156 {
157 if((req.method == SIP_METH_SUBSCRIBE) ||
158 (req.method == SIP_METH_REFER)) {
159
160 if(getState() == SubState_init) {
161 setState(SubState_notify_wait);
162 }
163
164 // start Timer N (RFC6665/4.1.2)
165 DBG("setTimer(%s,RFC6665_TIMER_N)\n",dlg()->getLocalTag().c_str());
166 AmAppTimer::instance()->setTimer(&timer_n,RFC6665_TIMER_N_DURATION);
167 }
168 else if(req.method == SIP_METH_NOTIFY) {
169 subs->onNotify(req,this);
170 }
171 }
172
onRequestIn(const AmSipRequest & req)173 bool SingleSubscription::onRequestIn(const AmSipRequest& req)
174 {
175 if((req.method == SIP_METH_SUBSCRIBE) ||
176 (req.method == SIP_METH_REFER)) {
177
178 if(pending_subscribe) {
179 dlg()->reply(req,500, SIP_REPLY_SERVER_INTERNAL_ERROR, NULL,
180 SIP_HDR_COLSP(SIP_HDR_RETRY_AFTER)
181 + int2str(get_random() % 10) + CRLF);
182 return false;
183 }
184 pending_subscribe++;
185 }
186
187 requestFSM(req);
188 return true;
189 }
190
onRequestSent(const AmSipRequest & req)191 void SingleSubscription::onRequestSent(const AmSipRequest& req)
192 {
193 //TODO: check pending_subscribe in onSendRequest
194 if((req.method == SIP_METH_SUBSCRIBE) ||
195 (req.method == SIP_METH_REFER)) {
196 pending_subscribe++;
197 }
198 requestFSM(req);
199 }
200
replyFSM(const AmSipRequest & req,const AmSipReply & reply)201 void SingleSubscription::replyFSM(const AmSipRequest& req, const AmSipReply& reply)
202 {
203 if(reply.code < 200)
204 return;
205
206 if((req.method == SIP_METH_SUBSCRIBE) ||
207 (req.method == SIP_METH_REFER)) {
208 // final reply
209
210 if(reply.code >= 300) {
211 if(getState() == SubState_notify_wait) {
212 // initial SUBSCRIBE failed
213 terminate();
214 subs->onFailureReply(reply,this);
215 }
216 else {
217 // subscription refresh failed
218 // from RFC 5057: terminate usage
219 switch(reply.code){
220 case 405:
221 case 489:
222 case 481:
223 case 501:
224 terminate();
225 subs->onFailureReply(reply,this);
226 break;
227 }
228 }
229 }
230 else {
231 // success
232
233 // set dialog identifier if not yet set
234 if(dlg()->getRemoteTag().empty()) {
235 dlg()->setRemoteTag(reply.to_tag);
236 dlg()->setRouteSet(reply.route);
237 }
238
239 // check Expires-HF
240 string expires_txt = getHeader(reply.hdrs,SIP_HDR_EXPIRES,true);
241 expires_txt = strip_header_params(expires_txt);
242
243 int sub_expires=0;
244 if(!expires_txt.empty() && str2int(expires_txt,sub_expires)){
245 if(sub_expires){
246 DBG("setTimer(%s,SUBSCRIPTION_EXPIRE)\n",dlg()->getLocalTag().c_str());
247 AmAppTimer::instance()->setTimer(&timer_expires,(double)sub_expires);
248 expires = sub_expires + AmAppTimer::instance()->unix_clock.get();
249
250 DBG("removeTimer(%s,RFC6665_TIMER_N)\n",dlg()->getLocalTag().c_str());
251 AmAppTimer::instance()->removeTimer(&timer_n);
252 }
253 else {
254 // we do not care too much, as timer N is set
255 // for each SUBSCRIBE request
256 DBG("Expires-HF equals 0\n");
257 }
258 }
259 else if(reply.cseq_method == SIP_METH_SUBSCRIBE){
260 // Should we really enforce that?
261 // -> we still have timer N...
262
263 // replies to SUBSCRIBE MUST contain a Expires-HF
264 // if not, or if not readable, we should probably
265 // quit the subscription
266 DBG("replies to SUBSCRIBE MUST contain a Expires-HF\n");
267 terminate();
268 subs->onFailureReply(reply,this);
269 }
270 }
271
272 pending_subscribe--;
273 }
274 else if(reply.cseq_method == SIP_METH_NOTIFY) {
275
276 if(reply.code >= 300) {
277 // final error reply
278 // from RFC 5057: terminate usage
279 switch(reply.code){
280 case 405:
281 case 481:
282 case 489:
283 case 501:
284 terminate();
285 subs->onFailureReply(reply,this);
286 break;
287
288 default:
289 // all other response codes:
290 // only the transaction fails
291 break;
292 }
293
294 return;
295 }
296
297 // check Subscription-State-HF
298 string sub_state_txt = getHeader(req.hdrs,SIP_HDR_SUBSCRIPTION_STATE,true);
299 string expires_txt = get_header_param(sub_state_txt,"expires");
300 int notify_expire=0;
301
302 if(!expires_txt.empty())
303 str2int(expires_txt,notify_expire);
304
305 // Kill timer N
306 DBG("removeTimer(%s,RFC6665_TIMER_N)\n",dlg()->getLocalTag().c_str());
307 AmAppTimer::instance()->removeTimer(&timer_n);
308
309 sub_state_txt = strip_header_params(sub_state_txt);
310 if(notify_expire && (sub_state_txt == "active")) {
311 setState(SubState_active);
312 }
313 else if(notify_expire && (sub_state_txt == "pending")){
314 setState(SubState_pending);
315 }
316 else {
317 terminate();
318 //subs->onFailureReply(reply,this);
319 return;
320 }
321
322 // reset expire timer
323 DBG("setTimer(%s,SUBSCRIPTION_EXPIRE)\n",dlg()->getLocalTag().c_str());
324 AmAppTimer::instance()->setTimer(&timer_expires,(double)notify_expire);
325 expires = notify_expire + AmAppTimer::instance()->unix_clock.get();
326 }
327
328 return;
329 }
330
setExpires(unsigned long exp)331 void SingleSubscription::setExpires(unsigned long exp)
332 {
333 double notify_expire = exp - AmAppTimer::instance()->unix_clock.get();
334 if(notify_expire > 0.0) {
335 AmAppTimer::instance()->setTimer(&timer_expires,notify_expire);
336 expires = exp;
337 }
338 else {
339 DBG("new 'expires' is already expired: sending event");
340 onTimer(SUBSCRIPTION_EXPIRE);
341 }
342 }
343
setState(unsigned int st)344 void SingleSubscription::setState(unsigned int st)
345 {
346 DBG("st = %s\n",__sub_state_str[st]);
347
348 if(sub_state == SubState_terminated)
349 return;
350
351 if(st == SubState_terminated) {
352 sub_state = SubState_terminated;
353 dlg()->decUsages();
354 }
355 else {
356 sub_state = st;
357 }
358 }
359
to_str()360 string SingleSubscription::to_str()
361 {
362 return "["
363 + str2json(event) + ","
364 + str2json(id) + ","
365 + (role == Subscriber ? str2json("SUB") : str2json("NOT")) + ","
366 + str2json(__sub_state_str[sub_state]) + "]";
367 }
368
369 /**
370 * AmSipSubscription
371 */
372
AmSipSubscription(AmBasicSipDialog * dlg,AmEventQueue * ev_q)373 AmSipSubscription::AmSipSubscription(AmBasicSipDialog* dlg, AmEventQueue* ev_q)
374 : dlg(dlg), ev_q(ev_q)
375 {
376 assert(dlg);
377 }
378
~AmSipSubscription()379 AmSipSubscription::~AmSipSubscription()
380 {
381 while(!subs.empty()) {
382 DBG("removing single subscription");
383 removeSubscription(subs.begin());
384 }
385 }
386
isActive()387 bool AmSipSubscription::isActive()
388 {
389 for(Subscriptions::iterator it=subs.begin();
390 it != subs.end(); it++) {
391 if((*it)->getState() == SingleSubscription::SubState_active)
392 return true;
393 }
394
395 return false;
396 }
397
terminate()398 void AmSipSubscription::terminate()
399 {
400 for(Subscriptions::iterator it=subs.begin();
401 it != subs.end(); it++) {
402 (*it)->terminate();
403 }
404 }
405
subscriptionExists(SingleSubscription::Role role,const string & event,const string & id)406 bool AmSipSubscription::subscriptionExists(SingleSubscription::Role role,
407 const string& event, const string& id)
408 {
409 return findSubscription(role,event,id) != subs.end();
410 }
411
412 AmSipSubscription::Subscriptions::iterator
findSubscription(SingleSubscription::Role role,const string & event,const string & id)413 AmSipSubscription::findSubscription(SingleSubscription::Role role,
414 const string& event, const string& id)
415 {
416 Subscriptions::iterator match = subs.end();
417 bool no_id = id.empty() && (event == "refer");
418
419 DBG("searching for event='%s'; id='%s'; no_id=%i",
420 event.c_str(),id.c_str(),no_id);
421
422 for(Subscriptions::iterator it = subs.begin();
423 it != subs.end(); it++) {
424
425 SingleSubscription* sub = *it;
426
427 DBG("role='%s';event='%s';id='%s'",
428 sub->role ? "Notifier" : "Subscriber",
429 sub->event.c_str(), sub->id.c_str());
430
431 if( (sub->role == role) &&
432 (sub->event == event) &&
433 (no_id || (sub->id == id)) ){
434
435 match = it;
436 DBG("\tmatched!");
437 break;
438 }
439 }
440
441 if((match != subs.end()) && (*match)->terminated()) {
442 DBG("matched terminated subscription: deleting it first\n");
443 removeSubscription(match);
444 match = subs.end();
445 }
446
447 return match;
448 }
449
450 AmSipSubscription::Subscriptions::iterator
createSubscription(const AmSipRequest & req,bool uac)451 AmSipSubscription::createSubscription(const AmSipRequest& req, bool uac)
452 {
453 SingleSubscription* sub = makeSubscription(req,uac);
454 if(!sub){
455 return subs.end();
456 }
457
458 dlg->incUsages();
459 DBG("new subscription: %s",sub->to_str().c_str());
460 return subs.insert(subs.end(),sub);
461 }
462
removeSubFromUACCSeqMap(Subscriptions::iterator sub)463 void AmSipSubscription::removeSubFromUACCSeqMap(Subscriptions::iterator sub)
464 {
465 for (CSeqMap::iterator i = uac_cseq_map.begin(); i != uac_cseq_map.end();) {
466 if (i->second == sub) {
467 DBG("removing UAC subnot transaction with cseq=%i",i->first);
468 CSeqMap::iterator del_i = i; ++i;
469 uac_cseq_map.erase(del_i);
470 continue;
471 }
472 ++i;
473 }
474 }
475
removeSubFromUASCSeqMap(Subscriptions::iterator sub)476 void AmSipSubscription::removeSubFromUASCSeqMap(Subscriptions::iterator sub)
477 {
478 for (CSeqMap::iterator i = uas_cseq_map.begin(); i != uas_cseq_map.end();) {
479 if (i->second == sub) {
480
481 unsigned int cseq = i->first;
482 CSeqMap::iterator del_i = i; ++i;
483 uas_cseq_map.erase(del_i);
484
485 DBG("removed UAS subnot transaction with cseq=%i",cseq);
486
487 // reply pending UAS transaction
488 AmSipRequest* req = dlg->getUASTrans(cseq);
489 if(req) {
490 DBG("found request(cseq=%i): replying 481 to pending UAS transaction",
491 req->cseq);
492 dlg->reply(*req,481,SIP_REPLY_NOT_EXIST);
493 }
494 else {
495 DBG("request not found: could not reply 481 to pending UAS transaction");
496 }
497
498 continue;
499 }
500
501 ++i;
502 }
503 }
504
removeSubscription(Subscriptions::iterator sub)505 void AmSipSubscription::removeSubscription(Subscriptions::iterator sub)
506 {
507 removeSubFromUACCSeqMap(sub);
508 removeSubFromUASCSeqMap(sub);
509
510 delete *sub;
511 subs.erase(sub);
512 }
513
514 /**
515 * match single subscription
516 * if none, create one
517 */
518 AmSipSubscription::Subscriptions::iterator
matchSubscription(const AmSipRequest & req,bool uac)519 AmSipSubscription::matchSubscription(const AmSipRequest& req, bool uac)
520 {
521 if((!uac && req.to_tag.empty()) || (uac && dlg->getRemoteTag().empty())
522 || (req.method == SIP_METH_REFER) || subs.empty()) {
523
524 DBG("no to-tag, REFER or subs empty: create new subscription\n");
525 return createSubscription(req,uac);
526 }
527
528 SingleSubscription::Role role;
529 string event;
530 string id;
531
532 if(req.method == SIP_METH_SUBSCRIBE) {
533 role = uac ? SingleSubscription::Subscriber : SingleSubscription::Notifier;
534 }
535 else if(req.method == SIP_METH_NOTIFY){
536 role = uac ? SingleSubscription::Notifier : SingleSubscription::Subscriber;
537 }
538 else {
539 DBG("unsupported request\n");
540 return subs.end();
541 }
542
543 // parse Event-HF
544 event = getHeader(req.hdrs,SIP_HDR_EVENT,true);
545 id = get_header_param(event,"id");
546 event = strip_header_params(event);
547
548 Subscriptions::iterator match = findSubscription(role,event,id);
549 if(match == subs.end()){
550 if(req.method == SIP_METH_SUBSCRIBE) {
551 // no match... new subscription?
552 DBG("no match found, SUBSCRIBE: create new subscription\n");
553 return createSubscription(req,uac);
554 }
555 }
556
557 return match;
558 }
559
onRequestIn(const AmSipRequest & req)560 bool AmSipSubscription::onRequestIn(const AmSipRequest& req)
561 {
562 // UAS side
563 Subscriptions::iterator sub_it = matchSubscription(req,false);
564 if((sub_it == subs.end()) || (*sub_it)->terminated()) {
565
566 if((sub_it == subs.end()) && (req.method == SIP_METH_NOTIFY)
567 && allow_subless_notify) {
568 return true;
569 }
570
571 dlg->reply(req, 481, SIP_REPLY_NOT_EXIST);
572 return false;
573 }
574
575 // process request;
576 uas_cseq_map[req.cseq] = sub_it;
577 return (*sub_it)->onRequestIn(req);
578 }
579
onRequestSent(const AmSipRequest & req)580 void AmSipSubscription::onRequestSent(const AmSipRequest& req)
581 {
582 // UAC side
583 Subscriptions::iterator sub_it = matchSubscription(req,true);
584 if(sub_it == subs.end()){
585
586 if((req.method == SIP_METH_NOTIFY)
587 && allow_subless_notify) {
588 return;
589 }
590
591 // should we exclude this case in onSendRequest???
592 ERROR("we just sent a request for which we could obtain no subscription\n");
593 return;
594 }
595
596 // process request;
597 uac_cseq_map[req.cseq] = sub_it;
598 (*sub_it)->onRequestSent(req);
599 }
600
onReplyIn(const AmSipRequest & req,const AmSipReply & reply)601 bool AmSipSubscription::onReplyIn(const AmSipRequest& req,
602 const AmSipReply& reply)
603 {
604 // UAC side
605 CSeqMap::iterator cseq_it = uac_cseq_map.find(req.cseq);
606 if(cseq_it == uac_cseq_map.end()){
607
608 if((req.method == SIP_METH_NOTIFY)
609 && allow_subless_notify) {
610 return true;
611 }
612
613 DBG("could not find %i in our uac_cseq_map\n",req.cseq);
614 return false;
615 }
616
617 Subscriptions::iterator sub_it = cseq_it->second;
618 SingleSubscription* sub = *sub_it;
619 uac_cseq_map.erase(cseq_it);
620
621 sub->replyFSM(req,reply);
622 if(sub->terminated()){
623 removeSubscription(sub_it);
624 }
625
626 return true;
627 }
628
onReplySent(const AmSipRequest & req,const AmSipReply & reply)629 void AmSipSubscription::onReplySent(const AmSipRequest& req,
630 const AmSipReply& reply)
631 {
632 // UAS side
633 CSeqMap::iterator cseq_it = uas_cseq_map.find(req.cseq);
634 if(cseq_it == uas_cseq_map.end())
635 return;
636
637 Subscriptions::iterator sub_it = cseq_it->second;
638 SingleSubscription* sub = *sub_it;
639 uas_cseq_map.erase(cseq_it);
640
641 sub->replyFSM(req,reply);
642 if(sub->terminated()){
643 removeSubscription(sub_it);
644 }
645 }
646
onTimeout(int timer_id,SingleSubscription * sub)647 void AmSipSubscription::onTimeout(int timer_id, SingleSubscription* sub)
648 {
649 Subscriptions::iterator it = subs.begin();
650 for(; it != subs.end(); it++) {
651 if(*it == sub) break;
652 }
653 if(it == subs.end())
654 return; // no match...
655
656 sub->terminate();
657 removeSubscription(it);
658 }
659
debug()660 void AmSipSubscription::debug()
661 {
662 DBG("subscriptions with lt=%s:",dlg->getLocalTag().c_str());
663 for(Subscriptions::iterator it = subs.begin(); it != subs.end(); it++) {
664 DBG("\t%s",(*it)->to_str().c_str());
665 }
666 }
667
668
SIPSubscriptionEvent(SubscriptionStatus status,const string & handle,unsigned int expires,unsigned int code,const string & reason)669 SIPSubscriptionEvent::SIPSubscriptionEvent(SubscriptionStatus status,
670 const string& handle,
671 unsigned int expires,
672 unsigned int code,
673 const string& reason)
674 : AmEvent(E_SIP_SUBSCRIPTION), handle(handle),
675 code(code), reason(reason), status(status),
676 expires(expires), notify_body(nullptr)
677 {}
678
getStatusText()679 const char* SIPSubscriptionEvent::getStatusText()
680 {
681 switch (status) {
682 case SubscribeActive: return "active";
683 case SubscribeFailed: return "failed";
684 case SubscribeTerminated: return "terminated";
685 case SubscribePending: return "pending";
686 case SubscriptionTimeout: return "timeout";
687 }
688 return "unknown";
689 }
690
AmSipSubscriptionDialog(const AmSipSubscriptionInfo & info,const string & sess_link,AmEventQueue * ev_q)691 AmSipSubscriptionDialog::AmSipSubscriptionDialog(const AmSipSubscriptionInfo& info,
692 const string& sess_link,
693 AmEventQueue* ev_q)
694 : AmBasicSipDialog(this),
695 AmSipSubscription(this,ev_q),
696 sess_link(sess_link)
697 {
698 user = info.user;
699 domain = info.domain;
700
701 local_uri = "sip:"+info.from_user+"@"+info.domain;
702 local_party = "<"+local_uri+">";
703
704 remote_uri = "sip:"+info.user+"@"+info.domain;
705 remote_party = "<"+remote_uri+">";
706
707 callid = AmSession::getNewId();
708 local_tag = AmSession::getNewId();
709
710 event = info.event;
711 event_id = info.id;
712 accept = info.accept;
713 }
714
subscribe(int expires)715 int AmSipSubscriptionDialog::subscribe(int expires)
716 {
717 string hdrs;
718
719 if(!event.empty()){
720 hdrs += SIP_HDR_COLSP(SIP_HDR_EVENT) + event;
721 if(!event_id.empty())
722 hdrs += ";id=" + event_id;
723 hdrs += CRLF;
724 }
725
726 if (!accept.empty()) {
727 hdrs += SIP_HDR_COLSP(SIP_HDR_ACCEPT) + accept + CRLF;
728 }
729 if (expires >= 0) {
730 hdrs += SIP_HDR_COLSP(SIP_HDR_EXPIRES) + int2str(expires) + CRLF;
731 }
732
733 return sendRequest(SIP_METH_SUBSCRIBE,NULL,hdrs);
734 }
735
getDescription()736 string AmSipSubscriptionDialog::getDescription()
737 {
738 return "'"+user+"@"+domain+", Event: "+event+"/"+event_id+"'";
739 }
740
onNotify(const AmSipRequest & req,SingleSubscription * sub)741 void AmSipSubscriptionDialog::onNotify(const AmSipRequest& req,
742 SingleSubscription* sub)
743 {
744 assert(sub);
745
746 // subscription state is update after the reply has been sent
747 reply(req, 200, "OK");
748
749 SIPSubscriptionEvent* sub_ev =
750 new SIPSubscriptionEvent(SIPSubscriptionEvent::SubscribeFailed, local_tag);
751
752 switch(sub->getState()){
753 case SingleSubscription::SubState_pending:
754 sub_ev->status = SIPSubscriptionEvent::SubscribePending;
755 sub_ev->expires = sub->getExpires();
756 break;
757 case SingleSubscription::SubState_active:
758 sub_ev->status = SIPSubscriptionEvent::SubscribeActive;
759 sub_ev->expires = sub->getExpires();
760 break;
761 case SingleSubscription::SubState_terminated:
762 sub_ev->status = SIPSubscriptionEvent::SubscribeTerminated;
763 break;
764 default:
765 break;
766 }
767
768 if(!req.body.empty())
769 sub_ev->notify_body.reset(new AmMimeBody(req.body));
770
771 DBG("posting event to '%s'\n", sess_link.c_str());
772 AmSessionContainer::instance()->postEvent(sess_link, sub_ev);
773 }
774
onFailureReply(const AmSipReply & reply,SingleSubscription * sub)775 void AmSipSubscriptionDialog::onFailureReply(const AmSipReply& reply,
776 SingleSubscription* sub)
777 {
778 assert(sub);
779 SIPSubscriptionEvent* sub_ev =
780 new SIPSubscriptionEvent(SIPSubscriptionEvent::SubscribeFailed,
781 local_tag, 0, reply.code, reply.reason);
782
783 DBG("posting event to '%s'\n", sess_link.c_str());
784 AmSessionContainer::instance()->postEvent(sess_link, sub_ev);
785 }
786
onTimeout(int timer_id,SingleSubscription * sub)787 void AmSipSubscriptionDialog::onTimeout(int timer_id, SingleSubscription* sub)
788 {
789 AmSipSubscription::onTimeout(timer_id,sub);
790
791 // possibly we've got a timeout for an already destroyed subscription.
792 // however, it only happens if the subscription has been destroyed right
793 // before this event (same processEvents() call).
794
795 SIPSubscriptionEvent* sub_ev =
796 new SIPSubscriptionEvent(SIPSubscriptionEvent::SubscriptionTimeout, local_tag);
797
798 DBG("posting event to '%s'\n", sess_link.c_str());
799 AmSessionContainer::instance()->postEvent(sess_link, sub_ev);
800 }
801