]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
Fri Nov 21 04:52:55 CST 2008 Pekka Pessi <first.last@nokia.com>
authorMichael Jerris <mike@jerris.com>
Tue, 16 Dec 2008 21:20:22 +0000 (21:20 +0000)
committerMichael Jerris <mike@jerris.com>
Tue, 16 Dec 2008 21:20:22 +0000 (21:20 +0000)
  * nua: try to cope if a re-INVITE nor its ACK contain SDP

  Some SIP user-agents use INVITE without SDP offer to refresh session.
  By default, NUA sends an offer in 200 OK to such an INVITE and expects
  an answer back in ACK. Now nua tries to recover from such a Offer/Answer
  protocol error.

  Also, if NUTAG_REFRESH_WITHOUT_SDP(1) tag is used, and if the re-INVITE was
  received without SDP, no SDP offer is sent in 200 OK.

  Thanks for Anthony Minessale for reporting the problem.

git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@10829 d0543943-73ff-0310-b7d9-9358b9ac24b2

libs/sofia-sip/.update
libs/sofia-sip/libsofia-sip-ua/nua/check_session.c
libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.h
libs/sofia-sip/libsofia-sip-ua/nua/nua_params.c
libs/sofia-sip/libsofia-sip-ua/nua/nua_params.h
libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c
libs/sofia-sip/libsofia-sip-ua/nua/nua_tag.c
libs/sofia-sip/libsofia-sip-ua/nua/sofia-sip/nua_tag.h

index 8ed3851df30db8ab0bc66aaf505bc0869709e199..74a8383624615b5a2f9c7cc06a0bab509f4df336 100644 (file)
@@ -1 +1 @@
-Tue Dec 16 14:46:55 CST 2008
+Tue Dec 16 15:19:47 CST 2008
index bc0d91a6a7773430ec60b78f079e6b38cb31ba0f..632b50e5767c049bbdd7d6092f1e38782a922460 100644 (file)
@@ -1077,6 +1077,71 @@ START_TEST(call_2_3_2)
 }
 END_TEST
 
+START_TEST(call_2_3_3)
+{
+  nua_handle_t *nh;
+  struct message *response;
+
+  s2_case("2.3.3", "Handling re-INVITE without SDP gracefully",
+         "NUA receives INVITE, "
+         "re-INVITE without SDP (w/o NUTAG_REFRESH_WITHOUT_SDP(), "
+         "re-INVITE without SDP (using NUTAG_REFRESH_WITHOUT_SDP(), "
+         "sends BYE.");
+
+  nh = invite_to_nua(
+    TAG_END());
+
+  s2_request_to(dialog, SIP_METHOD_INVITE, NULL,
+               SIPTAG_USER_AGENT_STR("evil (evil) evil"),
+               TAG_END());
+  
+  nua_respond(nh, SIP_200_OK, TAG_END());
+
+  fail_unless(s2_check_callstate(nua_callstate_completed));
+
+  response = s2_wait_for_response(200, SIP_METHOD_INVITE);
+
+  fail_if(!response);
+  s2_update_dialog(dialog, response);
+  fail_if(!response->sip->sip_content_type);
+  s2_free_message(response);
+
+  fail_if(s2_request_to(dialog, SIP_METHOD_ACK, NULL, TAG_END()));
+
+  fail_unless(s2_check_event(nua_i_ack, 200));
+  fail_unless(s2_check_callstate(nua_callstate_ready));
+
+  s2_fast_forward(10);
+
+  nua_set_hparams(nh, NUTAG_REFRESH_WITHOUT_SDP(1), TAG_END());
+  fail_unless(s2_check_event(nua_r_set_params, 200));
+
+  s2_request_to(dialog, SIP_METHOD_INVITE, NULL,
+               SIPTAG_USER_AGENT_STR("evil (evil) evil"),
+               TAG_END());
+  
+  nua_respond(nh, SIP_200_OK, TAG_END());
+
+  fail_unless(s2_check_callstate(nua_callstate_completed));
+
+  response = s2_wait_for_response(200, SIP_METHOD_INVITE);
+
+  fail_if(!response);
+  s2_update_dialog(dialog, response);
+  fail_if(response->sip->sip_content_type);
+  s2_free_message(response);
+
+  fail_if(s2_request_to(dialog, SIP_METHOD_ACK, NULL, TAG_END()));
+
+  fail_unless(s2_check_event(nua_i_ack, 200));
+  fail_unless(s2_check_callstate(nua_callstate_ready));
+
+  bye_by_nua(nh, TAG_END());
+
+  nua_handle_destroy(nh);
+}
+END_TEST
+
 
 TCase *session_timer_tcase(void)
 {
@@ -1085,6 +1150,7 @@ TCase *session_timer_tcase(void)
   {
     tcase_add_test(tc, call_2_3_1);
     tcase_add_test(tc, call_2_3_2);
+    tcase_add_test(tc, call_2_3_3);
   }
   return tc;
 }
