/*
* $Id: serialize.c 1598 2007-02-07 18:41:51Z bogdan_iancu $
*
* sequential forking implementation
*
* Copyright (C) 2005 Juha Heinanen
*
* This file is part of openser, a free SIP server.
*
* openser is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version
*
* openser is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* History:
* -------
* 2005-11-29 splitted from lcr module (bogdan)
*/
#include "str.h"
#include "qvalue.h"
#include "usr_avp.h"
#include "dset.h"
#include "action.h"
#include "route.h"
#include "parser/msg_parser.h"
struct serial_contact {
str uri;
qvalue_t q;
unsigned short q_flag;
int next;
};
/* usr_avp flag for sequential forking */
#define Q_FLAG (1<<4)
/* avp alias to be used */
#define SERIAL_AVP_ALIAS "serial_branch"
/* avp ID of serial AVP */
#define SERIAL_AVL_ID 0xff3434
static int_str serial_avp;
int init_serialization()
{
str alias = { SERIAL_AVP_ALIAS, sizeof(SERIAL_AVP_ALIAS)-1 };
serial_avp.n = SERIAL_AVL_ID;
return add_avp_galias( &alias, 0 /*type*/, serial_avp );
}
/*
* Loads contacts in destination set into "serial_fork" AVP in reverse
* priority order and associated each contact with Q_FLAG telling if
* contact is the last one in its priority class. Finally, removes
* all branches from destination set.
*/
int serialize_branches(struct sip_msg *msg, int clean_before )
{
static struct serial_contact contacts[MAX_BRANCHES];
int n, last, first, i;
str branch, *ruri;
qvalue_t q, ruri_q;
int_str val;
int idx;
/* Check if anything needs to be done */
if (nr_branches == 0) {
DBG("DEBUG:serialize_branches: nothing to do - no branches!\n");
return 0;
}
ruri = GET_RURI(msg);
ruri_q = get_ruri_q();
for( idx=0 ; (branch.s=get_branch(idx,&branch.len,&q,0,0,0,0))!=0 ; idx++ ) {
if (q != ruri_q)
break;
}
if (branch.s==0) {
DBG("DEBUG:serialize_branches: nothing to do - all same q!\n");
return 0;
}
/* reset contact array */
n = 0;
/* Insert Request-URI to contact list */
contacts[n].uri = *ruri;
contacts[n].q = ruri_q;
contacts[n].next = -1;
last = n;
first = n;
n++;
/* Insert branch URIs to contact list in increasing q order */
for( idx=0 ; (branch.s=get_branch(idx,&branch.len,&q,0,0,0,0))!=0 ; idx++ ) {
contacts[n].uri = branch;
contacts[n].q = q;
/* insert based on q */
for( i=0 ; i!=-1 && contacts[i].q < q ; i=contacts[i].next );
if (i==-1) {
/* append */
last = contacts[last].next = n;
contacts[n].next = -1;
} else {
if (i==0) {
/* first element */
contacts[n].next = first;
first = n;
} else {
/* after pos i */
contacts[n].next = contacts[i].next;
contacts[i].next = n;
}
}
n++;
}
/* Assign values for q_flags */
for( i=first ; contacts[i].next!=-1 ; i=contacts[i].next ) {
if (contacts[i].q < contacts[contacts[i].next].q)
contacts[contacts[i].next].q_flag = Q_FLAG;
else
contacts[contacts[i].next].q_flag = 0;
}
if (clean_before)
destroy_avps( 0/*type*/, serial_avp, 1/*all*/);
/* Add contacts to "contacts" AVP */
for ( i=first ; i!=-1; i=contacts[i].next ) {
val.s = contacts[i].uri;
if (add_avp( AVP_VAL_STR|contacts[i].q_flag, serial_avp,
val)!=0 ) {
LOG(L_ERR,"ERROR:serialize_branches: failed to add avp\n");
goto error;
}
DBG("DEBUG:serialize_branches: loaded <%s>, q=%d q_flag <%d>\n",
val.s.s, contacts[i].q, contacts[i].q_flag);
}
/* Clear all branches */
clear_branches();
return 0;
error:
return -1;
}
/*
* Adds to request a destination set that includes all highest priority
* class contacts in "serial_avp" AVP. If called from a route block,
* rewrites the request uri with first contact and adds the remaining
* contacts as branches. If called from failure route block, adds all
* contacts as brances. Removes added contacts from "serial_avp" AVP.
*/
int next_branches( struct sip_msg *msg)
{
struct usr_avp *avp, *prev;
int_str val;
struct action act;
int rval;
if ( route_type!=REQUEST_ROUTE && route_type!=FAILURE_ROUTE ) {
/* unsupported route type */
LOG(L_ERR,"ERROR:next_branch: called from unsupported route type %d\n",
route_type);
goto error;
}
/* Find first avp */
avp = search_first_avp( 0, serial_avp, &val, 0);
if (!avp) {
DBG("DEBUG:next_branches: no AVPs -- we are done!\n");
goto error;
}
if ( route_type == REQUEST_ROUTE) {
/* Set Request-URI */
act.type = SET_URI_T;
act.elem[0].type = STRING_ST;
act.elem[0].u.string = val.s.s;
rval = do_action(&act, msg);
if (rval != 1)
goto error1;
DBG("DEBUG:next_branches: R-URI is <%s>\n", val.s.s);
if (avp->flags & Q_FLAG) {
destroy_avp(avp);
return 0;
}
if ( (avp=search_next_avp(avp, &val))==0 )
return 0;
/* continue */
}
/* Append branches until out of branches or Q_FLAG is set */
do {
act.type = APPEND_BRANCH_T;
act.elem[0].type = STRING_ST;
act.elem[0].u.s = val.s;
act.elem[1].type = NUMBER_ST;
act.elem[1].u.number = 0;
rval = do_action(&act, msg);
if (rval != 1) {
LOG(L_ERR, "ERRORL:next_branches: do_action failed "
"with return value <%d>\n", rval);
goto error1;
}
DBG("DEBUG:next_branches: branch is <%s>\n", val.s.s);
/* continuu ? */
if (avp->flags & Q_FLAG) {
destroy_avp(avp);
return 0;
}
prev = avp;
avp=search_next_avp(prev, &val);
destroy_avp(prev);
}while ( avp );
return 0;
error1:
destroy_avp(avp);
error:
return -1;
}
syntax highlighted by Code2HTML, v. 0.9.1