/[resiprocate]/main/resip/dum/InviteSession.cxx
ViewVC logotype

Diff of /main/resip/dum/InviteSession.cxx

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 3676 by derek, Wed Dec 1 02:03:55 2004 UTC revision 4010 by jason, Sat Mar 19 03:54:17 2005 UTC
# Line 1  Line 1 
 #include "resiprocate/SdpContents.hxx"  
1  #include "resiprocate/MultipartMixedContents.hxx"  #include "resiprocate/MultipartMixedContents.hxx"
2    #include "resiprocate/SdpContents.hxx"
3  #include "resiprocate/SipMessage.hxx"  #include "resiprocate/SipMessage.hxx"
4    #include "resiprocate/Helper.hxx"
5  #include "resiprocate/dum/Dialog.hxx"  #include "resiprocate/dum/Dialog.hxx"
6  #include "resiprocate/dum/DialogUsageManager.hxx"  #include "resiprocate/dum/DialogUsageManager.hxx"
7  #include "resiprocate/dum/InviteSession.hxx"  #include "resiprocate/dum/InviteSession.hxx"
 #include "resiprocate/dum/ClientInviteSession.hxx"  
8  #include "resiprocate/dum/ServerInviteSession.hxx"  #include "resiprocate/dum/ServerInviteSession.hxx"
9    #include "resiprocate/dum/ClientSubscription.hxx"
10    #include "resiprocate/dum/ServerSubscription.hxx"
11    #include "resiprocate/dum/ClientInviteSession.hxx"
12  #include "resiprocate/dum/InviteSessionHandler.hxx"  #include "resiprocate/dum/InviteSessionHandler.hxx"
13  #include "resiprocate/dum/Profile.hxx"  #include "resiprocate/dum/MasterProfile.hxx"
14  #include "resiprocate/dum/UsageUseException.hxx"  #include "resiprocate/dum/UsageUseException.hxx"
15    #include "resiprocate/os/Inserter.hxx"
16  #include "resiprocate/os/Logger.hxx"  #include "resiprocate/os/Logger.hxx"
17  #include "resiprocate/os/Timer.hxx"  #include "resiprocate/os/Timer.hxx"
18  #include "resiprocate/os/Inserter.hxx"  #include "resiprocate/os/Random.hxx"
19    #include "resiprocate/os/compat.hxx"
20  #if defined(WIN32) && defined(_DEBUG) &&defined(LEAK_CHECK)// Used for tracking down memory leaks in Visual Studio  #include "resiprocate/os/WinLeakCheck.hxx"
 #define _CRTDBG_MAP_ALLOC  
 #include <stdlib.h>  
 #include <crtdbg.h>  
 #define new   new( _NORMAL_BLOCK, __FILE__, __LINE__)  
 #endif // defined(WIN32) && defined(_DEBUG)  
21    
22  // Remove warning about 'this' use in initiator list - pointer is only stored  // Remove warning about 'this' use in initiator list - pointer is only stored
23  #if defined(WIN32)  #if defined(WIN32)
24  #pragma warning( disable : 4355 ) // using this in base member initializer list  #pragma warning( disable : 4355 ) // using this in base member initializer list
25    #pragma warning( disable : 4800 ) // forcing value to bool (performance warning)
26  #endif  #endif
27    
28  #define RESIPROCATE_SUBSYSTEM Subsystem::DUM  #define RESIPROCATE_SUBSYSTEM Subsystem::DUM
29    #define THROW(msg)  throw DialogUsage::Exception(msg, __FILE__,__LINE__);
30    
31  using namespace resip;  using namespace resip;
32  using namespace std;  using namespace std;
33    
34  InviteSession::InviteSession(DialogUsageManager& dum, Dialog& dialog, State initialState)  InviteSession::InviteSession(DialogUsageManager& dum, Dialog& dialog)
35     : DialogUsage(dum, dialog),     : DialogUsage(dum, dialog),
36       mState(initialState),       mState(Undefined),
37       mNitState(NitComplete),       mNitState(NitComplete),
38       mOfferState(Nothing),       mCurrentRetransmit200(0),
      mCurrentLocalSdp(0),  
      mCurrentRemoteSdp(0),  
      mProposedLocalSdp(0),  
      mProposedRemoteSdp(0),  
      mNextOfferOrAnswerSdp(0),  
      mUserConnected(false),  
      mQueuedBye(0),  
39       mSessionInterval(0),       mSessionInterval(0),
40       mSessionRefresherUAS(false),       mSessionRefresherUAS(false),
41       mSessionTimerSeq(0),       mSessionTimerSeq(0),
42       mDestroyer(this),       mSentRefer(false)
      mCurrentRetransmit200(Timer::T1)  