index e24cbeaf4adf2eb06ab22497e532c4529152d201..25dde0a5ffc46aabb55881f705d2896509fbcf9f 100644 (file)
@@ -116,6 +116,7 @@ struct nua_server_request {
 
   unsigned sr_offer_sent:2;    /**< We have offered SDP (reliably, if >1) */
   unsigned sr_answer_recv:1;   /**< We have received SDP answer */
+
   unsigned :0;
 
   char const *sr_sdp;          /**< SDP received from client */
index 1869793e439f18e37e79d182913380825746d72f..73bbc84cf4b4e18a9b38f725db704a02ba688c96 100644 (file)
@@ -301,6 +301,7 @@ int nua_stack_init_instance(nua_handle_t *nh, tagi_t const *tags)
  *   NUTAG_PROXY() (aka NTATAG_DEFAULT_PROXY()) \n
  *   NUTAG_REFER_EXPIRES() \n
  *   NUTAG_REFER_WITH_ID() \n
+ *   NUTAG_REFRESH_WITHOUT_SDP() \n
  *   NUTAG_REGISTRAR() \n
  *   NUTAG_RETRY_COUNT() \n
  *   NUTAG_SERVICE_ROUTE_ENABLE() \n
@@ -422,6 +423,7 @@ int nua_stack_init_instance(nua_handle_t *nh, tagi_t const *tags)
  *   NUTAG_PROXY() (aka NTATAG_DEFAULT_PROXY()) \n
  *   NUTAG_REFER_EXPIRES() \n
  *   NUTAG_REFER_WITH_ID() \n
+ *   NUTAG_REFRESH_WITHOUT_SDP() \n
  *   NUTAG_REGISTRAR() \n
  *   NUTAG_RETRY_COUNT() \n
  *   NUTAG_SERVICE_ROUTE_ENABLE() \n
@@ -770,6 +772,10 @@ static int nhp_set_tags(su_home_t *home,
     else if (tag == nutag_update_refresh) {
       NHP_SET(nhp, update_refresh, value != 0);
     }
+    /* NUTAG_REFRESH_WITHOUT_SDP(refresh_without_sdp) */
+    else if (tag == nutag_refresh_without_sdp) {
+      NHP_SET(nhp, refresh_without_sdp, value != 0);
+    }
     /* NUTAG_ENABLEMESSAGE(message_enable) */
     else if (tag == nutag_enablemessage) {
       NHP_SET(nhp, message_enable, value != 0);
@@ -1478,6 +1484,7 @@ int nua_stack_set_smime_params(nua_t *nua, tagi_t const *tags)
  *   NUTAG_PATH_ENABLE() \n
  *   NUTAG_REFER_EXPIRES() \n
  *   NUTAG_REFER_WITH_ID() \n
+ *   NUTAG_REFRESH_WITHOUT_SDP() \n
  *   NUTAG_REGISTRAR() \n
  *   NUTAG_RETRY_COUNT() \n
  *   NUTAG_SERVICE_ROUTE_ENABLE() \n
@@ -1639,6 +1646,7 @@ int nua_stack_get_params(nua_t *nua, nua_handle_t *nh, nua_event_t e,
      TIF(NUTAG_MIN_SE, min_se),
      TIFD(NUTAG_SESSION_REFRESHER, refresher),
      TIF(NUTAG_UPDATE_REFRESH, update_refresh),
+     TIF(NUTAG_REFRESH_WITHOUT_SDP, refresh_without_sdp),
 
      TIF(NUTAG_ENABLEMESSAGE, message_enable),
      TIF(NUTAG_ENABLEMESSENGER, win_messenger_enable),
index 0b0abe2162f1023122e10fe3f627ef0f51154f9c..bdd44807d94e5f74c7078e0f7544a0fbf92cb2a8 100644 (file)
@@ -82,6 +82,9 @@ struct nua_handle_preferences
   /** no (preference), local or remote */
   enum nua_session_refresher nhp_refresher; 
   unsigned         nhp_update_refresh:1; /**< Use UPDATE to refresh */
+
+  /**< Accept refreshes without SDP */
+  unsigned         nhp_refresh_without_sdp:1;
   
   /* Messaging preferences */
   unsigned                nhp_message_enable : 1;
@@ -164,6 +167,7 @@ struct nua_handle_preferences
     unsigned nhb_min_se:1;
     unsigned nhb_refresher:1; 
     unsigned nhb_update_refresh:1;
+    unsigned nhb_refresh_without_sdp:1;
     unsigned nhb_message_enable:1;
     unsigned nhb_win_messenger_enable:1;
     unsigned nhb_message_auto_respond:1;
@@ -179,9 +183,9 @@ struct nua_handle_preferences
     unsigned nhb_keepalive:1;
     unsigned nhb_keepalive_stream:1;
     unsigned nhb_registrar:1;
+    unsigned :0;               /* at most 32 bits before this point */
 
     unsigned nhb_allow:1;
-    unsigned :0;               /* at most 32 bits before this point */
     unsigned nhb_supported:1;
 
     unsigned nhb_allow_events:1;
index 3232a469778a33698f1c39f67229a57b9b9bc8b6..63c65eced81e16f244331d4a91b9120306aed0ac 100644 (file)
@@ -171,6 +171,9 @@ typedef struct nua_session_usage
 
   /* Offer-Answer status */
   char const     *ss_oa_recv, *ss_oa_sent;
+
+  /**< Version of user SDP from latest successful O/A */
+  unsigned ss_sdp_version;
 } nua_session_usage_t;
 
 static char const Offer[] = "offer", Answer[] = "answer";
@@ -964,11 +967,14 @@ static int nua_session_client_response(nua_client_request_t *cr,
       /* XXX */
       sdp = NULL;
     }
-    else if (soa_activate(nh->nh_soa, NULL) < 0)
+    else if (soa_activate(nh->nh_soa, NULL) < 0) {
       /* XXX - what about errors? */
       LOG3("error activating media after");
-    else
+    }
+    else {
+      ss->ss_sdp_version = soa_get_user_version(nh->nh_soa);
       LOG5("processed SDP");
+    }
   }
   else if (cr->cr_method != sip_method_invite) {
     /* If non-invite request did not have offer, ignore SDP in response */
@@ -1309,7 +1315,8 @@ int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags)
       ;
     else if (nh->nh_soa && soa_is_complete(nh->nh_soa)) {
       /* signal SOA that O/A round(s) is (are) complete */
-       soa_activate(nh->nh_soa, NULL);
+      if (soa_activate(nh->nh_soa, NULL) >= 0)
+       ss->ss_sdp_version = soa_get_user_version(nh->nh_soa);
     }
     else if (nh->nh_soa == NULL
             /* NUA does not necessarily know dirty details */
@@ -1741,7 +1748,8 @@ static int nua_prack_client_request(nua_client_request_t *cr,
     }
     else {
       answer_sent = 1;
-      soa_activate(nh->nh_soa, NULL);
+      if (soa_activate(nh->nh_soa, NULL) >= 0)
+       ss->ss_sdp_version = soa_get_user_version(nh->nh_soa);
     }
   }
   else if (nh->nh_soa == NULL) {
@@ -1967,6 +1975,8 @@ static int
   process_ack_or_cancel(nua_server_request_t *, nta_incoming_t *, 
                        sip_t const *),
   process_ack(nua_server_request_t *, nta_incoming_t *, sip_t const *),
+  process_ack_error(nua_server_request_t *sr, msg_t *ackmsg,
+                   int status, char const *phrase, char const *reason),
   process_cancel(nua_server_request_t *, nta_incoming_t *, sip_t const *),
   process_timeout(nua_server_request_t *, nta_incoming_t *),
   process_prack(nua_server_request_t *,
@@ -2207,7 +2217,7 @@ int nua_invite_server_respond(nua_server_request_t *sr, tagi_t const *tags)
   msg_t *msg = sr->sr_response.msg; 
   sip_t *sip = sr->sr_response.sip; 
 
-  int reliable = 0, offer = 0, answer = 0, early_answer = 0, extra = 0;
+  int reliable = 0, maybe_answer = 0, offer = 0, answer = 0, extra = 0;
 
   enter;
 
@@ -2217,8 +2227,11 @@ int nua_invite_server_respond(nua_server_request_t *sr, tagi_t const *tags)
     return nua_base_server_respond(sr, tags);
   }
 
-  if (nua_invite_server_is_100rel(sr, tags)) {
-    reliable = 1, early_answer = 1;
+  if (200 <= sr->sr_status && sr->sr_status < 300) {
+    reliable = 1, maybe_answer = 1;
+  }
+  else if (nua_invite_server_is_100rel(sr, tags)) {
+    reliable = 1, maybe_answer = 1;
   }
   else if (!nh->nh_soa || sr->sr_status >= 300) {
     if (sr->sr_neutral)
@@ -2234,10 +2247,10 @@ int nua_invite_server_respond(nua_server_request_t *sr, tagi_t const *tags)
            SOATAG_USER_SDP_STR_REF(user_sdp_str),
            TAG_END());
 
-    early_answer = user_sdp || user_sdp_str;
+    maybe_answer = user_sdp || user_sdp_str;
   }
   else {
-    early_answer = NH_PGET(nh, early_answer);
+    maybe_answer = NH_PGET(nh, early_answer);
   }
 
   if (!nh->nh_soa) {
@@ -2259,11 +2272,12 @@ int nua_invite_server_respond(nua_server_request_t *sr, tagi_t const *tags)
     tagi_t const *t = tl_find_last(tags, nutag_include_extra_sdp);
     extra = t && t->t_value;
   }
-  else if (sr->sr_offer_recv && !sr->sr_answer_sent && early_answer) {
+  else if (sr->sr_offer_recv && !sr->sr_answer_sent && maybe_answer) {
     /* Generate answer */ 
     if (soa_generate_answer(nh->nh_soa, NULL) >= 0 &&
        soa_activate(nh->nh_soa, NULL) >= 0) {
       answer = 1;      /* signal that O/A answer sent (answer to invite) */
+      /* ss_sdp_version is updated only after answer is sent reliably */
     }
     /* We have an error! */
     else if (sr->sr_status >= 200) {
@@ -2288,12 +2302,16 @@ int nua_invite_server_respond(nua_server_request_t *sr, tagi_t const *tags)
       /* 1xx - we don't have to send answer */
     }
   }
-  else if (sr->sr_offer_recv && sr->sr_answer_sent == 1 && early_answer) {
+  else if (sr->sr_offer_recv && sr->sr_answer_sent == 1 && maybe_answer) {
     /* The answer was sent unreliably, keep sending it */
     answer = 1;
   }
   else if (!sr->sr_offer_recv && !sr->sr_offer_sent && reliable) {
-    /* Generate offer */
+    if (200 <= sr->sr_status && nua_callstate_ready <= ss->ss_state &&
+       NH_PGET(nh, refresh_without_sdp))
+      /* This is a re-INVITE without SDP - do not try to send offer in 200 */;
+    else
+      /* Generate offer */
     if (soa_generate_offer(nh->nh_soa, 0, NULL) < 0)
       sr->sr_status = soa_error_as_sip_response(nh->nh_soa, &sr->sr_phrase);
     else
@@ -2307,6 +2325,9 @@ int nua_invite_server_respond(nua_server_request_t *sr, tagi_t const *tags)
       sr->sr_offer_sent = 1 + reliable, ss->ss_oa_sent = Offer;
     else if (answer)
       sr->sr_answer_sent = 1 + reliable, ss->ss_oa_sent = Answer;
+
+    if (answer && reliable)
+      ss->ss_sdp_version = soa_get_user_version(nh->nh_soa);
   }
 
   if (reliable && sr->sr_status < 200) {
@@ -2340,16 +2361,15 @@ static
 int nua_invite_server_is_100rel(nua_server_request_t *sr, tagi_t const *tags)
 {
   nua_handle_t *nh = sr->sr_owner;
-  sip_t const *sip = sr->sr_response.sip;
   sip_require_t *require = sr->sr_request.sip->sip_require;
   sip_supported_t *supported = sr->sr_request.sip->sip_supported;
 
   if (sr->sr_status >= 200)
-    return 1;
+    return 0;
   else if (sr->sr_status == 100)
     return 0;
 
-  if (sip_has_feature(sip->sip_require, "100rel"))
+  if (sip_has_feature(sr->sr_response.sip->sip_require, "100rel"))
     return 1;
 
   if (require == NULL && supported == NULL)
@@ -2473,7 +2493,7 @@ int process_ack_or_cancel(nua_server_request_t *sr,
  * 
  * @END_NUA_EVENT
  */
-
+static
 int process_ack(nua_server_request_t *sr,
                nta_incoming_t *irq,
                sip_t const *sip)
@@ -2489,7 +2509,6 @@ int process_ack(nua_server_request_t *sr,
   if (sr->sr_offer_sent && !sr->sr_answer_recv) {
     char const *sdp;
     size_t len;
-    int error;
 
     if (session_get_description(sip, &sdp, &len))
       recv = Answer;
@@ -2499,32 +2518,45 @@ int process_ack(nua_server_request_t *sr,
       ss->ss_oa_recv = recv;
     }
 
-    if (nh->nh_soa == NULL)
+    if (nh->nh_soa == NULL) 
       ;
-    else if (recv == NULL ||
-            soa_set_remote_sdp(nh->nh_soa, NULL, sdp, len) < 0 ||
-            soa_process_answer(nh->nh_soa, NULL) < 0 ||
-            soa_activate(nh->nh_soa, NULL) < 0) {
+    else if (recv == NULL ) {
+      if (ss->ss_state >= nua_callstate_ready &&
+         soa_get_user_version(nh->nh_soa) == ss->ss_sdp_version &&
+         soa_process_reject(nh->nh_soa, NULL) >= 0) {
+       url_t const *m;
+
+       /* The re-INVITE was a refresh and re-INVITEr ignored our offer */
+       ss->ss_oa_sent = NULL; 
+
+       if (sr->sr_request.sip->sip_contact)
+         m = sr->sr_request.sip->sip_contact->m_url;
+       else
+         m = sr->sr_request.sip->sip_from->a_url;
+
+       SU_DEBUG_3(("nua(%p): re-INVITEr ignored offer in our %u response "
+                   "(Contact: <" URL_PRINT_FORMAT  ">)\n", 
+                   (void *)nh, sr->sr_status, URL_PRINT_ARGS(m)));
+       if (sr->sr_request.sip->sip_user_agent)
+         SU_DEBUG_3(("nua(%p): re-INVITE: \"User-Agent: %s\"\n", (void *)nh,
+                     sr->sr_request.sip->sip_user_agent->g_string));
+      }      
+      else 
+       return process_ack_error(sr, msg, 488, "Offer-Answer error", 
+                                "SIP;cause=488;text=\"No answer to offer\"");
+    }
+    else if (soa_set_remote_sdp(nh->nh_soa, NULL, sdp, len) >= 0 &&
+            soa_process_answer(nh->nh_soa, NULL) >= 0 &&
+            soa_activate(nh->nh_soa, NULL) >= 0) {
+      ss->ss_sdp_version = soa_get_user_version(nh->nh_soa);      
+    }
+    else {
       int status; char const *phrase, *reason;
 
       status = soa_error_as_sip_response(nh->nh_soa, &phrase);
       reason = soa_error_as_sip_reason(nh->nh_soa);
 
-      nua_stack_event(nh->nh_nua, nh, msg,
-                     nua_i_ack, status, phrase, NULL);
-      nua_stack_event(nh->nh_nua, nh, NULL,
-                     nua_i_media_error, status, phrase, NULL);
-
-      ss->ss_reporting = 1;    /* We report terminated state here if BYE fails */
-      error = nua_client_create(nh, nua_r_bye, &nua_bye_client_methods, NULL);
-      ss->ss_reporting = 0;
-
-      signal_call_state_change(nh, ss, 488, "Offer-Answer Error",
-                              error
-                              ? nua_callstate_terminated
-                              : nua_callstate_terminating);
-
-      return 0;
+      return process_ack_error(sr, msg, status, phrase, reason);
     }
   }
 
@@ -2540,6 +2572,38 @@ int process_ack(nua_server_request_t *sr,
   return 0;
 }
 
+static int 
+process_ack_error(nua_server_request_t *sr,
+                 msg_t *ackmsg,
+                 int status,
+                 char const *phrase,
+                 char const *reason)
+{
+  nua_handle_t *nh = sr->sr_owner;
+  nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage);
+  int error;
+  
+  nua_stack_event(nh->nh_nua, nh, ackmsg,
+                 nua_i_ack, status, phrase, NULL);
+  nua_stack_event(nh->nh_nua, nh, NULL,
+                 nua_i_media_error, status, phrase, NULL);
+
+  if (reason) ss->ss_reason = reason;
+  ss->ss_reporting = 1;        
+  error = nua_client_create(nh, nua_r_bye, &nua_bye_client_methods, NULL);
+  ss->ss_reporting = 0;
+
+  signal_call_state_change(nh, ss,
+                          488, "Offer-Answer Error",
+                          /* We report terminated state if BYE failed */
+                          error
+                          ? nua_callstate_terminated
+                          : nua_callstate_terminating);
+
+  return 0;
+}
+
+
 /** @NUA_EVENT nua_i_cancel
  *
  * Incoming INVITE has been cancelled by the client.
@@ -2809,8 +2873,10 @@ int nua_prack_server_report(nua_server_request_t *sr, tagi_t const *tags)
     signal_call_state_change(nh, ss,
                             status, phrase, 
                             ss->ss_state);
-    if (nh->nh_soa)
+    if (nh->nh_soa) {
       soa_activate(nh->nh_soa, NULL);
+      ss->ss_sdp_version = soa_get_user_version(nh->nh_soa);      
+    }
   }
 
   if (status < 200 || 300 <= status)
@@ -3469,6 +3535,7 @@ int nua_update_server_respond(nua_server_request_t *sr, tagi_t const *tags)
     }
     else {
       sr->sr_answer_sent = 1, ss->ss_oa_sent = Answer;
+      ss->ss_sdp_version = soa_get_user_version(nh->nh_soa);      
     }
   }
 
index fa533045a00a8cd3d59942f5a4e76d8a07b48df7..b519d4307f9bf4d22ee6a627907d928fc93c3431 100644 (file)
  * UPDATE requests.
  * Settings:
  * - NUTAG_MIN_SE(), NUTAG_SESSION_REFRESHER(),
- *   NUTAG_SESSION_TIMER(), NUTAG_UPDATE_REFRESH()
+ *   NUTAG_SESSION_TIMER(), NUTAG_UPDATE_REFRESH(),
+ *   NUTAG_REFRESH_WITHOUT_SDP(),
  * - "timer" in NUTAG_SUPPORTED()/SIPTAG_SUPPORTED()
  * Specifications:
  * - @RFC4028
@@ -954,6 +955,11 @@ tag_typedef_t nutag_invite_timer = UINTTAG_TYPEDEF(invite_timer);
  * @SessionExpires header sends a re-INVITE requests (or an UPDATE
  * request if NUTAG_UPDATE_REFRESH(1) parameter tag has been set).
  *
+ * Some SIP user-agents use INVITE without SDP offer to refresh session.
+ * By default, NUA sends an offer in 200 OK to such an INVITE and expects
+ * an answer back in ACK. If NUTAG_REFRESH_WITHOUT_SDP(1) tag is used,
+ * no SDP offer is sent in 200 OK if re-INVITE was received without SDP.
+ *
  * @par When to Use NUTAG_SESSION_TIMER()?
  *
  * The session time extension is enabled ("timer" feature tag is included in
@@ -1106,7 +1112,7 @@ tag_typedef_t nutag_session_refresher = INTTAG_TYPEDEF(session_refresher);
  * Corresponding tag taking reference parameter is NUTAG_UPDATE_REFRESH_REF().
  *
  * @sa #nua_r_update, NUTAG_SESSION_TIMER(), NUTAG_MIN_SE_REF(),
- * NUTAG_UPDATE_REFRESH(), @RFC4028, @SessionExpires, @MinSE
+ * NUTAG_SESSION_REFRESHER(), @RFC4028, @SessionExpires, @MinSE
  */
 tag_typedef_t nutag_update_refresh = BOOLTAG_TYPEDEF(update_refresh);
 
@@ -1115,6 +1121,45 @@ tag_typedef_t nutag_update_refresh = BOOLTAG_TYPEDEF(update_refresh);
  */
 
 
+/**@def NUTAG_REFRESH_WITHOUT_SDP(x)
+ *
+ * Do not send offer in response if re-INVITE was received without SDP.
+ *
+ * Some SIP user-agents use INVITE without SDP offer to refresh session.
+ * By default, NUA sends an offer in 200 OK to such an INVITE and expects
+ * an answer back in ACK.
+ *
+ * If NUTAG_REFRESH_WITHOUT_SDP(1) tag is used, no SDP offer is sent in 200
+ * OK if re-INVITE was received without SDP.
+ *
+ * @par Used with
+ *    nua_handle(), nua_invite(), nua_update(), nua_respond() \n
+ *    nua_set_params() or nua_set_hparams() \n
+ *    nua_get_params() or nua_get_hparams()
+ *
+ * See nua_set_hparams() for a complete list of all the nua operations that
+ * accept this tag.
+ *
+ * @par Parameter type
+ *    int (boolean: nonzero is true, zero is false)
+ *
+ * @par Values
+ *    - 1 (true, do not try to send offer in response to re-INVITE)
+ *    - 0 (false, always use SDP offer-answer in re-INVITEs)
+ *
+ * Corresponding tag taking reference parameter is NUTAG_REFRESH_WITHOUT_SDP_REF().
+ *
+ * @sa #nua_r_update, NUTAG_SESSION_TIMER(), NUTAG_MIN_SE_REF(),
+ * NUTAG_SESSION_REFRESHER(), NUTAG_UPDATE_REFRESH(), @RFC4028,
+ * @SessionExpires, @MinSE
+ */
+tag_typedef_t nutag_refresh_without_sdp = BOOLTAG_TYPEDEF(refresh_without_sdp);
+
+/**@def NUTAG_REFRESH_WITHOUT_SDP_REF(x)
+ * Reference tag for NUTAG_REFRESH_WITHOUT_SDP_REF().
+ */
+
+
 /**@def NUTAG_REFER_EXPIRES()
  *
  * Default lifetime for implicit subscriptions created by REFER.
index b8d71c7ca8dee3c75f195707dfe0a333fd0daad0..fe39c329811b78d627baeca8a9747a3e6706dda9 100644 (file)
@@ -183,6 +183,12 @@ SOFIAPUBVAR tag_typedef_t nutag_update_refresh;
 #define NUTAG_UPDATE_REFRESH_REF(x) nutag_update_refresh_ref, tag_bool_vr((&(x)))
 SOFIAPUBVAR tag_typedef_t nutag_update_refresh_ref;
 
+#define NUTAG_REFRESH_WITHOUT_SDP(x) nutag_refresh_without_sdp, tag_bool_v((x))
+SOFIAPUBVAR tag_typedef_t nutag_refresh_without_sdp;
+#define NUTAG_REFRESH_WITHOUT_SDP_REF(x) \
+  nutag_refresh_without_sdp_ref, tag_bool_vr((&(x)))
+SOFIAPUBVAR tag_typedef_t nutag_refresh_without_sdp_ref;
+
 #define NUTAG_AUTOALERT(x)      nutag_autoalert, tag_bool_v(x)
 SOFIAPUBVAR tag_typedef_t nutag_autoalert;
 #define NUTAG_AUTOALERT_REF(x)  nutag_autoalert_ref, tag_bool_vr(&(x))