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