/* * This mid-dialog request callback ensures that any extra/leg settings done * at script level before dialog matching is performed will get properly * transferred into the dialog context, once the dialog is matched */ static void acc_merge_contexts(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { acc_ctx_t *ctx; /* if there is already a acc context in the processing * context, be sure to destroy it for now */ if ((ctx = ACC_GET_CTX())) { push_ctx_to_ctx( ctx, (acc_ctx_t *)(*_params->param)); acc_unref(ctx); /* unref it now beause it will disapear from local ctx */ } ctx = (acc_ctx_t *)(*_params->param); acc_ref(ctx); ACC_PUT_CTX(ctx); }
static void tmcb_func( struct cell* t, int type, struct tmcb_params *ps ) { acc_ctx_t* ctx = *ps->param; if (ACC_GET_TM_CTX(t) == NULL) { acc_ref(ctx); ACC_PUT_TM_CTX(t, ctx); } if (type&TMCB_RESPONSE_OUT) { acc_onreply( t, ps->req, ps->rpl, ps->code, ctx); } else if (type&TMCB_ON_FAILURE) { on_missed( t, ps->req, ps->rpl, ps->code, ctx); } else if (type&TMCB_RESPONSE_IN) { acc_onreply_in( t, ps->req, ps->rpl, ps->code, ctx); } }
static void acc_dlg_ctx_cb(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { acc_ctx_t *ctx; /* set the acc context from dialog into the * current processing context */ /* if there is already a acc context in the processing * context, be sure to destroy it for now */ if ( (ctx=ACC_GET_CTX)!=NULL) { push_ctx_to_ctx( ctx, (acc_ctx_t *)(*_params->param)); acc_unref(ctx); /* unref it now beause it will disapear from local ctx */ } ctx = (acc_ctx_t *)(*_params->param); acc_ref(ctx); ACC_PUT_CTX(ctx); }
static void acc_dlg_ended(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { struct cell* t; acc_ctx_t* ctx; if (!_params) { LM_ERR("not enough info\n"); return; } /* resolve local/dlg ctx conflict by merging them together */ acc_merge_contexts(dlg, type, _params); ctx = (acc_ctx_t *)(*_params->param); /* * this way we "enable" the refcount * if opensips shuts down before dialog terminated then the refcount * won't be enabled */ if (was_dlg_cb_used(ctx->flags)) { LM_INFO("CDR callback already registered [%p|%u] - do not run it again!\n", *_params->param, ctx->ref_no); return; } set_dlg_cb_used(ctx->flags); /* this time will be used to set */ gettimeofday(&ctx->bye_time, NULL); /* if it's not a local transaction we do the accounting on the tm callbacks */ if (((t=tmb.t_gett()) == T_UNDEFINED) || !t || !tmb.t_is_local(_params->msg)) { /* normal dialogs will have to do accounting when the response for * the bye will come since users should be able to populate extra * vars and leg vars */ acc_ref(ctx); if (tmb.register_tmcb( _params->msg, NULL, TMCB_RESPONSE_OUT, acc_cdr_cb, ctx, unref_acc_ctx) < 0) { acc_unref(ctx); LM_ERR("failed to register cdr callback!\n"); return; } /* for local transactions we do the accounting here since all the messages * have been processed */ } else { /* expired dialogs will be handled here */ if (is_log_acc_on(ctx->flags)) { env_set_text( ACC_ENDED, ACC_ENDED_LEN); if (acc_log_cdrs(dlg, _params->msg, ctx) < 0) { LM_ERR("Cannot log values\n"); return; } } if (is_db_acc_on(ctx->flags)) { env_set_text( db_table_acc.s, db_table_acc.len); if (acc_db_cdrs(dlg, _params->msg, ctx) < 0) { LM_ERR("Cannot insert into database\n"); return; } } if (is_aaa_acc_on(ctx->flags) && acc_aaa_cdrs(dlg, _params->msg, ctx) < 0) { LM_ERR("Cannot create radius accounting\n"); return; } if (is_evi_acc_on(ctx->flags)) { env_set_event(acc_cdr_event); if (acc_evi_cdrs(dlg, _params->msg, ctx) < 0) { LM_ERR("cannot send accounting events\n"); return; } } } }
acc_ctx_t* try_fetch_ctx(void) { acc_ctx_t* ret=NULL; str ctx_s; struct cell* t; struct dlg_cell* dlg; t = tmb.t_gett ? tmb.t_gett() : NULL; t = t==T_UNDEFINED ? NULL : t; if ((ret = ACC_GET_CTX()) == NULL) { t = tmb.t_gett ? tmb.t_gett() : NULL; t = (t==T_UNDEFINED) ? NULL : t; dlg = dlg_api.get_dlg ? dlg_api.get_dlg() : NULL; /* search the flags in transaction context */ if (t && (ret=ACC_GET_TM_CTX(t))==NULL) { /* try fetching the context from dialog only if dialog exists */ if (!dlg || dlg_api.fetch_dlg_value(dlg, &acc_ctx_str, &ctx_s, 0) < 0) { /* can't find the flags anywhere */ return NULL; } else { /* found them in dialog; set in the processing context * and in the transaction */ /* set the flags in transaction and processing context */ memcpy(&ret, ctx_s.s, sizeof(acc_ctx_t *)); if (!ret) return NULL; acc_ref_ex(ret, 2); ACC_PUT_TM_CTX(t, ret); ACC_PUT_CTX(ret); } } else if (ret) { /* we have the flags in transaction */ /* in transaction; put them in dialog(if possible) and in processing context */ acc_ref(ret); ACC_PUT_CTX(ret); if (dlg) { ctx_s.s = (char *)&ret; ctx_s.len = sizeof(acc_ctx_t *); } } else if (dlg) { /* no (flags in) transaction; search only in dialog*/ if (dlg_api.fetch_dlg_value(dlg, &acc_ctx_str, &ctx_s, 0) < 0) { /* can't find the flags anywhere */ return NULL; } else { /* found them in dialog; set in processing context */ memcpy(&ret, ctx_s.s, sizeof(acc_ctx_t *)); if (!ret) return NULL; if (t) { acc_ref_ex(ret, 2); /* ref twice - for local and tm ctx */ ACC_PUT_TM_CTX(t, ret); } else acc_ref(ret); /* ref only once, for local ctx */ ACC_PUT_CTX(ret); } } } return ret; }
int w_do_acc_3(struct sip_msg* msg, char* type_p, char* flags_p, char* table_p) { unsigned long long type=0, flags=0; unsigned long long flag_mask; acc_ctx_t* acc_ctx; acc_type_param_t* acc_param; str in; str table_name; int tmcb_types; int is_invite; if (type_p == NULL) { LM_ERR("accounting type is mandatory!\n"); return -1; } if (!msg) { LM_ERR("no SIP message\n"); return -1; } if (skip_cancel(msg)) { LM_WARN("do_accounting() called on CANCEL but 'report_cancels' modparam" " not set - no accounting will be done for this transaction!\n"); return 1; } acc_param = (acc_type_param_t *)type_p; if (acc_param->t == DO_ACC_PARAM_TYPE_VALUE) { type = acc_param->u.ival; } else { if (pv_printf_s(msg, acc_param->u.pval, &in) < 0) { LM_ERR("failed to fetch type value!\n"); return -1; } if ((type=do_acc_parse(&in, do_acc_type_parser)) == DO_ACC_ERR) { LM_ERR("Invalid expression <%.*s> for acc type!\n", in.len, in.s); return -1; } } if (table_p != NULL) { if (fixup_get_svalue(msg, (gparam_p)table_p, &table_name) < 0) { LM_ERR("failed to fetch table name!\n"); return -1; } } if (flags_p != NULL) { flags= *(unsigned long long*)flags_p; } flag_mask = type + type * flags; if (is_cdr_acc_on(flag_mask)) { /* setting this flag will allow us to register everything * that is needed for CDR accounting only once */ set_cdr_values_registered(flag_mask); } /* is it the first time when the function was called ? */ acc_ctx = try_fetch_ctx(); /* we go in here only if do_accounting function was called before; * if accounting context is null or it's created but flags value is * 0(meaning that context was created from somewhere else but do_accounting * wasn't called) then we need to jump over this and register * all the callbacks we need */ if (acc_ctx != NULL && acc_ctx->flags != 0) { /* do_accounting already called once */ /* first check if the accounting table changed */ if (is_db_acc_on(flag_mask) && (table_p != NULL || ZSTR(acc_ctx->acc_table))) { if (table_p == NULL) { table_name = db_table_acc; } if (store_acc_table( acc_ctx, &table_name) < 0) { LM_ERR("failed to store acc table!\n"); return -1; } } if (!cdr_values_registered(acc_ctx->flags) && cdr_values_registered(flag_mask)) { /* CDR support requested for the first time, we need to create * the dialog support, if an initial INVITE */ if (!has_totag(msg)) { acc_ctx->created = time(NULL); if (msg->REQ_METHOD == METHOD_INVITE && create_acc_dlg(msg) < 0) { LM_ERR("cannot use dialog accounting module\n"); return -1; } } } /* if it's the first time the missed calls flag was used register the callback */ if (is_mc_acc_on(flag_mask) && !failure_cb_registered(acc_ctx->flags)) { acc_ref(acc_ctx); if (tmb.register_tmcb( msg, 0, TMCB_ON_FAILURE, tmcb_func, acc_ctx, unref_acc_ctx)<=0) { LM_ERR("cannot register missed calls callback\n"); acc_unref(acc_ctx); return -1; } /* don't allow the callback to be registered agian in the future */ set_failure_cb_registered(acc_ctx->flags); } acc_ctx->flags |= flag_mask; return 1; } /* initialize accounting context if not created before */ if (acc_ctx == NULL && init_acc_ctx(&acc_ctx) < 0) { LM_ERR("failed to create accounting context!\n"); return -1; } /* move acc table in context if we have database accounting */ if (is_db_acc_on(flag_mask)) { if (table_p == NULL) { table_name = db_table_acc; } if (store_acc_table( acc_ctx, &table_name) < 0) { LM_ERR("failed to store acc table!\n"); return -1; } } /* * the first bit in each byte will just tell that we want that type of * accounting * next bits will tell extra options for that type of accounting * so we keep the first bits in each byte and on the following positions * next flags */ acc_ctx->flags = flag_mask; if (is_acc_on(acc_ctx->flags) || is_mc_acc_on(acc_ctx->flags)) { /* do some parsing in advance */ if (acc_preparse_req(msg)<0) return -1; is_invite = (msg->REQ_METHOD==METHOD_INVITE)?1:0; /* install additional handlers */ tmcb_types = /* report on completed transactions */ TMCB_RESPONSE_IN|TMCB_RESPONSE_OUT; if (is_invite && is_mc_acc_on(acc_ctx->flags)) { /* register it manually; see explanation below * get incoming replies ready for processing */ /* TMCB_RESPONSE_OUT | */ /* report on missed calls */ tmcb_types |= TMCB_ON_FAILURE; /* the flag will help on further do_accounting calls to know * not to register the callback twice */ set_failure_cb_registered(acc_ctx->flags); } /* if cdr accounting is enabled */ if (is_cdr_acc_on(acc_ctx->flags) && !has_totag(msg)) { acc_ctx->created = time(NULL); if (is_invite && create_acc_dlg(msg) < 0) { LM_ERR("cannot use dialog accounting module\n"); return -1; } } acc_ref(acc_ctx); if (tmb.register_tmcb( msg, 0, tmcb_types, tmcb_func, acc_ctx, unref_acc_ctx)<=0) { LM_ERR("cannot register additional callbacks\n"); acc_unref(acc_ctx); return -1; } /* if required, determine request direction */ if( detect_direction && !rrb.is_direction(msg,RR_FLOW_UPSTREAM) ) { LM_DBG("detected an UPSTREAM req -> flaging it\n"); msg->msg_flags |= FL_REQ_UPSTREAM; } } return 1; }