obsolete/exec/exec_hf.c
de9ee5c6
 /*
  *
  * $Id$
  *
  *
95072403
  * Copyright (C) 2001-2003 FhG Fokus
de9ee5c6
  *
  * This file is part of ser, a free SIP server.
  *
  * ser 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
  *
  * For a license to use the ser software under conditions
  * other than those described here, or to purchase support for this
  * software, please contact iptel.org by e-mail at the following addresses:
  *    info@iptel.org
  *
  * ser 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 
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
bdc57f2f
  *
  * history
  * -------
95072403
  *  2003-02-28 scratchpad compatibility abandoned
d6b707fd
  *  2003-01-29 scratchpad removed
  *  2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri)
  *  2003-03-19  all mallocs/frees replaced w/ pkg_malloc/pkg_free (andrei)
de9ee5c6
  */
 
 /* functions for creating environment variables out of a request's
  * header; known compact header field names are translated to
95072403
  * canonical form; multiple header field occurrences are merged
de9ee5c6
  * into a single variable
  *
  * known limitations: 
  * - compact header field names unknown to parser will not be translated to 
  *   canonical form. Thus, environment variables may have either name and 
  *   users have to check for both of them.
  * - symbols in header field names will be translated to underscore
  *
  */
 
 #include <stdlib.h>
 
bdc57f2f
 #include "../../comp_defs.h"
de9ee5c6
 #include "../../parser/msg_parser.h"
 #include "../../parser/parse_to.h"
 #include "../../parser/parse_via.h"
 #include "../../parser/parse_uri.h"
 #include "../../mem/mem.h"
 #include "../../dprint.h"
 #include "../../md5utils.h"
817b9c5c
 #include "../../char_msg_val.h"
de9ee5c6
 #include "exec_hf.h"
 
 /* should be environment variables set by header fields ? */
 unsigned int setvars=1;
 
 /* insert a new header field into the structure; */
 static int insert_hf( struct hf_wrapper **list, struct hdr_field *hf )
 {
 	struct hf_wrapper *w; /* new wrapper */
 	struct hf_wrapper *i;
 
 	w=(struct hf_wrapper *)pkg_malloc(sizeof(struct hf_wrapper));
 	if (!w) {
 		LOG(L_ERR, "ERROR: insert_hf ran out of pkg mem\n");
 		return 0;
 	}
 	memset(w, 0, sizeof(struct hf_wrapper));
 	w->var_type=W_HF;w->u.hf=hf; 
 	w->prefix=HF_PREFIX; w->prefix_len=HF_PREFIX_LEN;
 
 	/* is there another hf of the same type?... */
 	for(i=*list; i; i=i->next_other) {
 		if (i->var_type==W_HF && i->u.hf->type==hf->type) {
 			/* if it is OTHER, check name too */
0bfbf63f
 			if (hf->type==HDR_OTHER_T && (hf->name.len!=i->u.hf->name.len
de9ee5c6
 					|| strncasecmp(i->u.hf->name.s, hf->name.s, 
 					   hf->name.len)!=0))
 				continue;
 			/* yes, we found a hf of same type */
 			w->next_same=i->next_same;
 			w->next_other=i->next_other;
 			i->next_same=w;
 			break;
 		}
 	}
 	/* ... no previous HF of the same type found */
 	if (i==0) {
 		w->next_other=*list;
 		*list=w;
 	}
 	return 1;
 }
 
 static void release_hf_struct( struct hf_wrapper *list )
 {
 	struct hf_wrapper *i, *j, *nexts, *nexto;
 
 	i=list;
 	while(i) {
 		nexto=i->next_other;
 		j=i->next_same;
 		pkg_free(i);
 		/* release list of same type hf */
 		while(j) {
 			nexts=j->next_same;
 			pkg_free(j);
 			j=nexts;
 		}
 		i=nexto;
 	}
 }
 
 /* if that is some of well-known header fields which have compact
  * form, return canonical form ... returns 1 and sets params;
  * 0 is returned otherwise */
0bfbf63f
 static int compacthdr_type2str(hdr_types_t type, char **hname, int *hlen )
