Sipper 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (326) hide show
  1. data/sipper/README.rb +26 -0
  2. data/sipper/b2bua_controller.rb +163 -0
  3. data/sipper/b2bua_session_mixin.rb +24 -0
  4. data/sipper/base_controller.rb +425 -0
  5. data/sipper/bin/common.rb +42 -0
  6. data/sipper/bin/generate.rb +70 -0
  7. data/sipper/bin/project.rb +44 -0
  8. data/sipper/bin/run.rb +85 -0
  9. data/sipper/bin/run_smoke.rb +8 -0
  10. data/sipper/config/log4r.xml +71 -0
  11. data/sipper/controller_class_loader.rb +29 -0
  12. data/sipper/controller_selector.rb +119 -0
  13. data/sipper/controllers/invite_controller.rb +30 -0
  14. data/sipper/controllers/order.yaml +3 -0
  15. data/sipper/custom_message.rb +4 -0
  16. data/sipper/detached_session.rb +11 -0
  17. data/sipper/docs/manual.txt +1621 -0
  18. data/sipper/generators/README +12 -0
  19. data/sipper/generators/gen_controller.rb +228 -0
  20. data/sipper/generators/gen_project.rb +45 -0
  21. data/sipper/generators/gen_test.rb +72 -0
  22. data/sipper/generators/project_template_dir/Rakefile +56 -0
  23. data/sipper/generators/project_template_dir/config/sipper.cfg +31 -0
  24. data/sipper/generators/project_template_dir/controllers/README.txt +2 -0
  25. data/sipper/generators/project_template_dir/dot_sipper.proj +2 -0
  26. data/sipper/generators/project_template_dir/logs/README.txt +2 -0
  27. data/sipper/generators/project_template_dir/tests/README.txt +2 -0
  28. data/sipper/lib/smc/statemap.rb +194 -0
  29. data/sipper/logs/dialog_info_store +0 -0
  30. data/sipper/logs/r.cmd +6 -0
  31. data/sipper/logs/r.sh +6 -0
  32. data/sipper/media/sipper_media_client.rb +268 -0
  33. data/sipper/media/sipper_media_event.rb +43 -0
  34. data/sipper/media/sipper_media_manager.rb +145 -0
  35. data/sipper/media/sipper_media_proxy.rb +60 -0
  36. data/sipper/media/sipper_offer_answer.rb +285 -0
  37. data/sipper/message.rb +512 -0
  38. data/sipper/modified_pattern_formatter.rb +119 -0
  39. data/sipper/proxy_controller.rb +143 -0
  40. data/sipper/registration.rb +52 -0
  41. data/sipper/request.rb +109 -0
  42. data/sipper/response.rb +123 -0
  43. data/sipper/ruby_ext/module.rb +27 -0
  44. data/sipper/ruby_ext/mutable_class.rb +17 -0
  45. data/sipper/ruby_ext/object.rb +38 -0
  46. data/sipper/ruby_ext/pqueue.rb +190 -0
  47. data/sipper/ruby_ext/snapshot.rb +201 -0
  48. data/sipper/ruby_ext/string.rb +18 -0
  49. data/sipper/ruby_ext/time.rb +9 -0
  50. data/sipper/run/run_sipper1.rb +28 -0
  51. data/sipper/run/run_sipper2.rb +56 -0
  52. data/sipper/sdp/sdp.rb +257 -0
  53. data/sipper/sdp/sdp_generator.rb +131 -0
  54. data/sipper/sdp/sdp_parser.rb +136 -0
  55. data/sipper/session.rb +1952 -0
  56. data/sipper/session_manager.rb +170 -0
  57. data/sipper/session_recorder.rb +190 -0
  58. data/sipper/session_state/DialogState.sm +54 -0
  59. data/sipper/session_state/DialogState_sm.rb +337 -0
  60. data/sipper/session_state/dialog_routes.rb +141 -0
  61. data/sipper/sip_headers/header.rb +632 -0
  62. data/sipper/sip_headers/sipuri.rb +352 -0
  63. data/sipper/sip_logger.rb +65 -0
  64. data/sipper/sip_message_router.rb +231 -0
  65. data/sipper/sip_test_driver_controller.rb +10 -0
  66. data/sipper/sipper.rb +329 -0
  67. data/sipper/sipper_assertions.rb +21 -0
  68. data/sipper/sipper_configurator.rb +376 -0
  69. data/sipper/sipper_http/sipper_http_request_dispatcher.rb +71 -0
  70. data/sipper/sipper_http/sipper_http_response.rb +25 -0
  71. data/sipper/stray_message_manager.rb +40 -0
  72. data/sipper/test_completion_signaling_helper.rb +77 -0
  73. data/sipper/transaction/Ict.sm +59 -0
  74. data/sipper/transaction/Ict_sm.rb +430 -0
  75. data/sipper/transaction/Ist.sm +74 -0
  76. data/sipper/transaction/Ist_sm.rb +460 -0
  77. data/sipper/transaction/Nict.sm +51 -0
  78. data/sipper/transaction/Nict_sm.rb +325 -0
  79. data/sipper/transaction/Nist.sm +59 -0
  80. data/sipper/transaction/Nist_sm.rb +356 -0
  81. data/sipper/transaction/invite_client_transaction.rb +274 -0
  82. data/sipper/transaction/invite_server_transaction.rb +319 -0
  83. data/sipper/transaction/non_invite_client_transaction.rb +230 -0
  84. data/sipper/transaction/non_invite_server_transaction.rb +263 -0
  85. data/sipper/transaction/state_machine_wrapper.rb +58 -0
  86. data/sipper/transaction/transaction.rb +212 -0
  87. data/sipper/transport/base_transport.rb +84 -0
  88. data/sipper/transport/rel_unrel.rb +19 -0
  89. data/sipper/transport/transport_and_route_resolver.rb +67 -0
  90. data/sipper/transport/udp_transport.rb +156 -0
  91. data/sipper/transport_manager.rb +33 -0
  92. data/sipper/udp_session.rb +17 -0
  93. data/sipper/util/command_element.rb +62 -0
  94. data/sipper/util/compact_converter.rb +50 -0
  95. data/sipper/util/counter.rb +26 -0
  96. data/sipper/util/digest/digest_authorizer.rb +204 -0
  97. data/sipper/util/expectation_parser.rb +164 -0
  98. data/sipper/util/locator.rb +31 -0
  99. data/sipper/util/message_fill.rb +58 -0
  100. data/sipper/util/persistence/ps_sipper_map.rb +63 -0
  101. data/sipper/util/persistence/sipper_map.rb +41 -0
  102. data/sipper/util/sipper_util.rb +305 -0
  103. data/sipper/util/timer/sip_timer_helper.rb +26 -0
  104. data/sipper/util/timer/timer_manager.rb +80 -0
  105. data/sipper/util/timer/timer_task.rb +56 -0
  106. data/sipper/util/validations.rb +44 -0
  107. data/sipper/version.rb +10 -0
  108. data/sipper_test/_test_media_uas.rb +79 -0
  109. data/sipper_test/base_test_case.rb +31 -0
  110. data/sipper_test/c_134.txt +7 -0
  111. data/sipper_test/driven_sip_test_case.rb +96 -0
  112. data/sipper_test/gold.txt +10 -0
  113. data/sipper_test/gold_res.txt +8 -0
  114. data/sipper_test/gold_sub.txt +9 -0
  115. data/sipper_test/hello_sipper.au +0 -0
  116. data/sipper_test/in_sipper.au +0 -0
  117. data/sipper_test/nonrr_proxy.rb +17 -0
  118. data/sipper_test/order_tests.yaml +4 -0
  119. data/sipper_test/rake_res.txt +399 -0
  120. data/sipper_test/rr_proxy.rb +17 -0
  121. data/sipper_test/run_test.cmd +94 -0
  122. data/sipper_test/sip_test_case.rb +104 -0
  123. data/sipper_test/test2xx_retransmission.rb +80 -0
  124. data/sipper_test/test2xx_retransmission_with_limit.rb +81 -0
  125. data/sipper_test/test2xx_retransmission_with_nist.rb +91 -0
  126. data/sipper_test/test2xx_retransmission_with_txns.rb +94 -0
  127. data/sipper_test/test_address_header.rb +66 -0
  128. data/sipper_test/test_b2bua1.rb +130 -0
  129. data/sipper_test/test_b2bua2.rb +120 -0
  130. data/sipper_test/test_b2bua3.rb +160 -0
  131. data/sipper_test/test_b2bua4.rb +130 -0
  132. data/sipper_test/test_base_controller.rb +110 -0
  133. data/sipper_test/test_base_transport.rb +37 -0
  134. data/sipper_test/test_cancel.rb +21 -0
  135. data/sipper_test/test_cancel_after2xx.rb +81 -0
  136. data/sipper_test/test_cancel_retransmission.rb +105 -0
  137. data/sipper_test/test_cancel_with481.rb +83 -0
  138. data/sipper_test/test_cancel_with487.rb +70 -0
  139. data/sipper_test/test_cancel_with_ist_without_nist.rb +99 -0
  140. data/sipper_test/test_cancel_with_nist.rb +84 -0
  141. data/sipper_test/test_cancel_without487.rb +77 -0
  142. data/sipper_test/test_command_element.rb +38 -0
  143. data/sipper_test/test_compact_converter.rb +33 -0
  144. data/sipper_test/test_controller_class_loader.rb +37 -0
  145. data/sipper_test/test_controller_selector.rb +53 -0
  146. data/sipper_test/test_controller_using_compact_headers.rb +74 -0
  147. data/sipper_test/test_controller_using_header_order.rb +85 -0
  148. data/sipper_test/test_controller_using_ict.rb +64 -0
  149. data/sipper_test/test_controller_using_ict_with_non_success.rb +72 -0
  150. data/sipper_test/test_controller_using_ict_with_tcbh.rb +24 -0
  151. data/sipper_test/test_controller_using_ict_with_tcbh_no_action.rb +24 -0
  152. data/sipper_test/test_controller_using_ist.rb +70 -0
  153. data/sipper_test/test_controller_using_ist_with_tcbh.rb +24 -0
  154. data/sipper_test/test_controller_using_nict.rb +115 -0
  155. data/sipper_test/test_controller_using_nict_with_tcbh.rb +24 -0
  156. data/sipper_test/test_controller_using_nist.rb +63 -0
  157. data/sipper_test/test_controller_using_pre_existing_rs0.rb +73 -0
  158. data/sipper_test/test_controller_using_pre_existing_rs1.rb +75 -0
  159. data/sipper_test/test_controller_using_pre_existing_rs2.rb +71 -0
  160. data/sipper_test/test_controller_using_route_set.rb +86 -0
  161. data/sipper_test/test_controller_with_sdp.rb +80 -0
  162. data/sipper_test/test_controllers/cancel/order.yaml +2 -0
  163. data/sipper_test/test_controllers/cancel/uac_cancel_controller.rb +35 -0
  164. data/sipper_test/test_controllers/cancel/uas_cancel_controller.rb +25 -0
  165. data/sipper_test/test_controllers/class_loading/ordered/first_ordered_controller.rb +5 -0
  166. data/sipper_test/test_controllers/class_loading/ordered/order.yaml +3 -0
  167. data/sipper_test/test_controllers/class_loading/ordered/recond_ordered_controller.rb +6 -0
  168. data/sipper_test/test_controllers/class_loading/ordered/second_ordered_controller.rb +6 -0
  169. data/sipper_test/test_controllers/class_loading/unordered/first_unordered_controller.rb +7 -0
  170. data/sipper_test/test_controllers/class_loading/unordered/second_unordered_controller.rb +6 -0
  171. data/sipper_test/test_controllers/ctrl_trhandler/lib/transport_filters/my_transport_handler.rb +22 -0
  172. data/sipper_test/test_controllers/ctrl_trhandler/uac_tr_handler_controller.rb +27 -0
  173. data/sipper_test/test_controllers/ctrl_trhandler/uas_tr_handler_controller.rb +21 -0
  174. data/sipper_test/test_controllers/ete/order.yaml +1 -0
  175. data/sipper_test/test_controllers/ete/uac_controller.rb +39 -0
  176. data/sipper_test/test_controllers/ete/uas_controller.rb +34 -0
  177. data/sipper_test/test_controllers/extensions/extension_uac_controller.rb +24 -0
  178. data/sipper_test/test_controllers/extensions/extension_uas_controller.rb +35 -0
  179. data/sipper_test/test_controllers/extensions/lib/sipper_extensions/my_from_extension.rb +16 -0
  180. data/sipper_test/test_controllers/ict_tcbh/lib/transaction_handlers/app_ict_handler.rb +23 -0
  181. data/sipper_test/test_controllers/ict_tcbh/uac_ict_tcbh_controller.rb +27 -0
  182. data/sipper_test/test_controllers/ict_tcbh/uac_ict_tcbh_no_action_controller.rb +28 -0
  183. data/sipper_test/test_controllers/ict_tcbh/uas_ict_tcbh_controller.rb +29 -0
  184. data/sipper_test/test_controllers/ict_tcbh/uas_ict_tcbh_no_action_controller.rb +31 -0
  185. data/sipper_test/test_controllers/ist_tcbh/lib/app_ist_handler.rb +13 -0
  186. data/sipper_test/test_controllers/ist_tcbh/uac_ist_tcbh_controller.rb +22 -0
  187. data/sipper_test/test_controllers/ist_tcbh/uas_ist_tcbh_controller.rb +31 -0
  188. data/sipper_test/test_controllers/multi_trhandlers/lib/transport_filters/in_order.yaml +2 -0
  189. data/sipper_test/test_controllers/multi_trhandlers/lib/transport_filters/my_transport_handler1.rb +21 -0
  190. data/sipper_test/test_controllers/multi_trhandlers/lib/transport_filters/my_transport_handler2.rb +21 -0
  191. data/sipper_test/test_controllers/multi_trhandlers/lib/transport_filters/out_order.yaml +2 -0
  192. data/sipper_test/test_controllers/multi_trhandlers/uac_multi_tr_handler_controller.rb +27 -0
  193. data/sipper_test/test_controllers/multi_trhandlers/uas_multi_tr_handler_controller.rb +29 -0
  194. data/sipper_test/test_controllers/multiple/lib/blank_test.rb +2 -0
  195. data/sipper_test/test_controllers/multiple/uac_info_controller.rb +21 -0
  196. data/sipper_test/test_controllers/multiple/uac_msg_controller.rb +20 -0
  197. data/sipper_test/test_controllers/multiple/uas_info_controller.rb +15 -0
  198. data/sipper_test/test_controllers/multiple/uas_msg_controller.rb +14 -0
  199. data/sipper_test/test_controllers/nict_tcbh/lib/transaction_handlers/app_nict_handler.rb +13 -0
  200. data/sipper_test/test_controllers/nict_tcbh/uac_nict_tcbh_controller.rb +26 -0
  201. data/sipper_test/test_controllers/nict_tcbh/uas_nict_tcbh_controller.rb +20 -0
  202. data/sipper_test/test_controllers/state_machine_based/lib/CreditControl.sm +43 -0
  203. data/sipper_test/test_controllers/state_machine_based/lib/CreditControl_sm.rb +194 -0
  204. data/sipper_test/test_controllers/state_machine_based/order.yaml +1 -0
  205. data/sipper_test/test_controllers/state_machine_based/uac_message_controller.rb +34 -0
  206. data/sipper_test/test_controllers/state_machine_based/uas_message_controller.rb +44 -0
  207. data/sipper_test/test_controllers/stray_message/lib/sipper_extensions/my_stray_handler.rb +9 -0
  208. data/sipper_test/test_controllers/stray_message/stray_uac_controller.rb +24 -0
  209. data/sipper_test/test_controllers/stray_message/stray_uas_controller.rb +31 -0
  210. data/sipper_test/test_controllers/string/order.yaml +1 -0
  211. data/sipper_test/test_controllers/string/uac_controller.rb +29 -0
  212. data/sipper_test/test_controllers/string/uas_controller.rb +22 -0
  213. data/sipper_test/test_controllers/test_controller.rb +24 -0
  214. data/sipper_test/test_detached_session1.rb +78 -0
  215. data/sipper_test/test_dialog_routes.rb +139 -0
  216. data/sipper_test/test_digest_challenge1.rb +89 -0
  217. data/sipper_test/test_digest_challenge2.rb +90 -0
  218. data/sipper_test/test_dynamic_parse.rb +249 -0
  219. data/sipper_test/test_empty_sdp.rb +89 -0
  220. data/sipper_test/test_ete.rb +37 -0
  221. data/sipper_test/test_expectation_parser.rb +76 -0
  222. data/sipper_test/test_extensions.rb +28 -0
  223. data/sipper_test/test_freeze.rb +81 -0
  224. data/sipper_test/test_generated.rb +90 -0
  225. data/sipper_test/test_header_parameters.rb +75 -0
  226. data/sipper_test/test_header_parse.rb +141 -0
  227. data/sipper_test/test_http_client1.rb +84 -0
  228. data/sipper_test/test_http_client2.rb +90 -0
  229. data/sipper_test/test_ict_with_timeout.rb +62 -0
  230. data/sipper_test/test_in_dialog_request.rb +61 -0
  231. data/sipper_test/test_inline_controller.rb +67 -0
  232. data/sipper_test/test_invite_client_transaction.rb +272 -0
  233. data/sipper_test/test_invite_replace.rb +147 -0
  234. data/sipper_test/test_invite_retransmission.rb +90 -0
  235. data/sipper_test/test_invite_server_transaction.rb +208 -0
  236. data/sipper_test/test_lower_cseq.rb +79 -0
  237. data/sipper_test/test_media.rb +52 -0
  238. data/sipper_test/test_message.rb +392 -0
  239. data/sipper_test/test_method_specific_response_handling.rb +81 -0
  240. data/sipper_test/test_multi_homed1.rb +127 -0
  241. data/sipper_test/test_multi_homed2.rb +109 -0
  242. data/sipper_test/test_multi_homed_duplicate.rb +87 -0
  243. data/sipper_test/test_multi_homed_with_detached.rb +88 -0
  244. data/sipper_test/test_multiple.rb +49 -0
  245. data/sipper_test/test_multiple_contacts.rb +89 -0
  246. data/sipper_test/test_non2xx_retransmission_with_ist.rb +87 -0
  247. data/sipper_test/test_non_invite_client_transaction.rb +210 -0
  248. data/sipper_test/test_non_invite_retransmission.rb +91 -0
  249. data/sipper_test/test_non_invite_server_transaction.rb +202 -0
  250. data/sipper_test/test_offer_answer_prack.rb +103 -0
  251. data/sipper_test/test_pickup.rb +258 -0
  252. data/sipper_test/test_prack.rb +105 -0
  253. data/sipper_test/test_prack_store_and_dispatch.rb +101 -0
  254. data/sipper_test/test_prack_timer.rb +105 -0
  255. data/sipper_test/test_ps_sipper_map.rb +56 -0
  256. data/sipper_test/test_re_invite_uas_ongoing_ict.rb +81 -0
  257. data/sipper_test/test_re_invite_uas_ongoing_ist.rb +84 -0
  258. data/sipper_test/test_re_invite_with_ongoing_ict.rb +101 -0
  259. data/sipper_test/test_re_invite_with_ongoing_ist.rb +89 -0
  260. data/sipper_test/test_recorder_swap.rb +157 -0
  261. data/sipper_test/test_refer.rb +121 -0
  262. data/sipper_test/test_registeration_clearing.rb +97 -0
  263. data/sipper_test/test_registrar.rb +109 -0
  264. data/sipper_test/test_registration_controller.rb +73 -0
  265. data/sipper_test/test_registration_timeout.rb +90 -0
  266. data/sipper_test/test_remote_controller.rb +39 -0
  267. data/sipper_test/test_remote_target_update_on2xx.rb +140 -0
  268. data/sipper_test/test_request.rb +106 -0
  269. data/sipper_test/test_response.rb +40 -0
  270. data/sipper_test/test_response_without_to_tag.rb +92 -0
  271. data/sipper_test/test_rport.rb +88 -0
  272. data/sipper_test/test_sdp.rb +91 -0
  273. data/sipper_test/test_sdp_parser.rb +145 -0
  274. data/sipper_test/test_session.rb +276 -0
  275. data/sipper_test/test_session_callback_handler.rb +68 -0
  276. data/sipper_test/test_session_lifetime.rb +66 -0
  277. data/sipper_test/test_session_manager.rb +141 -0
  278. data/sipper_test/test_session_recorder.rb +145 -0
  279. data/sipper_test/test_session_state_user_defined.rb +84 -0
  280. data/sipper_test/test_session_states.rb +91 -0
  281. data/sipper_test/test_simple_dialog_state.rb +86 -0
  282. data/sipper_test/test_simple_re_invite.rb +86 -0
  283. data/sipper_test/test_sip_uri.rb +234 -0
  284. data/sipper_test/test_sipper_util.rb +45 -0
  285. data/sipper_test/test_smc_controller.rb +17 -0
  286. data/sipper_test/test_smoke.rb +80 -0
  287. data/sipper_test/test_stray.rb +28 -0
  288. data/sipper_test/test_stray_inline.rb +89 -0
  289. data/sipper_test/test_stray_res_acked.rb +103 -0
  290. data/sipper_test/test_stray_respond.rb +104 -0
  291. data/sipper_test/test_stray_retry.rb +92 -0
  292. data/sipper_test/test_stray_retry_failure.rb +93 -0
  293. data/sipper_test/test_stray_retry_initial.rb +100 -0
  294. data/sipper_test/test_strict_router_with_angled.rb +84 -0
  295. data/sipper_test/test_string_record.rb +31 -0
  296. data/sipper_test/test_subscribe_notify.rb +141 -0
  297. data/sipper_test/test_subscribe_notify_client_timeout.rb +158 -0
  298. data/sipper_test/test_subscribe_notify_dialog.rb +146 -0
  299. data/sipper_test/test_subscribe_notify_expires.rb +155 -0
  300. data/sipper_test/test_subscribe_notify_multiple_subscription.rb +157 -0
  301. data/sipper_test/test_subscribe_notify_server_timeout.rb +144 -0
  302. data/sipper_test/test_subsequent_unsent.rb +88 -0
  303. data/sipper_test/test_target_refresh_proxy_detached.rb +128 -0
  304. data/sipper_test/test_target_refresh_proxy_udp.rb +130 -0
  305. data/sipper_test/test_target_refresh_rr_proxy_udp.rb +133 -0
  306. data/sipper_test/test_target_refresh_with_new_port.rb +158 -0
  307. data/sipper_test/test_target_refresh_with_proxy1.rb +113 -0
  308. data/sipper_test/test_target_refresh_with_rr_update_proxy.rb +145 -0
  309. data/sipper_test/test_target_refresh_with_update_proxy.rb +144 -0
  310. data/sipper_test/test_target_refresh_with_update_proxy2.rb +139 -0
  311. data/sipper_test/test_timer_manager.rb +71 -0
  312. data/sipper_test/test_timer_task.rb +60 -0
  313. data/sipper_test/test_transport_handler.rb +19 -0
  314. data/sipper_test/test_transport_multi_handler.rb +18 -0
  315. data/sipper_test/test_udp_transport.rb +119 -0
  316. data/sipper_test/test_unparsed.rb +70 -0
  317. data/sipper_test/test_validations.rb +69 -0
  318. data/sipper_test/test_with_rr_proxy.rb +111 -0
  319. data/sipper_test/testmediacontroller.rb +71 -0
  320. data/sipper_test/tracing.rb +23 -0
  321. data/sipper_test/transaction_test_helper.rb +176 -0
  322. data/sipper_test/transport_filters.rb +26 -0
  323. data/sipper_test/ts_sipper.rb +91 -0
  324. data/sipper_test/ts_smoke.rb +3 -0
  325. data/sipper_test/tt.cmd +20 -0
  326. metadata +455 -0
