reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
ServerSubscription.cxx
Go to the documentation of this file.
9#include "rutil/Logger.hxx"
10
11#include <time.h>
12
13using namespace resip;
14
15#define RESIPROCATE_SUBSYSTEM Subsystem::DUM
16
22
24 Dialog& dialog,
25 const SipMessage& req)
26 : BaseSubscription(dum, dialog, req),
27 mSubscriber(req.header(h_From).uri().getAor()),
28 mExpires(60),
29 mAbsoluteExpiry(0)
30{
31 if (req.header(h_RequestLine).method() == REFER && req.header(h_To).exists(p_tag))
32 {
33 // If this is an in-dialog REFER, then use a subscription id
34 mSubscriptionId = Data(req.header(h_CSeq).sequence());
35 }
37 mDum.mServerSubscriptions.insert(DialogUsageManager::ServerSubscriptions::value_type(key, this));
38}
39
41{
42 DebugLog(<< "ServerSubscription::~ServerSubscription");
43
45
46 std::pair<DialogUsageManager::ServerSubscriptions::iterator,DialogUsageManager::ServerSubscriptions::iterator> subs;
47 subs = mDum.mServerSubscriptions.equal_range(key);
48 for (DialogUsageManager::ServerSubscriptions::iterator i=subs.first; i!=subs.second; ++i)
49 {
50 if (i->second == this)
51 {
52 mDum.mServerSubscriptions.erase(i);
53 break;
54 }
55 }
56
57 mDialog.mServerSubscriptions.remove(this);
58}
59
62{
63 uint32_t timeleft = uint32_t(mAbsoluteExpiry - Timer::getTimeSecs());
64 if (timeleft < 0) // .kw. this can NEVER happen since unsigned!
65 {
66 return 0;
67 }
68 else
69 {
70 return timeleft;
71 }
72}
73
74std::shared_ptr<SipMessage>
76{
77 if (!mLastResponse)
78 {
79 throw UsageUseException("mLastResponse is not set, likely trying to respond a 2nd time", __FILE__, __LINE__);
80 }
81 // Response is built in dispatch when request arrives, just need to adjust the status code here
82 mLastResponse->header(h_StatusLine).responseCode() = statusCode;
83 Helper::getResponseCodeReason(statusCode, mLastResponse->header(h_StatusLine).reason());
84 mLastResponse->header(h_Expires).value() = mExpires;
85 return mLastResponse;
86}
87
88std::shared_ptr<SipMessage>
90{
91 if (statusCode < 300)
92 {
93 throw UsageUseException("Must reject with a code greater than or equal to 300", __FILE__, __LINE__);
94 }
95 if (!mLastResponse)
96 {
97 throw UsageUseException("mLastResponse is not set, likely trying to respond a 2nd time", __FILE__, __LINE__);
98 }
99 // Response is built in dispatch when request arrives, just need to adjust the status code here
100 mLastResponse->header(h_StatusLine).responseCode() = statusCode;
101 Helper::getResponseCodeReason(statusCode, mLastResponse->header(h_StatusLine).reason());
102 mLastResponse->remove(h_Contacts); // Remove any contact header for non-success response
103 return mLastResponse;
104}
105
106void ServerSubscription::terminateSubscription(ServerSubscriptionHandler* handler)
107{
108 handler->onTerminated(getHandle());
109 delete this;
110}
111
112void
113ServerSubscription::send(std::shared_ptr<SipMessage> msg)
114{
116 resip_assert(handler);
117 if (msg->isResponse())
118 {
119 mLastResponse.reset(); // Release ref count on memory - so message goes away when send is done
120 int code = msg->header(h_StatusLine).statusCode();
121 if (code < 200)
122 {
124 }
125 else if (code < 300)
126 {
127 if(msg->exists(h_Expires))
128 {
129 mDum.addTimer(DumTimeout::Subscription, msg->header(h_Expires).value(), getBaseHandle(), ++mTimerSeq);
131 mAbsoluteExpiry = Timer::getTimeSecs() + msg->header(h_Expires).value();
133 }
134 else
135 {
136 throw UsageUseException("2xx to a Subscribe MUST contain an Expires header", __FILE__, __LINE__);
137 }
138 }
139 else if (code < 400)
140 {
142 terminateSubscription(handler);
143 return;
144 }
145 else
146 {
147 if (shouldDestroyAfterSendingFailure(*msg))
148 {
150 terminateSubscription(handler);
151 return;
152 }
153 else
154 {
156 }
157 }
158 }
159 else
160 {
163 {
164 terminateSubscription(handler);
165 }
166 }
167}
168
169bool
170ServerSubscription::shouldDestroyAfterSendingFailure(const SipMessage& msg)
171{
172 int code = msg.header(h_StatusLine).statusCode();
173 switch(mSubDlgState)
174 {
175 case SubDlgInitial:
176 return true;
177 case SubDlgTerminating: //terminated state not using in ServerSubscription
178 resip_assert(0);
179 return true;
181 {
182 if (code == 405)
183 {
184 return true;
185 }
187 {
190 break;
193 // .bwc. Uh, no. ApplicationDependent should imply that the
194 // app-writer has decided what to do. We don't decide here. And
195 // OptionalRetryAfter certainly doesn't mean we should tear the
196 // Usage down.
197// throw UsageUseException("Not a reasonable code to reject a SUBSCIRBE(refresh) inside a dialog.",
198// __FILE__, __LINE__);
199 break;
200 case Helper::DialogTermination: //?dcm? -- throw or destroy this?
202 return true;
203 }
204 break;
205 }
206 default: // !jf!
207 resip_assert(0);
208 break;
209 }
210 return false;
211}
212
213void
215{
216 // Don't allow a transition out of Terminated state
218 {
219 mSubscriptionState = state;
220 }
221}
222
223void
225{
226 DebugLog( << "ServerSubscription::dispatch: " << msg.brief());
227
229 resip_assert(handler);
230
231 if (msg.isRequest())
232 {
234 //expiration times for an event package--part of handler API?
235 //added to handler for now.
236 if (!mLastResponse)
237 {
238 mLastResponse = std::make_shared<SipMessage>();
239 }
240 mDialog.makeResponse(*mLastResponse, msg, 200); // Generate response now and wait for user to accept or reject, then adjust status code
241
242 int errorResponseCode = 0;
243 handler->getExpires(msg,mExpires,errorResponseCode);
244 if (errorResponseCode >= 400)
245 {
246 handler->onError(getHandle(), msg);
247 auto response = reject(errorResponseCode);
248
249 if (errorResponseCode == 423 && handler->hasMinExpires())
250 {
251 response->header(h_MinExpires).value() = handler->getMinExpires();
252 }
253 send(response);
254 return;
255 }
256
257 InviteSessionHandle invSession;
258 if (getAppDialog().isValid())
259 {
260 invSession = getAppDialog()->getInviteSession();
261 }
262
263 if (mExpires == 0)
264 {
265 /* This is to handle the case where mExpires is zero because the client
266 is attempting to poll. In order for polling to work, the subscription
267 handler needs to get the onNewSubscription call. .mjf.
268 */
270 {
271 // Move to terminated state - application is not allowed to switch out of this state. This
272 // allows applications to treat polling requests just like normal subscriptions. Any attempt
273 // to call setSubscriptionState will NoOp.
275
276 if (mEventType != "refer" )
277 {
278 handler->onNewSubscription(getHandle(), msg);
279 }
280 else if (!invSession.isValid())
281 {
282 handler->onNewSubscriptionFromRefer(getHandle(), msg);
283 }
284
285 // note: it might be nice to call onExpiredByClient here, but it's dangerous, since inline calls
286 // to reject or the inline sending of a Notify in the onNewSubscription handler will cause
287 // 'this' to be deleted
288 return;
289 }
290
291 makeNotifyExpires(); // builds a NOTIFY message into mLastRequest
292 handler->onExpiredByClient(getHandle(), msg, *mLastRequest);
293
294 // Send 200 response to sender
295 mLastResponse->header(h_Expires).value() = mExpires;
297
298 // Send Notify Expires
300 return;
301 }
303 {
306 if (mEventType != "refer")
307 {
308 DebugLog(<< "onNewSubscription called");
309 handler->onNewSubscription(getHandle(), msg);
310 }
311 else if (!invSession.isValid())
312 {
313 DebugLog(<< "onNewSubscriptionFromRefer called");
314 handler->onNewSubscriptionFromRefer(getHandle(), msg);
315 }
316 }
317 else
318 {
319 DebugLog(<< "onRefresh called");
320 handler->onRefresh(getHandle(), msg);
321 }
322 }
323 else
324 {
325 //.dcm. - will need to change if retry-afters are reaching here
326 //mLastRequest->releaseContents();
327 mLastRequest.reset(); // Release ref count on memory - so message goes away when send is done
328 int code = msg.header(h_StatusLine).statusCode();
329
330 if(code < 200)
331 {
332 return;
333 }
334 else if (code < 300)
335 {
336 handler->onNotifyAccepted(getHandle(), msg);
337 return;
338 }
339 else if (code < 400)
340 {
341 //in dialog NOTIFY got redirected? Bizarre...
342 handler->onError(getHandle(), msg);
343 terminateSubscription(handler);
344 }
345 else
346 {
348 (mDum.getMasterProfile()->additionalTransactionTerminatingResponsesEnabled()) ?
349 &mDum.getMasterProfile()->getAdditionalTransactionTerminatingResponses() : NULL))
350 {
352 DebugLog( << "ServerSubscription::TransactionTermination: " << msg.brief());
353 handler->onNotifyRejected(getHandle(), msg);
354 break;
360 DebugLog( << "ServerSubscription::UsageTermination: " << msg.brief());
361 handler->onError(getHandle(), msg);
362 terminateSubscription(handler);
363 break;
364 }
365 }
366 }
367}
368
369void
370ServerSubscription::makeNotifyExpires()
371{
373 makeNotify();
374 mLastRequest->header(h_SubscriptionState).param(p_reason) = getTerminateReasonString(Timeout);
375}
376
377void
378ServerSubscription::makeNotify()
379{
380 if (!mLastRequest)
381 {
382 mLastRequest = std::make_shared<SipMessage>();
383 }
385 mLastRequest->header(h_SubscriptionState).value() = getSubscriptionStateString(mSubscriptionState);
387 {
388 mLastRequest->header(h_SubscriptionState).remove(p_expires);
389 }
390 else
391 {
392 mLastRequest->header(h_SubscriptionState).param(p_expires) = getTimeLeft();
393 }
394
395 mLastRequest->header(h_Event).value() = mEventType;
396 if (!mSubscriptionId.empty())
397 {
398 mLastRequest->header(h_Event).param(p_id) = mSubscriptionId;
399 }
400}
401
402
403void
404ServerSubscription::end(TerminateReason reason, const Contents* document, int retryAfter)
405{
406 if (mSubscriptionState != Terminated) // NoOp if called twice or already ending
407 {
409 makeNotify();
410 mLastRequest->header(h_SubscriptionState).param(p_reason) = getTerminateReasonString(reason);
411 if (document)
412 {
413 mLastRequest->setContents(document);
414 }
415 if (retryAfter != 0)
416 {
417 mLastRequest->header(h_SubscriptionState).param(p_retryAfter) = retryAfter;
418 }
420 }
421}
422
423void
428
429void
431{
433 if (timeout.seq() == mTimerSeq)
434 {
436 resip_assert(handler);
437 makeNotifyExpires();
438 handler->onExpired(getHandle(), *mLastRequest);
440 }
441}
442
443std::shared_ptr<SipMessage>
445{
446 makeNotify();
447 mLastRequest->setContents(document);
448 return mLastRequest;
449}
450
451std::shared_ptr<SipMessage>
453{
454 makeNotify();
455 mLastRequest->releaseContents();
456 return mLastRequest;
457}
458
459void
466
467void
475
477ServerSubscription::dump(EncodeStream& strm) const
478{
479 strm << "ServerSubscription " << mSubscriber;
480 return strm;
481}
482
483/* ====================================================================
484 * The Vovida Software License, Version 1.0
485 *
486 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
487 *
488 * Redistribution and use in source and binary forms, with or without
489 * modification, are permitted provided that the following conditions
490 * are met:
491 *
492 * 1. Redistributions of source code must retain the above copyright
493 * notice, this list of conditions and the following disclaimer.
494 *
495 * 2. Redistributions in binary form must reproduce the above copyright
496 * notice, this list of conditions and the following disclaimer in
497 * the documentation and/or other materials provided with the
498 * distribution.
499 *
500 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
501 * and "Vovida Open Communication Application Library (VOCAL)" must
502 * not be used to endorse or promote products derived from this
503 * software without prior written permission. For written
504 * permission, please contact vocal@vovida.org.
505 *
506 * 4. Products derived from this software may not be called "VOCAL", nor
507 * may "VOCAL" appear in their name, without prior written
508 * permission of Vovida Networks, Inc.
509 *
510 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
511 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
512 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
513 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
514 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
515 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
516 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
517 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
518 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
519 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
520 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
521 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
522 * DAMAGE.
523 *
524 * ====================================================================
525 *
526 * This software consists of voluntary contributions made by Vovida
527 * Networks, Inc. and many individuals on behalf of Vovida Networks,
528 * Inc. For more information on Vovida Networks, Inc., please see
529 * <http://www.vovida.org/>.
530 *
531 */
#define DebugLog(args_)
Definition Logger.hxx:128
#define resip_assert(x)
This file needs to work for both C and C++ code.
static Data getAor(const Data &filename, const Security::PEMType &pemType)
Definition Security.cxx:94
dcm! – update contact in dialog if required
std::shared_ptr< SipMessage > mLastResponse
const Data & getEventType() const noexcept
SubscriptionState mSubscriptionState
const Data & getId() const noexcept
const Data & getDocumentKey() const noexcept
std::shared_ptr< SipMessage > mLastRequest
BaseUsageHandle getBaseHandle() const
Definition BaseUsage.cxx:36
DialogUsageManager & mDum
Definition BaseUsage.hxx:43
Base class for all SIP body types.
Definition Contents.hxx:51
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
bool empty() const noexcept
Checks whether the Data is empty.
Definition Data.hxx:591
ServerSubscriptionHandler * getServerSubscriptionHandler(const Data &eventType)
std::shared_ptr< MasterProfile > & getMasterProfile()
AppDialogHandle getAppDialog() const
virtual void send(std::shared_ptr< SipMessage > msg)
dcm! – kill typedef std::list<DialogId> DialogIdSet;
Definition Dialog.hxx:29
void makeRequest(SipMessage &request, MethodTypes method, bool incrementCSeq=true)
Definition Dialog.cxx:1033
void makeResponse(SipMessage &response, const SipMessage &request, int responseCode)
Definition Dialog.cxx:1108
Type type() const
unsigned int seq() const
bool isValid() const
Definition Handle.hxx:25
static void getResponseCodeReason(int responseCode, Data &reason)
Returns the default reason string for a particular response code.
Definition Helper.cxx:610
@ TransactionTermination
Definition Helper.hxx:614
@ ApplicationDependant
Definition Helper.hxx:615
static FailureMessageEffect determineFailureMessageEffect(const SipMessage &response, const std::set< int > *additionalTransactionTerminatingResponses=NULL)
Definition Helper.cxx:2151
virtual Brief brief() const
return a facet for brief encoding of message
Definition Message.cxx:16
MethodTypes & method()
virtual void onNewSubscription(ServerSubscriptionHandle, const SipMessage &sub)=0
virtual void onFlowTerminated(ServerSubscriptionHandle)
Called when a TCP or TLS flow to the server has terminated.
virtual void onExpiredByClient(ServerSubscriptionHandle, const SipMessage &sub, SipMessage &notify)
virtual void onNewSubscriptionFromRefer(ServerSubscriptionHandle, const SipMessage &sub)
virtual void onRefresh(ServerSubscriptionHandle, const SipMessage &sub)
virtual void onNotifyAccepted(ServerSubscriptionHandle, const SipMessage &msg)
virtual void onExpired(ServerSubscriptionHandle, SipMessage &notify)
virtual uint32_t getMinExpires() const
virtual void onNotifyRejected(ServerSubscriptionHandle, const SipMessage &msg)
virtual void onError(ServerSubscriptionHandle, const SipMessage &msg)
virtual void onReadyToSend(ServerSubscriptionHandle, SipMessage &msg)
virtual void onTerminated(ServerSubscriptionHandle)=0
virtual void getExpires(const SipMessage &msg, uint32_t &expires, int &errorResponseCode)
Default behavior is to use the expires value in the SipMessage, if it exists.
void dispatch(const SipMessage &msg) override
std::shared_ptr< SipMessage > neutralNotify()
void setSubscriptionState(SubscriptionState state)
ServerSubscriptionHandle getHandle() const
std::shared_ptr< SipMessage > update(const Contents *document)
EncodeStream & dump(EncodeStream &strm) const override
std::shared_ptr< SipMessage > accept(int statusCode=202)
void send(std::shared_ptr< SipMessage > msg) override
std::shared_ptr< SipMessage > reject(int responseCode)
virtual void onReadyToSend(SipMessage &msg) override
ServerSubscription(const ServerSubscription &)=delete
Handle< ServerSubscription > ServerSubscriptionHandle
Represents a SIP message.
bool isRequest() const
Returns true if message is a request, false otherwise.
const RequestLine & header(const RequestLineType &l) const
Returns the RequestLine. This is only valid for request messages.
static uint64_t getTimeSecs()
Returns the current clock time in seconds.
Definition Timer.hxx:78
dcm! – add UnusedChecking(_enum) below;
StatusLineType h_StatusLine
Definition Headers.cxx:348
unsigned int uint32_t
Definition Stun.hxx:34
const Data & getTerminateReasonString(TerminateReason state)
RequestLineType h_RequestLine
Definition Headers.cxx:347
const Data & getSubscriptionStateString(SubscriptionState state)
#define EncodeStream