de9ee5c6
 {
 	switch(type) {
95072403
 		/* HDR_CONTENT_ENCODING: 'e' -- unsupported by parser */
 		/* HDR_SUBJECT: 's' -- unsupported by parser */
0bfbf63f
 		case HDR_VIA_T /* v */ : 
de9ee5c6
 			*hname=VAR_VIA;
 			*hlen=VAR_VIA_LEN;
 			break;
0bfbf63f
 		case HDR_CONTENTTYPE_T /* c */ : 
de9ee5c6
 			*hname=VAR_CTYPE;
 			*hlen=VAR_CTYPE_LEN;
 			break;
0bfbf63f
 		case HDR_FROM_T /* f */: 
de9ee5c6
 			*hname=VAR_FROM;
 			*hlen=VAR_FROM_LEN;
 			break;
0bfbf63f
 		case HDR_CALLID_T /* i */: 
de9ee5c6
 			*hname=VAR_CALLID;
 			*hlen=VAR_CALLID_LEN;
 			break;
0bfbf63f
 		case HDR_SUPPORTED_T /* k */: 
de9ee5c6
 			*hname=VAR_SUPPORTED;
 			*hlen=VAR_SUPPORTED_LEN;
 			break;
0bfbf63f
 		case HDR_CONTENTLENGTH_T /* l */: 
de9ee5c6
 			*hname=VAR_CLEN;
 			*hlen=VAR_CLEN_LEN;
 			break;
0bfbf63f
 		case HDR_CONTACT_T /* m */: 
de9ee5c6
 			*hname=VAR_CONTACT;
 			*hlen=VAR_CONTACT_LEN;
 			break;
0bfbf63f
 		case HDR_TO_T /* t */: 
de9ee5c6
 			*hname=VAR_TO;
 			*hlen=VAR_TO_LEN;
 			break;
0bfbf63f
 		case HDR_EVENT_T /* o */: 
de9ee5c6
 			*hname=VAR_EVENT;
 			*hlen=VAR_EVENT_LEN;
 			break;
 		default:	
 			return 0;
 	}
 	return 1;
 }
 
 
 static int canonize_headername(str *orig, char **hname, int *hlen )
 {
 	char *c;
 	int i;
 
 	*hlen=orig->len;
 	*hname=pkg_malloc(*hlen);
 	if (!*hname) {
 		LOG(L_ERR, "ERROR: print_vars: no mem for hname\n");
 		return 0;
 	}
 	for (c=orig->s, i=0; i<*hlen; i++, c++) {
 		/* lowercase to uppercase */
 		if (*c>='a' && *c<='z') 
 			*((*hname)+i)=*c-('a'-'A');
 			/* uppercase and numbers stay "as is" */
 		else if ((*c>='A' && *c<='Z')||(*c>='0' && *c<='9')) 
 			*((*hname)+i)=*c;
 		/* legal symbols will be translated to underscore */
 		else if (strchr(UNRESERVED_MARK HNV_UNRESERVED, *c)
 				|| (*c==ESCAPE))
 			*((*hname)+i)=HFN_SYMBOL;
 		else {
 			LOG(L_ERR, "ERROR: print_var unexpected char "
 					"'%c' in hfname %.*s\n", 
 					*c, *hlen, orig->s );
 			*((*hname)+i)=HFN_SYMBOL;
 		}
 	}
 	return 1;
 }
 
 
 static int print_av_var(struct hf_wrapper *w)
 {
 	int env_len;
 	char *env;
 	char *c;
 
 	env_len=w->u.av.attr.len+1/*assignment*/+w->u.av.val.len+1/*ZT*/;
d6b707fd
 	env=pkg_malloc(env_len);
de9ee5c6
 	if (!env) {
 		LOG(L_ERR, "ERROR: print_av_var: no malloc mem\n");
 		return 0;
 	}
 	c=env;
 	memcpy(c, w->u.av.attr.s, w->u.av.attr.len); c+=w->u.av.attr.len;
 	*c=EV_ASSIGN;c++;
 	memcpy(c, w->u.av.val.s, w->u.av.val.len);c+=w->u.av.val.len;
 	*c=0; /* zero termination */
 	w->envvar=env;
 	return 1;
 }
 