43  {  {
44     DebugLog ( << "^^^ InviteSession::InviteSession " << this);       DebugLog ( << "^^^ InviteSession::InviteSession " << this);  
45     assert(mDum.mInviteSessionHandler);     assert(mDum.mInviteSessionHandler);
# Line 55  Line 48 
48  InviteSession::~InviteSession()  InviteSession::~InviteSession()
49  {  {
50     DebugLog ( << "^^^ InviteSession::~InviteSession " << this);       DebugLog ( << "^^^ InviteSession::~InviteSession " << this);  
    delete mCurrentLocalSdp;  
    delete mCurrentRemoteSdp;  
    delete mProposedLocalSdp;  
    delete mProposedRemoteSdp;  
    delete mNextOfferOrAnswerSdp;  
    delete mQueuedBye;    
51     mDialog.mInviteSession = 0;     mDialog.mInviteSession = 0;
52  }  }
53    
 SipMessage&  
 InviteSession::modifySession()  
 {  
    DebugLog( << "InviteSession::modifySession: " << mDialog.getId());    
    if (mNextOfferOrAnswerSdp == 0 || mState != Connected || mOfferState != Answered)  
    {  
       throw UsageUseException("Must be in the connected state and have propsed an offer to call modifySession",  
                                   __FILE__, __LINE__);  
    }  
    mState = ReInviting;  
    mDialog.makeRequest(mLastRequest, INVITE);  
    return mLastRequest;  
 }  
   
 SipMessage&  
 InviteSession::makeFinalResponse(int code)  
 {  
    int cseq = mLastIncomingRequest.header(h_CSeq).sequence();  
    SipMessage& finalResponse = mFinalResponseMap[cseq];  
    mDialog.makeResponse(finalResponse, mLastIncomingRequest, 200);  
   
    // Add Session Timer info to response (if required)  
    handleSessionTimerRequest(mLastIncomingRequest, finalResponse);  
   
    // Check if we should add our capabilites to the invite success response  
    if(mDum.getProfile()->isAdvertisedCapability(Headers::Allow)) finalResponse.header(h_Allows) = mDum.getProfile()->getAllowedMethods();  
    if(mDum.getProfile()->isAdvertisedCapability(Headers::AcceptEncoding)) finalResponse.header(h_AcceptEncodings) = mDum.getProfile()->getSupportedEncodings();  
    if(mDum.getProfile()->isAdvertisedCapability(Headers::AcceptLanguage)) finalResponse.header(h_AcceptLanguages) = mDum.getProfile()->getSupportedLanguages();  
    if(mDum.getProfile()->isAdvertisedCapability(Headers::Supported)) finalResponse.header(h_Supporteds) = mDum.getProfile()->getSupportedOptionTags();  
   
    return finalResponse;  
 }  
   
 SipMessage&  
 InviteSession::acceptDialogModification(int statusCode)  
 {  
    if (mNextOfferOrAnswerSdp == 0 || mState != ReInviting)  
    {  
       throw UsageUseException("Must be in the ReInviting state and have propsed an answer to call answerModifySession",  
                                   __FILE__, __LINE__);  
    }  
    mState = Connected;  
    return makeFinalResponse(statusCode);  
 }  
   
54  void  void
55  InviteSession::setOffer(const SdpContents* sdp)  InviteSession::dialogDestroyed(const SipMessage& msg)
 {  
    DebugLog( << "InviteSession::setOffer: " << mDialog.getId());    
    if (mProposedRemoteSdp)  
56     {     {
57        throw UsageUseException("Cannot set an offer with an oustanding remote offer", __FILE__, __LINE__);     assert(0);
    }  
    assert(mNextOfferOrAnswerSdp == 0);  
    mNextOfferOrAnswerSdp = static_cast<SdpContents*>(sdp->clone());  
 }  
58    
59  void     // !jf! Is this correct? Merged from main...
60  InviteSession::setAnswer(const SdpContents* sdp)     // !jf! what reason - guessed for now?
61  {     //mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), InviteSessionHandler::PeerEnded, msg);  
62     DebugLog( << "InviteSession::setAnswer: " << mDialog.getId());       //delete this;  
    if (mProposedLocalSdp )  
    {  
       throw UsageUseException("Cannot set an answer with an oustanding offer", __FILE__, __LINE__);  
    }  
    assert(mNextOfferOrAnswerSdp == 0);  
    mNextOfferOrAnswerSdp = static_cast<SdpContents*>(sdp->clone());  
63  }  }
64    
65  const SdpContents*  const SdpContents&
66  InviteSession::getLocalSdp() const  InviteSession::getLocalSdp() const
67  {  {
68     return mCurrentLocalSdp;     return *mCurrentLocalSdp;
69  }  }
70    
71  const SdpContents*  const SdpContents&
72  InviteSession::getRemoteSdp() const  InviteSession::getRemoteSdp() const
73  {  {
74     return mCurrentRemoteSdp;     return *mCurrentRemoteSdp;
75  }  }
76    
77  InviteSessionHandle  InviteSessionHandle
# Line 151  Line 80 
80     return InviteSessionHandle(mDum, getBaseHandle().getId());     return InviteSessionHandle(mDum, getBaseHandle().getId());
81  }  }
82    
83  void  void InviteSession::storePeerCapabilities(const SipMessage& msg)
 InviteSession::dispatch(const DumTimeout& timeout)  
 {  
    Destroyer::Guard guard(mDestroyer);  
    if (timeout.type() == DumTimeout::Retransmit200)  
84     {     {
85        CSeqToMessageMap::iterator it = mFinalResponseMap.find(timeout.seq());     // !slg! ToDo - add methods to get this data, App may be interested
86        if (it != mFinalResponseMap.end())     if (msg.exists(h_Allows))
87        {        {
88           mDum.send(it->second);        mPeerSupportedMethods = msg.header(h_Allows);
          mCurrentRetransmit200 *= 2;  
          mDum.addTimerMs(DumTimeout::Retransmit200, resipMin(Timer::T2, mCurrentRetransmit200), getBaseHandle(),  timeout.seq());  
       }  
89     }     }
90     else if (timeout.type() == DumTimeout::WaitForAck)     if (msg.exists(h_Supporteds))
91     {     {
92        CSeqToMessageMap::iterator it = mFinalResponseMap.find(timeout.seq());        mPeerSupportedOptionTags = msg.header(h_Supporteds);
93        if (it != mFinalResponseMap.end())     }
94       if (msg.exists(h_AcceptEncodings))
95        {        {
96           // BYE could be queued if end() is called when we are still waiting for far end ACK to be received        mPeerSupportedEncodings = msg.header(h_AcceptEncodings);
97           if (mQueuedBye)     }
98       if (msg.exists(h_AcceptLanguages))
99           {           {
100              mState = Terminated;        mPeerSupportedLanguages = msg.header(h_AcceptLanguages);
             mLastRequest = *mQueuedBye;  
             delete mQueuedBye;  
             mQueuedBye = 0;                          
             send(mLastRequest);  
101           }           }
102           else     if (msg.exists(h_Accepts))
103           {           {
104              mDum.mInviteSessionHandler->onAckNotReceived(getSessionHandle(), it->second);        mPeerSupportedMimeTypes = msg.header(h_Accepts);
105       }
106           }           }
107    
108           mFinalResponseMap.erase(it);  bool
109    InviteSession::updateMethodSupported() const
110    {
111       // Check if Update is supported locally
112       if(mDum.getMasterProfile()->isMethodSupported(UPDATE))
113       {
114           // Check if peer supports UPDATE
115           return mPeerSupportedMethods.find(Token("UPDATE"));
116        }        }
117       return false;
118     }     }
119     else if (timeout.type() == DumTimeout::CanDiscardAck)  
120    const NameAddr&
121    InviteSession::myAddr() const
122     {     {
123        assert(mAckMap.find(timeout.seq()) != mFinalResponseMap.end());     return mDialog.mLocalNameAddr;
       mAckMap.erase(timeout.seq());  
124     }     }
125     else if (timeout.type() == DumTimeout::SessionExpiration)  
126    const NameAddr&
127    InviteSession::peerAddr() const
128     {     {
129        if(timeout.seq() == mSessionTimerSeq)     return mDialog.mRemoteNameAddr;
130    }
131    
132    bool
133    InviteSession::isConnected() const
134        {        {
135            if(mState != Terminated)     switch (mState)
136            {            {
137               end();  // end expired session        case Connected:
138            }        case SentUpdate:
139          case SentUpdateGlare:
140          case SentReinvite:
141          case SentReinviteGlare:
142          case ReceivedUpdate:
143          case ReceivedReinvite:
144          case ReceivedReinviteNoOffer:
145          case Answered:
146          case WaitingToOffer:
147             return true;
148    
149          default:
150             return false;
151        }        }
152     }     }
153     else if (timeout.type() == DumTimeout::SessionRefresh)  
154     {  bool
155        if(timeout.seq() == mSessionTimerSeq)  InviteSession::isEarly() const
       {  
          if(mState == Connected)  
156           {           {
157              mState = ReInviting;     switch (mState)
             setOffer(mCurrentLocalSdp);  
             // Should likely call targetRefresh when implemented - for now only ReInvites are used  
             mDialog.makeRequest(mLastRequest, INVITE);  
             if(mSessionInterval >= 90)  
158              {              {
159                 mLastRequest.header(h_SessionExpires).value() = mSessionInterval;        case UAC_Start:
160          case UAC_Early:
161          case UAC_EarlyWithOffer:
162          case UAC_EarlyWithAnswer:
163             //case UAC_Answered:
164             //case UAC_Terminated:
165          case UAC_SentUpdateEarly:
166          case UAC_SentUpdateConnected:
167          case UAC_ReceivedUpdateEarly:
168             //case UAC_SentAnswer:
169          case UAC_QueuedUpdate:
170             return true;
171    
172                 mLastRequest.header(h_SessionExpires).param(p_refresher) = Data(mSessionRefresherUAS ? "uas" : "uac");        default:
173              }           return false;
             send(mLastRequest);  
          }  
       }  
174     }     }
175  }  }
176    
 void  
 InviteSession::handleSessionTimerResponse(const SipMessage& msg)  
 {  
    // If session timers are locally supported then handle response  
    if(mDum.getProfile()->getSupportedOptionTags().find(Token(Symbols::Timer)))  
    {  
       bool fUAS = dynamic_cast<ServerInviteSession*>(this) != NULL;  
177    
178        // Process Session Timer headers  bool
179        if(msg.exists(h_Requires) && msg.header(h_Requires).find(Token(Symbols::Timer)))  InviteSession::isTerminated() const
       {  
          if(msg.exists(h_SessionExpires))  
180           {           {
181              mSessionInterval = msg.header(h_SessionExpires).value();     switch (mState)
             mSessionRefresherUAS = fUAS;  // Default to us as refresher  
             if(msg.header(h_SessionExpires).exists(p_refresher))  
182              {              {
183                  mSessionRefresherUAS = (msg.header(h_SessionExpires).param(p_refresher) == Data("uas"));                            case Terminated:
184          case WaitingToTerminate:
185          case UAC_Cancelled:
186          case UAS_WaitingToTerminate:
187          case UAS_WaitingToHangup:
188             return true;
189          default:
190             return false;
191              }              }
192           }           }
193           else  
194    std::ostream&
195    InviteSession::dump(std::ostream& strm) const
196           {           {
197              // If no Session Expires in response then session timer is to be 'turned off'     strm << "INVITE: " << mId
198              mSessionInterval = 0;          << " " << toData(mState)
199           }          << " ADDR=" << myAddr()
200            << " PEER=" << peerAddr();
201       return strm;
202        }        }
203        else if(msg.exists(h_SessionExpires))  // If UAS decides to be the refresher - then he MAY not set the Requires header to timer  
204    
205    void
206    InviteSession::provideOffer(const SdpContents& offer)
207        {        {
208           mSessionInterval = msg.header(h_SessionExpires).value();     switch (mState)
          mSessionRefresherUAS = fUAS;  // Default to us as refresher  
          if(msg.header(h_SessionExpires).exists(p_refresher))  
209           {           {
210               mSessionRefresherUAS = (msg.header(h_SessionExpires).param(p_refresher) == Data("uas"));                            case Connected:
211          case WaitingToOffer:
212          case UAS_WaitingToOffer:
213             if (updateMethodSupported())
214             {
215                transition(SentUpdate);
216                mDialog.makeRequest(mLastSessionModification, UPDATE);
217           }           }
218             else
219             {
220                transition(SentReinvite);
221                mDialog.makeRequest(mLastSessionModification, INVITE);
222        }        }
       // Note:  If no Requires or Session-Expires, then UAS does not support Session Timers - we are free to use our settings  
223    
224        if(mSessionInterval >= 90)  // 90 is the absolute minimum           if(mSessionInterval >= 90)  // If mSessionInterval is 0 then SessionTimers are considered disabled
225        {        {
226           // Check if we are the refresher              mLastSessionModification.header(h_SessionExpires).value() = mSessionInterval;
227           if((fUAS && mSessionRefresherUAS) || (!fUAS && !mSessionRefresherUAS))              mLastSessionModification.header(h_SessionExpires).param(p_refresher) = Data(mSessionRefresherUAS ? "uas" : "uac");
          {  
             // Start Session-Refresh Timer to mSessionInterval / 2 (recommended by draft-ietf-sip-session-timer-15)  
             mDum.addTimer(DumTimeout::SessionRefresh, mSessionInterval / 2, getBaseHandle(), ++mSessionTimerSeq);  
228           }           }
229           else           else
230           {           {
231              // Start Session-Expiration Timer to mSessionInterval - BYE should be sent a minimum of 32 or SessionInterval/3 seconds before the session expires (recommended by draft-ietf-sip-session-timer-15)              mLastSessionModification.remove(h_SessionExpires);
232              mDum.addTimer(DumTimeout::SessionExpiration, mSessionInterval - resipMin(32,mSessionInterval/3), getBaseHandle(), ++mSessionTimerSeq);              mLastSessionModification.remove(h_MinSE);
          }  
233        }        }
234    
235             InfoLog (<< "Sending " << mLastSessionModification.brief());
236             InviteSession::setSdp(mLastSessionModification, offer);
237             mProposedLocalSdp = InviteSession::makeSdp(offer);
238             mDialog.send(mLastSessionModification);
239             break;
240    
241          case Answered:
242             // queue the offer to be sent after the ACK is received
243             transition(WaitingToOffer);
244             mProposedLocalSdp = InviteSession::makeSdp(offer);
245             break;
246    
247          // !slg! Can we handle all of the states listed in isConnected() ???
248          default:
249             WarningLog (<< "Can't provideOffer when not in Connected state");
250             throw DialogUsage::Exception("Can't provide an offer", __FILE__,__LINE__);
251     }     }
252  }  }
253    
254  void  void
255  InviteSession::handleSessionTimerRequest(const SipMessage& request, SipMessage &response)  InviteSession::provideAnswer(const SdpContents& answer)
256  {  {
257     // If session timers are locally supported then add necessary headers to response     switch (mState)
    if(mDum.getProfile()->getSupportedOptionTags().find(Token(Symbols::Timer)))  
258     {     {
259        bool fUAS = dynamic_cast<ServerInviteSession*>(this) != NULL;        case ReceivedReinvite:
260             transition(Connected);
261             mDialog.makeResponse(mInvite200, mLastSessionModification, 200);
262             handleSessionTimerRequest(mInvite200, mLastSessionModification);
263             InviteSession::setSdp(mInvite200, answer);
264             mCurrentLocalSdp = InviteSession::makeSdp(answer);
265             mCurrentRemoteSdp = mProposedRemoteSdp;
266             InfoLog (<< "Sending " << mInvite200.brief());
267             mDialog.send(mInvite200);
268             startRetransmit200Timer();
269             break;
270    
271        // Check if we are the refresher        case ReceivedUpdate: // same as ReceivedReinvite case.
       if((fUAS && mSessionRefresherUAS) || (!fUAS && !mSessionRefresherUAS))  
272        {        {
273           // If we receive a reinvite, but we are the refresher - don't process for session timers (probably just a TargetRefresh or hold request)           transition(Connected);
          return;  
       }  
   
       mSessionInterval = mDum.getProfile()->getDefaultSessionTime();  // Used only if UAC doesn't request a time  
       mSessionRefresherUAS = true;  // Used only if UAC doesn't request a time  
274    
275        // Check if far-end supports           SipMessage response;
276        bool farEndSupportsTimer = false;           mDialog.makeResponse(response, mLastSessionModification, 200);
277        if(request.exists(h_Supporteds) && request.header(h_Supporteds).find(Token(Symbols::Timer)))           handleSessionTimerRequest(response, mLastSessionModification);
278        {           InviteSession::setSdp(response, answer);
279           farEndSupportsTimer = true;           mCurrentLocalSdp = InviteSession::makeSdp(answer);
280           if(request.exists(h_SessionExpires))           mCurrentRemoteSdp = mProposedRemoteSdp;
281           {           InfoLog (<< "Sending " << response.brief());
282              // Use Session Interval requested by UAC - if none then use local settings           mDialog.send(response);
283              mSessionInterval = request.header(h_SessionExpires).value();           break;
             mSessionRefresherUAS = fUAS;  // Default to us as refresher  
             if(request.header(h_SessionExpires).exists(p_refresher))  
             {  
                 mSessionRefresherUAS = (request.header(h_SessionExpires).param(p_refresher) == Data("uas"));                      
284              }              }
285    
286          default:
287             WarningLog (<< "Can't provideOffer when not in Connected state");
288             throw DialogUsage::Exception("Can't provide an offer", __FILE__,__LINE__);
289           }           }
290        }        }
291    
292        // Add Session-Expires if required  void
293        if(mSessionInterval >= 90)  InviteSession::end()
294        {        {
295           if(farEndSupportsTimer)     InviteSessionHandler* handler = mDum.mInviteSessionHandler;
296    
297       switch (mState)
298           {           {
299              response.header(h_Requires).push_back(Token(Symbols::Timer));        case Connected:
300          {
301             // !jf! do we need to store the BYE somewhere?
302             sendBye();
303             transition(Terminated);
304             handler->onTerminated(getSessionHandle(), InviteSessionHandler::Ended);
305             break;
306           }           }
          response.header(h_SessionExpires).value() = mSessionInterval;  
          response.header(h_SessionExpires).param(p_refresher) = Data(mSessionRefresherUAS ? "uas" : "uac");  
307    
308           // Check if we are the refresher        case SentUpdate:
309           if((fUAS && mSessionRefresherUAS) || (!fUAS && !mSessionRefresherUAS))           sendBye();
310             transition(Terminated);
311             handler->onTerminated(getSessionHandle(), InviteSessionHandler::Ended);
312             break;
313    
314          case SentReinvite:
315             transition(WaitingToTerminate);
316             break;
317    
318          case ReceivedUpdate:
319          case ReceivedReinvite:
320          case ReceivedReinviteNoOffer:
321           {           {
322              // Start Session-Refresh Timer to mSessionInterval / 2 (recommended by draft-ietf-sip-session-timer-15)           SipMessage response;
323              mDum.addTimer(DumTimeout::SessionRefresh, mSessionInterval / 2, getBaseHandle(), ++mSessionTimerSeq);           mDialog.makeResponse(response, mLastSessionModification, 488);
324             InfoLog (<< "Sending " << response.brief());
325             mDialog.send(response);
326    
327             sendBye();
328             transition(Terminated);
329             handler->onTerminated(getSessionHandle(), InviteSessionHandler::Ended);
330             break;
331           }           }
332           else  
333          case WaitingToTerminate:
334           {           {
335              // Start Session-Expiration Timer to mSessionInterval - BYE should be sent a minimum of 32 or SessionInterval/3 seconds before the session expires (recommended by draft-ietf-sip-session-timer-15)           sendBye();
336              mDum.addTimer(DumTimeout::SessionExpiration, mSessionInterval - resipMin(32,mSessionInterval/3), getBaseHandle(), ++mSessionTimerSeq);           transition(Terminated);
337           }           handler->onTerminated(getSessionHandle(), InviteSessionHandler::Ended);
338             break;
339        }        }
340    
341          case Terminated:
342             // no-op.
343             break;
344    
345          default:
346             assert(0);
347             break;
348     }     }
349  }  }
350    
351  void  void
352  InviteSession::dispatch(const SipMessage& msg)  InviteSession::reject(int statusCode, WarningCategory *warning)
353  {  {
354     Destroyer::Guard guard(mDestroyer);     switch (mState)
    std::pair<OfferAnswerType, const SdpContents*> offans;  
    offans = InviteSession::getOfferOrAnswer(msg);  
     
    //ugly. non-invite-transactions(nit) don't interact with the invite  
    //transaction state machine(for now we have a separate INFO state machine)  
    //it's written as a gerneric NIT satet machine, but method isn't checked, and  
    //info is the only NIT so far. This should eventually live in Dialog, with a  
    //current method to determine valid responses.  
    if (msg.header(h_CSeq).method() == INFO)  
355     {     {
356        if (msg.isRequest())        case ReceivedUpdate:
357          case ReceivedReinvite:
358          case ReceivedReinviteNoOffer:
359        {        {
360             transition(Connected);
361    
362           SipMessage response;                   SipMessage response;        
363           mDialog.makeResponse(response, msg, 200);           mDialog.makeResponse(response, mLastSessionModification, statusCode);
364           send(response);           if(warning)
          mDum.mInviteSessionHandler->onInfo(getSessionHandle(), msg);  
       }  
       else  
       {  
          if (mNitState == NitProceeding)  
365           {                       {            
366              int code = msg.header(h_StatusLine).statusCode();                          response.header(h_Warnings).push_back(*warning);
             if (code < 200)  
             {  
                //ignore  
             }  
             else if (code < 300)  
             {  
                mNitState = NitComplete;  
                mDum.mInviteSessionHandler->onInfoSuccess(getSessionHandle(), msg);  
             }  
             else  
             {  
                mNitState = NitComplete;  
                mDum.mInviteSessionHandler->onInfoFailure(getSessionHandle(), msg);  
367              }              }
368             InfoLog (<< "Sending " << response.brief());
369             mDialog.send(response);
370             break;
371           }           }
372    
373          default:
374             assert(0);
375             break;
376        }              }      
       return;  
377     }             }        
378    
379     if (msg.isRequest() && msg.header(h_RequestLine).method() == ACK &&  void
380         (mState == Connected || mState == ReInviting))  InviteSession::targetRefresh(const NameAddr& localUri)
381     {     {
382        //quench 200 retransmissions     if (isConnected()) // !slg! likely not safe in any state except Connected - what should behaviour be if state is ReceivedReinvite?
       mFinalResponseMap.erase(msg.header(h_CSeq).sequence());  
       if (msg.header(h_CSeq).sequence() == mLastIncomingRequest.header(h_CSeq).sequence())  
383        {        {
384           // BYE could be queued if end() is called when we are still waiting for far end ACK to be received        // !jf! add interface to Dialog
385           if (mQueuedBye)        //mDialog.setLocalContact(localUri);
386          provideOffer(*mCurrentLocalSdp);
387       }
388       else
389           {           {
390              mState = Terminated;        WarningLog (<< "Can't targetRefresh before Connected");
391              mLastRequest = *mQueuedBye;        assert(0);
392              delete mQueuedBye;        throw UsageUseException("targetRefresh not allowed in this context", __FILE__, __LINE__);
393              mQueuedBye = 0;                             }
             send(mLastRequest);  
             return;  
394           }           }
395    
396           if (offans.first != None)  void
397    InviteSession::refer(const NameAddr& referTo)
398           {                               {                    
399              if (mOfferState == Answered)     if (mSentRefer)
400              {              {
401                 //SDP in invite and in ACK.        throw UsageUseException("Attempted to send overlapping refer", __FILE__, __LINE__);
                mDum.mInviteSessionHandler->onIllegalNegotiation(getSessionHandle(), msg);  
402              }              }
403              else  
404              {     if (isConnected()) // !slg! likely not safe in any state except Connected - what should behaviour be if state is ReceivedReinvite?
                //delaying onConnected until late SDP  
                InviteSession::incomingSdp(msg, offans.second);  
                if (!mUserConnected)  
405                 {                 {
406                    mUserConnected = true;        mSentRefer = true;
407                    mDum.mInviteSessionHandler->onConnected(getSessionHandle(), msg);        SipMessage refer;
408                 }        mDialog.makeRequest(refer, REFER);
409          refer.header(h_ReferTo) = referTo;
410          refer.header(h_ReferredBy) = mDialog.mLocalContact; // !slg! is it ok to do this - should it be an option?
411          mDialog.send(refer);
412              }              }
413           }     else
          //temporary hack  
          else if (mState != ReInviting && mOfferState != Answered)  
414           {           {
415              //no SDP in ACK when one is required        WarningLog (<< "Can't refer before Connected");
416              mDum.mInviteSessionHandler->onIllegalNegotiation(getSessionHandle(), msg);        assert(0);
417           }        throw UsageUseException("REFER not allowed in this context", __FILE__, __LINE__);
418        }              }      
419     }     }
420    
421     switch(mState)  void
422    InviteSession::refer(const NameAddr& referTo, InviteSessionHandle sessionToReplace)
423     {     {
424        case Terminated:     if (!sessionToReplace.isValid())
          //!dcm! -- 481 behaviour here, should pretty much die on anything  
          //eventually 200 to BYE could be handled further out  
          if (msg.isResponse())  
425           {           {
426              int code = msg.header(h_StatusLine).statusCode();        throw UsageUseException("Attempted to make a refer w/ and invalid replacement target", __FILE__, __LINE__);
427              if ((code  == 200 && msg.header(h_CSeq).method() == BYE) || code > 399)     }
428    
429       if (mSentRefer)
430              {              {
431                 mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), msg);              throw UsageUseException("Attempted to send overlapping refer", __FILE__, __LINE__);
                guard.destroy();  
                return;  
432              }              }
433    
434       if (isConnected())  // !slg! likely not safe in any state except Connected - what should behaviour be if state is ReceivedReinvite?
435       {
436          mSentRefer = true;
437          SipMessage refer;
438          mDialog.makeRequest(refer, REFER);
439    
440          refer.header(h_ReferTo) = referTo;
441          refer.header(h_ReferredBy) = mDialog.mLocalContact; // !slg! is it ok to do this - should it be an option?
442          CallId replaces;
443          DialogId id = sessionToReplace->mDialog.getId();
444          replaces.value() = id.getCallId();
445          replaces.param(p_toTag) = id.getRemoteTag();
446          replaces.param(p_fromTag) = id.getLocalTag();
447    
448          refer.header(h_ReferTo).uri().embedded().header(h_Replaces) = replaces;
449          mDialog.send(refer);
450           }           }
451           else           else
452           {           {
453              //make a function to do this & the occurences of this in DialogUsageManager        WarningLog (<< "Can't refer before Connected");
454              SipMessage failure;        assert(0);
455              mDum.makeResponse(failure, msg, 481);        throw UsageUseException("REFER not allowed in this context", __FILE__, __LINE__);
             failure.header(h_AcceptLanguages) = mDum.mProfile->getSupportedLanguages();  
             mDum.sendResponse(failure);  
456           }           }
457           break;  }
458        case Connected:  
459           if (msg.isRequest())  void
460           {  InviteSession::info(const Contents& contents)
             switch(msg.header(h_RequestLine).method())  
             {  
                        // reINVITE  
                case INVITE:  
461                 {                 {
462                    if (mOfferState == Answered)     if (mNitState == NitComplete)
463                    {                    {
464                       mState = ReInviting;        if (isConnected())  // !slg! likely not safe in any state except Connected - what should behaviour be if state is ReceivedReinvite?
                      mDialog.update(msg);  
                      mLastIncomingRequest = msg;  
                      mDum.mInviteSessionHandler->onDialogModified(getSessionHandle(), offans.first, msg);  
                      if (offans.first != None)  
465                       {                       {
466                          incomingSdp(msg, offans.second);           mNitState = NitProceeding;
467             SipMessage info;
468             mDialog.makeRequest(info, INFO);
469             // !jf! handle multipart here
470             info.setContents(&contents);
471             mDialog.send(info);
472                       }                       }
473                       else                       else
474                       {                       {
475                          mDum.mInviteSessionHandler->onOfferRequired(getSessionHandle(), msg);                                   WarningLog (<< "Can't send INFO before Connected");
476             assert(0);
477             throw UsageUseException("Can't send INFO before Connected", __FILE__, __LINE__);
478                       }                                                                   }                                            
479                    }                    }
480                    else                    else
481                    {                    {
482                       //4??        throw UsageUseException("Cannot start a non-invite transaction until the previous one has completed",
483                       SipMessage failure;                                __FILE__, __LINE__);
                      mDialog.makeResponse(failure, msg, 491);  
                      InfoLog (<< "Sending 491 - overlapping Invite transactions");  
                      mDum.sendResponse(failure);  
484                    }                    }
485                 }                   }  
486    
487    void
488    InviteSession::dispatch(const SipMessage& msg)
489    {
490       // !jf! do we need to handle 3xx here or is it handled elsewhere?
491       switch (mState)
492       {
493          case Connected:
494             dispatchConnected(msg);
495                 break;                 break;
496                 case BYE:        case SentUpdate:
497                    mState = Terminated;           dispatchSentUpdate(msg);
                   mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), msg);  
                   mDialog.makeResponse(mLastResponse, msg, 200);  
                   send(mLastResponse);  
498                    break;                    break;
499          case SentReinvite:
500                 case UPDATE:           dispatchSentReinvite(msg);
                   assert(0);  
501                    break;                    break;
502                            case SentUpdateGlare:
503                 case INFO:        case SentReinviteGlare:
504                    mDum.mInviteSessionHandler->onInfo(getSessionHandle(), msg);           // The behavior is the same except for timer which is handled in dispatch(Timer)
505             dispatchGlare(msg);
506             break;
507          case ReceivedUpdate:
508          case ReceivedReinvite:
509          case ReceivedReinviteNoOffer:
510             dispatchReceivedUpdateOrReinvite(msg);
511                    break;                                                    break;                                
512                 case REFER:        case Answered:
513                    //handled in Dialog           dispatchAnswered(msg);
                   assert(0);                    
514                    break;                    break;
515          case WaitingToOffer:
516                             case CANCEL:           dispatchWaitingToOffer(msg);
                                   // A Cancel can get received in an established dialog if it crosses with our 200 response  
                                   // on the wire - it should be responsed to, but should not effect the dialog state (InviteSession).    
                                   // RFC3261 Section 9.2  
                   mDialog.makeResponse(mLastResponse, msg, 200);  
                   send(mLastResponse);  
517                                    break;                                    break;
518                            case WaitingToTerminate:
519             dispatchWaitingToTerminate(msg);
520             break;
521          case Terminated:
522             dispatchTerminated(msg);
523             break;
524          case Undefined:
525                 default:                 default:
526                    InfoLog (<< "Ignoring request in an INVITE dialog: " << msg.brief());           assert(0);
527                    break;                    break;
528              }              }
529           }           }
530           else  
531    void
532    InviteSession::dispatch(const DumTimeout& timeout)
533           {           {
534              if ( msg.header(h_StatusLine).statusCode() == 200 && msg.header(h_CSeq).method() == INVITE)     if (timeout.type() == DumTimeout::Retransmit200)
535              {              {
536                 CSeqToMessageMap::iterator it = mAckMap.find(msg.header(h_CSeq).sequence());        if (mCurrentRetransmit200)
                if (it != mAckMap.end())  
537                 {                 {
538                    mDum.send(it->second);           InfoLog (<< "Retransmitting: " << endl << mInvite200);
539             mDialog.send(mInvite200);
540             mCurrentRetransmit200 *= 2;
541             mDum.addTimerMs(DumTimeout::Retransmit200, resipMin(Timer::T2, mCurrentRetransmit200), getBaseHandle(),  timeout.seq());
542                 }                 }
543              }              }
544       else if (timeout.type() == DumTimeout::WaitForAck)
545       {
546          if(mCurrentRetransmit200)  // If retransmit200 timer is active then ACK is not received yet
547          {
548             mCurrentRetransmit200 = 0; // stop the 200 retransmit timer
549    
550             // this is so the app can decided to ignore this. default implementation
551             // will call end next
552             mDum.mInviteSessionHandler->onAckNotReceived(getSessionHandle());
553    
554             // If we are waiting for an Ack and it times out, then end with a BYE
555             if(mState == UAS_WaitingToHangup)
556             {
557                 sendBye();
558                 transition(Terminated);
559                 mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), InviteSessionHandler::Ended);
560             }
561          }
562       }
563       else if (timeout.type() == DumTimeout::Glare)
564       {
565          if (mState == SentUpdateGlare)
566          {
567             transition(SentUpdate);
568    
569             InfoLog (<< "Retransmitting the UPDATE (glare condition timer)");
570             mDialog.send(mLastSessionModification);
571          }
572          else if (mState == SentReinviteGlare)
573          {
574             transition(SentReinvite);
575    
576             InfoLog (<< "Retransmitting the reINVITE (glare condition timer)");
577             mDialog.send(mLastSessionModification);
578           }           }
579       }
580       else if (timeout.type() == DumTimeout::SessionExpiration)
581       {
582          if(timeout.seq() == mSessionTimerSeq)
583          {
584             // this is so the app can decided to ignore this. default implementation
585             // will call end next
586             mDum.mInviteSessionHandler->onSessionExpired(getSessionHandle());
587          }
588       }
589       else if (timeout.type() == DumTimeout::SessionRefresh)
590       {
591         if(timeout.seq() == mSessionTimerSeq)
592          {
593             if(mState == Connected)  // Note:  If not connected then we must be issueing a reinvite/update or receiving one - in either case the session timer stuff will get reset/renegotiated - thus just ignore this referesh
594             {
595                // Note:  If UPDATE is supported then UPDATE request should probably not contain an SDP, since it is not changing - changes are required for this (and to the QueuedUpdate state)
596                provideOffer(*mCurrentLocalSdp);
597             }
598          }
599       }
600    }
601    
602    
603    void
604    InviteSession::dispatchConnected(const SipMessage& msg)
605    {
606       InviteSessionHandler* handler = mDum.mInviteSessionHandler;
607       std::auto_ptr<SdpContents> sdp = InviteSession::getSdp(msg);
608    
609       switch (toEvent(msg, sdp.get()))
610       {
611          case OnInvite:
612          case OnInviteReliable:
613             mLastSessionModification = msg;
614             transition(ReceivedReinviteNoOffer);
615             //handler->onDialogModified(getSessionHandle(), None, msg);
616             handler->onOfferRequired(getSessionHandle(), msg);
617             break;
618    
619          case OnInviteOffer:
620          case OnInviteReliableOffer:
621             mLastSessionModification = msg;
622             transition(ReceivedReinvite);
623             //handler->onDialogModified(getSessionHandle(), Offer, msg);
624             handler->onOffer(getSessionHandle(), msg, *sdp);
625             break;
626    
627          case On2xx:
628          case On2xxOffer:
629          case On2xxAnswer:
630             // retransmission of 200I
631             // !jf! Need to include the answer here.
632             sendAck();
633             break;
634    
635          case OnUpdate:
636             transition(ReceivedUpdate);
637    
638             //  !kh!
639             //  Find out if it's an UPDATE requiring state change.
640             //  See rfc3311 5.2, 4th paragraph.
641             mLastSessionModification = msg;
642             handler->onOffer(getSessionHandle(), msg, *sdp);
643             break;
644    
645          case OnUpdateRejected:
646          case On200Update:
647                WarningLog (<< "DUM delivered an UPDATE response in an incorrect state " << endl << msg);
648                assert(0);
649           break;           break;
650        case ReInviting:  
651           if (msg.header(h_CSeq).method() == INVITE)        case OnAck:
652             mCurrentRetransmit200 = 0; // stop the 200 retransmit timer
653             break;
654    
655          default:
656             dispatchOthers(msg);
657             break;
658       }
659    }
660    
661    
662    
663    void
664    InviteSession::dispatchSentUpdate(const SipMessage& msg)
665           {           {
666              if (msg.isResponse())     InviteSessionHandler* handler = mDum.mInviteSessionHandler;
667       std::auto_ptr<SdpContents> sdp = InviteSession::getSdp(msg);
668    
669       switch (toEvent(msg, sdp.get()))
670              {              {
671                 int code = msg.header(h_StatusLine).statusCode();        case OnInvite:
672                 if (code < 200)        case OnInviteReliable:
673          case OnInviteOffer:
674          case OnInviteReliableOffer:
675          case OnUpdate:
676                 {                 {
677                    return;                             // glare
678             SipMessage response;
679             mDialog.makeResponse(response, msg, 491);
680             send(response);
681             break;
682                 }                                   }                  
683                 else if (code < 300)  
684          case On200Update:
685             transition(Connected);
686             handleSessionTimerResponse(msg);
687             if (sdp.get())
688                 {                 {
689                    if (msg.header(h_CSeq).sequence() == mLastRequest.header(h_CSeq).sequence())              mCurrentLocalSdp = mProposedLocalSdp;
690                mCurrentRemoteSdp = InviteSession::makeSdp(*sdp);
691                handler->onAnswer(getSessionHandle(), msg, *sdp);
692             }
693             else
694                    {                    {
695                       mState = Connected;              handler->onIllegalNegotiation(getSessionHandle(), msg);
696                       //user has called end, so no more callbacks relating to           }
697                       //this usage other than onTerminated           break;
698                       if (mQueuedBye)  
699          case On491Update:
700             transition(SentReinviteGlare);
701             start491Timer();
702             break;
703    
704          case OnUpdateRejected:
705             // !jf!
706             assert(0);
707             break;
708    
709          case OnGeneralFailure:
710             sendBye();
711             transition(Terminated);
712             handler->onTerminated(getSessionHandle(), InviteSessionHandler::GeneralFailure, &msg);
713             break;
714    
715          default:
716             dispatchOthers(msg);
717             break;
718       }
719    }
720    
721    void
722    InviteSession::dispatchSentReinvite(const SipMessage& msg)
723    {
724       InviteSessionHandler* handler = mDum.mInviteSessionHandler;
725       std::auto_ptr<SdpContents> sdp = InviteSession::getSdp(msg);
726    
727       switch (toEvent(msg, sdp.get()))
728       {
729          case OnInvite:
730          case OnInviteReliable:
731          case OnInviteOffer:
732          case OnInviteReliableOffer:
733          case OnUpdate:
734                       {                       {
735                          send(makeAck());   // ACK the 200 first then send BYE           SipMessage response;
736                          mState = Terminated;           mDialog.makeResponse(response, msg, 491);
737                          mLastRequest = *mQueuedBye;           send(response);
738                          delete mQueuedBye;           break;
                         mQueuedBye = 0;                          
                         send(mLastRequest);  
                         return;  
739                       }                       }
740    
741                       // Handle any Session Timer headers in response        case On1xx:
742          case On1xxEarly:
743             // !slg! Some UA's send a 100 response to a ReInvite - just ignore it
744             break;
745    
746          case On2xxAnswer:
747          case On2xxOffer:
748          {
749             transition(Connected);
750             handleSessionTimerResponse(msg);
751             mCurrentLocalSdp = mProposedLocalSdp;
752             mCurrentRemoteSdp = InviteSession::makeSdp(*sdp);
753             // !jf! I need to potentially include an answer in the ACK here
754             sendAck();
755             handler->onAnswer(getSessionHandle(), msg, *sdp);
756    
757    
758             // !jf! do I need to allow a reINVITE overlapping the retransmission of
759             // the ACK when a 200I is received? If yes, then I need to store all
760             // ACK messages for 64*T1
761             break;
762          }
763          case On2xx:
764             sendAck();
765             transition(Connected);
766                       handleSessionTimerResponse(msg);                       handleSessionTimerResponse(msg);
767             handler->onIllegalNegotiation(getSessionHandle(), msg);
768             break;
769    
770                       if (offans.first != None)        case On491Invite:
771             transition(SentUpdateGlare);
772             start491Timer();
773             break;
774    
775          case OnGeneralFailure:
776             sendBye();
777             transition(Terminated);
778             handler->onTerminated(getSessionHandle(), InviteSessionHandler::GeneralFailure, &msg);
779             break;
780    
781          case OnInviteFailure:
782             transition(Connected);
783             handler->onOfferRejected(getSessionHandle(), msg);
784             break;
785    
786          default:
787             dispatchOthers(msg);
788             break;
789       }
790    }
791    
792    void
793    InviteSession::dispatchGlare(const SipMessage& msg)
794                       {                       {
795                          if (offans.first == Answer)     InviteSessionHandler* handler = mDum.mInviteSessionHandler;
796       MethodTypes method = msg.header(h_CSeq).method();
797       if (method == INVITE && msg.isRequest())
798                          {                          {
799                             //no late media required, so just send the ACK        // Received inbound reinvite, when waiting to resend outbound reinvite or update
800                             send(makeAck());        transition(ReceivedReinvite);
801          handler->onOfferRejected(getSessionHandle(), msg);
802                          }                          }
803                          incomingSdp(msg, offans.second);     else if (method == UPDATE && msg.isRequest())
804       {
805          // Received inbound update, when waiting to resend outbound reinvite or update
806          transition(ReceivedUpdate);
807          handler->onOfferRejected(getSessionHandle(), msg);
808                       }                       }
809                       else                       else
810                       {                       {
811                          //no offer or answer in 200, this will eventually be        dispatchOthers(msg);
812                          //legal with PRACK/UPDATE     }
813                          send(makeAck());  }
814                          if (mOfferState != Answered)  
815    void
816    InviteSession::dispatchReceivedUpdateOrReinvite(const SipMessage& msg)
817    {
818       MethodTypes method = msg.header(h_CSeq).method();
819       if (method == INVITE || method == UPDATE)
820                          {                          {
821                             //reset the sdp state machine        // Means that the UAC has sent us a second reINVITE or UPDATE before we
822                             incomingSdp(msg, 0);        // responded to the first one. Bastard!
823                             mDum.mInviteSessionHandler->onIllegalNegotiation(getSessionHandle(), msg);        SipMessage response;
824          mDialog.makeResponse(response, msg, 500);
825          response.header(h_RetryAfter).value() = Random::getRandom() % 10;
826          mDialog.send(response);
827                          }                          }
828       else
829       {
830          dispatchOthers(msg);
831                       }                       }
832                    }                    }
833                    else //200 retransmission that overlaps with this Invite transaction  
834    
835    void
836    InviteSession::dispatchAnswered(const SipMessage& msg)
837                    {                    {
838                       CSeqToMessageMap::iterator it = mAckMap.find(msg.header(h_CSeq).sequence());     if (msg.isRequest() && msg.header(h_RequestLine).method() == ACK)
                      if (it != mAckMap.end())  
839                       {                       {
840                          mDum.send(it->second);        mCurrentRetransmit200 = 0; // stop the 200 retransmit timer
841          transition(Connected);
842                       }                       }
843       else
844       {
845          dispatchOthers(msg);
846                    }                    }
847                 }                 }
848                 else if(code == 408 || code == 481)    
849    void
850    InviteSession::dispatchWaitingToOffer(const SipMessage& msg)
851    {
852       if (msg.isRequest() && msg.header(h_RequestLine).method() == ACK)
853                 {                 {
854                     // If ReInvite response is Timeout (408) or Transaction Does not Exits (481) - end dialog        mCurrentRetransmit200 = 0; // stop the 200 retransmit timer
855                     mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), msg);        provideOffer(*mProposedLocalSdp);
                    guard.destroy();  
856                 }                             }            
857                 else                 else
858                 {                 {
859                    // !slg! handle 491 response and retry???        dispatchOthers(msg);
860       }
861    }
862    
863                    mState = Connected;  void
864                    //user has called end, so no more callbacks relating to  InviteSession::dispatchWaitingToTerminate(const SipMessage& msg)
865                    //this usage other than onTerminated  {
866                    if (mQueuedBye)     if (msg.isResponse() &&
867           msg.header(h_CSeq).method() == INVITE)
868       {
869          if(msg.header(h_StatusLine).statusCode() / 200 == 1)  // Note: stack ACK's non-2xx final responses only
870                    {                    {
871                       send(makeAck());   // ACK the 200 first then send BYE           // !jf! Need to include the answer here.
872                       mState = Terminated;           sendAck();
                      mLastRequest = *mQueuedBye;  
                      delete mQueuedBye;  
                      mQueuedBye = 0;                          
                      send(mLastRequest);  
                      return;  
873                    }                    }
874                    //reset the sdp state machine        sendBye();
875                    incomingSdp(msg, 0);        transition(Terminated);
876                    mDum.mInviteSessionHandler->onOfferRejected(getSessionHandle(), msg);        mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), InviteSessionHandler::Ended);
877                 }                 }
878              }              }
879    
880    void
881    InviteSession::dispatchTerminated(const SipMessage& msg)
882    {
883       InfoLog (<< "InviteSession::dispatchTerminated " << msg.brief());
884    
885       if (msg.isRequest())
886       {
887          SipMessage response;
888          mDialog.makeResponse(response, msg, 481);
889          mDialog.send(response);
890    
891          // !jf! means the peer sent BYE while we are waiting for response to BYE
892          //mDum.destroy(this);
893       }
894              else              else
895              {              {
896                 SipMessage failure;        mDum.destroy(this);
                mDialog.makeResponse(failure, msg, 491);  
                InfoLog (<< "Sending 491 - overlapping Invite transactions");  
                mDum.sendResponse(failure);  
                return;  
897              }              }
898           }           }
899           else if(msg.header(h_CSeq).method() == BYE && msg.isRequest())  
900    void
901    InviteSession::dispatchOthers(const SipMessage& msg)
902           {           {
903                  // Inbound BYE crosses with outbound REINVITE     // handle OnGeneralFailure
904       // handle OnRedirect
905    
906                  mState = Terminated;     switch (msg.header(h_CSeq).method())
907       {
908          case PRACK:
909             dispatchPrack(msg);
910             break;
911          case CANCEL:
912             dispatchCancel(msg);
913             break;
914          case BYE:
915             dispatchBye(msg);
916             break;
917          case INFO:
918             dispatchInfo(msg);
919             break;
920              case ACK:
921                      // Ignore duplicate ACKs from 2xx reTransmissions
922                      break;
923          default:
924             // handled in Dialog
925             WarningLog (<< "DUM delivered a "
926                         << msg.header(h_CSeq).unknownMethodName()
927                         << " to the InviteSession "
928                         << endl
929                         << msg);
930             assert(0);
931             break;
932       }
933    }
934    
935    void
936    InviteSession::dispatchUnhandledInvite(const SipMessage& msg)
937    {
938       assert(msg.isRequest());
939       assert(msg.header(h_CSeq).method() == INVITE);
940                    
941       // If we get an INVITE request from the wire and we are not in
942       // Connected state, reject the request and send a BYE
943       SipMessage response;
944       mDialog.makeResponse(response, msg, 400); // !jf! what code to use?
945       InfoLog (<< "Sending " << response.brief());
946       mDialog.send(response);
947    
948       sendBye();
949       transition(Terminated);
950       mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), InviteSessionHandler::GeneralFailure, &msg);
951    }
952    
953              mDum.mInviteSessionHandler->onTerminated(getSessionHandle(),msg);  void
954    InviteSession::dispatchPrack(const SipMessage& msg)
955    {
956       assert(msg.header(h_CSeq).method() == PRACK);
957       if(msg.isRequest())
958       {
959          SipMessage rsp;
960          mDialog.makeResponse(rsp, msg, 481);
961          mDialog.send(rsp);
962    
963                  mDialog.makeResponse(mLastResponse, msg, 200);        sendBye();
964          // !jf! should we make some other callback here
965          transition(Terminated);
966          mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), InviteSessionHandler::GeneralFailure, &msg);
967       }
968       else
969       {
970          // ignore. could be PRACK/200
971       }
972    }
973    
974              send(mLastResponse);  void
975    InviteSession::dispatchCancel(const SipMessage& msg)
976    {
977       InviteSessionHandler* handler = mDum.mInviteSessionHandler;
978       assert(msg.header(h_CSeq).method() == CANCEL);
979       if(msg.isRequest())
980       {
981          SipMessage rsp;
982          mDialog.makeResponse(rsp, msg, 200);
983          mDialog.send(rsp);
984    
985          sendBye();
986          // !jf! should we make some other callback here
987          transition(Terminated);
988          handler->onTerminated(getSessionHandle(), InviteSessionHandler::PeerEnded, &msg);
989           }           }
990           else           else
991           {           {
992              ErrLog ( << "Spurious message sent to UAS " << msg );                    WarningLog (<< "DUM let me send a CANCEL at an incorrect state " << endl << msg);
993              return;                    assert(0);
994           }           }
995           break;  }
996        default:  
997           DebugLog ( << "Throwing away strange message: " << msg );  void
998           //throw message away  InviteSession::dispatchBye(const SipMessage& msg)
999  //         assert(0);  //all other cases should be handled in base classes  {
1000       InviteSessionHandler* handler = mDum.mInviteSessionHandler;
1001    
1002       if (msg.isRequest())
1003       {
1004    
1005          SipMessage rsp;
1006          InfoLog (<< "Received " << msg.brief());
1007          mDialog.makeResponse(rsp, msg, 200);
1008          mDialog.send(rsp);
1009    
1010          // !jf! should we make some other callback here
1011          transition(Terminated);
1012          handler->onTerminated(getSessionHandle(), InviteSessionHandler::PeerEnded, &msg);
1013          mDum.destroy(this);
1014       }
1015       else
1016       {
1017          WarningLog (<< "DUM let me send a BYE at an incorrect state " << endl << msg);
1018          assert(0);
1019     }     }
1020  }  }
1021    
1022  SipMessage&  void
1023  InviteSession::makeInfo(auto_ptr<Contents> contents)  InviteSession::dispatchInfo(const SipMessage& msg)
1024  {  {
1025     if (mNitState == NitProceeding)     InviteSessionHandler* handler = mDum.mInviteSessionHandler;
1026       if (msg.isRequest())
1027     {     {
1028        throw UsageUseException("Cannot start a non-invite transaction until the previous one has completed",        InfoLog (<< "Received " << msg.brief());
1029                                    __FILE__, __LINE__);        mDialog.makeResponse(mLastNitResponse, msg, 200);
1030          handler->onInfo(getSessionHandle(), msg);
1031       }
1032       else
1033       {
1034          assert(mNitState == NitProceeding);
1035          mNitState = NitComplete;
1036          //!dcm! -- toss away 1xx to an info?
1037          if (msg.header(h_StatusLine).statusCode() >= 300)
1038          {
1039             handler->onInfoFailure(getSessionHandle(), msg);
1040          }
1041          else if (msg.header(h_StatusLine).statusCode() >= 200)
1042          {
1043             handler->onInfoSuccess(getSessionHandle(), msg);
1044          }
1045     }     }
    mNitState = NitProceeding;  
    mDialog.makeRequest(mLastNit, INFO);  
    mLastNit.releaseContents();    
    mLastNit.setContents(contents);  
    return mLastNit;    
1046  }  }
1047    
1048  SipMessage&  void
1049  InviteSession::makeRefer(const NameAddr& referTo)  InviteSession::acceptInfo(int statusCode)
1050  {  {
1051     mDialog.makeRequest(mLastRequest, REFER);     if (statusCode / 100  != 2)
1052     mLastRequest.header(h_ReferTo) = referTo;     {
1053  //   mLastRequest.header(h_ReferTo).param(p_method) = getMethodName(INVITE);          throw UsageUseException("Must accept with a 2xx", __FILE__, __LINE__);
1054     return mLastRequest;       }
1055    
1056       mLastNitResponse.header(h_StatusLine).statusCode() = statusCode;  
1057       send(mLastNitResponse);  
1058  }  }
1059    
1060  SipMessage&  void
1061  InviteSession::makeRefer(const NameAddr& referTo, InviteSessionHandle sessionToReplace)  InviteSession::rejectInfo(int statusCode)
1062  {  {
1063     if (!sessionToReplace.isValid())     if (statusCode < 400)
1064     {     {
1065        throw UsageUseException("Attempted to make a refer w/ and invalid replacement target", __FILE__, __LINE__);        throw UsageUseException("Must reject with a >= 4xx", __FILE__, __LINE__);
1066       }
1067       mLastNitResponse.header(h_StatusLine).statusCode() = statusCode;  
1068       send(mLastNitResponse);
1069     }     }
1070        
1071     mDialog.makeRequest(mLastRequest, REFER);  void
1072     mLastRequest.header(h_ReferTo) = referTo;  InviteSession::startRetransmit200Timer()
1073     CallId replaces;  {
1074     DialogId id = sessionToReplace->mDialog.getId();       mCurrentRetransmit200 = Timer::T1;
1075     replaces.value() = id.getCallId();     int seq = mLastSessionModification.header(h_CSeq).sequence();
1076     replaces.param(p_toTag) = id.getRemoteTag();     mDum.addTimerMs(DumTimeout::Retransmit200, mCurrentRetransmit200, getBaseHandle(), seq);
1077     replaces.param(p_fromTag) = id.getLocalTag();     mDum.addTimerMs(DumTimeout::WaitForAck, Timer::TH, getBaseHandle(), seq);
1078    }
1079    
1080     mLastRequest.header(h_ReferTo).uri().embedded().header(h_Replaces) = replaces;  void
1081     return mLastRequest;  InviteSession::start491Timer()
1082    {
1083       int seq = mLastSessionModification.header(h_CSeq).sequence();
1084       int timer = Random::getRandom() % 4000;
1085       mDum.addTimerMs(DumTimeout::Glare, timer, getBaseHandle(), seq);
1086  }  }
1087    
1088  void  void
1089  InviteSession::end()  InviteSession::handleSessionTimerResponse(const SipMessage& msg)
1090  {  {
1091     InfoLog ( << "InviteSession::end, state: " << mState);       assert(msg.header(h_CSeq).method() == INVITE || msg.header(h_CSeq).method() == UPDATE);
1092     switch (mState)  
1093       // If session timers are locally supported then handle response
1094       if(mDum.getMasterProfile()->getSupportedOptionTags().find(Token(Symbols::Timer)))
1095     {     {
1096        case Terminated:        //Profile::SessionTimerMode mode = mDialog.mDialogSet.getUserProfile()->getDefaultSessionTimerMode();
1097           throw UsageUseException("Cannot end a session that has already been cancelled.", __FILE__, __LINE__);        bool fUAS = dynamic_cast<ServerInviteSession*>(this) != NULL;
1098    
1099          // Set Defaults
1100          mSessionInterval = mDialog.mDialogSet.getUserProfile()->getDefaultSessionTime();  // Used only if response didn't request a time
1101          switch(mDialog.mDialogSet.getUserProfile()->getDefaultSessionTimerMode())
1102          {
1103          case Profile::PreferLocalRefreshes:
1104             mSessionRefresherUAS = fUAS;   // Default refresher is Local
1105           break;           break;
1106        case Connected:        case Profile::PreferRemoteRefreshes:
1107           // Check state of 200 retrans map to see if we have recieved an ACK or not yet           mSessionRefresherUAS = !fUAS;  // Default refresher is Remote
1108           if (mFinalResponseMap.find(mLastIncomingRequest.header(h_CSeq).sequence()) != mFinalResponseMap.end())           break;
1109          case Profile::PreferUACRefreshes:
1110             mSessionRefresherUAS = false;  // Default refresher is UAC
1111             break;
1112          case Profile::PreferUASRefreshes:
1113             mSessionRefresherUAS = true;   // Default refresher is UAS
1114             break;
1115          }
1116    
1117          if(msg.exists(h_Requires) && msg.header(h_Requires).find(Token(Symbols::Timer))
1118             && !msg.exists(h_SessionExpires))
1119           {           {
1120              if(!mQueuedBye)           // If no Session Expires in response and Requires header is present then session timer is to be 'turned off'
1121             mSessionInterval = 0;
1122          }
1123          // Process Session Timer headers
1124          else if(msg.exists(h_SessionExpires))
1125              {              {
1126                 // No ACK yet - send BYE after ACK is received           mSessionInterval = msg.header(h_SessionExpires).value();
1127                 mQueuedBye = new SipMessage(mLastRequest);           if(msg.header(h_SessionExpires).exists(p_refresher))
1128                 mDialog.makeRequest(*mQueuedBye, BYE);           {
1129                 // Remote end specified refresher preference
1130                 mSessionRefresherUAS = (msg.header(h_SessionExpires).param(p_refresher) == Data("uas"));
1131             }
1132              }              }
1133              else              else
1134              {              {
1135                 throw UsageUseException("Cannot end a session that has already been cancelled.", __FILE__, __LINE__);           // Note:  If no Requires or Session-Expires, then UAS does not support Session Timers
1136             // - we are free to use our SessionInterval settings (set above as a default)
1137             // If far end doesn't support then refresher must be local
1138             mSessionRefresherUAS = fUAS;
1139              }              }
1140    
1141          if(mSessionInterval >= 90)  // 90 is the absolute minimum
1142          {
1143             // Check if we are the refresher
1144             if((fUAS && mSessionRefresherUAS) || (!fUAS && !mSessionRefresherUAS))
1145             {
1146                // Start Session-Refresh Timer to mSessionInterval / 2 (recommended by draft-ietf-sip-session-timer-15)
1147                mDum.addTimer(DumTimeout::SessionRefresh, mSessionInterval / 2, getBaseHandle(), ++mSessionTimerSeq);
1148           }           }
1149           else           else
1150           {           {
1151              InfoLog ( << "InviteSession::end, Connected" );                // Start Session-Expiration Timer to mSessionInterval - BYE should be sent a minimum of 32 or SessionInterval/3 seconds before the session expires (recommended by draft-ietf-sip-session-timer-15)
1152              mDialog.makeRequest(mLastRequest, BYE);              mDum.addTimer(DumTimeout::SessionExpiration, mSessionInterval - resipMin(32,mSessionInterval/3), getBaseHandle(), ++mSessionTimerSeq);
             //new transaction  
             assert(mLastRequest.header(h_Vias).size() == 1);  
             mState = Terminated;  
             send(mLastRequest);  
1153           }           }
          break;  
       case ReInviting:  
          if(!mQueuedBye)  
          {  
             mQueuedBye = new SipMessage(mLastRequest);  
             mDialog.makeRequest(*mQueuedBye, BYE);  
1154           }           }
1155           else           else
1156           {           {
1157              throw UsageUseException("Cannot end a session that has already been cancelled.", __FILE__, __LINE__);            ++mSessionTimerSeq;  // increment seq, incase old timers are running and now session timers are disabled
1158           }           }
          break;  
       default:  
          assert(0); // out of states  
1159     }     }
1160  }  }
1161    
1162  void InviteSession::dialogDestroyed(const SipMessage& msg)  void
1163    InviteSession::handleSessionTimerRequest(SipMessage &response, const SipMessage& request)
1164  {  {
1165     mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), msg);       assert(request.header(h_CSeq).method() == INVITE || request.header(h_CSeq).method() == UPDATE);
    delete this;    
 }  
