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