95072403
 /* creates a malloc-ed string with environment variable; returns 1 on success,
de9ee5c6
  * 0 on failure  */
 static int print_hf_var(struct hf_wrapper *w, int offset)
 {
 	char *hname;
 	int hlen;
 	short canonical;
 	char *envvar;
 	int envvar_len;
 	struct hf_wrapper *wi;
 	char *c;
 
 	/* make -Wall happy */
 	hname=0;hlen=0;envvar=0;
 
 	/* Make sure header names with possible compact forms
95072403
 	 * will be printed canonically
de9ee5c6
 	 */
 	canonical=compacthdr_type2str(w->u.hf->type, &hname, &hlen);
 	/* header field has not been made canonical using a table;
 	 * do it now by uppercasing header-field name */
 	if (!canonical) {
 		if (!canonize_headername(&w->u.hf->name, &hname, &hlen)) {
95072403
 			LOG(L_ERR, "ERROR: print_hf_var: canonize_hn error\n");
de9ee5c6
 			return 0;
 		}
 	} 
 	/* now we have a header name, let us generate the var */
bdc57f2f
 	envvar_len=w->u.hf->body.len;
 	for(wi=w->next_same; wi; wi=wi->next_same) { /* other values, separated */
 		envvar_len+=1 /* separator */ + wi->u.hf->body.len;
de9ee5c6
 	}
d6b707fd
 	envvar=pkg_malloc(w->prefix_len+hlen+1/*assignment*/+envvar_len+1/*ZT*/);
de9ee5c6
 	if (!envvar) {
 		LOG(L_ERR, "ERROR: print_var: no envvar mem\n");
 		goto error00;
 	}
 	memcpy(envvar, w->prefix, w->prefix_len); c=envvar+w->prefix_len;
 	memcpy(c, hname, hlen ); c+=hlen;
 	*c=EV_ASSIGN;c++;
bdc57f2f
 	memcpy(c, w->u.hf->body.s+offset, w->u.hf->body.len );
 	c+=w->u.hf->body.len;
de9ee5c6
 	for (wi=w->next_same; wi; wi=wi->next_same) {
 		*c=HF_SEPARATOR;c++;
bdc57f2f
 		memcpy(c, wi->u.hf->body.s+offset, wi->u.hf->body.len );
 		c+=wi->u.hf->body.len;
de9ee5c6
 	}
 	*c=0; /* zero termination */
 	DBG("DEBUG: print_var: %s\n", envvar );
 	
 	w->envvar=envvar;
 	if (!canonical) pkg_free(hname);
 	return 1;
 
 error00:
 	if (!canonical) pkg_free(hname);
 	return 0;
 }
 
 static int print_var(struct hf_wrapper *w, int offset)
 {
 	switch(w->var_type) {
 		case W_HF: 
 			return print_hf_var(w, offset);
 		case W_AV: 
 			return print_av_var(w);
 		default:
 		   	LOG(L_CRIT, "BUG: print_var: unknown type: %d\n",
 					w->var_type );
 			return 0;
 	}
 }
 
 static void release_vars(struct hf_wrapper *list) 
 {
 	while(list) {
 		if (list->envvar) {
d6b707fd
 			pkg_free(list->envvar);
de9ee5c6
 			list->envvar=0;
 		}
 		list=list->next_other;
 	}
 }
 
 /* create ordered HF structure in pkg memory */
 static int build_hf_struct(struct sip_msg *msg, struct hf_wrapper **list)
 {
 	struct hdr_field *h;
 
 	*list=0;
 	/* create ordered header-field structure */
 	for (h=msg->headers; h; h=h->next) {
 		if (!insert_hf(list,h)) {
 			LOG(L_ERR, "ERROR: build_hf_struct: insert_hf failed\n");
 			goto error00;
 		}
 	}
 	return 1;
 error00:
 	release_hf_struct(*list);
 	*list=0;
 	return 0;
 
 }
 
 /* create env vars in malloc memory */
 static int create_vars(struct hf_wrapper *list, int offset)
 {
 	int var_cnt;
 	struct hf_wrapper *w;
 
 	/* create variables now */
 	var_cnt=0;
 	for(w=list;w;w=w->next_other) {
 		if (!print_var(w, offset)) {
 			LOG(L_ERR, "ERROR: build_hf_struct: create_vars failed\n");
 			return 0;
 		}
 		var_cnt++;
 	}
 
 	return var_cnt;
 }
 
 environment_t *replace_env(struct hf_wrapper *list)
 {
 	int var_cnt;
 	char **cp;
 	struct hf_wrapper *w;
 	char **new_env;
 	int i;
 	environment_t *backup_env;
 
 	backup_env=(environment_t *)pkg_malloc(sizeof(environment_t));
 	if (!backup_env) {
 		LOG(L_ERR, "ERROR: replace_env: no mem for backup env\n");
 		return 0;
 	}
 
 	/* count length of current env list */
 	var_cnt=0;
 	for (cp=environ; *cp; cp++) var_cnt++;
 	backup_env->old_cnt=var_cnt;
 	/* count length of our extensions */
 	for(w=list;w;w=w->next_other) var_cnt++;
d6b707fd
 	new_env=pkg_malloc((var_cnt+1)*sizeof(char *));
de9ee5c6
 	if (!new_env) {
 		LOG(L_ERR, "ERROR: replace_env: no mem\n");
 		return 0;
 	}
 	/* put all var pointers into new environment */
 	i=0;
 	for (cp=environ; *cp; cp++) { /* replicate old env */
 		new_env[i]=*cp;
 		i++;
 	}
 	for (w=list;w;w=w->next_other) { /* append new env */
 		new_env[i]=w->envvar;
 		i++;
 	}
 	new_env[i]=0; /* zero termination */
 	/* install new environment */
 	backup_env->env=environ;
 	environ=new_env;
 	/* return previous environment */
 	return backup_env;
 }
 
 void unset_env(environment_t *backup_env)
 {
 	char **cur_env, **cur_env0;
 	int i;
 
 	/* switch-over to backup environment */
 	cur_env0=cur_env=environ;
 	environ=backup_env->env;
 	i=0;
 	/* release environment */
 	while(*cur_env) {
 		/* leave previously existing vars alone */
 		if (i>=backup_env->old_cnt) {
d6b707fd
 			pkg_free(*cur_env);
de9ee5c6
 		}
 		cur_env++;
 		i++;
 	}
d6b707fd
 	pkg_free(cur_env0);
de9ee5c6
 	pkg_free(backup_env);
 }
 
 static int append_var(char *name, char *value, int len, struct hf_wrapper **list)
 {
 	struct hf_wrapper *w;
 
 	w=(struct hf_wrapper *)pkg_malloc(sizeof(struct hf_wrapper));
 	if (!w) {
 		LOG(L_ERR, "ERROR: append_var ran out of mem\n");
 		return 0;
 	}
 	memset(w, 0, sizeof(struct hf_wrapper)); 
 	w->var_type=W_AV;
 	w->u.av.attr.s=name;
 	w->u.av.attr.len=strlen(name);
 	w->u.av.val.s=value;
 	/* NULL strings considered empty, if len unknown, calculate it now */
 	w->u.av.val.len= value==0?0:(len==0? strlen(value) : len);
 	w->next_other=*list;
 	*list=w;
 	return 1;
 }
 
 static int append_fixed_vars(struct sip_msg *msg, struct hf_wrapper **list)
 {
 	static char tid[MD5_LEN];
 	str *uri;
eff5e1ac
 	struct sip_uri parsed_uri, oparsed_uri;
de9ee5c6
 	char *val;
 	int val_len;
 
 	/* source ip */
 	if (!append_var(EV_SRCIP, ip_addr2a(&msg->rcv.src_ip), 0, list)) {
 		LOG(L_ERR, "ERROR: append_var SRCIP failed \n");
 		return 0;
 	}
 	/* request URI */
 	uri=msg->new_uri.s && msg->new_uri.len ? 
 		&msg->new_uri : &msg->first_line.u.request.uri;
 	if (!append_var(EV_RURI, uri->s, uri->len, list )) {
 		LOG(L_ERR, "ERROR: append_var URI failed\n");
 		return 0;
 	}
 	/* userpart of request URI */
 	if (parse_uri(uri->s, uri->len, &parsed_uri)<0) {
 		LOG(L_WARN, "WARNING: append_var: URI not parsed\n");
 	} else {
 		if (!append_var(EV_USER, parsed_uri.user.s, 
 					parsed_uri.user.len, list)) {
 			LOG(L_ERR, "ERROR: append_var USER failed\n");
 			goto error;
 		}
 	}
 	/* original URI */
 	if (!append_var(EV_ORURI, msg->first_line.u.request.uri.s,
 				msg->first_line.u.request.uri.len, list)) {
 		LOG(L_ERR, "ERROR: append_var O-URI failed\n");
 		goto error;
 	}
eff5e1ac
 	/* userpart of request URI */
 	if (parse_uri(msg->first_line.u.request.uri.s, 
 				msg->first_line.u.request.uri.len, 
 				&oparsed_uri)<0) {
 		LOG(L_WARN, "WARNING: append_var: orig URI not parsed\n");
 	} else {
 		if (!append_var(EV_OUSER, oparsed_uri.user.s, 
 					oparsed_uri.user.len, list)) {
 			LOG(L_ERR, "ERROR: append_var OUSER failed\n");
d6302796
 			goto error;
eff5e1ac
 		}
 	}
de9ee5c6
 	/* tid, transaction id == via/branch */
 	if (!char_msg_val(msg, tid)) {
 		LOG(L_WARN, "WARNING: no tid can be determined\n");
 		val=0; val_len=0;
 	} else {
 		val=tid;val_len=MD5_LEN;
 	}
 	if (!append_var(EV_TID, val,val_len, list)) {
 		LOG(L_ERR, "ERROR: append_var TID failed\n");
d6302796
 		goto error;
de9ee5c6
 	}
 
 	/* did, dialogue id == To-tag */
 	if (!(msg->to && get_to(msg) ))  {
 		LOG(L_ERR, "ERROR: append_var: no to-tag\n");
 		val=0; val_len=0;
 	} else {
 		val=get_to(msg)->tag_value.s;
 		val_len=get_to(msg)->tag_value.len;
 	}
 	if (!append_var(EV_DID, val, val_len, list)) {
 		LOG(L_ERR, "ERROR: append_var DID failed\n");
d6302796
 		goto error;
de9ee5c6
 	}
 	return 1;
 error:
 	return 0;
 }
 
 environment_t *set_env(struct sip_msg *msg)
 {
 	struct hf_wrapper *hf_list;
 	environment_t *backup_env;
 
 	/* parse all so that we can pass all header fields to script */
0bfbf63f
 	if (parse_headers(msg, HDR_EOH_F, 0)==-1) {
de9ee5c6
 		LOG(L_ERR, "ERROR: set_env: parsing failed\n");
 		return 0;
 	}
 
 	hf_list=0;
 	/* create a temporary structure with ordered header fields
 	 * and create environment variables out of it */
 	if (!build_hf_struct(msg, &hf_list)) {
 		LOG(L_ERR, "ERROR: set_env: build_hf_struct failed\n");
 		return 0;
 	}
 	if (!append_fixed_vars(msg, &hf_list)) {
 		LOG(L_ERR, "ERROR: ser_env: append_fixed_vars failed\n");
 		goto error01;
 	}
 	/* create now the strings for environment variables */
00d35c21
 	if (!create_vars(hf_list, 0)) {
de9ee5c6
 		LOG(L_ERR, "ERROR: set_env: create_vars failed\n");
 		goto error00;
 	}
 	/* install the variables in current environment */
 	backup_env=replace_env(hf_list);
 	if (!backup_env) {
 		LOG(L_ERR, "ERROR: set_env: replace_env failed\n");
 		goto error00;
 	}
 	/* release the ordered HF structure -- we only need the vars now */
 	release_hf_struct(hf_list);
 	return backup_env;
 
 error00:
 	release_vars(hf_list); /* release variables */
 error01:
 	release_hf_struct(hf_list); /* release temporary ordered HF struct */
 	return 0;
 }