static void set_cause_code(struct respoke_session *session, enum respoke_status status) { struct ast_control_pvt_cause_code *cause_code; int size; const char *cause = respoke_status_to_str(status); /* size of the cause = sizeof + "RESPOKE " + cause */ size = sizeof(*cause_code) + 9 + strlen(cause); cause_code = ast_alloca(size); memset(cause_code, 0, size); ast_copy_string(cause_code->chan_name, ast_channel_name(session->channel), AST_CHANNEL_NAME); snprintf(cause_code->code, size - sizeof(*cause_code) + 1, "RESPOKE %s", cause); cause_code->ast_cause = hangup_reason_to_cause(status); ast_queue_control_data(session->channel, AST_CONTROL_PVT_CAUSE_CODE, cause_code, size); ast_channel_hangupcause_hash_set(session->channel, cause_code, size); }
/*! \brief Task for reacting to T.38 control frame */ static int t38_interpret_parameters(void *obj) { RAII_VAR(struct t38_parameters_task_data *, data, obj, ao2_cleanup); const struct ast_control_t38_parameters *parameters = data->frame->data.ptr; struct t38_state *state = t38_state_get_or_alloc(data->session); RAII_VAR(struct ast_sip_session_media *, session_media, ao2_find(data->session->media, "image", OBJ_KEY), ao2_cleanup); /* Without session media or state we can't interpret parameters */ if (!session_media || !state) { return 0; } switch (parameters->request_response) { case AST_T38_NEGOTIATED: case AST_T38_REQUEST_NEGOTIATE: /* Request T38 */ /* Negotiation can not take place without a valid max_ifp value. */ if (!parameters->max_ifp) { if (data->session->t38state == T38_PEER_REINVITE) { t38_change_state(data->session, session_media, state, T38_REJECTED); ast_sip_session_resume_reinvite(data->session); } else if (data->session->t38state == T38_ENABLED) { t38_change_state(data->session, session_media, state, T38_DISABLED); ast_sip_session_refresh(data->session, NULL, NULL, NULL, AST_SIP_SESSION_REFRESH_METHOD_INVITE, 1); } break; } else if (data->session->t38state == T38_PEER_REINVITE) { state->our_parms = *parameters; /* modify our parameters to conform to the peer's parameters, * based on the rules in the ITU T.38 recommendation */ if (!state->their_parms.fill_bit_removal) { state->our_parms.fill_bit_removal = 0; } if (!state->their_parms.transcoding_mmr) { state->our_parms.transcoding_mmr = 0; } if (!state->their_parms.transcoding_jbig) { state->our_parms.transcoding_jbig = 0; } state->our_parms.version = MIN(state->our_parms.version, state->their_parms.version); state->our_parms.rate_management = state->their_parms.rate_management; ast_udptl_set_local_max_ifp(session_media->udptl, state->our_parms.max_ifp); t38_change_state(data->session, session_media, state, T38_ENABLED); ast_sip_session_resume_reinvite(data->session); } else if (data->session->t38state != T38_ENABLED) { if (t38_initialize_session(data->session, session_media)) { break; } state->our_parms = *parameters; ast_udptl_set_local_max_ifp(session_media->udptl, state->our_parms.max_ifp); t38_change_state(data->session, session_media, state, T38_LOCAL_REINVITE); ast_sip_session_refresh(data->session, NULL, t38_reinvite_sdp_cb, t38_reinvite_response_cb, AST_SIP_SESSION_REFRESH_METHOD_INVITE, 1); } break; case AST_T38_TERMINATED: case AST_T38_REFUSED: case AST_T38_REQUEST_TERMINATE: /* Shutdown T38 */ if (data->session->t38state == T38_PEER_REINVITE) { t38_change_state(data->session, session_media, state, T38_REJECTED); ast_sip_session_resume_reinvite(data->session); } else if (data->session->t38state == T38_ENABLED) { t38_change_state(data->session, session_media, state, T38_DISABLED); ast_sip_session_refresh(data->session, NULL, NULL, NULL, AST_SIP_SESSION_REFRESH_METHOD_INVITE, 1); } break; case AST_T38_REQUEST_PARMS: { /* Application wants remote's parameters re-sent */ struct ast_control_t38_parameters parameters = state->their_parms; if (data->session->t38state == T38_PEER_REINVITE) { parameters.max_ifp = ast_udptl_get_far_max_ifp(session_media->udptl); parameters.request_response = AST_T38_REQUEST_NEGOTIATE; ast_queue_control_data(data->session->channel, AST_CONTROL_T38_PARAMETERS, ¶meters, sizeof(parameters)); } break; } default: break; } return 0; }
/*! \brief Helper function for changing the T.38 state */ static void t38_change_state(struct ast_sip_session *session, struct ast_sip_session_media *session_media, struct t38_state *state, enum ast_sip_session_t38state new_state) { enum ast_sip_session_t38state old_state = session->t38state; struct ast_control_t38_parameters parameters = { .request_response = 0, }; pj_time_val delay = { .sec = T38_AUTOMATIC_REJECTION_SECONDS }; if (old_state == new_state) { return; } session->t38state = new_state; ast_debug(2, "T.38 state changed to '%u' from '%u' on channel '%s'\n", new_state, old_state, session->channel ? ast_channel_name(session->channel) : "<gone>"); if (pj_timer_heap_cancel(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()), &state->timer)) { ast_debug(2, "Automatic T.38 rejection on channel '%s' terminated\n", session->channel ? ast_channel_name(session->channel) : "<gone>"); ao2_ref(session, -1); } if (!session->channel) { return; } switch (new_state) { case T38_PEER_REINVITE: ao2_ref(session, +1); if (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &state->timer, &delay) != PJ_SUCCESS) { ast_log(LOG_WARNING, "Scheduling of automatic T.38 rejection for channel '%s' failed\n", ast_channel_name(session->channel)); ao2_ref(session, -1); } parameters = state->their_parms; parameters.max_ifp = ast_udptl_get_far_max_ifp(session_media->udptl); parameters.request_response = AST_T38_REQUEST_NEGOTIATE; ast_udptl_set_tag(session_media->udptl, "%s", ast_channel_name(session->channel)); break; case T38_ENABLED: parameters = state->their_parms; parameters.max_ifp = ast_udptl_get_far_max_ifp(session_media->udptl); parameters.request_response = AST_T38_NEGOTIATED; ast_udptl_set_tag(session_media->udptl, "%s", ast_channel_name(session->channel)); break; case T38_REJECTED: case T38_DISABLED: if (old_state == T38_ENABLED) { parameters.request_response = AST_T38_TERMINATED; } else if (old_state == T38_LOCAL_REINVITE) { parameters.request_response = AST_T38_REFUSED; } break; case T38_LOCAL_REINVITE: /* wait until we get a peer response before responding to local reinvite */ break; case T38_MAX_ENUM: /* Well, that shouldn't happen */ ast_assert(0); break; } if (parameters.request_response) { ast_queue_control_data(session->channel, AST_CONTROL_T38_PARAMETERS, ¶meters, sizeof(parameters)); } } /*! \brief Task function which rejects a T.38 re-invite and resumes handling it */ static int t38_automatic_reject(void *obj) { RAII_VAR(struct ast_sip_session *, session, obj, ao2_cleanup); RAII_VAR(struct ast_datastore *, datastore, ast_sip_session_get_datastore(session, "t38"), ao2_cleanup); RAII_VAR(struct ast_sip_session_media *, session_media, ao2_find(session->media, "image", OBJ_KEY), ao2_cleanup); if (!datastore) { return 0; } ast_debug(2, "Automatically rejecting T.38 request on channel '%s'\n", session->channel ? ast_channel_name(session->channel) : "<gone>"); t38_change_state(session, session_media, datastore->data, T38_REJECTED); ast_sip_session_resume_reinvite(session); return 0; }