- Local UAC related functions do not register the TMCB_LOCAL_COMPLETED and TMCB_LOCAL...
[sip-router] / modules / tm / uac.c
index e1b0703..55f86ad 100644 (file)
@@ -7,7 +7,7 @@
  * supported during client generation, in all other places
  * it is -- adding it should be simple
  *
  * supported during client generation, in all other places
  * it is -- adding it should be simple
  *
- * Copyright (C) 2001-2003 Fhg Fokus
+ * Copyright (C) 2001-2003 FhG Fokus
  *
  * This file is part of ser, a free SIP server.
  *
  *
  * This file is part of ser, a free SIP server.
  *
  * along with this program; if not, write to the Free Software 
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  * along with this program; if not, write to the Free Software 
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * ***************************************************
- *             IMPORTANT NOTE
- *
- *    All UACs but t_uac_dlg are being deprecated now
- *    and will be removed from future versions of TM
- *    module. Eliminate all dependancies on them asap.
- *    For backwards compatibility (NOT RECOMMENDED)
- *    turn off DEPRECATE_OLD_STUFF in defs.h. Similarly,
- *    there is a new FIFO UAC.
- *
- * ****************************************************
- *
  * History:
  * --------
  * History:
  * --------
- * 2003-02-27 FIFO/UAC now dumps reply -- good for CTD (jiri)
- * 2003-02-13  t_uac, t _uac_dlg, gethfblock, uri2proxy changed to use 
- *              proto & rb->dst (andrei)
- * 2003-01-29  scratchpad removed (jiri)
- * 2003-01-27  fifo:t_uac_dlg completed (jiri)
- * 2003-01-23  t_uac_dlg now uses get_out_socket (jiri)
+ *  2003-01-23  t_uac_dlg now uses get_out_socket (jiri)
+ *  2003-01-27  fifo:t_uac_dlg completed (jiri)
+ *  2003-01-29  scratchpad removed (jiri)
+ *  2003-02-13  t_uac, t _uac_dlg, gethfblock, uri2proxy changed to use 
+ *               proto & rb->dst (andrei)
+ *  2003-02-27  FIFO/UAC now dumps reply -- good for CTD (jiri)
+ *  2003-02-28  scratchpad compatibility abandoned (jiri)
+ *  2003-03-01  kr set through a function now (jiri)
+ *  2003-03-19  replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei)
+ *  2003-04-02  port_no_str does not contain a leading ':' anymore (andrei)
+ *  2003-07-08  appropriate log messages in check_params(...), 
+ *               call calculate_hooks if next_hop==NULL in t_uac (dcm) 
+ *  2003-10-24  updated to the new socket_info lists (andrei)
+ *  2003-12-03  completion filed removed from transaction and uac callbacks
+ *              merged in transaction callbacks as LOCAL_COMPLETED (bogdan)
+ *  2004-02-11  FIFO/CANCEL + alignments (hash=f(callid,cseq)) (uli+jiri)
+ *  2004-02-13  t->is_invite, t->local, t->noisy_ctimer replaced (bogdan)
+ *  2004-08-23  avp support in t_uac (bogdan)
+ *  2005-12-16  t_uac will set the new_cell timers to the default values,
+ *               fixes 0 fr_timer bug (andrei)
+ *  2006-08-11  t_uac uses dns failover until it finds a send socket (andrei)
+ *  2007-03-15  TMCB_ONSEND callbacks support added (andrei)
+ *  2007-03-23  TMCB_LOCAL_REQUEST_IN callbacks support (andrei)
+ *  2007-04-23  per dialog callbacks support (andrei)
+ *  2007-06-01  support for per transaction different retransmissions intervals
+ *              (andrei)
  */
 
  */
 
-
-#include "../../comp_defs.h"
-#include "defs.h"
-
-
-#include <stdlib.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <limits.h>
 #include <string.h>
 #include <string.h>
-#include "../../parser/parse_from.h"
-#include "../../parser/msg_parser.h"
+#include "../../mem/shm_mem.h"
 #include "../../dprint.h"
 #include "../../dprint.h"
-#include "../../ut.h"
-#include "../../hash_func.h"
-#include "../../md5utils.h"
-#include "../../mem/mem.h"
-#include "../../fifo_server.h"
-#include "../../error.h"
-#include "../../pt.h"
+#include "../../globals.h"
+#include "../../md5.h"
 #include "../../crc.h"
 #include "../../crc.h"
-#include "t_funcs.h"
-#include "config.h"
-#include "sip_msg.h"
+#include "../../ip_addr.h"
+#include "../../socket_info.h"
+#include "../../compiler_opt.h"
 #include "ut.h"
 #include "ut.h"
+#include "h_table.h"
+#include "t_hooks.h"
+#include "t_funcs.h"
 #include "t_msgbuilder.h"
 #include "t_msgbuilder.h"
+#include "callid.h"
 #include "uac.h"
 #include "uac.h"
+#include "t_stats.h"
 
 
-/* header fields which are explicitely processed and are not copied
- * from FIFO line-by-line
- */
-#define skip_hf(_hf) (((_hf)->type==HDR_FROM) || ((_hf)->type==HDR_TO) \
-       || ((_hf)->type==HDR_CALLID) || ((_hf)->type==HDR_CSEQ))
-
-/* Call-ID has the following form: <callid_nr>-<pid>@<ip>
- * callid_nr is initialized as a random number and continually
- * increases; -<pid>@<ip> is kept in callid_suffix
- */
-
-#define CALLID_SUFFIX_LEN (1 /* - */ + 5 /* pid */ \
-       + 42 /* embedded v4inv6 address can be looong '128.' */ \
-       + 2 /* parenthessis [] */ + 1 /* ZT 0 */ \
-       + 16 /* one never knows ;-) */ )
-#define CALLID_NR_LEN 20
-
-/* the character which separates random from constant part */
-#define CID_SEP        '-'
-
-/* length of FROM tags */
-#define FROM_TAG_LEN (MD5_LEN +1 /* - */ + CRC16_LEN)
-
-struct str_list {
-       str s;
-       struct str_list *next;
-};
 
 
-static unsigned long callid_nr;
-static char *callid_suffix;
-static int callid_suffix_len;
-static int rand_len;   /* number of chars to display max rand */
-static char callid[CALLID_NR_LEN+CALLID_SUFFIX_LEN];
+#define FROM_TAG_LEN (MD5_LEN + 1 /* - */ + CRC16_LEN) /* length of FROM tags */
 
 
-#ifndef DEPRECATE_OLD_STUFF
-char *uac_from="\"UAC Account\" <sip:uac@dev.null:9>";
-str uac_from_str;
-#endif
-
-static char from_tag[ FROM_TAG_LEN+1 ];
+static char from_tag[FROM_TAG_LEN + 1];
 
 
+char* uac_from = "sip:foo@foo.bar"; /* Module parameter */
 
 
+/* Enable/disable passing of provisional replies to FIFO applications */
+int pass_provisional_replies = 0;
 
 
-int uac_init() {
-
-       int i; 
-       unsigned long uli;
-       int rand_len_bits;
-       int rand_cnt; /* number of rands() to be long enough */
-       int rand_bits; /* length of rands() in bits */
+/*
+ * Initialize UAC
+ */
+int uac_init(void) 
+{
        str src[3];
        str src[3];
+       struct socket_info *si;
 
 
-       if (RAND_MAX<TABLE_ENTRIES) {
+       if (RAND_MAX < TABLE_ENTRIES) {
                LOG(L_WARN, "Warning: uac does not spread "
                LOG(L_WARN, "Warning: uac does not spread "
-                       "accross the whole hash table\n");
+                   "across the whole hash table\n");
        }
        }
-
-       /* calculate the initial call-id */
-
-       /* how many bits and chars do we need to display the 
-        * whole ULONG number */
-       for (rand_len_bits=0,uli=ULONG_MAX;uli;
-                       uli>>=1, rand_len_bits++ );
-       rand_len=rand_len_bits/4;
-       if (rand_len>CALLID_NR_LEN) {
-               LOG(L_ERR, "ERROR: Too small callid buffer\n");
+       /* on tcp/tls bind_address is 0 so try to get the first address we listen
+        * on no matter the protocol */
+       si=bind_address?bind_address:get_first_socket();
+       if (si==0){
+               LOG(L_CRIT, "BUG: uac_init: null socket list\n");
                return -1;
        }
 
                return -1;
        }
 
-       /* how long are the rand()s ? */
-       for (rand_bits=0,i=RAND_MAX;i;i>>=1,rand_bits++);
-       /* how many rands() fit in the ULONG ? */
-       rand_cnt=rand_len_bits / rand_bits;
-
-       /* now fill in the callid with as many random
-        * numbers as you can + 1 */
-       callid_nr=rand(); /* this is the + 1 */
-       while(rand_cnt) {
-               rand_cnt--;
-               callid_nr<<=rand_bits;
-               callid_nr|=rand();
-       }
-       callid_suffix=callid+rand_len;
-       DBG("CALLID initialization: %lx (len=%d)\n", 
-                       callid_nr, rand_len );
-       DBG("CALLID0=%0*lx\n", rand_len, callid_nr );
-
-
        /* calculate the initial From tag */
        /* calculate the initial From tag */
-
-       src[0].s="Long live SER server";
-       src[0].len=strlen(src[0].s);
-       src[1].s=sock_info[bind_idx].address_str.s;
-       src[1].len=strlen(src[1].s);
-       src[2].s=sock_info[bind_idx].port_no_str.s;
-       src[2].len=strlen(src[2].s);
-
-       MDStringArray( from_tag, src, 3 );
-       from_tag[MD5_LEN]=CID_SEP;
-
-#ifndef DEPRECATE_OLD_STUFF
-       uac_from_str.s = uac_from;
-       uac_from_str.len = strlen(uac_from);
-#endif
-
+       src[0].s = "Long live SER server";
+       src[0].len = strlen(src[0].s);
+       src[1].s = si->address_str.s;
+       src[1].len = strlen(src[1].s);
+       src[2].s = si->port_no_str.s;
+       src[2].len = strlen(src[2].s);
+
+       MDStringArray(from_tag, src, 3);
+       from_tag[MD5_LEN] = '-';
        return 1;
 }
 
 
        return 1;
 }
 
 
