core: move redundant and identical implementation of a str_list type to core
[sip-router] / modules_k / tmx / t_mi.c
1 /*
2  * $Id: mi.c 5299 2008-12-04 18:12:33Z henningw $
3  *
4  * Header file for TM MI functions
5  *
6  * Copyright (C) 2001-2003 FhG Fokus
7  * Copyright (C) 2006 Voice Sistem SRL
8  *
9  * This file is part of Kamailio, a free SIP server.
10  *
11  * Kamailio is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version
15  *
16  * Kamailio is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License 
22  * along with this program; if not, write to the Free Software 
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  *
25  * History:
26  * --------
27  *  2006-12-04  created (bogdan)
28  */
29
30 /*! \file
31  * \brief TM :: MI functions
32  *
33  * \ingroup tm
34  * - Module: \ref tm
35  */
36
37 #include <stdlib.h>
38 #include "../../parser/parse_from.h"
39 #include "../../modules/tm/ut.h"
40 #include "../../lib/kmi/mi.h"
41 #include "../../str_list.h"
42 #include "tmx_mod.h"
43
44
45 /*! Which header fields should be skipped */
46 #define skip_hf(_hf) \
47         (((_hf)->type == HDR_FROM_T)  || \
48         ((_hf)->type == HDR_TO_T)     || \
49         ((_hf)->type == HDR_CALLID_T) || \
50         ((_hf)->type == HDR_CSEQ_T))
51
52
53 /*!
54  * \brief Convert a URI into socket address.
55  *
56  * Convert a URI into a socket address. Create a temporary proxy.
57  * \param uri input URI
58  * \param to_su target structure
59  * \param proto protocol
60  * \return choosen protocol
61  */
62 static inline int uri2su(str *uri, union sockaddr_union *to_su, int proto)
63 {
64         struct proxy_l *proxy;
65
66         proxy = uri2proxy(uri, proto);
67         if (!proxy) {
68                 ser_error = E_BAD_ADDRESS;
69                 LM_ERR("failed create a dst proxy\n");
70                 return -1;
71         }
72
73         hostent2su(to_su, &proxy->host, proxy->addr_idx, 
74                 (proxy->port) ? proxy->port : SIP_PORT);
75         proto = proxy->proto;
76
77         free_proxy(proxy);
78         pkg_free(proxy);
79         return proto;
80 }
81
82 /* should be replaced by tm's uri2dst instead */
83 static inline struct socket_info *uri2sock(struct sip_msg* msg, str *uri,
84                                                                         union sockaddr_union *to_su, int proto)
85 {
86         struct socket_info* send_sock;
87
88         if ( (proto=uri2su(uri, to_su, proto))==-1 )
89                 return 0;
90
91         send_sock = get_send_socket(msg, to_su, proto);
92         if (!send_sock) {
93                 LM_ERR("no corresponding socket for af %d\n", to_su->s.sa_family);
94                 ser_error = E_NO_SOCKET;
95         }
96
97         return send_sock;
98 }
99
100
101 /************** Helper functions (from previous FIFO impl) *****************/
102
103 /*!
104  * \brief Check if the request pushed via MI is correctly formed
105  *
106  * Check if the request pushed via MI is correctly formed. Test if
107  * necessary SIP header fileds are included, could be parsed and the
108  * CSEQ is correct.
109  * \param msg SIP message
110  * \param method SIP method
111  * \param body SIP body
112  * \param cseq SIP CSEQ value
113  * \param callid SIP callid, optional
114  * \return zero on success, or a mi_root with an error message included otherwise
115  */
116 static inline struct mi_root* mi_check_msg(struct sip_msg* msg, str* method,
117                                                                                 str* body, int* cseq, str* callid)
118 {
119         struct cseq_body *parsed_cseq;
120
121         if (body && body->len && !msg->content_type)
122                 return init_mi_tree( 400, "Content-Type missing", 19);
123
124         if (body && body->len && msg->content_length)
125                 return init_mi_tree( 400, "Content-Length disallowed", 24);
126
127         if (!msg->to)
128                 return init_mi_tree( 400, "To missing", 10);
129
130         if (!msg->from)
131                 return init_mi_tree( 400, "From missing", 12);
132
133         /* we also need to know if there is from-tag and add it otherwise */
134         if (parse_from_header(msg) < 0)
135                 return init_mi_tree( 400, "Error in From", 13);
136
137         if (msg->cseq && (parsed_cseq = get_cseq(msg))) {
138                 if (str2int( &parsed_cseq->number, (unsigned int*)cseq)!=0)
139                         return init_mi_tree( 400, "Bad CSeq number", 15);
140
141                 if (parsed_cseq->method.len != method->len
142                 || memcmp(parsed_cseq->method.s, method->s, method->len) !=0 )
143                         return init_mi_tree( 400, "CSeq method mismatch", 20);
144         } else {
145                 *cseq = -1;
146         }
147
148         if (msg->callid) {
149                 callid->s = msg->callid->body.s;
150                 callid->len = msg->callid->body.len;
151         } else {
152                 callid->s = 0;
153                 callid->len = 0;
154         }
155
156         return 0;
157 }
158
159
160 /*!
161  * \brief Convert a header field block to char array
162  *
163  * Convert a header field block to char array, allocated in
164  * pkg_mem.
165  * \param uri SIP URI
166  * \param hf header field
167  * \param l
168  * \param send_sock socket information
169  * \return new allocated char array on success, zero otherwise
170  */
171 static inline char *get_hfblock( str *uri, struct hdr_field *hf, int *l, struct socket_info** send_sock)
172 {
173         struct str_list sl, *last, *new, *i, *foo;
174         int hf_avail, frag_len, total_len;
175         char *begin, *needle, *dst, *ret, *d;
176         str *sock_name, *portname;
177         union sockaddr_union to_su;
178
179         ret=0; /* pessimist: assume failure */
180         total_len=0;
181         last=&sl;
182         last->next=0;
183         portname=sock_name=0;
184
185         for (; hf; hf=hf->next) {
186                 if (skip_hf(hf)) continue;
187
188                 begin=needle=hf->name.s; 
189                 hf_avail=hf->len;
190
191                 /* substitution loop */
192                 while(hf_avail) {
193                         d=memchr(needle, SUBST_CHAR, hf_avail);
194                         if (!d || d+1>=needle+hf_avail) { /* nothing to substitute */
195                                 new=append_str_list(begin, hf_avail, &last, &total_len); 
196                                 if (!new) goto error;
197                                 break;
198                         } else {
199                                 frag_len=d-begin;
200                                 d++; /* d not at the second substitution char */
201                                 switch(*d) {
202                                         case SUBST_CHAR:        /* double SUBST_CHAR: IP */
203                                                 /* string before substitute */
204                                                 new=append_str_list(begin, frag_len, &last, &total_len); 
205                                                 if (!new) goto error;
206                                                 /* substitute */
207                                                 if (!sock_name) {
208                                                         if (*send_sock==0){
209                                                                 *send_sock=uri2sock(0, uri, &to_su,PROTO_NONE);
210                                                                 if (!*send_sock) {
211                                                                         LM_ERR("send_sock failed\n");
212                                                                         goto error;
213                                                                 }
214                                                         }
215                                                         sock_name=&(*send_sock)->address_str;
216                                                         portname=&(*send_sock)->port_no_str;
217                                                 }
218                                                 new=append_str_list(sock_name->s, sock_name->len,
219                                                                 &last, &total_len );
220                                                 if (!new) goto error;
221                                                 /* inefficient - FIXME --andrei*/
222                                                 new=append_str_list(":", 1, &last, &total_len);
223                                                 if (!new) goto error;
224                                                 new=append_str_list(portname->s, portname->len,
225                                                                 &last, &total_len );
226                                                 if (!new) goto error;
227                                                 /* keep going ... */
228                                                 begin=needle=d+1;hf_avail-=frag_len+2;
229                                                 continue;
230                                         default:
231                                                 /* no valid substitution char -- keep going */
232                                                 hf_avail-=frag_len+1;
233                                                 needle=d;
234                                 }
235                         } /* possible substitute */
236                 } /* substitution loop */
237                 /* proceed to next header */
238                 /* new=append_str_list(CRLF, CRLF_LEN, &last, &total_len );
239                 if (!new) goto error; */
240                 LM_DBG("one more hf processed\n");
241         } /* header loop */
242
243
244         /* construct a single header block now */
245         ret=pkg_malloc(total_len);
246         if (!ret) {
247                 LM_ERR("no pkg mem for hf block\n");
248                 goto error;
249         }
250         i=sl.next;
251         dst=ret;
252         while(i) {
253                 foo=i;
254                 i=i->next;
255                 memcpy(dst, foo->s.s, foo->s.len);
256                 dst+=foo->s.len;
257                 pkg_free(foo);
258         }
259         *l=total_len;
260         return ret;
261
262 error:
263         i=sl.next;
264         while(i) {
265                 foo=i;
266                 i=i->next;
267                 pkg_free(foo);
268         }
269         *l=0;
270         return 0;
271 }
272
273
274 /*!
275  * \brief Print routes
276  *
277  * Print route to MI node, allocate temporary memory in pkg_mem.
278  * \param node MI node
279  * \param dlg route set
280  */
281 static inline void mi_print_routes( struct mi_node *node, dlg_t* dlg)
282 {
283 #define MI_ROUTE_PREFIX_S       "Route: "
284 #define MI_ROUTE_PREFIX_LEN     (sizeof(MI_ROUTE_PREFIX_S)-1)
285 #define MI_ROUTE_SEPARATOR_S    ", "
286 #define MI_ROUTE_SEPARATOR_LEN  (sizeof(MI_ROUTE_SEPARATOR_S)-1)
287         rr_t* ptr;
288         int len;
289         char *p, *s;
290
291         ptr = dlg->hooks.first_route;
292
293         if (ptr==NULL) {
294                 add_mi_node_child( node, 0, 0, 0, ".",1);
295                 return;
296         }
297
298         len = MI_ROUTE_PREFIX_LEN;
299         for( ; ptr ; ptr=ptr->next)
300                 len += ptr->len + MI_ROUTE_SEPARATOR_LEN*(ptr->next!=NULL);
301         if (dlg->hooks.last_route)
302                 len += dlg->hooks.last_route->len + 2;
303
304
305         s = pkg_malloc( len );
306         if (s==0) {
307                 LM_ERR("no more pkg mem\n");
308                 return;
309         }
310
311
312         p = s;
313         memcpy( p, MI_ROUTE_PREFIX_S, MI_ROUTE_PREFIX_LEN);
314         p += MI_ROUTE_PREFIX_LEN;
315
316         for( ptr = dlg->hooks.first_route ; ptr ; ptr=ptr->next) {
317                 memcpy( p, ptr->nameaddr.name.s, ptr->len);
318                 p += ptr->len;
319                 if (ptr->next) {
320                         memcpy( p, MI_ROUTE_SEPARATOR_S, MI_ROUTE_SEPARATOR_LEN);
321                         p += MI_ROUTE_SEPARATOR_LEN;
322                 }
323         }
324
325         if (dlg->hooks.last_route) {
326                 *(p++) = '<';
327                 memcpy( p, dlg->hooks.last_route->s, dlg->hooks.last_route->len);
328                 p += dlg->hooks.last_route->len;
329                 *(p++) = '>';
330         }
331
332         add_mi_node_child( node, MI_DUP_VALUE, 0, 0, s, len);
333         pkg_free(s);
334 }
335
336
337 /*!
338  * \brief Print URIs
339  *
340  * Print URIs to MI node, allocate temporary memory in shm_mem.
341  * \param node MI node
342  * \param reply SIP reply
343  * \return zero on success, -1 on errors
344  */
345 static inline int mi_print_uris( struct mi_node *node, struct sip_msg* reply)
346 {
347         dlg_t* dlg;
348
349         if (reply==0)
350                 goto empty;
351
352         dlg = (dlg_t*)shm_malloc(sizeof(dlg_t));
353         if (!dlg) {
354                 LM_ERR("no shm memory left\n");
355                 return -1;
356         }
357
358         memset(dlg, 0, sizeof(dlg_t));
359         if (_tmx_tmb.dlg_response_uac(dlg, reply, TARGET_REFRESH_UNKNOWN) < 0) {
360                 LM_ERR("failed to create dialog\n");
361                 _tmx_tmb.free_dlg(dlg);
362                 return -1;
363         }
364
365         if (dlg->state != DLG_CONFIRMED) {
366                 _tmx_tmb.free_dlg(dlg);
367                 goto empty;
368         }
369
370         if (dlg->hooks.request_uri->s) {
371                 add_mi_node_child( node, MI_DUP_VALUE, 0, 0,
372                         dlg->hooks.request_uri->s, dlg->hooks.request_uri->len);
373         } else {
374                 add_mi_node_child( node, 0, 0, 0, ".",1);
375         }
376         if (dlg->hooks.next_hop->s) {
377                 add_mi_node_child( node, MI_DUP_VALUE, 0, 0,
378                         dlg->hooks.next_hop->s, dlg->hooks.next_hop->len);
379         } else {
380                 add_mi_node_child( node, 0, 0, 0, ".",1);
381         }
382
383         mi_print_routes( node, dlg);
384
385         _tmx_tmb.free_dlg(dlg);
386         return 0;
387 empty:
388         add_mi_node_child( node, 0, 0, 0, ".",1);
389         add_mi_node_child( node, 0, 0, 0, ".",1);
390         add_mi_node_child( node, 0, 0, 0, ".",1);
391         return 0;
392 }
393
394
395 static void mi_uac_dlg_hdl( struct cell *t, int type, struct tmcb_params *ps )
396 {
397         struct mi_handler *mi_hdl;
398         struct mi_root *rpl_tree;
399         str text;
400
401         LM_DBG("MI UAC generated status %d\n", ps->code);
402         if (!*ps->param)
403                 return;
404
405         mi_hdl = (struct mi_handler *)(*ps->param);
406
407         rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
408         if (rpl_tree==0)
409                 goto done;
410
411         if (ps->rpl==FAKED_REPLY) {
412                 get_reply_status( &text, ps->rpl, ps->code);
413                 if (text.s==0) {
414                         LM_ERR("get_reply_status failed\n");
415                         rpl_tree = 0;
416                         goto done;
417                 }
418                 add_mi_node_child( &rpl_tree->node, MI_DUP_VALUE, 0, 0,
419                         text.s, text.len);
420                 pkg_free(text.s);
421                 mi_print_uris( &rpl_tree->node, 0 );
422                 add_mi_node_child( &rpl_tree->node, 0, 0, 0, ".",1);
423         } else { 
424                 addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "%d %.*s",
425                         ps->rpl->first_line.u.reply.statuscode,
426                         ps->rpl->first_line.u.reply.reason.len,
427                         ps->rpl->first_line.u.reply.reason.s);
428                 mi_print_uris( &rpl_tree->node, ps->rpl);
429                 add_mi_node_child( &rpl_tree->node, MI_DUP_VALUE, 0, 0,
430                         ps->rpl->headers->name.s,
431                         ps->rpl->len-(ps->rpl->headers->name.s - ps->rpl->buf));
432         }
433
434         LM_DBG("mi_callback successfully completed\n");
435 done:
436         if (ps->code >= 200) {
437                 mi_hdl->handler_f( rpl_tree, mi_hdl, 1 /*done*/ );
438                 *ps->param = 0;
439         } else {
440                 mi_hdl->handler_f( rpl_tree, mi_hdl, 0 );
441         }
442 }
443
444
445
446 /**************************** MI functions ********************************/
447
448
449 /*
450   Syntax of "t_uac_dlg" :
451     method
452     RURI
453     NEXT_HOP
454     socket
455     headers
456     [Body]
457 */
458 struct mi_root*  mi_tm_uac_dlg(struct mi_root* cmd_tree, void* param)
459 {
460         static char err_buf[MAX_REASON_LEN];
461         static struct sip_msg tmp_msg;
462         static dlg_t dlg;
463         struct mi_root *rpl_tree;
464         struct mi_node *node;
465         struct sip_uri pruri;
466         struct sip_uri pnexthop;
467         struct socket_info* sock;
468         str *method;
469         str *ruri;
470         str *nexthop;
471         str *socket;
472         str *hdrs;
473         str *body;
474         str s;
475         str callid = {0,0};
476         int sip_error;
477         int proto;
478         int port;
479         int cseq;
480         int n;
481         uac_req_t uac_r;
482
483         for( n=0,node = cmd_tree->node.kids; n<6 && node ; n++,node=node->next );
484         if ( !(n==5 || n==6) || node!=0)
485                 return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
486
487         /* method name (param 1) */
488         node = cmd_tree->node.kids;
489         method = &node->value;
490
491         /* RURI (param 2) */
492         node = node->next;
493         ruri = &node->value;
494         if (parse_uri( ruri->s, ruri->len, &pruri) < 0 )
495                 return init_mi_tree( 400, "Invalid RURI", 12);
496
497         /* nexthop RURI (param 3) */
498         node = node->next;
499         nexthop = &node->value;
500         if (nexthop->len==1 && nexthop->s[0]=='.') {
501                 nexthop = 0;
502         } else {
503                 if (parse_uri( nexthop->s, nexthop->len, &pnexthop) < 0 )
504                         return init_mi_tree( 400, "Invalid NEXTHOP", 15);
505         }
506
507         /* socket (param 4) */
508         node = node->next;
509         socket = &node->value;
510         if (socket->len==1 && socket->s[0]=='.' ) {
511                 sock = 0;
512         } else {
513                 if (parse_phostport( socket->s, &s.s, &s.len,
514                 &port,&proto)!=0)
515                         return init_mi_tree( 404, "Invalid local socket", 20);
516                 sock = grep_sock_info( &s, (unsigned short)port, proto);
517                 if (sock==0)
518                         return init_mi_tree( 404, "Local socket not found", 22);
519         }
520
521         /* new headers (param 5) */
522         node = node->next;
523         if (node->value.len==1 && node->value.s[0]=='.')
524                 hdrs = 0;
525         else {
526                 hdrs = &node->value;
527                 /* use SIP parser to look at what is in the FIFO request */
528                 memset( &tmp_msg, 0, sizeof(struct sip_msg));
529                 tmp_msg.len = hdrs->len; 
530                 tmp_msg.buf = tmp_msg.unparsed = hdrs->s;
531                 if (parse_headers( &tmp_msg, HDR_EOH_F, 0) == -1 )
532                         return init_mi_tree( 400, "Bad headers", 11);
533         }
534
535         /* body (param 5 - optional) */
536         node = node->next;
537         if (node)
538                 body = &node->value;
539         else
540                 body = 0;
541
542         /* at this moment, we collected all the things we got, let's
543          * verify user has not forgotten something */
544         rpl_tree = mi_check_msg( &tmp_msg, method, body, &cseq, &callid);
545         if (rpl_tree) {
546                 if (tmp_msg.headers) free_hdr_field_lst(tmp_msg.headers);
547                 return rpl_tree;
548         }
549
550         s.s = get_hfblock( nexthop ? nexthop : ruri,
551                         tmp_msg.headers, &s.len, &sock);
552         if (s.s==0) {
553                 if (tmp_msg.headers) free_hdr_field_lst(tmp_msg.headers);
554                 return 0;
555         }
556
557         memset( &dlg, 0, sizeof(dlg_t));
558         /* Fill in Call-ID, use given Call-ID if
559          * present and generate it if not present */
560         if (callid.s && callid.len)
561                 dlg.id.call_id = callid;
562         else
563                 _tmx_tmb.generate_callid(&dlg.id.call_id);
564
565         /* We will not fill in dlg->id.rem_tag because
566          * if present it will be printed within To HF */
567
568         /* Generate fromtag if not present */
569         if (!(get_from(&tmp_msg)->tag_value.len&&get_from(&tmp_msg)->tag_value.s))
570                 _tmx_tmb.generate_fromtag(&dlg.id.loc_tag, &dlg.id.call_id);
571
572         /* Fill in CSeq */
573         if (cseq!=-1)
574                 dlg.loc_seq.value = cseq;
575         else
576                 dlg.loc_seq.value = DEFAULT_CSEQ;
577         dlg.loc_seq.is_set = 1;
578
579         dlg.loc_uri = tmp_msg.from->body;
580         dlg.rem_uri = tmp_msg.to->body;
581         dlg.rem_target = *ruri;
582         if (nexthop)
583                 dlg.dst_uri = *nexthop;
584         dlg.send_sock = sock;
585
586         memset(&uac_r, 0, sizeof(uac_req_t));
587         uac_r.method = method;
588         uac_r.body = body;
589         uac_r.headers = &s;
590         uac_r.dialog = &dlg;
591         if (cmd_tree->async_hdl!=NULL)
592         {
593                 uac_r.cb = mi_uac_dlg_hdl;
594                 uac_r.cbp = (void*)cmd_tree->async_hdl;
595                 uac_r.cb_flags = TMCB_LOCAL_COMPLETED;
596         }
597         n = _tmx_tmb.t_uac(&uac_r);
598
599         pkg_free(s.s);
600         if (tmp_msg.headers) free_hdr_field_lst(tmp_msg.headers);
601
602         if (n<=0) {
603                 /* error */
604                 rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
605                 if (rpl_tree==0)
606                         return 0;
607
608                 n = err2reason_phrase( n, &sip_error, err_buf, sizeof(err_buf),
609                         "MI/UAC") ;
610                 if (n > 0 )
611                         addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "%d %.*s",
612                                 sip_error, n, err_buf);
613                 else
614                         add_mi_node_child( &rpl_tree->node, 0, 0, 0,
615                                 "500 MI/UAC failed", 17);
616
617                 return rpl_tree;
618         } else {
619                 if (cmd_tree->async_hdl==NULL)
620                         return init_mi_tree( 202, "Accepted", 8);
621                 else
622                         return MI_ROOT_ASYNC_RPL;
623         }
624 }
625
626
627 /*
628   Syntax of "t_uac_cancel" :
629     callid
630     cseq
631 */
632 struct mi_root* mi_tm_cancel(struct mi_root* cmd_tree, void* param)
633 {
634         struct cancel_info cancel_data;
635         struct mi_node *node;
636         struct cell *trans;
637
638         node =  cmd_tree->node.kids;
639         if ( !node || !node->next || node->next->next)
640                 return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
641
642         if( _tmx_tmb.t_lookup_callid( &trans, node->value, node->next->value) < 0 )
643                 return init_mi_tree( 481, "No such transaction", 19);
644
645         /* cancel the call */
646         LM_DBG("cancelling transaction %p\n",trans);
647
648         init_cancel_info(&cancel_data);
649         cancel_data.cancel_bitmap = ~0; /*all branches*/
650         _tmx_tmb.cancel_uacs(trans, &cancel_data, 0);
651
652         _tmx_tmb.unref_cell(trans);
653
654         return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
655 }
656
657
658 /*
659   Syntax of "t_hash" :
660     no nodes
661 */
662 struct mi_root* mi_tm_hash(struct mi_root* cmd_tree, void* param)
663 {
664 #ifndef TM_HASH_STATS
665         return init_mi_tree( 500, "No TM hash stats", 16);
666 #else
667         struct mi_root* rpl_tree= NULL;
668         struct mi_node* rpl;
669         struct mi_node* node;
670         struct mi_attr* attr;
671         struct s_table* tm_t;
672         char *p;
673         int i;
674         int len;
675
676         rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
677         if (rpl_tree==0)
678                 return 0;
679         rpl = &rpl_tree->node;
680         tm_t = _tmx_tmb.get_table();
681
682         for (i=0; i<TABLE_ENTRIES; i++) {
683                 if(tm_t->entries[i].cur_entries==0
684                                 && tm_t->entries[i].acc_entries==0)
685                         continue;
686
687                 p = int2str((unsigned long)i, &len );
688                 node = add_mi_node_child(rpl, MI_DUP_VALUE , 0, 0, p, len);
689                 if(node == NULL)
690                         goto error;
691
692                 p = int2str((unsigned long)tm_t->entries[i].cur_entries, &len );
693                 attr = add_mi_attr(node, MI_DUP_VALUE, "Current", 7, p, len );
694                 if(attr == NULL)
695                         goto error;
696
697                 p = int2str((unsigned long)tm_t->entries[i].acc_entries, &len );
698                 attr = add_mi_attr(node, MI_DUP_VALUE, "Total", 5, p, len );
699                 if(attr == NULL)
700                         goto error;
701         }
702
703         return rpl_tree;
704 error:
705         free_mi_tree(rpl_tree);
706         return init_mi_tree( 500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
707 #endif
708 }
709
710
711 /*
712   Syntax of "t_reply" :
713   code
714   reason
715   trans_id
716   to_tag
717   new headers
718   [Body]
719 */
720 struct mi_root* mi_tm_reply(struct mi_root* cmd_tree, void* param)
721 {
722         struct mi_node* node;
723         unsigned int hash_index;
724         unsigned int hash_label;
725         unsigned int rpl_code;
726         struct cell *trans;
727         str reason = {0, 0};
728         str totag = {0, 0};
729         str new_hdrs = {0, 0};
730         str body = {0, 0};
731         str tmp = {0, 0};
732         char *p;
733         int n;
734
735         for( n=0,node = cmd_tree->node.kids; n<6 && node ; n++,node=node->next );
736         if ( !(n==5 || n==6) || node!=0)
737                 return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
738
739         /* get all info from the command */
740
741         /* reply code (param 1) */
742         node = cmd_tree->node.kids;
743         if (str2int( &node->value, &rpl_code)!=0 || rpl_code>=700)
744                 return init_mi_tree( 400, "Invalid reply code", 18);
745
746         /* reason text (param 2) */
747         node = node->next;
748         reason = node->value;
749
750         /* trans_id (param 3) */
751         node = node->next;
752         tmp = node->value;
753         p = memchr( tmp.s, ':', tmp.len);
754         if(p==NULL)
755                 return init_mi_tree( 400, "Invalid trans_id", 16);
756
757         tmp.len = p-tmp.s;
758         if(str2int(&tmp, &hash_index)!=0)
759                 return init_mi_tree( 400, "Invalid index in trans_id", 25);
760
761         tmp.s = p+1;
762         tmp.len = (node->value.s+node->value.len) - tmp.s;
763         if(str2int(&tmp, &hash_label)!=0)
764                 return init_mi_tree( 400, "Invalid label in trans_id", 25);
765
766         if(_tmx_tmb.t_lookup_ident( &trans, hash_index, hash_label)<0)
767                 return init_mi_tree( 404, "Transaction not found", 21);
768
769         /* to_tag (param 4) */
770         node = node->next;
771         totag = node->value;
772
773         /* new headers (param 5) */
774         node = node->next;
775         if (!(node->value.len==1 && node->value.s[0]=='.'))
776                 new_hdrs = node->value;
777
778         /* body (param 5 - optional) */
779         node = node->next;
780         if (node)
781                 body = node->value;
782
783         /* it's refcounted now, t_reply_with body unrefs for me -- I can 
784          * continue but may not use T anymore  */
785         n = _tmx_tmb.t_reply_with_body(trans, rpl_code, &reason, &body,
786                         &new_hdrs, &totag);
787
788         if (n<0)
789                 return init_mi_tree( 500, "Reply failed", 12);
790
791         return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
792 }
793
794 /*
795   Syntax of "t_reply_callid" :
796   code
797   reason
798   callid
799   cseq
800   to_tag
801   new headers
802   [Body]
803 */
804 struct mi_root* mi_tm_reply_callid(struct mi_root* cmd_tree, void* param)
805 {
806         struct mi_node* node;
807         unsigned int rpl_code;
808         struct cell *trans;
809         str reason = {0, 0};
810         str totag = {0, 0};
811         str new_hdrs = {0, 0};
812         str body = {0, 0};
813         str callid = {0, 0};
814         str cseq = {0, 0};
815         int n;
816
817         for( n=0,node = cmd_tree->node.kids; n<7 && node ; n++,node=node->next );
818         if ( !(n==6 || n==7) || node!=0)
819                 return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
820
821         /* get all info from the command */
822
823         /* reply code (param 1) */
824         node = cmd_tree->node.kids;
825         if (str2int( &node->value, &rpl_code)!=0 || rpl_code>=700)
826                 return init_mi_tree( 400, "Invalid reply code", 18);
827
828         /* reason text (param 2) */
829         node = node->next;
830         reason = node->value;
831
832         /* callid (param 3) */
833         node = node->next;
834         callid = node->value;
835
836         /* cseq (param 4) */
837         node = node->next;
838         cseq = node->value;
839
840         if(_tmx_tmb.t_lookup_callid( &trans, callid, cseq) < 0 )
841                 return init_mi_tree( 400, "Lookup failed - no transaction", 30);
842
843         /* to_tag (param 5) */
844         node = node->next;
845         totag = node->value;
846
847         /* new headers (param 6) */
848         node = node->next;
849         if (!(node->value.len==1 && node->value.s[0]=='.'))
850                 new_hdrs = node->value;
851
852         /* body (param 7 - optional) */
853         node = node->next;
854         if (node)
855                 body = node->value;
856
857         /* it's refcounted now, t_reply_with body unrefs for me -- I can
858          * continue but may not use T anymore  */
859         n = _tmx_tmb.t_reply_with_body(trans, rpl_code, &reason, &body,
860                         &new_hdrs, &totag);
861
862         if (n<0)
863                 return init_mi_tree( 500, "Reply failed", 12);
864
865         return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
866 }
867