Mail archive
alpine-aports

[alpine-aports] [PATCH] main/kamailio backport mohqueue v0.11 module

From: Nathan Angelacos <nangel_at_alpinelinux.org>
Date: Wed, 24 Jun 2015 15:33:20 +0000

---
NOTE: This patch is to backport the 0.11 Mohqueue module to 
kamailio 4.2.5 in Alpine 3.2 stable
 main/kamailio/0002-mohqueue-0-11.patch | 2853 ++++++++++++++++++++++++++++++++
 1 file changed, 2853 insertions(+)
 create mode 100644 main/kamailio/0002-mohqueue-0-11.patch
diff --git a/main/kamailio/0002-mohqueue-0-11.patch b/main/kamailio/0002-mohqueue-0-11.patch
new file mode 100644
index 0000000..341ff6a
--- /dev/null
+++ b/main/kamailio/0002-mohqueue-0-11.patch
_at_@ -0,0 +1,2853 @@
+diff --git a/modules/mohqueue/NOTES b/modules/mohqueue/NOTES
+index 0741d9f..ef2fbb6 100644
+--- a/modules/mohqueue/NOTES
++++ b/modules/mohqueue/NOTES
+_at_@ -1,6 +1,9 @@
+ Things to look into:
+ 
+ * RFC3261, section 12.1.1 requires UAS to copy Record-Route
++
+ * Check to see if any memory leaks.
++
+ * Should probably respond when caller says BYE after queue says BYE.
+-* check RAck number
++
++* check RAck number
+\ No newline at end of file
+diff --git a/modules/mohqueue/mohq.c b/modules/mohqueue/mohq.c
+index 0a16606..bc54aca 100644
+--- a/modules/mohqueue/mohq.c
++++ b/modules/mohqueue/mohq.c
+_at_@ -1,9 +1,7 @@
+ /*
+- * $Id$
++ * Copyright (C) 2013-15 Robert Boisvert
+  *
+- * Copyright (C) 2013 Robert Boisvert
+- *
+- * This file is part of the mohqueue module for sip-router, a free SIP server.
++ * This file is part of the mohqueue module for Kamailio, a free SIP server.
+  *
+  * The mohqueue module is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+_at_@ -61,9 +59,9 @@ static cmd_export_t mod_cmds [] = {
+ };
+ 
+ /* PARAMETERS */
+-str db_url = str_init(DEFAULT_DB_URL);
+-str db_ctable = str_init("mohqcalls");
+-str db_qtable = str_init("mohqueues");
++str db_url = str_init (DEFAULT_DB_URL);
++str db_ctable = str_init ("mohqcalls");
++str db_qtable = str_init ("mohqueues");
+ char *mohdir = "";
+ int moh_maxcalls = 50;
+ 
+_at_@ -134,31 +132,29 @@ return 0;
+ static int init_cfg (void)
+ 
+ {
++int bfnd = 0;
++int berror = 0;
++struct stat psb [1];
++
+ /**********
+ * db_url, db_ctable, db_qtable exist?
+ **********/
+ 
+-  if (!db_url.s || db_url.len<=0)
++if (!db_url.s || db_url.len <= 0)
+   {
+-    LM_ERR ("db_url parameter not set!");
+-    return 0;
++  LM_ERR ("db_url parameter not set!\n");
++  berror = 1;
+   }
+-  pmod_data->pcfg->db_url = db_url;
+-
+-  if (!db_ctable.s || db_ctable.len<=0)
++if (!db_ctable.s || db_ctable.len <= 0)
+   {
+-    LM_ERR ("db_ctable parameter not set!");
+-    return 0;
++  LM_ERR ("db_ctable parameter not set!\n");
++  berror = 1;
+   }
+-  pmod_data->pcfg->db_ctable = db_ctable;
+-
+-  if (!db_qtable.s || db_qtable.len<=0)
++if (!db_qtable.s || db_qtable.len <= 0)
+   {
+-    LM_ERR ("db_qtable parameter not set!");
+-    return 0;
++  LM_ERR ("db_qtable parameter not set!\n");
++  berror = 1;
+   }
+-  pmod_data->pcfg->db_qtable = db_qtable;
+-
+ 
+ /**********
+ * mohdir
+_at_@ -166,49 +162,54 @@ static int init_cfg (void)
+ * o directory?
+ **********/
+ 
+-  if (!*mohdir)
++if (!*mohdir)
+   {
+-    LM_ERR ("mohdir parameter not set!");
+-    return 0;
++  LM_ERR ("mohdir parameter not set!\n");
++  berror = 1;
+   }
+-  if (strlen(mohdir) > MOHDIRLEN)
++else if (strlen (mohdir) > MOHDIRLEN)
+   {
+-    LM_ERR ("mohdir too long!");
+-    return 0;
++  LM_ERR ("mohdir too long!\n");
++  berror = 1;
+   }
+-  pmod_data->pcfg->mohdir = mohdir;
+-
+-  int bfnd = 0;
+-  struct stat psb [1];
+-  if (!lstat (mohdir, psb))
++else
+   {
++  if (!lstat (mohdir, psb))
++    {
+     if ((psb->st_mode & S_IFMT) == S_IFDIR)
+       { bfnd = 1; }
+-  }
++    }
+   if (!bfnd)
+-  {
+-    LM_ERR ("mohdir is not a directory!");
+-    return 0;
++    {
++    LM_ERR ("mohdir is not a directory!\n");
++    berror = 1;
++    }
+   }
+ 
+ /**********
+-* max calls
+-* o valid count?
++* o max calls valid?
+ * o alloc memory
++* o save data
+ **********/
+ 
+ if (moh_maxcalls < 1 || moh_maxcalls > 5000)
+   {
+-  LM_ERR ("moh_maxcalls not in range of 1-5000!");
+-  return 0;
++  LM_ERR ("moh_maxcalls not in range of 1-5000!\n");
++  berror = 1;
+   }
++if (berror)
++  { return 0; }
+ pmod_data->pcall_lst =
+   (call_lst *) shm_malloc (sizeof (call_lst) * moh_maxcalls);
+ if (!pmod_data->pcall_lst)
+   {
+-  LM_ERR ("Unable to allocate shared memory");
+-  return -1;
++  LM_ERR ("Unable to allocate shared memory!\n");
++  return 0;
+   }
++pmod_data->pcfg->db_url = db_url;
++pmod_data->pcfg->db_ctable = db_ctable;
++pmod_data->pcfg->db_qtable = db_qtable;
++pmod_data->pcfg->mohdir = mohdir;
+ memset (pmod_data->pcall_lst, 0, sizeof (call_lst) * moh_maxcalls);
+ pmod_data->call_cnt = moh_maxcalls;
+ return -1;
+_at_@ -234,13 +235,13 @@ static int init_db (void)
+ str *pdb_url = &pmod_data->pcfg->db_url;
+ if (db_bind_mod (pdb_url, pmod_data->pdb))
+   {
+-  LM_ERR ("Unable to bind DB API using %s", pdb_url->s);
++  LM_ERR ("Unable to bind DB API using %s!\n", pdb_url->s);
+   return 0;
+   }
+ db_func_t *pdb = pmod_data->pdb;
+ if (!DB_CAPABILITY ((*pdb), DB_CAP_ALL))
+   {
+-  LM_ERR ("Selected database %s lacks required capabilities", pdb_url->s);
++  LM_ERR ("Selected database %s lacks required capabilities!\n", pdb_url->s);
+   return 0;
+   }
+ db1_con_t *pconn = mohq_dbconnect ();
+_at_@ -256,14 +257,14 @@ if (!pconn)
+ if (db_check_table_version (pdb, pconn,
+   &pmod_data->pcfg->db_ctable, MOHQ_CTABLE_VERSION) < 0)
+   {
+-  LM_ERR ("%s table in DB %s not at version %d",
++  LM_ERR ("%s table in DB %s not at version %d!\n",
+     pmod_data->pcfg->db_ctable.s, pdb_url->s, MOHQ_CTABLE_VERSION);
+   goto dberr;
+   }
+ if (db_check_table_version (pdb, pconn,
+   &pmod_data->pcfg->db_qtable, MOHQ_QTABLE_VERSION) < 0)
+   {
+-  LM_ERR ("%s table in DB %s not at version %d",
++  LM_ERR ("%s table in DB %s not at version %d!\n",
+     pmod_data->pcfg->db_qtable.s, pdb_url->s, MOHQ_QTABLE_VERSION);
+   goto dberr;
+   }
+_at_@ -303,7 +304,7 @@ if (rank == PROC_INIT || rank == PROC_TCP_MAIN || rank == PROC_MAIN)
+   { return 0; }
+ if (!pmod_data->pdb->init)
+   {
+-  LM_CRIT ("DB API not loaded!");
++  LM_CRIT ("DB API not loaded!\n");
+   return -1;
+   }
+ return 0;
+_at_@ -357,7 +358,7 @@ int mod_init (void)
+ pmod_data = (mod_data *) shm_malloc (sizeof (mod_data));
+ if (!pmod_data)
+   {
+-  LM_ERR ("Unable to allocate shared memory");
++  LM_ERR ("Unable to allocate shared memory!\n");
+   return -1;
+   }
+ memset (pmod_data, 0, sizeof (mod_data));
+_at_@ -373,47 +374,59 @@ if (!init_db ())
+ 
+ if (sl_load_api (pmod_data->psl))
+   {
+-  LM_ERR ("Unable to load SL module");
++  LM_ERR ("Unable to load SL module!\n");
+   goto initerr;
+   }
+ if (load_tm_api (pmod_data->ptm))
+   {
+-  LM_ERR ("Unable to load TM module");
++  LM_ERR ("Unable to load TM module!\n");
+   goto initerr;
+   }
+ if (load_rr_api (pmod_data->prr))
+   {
+-  LM_ERR ("Unable to load RR module");
++  LM_ERR ("Unable to load RR module!\n");
+   goto initerr;
+   }
+ pmod_data->fn_rtp_answer = find_export ("rtpproxy_answer", 0, 0);
+ if (!pmod_data->fn_rtp_answer)
+   {
+-  LM_ERR ("Unable to load rtpproxy_answer");
++  LM_ERR ("Unable to load rtpproxy_answer!\n");
+   goto initerr;
+   }
+ pmod_data->fn_rtp_offer = find_export ("rtpproxy_offer", 0, 0);
+ if (!pmod_data->fn_rtp_offer)
+   {
+-  LM_ERR ("Unable to load rtpproxy_offer");
++  LM_ERR ("Unable to load rtpproxy_offer!\n");
+   goto initerr;
+   }
+ pmod_data->fn_rtp_stream_c = find_export ("rtpproxy_stream2uac", 2, 0);
+ if (!pmod_data->fn_rtp_stream_c)
+   {
+-  LM_ERR ("Unable to load rtpproxy_stream2uac");
++  LM_ERR ("Unable to load rtpproxy_stream2uac!\n");
+   goto initerr;
+   }
+ pmod_data->fn_rtp_stream_s = find_export ("rtpproxy_stream2uas", 2, 0);
+ if (!pmod_data->fn_rtp_stream_s)
+   {
+-  LM_ERR ("Unable to load rtpproxy_stream2uas");
++  LM_ERR ("Unable to load rtpproxy_stream2uas!\n");
++  goto initerr;
++  }
++pmod_data->fn_rtp_stop_c = find_export ("rtpproxy_stop_stream2uac", 0, 0);
++if (!pmod_data->fn_rtp_stop_c)
++  {
++  LM_ERR ("Unable to load rtpproxy_stop_stream2uac!\n");
++  goto initerr;
++  }
++pmod_data->fn_rtp_stop_s = find_export ("rtpproxy_stop_stream2uas", 0, 0);
++if (!pmod_data->fn_rtp_stop_s)
++  {
++  LM_ERR ("Unable to load rtpproxy_stop_stream2uas!\n");
+   goto initerr;
+   }
+ pmod_data->fn_rtp_destroy = find_export ("rtpproxy_destroy", 0, 0);
+ if (!pmod_data->fn_rtp_destroy)
+   {
+-  LM_ERR ("Unable to load rtpproxy_destroy");
++  LM_ERR ("Unable to load rtpproxy_destroy!\n");
+   goto initerr;
+   }
+ 
+_at_@ -440,4 +453,4 @@ if (pmod_data->pcall_lock->plock)
+ shm_free (pmod_data);
+ pmod_data = NULL;
+ return -1;
+-}
++}
+\ No newline at end of file
+diff --git a/modules/mohqueue/mohq.h b/modules/mohqueue/mohq.h
+index bc40432..3d5346b 100644
+--- a/modules/mohqueue/mohq.h
++++ b/modules/mohqueue/mohq.h
+_at_@ -1,9 +1,7 @@
+ /*
+- * $Id$
++ * Copyright (C) 2013-15 Robert Boisvert
+  *
+- * Copyright (C) 2013 Robert Boisvert
+- *
+- * This file is part of the mohqueue module for sip-router, a free SIP server.
++ * This file is part of the mohqueue module for Kamailio, a free SIP server.
+  *
+  * The mohqueue module is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+_at_@ -63,31 +61,35 @@ typedef struct
+ 
+ /* call_state values */
+ #define CLSTA_ENTER     100
+-#define CLSTA_PRACKSTRT 101
+-#define CLSTA_PRACKRPLY 102
+-#define CLSTA_RINGING   103
++#define CLSTA_TRYING    101
++#define CLSTA_PRACKSTRT 102
++#define CLSTA_PRACKRPLY 103
+ #define CLSTA_INVITED   104
+ #define CLSTA_CANCEL    105
+ #define CLSTA_INQUEUE   200
+ #define CLSTA_REFER     301
+ #define CLSTA_RFRWAIT   302
++#define CLSTA_BYEOK     304
+ #define CLSTA_BYE       305
+ 
+ typedef struct
+   {
+-  int call_active;
+-  char call_id [101];
+-  char call_from [URI_LEN + 1];
++  char call_buffer [1024];
++  size_t call_bufpos;
++  char *call_id;
++  char *call_from;
+   char call_referto [URI_LEN + 1];
+-  char call_contact [URI_LEN + 1];
+-  char call_tag [101];
+-  char call_via [1024];
++  char *call_contact;
++  char *call_tag;
++  char *call_via;
++  char *call_route;
+   char call_addr [IP_ADDR_MAX_STR_SIZE + 4];
+   int call_state;
+   int call_cseq;
+   int call_aport;
+   mohq_lst *pmohq;
+   time_t call_time;
++  time_t refer_time;
+   unsigned int call_hash;
+   unsigned int call_label;
+   sip_msg_t *call_pmsg;
+_at_@ -120,6 +122,8 @@ typedef struct
+   cmd_function fn_rtp_offer;
+   cmd_function fn_rtp_stream_c;
+   cmd_function fn_rtp_stream_s;
++  cmd_function fn_rtp_stop_c;
++  cmd_function fn_rtp_stop_s;
+   } mod_data;
+ 
+ /**********
+diff --git a/modules/mohqueue/mohq_common.h b/modules/mohqueue/mohq_common.h
+index 8cf4781..10f6249 100644
+--- a/modules/mohqueue/mohq_common.h
++++ b/modules/mohqueue/mohq_common.h
+_at_@ -1,9 +1,7 @@
+ /*
+- * $Id$
++ * Copyright (C) 2013-15 Robert Boisvert
+  *
+- * Copyright (C) 2013 Robert Boisvert
+- *
+- * This file is part of the mohqueue module for sip-router, a free SIP server.
++ * This file is part of the mohqueue module for Kamailio, a free SIP server.
+  *
+  * The mohqueue module is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+_at_@ -53,6 +51,7 @@
+ #include "../../parser/contact/parse_contact.h"
+ #include "../../parser/parse_expires.h"
+ #include "../../parser/parse_from.h"
++#include "../../parser/parse_rr.h"
+ #include "../../parser/sdp/sdp.h"
+ 
+ /* convenience macros */
+_at_@ -69,7 +68,7 @@
+ 
+ #define MOHQ_STR_APPEND_L( str1, str1_lim, s2, s2_len ) \
+ 	if ((str1)->len + (s2_len) >= (str1_lim)) { \
+-	    LM_ERR( "Failed to append to str: too long" ); \
++	    LM_ERR( "Failed to append to str: too long!\n" ); \
+ 	} else { \
+ 	    MOHQ_STR_APPEND((str1), (s2), (s2_len)); \
+ 	    (str1_lim) -= (s2_len); \
+_at_@ -84,7 +83,7 @@
+ 
+ #define MOHQ_STR_APPEND_CSTR_L( str1, str1_lim, cstr1 ) \
+ 	if ((str1)->len + strlen(cstr1) >= (str1_lim)) { \
+-	    LM_ERR( "Failed to append to str: too long" ); \
++	    LM_ERR( "Failed to append to str: too long!\n" ); \
+ 	} else { \
+ 	    MOHQ_STR_APPEND_CSTR((str1), (cstr1)); \
+ 	}
+diff --git a/modules/mohqueue/mohq_db.c b/modules/mohqueue/mohq_db.c
+index b07985d..56e420f 100644
+--- a/modules/mohqueue/mohq_db.c
++++ b/modules/mohqueue/mohq_db.c
+_at_@ -1,9 +1,7 @@
+ /*
+- * $Id$
++ * Copyright (C) 2013-15 Robert Boisvert
+  *
+- * Copyright (C) 2013 Robert Boisvert
+- *
+- * This file is part of the mohqueue module for sip-router, a free SIP server.
++ * This file is part of the mohqueue module for Kamailio, a free SIP server.
+  *
+  * The mohqueue module is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+_at_@ -216,7 +214,7 @@ pcall->call_time = time (0);
+ fill_call_vals (prvals, pcall, CALL_COLCNT);
+ if (pdb->insert (pconn, prkeys, prvals, CALL_COLCNT) < 0)
+   {
+-  LM_WARN ("%sUnable to add new row to %s", pfncname,
++  LM_WARN ("%sUnable to add new row to %s\n", pfncname,
+     pmod_data->pcfg->db_ctable.s);
+   }
+ mohq_dbdisconnect (pconn);
+_at_@ -243,7 +241,7 @@ db_func_t *pdb = pmod_data->pdb;
+ pdb->use_table (pconn, &pmod_data->pcfg->db_ctable);
+ if (pdb->delete (pconn, 0, 0, 0, 0) < 0)
+   {
+-  LM_WARN ("%sUnable to delete all rows from %s", pfncname,
++  LM_WARN ("%sUnable to delete all rows from %s\n", pfncname,
+     pmod_data->pcfg->db_ctable.s);
+   }
+ return;
+_at_@ -277,7 +275,7 @@ db_val_t prvals [1];
+ set_call_val (prvals, 0, CALLCOL_CALL, pcall->call_id);
+ if (pdb->delete (pconn, prkeys, 0, prvals, 1) < 0)
+   {
+-  LM_WARN ("%sUnable to delete row from %s", pfncname,
++  LM_WARN ("%sUnable to delete row from %s\n", pfncname,
+     pmod_data->pcfg->db_ctable.s);
+   }
+ mohq_dbdisconnect (pconn);
+_at_@ -297,7 +295,7 @@ db1_con_t *mohq_dbconnect (void)
+ str *pdb_url = &pmod_data->pcfg->db_url;
+ db1_con_t *pconn = pmod_data->pdb->init (pdb_url);
+ if (!pconn)
+-  { LM_ERR ("Unable to connect to DB %s", pdb_url->s); }
++  { LM_ERR ("Unable to connect to DB %s!\n", pdb_url->s); }
+ return pconn;
+ }
+ 
+_at_@ -348,7 +346,7 @@ db_val_t puvals [1];
+ fill_call_vals (puvals, pcall, CALLCOL_STATE);
+ if (pdb->update (pconn, pqkeys, 0, pqvals, pukeys, puvals, 1, 1) < 0)
+   {
+-  LM_WARN ("%sUnable to update row in %s", pfncname,
++  LM_WARN ("%sUnable to update row in %s\n", pfncname,
+     pmod_data->pcfg->db_ctable.s);
+   }
+ mohq_dbdisconnect (pconn);
+_at_@ -390,7 +388,7 @@ puvals->type = DB1_INT;
+ puvals->nul = 0;
+ if (pdb->update (pconn, pqkeys, 0, pqvals, pukeys, puvals, 1, 1) < 0)
+   {
+-  LM_WARN ("%sUnable to update row in %s", pfncname,
++  LM_WARN ("%sUnable to update row in %s\n", pfncname,
+     pmod_data->pcfg->db_qtable.s);
+   }
+ mohq_dbdisconnect (pconn);
+_at_@ -428,7 +426,7 @@ for (nidx = 0; nidx < MOHQ_COLCNT; nidx++)
+ db1_res_t *presult = NULL;
+ if (pdb->query (pconn, 0, 0, 0, prkeys, 0, MOHQ_COLCNT, 0, &presult))
+   {
+-  LM_ERR ("%stable query (%s) failed!", pfncname,
++  LM_ERR ("%stable query (%s) failed!\n", pfncname,
+     pmod_data->pcfg->db_qtable.s);
+   return;
+   }
+_at_@ -449,7 +447,7 @@ for (nidx = 0; nidx < nrows; nidx++)
+   struct sip_uri puri_parsed [1];
+   if (parse_uri (puri, strlen (puri), puri_parsed))
+     {
+-    LM_ERR ("Queue,Field (%s,%.*s): %s is not a valid URI!", pqname,
++    LM_ERR ("Queue,Field (%s,%.*s): %s is not a valid URI!\n", pqname,
+       STR_FMT (&MOHQCSTR_URI), puri);
+     continue;
+     }
+_at_@ -477,7 +475,7 @@ for (nidx = 0; nidx < nrows; nidx++)
+       struct stat psb [1];
+       if (lstat (pmohdir, psb))
+         {
+-        LM_ERR ("Queue,Field (%s,%.*s): Unable to find %s!", pqname,
++        LM_ERR ("Queue,Field (%s,%.*s): Unable to find %s!\n", pqname,
+           STR_FMT (&MOHQCSTR_MDIR), pmohdir);
+         continue;
+         }
+_at_@ -485,7 +483,7 @@ for (nidx = 0; nidx < nrows; nidx++)
+         {
+         if ((psb->st_mode & S_IFMT) != S_IFDIR)
+           {
+-          LM_ERR ("Queue,Field (%s,%.*s): %s is not a directory!", pqname,
++          LM_ERR ("Queue,Field (%s,%.*s): %s is not a directory!\n", pqname,
+             STR_FMT (&MOHQCSTR_MDIR), pmohdir);
+           continue;
+           }
+_at_@ -501,7 +499,7 @@ for (nidx = 0; nidx < nrows; nidx++)
+     (char *)VAL_STRING (prowvals + MOHQCOL_MFILE));
+   if (!pmohfiles [0])
+     {
+-    LM_ERR ("Queue,Field (%s,%.*s): Unable to find MOH files (%s/%s.*)!",
++    LM_ERR ("Queue,Field (%s,%.*s): Unable to find MOH files (%s/%s.*)!\n",
+       pqname, STR_FMT (&MOHQCSTR_MDIR), pmohdir,
+       (char *)VAL_STRING (prowvals + MOHQCOL_MFILE));
+     continue;
+_at_@ -525,21 +523,21 @@ for (nidx = 0; nidx < nrows; nidx++)
+       if (strcmp (pqlst [nidx2].mohq_mohdir, pmohdir))
+         {
+         strcpy (pqlst [nidx2].mohq_mohdir, pmohdir);
+-        LM_INFO ("Queue,Field (%s,%.*s): Changed", pqname,
++        LM_INFO ("Queue,Field (%s,%.*s): Changed\n", pqname,
+           STR_FMT (&MOHQCSTR_MDIR));
+         }
+       ptext = (char *)VAL_STRING (prowvals + MOHQCOL_MFILE);
+       if (strcmp (pqlst [nidx2].mohq_mohfile, ptext))
+         {
+         strcpy (pqlst [nidx2].mohq_mohfile, ptext);
+-        LM_INFO ("Queue,Field (%s,%.*s): Changed", pqname,
++        LM_INFO ("Queue,Field (%s,%.*s): Changed\n", pqname,
+           STR_FMT (&MOHQCSTR_MFILE));
+         }
+       ptext = (char *)VAL_STRING (prowvals + MOHQCOL_NAME);
+       if (strcmp (pqlst [nidx2].mohq_name, ptext))
+         {
+         strcpy (pqlst [nidx2].mohq_name, ptext);
+-        LM_INFO ("Queue,Field (%s,%.*s): Changed", pqname,
++        LM_INFO ("Queue,Field (%s,%.*s): Changed\n", pqname,
+           STR_FMT (&MOHQCSTR_NAME));
+         }
+       int bdebug = VAL_INT (prowvals + MOHQCOL_DEBUG) ? MOHQF_DBG : 0;
+_at_@ -549,7 +547,7 @@ for (nidx = 0; nidx < nrows; nidx++)
+           { pqlst [nidx2].mohq_flags |= MOHQF_DBG; }
+         else
+           { pqlst [nidx2].mohq_flags &= ~MOHQF_DBG; }
+-        LM_INFO ("Queue,Field (%s,%.*s): Changed", pqname,
++        LM_INFO ("Queue,Field (%s,%.*s): Changed\n", pqname,
+           STR_FMT (&MOHQCSTR_DEBUG));
+         }
+       bfnd = -1;
+_at_@ -576,7 +574,7 @@ for (nidx = 0; nidx < nrows; nidx++)
+     pnewlst = (mohq_lst *) shm_malloc (sizeof (mohq_lst) * nsize);
+     if (!pnewlst)
+       {
+-      LM_ERR ("%sUnable to allocate shared memory!", pfncname);
++      LM_ERR ("%sUnable to allocate shared memory!\n", pfncname);
+       return;
+       }
+     pmod_data->mohq_cnt = nsize;
+_at_@ -592,7 +590,7 @@ for (nidx = 0; nidx < nrows; nidx++)
+       (char *)VAL_STRING (prowvals + MOHQCOL_NAME));
+     if (VAL_INT (prowvals + MOHQCOL_DEBUG))
+       { pnewlst [nsize].mohq_flags |= MOHQF_DBG; }
+-    LM_INFO ("Added new queue (%s)", pnewlst [nsize].mohq_name);
++    LM_INFO ("Added new queue (%s)\n", pnewlst [nsize].mohq_name);
+     if (nsize)
+       { shm_free (pmod_data->pmohq_lst); }
+     pmod_data->pmohq_lst = pnewlst;
+_at_@ -613,7 +611,7 @@ for (nidx = 0; nidx < pmod_data->mohq_cnt; nidx++)
+ 
+   if (pqlst [nidx].mohq_flags & MOHQF_CHK)
+     { continue; }
+-  LM_INFO ("Removed queue (%s)", pqlst [nidx].mohq_name);
++  LM_INFO ("Removed queue (%s)\n", pqlst [nidx].mohq_name);
+   if (nidx != (pmod_data->mohq_cnt - 1))
+     {
+     memcpy (&pqlst [nidx], &pqlst [pmod_data->mohq_cnt - 1],
+diff --git a/modules/mohqueue/mohq_db.h b/modules/mohqueue/mohq_db.h
+index f857214..c343601 100644
+--- a/modules/mohqueue/mohq_db.h
++++ b/modules/mohqueue/mohq_db.h
+_at_@ -1,9 +1,7 @@
+ /*
+- * $Id$
++ * Copyright (C) 2013-15 Robert Boisvert
+  *
+- * Copyright (C) 2013 Robert Boisvert
+- *
+- * This file is part of the mohqueue module for sip-router, a free SIP server.
++ * This file is part of the mohqueue module for Kamailio, a free SIP server.
+  *
+  * The mohqueue module is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+diff --git a/modules/mohqueue/mohq_funcs.c b/modules/mohqueue/mohq_funcs.c
+index 17316d3..210f14e 100644
+--- a/modules/mohqueue/mohq_funcs.c
++++ b/modules/mohqueue/mohq_funcs.c
+_at_@ -1,9 +1,7 @@
+ /*
+- * $Id$
++ * Copyright (C) 2013-15 Robert Boisvert
+  *
+- * Copyright (C) 2013 Robert Boisvert
+- *
+- * This file is part of the mohqueue module for sip-router, a free SIP server.
++ * This file is part of the mohqueue module for Kamailio, a free SIP server.
+  *
+  * The mohqueue module is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+_at_@ -34,7 +32,7 @@
+ #define ALLOWHDR "Allow: INVITE, ACK, BYE, CANCEL, NOTIFY, PRACK"
+ #define CLENHDR "Content-Length"
+ #define SIPEOL  "\r\n"
+-#define USRAGNT "Kamailio MOH Queue v1.0"
++#define USRAGNT "Kamailio MOH Queue v1.2"
+ 
+ /**********
+ * local constants
+_at_@ -48,6 +46,7 @@ str pinvite [1] = {STR_STATIC_INIT ("INVITE")};
+ str pmi_nolock [1] = {STR_STATIC_INIT ("Unable to lock queue")};
+ str pmi_noqueue [1] = {STR_STATIC_INIT ("No matching queue name found")};
+ str prefer [1] = {STR_STATIC_INIT ("REFER")};
++str presp_busy [1] = {STR_STATIC_INIT ("Busy Here")};
+ str presp_noaccept [1] = {STR_STATIC_INIT ("Not Acceptable Here")};
+ str presp_noallow [1] = {STR_STATIC_INIT ("Method Not Allowed")};
+ str presp_nocall [1] = {STR_STATIC_INIT ("Call/Transaction Does Not Exist")};
+_at_@ -57,6 +56,7 @@ str presp_reqterm [1] = {STR_STATIC_INIT ("Request Terminated")};
+ str presp_ring [1] = {STR_STATIC_INIT ("Ringing")};
+ str psipfrag [1] = {STR_STATIC_INIT ("message/sipfrag")};
+ str presp_srverr [1] = {STR_STATIC_INIT ("Server Internal Error")};
++str presp_trying [1] = {STR_STATIC_INIT ("Trying to enter MOH queue")};
+ str presp_unsupp [1] = {STR_STATIC_INIT ("Unsupported Media Type")};
+ 
+ rtpmap prtpmap [] =
+_at_@ -88,9 +88,9 @@ str pallowhdr [1] = { STR_STATIC_INIT (ALLOWHDR SIPEOL) };
+ char pbyemsg [] =
+   {
+   "%s"
++  "%s"
+   "Max-Forwards: 70" SIPEOL
+   "Contact: <%s>" SIPEOL
+-  "User-Agent: " USRAGNT SIPEOL
+   };
+ 
+ str pextrahdr [1] =
+_at_@ -100,7 +100,6 @@ str pextrahdr [1] =
+   "Supported: 100rel" SIPEOL
+   "Accept-Language: en" SIPEOL
+   "Content-Type: application/sdp" SIPEOL
+-  "User-Agent: " USRAGNT SIPEOL
+   )
+   };
+ 
+_at_@ -118,10 +117,11 @@ char pinvitesdp [] =
+ char prefermsg [] =
+   {
+   "%s"
++  "%s"
++  "Contact: <%s>" SIPEOL
+   "Max-Forwards: 70" SIPEOL
+   "Refer-To: <%s>" SIPEOL
+-  "Referred-By: <%.*s>" SIPEOL
+-  "User-Agent: " USRAGNT SIPEOL
++  "Referred-By: <%s>" SIPEOL
+   };
+ 
+ char preinvitemsg [] =
+_at_@ -131,7 +131,6 @@ char preinvitemsg [] =
+   "Contact: <%s>" SIPEOL
+   ALLOWHDR SIPEOL
+   "Supported: 100rel" SIPEOL
+-  "User-Agent: " USRAGNT SIPEOL
+   "Accept-Language: en" SIPEOL
+   "Content-Type: application/sdp" SIPEOL
+   };
+_at_@ -153,8 +152,7 @@ char prtpsdp [] =
+ **********/
+ 
+ void delete_call (call_lst *);
+-void drop_call (sip_msg_t *, call_lst *);
+-int find_call (sip_msg_t *, call_lst **);
++void end_RTP (sip_msg_t *, call_lst *);
+ dlg_t *form_dialog (call_lst *, struct to_body *);
+ int form_rtp_SDP (str *, call_lst *, char *);
+ static void invite_cb (struct cell *, int, struct tmcb_params *);
+_at_@ -164,6 +162,7 @@ int send_prov_rsp (sip_msg_t *, call_lst *);
+ int send_rtp_answer (sip_msg_t *, call_lst *);
+ int search_hdr_ext (struct hdr_field *, str *);
+ int start_stream (sip_msg_t *, call_lst *, int);
++int stop_stream (sip_msg_t *, call_lst *, int);
+ 
+ /**********
+ * local functions
+_at_@ -175,10 +174,10 @@ int start_stream (sip_msg_t *, call_lst *, int);
+ * INPUT:
+ *   Arg (1) = SIP message pointer
+ *   Arg (2) = call pointer
+-* OUTPUT: 0=failed
++* OUTPUT: none
+ **********/
+ 
+-int ack_msg (sip_msg_t *pmsg, call_lst *pcall)
++void ack_msg (sip_msg_t *pmsg, call_lst *pcall)
+ 
+ {
+ /**********
+_at_@ -195,13 +194,13 @@ if (pcall->call_state != CLSTA_INVITED)
+   **********/
+ 
+   if (pcall->call_state != CLSTA_INQUEUE)
+-    { LM_ERR ("%sUnexpected ACK (%s)!", pfncname, pcall->call_from); }
++    { LM_ERR ("%sUnexpected ACK (%s)!\n", pfncname, pcall->call_from); }
+   else
+     {
+-    mohq_debug (pcall->pmohq, "%sACK from refused re-INVITE (%s)!",
++    mohq_debug (pcall->pmohq, "%sACK from refused re-INVITE (%s)!\n",
+       pfncname, pcall->call_from);
+     }
+-  return 1;
++  return;
+   }
+ 
+ /**********
+_at_@ -212,17 +211,17 @@ if (pcall->call_state != CLSTA_INVITED)
+ 
+ if (ptm->t_lookup_ident (&ptrans, pcall->call_hash, pcall->call_label) < 0)
+   {
+-  LM_ERR ("%sINVITE transaction missing for call (%s)!",
++  LM_ERR ("%sINVITE transaction missing for call (%s)!\n",
+     pfncname, pcall->call_from);
+-  return 1;
++  return;
+   }
+ else
+   {
+   if (ptm->t_release (pcall->call_pmsg) < 0)
+     {
+-    LM_ERR ("%sRelease transaction failed for call (%s)!",
++    LM_ERR ("%sRelease transaction failed for call (%s)!\n",
+       pfncname, pcall->call_from);
+-    return 1;
++    return;
+     }
+   }
+ pcall->call_hash = pcall->call_label = 0;
+_at_@ -235,6 +234,47 @@ pcall->call_cseq = 1;
+ mohq_debug (pcall->pmohq,
+   "%sACK received for call (%s); placed in queue (%s)",
+   pfncname, pcall->call_from, pcall->pmohq->mohq_name);
++return;
++}
++
++/**********
++* Add String to Buffer
++*
++* INPUT:
++*   Arg (1) = string pointer
++*   Arg (2) = string length
++*   Arg (3) = pointer to buffer pointer
++*   Arg (4) = pointer to buffer size
++*   Arg (5) = add NUL flag
++* OUTPUT: =0 if insufficent space
++*   bufpointer incremented, size decremented
++**********/
++
++int addstrbfr (char *pstr, size_t nlen, char **pbuf, size_t *nmax, int bnull)
++
++{
++/**********
++* o enough space?
++* o copy string
++* o adjust position/size
++**********/
++
++size_t nsize = nlen;
++if (bnull)
++  { nsize++; }
++if (nsize > *nmax)
++  { return 0; }
++if (nlen)
++  {
++  strncpy (*pbuf, pstr, nlen);
++  *pbuf += nlen;
++  }
++if (bnull)
++  {
++  **pbuf = '\0';
++  (*pbuf)++;
++  }
++*nmax -= nsize;
+ return 1;
+ }
+ 
+_at_@ -261,7 +301,7 @@ char *pfncname = "bye_cb: ";
+ call_lst *pcall = (call_lst *)*pcbp->param;
+ if (ntype == TMCB_ON_FAILURE)
+   {
+-  LM_ERR ("%sCall (%s) did not respond to BYE", pfncname,
++  LM_ERR ("%sCall (%s) did not respond to BYE!\n", pfncname,
+     pcall->call_from);
+   }
+ else
+_at_@ -269,7 +309,7 @@ else
+   int nreply = pcbp->code;
+   if ((nreply / 100) != 2)
+     {
+-    LM_ERR ("%sCall (%s) BYE error (%d)", pfncname,
++    LM_ERR ("%sCall (%s) BYE error (%d)!\n", pfncname,
+       pcall->call_from, nreply);
+     }
+   else
+_at_@ -288,33 +328,43 @@ return;
+ * INPUT:
+ *   Arg (1) = SIP message pointer
+ *   Arg (2) = call pointer
+-* OUTPUT: 0=failed
++* OUTPUT: none
+ **********/
+ 
+-int bye_msg (sip_msg_t *pmsg, call_lst *pcall)
++void bye_msg (sip_msg_t *pmsg, call_lst *pcall)
+ 
+ {
+ /**********
+-* o send OK
+-* o teardown call
++* o responded?
++* o teardown RTP
+ **********/
+ 
+ char *pfncname = "bye_msg: ";
+-if (pmod_data->psl->freply (pmsg, 200, presp_ok) < 0)
++if (pcall->call_state == CLSTA_BYEOK)
++  { return; }
++if (pcall->call_state >= CLSTA_INQUEUE)
+   {
+-  LM_ERR ("%sUnable to create reply to call (%s)", pfncname,
+-    pcall->call_from);
+-  return 1;
++  pcall->call_state = CLSTA_BYEOK;
++  end_RTP (pmsg, pcall);
+   }
+-if (pcall->call_state >= CLSTA_INQUEUE)
+-  { drop_call (pmsg, pcall); }
+ else
+   {
+-  LM_ERR ("%sEnding call (%s) before placed in queue!",
++  LM_ERR ("%sEnding call (%s) before placed in queue!\n",
+     pfncname, pcall->call_from);
+-  delete_call (pcall);
+   }
+-return 1;
++
++/**********
++* send OK and delete from queue
++**********/
++
++if (pmod_data->psl->freply (pmsg, 200, presp_ok) < 0)
++  {
++  LM_ERR ("%sUnable to create reply to call (%s)!\n", pfncname,
++    pcall->call_from);
++  return;
++  }
++delete_call (pcall);
++return;
+ }
+ 
+ /**********
+_at_@ -323,14 +373,15 @@ return 1;
+ * INPUT:
+ *   Arg (1) = SIP message pointer
+ *   Arg (2) = call pointer
+-* OUTPUT: 0=failed
++* OUTPUT: none
+ **********/
+ 
+-int cancel_msg (sip_msg_t *pmsg, call_lst *pcall)
++void cancel_msg (sip_msg_t *pmsg, call_lst *pcall)
+ 
+ {
+ /**********
+-* still in INVITE dialog?
++* RFC 3261 section 9.2
++* o still in INVITE dialog?
+ **********/
+ 
+ char *pfncname = "cancel_msg: ";
+_at_@ -340,16 +391,16 @@ if (pcall->call_state < CLSTA_INQUEUE)
+   mohq_debug (pcall->pmohq, "%sCANCELed call (%s)",
+     pfncname, pcall->call_from);
+   if (pmod_data->psl->freply (pmsg, 487, presp_reqterm) < 0)
+-    { LM_ERR ("%sUnable to create reply!", pfncname); }
++    { LM_ERR ("%sUnable to create reply!\n", pfncname); }
+   }
+ else
+   {
+-  LM_ERR ("%sUnable to CANCEL because accepted INVITE for call (%s)!",
++  LM_ERR ("%sUnable to CANCEL because accepted INVITE for call (%s)!\n",
+     pfncname, pcall->call_from);
+   if (pmod_data->psl->freply (pmsg, 481, presp_nocall) < 0)
+-    { LM_ERR ("%sUnable to create reply!", pfncname); }
++    { LM_ERR ("%sUnable to create reply!\n", pfncname); }
+   }
+-return 1;
++return;
+ }
+ 
+ /**********
+_at_@ -365,23 +416,14 @@ void close_call (sip_msg_t *pmsg, call_lst *pcall)
+ 
+ {
+ /**********
+-* o destroy proxy connection
++* o destroy RTP connection
+ * o create dialog
+ **********/
+ 
+ char *pfncname = "close_call: ";
+ int bsent = 0;
+ char *phdr = 0;
+-if (pmsg != FAKED_REPLY)
+-  {
+-  mohq_debug (pcall->pmohq, "%sDestroying RTP link for call (%s)",
+-    pfncname, pcall->call_from);
+-  if (pmod_data->fn_rtp_destroy (pmsg, 0, 0) != 1)
+-    {
+-    LM_ERR ("%srtpproxy_destroy refused for call (%s)!",
+-      pfncname, pcall->call_from);
+-    }
+-  }
++end_RTP (pmsg, pcall);
+ struct to_body ptob [2];
+ dlg_t *pdlg = form_dialog (pcall, ptob);
+ if (!pdlg)
+_at_@ -398,15 +440,17 @@ tm_api_t *ptm = pmod_data->ptm;
+ char *pquri = pcall->pmohq->mohq_uri;
+ int npos1 = sizeof (pbyemsg) // BYE template
+   + strlen (pcall->call_via) // Via
++  + strlen (pcall->call_route) // Route
+   + strlen (pquri); // Contact
+ phdr = pkg_malloc (npos1);
+ if (!phdr)
+   {
+-  LM_ERR ("%sNo more memory!", pfncname);
++  LM_ERR ("%sNo more memory!\n", pfncname);
+   goto bye_err;
+   }
+ sprintf (phdr, pbyemsg,
+   pcall->call_via, // Via
++  pcall->call_route, // Route
+   pquri); // Contact
+ str phdrs [1];
+ phdrs->s = phdr;
+_at_@ -422,7 +466,7 @@ set_uac_req (puac, pbye, phdrs, 0, pdlg,
+ pcall->call_state = CLSTA_BYE;
+ if (ptm->t_request_within (puac) < 0)
+   {
+-  LM_ERR ("%sUnable to create BYE request for call (%s)!",
++  LM_ERR ("%sUnable to create BYE request for call (%s)!\n",
+     pfncname, pcall->call_from);
+   goto bye_err;
+   }
+_at_@ -449,138 +493,127 @@ return;
+ * Create New Call Record
+ *
+ * INPUT:
+-*   Arg (1) = queue index
+-*   Arg (2) = SIP message pointer
+-* OUTPUT: call index; -1 if unable to create
++*   Arg (1) = SIP message pointer
++*   Arg (2) = call pointer
++*   Arg (3) = call index
++*   Arg (4) = queue index
++* OUTPUT: initializes call record; =0 if failed
+ **********/
+ 
+-int create_call (int mohq_idx, sip_msg_t *pmsg)
++int
++create_call (sip_msg_t *pmsg, call_lst *pcall, int ncall_idx, int mohq_idx)
+ 
+ {
+ /**********
+-* o lock calls
+-* o already in use?
+-* o find inactive slot
+-**********/
+-
+-char *pfncname = "create_call: ";
+-if (!mohq_lock_set (pmod_data->pcall_lock, 1, 2000))
+-  {
+-  LM_ERR ("%sUnable to lock calls!", pfncname);
+-  return -1;
+-  }
+-call_lst *pcall;
+-int ncall_idx = find_call (pmsg, &pcall);
+-if (pcall)
+-  {
+-  mohq_lock_release (pmod_data->pcall_lock);
+-  LM_ERR ("%sCall already in use (%s)!", pfncname, pcall->call_from);
+-  return -1;
+-  }
+-for (ncall_idx = 0; ncall_idx < pmod_data->call_cnt; ncall_idx++)
+-  {
+-  if (!pmod_data->pcall_lst [ncall_idx].call_active)
+-    { break; }
+-  }
+-if (ncall_idx == pmod_data->call_cnt)
+-  {
+-  mohq_lock_release (pmod_data->pcall_lock);
+-  LM_ERR ("%sNo call slots available!", pfncname);
+-  return -1;
+-  }
+-
+-/**********
+ * add values to new entry
+ **********/
+ 
+-pcall = &pmod_data->pcall_lst [ncall_idx];
+-pcall->call_active = 1;
++char *pfncname = "create_call: ";
+ pcall->pmohq = &pmod_data->pmohq_lst [mohq_idx];
+-pcall->call_state = 0;
+ str *pstr = &pmsg->callid->body;
+-strncpy (pcall->call_id, pstr->s, pstr->len);
+-pcall->call_id [pstr->len] = '\0';
++char *pbuf = pcall->call_buffer;
++pcall->call_bufpos = sizeof (pcall->call_buffer);
++pcall->call_id = pbuf;
++if (!addstrbfr (pstr->s, pstr->len, &pbuf, &pcall->call_bufpos, 1))
++  { return 0; }
+ pstr = &pmsg->from->body;
+-strncpy (pcall->call_from, pstr->s, pstr->len);
+-pcall->call_from [pstr->len] = '\0';
+-*pcall->call_tag = '\0';
+-if (!pmsg->contact)
+-  { *pcall->call_contact = '\0'; }
+-else
++pcall->call_from = pbuf;
++if (!addstrbfr (pstr->s, pstr->len, &pbuf, &pcall->call_bufpos, 1))
++  { return 0; }
++pcall->call_tag = pbuf;
++if (!addstrbfr (0, 0, &pbuf, &pcall->call_bufpos, 1))
++  { return 0; }
++pcall->call_contact = pbuf;
++if (pmsg->contact)
+   {
+   pstr = &pmsg->contact->body;
+-  strncpy (pcall->call_contact, pstr->s, pstr->len);
+-  pcall->call_contact [pstr->len] = '\0';
++  if (!addstrbfr (pstr->s, pstr->len, &pbuf, &pcall->call_bufpos, 0))
++    { return 0; }
+   }
++if (!addstrbfr (0, 0, &pbuf, &pcall->call_bufpos, 1))
++  { return 0; }
+ 
+ /**********
+ * extract Via headers
+ **********/
+ 
++pcall->call_via = pbuf;
+ hdr_field_t *phdr = pmsg->h_via1;
+ if (phdr)
+   {
+-  int npos1 = 0;
+   while ((phdr = next_sibling_hdr (phdr)))
+     {
+     struct via_body *pvia;
+     char *pviabuf;
+-    int bovrflow = 0;
+-    int npos2;
+-    int nvia_max = sizeof (pcall->call_via);
++    int npos;
+     for (pvia = (struct via_body *)phdr->parsed; pvia; pvia = pvia->next)
+       {
+       /**********
+-      * o skip trailing whitespace
+-      * o check if overflow
++      * skip trailing whitespace
+       **********/
+ 
+-      npos2 = pvia->bsize;
++      npos = pvia->bsize;
+       pviabuf = pvia->name.s;
+-      while (npos2)
++      while (npos)
+         {
+-        --npos2;
+-        if (pviabuf [npos2] == ' ' || pviabuf [npos2] == '\r'
+-          || pviabuf [npos2] == '\n' || pviabuf [npos2] == '\t' || pviabuf [npos2] == ',')
++        --npos;
++        if (pviabuf [npos] == ' ' || pviabuf [npos] == '\r'
++          || pviabuf [npos] == '\n' || pviabuf [npos] == '\t'
++          || pviabuf [npos] == ',')
+           { continue; }
+         break;
+         }
+-      if ((npos2 + npos1 + 7) >= nvia_max)
+-        {
+-        LM_WARN ("%sVia buffer overflowed!", pfncname);
+-        bovrflow = 1;
+-        break;
+-        }
+ 
+       /**********
+       * copy via
+       **********/
+ 
+-      strcpy (&pcall->call_via [npos1], "Via: ");
+-      npos1 += 5;
+-      strncpy (&pcall->call_via [npos1], pviabuf, npos2);
+-      npos1 += npos2;
+-      strcpy (&pcall->call_via [npos1], SIPEOL);
+-      npos1 += 2;
++      if (!addstrbfr ("Via: ", 5, &pbuf, &pcall->call_bufpos, 0))
++        { return 0; }
++      if (!addstrbfr (pviabuf, npos, &pbuf, &pcall->call_bufpos, 0))
++        { return 0; }
++      if (!addstrbfr (SIPEOL, 2, &pbuf, &pcall->call_bufpos, 0))
++        { return 0; }
+       }
+-    if (bovrflow)
+-      { break; }
+     }
+   }
++if (!addstrbfr (0, 0, &pbuf, &pcall->call_bufpos, 1))
++  { return 0; }
+ 
+ /**********
+-* o release call lock
+-* o update DB
+-* o lock MOH queue
++* extract Route headers
++**********/
++
++pcall->call_route = pbuf;
++struct hdr_field *proute;
++for (proute = pmsg->record_route; proute; proute = next_sibling_hdr (proute))
++  {
++  if (parse_rr (proute) < 0)
++    { return 0; }
++  rr_t *prouterr;
++  for (prouterr = proute->parsed; prouterr; prouterr = prouterr->next)
++    {
++    if (!addstrbfr ("Route: ", 7, &pbuf, &pcall->call_bufpos, 0))
++      { return 0; }
++    if (!addstrbfr (prouterr->nameaddr.name.s, prouterr->len,
++      &pbuf, &pcall->call_bufpos, 0))
++      { return 0; }
++    if (!addstrbfr (SIPEOL, 2, &pbuf, &pcall->call_bufpos, 0))
++      { return 0; }
++    }
++  }
++if (!addstrbfr (0, 0, &pbuf, &pcall->call_bufpos, 1))
++  { return 0; }
++
++/**********
++* update DB
+ **********/
+ 
+ pcall->call_state = CLSTA_ENTER;
+-mohq_lock_release (pmod_data->pcall_lock);
+ add_call_rec (ncall_idx);
+-mohq_lock_set (pmod_data->pmohq_lock, 0, 0);
+ mohq_debug (pcall->pmohq, "%sAdded call (%s) to queue (%s)",
+   pfncname, pcall->call_from, pcall->pmohq->mohq_name);
+-return ncall_idx;
++return 1;
+ }
+ 
+ /**********
+_at_@ -605,15 +638,15 @@ if (pcall->call_hash || pcall->call_label)
+   {
+   if (ptm->t_lookup_ident (&ptrans, pcall->call_hash, pcall->call_label) < 0)
+     {
+-    LM_ERR ("%sLookup transaction failed for call (%s)!", pfncname,
+-      pcall->call_from);
++    LM_ERR ("%sLookup transaction failed for call (%s) from queue (%s)!\n",
++      pfncname, pcall->call_from, pcall->pmohq->mohq_name);
+     }
+   else
+     {
+     if (ptm->t_release (pcall->call_pmsg) < 0)
+       {
+-      LM_ERR ("%sRelease transaction failed for call (%s)!",
+-        pfncname, pcall->call_from);
++      LM_ERR ("%sRelease transaction failed for call (%s) from queue (%s)!\n",
++        pfncname, pcall->call_from, pcall->pmohq->mohq_name);
+       }
+     }
+   pcall->call_hash = pcall->call_label = 0;
+_at_@ -622,14 +655,21 @@ if (pcall->call_hash || pcall->call_label)
+ /**********
+ * o update DB
+ * o inactivate slot
+-* o release MOH queue
+ **********/
+ 
+-mohq_debug (pcall->pmohq, "delete_call: Deleting call (%s) from queue (%s)",
+-  pcall->call_from, pcall->pmohq->mohq_name);
+-delete_call_rec (pcall);
+-pcall->call_active = 0;
+-mohq_lock_release (pmod_data->pmohq_lock);
++if (!mohq_lock_set (pmod_data->pcall_lock, 1, 5000))
++  {
++  LM_ERR ("%sUnable to set call lock for call (%s) from queue (%s)!\n",
++    pfncname, pcall->call_from, pcall->pmohq->mohq_name);
++  }
++else
++  {
++  mohq_debug (pcall->pmohq, "%sDeleting call (%s) from queue (%s)",
++    pfncname, pcall->call_from, pcall->pmohq->mohq_name);
++  delete_call_rec (pcall);
++  mohq_lock_release (pmod_data->pcall_lock);
++  }
++pcall->call_state = 0;
+ return;
+ }
+ 
+_at_@ -655,28 +695,28 @@ char *pfncname = "deny_method: ";
+ tm_api_t *ptm = pmod_data->ptm;
+ if (ptm->t_newtran (pmsg) < 0)
+   {
+-  LM_ERR ("%sUnable to create new transaction!", pfncname);
++  LM_ERR ("%sUnable to create new transaction!\n", pfncname);
+   if (pmod_data->psl->freply (pmsg, 500, presp_srverr) < 0)
+     {
+-    LM_ERR ("%sUnable to create reply to %.*s!", pfncname,
++    LM_ERR ("%sUnable to create reply to %.*s!\n", pfncname,
+       STR_FMT (&REQ_LINE (pmsg).method));
+     }
+   return;
+   }
+ if (!add_lump_rpl2 (pmsg, pallowhdr->s, pallowhdr->len, LUMP_RPL_HDR))
+-  { LM_ERR ("%sUnable to add Allow header!", pfncname); }
+-LM_ERR ("%sRefused %.*s for call (%s)!", pfncname,
++  { LM_ERR ("%sUnable to add Allow header!\n", pfncname); }
++LM_ERR ("%sRefused %.*s for call (%s)!\n", pfncname,
+   STR_FMT (&REQ_LINE (pmsg).method), pcall->call_from);
+ if (ptm->t_reply (pmsg, 405, presp_noallow->s) < 0)
+   {
+-  LM_ERR ("%sUnable to create reply to %.*s!", pfncname,
++  LM_ERR ("%sUnable to create reply to %.*s!\n", pfncname,
+     STR_FMT (&REQ_LINE (pmsg).method));
+   }
+ return;
+ }
+ 
+ /**********
+-* Drop the Call
++* End RTP
+ *
+ * INPUT:
+ *   Arg (1) = SIP message pointer
+_at_@ -684,26 +724,24 @@ return;
+ * OUTPUT: none
+ **********/
+ 
+-void drop_call (sip_msg_t *pmsg, call_lst *pcall)
++void end_RTP (sip_msg_t *pmsg, call_lst *pcall)
+ 
+ {
+ /**********
+-* o destroy proxy connection
+-* o delete call
++* destroy RTP connection
+ **********/
+ 
+-char *pfncname = "drop_call: ";
+-if (pmsg != FAKED_REPLY)
++char *pfncname = "end_RTP: ";
++if ((pmsg != FAKED_REPLY) && (pcall->call_state != CLSTA_ENTER))
+   {
+   mohq_debug (pcall->pmohq, "%sDestroying RTP link for call (%s)",
+     pfncname, pcall->call_from);
+   if (pmod_data->fn_rtp_destroy (pmsg, 0, 0) != 1)
+     {
+-    LM_ERR ("%srtpproxy_destroy refused for call (%s)!",
++    LM_ERR ("%srtpproxy_destroy refused for call (%s)!\n",
+       pfncname, pcall->call_from);
+     }
+   }
+-delete_call (pcall);
+ return;
+ }
+ 
+_at_@ -712,62 +750,37 @@ return;
+ *
+ * INPUT:
+ *   Arg (1) = SIP message pointer
+-*   Arg (2) = pointer to call pointer
+-* OUTPUT: queue index; -1 if unable to find
++*   Arg (2) = queue index
++* OUTPUT: call pointer; =0 if unable to find/create
+ **********/
+ 
+-int find_call (sip_msg_t *pmsg, call_lst **ppcall)
++call_lst *find_call (sip_msg_t *pmsg, int mohq_idx)
+ 
+ {
+ /**********
+-* o find current RURI
+-* o strip off parms or headers
+-* o search MOH queue
+-**********/
+-
+-str *pruri =
+-  pmsg->new_uri.s ? &pmsg->new_uri : &pmsg->first_line.u.request.uri;
+-int nidx;
+-str pstr [1];
+-pstr->s = pruri->s;
+-pstr->len = pruri->len;
+-for (nidx = 0; nidx < pruri->len; nidx++)
+-  {
+-  if (pstr->s [nidx] == ';' || pstr->s [nidx] == '?')
+-    {
+-    pstr->len = nidx;
+-    break;
+-    }
+-  }
+-mohq_lst *pqlst = pmod_data->pmohq_lst;
+-int nqidx;
+-for (nqidx = 0; nqidx < pmod_data->mohq_cnt; nqidx++)
+-  {
+-  str pmohstr [1];
+-  pmohstr->s = pqlst [nqidx].mohq_uri;
+-  pmohstr->len = strlen (pmohstr->s);
+-  if (STR_EQ (*pmohstr, *pstr))
+-    { break; }
+-  }
+-*ppcall = 0;
+-if (nqidx == pmod_data->mohq_cnt)
+-  { return -1;}
+-
+-/**********
+ * o get to tag
+ * o get callID
+-* o ignore to tag if CANCEL on first INVITE
+-* o search call queue
++* o search calls
+ **********/
+ 
++char *pfncname = "find_call: ";
+ str *ptotag = &(get_to (pmsg)->tag_value);
+ if (!ptotag->len)
+   { ptotag = 0; }
+ if (!pmsg->callid)
+-  { return -1; }
++  {
++  LM_ERR ("%sNo call ID!\n", pfncname);
++  return 0;
++  }
+ str *pcallid = &pmsg->callid->body;
+ if (!pcallid)
+-  { return -1; }
++  {
++  LM_ERR ("%sNo call ID!\n", pfncname);
++  return 0;
++  }
++int nopen = -1;
++int nidx;
++call_lst *pcall;
+ for (nidx = 0; nidx < pmod_data->call_cnt; nidx++)
+   {
+   /**********
+_at_@ -775,21 +788,25 @@ for (nidx = 0; nidx < pmod_data->call_cnt; nidx++)
+   * o call timed out on ACK?
+   * o callID matches?
+   * o to tag matches?
+-  * o return call pointer
+   **********/
+ 
+-  call_lst *pcall = &pmod_data->pcall_lst [nidx];
+-  if (!pcall->call_active)
+-    { continue; }
++  pcall = &pmod_data->pcall_lst [nidx];
++  if (!pcall->call_state)
++    {
++    nopen = nidx;
++    continue;
++    }
++#if 0 /* ??? need to handle */
+   if (pcall->call_time && (pcall->call_state < CLSTA_INQUEUE))
+     {
+     if ((pcall->call_time + 32) < time (0))
+       {
+-      LM_ERR ("find_call: No ACK response for call (%s)", pcall->call_from);
++      LM_ERR ("%sNo ACK response for call (%s)!\n", pfncname, pcall->call_from);
+       delete_call (pcall);
+       continue;
+       }
+     }
++#endif /* ??? */
+   str tmpstr [1];
+   tmpstr->s = pcall->call_id;
+   tmpstr->len = strlen (tmpstr->s);
+_at_@ -802,36 +819,101 @@ for (nidx = 0; nidx < pmod_data->call_cnt; nidx++)
+     if (!STR_EQ (*tmpstr, *ptotag))
+       { continue; }
+     }
+-  *ppcall = pcall;
+-  return nqidx;
++  else
++    {
++    /**********
++    * match not allowed for INVITE
++    **********/
++
++    if (pmsg->REQ_METHOD == METHOD_INVITE)
++      { return 0; }
++    }
++  return pcall;
+   }
+ 
+ /**********
+-* first INVITE?
++* o first INVITE?
++* o create a new call record
+ **********/
+ 
+-if (pmsg->REQ_METHOD == METHOD_INVITE)
++if (pmsg->REQ_METHOD != METHOD_INVITE)
+   { return 0; }
+-return -1;
++if (ptotag)
++  { return 0; }
++if (nopen < 0)
++  {
++  LM_ERR ("%sNo call slots available!\n", pfncname);
++  return 0;
++  }
++pcall = &pmod_data->pcall_lst [nopen];
++if (!create_call (pmsg, pcall, nopen, mohq_idx))
++  { return 0; }
++return pcall;
+ }
+ 
+ /**********
+ * Find Queue
+ *
+ * INPUT:
++*   Arg (1) = SIP message pointer
++* OUTPUT: queue index; -1 if unable to find
++**********/
++
++int find_queue (sip_msg_t *pmsg)
++
++{
++/**********
++* o find current RURI
++* o strip off parms or headers
++* o search queues
++**********/
++
++str *pruri =
++  pmsg->new_uri.s ? &pmsg->new_uri : &pmsg->first_line.u.request.uri;
++int nidx;
++str pstr [1];
++pstr->s = pruri->s;
++pstr->len = pruri->len;
++for (nidx = 0; nidx < pruri->len; nidx++)
++  {
++  if (pstr->s [nidx] == ';' || pstr->s [nidx] == '?')
++    {
++    pstr->len = nidx;
++    break;
++    }
++  }
++mohq_lst *pqlst = pmod_data->pmohq_lst;
++int nqidx;
++for (nqidx = 0; nqidx < pmod_data->mohq_cnt; nqidx++)
++  {
++  str pmohstr [1];
++  pmohstr->s = pqlst [nqidx].mohq_uri;
++  pmohstr->len = strlen (pmohstr->s);
++  if (STR_EQ (*pmohstr, *pstr))
++    { break; }
++  }
++if (nqidx == pmod_data->mohq_cnt)
++  { return -1;}
++return nqidx;
++}
++
++/**********
++* Find Queue Name
++*
++* INPUT:
+ *   Arg (1) = queue name str pointer
+ * OUTPUT: queue index; -1 if unable to find
+ **********/
+ 
+-int find_queue (str *pqname)
++int find_qname (str *pqname)
+ 
+ {
+-char *pfncname = "find_queue: ";
++char *pfncname = "find_qname: ";
+ int nidx;
+ str tmpstr;
+ if (!mohq_lock_set (pmod_data->pmohq_lock, 0, 500))
+   {
+-  LM_ERR ("%sUnable to lock queues!", pfncname);
++  LM_ERR ("%sUnable to lock queues!\n", pfncname);
+   return -1;
+   }
+ for (nidx = 0; nidx < pmod_data->mohq_cnt; nidx++)
+_at_@ -843,7 +925,7 @@ for (nidx = 0; nidx < pmod_data->mohq_cnt; nidx++)
+   }
+ if (nidx == pmod_data->mohq_cnt)
+   {
+-  LM_ERR ("%sUnable to find queue (%.*s)!", pfncname, STR_FMT (pqname));
++  LM_ERR ("%sUnable to find queue (%.*s)!\n", pfncname, STR_FMT (pqname));
+   nidx = -1;
+   }
+ mohq_lock_release (pmod_data->pmohq_lock);
+_at_@ -871,7 +953,7 @@ parse_to (pvalue->s, &pvalue->s [pvalue->len + 1], pref);
+ if (pref->error != PARSE_OK)
+   {
+   // should never happen
+-  LM_ERR ("%sInvalid Referred-By URI (%.*s)!", pfncname, STR_FMT (pvalue));
++  LM_ERR ("%sInvalid Referred-By URI (%.*s)!\n", pfncname, STR_FMT (pvalue));
+   return -1;
+   }
+ if (pref->param_lst)
+_at_@ -886,7 +968,7 @@ str tmpstr;
+ struct to_body pfrom [1];
+ for (nidx = 0; nidx < pmod_data->call_cnt; nidx++)
+   {
+-  if (!pmod_data->pcall_lst [nidx].call_active)
++  if (!pmod_data->pcall_lst [nidx].call_state)
+     { continue; }
+   tmpstr.s = pmod_data->pcall_lst [nidx].call_from;
+   tmpstr.len = strlen (tmpstr.s);
+_at_@ -894,7 +976,7 @@ for (nidx = 0; nidx < pmod_data->call_cnt; nidx++)
+   if (pfrom->error != PARSE_OK)
+     {
+     // should never happen
+-    LM_ERR ("%sInvalid From URI (%.*s)!", pfncname, STR_FMT (&tmpstr));
++    LM_ERR ("%sInvalid From URI (%.*s)!\n", pfncname, STR_FMT (&tmpstr));
+     continue;
+     }
+   if (pfrom->param_lst)
+_at_@ -910,55 +992,65 @@ return -1;
+ *
+ * INPUT:
+ *   Arg (1) = SIP message pointer
+-*   Arg (2) = queue index
+-* OUTPUT: 0=failed
++*   Arg (2) = call pointer
++* OUTPUT: none
+ **********/
+ 
+-int first_invite_msg (sip_msg_t *pmsg, int mohq_idx)
++void first_invite_msg (sip_msg_t *pmsg, call_lst *pcall)
+ 
+ {
+-/**********
+-* create call record
+-**********/
+-
+ char *pfncname = "first_invite_msg: ";
+-int ncall_idx = create_call (mohq_idx, pmsg);
+-if (ncall_idx == -1)
+-  { return 0; }
+-call_lst *pcall = &pmod_data->pcall_lst [ncall_idx];
+ 
+ /**********
+ * o SDP exists?
+ * o accepts REFER?
+-* o send rtpproxy offer
++* o send RTP offer
+ **********/
+ 
+ if (!(pmsg->msg_flags & FL_SDP_BODY))
+   {
+   if (parse_sdp (pmsg))
+     {
+-    LM_ERR ("%sINVITE lacks SDP (%s)!", pfncname, pcall->call_from);
++    if (pmod_data->psl->freply (pmsg, 488, presp_noaccept) < 0)
++      {
++      LM_ERR ("%sUnable to create reply!\n", pfncname);
++      return;
++      }
++    LM_ERR ("%sINVITE lacks SDP (%s) from queue (%s)!\n",
++      pfncname, pcall->call_from, pcall->pmohq->mohq_name);
+     delete_call (pcall);
+-    return 0;
++    return;
+     }
+   }
+ if (pmsg->allow)
+   {
+   if (!search_hdr_ext (pmsg->allow, prefer))
+     {
+-    LM_ERR ("%sMissing REFER support (%s)!", pfncname, pcall->call_from);
++    if (pmod_data->psl->freply (pmsg, 488, presp_noaccept) < 0)
++      {
++      LM_ERR ("%sUnable to create reply!\n", pfncname);
++      return;
++      }
++    LM_ERR ("%sMissing REFER support (%s) from queue (%s)!\n",
++      pfncname, pcall->call_from, pcall->pmohq->mohq_name);
+     delete_call (pcall);
+-    return 0;
++    return;
+     }
+   }
+-mohq_debug (pcall->pmohq, "%sMaking offer for RTP link for call (%s)",
+-  pfncname, pcall->call_from);
++mohq_debug (pcall->pmohq,
++  "%sMaking offer for RTP link for call (%s) from queue (%s)",
++  pfncname, pcall->call_from, pcall->pmohq->mohq_name);
+ if (pmod_data->fn_rtp_offer (pmsg, 0, 0) != 1)
+   {
+-  LM_ERR ("%srtpproxy_offer refused for call (%s)!",
+-    pfncname, pcall->call_from);
++  if (pmod_data->psl->freply (pmsg, 486, presp_busy) < 0)
++    {
++    LM_ERR ("%sUnable to create reply!\n", pfncname);
++    return;
++    }
++  LM_ERR ("%srtpproxy_offer refused for call (%s)!\n",
++      pfncname, pcall->call_from);
+   delete_call (pcall);
+-  return 0;
++  return;
+   }
+ 
+ /**********
+_at_@ -971,10 +1063,11 @@ if (pmod_data->fn_rtp_offer (pmsg, 0, 0) != 1)
+ tm_api_t *ptm = pmod_data->ptm;
+ if (ptm->t_newtran (pmsg) < 0)
+   {
+-  LM_ERR ("%sUnable to create new transaction for call (%s)!",
++  LM_ERR ("%sUnable to create new transaction for call (%s)!\n",
+     pfncname, pcall->call_from);
++  end_RTP (pmsg, pcall);
+   delete_call (pcall);
+-  return 0;
++  return;
+   }
+ struct cell *ptrans = ptm->t_gett ();
+ pcall->call_hash = ptrans->hash_index;
+_at_@ -982,28 +1075,53 @@ pcall->call_label = ptrans->label;
+ str ptotag [1];
+ if (ptm->t_get_reply_totag (pmsg, ptotag) != 1)
+   {
+-  LM_ERR ("%sUnable to create totag for call (%s)!",
++  LM_ERR ("%sUnable to create totag for call (%s)!\n",
+     pfncname, pcall->call_from);
+   if (ptm->t_reply (pmsg, 500, presp_srverr->s) < 0)
+-    { LM_ERR ("%sUnable to reply to INVITE!", pfncname); }
++    { LM_ERR ("%sUnable to reply to INVITE!\n", pfncname); }
++  end_RTP (pmsg, pcall);
+   delete_call (pcall);
+-  return 1;
++  return;
++  }
++char *pbuf = &pcall->call_buffer [pcall->call_bufpos];
++pcall->call_tag = pbuf;
++if (!addstrbfr (ptotag->s, ptotag->len, &pbuf, &pcall->call_bufpos, 1))
++  {
++  LM_ERR ("%sInsufficient buffer space for call (%s)!\n",
++    pfncname, pcall->call_from);
++  if (ptm->t_reply (pmsg, 500, presp_srverr->s) < 0)
++    { LM_ERR ("%sUnable to reply to INVITE!\n", pfncname); }
++  end_RTP (pmsg, pcall);
++  delete_call (pcall);
++  return;
+   }
+-strncpy (pcall->call_tag, ptotag->s, ptotag->len);
+-pcall->call_tag [ptotag->len] = '\0';
+ pcall->call_cseq = 1;
+ if (ptm->register_tmcb (pmsg, 0, TMCB_DESTROY | TMCB_ON_FAILURE,
+   invite_cb, pcall, 0) < 0)
+   {
+-  LM_ERR ("%sUnable to set callback for call (%s)!",
++  LM_ERR ("%sUnable to set callback for call (%s)!\n",
+     pfncname, pcall->call_from);
+   if (ptm->t_reply (pmsg, 500, presp_srverr->s) < 0)
+-    { LM_ERR ("%sUnable to reply to INVITE!", pfncname); }
++    { LM_ERR ("%sUnable to reply to INVITE!\n", pfncname); }
++  end_RTP (pmsg, pcall);
+   delete_call (pcall);
+-  return 1;
++  return;
+   }
+ 
+ /**********
++* reply with trying
++**********/
++
++if (ptm->t_reply (pmsg, 100, presp_trying->s) < 0)
++  {
++  LM_ERR ("%sUnable to create reply!\n", pfncname);
++  end_RTP (pmsg, pcall);
++  delete_call (pcall);
++  return;
++  }
++pcall->call_state = CLSTA_TRYING;
++
++/**********
+ * o add contact to reply
+ * o supports/requires PRACK? (RFC 3262 section 3)
+ * o exit if not ringing
+_at_@ -1011,19 +1129,20 @@ if (ptm->register_tmcb (pmsg, 0, TMCB_DESTROY | TMCB_ON_FAILURE,
+ 
+ str pcontact [1];
+ char *pcontacthdr = "Contact: <%s>" SIPEOL;
+-pcontact->s = pkg_malloc (strlen (pmod_data->pmohq_lst [mohq_idx].mohq_uri)
+-  + strlen (pcontacthdr));
++pcontact->s
++  = pkg_malloc (strlen (pcall->pmohq->mohq_uri) + strlen (pcontacthdr));
+ if (!pcontact->s)
+   {
+-  LM_ERR ("%sNo more memory!", pfncname);
++  LM_ERR ("%sNo more memory!\n", pfncname);
++  end_RTP (pmsg, pcall);
+   delete_call (pcall);
+-  return 1;
++  return;
+   }
+-sprintf (pcontact->s, pcontacthdr, pmod_data->pmohq_lst [mohq_idx].mohq_uri);
++sprintf (pcontact->s, pcontacthdr, pcall->pmohq->mohq_uri);
+ pcontact->len = strlen (pcontact->s);
+ if (!add_lump_rpl2 (pmsg, pcontact->s, pcontact->len, LUMP_RPL_HDR))
+   {
+-  LM_ERR ("%sUnable to add contact (%s) to call (%s)!",
++  LM_ERR ("%sUnable to add contact (%s) to call (%s)!\n",
+     pfncname, pcontact->s, pcall->call_from);
+   }
+ pkg_free (pcontact->s);
+_at_@ -1032,42 +1151,30 @@ if (search_hdr_ext (pmsg->require, p100rel))
+   {
+   if (!send_prov_rsp (pmsg, pcall))
+     {
++    end_RTP (pmsg, pcall);
+     delete_call (pcall);
+-    return 1;
+-    }
+-  }
+-else
+-  {
+-  if (ptm->t_reply (pmsg, 180, presp_ring->s) < 0)
+-    {
+-    LM_ERR ("%sUnable to reply to INVITE!", pfncname);
+-    return 1;
++    return;
+     }
+-  else
++  if (pcall->call_state == CLSTA_CANCEL)
+     {
+-    pcall->call_state = CLSTA_RINGING;
+-    mohq_debug (pcall->pmohq, "%sSent RINGING for call (%s)",
+-      pfncname, pcall->call_from);
++    end_RTP (pmsg, pcall);
++    delete_call (pcall);
++    return;
+     }
+   }
+ 
+ /**********
+-* o call cancelled?
+-* o accept call with RTP
++* accept call with RTP
+ **********/
+ 
+-if (pcall->call_state == CLSTA_CANCEL)
+-  {
+-  delete_call (pcall);
+-  return 1;
+-  }
+ if (!send_rtp_answer (pmsg, pcall))
+   {
+   if (pmod_data->psl->freply (pmsg, 500, presp_srverr) < 0)
+-    { LM_ERR ("%sUnable to create reply!", pfncname); }
++    { LM_ERR ("%sUnable to create reply!\n", pfncname); }
++  end_RTP (pmsg, pcall);
+   delete_call (pcall);
+   }
+-return 1;
++return;
+ }
+ 
+ /**********
+_at_@ -1087,6 +1194,9 @@ dlg_t *form_dialog (call_lst *pcall, struct to_body *pto_body)
+ **********/
+ 
+ char *pfncname = "form_dialog: ";
++str pdsturi [1], ptarget [1];
++int index;
++name_addr_t pname [1];
+ struct to_body *ptob = &pto_body [0];
+ struct to_body *pcontact = &pto_body [1];
+ parse_to (pcall->call_from,
+_at_@ -1094,12 +1204,49 @@ parse_to (pcall->call_from,
+ if (ptob->error != PARSE_OK)
+   {
+   // should never happen
+-  LM_ERR ("%sInvalid from URI (%s)!", pfncname, pcall->call_from);
++  LM_ERR ("%sInvalid from URI (%s)!\n", pfncname, pcall->call_from);
+   return 0;
+   }
+ if (ptob->param_lst)
+   { free_to_params (ptob); }
+-str ptarget [1];
++
++/**********
++* form dest URI from record route
++**********/
++
++if (!*pcall->call_route)
++  { pdsturi->s = 0; }
++else
++  {
++  /**********
++  * o find first route URI
++  * o strip off parameter
++  **********/
++
++  pdsturi->s = pcall->call_route;
++  pdsturi->len = strlen (pcall->call_route);
++  if (parse_nameaddr (pdsturi, pname) < 0)
++    {
++    // should never happen
++    LM_ERR ("%sUnable to parse route (%s)!\n", pfncname, pcall->call_from);
++    return 0;
++    }
++  pdsturi->s = pname->uri.s;
++  pdsturi->len = pname->uri.len;
++  for (index = 1; index < pdsturi->len; index++)
++    {
++    if (pdsturi->s [index] == ';')
++      {
++      pdsturi->len = index;
++      break;
++      }
++    }
++  }
++
++/**********
++* form target URI
++**********/
++
+ if (!*pcall->call_contact)
+   {
+   ptarget->s = ptob->uri.s;
+_at_@ -1112,7 +1259,7 @@ else
+   if (pcontact->error != PARSE_OK)
+     {
+     // should never happen
+-    LM_ERR ("%sInvalid contact (%s) for call (%s)!", pfncname,
++    LM_ERR ("%sInvalid contact (%s) for call (%s)!\n", pfncname,
+       pcall->call_contact, pcall->call_from);
+     return 0;
+     }
+_at_@ -1129,7 +1276,7 @@ else
+ dlg_t *pdlg = (dlg_t *)pkg_malloc (sizeof (dlg_t));
+ if (!pdlg)
+   {
+-  LM_ERR ("%sNo more memory!", pfncname);
++  LM_ERR ("%sNo more memory!\n", pfncname);
+   return 0;
+   }
+ memset (pdlg, 0, sizeof (dlg_t));
+_at_@ -1147,6 +1294,11 @@ pdlg->loc_uri.s = pcall->pmohq->mohq_uri;
+ pdlg->loc_uri.len = strlen (pdlg->loc_uri.s);
+ pdlg->rem_uri.s = ptob->uri.s;
+ pdlg->rem_uri.len = ptob->uri.len;
++if (pdsturi->s)
++  {
++  pdlg->dst_uri.s = pdsturi->s;
++  pdlg->dst_uri.len = pdsturi->len;
++  }
+ return pdlg;
+ }
+ 
+_at_@ -1173,7 +1325,7 @@ rtpmap **pmohfiles = find_MOH (pcall->pmohq->mohq_mohdir,
+   pcall->pmohq->mohq_mohfile);
+ if (!pmohfiles [0])
+   {
+-  LM_ERR ("%sUnable to find any MOH files for queue (%s)!", pfncname,
++  LM_ERR ("%sUnable to find any MOH files for queue (%s)!\n", pfncname,
+     pcall->pmohq->mohq_name);
+   return 0;
+   }
+_at_@ -1193,7 +1345,7 @@ for (nidx = 0; pmohfiles [nidx]; nidx++)
+ pstr->s = pkg_malloc (nsize + 1);
+ if (!pstr->s)
+   {
+-  LM_ERR ("%sNo more memory!", pfncname);
++  LM_ERR ("%sNo more memory!\n", pfncname);
+   return 0;
+   }
+ strcpy (pstr->s, pSDP);
+_at_@ -1240,7 +1392,7 @@ static void
+ call_lst *pcall = (call_lst *)*pcbp->param;
+ if (ntype == TMCB_DESTROY)
+   { pcall->call_hash = pcall->call_label = 0; }
+-LM_ERR ("invite_cb: INVITE failed for call (%s)!", pcall->call_from);
++LM_ERR ("invite_cb: INVITE failed for call (%s)!\n", pcall->call_from);
+ delete_call (pcall);
+ return;
+ }
+_at_@ -1251,10 +1403,10 @@ return;
+ * INPUT:
+ *   Arg (1) = SIP message pointer
+ *   Arg (2) = call pointer
+-* OUTPUT: 0=failed
++* OUTPUT: none
+ **********/
+ 
+-int notify_msg (sip_msg_t *pmsg, call_lst *pcall)
++void notify_msg (sip_msg_t *pmsg, call_lst *pcall)
+ 
+ {
+ /**********
+_at_@ -1264,11 +1416,11 @@ int notify_msg (sip_msg_t *pmsg, call_lst *pcall)
+ char *pfncname = "notify_msg: ";
+ if (pcall->call_state != CLSTA_RFRWAIT)
+   {
+-  LM_ERR ("%sNot waiting on a REFER for call (%s)!", pfncname,
++  LM_ERR ("%sNot waiting on a REFER for call (%s)!\n", pfncname,
+     pcall->call_from);
+   if (pmod_data->psl->freply (pmsg, 481, presp_nocall) < 0)
+-    { LM_ERR ("%sUnable to create reply!", pfncname); }
+-  return 1;
++    { LM_ERR ("%sUnable to create reply!\n", pfncname); }
++  return;
+   }
+ 
+ /**********
+_at_@ -1279,28 +1431,28 @@ if (pcall->call_state != CLSTA_RFRWAIT)
+ 
+ if (!search_hdr_ext (pmsg->content_type, psipfrag))
+   {
+-  LM_ERR ("%sNot a %s type for call (%s)!", pfncname,
++  LM_ERR ("%sNot a %s type for call (%s)!\n", pfncname,
+     psipfrag->s, pcall->call_from);
+   if (pmod_data->psl->freply (pmsg, 415, presp_unsupp) < 0)
+-    { LM_ERR ("%sUnable to create reply!", pfncname); }
+-  return 1;
++    { LM_ERR ("%sUnable to create reply!\n", pfncname); }
++  return;
+   }
+ char *pfrag = get_body (pmsg);
+ if (!pfrag)
+   {
+-  LM_ERR ("%s%s body missing for call (%s)!", pfncname,
++  LM_ERR ("%s%s body missing for call (%s)!\n", pfncname,
+     psipfrag->s, pcall->call_from);
+   if (pmod_data->psl->freply (pmsg, 415, presp_unsupp) < 0)
+-    { LM_ERR ("%sUnable to create reply!", pfncname); }
+-  return 1;
++    { LM_ERR ("%sUnable to create reply!\n", pfncname); }
++  return;
+   }
+ str pbody [1];
+ pbody->len = pmsg->len - (int)(pfrag - pmsg->buf);
+ pbody->s = pkg_malloc (pbody->len + 2);
+ if (!pbody->s)
+   {
+-  LM_ERR ("%sNo more memory!", pfncname);
+-  return 1;
++  LM_ERR ("%sNo more memory!\n", pfncname);
++  return;
+   }
+ strncpy (pbody->s, pfrag, pbody->len);
+ if (pbody->s [pbody->len - 1] != '\n')
+_at_@ -1313,10 +1465,10 @@ parse_first_line (pbody->s, pbody->len + 1, pstart);
+ pkg_free (pbody->s);
+ if (pstart->type != SIP_REPLY)
+   {
+-  LM_ERR ("%sReply missing for call (%s)!", pfncname, pcall->call_from);
++  LM_ERR ("%sReply missing for call (%s)!\n", pfncname, pcall->call_from);
+   if (pmod_data->psl->freply (pmsg, 415, presp_unsupp) < 0)
+-    { LM_ERR ("%sUnable to create reply!", pfncname); }
+-  return 1;
++    { LM_ERR ("%sUnable to create reply!\n", pfncname); }
++  return;
+   }
+ 
+ /**********
+_at_@ -1324,32 +1476,34 @@ if (pstart->type != SIP_REPLY)
+ * o REFER done?
+ **********/
+ 
++int nreply = pstart->u.reply.statuscode;
+ if (pmod_data->psl->freply (pmsg, 200, presp_ok) < 0)
+   {
+-  LM_ERR ("%sUnable to create reply for call (%s)!",
++  LM_ERR ("%sUnable to create reply for call (%s)!\n",
+     pfncname, pcall->call_from);
+-  return 1;
++  return;
+   }
+-int nreply = pstart->u.reply.statuscode;
+ mohq_debug (pcall->pmohq, "%sNOTIFY received reply (%d) for call (%s)",
+   pfncname, nreply, pcall->call_from);
+ switch (nreply / 100)
+   {
+   case 1:
++    pcall->refer_time = time (0);
+     break;
+   case 2:
+     close_call (pmsg, pcall);
+     break;
+   default:
+-    LM_WARN ("%sUnable to redirect call (%s)!", pfncname, pcall->call_from);
++    LM_WARN ("%sUnable to redirect call (%s)\n", pfncname, pcall->call_from);
+     if (nreply == 487)
+       {
+       /**********
+-      * call was canceled
++      * call was cancelled
+       **********/
+ 
+-      drop_call (pmsg, pcall);
+-      return 1;
++      end_RTP (pmsg, pcall);
++      delete_call (pcall);
++      return;
+       }
+ 
+     /**********
+_at_@ -1360,7 +1514,7 @@ switch (nreply / 100)
+     update_call_rec (pcall);
+     break;
+   }
+-return 1;
++return;
+ }
+ 
+ /**********
+_at_@ -1369,10 +1523,10 @@ return 1;
+ * INPUT:
+ *   Arg (1) = SIP message pointer
+ *   Arg (2) = call pointer
+-* OUTPUT: 0=failed
++* OUTPUT: none
+ **********/
+ 
+-int prack_msg (sip_msg_t *pmsg, call_lst *pcall)
++void prack_msg (sip_msg_t *pmsg, call_lst *pcall)
+ 
+ {
+ /**********
+_at_@ -1383,10 +1537,10 @@ char *pfncname = "prack_msg: ";
+ tm_api_t *ptm = pmod_data->ptm;
+ if (pcall->call_state != CLSTA_PRACKSTRT)
+   {
+-  LM_ERR ("%sUnexpected PRACK (%s)!", pfncname, pcall->call_from);
++  LM_ERR ("%sUnexpected PRACK (%s)!\n", pfncname, pcall->call_from);
+   if (pmod_data->psl->freply (pmsg, 481, presp_nocall) < 0)
+-    { LM_ERR ("%sUnable to create reply!", pfncname); }
+-  return 1;
++    { LM_ERR ("%sUnable to create reply!\n", pfncname); }
++  return;
+   }
+ 
+ /**********
+_at_@ -1396,20 +1550,20 @@ if (pcall->call_state != CLSTA_PRACKSTRT)
+ 
+ if (ptm->t_newtran (pmsg) < 0)
+   {
+-  LM_ERR ("%sUnable to create new transaction for call (%s)!",
++  LM_ERR ("%sUnable to create new transaction for call (%s)!\n",
+     pfncname, pcall->call_from);
+   if (pmod_data->psl->freply (pmsg, 500, presp_srverr) < 0)
+-    { LM_ERR ("%sUnable to create reply!", pfncname); }
+-  return 1;
++    { LM_ERR ("%sUnable to create reply!\n", pfncname); }
++  return;
+   }
+ if (ptm->t_reply (pmsg, 200, presp_ok->s) < 0)
+   {
+-  LM_ERR ("%sUnable to reply to PRACK for call (%s)!",
++  LM_ERR ("%sUnable to reply to PRACK for call (%s)!\n",
+     pfncname, pcall->call_from);
+-  return 1;
++  return;
+   }
+ pcall->call_state = CLSTA_PRACKRPLY;
+-return 1;
++return;
+ }
+ 
+ /**********
+_at_@ -1450,18 +1604,22 @@ puri->s = pcall->call_referto;
+ puri->len = strlen (puri->s);
+ int npos1 = sizeof (prefermsg) // REFER template
+   + strlen (pcall->call_via) // Via
++  + strlen (pcall->call_route) // Route
++  + strlen (pcall->pmohq->mohq_uri) // Contact
+   + puri->len // Refer-To
+-  + ptob->uri.len; // Referred-By
++  + strlen (pcall->pmohq->mohq_uri); // Referred-By
+ char *pbuf = pkg_malloc (npos1);
+ if (!pbuf)
+   {
+-  LM_ERR ("%sNo more memory!", pfncname);
++  LM_ERR ("%sNo more memory!\n", pfncname);
+   goto refererr;
+   }
+ sprintf (pbuf, prefermsg,
+   pcall->call_via, // Via
++  pcall->call_route, // Route
++  pcall->pmohq->mohq_uri, // Contact
+   puri->s, // Refer-To
+-  STR_FMT (&ptob->uri)); // Referred-By
++  pcall->pmohq->mohq_uri); // Referred-By
+ 
+ /**********
+ * send REFER request
+_at_@ -1474,13 +1632,14 @@ phdrs->s = pbuf;
+ phdrs->len = strlen (pbuf);
+ set_uac_req (puac, prefer, phdrs, 0, pdlg,
+   TMCB_LOCAL_COMPLETED | TMCB_ON_FAILURE, refer_cb, pcall);
++pcall->refer_time = time (0);
+ pcall->call_state = CLSTA_REFER;
+ update_call_rec (pcall);
+ mohq_lock_release (plock);
+ if (ptm->t_request_within (puac) < 0)
+   {
+   pcall->call_state = CLSTA_INQUEUE;
+-  LM_ERR ("%sUnable to create REFER request for call (%s)!",
++  LM_ERR ("%sUnable to create REFER request for call (%s)!\n",
+     pfncname, pcall->call_from);
+   update_call_rec (pcall);
+   goto refererr;
+_at_@ -1514,21 +1673,23 @@ char *pfncname = "refer_cb: ";
+ call_lst *pcall = (call_lst *)*pcbp->param;
+ if ((ntype == TMCB_ON_FAILURE) || (pcbp->req == FAKED_REPLY))
+   {
+-  LM_ERR ("%sCall (%s) did not respond to REFER", pfncname,
++  LM_ERR ("%sCall (%s) did not respond to REFER!\n", pfncname,
+     pcall->call_from);
+-  drop_call (pcbp->req, pcall);
++  end_RTP (pcbp->req, pcall);
++  delete_call (pcall);
+   return;
+   }
+ int nreply = pcbp->code;
+ if ((nreply / 100) == 2)
+   {
++  pcall->refer_time = time (0);
+   pcall->call_state = CLSTA_RFRWAIT;
+   mohq_debug (pcall->pmohq, "%sCall (%s) REFER reply=%d",
+     pfncname, pcall->call_from, nreply);
+   }
+ else
+   {
+-  LM_ERR ("%sCall (%s) REFER error (%d)", pfncname,
++  LM_ERR ("%sCall (%s) REFER error (%d)!\n", pfncname,
+     pcall->call_from, nreply);
+   if (nreply == 481)
+     { delete_call (pcall); }
+_at_@ -1547,10 +1708,10 @@ return;
+ * INPUT:
+ *   Arg (1) = SIP message pointer
+ *   Arg (2) = call pointer
+-* OUTPUT: 0=failed
++* OUTPUT: none
+ **********/
+ 
+-int reinvite_msg (sip_msg_t *pmsg, call_lst *pcall)
++void reinvite_msg (sip_msg_t *pmsg, call_lst *pcall)
+ 
+ {
+ /**********
+_at_@ -1565,17 +1726,17 @@ if ((pcall->call_state / 100) < 2)
+   mohq_debug (pcall->pmohq, "%sINVITE still pending for call (%s)",
+     pfncname, pcall->call_from);
+   if (pmod_data->psl->freply (pmsg, 491, presp_reqpend) < 0)
+-    { LM_ERR ("%sUnable to create reply!", pfncname); }
+-  return 1;
++    { LM_ERR ("%sUnable to create reply!\n", pfncname); }
++  return;
+   }
+ if (!(pmsg->msg_flags & FL_SDP_BODY))
+   {
+   if (parse_sdp (pmsg))
+     {
+-    LM_ERR ("%sre-INVITE lacks SDP (%s)!", pfncname, pcall->call_from);
++    LM_ERR ("%sre-INVITE lacks SDP (%s)!\n", pfncname, pcall->call_from);
+     if (pmod_data->psl->freply (pmsg, 488, presp_noaccept) < 0)
+-      { LM_ERR ("%sUnable to create reply!", pfncname); }
+-    return 1;
++      { LM_ERR ("%sUnable to create reply!\n", pfncname); }
++    return;
+     }
+   }
+ 
+_at_@ -1645,12 +1806,12 @@ if (!bhold)
+   {
+   if (!bmatch)
+     {
+-    LM_ERR ("%sre-INVITE refused because no matching payload for call (%s)!",
++    LM_ERR ("%sre-INVITE refused because no matching payload for call (%s)!\n",
+       pfncname, pcall->call_from);
+     if (pmod_data->psl->freply (pmsg, 488, presp_noaccept) < 0)
+       {
+-      LM_ERR ("%sUnable to create reply!", pfncname);
+-      return 1;
++      LM_ERR ("%sUnable to create reply!\n", pfncname);
++      return;
+       }
+     }
+   else
+_at_@ -1659,26 +1820,26 @@ if (!bhold)
+       pfncname, pcall->call_from);
+     if (pmod_data->psl->freply (pmsg, 200, presp_ok) < 0)
+       {
+-      LM_ERR ("%sUnable to create reply!", pfncname);
+-      return 1;
++      LM_ERR ("%sUnable to create reply!\n", pfncname);
++      return;
+       }
+     }
+-  return 1;
++  return;
+   }
+ 
+ /**********
+ * hold not allowed, say good-bye
+ **********/
+ 
+-LM_ERR ("%sTerminating call (%s) because hold not allowed!",
++LM_ERR ("%sTerminating call (%s) because hold not allowed!\n",
+   pfncname, pcall->call_from);
+ if (pmod_data->psl->freply (pmsg, 200, presp_ok) < 0)
+   {
+-  LM_ERR ("%sUnable to create reply!", pfncname);
+-  return 1;
++  LM_ERR ("%sUnable to create reply!\n", pfncname);
++  return;
+   }
+ close_call (pmsg, pcall);
+-return 1;
++return;
+ }
+ 
+ /**********
+_at_@ -1746,22 +1907,21 @@ char *phdrtmplt =
+   "Accept-Language: en" SIPEOL
+   "Require: 100rel" SIPEOL
+   "RSeq: %d" SIPEOL
+-  "User-Agent: " USRAGNT SIPEOL
+   ;
+ sprintf (phdrtmp, phdrtmplt, pcall->call_cseq);
+ struct lump_rpl **phdrlump = add_lump_rpl2 (pmsg, phdrtmp,
+   strlen (phdrtmp), LUMP_RPL_HDR);
+ if (!phdrlump)
+   {
+-  LM_ERR ("%sUnable to create new header for call (%s)!",
++  LM_ERR ("%sUnable to create new header for call (%s)!\n",
+     pfncname, pcall->call_from);
+   if (pmod_data->psl->freply (pmsg, 500, presp_srverr) < 0)
+-    { LM_ERR ("%sUnable to create reply!", pfncname); }
++    { LM_ERR ("%sUnable to create reply!\n", pfncname); }
+   return 0;
+   }
+ if (ptm->t_reply (pmsg, 180, presp_ring->s) < 0)
+   {
+-  LM_ERR ("%sUnable to reply to INVITE for call (%s)",
++  LM_ERR ("%sUnable to reply to INVITE for call (%s)!\n",
+     pfncname, pcall->call_from);
+   return 0;
+   }
+_at_@ -1782,7 +1942,7 @@ while (1)
+     { break; }
+   if (nstart < time (0))
+     {
+-    LM_ERR ("%sNo PRACK response for call (%s)",
++    LM_ERR ("%sNo PRACK response for call (%s)!\n",
+       pfncname, pcall->call_from);
+     break;
+     }
+_at_@ -1822,7 +1982,7 @@ pbuf->s = build_res_buf_from_sip_req (200, presp_ok, ptotag, ptrans->uas.request
+   (unsigned int *)&pbuf->len, pBM);
+ if (!pbuf->s || !pbuf->len)
+   {
+-  LM_ERR ("%sUnable to create SDP response for call (%s)!",
++  LM_ERR ("%sUnable to create SDP response for call (%s)!\n",
+     pfncname, pcall->call_from);
+   return 0;
+   }
+_at_@ -1895,7 +2055,7 @@ npos1 += pextrahdr->len + strlen (pbodylen) + pSDP->len + 1;
+ char *pnewbuf = pkg_malloc (npos1);
+ if (!pnewbuf)
+   {
+-  LM_ERR ("%sNo more memory!", pfncname);
++  LM_ERR ("%sNo more memory!\n", pfncname);
+   goto answer_done;
+   }
+ for (npos1 = npos2 = 0; npos2 < nhdrcnt; npos2++)
+_at_@ -1925,7 +2085,7 @@ build_sip_msg_from_buf (pnmsg, pbuf->s, pbuf->len, 0);
+ memcpy (&pnmsg->rcv, &pmsg->rcv, sizeof (struct receive_info));
+ 
+ /**********
+-* o send rtpproxy answer
++* o send RTP answer
+ * o form stream file
+ * o send stream
+ **********/
+_at_@ -1934,7 +2094,7 @@ mohq_debug (pcall->pmohq, "%sAnswering RTP link for call (%s)",
+   pfncname, pcall->call_from);
+ if (pmod_data->fn_rtp_answer (pnmsg, 0, 0) != 1)
+   {
+-  LM_ERR ("%srtpproxy_answer refused for call (%s)!",
++  LM_ERR ("%srtpproxy_answer refused for call (%s)!\n",
+     pfncname, pcall->call_from);
+   goto answer_done;
+   }
+_at_@ -1951,7 +2111,7 @@ pkg_free (pnewbuf);
+ free_sip_msg (pnmsg);
+ if (!pbuf->s || !pbuf->len)
+   {
+-  LM_ERR ("%sUnable to create SDP response for call (%s)!",
++  LM_ERR ("%sUnable to create SDP response for call (%s)!\n",
+     pfncname, pcall->call_from);
+   goto answer_done;
+   }
+_at_@ -1975,26 +2135,26 @@ char *pfnd = strstr (pnewSDP->s, "m=audio ");
+ if (!pfnd)
+   {
+   // should not happen
+-  LM_ERR ("%sUnable to find audio port for call (%s)!",
++  LM_ERR ("%sUnable to find audio port for call (%s)!\n",
+     pfncname, pcall->call_from);
+   goto answer_done;
+   }
+ pcall->call_aport = strtol (pfnd + 8, NULL, 10);
+ if (!add_lump_rpl2 (pmsg, pextrahdr->s, pextrahdr->len, LUMP_RPL_HDR))
+   {
+-  LM_ERR ("%sUnable to add header for call (%s)!",
++  LM_ERR ("%sUnable to add header for call (%s)!\n",
+     pfncname, pcall->call_from);
+   goto answer_done;
+   }
+ if (!add_lump_rpl2 (pmsg, pnewSDP->s, pnewSDP->len, LUMP_RPL_BODY))
+   {
+-  LM_ERR ("%sUnable to add SDP body for call (%s)!",
++  LM_ERR ("%sUnable to add SDP body for call (%s)!\n",
+     pfncname, pcall->call_from);
+   goto answer_done;
+   }
+ if (ptm->t_reply (pmsg, 200, presp_ok->s) < 0)
+   {
+-  LM_ERR ("%sUnable to reply to INVITE for call (%s)!",
++  LM_ERR ("%sUnable to reply to INVITE for call (%s)!\n",
+     pfncname, pcall->call_from);
+   goto answer_done;
+   }
+_at_@ -2043,7 +2203,34 @@ mohq_debug (pcall->pmohq, "%sStarting RTP link for call (%s)",
+   pfncname, pcall->call_from);
+ if (fn_stream (pmsg, (char *)pmodel, (char *)-1) != 1)
+   {
+-  LM_ERR ("%srtpproxy_stream refused for call (%s)!",
++  LM_ERR ("%srtpproxy_stream refused for call (%s)!\n",
++    pfncname, pcall->call_from);
++  return 0;
++  }
++return 1;
++}
++
++/**********
++* Stop Streaming
++*
++* INPUT:
++*   Arg (1) = SIP message pointer
++*   Arg (2) = call pointer
++*   Arg (3) = server flag
++* OUTPUT: 0 if failed
++**********/
++
++int stop_stream (sip_msg_t *pmsg, call_lst *pcall, int bserver)
++
++{
++char *pfncname = "stop_stream: ";
++cmd_function fn_stop = bserver ? pmod_data->fn_rtp_stop_s
++  : pmod_data->fn_rtp_stop_c;
++mohq_debug (pcall->pmohq, "%sStopping RTP link for call (%s)",
++  pfncname, pcall->call_from);
++if (fn_stop (pmsg, (char *)-1, (char *)-1) != 1)
++  {
++  LM_ERR ("%srtpproxy_stop refused for call (%s)!\n",
+     pfncname, pcall->call_from);
+   return 0;
+   }
+_at_@ -2064,7 +2251,7 @@ char *form_tmpstr (str *pstr)
+ char *pcstr = malloc (pstr->len + 1);
+ if (!pcstr)
+   {
+-  LM_ERR ("No more memory!");
++  LM_ERR ("No more memory!\n");
+   return NULL;
+   }
+ memcpy (pcstr, pstr->s, pstr->len);
+_at_@ -2164,7 +2351,7 @@ struct mi_root *mi_debug (struct mi_root *pcmd_tree, void *parms)
+ struct mi_node *pnode = pcmd_tree->node.kids;
+ if (!pnode || !pnode->next || pnode->next->next)
+   { return init_mi_tree (400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); }
+-int nq_idx = find_queue (&pnode->value);
++int nq_idx = find_qname (&pnode->value);
+ if (nq_idx == -1)
+   { return init_mi_tree (400, pmi_noqueue->s, pmi_noqueue->len); }
+ char pint [20];
+_at_@ -2217,7 +2404,7 @@ struct mi_root *mi_drop_call (struct mi_root *pcmd_tree, void *parms)
+ struct mi_node *pnode = pcmd_tree->node.kids;
+ if (!pnode || !pnode->next || pnode->next->next)
+   { return init_mi_tree (400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); }
+-int nq_idx = find_queue (&pnode->value);
++int nq_idx = find_qname (&pnode->value);
+ if (nq_idx == -1)
+   { return init_mi_tree (400, pmi_noqueue->s, pmi_noqueue->len); }
+ if (!mohq_lock_set (pmod_data->pcall_lock, 0, 5000))
+_at_@ -2240,7 +2427,7 @@ for (nidx = 0; nidx < pmod_data->call_cnt; nidx++)
+   **********/
+ 
+   call_lst *pcall = &pmod_data->pcall_lst [nidx];
+-  if (!pcall->call_active)
++  if (!pcall->call_state)
+     { continue; }
+   if (pqueue->mohq_id != pcall->pmohq->mohq_id)
+     { continue; }
+_at_@ -2279,12 +2466,12 @@ char *pfncname = "mohq_count: ";
+ str pqname [1];
+ if (!pqueue || !presult)
+   {
+-  LM_ERR ("%sParameters missing!", pfncname);
++  LM_ERR ("%sParameters missing!\n", pfncname);
+   return -1;
+   }
+ if (fixup_get_svalue (pmsg, (gparam_p)pqueue, pqname))
+   {
+-  LM_ERR ("%sInvalid queue name!", pfncname);
++  LM_ERR ("%sInvalid queue name!\n", pfncname);
+   return -1;
+   }
+ 
+_at_@ -2294,12 +2481,12 @@ if (fixup_get_svalue (pmsg, (gparam_p)pqueue, pqname))
+ * o count items in queue
+ **********/
+ 
+-int nq_idx = find_queue (pqname);
++int nq_idx = find_qname (pqname);
+ int ncount = 0;
+ call_lst *pcalls = pmod_data->pcall_lst;
+ int ncall_idx, mohq_id;
+ if (!mohq_lock_set (pmod_data->pcall_lock, 0, 200))
+-  { LM_ERR ("%sUnable to lock calls!", pfncname); }
++  { LM_ERR ("%sUnable to lock calls!\n", pfncname); }
+ else
+   {
+   if (nq_idx != -1)
+_at_@ -2307,7 +2494,7 @@ else
+     mohq_id = pmod_data->pmohq_lst [nq_idx].mohq_id;
+     for (ncall_idx = 0; ncall_idx < pmod_data->call_cnt; ncall_idx++)
+       {
+-      if (!pcalls [ncall_idx].call_active)
++      if (!pcalls [ncall_idx].call_state)
+         { continue; }
+       if (pcalls [ncall_idx].pmohq->mohq_id == mohq_id
+         && pcalls [ncall_idx].call_state == CLSTA_INQUEUE)
+_at_@ -2328,7 +2515,7 @@ pavp_val->ri = ncount;
+ pavp_val->flags = PV_TYPE_INT | PV_VAL_INT;
+ if (presult->setf (pmsg, &presult->pvp, (int)EQ_T, pavp_val) < 0)
+   {
+-  LM_ERR ("%sUnable to set pv value for mohq_count ()!", pfncname);
++  LM_ERR ("%sUnable to set pv value for mohq_count ()!\n", pfncname);
+   return -1;
+   }
+ return 1;
+_at_@ -2366,7 +2553,7 @@ va_list ap;
+ va_start (ap, pfmt);
+ vsnprintf (ptext, sizeof (ptext), pfmt, ap);
+ va_end (ap);
+-LM_DBG ("%s", ptext);
++LM_DBG ("%s\n", ptext);
+ if (nsys_log < nmohq_log)
+   { reset_local_debug_level (); }
+ return;
+_at_@ -2377,39 +2564,28 @@ return;
+ *
+ * INPUT:
+ *   Arg (1) = SIP message pointer
+-* OUTPUT: -1=not directed to queue; 1=successfully processed
++* OUTPUT: -1=not directed to queue or other error; 1=processed
+ **********/
+ 
+ int mohq_process (sip_msg_t *pmsg)
+ 
+ {
+ /**********
+-* o parse headers
+-* o lock MOH queue
+-* o directed to message queue?
+-* o connect to database
++* read lock queue and check for updates
+ **********/
+ 
+ char *pfncname = "mohq_process: ";
+-if (parse_headers (pmsg, HDR_EOH_F, 0) < 0)
+-  {
+-  LM_ERR ("%sUnable to parse header!", pfncname);
+-  return -1;
+-  }
+-if (!mohq_lock_set (pmod_data->pmohq_lock, 0, 2000))
++if (!mohq_lock_set (pmod_data->pmohq_lock, 0, 500))
+   {
+-  LM_ERR ("%sUnable to lock calls!", pfncname);
++  LM_ERR ("%sUnable to read lock queue!\n", pfncname);
+   return -1;
+   }
+-call_lst *pcall;
+-int mohq_idx = find_call (pmsg, &pcall);
+ db1_con_t *pconn = mohq_dbconnect ();
+ if (pconn)
+   {
+   /**********
+   * o last update older than 1 minute?
+-  * o exclusively lock MOH queue
+-  * o update queue
++  * o update write locked queue
+   **********/
+ 
+   if (pmod_data->mohq_update + 60 < time (0))
+_at_@ -2423,22 +2599,51 @@ if (pconn)
+     }
+   mohq_dbdisconnect (pconn);
+   }
++
++/**********
++* o parse headers
++* o directed to message queue?
++* o write lock calls
++* o search for call
++* o release call lock
++**********/
++
++if (parse_headers (pmsg, HDR_EOH_F, 0) < 0)
++  {
++  mohq_lock_release (pmod_data->pmohq_lock);
++  LM_ERR ("%sUnable to parse header!\n", pfncname);
++  return -1;
++  }
++int mohq_idx = find_queue (pmsg);
+ if (mohq_idx < 0)
+   {
+   mohq_lock_release (pmod_data->pmohq_lock);
+   return -1;
+   }
++if (!mohq_lock_set (pmod_data->pcall_lock, 1, 500))
++  {
++  mohq_lock_release (pmod_data->pmohq_lock);
++  LM_ERR ("%sUnable to write lock calls!\n", pfncname);
++  return 1;
++  }
++call_lst *pcall = find_call (pmsg, mohq_idx);
++mohq_lock_release (pmod_data->pcall_lock);
++if (!pcall)
++  {
++  mohq_lock_release (pmod_data->pmohq_lock);
++  return 1;
++  }
+ 
+ /**********
+ * o process message
+-* o release MOH queue
++* o release queue lock
+ **********/
+ 
+ mohq_debug (&pmod_data->pmohq_lst [mohq_idx],
+   "%sProcessing %.*s, queue (%s)", pfncname,
+   STR_FMT (&REQ_LINE (pmsg).method),
+   pmod_data->pmohq_lst [mohq_idx].mohq_name);
+-int ret;
++str *ptotag;
+ switch (pmsg->REQ_METHOD)
+   {
+   case METHOD_INVITE:
+_at_@ -2446,33 +2651,35 @@ switch (pmsg->REQ_METHOD)
+     * initial INVITE?
+     **********/
+ 
+-    if (!pcall)
+-      { ret = first_invite_msg (pmsg, mohq_idx); }
++    ptotag = &(get_to (pmsg)->tag_value);
++    if (!ptotag->len)
++      { ptotag = 0; }
++    if (!ptotag)
++      { first_invite_msg (pmsg, pcall); }
+     else
+-      { ret = reinvite_msg (pmsg, pcall); }
++      { reinvite_msg (pmsg, pcall); }
+     break;
+   case METHOD_NOTIFY:
+-    ret = notify_msg (pmsg, pcall);
++    notify_msg (pmsg, pcall);
+     break;
+   case METHOD_PRACK:
+-    ret = prack_msg (pmsg, pcall);
++    prack_msg (pmsg, pcall);
+     break;
+   case METHOD_ACK:
+-    ret = ack_msg (pmsg, pcall);
++    ack_msg (pmsg, pcall);
+     break;
+   case METHOD_BYE:
+-    ret = bye_msg (pmsg, pcall);
++    bye_msg (pmsg, pcall);
+     break;
+   case METHOD_CANCEL:
+-    ret = cancel_msg (pmsg, pcall);
++    cancel_msg (pmsg, pcall);
+     break;
+   default:
+     deny_method (pmsg, pcall);
+-    ret = 1;
+     break;
+   }
+ mohq_lock_release (pmod_data->pmohq_lock);
+-return ret ? 1 : -1;
++return 1;
+ }
+ 
+ /**********
+_at_@ -2497,28 +2704,28 @@ char *pfncname = "mohq_retrieve: ";
+ str puri [1], pqname [1];
+ if (!pqueue || !pURI)
+   {
+-  LM_ERR ("%sParameters missing!", pfncname);
++  LM_ERR ("%sParameters missing!\n", pfncname);
+   return -1;
+   }
+ if (fixup_get_svalue (pmsg, (gparam_p)pqueue, pqname))
+   {
+-  LM_ERR ("%sInvalid queue name!", pfncname);
++  LM_ERR ("%sInvalid queue name!\n", pfncname);
+   return -1;
+   }
+ if (fixup_get_svalue (pmsg, (gparam_p)pURI, puri))
+   {
+-  LM_ERR ("%sInvalid URI!", pfncname);
++  LM_ERR ("%sInvalid URI!\n", pfncname);
+   return -1;
+   }
+ if (puri->len > URI_LEN)
+   {
+-  LM_ERR ("%sURI too long!", pfncname);
++  LM_ERR ("%sURI too long!\n", pfncname);
+   return -1;
+   }
+ struct sip_uri puri_parsed [1];
+ if (parse_uri (puri->s, puri->len, puri_parsed))
+   {
+-  LM_ERR ("%sInvalid URI (%.*s)!", pfncname, STR_FMT (puri));
++  LM_ERR ("%sInvalid URI (%.*s)!\n", pfncname, STR_FMT (puri));
+   return -1;
+   }
+ 
+_at_@ -2528,12 +2735,12 @@ if (parse_uri (puri->s, puri->len, puri_parsed))
+ * o find oldest call
+ **********/
+ 
+-int nq_idx = find_queue (pqname);
++int nq_idx = find_qname (pqname);
+ if (nq_idx == -1)
+   { return -1; }
+ if (!mohq_lock_set (pmod_data->pcall_lock, 0, 200))
+   {
+-  LM_ERR ("%sUnable to lock calls!", pfncname);
++  LM_ERR ("%sUnable to lock calls!\n", pfncname);
+   return -1;
+   }
+ call_lst *pcall = 0;
+_at_@ -2546,15 +2753,27 @@ for (ncall_idx = 0; ncall_idx < pmod_data->call_cnt; ncall_idx++)
+   /**********
+   * o active call?
+   * o matching queue?
++  * o refer stuck?
+   * o in queue?
+   * o check age
+   **********/
+ 
+   pcall = &pmod_data->pcall_lst [ncall_idx];
+-  if (!pcall->call_active)
++  if (!pcall->call_state)
+     { continue; }
+   if (pcall->pmohq->mohq_id != mohq_id)
+     { continue; }
++  if ((pcall->call_state == CLSTA_REFER)
++    || (pcall->call_state == CLSTA_RFRWAIT))
++    {
++    if ((pcall->refer_time + 32) < time (0))
++      {
++      LM_ERR
++        ("%sDropping call because no response to REFER for call (%s)!\n",
++        pfncname, pcall->call_from);
++      close_call (FAKED_REPLY, pcall);
++      }
++    }
+   if (pcall->call_state != CLSTA_INQUEUE)
+     { continue; }
+   if (!ntime)
+_at_@ -2573,7 +2792,7 @@ for (ncall_idx = 0; ncall_idx < pmod_data->call_cnt; ncall_idx++)
+   }
+ if (nfound == -1)
+   {
+-  LM_WARN ("%sNo calls in queue (%.*s)", pfncname, STR_FMT (pqname));
++  LM_WARN ("%sNo calls in queue (%.*s)\n", pfncname, STR_FMT (pqname));
+   mohq_lock_release (pmod_data->pcall_lock);
+   return -1;
+   }
+_at_@ -2588,7 +2807,7 @@ strncpy (pcall->call_referto, puri->s, puri->len);
+ pcall->call_referto [puri->len] = '\0';
+ if (refer_call (pcall, pmod_data->pcall_lock))
+   { return 1; }
+-LM_ERR ("%sUnable to refer call (%s)!", pfncname, pcall->call_from);
++LM_ERR ("%sUnable to refer call (%s)!\n", pfncname, pcall->call_from);
+ return -1;
+ }
+ 
+_at_@ -2612,24 +2831,24 @@ int mohq_send (sip_msg_t *pmsg, char *pqueue)
+ char *pfncname = "mohq_send: ";
+ if (pmsg->REQ_METHOD != METHOD_INVITE)
+   {
+-  LM_ERR ("%sNot an INVITE message!", pfncname);
++  LM_ERR ("%sNot an INVITE message!\n", pfncname);
+   return -1;
+   }
+ to_body_t *pto_body = get_to (pmsg);
+ if (pto_body->tag_value.len)
+   {
+-  LM_ERR ("%sNot a first INVITE message!", pfncname);
++  LM_ERR ("%sNot a first INVITE message!\n", pfncname);
+   return -1;
+   }
+ str pqname [1];
+ if (!pqueue)
+   {
+-  LM_ERR ("%sParameters missing!", pfncname);
++  LM_ERR ("%sParameters missing!\n", pfncname);
+   return -1;
+   }
+ if (fixup_get_svalue (pmsg, (gparam_p)pqueue, pqname))
+   {
+-  LM_ERR ("%sInvalid queue name!", pfncname);
++  LM_ERR ("%sInvalid queue name!\n", pfncname);
+   return -1;
+   }
+ 
+_at_@ -2639,14 +2858,14 @@ if (fixup_get_svalue (pmsg, (gparam_p)pqueue, pqname))
+ * o relay message
+ **********/
+ 
+-int nq_idx = find_queue (pqname);
++int nq_idx = find_qname (pqname);
+ if (nq_idx == -1)
+   { return -1; }
+ str pruri [1] = {{0, strlen (pmod_data->pmohq_lst [nq_idx].mohq_uri)}};
+ pruri->s = pkg_malloc (pruri->len + 1);
+ if (!pruri->s)
+   {
+-  LM_ERR ("%sNo more memory!", pfncname);
++  LM_ERR ("%sNo more memory!\n", pfncname);
+   return -1;
+   }
+ strcpy (pruri->s, pmod_data->pmohq_lst [nq_idx].mohq_uri);
+_at_@ -2658,7 +2877,7 @@ pmsg->parsed_uri_ok = 0;
+ pmsg->parsed_orig_ruri_ok = 0;
+ if (pmod_data->ptm->t_relay (pmsg, 0, 0) < 0)
+   {
+-  LM_ERR ("%sUnable to relay INVITE!", pfncname);
++  LM_ERR ("%sUnable to relay INVITE!\n", pfncname);
+   return -1;
+   }
+ return 1;
+diff --git a/modules/mohqueue/mohq_funcs.h b/modules/mohqueue/mohq_funcs.h
+index 25c06d7..5d4dfd1 100644
+--- a/modules/mohqueue/mohq_funcs.h
++++ b/modules/mohqueue/mohq_funcs.h
+_at_@ -1,9 +1,7 @@
+ /*
+- * $Id$
++ * Copyright (C) 2013-15 Robert Boisvert
+  *
+- * Copyright (C) 2013 Robert Boisvert
+- *
+- * This file is part of the mohqueue module for sip-router, a free SIP server.
++ * This file is part of the mohqueue module for Kamailio, a free SIP server.
+  *
+  * The mohqueue module is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+diff --git a/modules/mohqueue/mohq_locks.c b/modules/mohqueue/mohq_locks.c
+index 996ac52..50ecb1c 100644
+--- a/modules/mohqueue/mohq_locks.c
++++ b/modules/mohqueue/mohq_locks.c
+_at_@ -1,9 +1,7 @@
+ /*
+- * $Id$
++ * Copyright (C) 2013-15 Robert Boisvert
+  *
+- * Copyright (C) 2013 Robert Boisvert
+- *
+- * This file is part of the mohqueue module for sip-router, a free SIP server.
++ * This file is part of the mohqueue module for Kamailio, a free SIP server.
+  *
+  * The mohqueue module is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+_at_@ -103,12 +101,12 @@ char *pfncname = "mohq_lock_init: ";
+ plock->plock = lock_alloc ();
+ if (!plock->plock)
+   {
+-  LM_ERR ("%sUnable to allocate lock memory!", pfncname);
++  LM_ERR ("%sUnable to allocate lock memory!\n", pfncname);
+   return 0;
+   }
+ if (!lock_init (plock->plock))
+   {
+-  LM_ERR ("%sUnable to init lock!", pfncname);
++  LM_ERR ("%sUnable to init lock!\n", pfncname);
+   lock_dealloc (plock->plock);
+   return 0;
+   }
+_at_@ -140,7 +138,7 @@ switch (plock->lock_cnt)
+     plock->lock_cnt = 0;
+     break;
+   case 0:
+-    LM_WARN ("mohq_lock_release: Lock was not set");
++    LM_WARN ("mohq_lock_release: Lock was not set.\n");
+     break;
+   default:
+     plock->lock_cnt--;
+diff --git a/modules/mohqueue/mohq_locks.h b/modules/mohqueue/mohq_locks.h
+index ddecff5..18c23fe 100644
+--- a/modules/mohqueue/mohq_locks.h
++++ b/modules/mohqueue/mohq_locks.h
+_at_@ -1,9 +1,7 @@
+ /*
+- * $Id$
++ * Copyright (C) 2013-15 Robert Boisvert
+  *
+- * Copyright (C) 2013 Robert Boisvert
+- *
+- * This file is part of the mohqueue module for sip-router, a free SIP server.
++ * This file is part of the mohqueue module for Kamailio, a free SIP server.
+  *
+  * The mohqueue module is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
-- 
2.4.1
---
Unsubscribe:  alpine-aports+unsubscribe_at_lists.alpinelinux.org
Help:         alpine-aports+help_at_lists.alpinelinux.org
---
Received on Wed Jun 24 2015 - 15:33:20 GMT