Merge branch 'master' into treimann/acc-cdr
authorTimo Reimann <timo.reimann@1und1.de>
Mon, 15 Aug 2011 12:58:20 +0000 (14:58 +0200)
committerTimo Reimann <timo.reimann@1und1.de>
Mon, 15 Aug 2011 12:58:20 +0000 (14:58 +0200)
* master: (76 commits)
  core: new core event for received datagrams
  Added documentation to the DMQ module
  htable: docs updated with db_expires parameter
  htable: control load/save of expires value from db via parameter
  srdb1: updated the definition of siptrace table to include time_us column
  modules_k/siptrace: Add time to x-headers
  modules_k/siptrace: Add column time_us
  modules_k/siptrace: Add "x-headers" feature
  modules_k/siptrace: Add trace_to_database configuration parameter
  modules_k/siptrace: separately store to db and/or send duplicate
  pua_xmpp: fixes to SIP-XMPP presence status translations
  Expose terminate_dlg through C API
  Expose terminate_dlg through C API
  Added terminate_dlg description
  Expose terminate_dlg through C API
  modules_k/dialog: Fix a bug that would cause a segfault when caller bind address information could not be retrieved from database and "dlg_list" was fifo-issued.
  modules_k/dialog: Improve dialog timer list handling.
  modules/tm, modules_k/pua: Fix for concurrency issue in PUA module
  tm: keep internal retr. intervals in ms
  lib/srdb1/schema: forgot to increase version number of pua table
  ...

Conflicts:
modules_k/dialog/dialog.c
modules_k/dialog/dlg_load.h

26 files changed:
modules/mediaproxy/mediaproxy.c
modules_k/acc/acc.h
modules_k/acc/acc_cdr.c [new file with mode: 0644]
modules_k/acc/acc_cdr.h [new file with mode: 0644]
modules_k/acc/acc_mod.c
modules_k/acc/acc_mod.h
modules_k/acc/doc/acc.xml
modules_k/acc/doc/acc_admin.xml
modules_k/call_control/call_control.c
modules_k/dialog/dialog.c
modules_k/dialog/dlg_cb.c
modules_k/dialog/dlg_cb.h
modules_k/dialog/dlg_handlers.c
modules_k/dialog/dlg_handlers.h
modules_k/dialog/dlg_hash.c
modules_k/dialog/dlg_load.h
modules_k/dialog/dlg_profile.c
modules_k/dialog/dlg_profile.h
modules_k/dialog/dlg_req_within.c
modules_k/dialog/dlg_var.c
modules_k/dialog/dlg_var.h
modules_k/dialog/doc/dialog_admin.xml
modules_k/dialog/doc/dialog_devel.xml
modules_k/nat_traversal/nat_traversal.c
modules_k/qos/qos_handlers.c
modules_k/sst/sst_handlers.c