-int uac_child_init( int rank ) 
+/*
+ * Generate a From tag
+ */
+void generate_fromtag(str* tag, str* callid)
 {
 {
-       callid_suffix_len=snprintf(callid_suffix,CALLID_SUFFIX_LEN,
-                       "%c%d@%.*s", CID_SEP, my_pid(), 
-                       sock_info[bind_idx].address_str.len,
-                       sock_info[bind_idx].address_str.s );
-       if (callid_suffix_len==-1 || callid_suffix_len>=CALLID_SUFFIX_LEN) {
-               LOG(L_ERR, "ERROR: uac_child_init: buffer too small\n");
-               return -1;
-       }
-       DBG("DEBUG: callid_suffix: %s\n", callid_suffix );
-       return 1;
+            /* calculate from tag from callid */
+       crcitt_string_array(&from_tag[MD5_LEN + 1], callid, 1);
+       tag->s = from_tag; 
+       tag->len = FROM_TAG_LEN;
 }
 
 }
 
-#ifndef DEPRECATE_OLD_STUFF
-int t_uac( str *msg_type, str *dst, int proto, 
-       str *headers, str *body, str *from, 
-       transaction_cb completion_cb, void *cbp, 
-       dlg_t dlg)
-{
 
 
-       int r;
-       struct cell *new_cell;
-       struct proxy_l *proxy;
-       int branch;
-       int ret;
-       unsigned int req_len;
-       char *buf;
-       union sockaddr_union to;
-       struct socket_info* send_sock;
-       struct retr_buf *request;
-       str dummy_from;
-       str callid_s;
-       str fromtag;
-
-       /* make -Wall shut up */
-       ret=0;
-
-       proxy=uri2proxy( dst, proto );
-       if (proxy==0) {
-               ser_error=ret=E_BAD_ADDRESS;
-               LOG(L_ERR, "ERROR: t_uac: can't create a dst proxy\n");
-               goto done;
-       }
-       branch=0;
-       /* might go away -- we ignore it in send_pr_buffer anyway */
-       /* T->uac[branch].request.to_len=sizeof(union sockaddr_union); */
-       hostent2su(&to, &proxy->host, proxy->addr_idx, 
-               (proxy->port)?htons(proxy->port):htons(SIP_PORT));
-       /* send_sock=get_send_socket( &to, PROTO_UDP ); */
-       send_sock=get_out_socket( &to, proto );
-       if (send_sock==0) {
-               LOG(L_ERR, "ERROR: t_uac: no corresponding listening socket "
-                       "for af %d\n", to.s.sa_family );
-               ret=E_NO_SOCKET;
-               goto error00;
+/*
+ * Check value of parameters
+ */
+static inline int check_params(uac_req_t *uac_r, str* to, str* from)
+{
+       if (!uac_r || !uac_r->method || !to || !from) {
+               LOG(L_ERR, "check_params(): Invalid parameter value\n");
+               return -1;
        }
 
        }
 
-       /* update callid */
-       /* generate_callid(); */
-       callid_nr++;
-       r=snprintf(callid, rand_len+1, "%0*lx", rand_len, callid_nr );
-       if (r==-1 || r>=rand_len+1) {
-               LOG(L_CRIT, "BUG: SORRY, callid calculation failed\n");
-               goto error00;
+       if (!uac_r->method->s || !uac_r->method->len) {
+               LOG(L_ERR, "check_params(): Invalid request method\n");
+               return -2;
        }
        }
-       /* fix the ZT 0 */
-       callid[rand_len]=CID_SEP;
-       callid_s.s=callid;
-       callid_s.len=rand_len+callid_suffix_len;
-       DBG("DEBUG: sufix_len = %d\n",callid_suffix_len);
-       DBG("DEBUG: NEW CALLID:%.*s[%d]:\n", callid_s.len, callid_s.s 
-               , callid_s.len);
-       new_cell = build_cell( NULL ) ; 
-       if (!new_cell) {
-               ret=E_OUT_OF_MEM;
-               LOG(L_ERR, "ERROR: t_uac: short of cell shmem\n");
-               goto error00;
-       }
-       new_cell->completion_cb=completion_cb;
-       new_cell->cbp=cbp;
-       /* cbp is installed -- tell error handling bellow not to free it */
-       cbp=0;
-       new_cell->is_invite=msg_type->len==INVITE_LEN 
-               && memcmp(msg_type->s, INVITE, INVITE_LEN)==0;
-       new_cell->local=1;
-       new_cell->kr=REQ_FWDED;
-
 
 
-       request=&new_cell->uac[branch].request;
-       request->dst.to=to;
-       request->dst.send_sock=send_sock;
-       request->dst.proto=proto;
-       request->dst.proto_reserved1=0; /* no special connection required */
-
-       /* need to put in table to calculate label which is needed for printing */
-       LOCK_HASH(new_cell->hash_index);
-       insert_into_hash_table_unsafe(  new_cell );
-       UNLOCK_HASH(new_cell->hash_index);
-
-       if (from) dummy_from=*from; else { dummy_from.s=0; dummy_from.len=0; }
-       /* calculate from tag from callid */
-       crcitt_string_array(&from_tag[MD5_LEN+1], &callid_s, 1 );
-       fromtag.s=from_tag; fromtag.len=FROM_TAG_LEN;
-       buf=build_uac_request(  *msg_type, *dst, 
-                       dummy_from, fromtag,
-                       DEFAULT_CSEQ, callid_s, 
-                       *headers, *body, branch,
-                       new_cell, /* t carries hash_index, label, md5,
-                               uac[].send_sock and other pieces of
-                               information needed to print a message*/
-               &req_len );
-       if (!buf) {
-               ret=E_OUT_OF_MEM;
-               LOG(L_ERR, "ERROR: t_uac: short of req shmem\n");
-               goto error01;
-       }      
-       new_cell->method.s=buf;new_cell->method.len=msg_type->len;
-
-
-       request->buffer = buf;
-       request->buffer_len = req_len;
-       new_cell->nr_of_outgoings++;
-
-
-       proxy->tx++;
-       proxy->tx_bytes+=req_len;
-
-       if (SEND_BUFFER( request)==-1) {
-               LOG(L_ERR, "ERROR: t_uac: UAC sending to %.*s failed\n",
-                       dst->len, dst->s );
-               proxy->errors++;
-               proxy->ok=0;
+       if (!to->s || !to->len) {
+               LOG(L_ERR, "check_params(): Invalid To URI\n");
+               return -4;
        }
        }
-       start_retr( request );
 
 
-       /* success */
-       return 1;
-
-error01:
-       LOCK_HASH(new_cell->hash_index);
-       remove_from_hash_table_unsafe( new_cell );
-       UNLOCK_HASH(new_cell->hash_index);
-       free_cell(new_cell);
-error00:
-       free_proxy( proxy );
-       free( proxy );
-done: 
-       /* if we did not install cbp, release it now */
-       if (cbp) shm_free(cbp);
-       return ser_error=ret;
+       if (!from->s || !from->len) {
+               LOG(L_ERR, "check_params(): Invalid From URI\n");
+               return -5;
+       }
+       return 0;
 }
 }