1166    
1167  // If sdp==0, it means the last offer failed     // If session timers are locally supported then add necessary headers to response
1168  // !dcm! -- eventually handle confused UA's that send offers/answers at     if(mDum.getMasterProfile()->getSupportedOptionTags().find(Token(Symbols::Timer)))
 // inappropriate times, probably with a different callback  
 void  
 InviteSession::incomingSdp(const SipMessage& msg, const SdpContents* sdp)  
1169  {  {
1170     switch (mOfferState)        bool fUAS = dynamic_cast<ServerInviteSession*>(this) != NULL;
1171    
1172          mSessionInterval = mDialog.mDialogSet.getUserProfile()->getDefaultSessionTime();  // Used only if remote doesn't request a time
1173          switch(mDialog.mDialogSet.getUserProfile()->getDefaultSessionTimerMode())
1174     {     {
1175        case Nothing:        case Profile::PreferLocalRefreshes:
1176           assert(mCurrentLocalSdp == 0);           mSessionRefresherUAS = fUAS;   // Default refresher is Local
          assert(mCurrentRemoteSdp == 0);  
          assert(mProposedLocalSdp == 0);  
          assert(mProposedRemoteSdp == 0);  
          mProposedRemoteSdp = static_cast<SdpContents*>(sdp->clone());  
          mOfferState = Offerred;  
          mDum.mInviteSessionHandler->onOffer(getSessionHandle(), msg, sdp);  
1177           break;           break;
1178                  case Profile::PreferRemoteRefreshes:
1179        case Offerred:           mSessionRefresherUAS = !fUAS;  // Default refresher is Remote
          assert(mCurrentLocalSdp == 0);  
          assert(mCurrentRemoteSdp == 0);  
          mCurrentLocalSdp = mProposedLocalSdp;  
          mCurrentRemoteSdp = static_cast<SdpContents*>(sdp->clone());  
                  delete mProposedRemoteSdp;  
          mProposedLocalSdp = 0;  
          mProposedRemoteSdp = 0;  
          mOfferState = Answered;  
          mDum.mInviteSessionHandler->onAnswer(getSessionHandle(), msg, sdp);  
1180           break;           break;
1181          case Profile::PreferUACRefreshes:
1182        case Answered:           mSessionRefresherUAS = false;  // Default refresher is UAC
          assert(mProposedLocalSdp == 0);  
          assert(mProposedRemoteSdp == 0);  
          mProposedRemoteSdp = static_cast<SdpContents*>(sdp->clone());  
          mOfferState = CounterOfferred;  
          mDum.mInviteSessionHandler->onOffer(getSessionHandle(), msg, sdp);  
1183           break;           break;
1184          case Profile::PreferUASRefreshes:
1185             mSessionRefresherUAS = true;   // Default refresher is UAS
1186             break;
1187          }
1188                                        
1189        case CounterOfferred:        // Check if far-end supports
1190           assert(mCurrentLocalSdp);        bool farEndSupportsTimer = false;
1191           assert(mCurrentRemoteSdp);        if(request.exists(h_Supporteds) && request.header(h_Supporteds).find(Token(Symbols::Timer)))
          mOfferState = Answered;  
          if (sdp)  // !slg! There currenlty doesn't seem to be anyone calling this with sdp == 0  
1192           {           {
1193              delete mCurrentLocalSdp;           farEndSupportsTimer = true;
1194              delete mCurrentRemoteSdp;           if(request.exists(h_SessionExpires))
1195              mCurrentLocalSdp = mProposedLocalSdp;           {
1196              mCurrentRemoteSdp = static_cast<SdpContents*>(sdp->clone());              // Use Session Interval requested by remote - if none then use local settings
1197                          delete mProposedRemoteSdp;              mSessionInterval = request.header(h_SessionExpires).value();
1198              mProposedLocalSdp = 0;              if(request.header(h_SessionExpires).exists(p_refresher))
1199              mProposedRemoteSdp = 0;              {
1200              mOfferState = Answered;                  mSessionRefresherUAS = (request.header(h_SessionExpires).param(p_refresher) == Data("uas"));
1201              mDum.mInviteSessionHandler->onAnswer(getSessionHandle(), msg, sdp);              }
1202             }
1203           }           }
1204           else           else
1205           {           {
1206                          delete mProposedLocalSdp;           // If far end doesn't support then refresher must be local
1207                          delete mProposedRemoteSdp;           mSessionRefresherUAS = fUAS;
             mProposedLocalSdp = 0;  
             mProposedRemoteSdp = 0;  
             // !jf! is this right?  
 //            mDum.mInviteSessionHandler->onOfferRejected(getSessionHandle(), msg);  
1208           }           }
1209           break;  
1210          // Add Session-Expires to response if required
1211          if(mSessionInterval >= 90)
1212          {
1213             if(farEndSupportsTimer)
1214             {
1215                // If far end supports session-timer then require it, if not already present
1216                if(!response.header(h_Requires).find(Token(Symbols::Timer)))
1217                {
1218                    response.header(h_Requires).push_back(Token(Symbols::Timer));
1219     }     }
1220  }  }
1221             response.header(h_SessionExpires).value() = mSessionInterval;
1222             response.header(h_SessionExpires).param(p_refresher) = Data(mSessionRefresherUAS ? "uas" : "uac");
1223    
1224  void           // Check if we are the refresher
1225  InviteSession::send(SipMessage& msg)           if((fUAS && mSessionRefresherUAS) || (!fUAS && !mSessionRefresherUAS))
1226  {  {
1227     Destroyer::Guard guard(mDestroyer);              // Start Session-Refresh Timer to mSessionInterval / 2 (recommended by draft-ietf-sip-session-timer-15)
1228     //handle NITs separately              mDum.addTimer(DumTimeout::SessionRefresh, mSessionInterval / 2, getBaseHandle(), ++mSessionTimerSeq);
1229     if (msg.header(h_CSeq).method() == INFO)           }
1230             else
1231     {     {
1232        mDum.send(msg);              // Start Session-Expiration Timer to mSessionInterval - BYE should be sent a minimum of 32 or SessionInterval/3 seconds before the session expires (recommended by draft-ietf-sip-session-timer-15)
1233        return;                    mDum.addTimer(DumTimeout::SessionExpiration, mSessionInterval - resipMin(32,mSessionInterval/3), getBaseHandle(), ++mSessionTimerSeq);
1234     }     }
1235          }
1236     msg.releaseContents();        else
    if (mQueuedBye && (mQueuedBye == &msg))  
1237     {     {
1238        //queued            ++mSessionTimerSeq;  // increment seq, incase old timers are running and now session timers are disabled
1239        return;        }
1240       }
1241     }     }
1242                
1243     if (msg.isRequest())  Data
1244    InviteSession::toData(State state)
1245     {     {
1246        switch(msg.header(h_RequestLine).getMethod())     switch (state)
1247        {        {
1248           case INVITE:        case Undefined:
1249           case UPDATE:           return "InviteSession::Undefined";
1250           case ACK:        case Connected:
1251              if (mNextOfferOrAnswerSdp)           return "InviteSession::Connected";
1252          case SentUpdate:
1253             return "InviteSession::SentUpdate";
1254          case SentUpdateGlare:
1255             return "InviteSession::SentUpdateGlare";
1256          case SentReinvite:
1257             return "InviteSession::SentReinvite";
1258          case SentReinviteGlare:
1259             return "InviteSession::SentReinviteGlare";
1260          case ReceivedUpdate:
1261             return "InviteSession::ReceivedUpdate";
1262          case ReceivedReinvite:
1263             return "InviteSession::ReceivedReinvite";
1264          case ReceivedReinviteNoOffer:
1265             return "InviteSession::ReceivedReinviteNoOffer";
1266          case Answered:
1267             return "InviteSession::Answered";
1268          case WaitingToOffer:
1269             return "InviteSession::WaitingToOffer";
1270          case WaitingToTerminate:
1271             return "InviteSession::WaitingToTerminate";
1272          case Terminated:
1273             return "InviteSession::Terminated";
1274    
1275          case UAC_Start:
1276             return "UAC_Start";
1277          case UAS_Offer:
1278             return "UAS_Offer";
1279          case UAS_OfferProvidedAnswer:
1280             return "UAS_OfferProvidedAnswer";
1281          case UAS_EarlyOffer:
1282             return "UAS_EarlyOffer";
1283          case UAS_EarlyProvidedAnswer:
1284             return "UAS_EarlyProvidedAnswer";
1285          case UAS_NoOffer:
1286             return "UAS_NoOffer";
1287          case UAS_ProvidedOffer:
1288             return "UAS_ProvidedOffer";
1289          case UAS_EarlyNoOffer:
1290             return "UAS_EarlyNoOffer";
1291          case UAS_EarlyProvidedOffer:
1292             return "UAS_EarlyProvidedOffer";
1293          case UAS_Accepted:
1294             return "UAS_Accepted";
1295          case UAS_WaitingToOffer:
1296             return "UAS_WaitingToOffer";
1297          case UAS_AcceptedWaitingAnswer:
1298             return "UAS_AcceptedWaitingAnswer";
1299          case UAC_Early:
1300             return "UAC_Early";
1301          case UAC_EarlyWithOffer:
1302             return "UAC_EarlyWithOffer";
1303          case UAC_EarlyWithAnswer:
1304             return "UAC_EarlyWithAnswer";
1305          case UAC_Answered:
1306             return "UAC_Answered";
1307          case UAC_SentUpdateEarly:
1308             return "UAC_SentUpdateEarly";
1309          case UAC_SentUpdateConnected:
1310             return "UAC_SentUpdateConnected";
1311          case UAC_ReceivedUpdateEarly:
1312             return "UAC_ReceivedUpdateEarly";
1313          case UAC_SentAnswer:
1314             return "UAC_SentAnswer";
1315          case UAC_QueuedUpdate:
1316             return "UAC_QueuedUpdate";
1317          case UAC_Cancelled:
1318             return "UAC_Cancelled";
1319    
1320          case UAS_Start:
1321             return "UAS_Start";
1322          case UAS_OfferReliable:
1323             return "UAS_OfferReliable";
1324          case UAS_NoOfferReliable:
1325             return "UAS_NoOfferReliable";
1326          case UAS_FirstSentOfferReliable:
1327             return "UAS_FirstSentOfferReliable";
1328          case UAS_FirstEarlyReliable:
1329             return "UAS_FirstEarlyReliable";
1330          case UAS_EarlyReliable:
1331             return "UAS_EarlyReliable";
1332          case UAS_SentUpdate:
1333             return "UAS_SentUpdate";
1334          case UAS_SentUpdateAccepted:
1335             return "UAS_SentUpdateAccepted";
1336          case UAS_ReceivedUpdate:
1337             return "UAS_ReceivedUpdate";
1338          case UAS_ReceivedUpdateWaitingAnswer:
1339             return "UAS_ReceivedUpdateWaitingAnswer";
1340          case UAS_WaitingToTerminate:
1341             return "UAS_WaitingToTerminate";
1342          case UAS_WaitingToHangup:
1343             return "UAS_WaitingToHangup";
1344       }
1345       assert(0);
1346       return "Undefined";
1347    }
1348    
1349    
1350    void
1351    InviteSession::transition(State target)
1352              {              {
1353                 msg.setContents(mNextOfferOrAnswerSdp);     InfoLog (<< "Transition " << toData(mState) << " -> " << toData(target));
1354                 sendSdp(mNextOfferOrAnswerSdp);     mState = target;
                mNextOfferOrAnswerSdp = 0;              
1355              }              }
1356              break;              
1357           default:  bool
1358              break;              InviteSession::isReliable(const SipMessage& msg)
1359    {
1360       // Ensure supported both locally and remotely
1361       return msg.exists(h_Supporteds) && msg.header(h_Supporteds).find(Token(Symbols::C100rel)) &&
1362              mDum.getMasterProfile()->getSupportedOptionTags().find(Token(Symbols::C100rel));
1363        }        }
1364        mDum.send(msg);  
1365    std::auto_ptr<SdpContents>
1366    InviteSession::getSdp(const SipMessage& msg)
1367    {
1368       // !jf! this code doesn't yet work - definitely if USE_SSL=false
1369       //Helper::ContentsSecAttrs attrs = Helper::extractFromPkcs7(msg, mDum.getSecurity());
1370       //return std::auto_ptr<SdpContents>(dynamic_cast<SdpContents*>(attrs.mContents.get()));
1371       SdpContents* sdp = dynamic_cast<SdpContents*>(msg.getContents());
1372       if (sdp)
1373       {
1374          SdpContents* cloned = static_cast<SdpContents*>(sdp->clone());
1375          return std::auto_ptr<SdpContents>(cloned);
1376     }     }
1377     else     else
1378     {     {
1379        int code = msg.header(h_StatusLine).statusCode();        static std::auto_ptr<SdpContents> empty;
1380        //!dcm! -- probably kill this object earlier, handle 200 to bye in        return empty;
1381        //DialogUsageManager...very soon     }
1382        if (msg.header(h_CSeq).method() == BYE && code == 200) //!dcm! -- not 2xx?  }
1383    
1384    std::auto_ptr<SdpContents>
1385    InviteSession::makeSdp(const SdpContents& sdp)
1386        {        {
1387           mState = Terminated;     return std::auto_ptr<SdpContents>(static_cast<SdpContents*>(sdp.clone()));
          mDum.send(msg);  
              //mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), msg);      // This is actually called when recieving the BYE message so that the BYE message can be passed to onTerminated  
          guard.destroy();  
1388        }        }
1389        else if (code >= 200 && code < 300 && msg.header(h_CSeq).method() == INVITE)  
1390    void
1391    InviteSession::setSdp(SipMessage& msg, const SdpContents& sdp)
1392        {        {
1393           int seq = msg.header(h_CSeq).sequence();     // !jf! should deal with multipart here
          mCurrentRetransmit200 = Timer::T1;          
          mDum.addTimerMs(DumTimeout::Retransmit200, mCurrentRetransmit200, getBaseHandle(), seq);  
          mDum.addTimerMs(DumTimeout::WaitForAck, Timer::TH, getBaseHandle(), seq);  
1394                            
1395           //!dcm! -- this should be mFinalResponse...maybe assign here in     // This will clone the sdp since the InviteSession also wants to keep its own
1396           //case the user wants to be very strange     // copy of the sdp around for the application to access
1397           if (mNextOfferOrAnswerSdp)     msg.setContents(&sdp);
1398    }
1399    
1400    InviteSession::Event
1401    InviteSession::toEvent(const SipMessage& msg, const SdpContents* sdp)
1402    {
1403       MethodTypes method = msg.header(h_CSeq).method();
1404       int code = msg.isResponse() ? msg.header(h_StatusLine).statusCode() : 0;
1405       bool reliable = isReliable(msg);
1406       bool sentOffer = mProposedLocalSdp.get();
1407    
1408       if (code == 481 || code == 408)
1409           {           {
1410              msg.setContents(mNextOfferOrAnswerSdp);        return OnGeneralFailure;
             sendSdp(mNextOfferOrAnswerSdp);  
             mNextOfferOrAnswerSdp = 0;              
1411           }           }
1412           mDum.send(msg);     else if (code >= 300 && code <= 399)
1413       {
1414          return OnRedirect;
1415       }
1416       else if (method == INVITE && code == 0)
1417       {
1418          if (sdp)
1419          {
1420             if (reliable)
1421             {
1422                return OnInviteReliableOffer;
1423        }        }
1424        else        else
1425        {        {
1426           mDum.send(msg);              return OnInviteOffer;
1427        }        }
1428     }           }      
1429          else
1430          {
1431             if (reliable)
1432             {
1433                return OnInviteReliable;
1434  }  }
1435             else
 void  
 InviteSession::sendSdp(SdpContents* sdp)  
1436  {  {
1437     switch (mOfferState)              return OnInvite;
1438             }
1439          }
1440       }
1441       else if (method == INVITE && code > 100 && code < 200)   // !kh! 100 is handled by transaction layer.
1442       {
1443          if (reliable)
1444     {     {
       case Nothing:  
          assert(mCurrentLocalSdp == 0);  
          assert(mCurrentRemoteSdp == 0);  
          mProposedLocalSdp = sdp;  
          mOfferState = Offerred;  
          break;  
           
       case Offerred:  
          assert(mCurrentLocalSdp == 0);  
          assert(mCurrentRemoteSdp == 0);  
          mCurrentLocalSdp = sdp;  
          mCurrentRemoteSdp = mProposedRemoteSdp;  
                  delete mProposedLocalSdp;  
          mProposedLocalSdp = 0;  
          mProposedRemoteSdp = 0;  
          mOfferState = Answered;  
          break;  
   
       case Answered:  
          assert(mProposedLocalSdp == 0);  
          assert(mProposedRemoteSdp == 0);  
          mProposedLocalSdp = sdp;  
          mOfferState = CounterOfferred;  
          break;  
           
       case CounterOfferred:  
          assert(mCurrentLocalSdp);  
          assert(mCurrentRemoteSdp);  
1445           if (sdp)           if (sdp)
1446           {           {
1447              delete mCurrentLocalSdp;              if (sentOffer)
1448              delete mCurrentRemoteSdp;              {
1449              mCurrentLocalSdp = sdp;                 return On1xxAnswer;
             mCurrentRemoteSdp = mProposedRemoteSdp;  
                         delete mProposedLocalSdp;  
                         mProposedLocalSdp = 0;  
                         mProposedRemoteSdp = 0;                    
1450           }           }
1451                   else                   else
1452                   {                   {
1453                          delete mProposedLocalSdp;                 return On1xxOffer;
                         delete mProposedRemoteSdp;  
                         mProposedLocalSdp = 0;  
                         mProposedRemoteSdp = 0;                    
1454                   }                   }
          mOfferState = Answered;  
          break;  
1455     }     }
1456             else
1457             {
1458                return On1xx;
1459  }  }
1460          }
1461  std::pair<InviteSession::OfferAnswerType, const SdpContents*>        else
 InviteSession::getOfferOrAnswer(const SipMessage& msg) const  
1462  {  {
1463     std::pair<InviteSession::OfferAnswerType, const SdpContents*> ret;           if (sdp)
    ret.first = None;  
    const SdpContents* contents = NULL;  
   
    MultipartMixedContents* mixed = dynamic_cast<MultipartMixedContents*>(msg.getContents());  
    if ( mixed )  
1464     {     {
1465        // Look for first SDP Contents in a multipart contents                  return On1xxEarly;
1466        MultipartMixedContents::Parts& parts = mixed->parts();           }
1467        for( MultipartMixedContents::Parts::const_iterator i = parts.begin();           else
            i != parts.end();    
            ++i)  
1468        {        {
1469               contents = dynamic_cast<const SdpContents*>(*i);              return On1xx;
                  if(contents != NULL) break;  // Found SDP contents  
1470        }        }
1471     }     }
1472       }
1473       else if (method == INVITE && code >= 200 && code < 300)
1474       {
1475          if (sdp)
1476          {
1477             if (sentOffer)
1478             {
1479                return On2xxAnswer;
1480             }
1481     else     else
1482     {     {
1483        contents = dynamic_cast<const SdpContents*>(msg.getContents());              return On2xxOffer;
1484     }     }
1485          }
1486     if (contents)        else
1487     {     {
1488        static Token c100rel(Symbols::C100rel);           return On2xx;
1489        if (msg.isRequest() || msg.header(h_StatusLine).responseCode() == 200 ||        }
1490            (msg.exists(h_Requires) && msg.header(h_Requires).find(c100rel)))     }
1491       else if (method == INVITE && code == 487)
1492        {        {
1493           switch (mOfferState)        return On487Invite;
1494       }
1495       else if (method == INVITE && code == 489)
1496           {           {
1497              case Nothing:        return On489Invite;
1498                 ret.first = Offer;     }
1499                 ret.second = contents;     else if (method == INVITE && code == 491)
1500                 break;     {
1501                        return On491Invite;
1502              case Offerred:     }
1503                 ret.first = Answer;     else if (method == INVITE && code >= 400)
1504                 ret.second = contents;     {
1505                 break;        return OnInviteFailure;
   
             case Answered:  
                ret.first = Offer;  
                ret.second = contents;  
                break;  
                 
             case CounterOfferred:  
                ret.first = Answer;  
                ret.second = contents;  
                break;  
1506           }           }
1507       else if (method == ACK)
1508       {
1509          if (sdp)
1510          {
1511             return OnAckAnswer;
1512        }        }
1513        else if (msg.isResponse() &&        else
                msg.header(h_StatusLine).responseCode() < 200 &&  
                msg.header(h_StatusLine).responseCode() >= 180)  
1514        {        {
1515           ret.second = contents;           return OnAck;
1516        }        }
1517     }     }
1518     return ret;     else if (method == CANCEL && code == 0)
1519       {
1520          return OnCancel;
1521  }  }
1522       else if (method == CANCEL && code / 200 == 1)
 SipMessage&  
 InviteSession::rejectDialogModification(int statusCode)  
1523  {  {
1524     if (statusCode < 400)        return On200Cancel;
1525       }
1526       else if (method == CANCEL && code >= 400)
1527     {     {
1528        throw UsageUseException("Must reject with a 4xx", __FILE__, __LINE__);        return OnCancelFailure;
1529     }     }
1530     mDialog.makeResponse(mLastResponse, mLastIncomingRequest, statusCode);     else if (method == BYE && code == 0)
1531     mState = Connected;     {
1532     sendSdp(0);          return OnBye;
    return mLastResponse;  
1533  }  }
1534       else if (method == BYE && code / 200 == 1)
 SipMessage&  
 InviteSession::targetRefresh(const NameAddr& localUri)  
1535  {  {
1536     assert(0);        return On200Bye;
    return mLastRequest;  
1537  }  }
1538       else if (method == PRACK && code == 0)
 void  
 InviteSession::send()  
1539  {  {
1540     InfoLog ( << "InviteSession::send(void)");          return OnPrack;
1541     if (mOfferState == Answered || mState != Connected)     }
1542       else if (method == PRACK && code / 200 == 1)
1543     {     {
1544        throw UsageUseException("Cannot call send when there it no Offer/Answer negotiation to do", __FILE__, __LINE__);        return On200Prack;
1545     }     }
1546     send(makeAck());     else if (method == UPDATE && code == 0)
1547       {
1548          return OnUpdate;
1549  }  }
1550       else if (method == UPDATE && code / 200 == 1)
 SipMessage&  
 InviteSession::makeAck()  
1551  {  {
1552     InfoLog ( << "InviteSession::makeAck" );        return On200Update;
1553       }
1554     int cseq = mLastRequest.header(h_CSeq).sequence();     else if (method == UPDATE && code == 489)
1555     if (mAckMap.find(cseq) != mAckMap.end())     {
1556          return On489Update;
1557       }
1558       else if (method == UPDATE && code == 491)
1559     {     {
1560        InfoLog ( << "CSeq collision in ack map: " << Inserter(mAckMap) );        return On491Update;
1561       }
1562       else if (method == UPDATE && code >= 400)
1563       {
1564          return OnUpdateRejected;
1565       }
1566       else
1567       {
1568          assert(0);
1569          return Unknown;
1570       }
1571     }     }
1572                
1573     assert(mAckMap.find(cseq) == mAckMap.end());  void InviteSession::sendAck(const SdpContents *sdp)
1574     SipMessage& ack = mAckMap[cseq];  {
1575     ack = mLastRequest;     SipMessage ack;
1576     mDialog.makeRequest(ack, ACK);     mDialog.makeRequest(ack, ACK);
1577     mDum.addTimerMs(DumTimeout::CanDiscardAck, Timer::TH, getBaseHandle(), cseq);     if(sdp != 0)
1578       {
1579     assert(ack.header(h_Vias).size() == 1);        setSdp(ack, *sdp);
1580       }
1581       InfoLog (<< "Sending " << ack.brief());
1582       mDialog.send(ack);
1583    }
1584    
1585  //    if (mNextOfferOrAnswerSdp)  void InviteSession::sendBye()
1586  //    {  {
1587  //       ack.setContents(mNextOfferOrAnswerSdp);     SipMessage bye;
1588  //       sendSdp(mNextOfferOrAnswerSdp);     mDialog.makeRequest(bye, BYE);
1589  //       mNextOfferOrAnswerSdp = 0;     InfoLog (<< "Sending " << bye.brief());
1590  //    }     mDialog.send(bye);
    return ack;  
1591  }  }
1592    
1593    
1594  /* ====================================================================  /* ====================================================================
1595   * The Vovida Software License, Version 1.0   * The Vovida Software License, Version 1.0
1596   *   *

Legend:
Removed from v.3676  
changed lines
  Added in v.4010

webmaster AT resiprocate DOT org
ViewVC Help
Powered by ViewVC 1.1.27