acc_syslog.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 "../../id.h"
00047 #include "attrs.h"
00048 #include "../../modules/tm/tm_load.h"
00049 
00050 /*
00051  * TODO:
00052  * - Quote attribute values properly
00053  * - Save request timestamp
00054  * - Save response timestamp
00055  */
00056 
00057 /*
00058  * a: attr
00059  * c: sip_callid
00060  * d: to_tag
00061  * f: sip_from
00062  * g: flags
00063  * i: inbound_ruri
00064  * m: sip_method
00065  * n: sip_cseq
00066  * o: outbound_ruri
00067  * p: source_ip
00068  * r: from_tag
00069  * s: server_id
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  * R: digest_realm
00078  * P: source_port
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 #define A_SEPARATOR ", " /* must be shorter than ACC! */
00089 #define A_SEPARATOR_LEN (sizeof(A_SEPARATOR) - 1)
00090 #define A_EQ "="
00091 #define A_EQ_LEN (sizeof(A_EQ) - 1)
00092 #define A_EOL "\n\0"
00093 #define A_EOL_LEN (sizeof(A_EOL) - 1)
00094 
00095 #define ACC "ACC: "  /* Prefix of accounting messages in syslog */
00096 #define ACC_LEN (sizeof(ACC) - 1)
00097 
00098 #define ACC_REQUEST "request accounted: "
00099 #define ACC_MISSED "call missed: "
00100 #define ACC_ANSWERED "transaction answered: "
00101 #define ACC_ACKED "request acknowledged: "
00102 
00103 #define NA "n/a"
00104 
00105 #define ATR(atr) atr_arr[cnt].s = A_##atr; atr_arr[cnt].len = sizeof(A_##atr) - 1;
00106 
00107 #define A_ATTRS        "attrs"
00108 #define A_CALLID       "callid"
00109 #define A_TOTAG        "to_tag"
00110 #define A_FROM         "from"
00111 #define A_FLAGS        "flags"
00112 #define A_IURI         "in_ruri"
00113 #define A_METHOD       "sip_method"
00114 #define A_CSEQ         "cseq"
00115 #define A_OURI         "out_ruri"
00116 #define A_FROMTAG      "from_tag"
00117 #define A_TO           "to"
00118 #define A_DIGUSER      "digest_username"
00119 #define A_REQTIMESTAMP "request_timestamp"
00120 #define A_TODID        "to_did"
00121 #define A_FROMURI      "from_uri"
00122 #define A_FROMUID      "from_uid"
00123 #define A_FROMDID      "from_did"
00124 #define A_DIGREALM     "digest_realm"
00125 #define A_STATUS       "sip_status"
00126 #define A_TOURI        "to_uri"
00127 #define A_TOUID        "to_uid"
00128 #define A_RESTIMESTAMP "response_timestamp"
00129 #define A_SRCIP        "src_ip"
00130 #define A_SRCPORT      "src_port"
00131 #define A_SERVERID     "server_id"
00132 
00133 MODULE_VERSION
00134 
00135 struct tm_binds tmb;
00136 
00137 static int mod_init( void );
00138 static int fix_log_flag( modparam_t type, void* val);
00139 static int fix_log_missed_flag( modparam_t type, void* val);
00140 
00141 static int log_level = L_NOTICE; /* noisiness level logging facilities are used */
00142 
00143 static int early_media = 0;         /* Enable/disable early media (183) accounting */
00144 static int failed_transactions = 0; /* Enable/disable accounting of failed (>= 300) transactions */
00145 static int report_cancels = 0;      /* Enable/disable CANCEL reporting */
00146 static int report_ack = 0;          /* Enable/disable end-to-end ACK reports */
00147 static int log_flag = 0;            /* Flag that marks transactions to be accounted */
00148 static int log_missed_flag = 0;     /* Transaction having this flag set will be accounted in missed calls when fails */
00149 static char* log_fmt = ALL_LOG_FMT; /* Formating string that controls what information will be collected and accounted */
00150 
00151 /* Attribute-value pairs */
00152 static char* attrs = "";
00153 avp_ident_t* avps;
00154 int avps_n;
00155 
00156 static int acc_log_request0(struct sip_msg *rq, char *p1, char *p2);
00157 static int acc_log_missed0(struct sip_msg *rq, char *p1, char *p2);
00158 static int acc_log_request1(struct sip_msg *rq, char *p1, char *p2);
00159 static int acc_log_missed1(struct sip_msg *rq, char *p1, char *p2);
00160 
00161 static str na = STR_STATIC_INIT(NA);
00162 
00163 
00164 static cmd_export_t cmds[] = {
00165         {"acc_syslog_log",    acc_log_request0, 0, 0,               REQUEST_ROUTE | FAILURE_ROUTE},
00166         {"acc_syslog_missed", acc_log_missed0,  0, 0,               REQUEST_ROUTE | FAILURE_ROUTE},
00167         {"acc_syslog_log",    acc_log_request1, 1, fixup_var_str_1, REQUEST_ROUTE | FAILURE_ROUTE},
00168         {"acc_syslog_missed", acc_log_missed1,  1, fixup_var_str_1, REQUEST_ROUTE | FAILURE_ROUTE},
00169         {0, 0, 0, 0, 0}
00170 };
00171 
00172 
00173 static param_export_t params[] = {
00174         {"early_media",         PARAM_INT, &early_media         },
00175         {"failed_transactions", PARAM_INT, &failed_transactions },
00176         {"report_ack",          PARAM_INT, &report_ack          },
00177         {"report_cancels",      PARAM_INT, &report_cancels      },
00178         {"log_flag",            PARAM_INT, &log_flag            },
00179         {"log_flag",            PARAM_STRING|PARAM_USE_FUNC, fix_log_flag},
00180         {"log_missed_flag",     PARAM_INT, &log_missed_flag     },
00181         {"log_missed_flag",     PARAM_STRING|PARAM_USE_FUNC, fix_log_missed_flag},
00182         {"log_level",           PARAM_INT, &log_level           },
00183         {"log_fmt",             PARAM_STRING, &log_fmt          },
00184         {"attrs",               PARAM_STRING, &attrs            },
00185         {0, 0, 0}
00186 };
00187 
00188 
00189 struct module_exports exports= {
00190         "acc_syslog",
00191         cmds,     /* exported functions */
00192         0,        /* RPC methods */
00193         params,   /* exported params */
00194         mod_init, /* initialization module */
00195         0,        /* response function */
00196         0,        /* destroy function */
00197         0,        /* oncancel function */
00198         0         /* per-child init function */
00199 };
00200 
00201 
00202 
00203 /* fixes log_flag param (resolves possible named flags) */
00204 static int fix_log_flag( modparam_t type, void* val)
00205 {
00206         return fix_flag(type, val, "acc_syslog", "log_flag", &log_flag);
00207 }
00208 
00209 
00210 
00211 /* fixes log_missed_flag param (resolves possible named flags) */
00212 static int fix_log_missed_flag( modparam_t type, void* val)
00213 {
00214         return fix_flag(type, val, "acc_syslog", "log_missed_flag", &log_missed_flag);
00215 }
00216 
00217 
00218 #define TM_BUF_LEN sizeof("10000-12-31 23:59:59")
00219 
00220 static inline int convert_time(str* buf, time_t time)
00221 {
00222         struct tm* tm;
00223 
00224         if (!buf->s || buf->len < TM_BUF_LEN) {
00225                 LOG(L_ERR, "ERROR:acc:convert_time: Buffer too short\n");
00226                 return -1;
00227         }
00228 
00229         tm = gmtime(&time);
00230         buf->len = strftime(buf->s, buf->len, "%Y-%m-%d %H:%M:%S", tm);
00231         return 0;
00232 }
00233 
00234 
00235 static inline int skip_cancel(struct sip_msg *msg)
00236 {
00237         return (msg->REQ_METHOD == METHOD_CANCEL) && report_cancels == 0;
00238 }
00239 
00240 
00241 /*
00242  * Append a constant string, uses sizeof to figure the length
00243  * of the string
00244  */
00245 #define append(buf, ptr)                         \
00246     do {                                         \
00247         memcpy((buf).s, (ptr), sizeof(ptr) - 1); \
00248         (buf).s += sizeof(ptr) - 1;              \
00249         (buf).len -= sizeof(ptr) - 1;            \
00250     } while(0);
00251 
00252 
00253 #define str_append_str(buf, str)                          \
00254     do {                                      \
00255         memcpy((buf).s, (str).s, (str).len);  \
00256         (buf).s += (str).len;                 \
00257         (buf).len -= (str).len;               \
00258     } while(0);
00259 
00260 
00261 int verify_fmt(char *fmt) {
00262 
00263         if (!fmt) {
00264                 LOG(L_ERR, "ERROR:acc:verify_fmt: formatting string zero\n");
00265                 return -1;
00266         }
00267 
00268         if (!(*fmt)) {
00269                 LOG(L_ERR, "ERROR:acc:verify_fmt: formatting string empty\n");
00270                 return -1;
00271         }
00272 
00273         if (strlen(fmt) > ALL_LOG_FMT_LEN) {
00274                 LOG(L_ERR, "ERROR:acc:verify_fmt: formatting string too long\n");
00275                 return -1;
00276         }
00277 
00278         while(*fmt) {
00279                 if (!strchr(ALL_LOG_FMT, *fmt)) {
00280                         LOG(L_ERR, "ERROR:acc:verify_fmt: char in log_fmt invalid: %c\n", *fmt);
00281                         return -1;
00282                 }
00283                 fmt++;
00284         }
00285         return 1;
00286 }
00287 
00288 
00289 /*
00290  * Return true if accounting is enabled and the
00291  * transaction is marked for accounting
00292  */
00293 static inline int is_acc_on(struct sip_msg *rq)
00294 {
00295         return log_flag && isflagset(rq, log_flag) == 1;
00296 }
00297 
00298 
00299 /*
00300  * Return true if missed_call accounting is enabled
00301  * and the transaction has the flag set
00302  */
00303 static inline int is_mc_on(struct sip_msg *rq)
00304 {
00305         return log_missed_flag && isflagset(rq, log_missed_flag) == 1;
00306 }
00307 
00308 
00309 static inline void preparse_req(struct sip_msg *rq)
00310 {
00311              /* try to parse from for From-tag for accounted transactions;
00312               * don't be worried about parsing outcome -- if it failed,
00313               * we will report N/A. There is no need to parse digest credentials
00314               * here even if we account them, because the authentication function
00315               * will do it before us and if not then we will account n/a.
00316               */
00317         parse_headers(rq, HDR_CALLID_F | HDR_FROM_F | HDR_TO_F | HDR_CSEQ_F, 0 );
00318         parse_from_header(rq);
00319 }
00320 
00321 
00322 /* is this reply of interest for accounting ? */
00323 static inline int should_acc_reply(struct cell* t, int code)
00324 {
00325         struct sip_msg *r;
00326 
00327         r = t->uas.request;
00328 
00329              /* validation */
00330         if (r == 0) {
00331                 LOG(L_ERR, "ERROR:acc:should_acc_reply: 0 request\n");
00332                 return 0;
00333         }
00334 
00335              /* negative transactions reported otherwise only if explicitly
00336               * demanded */
00337         if (!failed_transactions && code >= 300) return 0;
00338         if (!is_acc_on(r)) return 0;
00339         if (skip_cancel(r)) return 0;
00340         if (code < 200 && ! (early_media && code == 183)) return 0;
00341         return 1; /* seed is through, we will account this reply */
00342 }
00343 
00344 
00345 /* Extract username attribute from authorized credentials */
00346 static inline str* cred_user(struct sip_msg* rq)
00347 {
00348         struct hdr_field* h;
00349         auth_body_t* cred;
00350 
00351         get_authorized_cred(rq->proxy_auth, &h);
00352         if (!h) get_authorized_cred(rq->authorization, &h);
00353         if (!h) return 0;
00354         cred = (auth_body_t*)(h->parsed);
00355         if (!cred || !cred->digest.username.user.len)
00356                 return 0;
00357         return &cred->digest.username.user;
00358 }
00359 
00360 
00361 /* Extract realm attribute from authorized credentials */
00362 static inline str* cred_realm(struct sip_msg* rq)
00363 {
00364         str* realm;
00365         struct hdr_field* h;
00366         auth_body_t* cred;
00367 
00368         get_authorized_cred(rq->proxy_auth, &h);
00369         if (!h) get_authorized_cred(rq->authorization, &h);
00370         if (!h) return 0;
00371         cred = (auth_body_t*)(h->parsed);
00372         if (!cred) return 0;
00373         realm = GET_REALM(&cred->digest);
00374         if (!realm->len || !realm->s) {
00375                 return 0;
00376         }
00377         return realm;
00378 }
00379 
00380 
00381 /* Return To header field from the request in case of faked reply or
00382  * missing To header field in the reply
00383  */
00384 static inline struct hdr_field* valid_to(struct cell* t, struct sip_msg* reply)
00385 {
00386         if (reply == FAKED_REPLY || !reply || !reply->to) {
00387                 return t->uas.request->to;
00388         } else {
00389                 return reply->to;
00390         }
00391 }
00392 
00393 
00394 /* create an array of str's for accounting using a formatting string;
00395  * this is the heart of the accounting module -- it prints whatever
00396  * requested in a way, that can be used for syslog, radius,
00397  * sql, whatsoever
00398  * tm sip_msg_clones does not clone (shmmem-zed) parsed fields, other then Via1,2. Such fields clone now or use from rq_rp
00399  */
00400 static int fmt2strar(char *fmt,             /* what would you like to account ? */
00401                      struct sip_msg *rq,    /* accounted message */
00402                      str* ouri,             /* Outbound Request-URI */
00403                      struct hdr_field *to,  /* To header field (used to extract tag) */
00404                      str *phrase,
00405                      int *total_len,        /* total length of accounted values */
00406                      int *attr_len,         /* total length of accounted attribute names */
00407                      str **val_arr,         /* that's the output -- must have MAX_ACC_COLUMNS */
00408                      str *atr_arr,
00409                      time_t req_time)       /* Timestamp of the request */
00410 {
00411         static char flags_buf[INT2STR_MAX_LEN], tm_buf[TM_BUF_LEN],
00412                 rqtm_buf[TM_BUF_LEN], srcip_buf[IP_ADDR_MAX_STR_SIZE],
00413                 srcport_buf[INT2STR_MAX_LEN], serverid_buf[INT2STR_MAX_LEN];
00414         int cnt, tl, al;
00415         struct to_body* from, *pto;
00416         static str mycode, flags, tm_s, rqtm_s, src_ip, src_port, from_uid, to_uid, server_id_str;
00417         str *cr, *at;
00418         struct cseq_body *cseq;
00419         char* p;
00420 
00421         cnt = tl = al = 0;
00422 
00423              /* we don't care about parsing here; either the function
00424               * was called from script, in which case the wrapping function
00425               * is supposed to parse, or from reply processing in which case
00426               * TM should have preparsed from REQUEST_IN callback; what's not
00427               * here is replaced with NA
00428               */
00429         while(*fmt) {
00430                 if (cnt == ALL_LOG_FMT_LEN) {
00431                         LOG(L_ERR, "ERROR:acc:fmt2strar: Formatting string is too long\n");
00432                         return 0;
00433                 }
00434 
00435                 switch(*fmt) {
00436                 case 'a': /* attr */
00437                         at = print_attrs(avps, avps_n, 1);
00438                         if (!at) {
00439                                 val_arr[cnt] = &na;
00440                         } else {
00441                                 val_arr[cnt] = at;
00442                         }
00443                         ATR(ATTRS);
00444                         break;
00445 
00446                 case 'c': /* sip_callid */
00447                         val_arr[cnt] = (rq->callid && rq->callid->body.len) ? &rq->callid->body : &na;
00448                         ATR(CALLID);
00449                         break;
00450 
00451                 case 'd': /* to_tag */
00452                         val_arr[cnt] = (to && (pto = (struct to_body*)(to->parsed)) && pto->tag_value.len) ? & pto->tag_value : &na;
00453                         ATR(TOTAG);
00454                         break;
00455 
00456                 case 'f': /* sip_from */
00457                         val_arr[cnt] = (rq->from && rq->from->body.len) ? &rq->from->body : &na;
00458                         ATR(FROM);
00459                         break;
00460 
00461                 case 'g': /* flags */
00462                         p = int2str(rq->flags, &flags.len);
00463                         memcpy(flags_buf, p, flags.len);
00464                         flags.s = flags_buf;
00465                         val_arr[cnt] = &flags;
00466                         ATR(FLAGS);
00467                         break;
00468 
00469                 case 'i': /* inbound_ruri */
00470                         val_arr[cnt] = &rq->first_line.u.request.uri;
00471                         ATR(IURI);
00472                         break;
00473 
00474                 case 'm': /* sip_method */
00475                         val_arr[cnt] = &rq->first_line.u.request.method;
00476                         ATR(METHOD);
00477                         break;
00478 
00479                 case 'n': /* sip_cseq */
00480                         if (rq->cseq && (cseq = get_cseq(rq)) && cseq->number.len) val_arr[cnt] = &cseq->number;
00481                         else val_arr[cnt]=&na;
00482                         ATR(CSEQ);
00483                         break;
00484 
00485                 case 'o': /* outbound_ruri */
00486                         val_arr[cnt] = ouri;
00487                         ATR(OURI);
00488                         break;
00489 
00490                 case 'p': /* source_ip */
00491                              /* We need to make a copy of the string here because ip_addr2a uses static
00492                               * buffer and subseqent calls to the function would destroy the result
00493                               */
00494                         src_ip.s = srcip_buf;
00495                         p = ip_addr2a(&rq->rcv.src_ip);
00496                         src_ip.len = strlen(p);
00497                         memcpy(src_ip.s, p, src_ip.len);
00498                         val_arr[cnt] = &src_ip;
00499                         ATR(SRCIP);
00500                         break;
00501 
00502                 case 'r': /* from_tag */
00503                         if (rq->from && (from = get_from(rq)) && from->tag_value.len) {
00504                                 val_arr[cnt] = &from->tag_value;
00505                         } else {
00506                                 val_arr[cnt] = &na;
00507                         }
00508                         ATR(FROMTAG);
00509                         break;
00510 
00511                 case 's': /* server_id */
00512                         p = int2str(server_id, &server_id_str.len);
00513                         memcpy(serverid_buf, p, server_id_str.len);
00514                         server_id_str.s = serverid_buf;
00515                         val_arr[cnt] = &server_id_str;
00516                         ATR(SERVERID);
00517                         break;
00518 
00519                 case 't': /* sip_to */
00520                         val_arr[cnt] = (to && to->body.len) ? &to->body : &na;
00521                         ATR(TO);
00522                         break;
00523 
00524                 case 'u': /* digest_username */
00525                         cr = cred_user(rq);
00526                         if (cr) val_arr[cnt] = cr;
00527                         else val_arr[cnt] = &na;
00528                         ATR(DIGUSER);
00529                         break;
00530 
00531                 case 'x': /* request_timestamp */
00532                         rqtm_s.s = rqtm_buf;
00533                         rqtm_s.len = TM_BUF_LEN;
00534                         convert_time(&rqtm_s, req_time);
00535                         val_arr[cnt] = &rqtm_s;
00536                         ATR(REQTIMESTAMP);
00537                         break;
00538 
00539                 case 'D': /* to_did */
00540                         val_arr[cnt] = &na;
00541                         ATR(TODID);
00542                         break;
00543 
00544                 case 'F': /* from_uri */
00545                         if (rq->from && (from = get_from(rq)) && from->uri.len) {
00546                                 val_arr[cnt] = &from->uri;
00547                         } else val_arr[cnt] = &na;
00548                         ATR(FROMURI);
00549                         break;
00550 
00551                 case 'I': /* from_uid */
00552                         if (get_from_uid(&from_uid, rq) < 0) {
00553                                 val_arr[cnt] = &na;
00554                         } else {
00555                                 val_arr[cnt] = &from_uid;
00556                         }
00557                         ATR(FROMUID);
00558                         break;
00559 
00560                 case 'M': /* from_did */
00561                         val_arr[cnt] = &na;
00562                         ATR(FROMDID);
00563                         break;
00564 
00565                 case 'P': /* source_port */
00566                         p = int2str(rq->rcv.src_port, &src_port.len);
00567                         memcpy(srcport_buf, p, src_port.len);
00568                         src_port.s = srcport_buf;
00569                         val_arr[cnt] = &src_port;
00570                         ATR(SRCPORT);
00571                         break;
00572 
00573                 case 'R': /* digest_realm */
00574                         cr = cred_realm(rq);
00575                         if (cr) val_arr[cnt] = cr;
00576                         else val_arr[cnt] = &na;
00577                         ATR(DIGREALM);
00578                         break;
00579 
00580                 case 'S': /* sip_status */
00581                         if (phrase->len >= 3) {
00582                                 mycode.s = phrase->s;
00583                                 mycode.len = 3;
00584                                 val_arr[cnt] = &mycode;
00585                         } else val_arr[cnt] = &na;
00586                         ATR(STATUS);
00587                         break;
00588 
00589                 case 'T': /* to_uri */
00590                         if (rq->to && (pto = get_to(rq)) && pto->uri.len) val_arr[cnt] = &pto->uri;
00591                         else val_arr[cnt] = &na;
00592                         ATR(TOURI);
00593                         break;
00594 
00595                 case 'U': /* to_uid */
00596                         if (get_to_uid(&to_uid, rq) < 0) {
00597                                 val_arr[cnt] = &na;
00598                         } else {
00599                                 val_arr[cnt] = &to_uid;
00600                         }
00601                         ATR(TOUID);
00602                         break;
00603 
00604                 case 'X': /* response_timestamp */
00605                         tm_s.s = tm_buf;
00606                         tm_s.len = TM_BUF_LEN;
00607                         convert_time(&tm_s, time(0));
00608                         val_arr[cnt] = &tm_s;
00609                         ATR(RESTIMESTAMP);
00610                         break;
00611 
00612                 default:
00613                         LOG(L_CRIT, "BUG:acc:fmt2strar: unknown char: %c\n", *fmt);
00614                         return 0;
00615                 } /* switch (*fmt) */
00616 
00617                 tl += val_arr[cnt]->len;
00618                 al += atr_arr[cnt].len;
00619                 fmt++;
00620                 cnt++;
00621         } /* while (*fmt) */
00622 
00623         *total_len = tl;
00624         *attr_len = al;
00625         return cnt;
00626 }
00627 
00628 
00629 
00630 static int log_request(struct sip_msg* rq, str* ouri, struct hdr_field* to, str* txt, str* phrase, time_t req_time)
00631 {
00632         static str* val_arr[ALL_LOG_FMT_LEN];
00633         static str atr_arr[ALL_LOG_FMT_LEN];
00634         int len, attr_cnt, attr_len, i;
00635         char *log_msg;
00636         str buf;
00637 
00638         if (skip_cancel(rq)) return 1;
00639 
00640         attr_cnt = fmt2strar(log_fmt, rq, ouri, to, phrase, &len, &attr_len, val_arr, atr_arr, req_time);
00641         if (!attr_cnt) {
00642                 LOG(L_ERR, "ERROR:acc:log_request: fmt2strar failed\n");
00643                 return -1;
00644         }
00645 
00646         len += attr_len + ACC_LEN + txt->len + A_EOL_LEN + attr_cnt * (A_SEPARATOR_LEN + A_EQ_LEN) - A_SEPARATOR_LEN;
00647         log_msg = pkg_malloc(len);
00648         if (!log_msg) {
00649                 LOG(L_ERR, "ERROR:acc:log_request: No memory left for %d bytes\n", len);
00650                 return -1;
00651         }
00652 
00653              /* skip leading text and begin with first item's
00654               * separator ", " which will be overwritten by the
00655               * leading text later
00656               * */
00657         buf.s = log_msg + ACC_LEN + txt->len - A_SEPARATOR_LEN;
00658         buf.len = len - ACC_LEN - txt->len + A_SEPARATOR_LEN;
00659 
00660         for (i = 0; i < attr_cnt; i++) {
00661                 append(buf, A_SEPARATOR);
00662                 str_append_str(buf, atr_arr[i]);
00663                 append(buf, A_EQ);
00664                 str_append_str(buf, *(val_arr[i]))
00665         }
00666 
00667              /* terminating text */
00668         append(buf, A_EOL);
00669 
00670              /* leading text */
00671         buf.s = log_msg;
00672         buf.len = len;
00673         append(buf, ACC);
00674         str_append_str(buf, *txt);
00675 
00676         LOG(log_level, "%s", log_msg);
00677         pkg_free(log_msg);
00678         return 1;
00679 }
00680 
00681 
00682 static void log_reply(struct cell* t , struct sip_msg* reply, unsigned int code, time_t req_time)
00683 {
00684         str code_str, *ouri;
00685         static str lead = STR_STATIC_INIT(ACC_ANSWERED);
00686         static char code_buf[INT2STR_MAX_LEN];
00687         char* p;
00688 
00689         p = int2str(code, &code_str.len);
00690         memcpy(code_buf, p, code_str.len);
00691         code_str.s = code_buf;
00692 
00693         if (t->relayed_reply_branch >= 0) {
00694             ouri = &t->uac[t->relayed_reply_branch].uri;
00695         } else {
00696             ouri = GET_NEXT_HOP(t->uas.request);
00697         }
00698 
00699         log_request(t->uas.request, ouri, valid_to(t,reply), &lead, &code_str, req_time);
00700 }
00701 
00702 
00703 static void log_ack(struct cell* t , struct sip_msg *ack, time_t req_time)
00704 {
00705         struct sip_msg *rq;
00706         struct hdr_field *to;
00707         static str lead = STR_STATIC_INIT(ACC_ACKED);
00708         static char code_buf[INT2STR_MAX_LEN];
00709         str code_str;
00710         char* p;
00711 
00712         rq = t->uas.request;
00713         if (ack->to) to = ack->to;
00714         else to = rq->to;
00715         p = int2str(t->uas.status, &code_str.len);
00716         memcpy(code_buf, p, code_str.len);
00717         code_str.s = code_buf;
00718         log_request(ack, GET_RURI(ack), to, &lead, &code_str, req_time);
00719 }
00720 
00721 
00722 static void log_missed(struct cell* t, struct sip_msg* reply, unsigned int code, time_t req_time)
00723 {
00724         str acc_text, *ouri;
00725         static str leading_text = STR_STATIC_INIT(ACC_MISSED);
00726 
00727         get_reply_status(&acc_text, reply, code);
00728         if (acc_text.s == 0) {
00729                 LOG(L_ERR, "ERROR:acc:log_missed: get_reply_status failed\n" );
00730                 return;
00731         }
00732 
00733         if (t->relayed_reply_branch >= 0) {
00734             ouri = &t->uac[t->relayed_reply_branch].uri;
00735         } else {
00736             ouri = GET_NEXT_HOP(t->uas.request);
00737         }
00738 
00739         log_request(t->uas.request, ouri, valid_to(t, reply), &leading_text, &acc_text, req_time);
00740         pkg_free(acc_text.s);
00741 }
00742 
00743 
00744 /* these wrappers parse all what may be needed; they don't care about
00745  * the result -- accounting functions just display "unavailable" if there
00746  * is nothing meaningful
00747  */
00748 static int acc_log_request1(struct sip_msg *rq, char* p1, char* p2)
00749 {
00750         str phrase;
00751         str txt = STR_STATIC_INIT(ACC_REQUEST);
00752 
00753         if (get_str_fparam(&phrase, rq, (fparam_t*)p1) < 0) {
00754             phrase.s = 0;
00755             phrase.len = 0;
00756         }
00757         preparse_req(rq);
00758         return log_request(rq, GET_RURI(rq), rq->to, &txt, &phrase, time(0));
00759 }
00760 
00761 
00762 /* these wrappers parse all what may be needed; they don't care about
00763  * the result -- accounting functions just display "unavailable" if there
00764  * is nothing meaningful
00765  */
00766 static int acc_log_missed1(struct sip_msg *rq, char* p1, char* p2)
00767 {
00768         str phrase;
00769         str txt = STR_STATIC_INIT(ACC_MISSED);
00770 
00771         if (get_str_fparam(&phrase, rq, (fparam_t*)p1) < 0) {
00772             phrase.s = 0;
00773             phrase.len = 0;
00774         }
00775         preparse_req(rq);
00776         return log_request(rq, GET_RURI(rq), rq->to, &txt, &phrase, time(0));
00777 }
00778 
00779 
00780 
00781 /* these wrappers parse all what may be needed; they don't care about
00782  * the result -- accounting functions just display "unavailable" if there
00783  * is nothing meaningful
00784  */
00785 static int acc_log_request0(struct sip_msg *rq, char* p1, char* p2)
00786 {
00787         static str phrase = STR_NULL;
00788         str txt = STR_STATIC_INIT(ACC_REQUEST);
00789         return log_request(rq, GET_RURI(rq), rq->to, &txt, &phrase, time(0));
00790 }
00791 
00792 
00793 /* these wrappers parse all what may be needed; they don't care about
00794  * the result -- accounting functions just display "unavailable" if there
00795  * is nothing meaningful
00796  */
00797 static int acc_log_missed0(struct sip_msg *rq, char* p1, char* p2)
00798 {
00799         static str phrase = STR_NULL;
00800         str txt = STR_STATIC_INIT(ACC_MISSED);
00801         preparse_req(rq);
00802         return log_request(rq, GET_RURI(rq), rq->to, &txt, &phrase, time(0));
00803 }
00804 
00805 
00806 
00807 static void ack_handler(struct cell* t, int type, struct tmcb_params* ps)
00808 {
00809         if (is_acc_on(t->uas.request)) {
00810                 preparse_req(ps->req);
00811                 log_ack(t, ps->req, (time_t)*(ps->param));
00812         }
00813 }
00814 
00815 
00816 /* initiate a report if we previously enabled MC accounting for this t */
00817 static void failure_handler(struct cell *t, int type, struct tmcb_params* ps)
00818 {
00819         /* validation */
00820         if (t->uas.request == 0) {
00821                 DBG("DBG:acc:failure_handler: No uas.request, skipping local transaction\n");
00822                 return;
00823         }
00824 
00825         if (is_invite(t) && ps->code >= 300) {
00826                 if (is_mc_on(t->uas.request)) {
00827                         log_missed(t, ps->rpl, ps->code, (time_t)*(ps->param));
00828                         resetflag(t->uas.request, log_missed_flag);
00829                 }
00830         }
00831 }
00832 
00833 
00834 /* initiate a report if we previously enabled accounting for this t */
00835 static void replyout_handler(struct cell* t, int type, struct tmcb_params* ps)
00836 {
00837         if (t->uas.request == 0) {
00838                 DBG("DBG:acc:replyout_handler: No uas.request, local transaction, skipping\n");
00839                 return;
00840         }
00841 
00842              /* acc_onreply is bound to TMCB_REPLY which may be called
00843               * from _reply, like when FR hits; we should not miss this
00844               * event for missed calls either
00845               */
00846         failure_handler(t, type, ps);
00847         if (!should_acc_reply(t, ps->code)) return;
00848         if (is_acc_on(t->uas.request)) log_reply(t, ps->rpl, ps->code, (time_t)*(ps->param));
00849 }
00850 
00851 
00852 /* parse incoming replies before cloning */
00853 static void replyin_handler(struct cell *t, int type, struct tmcb_params* ps)
00854 {
00855              /* validation */
00856         if (t->uas.request == 0) {
00857                 LOG(L_ERR, "ERROR:acc:replyin_handler:replyin_handler: 0 request\n");
00858                 return;
00859         }
00860 
00861              /* don't parse replies in which we are not interested */
00862              /* missed calls enabled ? */
00863         if (((is_invite(t) && ps->code >= 300 && is_mc_on(t->uas.request))
00864              || should_acc_reply(t, ps->code))
00865             && (ps->rpl && ps->rpl != FAKED_REPLY)) {
00866                 parse_headers(ps->rpl, HDR_TO_F, 0);
00867         }
00868 }
00869 
00870 
00871 /* prepare message and transaction context for later accounting */
00872 void on_req(struct cell* t, int type, struct tmcb_params *ps)
00873 {
00874         time_t req_time;
00875              /* Pass the timestamp of the request as a parameter to callbacks */
00876         req_time = time(0);
00877 
00878         if (is_acc_on(ps->req) || is_mc_on(ps->req)) {
00879                 if (tmb.register_tmcb(0, t, TMCB_RESPONSE_OUT, replyout_handler,
00880                                                                 (void*)req_time, 0) <= 0) {
00881                         LOG(L_ERR, "ERROR:acc:on_req: Error while registering TMCB_RESPONSE_OUT callback\n");
00882                         return;
00883                 }
00884 
00885                 if (report_ack) {
00886                         if (tmb.register_tmcb(0, t, TMCB_E2EACK_IN, ack_handler,
00887                                                                         (void*)req_time, 0) <= 0) {
00888                                 LOG(L_ERR, "ERROR:acc:on_req: Error while registering TMCB_E2EACK_IN callback\n");
00889                                 return;
00890                         }
00891                 }
00892 
00893                 if (tmb.register_tmcb(0, t, TMCB_ON_FAILURE_RO, failure_handler,
00894                                                                 (void*)req_time, 0) <= 0) {
00895                         LOG(L_ERR, "ERROR:acc:on_req: Error while registering TMCB_ON_FAILURE_RO callback\n");
00896                         return;
00897                 }
00898 
00899                 if (tmb.register_tmcb(0, t, TMCB_RESPONSE_IN, replyin_handler,
00900                                                                 (void*)req_time, 0) <= 0) {
00901                         LOG(L_ERR, "ERROR:acc:on_req: Error while registering TMCB_RESPONSE_IN callback\n");
00902                         return;
00903                 }
00904 
00905                      /* do some parsing in advance */
00906                 preparse_req(ps->req);
00907                      /* also, if that is INVITE, disallow silent t-drop */
00908                 if (ps->req->REQ_METHOD == METHOD_INVITE) {
00909                         DBG("DEBUG: noisy_timer set for accounting\n");
00910                         t->flags |= T_NOISY_CTIMER_FLAG;
00911                 }
00912         }
00913 }
00914 
00915 
00916 static int mod_init(void)
00917 {
00918         load_tm_f load_tm;
00919 
00920              /* import the TM auto-loading function */
00921         if ( !(load_tm=(load_tm_f)find_export("load_tm", NO_SCRIPT, 0))) {
00922                 LOG(L_ERR, "ERROR:acc:mod_init: can't import load_tm\n");
00923                 return -1;
00924         }
00925              /* let the auto-loading function load all TM stuff */
00926         if (load_tm( &tmb )==-1) return -1;
00927         if (verify_fmt(log_fmt)==-1) return -1;
00928 
00929              /* register callbacks*/
00930              /* listen for all incoming requests  */
00931         if (tmb.register_tmcb( 0, 0, TMCB_REQUEST_IN, on_req, 0, 0) <= 0) {
00932                 LOG(L_ERR,"ERROR:acc:mod_init: cannot register TMCB_REQUEST_IN "
00933                     "callback\n");
00934                 return -1;
00935         }
00936 
00937         if (parse_attrs(&avps, &avps_n, attrs) < 0) {
00938                 ERR("Error while parsing 'attrs' module parameter\n");
00939                 return -1;
00940         }
00941 
00942         return 0;
00943 }