@@ -0,0 +1,136 @@
1
+
2
+ =begin
3
+
4
+ RFC 4566
5
+
6
+ Session description
7
+ v= (protocol version)
8
+ o= (originator and session identifier)
9
+ s= (session name)
10
+ i=* (session information)
11
+ u=* (URI of description)
12
+ e=* (email address)
13
+ p=* (phone number)
14
+ c=* (connection information -- not required if included in
15
+ all media)
16
+ b=* (zero or more bandwidth information lines)
17
+ One or more time descriptions ("t=" and "r=" lines; see below)
18
+ z=* (time zone adjustments)
19
+ k=* (encryption key)
20
+ a=* (zero or more session attribute lines)
21
+ Zero or more media descriptions
22
+
23
+ Time description
24
+ t= (time the session is active)
25
+ r=* (zero or more repeat times)
26
+
27
+ Media description, if present
28
+ m= (media name and transport address)
29
+ i=* (media title)
30
+ c=* (connection information -- optional if included at
31
+ session level)
32
+ b=* (zero or more bandwidth information lines)
33
+ k=* (encryption key)
34
+ a=* (zero or more media attribute lines)
35
+
36
+
37
+
38
+ Example -
39
+ v=0
40
+ o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5
41
+ s=SDP Seminar
42
+ i=A Seminar on the session description protocol
43
+ u=http://www.example.com/seminars/sdp.pdf
44
+ e=j.doe@example.com (Jane Doe)
45
+ c=IN IP4 224.2.17.12/127
46
+ t=2873397496 2873404696
47
+ a=recvonly
48
+ m=audio 49170 RTP/AVP 0
49
+ m=video 51372 RTP/AVP 99
50
+ a=rtpmap:99 h263-1998/90000
51
+
52
+ =end
53
+
54
+ require 'sdp/sdp'
55
+
56
+ module SDP
57
+
58
+ class SdpParser
59
+
60
+
61
+ def self._copy_default_from_session(session, h)
62
+ if session[:c]
63
+ h[:c] = session[:c] unless h[:c]
64
+ end
65
+
66
+ sessionStatus = nil
67
+
68
+ if session[:a]
69
+ session[:a].split("||").each do |val|
70
+ sessionStatus = "inactive" if val == "inactive"
71
+ sessionStatus = "sendrecv" if val == "sendrecv"
72
+ sessionStatus = "recvonly" if val == "recvonly"
73
+ sessionStatus = "sendonly" if val == "sendonly"
74
+ end
75
+ end
76
+
77
+ mediaStatus = nil
78
+
79
+ if h[:a]
80
+ h[:a].split("||").each do |val|
81
+ mediaStatus = "inactive" if val == "inactive"
82
+ mediaStatus = "sendrecv" if val == "sendrecv"
83
+ mediaStatus = "recvonly" if val == "recvonly"
84
+ mediaStatus = "sendonly" if val == "sendonly"
85
+ end
86
+ end
87
+
88
+ if (sessionStatus != nil) && (mediaStatus == nil)
89
+ unless h[:a]
90
+ h[:a] = sessionStatus
91
+ else
92
+ h[:a] << "||" << sessionStatus
93
+ end
94
+ end
95
+ end
96
+
97
+ def self.parse(arr, addDefault = false)
98
+ parsing_session = true
99
+ h = {}
100
+ sdp = SDP::Sdp.new
101
+ arr.each do |line|
102
+ line.strip!
103
+ next unless line =~ /=/
104
+ n, v = line.split("=", 2)
105
+ if n == "m"
106
+ if parsing_session
107
+ sdp.session_lines = h
108
+ h = {}
109
+ parsing_session = false
110
+ else
111
+ if addDefault
112
+ _copy_default_from_session(sdp.session_lines, h)
113
+ end
114
+ sdp.add_media_lines(h)
115
+ h = {}
116
+ end
117
+ end
118
+ if h[n.to_sym].nil?
119
+ h[n.to_sym] = v
120
+ else
121
+ h[n.to_sym] << "||" << v
122
+ end
123
+ end # each
124
+ if parsing_session
125
+ sdp.session_lines = h
126
+ else
127
+ if addDefault
128
+ _copy_default_from_session(sdp.session_lines, h)
129
+ end
130
+ sdp.add_media_lines(h)
131
+ end
132
+ return sdp
133
+ end
134
+ end # class
135
+
136
+ end
@@ -0,0 +1,1952 @@
1
+ require 'transport/udp_transport'
2
+ require 'transport/transport_and_route_resolver'
3
+ require 'util/message_fill'
4
+ require 'sip_logger'
5
+ require 'util/sipper_util'
6
+ require 'util/locator'
7
+ require 'util/validations'
8
+ require 'util/digest/digest_authorizer'
9
+ require 'sipper_configurator'
10
+ require 'response'
11
+ require 'request'
12
+ require 'session_manager'
13
+ require 'monitor'
14
+ require 'session_recorder'
15
+ require 'transaction/transaction'
16
+ require 'transaction/invite_client_transaction'
17
+ require 'transaction/invite_server_transaction'
18
+ require 'transaction/non_invite_client_transaction'
19
+ require 'transaction/non_invite_server_transaction'
20
+ require 'test_completion_signaling_helper'
21
+ require 'ostruct'
22
+ require 'rubygems'
23
+ gem 'facets', '= 1.8.54'
24
+ require 'facets/more/synchash'
25
+ require 'ruby_ext/snapshot'
26
+ require 'session_state/dialog_routes'
27
+ require 'b2bua_session_mixin'
28
+ require 'media/sipper_media_event'
29
+ require 'media/sipper_media_client'
30
+ require 'media/sipper_offer_answer'
31
+ require 'sdp/sdp'
32
+ require 'sdp/sdp_parser'
33
+ require 'sdp/sdp_generator'
34
+ require 'custom_message'
35
+ require 'registration'
36
+
37
+ class Session
38
+ include SipLogger
39
+ include SipperUtil
40
+ include SIP::Validations
41
+ include B2buaSessionMixin
42
+
43
+ attr_accessor :transport, :local_tag, :remote_tag, :prack_seq, :local_cseq, :remote_cseq,
44
+ :call_id, :our_contact, :max_fwd, :local_uri,
45
+ :remote_uri, :rip, :rp, :tp_flags, :imessage, :irequest, :iresponse, :imedia_event,
46
+ :session_map, :session_key, :half_dialog_key, :controller, :use_transactions, :use_ict,
47
+ :use_nict, :use_ist, :use_nist, :session_timer, :session_limit, :tmr_hash,
48
+ :use_2xx_retrans, :use_1xx_retrans, :t2xx_retrans_timers, :t1xx_retrans_timers,
49
+ :dialog_routes, :force_update_session_map, :reliable_1xx_status, :ihttp_response,
50
+ :subscriptionMap, :name, :offer_answer, :registrations
51
+
52
+ class SubscriptionData
53
+ attr_accessor :key, :timer, :source, :event, :event_id, :state, :method
54
+ end
55
+
56
+ snap_fields :@dialog_routes_snap, :@remote_tag, :@remote_uri, :@state
57
+
58
+ # todo check if there ever would be a case of receiving messages from
59
+ # different transports for the same session.
60
+ # Yes it is possible. UDP to TCP or TLS or back to UDP etc.
61
+ def initialize(*tipportrsl)
62
+ @local_tag = nil
63
+ @remote_tag = nil
64
+ #todo fix the local_tag and local_cseq story for R/R
65
+ @local_cseq = 0
66
+ @prack_seq = 10
67
+ @local_cseq_before_send = nil # this is set once when a request is created and reset on send
68
+ @remote_cseq = 0
69
+ @call_id = nil
70
+ @our_contact = nil
71
+ @max_fwd = 70
72
+ @local_uri = nil #"From" of out requests and To of out responses
73
+ @remote_uri = nil #"To" of out requests and From for out responses
74
+ @transport = tipportrsl[0]
75
+ @rip = tipportrsl[1]
76
+ @rp = tipportrsl[2]
77
+ @tp_flags = 0
78
+ @attr_hash = {}
79
+ @session_recorder = nil
80
+ @session_record = nil
81
+ @last_sent_request = nil
82
+ @last_sent_invite = nil
83
+ @pending_cancels = {} # keyed on txn key and value is cancel message
84
+ @cancellable_txns = []
85
+ @session_queue = []
86
+ @sq_lock = ["free"] #inuse or free, array because we want to keep the same object as we are mixing in a monitor
87
+ @sq_lock.extend(MonitorMixin)
88
+ # set the config for transaction usage
89
+ set_transaction_usage SipperConfigurator[:SessionTxnUsage]
90
+ @tmr_hash = {} # a hash of hash of transaction timer values for each type of Txns
91
+ SIP::Transaction::Transactions.each do |tname|
92
+ @tmr_hash[tname] = {}
93
+ end
94
+ @session_timer = SipperConfigurator[:SessionTimer]
95
+ @session_limit = SipperConfigurator[:SessionLimit]
96
+ @session_life_so_far = 0
97
+ @transactions = SyncHash.new # the total transcations of this session
98
+ @transaction_handlers = {} # the name of transaction handler classes, keyed with types.
99
+ @use_2xx_retrans = SipperConfigurator[:T2xxUsage] # boolean
100
+ @use_1xx_retrans = SipperConfigurator[:T1xxUsage] # boolean
101
+ @reliable_1xx_status = false
102
+ @t2xx_retrans_timers = {:Start=>SIP::Transaction::T1,
103
+ :Cap=>SIP::Transaction::T2,
104
+ :Limit=>(64*SIP::Transaction::T1)}
105
+ @t2xx_retrans_timers = @t2xx_retrans_timers.merge SipperConfigurator[:T2xxTimers] if SipperConfigurator[:T2xxTimers] # 3 timer settings for 2xx retransmission
106
+
107
+ @t1xx_retrans_timers = {:Start=>SIP::Transaction::T1,
108
+ :Limit=>(64*SIP::Transaction::T1)}
109
+ @t1xx_retrans_timers = @t1xx_retrans_timers.merge SipperConfigurator[:T1xxTimers] if SipperConfigurator[:T1xxTimers] # 2 timer settings for 1xx retransmission
110
+
111
+ @dialog_routes = DialogRoutes.new(tipportrsl[3])
112
+ @session_limit = tipportrsl[4] if tipportrsl[4]
113
+ @pending_response_queue = Queue.new
114
+ @subscriptionMap = {}
115
+ @state = ["initial"]
116
+ @user_defined_state = false
117
+ _schedule_timer_for_session(:session_limit, @session_limit)
118
+ @offer_answer = Media::SipperOfferAnswer.new(self)
119
+ end
120
+
121
+ def get_state_array
122
+ @state
123
+ end
124
+
125
+ def last_state
126
+ @state[-1]
127
+ end
128
+
129
+ def set_state(state)
130
+ @state = [state]
131
+ @user_defined_state = true
132
+ end
133
+
134
+ def remote_target
135
+ @dialog_routes.get_ruri_and_routes[0]
136
+ end
137
+
138
+ def remote_target=(rt)
139
+ @dialog_routes.remote_target = rt
140
+ end
141
+
142
+ def [](k)
143
+ @attr_hash[k]
144
+ end
145
+
146
+ def []=(k,v)
147
+ @attr_hash[k] = v
148
+ end
149
+
150
+
151
+ def _fixed_local_tag(ftag_from_msg=nil)
152
+ unless @local_tag
153
+ if ftag_from_msg && ftag_from_msg != "_PH_LCTG_"
154
+ @local_tag = ftag_from_msg
155
+ return @local_tag
156
+ end
157
+ unless @remote_tag
158
+ @local_tag = "2"
159
+ else
160
+ @local_tag = @remote_tag+"1"
161
+ end
162
+ end
163
+ @local_tag
164
+ end
165
+
166
+ def send(msg)
167
+ # todo: as performance improvement set only when needed.
168
+ unless msg.class < Message
169
+ err_msg = "Message is not a SIP message #{msg}"
170
+ log_and_raise err_msg, ArgumentError
171
+ end
172
+
173
+ # A final check to see if any route headers were pushed by the controller on the initial
174
+ # request and here we
175
+ # adjust the request uri and routes one last time only for initial requests.
176
+ if msg.class == Request && msg.initial
177
+ rrt = @dialog_routes.get_ruri_and_routes_for_pushed_routes(msg)
178
+ msg.uri = rrt[0] if rrt[0]
179
+ msg = _add_route_headers_if_present(msg, rrt)
180
+ end
181
+
182
+ _check_transport_and_destination(msg)
183
+ unless @transport && @rip && @rp
184
+ err_msg = "Cannot send message, as transport is not properly set #{msg}"
185
+ log_and_raise err_msg, RuntimeError
186
+ end
187
+ case msg
188
+ when Request
189
+ send_request(msg)
190
+ when Response
191
+ send_response(msg)
192
+ end
193
+ end
194
+
195
+
196
+ def send_request(msg)
197
+ if SipperConfigurator[:ProtocolCompliance] == 'strict'
198
+ msg.from.tag = '_PH_LCTG_' unless msg.from.tag
199
+ end
200
+ @local_uri, @remote_uri = begin
201
+ log_and_raise "Cannot send CANCEL as no response received so far" unless _check_cancel_state(msg) if (msg.method == "CANCEL" && SipperConfigurator[:ProtocolCompliance]=='strict')
202
+ logd("This is a request and is initial "+msg.initial.to_s)
203
+ if msg.initial
204
+ _increment_local_cseq
205
+ SipperUtil::MessageFill.fill(msg, :lcnts=>@local_cseq.to_s, :lctg=>_fixed_local_tag(msg.from.tag).to_s,
206
+ :lcntr=>@remote_cseq.to_s, :gcnt=>SipperUtil::Counter.instance.next.to_s, :rnd=>SipperUtil.trand,
207
+ :lip=>@transport.ip, :lp=>@transport.port.to_s,
208
+ :rip=>@rip, :rp=>@rp.to_s, :trans=>@transport.tid)
209
+
210
+ self.remote_target = msg.uri
211
+ end
212
+ if ((SipperConfigurator[:ProtocolCompliance]=='strict') &&
213
+ (@local_cseq_before_send) &&
214
+ (@local_cseq_before_send+1 != @local_cseq))
215
+ @local_cseq -= 1 # one step back
216
+ log_and_raise "The CSeq has increased by more than one, you may have created but not sent a request"
217
+ end
218
+ @max_fwd = msg.max_forwards.to_s.to_i
219
+ @last_sent_request = msg
220
+ if (msg.method == "INVITE")
221
+ @last_sent_invite = msg
222
+ end
223
+ [msg.from.to_s, msg.to.to_s]
224
+ end
225
+ @local_tag = msg.from.tag if msg.from.tag
226
+ @remote_tag = msg.to.tag if msg.to.tag
227
+
228
+
229
+ # todo this is where we will look for 1300 size and flip over the transports and modify Vias
230
+ # we will have to re-write our Via, so do a pop_via and a push_via and continue to send message
231
+ # in the transport layer we will once again look for filling up the values. Also keep the
232
+ # popped via such that if sending of message fails then we revert back to popped via.
233
+ # Same thing needs to be done for Contact and also Record-Route if proxy
234
+ #
235
+ if msg.method == "INVITE"
236
+ if self.use_ict
237
+ # check for existing transactions before sending re-invite
238
+ # 3261 14.1
239
+ # If there is an ongoing INVITE client transaction, the TU MUST wait until the
240
+ # transaction reaches the completed or terminated state before initiating the new INVITE.
241
+ # And
242
+ # If there is an ongoing INVITE server transaction, the TU MUST wait until the
243
+ # transaction reaches the confirmed or terminated state before initiating the new INVITE.
244
+ if !msg.initial && SipperConfigurator[:ProtocolCompliance]=='strict'
245
+ @transactions.values.each do |txn|
246
+ if txn.transaction_name == :Ict
247
+ unless ["IctMap.Completed", "IctMap.Terminated"].include? txn.state
248
+ rollback_to_unsent_state
249
+ log_and_raise "Another ICT #{txn} in progress, cannot send re-INVITE"
250
+ end
251
+ elsif txn.transaction_name == :Ist
252
+ unless ["IstMap.Confirmed", "IstMap.Terminated", "IstMap.Finished"].include? txn.state
253
+ rollback_to_unsent_state
254
+ log_and_raise "Another IST #{txn} in progress, cannot send re-INVITE"
255
+ end
256
+ end
257
+ end
258
+ end
259
+ branch = msg.via.branch
260
+ klass = @transaction_handlers[:Ict] || @transaction_handlers[:Base]
261
+ txn_handler = klass.new if klass
262
+ ict = SIP::Transaction::InviteClientTransaction.new(self, branch, txn_handler, transport, @tp_flags)
263
+ @tmr_hash[:Ict].each_pair {|k,v| ict.send("#{k}=".to_sym, v) } #check if sym required
264
+ msg.transaction = ict
265
+ @transactions[branch] = ict
266
+ end
267
+ elsif msg.method == "ACK"
268
+ # nothing special because ict txn is already terminated
269
+ else
270
+ if self.use_nict
271
+ branch = msg.via.branch
272
+ klass = @transaction_handlers[:Nict] || @transaction_handlers[:Base]
273
+ txn_handler = klass.new if klass
274
+ nict = SIP::Transaction::NonInviteClientTransaction.new(self, branch, txn_handler, transport, @tp_flags)
275
+ @tmr_hash[:Nict].each_pair {|k,v| nict.send("#{k}=".to_sym, v) } #check if sym required
276
+ msg.transaction = nict
277
+ if msg.method == "CANCEL"
278
+ ctx = @transactions[branch]
279
+ logd("Sending CANCEL found a Ctx #{ctx} for branch #{branch}")
280
+ if ctx
281
+ ctx.cancel_ctxn = nict # ict or even nict
282
+ else
283
+ @transactions[branch] = nict # in the unlikely event of no ctx
284
+ end
285
+ else
286
+ @transactions[branch] = nict
287
+ end
288
+ end
289
+ end
290
+ @local_cseq_before_send = nil unless msg.method == "ACK" || msg.method == "CANCEL"
291
+ @offer_answer.handle_outgoing_request(msg) if @offer_answer
292
+ _send_common(msg)
293
+ end
294
+
295
+
296
+ # 13.3.1.4
297
+ # Once the response has been constructed, it is passed to the INVITE server transaction.
298
+ # Note, however, that the INVITE server transaction will be destroyed as soon as it
299
+ # receives this final response and passes it to the transport. Therefore, it is
300
+ # necessary to periodically pass the response directly to the transport until the ACK arrives.
301
+ # The 2xx response is passed to the transport with an interval that starts at T1 seconds
302
+ # and doubles for each retransmission until it reaches T2 seconds (T1 and T2 are defined
303
+ # in Section 17). Response retransmissions cease when an ACK request for the response
304
+ # is received. This is independent of whatever transport protocols are used to send
305
+ # the response .
306
+ # Since 2xx is retransmitted end-to-end, there may be hops between UAS and UAC that are UDP.
307
+ # To ensure reliable delivery across these hops , the response is retransmitted
308
+ # periodically even if the transport at the UAS is reliable.
309
+ # If the server retransmits the 2xx response for 64*T1 seconds without receiving an ACK,
310
+ # the dialog is confirmed, but the session SHOULD be terminated. This is accomplished
311
+ # with a BYE, as described in Section 15.
312
+
313
+ def send_response(msg, check_txn=true)
314
+ if (@reliable_1xx_status == true && msg.get_request_method == "INVITE")
315
+ logd("Adding response message to pending queue as Reliable provision is in progress.")
316
+ @pending_response_queue << msg
317
+ @pending_response_queue << check_txn
318
+ return
319
+ end
320
+
321
+ @offer_answer.handle_outgoing_response(msg) if @offer_answer
322
+
323
+ @local_uri, @remote_uri = begin
324
+ [msg.to.to_s, msg.from.to_s]
325
+ end
326
+
327
+ @local_tag = msg.to.tag if msg.to.tag
328
+ @remote_tag = msg.from.tag if msg.from.tag
329
+
330
+ if check_txn
331
+ txn = @transactions[msg.via.branch] if msg.via
332
+ if txn
333
+ if msg.get_request_method != "CANCEL"
334
+ logd("Found transaction for branch #{msg.via.branch} giving it the response")
335
+ msg.transaction = txn
336
+ end
337
+ end
338
+ end
339
+
340
+
341
+ _send_common(msg)
342
+
343
+ if ((SipperUtil::SUCC_RANGE.include?msg.code) && (msg.get_request_method=="INVITE") && @use_2xx_retrans)
344
+ @ok_to_retrans_2xx = true
345
+ @two_xx = OpenStruct.new
346
+ @two_xx.response = msg
347
+ @two_xx.tp_flags = @tp_flags
348
+ rd = get_response_destination(msg)
349
+ @two_xx.rip = rd[1]
350
+ @two_xx.rp = rd[2]
351
+ @current_t2xx = @t2xx_retrans_timers[:Start]
352
+ _schedule_timer_for_session(:t2xx_timer, @current_t2xx)
353
+ _schedule_timer_for_session(:t2xx_limit_timer, @t2xx_retrans_timers[:Limit])
354
+ logd("Started the 2xx retransmission timer for #{self}")
355
+ end
356
+ if ((SipperUtil::RPROV_RANGE.include?msg.code) && (msg.get_request_method=="INVITE") && msg.rseq)
357
+ @reliable_1xx_status = true
358
+ @ok_to_retrans_1xx = true
359
+ @one_xx = OpenStruct.new
360
+ @one_xx.response = msg
361
+ @one_xx.tp_flags = @tp_flags
362
+ rd = get_response_destination(msg)
363
+ @one_xx.rip = rd[1]
364
+ @one_xx.rp = rd[2]
365
+ @current_t1xx = @t1xx_retrans_timers[:Start]
366
+ @active_1xx_timer.cancel if @active_1xx_timer
367
+
368
+ if @use_1xx_retrans
369
+ @active_1xx_timer = _schedule_timer_for_session(:t1xx_timer, @current_t1xx)
370
+ end
371
+ _schedule_timer_for_session(:t1xx_limit_timer, @t1xx_retrans_timers[:Limit])
372
+ logd("Started the 1xx retransmission timer for #{self}")
373
+ @last_sent_reliable_response = msg
374
+ end
375
+
376
+ if (@reliable_1xx_status == true && msg.get_request_method == "PRACK")
377
+ unless @active_1xx_timer && !@active_1xx_timer.canceled?
378
+ @reliable_1xx_status = false
379
+
380
+ if(@pending_response_queue.length >= 2)
381
+ msgval = @pending_response_queue.pop
382
+ chkval = @pending_response_queue.pop
383
+ send_response(msgval, chkval)
384
+ end
385
+ end
386
+ end
387
+ end
388
+
389
+ def _send_common(msg)
390
+ @call_id = msg.call_id.to_s unless @call_id
391
+ @our_contact = msg.contact.to_s if msg.contact
392
+ logd("In send_common @local_uri=#{@local_uri.to_s}, @remote_uri=#{@remote_uri.to_s}, @call_id=#{@call_id.to_s}")
393
+ raise StandardError if transport.nil?
394
+ # Now set the content length
395
+ msg.content_length = msg.content_len.to_s unless msg.respond_to?(:content_length) && msg.content_length && msg.content_length.respond_to?(:frozen_str) && msg.content_length.frozen_str
396
+ #logd("Sending msg #{msg}")
397
+ SessionManager.add_session self, ((msg.class == Response) && (SipperUtil::SUCC_RANGE.include?msg.code))
398
+ logd("Session map is #{@session_map}")
399
+ if @header_order_arr
400
+ msg.header_order = @header_order_arr
401
+ end
402
+ if @compact_header_arr
403
+ msg.compact_headers = @compact_header_arr
404
+ end
405
+ # now add a custom header to trace back message to session
406
+
407
+ msg.p_sipper_session = (self.name || self.to_s) if SipperConfigurator[:ShowSessionIdInMessages]
408
+ if msg.transaction
409
+ msg.transaction.txn_send msg
410
+ m_s = msg.transaction.msg_sent
411
+ else
412
+ if msg.is_request?
413
+ rd = get_request_destination()
414
+ elsif msg.is_response?
415
+ rd = get_response_destination(msg)
416
+ end
417
+ m_s = transport.send(msg, @tp_flags, rd[1], rd[2])
418
+ logd("Now record the outgoing message from session")
419
+ _do_record_sip("out", msg, m_s)
420
+ end
421
+ msg
422
+ end
423
+
424
+ #-- callbacks from Ict into TU (TU callbacks)
425
+ def transaction_transport_err(txn)
426
+ # todo simulate a 503 response
427
+ end
428
+
429
+ #++
430
+
431
+ # The timeout happens for IST on TimerH(completed state) and TimerI(confirmed state) expiry
432
+ # For ICT it happens for TimerB expiry when 408 is to be simulated
433
+ #
434
+ # NICT does not send 408 as per RFC 4320
435
+ # 4.2. Action 2
436
+ # A transaction-stateful SIP element MUST NOT send a response with
437
+ # Status-Code of 408 to a non-INVITE request. As a consequence, an
438
+ # element that cannot respond before the transaction expires will not
439
+ # send a final response at all.
440
+ # But this is a notification from the transaction to the TU and onwards to
441
+ # the controller.
442
+ def transaction_timeout(txn)
443
+ if (txn.transaction_name == :Ict || txn.transaction_name == :Nict)
444
+ req = txn.message
445
+ r = Response.create(408, "Transaction Timeout")
446
+ r.local = true
447
+ logd("In timeout, response now is #{r} and calling copy with request #{req}")
448
+ r.copy_from(req, :call_id, :cseq, :via, :to, :from) unless req.nil?
449
+ # note this thread is actually worker thread and not timer thread as
450
+ # we had queued this timer on the transport queue.
451
+ logd("Now sending 408 response locally for consumption to #{self}")
452
+ self.on_message(r)
453
+ end
454
+
455
+ end
456
+
457
+ def transaction_cleanup(txn)
458
+ @transactions.delete txn.branch_id
459
+ end
460
+
461
+ def transaction_wrong_state(txn)
462
+ # todo simulate a 503 response
463
+ end
464
+
465
+ # The rip and rp are set by looking at the dialog state, remote target
466
+ # route headers etc. However, if it cannot be ascertained from the
467
+ # message (like name instead of IP/port in RURI or Route) then we default
468
+ # to the rp and rip provided at the time of session creation.
469
+ def get_request_destination
470
+ [@transport, @rip, @rp]
471
+ end
472
+
473
+
474
+ # From RFC 3581
475
+ # When a server attempts to send a response, it examines the topmost
476
+ # Via header field value of that response. If the "sent-protocol"
477
+ # component indicates an unreliable unicast transport protocol, such as
478
+ # UDP, and there is no "maddr" parameter, but there is both a
479
+ # "received" parameter and an "rport" parameter, the response MUST be
480
+ # sent to the IP address listed in the "received" parameter, and the
481
+ # port in the "rport" parameter. The response MUST be sent from the
482
+ # same address and port that the corresponding request was received on.
483
+ # This effectively adds a new processing step between bullets two and
484
+ # three in Section 18.2.2 of SIP 3261
485
+ def get_response_destination(res)
486
+ if res.via
487
+ rip = res.via.received
488
+ if res.via.has_param?(:rport) && res.via.transport.downcase == "udp"
489
+ rp = res.via.rport
490
+ else
491
+ if x=res.via.sent_by_port
492
+ rp = x
493
+ else
494
+ rp = "5060" # not @rp here
495
+ end
496
+ end
497
+ else
498
+ rip = @rip
499
+ rp = @rp
500
+ end
501
+ [@transport, rip, rp]
502
+ end
503
+
504
+ #-- TU callbacks
505
+ #++
506
+
507
+ # Create an initial request, initial request is one which does not have any existing dialog
508
+ # state in sipper. Specifically the new initial request will not have a to tag.
509
+ def create_initial_request(method, uri, *rest)
510
+ log_and_raise "Cannot send initial request as some signaling has happened" unless initial_state?
511
+ self.remote_target = uri
512
+ rrt = @dialog_routes.get_ruri_and_routes
513
+ r = Request.create_initial(method, rrt[0], *rest)
514
+ r = _add_route_headers_if_present(r, rrt)
515
+ return r
516
+ end
517
+
518
+ def create_prack(response = @iresponse)
519
+ logd("Creating a prack request ")
520
+ _increment_local_cseq
521
+ h = {
522
+ :call_id => @call_id,
523
+ :from => @local_uri,
524
+ :to => @remote_uri,
525
+ :cseq => sprintf("%s PRACK", @local_cseq),
526
+ :via => sprintf("SIP/2.0/%s %s:%s;branch=z9hG4bK-%s-%s-%s",
527
+ @transport.tid, @transport.ip, @transport.port.to_s,
528
+ @local_cseq, @remote_cseq, SipperUtil::Counter.instance.next.to_s),
529
+ :contact => @our_contact,
530
+ :max_forwards => @max_fwd.to_s,
531
+ :rack => sprintf("%s %s", response.rseq, response.cseq)
532
+ }
533
+
534
+ rrt = @dialog_routes.get_ruri_and_routes
535
+ r = Request.create_subsequent("PRACK", rrt[0], h)
536
+ r = _add_route_headers_if_present(r, rrt)
537
+ return r
538
+ end
539
+
540
+ def create_and_send_prack(response = @iresponse)
541
+ req = create_prack(response)
542
+ send(req)
543
+ end
544
+
545
+ # Create a new subsequent request. A subsequent request is created either within a dialog or for
546
+ # cases when a request is to be sent after some request was originally sent as an example a
547
+ # CANCEL request. However for generating a CANCEL use helper methods for it like create_cancel or
548
+ # create_and_send_cancel_when_ready
549
+ def create_subsequent_request(method, increment_cseq=true)
550
+ logd("Creating a subsequent request for #{method} and increment_cseq flag is #{increment_cseq}")
551
+ log_and_raise "As call_id is not set, it is likely that no initial req was sent or recvd" unless @call_id
552
+ if increment_cseq
553
+ _increment_local_cseq
554
+ end
555
+ if (method == "ACK")
556
+ h = {
557
+ :call_id => @call_id,
558
+ :from => @local_uri,
559
+ :to => @remote_uri,
560
+ :cseq => sprintf("%s %s", SipperUtil.cseq_number(@last_sent_invite.cseq), "ACK"),
561
+ :via => sprintf("SIP/2.0/%s %s:%s;branch=z9hG4bK-%s-%s-%s-%s",
562
+ @transport.tid, @transport.ip, @transport.port.to_s,
563
+ @local_cseq, @remote_cseq, SipperUtil::Counter.instance.next.to_s,
564
+ SipperUtil.trand),
565
+ :contact => @our_contact,
566
+ :max_forwards => @max_fwd.to_s
567
+ }
568
+ else
569
+ h = {
570
+ :call_id => @call_id,
571
+ :from => @local_uri,
572
+ :to => @remote_uri,
573
+ :cseq => sprintf("%s %s", @local_cseq, method.upcase),
574
+ :via => sprintf("SIP/2.0/%s %s:%s;branch=z9hG4bK-%s-%s-%s-%s",
575
+ @transport.tid, @transport.ip, @transport.port.to_s,
576
+ @local_cseq, @remote_cseq, SipperUtil::Counter.instance.next.to_s,
577
+ SipperUtil.trand),
578
+ :contact => @our_contact,
579
+ :max_forwards => @max_fwd.to_s
580
+ }
581
+ end
582
+
583
+ rrt = @dialog_routes.get_ruri_and_routes
584
+ r = Request.create_subsequent(method, rrt[0], h)
585
+ r = _add_route_headers_if_present(r, rrt)
586
+ return r
587
+ end
588
+
589
+ # Challenge here is the header object of WWW-Authenticate or Proxy-Authenticate header.
590
+ def create_request_with_response_to_challenge(challenge, proxy_challenge, user, passwd, lsr = @last_sent_request)
591
+ new_req = lsr.dup
592
+ new_req.initial = false
593
+ _increment_local_cseq
594
+ new_req.cseq = sprintf("%s %s", @local_cseq, lsr.method.upcase)
595
+ new_req.via = sprintf("SIP/2.0/%s %s:%s;branch=z9hG4bK-%s-%s-%s",
596
+ @transport.tid, @transport.ip, @transport.port.to_s,
597
+ @local_cseq, @remote_cseq, SipperUtil::Counter.instance.next.to_s)
598
+ if proxy_challenge
599
+ new_req.proxy_authorization = (@da.nil? ? @da = SipperUtil::DigestAuthorizer.new : @da).create_authorization_header(challenge, proxy_challenge, user, passwd, lsr)
600
+ else
601
+ new_req.authorization = (@da.nil? ? @da = SipperUtil::DigestAuthorizer.new : @da).create_authorization_header(challenge, proxy_challenge, user, passwd, lsr)
602
+ end
603
+
604
+ new_req
605
+ end
606
+
607
+ def create_response(code, phrase="SELECT", req=irequest, reliability=false)
608
+ log_and_raise "There is no request, cannot create response" unless req
609
+ code = Integer(code) unless code.is_a? Numeric
610
+ if @local_uri
611
+ if code > 100
612
+ @local_uri << ";tag=" << _fixed_local_tag.to_s unless @local_uri =~ /;tag=/
613
+ end
614
+ end
615
+ logd("@local_uri is now #{@local_uri.to_s}")
616
+ r = Response.create(code, phrase)
617
+ logd("Response now is #{r} and calling copy with request #{req}")
618
+ r.copy_from(req, :call_id, :cseq, :via, :to, :from, :record_route) unless req.nil?
619
+ r.to = @local_uri if @local_uri
620
+ r.contact = (@our_contact ||= sprintf("<sip:%s:%s;transport=%s>",
621
+ @transport.ip, @transport.port.to_s, @transport.tid)) unless code == 100
622
+
623
+ if ((req.method == "SUBSCRIBE") && (code >= 200) && (code < 300))
624
+ r.copy_from(req, :expires) unless req.nil?
625
+ end
626
+
627
+ if (reliability && code > 100 && code < 200)
628
+ r.require = "100rel"
629
+ r.rseq = sprintf("%d", @prack_seq)
630
+ @prack_seq = @prack_seq + 1
631
+ end
632
+
633
+ if req.method == "REGISTER" && @registrations
634
+ r.contact = nil
635
+ @registrations.each do |data|
636
+ r.add_contact(data.contact_uri)
637
+ r.contacts[-1].expires = data.expires
638
+ end
639
+ r.format_as_separate_headers_for_mv(:contact)
640
+ end
641
+ #logd("Response now is #{r}")
642
+ return r
643
+ end
644
+
645
+ # A simple operation to create and send the response to the request received
646
+ # todo automatically look up reason phrases
647
+ def respond_with(code, req=irequest, phrase=nil)
648
+ send(create_response(code, phrase, req))
649
+ end
650
+
651
+
652
+ def respond_reliably_with(code, req=irequest, phrase=nil)
653
+ raise ArgumentError, "Can only be used for provisional responses" if code < 101 || code > 200
654
+ send_response(create_response(code, phrase, req, true))
655
+ end
656
+
657
+ # A simple operation to create and send a request, initial or subsequent as the case
658
+ # may be.
659
+ def request_with(*args)
660
+ method = args[0].downcase
661
+ if(method == "cancel")
662
+ return create_and_send_cancel_when_ready
663
+ end
664
+
665
+ if(method == "ack")
666
+ return create_and_send_ack
667
+ end
668
+
669
+ if (initial_state?)
670
+ send(create_initial_request(*args))
671
+ else
672
+ send(create_subsequent_request(*args))
673
+ end
674
+ end
675
+
676
+
677
+ def initial_state?
678
+ @local_cseq+@remote_cseq == 0
679
+ end
680
+
681
+ # todo check for strict / lax operation waiting for prov. creating only for the INVITE
682
+ # request etc.
683
+ def create_cancel(lsr = @last_sent_invite)
684
+ log_and_raise "Cannot create CANCEL as no request was sent so far" unless lsr
685
+ rrt = @dialog_routes.get_ruri_and_routes
686
+ r = Request.create_subsequent("CANCEL", rrt[0], :cseq => sprintf("%s %s", SipperUtil.cseq_number(lsr.cseq), "CANCEL") )
687
+ r.copy_from(lsr, :call_id, :from, :to, :via, :contact, :max_forwards, :route)
688
+ r = _add_route_headers_if_present(r, rrt)
689
+ end
690
+
691
+ def create_and_send_cancel_when_ready(lsr = @last_sent_invite)
692
+ lsr = @last_sent_request unless lsr
693
+ c = create_cancel(lsr)
694
+ if _check_cancel_state(c)
695
+ send(c)
696
+ else
697
+ @pending_cancels[lsr.txn_id] = c
698
+ end
699
+ return c
700
+ end
701
+
702
+ # To be called by controller to generate an ACK for 2xx response.
703
+ #13.2.2.4
704
+ #The UAC core MUST generate an ACK request for each 2xx received from the transaction layer.
705
+ #The header fields of the ACK are constructed in the same way as for any request sent within
706
+ #a dialog (see Section 12) with the exception of the CSeq and the header fields related to authentication.
707
+ #The sequence number of the CSeq header field MUST be the same as the INVITE being acknowledged, but the CSeq
708
+ #method MUST be ACK. The ACK MUST contain the same credentials as the INVITE.
709
+ #If the 2xx contains an offer (based on the rules above), the ACK MUST
710
+ #carry an answer in its body. If the offer in the 2xx response is not acceptable, the
711
+ #UAC core MUST generate a valid answer in the ACK and then send a BYE immediately .
712
+ # Once the ACK has been constructed, the procedures of [4] are used to determine the destination address,
713
+ # port and transport. However, the request is passed to the transport layer directly for transmission,
714
+ # rather than a client transaction. This is because the UAC core handles retransmissions of the ACK,
715
+ # not the transaction layer. The ACK MUST be passed to the client transport every time a
716
+ # retransmission of the 2xx final response that triggered the ACK arrives.
717
+
718
+ def create_2xx_ack
719
+ #todo ACK must have the same credentials as INVITE.
720
+ ack = create_subsequent_request("ACK", false)
721
+ end
722
+
723
+
724
+ # Provided such that it can be used by controllers those are not using the transactions, otherwise this
725
+ # method is invoked from the ICT.
726
+ # 17.1.1.3
727
+ # The ACK request constructed by the client transaction MUST contain values for the Call-ID, From,
728
+ # and Request-URI that are equal to the values of those header fields in the request passed to the
729
+ # transport by the client transaction (call this the "original request" ). The To header field in the
730
+ # ACK MUST equal the To header field in the response being acknowledged, and therefore will usually
731
+ # differ from the To header field in the original request by the addition of the tag parameter.
732
+ # The ACK MUST contain a single Via header field, and this MUST be equal to the top Via header field
733
+ # of the original request. The CSeq header field in the ACK MUST contain the same value for the sequence
734
+ # number as was present in the original request, but the method parameter MUST be equal to "ACK".
735
+ # If the INVITE request whose response is being acknowledged had Route header fields, those header
736
+ # fields MUST appear in the ACK. This is to ensure that the ACK can be routed properly through
737
+ # any downstream stateless proxies.
738
+
739
+ def create_non_2xx_ack(invite=@last_sent_invite, response=@iresponse)
740
+ h = {
741
+ :to => response.to.to_s,
742
+ :cseq => sprintf("%s %s", SipperUtil.cseq_number(invite.cseq),"ACK")
743
+ }
744
+ ack = Request.create_subsequent("ACK", invite.uri, h)
745
+ ack.copy_from(invite, :call_id, :from, :via, :route, :contact, :max_forwards)
746
+ ack
747
+ end
748
+
749
+ # To be used only by the controllers on receipt of a response which will be used to figure out if
750
+ # the ACK is for 2xx or non-2xx. If it is required to generate an ACK to a response received previously
751
+ # (not the latest response)
752
+ # then create the ACK using the appropriate method (create_2xx_ack or create_non_2xx_ack) and then send
753
+ # it using session.send
754
+ #
755
+ def create_and_send_ack
756
+ send(create_ack)
757
+ end
758
+
759
+
760
+ def create_ack
761
+ if SipperUtil::SUCC_RANGE.include?@iresponse.code
762
+ a = create_2xx_ack
763
+ else
764
+ if use_ict
765
+ if SipperConfigurator[:ProtocolCompliance]=='strict'
766
+ log_and_raise "As InviteClientTransaction is in use, you MUST not send non-2xx ACK from here"
767
+ else
768
+ logw("As InviteClientTransaction is in use, you should not send non-2xx ACK from here")
769
+ end
770
+ end
771
+ a = create_non_2xx_ack
772
+ end
773
+ return a
774
+ end
775
+
776
+
777
+ # The media attributes set here shall be used in the next
778
+ # request or response sent from the controller.
779
+ # The argument is an attribute hash and can take the form
780
+ # set_media_attributes(:codec=>["G711U", "DTMF"] :type=>"SENDONLY", :play=>{:file=><file_name>})
781
+ # set_media_attributes can be called any number of times, as soon as
782
+ # ":codec" can be from of G711U, G711A or DTMF and will be in the form of an array and
783
+ # there may be more that one value for it. This can be added or removed at a later
784
+ # time with a new set_media_attributes() invocation. If a new codec is added
785
+ # then the earlier codec should still be listed in the array. Absense of codec
786
+ # from the list would mean that it is being withdrawn from the offers.
787
+ # ":type" can be one of SENDONLY or RECVONLY or SENDRECV
788
+ # ":play" can be hash of {file=> <file_to_play>, repeat=> true|false, duration=> <seconds>}
789
+ # ":play_spec" can be string "PLAY [file] [duration] | PLAY_REPEAT [file] [duration] | SLEEP [duration] | file"
790
+ # ":play_spec" can also be used to send out DTMF like "5,SLEEP 3,6,SLEEP 2,9"
791
+ # note if play_spec is present the play attribute will be ignored
792
+ # ":record_file" is the name of the file to record the incoming stream
793
+ # ":remote_m_line" the selected "m" line from the remote media stream. It can also be selected as
794
+ # the value "any" in which case the system selects the best option from the offer.
795
+ # ":remote_a_line" "a" line corresponding to the m line. This is an array, corresponding to
796
+ # the payloads.
797
+ # "remote_session_a_line" the "a" line (if any) from the session part of SDP.
798
+ # ":remote_c_line" the selected "c" line from session or media for the remote media stream
799
+ # This function can be called repeatedly, when you send out your offer and when to received
800
+ # an answer.
801
+ # As soon as media object has enough information the media is established. As an example you may have
802
+ # set attributes to set your prefered codec, type and what you want to play but do not have
803
+ # the remote information as you may have just sent out the offer. So initially you will set
804
+ # attributes with :codec, :type, :play. Then when you receive the 200 OK you may select a media line
805
+ # of your choice and call set_attributes with :remote_m_line and :remote_c_line.
806
+ # Alternatively you could have selected :remote_m_line as "any" upfront and on getting answer sipper
807
+ # could have selected the best answer from the given options.
808
+ def set_media_attributes(mattr)
809
+ play_spec = mattr[:play_spec]
810
+ dtmf_spec = mattr[:dtmf_spec]
811
+ rec_spec = mattr[:rec_spec]
812
+
813
+ play = mattr[:play]
814
+ unless play_spec
815
+ if play
816
+ play_spec = play[:repeat] ? "PLAY_REPEAT " : "PLAY "
817
+ play_spec << play[:file] if play[:file]
818
+ play_spec << " " << play[:duration] if play[:duration]
819
+ end
820
+ end
821
+ @offer_answer.setup_media_spec(play_spec, rec_spec, dtmf_spec) if @offer_answer
822
+ end
823
+
824
+ def update_dtmf_spec(mattr)
825
+ set_media_attributes(mattr)
826
+ end
827
+
828
+ def update_audio_spec(mattr)
829
+ set_media_attributes(mattr)
830
+ @offer_answer.refresh_sipper_media if @offer_answer
831
+ end
832
+
833
+
834
+ # todo write tests for on_message, on_request, on_response when doing IT
835
+ def on_message(r)
836
+ logd("session.on_message called for session #{self} and message #{r.short_to_s}")
837
+ @sq_lock.synchronize do
838
+ @session_queue << r
839
+ if @sq_lock[0] == "free"
840
+ @sq_lock[0] = "inuse"
841
+ else
842
+ return
843
+ end
844
+ end
845
+ msg = nil
846
+ loop do
847
+ logd("Looking for a new message from session level queue")
848
+ @sq_lock.synchronize do
849
+ msg = @session_queue.shift
850
+ unless msg
851
+ @sq_lock[0] = "free"
852
+ return
853
+ end
854
+ end
855
+ logd("In session.on_message now processing message #{msg}")
856
+ case msg
857
+ when Request
858
+ _on_request(msg)
859
+ when Response
860
+ _on_response(msg)
861
+ when SIP::TimerTask
862
+ _on_timer(msg) unless msg.canceled?
863
+ when Media::SipperMediaEvent
864
+ _on_media_event(msg)
865
+ when SipperHttp::SipperHttpResponse
866
+ _on_http_response(msg)
867
+ when CustomMessage
868
+ _on_custom_message(msg)
869
+ end
870
+ end
871
+ end
872
+
873
+ # invoked on incoming request
874
+ def _on_request(request)
875
+ _update_and_check_dialog_state_on_request(request)
876
+ @offer_answer.handle_incoming_request(request) if @offer_answer
877
+
878
+ # We forward the CANCEL to the transaction being canceled only after the
879
+ # NIST processing of the CANCEL is over. So typically a 200/CANCEL shall precede
880
+ # the 487/INVITE.
881
+ forward_cancel_to_stxn = false
882
+ nist = nil
883
+ stxn = nil
884
+
885
+ if request.method == "PRACK"
886
+ if request[:rack] && @last_sent_reliable_response && @last_sent_reliable_response[:rseq]
887
+ if request.rack.header_value.strip.split(' ')[0] == @last_sent_reliable_response.rseq.header_value.strip
888
+ @ok_to_retrans_1xx = false
889
+ @active_1xx_timer.cancel if @active_1xx_timer
890
+ else
891
+ logd("PRACK doesnt match the last sent reliable response.")
892
+ end
893
+ end
894
+ end
895
+
896
+ if request.method == "INVITE"
897
+ if self.use_ist
898
+ branch = request.via.branch
899
+ ist = @transactions[branch]
900
+ if ist
901
+ logd("Found transaction #{ist} for branch #{request.via.branch} for INVITE retransmission")
902
+ else
903
+ # one final check for equal CSeq since this is not a retransmission now and therefore
904
+ # must be a bad request.
905
+ _equal_cseq_check(request) unless request.attributes[:_sipper_rejection_response]
906
+ # check for existing invite transactions
907
+ _check_pending_invite_txns_on_invite_in(request)unless request.attributes[:_sipper_rejection_response]
908
+ logd("Not found transaction for branch #{request.via.branch} for INVITE, creating a new IST")
909
+ klass = @transaction_handlers[:Ist] || @transaction_handlers[:Base]
910
+ txn_handler = klass.new if klass
911
+ ist = SIP::Transaction::InviteServerTransaction.new(self, branch, txn_handler, transport, @tp_flags)
912
+ @tmr_hash[:Ist].each_pair {|k,v| ist.send("#{k}=".to_sym, v) }
913
+ @transactions[branch] = ist
914
+ end
915
+ request.transaction = ist
916
+ ist.txn_received(request)
917
+ unless ist.consume?
918
+ logd("Not consuming this request #{request.method} as txn has taken care of it.")
919
+ return
920
+ end
921
+ end # ist being used
922
+ elsif request.method == "ACK"
923
+ txn = @transactions[request.via.branch]
924
+ if txn
925
+ logd("Found transaction for branch #{request.via.branch} for ACK")
926
+ request.transaction = txn
927
+ txn.txn_received(request)
928
+ unless txn.consume?
929
+ logd("Not consuming this request #{request.method} as txn has taken care of it.")
930
+ return
931
+ end
932
+ else # no transaction found, this must be a ACK for 2xx as new branch
933
+ @ok_to_retrans_2xx = false
934
+ end
935
+ else # method other than INV/ACK
936
+ if self.use_nist
937
+ branch = request.via.branch
938
+ stxn = @transactions[branch]
939
+ if request.method == "CANCEL"
940
+ if stxn
941
+ nist = stxn.cancel_stxn
942
+ logd("Found CANCEL transaction #{nist} for branch #{request.via.branch} for CANCEL retransmission") if nist
943
+ end
944
+ else
945
+ nist = stxn
946
+ logd("Found NIST transaction #{nist} for branch #{request.via.branch} for non-INVITE retransmission") if nist
947
+ end
948
+ unless nist
949
+ # one final check for equal CSeq since this is not a retransmission now and therefore
950
+ # must be a bad request, unless it is a CANCEL.
951
+ unless request.method == "CANCEL"
952
+ _equal_cseq_check(request) unless request.attributes[:_sipper_rejection_response]
953
+ end
954
+ logd("Not found transaction for branch #{request.via.branch} for non-INVITE, creating a new NIST")
955
+ klass = @transaction_handlers[:Nist] || @transaction_handlers[:Base]
956
+ txn_handler = klass.new if klass
957
+ nist = SIP::Transaction::NonInviteServerTransaction.new(self, branch, txn_handler, transport, @tp_flags)
958
+ @tmr_hash[:Nist].each_pair {|k,v| nist.send("#{k}=".to_sym, v) }
959
+ end
960
+ request.transaction = nist
961
+ if request.method == "CANCEL"
962
+ logd("Received CANCEL found a Stx #{stxn} for branch #{branch}")
963
+ if stxn
964
+ nist.transaction_being_canceled = stxn if nist
965
+ forward_cancel_to_stxn = true
966
+ else
967
+ nist.ok_to_send_481_to_cancel = true if self.use_ist && self.use_nist
968
+ logd("Processing CANCEL, no stx found, check if 481 is to be sent")
969
+ end
970
+ else
971
+ @transactions[branch] = nist unless stxn
972
+ end
973
+ nist.txn_received(request)
974
+ unless nist.consume?
975
+ logd("Not consuming this request #{request.method} as txn has taken care of it.")
976
+ return
977
+ end
978
+ else # nist not being used, but still look for IST for CANCEL
979
+ if request.method == "CANCEL"
980
+ branch = request.via.branch
981
+ stxn = @transactions[branch] # should be IST
982
+ logd("Received CANCEL found a Stx #{stxn} for branch #{branch}")
983
+ if stxn
984
+ forward_cancel_to_stxn = true
985
+ else
986
+ logd("Processing CANCEL, no stx found, not using nist either")
987
+ end
988
+ end
989
+ end
990
+ end
991
+
992
+ # Check for the rejection response here
993
+ if request.attributes[:_sipper_rejection_response]
994
+ logi("Found the rejection response, sending it and not invoking controller")
995
+ send_response(request.attributes[:_sipper_rejection_response])
996
+ return
997
+ end
998
+
999
+ if request.method == "REGISTER"
1000
+ @registrations = _create_registration_object(request)
1001
+ end
1002
+
1003
+ if @controller
1004
+ logd("Dispatching request to controller #{@controller.name}")
1005
+ begin
1006
+ result = @controller.on_request(self)
1007
+ rescue Exception => e
1008
+ loge("Exception #{e} occured while request processing by controller")
1009
+ loge(e.backtrace.join("\n"))
1010
+ end
1011
+ logw("#{@controller} could not process the request") if result == false
1012
+ else
1013
+ loge("No controller associated with this session")
1014
+ end
1015
+ stxn.cancel_received(request, nist) if forward_cancel_to_stxn && stxn
1016
+ end
1017
+
1018
+
1019
+ def pre_set_dialog_id(r)
1020
+ @remote_tag = r.from_tag
1021
+ @call_id = r.call_id.to_s
1022
+ end
1023
+
1024
+ # invoked on incoming response
1025
+ def _on_response(response)
1026
+ if ((SipperConfigurator[:ProtocolCompliance]=='strict') && !(response.locally_generated?))
1027
+ # check 18.1.2
1028
+ # When a response is received, the client transport examines the top Via header field value.
1029
+ # If the value of the "sent-by" parameter in that header field value does not
1030
+ # correspond to a value that the client transport is configured to insert
1031
+ # into requests, the response MUST be silently discarded.
1032
+ if (transport.ip != response.via.sent_by_ip) || (transport.port.to_s != response.via.sent_by_port) && SipperConfigurator[:ProtocolCompliance] == 'strict'
1033
+ logw("Response via #{response.via} sent_by does not match our transport, dropping message")
1034
+ return
1035
+ end
1036
+ end
1037
+ _update_dialog_state_on_response(response)
1038
+ @offer_answer.handle_incoming_response(response) if @offer_answer
1039
+ txn = @transactions[response.via.branch]
1040
+ if txn && !(response.locally_generated?) && response.get_request_method == "CANCEL"
1041
+ txn = txn.cancel_ctxn || txn # for the case when NICT but not ICT in use
1042
+ end
1043
+ if (txn && !(response.locally_generated?))
1044
+ logd("Found transaction #{txn} for branch #{response.via.branch} giving it the response")
1045
+ response.transaction = txn
1046
+ txn.txn_received(response)
1047
+ unless txn.consume?
1048
+ logd("Not consuming this response #{response.code} as txn has taken care of it.")
1049
+ return
1050
+ end
1051
+ end
1052
+
1053
+ if response.get_request_method == "REGISTER"
1054
+ @registrations = _create_registration_object(response, false)
1055
+ end
1056
+
1057
+ if @controller
1058
+ begin
1059
+ @controller.on_response(self)
1060
+ rescue Exception => e
1061
+ loge("Exception #{e} occured while response processing by controller")
1062
+ loge(e.backtrace.join("\n"))
1063
+ end
1064
+ else
1065
+ loge("No controller associated with this session")
1066
+ end
1067
+ end
1068
+
1069
+ # On receipt of request or response
1070
+ def _on_common_sip(msg)
1071
+ if msg[:content_type] && msg.content_type.to_s =~ /sdp/
1072
+ msg.sdp = SDP::SdpParser.parse(msg.contents, true) if msg[:content]
1073
+ end
1074
+ @call_id = msg.call_id.to_s
1075
+ logd("Now record the incoming message")
1076
+ _do_record_sip("in", msg)
1077
+ end
1078
+
1079
+ #-------------------------------
1080
+ # Transcation handling setting
1081
+
1082
+ def use_ict
1083
+ return @use_ict unless @use_ict.nil?
1084
+ @use_transactions
1085
+ end
1086
+
1087
+ def use_nict
1088
+ return @use_nict unless @use_nict.nil?
1089
+ @use_transactions
1090
+ end
1091
+
1092
+ def use_ist
1093
+ return @use_ist unless @use_ist.nil?
1094
+ @use_transactions
1095
+ end
1096
+
1097
+ def use_nist
1098
+ return @use_nist unless @use_nist.nil?
1099
+ @use_transactions
1100
+ end
1101
+
1102
+ def set_transaction_usage(tr_hash)
1103
+ tr_hash.each_pair { |k,v| self.__send__((k.to_s+'=').to_sym, v) } if tr_hash
1104
+ end
1105
+
1106
+ # returns the consolidated hash of timers which can be set to the
1107
+ # transaction in question by calling SipperUtil.hash_to_iv(@timers, txn) if @timers
1108
+ # the type of transactions can be either the names of transactions as @transation_name
1109
+ # defined in the transaction or it can be :Base which is the override for all types of
1110
+ # transactions.
1111
+ def set_transaction_timers(type, tmr_hash)
1112
+ if tmr_hash
1113
+ if type == :Base
1114
+ @tmr_hash.each_key {|k| @tmr_hash[k]= tmr_hash }
1115
+ else
1116
+ @tmr_hash[type] = @tmr_hash[type].merge(tmr_hash)
1117
+ end
1118
+ end
1119
+ #@tmr_hash.each_key {|k| puts "#{k} = #{@tmr_hash[k]}" }
1120
+ end
1121
+
1122
+ # e.g. :Ict=>MyIctHandler, :Nict=>MyNictHandler, :Base=>CatchAllHandler
1123
+ def set_transaction_handlers(tx_hnd_hash)
1124
+ @transaction_handlers = tx_hnd_hash if tx_hnd_hash
1125
+ end
1126
+
1127
+ def set_session_timer(val)
1128
+ @session_timer = val if val
1129
+ end
1130
+
1131
+ def set_session_limit(val)
1132
+ @session_limit = val if val
1133
+ end
1134
+
1135
+
1136
+ def set_session_record(val)
1137
+ @session_record = val
1138
+ end
1139
+
1140
+ # boolean, whether the 2xx retransmission timer is to be used or not for
1141
+ # the UAS
1142
+ def set_t2xx_retrans_usage(val)
1143
+ @use_2xx_retrans = val
1144
+ end
1145
+
1146
+ def set_t1xx_retrans_usage(val)
1147
+ @use_1xx_retrans = val
1148
+ end
1149
+
1150
+ # the hash of 3 timer values that affect 2xx retransmissions for UAS
1151
+ # i.e {:Start=>100, :Cap=>400, :Limit=>1600} that roughly
1152
+ # correspond with T1, T2 and 64*T1 respectively, which are also the
1153
+ # defaults.
1154
+ def set_t2xx_retrans_timers(t_hash)
1155
+ if t_hash
1156
+ @t2xx_retrans_timers = @t2xx_retrans_timers.merge t_hash
1157
+ end
1158
+ end
1159
+
1160
+ def set_t1xx_retrans_timers(t_hash)
1161
+ if t_hash
1162
+ @t1xx_retrans_timers = @t1xx_retrans_timers.merge t_hash
1163
+ end
1164
+ end
1165
+
1166
+ # The partial or full list of headers in the order in which the message
1167
+ # is to be formatted. In the absense of this the header ordering is
1168
+ # arbitrary. e.g.
1169
+ # set_header_order([:from, :to, :call_id, :via])
1170
+ # shall format the headers in the order
1171
+ # ....
1172
+ # From: sut <sip:service@127.0.0.1:5060>;tag=azxs21
1173
+ # To: sipp <sip:sipp@127.0.0.1:6061>;tag=1
1174
+ # Call-Id: 6766_112@127.0.0.1
1175
+ # Via: SIP/2.0/UDP 127.0.0.1:6061;branch=z9hG4bK-2352-1-0
1176
+ # ....
1177
+ # once set this setting affects all the messages created by this session.
1178
+ def set_header_order(arr)
1179
+ @header_order_arr = arr
1180
+ end
1181
+ #------------------------------
1182
+
1183
+ # The headers which are required to be expressed in their compact form.
1184
+ # e.g.
1185
+ # set_compact_headers [:from, :via]
1186
+ # shall use the compact form for headers From and Via and the message shall
1187
+ # look like -
1188
+ # f: sut <sip:service@127.0.0.1:5060>;tag=azxs21
1189
+ # To: sipp <sip:sipp@127.0.0.1:6061>;tag=1
1190
+ # Call-Id: 6766_112@127.0.0.1
1191
+ # v: SIP/2.0/UDP 127.0.0.1:6061;branch=z9hG4bK-2352-1-0
1192
+ #
1193
+ # In case all possible headers are required in compact form then use
1194
+ # [:all_headers] as the argument value.
1195
+ # A complete list of headers with known compact form can be obtained
1196
+ # from - http://www.iana.org/assignments/sip-parameters
1197
+ def set_compact_headers(arr)
1198
+ @compact_header_arr = arr
1199
+ end
1200
+
1201
+ # public method called when the timer of this session target is fired
1202
+ def on_timer_expiration(task)
1203
+ # we treat the incoming timer task as a message to leverage
1204
+ # the session level queueing for synchronization.
1205
+ on_message(task)
1206
+ end
1207
+
1208
+ # this is internally called when the timer is acted upon
1209
+ def _on_timer(task)
1210
+ if @invalidated
1211
+ logi("This session #{self.session_key} is invalidated, not firing timer #{task}")
1212
+ return
1213
+ end
1214
+ logd("Timer task #{task} invoked")
1215
+ if task.type == :app
1216
+ begin
1217
+ @controller.on_timer(self, task) if @controller
1218
+ rescue Exception => e
1219
+ loge("Exception #{e} occured while timer processing by controller")
1220
+ loge(e.backtrace.join("\n"))
1221
+ end
1222
+ elsif task.type == :subscription
1223
+ begin
1224
+ subscription = task.tid
1225
+ if subscription.state != 'active'
1226
+ return
1227
+ end
1228
+ if task.equal?subscription.timer
1229
+ if subscription.source == 'uac'
1230
+ @controller.on_subscription_refresh_timeout(self, subscription)
1231
+ else
1232
+ @controller.on_subscription_timeout(self, subscription)
1233
+ end
1234
+ end
1235
+ end
1236
+ elsif task.type == :registration
1237
+ registration = task.tid
1238
+ begin
1239
+ @controller.on_registration_expiry(self, registration)
1240
+ end
1241
+ elsif task.type == :session
1242
+ if task.tid == :session_timer
1243
+ if @controller
1244
+ logd("Invoking session listener for #{@controller.name}")
1245
+ begin
1246
+ result = @controller.session_being_invalidated_ok_to_proceed?(self)
1247
+ rescue Exception => e
1248
+ loge("Exception #{e} occured while callback processing by controller")
1249
+ loge(e.backtrace.join("\n"))
1250
+ end
1251
+ if result
1252
+ logd("#{@controller} not interested in session invalidation")
1253
+ else
1254
+ logd("#{@controller} decided to increase the lifetime of session")
1255
+ if (@session_life_so_far+@session_timer < @session_limit)
1256
+ @invalidating = false # to force the timer to start
1257
+ self.invalidate
1258
+ return
1259
+ else
1260
+ logw("Not increasing the lifetime of session, as upper session limit reached")
1261
+ end
1262
+ end
1263
+ else
1264
+ logd("No controller interested in session invalidation")
1265
+ end
1266
+ self.invalidate(true)
1267
+ elsif task.tid == :t2xx_timer
1268
+ logd("2xx retransmission timer fired for #{self}")
1269
+ if @ok_to_retrans_2xx
1270
+ _do_record_sip("out", @two_xx.response, @two_xx.response.to_s)
1271
+ transport.send(@two_xx.response, @two_xx.tp_flags, @two_xx.rip, @two_xx.rp)
1272
+ logd("Retransmitted the 2xx response from #{self}")
1273
+ @current_t2xx = [@t2xx_retrans_timers[:Start]*2, @t2xx_retrans_timers[:Cap]].min
1274
+ _schedule_timer_for_session(:t2xx_timer, @current_t2xx)
1275
+ end # still ok to retrans
1276
+ elsif task.tid == :t2xx_limit_timer
1277
+ @ok_to_retrans_2xx = false
1278
+ if @controller
1279
+ logd("Invoking no_ack_received session listener for #{@controller.name}")
1280
+ begin
1281
+ result = @controller.no_ack_received(self)
1282
+ rescue Exception => e
1283
+ loge("Exception #{e} occured while callback processing by controller")
1284
+ loge(e.backtrace.join("\n"))
1285
+ end
1286
+ end # if controller
1287
+ elsif task.tid == :t1xx_timer
1288
+ if task.equal?@active_1xx_timer
1289
+ logd("1xx retransmission timer fired for #{self}")
1290
+ if @ok_to_retrans_1xx
1291
+ _do_record_sip("out", @one_xx.response, @one_xx.response.to_s)
1292
+ transport.send(@one_xx.response, @one_xx.tp_flags, @one_xx.rip, @one_xx.rp)
1293
+ logd("Retransmitted the 1xx response from #{self}")
1294
+ @current_t1xx = @current_t1xx*2
1295
+ @active_1xx_timer.cancel if @active_1xx_timer
1296
+ @active_1xx_timer = _schedule_timer_for_session(:t1xx_timer, @current_t1xx)
1297
+ end # still ok to retrans
1298
+ else
1299
+ logd("Ignoring invalid 1xx timer #{task}")
1300
+ end
1301
+ elsif task.tid == :t1xx_limit_timer
1302
+ @ok_to_retrans_1xx = false
1303
+ if @controller
1304
+ logd("Invoking no_prack_received session listener for #{@controller.name}")
1305
+ begin
1306
+ result = @controller.no_prack_received(self)
1307
+ rescue Exception => e
1308
+ loge("Exception #{e} occured while callback processing by controller")
1309
+ loge(e.backtrace.join("\n"))
1310
+ end
1311
+ end # if controller
1312
+ elsif task.tid == :session_limit
1313
+ logi("Upper limit of session time limit reached, now invalidating #{self}")
1314
+ self.invalidate(true)
1315
+ end #type of session timer
1316
+ end #session or app
1317
+ end
1318
+
1319
+
1320
+ def schedule_timer_for(tid, duration, &block)
1321
+ logd("Scheduling an app timer #{tid} for #{duration}")
1322
+ SIP::Locator[:Sth].schedule_for(self, tid, block, :app, duration)
1323
+ end
1324
+
1325
+ def _schedule_timer_for_session(tid, duration, &block)
1326
+ logd("Scheduling a session level timer #{tid} for #{duration}")
1327
+ SIP::Locator[:Sth].schedule_for(self, tid, block, :session, duration)
1328
+ end
1329
+
1330
+ def send_http_post_to(url, params)
1331
+ SIP::Locator[:HttpRequestDispatcher].request_post(url, self, params)
1332
+ end
1333
+
1334
+ def send_http_get_to(url)
1335
+ SIP::Locator[:HttpRequestDispatcher].request_get(url, self)
1336
+ end
1337
+
1338
+ # Invoked when the HTTP response is ready to be consumed
1339
+ def on_http_response(http_res)
1340
+ on_message(http_res)
1341
+ end
1342
+
1343
+ # Invoked when either the Sipper media response or an actual media event
1344
+ # is received.
1345
+ def on_media_event(media_evt)
1346
+ # we treat the incoming media event as a message to leverage
1347
+ # the session level queueing for synchronization.
1348
+ on_message(media_evt)
1349
+ end
1350
+
1351
+ def _on_media_event(media_event)
1352
+ @imedia_event = media_event
1353
+
1354
+ if @controller
1355
+ logd("Dispatching media event to controller #{@controller.name}")
1356
+ begin
1357
+ result = @controller.on_media_event(self)
1358
+ rescue Exception => e
1359
+ loge("Exception #{e} occured while media processing by controller")
1360
+ loge(e.backtrace.join("\n"))
1361
+ end
1362
+ logw("#{@controller} could not process the media event") if result == false
1363
+ else
1364
+ loge("No controller associated with this session")
1365
+ end
1366
+ end
1367
+
1368
+ def _on_http_response(http_res)
1369
+ @ihttp_response = http_res
1370
+ if @controller
1371
+ logd("Dispatching http response to controller #{@controller.name}")
1372
+ begin
1373
+ result = @controller.on_http_res(self)
1374
+ rescue Exception => e
1375
+ loge("Exception #{e} occured while http response processing by controller")
1376
+ loge(e.backtrace.join("\n"))
1377
+ end
1378
+ logw("#{@controller} could not process the http response") if result == false
1379
+ else
1380
+ loge("No controller associated with this session")
1381
+ end
1382
+ end
1383
+
1384
+ def _on_custom_message(custom_msg)
1385
+ if @controller
1386
+ logd("Dispatching custom message to controller #{@controller.name}")
1387
+ begin
1388
+ result = @controller.on_custom_msg(self, custom_msg)
1389
+ rescue Exception => e
1390
+ loge("Exception #{e} occured while processing custom message by controller")
1391
+ loge(e.backtrace.join("\n"))
1392
+ end
1393
+ logw("#{@controller} could not process the custom message") if result == false
1394
+ else
1395
+ loge("No controller associated with this session")
1396
+ end
1397
+ end
1398
+
1399
+ def post_custom_message(custom_msg)
1400
+ on_message(custom_msg)
1401
+ end
1402
+
1403
+
1404
+ def invalidate(force=false)
1405
+ if @invalidated
1406
+ logw("This session with key #{self.session_key} is already invalidated")
1407
+ return
1408
+ end
1409
+
1410
+ if force
1411
+ logd("Now invalidating the session #{self} with key #{self.session_key}")
1412
+ @offer_answer.close if @offer_answer
1413
+ @transactions.each_value do |t|
1414
+ logd("Invalidating the txn #{t} in session #{self}")
1415
+ t.invalidate
1416
+ end
1417
+ @session_recorder.save if @session_recorder
1418
+ SessionManager.remove_session self
1419
+ @invalidated = true
1420
+ SIP::TestCompletionSignalingHelper.signal_waiting_test(@signal_test_complete_when_invalidated) if @signal_test_complete_when_invalidated
1421
+ else
1422
+ if @invalidating
1423
+ logi("This session with key #{self.session_key} is already scheduled for invalidation")
1424
+ return
1425
+ end
1426
+ @invalidating = true
1427
+ tmr = _schedule_timer_for_session(:session_timer, @session_timer)
1428
+ @session_life_so_far += @session_timer
1429
+ logd("Now scheduling the session #{self.session_key} for invalidation after #{@session_timer} #{tmr}")
1430
+ end
1431
+ end
1432
+
1433
+
1434
+ def record_io=(rio)
1435
+ @rio = rio
1436
+ end
1437
+
1438
+ def do_record(msg)
1439
+ msg = SipperUtil.recordify(msg)
1440
+ _do_record_sip("neutral", msg)
1441
+ end
1442
+
1443
+ def transaction_record(direction, msg)
1444
+ _do_record_sip(direction, msg)
1445
+ end
1446
+
1447
+ def flow_completed_for(test_name)
1448
+ unless @invalidated
1449
+ logd("Session not invalidated yet, setting flag signaling completion")
1450
+ @signal_test_complete_when_invalidated = test_name.to_s
1451
+ return true
1452
+ end
1453
+ logd("In flow_completed_for() now siganling the waiting test")
1454
+ SIP::TestCompletionSignalingHelper.signal_waiting_test(test_name.to_s)
1455
+ end
1456
+
1457
+ # The first recording will create the recording file name using in or out
1458
+ # todo will it be enough for uniqueness?
1459
+ def _do_record_sip(direction, msg, msg_s=nil)
1460
+ logd("record() invoked for #{direction} and #{msg.call_id.to_s}")
1461
+ # maintain simple state in the session
1462
+ unless @user_defined_state
1463
+ if direction == "out"
1464
+ if msg.is_request?
1465
+ @state << ("sent_" + msg.method.downcase)
1466
+ elsif msg.is_response?
1467
+ @state << ("sent_" + msg.code.to_s)
1468
+ end
1469
+ elsif direction == "in"
1470
+ if msg.is_request?
1471
+ @state << ("received_" + msg.method.downcase)
1472
+ elsif msg.is_response?
1473
+ @state << ("received_" + msg.code.to_s)
1474
+ end
1475
+ end
1476
+ end
1477
+ unless @session_recorder
1478
+ @session_recorder = SessionRecorder.create_and_record(@rio, msg, msg_s, direction, @session_record)
1479
+ else
1480
+ @session_recorder.record(direction, msg, msg_s)
1481
+ end
1482
+ rescue RuntimeError => e
1483
+ loge("Unable to record the #{msg} as recorder is closed")
1484
+ end
1485
+
1486
+
1487
+ def _check_cancel_state(msg)
1488
+ @cancellable_txns.include? msg.txn_id
1489
+ end
1490
+
1491
+ def _check_for_pending_cancel
1492
+ txn_id = @iresponse.txn_id
1493
+ @cancellable_txns << txn_id
1494
+ if pc = @pending_cancels[txn_id]
1495
+ @pending_cancels[txn_id] = nil
1496
+ if SipperConfigurator[:ProtocolCompliance] == 'strict'
1497
+ return if @iresponse.code >= 200
1498
+ end
1499
+ send pc
1500
+ end
1501
+ end
1502
+
1503
+ def _increment_local_cseq
1504
+ @local_cseq_before_send ||= @local_cseq
1505
+ @local_cseq += 1
1506
+ logd "Local cseq before send set to #{@local_cseq_before_send} and @local_cseq is #{@local_cseq}"
1507
+ end
1508
+
1509
+ # Useful when you create a subsequent request but do not send it.
1510
+ # This resets the state to the time when the request was not created.
1511
+ def rollback_to_unsent_state
1512
+ @local_cseq = @local_cseq_before_send if @local_cseq_before_send
1513
+ @local_cseq_before_send = nil
1514
+ end
1515
+
1516
+ # 3261 Section 8.2
1517
+ # Note that request processing is atomic.
1518
+ # If a request is accepted, all state changes associated with it MUST be performed.
1519
+ # If it is rejected, all state changes MUST NOT be performed.
1520
+ #
1521
+ # This rollback is invoked automatically when Sipper rejects the request before it invokes
1522
+ # the controller. The controller can invoke it to rollback any dialog state if it is
1523
+ # going to reject the request or alternatively if the compliance flag is set to strict
1524
+ # this will automatically be called on request rejection.
1525
+ # Note that the snapshot is single-depth state capture, does not save any contained
1526
+ # object's state.
1527
+ def rollback_to_before_request_received_state(request)
1528
+ if request.session_state_snapshot
1529
+ self.restore_snapshot(request.session_state_snapshot)
1530
+ @dialog_routes.restore_snapshot(@dialog_routes_snap)
1531
+ @dialog_routes_snap = nil
1532
+ request.session_state_snapshot = nil
1533
+ logd("Restored to previous session state for request #{request.method}")
1534
+ else
1535
+ logd("No state to rollback.")
1536
+ end
1537
+ end
1538
+
1539
+ # creates a failure response and also rolls back the session state, this is called
1540
+ # when the request is rejected by Sipper itself.
1541
+ def rejection_response_with(code, request)
1542
+ logi("Rejecting the request #{request.method} with a #{code}")
1543
+ r = create_response(code, "SELECT", request)
1544
+ rollback_to_before_request_received_state(request)
1545
+ return r
1546
+ end
1547
+
1548
+ # Sometimes you create a new session but would like to continue the
1549
+ # recording process from previous session for validation purposes.
1550
+ # This would typically be used when you drop a leg and create a new
1551
+ # one.
1552
+ def continue_recording_from(old_session)
1553
+ @session_recorder = old_session._get_recorder
1554
+ old_session._remove_recorder
1555
+ end
1556
+
1557
+
1558
+ def _update_and_check_dialog_state_on_request(request)
1559
+ new_remote_cseq = SipperUtil.cseq_number(request.cseq)
1560
+ @dialog_routes_snap = @dialog_routes.take_snapshot
1561
+ request.session_state_snapshot = self.take_snapshot
1562
+ @dialog_routes.request_received(request)
1563
+ _on_common_sip(request)
1564
+ @imessage = @irequest = request
1565
+ @remote_tag = @irequest.from_tag unless @remote_tag
1566
+ @local_uri = @irequest.to.to_s
1567
+ @remote_uri = @irequest.from.to_s
1568
+
1569
+ # 3261 12.2.2 testing for < because a retransmission will have same sequence, as will be
1570
+ # ACK and CANCEL
1571
+ if SipperConfigurator[:ProtocolCompliance]=='strict' &&
1572
+ new_remote_cseq < @remote_cseq
1573
+ if @irequest.method == "ACK"
1574
+ logd("Dropping an ACK for an out of CSeq rejected request")
1575
+ else
1576
+ request.attributes[:_sipper_rejection_response] = rejection_response_with(500, @irequest)
1577
+ logd("Rejected the request #{@irequest.method} for out of CSeq")
1578
+ end
1579
+ return false
1580
+ end
1581
+ request.attributes[:_sipper_old_cseq] = @remote_cseq # save old for later check
1582
+ @remote_cseq = new_remote_cseq
1583
+
1584
+ if request.method == 'NOTIFY'
1585
+ if @session_map == :half
1586
+ if get_subscription(request) != nil
1587
+ logd("Moving the dialog to full on Subscription notify.")
1588
+ SessionManager.find_session(request.call_id, request.to_tag, request.from_tag, true)
1589
+ end
1590
+ end
1591
+ end
1592
+ return true
1593
+ end
1594
+
1595
+ def _update_dialog_state_on_response(response)
1596
+ @dialog_routes.response_received(response)
1597
+ _on_common_sip(response)
1598
+ @imessage = @iresponse = response
1599
+ _check_for_pending_cancel
1600
+ @remote_tag = response.to_tag
1601
+ @remote_uri = response.to.to_s
1602
+ end
1603
+
1604
+ # Reject the request if we have seen thsi CSeq before and there is no transaction
1605
+ # found for this request which rules out a retransmission.
1606
+ def _equal_cseq_check(request)
1607
+ if SipperConfigurator[:ProtocolCompliance] == 'strict' &&
1608
+ SipperUtil.cseq_number(request.cseq) == request.attributes[:_sipper_old_cseq]
1609
+ request.attributes[:_sipper_rejection_response] = rejection_response_with(500, @irequest)
1610
+ logd("Rejected the request #{request.method} for out of (equal) CSeq")
1611
+ false
1612
+ else
1613
+ true
1614
+ end
1615
+ end
1616
+
1617
+ # A UAS that receives a second INVITE before it sends the final response to
1618
+ # a first INVITE with a lower CSeq sequence number on the same dialog MUST
1619
+ # return a 500 (Server Internal Error) response to the second INVITE and
1620
+ # MUST include a Retry-After header field with a randomly chosen value
1621
+ # of between 0 and 10 seconds.
1622
+ #
1623
+ # A UAS that receives an INVITE on a dialog
1624
+ # while an INVITE it had sent on that dialog is in progress MUST return a
1625
+ # 491 (Request Pending) response to the received INVITE.
1626
+ def _check_pending_invite_txns_on_invite_in(request)
1627
+ @transactions.values.each do |txn|
1628
+ if txn.transaction_name == :Ict
1629
+ unless ["IctMap.Completed", "IctMap.Terminated"].include? txn.state
1630
+ request.attributes[:_sipper_rejection_response] = rejection_response_with(491, request)
1631
+ return false
1632
+ end
1633
+ elsif txn.transaction_name == :Ist
1634
+ unless ["IstMap.Confirmed", "IstMap.Terminated", "IstMap.Finished"].include? txn.state
1635
+ r = rejection_response_with(500, request)
1636
+ r.retry_after = rand(10).to_s
1637
+ request.attributes[:_sipper_rejection_response] = r
1638
+ return false
1639
+ end
1640
+ end
1641
+ end
1642
+ return true
1643
+ end
1644
+
1645
+
1646
+ def _add_route_headers_if_present(msg, rrt)
1647
+ unless rrt[1].empty?
1648
+ msg.route = rrt[1]
1649
+ msg.format_as_separate_headers_for_mv(:route)
1650
+ end
1651
+ msg.attributes[:_sipper_use_ruri_to_send] = rrt[2]
1652
+ msg
1653
+ end
1654
+
1655
+
1656
+ # populates the transport, rip and rp attributes in the session if they are not
1657
+ # set. This can happen if an unbound session is created.
1658
+ def _check_transport_and_destination(msg)
1659
+ if !(@transport && @rip && @rp) || @dialog_routes.target_refreshed?
1660
+ if (@controller && (cstp=@controller.specified_transport))
1661
+ msg.attributes[:_sipper_controller_specified_transport] = cstp
1662
+ end
1663
+ rv = Transport::TransportAndRouteResolver.ascertain_transport_and_destination(msg)
1664
+ @transport = rv[0]||@transport
1665
+ @rip = rv[1] || @rip
1666
+ @rp = rv[2] || @rp
1667
+ end
1668
+ end
1669
+
1670
+ def _get_sq_lock
1671
+ @sq_lock
1672
+ end
1673
+
1674
+ def _get_recorder
1675
+ @session_recorder
1676
+ end
1677
+
1678
+ def _remove_recorder
1679
+ @session_recorder = nil
1680
+ end
1681
+
1682
+ def create_subscription_from_request(request)
1683
+ # Creating subscription based on Subscribe/Refer request received.
1684
+ event = request.event.header_value if request["event".to_sym]
1685
+
1686
+ unless event
1687
+ if request.method == "REFER"
1688
+ event = "refer"
1689
+ else
1690
+ log_and_raise "Invalid subscription", ArgumentError
1691
+ end
1692
+ end
1693
+
1694
+ if event == "refer" && request.method != "REFER"
1695
+ log_and_raise "Only Refer method can create refer subscription", ArgumentError
1696
+ end
1697
+
1698
+ id_val = request.event['id'] if request["event".to_sym]
1699
+ unless id_val
1700
+ id_val = 0
1701
+ end
1702
+
1703
+ key = sprintf("|%s|%d", event, id_val)
1704
+ subscription = @subscriptionMap[key]
1705
+ unless subscription
1706
+ logd("Creating new uas subscription object.")
1707
+ subscription = SubscriptionData.new
1708
+ subscription.key = key
1709
+ subscription.timer = nil
1710
+ subscription.source = 'uas'
1711
+ subscription.event = event
1712
+ subscription.event_id = id_val
1713
+ subscription.state = 'active'
1714
+ subscription.method = request.method
1715
+
1716
+ if request.method == "SUBSCRIBE"
1717
+ if request.expires.header_value == '0'
1718
+ subscription.state = "terminated"
1719
+ end
1720
+ end
1721
+ @subscriptionMap[key] = subscription
1722
+ end
1723
+
1724
+ return @subscriptionMap[key]
1725
+ end
1726
+
1727
+ def create_subscription(event, id_val=0, method="SUBSCRIBE")
1728
+ # Creating new subscription.
1729
+ key = sprintf("|%s|%d", event, id_val)
1730
+ subscription = @subscriptionMap[key]
1731
+ unless subscription
1732
+ logd("Creating new uac subscription object.")
1733
+ subscription = SubscriptionData.new
1734
+ subscription.key = key
1735
+ subscription.timer = nil
1736
+ subscription.source = 'uac'
1737
+ subscription.event = event
1738
+ subscription.event_id = id_val
1739
+ subscription.state = 'active'
1740
+ subscription.method = method
1741
+ @subscriptionMap[key] = subscription
1742
+ end
1743
+
1744
+ return subscription
1745
+ end
1746
+
1747
+ def get_subscription(request)
1748
+ event = request.event.header_value if request["event".to_sym]
1749
+
1750
+ unless event
1751
+ if request.method == "REFER"
1752
+ event = "refer"
1753
+ else
1754
+ log_and_raise "Invalid subscription", ArgumentError
1755
+ end
1756
+ end
1757
+
1758
+ event_id = request.event['id'] if request["event".to_sym]
1759
+ unless event_id
1760
+ event_id = 0
1761
+ end
1762
+
1763
+ key = sprintf("|%s|%d", event, event_id)
1764
+ subscription = @subscriptionMap[key]
1765
+
1766
+ return subscription
1767
+ end
1768
+
1769
+ def update_subscription(request)
1770
+ subscription = get_subscription(request)
1771
+
1772
+ if request.method == "NOTIFY"
1773
+ subscription.state = request.subscription_state.header_value
1774
+
1775
+ if request.subscription_state["expires"] != nil
1776
+ if request.subscription_state["expires"] == '0'
1777
+ subscription.state = "terminated"
1778
+ end
1779
+ end
1780
+ end
1781
+
1782
+ if request.method == "SUBSCRIBE"
1783
+ if request.expires.header_value == '0'
1784
+ subscription.state = "terminated"
1785
+ end
1786
+ end
1787
+
1788
+ return subscription
1789
+ end
1790
+
1791
+ def remove_subscription(request)
1792
+ event = request.event.header_value if request["event".to_sym]
1793
+
1794
+ unless event
1795
+ if request.method == "REFER"
1796
+ event = "refer"
1797
+ else
1798
+ log_and_raise "Invalid subscription", ArgumentError
1799
+ end
1800
+ end
1801
+
1802
+ event_id = request.event.id if request["event".to_sym]
1803
+ unless event_id
1804
+ event_id = 0
1805
+ end
1806
+
1807
+ key = sprintf("|%s|%d", event, event_id)
1808
+ @subscriptionMap[key] = nil
1809
+ end
1810
+
1811
+ def remove_subscription(subscription)
1812
+ @subscriptionMap[subscription.key] = nil
1813
+ end
1814
+
1815
+ def add_subscription_to_request(request, subscription)
1816
+ logd(subscription.to_s)
1817
+
1818
+ if subscription.event != "refer" || request.method != "REFER" || subscription.event_id != 0
1819
+ request.event = subscription.event
1820
+ end
1821
+
1822
+ if subscription.event_id != 0
1823
+ request.event.id = subscription.event_id.to_s
1824
+ end
1825
+
1826
+ if request.method == "NOTIFY"
1827
+ request['subscription-state'] = subscription.state
1828
+ end
1829
+ end
1830
+
1831
+ def _schedule_timer_for_subscription(duration, subscription)
1832
+ logd("Scheduling a subscription timer for #{duration}")
1833
+ subscription.timer = SIP::Locator[:Sth].schedule_for(self, subscription, nil, :subscription, duration)
1834
+ end
1835
+
1836
+ def start_subscription_expiry_timer(subscription, response)
1837
+ logd('starting subscription expiry timer')
1838
+ if subscription.state != "active"
1839
+ logd("Not scheduling timer as state is not active")
1840
+ return
1841
+ end
1842
+
1843
+ expires = response.expires.header_value
1844
+ if expires == nil
1845
+ logd("Not scheduling timer as expires is empty")
1846
+ return
1847
+ end
1848
+
1849
+ expiresDuration = expires.to_i * 1000
1850
+
1851
+ logd("Scheduling timer for subscription")
1852
+ _schedule_timer_for_subscription(expiresDuration, subscription)
1853
+ end
1854
+
1855
+ def start_subscription_refresh_timer(subscription, response)
1856
+ if subscription.state != "active"
1857
+ logd("Not starting refresh Timer as state is #{subscription.state}");
1858
+ return
1859
+ end
1860
+
1861
+ expires = response.expires.header_value
1862
+ if expires == nil
1863
+ logd("Not starting refresh Timer as expires is nil")
1864
+ return
1865
+ end
1866
+
1867
+ expiresDuration = (expires.to_i - 10) * 1000
1868
+
1869
+ if (expiresDuration < 0)
1870
+ expiresDuration = expires.to_i * 500
1871
+ end
1872
+
1873
+ logd("Starting refresh Timer for #{expiresDuration}");
1874
+ _schedule_timer_for_subscription(expiresDuration, subscription)
1875
+ end
1876
+
1877
+ # Persists for REGISTRAR not REGISTRANT
1878
+ def _create_registration_object(message, persist=true)
1879
+ if message[:to]
1880
+ aor = message.to.header_value
1881
+ else
1882
+ log_and_raise "Invalid registration", ArgumentError
1883
+ end
1884
+
1885
+ key = sprintf("|%s", aor)
1886
+ reg_list = SIP::Locator[:RegistrationStore].get(key) if persist
1887
+ unless reg_list
1888
+ logd("Creating new registration object.")
1889
+ reg_list =[]
1890
+ if message[:contact]
1891
+ message.contacts.each do|cn|
1892
+ registration = Registration.add_registration_data(cn,message)
1893
+ reg_list.push(registration) if registration.expires.to_i != 0
1894
+ end
1895
+ end
1896
+ else
1897
+ logd("Updating registration object.")
1898
+ message.contacts.each do|cn|
1899
+ updated = Registration.update_registration_data(cn, reg_list, message)
1900
+ if not updated
1901
+ registration = Registration.add_registration_data(cn,message)
1902
+ reg_list.push(registration) if registration.expires.to_i != 0
1903
+ end
1904
+ end
1905
+ end
1906
+
1907
+ if message[:expires]
1908
+ if message.expires.header_value == '0' and message.contact.header_value == '*'
1909
+ reg_list.clear
1910
+ end
1911
+ end
1912
+ SIP::Locator[:RegistrationStore].put(key, reg_list) if persist
1913
+ return reg_list
1914
+ end
1915
+
1916
+ def _schedule_timer_for_registration(duration, registration)
1917
+ logd("Scheduling a registration refresh timer for #{duration}")
1918
+ SIP::Locator[:Sth].schedule_for(self, registration, nil, :registration, duration)
1919
+ end
1920
+
1921
+ def start_registration_expiry_timer
1922
+ expires = '99999'
1923
+ @registrations.each do | reg_data |
1924
+ expires = reg_data.expires if expires.to_i > reg_data.expires.to_i
1925
+ end
1926
+ if expires.to_i == 0
1927
+ logd("Not starting registration refresh Timer as expires is 0")
1928
+ return
1929
+ end
1930
+
1931
+ expiresDuration = (expires.to_i - 10)*1000
1932
+
1933
+ if (expiresDuration < 0)
1934
+ expiresDuration = expires.to_i * 500
1935
+ end
1936
+ logd("Starting registration expiry Timer for #{expiresDuration}");
1937
+ _schedule_timer_for_registration(expiresDuration, @registrations)
1938
+ end
1939
+
1940
+ def offer_answer=(val)
1941
+ @offer_answer.close if @offer_answer && val.nil?
1942
+ @offer_answer = val
1943
+ end
1944
+
1945
+
1946
+ protected :_get_sq_lock, :_get_recorder, :_remove_recorder
1947
+ private :_on_request, :_on_response, :_on_common_sip, :_do_record_sip, :_check_cancel_state,
1948
+ :_check_for_pending_cancel, :_fixed_local_tag, :_send_common,
1949
+ :_schedule_timer_for_session, :_increment_local_cseq, :_update_and_check_dialog_state_on_request,
1950
+ :_add_route_headers_if_present, :_check_transport_and_destination, :_create_registration_object
1951
+
1952
+ end