-#endif
 
 
-static struct socket_info *uri2sock( str *uri, union sockaddr_union *to_su,
-                                                                        int proto )
+static inline unsigned int dlg2hash( dlg_t* dlg )
 {
 {
-       struct proxy_l *proxy;
-       struct socket_info* send_sock;
-
-       proxy = uri2proxy(uri, proto);
-       if (proxy == 0) {
-               ser_error = E_BAD_ADDRESS;
-               LOG(L_ERR, "ERROR: uri2sock: Can't create a dst proxy\n");
-               return 0;
-       }
-
-       hostent2su(to_su, &proxy->host, proxy->addr_idx, 
-                       (proxy->port) ? htons(proxy->port) : htons(SIP_PORT));
-       send_sock=get_out_socket(to_su, proto);
-       if (send_sock == 0) {
-               LOG(L_ERR, "ERROR: uri2sock: no corresponding socket for af %d\n", 
-                                               to_su->s.sa_family );
-               ser_error = E_NO_SOCKET;
-       }
-
+       str cseq_nr;
+       unsigned int hashid;
 
 
-       free_proxy(proxy);
-       free(proxy);
-       return send_sock;
+       cseq_nr.s=int2str(dlg->loc_seq.value, &cseq_nr.len);
+       hashid=hash(dlg->id.call_id, cseq_nr);
+       DBG("DEBUG: dlg2hash: %d\n", hashid);
+       return hashid;
 }
 }
-       
 
 
 
 
-/*
- * Send a request within a dialog
- * 
- * Some parameters are required, some are optional (i.e., ephemeral,
- * default or empty values are created if 0 is passed as parameter). The
- * optional parameters are typically used to set some header fields
- * to dialog-related values (as opposed to having them set to
- * ephemeral values).
- *
- * Required:
- * - msg ..   specifies type of message, such as "OPTIONS"
- * - ruri ..  specifies request URI; 
- * - from ..  value of From header field (if it already includes from tag, 
- *            the fromtag parameter MUST point to en empty string; if 
- *            fromtag is 0, an ephemeral tag is always appended)
- * - to ...   value of To header field (if it already includes to tag in it,
- *            or you do not wish to set a to-tag the totag parameter MUST be 0)
- * 
- * Optional:
- * - dst     transport destination (expressed as URI) -- if present,
- *           request is physically forwarded to address indicated in it,
- *           overriding the transport address in ruri; useful for use with 
- *           outbound proxies or loose routers (that is where the first 
- *           element of route set comes in)
- * - fromtag from HF tag -- dialog-less applications do not to set it (==0),
- *           in which case an ephemeral value is created; if fromtag present,
- *           its appended to the From header field; it may be also present 
- *           and point to an empty string -- that only makes sense if
- *           application includes the tag in From and does not care to
- *           separate the tag from the rest of header field
- * - totag   To HF tag; if 0, no to-tag is appended (unless it is already
- *           part of to)
- * - cid ..  callid; if 0, ephemeral value is created; transactions
- *           within a dialog need to set this value to dialog's callid
- * - cseq .. CSeq; if 0, default value (DEFAULT_CSEQ) is used; transactions
- *           within a dialog need to set this value to current local cseq,
- *           which grows continously with transactions sent
- * - headers .. block of header fields that will be included in the
- *           message. It MAY NOT include header fields already described
- *           in other parameters (From, to, cid, cseq) or created 
- *           automatically   (Content_length)   otherwise the parameter
- *           would appear multiple times. It MUST include all additional
- *           header fields required for a given SIP message, like Content-Type 
- *           for any messages including a body or Contact for INVITEs.
- * - body .. if present, body and Content-Length is appended to the 
- *           SIP message; Content-Type then needs to be present inside
- *           'headers' parameter
- * - cb ..   callback to be called when transaction completes; if none
- *           present, no callback will be called
- * - cbp ..  callback parameter -- value passed to callback function
- *           when called
- *
+/* WARNING: - dst_cell contains the created cell, but it is un-referenced
+ *            (before using it make sure you REF() it first)
+ *          - if  ACK (method==ACK), a cell will be created but it will not
+ *            be added in the hash table (should be either deleted by the 
+ *            caller) 
  */
  */
