Browse code

reworked SST + B2BUA implementation

- B2BUA app with SIP Session Timer (SST) now can also use UPDATE
(see session_refresh_method in sst_b2b.conf)

- if re-INVITE is used, normal SDP OA (INVITE+SDP/200+SDP) is done
using last established SDP (instead of delayed SDP negotiation
and SDP ping-pong)

- SDP is compared only after o= line (no SDP ping-pong with UAs which
always increase SDP version)

Stefan Sayer authored on 21/09/2010 21:49:29
Showing 35 changed files
... ...
@@ -181,7 +181,7 @@ void AnnounceTransferDialog::onSipRequest(const AmSipRequest& req)
181 181
 
182 182
 }
183 183
 
184
-void AnnounceTransferDialog::onSipReply(const AmSipReply& rep, int old_dlg_status) {
184
+void AnnounceTransferDialog::onSipReply(const AmSipReply& rep, int old_dlg_status, const string& trans_method) {
185 185
   if ((status==Transfering ||status==Hangup)  && 
186 186
       dlg.get_uac_trans_method(rep.cseq) == "REFER") {
187 187
     if (rep.code >= 300) {
... ...
@@ -191,7 +191,7 @@ void AnnounceTransferDialog::onSipReply(const AmSipReply& rep, int old_dlg_statu
191 191
     }
192 192
   }
193 193
 
194
-  AmSession::onSipReply(rep, old_dlg_status);
194
+  AmSession::onSipReply(rep, old_dlg_status, trans_method);
195 195
 }
196 196
 
197 197
 void AnnounceTransferDialog::onBye(const AmSipRequest& req)
... ...
@@ -72,7 +72,7 @@ public:
72 72
   void startSession();
73 73
   void onBye(const AmSipRequest& req);
74 74
   void onSipRequest(const AmSipRequest& req);
75
-    void onSipReply(const AmSipReply& rep, int old_dlg_status);
75
+  void onSipReply(const AmSipReply& rep, int old_dlg_status, const string& trans_method);
76 76
   void onDtmf(int event, int duration_msec) {}
77 77
 
78 78
   void process(AmEvent* event);
... ...
@@ -314,15 +314,15 @@ inline UACAuthCred* AuthB2BCalleeSession::getCredentials() {
314 314
   return &credentials;
315 315
 }
316 316
 
317
-void AuthB2BCalleeSession::onSipReply(const AmSipReply& reply, int old_dlg_status) {
317
+void AuthB2BCalleeSession::onSipReply(const AmSipReply& reply, int old_dlg_status, const string& trans_method) {
318 318
   if (NULL == auth) {    
319
-      AmB2BCalleeSession::onSipReply(reply,old_dlg_status);
319
+    AmB2BCalleeSession::onSipReply(reply,old_dlg_status,trans_method);
320 320
     return;
321 321
   }
322 322
   
323 323
   unsigned int cseq_before = dlg.cseq;
324
-  if (!auth->onSipReply(reply, old_dlg_status)) {
325
-      AmB2BCalleeSession::onSipReply(reply,old_dlg_status);
324
+  if (!auth->onSipReply(reply, old_dlg_status,trans_method)) {
325
+    AmB2BCalleeSession::onSipReply(reply,old_dlg_status,trans_method);
326 326
   } else {
327 327
     if (cseq_before != dlg.cseq) {
328 328
       DBG("uac_auth consumed reply with cseq %d and resent with cseq %d; "
... ...
@@ -95,7 +95,7 @@ class AuthB2BCalleeSession
95 95
   AmSessionEventHandler* auth;
96 96
 
97 97
  protected:
98
-  void onSipReply(const AmSipReply& reply, int old_dlg_status);
98
+  void onSipReply(const AmSipReply& reply, int old_dlg_status, const string& trans_method);
99 99
   void onSendRequest(const string& method, const string& content_type,
100 100
 		     const string& body, string& hdrs, int flags, unsigned int cseq);
101 101
 
... ...
@@ -862,10 +862,10 @@ void ConferenceDialog::onSipRequest(const AmSipRequest& req)
862 862
   return;
863 863
 }
864 864
 
865
-void ConferenceDialog::onSipReply(const AmSipReply& reply, int old_dlg_status)
865
+void ConferenceDialog::onSipReply(const AmSipReply& reply, int old_dlg_status, const string& trans_method)
866 866
 {
867 867
   int status = dlg.getStatus();
868
-  AmSession::onSipReply(reply,old_dlg_status);
868
+  AmSession::onSipReply(reply, old_dlg_status, trans_method);
869 869
 
870 870
   DBG("ConferenceDialog::onSipReply: code = %i, reason = %s\n, status = %i\n",
871 871
       reply.code,reply.reason.c_str(),dlg.getStatus());
... ...
@@ -153,7 +153,8 @@ public:
153 153
   void onBye(const AmSipRequest& req);
154 154
 
155 155
   void onSipRequest(const AmSipRequest& req);
156
-  void onSipReply(const AmSipReply& reply, int old_dlg_status);
156
+  void onSipReply(const AmSipReply& reply, int old_dlg_status,
157
+		  const string& trans_method);
157 158
 
158 159
 #ifdef WITH_SAS_TTS
159 160
   void onZRTPEvent(zrtp_event_t event, zrtp_stream_ctx_t *stream_ctx);
... ...
@@ -287,7 +287,7 @@ void DSMCall::onSipRequest(const AmSipRequest& req) {
287 287
   AmB2BCallerSession::onSipRequest(req);  
288 288
 }
289 289
 
290
-void DSMCall::onSipReply(const AmSipReply& reply, int old_dlg_status) {
290
+void DSMCall::onSipReply(const AmSipReply& reply, int old_dlg_status, const string& trans_method) {
291 291
 
292 292
   if (checkVar(DSM_ENABLE_REPLY_EVENTS, DSM_TRUE)) {
293 293
     map<string, string> params;
... ...
@@ -318,7 +318,7 @@ void DSMCall::onSipReply(const AmSipReply& reply, int old_dlg_status) {
318 318
     }
319 319
   }
320 320
 
321
-  AmB2BCallerSession::onSipReply(reply,old_dlg_status);
321
+  AmB2BCallerSession::onSipReply(reply,old_dlg_status,trans_method);
322 322
 
323 323
   if ((old_dlg_status < AmSipDialog::Connected) && 
324 324
       (dlg.getStatus() == AmSipDialog::Disconnected)) {
... ...
@@ -83,7 +83,7 @@ public:
83 83
   void onDtmf(int event, int duration_msec);
84 84
 
85 85
   void onSipRequest(const AmSipRequest& req);
86
-  void onSipReply(const AmSipReply& reply, int old_dlg_status);
86
+  void onSipReply(const AmSipReply& reply, int old_dlg_status, const string& trans_method);
87 87
 
88 88
   void process(AmEvent* event);
89 89
 
... ...
@@ -286,8 +286,10 @@ inline UACAuthCred* b2b_connectCalleeSession::getCredentials() {
286 286
   return &credentials;
287 287
 }
288 288
 
289
-void b2b_connectCalleeSession::onSipReply(const AmSipReply& reply, int old_dlg_status) {
290
-  AmB2ABCalleeSession::onSipReply(reply, old_dlg_status);
289
+void b2b_connectCalleeSession::onSipReply(const AmSipReply& reply,
290
+					  int old_dlg_status,
291
+					  const string& trans_method) {
292
+  AmB2ABCalleeSession::onSipReply(reply, old_dlg_status, trans_method);
291 293
  
292 294
   if ((old_dlg_status == AmSipDialog::Pending)&&
293 295
       (dlg.getStatus() == AmSipDialog::Disconnected)) {
... ...
@@ -88,7 +88,8 @@ class b2b_connectCalleeSession
88 88
   AmSipRequest invite_req;
89 89
 
90 90
  protected:
91
-  void onSipReply(const AmSipReply& reply, int old_dlg_status);
91
+  void onSipReply(const AmSipReply& reply, int old_dlg_status,
92
+		  const string& trans_method);
92 93
  
93 94
 public:
94 95
   b2b_connectCalleeSession(const string& other_tag, 
... ...
@@ -90,7 +90,7 @@ void GWSession::onSipRequest(const AmSipRequest& req)
90 90
   }
91 91
 }
92 92
 
93
-void GWSession::onSipReply(const AmSipReply& reply, int old_dlg_status) {
93
+void GWSession::onSipReply(const AmSipReply& reply, int old_dlg_status, const string& trans_method) {
94 94
     int status = dlg.getStatus();
95 95
     DBG("GWSession::onSipReply: code = %i, reason = %s\n, status = %i\n",  
96 96
 	reply.code,reply.reason.c_str(),dlg.getStatus());
... ...
@@ -99,7 +99,7 @@ void GWSession::onSipReply(const AmSipReply& reply, int old_dlg_status) {
99 99
 	int ret=((mISDNChannel*)m_OtherLeg)->hangup();
100 100
     }
101 101
     DBG("GWSession::onSipReply calling parent\n");
102
-    AmSession::onSipReply(reply, old_dlg_status);
102
+    AmSession::onSipReply(reply, old_dlg_status, trans_method);
103 103
 }
104 104
 
105 105
 void GWSession::on_stop() {
... ...
@@ -33,7 +33,7 @@ static    GWSession* CallFromOutside(std::string &fromnumber, std::string &tonum
33 33
   void onBye(const AmSipRequest& req);
34 34
 //virtual void onSipEvent(AmSipEvent* sip_ev);
35 35
   void onSipRequest(const AmSipRequest& req);
36
-  void onSipReply(const AmSipReply& reply, int old_dlg_status);
36
+  void onSipReply(const AmSipReply& reply, int old_dlg_status, const string& trans_method);
37 37
 //virtual void onRtpTimeout();
38 38
 //virtual void onSendRequest(const string& method, const string& content_type, const string& body, string& hdrs, int flags, unsigned int cseq);
39 39
 //virtual void onSendReply(const AmSipRequest& req, unsigned int code,const string& reason,const string& content_type, const string& body,string& hdrs,int flags)
... ...
@@ -749,11 +749,11 @@ void safe_Py_DECREF(PyObject* pyo) {
749 749
   Py_DECREF(pyo);
750 750
 }
751 751
 
752
-void IvrDialog::onSipReply(const AmSipReply& r, int old_dlg_status) {
752
+void IvrDialog::onSipReply(const AmSipReply& r, int old_dlg_status, const string& trans_method) {
753 753
   PyObject* pyo = getPySipReply(r);
754 754
   callPyEventHandler("onSipReply","(O)", pyo);
755 755
   safe_Py_DECREF(pyo);
756
-  AmB2BSession::onSipReply(r,old_dlg_status);
756
+  AmB2BSession::onSipReply(r,old_dlg_status,trans_method);
757 757
 }
758 758
 
759 759
 void IvrDialog::onSipRequest(const AmSipRequest& r){
... ...
@@ -149,7 +149,7 @@ class IvrDialog : public AmB2BCallerSession
149 149
   void onOtherBye(const AmSipRequest& req);
150 150
   bool onOtherReply(const AmSipReply& r);
151 151
 
152
-  void onSipReply(const AmSipReply& r,int old_dlg_status);
152
+  void onSipReply(const AmSipReply& r,int old_dlg_status, const string& trans_method);
153 153
   void onSipRequest(const AmSipRequest& r);
154 154
 
155 155
   void onRtpTimeout();
... ...
@@ -369,9 +369,9 @@ bool SIPRegistration::registerExpired(time_t now_sec) {
369 369
   return ((reg_begin+reg_expires) < (unsigned int)now_sec);	
370 370
 }
371 371
 
372
-void SIPRegistration::onSipReply(const AmSipReply& reply, int old_dlg_status)
372
+void SIPRegistration::onSipReply(const AmSipReply& reply, int old_dlg_status, const string& trans_method)
373 373
 {
374
-  if ((seh!=NULL) && seh->onSipReply(reply, old_dlg_status)) 
374
+  if ((seh!=NULL) && seh->onSipReply(reply, old_dlg_status, trans_method))
375 375
     return;
376 376
 
377 377
   waiting_result = false;
... ...
@@ -498,7 +498,7 @@ void SIPRegistrarClient::onRemoveRegistration(SIPRemoveRegistrationEvent* new_re
498 498
 void SIPRegistrarClient::on_stop() { }
499 499
 
500 500
 
501
-bool SIPRegistrarClient::onSipReply(const AmSipReply& rep) {
501
+bool SIPRegistrarClient::onSipReply(const AmSipReply& rep, int old_dlg_status, const string& trans_method) {
502 502
   DBG("got reply with tag '%s'\n", rep.local_tag.c_str());
503 503
 	
504 504
   if (instance()->hasRegistration(rep.local_tag)) {
... ...
@@ -124,7 +124,7 @@ class SIPRegistration : public AmSipDialogEventHandler,
124 124
   // CredentialHolder	
125 125
   UACAuthCred* getCredentials() { return &cred; }
126 126
 
127
-  void onSipReply(const AmSipReply& reply, int old_dlg_status);
127
+  void onSipReply(const AmSipReply& reply, int old_dlg_status, const string& trans_method);
128 128
   void onSipRequest(const AmSipRequest& req) {}
129 129
   void onInvite2xx(const AmSipReply&) {}
130 130
   void onNoAck(unsigned int) {}
... ...
@@ -192,7 +192,7 @@ class SIPRegistrarClient  : public AmThread,
192 192
   void invoke(const string& method, 
193 193
 	      const AmArg& args, AmArg& ret);
194 194
 	
195
-  bool onSipReply(const AmSipReply& rep);
195
+  bool onSipReply(const AmSipReply& rep, int old_dlg_status, const string& trans_method);
196 196
   int onLoad();
197 197
 	
198 198
   void run();
... ...
@@ -132,25 +132,10 @@ void SSTB2BDialog::onInvite(const AmSipRequest& req)
132 132
   recvd_req.insert(std::make_pair(req.cseq,req));
133 133
   
134 134
   set_sip_relay_only(true);
135
-  DBG("##### connecting to <%s>",req.r_uri.c_str());
135
+  DBG("##### connecting to <%s> #####\n",req.r_uri.c_str());
136 136
   connectCallee("<" + req.r_uri + ">", req.r_uri, true);
137 137
 }
138 138
 
139
-void SSTB2BDialog::sendReinvite(bool updateSDP, const string& headers) {
140
-  if (sip_relay_only) {
141
-    // we send empty reinvite 
142
-    DBG("sending empty reinvite in callee session\n");
143
-    dlg.reinvite(headers, "", ""); 
144
-  } else {
145
-    AmB2BCallerSession::sendReinvite(updateSDP, headers);
146
-  }
147
-
148
-  // // we send empty reinvite
149
-  // dlg.reinvite(headers, "", "");
150
-  // we send reinvite with the last body we got from the other side
151
-  // last_otherleg_content_type, last_otherleg_body);
152
-}
153
-
154 139
 void SSTB2BDialog::process(AmEvent* ev)
155 140
 {
156 141
   AmB2BCallerSession::process(ev);
... ...
@@ -171,7 +156,8 @@ void SSTB2BDialog::onSipRequest(const AmSipRequest& req) {
171 156
   AmB2BCallerSession::onSipRequest(req);
172 157
 }
173 158
 
174
-void SSTB2BDialog::onSipReply(const AmSipReply& reply, int old_dlg_status) 
159
+void SSTB2BDialog::onSipReply(const AmSipReply& reply, int old_dlg_status,
160
+			      const string& trans_method)
175 161
 {
176 162
   TransMap::iterator t = relayed_req.find(reply.cseq);
177 163
   bool fwd = t != relayed_req.end();
... ...
@@ -179,10 +165,10 @@ void SSTB2BDialog::onSipReply(const AmSipReply& reply, int old_dlg_status)
179 165
   DBG("onSipReply: %i %s (fwd=%i)\n",reply.code,reply.reason.c_str(),fwd);
180 166
   DBG("onSipReply: content-type = %s\n",reply.content_type.c_str());
181 167
   if (fwd) {
182
-      CALL_EVENT_H(onSipReply,reply, old_dlg_status);
168
+      CALL_EVENT_H(onSipReply,reply, old_dlg_status, trans_method);
183 169
   }
184 170
 
185
-  AmB2BCallerSession::onSipReply(reply,old_dlg_status);
171
+  AmB2BCallerSession::onSipReply(reply,old_dlg_status, trans_method);
186 172
 }
187 173
 
188 174
 bool SSTB2BDialog::onOtherReply(const AmSipReply& reply)
... ...
@@ -344,7 +330,8 @@ void SSTB2BCalleeSession::onSipRequest(const AmSipRequest& req) {
344 330
   AmB2BCalleeSession::onSipRequest(req);
345 331
 }
346 332
 
347
-void SSTB2BCalleeSession::onSipReply(const AmSipReply& reply, int old_dlg_status) 
333
+void SSTB2BCalleeSession::onSipReply(const AmSipReply& reply, int old_dlg_status,
334
+				     const string& trans_method)
348 335
 {
349 336
   // call event handlers where it is not done 
350 337
   TransMap::iterator t = relayed_req.find(reply.cseq);
... ...
@@ -352,17 +339,17 @@ void SSTB2BCalleeSession::onSipReply(const AmSipReply& reply, int old_dlg_status
352 339
   DBG("onSipReply: %i %s (fwd=%i)\n",reply.code,reply.reason.c_str(),fwd);
353 340
   DBG("onSipReply: content-type = %s\n",reply.content_type.c_str());
354 341
   if(fwd) {
355
-      CALL_EVENT_H(onSipReply,reply, old_dlg_status);
342
+    CALL_EVENT_H(onSipReply,reply, old_dlg_status, trans_method);
356 343
   }
357 344
 
358 345
   if (NULL == auth) {    
359
-      AmB2BCalleeSession::onSipReply(reply,old_dlg_status);
346
+    AmB2BCalleeSession::onSipReply(reply,old_dlg_status, trans_method);
360 347
     return;
361 348
   }
362 349
   
363 350
   unsigned int cseq_before = dlg.cseq;
364
-  if (!auth->onSipReply(reply, old_dlg_status)) {
365
-      AmB2BCalleeSession::onSipReply(reply,old_dlg_status);
351
+  if (!auth->onSipReply(reply, old_dlg_status, trans_method)) {
352
+      AmB2BCalleeSession::onSipReply(reply, old_dlg_status, trans_method);
366 353
   } else {
367 354
     if (cseq_before != dlg.cseq) {
368 355
       DBG("uac_auth consumed reply with cseq %d and resent with cseq %d; "
... ...
@@ -390,13 +377,3 @@ void SSTB2BCalleeSession::onSendRequest(const string& method, const string& cont
390 377
 				     body, hdrs, flags, cseq);
391 378
 }
392 379
 
393
-void SSTB2BCalleeSession::sendReinvite(bool updateSDP, const string& headers) {
394
-  if (sip_relay_only) {
395
-    // we send empty reinvite 
396
-    DBG("sending empty reinvite in callee session\n");
397
-    dlg.reinvite(headers, "", ""); 
398
-  } else {
399
-    AmB2BCalleeSession::sendReinvite(updateSDP, headers);
400
-  }
401
-}
402
-
... ...
@@ -90,10 +90,9 @@ class SSTB2BDialog : public AmB2BCallerSession
90 90
   void onInvite(const AmSipRequest& req);
91 91
   void onCancel();
92 92
 
93
-  void sendReinvite(bool updateSDP, const string& headers);
94
-
95 93
  protected:
96
-    void onSipReply(const AmSipReply& reply, int old_dlg_status);
94
+  void onSipReply(const AmSipReply& reply, int old_dlg_status,
95
+		  const string& trans_method);
97 96
   void onSipRequest(const AmSipRequest& req);  
98 97
 
99 98
  protected:
... ...
@@ -115,7 +114,8 @@ class SSTB2BCalleeSession
115 114
 
116 115
  protected:
117 116
   void onSipRequest(const AmSipRequest& req);
118
-    void onSipReply(const AmSipReply& reply, int old_dlg_status);
117
+  void onSipReply(const AmSipReply& reply, int old_dlg_status,
118
+		  const string& trans_method);
119 119
   void onSendRequest(const string& method, const string& content_type,
120 120
 		     const string& body, string& hdrs, int flags, unsigned int cseq);
121 121
 
... ...
@@ -128,7 +128,5 @@ class SSTB2BCalleeSession
128 128
   inline UACAuthCred* getCredentials();
129 129
   
130 130
   void setAuthHandler(AmSessionEventHandler* h) { auth = h; }
131
-
132
-  void sendReinvite(bool updateSDP, const string& headers);
133 131
 };
134 132
 #endif                           
... ...
@@ -173,11 +173,11 @@ void WebConferenceDialog::onSessionStart(const AmSipReply& rep) {
173 173
   connectConference(dlg.user);
174 174
 }
175 175
 
176
-void WebConferenceDialog::onSipReply(const AmSipReply& reply, int old_dlg_status) 
176
+void WebConferenceDialog::onSipReply(const AmSipReply& reply, int old_dlg_status, const string& trans_method)
177 177
 {
178 178
   //int status = dlg.getStatus();
179 179
 
180
-  AmSession::onSipReply(reply,old_dlg_status);
180
+  AmSession::onSipReply(reply,old_dlg_status,trans_method);
181 181
 
182 182
   DBG("reply: %u %s, old_dlg_status = %s, status = %s\n",
183 183
       reply.code, reply.reason.c_str(),
... ...
@@ -94,7 +94,7 @@ public:
94 94
   ~WebConferenceDialog();
95 95
 
96 96
   void process(AmEvent* ev);
97
-  void onSipReply(const AmSipReply& reply, int old_dlg_status);
97
+  void onSipReply(const AmSipReply& reply, int old_dlg_status, const string& trans_method);
98 98
   void onSessionStart(const AmSipRequest& req);
99 99
   void onSessionStart(const AmSipReply& rep);
100 100
   void onEarlySessionStart(const AmSipReply& rep);
... ...
@@ -353,13 +353,13 @@ void AmB2ABCalleeSession::onSessionStart(const AmSipReply& rep) {
353 353
   relayEvent(new B2ABConnectAudioEvent());
354 354
 }
355 355
 
356
-void AmB2ABCalleeSession::onSipReply(const AmSipReply& rep, int old_dlg_status) {
357
-
358
-    int status_before = old_dlg_status;//dlg.getStatus();
359
-    AmB2ABSession::onSipReply(rep, old_dlg_status);
356
+void AmB2ABCalleeSession::onSipReply(const AmSipReply& rep,
357
+				     int old_dlg_status,
358
+				     const string& trans_method) {
359
+  AmB2ABSession::onSipReply(rep, old_dlg_status, trans_method);
360 360
   int status = dlg.getStatus();
361 361
  
362
-  if ((status_before == AmSipDialog::Pending)&&
362
+  if ((old_dlg_status == AmSipDialog::Pending)&&
363 363
       (status == AmSipDialog::Disconnected)) {
364 364
 	  
365 365
     DBG("callee session creation failed. notifying caller session.\n");
... ...
@@ -247,7 +247,8 @@ class AmB2ABCalleeSession: public AmB2ABSession
247 247
 
248 248
   void onEarlySessionStart(const AmSipReply& rep);
249 249
   void onSessionStart(const AmSipReply& rep);
250
-  void onSipReply(const AmSipReply& rep, int old_dlg_status);	
250
+  void onSipReply(const AmSipReply& rep, int old_dlg_status,
251
+		  const string& trans_method);
251 252
 
252 253
  protected:
253 254
   void onB2ABEvent(B2ABEvent* ev);
... ...
@@ -90,6 +90,16 @@ void AmB2BSession::onB2BEvent(B2BEvent* ev)
90 90
       assert(req_ev);
91 91
 
92 92
       if(req_ev->forward){
93
+	if (req_ev->req.method == SIP_METH_INVITE &&
94
+	    dlg.getUACInvTransPending()) {
95
+	  // don't relay INVITE if INV trans pending
96
+	  AmSipReply n_reply;
97
+	  n_reply.code = 491;
98
+	  n_reply.reason = "Request Pending";
99
+	  n_reply.cseq = req_ev->req.cseq;
100
+	  relayEvent(new B2BSipReplyEvent(n_reply, true, SIP_METH_INVITE));
101
+	  return;
102
+	}
93 103
 
94 104
 	relaySip(req_ev->req);
95 105
       }
... ...
@@ -106,19 +116,21 @@ void AmB2BSession::onB2BEvent(B2BEvent* ev)
106 116
       B2BSipReplyEvent* reply_ev = dynamic_cast<B2BSipReplyEvent*>(ev);
107 117
       assert(reply_ev);
108 118
 
109
-      DBG("B2BSipReply: %i %s (fwd=%i)\n",reply_ev->reply.code,
110
-	  reply_ev->reply.reason.c_str(),reply_ev->forward);
119
+      DBG("B2BSipReply: %i %s (fwd=%s)\n",reply_ev->reply.code,
120
+	  reply_ev->reply.reason.c_str(),reply_ev->forward?"true":"false");
111 121
       DBG("B2BSipReply: content-type = %s\n",reply_ev->reply.content_type.c_str());
112 122
 
113 123
       if(reply_ev->forward){
114 124
 
115
-        std::map<int,AmSipRequest>::iterator t_req = recvd_req.find(reply_ev->reply.cseq);
125
+        std::map<int,AmSipRequest>::iterator t_req =
126
+	  recvd_req.find(reply_ev->reply.cseq);
127
+
116 128
 	if (t_req != recvd_req.end()) {
117 129
 	  relaySip(t_req->second,reply_ev->reply);
118 130
 		
119 131
 	  if(reply_ev->reply.code >= 200){
120 132
 
121
-	    if( (t_req->second.method == "INVITE") &&
133
+	    if( (t_req->second.method == SIP_METH_INVITE) &&
122 134
 		(reply_ev->reply.code == 487)){
123 135
 	      
124 136
 	      terminateLeg();
... ...
@@ -129,6 +141,24 @@ void AmB2BSession::onB2BEvent(B2BEvent* ev)
129 141
 	  ERROR("Request with CSeq %u not found in recvd_req.\n",
130 142
 		reply_ev->reply.cseq);
131 143
 	}
144
+      } else {
145
+	// check whether not-forwarded (locally initiated)
146
+	// INV/UPD transaction changed session in other leg
147
+	if (SIP_IS_200_CLASS(reply_ev->reply.code) &&
148
+	    (!reply_ev->reply.body.empty()) &&
149
+	    (reply_ev->reply.method == SIP_METH_INVITE ||
150
+	     reply_ev->reply.method == SIP_METH_UPDATE)) {
151
+	  if (updateSessionDescription(reply_ev->reply.content_type,
152
+				       reply_ev->reply.body)) {
153
+	    if (dlg.getUACInvTransPending()) {
154
+	      DBG("changed session, but UAC INVITE trans pending\n");
155
+	      // todo(?): save until trans is finished?
156
+	      return;
157
+	    }
158
+	    DBG("session description changed - refreshing\n");
159
+	    sendEstablishedReInvite();
160
+	  }
161
+	}
132 162
       }
133 163
     }
134 164
     return;
... ...
@@ -137,44 +167,6 @@ void AmB2BSession::onB2BEvent(B2BEvent* ev)
137 167
     terminateLeg();
138 168
     break;
139 169
 
140
-  case B2BMsgBody:
141
-    {
142
-      if (!sip_relay_only) {
143
-	ERROR("relayed message body received but not in sip_relay_only mode\n");
144
-	return;
145
-      }
146
-
147
-      B2BMsgBodyEvent* body_ev = dynamic_cast<B2BMsgBodyEvent*>(ev);
148
-      assert(body_ev);
149
-
150
-      DBG("received B2B Msg body event; is_offer=%s, r_cseq=%d\n",
151
-	  body_ev->is_offer?"true":"false", body_ev->r_cseq);
152
-      
153
-      if (body_ev->is_offer) {
154
-	// send INVITE with SDP
155
-	trans_ticket tt; // empty transaction ticket
156
-	relayed_body_req[dlg.cseq] = AmSipTransaction("INVITE", body_ev->r_cseq, tt);
157
-	if (dlg.reinvite("", body_ev->content_type, body_ev->body)) {
158
-	  ERROR("sending reinvite with relayed body\n");
159
-	  relayed_body_req.erase(dlg.cseq);
160
-	  // TODO?: relay error back instead?
161
-	  // tear down:
162
-	  DBG("error sending reinvite - terminating this and the other leg\n");
163
-	  terminateOtherLeg();	   
164
-	  terminateLeg();
165
-	}
166
-      } else {
167
-	// is_answer - send 200 ACK
168
-	// todo: use that from uas_trans? 
169
-	trans_ticket tt; // not used for ACK
170
-	AmSipTransaction trans("INVITE", body_ev->r_cseq, tt);
171
-	if (dlg.send_200_ack(trans, body_ev->content_type, body_ev->body, 
172
-			     "" /* hdrs - todo */, SIP_FLAGS_VERBATIM)) {
173
-	  ERROR("sending ACK with SDP\n");
174
-	}
175
-      }
176
-      return; 
177
-    }; break;
178 170
   }
179 171
 
180 172
   //ERROR("unknown event caught\n");
... ...
@@ -189,7 +181,6 @@ void AmB2BSession::onSipRequest(const AmSipRequest& req)
189 181
   if(!fwd)
190 182
     AmSession::onSipRequest(req);
191 183
   else {
192
-    //dlg.updateStatus(req);
193 184
     updateRefreshMethod(req.hdrs);
194 185
     recvd_req.insert(std::make_pair(req.cseq,req));
195 186
   }
... ...
@@ -197,73 +188,33 @@ void AmB2BSession::onSipRequest(const AmSipRequest& req)
197 188
   relayEvent(new B2BSipRequestEvent(req,fwd));
198 189
 }
199 190
 
200
-void AmB2BSession::onSipReply(const AmSipReply& reply, int old_dlg_status)
191
+void AmB2BSession::onSipReply(const AmSipReply& reply,
192
+			      int old_dlg_status,
193
+			      const string& trans_method)
201 194
 {
202
-  updateRefreshMethod(reply.hdrs);
203
-
204 195
   TransMap::iterator t = relayed_req.find(reply.cseq);
205 196
   bool fwd = (t != relayed_req.end()) && (reply.code != 100);
206 197
 
207
-  DBG("onSipReply: %i %s (fwd=%i)\n",reply.code,reply.reason.c_str(),fwd);
198
+  DBG("onSipReply: %s -> %i %s (fwd=%s)\n",
199
+      trans_method.c_str(), reply.code,reply.reason.c_str(),fwd?"true":"false");
208 200
   DBG("onSipReply: content-type = %s\n",reply.content_type.c_str());
209 201
   if(fwd) {
202
+    updateRefreshMethod(reply.hdrs);
203
+
210 204
     AmSipReply n_reply = reply;
211 205
     n_reply.cseq = t->second.cseq;
212 206
     
213
-    //dlg.updateStatus(reply, false);
214
-    relayEvent(new B2BSipReplyEvent(n_reply,true));
207
+    relayEvent(new B2BSipReplyEvent(n_reply, true, t->second.method));
215 208
 
216 209
     if(reply.code >= 200) {
217
-      if ((reply.code < 300) && (t->second.method == "INVITE")) {
210
+      if ((reply.code < 300) && (t->second.method == SIP_METH_INVITE)) {
218 211
 	DBG("not removing relayed INVITE transaction yet...\n");
219 212
       } else 
220 213
 	relayed_req.erase(t);
221 214
     }
222 215
   } else {
223
-    string trans_method = dlg.get_uac_trans_method(reply.cseq);
224
-
225
-    bool relay_body = 
226
-      // is a reply to request we sent, 
227
-      // even though we are in sip_relay_only  mode
228
-      (sip_relay_only && 
229
-       // positive reply
230
-       (200 <= reply.code) && (reply.code < 300) &&
231
-       // with body
232
-       !reply.body.empty() &&
233
-       trans_method == SIP_METH_INVITE);
234
-    
235
-    if (relay_body) {
236
-      // is it an answer to a relayed body, or an answer to empty re-INVITE? 
237
-      TransMap::iterator rel_body_it = relayed_body_req.find(reply.cseq);
238
-      bool is_offer =  (rel_body_it == relayed_body_req.end());
239
-      // answer to empty re-INVITE we have sent
240
-      relayEvent(new B2BMsgBodyEvent(reply.content_type, reply.body, 
241
-				     is_offer, is_offer ? reply.cseq : rel_body_it->second.cseq));
242
-
243
-      if (is_offer) {	
244
-	// onSipReply from AmSession without do_200_ack in dlg.updateStatus(reply)
245
-	// todo (?): add do_200_ack flag to AmSession::onSipReply and call AmSession::onSipReply
246
-	CALL_EVENT_H(onSipReply,reply,old_dlg_status);
247
-	
248
-	//int status = dlg.getStatus();
249
-	//dlg.updateStatus(reply, false);
250
-	
251
-	if (old_dlg_status != dlg.getStatus())
252
-	  DBG("Dialog status changed %s -> %s (stopped=%s) \n", 
253
-	      AmSipDialog::status2str[old_dlg_status], 
254
-	      AmSipDialog::status2str[dlg.getStatus()],
255
-	      getStopped() ? "true" : "false");
256
-	else 
257
-	  DBG("Dialog status stays %s (stopped=%s)\n", AmSipDialog::status2str[old_dlg_status], 
258
-	      getStopped() ? "true" : "false");
259
-      } else {
260
-	relayed_body_req.erase(rel_body_it);
261
-	AmSession::onSipReply(reply, old_dlg_status);
262
-      }      
263
-    } else {
264
-      AmSession::onSipReply(reply, old_dlg_status);
265
-    }
266
-    relayEvent(new B2BSipReplyEvent(reply,false));
216
+    AmSession::onSipReply(reply, old_dlg_status, trans_method);
217
+    relayEvent(new B2BSipReplyEvent(reply, false, trans_method));
267 218
   }
268 219
 }
269 220
 
... ...
@@ -325,12 +276,97 @@ void AmB2BSession::onSessionTimeout() {
325 276
   AmSession::onSessionTimeout();
326 277
 }
327 278
 
279
+void AmB2BSession::saveSessionDescription(const string& content_type,
280
+					  const string& body) {
281
+  DBG("saving session description (%s, %.*s...)\n",
282
+      content_type.c_str(), 50, body.c_str());
283
+  established_content_type = content_type;
284
+  established_body = body;
285
+
286
+  const char* cmp_body_begin = body.c_str();
287
+  size_t cmp_body_length = body.length();
288
+  if (content_type == SIP_APPLICATION_SDP) {
289
+    // for SDP, skip v and o line
290
+    // (o might change even if SDP unchanged)
291
+#define skip_line						\
292
+    while (cmp_body_length-- && *cmp_body_begin != '\n')	\
293
+      cmp_body_begin++;						\
294
+    cmp_body_begin++;						\
295
+
296
+    skip_line;
297
+    skip_line;
298
+  }
299
+
300
+  body_hash = hashlittle(cmp_body_begin, cmp_body_length, 0);
301
+}
302
+
303
+bool AmB2BSession::updateSessionDescription(const string& content_type,
304
+					    const string& body) {
305
+  const char* cmp_body_begin = body.c_str();
306
+  size_t cmp_body_length = body.length();
307
+  if (content_type == SIP_APPLICATION_SDP) {
308
+    // for SDP, skip v and o line
309
+    // (o might change even if SDP unchanged)
310
+    skip_line;
311
+    skip_line;
312
+  }
313
+
314
+#undef skip_line
315
+
316
+  uint32_t new_body_hash = hashlittle(cmp_body_begin, cmp_body_length, 0);
317
+
318
+  if (body_hash != new_body_hash) {
319
+    DBG("session description changed - saving (%s, %.*s...)\n",
320
+	content_type.c_str(), 50, body.c_str());
321
+    body_hash = new_body_hash;
322
+    established_content_type = content_type;
323
+    established_body = body;
324
+    return true;
325
+  }
326
+
327
+  return false;
328
+}
329
+
330
+int AmB2BSession::sendEstablishedReInvite() {
331
+  if (established_content_type.empty() || established_body.empty()) {
332
+    ERROR("trying to re-INVITE with saved description, but none saved\n");
333
+    return -1;
334
+  }
335
+
336
+  DBG("sending re-INVITE with saved session description\n");
337
+  return dlg.reinvite(get_100rel_hdr(reliable_1xx),
338
+		      established_content_type, established_body);
339
+}
340
+
341
+bool AmB2BSession::refresh() {
342
+  // not in B2B mode
343
+  if (other_id.empty() ||
344
+      // UPDATE as refresh handled like normal session
345
+      refresh_method == REFRESH_UPDATE) {
346
+    return AmSession::refresh();
347
+  }
348
+
349
+  // refresh with re-INVITE
350
+  if (dlg.getUACInvTransPending()) {
351
+    DBG("INVITE transaction pending - not refreshing now\n");
352
+    return false;
353
+  }
354
+  return sendEstablishedReInvite() == 0;
355
+}
356
+
328 357
 void AmB2BSession::relaySip(const AmSipRequest& req)
329 358
 {
330 359
   if (req.method != "ACK") {
331 360
     relayed_req[dlg.cseq] = AmSipTransaction(req.method,req.cseq,req.tt);
332 361
     dlg.sendRequest(req.method,req.content_type, req.body, req.hdrs, SIP_FLAGS_VERBATIM);
333 362
     // todo: relay error event back if sending fails
363
+
364
+    if ((req.method == SIP_METH_INVITE ||
365
+	 req.method == SIP_METH_UPDATE) &&
366
+	!req.body.empty()) {
367
+      saveSessionDescription(req.content_type, req.body);
368
+    }
369
+
334 370
   } else {
335 371
     //its a (200) ACK 
336 372
     TransMap::iterator t = relayed_req.begin(); 
... ...
@@ -344,9 +380,16 @@ void AmB2BSession::relaySip(const AmSipRequest& req)
344 380
       ERROR("transaction for ACK not found in relayed requests\n");
345 381
       return;
346 382
     }
383
+
347 384
     DBG("sending relayed ACK\n");
348 385
     dlg.send_200_ack(AmSipTransaction(t->second.method, t->first,t->second.tt), 
349 386
 		     req.content_type, req.body, req.hdrs, SIP_FLAGS_VERBATIM);
387
+
388
+    if (!req.body.empty() && t->second.method == SIP_METH_INVITE) {
389
+    // delayed SDP negotiation - save SDP
390
+      saveSessionDescription(req.content_type, req.body);
391
+    }
392
+
350 393
     relayed_req.erase(t);
351 394
   }
352 395
 }
... ...
@@ -356,6 +399,13 @@ void AmB2BSession::relaySip(const AmSipRequest& orig, const AmSipReply& reply)
356 399
   dlg.reply(orig,reply.code,reply.reason,
357 400
 	    reply.content_type,
358 401
 	    reply.body,reply.hdrs,SIP_FLAGS_VERBATIM);
402
+
403
+  if ((orig.method == SIP_METH_INVITE ||
404
+       orig.method == SIP_METH_UPDATE) &&
405
+      !reply.body.empty()) {
406
+    saveSessionDescription(reply.content_type, reply.body);
407
+  }
408
+
359 409
 }
360 410
 
361 411
 // 
... ...
@@ -411,7 +461,8 @@ void AmB2BCallerSession::onB2BEvent(B2BEvent* ev)
411 461
 	if ((!sip_relay_only) && sip_relay_early_media_sdp && 
412 462
 	    reply.code>=180 && reply.code<=183 && (!reply.body.empty())) {
413 463
 	  if (reinviteCaller(reply)) {
414
-	    ERROR("re-INVITEing caller for early session - stopping this and other leg\n");
464
+	    ERROR("re-INVITEing caller for early session - "
465
+		  "stopping this and other leg\n");
415 466
 	    terminateOtherLeg();
416 467
 	    terminateLeg();
417 468
 	  }
... ...
@@ -500,7 +551,9 @@ void AmB2BCallerSession::connectCallee(const string& remote_party,
500 551
 
501 552
 int AmB2BCallerSession::reinviteCaller(const AmSipReply& callee_reply)
502 553
 {
503
-  return dlg.sendRequest("INVITE",callee_reply.content_type,callee_reply.body, "", SIP_FLAGS_VERBATIM);
554
+  return dlg.sendRequest(SIP_METH_INVITE,
555
+			 callee_reply.content_type, callee_reply.body,
556
+			 "" /* hdrs */, SIP_FLAGS_VERBATIM);
504 557
 }
505 558
 
506 559
 void AmB2BCallerSession::createCalleeSession() {
... ...
@@ -572,12 +625,17 @@ void AmB2BCalleeSession::onB2BEvent(B2BEvent* ev)
572 625
     dlg.remote_uri   = co_ev->remote_uri;
573 626
 
574 627
     if (co_ev->relayed_invite) {
575
-      relayed_req[dlg.cseq] = AmSipTransaction("INVITE", co_ev->r_cseq, trans_ticket());
628
+      relayed_req[dlg.cseq] =
629
+	AmSipTransaction(SIP_METH_INVITE, co_ev->r_cseq, trans_ticket());
576 630
     }
577 631
 
578
-    dlg.sendRequest("INVITE",co_ev->content_type,co_ev->body,co_ev->hdrs,SIP_FLAGS_VERBATIM);
632
+    dlg.sendRequest(SIP_METH_INVITE, 
633
+		    co_ev->content_type, co_ev->body,
634
+		    co_ev->hdrs, SIP_FLAGS_VERBATIM);
579 635
     // todo: relay error event back if sending fails
580 636
 
637
+    saveSessionDescription(co_ev->content_type, co_ev->body);
638
+
581 639
     return;
582 640
   }    
583 641
 
... ...
@@ -30,6 +30,7 @@
30 30
 
31 31
 #include "AmSession.h"
32 32
 #include "AmSipDialog.h"
33
+#include "sip/hash.h"
33 34
 
34 35
 enum { B2BTerminateLeg, 
35 36
        B2BConnectLeg, 
... ...
@@ -65,18 +66,20 @@ struct B2BSipRequestEvent: public B2BSipEvent
65 66
   B2BSipRequestEvent(const AmSipRequest& req, bool forward)
66 67
     : B2BSipEvent(B2BSipRequest,forward),
67 68
        req(req)
68
-  {}
69
+  { }
69 70
 };
70 71
 
71 72
 /** \brief SIP reply in B2B session */
72 73
 struct B2BSipReplyEvent: public B2BSipEvent
73 74
 {
74 75
   AmSipReply reply;
76
+  string trans_method;
75 77
 
76
-  B2BSipReplyEvent(const AmSipReply& reply, bool forward)
77
-    : B2BSipEvent(B2BSipReply,forward),
78
-       reply(reply)
79
-  {}
78
+ B2BSipReplyEvent(const AmSipReply& reply, bool forward,
79
+		  string trans_method)
80
+   : B2BSipEvent(B2BSipReply,forward),
81
+    reply(reply), trans_method(trans_method)
82
+  { }
80 83
 };
81 84
 
82 85
 /** \brief relay a message body to other leg in B2B session */
... ...
@@ -84,17 +87,10 @@ struct B2BMsgBodyEvent : public B2BEvent {
84 87
   string content_type;
85 88
   string body;
86 89
 
87
-  bool is_offer;
88
-  unsigned int r_cseq;
89
-
90
-  B2BMsgBodyEvent(const string& content_type, 
91
-	       const string& body, 
92
-	       bool is_offer,
93
-	       unsigned int r_cseq)
90
+  B2BMsgBodyEvent(const string& content_type,
91
+		  const string& body)
94 92
     : B2BEvent(B2BMsgBody),
95
-    content_type(content_type), body(body), 
96
-    is_offer(is_offer), r_cseq(r_cseq)  {
97
-  }
93
+    content_type(content_type), body(body) { }
98 94
   ~B2BMsgBodyEvent() { }
99 95
 };
100 96
 
... ...
@@ -124,7 +120,8 @@ struct B2BConnectEvent: public B2BEvent
124 120
 /**
125 121
  * \brief Base class for Sessions in B2BUA mode.
126 122
  * 
127
- * It has two legs: Callee- and caller-leg.
123
+ * It has two legs as independent sessions:
124
+ * Callee- and caller-leg.
128 125
  */
129 126
 class AmB2BSession: public AmSession
130 127
 {
... ...
@@ -134,32 +131,40 @@ class AmB2BSession: public AmSession
134 131
 
135 132
   /** Tell if the session should
136 133
    *  process SIP request itself
137
-   * or only relay them.
134
+   * or only relay them (B2B mode).
138 135
    */
139 136
   bool sip_relay_only;
140 137
 
141 138
   /** 
142
-   * Requests which
143
-   * have been relayed (sent)
139
+   * Requests which have been relayed 
140
+   * from the other leg and sent as SIP
144 141
    */
145 142
   TransMap relayed_req;
146 143
 
147
-  /** 
148
-   * Requests which have been originated 
149
-   * from local dialog but with 
150
-   * relayed body
151
-   */
152
-  TransMap relayed_body_req;
153
-
154 144
   /** Requests received for relaying */
155 145
   std::map<int,AmSipRequest> recvd_req;
156 146
 
147
+  /** content-type of established session */
148
+  string established_content_type;
149
+  /** body of established session */
150
+  string established_body;
151
+  /** hash of body (from o-line) */
152
+  uint32_t body_hash;
153
+  /** save current session description (SDP) */
154
+  void saveSessionDescription(const string& content_type, const string& body);
155
+  /** @return whether session has changed */
156
+  bool updateSessionDescription(const string& content_type, const string& body);
157
+
158
+  /** reset relation with other leg */
157 159
   void clear_other();
158 160
 
159 161
   /** Relay one event to the other side. */
160 162
   virtual void relayEvent(AmEvent* ev);
161 163
 
164
+  /** send a relayed SIP Request */
162 165
   void relaySip(const AmSipRequest& req);
166
+
167
+  /** send a relayed SIP Reply */
163 168
   void relaySip(const AmSipRequest& orig, const AmSipReply& reply);
164 169
 
165 170
   /** Terminate our leg and forget the other. */
... ...
@@ -170,18 +175,27 @@ class AmB2BSession: public AmSession
170 175
 
171 176
   /** @see AmSession */
172 177
   void onSipRequest(const AmSipRequest& req);
173
-  void onSipReply(const AmSipReply& reply, int old_dlg_status);
178
+  void onSipReply(const AmSipReply& reply,
179
+		  int old_dlg_status, const string& trans_method);
174 180
   void onInvite2xx(const AmSipReply& reply);
175 181
 
176 182
   void onSessionTimeout();
177 183
 
184
+  /** send re-INVITE with established session description 
185
+   *  @return 0 on success
186
+   */
187
+  int sendEstablishedReInvite();
188
+
189
+  /** do session refresh */
190
+  bool refresh();
191
+
178 192
   /** @see AmEventQueue */
179 193
   void process(AmEvent* event);
180 194
 
181 195
   /** B2BEvent handler */
182 196
   virtual void onB2BEvent(B2BEvent* ev);
183 197
 
184
-  // Other leg received a BYE
198
+  /** handle BYE on other leg */
185 199
   virtual void onOtherBye(const AmSipRequest& req);
186 200
 
187 201
   /** 
... ...
@@ -36,7 +36,6 @@
36 36
 #include "AmMediaProcessor.h"
37 37
 #include "AmDtmfDetector.h"
38 38
 #include "AmPlayoutBuffer.h"
39
-#include "AmSipHeaders.h"
40 39
 
41 40
 #ifdef WITH_ZRTP
42 41
 #include "AmZRTP.h"
... ...
@@ -774,9 +773,10 @@ void AmSession::onSipRequest(const AmSipRequest& req)
774 773
   }
775 774
 }
776 775
 
777
-void AmSession::onSipReply(const AmSipReply& reply, int old_dlg_status)
776
+void AmSession::onSipReply(const AmSipReply& reply,
777
+			   int old_dlg_status, const string& trans_method)
778 778
 {
779
-  CALL_EVENT_H(onSipReply,reply,old_dlg_status);
779
+  CALL_EVENT_H(onSipReply, reply, old_dlg_status, trans_method);
780 780
 
781 781
   updateRefreshMethod(reply.hdrs);
782 782
 
... ...
@@ -786,7 +786,8 @@ void AmSession::onSipReply(const AmSipReply& reply, int old_dlg_status)
786 786
 	AmSipDialog::status2str[dlg.getStatus()],
787 787
 	sess_stopped.get() ? "true" : "false");
788 788
   else 
789
-    DBG("Dialog status stays %s (stopped=%s)\n", AmSipDialog::status2str[old_dlg_status], 
789
+    DBG("Dialog status stays %s (stopped=%s)\n",
790
+	AmSipDialog::status2str[old_dlg_status], 
790 791
 	sess_stopped.get() ? "true" : "false");
791 792
 
792 793
 
... ...
@@ -953,7 +954,7 @@ void AmSession::onInvite(const AmSipRequest& req)
953 954
 
954 955
     acceptAudio(req.body,req.hdrs,&sdp_reply);
955 956
     if(dlg.reply(req,200,"OK",
956
-		 "application/sdp",sdp_reply) != 0)
957
+		 SIP_APPLICATION_SDP, sdp_reply) != 0)
957 958
       throw AmSession::Exception(500,"could not send response");
958 959
 	
959 960
   }catch(const AmSession::Exception& e){
... ...
@@ -1103,20 +1104,26 @@ void AmSession::updateRefreshMethod(const string& headers) {
1103 1104
   }
1104 1105
 }
1105 1106
 
1106
-void AmSession::refresh() {
1107
+bool AmSession::refresh() {
1107 1108
   if (refresh_method == REFRESH_UPDATE) {
1108 1109
     DBG("Refreshing session with UPDATE\n");
1109
-    sendUpdate("", "", "");
1110
+    return sendUpdate("", "", "") == 0;
1110 1111
   } else {
1112
+
1113
+    if (dlg.getUACInvTransPending()) {
1114
+      DBG("INVITE transaction pending - not refreshing now\n");
1115
+      return false;
1116
+    }
1117
+
1111 1118
     DBG("Refreshing session with re-INVITE\n");
1112
-    sendReinvite(true);
1119
+    return sendReinvite(true) == 0;
1113 1120
   }
1114 1121
 }
1115 1122
 
1116
-void AmSession::sendUpdate(const string &cont_type, const string &body,
1123
+int AmSession::sendUpdate(const string &cont_type, const string &body,
1117 1124
 			   const string &hdrs)
1118 1125
 {
1119
-  dlg.update(cont_type, body, hdrs);
1126
+  return dlg.update(cont_type, body, hdrs);
1120 1127
 }
1121 1128
 
1122 1129
 void AmSession::sendPrack(const string &sdp_offer, 
... ...
@@ -1141,32 +1148,16 @@ string AmSession::sid4dbg()
1141 1148
   return dbg;
1142 1149
 }
1143 1150
 
1144
-static inline string get_100rel_hdr(unsigned char reliable_1xx)
1145
-{
1146
-  switch(reliable_1xx) {
1147
-    case REL100_SUPPORTED: 
1148
-      return SIP_HDR_COLSP(SIP_HDR_SUPPORTED) SIP_EXT_100REL CRLF;
1149
-    case REL100_REQUIRE: 
1150
-      return SIP_HDR_COLSP(SIP_HDR_REQUIRE) SIP_EXT_100REL CRLF;
1151
-    default:
1152
-      ERROR("BUG: unexpected reliability switch value of '%d'.\n",
1153
-          reliable_1xx);
1154
-    case 0:
1155
-      break;
1156
-  }
1157
-  return "";
1158
-}
1159
-
1160
-void AmSession::sendReinvite(bool updateSDP, const string& headers) 
1151
+int AmSession::sendReinvite(bool updateSDP, const string& headers) 
1161 1152
 {
1162 1153
   if (updateSDP) {
1163 1154
     RTPStream()->setLocalIP(AmConfig::LocalIP);
1164 1155
     string sdp_body;
1165 1156
     sdp.genResponse(advertisedIP(), RTPStream()->getLocalPort(), sdp_body);
1166
-    dlg.reinvite(headers + get_100rel_hdr(reliable_1xx), "application/sdp",
1157
+    return dlg.reinvite(headers + get_100rel_hdr(reliable_1xx), SIP_APPLICATION_SDP,
1167 1158
         sdp_body);
1168 1159
   } else {
1169
-    dlg.reinvite(headers + get_100rel_hdr(reliable_1xx), "", "");
1160
+    return dlg.reinvite(headers + get_100rel_hdr(reliable_1xx), "", "");
1170 1161
   }
1171 1162
 }
1172 1163
 
... ...
@@ -1181,7 +1172,7 @@ int AmSession::sendInvite(const string& headers)
1181 1172
   // Generate SDP.
1182 1173
   string sdp_body;
1183 1174
   sdp.genRequest(advertisedIP(), RTPStream()->getLocalPort(), sdp_body);
1184
-  return dlg.invite(headers + get_100rel_hdr(reliable_1xx), "application/sdp",
1175
+  return dlg.invite(headers + get_100rel_hdr(reliable_1xx), SIP_APPLICATION_SDP,
1185 1176
       sdp_body);
1186 1177
 }
1187 1178
 
... ...
@@ -34,6 +34,7 @@
34 34
 #include "AmRtpAudio.h"
35 35
 #include "AmDtmfDetector.h"
36 36
 #include "AmSipMsg.h"
37
+#include "AmSipHeaders.h"
37 38
 #include "AmSipDialog.h"
38 39
 #include "AmSipEvent.h"
39 40
 #include "AmApi.h"
... ...
@@ -360,13 +361,13 @@ public:
360 361
 			 string* sdp_reply);
361 362
 
362 363
   /** refresh the session - re-INVITE or UPDATE*/
363
-  virtual void refresh();
364
+  virtual bool refresh();
364 365
 
365 366
   /** send an UPDATE in the session */
366
-  virtual void sendUpdate(const string &cont_type, const string &body, const string &hdrs);
367
+  virtual int sendUpdate(const string &cont_type, const string &body, const string &hdrs);
367 368
 
368 369
   /** send a Re-INVITE (if connected) */
369
-  virtual void sendReinvite(bool updateSDP = true, const string& headers = "");
370
+  virtual int sendReinvite(bool updateSDP = true, const string& headers = "");
370 371
 
371 372
   /** send an INVITE */
372 373
   virtual int sendInvite(const string& headers = "");
... ...
@@ -521,7 +522,8 @@ public:
521 522
   virtual void onSipRequest(const AmSipRequest& req);
522 523
 
523 524
   /** Entry point for SIP Replies   */
524
-  virtual void onSipReply(const AmSipReply& reply, int old_dlg_status);
525
+  virtual void onSipReply(const AmSipReply& reply, int old_dlg_status,
526
+			      const string& trans_method);
525 527
 
526 528
   /** 2xx reply has been received for an INVITE transaction */
527 529
   virtual void onInvite2xx(const AmSipReply& reply);
... ...
@@ -586,6 +588,7 @@ public:
586 588
   // The IP address to put as c= in SDP bodies and to use for Contact:.
587 589
   string advertisedIP();
588 590
 
591
+  /** format session id for debugging */
589 592
   string sid4dbg();
590 593
 };
591 594
 
... ...
@@ -598,6 +601,22 @@ inline AmRtpAudio* AmSession::RTPStream() {
598 601
   return _rtp_str.get();
599 602
 }
600 603
 
604
+static inline string get_100rel_hdr(unsigned char reliable_1xx)
605
+{
606
+  switch(reliable_1xx) {
607
+    case REL100_SUPPORTED:
608
+      return SIP_HDR_COLSP(SIP_HDR_SUPPORTED) SIP_EXT_100REL CRLF;
609
+    case REL100_REQUIRE:
610
+      return SIP_HDR_COLSP(SIP_HDR_REQUIRE) SIP_EXT_100REL CRLF;
611
+    default:
612
+      ERROR("BUG: unexpected reliability switch value of '%d'.\n",
613
+          reliable_1xx);
614
+    case 0:
615
+      break;
616
+  }
617
+  return "";
618
+}
619
+
601 620
 #endif
602 621
 
603 622
 /** EMACS **
... ...
@@ -43,7 +43,8 @@ bool AmSessionEventHandler::onSipRequest(const AmSipRequest&)
43 43
   return false;
44 44
 }
45 45
 
46
-bool AmSessionEventHandler::onSipReply(const AmSipReply& reply, int old_dlg_status)
46
+bool AmSessionEventHandler::onSipReply(const AmSipReply& reply, int old_dlg_status,
47
+				       const string& trans_method)
47 48
 {
48 49
   return false;
49 50
 }
... ...
@@ -66,7 +66,8 @@ public:
66 66
   virtual bool process(AmEvent*);
67 67
 
68 68
   virtual bool onSipRequest(const AmSipRequest&);
69
-  virtual bool onSipReply(const AmSipReply&, int old_dlg_status);
69
+  virtual bool onSipReply(const AmSipReply&, int old_dlg_status,
70
+			  const string& trans_method);
70 71
   virtual bool onSipReqTimeout(const AmSipRequest &);
71 72
   virtual bool onSipRplTimeout(const AmSipRequest &, const AmSipReply &);
72 73
 
... ...
@@ -226,6 +226,7 @@ void AmSipDialog::updateStatus(const AmSipReply& reply)
226 226
 
227 227
   AmSipTransaction& t = t_it->second;
228 228
   int old_dlg_status = status;
229
+  string trans_method = t.method;
229 230
 
230 231
   // rfc3261 12.1
231 232
   // Dialog established only by 101-199 or 2xx 
... ...
@@ -240,7 +241,7 @@ void AmSipDialog::updateStatus(const AmSipReply& reply)
240 241
     remote_tag = reply.remote_tag;
241 242
   }
242 243
 
243
-  // allow route overwritting
244
+  // allow route overwriting
244 245
   if ((status < Connected) && !reply.route.empty()) {
245 246
       route = reply.route;
246 247
   }
... ...
@@ -250,7 +251,7 @@ void AmSipDialog::updateStatus(const AmSipReply& reply)
250 251
 
251 252
   switch(status){
252 253
   case Disconnecting:
253
-    if( t.method == "INVITE" ){
254
+    if (trans_method == SIP_METH_INVITE) {
254 255
 
255 256
       if(reply.code == 487){
256 257
 	// CANCEL accepted
... ...
@@ -270,7 +271,7 @@ void AmSipDialog::updateStatus(const AmSipReply& reply)
270 271
   case Disconnected:
271 272
     // only change status of dialog if reply 
272 273
     // to INVITE received
273
-    if(t.method == "INVITE"){ 
274
+    if (trans_method == SIP_METH_INVITE) { 
274 275
       if(reply.code < 200)
275 276
 	status = Pending;
276 277
       else if(reply.code >= 300)
... ...
@@ -289,7 +290,7 @@ void AmSipDialog::updateStatus(const AmSipReply& reply)
289 290
     // TODO: 
290 291
     // - place this somewhere else.
291 292
     //   (probably in AmSession...)
292
-    if((reply.code < 300) && (t.method == "INVITE")) {
293
+    if((reply.code < 300) && (trans_method == SIP_METH_INVITE)) {
293 294
 
294 295
 	if(hdl) {
295 296
 	    hdl->onInvite2xx(reply);
... ...
@@ -304,7 +305,7 @@ void AmSipDialog::updateStatus(const AmSipReply& reply)
304 305
   }
305 306
 
306 307
   if(hdl)
307
-      hdl->onSipReply(reply, old_dlg_status);
308
+    hdl->onSipReply(reply, old_dlg_status, trans_method);
308 309
 }
309 310
 
310 311
 void AmSipDialog::uasTimeout(AmSipTimeoutEvent* to_ev)
... ...
@@ -330,6 +331,19 @@ void AmSipDialog::uasTimeout(AmSipTimeoutEvent* to_ev)
330 331
   to_ev->processed = true;
331 332
 }
332 333
 
334
+bool AmSipDialog::getUACTransPending() {
335
+  return !uac_trans.empty();
336
+}
337
+
338
+bool AmSipDialog::getUACInvTransPending() {
339
+  for (TransMap::iterator it=uac_trans.begin();
340
+       it != uac_trans.end(); it++) {
341
+    if (it->second.method == "INVITE")
342
+      return true;
343
+  }
344
+  return false;
345
+}
346
+
333 347
 string AmSipDialog::getContactHdr()
334 348
 {
335 349
   if(contact_uri.empty()) {
... ...
@@ -74,7 +74,8 @@ class AmSipDialogEventHandler
74 74
   virtual void onSipRequest(const AmSipRequest& req)=0;
75 75
 
76 76
   /** Hook called when a reply has been received */
77
-  virtual void onSipReply(const AmSipReply& reply, int old_dlg_status)=0;
77
+  virtual void onSipReply(const AmSipReply& reply, int old_dlg_status,
78
+			  const string& trans_method)=0;
78 79
 
79 80
   /** Hook called before a request is sent */
80 81
   virtual void onSendRequest(const string& method,
... ...
@@ -160,7 +161,11 @@ class AmSipDialog
160 161
   AmSipDialog(AmSipDialogEventHandler* h=0);
161 162
   ~AmSipDialog();
162 163
 
163
-  bool   getUACTransPending() { return !uac_trans.empty(); }
164
+  /** @return whether UAC transaction is pending */
165
+  bool   getUACTransPending();
166
+
167
+  /** @return whether INVITE transaction is pending */
168
+  bool   getUACInvTransPending();
164 169
   int    getStatus() { return status; }
165 170
   void   setStatus(int new_status);
166 171
 
... ...
@@ -174,6 +179,7 @@ class AmSipDialog
174 179
 
175 180
   void uasTimeout(AmSipTimeoutEvent* to_ev);
176 181
     
182
+  /** @return 0 on success */
177 183
   int reply(const AmSipRequest& req,
178 184
 	    unsigned int  code, 
179 185
 	    const string& reason,
... ...
@@ -182,34 +188,51 @@ class AmSipDialog
182 188
 	    const string& hdrs = "",
183 189
 	    int flags = 0);
184 190
 
191
+  /** @return 0 on success */
185 192
   int sendRequest(const string& method, 
186 193
 		  const string& content_type = "",
187 194
 		  const string& body = "",
188 195
 		  const string& hdrs = "",
189 196
 		  int flags = 0);
190 197
 
198
+  /** @return 0 on success */
191 199
   int send_200_ack(const AmSipTransaction& t,
192 200
 		   const string& content_type = "",
193 201
 		   const string& body = "",
194 202
 		   const string& hdrs = "",
195 203
 		   int flags = 0);
196 204
     
205
+  /** @return 0 on success */
197 206
   int bye(const string& hdrs = "");
207
+
208
+  /** @return 0 on success */
198 209
   int cancel();
210
+
211
+  /** @return 0 on success */
199 212
   int prack(const string &cont_type, 
200 213
             const string &body, 
201 214
             const string &hdrs);
215
+
216
+  /** @return 0 on success */
202 217
   int update(const string &cont_type, 
203 218
             const string &body, 
204 219
             const string &hdrs);
220
+
221
+  /** @return 0 on success */
205 222
   int reinvite(const string& hdrs,  
206 223
 	       const string& content_type,
207 224
 	       const string& body);
225
+
226
+  /** @return 0 on success */
208 227
   int invite(const string& hdrs,  
209 228
 	     const string& content_type,
210 229
 	     const string& body);
230
+
231
+  /** @return 0 on success */
211 232
   int refer(const string& refer_to,
212 233
 	    int expires = -1);
234
+
235
+  /** @return 0 on success */
213 236
   int transfer(const string& target);
214 237
   int drop();
215 238
 
... ...
@@ -41,4 +41,8 @@
41 41
 #define SIP_EXT_100REL          "100rel"
42 42
 
43 43
 #define SIP_HDR_SESSION_EXPIRES_COMPACT "x"
44
+
45
+#define SIP_IS_200_CLASS(code)  ((code >= 200) && (code < 300))
46
+
47
+#define SIP_APPLICATION_SDP     "application/sdp"
44 48
 #endif /* __AMSIPHEADERS_H__ */
... ...
@@ -77,7 +77,8 @@ bool SessionTimer::onSipRequest(const AmSipRequest& req)
77 77
   return false;
78 78
 }
79 79
 
80
-bool SessionTimer::onSipReply(const AmSipReply& reply, int old_dlg_status)
80
+bool SessionTimer::onSipReply(const AmSipReply& reply, int old_dlg_status,
81
+			      const string& trans_method)
81 82
 {
82 83
   updateTimer(s,reply);
83 84
   return false;
... ...
@@ -327,6 +328,15 @@ void SessionTimer::setTimers(AmSession* s)
327 328
   }
328 329
 }
329 330
 
331
+void SessionTimer::retryRefreshTimer(AmSession* s) {
332
+  DBG("Retrying session refresh timer: T-2s, tag '%s' \n",
333
+      s->getLocalTag().c_str());
334
+
335
+  UserTimer::instance()->
336
+    setTimer(ID_SESSION_REFRESH_TIMER, 2, s->getLocalTag());
337
+}
338
+
339
+
330 340
 void SessionTimer::removeTimers(AmSession* s) 
331 341
 {
332 342
   UserTimer::instance()->
... ...
@@ -342,7 +352,9 @@ void SessionTimer::onTimeoutEvent(AmTimeoutEvent* timeout_ev)
342 352
   if (timer_id == ID_SESSION_REFRESH_TIMER) {
343 353
     if (session_refresher == refresh_local) {
344 354
       DBG("Session Timer: initiating session refresh\n");
345
-      s->refresh();
355
+      if (!s->refresh()) {
356
+	retryRefreshTimer(s);
357
+      }
346 358
     } else {
347 359
       DBG("need session refresh but remote session is refresher\n");
348 360
     }
... ...
@@ -120,6 +120,7 @@ class SessionTimer: public AmSessionEventHandler
120 120
   void updateTimer(AmSession* s,const AmSipReply& reply);
121 121
     
122 122
   void setTimers(AmSession* s);
123
+  void retryRefreshTimer(AmSession* s);
123 124
   void removeTimers(AmSession* s);
124 125
 
125 126
   string getReplyHeaders(const AmSipRequest& req);
... ...
@@ -140,7 +141,8 @@ class SessionTimer: public AmSessionEventHandler
140 141
   virtual bool process(AmEvent*);
141 142
 
142 143
   virtual bool onSipRequest(const AmSipRequest&);
143
-  virtual bool onSipReply(const AmSipReply&, int old_dlg_status);
144
+  virtual bool onSipReply(const AmSipReply&, int old_dlg_status,
145
+			  const string& trans_method);
144 146
 
145 147
   virtual bool onSendRequest(const string& method, 
146 148
 			     const string& content_type,
... ...
@@ -124,7 +124,7 @@ bool UACAuth::onSipRequest(const AmSipRequest& req)
124 124
   return false;
125 125
 }
126 126
 
127
-bool UACAuth::onSipReply(const AmSipReply& reply, int old_dlg_status)
127
+bool UACAuth::onSipReply(const AmSipReply& reply, int old_dlg_status, const string& trans_method)
128 128
 {
129 129
   bool processed = false;
130 130
   if (reply.code==407 || reply.code==401) {
... ...
@@ -138,7 +138,7 @@ class UACAuth : public AmSessionEventHandler
138 138
   virtual bool process(AmEvent*);
139 139
   virtual bool onSipEvent(AmSipEvent*);
140 140
   virtual bool onSipRequest(const AmSipRequest&);
141
-  virtual bool onSipReply(const AmSipReply&, int old_dlg_status);
141
+  virtual bool onSipReply(const AmSipReply&, int old_dlg_status, const string& trans_method);
142 142
 	
143 143
   virtual bool onSendRequest(const string& method, 
144 144
 			     const string& content_type,