index c17971f..614c7a4 100644 (file)
@@ -1903,14 +1903,14 @@ __free_dialog_data(void *data)
 static void
 __dialog_requests(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
 {
-    use_media_proxy(_params->msg, get_dialog_id(dlg), (ice_candidate_data*)*_params->param);
+    use_media_proxy(_params->req, get_dialog_id(dlg), (ice_candidate_data*)*_params->param);
 }
 
 
 static void
 __dialog_replies(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
 {
-    struct sip_msg *reply = _params->msg;
+    struct sip_msg *reply = _params->rpl;
 
     if (reply == FAKED_REPLY)
         return;
@@ -1934,7 +1934,7 @@ __dialog_ended(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
 static void
 __dialog_created(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
 {
-    struct sip_msg *request = _params->msg;
+    struct sip_msg *request = _params->req;
     ice_candidate_data *ice_data;
 
     if (request->REQ_METHOD != METHOD_INVITE)
index 659a04d..cd0f642 100644 (file)
@@ -69,6 +69,7 @@
 #define A_STATUS_LEN (sizeof(A_STATUS)-1)
 
 #define A_SEPARATOR_CHR ';'
+#define A_SEPARATOR_CHR_2 ' '
 #define A_EQ_CHR '='
 
 #define MAX_SYSLOG_SIZE  65536
diff --git a/modules_k/acc/acc_cdr.c b/modules_k/acc/acc_cdr.c
new file mode 100644 (file)
index 0000000..e8678df
--- /dev/null
@@ -0,0 +1,692 @@
+/*
+ * Accounting module
+ *
+ * Copyright (C) 2011 - Sven Knoblich 1&1 Internet AG
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/*! \file
+ * \ingroup acc
+ * \brief Acc:: File to handle CRD generation by the help of the dialog-module
+ *
+ * - Module: \ref acc
+ */
+
+/*! \defgroup acc ACC :: The Kamailio accounting Module
+ *
+ * The ACC module is used to account transactions information to
+ *  different backends like syslog, SQL, RADIUS and DIAMETER (beta
+ *  version).
+ *
+ */
+#include "../../modules/tm/tm_load.h"
+#include "../dialog/dlg_load.h"
+
+#include "acc_api.h"
+#include "acc_cdr.h"
+#include "acc_mod.h"
+#include "acc_extra.h"
+#include "acc.h"
+
+#include <sys/timeb.h>
+
+struct dlg_binds dlgb;
+struct acc_extra* cdr_extra = NULL;
+int cdr_facility = LOG_DAEMON;
+
+static const str start_id = { "sz", 2};
+static const str end_id = { "ez", 2};
+static const str duration_id = { "d", 1};
+static const str zero_duration = { "0", 1};
+static const struct timeb time_error = {0,0,0,0};
+static const char time_separator = {'.'};
+static const int milliseconds_max = 1000;
+static const unsigned int time_buffer_length = 256;
+static const str empty_string = { "", 0};
+
+// buffers which are used to collect the crd data for writing
+static str cdr_attrs[ MAX_CDR_CORE + MAX_CDR_EXTRA];
+static str cdr_value_array[ MAX_CDR_CORE + MAX_CDR_EXTRA];
+static int cdr_int_arr[ MAX_CDR_CORE + MAX_CDR_EXTRA];
+static char cdr_type_array[ MAX_CDR_CORE + MAX_CDR_EXTRA];
+
+extern struct tm_binds tmb;
+
+/* compare two times */
+static int is_time_equal( struct timeb first_time,
+                          struct timeb second_time)
+{
+    if( first_time.time == second_time.time &&
+        first_time.millitm == second_time.millitm &&
+        first_time.timezone == second_time.timezone &&
+        first_time.dstflag == second_time.dstflag )
+    {
+        return 1;
+    }
+
+    return 0;
+}
+
+/* write all basic information to buffers(e.g. start-time ...) */
+static int cdr_core2strar( struct dlg_cell* dlg,
+                           str* values,
+                           int* unused,
+                           char* types)
+{
+    str* start = NULL;
+    str* end = NULL;
+    str* duration = NULL;
+
+    if( !dlg || !values || !types)
+    {
+        LM_ERR( "invalid input parameter!\n");
+        return 0;
+    }
+
+    start = dlgb.get_dlg_var( dlg, (str*)&start_id);
+    end = dlgb.get_dlg_var( dlg, (str*)&end_id);
+    duration = dlgb.get_dlg_var( dlg, (str*)&duration_id);
+
+    values[0] = ( start != NULL ? *start : empty_string);
+    types[0] = ( start != NULL ? TYPE_STR : TYPE_NULL);
+
+    values[1] = ( end != NULL ? *end : empty_string);
+    types[1] = ( end != NULL ? TYPE_STR : TYPE_NULL);
+
+    values[2] = ( duration != NULL ? *duration : empty_string);
+    types[2] = ( duration != NULL ? TYPE_STR : TYPE_NULL);
+
+    return MAX_CDR_CORE;
+}
+
+/* collect all crd data and write it to a syslog */
+static int write_cdr( struct dlg_cell* dialog,
+                      struct sip_msg* message)
+{
+    static char cdr_message[ MAX_SYSLOG_SIZE];
+    static char* const cdr_message_end = cdr_message +
+                                         MAX_SYSLOG_SIZE -
+                                         2;// -2 because of the string ending '\n\0'
+    char* message_position = NULL;
+    int message_index = 0;
+    int counter = 0;
+
+    if( !dialog || !message)
+    {
+        LM_ERR( "dialog and/or message is/are empty!");
+        return -1;
+    }
+
+    /* get default values */
+    message_index = cdr_core2strar( dialog,
+                                    cdr_value_array,
+                                    cdr_int_arr,
+                                    cdr_type_array);
+
+    /* get extra values */
+    message_index += extra2strar( cdr_extra,
+                                  message,
+                                  cdr_value_array + message_index,
+                                  cdr_int_arr + message_index,
+                                  cdr_type_array + message_index);
+
+    for( counter = 0, message_position = cdr_message;
+         counter < message_index ;
+         counter++ )
+    {
+        const char* const next_message_end = message_position +
+                                             2 + // ', ' -> two letters
+                                             cdr_attrs[ counter].len +
+                                             1 + // '=' -> one letter
+                                             cdr_value_array[ counter].len;
+
+        if( next_message_end >= cdr_message_end ||
+            next_message_end < message_position)
+        {
+            LM_WARN("cdr message too long, truncating..\n");
+            message_position = cdr_message_end;
+            break;
+        }
+
+        if( counter > 0)
+        {
+            *(message_position++) = A_SEPARATOR_CHR;
+            *(message_position++) = A_SEPARATOR_CHR_2;
+        }
+
+        memcpy( message_position,
+                cdr_attrs[ counter].s,
+                cdr_attrs[ counter].len);
+
+        message_position += cdr_attrs[ counter].len;
+
+        *( message_position++) = A_EQ_CHR;
+
+        memcpy( message_position,
+                cdr_value_array[ counter].s,
+                cdr_value_array[ counter].len);
+
+        message_position += cdr_value_array[ counter].len;
+    }
+
+    /* terminating line */
+    *(message_position++) = '\n';
+    *(message_position++) = '\0';
+
+    LM_GEN2( cdr_facility, log_level, "%s", cdr_message);
+
+    return 0;
+}
+
+/* convert a string into a timeb struct */
+static struct timeb time_from_string( str* time_value)
+{
+    char* point_adresse = NULL;
+    int point_position = -1;
+
+    if( !time_value)
+    {
+        LM_ERR( "time_value is empty!");
+        return time_error;
+    }
+
+    point_adresse = strchr( time_value->s, time_separator);
+
+    if( !point_adresse)
+    {
+        LM_ERR( "failed to find separator('%c') in '%s'!\n",
+                time_separator,
+                time_value->s);
+        return time_error;
+    }
+
+    point_position = point_adresse-time_value->s + 1;
+
+    if( point_position >= strlen(time_value->s) ||
+        strchr(point_adresse + 1, time_separator))
+    {
+        LM_ERR( "invalid time-string '%s'\n", time_value->s);
+        return time_error;
+    }
+
+    return (struct timeb) { atoi( time_value->s),
+                            atoi( point_adresse + 1),
+                            0,
+                            0};
+}
+
+/* set the duration in the dialog struct */
+static int set_duration( struct dlg_cell* dialog)
+{
+    struct timeb start_timeb = time_error;
+    struct timeb end_timeb = time_error;
+    int milliseconds = -1;
+    int seconds = -1;
+    char buffer[ time_buffer_length];
+    int buffer_length = -1;
+    str duration_time = empty_string;
+
+    if( !dialog)
+    {
+        LM_ERR("dialog is empty!\n");
+        return -1;
+    }
+
+    start_timeb = time_from_string( dlgb.get_dlg_var( dialog, (str*)&start_id));
+    end_timeb  = time_from_string( dlgb.get_dlg_var( dialog, (str*)&end_id));
+
+    if( is_time_equal( start_timeb, time_error) ||
+        is_time_equal( end_timeb, time_error))
+    {
+        LM_ERR( "failed to extract time from start or/and end-time\n");
+        return -1;
+    }
+
+    if( start_timeb.millitm >= milliseconds_max ||
+        end_timeb.millitm >= milliseconds_max)
+    {
+        LM_ERR( "start-(%d) or/and end-time(%d) is out of the maximum of %d\n",
+                start_timeb.millitm,
+                end_timeb.millitm,
+                milliseconds_max);
+        return -1;
+    }
+
+    milliseconds = end_timeb.millitm < start_timeb.millitm ?
+                                ( milliseconds_max +
+                                  end_timeb.millitm -
+                                  start_timeb.millitm) :
+                                ( end_timeb.millitm - start_timeb.millitm);
+
+    seconds = end_timeb.time -
+              start_timeb.time -
+              ( end_timeb.millitm < start_timeb.millitm ? 1 : 0);
+
+    if( seconds < 0)
+    {
+        LM_ERR( "negativ seconds(%d) for duration calculated.\n", seconds);
+        return -1;
+    }
+
+    if( milliseconds < 0 || milliseconds >= milliseconds_max)
+    {
+        LM_ERR( "milliseconds %d are out of range 0 < x < %d.\n",
+                milliseconds,
+                milliseconds_max);
+        return -1;
+    }
+
+    buffer_length = snprintf( buffer,
+                              time_buffer_length,
+                              "%d%c%03d",
+                              seconds,
+                              time_separator,
+                              milliseconds);
+
+    if( buffer_length < 0)
+    {
+        LM_ERR( "failed to write to buffer.\n");
+        return -1;
+    }
+
+    duration_time = ( str){ buffer, buffer_length};
+
+    if( dlgb.set_dlg_var( dialog,
+                          (str*)&duration_id,
+                          (str*)&duration_time) != 0)
+    {
+        LM_ERR( "failed to set duration time");
+        return -1;
+    }
+
+    return 0;
+}
+
+/* set the current time as start-time in the dialog struct */
+static int set_start_time( struct dlg_cell* dialog)
+{
+    char buffer[ time_buffer_length];
+    struct timeb current_time = time_error;
+    int buffer_length = -1;
+    str start_time = empty_string;
+
+    if( !dialog)
+    {
+        LM_ERR("dialog is empty!\n");
+        return -1;
+    }
+
+    if( ftime( &current_time) < 0)
+    {
+        LM_ERR( "failed to get current time!\n");
+        return -1;
+    }
+
+    buffer_length = snprintf( buffer,
+                              time_buffer_length,
+                              "%d%c%03d",
+                              (int)current_time.time,
+                              time_separator,
+                              (int)current_time.millitm);
+
+    if( buffer_length < 0)
+    {
+        LM_ERR( "reach buffer size\n");
+        return -1;
+    }
+
+    start_time = (str){ buffer, buffer_length};
+
+    if( dlgb.set_dlg_var( dialog,
+                          (str*)&start_id,
+                          (str*)&start_time) != 0)
+    {
+        LM_ERR( "failed to set start time\n");
+        return -1;
+    }
+
+    if( dlgb.set_dlg_var( dialog,
+                          (str*)&end_id,
+                          (str*)&start_time) != 0)
+    {
+        LM_ERR( "failed to set initiation end time\n");
+        return -1;
+    }
+
+    if( dlgb.set_dlg_var( dialog,
+                          (str*)&duration_id,
+                          (str*)&zero_duration) != 0)
+    {
+        LM_ERR( "failed to set initiation duration time\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+/* set the current time as end-time in the dialog struct */
+static int set_end_time( struct dlg_cell* dialog)
+{
+    char buffer[ time_buffer_length];
+    struct timeb current_time = time_error;
+    int buffer_length = -1;
+    str end_time = empty_string;
+
+    if( !dialog)
+    {
+        LM_ERR("dialog is empty!\n");
+        return -1;
+    }
+
+    if( ftime( &current_time) < 0)
+    {
+        LM_ERR( "failed to set time!\n");
+        return -1;
+    }
+
+    buffer_length = snprintf( buffer,
+                              time_buffer_length,
+                              "%d%c%03d",
+                              (int)current_time.time,
+                              time_separator,
+                              (int)current_time.millitm);
+
+    if( buffer_length < 0)
+    {
+        LM_ERR( "failed to write buffer\n");
+        return -1;
+    }
+
+    end_time = ( str){ buffer, buffer_length};
+
+    if( dlgb.set_dlg_var( dialog,
+                          (str*)&end_id,
+                          (str*)&end_time) != 0)
+    {
+        LM_ERR( "failed to set start time");
+        return -1;
+    }
+
+    return 0;
+}
+
+/* callback for a confirmed (INVITE) dialog. */
+static void cdr_on_start( struct dlg_cell* dialog,
+                          int type,
+                          struct dlg_cb_params* params)
+{
+    if( !dialog || !params)
+    {
+        LM_ERR("invalid values\n!");
+        return;
+    }
+
+    if( cdr_start_on_confirmed == 0)
+    {
+        return;
+    }
+
+    if( set_start_time( dialog) != 0)
+    {
+        LM_ERR( "failed to set start time!\n");
+        return;
+    }
+}
+
+/* callback for a failure during a dialog. */
+static void cdr_on_failed( struct dlg_cell* dialog,
+                           int type,
+                           struct dlg_cb_params* params)
+{
+    struct sip_msg* msg = 0;
+
+    if( !dialog || !params)
+    {
+        LM_ERR("invalid values\n!");
+        return;
+    }
+
+    if( params->rpl && params->rpl != FAKED_REPLY)
+    {
+        msg = params->rpl;
+    }
+    else if( params->req)
+    {
+        msg = params->req;
+    }
+    else
+    {
+        LM_ERR( "request and response are invalid!");
+        return;
+    }
+
+    if( write_cdr( dialog, msg) != 0)
+    {
+        LM_ERR( "failed to write cdr!\n");
+        return;
+    }
+}
+
+/* callback for the finish of a dialog (reply to BYE). */
+void cdr_on_end_confirmed( struct dlg_cell* dialog,
+                        int type,
+                        struct dlg_cb_params* params)
+{
+    if( !dialog || !params || !params->req)
+    {
+        LM_ERR("invalid values\n!");
+        return;
+    }
+
+    if( write_cdr( dialog, params->req) != 0)
+    {
+        LM_ERR( "failed to write cdr!\n");
+        return;
+    }
+}
+
+/* callback for the end of a dialog (BYE). */
+static void cdr_on_end( struct dlg_cell* dialog,
+                        int type,
+                        struct dlg_cb_params* params)
+{
+    if( !dialog || !params || !params->req)
+    {
+        LM_ERR("invalid values\n!");
+        return;
+    }
+
+    if( set_end_time( dialog) != 0)
+    {
+        LM_ERR( "failed to set end time!\n");
+        return;
+    }
+
+    if( set_duration( dialog) != 0)
+    {
+        LM_ERR( "failed to set duration!\n");
+        return;
+    }
+}
+
+/* callback for a expired dialog. */
+static void cdr_on_expired( struct dlg_cell* dialog,
+                            int type,
+                            struct dlg_cb_params* params)
+{
+    if( !dialog || !params)
+    {
+        LM_ERR("invalid values\n!");
+        return;
+    }
+
+    LM_DBG("dialog '%p' expired!\n", dialog);
+}
+
+/* callback for the cleanup of a dialog. */
+static void cdr_on_destroy( struct dlg_cell* dialog,
+                            int type,
+                            struct dlg_cb_params* params)
+{
+    if( !dialog || !params)
+    {
+        LM_ERR("invalid values\n!");
+        return;
+    }
+
+    LM_DBG("dialog '%p' destroyed!\n", dialog);
+}
+
+/* callback for the creation of a dialog. */
+static void cdr_on_create( struct dlg_cell* dialog,
+                           int type,
+                           struct dlg_cb_params* params)
+{
+    if( !dialog || !params || !params->req)
+    {
+        LM_ERR( "invalid values\n!");
+        return;
+    }
+
+    if( cdr_enable == 0)
+    {
+        return;
+    }
+
+    if( dlgb.register_dlgcb( dialog, DLGCB_CONFIRMED, cdr_on_start, 0, 0) != 0)
+    {
+        LM_ERR("can't register create dialog CONFIRM callback\n");
+        return;
+    }
+
+    if( dlgb.register_dlgcb( dialog, DLGCB_FAILED, cdr_on_failed, 0, 0) != 0)
+    {
+        LM_ERR("can't register create dialog FAILED callback\n");
+        return;
+    }
+
+    if( dlgb.register_dlgcb( dialog, DLGCB_TERMINATED, cdr_on_end, 0, 0) != 0)
+    {
+        LM_ERR("can't register create dialog TERMINATED callback\n");
+        return;
+    }
+
+    if( dlgb.register_dlgcb( dialog, DLGCB_TERMINATED_CONFIRMED, cdr_on_end_confirmed, 0, 0) != 0)
+    {
+        LM_ERR("can't register create dialog TERMINATED CONFIRMED callback\n");
+        return;
+    }
+
+    if( dlgb.register_dlgcb( dialog, DLGCB_EXPIRED, cdr_on_expired, 0, 0) != 0)
+    {
+        LM_ERR("can't register create dialog EXPIRED callback\n");
+        return;
+    }
+
+    if( dlgb.register_dlgcb( dialog, DLGCB_DESTROY, cdr_on_destroy, 0, 0) != 0)
+    {
+        LM_ERR("can't register create dialog DESTROY callback\n");
+        return;
+    }
+
+    LM_DBG("dialog '%p' created!", dialog);
+
+    if( set_start_time( dialog) != 0)
+    {
+        LM_ERR( "failed to set start time");
+        return;
+    }
+}
+/* convert the extra-data string into a list and store it */
+int set_cdr_extra( char* cdr_extra_value)
+{
+    struct acc_extra* extra = 0;
+    int counter = 0;
+
+    if( cdr_extra_value && ( cdr_extra = parse_acc_extra( cdr_extra_value))==0)
+    {
+        LM_ERR("failed to parse crd_extra param\n");
+        return -1;
+    }
+
+    /* fixed core attributes */
+    cdr_attrs[ counter++] = start_id;
+    cdr_attrs[ counter++] = end_id;
+    cdr_attrs[ counter++] = duration_id;
+
+    for(extra=cdr_extra; extra ; extra=extra->next)
+    {
+        cdr_attrs[ counter++] = extra->name;
+    }
+
+    return 0;
+}
+
+/* convert the facility-name string into a id and store it */
+int set_cdr_facility( char* cdr_facility_str)
+{
+    int facility_id = -1;
+
+    if( !cdr_facility_str)
+    {
+        LM_ERR( "facility is empty\n");
+        return -1;
+    }
+
+    facility_id = str2facility( cdr_facility_str);
+
+    if( facility_id == -1)
+    {
+        LM_ERR("invalid cdr facility configured\n");
+        return -1;
+    }
+
+    cdr_facility = facility_id;
+
+    return 0;
+}
+
+/* initialization of all necessary callbacks to track a dialog */
+int init_cdr_generation( void)
+{
+    if( load_dlg_api( &dlgb) != 0)
+    {
+        LM_ERR("can't load dialog API\n");
+        return -1;
+    }
+
+    if( dlgb.register_dlgcb( 0, DLGCB_CREATED, cdr_on_create, 0, 0) != 0)
+    {
+        LM_ERR("can't register create callback\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+/* convert the facility-name string into a id and store it */
+void destroy_cdr_generation( void)
+{
+    if( !cdr_extra)
+    {
+        return;
+    }
+
+    destroy_extras( cdr_extra);
+}
diff --git a/modules_k/acc/acc_cdr.h b/modules_k/acc/acc_cdr.h
new file mode 100644 (file)
index 0000000..46687ef
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Accounting module
+ *
+ * Copyright (C) 2011 - Sven Knoblich 1&1 Internet AG
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/*! \file
+ * \ingroup acc
+ * \brief Acc:: File to handle CRD generation by the help of the dialog-module
+ *
+ * - Module: \ref acc
+ */
+
+/*! \defgroup acc ACC :: The Kamailio accounting Module
+ *
+ * The ACC module is used to account transactions information to
+ *  different backends like syslog, SQL, RADIUS and DIAMETER (beta
+ *  version).
+ *
+ */
+
+#define MAX_CDR_CORE 3
+#define MAX_CDR_EXTRA 64
+
+
+int set_cdr_extra( char* cdr_extra_value);
+int set_cdr_facility( char* cdr_facility);
+int init_cdr_generation( void);
+void destroy_cdr_generation( void);
+
+
+
index a652c28..313b825 100644 (file)
@@ -73,6 +73,7 @@
 #include "acc_mod.h"
 #include "acc_extra.h"
 #include "acc_logic.h"
+#include "acc_cdr.h"
 
 #ifdef RAD_ACC
 #include "../../lib/kcore/radius.h"
@@ -110,7 +111,7 @@ struct acc_extra *leg_info = 0;
 
 
 /* ----- SYSLOG acc variables ----------- */
-/*! \name AccSyslogVariables  Syslog Variables */     
+/*! \name AccSyslogVariables  Syslog Variables */
 /*@{*/
 
 int log_flag = -1;
@@ -123,6 +124,16 @@ struct acc_extra *log_extra = 0; /*!< Log extra attributes */
 
 /*@}*/
 
+/* ----- CDR generation variables ------- */
+/*! \name AccCdrVariables  CDR Variables */
+/*@{*/
+
+int cdr_enable  = 0;
+int cdr_start_on_confirmed = 0;
+static char* cdr_facility_str = 0;
+static char* cdr_log_extra_str = 0;
+/*@{*/
+
 /* ----- RADIUS acc variables ----------- */
 /*! \name AccRadiusVariables  Radius Variables */     
 /*@{*/
@@ -230,6 +241,11 @@ static param_export_t params[] = {
        {"log_level",            INT_PARAM, &log_level            },
        {"log_facility",         STR_PARAM, &log_facility_str     },
        {"log_extra",            STR_PARAM, &log_extra_str        },
+    /* cdr specific */
+    {"cdr_enable",           INT_PARAM, &cdr_enable                     },
+    {"cdr_start_on_confirmed", INT_PARAM, &cdr_start_on_confirmed   },
+    {"cdr_facility",         STR_PARAM, &cdr_facility_str                },
+    {"cdr_extra",            STR_PARAM, &cdr_log_extra_str              },
 #ifdef RAD_ACC
        {"radius_config",        STR_PARAM, &radius_config        },
        {"radius_flag",          INT_PARAM, &radius_flag          },
@@ -503,6 +519,35 @@ static int mod_init( void )
 
        acc_log_init();
 
+    /* ----------- INIT CDR GENERATION ----------- */
+
+    if( cdr_enable < 0 || cdr_enable > 1)
+    {
+        LM_ERR("cdr_enable is out of rage\n");
+        return -1;
+    }
+
+    if( cdr_enable)
+    {
+        if( cdr_log_extra_str && set_cdr_extra( cdr_log_extra_str) != 0)
+        {
+            LM_ERR( "failed to set cdr extra '%s'\n", cdr_log_extra_str);
+            return -1;
+        }
+
+        if( cdr_facility_str && set_cdr_facility( cdr_facility_str) != 0)
+        {
+            LM_ERR( "failed to set cdr facility '%s'\n", cdr_facility_str);
+            return -1;
+        }
+
+        if( init_cdr_generation() != 0)
+        {
+            LM_ERR("failed to init cdr generation\n");
+            return -1;
+        }
+    }
+
        /* ------------ SQL INIT SECTION ----------- */
 
 #ifdef SQL_ACC
index ce13583..cf900fe 100644 (file)
@@ -55,6 +55,9 @@ extern int log_level;
 extern int log_flag;
 extern int log_missed_flag;
 
+extern int cdr_enable;
+extern int cdr_start_on_confirmed;
+extern int cdr_log_facility;
 
 #ifdef RAD_ACC
 extern int radius_flag;
index b446454..568a3f9 100644 (file)
                <affiliation><orgname>Voice Sistem SRL</orgname></affiliation>
                <email>bogdan@voice-system.ro</email>
                </editor>
+               <editor>
+               <firstname>Sven</firstname>
+               <surname>Knoblich</surname>
+               <affiliation><orgname>1&amp;1 Internet AG</orgname></affiliation>
+               <email>sven.knoblich@1und1.de</email>
+               </editor>
        </authorgroup>
        <copyright>
                <year>2002</year>
                <year>2006</year>
                <holder>Voice Sistem SRL</holder>
        </copyright>
+       <copyright>
+               <year>2011</year>
+               <holder>1&amp;1 Internet AG</holder>
+       </copyright>
+       <revhistory>
+               <revision>
+               <revnumber>$Revision$</revnumber>
+               <date>$Date$</date>
+               </revision>
+       </revhistory>
        </bookinfo>
        <toc></toc>
 
index 970ffe8..6f88be9 100644 (file)
                        types of routes.
                        </para>
                </listitem>
-               <listitem>
-                       <para>
-                       There is no session/dialog accounting (yet) -- &kamailio; maintains
-                       no sessions. If one needs to correlate INVITEs with BYEs for 
-                       generating proper CDRs for example for purpose of billing, then 
-                       it is better done in the entity which processes accounting 
-                       information.
-                       </para>
-               </listitem>
                <listitem>
                        <para>
                        If a UA fails in middle of conversation, a proxy will never 
@@ -173,7 +164,7 @@ if (uri=~"sip:+40") /* calls to Romania */ {
                        the request, etc).
                        </para>
                </section>
-               <section>
+               <section id="acc-def-syn">
                        <title>Definitions and syntax</title>
                        <para>
                        Selection of extra information is done via 
@@ -326,8 +317,127 @@ if (uri=~"sip:+40") /* calls to Romania */ {
                        </itemizedlist>
                </section>
        </section>
+       <section>
+               <title>Call Data Record generation</title>
+        <section>
+            <title>Overview</title>
+                <para>
+                It is possible to generate and log Call Data Records (CDRs) directly from &kamailio;
+                in addition to transaction-based logging. Apart from a basic set of CDR fields which
+                are always included (covering start time, end time, and duration), the approach allows
+                flexible specification of additional fields that should be taken into account using
+                the configuration script. This is very similar to how transaction-based logging may
+                be customized with the exception that CDRs rely on dialogs instead of transactions
+                to store relevant information during a call.
+                </para>
+
+                <para>
+                In order to set up CDR generation, you must enable the CDR switch and load the dialog
+                module. You probably also want to specify a set of pseudo-variables that define more
+                relevant CDR fields. Pseudo-variables may be assigned arbitrarily during script
+                execution, and the module will make sure that the variable content will be transformed
+                into a CDR by the end of the dialog.
+                </para>
+
+                <para>
+                To use CDR logging in a correct manner, you should only use the dialog-based
+                pseudo-variables (dlg_var) from the dialog module. This allows you to save values
+                right from the beginning through all requests and replies until termination of the
+                call. While not recommended, it is still possible to use other pseudo-variables as
+                well. Except for pseudo-variables valid in the call-final transaction, however,
+                information given will not be stored in the CDR as they cannot be accessed by the
+                end of the call when the CDR is logged.
+                </para>
+        </section>
+               <section id="cdr-extra-id">
+                       <title>CDR Extra</title>
+                                       This section is similar to the <quote>LOG accounting</quote> part of
+                                       <xref linkend="ACC-extra-id"/>.
+                       <section>
+                               <title>Definitions and syntax</title>
+                                       <para>
+                                       Selection of extra information is done similar to the transaction extra
+                                       <xref linkend="acc-def-syn"/>.
+                                       </para>
+                                       <itemizedlist>
+                                               <listitem><para><emphasis>
+                                               cdr_log_extra = cdr_extra_definition (';'cdr_extra_definition)*
+                                               </emphasis></para></listitem>
+                                               <listitem><para><emphasis>
+                                               cdr_extra_definition = cdr_log_name '=' pseudo_variable
+                                               </emphasis></para></listitem>
+                                       </itemizedlist>
+                                       See also <xref linkend="cdr_log_extra"/>.
+                                       <para>
+                                       The full list of supported pseudo-variables in Sip-Router is
+                                       available at:
+                                       <ulink url="http://sip-router.org/wiki/cookbooks/pseudo-variables/devel">
+                                       http://sip-router.org/wiki/cookbooks/pseudo-variables/devel</ulink>
+                                       </para>
+                       </section>
+               </section>
+               <section id="multi-cdr-call-legs">
+                       <title>CDR with Multi Call-Legs</title>
+                       <section>
+                               <title>Overview</title>
+                               <para>
+                               As mentioned in <xref linkend="multi-call-legs"/>, a leg represents a parallel
+                               or forwarded call. In contrast to the normal accounting the cdr logging uses dialogs
+                               instead of transaction to log data. This may reduce the amount of information 
+                               but it also make it possible to combine all important data in one cdr at 
+                               once. A second mechanism to process multiple data-sets into one cdr is not further
+                               necessary.
+                               </para>
+                       </section>
+                       <section>
+                               <title>Configuration</title>
+                               <para>
+                               When you route messages multiple times through your proxy (e.g. to
+                               handle <quote>call-forwardings</quote>) you have to use detect_spirals
+                               from the dialog modules. Otherwise the proxy can't identify and reuse existing
+                               dialogs.
+                               </para>
+                               <para>
+                               To get the correct call-forwarding-chain you have to store each cf* with the
+                               corresponding caller and callee in a dialog based pseudo-variable (dlg_var)
+                               (e.g. chain=B;cfa;C|C;cfnr;D). Additionally it is necessary to store the
+                               caller and callee for each leg. All this helps to identify the involved
+                               phone parners and forwarding chain. When you route such calls multiple times
+                               to the same Proxy, you could store the caller and callee within an transaction
+                               based avp and write it into the dialog based dlg_var pv during a 200 INVITE.
+                               </para>
+                               <section>
+                                       <title>Example for a spiraled Proxy</title>
+                                       <programlisting format="linespecific">
+# A calls B (transaction 1)
+$avp(caller)='A'
+$avp(callee)='B';
+$dlg_var(chain)='';
 
+# B cfa C (transaction 2)
+$avp(caller)='B'
+$avp(callee)='C';
+$dlg_var(chain)='B;cfu;C';
 
+# C cfnr D (transaction 3)
+$avp(caller)='C'
+$avp(callee)='D';
+$dlg_var(chain)=$dlg_var(chain) + "|" + "C;cfnr;D";
+
+# C confirms call (200 reply of transaction 2)
+$dlg_var(caller) = $avp(caller); #caller='B'
+$dlg_var(callee) = $avp(callee); #callee='C'
+                                       </programlisting>
+                               </section>
+                       </section>
+                       <section>
+                               <title>Logged data</title>
+                               For each call, all dialog corresponding variables will be logged. After a call
+                               is finished, the generated call data record information will be logged as string
+                               (VAR1=xxx,VAR2=xxxx,...) to the syslog.
+                       </section>
+               </section>
+       </section>
        <section>
                <title>Dependencies</title>
                <section>
@@ -348,6 +458,11 @@ if (uri=~"sip:+40") /* calls to Romania */ {
                                <quote>detect_direction</quote> module parameter is enabled.
                                </para>
                                </listitem>
+                               <listitem>
+                               <para><emphasis>dialog</emphasis> -- Dialog, if
+                               <quote>cdr_enable</quote> module parameter is enabled.
+                               </para>
+                               </listitem>
                        </itemizedlist>
                        </para>
                </section>
@@ -572,7 +687,6 @@ modparam("acc", "log_facility", "LOG_DAEMON")
 </programlisting>
                </example>
        </section>
-
        <section>
                <title><varname>log_extra</varname> (string)</title>
                <para>
@@ -962,6 +1076,73 @@ modparam("acc", "diameter_extra", "7846=$hdr(Content-type);7847=$avp(s:email)")
 </programlisting>
                </example>
        </section>
+       <section>
+               <title><varname>cdr_enable</varname> (integer)</title>
+               <para>
+               Should CDR-based logging be enabled?
+               </para>
+               <para>
+               0 - off (default)
+               1 - on
+               </para>
+               <example>
+               <title>cdr_enable example</title>
+               <programlisting format="linespecific">
+modparam("acc", "cdr_enable", 1)
+</programlisting>
+               </example>
+       </section>
+       <section>
+               <title><varname>cdr_start_when_confirmed</varname> (integer)</title>
+               <para>
+               Should the start time be taken from the time when the dialog is created,
+        or when the dialog is confirmed?
+               </para>
+               <para>
+               0 - use time of dialog creation (default).
+               1 - use time of dialog confirmation.
+               </para>
+               <example>
+               <title>cdr_start_when_confirmed example</title>
+               <programlisting format="linespecific">
+modparam("acc", "cdr_start_when_confirmed", 1)
+</programlisting>
+               </example>
+       </section>
+       <section>
+               <title><varname>cdr_log_facility</varname> (integer)</title>
+               <para>
+               Log facility to which CDR messages are issued to syslog.
+               This allows to easily seperate CDR-specific logging from
+               the other log messages.
+               </para>
+               <para>
+               Default value is LOG_DAEMON.
+               </para>
+               <example>
+               <title>cdr_log_facility example</title>
+               <programlisting format="linespecific">
+modparam("acc", "cdr_log_facility", "LOG_DAEMON")
+</programlisting>
+               </example>
+       </section>
+       <section id="cdr_log_extra">
+               <title><varname>cdr_log_extra</varname> (string)</title>
+               <para>
+               Set of pseudo-variables defining custom CDR fields. See
+        <xref linkend="cdr-extra-id"/> for more details.
+               </para>
+               <para>
+               Default value is NULL.
+               </para>
+               <example>
+               <title>cdr_log_extra example</title>
+               <programlisting format="linespecific">
+modparam("acc", "cdr_log_extra", "c1=$dlg_var(caller);c2=$dlg_var(callee)"
+</programlisting>
+               </example>
+       </section>
+
        </section>
 
        <section>
index 6d9dad8..fc3b0fc 100644 (file)
@@ -946,7 +946,7 @@ typedef enum {
 static void
 __dialog_replies(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
 {
-    struct sip_msg *reply = _params->msg;
+    struct sip_msg *reply = _params->rpl;
 
     if (reply!=FAKED_REPLY && reply->REPLY_STATUS==200) {
         call_control_start(reply, dlg);
@@ -958,7 +958,10 @@ static void
 __dialog_ended(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
 {
     if ((int)(long)*_params->param == CCActive) {
-        call_control_stop(_params->msg, dlg->callid);
+        struct sip_msg* msg = _params->rpl;
+        if( !msg || msg == FAKED_REPLY)
+            msg = _params->req;
+        call_control_stop(msg, dlg->callid);
         *_params->param = CCInactive;
     }
 }
@@ -967,7 +970,7 @@ __dialog_ended(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
 static void
 __dialog_created(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
 {
-    struct sip_msg *request = _params->msg;
+    struct sip_msg *request = _params->req;
 
     if (request->REQ_METHOD != METHOD_INVITE)
         return;
index d17fdb1..d31135a 100644 (file)
@@ -104,6 +104,7 @@ static char* profiles_wv_s = NULL;
 static char* profiles_nv_s = NULL;
 str dlg_extra_hdrs = {NULL,0};
 static int db_fetch_rows = 200;
+int initial_cbs_inscript = 1;
 
 str dlg_bridge_controller = {"sip:controller@kamailio.org", 27};
 
@@ -241,6 +242,7 @@ static param_export_t mod_params[]={
        { "profiles_no_value",     STR_PARAM, &profiles_nv_s            },
        { "bridge_controller",     STR_PARAM, &dlg_bridge_controller.s  },
        { "ruri_pvar",             STR_PARAM, &ruri_pvar_param.s        },
+       { "initial_cbs_inscript",  INT_PARAM, &initial_cbs_inscript     },
        { 0,0,0 }
 };
 
@@ -372,6 +374,8 @@ int load_dlg( struct dlg_binds *dlgb )
 {
        dlgb->register_dlgcb = register_dlgcb;
        dlgb->terminate_dlg = dlg_bye_all;
+       dlgb->set_dlg_var = set_dlg_variable;
+       dlgb->get_dlg_var = get_dlg_variable;
        return 1;
 }
 
@@ -502,6 +506,11 @@ static int mod_init(void)
                return -1;
        }
 
+       if (initial_cbs_inscript != 0 && initial_cbs_inscript != 1) {
+               LM_ERR("invalid parameter for running initial callbacks in-script (must be either 0 or 1)\n");
+               return -1;
+       }
+
        /* update the len of the extra headers */
        if (dlg_extra_hdrs.s)
                dlg_extra_hdrs.len = strlen(dlg_extra_hdrs.s);
@@ -574,6 +583,11 @@ static int mod_init(void)
                return -1;
        }
 
+       if (register_script_cb( spiral_detect_reset, POST_SCRIPT_CB|REQUEST_CB,0)<0) {
+               LM_ERR("cannot register req pre-script spiral detection reset callback\n");
+               return -1;
+       }
+
        if ( register_timer( dlg_timer_routine, 0, 1)<0 ) {
                LM_ERR("failed to register timer \n");
                return -1;
@@ -901,7 +915,7 @@ static int w_dlg_manage(struct sip_msg *msg, char *s1, char *s2)
                dlg_onroute(msg, NULL, NULL);
                seq_match_mode = backup_mode;
        } else {
-               if(dlg_new_dialog(msg, 0)!=0)
+               if(dlg_new_dialog(msg, 0, initial_cbs_inscript)!=0)
                        return -1;
        }
        return 1;
@@ -1212,7 +1226,7 @@ static inline void internal_rpc_print_dlg(rpc_t *rpc, void *c, struct dlg_cell *
        if (with_context) {
                rpc_cb.rpc = rpc;
                rpc_cb.c = c;
-               run_dlg_callbacks( DLGCB_RPC_CONTEXT, dlg, NULL, DLG_DIR_NONE, (void *)&rpc_cb);
+               run_dlg_callbacks( DLGCB_RPC_CONTEXT, dlg, NULL, NULL, DLG_DIR_NONE, (void *)&rpc_cb);
        }
 }
 
index 24b3b39..3f2f2cd 100644 (file)
@@ -40,7 +40,7 @@ static struct dlg_head_cbl* create_cbs = 0;
 
 static struct dlg_head_cbl* load_cbs = 0;
 
-static struct dlg_cb_params params = {NULL, DLG_DIR_NONE, NULL, NULL};
+static struct dlg_cb_params params = {NULL, NULL, DLG_DIR_NONE, NULL, NULL};
 
 
 #define POINTER_CLOSED_MARKER  ((void *)(-1))
@@ -184,7 +184,8 @@ static void run_load_callback(struct dlg_callback *cb)
        struct dlg_cell *dlg;
        unsigned int i;
 
-       params.msg = NULL;
+       params.req = NULL;
+       params.rpl = NULL;
        params.direction = DLG_DIR_NONE;
        params.param = &cb->param;
 
@@ -217,7 +218,8 @@ void run_create_callbacks(struct dlg_cell *dlg, struct sip_msg *msg)
        if (create_cbs==NULL || create_cbs->first==NULL)
                return;
 
-       params.msg = msg;
+       params.req = msg;
+       params.rpl = NULL;
        /* initial request goes DOWNSTREAM all the time */
        params.direction = DLG_DIR_DOWNSTREAM;
        /* avoid garbage due static structure */
@@ -233,12 +235,16 @@ void run_create_callbacks(struct dlg_cell *dlg, struct sip_msg *msg)
 }
 
 
-void run_dlg_callbacks(int type , struct dlg_cell *dlg, struct sip_msg *msg,
-                                                                                       unsigned int dir, void *dlg_data)
+void run_dlg_callbacks( int type ,
+                                               struct dlg_cell *dlg,
+                                               struct sip_msg *req,
+                                               struct sip_msg *rpl,
+                                               unsigned int dir, void *dlg_data)
 {
        struct dlg_callback *cb;
 
-       params.msg = msg;
+       params.req = req;
+       params.rpl = rpl;
        params.direction = dir;
        params.dlg_data = dlg_data;
 
index fc8b05a..9c6fa14 100644 (file)
@@ -37,7 +37,8 @@
 struct dlg_cell;
 
 struct dlg_cb_params {
-       struct sip_msg* msg;       /* sip msg related to the callback event */
+       struct sip_msg* req;       /* sip request msg related to the callback event */
+    struct sip_msg* rpl;       /* sip reply msg related to the callback event */
        unsigned int direction;    /* direction of the sip msg */
        void *dlg_data;            /* generic paramter, specific to callback */
        void **param;              /* parameter passed at callback registration*/
@@ -52,6 +53,13 @@ typedef void (param_free_cb) (void *param);
 typedef int (*register_dlgcb_f)(struct dlg_cell* dlg, int cb_types,
                dialog_cb f, void *param, param_free_cb ff);
 
+/* method to set a variable within a dialog */
+typedef int (*set_dlg_variable_f)( struct dlg_cell* dlg,
+                                   str* key,
+                                   str* val);
+/* method to get a variable from a dialog */
+typedef str* (*get_dlg_variable_f)( struct dlg_cell* dlg,
+                                    str* key);
 
 #define DLGCB_LOADED          (1<<0)
 #define DLGCB_CREATED         (1<<1)
@@ -67,6 +75,7 @@ typedef int (*register_dlgcb_f)(struct dlg_cell* dlg, int cb_types,
 #define DLGCB_RPC_CONTEXT     (1<<11)
 #define DLGCB_DESTROY         (1<<12)
 #define DLGCB_SPIRALED        (1<<13)
+#define DLGCB_TERMINATED_CONFIRMED (1<<14)
 
 struct dlg_callback {
        int types;
@@ -91,8 +100,12 @@ int register_dlgcb( struct dlg_cell* dlg, int types, dialog_cb f, void *param, p
 
 void run_create_callbacks(struct dlg_cell *dlg, struct sip_msg *msg);
 
-void run_dlg_callbacks( int type , struct dlg_cell *dlg, struct sip_msg *msg,
-               unsigned int dir, void *dlg_data);
+void run_dlg_callbacks( int type ,
+                        struct dlg_cell *dlg,
+                        struct sip_msg *req,
+                        struct sip_msg *rpl,
+                        unsigned int dir,
+                        void *dlg_data);
 
 void run_load_callbacks( void );
 
index ec0befd..450fd09 100644 (file)
@@ -83,6 +83,8 @@ static int       default_timeout;     /*!< default dialog timeout */
 static int       seq_match_mode;       /*!< dlg_match mode */ 
 static int       shutdown_done = 0;    /*!< 1 when destroy_dlg_handlers was called */
 extern int       detect_spirals;
+extern int       initial_cbs_inscript;
+int              spiral_detected = -1;
 
 extern struct rr_binds d_rrb;          /*!< binding to record-routing module */
 
@@ -279,6 +281,72 @@ error0:
        return -1;
 }
 
+/*!
+ * \brief Function that executes BYE reply callbacks
+ * \param t transaction, unused
+ * \param type type of the callback, should be TMCB_RESPONSE_FWDED
+ * \param param saved dialog structure inside the callback
+ */
+static void dlg_terminated_confirmed(struct cell* t,
+                                     int type,
+                                     struct tmcb_params* params)
+{
+    if(!params || !params->req || !params->param)
+    {
+        LM_ERR("invalid parameters!\n");
+        return;
+    }
+
+    struct dlg_cell* dlg = (struct dlg_cell*)*params->param;
+
+    if(!dlg)
+    {
+        LM_ERR("failed to get dialog from params!\n");
+        return;
+    }
+    /* dialog termination confirmed (BYE reply) */
+    run_dlg_callbacks(DLGCB_TERMINATED_CONFIRMED,
+                      dlg,
+                      params->req,
+                      params->rpl,
+                      DLG_DIR_UPSTREAM,
+                      0);
+}
+
+/*!
+ * \brief Execute callback for the BYE request and register callback for the BYE reply
+ * \param req request message
+ * \param dlg corresponding dialog
+ * \param dir message direction
+ */
+static void dlg_terminated(struct sip_msg* req,
+                           struct dlg_cell* dlg,
+                           unsigned int dir)
+{
+    if(!req) {
+        LM_ERR("request is empty!");
+        return;
+    }
+
+    if(!dlg) {
+        LM_ERR("dialog is empty!");
+        return;
+    }
+
+    /* dialog terminated (BYE) */
+    run_dlg_callbacks(DLGCB_TERMINATED, dlg, req, NULL, dir, 0);
+
+    /* register callback for the coresponding reply */
+    if (d_tmb.register_tmcb(req,
+                            0,
+                            TMCB_RESPONSE_OUT,
+                            dlg_terminated_confirmed,
+                            (void*) dlg,
+                            0 ) <= 0 ) {
+        LM_ERR("cannot register response callback for BYE request\n");
+        return;
+    }
+}
 
 /*!
  * \brief Function that is registered as TM callback and called on replies
@@ -294,20 +362,19 @@ error0:
  */
 static void dlg_onreply(struct cell* t, int type, struct tmcb_params *param)
 {
-       struct sip_msg *rpl;
-       struct dlg_cell *dlg;
-       int new_state, old_state, unref, event;
-       str tag;
+    struct dlg_cell *dlg;
+    int new_state, old_state, unref, event;
+    str tag;
+    struct sip_msg *req = param->req;
+       struct sip_msg *rpl = param->rpl;
 
        dlg = (struct dlg_cell *)(*param->param);
        if (shutdown_done || dlg==0)
                return;
 
-       rpl = param->rpl;
-
        if (type==TMCB_RESPONSE_FWDED) {
                /* The state does not change, but the msg is mutable in this callback*/
-               run_dlg_callbacks(DLGCB_RESPONSE_FWDED, dlg, rpl, DLG_DIR_UPSTREAM, 0);
+               run_dlg_callbacks(DLGCB_RESPONSE_FWDED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0);
                return;
        }
 
@@ -323,7 +390,7 @@ static void dlg_onreply(struct cell* t, int type, struct tmcb_params *param)
        next_state_dlg( dlg, event, &old_state, &new_state, &unref);
 
        if (new_state==DLG_STATE_EARLY) {
-               run_dlg_callbacks(DLGCB_EARLY, dlg, rpl, DLG_DIR_UPSTREAM, 0);
+               run_dlg_callbacks(DLGCB_EARLY, dlg, req, rpl, DLG_DIR_UPSTREAM, 0);
                if (old_state!=DLG_STATE_EARLY)
                        if_update_stat(dlg_enable_stats, early_dlgs, 1);
                return;
@@ -379,7 +446,7 @@ static void dlg_onreply(struct cell* t, int type, struct tmcb_params *param)
                }
 
                /* dialog confirmed */
-               run_dlg_callbacks( DLGCB_CONFIRMED, dlg, rpl, DLG_DIR_UPSTREAM, 0);
+               run_dlg_callbacks( DLGCB_CONFIRMED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0);
 
                if (old_state==DLG_STATE_EARLY)
                        if_update_stat(dlg_enable_stats, early_dlgs, -1);
@@ -392,7 +459,7 @@ static void dlg_onreply(struct cell* t, int type, struct tmcb_params *param)
        if ( old_state!=DLG_STATE_DELETED && new_state==DLG_STATE_DELETED ) {
                LM_DBG("dialog %p failed (negative reply)\n", dlg);
                /* dialog setup not completed (3456XX) */
-               run_dlg_callbacks( DLGCB_FAILED, dlg, rpl, DLG_DIR_UPSTREAM, 0);
+               run_dlg_callbacks( DLGCB_FAILED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0);
                /* do unref */
                if (unref)
                        unref_dlg(dlg,unref);
@@ -428,9 +495,14 @@ static void dlg_seq_onreply_helper(struct cell* t, int type,
        if (shutdown_done || dlg==0)
                return;
 
-       if (type==TMCB_RESPONSE_FWDED) {
-               run_dlg_callbacks(DLGCB_RESPONSE_WITHIN, dlg, param->rpl,
-                       direction, 0);
+       if (type==TMCB_RESPONSE_FWDED)
+       {
+               run_dlg_callbacks( DLGCB_RESPONSE_WITHIN,
+                                  dlg,
+                                  param->req,
+                                  param->rpl,
+                                  direction,
+                                  0);
                return;
        }
 
@@ -537,53 +609,19 @@ static inline int pre_match_parse( struct sip_msg *req, str *callid,
  */
 void dlg_onreq(struct cell* t, int type, struct tmcb_params *param)
 {
-       struct dlg_cell *dlg;
-       str callid;
-       str ftag;
-       str ttag;
-       unsigned int dir;
-       unsigned int del;
        struct sip_msg *req = param->req;
 
+       if (!initial_cbs_inscript) {
+               if (spiral_detected == 1)
+                       run_dlg_callbacks( DLGCB_SPIRALED, current_dlg_pointer, req, NULL, DLG_DIR_DOWNSTREAM, 0);
+               else if (spiral_detected == 0)
+                       run_create_callbacks( current_dlg_pointer, req);
+       }
        if((req->flags&dlg_flag)!=dlg_flag)
                return;
        if (current_dlg_pointer!=NULL)
                return;
-       if (!detect_spirals)
-               goto create;
-
-       /* skip initial requests - they may end up here because of the
-        * preloaded route */
-       if ( (!req->to && parse_headers(req, HDR_TO_F,0)<0) || !req->to ) {
-               LM_ERR("bad request or missing TO hdr :-/\n");
-               return;
-       }
-
-       dlg = 0;
-       dir = DLG_DIR_NONE;
-
-       if (pre_match_parse( req, &callid, &ftag, &ttag, 0)<0) {
-               LM_WARN("pre-matching failed\n");
-               return;
-       }
-       dlg = get_dlg(&callid, &ftag, &ttag, &dir, &del);
-       if (del == 1) {
-               LM_DBG("dialog marked for deletion, ignoring\n");
-               return;
-       }
-       if (!dlg){
-               LM_DBG("Callid '%.*s' not found, must be a new dialog\n",
-                               req->callid->body.len, req->callid->body.s);
-               goto create;
-       }
-
-       run_dlg_callbacks( DLGCB_SPIRALED, dlg, req, DLG_DIR_DOWNSTREAM, 0);
-
-       unref_dlg(dlg, 1);
-       return;
-
-create:
-       dlg_new_dialog(req, t);
+       dlg_new_dialog(req, t, 1);
 }
 
 
@@ -601,6 +639,18 @@ static void unref_new_dialog(void *dialog)
        dlg_onreply(0, TMCB_DESTROY, &p);
 }
 
+/*!
+ * \brief Unreference a dialog (small wrapper to take care of shutdown)
+ * \see unref_dlg
+ * \param dialog unreferenced dialog
+ */
+static void unreference_dialog(void *dialog)
+{
+    // if the dialog table is gone, it means the system is shutting down.
+    if (!d_table)
+        return;
+    unref_dlg((struct dlg_cell*)dialog, 1);
+}
 
 /*!
  * \brief Dummy callback just to keep the compiler happy
@@ -613,6 +663,91 @@ void dlg_tmcb_dummy(struct cell* t, int type, struct tmcb_params *param)
        return;
 }
 
+/*!
+ * \brief Release a transaction from a dialog
+ * \param t transaction
+ * \param type type of the entered callback
+ * \param param saved dialog structure in the callback
+ */
+static void release_dlg_from_tm(struct cell* t,
+                                int type,
+                                struct tmcb_params *param)
+{
+    struct dlg_cell *dlg = get_dialog_from_tm(t);
+
+    if (!dlg)
+    {
+        LM_ERR("Failed to get and unref dialog from transaction!");
+        return;
+    }
+
+    unreference_dialog(dlg);
+}
+
+/*!
+ * \brief Register a transaction on a dialog
+ * \param t transaction
+ * \param type type of the entered callback
+ * \param param saved dialog structure in the callback
+ */
+static void store_dlg_in_tm(struct sip_msg* msg,
+                            struct cell* t,
+                            struct dlg_cell *dlg)
+{
+    if( !msg || msg == FAKED_REPLY || !t || !dlg)
+    {
+        LM_ERR("invalid parameter msg(%p), t(%p), dlg(%p)\n", msg, t, dlg);
+        return;
+    }
+
+    if(get_dialog_from_tm(t))
+    {
+        LM_NOTICE("dialog %p is already set for this transaction!\n",dlg);
+        return;
+    }
+
+    if( d_tmb.register_tmcb (msg,
+                             t,
+                             TMCB_MAX,
+                             dlg_tmcb_dummy,
+                             (void*)dlg, 0)<0 )
+    {
+        LM_ERR("failed cache in T the shortcut to dlg %p\n",dlg);
+        return;
+    }
+
+    ref_dlg(dlg, 1);
+
+    if (d_tmb.register_tmcb (msg,
+                             t,
+                             TMCB_DESTROY,
+                             release_dlg_from_tm,
+                             (void*)dlg, NULL)<0 )
+    {
+        LM_ERR("failed to register unref tm for handling dialog-termination\n");
+    }
+}
+
+/*!
+ * \brief Callback to register a transaction on a dialog
+ * \param t transaction, unused
+ * \param type type of the entered callback
+ * \param param saved dialog structure in the callback
+ */
+static void store_dlg_in_tm_cb (struct cell* t,
+                                int type,
+                                struct tmcb_params *param)
+{
+    struct dlg_cell *dlg = (struct dlg_cell *)(*param->param);
+
+    struct sip_msg* msg = param->rpl;
+    if (msg == NULL || msg == FAKED_REPLY)
+    {
+        msg = param->req;
+    }
+
+    store_dlg_in_tm (msg, t, dlg);
+}
 
 /*!
  * \brief Create a new dialog from a sip message
@@ -627,92 +762,113 @@ void dlg_tmcb_dummy(struct cell* t, int type, struct tmcb_params *param)
  * \param t transaction
  * \return 0 on success, -1 on failure
  */ 
-int dlg_new_dialog(struct sip_msg *msg, struct cell *t)
+int dlg_new_dialog(struct sip_msg *req, struct cell *t, const int run_initial_cbs)
 {
        struct dlg_cell *dlg;
        str s;
-       str req_uri;
+       str callid;
+    str ftag;
+    str ttag;
+    str req_uri;
+    unsigned int dir;
+    unsigned int del;
 
-       if((msg->to==NULL && parse_headers(msg, HDR_TO_F,0)<0) || msg->to==NULL)
-       {
-               LM_ERR("bad request or missing TO hdr\n");
-               return -1;
-       }
-       s = get_to(msg)->tag_value;
-       if(s.s!=0 && s.len!=0)
-               return -1;
+    if(current_dlg_pointer != NULL)
+        return -1;
 
-       if(msg->first_line.u.request.method_value==METHOD_CANCEL)
+       if(req->first_line.u.request.method_value == METHOD_CANCEL)
                return -1;
 
-       if(parse_from_header(msg))
+    if(pre_match_parse( req, &callid, &ftag, &ttag, 0)<0) {
+        LM_WARN("pre-matching failed\n");
+        return -1;
+    }
+
+    if(ttag.s!=0 && ttag.len!=0)
+        return -1;
+
+    if(pv_printf_s(req, ruri_param_model, &req_uri)<0) {
+        LM_ERR("error - cannot print the r-uri format\n");
+        return -1;
+    }
+    trim(&req_uri);
+
+    if (detect_spirals)
+    {
+        if (spiral_detected == 1)
+            return 0;
+
+        dir = DLG_DIR_NONE;
+
+        dlg = get_dlg(&callid, &ftag, &ttag, &dir, &del);
+        if (del == 1)
+        {
+            LM_WARN("Failed to get dialog (callid: '%.*s') because it is marked for deletion!\n",
+                callid.len, callid.s);
+            unref_dlg(dlg, 1);
+            return 0;
+        }
+        if (dlg)
+        {
+            LM_DBG("Callid '%.*s' found, must be a spiraled request\n",
+                callid.len, callid.s);
+            spiral_detected = 1;
+
+            if (run_initial_cbs)
+                run_dlg_callbacks( DLGCB_SPIRALED, dlg, req, NULL, DLG_DIR_DOWNSTREAM, 0);
+            // get_dlg with del==0 has incremented the ref count by 1
+            unref_dlg(dlg, 1);
+            goto finish;
+        }
+    }
+    spiral_detected = 0;
+
+    dlg = build_new_dlg (&callid /*callid*/,
+                         &(get_from(req)->uri) /*from uri*/,
+                         &(get_to(req)->uri) /*to uri*/,
+                         &ftag/*from_tag*/,
+                         &req_uri /*r-uri*/ );
+
+       if (dlg==0)
        {
-               LM_ERR("bad request or missing FROM hdr\n");
-               return -1;
-       }
-       if((msg->callid==NULL && parse_headers(msg,HDR_CALLID_F,0)<0)
-                       || msg->callid==NULL){
-               LM_ERR("bad request or missing CALLID hdr\n");
-               return -1;
-       }
-       s = msg->callid->body;
-       trim(&s);
-
-       if (pv_printf_s(msg, ruri_param_model, &req_uri)<0) {
-               LM_ERR("error - cannot print the r-uri format\n");
-               return -1;
-       }
-       trim(&req_uri);
-
-       /* some sanity checks */
-       if (s.len==0 || get_from(msg)->tag_value.len==0) {
-               LM_ERR("invalid request -> callid (%d) or from TAG (%d) empty\n",
-                       s.len, get_from(msg)->tag_value.len);
-               return -1;
-       }
-
-       dlg = build_new_dlg(&s /*callid*/, &(get_from(msg)->uri) /*from uri*/,
-               &(get_to(msg)->uri) /*to uri*/,
-               &(get_from(msg)->tag_value)/*from_tag*/, &req_uri /*r-uri*/ );
-       if (dlg==0) {
                LM_ERR("failed to create new dialog\n");
                return -1;
        }
 
-       
-
        /* save caller's tag, cseq, contact and record route*/
-       if (populate_leg_info(dlg, msg, t, DLG_CALLER_LEG,
-                       &(get_from(msg)->tag_value)) !=0)
+       if (populate_leg_info(dlg, req, t, DLG_CALLER_LEG,
+                       &(get_from(req)->tag_value)) !=0)
        {
                LM_ERR("could not add further info to the dialog\n");
                shm_free(dlg);
                return -1;
        }
 
-       set_current_dialog(msg, dlg);
-       _dlg_ctx.dlg = dlg;
 
        /* Populate initial varlist: */
-       dlg->vars = get_local_varlist_pointer(msg, 1);
+       dlg->vars = get_local_varlist_pointer(req, 1);
+
+       link_dlg(dlg,0);
 
-       link_dlg(dlg, 2/* extra ref for the callback and current dlg hook */);
+    if (run_initial_cbs)  run_create_callbacks( dlg, req);
 
        /* first INVITE seen (dialog created, unconfirmed) */
        if ( seq_match_mode!=SEQ_MATCH_NO_ID &&
-                       add_dlg_rr_param( msg, dlg->h_entry, dlg->h_id)<0 ) {
+                       add_dlg_rr_param( req, dlg->h_entry, dlg->h_id)<0 ) {
                LM_ERR("failed to add RR param\n");
                goto error;
        }
 
-       if ( d_tmb.register_tmcb( msg, t,
+       if ( d_tmb.register_tmcb( req, t,
                                TMCB_RESPONSE_READY|TMCB_RESPONSE_FWDED,
                                dlg_onreply, (void*)dlg, unref_new_dialog)<0 ) {
                LM_ERR("failed to register TMCB\n");
                goto error;
        }
+    // increase reference counter because of registered callback
+    ref_dlg(dlg, 1);
 
-       dlg->lifetime = get_dlg_timeout(msg);
+       dlg->lifetime = get_dlg_timeout(req);
        s.s   = _dlg_ctx.to_route_name;
        s.len = strlen(s.s);
        dlg_set_toroute(dlg, &s);
@@ -721,25 +877,29 @@ int dlg_new_dialog(struct sip_msg *msg, struct cell *t)
        if (_dlg_ctx.to_bye!=0)
                dlg->dflags |= DLG_FLAG_TOBYE;
 
-       if (t) {
-               if ( d_tmb.register_tmcb( msg, t, TMCB_MAX,
-                                       dlg_tmcb_dummy, (void*)dlg, 0)<0 ) {
-                       LM_ERR("failed cache in T the shortcut to dlg\n");
-                       goto error;
-               }
-       }
-#if 0
-               t->dialog_ctx = (void*) dlg;
-#endif
+    if_update_stat( dlg_enable_stats, processed_dlgs, 1);
 
-       run_create_callbacks( dlg, msg);
+finish:
 
-       if_update_stat( dlg_enable_stats, processed_dlgs, 1);
+    set_current_dialog(req, dlg);
+    _dlg_ctx.dlg = dlg;
+    ref_dlg(dlg, 1);
+
+       if (t) {
+           store_dlg_in_tm( req, t, dlg);
+       }
+       else
+       {
+        if ( d_tmb.register_tmcb( req, NULL, TMCB_REQUEST_FWDED,
+                store_dlg_in_tm_cb, (void*)dlg, NULL)<0 ) {
+            LM_ERR("failed to store dialog in transaction during dialog creation for later reference\n");
+        }
+       }
 
        return 0;
 error:
        unref_dlg(dlg,1);
-       profile_cleanup(msg, 0, NULL);
+       profile_cleanup(req, 0, NULL);
        return -1;
 }
 
@@ -802,21 +962,6 @@ static inline int update_cseqs(struct dlg_cell *dlg, struct sip_msg *req,
        }
 }
 
-
-/*!
- * \brief Unreference a dialog, small wrapper to care for shutdown
- * \see unref_dlg 
- * \param dialog unreferenced dialog
- */
-static void unreference_dialog(void *dialog)
-{
-       // if the dialog table is gone, it means the system is shutting down.
-       if (!d_table)
-               return;
-       unref_dlg((struct dlg_cell*)dialog, 1);
-}
-
-
 /*!
  * \brief Unreference a dialog from tm callback (another wrapper)
  * \param t transaction, unused
@@ -945,6 +1090,21 @@ void dlg_onroute(struct sip_msg* req, str *route_params, void *param)
                }
        }
 
+    /* set current dialog - it will keep a ref! */
+    set_current_dialog( req, dlg);
+    _dlg_ctx.dlg = dlg;
+
+    if ( d_tmb.register_tmcb( req, NULL, TMCB_REQUEST_FWDED,
+            store_dlg_in_tm_cb, (void*)dlg, NULL)<0 ) {
+        LM_ERR("failed to store dialog in transaction during dialog creation for later reference\n");
+    }
+
+    if (del == 1) {
+        LM_DBG( "Use the dialog (callid: '%.*s') without further handling because it is marked for deletion\n",
+            callid.len, callid.s);
+        return;
+    }
+
        /* run state machine */
        switch ( req->first_line.u.request.method_value ) {
                case METHOD_PRACK:
@@ -963,10 +1123,6 @@ void dlg_onroute(struct sip_msg* req, str *route_params, void *param)
        CURR_DLG_LIFETIME = (unsigned int)(time(0))-dlg->start_ts;
        CURR_DLG_STATUS = new_state;
 
-       /* set current dialog - it will keep a ref! */
-       set_current_dialog( req, dlg);
-       _dlg_ctx.dlg = dlg;
-
        /* delay deletion of dialog until transaction has died off in order
         * to absorb in-air messages */
        if (new_state==DLG_STATE_DELETED && old_state!=DLG_STATE_DELETED) {
@@ -1002,11 +1158,7 @@ void dlg_onroute(struct sip_msg* req, str *route_params, void *param)
                        unref++;
                }
                /* dialog terminated (BYE) */
-               run_dlg_callbacks( DLGCB_TERMINATED, dlg, req, dir, 0);
-
-               /* delete the dialog from DB */
-               if (dlg_db_mode)
-                       remove_dialog_from_db(dlg);
+        dlg_terminated( req, dlg, dir);
 
                unref_dlg(dlg, unref);
 
@@ -1033,7 +1185,7 @@ void dlg_onroute(struct sip_msg* req, str *route_params, void *param)
                }
 
                /* within dialog request */
-               run_dlg_callbacks( DLGCB_REQ_WITHIN, dlg, req, dir, 0);
+               run_dlg_callbacks( DLGCB_REQ_WITHIN, dlg, req, NULL, dir, 0);
 
                if ( (event!=DLG_EVENT_REQACK) &&
                (dlg->cbs.types)&DLGCB_RESPONSE_WITHIN ) {
@@ -1108,11 +1260,7 @@ void dlg_ontimeout( struct dlg_tl *tl)
                        dlg->tag[DLG_CALLEE_LEG].len, dlg->tag[DLG_CALLEE_LEG].s);
 
                /* dialog timeout */
-               run_dlg_callbacks( DLGCB_EXPIRED, dlg, 0, DLG_DIR_NONE, 0);
-
-               /* delete the dialog from DB */
-               if (dlg_db_mode)
-                       remove_dialog_from_db(dlg);
+               run_dlg_callbacks( DLGCB_EXPIRED, dlg, NULL, NULL, DLG_DIR_NONE, 0);
 
                unref_dlg(dlg, unref+1);
 
index d56b7fb..eef5a8f 100644 (file)
@@ -128,7 +128,7 @@ void dlg_ontimeout( struct dlg_tl *tl);
  * \param t transaction
  * \return 0 on success, -1 on failure
  */ 
-int dlg_new_dialog(struct sip_msg *msg, struct cell *t);
+int dlg_new_dialog(struct sip_msg *msg, struct cell *t, const int run_initial_cbs);
 
 
 /*!
index 412440a..ed474af 100644 (file)
@@ -61,6 +61,7 @@
 #include "dlg_hash.h"
 #include "dlg_profile.h"
 #include "dlg_req_within.h"
+#include "dlg_db_handler.h"
 
 #define MAX_LDG_LOCKS  2048
 #define MIN_LDG_LOCKS  2
@@ -153,7 +154,12 @@ inline void destroy_dlg(struct dlg_cell *dlg)
                        dlg->tag[DLG_CALLEE_LEG].len, dlg->tag[DLG_CALLEE_LEG].s);
        }
 
-       run_dlg_callbacks( DLGCB_DESTROY , dlg, 0, DLG_DIR_NONE, 0);
+       run_dlg_callbacks( DLGCB_DESTROY , dlg, NULL, NULL, DLG_DIR_NONE, 0);
+
+
+       /* delete the dialog from DB*/
+       if (dlg_db_mode)
+               remove_dialog_from_db(dlg);
 
        if(dlg==get_current_dlg_pointer())
                reset_current_dlg_pointer();
@@ -841,6 +847,11 @@ static inline int internal_mi_print_dlg(struct mi_node *rpl,
        if (node1==0)
                goto error;
 
+       p= int2str((unsigned long)dlg->ref, &len);
+       node1 = add_mi_node_child( node, MI_DUP_VALUE, "ref_count", 9, p, len);
+       if (node1==0)
+               goto error;
+
        p= int2str((unsigned long)dlg->start_ts, &len);
        node1 = add_mi_node_child(node,MI_DUP_VALUE,"timestart",9, p, len);
        if (node1==0)
@@ -939,8 +950,12 @@ static inline int internal_mi_print_dlg(struct mi_node *rpl,
                node1 = add_mi_node_child(node, 0, "context", 7, 0, 0);
                if(node1 == 0)
                        goto error;
-               run_dlg_callbacks( DLGCB_MI_CONTEXT, dlg, NULL, 
-                       DLG_DIR_NONE, (void *)node1);
+               run_dlg_callbacks( DLGCB_MI_CONTEXT,
+                                  dlg,
+                                  NULL,
+                                  NULL,
+                                  DLG_DIR_NONE,
+                                  (void *)node1);
        }
        return 0;
 
index ad6d060..f26115f 100644 (file)
@@ -38,6 +38,8 @@ typedef int (*terminate_dlg_f)(struct dlg_cell* dlg, str *hdrs);
 struct dlg_binds {
        register_dlgcb_f  register_dlgcb;
        terminate_dlg_f terminate_dlg;
+    set_dlg_variable_f set_dlg_var;
+       get_dlg_variable_f get_dlg_var;
 };
 
 
index a5f7558..fe3ddba 100644 (file)
@@ -327,17 +327,33 @@ int profile_cleanup( struct sip_msg *msg, unsigned int flags, void *param )
 }
 
 
+
+struct dlg_cell* get_dialog_from_tm(struct cell *t)
+{
+    if (t==NULL || t==T_UNDEFINED)
+        return NULL;
+
+    struct tm_callback* x = (struct tm_callback*)(t->tmcb_hl.first);
+
+    while(x){
+        membar_depends();
+        if (x->types==TMCB_MAX && x->callback==dlg_tmcb_dummy){
+            return (struct dlg_cell*)(x->param);
+        }
+        x=x->next;
+    }
+
+    return NULL;
+}
+
 /*!
  * \brief Get the current dialog for a message, if exists
  * \param msg SIP message
  * \return NULL if called in REQUEST_ROUTE, pointer to dialog ctx otherwise
  */ 
-static struct dlg_cell *get_current_dialog(struct sip_msg *msg)
+struct dlg_cell *get_current_dialog(struct sip_msg *msg)
 {
-       struct cell *trans;
-       struct tm_callback* x;
-
-       if (is_route_type(REQUEST_ROUTE)) {
+       if (is_route_type(REQUEST_ROUTE|BRANCH_ROUTE)) {
                /* use the per-process static holder */
                if (msg->id==current_dlg_msg_id)
                        return current_dlg_pointer;
@@ -348,18 +364,7 @@ static struct dlg_cell *get_current_dialog(struct sip_msg *msg)
                return NULL;
        } else {
                /* use current transaction to get dialog */
-               trans = d_tmb.t_gett();
-               if (trans==NULL || trans==T_UNDEFINED)
-                       return NULL;
-               x=(struct tm_callback*)(trans->tmcb_hl.first);
-               while(x){
-                       membar_depends();
-                       if (x->types==TMCB_MAX && x->callback==dlg_tmcb_dummy){
-                               return (struct dlg_cell*)(x->param);
-                       }
-                       x=x->next;
-               }
-               return NULL;
+           return get_dialog_from_tm(d_tmb.t_gett());
        }
 }
 
index 608102b..f11af76 100644 (file)
@@ -33,6 +33,8 @@
 #include "../../parser/msg_parser.h"
 #include "../../locking.h"
 #include "../../str.h"
+#include "../../modules/tm/h_table.h"
+
 
 
 /*!
@@ -83,6 +85,10 @@ struct dlg_cell *get_current_dlg_pointer(void);
 
 void reset_current_dlg_pointer(void);
 
+struct dlg_cell* get_dialog_from_tm(struct cell *t);
+
+struct dlg_cell *get_current_dialog(struct sip_msg *msg);
+
 /*!
  * \brief Add profile definitions to the global list
  * \see new_dlg_profile
index 36e3012..a1c5d95 100644 (file)
@@ -175,7 +175,7 @@ void bye_reply_cb(struct cell* t, int type, struct tmcb_params* ps){
                        unref++;
                }
                /* dialog terminated (BYE) */
-               run_dlg_callbacks( DLGCB_TERMINATED, dlg, ps->req, DLG_DIR_NONE, 0);
+               run_dlg_callbacks( DLGCB_TERMINATED, dlg, ps->req, ps->rpl, DLG_DIR_NONE, 0);
 
                LM_DBG("first final reply\n");
                /* derefering the dialog */
index a56dba4..6483233 100644 (file)
@@ -29,6 +29,7 @@
 #include "dlg_db_handler.h"
 
 dlg_ctx_t _dlg_ctx;
+extern int spiral_detected;
 
 /*! global variable table, in case the dialog does not exist yet */
 struct dlg_var * var_table = 0;
@@ -214,6 +215,61 @@ void print_lists(struct dlg_cell *dlg) {
        }
 }
 
+str * get_dlg_variable(struct dlg_cell *dlg, str *key)
+{
+    str* var = NULL;
+
+    if( !dlg || !key || key->len > strlen(key->s))
+    {
+        LM_ERR("BUG - bad parameters\n");
+
+        return NULL;
+    }
+
+    dlg_lock(d_table, &(d_table->entries[dlg->h_entry]));
+    var = get_dlg_variable_unsafe( dlg, key);
+    dlg_unlock(d_table, &(d_table->entries[dlg->h_entry]));
+
+    return var;
+}
+
+int set_dlg_variable(struct dlg_cell *dlg, str *key, str *val)
+{
+    if( !dlg || !key || key->len > strlen(key->s) || (val && val->len > strlen(val->s)))
+    {
+        LM_ERR("BUG - bad parameters\n");
+        return -1;
+    }
+
+    dlg_lock(d_table, &(d_table->entries[dlg->h_entry]));
+
+    if( !val)
+    {
+        if (set_dlg_variable_unsafe(dlg, key, NULL, 1)!=0) {
+            LM_ERR("failed to delete dialog variable <%.*s>\n", key->len,key->s);
+            goto error;
+        }
+    } else {
+        if (set_dlg_variable_unsafe(dlg, key, val, 1)!=0) {
+            LM_ERR("failed to store dialog values <%.*s>\n",key->len,key->s);
+            goto error;
+        }
+    }
+
+    dlg->dflags &= DLG_FLAG_CHANGED_VARS;
+    dlg_unlock(d_table, &(d_table->entries[dlg->h_entry]));
+    if ( dlg_db_mode==DB_MODE_REALTIME )
+        update_dialog_dbinfo(dlg);
+
+    print_lists(dlg);
+
+    return 0;
+
+error:
+    dlg_unlock(d_table, &(d_table->entries[dlg->h_entry]));
+    return -1;
+}
+
 int pv_get_dlg_variable(struct sip_msg *msg, pv_param_t *param, pv_value_t *res)
 {
        struct dlg_cell *dlg;
@@ -225,7 +281,7 @@ int pv_get_dlg_variable(struct sip_msg *msg, pv_param_t *param, pv_value_t *res)
        }
 
        /* Retrieve the current dialog */
-       dlg=get_current_dlg_pointer();
+       dlg=get_current_dialog( msg);
 
        if (dlg) {
                /* Lock the dialog */
@@ -254,7 +310,7 @@ int pv_set_dlg_variable(struct sip_msg* msg, pv_param_t *param, int op, pv_value
        struct dlg_cell *dlg;
 
        /* Retrieve the current dialog */
-       dlg=get_current_dlg_pointer();
+       dlg=get_current_dialog( msg);
        
        if (dlg) {
                /* Lock the dialog */
@@ -666,3 +722,10 @@ dlg_ctx_t* dlg_get_dlg_ctx(void)
 {
        return &_dlg_ctx;
 }
+
+int spiral_detect_reset(struct sip_msg *foo, unsigned int flags, void *bar)
+{
+       spiral_detected = -1;
+
+       return 0;
+}
index 14ffa58..cd53807 100644 (file)
@@ -49,6 +49,8 @@ struct dlg_var {
        struct dlg_var *next;
 };
 
+str * get_dlg_variable(struct dlg_cell *dlg, str *key);
+int set_dlg_variable(struct dlg_cell *dlg, str *key, str *val);
 
 int pv_parse_dialog_var_name(pv_spec_p sp, str *in);
 
@@ -81,4 +83,6 @@ struct dlg_cell* dlg_get_ctx_dialog(void);
 
 dlg_ctx_t* dlg_get_dlg_ctx(void);
 
+int spiral_detect_reset(struct sip_msg *foo, unsigned int flags, void *bar);
+
 #endif
index 34ac2ac..ae6c95d 100644 (file)
@@ -1054,6 +1054,46 @@ modparam("dialog", "bridge_controller", "sip:ctd@kamailio.org")
                </example>
        </section>
 
+       <section>
+               <title><varname>initial_cbs_inscript</varname> (string)</title>
+               <para>
+            If the initial dialog callbacks (i.e., DLGCB_CREATED and
+            DLGCB_SPIRALED) should be executed in-script or post-script.
+            If dlg_manage() is not used, the setting of this parameter does
+            not matter; otherwise, initial callbacks will be executed
+            directly after dlg_manage() is called if this parameter is
+            enabled. If it is disabled, initial callback execution will be
+            postponed until configuration script execution completes.
+               </para>
+               <para>
+               The supported values are:
+               </para>
+               <itemizedlist>
+                       <listitem><para>
+                               <emphasis>0 - POST-SCRIPT</emphasis> - execute initial
+                               callbacks after the script completes;
+                       </para></listitem>
+                       <listitem><para>
+                               <emphasis>1 - IN-SCRIPT</emphasis> - execute initial
+                callbacks during script execution, i.e., right after
+                dlg_manage() is called;
+                       </para></listitem>
+               </itemizedlist>
+               <para>
+               <emphasis>
+                       Default value is <quote>1</quote>.
+               </emphasis>
+               </para>
+               <example>
+               <title>Set <varname>initial_cbs_inscript</varname> parameter</title>
+               <programlisting format="linespecific">
+...
+modparam("dialog", "initial_cbs_inscript", "0")
+...
+</programlisting>
+               </example>
+       </section>
+
        </section>
 
 
index df20e88..f83a05d 100644 (file)
                                dialog is terminated via BYE - it's a per dialog type.
                                </para>
                        </listitem>
+                       <listitem>
+                               <para><emphasis>DLGCB_TERMINATED_CONFIRMED</emphasis> -
+                               called when response to a BYE request is received - it's a
+                               per dialog type.
+                               </para>
+                       </listitem>
                        <listitem>
                                <para><emphasis>DLGCB_EXPIRED</emphasis> - called when the 
                                dialog expires without receiving a BYE - it's a per dialog 
index 791488d..cc65354 100644 (file)
@@ -1035,7 +1035,7 @@ __dialog_early(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
         return;
     }
 
-    uri = get_source_uri(_params->msg);
+    uri = get_source_uri(_params->rpl);
     if (!Dialog_Param_has_candidate(param, uri)) {
         if (!Dialog_Param_add_candidate(param, uri)) {
             LM_ERR("cannot add callee candidate uri to the list\n");
@@ -1079,7 +1079,7 @@ __dialog_confirmed(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params
 
     param->confirmed = True;
 
-    callee_uri = get_source_uri(_params->msg);
+    callee_uri = get_source_uri(_params->rpl);
 
     // remove all keepalives on unanswered branches
     for (i=0; i<param->callee_candidates.count; i++) {
@@ -1224,7 +1224,7 @@ __dialog_destroy(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
 static void
 __dialog_created(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
 {
-    struct sip_msg *request = _params->msg;
+    struct sip_msg *request = _params->req;
     NAT_Contact *contact;
     SIP_Dialog *dialog;
     Dialog_Param *param;
index b832cd9..ea6686c 100644 (file)
@@ -116,7 +116,7 @@ static void setup_dialog_callbacks(struct dlg_cell *did, qos_ctx_t *ctx)
 void qos_dialog_created_CB(struct dlg_cell *did, int type, struct dlg_cb_params * params)
 {
        qos_ctx_t *qos_ctx = NULL;
-       struct sip_msg* msg = params->msg;
+       struct sip_msg* msg = params->req;
        unsigned int dir = params->direction, role, other_role;
 
        if (dir == DLG_DIR_UPSTREAM) {
@@ -173,7 +173,7 @@ void qos_dialog_created_CB(struct dlg_cell *did, int type, struct dlg_cb_params
  */
 static void qos_dialog_destroy_CB(struct dlg_cell* did, int type, struct dlg_cb_params * params)
 {
-       struct sip_msg* msg = params->msg;
+       struct sip_msg* msg = params->req;
        qos_ctx_t* qos_ctx = (qos_ctx_t*)*(params->param);
 
        /* run the QOSCB_TERMINATED callback */
@@ -196,7 +196,7 @@ static void qos_dialog_destroy_CB(struct dlg_cell* did, int type, struct dlg_cb_
  */
 static void qos_dialog_request_CB(struct dlg_cell* did, int type, struct dlg_cb_params * params)
 {
-       struct sip_msg* msg = params->msg;
+       struct sip_msg* msg = params->req;
        unsigned int dir = params->direction, role, other_role;
        qos_ctx_t* qos_ctx = (qos_ctx_t*)*(params->param);
 
@@ -245,7 +245,7 @@ static void qos_dialog_request_CB(struct dlg_cell* did, int type, struct dlg_cb_
  */
 static void qos_dialog_response_CB(struct dlg_cell* did, int type, struct dlg_cb_params * params)
 {
-       struct sip_msg* msg = params->msg;
+       struct sip_msg* msg = params->rpl;
        unsigned int dir = params->direction, role, other_role;
        qos_ctx_t* qos_ctx = (qos_ctx_t*)*(params->param);
 
index 6a17c37..fb3351f 100644 (file)
@@ -252,7 +252,7 @@ void sst_dialog_created_CB(struct dlg_cell *did, int type,
 {
        sst_info_t *info = NULL;
        sst_msg_info_t minfo;
-       struct sip_msg* msg = params->msg;
+       struct sip_msg* msg = params->req;
 
        memset(&minfo, 0, sizeof(sst_msg_info_t));
        /*
@@ -431,7 +431,7 @@ static void sst_dialog_request_within_CB(struct dlg_cell* did, int type,
 {
        sst_info_t *info = (sst_info_t *)*(params->param);
        sst_msg_info_t minfo = {0,0,0,0};
-       struct sip_msg* msg = params->msg;
+       struct sip_msg* msg = params->req;
 
        if (msg->first_line.type == SIP_REQUEST) {
                if ((msg->first_line.u.request.method_value == METHOD_INVITE ||
@@ -497,7 +497,7 @@ static void sst_dialog_request_within_CB(struct dlg_cell* did, int type,
 static void sst_dialog_response_fwded_CB(struct dlg_cell* did, int type,
                struct dlg_cb_params * params) 
 {
-       struct sip_msg* msg = params->msg;
+       struct sip_msg* msg = params->rpl;
 
        /*
         * This test to see if the message is a response sould ALWAYS be