-int t_uac_dlg(str* msg,                     /* Type of the message - MESSAGE, OPTIONS etc. */
-             str* dst,                     /* Real destination (can be different than R-URI) */
-                 int proto,
-             str* ruri,                    /* Request-URI */
-             str* to,                      /* To - w/o tag*/
-             str* from,                    /* From - w/o tag*/
-             str* totag,                   /* To tag */
-             str* fromtag,                 /* From tag */
-             int* cseq,                    /* Variable holding CSeq */
-             str* cid,                     /* Call-ID */
-             str* headers,                 /* Optional headers including CRLF */
-             str* body,                    /* Message body */
-             transaction_cb completion_cb, /* Callback parameter */
-             void* cbp                     /* Callback pointer */
-             )
+static inline int t_uac_prepare(uac_req_t *uac_r, 
+               struct retr_buf **dst_req,
+               struct cell **dst_cell)
 {
 {
-
-       int r, branch, ret;
-       unsigned int req_len;
-       char *buf;
+       struct dest_info dst;
        struct cell *new_cell;
        struct cell *new_cell;
-       struct socket_info* send_sock;
        struct retr_buf *request;
        struct retr_buf *request;
-       str callid_s, ftag, tmp;
-       union sockaddr_union to_su;
-
-       /* make -Wall shut up */
-       ret=0;
-
-       /* check for invalid parameter */
-       if (!msg || !msg->s
-                               || !ruri || !ruri->s
-                               || !from || !from->s
-                               || !to || !to->s ) {
-               LOG(L_ERR, "ERROR: t_uac_dlg: invalid parameters\n");
-               ser_error = ret = E_INVALID_PARAMS;
-               goto done;
-       }
-
-       send_sock=uri2sock( dst? dst: ruri, &to_su, proto );
-       if (send_sock==0) {
-               LOG(L_ERR, "ERROR: t_uac_dlg: no socket found\n");
-               goto error00;
-       }
+       char* buf;
+        int buf_len, ret;
+       unsigned int hi;
+       int is_ack;
+       ticks_t lifetime;
+#ifdef USE_DNS_FAILOVER
+       struct dns_srv_handle dns_h;
+#endif
 
 
-       branch=0;
-            /* No Call-ID given, calculate it */
-       if (cid == 0) {
-               callid_nr++;
-               r = snprintf(callid, rand_len + 1, "%0*lx", rand_len, callid_nr);
-               if (r == -1 || r>=rand_len+1) {
-                       LOG(L_CRIT, "BUG: SORRY, callid calculation failed\n");
-                       goto error00;
+       ret=-1;
+       hi=0; /* make gcc happy */
+       /*if (dst_req) *dst_req = NULL;*/
+       is_ack = (((uac_r->method->len == 3) && (memcmp("ACK", uac_r->method->s, 3)==0)) ? 1 : 0);
+       
+       /*** added by dcm 
+        * - needed by external ua to send a request within a dlg
+        */
+       if (w_calculate_hooks(uac_r->dialog)<0 && !uac_r->dialog->hooks.next_hop)
+               goto error2;
+
+       if (!uac_r->dialog->loc_seq.is_set) {
+               /* this is the first request in the dialog,
+               set cseq to default value now - Miklos */
+               uac_r->dialog->loc_seq.value = DEFAULT_CSEQ;
+               uac_r->dialog->loc_seq.is_set = 1;
+       }
+
+       DBG("DEBUG:tm:t_uac: next_hop=<%.*s>\n",uac_r->dialog->hooks.next_hop->len,
+                       uac_r->dialog->hooks.next_hop->s);
+       /* it's a new message, so we will take the default socket */
+#ifdef USE_DNS_FAILOVER
+       if (use_dns_failover){
+               dns_srv_handle_init(&dns_h);
+               if ((uri2dst(&dns_h, &dst, 0, uac_r->dialog->hooks.next_hop, PROTO_NONE)==0)
+                               || (dst.send_sock==0)){
+                       dns_srv_handle_put(&dns_h);
+                       ser_error = E_NO_SOCKET;
+                       ret=ser_error;
+                       LOG(L_ERR, "t_uac: no socket found\n");
+                       goto error2;
+               }
+               dns_srv_handle_put(&dns_h); /* not needed anymore */
+       }else{
+               if ((uri2dst(0, &dst, 0, uac_r->dialog->hooks.next_hop, PROTO_NONE)==0) ||
+                               (dst.send_sock==0)){
+                       ser_error = E_NO_SOCKET;
+                       ret=ser_error;
+                       LOG(L_ERR, "t_uac: no socket found\n");
+                       goto error2;
                }
                }
-
-                    /* fix the ZT 0 */
-               callid[rand_len] = CID_SEP;
-               callid_s.s = callid;
-               callid_s.len = rand_len + callid_suffix_len;
        }
        }
+#else
+       if ((uri2dst(&dst, 0, uac_r->dialog->hooks.next_hop, PROTO_NONE)==0) ||
+                       (dst.send_sock==0)){
+               ser_error = E_NO_SOCKET;
+               ret=ser_error;
+               LOG(L_ERR, "t_uac: no socket found\n");
+               goto error2;
+       }
+#endif
 
        new_cell = build_cell(0); 
        if (!new_cell) {
 
        new_cell = build_cell(0); 
        if (!new_cell) {
-               ret = E_OUT_OF_MEM;
-               LOG(L_ERR, "ERROR: t_uac: short of cell shmem\n");
-               goto error00;
-       }
-
-       new_cell->completion_cb = completion_cb;
-       new_cell->cbp = cbp;
-
-       /* cbp is installed -- tell error handling bellow not to free it */
-       cbp = 0;
+               ret=E_OUT_OF_MEM;
+               LOG(L_ERR, "t_uac: short of cell shmem\n");
+               goto error2;
+       }
+       if (uac_r->method->len==INVITE_LEN && memcmp(uac_r->method->s, INVITE, INVITE_LEN)==0){
+               new_cell->flags |= T_IS_INVITE_FLAG;
+               new_cell->flags|=T_AUTO_INV_100 & (!tm_auto_inv_100 -1);
+               lifetime=tm_max_inv_lifetime;
+       }else
+               lifetime=tm_max_noninv_lifetime;
+       new_cell->flags |= T_IS_LOCAL_FLAG;
+       /* init timers hack, new_cell->fr_timer and new_cell->fr_inv_timer
+        * must be set, or else the fr will happen immediately
+        * we can't call init_new_t() because we don't have a sip msg
+        * => we'll ignore t_set_fr() or avp timer value and will use directly the
+        * module params fr_inv_timer and fr_timer -- andrei */
+       new_cell->fr_timeout=fr_timeout;
+       new_cell->fr_inv_timeout=fr_inv_timeout;
+       new_cell->end_of_life=get_ticks_raw()+lifetime;
+#ifdef TM_DIFF_RT_TIMEOUT
+       /* same as above for retransmission intervals */
+       new_cell->rt_t1_timeout=rt_t1_timeout;
+       new_cell->rt_t2_timeout=rt_t2_timeout;
+#endif
 
 
-       new_cell->is_invite = msg->len == INVITE_LEN && memcmp(msg->s, INVITE, INVITE_LEN) == 0;
-       new_cell->local= 1 ;
-       new_cell->kr = REQ_FWDED;
+       /* better reset avp list now - anyhow, it's useless from
+        * this point (bogdan) */
+       reset_avps();
 
 
-       request = &new_cell->uac[branch].request;
-       request->dst.to = to_su;
-       request->dst.send_sock = send_sock;
-       request->dst.proto = proto;
-       request->dst.proto_reserved1 = 0;
+       set_kr(REQ_FWDED);
 
 
-       /* need to put in table to calculate label which is needed for printing */
-       LOCK_HASH(new_cell->hash_index);
-       insert_into_hash_table_unsafe(new_cell);
-       UNLOCK_HASH(new_cell->hash_index);
+       request = &new_cell->uac[0].request;
+       
+       request->dst = dst;
 
 
-       if (fromtag == 0) {
-                    /* calculate from tag from callid */
-               crcitt_string_array(&from_tag[MD5_LEN + 1], (cid) ? (cid) : (&callid_s), 1);
-               ftag.s = from_tag; 
-               ftag.len = FROM_TAG_LEN;
+       if (!is_ack) {
+#ifdef TM_DEL_UNREF
+               INIT_REF(new_cell, 1); /* ref'ed only from the hash */
+#endif
+               hi=dlg2hash(uac_r->dialog);
+               LOCK_HASH(hi);
+               insert_into_hash_table_unsafe(new_cell, hi);
+               UNLOCK_HASH(hi);
        }
 
        }
 
-       buf = build_uac_request_dlg(msg, 
-                                   ruri,
-                                   to, 
-                                   from,
-                                   totag,
-                                   (fromtag) ? (fromtag) : (&ftag), 
-                                   (cseq) ? (*cseq) : DEFAULT_CSEQ, 
-                                   (cid) ? (cid) : (&callid_s), 
-                                   headers, 
-                                   body, 
-                                   branch,
-                                   new_cell,
-                                   &req_len,
-                                       send_sock);
+       buf = build_uac_req(uac_r->method, uac_r->headers, uac_r->body, uac_r->dialog, 0, new_cell,
+               &buf_len, &dst);
        if (!buf) {
        if (!buf) {
-               ret = E_OUT_OF_MEM;
-               LOG(L_ERR, "ERROR: t_uac: short of req shmem\n");
-               goto error01;
+               LOG(L_ERR, "t_uac: Error while building message\n");
+               ret=E_OUT_OF_MEM;
+               goto error1;
        }
        }
+
        new_cell->method.s = buf;
        new_cell->method.s = buf;
-       new_cell->method.len = msg->len;
+       new_cell->method.len = uac_r->method->len;
 
        request->buffer = buf;
 
        request->buffer = buf;
-       request->buffer_len = req_len;
+       request->buffer_len = buf_len;
        new_cell->nr_of_outgoings++;
 
        new_cell->nr_of_outgoings++;
 
-/*
-       proxy->tx++;
-       proxy->tx_bytes += req_len;
-*/
-
-       if (SEND_BUFFER(request) == -1) {
-               if (dst) {
-                       tmp = *dst;
-               } else {
-                       tmp = *ruri;
-               }
-               LOG(L_ERR, "ERROR: t_uac: UAC sending to \'%.*s\' failed\n", tmp.len, tmp.s);
-/*
-               proxy->errors++;
-               proxy->ok = 0;
-*/
+       /* Register the callbacks after everything is successful and nothing can fail.
+       Otherwise the callback parameter would be freed twise, once from TMCB_DESTROY,
+       and again because of the negative return code. */
+       if(uac_r->cb && insert_tmcb(&(new_cell->tmcb_hl), uac_r->cb_flags, *(uac_r->cb), uac_r->cbp)!=1){
+               ret=E_OUT_OF_MEM; 
+               LOG(L_ERR, "t_uac: short of tmcb shmem\n");
+               goto error1;
+       }
+       if (has_local_reqin_tmcbs())
+                       run_local_reqin_callbacks(new_cell, 0, 0);
+#ifdef DIALOG_CALLBACKS
+       run_trans_dlg_callbacks(uac_r->dialog, new_cell, request);
+#endif /* DIALOG_CALLBACKS */
+       if (dst_req) *dst_req = request;
+       if (dst_cell) *dst_cell = new_cell;
+       else if(is_ack && dst_req==0){
+               free_cell(new_cell);
        }
        
        }
        
-       start_retr(request);
-
-       /* success */
        return 1;
 
        return 1;
 
-error01:
-       LOCK_HASH(new_cell->hash_index);
-       remove_from_hash_table_unsafe(new_cell);
-       UNLOCK_HASH(new_cell->hash_index);
-       free_cell(new_cell);
-
-error00:
-/*
-       free_proxy(proxy);
-       free(proxy);
-*/
-
-done: 
-       /* if we did not install cbp, release it now */
-       if (cbp) shm_free(cbp);
-       return ser_error = ret;
+ error1:
+       if (!is_ack) {
+               LOCK_HASH(hi);
+               remove_from_hash_table_unsafe(new_cell);
+               UNLOCK_HASH(hi);
+#ifdef TM_DEL_UNREF
+               UNREF_FREE(new_cell);
+       }else
+#else
+       }
+#endif
+               free_cell(new_cell);
+error2:
+       return ret;
 }
 
 }
 
