acc_db.c

00001 /*
00002  * Accounting module
00003  *
00004  * $Id$
00005  *
00006  * Copyright (C) 2001-2003 FhG FOKUS
00007  * Copyright (C) 2005 iptelorg GmbH
00008  *
00009  * This file is part of ser, a free SIP server.
00010  *
00011  * ser is free software; you can redistribute it and/or modify
00012  * it under the terms of the GNU General Public License as published by
00013  * the Free Software Foundation; either version 2 of the License, or
00014  * (at your option) any later version
00015  *
00016  * For a license to use the ser software under conditions
00017  * other than those described here, or to purchase support for this
00018  * software, please contact iptel.org by e-mail at the following addresses:
00019  *    info@iptel.org
00020  *
00021  * ser is distributed in the hope that it will be useful,
00022  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00023  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00024  * GNU General Public License for more details.
00025  *
00026  * You should have received a copy of the GNU General Public License
00027  * along with this program; if not, write to the Free Software
00028  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00029  *
00030  */
00031 
00032 #include <stdio.h>
00033 #include <string.h>
00034 #include <time.h>
00035 
00036 #include "../../sr_module.h"
00037 #include "../../dprint.h"
00038 #include "../../mem/mem.h"
00039 #include "../../modules/tm/t_hooks.h"
00040 #include "../../modules/tm/tm_load.h"
00041 #include "../../modules/tm/h_table.h"
00042 #include "../../parser/msg_parser.h"
00043 #include "../../parser/parse_from.h"
00044 #include "../../parser/digest/digest.h"
00045 #include "../../usr_avp.h"
00046 #include "../../modules/tm/tm_load.h"
00047 #include "../../usr_avp.h"
00048 #include "../../lib/srdb2/db.h"
00049 #include "../../trim.h"
00050 #include "../../id.h"
00051 #include "../acc_syslog/attrs.h"
00052 
00053 /*
00054  * TODO:
00055  * - Quote attribute values properly
00056  */
00057 
00058 /*
00059  * a: attr
00060  * c: sip_callid
00061  * d: to_tag
00062  * f: sip_from
00063  * g: flags
00064  * i: inbound_ruri
00065  * m: sip_method
00066  * n: sip_cseq
00067  * o: outbound_ruri
00068  * p: source_ip
00069  * r: from_tag
00070  * t: sip_to
00071  * u: digest_username
00072  * x: request_timestamp
00073  * D: to_did
00074  * F: from_uri
00075  * I: from_uid
00076  * M: from_did
00077  * P: source_port
00078  * R: digest_realm
00079  * S: sip_status
00080  * T: to_uri
00081  * U: to_uid
00082  * X: response_timestamp
00083  */
00084 
00085 #define ALL_LOG_FMT "acdfgimnoprstuxDFIMPRSTUX"
00086 #define ALL_LOG_FMT_LEN (sizeof(ALL_LOG_FMT) - 1)
00087 
00088 /* Default column names */
00089 #define A_ATTRS        "attrs"
00090 #define A_CALLID       "sip_callid"
00091 #define A_TOTAG        "to_tag"
00092 #define A_FROM         "sip_from"
00093 #define A_FLAGS        "flags"
00094 #define A_IURI         "in_ruri"
00095 #define A_METHOD       "sip_method"
00096 #define A_CSEQ         "sip_cseq"
00097 #define A_OURI         "out_ruri"
00098 #define A_FROMTAG      "from_tag"
00099 #define A_TO           "sip_to"
00100 #define A_DIGUSER      "digest_username"
00101 #define A_REQTIMESTAMP "request_timestamp"
00102 #define A_TODID        "to_did"
00103 #define A_FROMURI      "from_uri"
00104 #define A_FROMUID      "from_uid"
00105 #define A_FROMDID      "from_did"
00106 #define A_DIGREALM     "digest_realm"
00107 #define A_STATUS       "sip_status"
00108 #define A_TOURI        "to_uri"
00109 #define A_TOUID        "to_uid"
00110 #define A_RESTIMESTAMP "response_timestamp"
00111 #define A_SRCIP        "src_ip"
00112 #define A_SRCPORT      "src_port"
00113 #define A_SERVER_ID    "server_id"
00114 
00115 MODULE_VERSION
00116 
00117 struct tm_binds tmb;
00118 
00119 static int mod_init(void);
00120 static void mod_destroy(void);
00121 static int child_init(int rank);
00122 static int fix_log_flag( modparam_t type, void* val);
00123 static int fix_log_missed_flag( modparam_t type, void* val);
00124 
00125 static int early_media = 0;         /* Enable/disable early media (183) accounting */
00126 static int failed_transactions = 0; /* Enable/disable accounting of failed (>= 300) transactions */
00127 static int report_cancels = 0;      /* Enable/disable CANCEL reporting */
00128 static int report_ack = 0;          /* Enable/disable end-to-end ACK reports */
00129 static int log_flag = 0;            /* Flag that marks transactions to be accounted */
00130 static int log_missed_flag = 0;     /* Transaction having this flag set will be accounted in missed calls when fails */
00131 static char* log_fmt = ALL_LOG_FMT; /* Formating string that controls what information will be collected and accounted */
00132 
00133 #define DEFAULT_ACC_TABLE "acc"
00134 #define DEFAULT_MC_TABLE "missed_calls"
00135 
00136 static str db_url = STR_STATIC_INIT(DEFAULT_DB_URL);
00137 static str acc_table = STR_STATIC_INIT(DEFAULT_ACC_TABLE);
00138 static str mc_table = STR_STATIC_INIT(DEFAULT_MC_TABLE);
00139 
00140 static str attrs_col = STR_STATIC_INIT(A_ATTRS);
00141 static str callid_col = STR_STATIC_INIT(A_CALLID);
00142 static str totag_col = STR_STATIC_INIT(A_TOTAG);
00143 static str from_col = STR_STATIC_INIT(A_FROM);
00144 static str flags_col = STR_STATIC_INIT(A_FLAGS);
00145 static str iuri_col = STR_STATIC_INIT(A_IURI);
00146 static str method_col = STR_STATIC_INIT(A_METHOD);
00147 static str cseq_col = STR_STATIC_INIT(A_CSEQ);
00148 static str ouri_col = STR_STATIC_INIT(A_OURI);
00149 static str fromtag_col = STR_STATIC_INIT(A_FROMTAG);
00150 static str to_col = STR_STATIC_INIT(A_TO);
00151 static str diguser_col = STR_STATIC_INIT(A_DIGUSER);
00152 static str reqtimestamp_col = STR_STATIC_INIT(A_REQTIMESTAMP);
00153 static str todid_col = STR_STATIC_INIT(A_TODID);
00154 static str fromuri_col = STR_STATIC_INIT(A_FROMURI);
00155 static str fromuid_col = STR_STATIC_INIT(A_FROMUID);
00156 static str fromdid_col = STR_STATIC_INIT(A_FROMDID);
00157 static str digrealm_col = STR_STATIC_INIT(A_DIGREALM);
00158 static str status_col = STR_STATIC_INIT(A_STATUS);
00159 static str touri_col = STR_STATIC_INIT(A_TOURI);
00160 static str touid_col = STR_STATIC_INIT(A_TOUID);
00161 static str restimestamp_col = STR_STATIC_INIT(A_RESTIMESTAMP);
00162 static str src_ip_col = STR_STATIC_INIT(A_SRCIP);
00163 static str src_port_col = STR_STATIC_INIT(A_SRCPORT);
00164 static str server_id_col = STR_STATIC_INIT(A_SERVER_ID);
00165 
00166 static db_ctx_t* acc_db = NULL;
00167 static db_fld_t fld[sizeof(ALL_LOG_FMT) - 1];
00168 static db_cmd_t* write_mc = NULL, *write_acc = NULL;
00169 
00170 
00171 /* Attribute-value pairs */
00172 static char* attrs = "";
00173 avp_ident_t* avps;
00174 int avps_n;
00175 
00176 static int acc_db_request0(struct sip_msg *rq, char *p1, char *p2);
00177 static int acc_db_missed0(struct sip_msg *rq, char *p1, char *p2);
00178 static int acc_db_request1(struct sip_msg *rq, char *p1, char *p2);
00179 static int acc_db_missed1(struct sip_msg *rq, char *p1, char *p2);
00180 
00181 static cmd_export_t cmds[] = {
00182         {"acc_db_log",    acc_db_request0, 0, 0,               REQUEST_ROUTE | FAILURE_ROUTE},
00183         {"acc_db_missed", acc_db_missed0,  0, 0,               REQUEST_ROUTE | FAILURE_ROUTE},
00184         {"acc_db_log",    acc_db_request1, 1, fixup_var_int_1, REQUEST_ROUTE | FAILURE_ROUTE},
00185         {"acc_db_missed", acc_db_missed1,  1, fixup_var_int_1, REQUEST_ROUTE | FAILURE_ROUTE},
00186         {0, 0, 0, 0, 0}
00187 };
00188 
00189 
00190 static param_export_t params[] = {
00191         {"early_media",         PARAM_INT, &early_media },
00192         {"failed_transactions", PARAM_INT, &failed_transactions },
00193         {"report_ack",          PARAM_INT, &report_ack },
00194         {"report_cancels",      PARAM_INT, &report_cancels },
00195         {"log_flag",            PARAM_INT, &log_flag },
00196         {"log_flag",            PARAM_STRING|PARAM_USE_FUNC, fix_log_flag},
00197         {"log_missed_flag",     PARAM_INT, &log_missed_flag },
00198         {"log_missed_flag",     PARAM_STRING|PARAM_USE_FUNC, fix_log_missed_flag},
00199         {"log_fmt",             PARAM_STRING, &log_fmt },
00200         {"attrs",               PARAM_STRING, &attrs },
00201         {"db_url",              PARAM_STR, &db_url },
00202         {"acc_table",           PARAM_STR, &acc_table },
00203         {"mc_table",            PARAM_STR, &mc_table },
00204         {"attrs_column",        PARAM_STR, &attrs_col },
00205         {"callid_column",       PARAM_STR, &callid_col },
00206         {"totag_column",        PARAM_STR, &totag_col },
00207         {"from_column",         PARAM_STR, &from_col },
00208         {"flags_column",        PARAM_STR, &flags_col },
00209         {"iuri_column",         PARAM_STR, &iuri_col },
00210         {"method_column",       PARAM_STR, &method_col },
00211         {"cseq_column",         PARAM_STR, &cseq_col },
00212         {"ouri_column",         PARAM_STR, &ouri_col },
00213         {"fromtag_column",      PARAM_STR, &fromtag_col },
00214         {"to_column",           PARAM_STR, &to_col },
00215         {"diguser_column",      PARAM_STR, &diguser_col },
00216         {"reqtimestamp_column", PARAM_STR, &reqtimestamp_col },
00217         {"todid_column",        PARAM_STR, &todid_col },
00218         {"fromuri_column",      PARAM_STR, &fromuri_col },
00219         {"fromuid_column",      PARAM_STR, &fromuid_col },
00220         {"fromdid_column",      PARAM_STR, &fromdid_col },
00221         {"digrealm_column",     PARAM_STR, &digrealm_col },
00222         {"status_column",       PARAM_STR, &status_col },
00223         {"touri_column",        PARAM_STR, &touri_col },
00224         {"touid_column",        PARAM_STR, &touid_col },
00225         {"restimestamp_column", PARAM_STR, &restimestamp_col },
00226         {"src_ip_column",       PARAM_STR, &src_ip_col },
00227         {"src_port_column",     PARAM_STR, &src_port_col },
00228         {"server_id_column",    PARAM_STR, &server_id_col},
00229         {0, 0, 0}
00230 };
00231 
00232 
00233 struct module_exports exports= {
00234         "acc_db",
00235         cmds,        /* exported functions */
00236         0,           /* RPC methods */
00237         params,      /* exported params */
00238         mod_init,    /* initialization module */
00239         0,           /* response function */
00240         mod_destroy, /* destroy function */
00241         0,           /* oncancel function */
00242         child_init   /* per-child init function */
00243 };
00244 
00245 
00246 
00247 /* fixes log_flag param (resolves possible named flags) */
00248 static int fix_log_flag( modparam_t type, void* val)
00249 {
00250         return fix_flag(type, val, "acc_db", "log_flag", &log_flag);
00251 }
00252 
00253 
00254 
00255 /* fixes log_missed_flag param (resolves possible named flags) */
00256 static int fix_log_missed_flag( modparam_t type, void* val)
00257 {
00258         return fix_flag(type, val, "acc_db", "log_missed_flag", &log_missed_flag);
00259 }
00260 
00261 
00262 
00263 static inline int skip_cancel(struct sip_msg *msg)
00264 {
00265         return (msg->REQ_METHOD == METHOD_CANCEL) && report_cancels == 0;
00266 }
00267 
00268 static int verify_fmt(char *fmt) {
00269 
00270         if (!fmt) {
00271                 LOG(L_ERR, "ERROR:acc:verify_fmt: formatting string zero\n");
00272                 return -1;
00273         }
00274 
00275         if (!(*fmt)) {
00276                 LOG(L_ERR, "ERROR:acc:verify_fmt: formatting string empty\n");
00277                 return -1;
00278         }
00279 
00280         if (strlen(fmt) > ALL_LOG_FMT_LEN) {
00281                 LOG(L_ERR, "ERROR:acc:verify_fmt: formatting string too long\n");
00282                 return -1;
00283         }
00284 
00285         while(*fmt) {
00286                 if (!strchr(ALL_LOG_FMT, *fmt)) {
00287                         LOG(L_ERR, "ERROR:acc:verify_fmt: char in log_fmt invalid: %c\n", *fmt);
00288                         return -1;
00289                 }
00290                 fmt++;
00291         }
00292         return 1;
00293 }
00294 
00295 
00296 /*
00297  * Return true if accounting is enabled and the
00298  * transaction is marked for accounting
00299  */
00300 static inline int is_acc_on(struct sip_msg *rq)
00301 {
00302         return log_flag && isflagset(rq, log_flag) == 1;
00303 }
00304 
00305 
00306 /*
00307  * Return true if missed_call accounting is enabled
00308  * and the transaction has the flag set
00309  */
00310 static inline int is_mc_on(struct sip_msg *rq)
00311 {
00312         return log_missed_flag && isflagset(rq, log_missed_flag) == 1;
00313 }
00314 
00315 
00316 static inline void preparse_req(struct sip_msg *rq)
00317 {
00318              /* try to parse from for From-tag for accounted transactions;
00319               * don't be worried about parsing outcome -- if it failed,
00320               * we will report N/A. There is no need to parse digest credentials
00321               * here even if we account them, because the authentication function
00322               * will do it before us and if not then we will account n/a.
00323               */
00324         parse_headers(rq, HDR_CALLID_F | HDR_FROM_F | HDR_TO_F | HDR_CSEQ_F, 0 );
00325         parse_from_header(rq);
00326 }
00327 
00328 
00329 /* is this reply of interest for accounting ? */
00330 static inline int should_acc_reply(struct cell* t, int code)
00331 {
00332         struct sip_msg *r;
00333 
00334         r = t->uas.request;
00335 
00336              /* validation */
00337         if (r == 0) {
00338                 LOG(L_ERR, "ERROR:acc:should_acc_reply: 0 request\n");
00339                 return 0;
00340         }
00341 
00342              /* negative transactions reported otherwise only if explicitly
00343               * demanded */
00344         if (!failed_transactions && code >= 300) return 0;
00345         if (!is_acc_on(r)) return 0;
00346         if (skip_cancel(r)) return 0;
00347         if (code < 200 && ! (early_media && code == 183)) return 0;
00348         return 1; /* seed is through, we will account this reply */
00349 }
00350 
00351 
00352 /* Extract username attribute from authorized credentials */
00353 static inline str* cred_user(struct sip_msg* rq)
00354 {
00355         struct hdr_field* h;
00356         auth_body_t* cred;
00357 
00358         get_authorized_cred(rq->proxy_auth, &h);
00359         if (!h) get_authorized_cred(rq->authorization, &h);
00360         if (!h) return 0;
00361         cred = (auth_body_t*)(h->parsed);
00362         if (!cred || !cred->digest.username.user.len)
00363                 return 0;
00364         return &cred->digest.username.user;
00365 }
00366 
00367 
00368 /* Extract realm attribute from authorized credentials */
00369 static inline str* cred_realm(struct sip_msg* rq)
00370 {
00371         str* realm;
00372         struct hdr_field* h;
00373         auth_body_t* cred;
00374 
00375         get_authorized_cred(rq->proxy_auth, &h);
00376         if (!h) get_authorized_cred(rq->authorization, &h);
00377         if (!h) return 0;
00378         cred = (auth_body_t*)(h->parsed);
00379         if (!cred) return 0;
00380         realm = GET_REALM(&cred->digest);
00381         if (!realm->len || !realm->s) {
00382                 return 0;
00383         }
00384         return realm;
00385 }
00386 
00387 
00388 /* Return To header field from the request in case of faked reply or
00389  * missing To header field in the reply
00390  */
00391 static inline struct hdr_field* valid_to(struct cell* t, struct sip_msg* reply)
00392 {
00393         if (reply == FAKED_REPLY || !reply || !reply->to) {
00394                 return t->uas.request->to;
00395         } else {
00396                 return reply->to;
00397         }
00398 }
00399 
00400 
00401 static int init_data(char* fmt)
00402 {
00403         int i = 0;
00404 
00405         memset(fld, '\0', sizeof(fld));
00406         while(*fmt) {
00407                 switch(*fmt) {
00408                 case 'a': /* attr */
00409                         fld[i].name = attrs_col.s;
00410                         fld[i].type = DB_STR;
00411                         break;
00412 
00413                 case 'c': /* callid */
00414                         fld[i].type = DB_STR;
00415                         fld[i].name = callid_col.s;
00416                         break;
00417 
00418                 case 'd': /* To tag */
00419                         fld[i].type = DB_STR;
00420                         fld[i].name = totag_col.s;
00421                         break;
00422 
00423                 case 'f': /* From */
00424                         fld[i].type = DB_STR;
00425                         fld[i].name = from_col.s;
00426                         break;
00427 
00428                 case 'g': /* flags */
00429                         fld[i].type = DB_INT;
00430                         fld[i].name = flags_col.s;
00431                         break;
00432 
00433                 case 'i': /* Inbound ruri */
00434                         fld[i].type = DB_STR;
00435                     fld[i].name = iuri_col.s;
00436                         break;
00437 
00438                 case 'm': /* method */
00439                         fld[i].type = DB_STR;
00440                         fld[i].name = method_col.s;
00441                         break;
00442 
00443                 case 'n': /* sip_cseq */
00444                         fld[i].type = DB_INT;
00445                         fld[i].name = cseq_col.s;
00446                         break;
00447 
00448                 case 'o': /* outbound_ruri */
00449                         fld[i].type = DB_STR;
00450                         fld[i].name = ouri_col.s;
00451                         break;
00452 
00453                 case 'p': /* src_ip */
00454                         fld[i].type = DB_INT;
00455                         fld[i].name = src_ip_col.s;
00456                         break;
00457 
00458                 case 'r': /* from_tag */
00459                         fld[i].type = DB_STR;
00460                         fld[i].name = fromtag_col.s;
00461                         break;
00462 
00463                 case 't': /* sip_to */
00464                         fld[i].type = DB_STR;
00465                         fld[i].name = to_col.s;
00466                         break;
00467 
00468                 case 'u': /* digest_username */
00469                         fld[i].type = DB_STR;
00470                         fld[i].name = diguser_col.s;
00471                         break;
00472 
00473                 case 'x': /* request_timestamp */
00474                         fld[i].type = DB_DATETIME;
00475                         fld[i].name = reqtimestamp_col.s;
00476                         break;
00477 
00478                 case 'D': /* to_did */
00479                         fld[i].type = DB_STR;
00480                         fld[i].name = todid_col.s;
00481                         break;
00482 
00483                 case 'F': /* from_uri */
00484                         fld[i].type = DB_STR;
00485                         fld[i].name = fromuri_col.s;
00486                         break;
00487 
00488                 case 'I': /* from_uid */
00489                         fld[i].type = DB_STR;
00490                         fld[i].name = fromuid_col.s;
00491                         break;
00492 
00493                 case 'M': /* from_did */
00494                     fld[i].type = DB_STR;
00495                         fld[i].name = fromdid_col.s;
00496                         break;
00497 
00498                 case 'P': /* src_port */
00499                         fld[i].type = DB_INT;
00500                         fld[i].name = src_port_col.s;
00501                         break;
00502 
00503                 case 'R': /* digest_realm */
00504                         fld[i].type = DB_STR;
00505                         fld[i].name = digrealm_col.s;
00506                         break;
00507 
00508                 case 'S': /* sip_status */
00509                         fld[i].type = DB_INT;
00510                         fld[i].name = status_col.s;
00511                         break;
00512 
00513                 case 'T': /* to_uri */
00514                         fld[i].type = DB_STR;
00515                         fld[i].name = touri_col.s;
00516                         break;
00517 
00518                 case 'U': /* to_uid */
00519                         fld[i].type = DB_STR;
00520                         fld[i].name = touid_col.s;
00521                         break;
00522 
00523                 case 'X': /* response_timestamp */
00524                         fld[i].type = DB_DATETIME;
00525                         fld[i].name = restimestamp_col.s;
00526                         break;
00527 
00528                 case 's': /* server_id */
00529                         fld[i].type = DB_INT;
00530                         fld[i].name = server_id_col.s;
00531                 }
00532 
00533                 fmt++;
00534                 i++;
00535         }
00536         fld[i].name = NULL;
00537         return 0;
00538 }
00539 
00540 
00541 
00542 /* create an array of str's for accounting using a formatting string;
00543  * this is the heart of the accounting module -- it prints whatever
00544  * requested in a way, that can be used for syslog, radius,
00545  * sql, whatsoever
00546  * tm sip_msg_clones does not clone (shmmem-zed) parsed fields, other then Via1,2. Such fields clone now or use from rq_rp
00547  */
00548 static int fmt2strar(char *fmt,             /* what would you like to account ? */
00549                                          struct sip_msg *rq,    /* accounted message */
00550                                          str* ouri,             /* Outbound Request-URI */
00551                                          struct hdr_field *to,
00552                                          unsigned int code,
00553                                          time_t req_time,
00554                                          db_fld_t* params)       /* Timestamp of the request */
00555 {
00556         int cnt;
00557         struct to_body* from, *pto;
00558         str *cr, *at;
00559         struct cseq_body *cseq;
00560 
00561         cnt = 0;
00562 
00563              /* we don't care about parsing here; either the function
00564               * was called from script, in which case the wrapping function
00565               * is supposed to parse, or from reply processing in which case
00566               * TM should have preparsed from REQUEST_IN callback; what's not
00567               * here is replaced with NA
00568               */
00569         while(*fmt) {
00570                 if (cnt == ALL_LOG_FMT_LEN) {
00571                         LOG(L_ERR, "ERROR:acc:fmt2strar: Formatting string is too long\n");
00572                         return 0;
00573                 }
00574 
00575                 params[cnt].flags &= ~DB_NULL;
00576                 switch(*fmt) {
00577                 case 'a': /* attr */
00578                         at = print_attrs(avps, avps_n, 0);
00579                         if (!at) {
00580                                 params[cnt].flags |= DB_NULL;
00581                         } else {
00582                                 params[cnt].v.lstr = *at;
00583                         }
00584                         break;
00585 
00586                 case 'c': /* sip_callid */
00587                         if (rq->callid && rq->callid->body.len) {
00588                                 params[cnt].v.lstr = rq->callid->body;
00589                         } else {
00590                                 params[cnt].flags |= DB_NULL;
00591                         }
00592                         break;
00593 
00594                 case 'd': /* to_tag */
00595                         if (to && (pto = (struct to_body*)(to->parsed)) && pto->tag_value.len) {
00596                                 params[cnt].v.lstr = pto->tag_value;
00597                         } else {
00598                                 params[cnt].flags |= DB_NULL;
00599                         }
00600                         break;
00601 
00602                 case 'f': /* sip_from */
00603                         if (rq->from && rq->from->body.len) {
00604                                 params[cnt].v.lstr = rq->from->body;
00605                         } else {
00606                                 params[cnt].flags |= DB_NULL;                           
00607                         }
00608                         break;
00609 
00610                 case 'g': /* flags */
00611                         params[cnt].v.int4 = rq->flags;
00612                         break;
00613 
00614                 case 'i': /* inbound_ruri */
00615                         params[cnt].v.lstr = rq->first_line.u.request.uri;
00616                         break;
00617 
00618                 case 'm': /* sip_method */
00619                         params[cnt].v.lstr = rq->first_line.u.request.method;
00620                         break;
00621 
00622                 case 'n': /* sip_cseq */
00623                         if (rq->cseq && (cseq = get_cseq(rq)) && cseq->number.len) {
00624                                 str2int(&cseq->number, (unsigned int*)&params[cnt].v.int4);
00625                         } else {
00626                                 params[cnt].flags |= DB_NULL;
00627                         }
00628                         break;
00629 
00630                 case 'o': /* outbound_ruri */
00631                         params[cnt].v.lstr = *ouri;
00632                         break;
00633 
00634                 case 'p':
00635                         params[cnt].v.int4 = rq->rcv.src_ip.u.addr32[0];
00636                         break;
00637 
00638                 case 'r': /* from_tag */
00639                         if (rq->from && (from = get_from(rq)) && from->tag_value.len) {
00640                                 params[cnt].v.lstr = from->tag_value;
00641                         } else {
00642                                 params[cnt].flags |= DB_NULL;
00643                         }
00644                         break;
00645 
00646                 case 't': /* sip_to */
00647                         if (to && to->body.len) params[cnt].v.lstr = to->body;
00648                         else params[cnt].flags |= DB_NULL;
00649                         break;
00650 
00651                 case 'u': /* digest_username */
00652                         cr = cred_user(rq);
00653                         if (cr) params[cnt].v.lstr = *cr;
00654                         else params[cnt].flags |= DB_NULL;
00655                         break;
00656 
00657                 case 'x': /* request_timestamp */
00658                         params[cnt].v.time = req_time;
00659                         break;
00660 
00661                 case 'D': /* to_did */
00662                         params[cnt].flags |= DB_NULL;
00663                         break;
00664 
00665                 case 'F': /* from_uri */
00666                         if (rq->from && (from = get_from(rq)) && from->uri.len) {
00667                                 params[cnt].v.lstr = from->uri;
00668                         } else params[cnt].flags |= DB_NULL;
00669                         break;
00670 
00671                 case 'I': /* from_uid */
00672                         if (get_from_uid(&params[cnt].v.lstr, rq) < 0) {
00673                                 params[cnt].flags |= DB_NULL;
00674                         }
00675                         break;
00676 
00677                 case 'M': /* from_did */
00678                         params[cnt].flags |= DB_NULL;
00679                         break;
00680 
00681                 case 'P': /* source_port */
00682                         params[cnt].v.int4 = rq->rcv.src_port;
00683                         break;
00684 
00685                 case 'R': /* digest_realm */
00686                         cr = cred_realm(rq);
00687                         if (cr) params[cnt].v.lstr = *cr;
00688                         else params[cnt].flags |= DB_NULL;
00689                         break;
00690 
00691                 case 's': /* server_id */
00692                         params[cnt].v.int4 = server_id;
00693                         break;
00694 
00695                 case 'S': /* sip_status */
00696                         if (code > 0) params[cnt].v.int4 = code;
00697                         else params[cnt].flags |= DB_NULL;
00698                         break;
00699 
00700                 case 'T': /* to_uri */
00701                         if (rq->to && (pto = get_to(rq)) && pto->uri.len) params[cnt].v.lstr = pto->uri;
00702                         else params[cnt].flags |= DB_NULL;
00703                         break;
00704 
00705                 case 'U': /* to_uid */
00706                         if (get_to_uid(&params[cnt].v.lstr, rq) < 0) {
00707                                 params[cnt].flags |= DB_NULL;
00708                         }
00709                         break;
00710 
00711                 case 'X': /* response_timestamp */
00712                         params[cnt].v.time = time(0);
00713                         break;
00714 
00715                 default:
00716                         LOG(L_CRIT, "BUG:acc:fmt2strar: unknown char: %c\n", *fmt);
00717                         return 0;
00718                 } /* switch (*fmt) */
00719 
00720                 fmt++;
00721                 cnt++;
00722         } /* while (*fmt) */
00723 
00724         return cnt;
00725 }
00726 
00727 
00728 int log_request(struct sip_msg *rq, str* ouri, struct hdr_field *to, db_cmd_t* cmd, unsigned int code, time_t req_timestamp)
00729 {
00730         int cnt;
00731         if (skip_cancel(rq)) return 1;
00732 
00733         cnt = fmt2strar(log_fmt, rq, ouri, to, code, req_timestamp, cmd->vals);
00734         if (cnt == 0) {
00735                 LOG(L_ERR, "ERROR:acc:log_request: fmt2strar failed\n");
00736                 return -1;
00737         }
00738 
00739         if (!db_url.len) {
00740                 LOG(L_ERR, "ERROR:acc:log_request: can't log -- no db_url set\n");
00741                 return -1;
00742         }
00743 
00744         if (db_exec(NULL, cmd) < 0) {
00745                 ERR("Error while inserting to database\n");
00746                 return -1;
00747         }
00748 
00749         return 1;
00750 }
00751 
00752 
00753 static void log_reply(struct cell* t , struct sip_msg* reply, unsigned int code, time_t req_time)
00754 {
00755         str* ouri;
00756         
00757         if (t->relayed_reply_branch >= 0) {
00758             ouri = &t->uac[t->relayed_reply_branch].uri;
00759         } else {
00760             ouri = GET_NEXT_HOP(t->uas.request);
00761         }
00762         
00763         log_request(t->uas.request, ouri, valid_to(t,reply), write_acc, code, req_time);
00764 }
00765 
00766 
00767 static void log_ack(struct cell* t , struct sip_msg *ack, time_t req_time)
00768 {
00769         struct sip_msg *rq;
00770         struct hdr_field *to;
00771 
00772         rq = t->uas.request;
00773         if (ack->to) to = ack->to;
00774         else to = rq->to;
00775 
00776         log_request(ack, GET_RURI(ack), to, write_acc, t->uas.status, req_time);
00777 }
00778 
00779 
00780 static void log_missed(struct cell* t, struct sip_msg* reply, unsigned int code, time_t req_time)
00781 {
00782         str* ouri;
00783 
00784         if (t->relayed_reply_branch >= 0) {
00785             ouri = &t->uac[t->relayed_reply_branch].uri;
00786         } else {
00787             ouri = GET_NEXT_HOP(t->uas.request);
00788         }
00789         
00790         log_request(t->uas.request, ouri, valid_to(t, reply), write_mc, code, req_time);
00791 }
00792 
00793 
00794 /* these wrappers parse all what may be needed; they don't care about
00795  * the result -- accounting functions just display "unavailable" if there
00796  * is nothing meaningful
00797  */
00798 static int acc_db_request0(struct sip_msg *rq, char* s1, char* s2)
00799 {
00800         preparse_req(rq);
00801         return log_request(rq, GET_RURI(rq), rq->to, write_acc, 0, time(0));
00802 }
00803 
00804 
00805 /* these wrappers parse all what may be needed; they don't care about
00806  * the result -- accounting functions just display "unavailable" if there
00807  * is nothing meaningful
00808  */
00809 static int acc_db_missed0(struct sip_msg *rq, char* s1, char* s2)
00810 {
00811         preparse_req(rq);
00812         return log_request(rq, GET_RURI(rq), rq->to, write_mc, 0, time(0));
00813 }
00814 
00815 
00816 /* these wrappers parse all what may be needed; they don't care about
00817  * the result -- accounting functions just display "unavailable" if there
00818  * is nothing meaningful
00819  */
00820 static int acc_db_request1(struct sip_msg *rq, char* p1, char* p2)
00821 {
00822     int code;
00823 
00824     if (get_int_fparam(&code, rq, (fparam_t*)p1) < 0) {
00825         code = 0;
00826     }
00827     preparse_req(rq);
00828     return log_request(rq, GET_RURI(rq), rq->to, write_acc, code, time(0));
00829 }
00830 
00831 
00832 /* these wrappers parse all what may be needed; they don't care about
00833  * the result -- accounting functions just display "unavailable" if there
00834  * is nothing meaningful
00835  */
00836 static int acc_db_missed1(struct sip_msg *rq, char* p1, char* p2)
00837 {
00838     int code;
00839 
00840     if (get_int_fparam(&code, rq, (fparam_t*)p1) < 0) {
00841         code = 0;
00842     }
00843     preparse_req(rq);
00844     return log_request(rq, GET_RURI(rq), rq->to, write_mc, code, time(0));
00845 }
00846 
00847 
00848 static void ack_handler(struct cell* t, int type, struct tmcb_params* ps)
00849 {
00850         if (is_acc_on(t->uas.request)) {
00851                 preparse_req(ps->req);
00852                 log_ack(t, ps->req, (time_t)*(ps->param));
00853         }
00854 }
00855 
00856 
00857 /* initiate a report if we previously enabled MC accounting for this t */
00858 static void failure_handler(struct cell *t, int type, struct tmcb_params* ps)
00859 {
00860         /* validation */
00861         if (t->uas.request == 0) {
00862                 DBG("DBG:acc:failure_handler: No uas.request, skipping local transaction\n");
00863                 return;
00864         }
00865 
00866         if (is_invite(t) && ps->code >= 300) {
00867                 if (is_mc_on(t->uas.request)) {
00868                         log_missed(t, ps->rpl, ps->code, (time_t)*(ps->param));
00869                         resetflag(t->uas.request, log_missed_flag);
00870                 }
00871         }
00872 }
00873 
00874 
00875 /* initiate a report if we previously enabled accounting for this t */
00876 static void replyout_handler(struct cell* t, int type, struct tmcb_params* ps)
00877 {
00878         if (t->uas.request == 0) {
00879                 DBG("DBG:acc:replyout_handler: No uas.request, local transaction, skipping\n");
00880                 return;
00881         }
00882 
00883              /* acc_onreply is bound to TMCB_REPLY which may be called
00884               * from _reply, like when FR hits; we should not miss this
00885               * event for missed calls either
00886               */
00887         failure_handler(t, type, ps);
00888         if (!should_acc_reply(t, ps->code)) return;
00889         if (is_acc_on(t->uas.request)) log_reply(t, ps->rpl, ps->code, (time_t)*(ps->param));
00890 }
00891 
00892 
00893 /* parse incoming replies before cloning */
00894 static void replyin_handler(struct cell *t, int type, struct tmcb_params* ps)
00895 {
00896              /* validation */
00897         if (t->uas.request == 0) {
00898                 LOG(L_ERR, "ERROR:acc:replyin_handler:replyin_handler: 0 request\n");
00899                 return;
00900         }
00901 
00902              /* don't parse replies in which we are not interested */
00903              /* missed calls enabled ? */
00904         if (((is_invite(t) && ps->code >= 300 && is_mc_on(t->uas.request))
00905              || should_acc_reply(t, ps->code))
00906             && (ps->rpl && ps->rpl != FAKED_REPLY)) {
00907                 parse_headers(ps->rpl, HDR_TO_F, 0);
00908         }
00909 }
00910 
00911 
00912 /* prepare message and transaction context for later accounting */
00913 static void on_req(struct cell* t, int type, struct tmcb_params *ps)
00914 {
00915         time_t req_time;
00916              /* Pass the timestamp of the request as a parameter to callbacks */
00917         req_time = time(0);
00918 
00919         if (is_acc_on(ps->req) || is_mc_on(ps->req)) {
00920                 if (tmb.register_tmcb(0, t, TMCB_RESPONSE_OUT, replyout_handler,
00921                                                                 (void*)req_time, 0) <= 0) {
00922                         LOG(L_ERR, "ERROR:acc:on_req: Error while registering TMCB_RESPONSE_OUT callback\n");
00923                         return;
00924                 }
00925 
00926                 if (report_ack) {
00927                         if (tmb.register_tmcb(0, t, TMCB_E2EACK_IN, ack_handler,
00928                                                                         (void*)req_time, 0) <= 0) {
00929                                 LOG(L_ERR, "ERROR:acc:on_req: Error while registering TMCB_E2EACK_IN callback\n");
00930                                 return;
00931                         }
00932                 }
00933 
00934                 if (tmb.register_tmcb(0, t, TMCB_ON_FAILURE_RO, failure_handler,
00935                                                                 (void*)req_time, 0) <= 0) {
00936                         LOG(L_ERR, "ERROR:acc:on_req: Error while registering TMCB_ON_FAILURE_RO callback\n");
00937                         return;
00938                 }
00939 
00940                 if (tmb.register_tmcb(0, t, TMCB_RESPONSE_IN, replyin_handler,
00941                                                                 (void*)req_time, 0) <= 0) {
00942                         LOG(L_ERR, "ERROR:acc:on_req: Error while registering TMCB_RESPONSE_IN callback\n");
00943                         return;
00944                 }
00945 
00946                      /* do some parsing in advance */
00947                 preparse_req(ps->req);
00948                      /* also, if that is INVITE, disallow silent t-drop */
00949                 if (ps->req->REQ_METHOD == METHOD_INVITE) {
00950                         DBG("DEBUG: noisy_timer set for accounting\n");
00951                         t->flags |= T_NOISY_CTIMER_FLAG;
00952                 }
00953         }
00954 }
00955 
00956 
00957 static int child_init(int rank)
00958 {
00959         if (rank==PROC_INIT || rank==PROC_MAIN || rank==PROC_TCP_MAIN)
00960                 return 0; /* do nothing for the main process */
00961 
00962         if (db_url.s) {
00963                 acc_db = db_ctx("acc_db");
00964                 if (acc_db == NULL) {
00965                         ERR("Error while initializing database layer\n");
00966                         return -1;
00967                 }
00968 
00969                 if (db_add_db(acc_db, db_url.s) < 0) goto error;
00970                 if (db_connect(acc_db) < 0) goto error;
00971 
00972                 write_acc = db_cmd(DB_PUT, acc_db, acc_table.s, NULL, NULL, fld);
00973                 if (write_acc == NULL) {
00974                         ERR("Error while compiling database query\n");
00975                         goto error;
00976                 }
00977 
00978                 write_mc = db_cmd(DB_PUT, acc_db, mc_table.s, NULL, NULL, fld);
00979                 if (write_mc == NULL) {
00980                         ERR("Error while compiling database query\n");
00981                         goto error;
00982                 }
00983 
00984                 return 0;
00985         } else {
00986                 LOG(L_CRIT, "BUG:acc:child_init: null db url\n");
00987                 return -1;
00988         }
00989  error:
00990         if (write_acc) db_cmd_free(write_acc);
00991         write_acc = NULL;
00992         if (write_mc) db_cmd_free(write_mc);
00993         write_mc = NULL;
00994         if (acc_db) db_ctx_free(acc_db);
00995         acc_db = NULL;
00996         return -1;
00997 }
00998 
00999 
01000 static void mod_destroy(void)
01001 {
01002         if (write_mc) db_cmd_free(write_mc);
01003         if (write_acc) db_cmd_free(write_acc);
01004         if (acc_db) {
01005                 db_disconnect(acc_db);
01006                 db_ctx_free(acc_db);
01007         }
01008 }
01009 
01010 
01011 static int mod_init(void)
01012 {
01013         load_tm_f load_tm;
01014 
01015              /* import the TM auto-loading function */
01016         if ( !(load_tm = (load_tm_f)find_export("load_tm", NO_SCRIPT, 0))) {
01017                 LOG(L_ERR, "ERROR:acc:mod_init: can't import load_tm\n");
01018                 return -1;
01019         }
01020              /* let the auto-loading function load all TM stuff */
01021         if (load_tm( &tmb )==-1) return -1;
01022         if (verify_fmt(log_fmt)==-1) return -1;
01023 
01024              /* register callbacks*/
01025              /* listen for all incoming requests  */
01026         if (tmb.register_tmcb( 0, 0, TMCB_REQUEST_IN, on_req, 0, 0) <= 0) {
01027                 LOG(L_ERR,"ERROR:acc:mod_init: cannot register TMCB_REQUEST_IN "
01028                     "callback\n");
01029                 return -1;
01030         }
01031 
01032         init_data(log_fmt);
01033 
01034         if (parse_attrs(&avps, &avps_n, attrs) < 0) {
01035                 ERR("Error while parsing 'attrs' module parameter\n");
01036                 return -1;
01037         }
01038 
01039         return 0;
01040 }