1 |
#include "resip/stack/Contents.hxx" |
2 |
#include "resip/stack/Helper.hxx" |
3 |
#include "resip/stack/SipMessage.hxx" |
4 |
#include "resip/dum/AppDialog.hxx" |
5 |
#include "resip/dum/BaseCreator.hxx" |
6 |
#include "resip/dum/ClientAuthManager.hxx" |
7 |
#include "resip/dum/ClientInviteSession.hxx" |
8 |
#include "resip/dum/ClientSubscription.hxx" |
9 |
#include "resip/dum/Dialog.hxx" |
10 |
#include "resip/dum/DialogUsageManager.hxx" |
11 |
#include "resip/dum/MasterProfile.hxx" |
12 |
#include "resip/dum/InviteSessionCreator.hxx" |
13 |
#include "resip/dum/InviteSessionHandler.hxx" |
14 |
#include "resip/dum/ServerInviteSession.hxx" |
15 |
#include "resip/dum/ServerSubscription.hxx" |
16 |
#include "resip/dum/SubscriptionHandler.hxx" |
17 |
#include "resip/dum/UsageUseException.hxx" |
18 |
#include "rutil/Logger.hxx" |
19 |
#include "rutil/Inserter.hxx" |
20 |
#include "rutil/TransportType.hxx" |
21 |
#include "rutil/WinLeakCheck.hxx" |
22 |
|
23 |
#define RESIPROCATE_SUBSYSTEM Subsystem::DUM |
24 |
|
25 |
using namespace resip; |
26 |
using namespace std; |
27 |
|
28 |
Dialog::Dialog(DialogUsageManager& dum, const SipMessage& msg, DialogSet& ds) |
29 |
: mDum(dum), |
30 |
mDialogSet(ds), |
31 |
mId("INVALID", "INVALID", "INVALID"), |
32 |
mClientSubscriptions(), |
33 |
mServerSubscriptions(), |
34 |
mInviteSession(0), |
35 |
mType(Fake), |
36 |
mRouteSet(), |
37 |
mLocalContact(), |
38 |
mLocalCSeq(0), |
39 |
mRemoteCSeq(0), |
40 |
mRemoteTarget(), |
41 |
mLocalNameAddr(), |
42 |
mRemoteNameAddr(), |
43 |
mCallId(msg.header(h_CallID)), |
44 |
mDefaultSubExpiration(0), |
45 |
mAppDialog(0), |
46 |
mDestroying(false), |
47 |
mReUseDialogSet(false) |
48 |
{ |
49 |
assert(msg.isExternal()); |
50 |
|
51 |
assert(msg.header(h_CSeq).method() != MESSAGE); |
52 |
assert(msg.header(h_CSeq).method() != REGISTER); |
53 |
assert(msg.header(h_CSeq).method() != PUBLISH); |
54 |
|
55 |
mNetworkAssociation.setDum(&dum); |
56 |
|
57 |
if (msg.isRequest()) // UAS |
58 |
{ |
59 |
const SipMessage& request = msg; |
60 |
|
61 |
switch (request.header(h_CSeq).method()) |
62 |
{ |
63 |
case INVITE: |
64 |
mType = Invitation; |
65 |
break; |
66 |
|
67 |
case SUBSCRIBE: |
68 |
case REFER: |
69 |
case NOTIFY: |
70 |
//!dcm! -- event header check |
71 |
mType = Subscription; |
72 |
break; |
73 |
|
74 |
default: |
75 |
mType = Fake; |
76 |
} |
77 |
if (request.exists(h_RecordRoutes)) |
78 |
{ |
79 |
mRouteSet = request.header(h_RecordRoutes); |
80 |
} |
81 |
|
82 |
switch (request.header(h_CSeq).method()) |
83 |
{ |
84 |
case INVITE: |
85 |
case SUBSCRIBE: |
86 |
case REFER: |
87 |
case NOTIFY: |
88 |
DebugLog ( << "UAS dialog ID creation, DS: " << ds.getId()); |
89 |
mId = DialogId(ds.getId(), |
90 |
request.header(h_From).exists(p_tag) ? request.header(h_From).param(p_tag) : Data::Empty); |
91 |
|
92 |
mRemoteNameAddr = request.header(h_From); |
93 |
mLocalNameAddr = request.header(h_To); |
94 |
mLocalNameAddr.param(p_tag) = mId.getLocalTag(); |
95 |
if (request.exists(h_Contacts) && request.header(h_Contacts).size() == 1) |
96 |
{ |
97 |
const NameAddr& contact = request.header(h_Contacts).front(); |
98 |
if (isEqualNoCase(contact.uri().scheme(), Symbols::Sips) || |
99 |
isEqualNoCase(contact.uri().scheme(), Symbols::Sip)) |
100 |
{ |
101 |
// Store Remote Target |
102 |
mRemoteTarget = contact; |
103 |
|
104 |
// Create Local Contact |
105 |
if(mDialogSet.mUserProfile->hasUserAgentCapabilities()) |
106 |
{ |
107 |
mLocalContact = mDialogSet.mUserProfile->getUserAgentCapabilities(); |
108 |
} |
109 |
if(!mDialogSet.mUserProfile->isAnonymous() && mDialogSet.mUserProfile->hasPublicGruu()) |
110 |
{ |
111 |
mLocalContact.uri() = mDialogSet.mUserProfile->getPublicGruu(); |
112 |
} |
113 |
else if(mDialogSet.mUserProfile->isAnonymous() && mDialogSet.mUserProfile->hasTempGruu()) |
114 |
{ |
115 |
mLocalContact.uri() = mDialogSet.mUserProfile->getTempGruu(); |
116 |
} |
117 |
else |
118 |
{ |
119 |
if (mDialogSet.mUserProfile->hasOverrideHostAndPort()) |
120 |
{ |
121 |
mLocalContact.uri() = mDialogSet.mUserProfile->getOverrideHostAndPort(); |
122 |
} |
123 |
if(request.header(h_RequestLine).uri().user().empty()) |
124 |
{ |
125 |
mLocalContact.uri().user() = request.header(h_To).uri().user(); |
126 |
} |
127 |
else |
128 |
{ |
129 |
mLocalContact.uri().user() = request.header(h_RequestLine).uri().user(); |
130 |
} |
131 |
const Data& instanceId = mDialogSet.mUserProfile->getInstanceId(); |
132 |
if (!contact.uri().exists(p_gr) && !instanceId.empty()) |
133 |
{ |
134 |
mLocalContact.param(p_Instance) = instanceId; |
135 |
} |
136 |
} |
137 |
if(mDialogSet.mUserProfile->clientOutboundEnabled()) |
138 |
{ |
139 |
// Add ;ob parm to non-register requests - RFC5626 pg17 |
140 |
mLocalContact.uri().param(p_ob); |
141 |
} |
142 |
} |
143 |
else |
144 |
{ |
145 |
InfoLog(<< "Got an INVITE or SUBSCRIBE with invalid scheme"); |
146 |
InfoLog(<< request); |
147 |
throw Exception("Invalid scheme in request", __FILE__, __LINE__); |
148 |
} |
149 |
} |
150 |
else |
151 |
{ |
152 |
InfoLog (<< "Got an INVITE or SUBSCRIBE that doesn't have exactly one contact"); |
153 |
InfoLog (<< request); |
154 |
throw Exception("Too many (or no contact) contacts in request", __FILE__, __LINE__); |
155 |
} |
156 |
break; |
157 |
default: |
158 |
break; |
159 |
} |
160 |
|
161 |
mRemoteCSeq = request.header(h_CSeq).sequence(); |
162 |
|
163 |
// This may actually be a UAC dialogset - ie. the case where the first NOTIFY creates the |
164 |
// SUBSCRIPTION dialog, instead of the 200/SUB. If so, then we need to make sure the local |
165 |
// CSeq is correct - it's value may be greator than 1, if the original request (SUBSCRIBE) |
166 |
// got digest challenged. |
167 |
BaseCreator* creator = mDialogSet.getCreator(); |
168 |
if(creator) |
169 |
{ |
170 |
mLocalCSeq = creator->getLastRequest()->header(h_CSeq).sequence(); |
171 |
} |
172 |
else |
173 |
{ |
174 |
mLocalCSeq = 1; |
175 |
} |
176 |
|
177 |
DebugLog ( << "************** Created Dialog as UAS **************" ); |
178 |
DebugLog ( << "mRemoteNameAddr: " << mRemoteNameAddr ); |
179 |
DebugLog ( << "mLocalNameAddr: " << mLocalNameAddr ); |
180 |
DebugLog ( << "mLocalContact: " << mLocalContact ); |
181 |
DebugLog ( << "mRemoteTarget: " << mRemoteTarget ); |
182 |
} |
183 |
else if (msg.isResponse()) |
184 |
{ |
185 |
mId = DialogId(msg); |
186 |
const SipMessage& response = msg; |
187 |
mRemoteNameAddr = response.header(h_To); |
188 |
mLocalNameAddr = response.header(h_From); |
189 |
|
190 |
switch (msg.header(h_CSeq).method()) |
191 |
{ |
192 |
case INVITE: |
193 |
mType = Invitation; |
194 |
break; |
195 |
|
196 |
case SUBSCRIBE: |
197 |
case REFER: |
198 |
mType = Subscription; |
199 |
break; |
200 |
|
201 |
default: |
202 |
mType = Fake; |
203 |
} |
204 |
|
205 |
if (response.exists(h_RecordRoutes)) |
206 |
{ |
207 |
mRouteSet = response.header(h_RecordRoutes).reverse(); |
208 |
} |
209 |
|
210 |
switch (response.header(h_CSeq).method()) |
211 |
{ |
212 |
case INVITE: |
213 |
case SUBSCRIBE: |
214 |
case REFER: |
215 |
if (response.header(h_StatusLine).statusCode() > 100 && |
216 |
response.header(h_StatusLine).statusCode() < 300) |
217 |
{ |
218 |
|
219 |
if (response.exists(h_Contacts) && response.header(h_Contacts).size() == 1) |
220 |
{ |
221 |
const NameAddr& contact = response.header(h_Contacts).front(); |
222 |
if (isEqualNoCase(contact.uri().scheme(), Symbols::Sips) || |
223 |
isEqualNoCase(contact.uri().scheme(), Symbols::Sip)) |
224 |
{ |
225 |
BaseCreator* creator = mDialogSet.getCreator(); |
226 |
if(0 == creator) |
227 |
{ |
228 |
ErrLog(<< "BaseCreator is null for DialogSet"); |
229 |
ErrLog(<< response); |
230 |
throw Exception("BaseCreator is null for DialogSet", __FILE__, __LINE__); |
231 |
} |
232 |
|
233 |
SharedPtr<SipMessage> lastRequest(creator->getLastRequest()); |
234 |
|
235 |
if( 0 == lastRequest.get() || |
236 |
!lastRequest->exists(h_Contacts) || |
237 |
lastRequest->header(h_Contacts).empty()) |
238 |
{ |
239 |
InfoLog(<< "lastRequest does not contain a valid contact"); |
240 |
InfoLog(<< response); |
241 |
throw Exception("lastRequest does not contain a valid contact.", __FILE__, __LINE__); |
242 |
} |
243 |
mLocalContact = creator->getLastRequest()->header(h_Contacts).front(); |
244 |
mRemoteTarget = contact; |
245 |
} |
246 |
else |
247 |
{ |
248 |
InfoLog (<< "Got an INVITE or SUBSCRIBE with invalid scheme"); |
249 |
DebugLog (<< response); |
250 |
throw Exception("Bad scheme in contact in response", __FILE__, __LINE__); |
251 |
} |
252 |
} |
253 |
else |
254 |
{ |
255 |
InfoLog (<< "Got an INVITE or SUBSCRIBE that doesn't have exactly one contact"); |
256 |
DebugLog (<< response); |
257 |
throw Exception("Too many contacts (or no contact) in response", __FILE__, __LINE__); |
258 |
} |
259 |
break; |
260 |
default: |
261 |
break; |
262 |
} |
263 |
} |
264 |
|
265 |
mLocalCSeq = response.header(h_CSeq).sequence(); |
266 |
mRemoteCSeq = 0; |
267 |
DebugLog ( << "************** Created Dialog as UAC **************" ); |
268 |
DebugLog ( << "mRemoteNameAddr: " << mRemoteNameAddr ); |
269 |
DebugLog ( << "mLocalNameAddr: " << mLocalNameAddr ); |
270 |
DebugLog ( << "mLocalContact: " << mLocalContact ); |
271 |
DebugLog ( << "mRemoteTarget: " << mRemoteTarget ); |
272 |
} |
273 |
mDialogSet.addDialog(this); |
274 |
DebugLog ( <<"Dialog::Dialog " << mId); |
275 |
} |
276 |
|
277 |
Dialog::~Dialog() |
278 |
{ |
279 |
DebugLog ( <<"Dialog::~Dialog() "); |
280 |
|
281 |
mDestroying = true; |
282 |
|
283 |
while (!mClientSubscriptions.empty()) |
284 |
{ |
285 |
delete *mClientSubscriptions.begin(); |
286 |
} |
287 |
|
288 |
while (!mServerSubscriptions.empty()) |
289 |
{ |
290 |
delete *mServerSubscriptions.begin(); |
291 |
} |
292 |
|
293 |
delete mInviteSession; |
294 |
mDialogSet.mDialogs.erase(this->getId()); |
295 |
delete mAppDialog; |
296 |
if(!mReUseDialogSet) |
297 |
{ |
298 |
mDialogSet.possiblyDie(); |
299 |
} |
300 |
} |
301 |
|
302 |
const DialogId& |
303 |
Dialog::getId() const |
304 |
{ |
305 |
return mId; |
306 |
} |
307 |
|
308 |
const NameAddr& |
309 |
Dialog::getLocalNameAddr() const |
310 |
{ |
311 |
return mLocalNameAddr; |
312 |
} |
313 |
|
314 |
const NameAddr& |
315 |
Dialog::getLocalContact() const |
316 |
{ |
317 |
return mLocalContact; |
318 |
} |
319 |
|
320 |
const NameAddr& |
321 |
Dialog::getRemoteNameAddr() const |
322 |
{ |
323 |
return mRemoteNameAddr; |
324 |
} |
325 |
|
326 |
const NameAddr& |
327 |
Dialog::getRemoteTarget() const |
328 |
{ |
329 |
return mRemoteTarget; |
330 |
} |
331 |
|
332 |
const NameAddrs& |
333 |
Dialog::getRouteSet() const |
334 |
{ |
335 |
return mRouteSet; |
336 |
} |
337 |
|
338 |
void |
339 |
Dialog::cancel() |
340 |
{ |
341 |
assert(mType == Invitation); |
342 |
ClientInviteSession* uac = dynamic_cast<ClientInviteSession*>(mInviteSession); |
343 |
assert (uac); |
344 |
uac->cancel(); |
345 |
} |
346 |
|
347 |
void |
348 |
Dialog::end() |
349 |
{ |
350 |
if (mInviteSession) |
351 |
{ |
352 |
mInviteSession->end(); |
353 |
} |
354 |
|
355 |
// End Subscriptions |
356 |
// !jrm! WARN ClientSubscription and ServerSubscription have access to this dialog and will remove themselves |
357 |
// from the m<client|server>Subscriptions collections in the call to end(). |
358 |
for (list<ClientSubscription*>::iterator it(mClientSubscriptions.begin()); |
359 |
it != mClientSubscriptions.end();) |
360 |
{ |
361 |
ClientSubscription* c = *it; |
362 |
it++; |
363 |
c->end(); |
364 |
} |
365 |
|
366 |
for (list<ServerSubscription*>::iterator it2(mServerSubscriptions.begin()); |
367 |
it2 != mServerSubscriptions.end();) |
368 |
{ |
369 |
ServerSubscription* s = *it2; |
370 |
it2++; |
371 |
s->end(); |
372 |
} |
373 |
} |
374 |
|
375 |
void |
376 |
Dialog::handleTargetRefresh(const SipMessage& msg) |
377 |
{ |
378 |
switch(msg.header(h_CSeq).method()) |
379 |
{ |
380 |
case INVITE: |
381 |
case UPDATE: |
382 |
if (msg.isRequest() || (msg.isResponse() && msg.header(h_StatusLine).statusCode()/100 == 2)) |
383 |
{ |
384 |
//?dcm? modify local target; 12.2.2 of 3261 implies that the remote |
385 |
//target is immediately modified. Should we wait until a 2xx class |
386 |
//reponse is sent to a re-invite(easy when all send requests go |
387 |
//through Dialog) |
388 |
if (msg.exists(h_Contacts)) |
389 |
{ |
390 |
//.dcm. replace or check then replace |
391 |
mRemoteTarget = msg.header(h_Contacts).front(); |
392 |
} |
393 |
} |
394 |
break; |
395 |
default: |
396 |
return; |
397 |
} |
398 |
} |
399 |
|
400 |
void |
401 |
Dialog::dispatch(const SipMessage& msg) |
402 |
{ |
403 |
// !jf! Should be checking for messages with out of order CSeq and rejecting |
404 |
|
405 |
DebugLog ( << "Dialog::dispatch: " << msg.brief()); |
406 |
|
407 |
if(msg.isExternal()) |
408 |
{ |
409 |
TransportType receivedTransport = toTransportType( |
410 |
msg.header(h_Vias).front().transport()); |
411 |
int keepAliveTime = 0; |
412 |
if(isReliable(receivedTransport)) |
413 |
{ |
414 |
keepAliveTime = mDialogSet.mUserProfile->getKeepAliveTimeForStream(); |
415 |
} |
416 |
else |
417 |
{ |
418 |
keepAliveTime = mDialogSet.mUserProfile->getKeepAliveTimeForDatagram(); |
419 |
} |
420 |
|
421 |
if(keepAliveTime > 0) |
422 |
{ |
423 |
mNetworkAssociation.update(msg, keepAliveTime, false /* targetSupportsOutbound */); // target supports outbound is detected in registration responses only |
424 |
} |
425 |
} |
426 |
|
427 |
handleTargetRefresh(msg); |
428 |
if (msg.isRequest()) |
429 |
{ |
430 |
const SipMessage& request = msg; |
431 |
switch (request.header(h_CSeq).method()) |
432 |
{ |
433 |
case INVITE: // new INVITE |
434 |
if (mInviteSession == 0) |
435 |
{ |
436 |
DebugLog ( << "Dialog::dispatch -- Created new server invite session" << msg.brief()); |
437 |
mInviteSession = makeServerInviteSession(request); |
438 |
} |
439 |
mInviteSession->dispatch(request); |
440 |
break; |
441 |
//refactor, send bad request for BYE, INFO, CANCEL? |
442 |
case BYE: |
443 |
if (mInviteSession == 0) |
444 |
{ |
445 |
InfoLog ( << "Spurious BYE" ); |
446 |
return; |
447 |
} |
448 |
else |
449 |
{ |
450 |
mInviteSession->dispatch(request); |
451 |
} |
452 |
break; |
453 |
case UPDATE: |
454 |
if (mInviteSession == 0) |
455 |
{ |
456 |
InfoLog ( << "Spurious UPDATE" ); |
457 |
return; |
458 |
} |
459 |
else |
460 |
{ |
461 |
mInviteSession->dispatch(request); |
462 |
} |
463 |
break; |
464 |
case INFO: |
465 |
if (mInviteSession == 0) |
466 |
{ |
467 |
InfoLog ( << "Spurious INFO" ); |
468 |
return; |
469 |
} |
470 |
else |
471 |
{ |
472 |
mInviteSession->dispatch(request); |
473 |
} |
474 |
break; |
475 |
case MESSAGE: |
476 |
if (mInviteSession == 0) |
477 |
{ |
478 |
InfoLog ( << "Spurious MESSAGE" ); |
479 |
return; |
480 |
} |
481 |
else |
482 |
{ |
483 |
mInviteSession->dispatch(request); |
484 |
} |
485 |
break; |
486 |
case ACK: |
487 |
case CANCEL: |
488 |
if (mInviteSession == 0) |
489 |
{ |
490 |
InfoLog (<< "Drop stray ACK or CANCEL in dialog on the floor"); |
491 |
DebugLog (<< request); |
492 |
} |
493 |
else |
494 |
{ |
495 |
mInviteSession->dispatch(request); |
496 |
} |
497 |
break; |
498 |
case SUBSCRIBE: |
499 |
{ |
500 |
ServerSubscription* server = findMatchingServerSub(request); |
501 |
if (server) |
502 |
{ |
503 |
server->dispatch(request); |
504 |
} |
505 |
else |
506 |
{ |
507 |
if (request.exists(h_Event) && request.header(h_Event).value() == "refer") |
508 |
{ |
509 |
InfoLog (<< "Received a subscribe to a non-existent refer subscription: " << request.brief()); |
510 |
SipMessage failure; |
511 |
makeResponse(failure, request, 403); |
512 |
mDum.sendResponse(failure); |
513 |
return; |
514 |
} |
515 |
else |
516 |
{ |
517 |
if (mDum.checkEventPackage(request)) |
518 |
{ |
519 |
server = makeServerSubscription(request); |
520 |
mServerSubscriptions.push_back(server); |
521 |
server->dispatch(request); |
522 |
} |
523 |
} |
524 |
} |
525 |
} |
526 |
break; |
527 |
case REFER: |
528 |
{ |
529 |
// if (mInviteSession == 0) |
530 |
// { |
531 |
// InfoLog (<< "Received an in dialog refer in a non-invite dialog: " << request.brief()); |
532 |
// SipMessage failure; |
533 |
// makeResponse(failure, request, 603); |
534 |
// mDum.sendResponse(failure); |
535 |
// return; |
536 |
// } |
537 |
// else |
538 |
|
539 |
if (!request.exists(h_ReferTo)) |
540 |
{ |
541 |
InfoLog (<< "Received refer w/out a Refer-To: " << request.brief()); |
542 |
SipMessage failure; |
543 |
makeResponse(failure, request, 400); |
544 |
mDum.sendResponse(failure); |
545 |
return; |
546 |
} |
547 |
else |
548 |
{ |
549 |
if ((request.exists(h_ReferSub) && |
550 |
request.header(h_ReferSub).isWellFormed() && |
551 |
request.header(h_ReferSub).value()=="false") || |
552 |
(request.exists(h_Requires) && |
553 |
request.header(h_Requires).find(Token("norefersub")))) |
554 |
{ |
555 |
assert(mInviteSession); |
556 |
mInviteSession->referNoSub(msg); |
557 |
} |
558 |
else |
559 |
{ |
560 |
ServerSubscription* server = findMatchingServerSub(request); |
561 |
ServerSubscriptionHandle serverHandle; |
562 |
if (server) |
563 |
{ |
564 |
serverHandle = server->getHandle(); |
565 |
server->dispatch(request); |
566 |
} |
567 |
else |
568 |
{ |
569 |
server = makeServerSubscription(request); |
570 |
mServerSubscriptions.push_back(server); |
571 |
serverHandle = server->getHandle(); |
572 |
server->dispatch(request); |
573 |
} |
574 |
|
575 |
if (mInviteSession) |
576 |
{ |
577 |
mDum.mInviteSessionHandler->onRefer(mInviteSession->getSessionHandle(), serverHandle, msg); |
578 |
} |
579 |
|
580 |
} |
581 |
} |
582 |
} |
583 |
break; |
584 |
case NOTIFY: |
585 |
{ |
586 |
ClientSubscription* client = findMatchingClientSub(request); |
587 |
if (client) |
588 |
{ |
589 |
client->dispatch(request); |
590 |
} |
591 |
else |
592 |
{ |
593 |
BaseCreator* creator = mDialogSet.getCreator(); |
594 |
if (creator && (creator->getLastRequest()->header(h_RequestLine).method() == SUBSCRIBE || |
595 |
creator->getLastRequest()->header(h_RequestLine).method() == REFER)) |
596 |
{ |
597 |
DebugLog (<< "Making subscription (from creator) request: " << *creator->getLastRequest()); |
598 |
ClientSubscription* sub = makeClientSubscription(*creator->getLastRequest()); |
599 |
mClientSubscriptions.push_back(sub); |
600 |
sub->dispatch(request); |
601 |
} |
602 |
else |
603 |
{ |
604 |
if (mInviteSession != 0 && (!msg.exists(h_Event) || msg.header(h_Event).value() == "refer") && |
605 |
mDum.getClientSubscriptionHandler("refer")!=0) |
606 |
{ |
607 |
DebugLog (<< "Making subscription from NOTIFY: " << msg); |
608 |
ClientSubscription* sub = makeClientSubscription(msg); |
609 |
mClientSubscriptions.push_back(sub); |
610 |
ClientSubscriptionHandle client = sub->getHandle(); |
611 |
mDum.mInviteSessionHandler->onReferAccepted(mInviteSession->getSessionHandle(), client, msg); |
612 |
sub->dispatch(request); |
613 |
} |
614 |
else |
615 |
{ |
616 |
SharedPtr<SipMessage> response(new SipMessage); |
617 |
makeResponse(*response, msg, 406); |
618 |
send(response); |
619 |
} |
620 |
} |
621 |
} |
622 |
} |
623 |
break; |
624 |
default: |
625 |
assert(0); |
626 |
return; |
627 |
} |
628 |
} |
629 |
else if (msg.isResponse()) |
630 |
{ |
631 |
// !jf! There is a substantial change in how this works in teltel-branch |
632 |
// from how it worked in main branch pre merge. |
633 |
// If the response doesn't match a cseq for a request I've sent, ignore |
634 |
// the response |
635 |
{//scope 'r' as it is invalidated below |
636 |
RequestMap::iterator r = mRequests.find(msg.header(h_CSeq).sequence()); |
637 |
if (r != mRequests.end()) |
638 |
{ |
639 |
bool handledByAuth = false; |
640 |
if (mDum.mClientAuthManager.get() && |
641 |
mDum.mClientAuthManager->handle(*mDialogSet.mUserProfile, *r->second, msg)) |
642 |
{ |
643 |
InfoLog( << "about to re-send request with digest credentials" << r->second->brief()); |
644 |
|
645 |
assert (r->second->isRequest()); |
646 |
|
647 |
mLocalCSeq++; |
648 |
send(r->second); |
649 |
handledByAuth = true; |
650 |
} |
651 |
mRequests.erase(r); |
652 |
if (handledByAuth) return; |
653 |
} |
654 |
} |
655 |
|
656 |
const SipMessage& response = msg; |
657 |
int code = response.header(h_StatusLine).statusCode(); |
658 |
// If this is a 200 response to the initial request, then store the routeset (if present) |
659 |
BaseCreator* creator = mDialogSet.getCreator(); |
660 |
if (creator && (creator->getLastRequest()->header(h_CSeq) == response.header(h_CSeq)) && code >=200 && code < 300) |
661 |
{ |
662 |
if (response.exists(h_RecordRoutes)) |
663 |
{ |
664 |
mRouteSet = response.header(h_RecordRoutes).reverse(); |
665 |
} |
666 |
else |
667 |
{ |
668 |
// Ensure that if the route-set in the 200 is empty, then we overwrite any existing route-sets |
669 |
mRouteSet.clear(); |
670 |
} |
671 |
} |
672 |
|
673 |
// !jf! should this only be for 2xx responses? !jf! Propose no as an |
674 |
// answer !dcm! what is he on? |
675 |
switch (response.header(h_CSeq).method()) |
676 |
{ |
677 |
case INVITE: |
678 |
if (mInviteSession == 0) |
679 |
{ |
680 |
DebugLog ( << "Dialog::dispatch -- Created new client invite session" << msg.brief()); |
681 |
|
682 |
mInviteSession = makeClientInviteSession(response); |
683 |
if (mInviteSession) |
684 |
{ |
685 |
mInviteSession->dispatch(response); |
686 |
} |
687 |
else |
688 |
{ |
689 |
ErrLog( << "Dialog::dispatch -- Unable to create invite session from response" << msg.brief()); |
690 |
} |
691 |
} |
692 |
else |
693 |
{ |
694 |
mInviteSession->dispatch(response); |
695 |
} |
696 |
break; |
697 |
case BYE: |
698 |
case ACK: |
699 |
case CANCEL: |
700 |
case INFO: |
701 |
case MESSAGE: |
702 |
case UPDATE: |
703 |
if (mInviteSession) |
704 |
{ |
705 |
mInviteSession->dispatch(response); |
706 |
} |
707 |
// else drop on the floor |
708 |
break; |
709 |
|
710 |
case REFER: |
711 |
if(mInviteSession) |
712 |
{ |
713 |
if (code >= 300) |
714 |
{ |
715 |
mDum.mInviteSessionHandler->onReferRejected(mInviteSession->getSessionHandle(), msg); |
716 |
} |
717 |
else |
718 |
{ |
719 |
//!dys! the OR condition below is not draft compliant. |
720 |
if (!mInviteSession->mReferSub && |
721 |
((msg.exists(h_ReferSub) && msg.header(h_ReferSub).value()=="false") || |
722 |
!msg.exists(h_ReferSub))) |
723 |
{ |
724 |
DebugLog(<< "refer accepted with norefersub"); |
725 |
mDum.mInviteSessionHandler->onReferAccepted(mInviteSession->getSessionHandle(), ClientSubscriptionHandle::NotValid(), msg); |
726 |
} |
727 |
// else no need for action - first Notify will cause onReferAccepted to be called |
728 |
} |
729 |
mInviteSession->nitComplete(); |
730 |
break; |
731 |
} |
732 |
// fall through, out of dialog refer was sent. |
733 |
|
734 |
case SUBSCRIBE: |
735 |
{ |
736 |
int code = response.header(h_StatusLine).statusCode(); |
737 |
ClientSubscription* client = findMatchingClientSub(response); |
738 |
if (client) |
739 |
{ |
740 |
client->dispatch(response); |
741 |
} |
742 |
else if (code < 300) |
743 |
{ |
744 |
/* |
745 |
we're capturing the value from the expires header off |
746 |
the 2xx because the ClientSubscription is only created |
747 |
after receiving the NOTIFY that comes (usually) after |
748 |
this 2xx. We really should be creating the |
749 |
ClientSubscription at either the 2xx or the NOTIFY |
750 |
whichever arrives first. .mjf. |
751 |
Note: we're capturing a duration here (not the |
752 |
absolute time because all the inputs to |
753 |
ClientSubscription desling with the expiration are expecting |
754 |
duration type values from the headers. .mjf. |
755 |
*/ |
756 |
if(response.exists(h_Expires)) |
757 |
{ |
758 |
mDefaultSubExpiration = response.header(h_Expires).value(); |
759 |
} |
760 |
else |
761 |
{ |
762 |
//?dcm? defaults to 3600 in ClientSubscription if no expires value |
763 |
//is provided anywhere...should we assume the value from the |
764 |
//sub in the basecreator if it exists? |
765 |
mDefaultSubExpiration = 0; |
766 |
} |
767 |
return; |
768 |
} |
769 |
else |
770 |
{ |
771 |
//!dcm! -- can't subscribe in an existing Dialog, this is all |
772 |
//a bit of a hack; currently, spurious failure messages may cause callbacks |
773 |
BaseCreator* creator = mDialogSet.getCreator(); |
774 |
if (!creator || !creator->getLastRequest()->exists(h_Event)) |
775 |
{ |
776 |
return; |
777 |
} |
778 |
else |
779 |
{ |
780 |
ClientSubscriptionHandler* handler = |
781 |
mDum.getClientSubscriptionHandler(creator->getLastRequest()->header(h_Event).value()); |
782 |
if (handler) |
783 |
{ |
784 |
ClientSubscription* sub = makeClientSubscription(*creator->getLastRequest()); |
785 |
mClientSubscriptions.push_back(sub); |
786 |
sub->dispatch(response); |
787 |
} |
788 |
} |
789 |
} |
790 |
|
791 |
} |
792 |
break; |
793 |
case NOTIFY: |
794 |
{ |
795 |
//Failures are dispatched to all ServerSubsscriptions, |
796 |
//which may not be correct as per RFC 5057. |
797 |
|
798 |
int code = msg.header(h_StatusLine).statusCode(); |
799 |
if (code >= 300) |
800 |
{ |
801 |
//!dcm! -- ick, write guard |
802 |
mDestroying = true; |
803 |
for (list<ServerSubscription*>::iterator it = mServerSubscriptions.begin(); |
804 |
it != mServerSubscriptions.end(); ) |
805 |
{ |
806 |
ServerSubscription* s = *it; |
807 |
it++; |
808 |
s->dispatch(msg); |
809 |
} |
810 |
mDestroying = false; |
811 |
possiblyDie(); |
812 |
} |
813 |
else if (code >= 200) |
814 |
{ |
815 |
ServerSubscription* server = findMatchingServerSub(response); |
816 |
if (server) |
817 |
{ |
818 |
server->dispatch(response); |
819 |
} |
820 |
} |
821 |
} |
822 |
break; |
823 |
default: |
824 |
assert(0); |
825 |
return; |
826 |
} |
827 |
|
828 |
#if 0 // merged from head back to teltel-branch |
829 |
if (msg.header(h_StatusLine).statusCode() >= 400 |
830 |
&& Helper::determineFailureMessageEffect(msg) == Helper::DialogTermination) |
831 |
{ |
832 |
//kill all usages |
833 |
mDestroying = true; |
834 |
|
835 |
for (list<ServerSubscription*>::iterator it = mServerSubscriptions.begin(); |
836 |
it != mServerSubscriptions.end(); ) |
837 |
{ |
838 |
ServerSubscription* s = *it; |
839 |
it++; |
840 |
s->dialogDestroyed(msg); |
841 |
} |
842 |
|
843 |
for (list<ClientSubscription*>::iterator it = mClientSubscriptions.begin(); |
844 |
it != mClientSubscriptions.end(); ) |
845 |
{ |
846 |
ClientSubscription* s = *it; |
847 |
it++; |
848 |
s->dialogDestroyed(msg); |
849 |
} |
850 |
if (mInviteSession) |
851 |
{ |
852 |
mInviteSession->dialogDestroyed(msg); |
853 |
} |
854 |
mDestroying = false; |
855 |
possiblyDie(); //should aways result in destruction of this |
856 |
return; |
857 |
} |
858 |
#endif |
859 |
} |
860 |
} |
861 |
|
862 |
ServerSubscription* |
863 |
Dialog::findMatchingServerSub(const SipMessage& msg) |
864 |
{ |
865 |
for (std::list<ServerSubscription*>::iterator i=mServerSubscriptions.begin(); |
866 |
i != mServerSubscriptions.end(); ++i) |
867 |
{ |
868 |
if ((*i)->matches(msg)) |
869 |
{ |
870 |
return *i; |
871 |
} |
872 |
} |
873 |
return 0; |
874 |
} |
875 |
|
876 |
ClientSubscription* |
877 |
Dialog::findMatchingClientSub(const SipMessage& msg) |
878 |
{ |
879 |
for (std::list<ClientSubscription*>::iterator i=mClientSubscriptions.begin(); |
880 |
i != mClientSubscriptions.end(); ++i) |
881 |
{ |
882 |
if ((*i)->matches(msg)) |
883 |
{ |
884 |
return *i; |
885 |
} |
886 |
} |
887 |
return 0; |
888 |
} |
889 |
|
890 |
InviteSessionHandle |
891 |
Dialog::getInviteSession() |
892 |
{ |
893 |
if (mInviteSession) |
894 |
{ |
895 |
return mInviteSession->getSessionHandle(); |
896 |
} |
897 |
else |
898 |
{ |
899 |
return InviteSessionHandle::NotValid(); |
900 |
} |
901 |
} |
902 |
|
903 |
std::vector<ClientSubscriptionHandle> |
904 |
Dialog::findClientSubscriptions(const Data& event) |
905 |
{ |
906 |
std::vector<ClientSubscriptionHandle> handles; |
907 |
|
908 |
for (std::list<ClientSubscription*>::const_iterator i = mClientSubscriptions.begin(); |
909 |
i != mClientSubscriptions.end(); ++i) |
910 |
{ |
911 |
if ( (*i)->getEventType() == event) |
912 |
{ |
913 |
handles.push_back((*i)->getHandle()); |
914 |
} |
915 |
} |
916 |
return handles; |
917 |
} |
918 |
|
919 |
std::vector<ServerSubscriptionHandle> |
920 |
Dialog::findServerSubscriptions(const Data& event) |
921 |
{ |
922 |
std::vector<ServerSubscriptionHandle> handles; |
923 |
|
924 |
for (std::list<ServerSubscription*>::const_iterator i = mServerSubscriptions.begin(); |
925 |
i != mServerSubscriptions.end(); ++i) |
926 |
{ |
927 |
if ( (*i)->getEventType() == event) |
928 |
{ |
929 |
handles.push_back((*i)->getHandle()); |
930 |
} |
931 |
} |
932 |
return handles; |
933 |
} |
934 |
|
935 |
std::vector<ClientSubscriptionHandle> |
936 |
Dialog::getClientSubscriptions() |
937 |
{ |
938 |
std::vector<ClientSubscriptionHandle> handles; |
939 |
|
940 |
for (std::list<ClientSubscription*>::const_iterator i = mClientSubscriptions.begin(); |
941 |
i != mClientSubscriptions.end(); ++i) |
942 |
{ |
943 |
handles.push_back((*i)->getHandle()); |
944 |
} |
945 |
|
946 |
return handles; |
947 |
} |
948 |
|
949 |
std::vector<ServerSubscriptionHandle> |
950 |
Dialog::getServerSubscriptions() |
951 |
{ |
952 |
std::vector<ServerSubscriptionHandle> handles; |
953 |
|
954 |
for (std::list<ServerSubscription*>::const_iterator i = mServerSubscriptions.begin(); |
955 |
i != mServerSubscriptions.end(); ++i) |
956 |
{ |
957 |
handles.push_back((*i)->getHandle()); |
958 |
} |
959 |
|
960 |
return handles; |
961 |
} |
962 |
|
963 |
void |
964 |
Dialog::redirected(const SipMessage& msg) |
965 |
{ |
966 |
//Established dialogs are not destroyed by a redirect |
967 |
if (!mClientSubscriptions.empty() || !mServerSubscriptions.empty()) |
968 |
{ |
969 |
return; |
970 |
} |
971 |
if (mInviteSession) |
972 |
{ |
973 |
ClientInviteSession* cInv = dynamic_cast<ClientInviteSession*>(mInviteSession); |
974 |
if (cInv) |
975 |
{ |
976 |
cInv->handleRedirect(msg); |
977 |
mReUseDialogSet = true; // Set flag so that DialogSet will not be destroyed and new Request can use it |
978 |
} |
979 |
} |
980 |
} |
981 |
|
982 |
void |
983 |
Dialog::makeRequest(SipMessage& request, MethodTypes method) |
984 |
{ |
985 |
RequestLine rLine(method); |
986 |
|
987 |
rLine.uri() = mRemoteTarget.uri(); |
988 |
|
989 |
request.header(h_RequestLine) = rLine; |
990 |
request.header(h_To) = mRemoteNameAddr; |
991 |
// request.header(h_To).param(p_tag) = mId.getRemoteTag(); |
992 |
request.header(h_From) = mLocalNameAddr; |
993 |
// request.header(h_From).param(p_tag) = mId.getLocalTag(); |
994 |
|
995 |
request.header(h_CallId) = mCallId; |
996 |
|
997 |
request.remove(h_RecordRoutes); //!dcm! -- all of this is rather messy |
998 |
request.remove(h_Replaces); |
999 |
|
1000 |
request.remove(h_Contacts); |
1001 |
request.header(h_Contacts).push_front(mLocalContact); |
1002 |
|
1003 |
request.header(h_CSeq).method() = method; |
1004 |
request.header(h_MaxForwards).value() = 70; |
1005 |
|
1006 |
//must keep old via for cancel |
1007 |
if (method != CANCEL) |
1008 |
{ |
1009 |
request.header(h_Routes) = mRouteSet; |
1010 |
request.remove(h_Vias); |
1011 |
Via via; |
1012 |
via.param(p_branch); // will create the branch |
1013 |
request.header(h_Vias).push_front(via); |
1014 |
} |
1015 |
else |
1016 |
{ |
1017 |
assert(request.exists(h_Vias)); |
1018 |
} |
1019 |
|
1020 |
//don't increment CSeq for ACK or CANCEL |
1021 |
if (method != ACK && method != CANCEL) |
1022 |
{ |
1023 |
request.header(h_CSeq).sequence() = ++mLocalCSeq; |
1024 |
} |
1025 |
else |
1026 |
{ |
1027 |
// ACK and cancel have a minimal header set |
1028 |
request.remove(h_Accepts); |
1029 |
request.remove(h_AcceptEncodings); |
1030 |
request.remove(h_AcceptLanguages); |
1031 |
request.remove(h_Allows); |
1032 |
request.remove(h_Requires); |
1033 |
request.remove(h_ProxyRequires); |
1034 |
request.remove(h_Supporteds); |
1035 |
// request.header(h_CSeq).sequence() = ?; // Caller should provide original request, or modify CSeq to proper value after calling this method |
1036 |
} |
1037 |
|
1038 |
// If method is INVITE then advertise required headers |
1039 |
if(method == INVITE || method == UPDATE) |
1040 |
{ |
1041 |
if(mDialogSet.mUserProfile->isAdvertisedCapability(Headers::Allow)) request.header(h_Allows) = mDum.getMasterProfile()->getAllowedMethods(); |
1042 |
if(mDialogSet.mUserProfile->isAdvertisedCapability(Headers::AcceptEncoding)) request.header(h_AcceptEncodings) = mDum.getMasterProfile()->getSupportedEncodings(); |
1043 |
if(mDialogSet.mUserProfile->isAdvertisedCapability(Headers::AcceptLanguage)) request.header(h_AcceptLanguages) = mDum.getMasterProfile()->getSupportedLanguages(); |
1044 |
if(mDialogSet.mUserProfile->isAdvertisedCapability(Headers::AllowEvents)) request.header(h_AllowEvents) = mDum.getMasterProfile()->getAllowedEvents(); |
1045 |
if(mDialogSet.mUserProfile->isAdvertisedCapability(Headers::Supported)) request.header(h_Supporteds) = mDum.getMasterProfile()->getSupportedOptionTags(); |
1046 |
} |
1047 |
|
1048 |
if (mDialogSet.mUserProfile->isAnonymous()) |
1049 |
{ |
1050 |
request.header(h_Privacys).push_back(PrivacyCategory(Symbols::id)); |
1051 |
} |
1052 |
|
1053 |
DebugLog ( << "Dialog::makeRequest: " << std::endl << std::endl << request ); |
1054 |
} |
1055 |
|
1056 |
|
1057 |
void |
1058 |
Dialog::makeResponse(SipMessage& response, const SipMessage& request, int code) |
1059 |
{ |
1060 |
assert( code >= 100 ); |
1061 |
response.remove(h_Contacts); |
1062 |
if (code < 300 && code > 100) |
1063 |
{ |
1064 |
assert(request.isRequest()); |
1065 |
assert(request.header(h_RequestLine).getMethod() == INVITE || |
1066 |
request.header(h_RequestLine).getMethod() == SUBSCRIBE || |
1067 |
request.header(h_RequestLine).getMethod() == BYE || |
1068 |
request.header(h_RequestLine).getMethod() == CANCEL || |
1069 |
request.header(h_RequestLine).getMethod() == REFER || |
1070 |
request.header(h_RequestLine).getMethod() == MESSAGE || |
1071 |
request.header(h_RequestLine).getMethod() == NOTIFY || |
1072 |
request.header(h_RequestLine).getMethod() == INFO || |
1073 |
request.header(h_RequestLine).getMethod() == OPTIONS || |
1074 |
request.header(h_RequestLine).getMethod() == UPDATE |
1075 |
); |
1076 |
|
1077 |
// assert (request.header(h_RequestLine).getMethod() == CANCEL || // Contact header is not required for Requests that do not form a dialog |
1078 |
// request.header(h_RequestLine).getMethod() == BYE || |
1079 |
// request.header(h_Contacts).size() == 1); |
1080 |
Helper::makeResponse(response, request, code, mLocalContact); |
1081 |
response.header(h_To).param(p_tag) = mId.getLocalTag(); |
1082 |
|
1083 |
if((request.header(h_RequestLine).getMethod() == INVITE || |
1084 |
request.header(h_RequestLine).getMethod() == UPDATE) |
1085 |
&& code >= 200 && code < 300) |
1086 |
{ |
1087 |
// Check if we should add our capabilites to the invite success response |
1088 |
if(mDialogSet.mUserProfile->isAdvertisedCapability(Headers::Allow)) |
1089 |
{ |
1090 |
response.header(h_Allows) = mDum.getMasterProfile()->getAllowedMethods(); |
1091 |
} |
1092 |
if(mDialogSet.mUserProfile->isAdvertisedCapability(Headers::AcceptEncoding)) |
1093 |
{ |
1094 |
response.header(h_AcceptEncodings) = mDum.getMasterProfile()->getSupportedEncodings(); |
1095 |
} |
1096 |
if(mDialogSet.mUserProfile->isAdvertisedCapability(Headers::AcceptLanguage)) |
1097 |
{ |
1098 |
response.header(h_AcceptLanguages) = mDum.getMasterProfile()->getSupportedLanguages(); |
1099 |
} |
1100 |
if(mDialogSet.mUserProfile->isAdvertisedCapability(Headers::AllowEvents)) |
1101 |
{ |
1102 |
response.header(h_AllowEvents) = mDum.getMasterProfile()->getAllowedEvents(); |
1103 |
} |
1104 |
if(mDialogSet.mUserProfile->isAdvertisedCapability(Headers::Supported)) |
1105 |
{ |
1106 |
response.header(h_Supporteds) = mDum.getMasterProfile()->getSupportedOptionTags(); |
1107 |
} |
1108 |
} |
1109 |
} |
1110 |
else |
1111 |
{ |
1112 |
Helper::makeResponse(response, request, code); |
1113 |
response.header(h_To).param(p_tag) = mId.getLocalTag(); |
1114 |
} |
1115 |
|
1116 |
DebugLog ( << "Dialog::makeResponse: " << std::endl << std::endl << response); |
1117 |
} |
1118 |
|
1119 |
|
1120 |
ClientInviteSession* |
1121 |
Dialog::makeClientInviteSession(const SipMessage& response) |
1122 |
{ |
1123 |
InviteSessionCreator* creator = dynamic_cast<InviteSessionCreator*>(mDialogSet.getCreator()); |
1124 |
if (!creator) |
1125 |
{ |
1126 |
assert(0); // !jf! this maybe can assert by evil UAS |
1127 |
return 0; |
1128 |
} |
1129 |
//return mDum.createAppClientInviteSession(*this, *creator); |
1130 |
return new ClientInviteSession(mDum, *this, creator->getLastRequest(), |
1131 |
creator->getInitialOffer(), creator->getEncryptionLevel(), creator->getServerSubscription()); |
1132 |
} |
1133 |
|
1134 |
|
1135 |
|
1136 |
ClientSubscription* |
1137 |
Dialog::makeClientSubscription(const SipMessage& request) |
1138 |
{ |
1139 |
return new ClientSubscription(mDum, *this, request, mDefaultSubExpiration); |
1140 |
} |
1141 |
|
1142 |
|
1143 |
ServerInviteSession* |
1144 |
Dialog::makeServerInviteSession(const SipMessage& request) |
1145 |
{ |
1146 |
return new ServerInviteSession(mDum, *this, request); |
1147 |
} |
1148 |
|
1149 |
ServerSubscription* |
1150 |
Dialog::makeServerSubscription(const SipMessage& request) |
1151 |
{ |
1152 |
return new ServerSubscription(mDum, *this, request); |
1153 |
} |
1154 |
|
1155 |
Dialog::Exception::Exception(const Data& msg, const Data& file, int line) |
1156 |
: BaseException(msg, file, line) |
1157 |
{ |
1158 |
} |
1159 |
|
1160 |
|
1161 |
void |
1162 |
Dialog::send(SharedPtr<SipMessage> msg) |
1163 |
{ |
1164 |
if (msg->isRequest() && msg->header(h_CSeq).method() != ACK) |
1165 |
{ |
1166 |
mRequests[msg->header(h_CSeq).sequence()] = msg; |
1167 |
} |
1168 |
mDum.send(msg); |
1169 |
} |
1170 |
|
1171 |
void |
1172 |
Dialog::onForkAccepted() |
1173 |
{ |
1174 |
ClientInviteSession* uac = dynamic_cast<ClientInviteSession*>(mInviteSession); |
1175 |
if (uac) |
1176 |
{ |
1177 |
uac->onForkAccepted(); |
1178 |
} |
1179 |
} |
1180 |
|
1181 |
void |
1182 |
Dialog::possiblyDie() |
1183 |
{ |
1184 |
if (!mDestroying) |
1185 |
{ |
1186 |
if (mClientSubscriptions.empty() && |
1187 |
mServerSubscriptions.empty() && |
1188 |
!mInviteSession) |
1189 |
{ |
1190 |
mDestroying = true; |
1191 |
mDum.destroy(this); |
1192 |
} |
1193 |
} |
1194 |
} |
1195 |
|
1196 |
void |
1197 |
Dialog::flowTerminated() |
1198 |
{ |
1199 |
// Clear the network association |
1200 |
mNetworkAssociation.clear(); |
1201 |
|
1202 |
// notify server subscirption dialogs |
1203 |
std::list<ServerSubscription*> tempServerList = mServerSubscriptions; // Create copy since subscription can be deleted |
1204 |
for (std::list<ServerSubscription*>::iterator is=tempServerList.begin(); |
1205 |
is != tempServerList.end(); ++is) |
1206 |
{ |
1207 |
(*is)->flowTerminated(); |
1208 |
} |
1209 |
|
1210 |
// notify client subscription dialogs |
1211 |
std::list<ClientSubscription*> tempClientList = mClientSubscriptions; // Create copy since subscription can be deleted |
1212 |
for (std::list<ClientSubscription*>::iterator ic=tempClientList.begin(); |
1213 |
ic != tempClientList.end(); ++ic) |
1214 |
{ |
1215 |
(*ic)->flowTerminated(); |
1216 |
} |
1217 |
|
1218 |
// notify invite session dialog |
1219 |
if (mInviteSession) |
1220 |
{ |
1221 |
mInviteSession->flowTerminated(); |
1222 |
} |
1223 |
} |
1224 |
|
1225 |
EncodeStream& |
1226 |
resip::operator<<(EncodeStream& strm, const Dialog& dialog) |
1227 |
{ |
1228 |
strm |
1229 |
<< "mClientSubscriptions(" |
1230 |
<< dialog.mClientSubscriptions.size() |
1231 |
<< "), " |
1232 |
<< "mServerSubscriptions(" |
1233 |
<< dialog.mServerSubscriptions.size() |
1234 |
<< ")"; |
1235 |
return strm; |
1236 |
} |
1237 |
|
1238 |
|
1239 |
/* ==================================================================== |
1240 |
* The Vovida Software License, Version 1.0 |
1241 |
* |
1242 |
* Copyright (c) 2000 Vovida Networks, Inc. All rights reserved. |
1243 |
* |
1244 |
* Redistribution and use in source and binary forms, with or without |
1245 |
* modification, are permitted provided that the following conditions |
1246 |
* are met: |
1247 |
* |
1248 |
* 1. Redistributions of source code must retain the above copyright |
1249 |
* notice, this list of conditions and the following disclaimer. |
1250 |
* |
1251 |
* 2. Redistributions in binary form must reproduce the above copyright |
1252 |
* notice, this list of conditions and the following disclaimer in |
1253 |
* the documentation and/or other materials provided with the |
1254 |
* distribution. |
1255 |
* |
1256 |
* 3. The names "VOCAL", "Vovida Open Communication Application Library", |
1257 |
* and "Vovida Open Communication Application Library (VOCAL)" must |
1258 |
* not be used to endorse or promote products derived from this |
1259 |
* software without prior written permission. For written |
1260 |
* permission, please contact vocal@vovida.org. |
1261 |
* |
1262 |
* 4. Products derived from this software may not be called "VOCAL", nor |
1263 |
* may "VOCAL" appear in their name, without prior written |
1264 |
* permission of Vovida Networks, Inc. |
1265 |
* |
1266 |
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED |
1267 |
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
1268 |
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND |
1269 |
* NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA |
1270 |
* NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES |
1271 |
* IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL, |
1272 |
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
1273 |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
1274 |
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
1275 |
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
1276 |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE |
1277 |
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
1278 |
* DAMAGE. |
1279 |
* |
1280 |
* ==================================================================== |
1281 |
* |
1282 |
* This software consists of voluntary contributions made by Vovida |
1283 |
* Networks, Inc. and many individuals on behalf of Vovida Networks, |
1284 |
* Inc. For more information on Vovida Networks, Inc., please see |
1285 |
* <http://www.vovida.org/>. |
1286 |
* |
1287 |
*/ |
1288 |
|
1289 |
|
1290 |
|