-
-static void fifo_callback( struct cell *t, struct sip_msg *reply,
-       int code, void *param)
+/*
+ * Prepare a message within a dialog
+ */
+int prepare_req_within(uac_req_t *uac_r,
+               struct retr_buf **dst_req)
 {
 {
-
-       char *filename;
-       str text;
-
-       DBG("DEBUG: fifo UAC completed with status %d\n", code);
-       if (!t->cbp) {
-               LOG(L_INFO, "INFO: fifo UAC completed with status %d\n", code);
-               return;
+       if (!uac_r || !uac_r->method || !uac_r->dialog) {
+               LOG(L_ERR, "req_within: Invalid parameter value\n");
+               goto err;
        }
 
        }
 
-       filename=(char *)(t->cbp);
-       if (reply==FAKED_REPLY) {
-               get_reply_status(&text,reply,code);
-               if (text.s==0) {
-                       LOG(L_ERR, "ERROR: fifo_callback: get_reply_status failed\n");
-                       fifo_reply(filename, "500 fifo_callback: get_reply_status failed\n");
-                       return;
-               }
-               fifo_reply(filename, "%.*s", text.len, text.s );
-               pkg_free(text.s);
-       } else {
-               text.s=reply->first_line.u.reply.status.s;
-               text.len=reply->len-(reply->first_line.u.reply.status.s-reply->buf);
-               fifo_reply(filename, "%.*s", text.len, text.s );
+       if (uac_r->dialog->state != DLG_CONFIRMED) {
+               LOG(L_ERR, "req_within: Dialog is not confirmed yet\n");
+               goto err;
        }
        }
-       DBG("DEBUG: fifo_callback sucesssfuly completed\n");
-}      
 
 
-#ifndef DEPRECATE_OLD_STUFF
-
-/* to be obsoleted in favor of fifo_uac_from */
-int fifo_uac( FILE *stream, char *response_file ) 
-{
-       str sm, sh, sb, sd; /* method, header, body, dst(outbound) */
-       char method[MAX_METHOD]; /* read buffers for these ... */
-       char header[MAX_HEADER];
-       char body[MAX_BODY];
-       char dst[MAX_DST];
-       char *shmem_file;
-       int fn_len;
-       int ret;
-       int sip_error;
-       char err_buf[MAX_REASON_LEN];
+       if ((uac_r->method->len == 3) && (!memcmp("ACK", uac_r->method->s, 3))) goto send;
+       if ((uac_r->method->len == 6) && (!memcmp("CANCEL", uac_r->method->s, 6))) goto send;
+       uac_r->dialog->loc_seq.value++; /* Increment CSeq */
+ send:
+       return t_uac_prepare(uac_r, dst_req, 0);
 
 
-       sm.s=method; sh.s=header; sb.s=body; sd.s=dst;
-       if (!read_line(method, MAX_METHOD, stream,&sm.len)||sm.len==0) {
-               /* line breaking must have failed -- consume the rest
-                  and proceed to a new request
-               */
-               LOG(L_ERR, "ERROR: fifo_uac: method expected\n");
-               fifo_reply(response_file, 
-                       "400 fifo_uac: method expected");
-               return 1;
-       }
-       DBG("DEBUG: fifo_uac: method: %.*s\n", sm.len, method );
-       if (!read_line(dst, MAX_DST, stream, &sd.len)||sd.len==0) {
-               fifo_reply(response_file, 
-                       "400 fifo_uac: destination expected\n");
-               LOG(L_ERR, "ERROR: fifo_uac: destination expected\n");
-               return 1;
-       }
-       DBG("DEBUG: fifo_uac:  dst: %.*s\n", sd.len, dst );
-       /* now read header fields line by line */
-       if (!read_line_set(header, MAX_HEADER, stream, &sh.len)) {
-               fifo_reply(response_file, 
-                       "400 fifo_uac: HFs expected\n");
-               LOG(L_ERR, "ERROR: fifo_uac: header fields expected\n");
-               return 1;
-       }
-       DBG("DEBUG: fifo_uac: header: %.*s\n", sh.len, header );
-       /* and eventually body */
-       if (!read_body(body, MAX_BODY, stream, &sb.len)) {
-               fifo_reply(response_file, 
-                       "400 fifo_uac: body expected\n");
-               LOG(L_ERR, "ERROR: fifo_uac: body expected\n");
-               return 1;
-       }
-       DBG("DEBUG: fifo_uac: body: %.*s\n", sb.len, body );
-       DBG("DEBUG: fifo_uac: EoL -- proceeding to transaction creation\n");
-       /* we got it all, initiate transaction now! */
-       if (response_file) {
-               fn_len=strlen(response_file)+1;
-               shmem_file=shm_malloc(fn_len);
-               if (shmem_file==0) {
-                       LOG(L_ERR, "ERROR: fifo_uac: no shmem\n");
-                       fifo_reply(response_file, 
-                               "500 fifo_uac: no shmem for shmem_file\n");
-                       return 1;
-               }
-               memcpy(shmem_file, response_file, fn_len );
-       } else {
-               shmem_file=0;
-       }
-       ret=t_uac(&sm,&sd, PROTO_UDP,&sh,&sb, 0 /* default from */,
-               fifo_callback,shmem_file,0 /* no dialog */);
-       if (ret>0) {
-               if (err2reason_phrase(ret, &sip_error, err_buf,
-                               sizeof(err_buf), "FIFO/UAC" ) > 0 ) 
-               {
-                       fifo_reply(response_file, "500 FIFO/UAC error: %d\n",
-                               ret );
-               } else {
-                       fifo_reply(response_file, err_buf );
-               }
-       }
-       return 1;
+ err:
+       /* if (cbp) shm_free(cbp); */
+       /* !! never free cbp here because if t_uac_prepare fails, cbp is not freed
+        * and thus caller has no chance to discover if it is freed or not !! */
+       return -1;
 }
 
 }
 
-
-/* syntax:
-
-       :t_uac_from:[file] EOL
-       method EOL
-       [from] EOL (if none, server's default from is taken)
-       dst EOL (put in r-uri and To)
-       [CR-LF separated HFs]* EOL
-       EOL
-       [body] EOL
-       EOL
-
-*/
-
-int fifo_uac_from( FILE *stream, char *response_file ) 
+static inline void send_prepared_request_impl(struct retr_buf *request, int retransmit)
 {
 {
-       char method[MAX_METHOD];
-       char header[MAX_HEADER];
-       char body[MAX_BODY];
-       char dst[MAX_DST];
-       char from[MAX_FROM];
-       str sm, sh, sb, sd, sf;
-       char *shmem_file;
-       int fn_len;
-       int ret;
-       int sip_error;
-       char err_buf[MAX_REASON_LEN];
-       int err_ret;
-
-       sm.s=method; sh.s=header; sb.s=body; sd.s=dst;sf.s=from;
-
-       if (!read_line(method, MAX_METHOD, stream,&sm.len)||sm.len==0) {
-               /* line breaking must have failed -- consume the rest
-                  and proceed to a new request
-               */
-               LOG(L_ERR, "ERROR: fifo_uac: method expected\n");
-               fifo_reply(response_file, 
-                       "400 fifo_uac: method expected");
-               return 1;
-       }
-       DBG("DEBUG: fifo_uac: method: %.*s\n", sm.len, method );
-       if (!read_line(from, MAX_FROM, stream, &sf.len)) {
-               fifo_reply(response_file, 
-                       "400 fifo_uac: from expected\n");
-               LOG(L_ERR, "ERROR: fifo_uac: from expected\n");
-               return 1;
-       }
-       DBG("DEBUG: fifo_uac:  from: %.*s\n", sf.len, from);
-       if (!read_line(dst, MAX_DST, stream, &sd.len)||sd.len==0) {
-               fifo_reply(response_file, 
-                       "400 fifo_uac: destination expected\n");
-               LOG(L_ERR, "ERROR: fifo_uac: destination expected\n");
-               return 1;
-       }
-       DBG("DEBUG: fifo_uac:  dst: %.*s\n", sd.len, dst );
-       /* now read header fields line by line */
-       if (!read_line_set(header, MAX_HEADER, stream, &sh.len)) {
-               fifo_reply(response_file, 
-                       "400 fifo_uac: HFs expected\n");
-               LOG(L_ERR, "ERROR: fifo_uac: header fields expected\n");
-               return 1;
-       }
-       DBG("DEBUG: fifo_uac: header: %.*s\n", sh.len, header );
-       /* and eventually body */
-       if (!read_body(body, MAX_BODY, stream, &sb.len)) {
-               fifo_reply(response_file, 
-                       "400 fifo_uac: body expected\n");
-               LOG(L_ERR, "ERROR: fifo_uac: body expected\n");
-               return 1;
-       }
-       DBG("DEBUG: fifo_uac: body: %.*s\n", sb.len, body );
-       DBG("DEBUG: fifo_uac: EoL -- proceeding to transaction creation\n");
-       /* we got it all, initiate transaction now! */
-       if (response_file) {
-               fn_len=strlen(response_file)+1;
-               shmem_file=shm_malloc(fn_len);
-               if (shmem_file==0) {
-                       LOG(L_ERR, "ERROR: fifo_uac: no shmem\n");
-                       fifo_reply(response_file, 
-                               "500 fifo_uac: no memory for shmem_file\n");
-                       return 1;
-               }
-               memcpy(shmem_file, response_file, fn_len );
-       } else {
-               shmem_file=0;
-       }
-       /* HACK: there is yet a shortcoming -- if t_uac fails, callback
-          will not be triggered and no feedback will be printed
-          to shmem_file
-       */
-       ret=t_uac(&sm,&sd, PROTO_UDP, &sh,&sb, sf.len==0 ? 0: &sf/*default from*/,
-               fifo_callback,shmem_file,0 /* no dialog */);
-       if (ret<=0) {
-               err_ret=err2reason_phrase(ret, &sip_error, err_buf,
-                               sizeof(err_buf), "FIFO/UAC" ) ;
-               if (err_ret > 0 )
-               {
-                       fifo_reply(response_file, "%d %s", sip_error, err_buf );
-               } else {
-                       fifo_reply(response_file, "500 FIFO/UAC error: %d\n",
-                               ret );
-               }
+       if (SEND_BUFFER(request) == -1) {
+               LOG(L_ERR, "t_uac: Attempt to send to precreated request failed\n");
        }
        }
-       return 1;
-
-}
-
+#ifdef TMCB_ONSEND
+       else if (unlikely(has_tran_tmcbs(request->my_T, TMCB_REQUEST_SENT)))
+               /* we don't know the method here */
+                       run_onsend_callbacks(TMCB_REQUEST_SENT, request, 0, 0,
+                                                                       TMCB_LOCAL_F);
 #endif
 #endif
-
-static struct str_list *new_str(char *s, int len, struct str_list **last, int *total)
-{
-       struct str_list *new;
-       new=pkg_malloc(sizeof(struct str_list));
-       if (!new) {
-               LOG(L_ERR, "ERROR: get_hfblock: not enough mem\n");
-               return 0;
-       }
-       new->s.s=s;
-       new->s.len=len;
-       new->next=0;
-
-       (*last)->next=new;
-       *last=new;
-       *total+=len;
-
-       return new;
+       
+       if (retransmit && (start_retr(request)!=0))
+               LOG(L_CRIT, "BUG: t_uac: failed to start retr. for %p\n", request);
 }
 
 }
 
-
-static char *get_hfblock(str *uri, struct hdr_field *hf, int *l, int proto) 
+void send_prepared_request(struct retr_buf *request)
 {
 {
-       struct str_list sl, *last, *new, *i, *foo;
-       int hf_avail, frag_len, total_len;
-       char *begin, *needle, *dst, *ret, *d;
-       str *sock_name, *portname;
-       union sockaddr_union to_su;
-       struct socket_info* send_sock;
-
-       ret=0; /* pesimist: assume failure */
-       total_len=0;
-       last=&sl;
-       last->next=0;
-       portname=sock_name=0;
-
-       for (; hf; hf=hf->next) {
-               if (skip_hf(hf)) continue;
-
-               begin=needle=hf->name.s; 
-               hf_avail=hf->len;
-
-               /* substitution loop */
-               while(hf_avail) {
-                       d=memchr(needle, SUBST_CHAR, hf_avail);
-                       if (!d || d+1>=needle+hf_avail) { /* nothing to substitute */
-                               new=new_str(begin, hf_avail, &last, &total_len); 
-                               if (!new) goto error;
-                               break;
-                       } else {
-                               frag_len=d-begin;
-                               d++; /* d not at the second substitution char */
-                               switch(*d) {
-                                       case SUBST_CHAR:        /* double SUBST_CHAR: IP */
-                                               /* string before substitute */
-                                               new=new_str(begin, frag_len, &last, &total_len); 
-                                               if (!new) goto error;
-                                               /* substitute */
-                                               if (!sock_name) {
-                                                       send_sock=uri2sock( uri, &to_su, proto );
-                                                       if (!send_sock) {
-                                                               LOG(L_ERR, "ERROR: get_hf_block: send_sock failed\n");
-                                                               goto error;
-                                                       }
-                                                       sock_name=&send_sock->address_str;
-                                                       portname=&send_sock->port_no_str;
-                                               }
-                                               new=new_str(sock_name->s, sock_name->len,
-                                                               &last, &total_len );
-                                               if (!new) goto error;
-                                               new=new_str(portname->s, portname->len,
-                                                               &last, &total_len );
-                                               if (!new) goto error;
-                                               /* keep going ... */
-                                               begin=needle=d+1;hf_avail-=frag_len+2;
-                                               continue;
-                                       default:
-                                               /* no valid substitution char -- keep going */
-                                               hf_avail-=frag_len+1;
-                                               needle=d;
-                               }
-                       } /* possible substitute */
-               } /* substitution loop */
-               /* proceed to next header */
-               /* new=new_str(CRLF, CRLF_LEN, &last, &total_len );
-               if (!new) goto error; */
-               DBG("DEBUG: get_hf_block: one more hf processed\n");
-       } /* header loop */
-
-
-       /* construct a single header block now */
-       ret=pkg_malloc(total_len);
-       if (!ret) {
-               LOG(L_ERR, "ERROR: get_hf_block no pkg mem for hf block\n");
-               goto error;
-       }
-       i=sl.next;
-       dst=ret;
-       while(i) {
-               foo=i;
-               i=i->next;
-               memcpy(dst, foo->s.s, foo->s.len);
-               dst+=foo->s.len;
-               pkg_free(foo);
-       }
-       *l=total_len;
-       return ret;
-
-error:
-       i=sl.next;
-       while(i) {
-               foo=i;
-               i=i->next;
-               pkg_free(foo);
-       }
-       *l=0;
-       return 0;
+       send_prepared_request_impl(request, 1 /* retransmit */);
 }
 
 }
 
-static void fifo_uac_error(char *reply_fifo, int code, char *msg)
+/*
+ * Send a request using data from the dialog structure
+ */
+int t_uac(uac_req_t *uac_r)
 {
 {
-       LOG(L_ERR, "ERROR: fifo_uac: %s\n", msg ); 
-       fifo_reply(reply_fifo, "%d fifo_uac: %s", code, msg);
+       struct retr_buf *request;
+       struct cell *cell;
+       int ret;
+       int is_ack;
+
+       ret = t_uac_prepare(uac_r, &request, &cell);
+       if (ret < 0) return ret;
+       is_ack = (uac_r->method->len == 3) && (memcmp("ACK", uac_r->method->s, 3)==0) ? 1 : 0;
+       send_prepared_request_impl(request, !is_ack /* retransmit */);
+       if (cell && is_ack)
+               free_cell(cell);
+       return ret;
 }
 
 }
 
-/* syntax:
-
-       :t_uac_dlg:[file] EOL
-       method EOL
-       r-uri EOL 
-       dst EOL                                 // ("." if no outbound server used)
-                                                       // must be used with dialogs/lr
-       <EOL separated HFs>+    // From and To must be present at least;
-                                                       // dialog-apps must include tag in From
-                                                       // (an ephemeral is appended otherwise)
-                                                       // and supply CSeq/CallId
-       .[EOL]
-       [body] 
-       .EOL
-
-
-       there is also the possibility to have server placed its
-    hostname:portnumber in header fields -- just put double
-       exclamation mark in any of the optional header fields
-       (i.e., any but From/To/CallID,CSeq), they will be 
-       substituted hn:pn
-
-Example:
-
-sc fifo t_uac_dlg MESSAGE sip:joe@192.168.2.1 \
-       . \ # no outbound proxy
-       'From:sender@iptel.org;tagd=123'  \ # no to-tag -> ephemeral
-       'To:sender@iptel.org' \
-       'Foo: sip:user@!! '  \ # expansion here
-       'CSEQ: 11 MESSAGE   ' \
-       . \ # EoH
-       .       # empty body
----
-U 192.168.2.16:5060 -> 192.168.2.1:5060
-MESSAGE sip:joe@192.168.2.1 SIP/2.0..
-Via: SIP/2.0/UDP 192.168.2.16;branch=z9hG4bK760c.922ea6a1.0..
-To: sender@iptel.org..
-From: sender@iptel.org;tagd=123;tag=5405e669bc2980663aed2624dc31396f-fa77..
-CSeq: 11 MESSAGE..
-Call-ID: e863bf56-22255@192.168.2.16..
-Content-Length: 0..
-User-Agent: Sip EXpress router (0.8.11pre4-tcp1-locking (i386/linux))..
-Foo: sip:user@192.168.2.16:5060..
-..
-
-
-*/
-
-int fifo_uac_dlg( FILE *stream, char *response_file ) 
+/*
+ * Send a request using data from the dialog structure
+ * ret_index and ret_label will identify the new cell
+ */
+int t_uac_with_ids(uac_req_t *uac_r,
+       unsigned int *ret_index, unsigned int *ret_label)
 {
 {
-       char method_buf[MAX_METHOD];
-       char ruri_buf[MAX_URI_SIZE];
-       char outbound_buf[MAX_URI_SIZE];
-       char header_buf[MAX_HEADER]; 
-       char body_buf[MAX_BODY]; 
-       str method, ruri, outbound, header, body;
-       str hfb; /* header field block */
-       struct sip_uri parsed_ruri, parsed_outbound;
-       str dummy_empty;
-       int fromtag;
-       int cseq;
-       struct cseq_body *parsed_cseq;
-       int i;
-       char c;
-       struct to_body *parsed_from;
-
-
-       char *shmem_file;
-       int fn_len;
+       struct retr_buf *request;
+       struct cell *cell;
        int ret;
        int ret;
-       int sip_error;
-       char err_buf[MAX_REASON_LEN];
-       int err_ret;
-       struct sip_msg faked_msg;
-
-
-       if (!read_line(method_buf, MAX_METHOD, stream,&method.len)
-                                       ||method.len==0) {
-               /* line breaking must have failed -- consume the rest
-                  and proceed to a new request
-               */
-               fifo_uac_error(response_file, 400, "method expected");
-               return 1;
-       }
-       method.s=method_buf;
-       DBG("DEBUG: fifo_uac: method: %.*s\n", method.len, method.s );
-
-       if (!read_line(ruri_buf, MAX_URI_SIZE, stream, &ruri.len)
-                                       || ruri.len==0) {
-               fifo_uac_error(response_file, 400, "ruri expected");
-               return 1;
-       }
-       if (parse_uri(ruri_buf, ruri.len, &parsed_ruri) < 0 ) {
-               fifo_uac_error(response_file, 400, "ruri invalid\n");
-               return 1;
-       }
-       ruri.s=ruri_buf;
-       DBG("DEBUG: fifo_uac:  ruri: %.*s\n", ruri.len, ruri.s);
-
-       if (!read_line(outbound_buf, MAX_URI_SIZE, stream, &outbound.len)
-                                       ||outbound.len==0) {
-               fifo_uac_error(response_file, 400, "outbound address expected\n");
-               return 1;
-       }
-       if (outbound.len==1 && outbound_buf[0]=='.' ) {
-               DBG("DEBUG: fifo_uac: outbound empty\n");
-               outbound.s=0; outbound.len=0;
-       } else if (parse_uri(outbound_buf, outbound.len, 
-                                                       &parsed_outbound) < 0 ) {
-               fifo_uac_error(response_file, 400, "outbound uri invalid\n");
-               return 1;
+       int is_ack;
+
+       ret = t_uac_prepare(uac_r, &request, &cell);
+       if (ret < 0) return ret;
+       is_ack = (uac_r->method->len == 3) && (memcmp("ACK", uac_r->method->s, 3)==0) ? 1 : 0;
+       send_prepared_request_impl(request, !is_ack /* retransmit */);
+       if (is_ack) {
+               if (cell) free_cell(cell);
+               if (ret_index && ret_label)
+                       *ret_index = *ret_label = 0;
        } else {
        } else {
-               outbound.s=outbound_buf;
-               DBG("DEBUG: fifo_uac:  dst: %.*s\n", outbound.len, outbound.s);
+               if (ret_index && ret_label) {
+                       *ret_index = cell->hash_index;
+                       *ret_label = cell->label;
+               }
        }
        }
+       return ret;
+}
 
 
-
-       /* now read and parse header fields */
-       if (!read_line_set(header_buf, MAX_HEADER, stream, &header.len)
-                                       || header.len==0 ) {
-               fifo_uac_error(response_file, 400, "HFs expected");
-               return 1;
-       }
-       header.s=header_buf;
-       DBG("DEBUG: fifo_uac: header: %.*s\n", header.len, header.s );
-       /* use SIP parser to look at what is in the FIFO request */
-       memset(&faked_msg, 0, sizeof(struct sip_msg));
-       faked_msg.len=header.len; 
-       faked_msg.buf=faked_msg.unparsed=header_buf;
-#ifdef SCRATCH
-       faked_msg.orig=faked_msg.buf;
-#endif
-       if (parse_headers(&faked_msg, HDR_EOH, 0)==-1 ) {
-                       DBG("DEBUG: fifo_uac: parse_headers failed\n");
-                       fifo_uac_error(response_file, 400, "HFs unparseable");
-                       goto error;
+/*
+ * Send a message within a dialog
+ */
+int req_within(uac_req_t *uac_r)
+{
+       if (!uac_r || !uac_r->method || !uac_r->dialog) {
+               LOG(L_ERR, "req_within: Invalid parameter value\n");
+               goto err;
        }
        }
-       DBG("DEBUG: fifo_uac: parse_headers succeeded\n");
 
 
-       /* and eventually body */
-       if (!read_body(body_buf, MAX_BODY, stream, &body.len)) {
-               fifo_uac_error(response_file, 400, "body expected");
-               goto error;
-       }
-       body.s=body_buf;
-       DBG("DEBUG: fifo_uac: body: %.*s\n", body.len, body.s );
+       if ((uac_r->method->len == 3) && (!memcmp("ACK", uac_r->method->s, 3))) goto send;
+       if ((uac_r->method->len == 6) && (!memcmp("CANCEL", uac_r->method->s, 6))) goto send;
+       uac_r->dialog->loc_seq.value++; /* Increment CSeq */
+ send:
+       return t_uac(uac_r);
 
 
+ err:
+       /* callback parameter must be freed outside of tm module
+       if (cbp) shm_free(cbp); */
+       return -1;
+}
 
 
-       /* at this moment, we collected all the things we got, let's
-        * verify user has not forgotten something */
-       if (body.len && !faked_msg.content_type) {
-               fifo_uac_error(response_file, 400, "Content_type missing");
-               goto error;
-       }
-       if (body.len && faked_msg.content_length) {
-               fifo_uac_error(response_file, 400, "Content_length disallowed");
-               goto error;
-       }
-       if (!faked_msg.to) {
-               fifo_uac_error(response_file, 400, "To missing");
-               goto error;
-       }
-       if (!faked_msg.from) {
-               fifo_uac_error(response_file, 400, "From missing");
-               goto error;
-       }
-       /* we also need to know if there is from-tag and add it otherwise */
-       if (parse_from_header(&faked_msg)<0) {
-               fifo_uac_error(response_file, 400, "Error in From");
-               goto error;
-       }
-       parsed_from=(struct to_body*)faked_msg.from->parsed;
-       fromtag=parsed_from->tag_value.s &&
-                       parsed_from->tag_value.len;
-       cseq=0;
-       if (faked_msg.cseq && (parsed_cseq=get_cseq(&faked_msg))) {
-               for (i=0; i<parsed_cseq->number.len; i++ ) {
-                       c=parsed_cseq->number.s[i];
-                       if (c>='0' && c<'9' ) cseq=cseq*10+c-'0';
-                       else {
-                               fifo_uac_error(response_file, 400, "non-nummerical CSeq");
-                               goto error;
-                       }
-               }
-               if (parsed_cseq->method.len!=method.len 
-                               || memcmp(parsed_cseq->method.s, method.s, method.len)!=0) {
-                       fifo_uac_error(response_file, 400, "CSeq method mismatch");
-                       goto error;
-               }
-       }
 
 
-       hfb.s=get_hfblock(outbound.len ? &outbound : &ruri, 
-                                       faked_msg.headers, &hfb.len, PROTO_UDP);
-       if (!hfb.s) {
-               fifo_uac_error(response_file, 500, "no mem for hf block");
-               goto error;
-       }
+/*
+ * Send an initial request that will start a dialog
+ * WARNING: writes uac_r->dialog
+ */
+int req_outside(uac_req_t *uac_r, str* to, str* from)
+{
+       str callid, fromtag;
 
 
+       if (check_params(uac_r, to, from) < 0) goto err;
+       
+       generate_callid(&callid);
+       generate_fromtag(&fromtag, &callid);
 
 
-       DBG("DEBUG: fifo_uac: EoL -- proceeding to transaction creation\n");
-       /* we got it all, initiate transaction now! */
-       if (response_file) {
-               fn_len=strlen(response_file)+1;
-               shmem_file=shm_malloc(fn_len);
-               if (shmem_file==0) {
-                       fifo_uac_error(response_file, 500, "no shmem");
-                       goto error01;
-               }
-               memcpy(shmem_file, response_file, fn_len );
-       } else {
-               shmem_file=0;
+       if (new_dlg_uac(&callid, &fromtag, DEFAULT_CSEQ, from, to, &uac_r->dialog) < 0) {
+               LOG(L_ERR, "req_outside(): Error while creating new dialog\n");
+               goto err;
        }
        }
-       /* HACK: there is yet a shortcoming -- if t_uac fails, callback
-          will not be triggered and no feedback will be printed
-          to shmem_file
-       */
-       dummy_empty.s=0; dummy_empty.len=0;
-       ret=t_uac_dlg( &method, 
-               outbound.len ? &outbound: 0,
-               PROTO_UDP,
-               &ruri, 
-               &faked_msg.to->body,    /* possibly w/to-tag in it */
-               &faked_msg.from->body,
-               &dummy_empty,                   /* if present, to-tag passed in to */
-               fromtag ?                               /* if fromtag present, ... */
-                       &dummy_empty:           /* ... pass it in from ... */
-                       0,                                      /* use ephemeral otherwise */
-               cseq ? &cseq : 0,
-               faked_msg.callid ?
-                       &faked_msg.callid->body:
-                       0,
-               &hfb,                                           /* headers -- TBD */
-               &body,
-               fifo_callback, shmem_file );
-
 
 
-       if (ret<=0) {
-               err_ret=err2reason_phrase(ret, &sip_error, err_buf,
-                               sizeof(err_buf), "FIFO/UAC" ) ;
-               if (err_ret > 0 )
-               {
+       return t_uac(uac_r);
 
 
-                       fifo_uac_error(response_file, sip_error, err_buf);
-               } else {
-                       fifo_uac_error(response_file, 500, "FIFO/UAC error" );
-#ifdef _OBSO
-                       fifo_reply(response_file, "500 FIFO/UAC error: %d\n",
-                               ret );
-#endif
-               }
-       }
+ err:
+       /* callback parameter must be freed outside of tm module
+       if (cbp) shm_free(cbp); */
+       return -1;
+}
 
 
-error01:
-       pkg_free(hfb.s);
 
 
-error:
-       /* free_sip_msg(&faked_msg); */
-       if (faked_msg.headers) free_hdr_field_lst(faked_msg.headers);
-       return 1;
+/*
+ * Send a transactional request, no dialogs involved
+ * WARNING: writes uac_r->dialog
+ */
+int request(uac_req_t *uac_r, str* ruri, str* to, str* from, str *next_hop)
+{
+       str callid, fromtag;
+       dlg_t* dialog;
+       int res;
+
+       if (check_params(uac_r, to, from) < 0) goto err;
+
+       generate_callid(&callid);
+       generate_fromtag(&fromtag, &callid);
+
+       if (new_dlg_uac(&callid, &fromtag, DEFAULT_CSEQ, from, to, &dialog) < 0) {
+               LOG(L_ERR, "request(): Error while creating temporary dialog\n");
+               goto err;
+       }
+
+       if (ruri) {
+               dialog->rem_target.s = ruri->s;
+               dialog->rem_target.len = ruri->len;
+               /* hooks will be set from w_calculate_hooks */
+       }
+
+       if (next_hop) dialog->dst_uri = *next_hop;
+       w_calculate_hooks(dialog);
+
+       /* WARNING:
+        * to be clean it should be called 
+        *   set_dlg_target(dialog, ruri, next_hop);
+        * which sets both uris if given [but it duplicates them in shm!]
+        *
+        * but in this case the _ruri parameter in set_dlg_target
+        * must be optional (it is needed now) and following hacks
+        *   dialog->rem_target.s = 0;
+        *   dialog->dst_uri.s = 0;
+        * before freeing dialog here must be removed
+        */
+       uac_r->dialog = dialog;
+       res = t_uac(uac_r);
+       dialog->rem_target.s = 0;
+       dialog->dst_uri.s = 0;
+       free_dlg(dialog);
+       uac_r->dialog = 0;
+       return res;
+
+ err:
+       /* callback parameter must be freed outside of tm module
+       if (cp) shm_free(cp); */
+       return -1;
 }
 }