1 /*
2  * Copyright (C) 2001-2003 FhG Fokus
3  *
4  * This file is part of Kamailio, a free SIP server.
5  *
6  * Kamailio is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version
10  *
11  * Kamailio is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21 
22 
23 #include <stdio.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <signal.h>
30 
31 #include "../../core/mem/shm_mem.h"
32 #include "../../core/mem/mem.h"
33 #include "../../core/sr_module.h"
34 #include "../../core/str.h"
35 #include "../../core/ut.h"
36 #include "../../core/dprint.h"
37 #include "../../core/data_lump_rpl.h"
38 #include "../../core/pvar.h"
39 #include "../../core/mod_fix.h"
40 #include "../../core/parser/parse_uri.h"
41 #include "../../core/parser/parse_from.h"
42 #include "../../core/parser/parse_content.h"
43 #include "../../core/parser/parse_disposition.h"
44 #include "../../core/cfg/cfg_struct.h"
45 #include "../../lib/srdb1/db.h"
46 #include "../../modules/sl/sl.h"
47 #include "cpl_run.h"
48 #include "cpl_env.h"
49 #include "cpl_db.h"
50 #include "cpl_loader.h"
51 #include "cpl_parser.h"
52 #include "cpl_nonsig.h"
53 #include "loc_set.h"
54 
55 
56 #define MAX_PROXY_RECURSE  10
57 #define MAX_USERHOST_LEN    256
58 
59 
60 /* modules param variables */
61 static str db_url           = str_init(DEFAULT_DB_URL); /* database url */
62 static str db_table        = str_init("cpl");  /* database table */
63 static char *dtd_file      = 0;  /* name of the DTD file for CPL parser */
64 static char *lookup_domain = 0;
65 static str  timer_avp      = STR_NULL;  /* name of variable timer AVP */
66 static str  proxy_route    = STR_NULL;
67 
68 
69 struct cpl_enviroment    cpl_env = {
70 		0, /* no cpl logging */
71 		0, /* recurse proxy level is 0 */
72 		0, /* no script route to be run before proxy */
73 		0, /* user part is not case sensitive */
74 		{0,0},   /* no domain prefix to be ignored */
75 		{-1,-1}, /* communication pipe to aux_process */
76 		{0,0},   /* original TZ \0 terminated "TZ=value" format */
77 		0,   /* udomain */
78 		0,   /* no branches on lookup */
79 		0,   /* timer avp type */
80 		{0}, /* timer avp name/ID */
81 		0    /* use_domain */
82 };
83 
84 struct cpl_functions  cpl_fct;
85 static str cpl_ok_rpl = str_init("OK");
86 
87 
88 MODULE_VERSION
89 
90 
91 static int cpl_invoke_script (struct sip_msg* msg, char* str1, char* str2);
92 static int cpl_invoke_script3 (struct sip_msg* msg, char* str1, char* str2, char* str3);
93 static int w_process_register(struct sip_msg* msg, char* str1, char* str2);
94 static int w_process_register_norpl(struct sip_msg* msg, char* str1,char* str2);
95 static int cpl_process_register(struct sip_msg* msg, int no_rpl);
96 static int fixup_cpl_run_script(void** param, int param_no);
97 static int fixup_cpl_run_script3(void** param, int param_no);
98 static int cpl_init(void);
99 static int cpl_child_init(int rank);
100 static void cpl_exit(void);
101 static void cpl_process(int rank);
102 
103 /*
104  * Exported functions
105  */
106 static cmd_export_t cmds[] = {
107 	{"cpl_run_script",            (cmd_function)cpl_invoke_script,        2,
108 			fixup_cpl_run_script, 0, REQUEST_ROUTE},
109 	{"cpl_run_script",            (cmd_function)cpl_invoke_script3,       3,
110 			fixup_cpl_run_script3, 0, REQUEST_ROUTE},
111 	{"cpl_process_register",      (cmd_function)w_process_register,       0,
112 			0, 0,                    REQUEST_ROUTE},
113 	{"cpl_process_register_norpl",(cmd_function)w_process_register_norpl, 0,
114 			0, 0,                    REQUEST_ROUTE},
115 	{0, 0, 0, 0, 0, 0}
116 };
117 
118 
119 /*
120  * Exported parameters
121  */
122 static param_export_t params[] = {
123 	{"db_url",         PARAM_STR, &db_url                          },
124 	{"db_table",       PARAM_STR, &db_table                        },
125 	{"cpl_dtd_file",   PARAM_STRING, &dtd_file                          },
126 	{"proxy_recurse",  INT_PARAM, &cpl_env.proxy_recurse             },
127 	{"proxy_route",    PARAM_STR, &proxy_route                     },
128 	{"log_dir",        PARAM_STRING, &cpl_env.log_dir                   },
129 	{"case_sensitive", INT_PARAM, &cpl_env.case_sensitive            },
130 	{"realm_prefix",   PARAM_STR, &cpl_env.realm_prefix            },
131 	{"lookup_domain",  PARAM_STRING, &lookup_domain                     },
132 	{"lookup_append_branches", INT_PARAM, &cpl_env.lu_append_branches},
133 	{"timer_avp",      PARAM_STR, &timer_avp                       },
134 	{"username_column",PARAM_STR, &cpl_username_col                  },
135 	{"domain_column",  PARAM_STR, &cpl_domain_col                    },
136 	{"cpl_xml_column", PARAM_STR, &cpl_xml_col                       },
137 	{"cpl_bin_column", PARAM_STR, &cpl_bin_col                       },
138 	{"use_domain",     INT_PARAM, &cpl_env.use_domain                },
139 	{0, 0, 0}
140 };
141 
142 
143 struct module_exports exports = {
144 	"cplc",
145 	DEFAULT_DLFLAGS, /* dlopen flags */
146 	cmds,            /* exported functions */
147 	params,          /* exported parameters */
148 	0,               /* exported rpc functions */
149 	0,               /* exported pseudo-variables */
150 	0,               /* response handling function */
151 	cpl_init,        /* module init function */
152 	cpl_child_init,  /* child init function */
153 	cpl_exit         /* module destroy function */
154 };
155 
156 
157 
fixup_cpl_run_script(void ** param,int param_no)158 static int fixup_cpl_run_script(void** param, int param_no)
159 {
160 	long flag;
161 
162 	if (param_no==1) {
163 		if (!strcasecmp( "incoming", *param))
164 			flag = CPL_RUN_INCOMING;
165 		else if (!strcasecmp( "outgoing", *param))
166 			flag = CPL_RUN_OUTGOING;
167 		else {
168 			LM_ERR("script directive \"%s\" unknown!\n",(char*)*param);
169 			return E_UNSPEC;
170 		}
171 		pkg_free(*param);
172 		*param=(void*)flag;
173 		return 0;
174 	} else if (param_no==2) {
175 		if ( !strcasecmp("is_stateless", *param) ) {
176 			flag = 0;
177 		} else if ( !strcasecmp("is_stateful", *param) ) {
178 			flag = CPL_IS_STATEFUL;
179 		} else if ( !strcasecmp("force_stateful", *param) ) {
180 			flag = CPL_FORCE_STATEFUL;
181 		} else {
182 			LM_ERR("flag \"%s\" (second param) unknown!\n",(char*)*param);
183 			return E_UNSPEC;
184 		}
185 		pkg_free(*param);
186 		*param=(void*)flag;
187 	}
188 	return 0;
189 }
190 
fixup_cpl_run_script3(void ** param,int param_no)191 static int fixup_cpl_run_script3(void** param, int param_no)
192 {
193 	if (param_no==1 || param_no==2) {
194 		return fixup_cpl_run_script(param, param_no);
195 	} else if (param_no==3) {
196 		return fixup_spve_null(param, 1);
197 	}
198 	return 0;
199 }
200 
201 
202 
cpl_init(void)203 static int cpl_init(void)
204 {
205 	bind_usrloc_t bind_usrloc;
206 	struct stat   stat_t;
207 	char *ptr;
208 	int val;
209 	pv_spec_t avp_spec;
210 	unsigned short avp_type;
211 
212 	if(cpl_rpc_init()<0)
213 	{
214 		LM_ERR("failed to register RPC commands\n");
215 		return -1;
216 	}
217 
218 	if (cpl_env.proxy_recurse>MAX_PROXY_RECURSE) {
219 		LM_CRIT("value of proxy_recurse param (%d) exceeds "
220 			"the maximum safety value (%d)\n",
221 			cpl_env.proxy_recurse,MAX_PROXY_RECURSE);
222 		goto error;
223 	}
224 
225 	if (proxy_route.len>0) {
226 		cpl_env.proxy_route=route_lookup(&main_rt, proxy_route.s);
227 		if (cpl_env.proxy_route==-1) {
228 			LM_CRIT("route <%s> defined in proxy_route does not exist\n",proxy_route.s);
229 			goto error;
230 		}
231 	}
232 
233 	/* fix the timer_avp name */
234 	if (timer_avp.s && timer_avp.len > 0) {
235 		if (pv_parse_spec(&timer_avp, &avp_spec)==0
236 				|| avp_spec.type!=PVT_AVP) {
237 			LM_ERR("malformed or non AVP %.*s AVP definition\n", timer_avp.len, timer_avp.s);
238 			return -1;
239 		}
240 
241 		if(pv_get_avp_name(0, &(avp_spec.pvp), &cpl_env.timer_avp,
242 							&avp_type)!=0)
243 		{
244 			LM_ERR("[%.*s]- invalid AVP definition\n", timer_avp.len, timer_avp.s);
245 			return -1;
246 		}
247 		cpl_env.timer_avp_type = avp_type;
248 	}
249 
250 	if (dtd_file==0) {
251 		LM_CRIT("mandatory parameter \"cpl_dtd_file\" found empty\n");
252 		goto error;
253 	} else {
254 		/* check if the dtd file exists */
255 		if (stat( dtd_file, &stat_t)==-1) {
256 			LM_ERR("checking file \"%s\" status failed; stat returned %s\n",
257 					dtd_file,strerror(errno));
258 			goto error;
259 		}
260 		if ( !S_ISREG( stat_t.st_mode ) ) {
261 			LM_ERR("dir \"%s\" is not a regular file!\n", dtd_file);
262 			goto error;
263 		}
264 		if (access( dtd_file, R_OK )==-1) {
265 			LM_ERR("checking file \"%s\" for permissions "
266 				"failed; access returned %s\n",dtd_file,strerror(errno));
267 			goto error;
268 		}
269 	}
270 
271 	if (cpl_env.log_dir==0) {
272 		LM_INFO("log_dir param found empty -> logging disabled!\n");
273 	} else {
274 		if ( strlen(cpl_env.log_dir)>MAX_LOG_DIR_SIZE ) {
275 			LM_ERR("dir \"%s\" has a too long name :-(!\n",	cpl_env.log_dir);
276 			goto error;
277 		}
278 		/* check if the dir exists */
279 		if (stat( cpl_env.log_dir, &stat_t)==-1) {
280 			LM_ERR("checking dir \"%s\" status failed;"
281 				" stat returned %s\n",cpl_env.log_dir,strerror(errno));
282 			goto error;
283 		}
284 		if ( !S_ISDIR( stat_t.st_mode ) ) {
285 			LM_ERR("dir \"%s\" is not a directory!\n", cpl_env.log_dir);
286 			goto error;
287 		}
288 		if (access( cpl_env.log_dir, R_OK|W_OK )==-1) {
289 			LM_ERR("checking dir \"%s\" for permissions failed; access "
290 					"returned %s\n", cpl_env.log_dir, strerror(errno));
291 			goto error;
292 		}
293 	}
294 
295 	/* bind to the mysql module */
296 	if (cpl_db_bind(&db_url, &db_table)<0) goto error;
297 
298 	/* load TM API */
299 	if (load_tm_api(&cpl_fct.tmb)!=0) {
300 		LM_ERR("can't load TM API\n");
301 		goto error;
302 	}
303 	/* bind the SL API */
304 	if (sl_load_api(&cpl_fct.slb)!=0) {
305 		LM_ERR("cannot bind to SL API\n");
306 		return -1;
307 	}
308 
309 	/* bind to usrloc module if requested */
310 	if (lookup_domain) {
311 		/* import all usrloc functions */
312 		bind_usrloc = (bind_usrloc_t)find_export("ul_bind_usrloc", 1, 0);
313 		if (!bind_usrloc) {
314 			LM_ERR("can't bind usrloc\n");
315 			goto error;
316 		}
317 		if (bind_usrloc( &(cpl_fct.ulb) ) < 0) {
318 			LM_ERR("importing usrloc failed\n");
319 			goto error;
320 		}
321 		/* convert lookup_domain from char* to udomain_t* pointer */
322 		if (cpl_fct.ulb.register_udomain( lookup_domain, &cpl_env.lu_domain)
323 		< 0) {
324 			LM_ERR("failed to register domain <%s>\n",lookup_domain);
325 			goto error;
326 		}
327 	} else {
328 		LM_NOTICE("no lookup_domain given -> disable lookup node\n");
329 	}
330 
331 	/* build a pipe for sending commands to aux process */
332 	if ( pipe( cpl_env.cmd_pipe )==-1 ) {
333 		LM_CRIT("cannot create command pipe: %s!\n", strerror(errno) );
334 		goto error;
335 	}
336 	/* set the writing non blocking */
337 	if ( (val=fcntl(cpl_env.cmd_pipe[1], F_GETFL, 0))<0 ) {
338 		LM_ERR("getting flags from pipe[1] failed: fcntl said %s!\n",
339 				strerror(errno));
340 		goto error;
341 	}
342 	if ( fcntl(cpl_env.cmd_pipe[1], F_SETFL, val|O_NONBLOCK) ) {
343 		LM_ERR("setting flags to pipe[1] failed: fcntl said %s!\n",
344 				strerror(errno));
345 		goto error;
346 	}
347 
348 	/* init the CPL parser */
349 	if (init_CPL_parser( dtd_file )!=1 ) {
350 		LM_ERR("init_CPL_parser failed!\n");
351 		goto error;
352 	}
353 
354 	/* make a copy of the original TZ env. variable */
355 	ptr = getenv("TZ");
356 	cpl_env.orig_tz.len = 3/*"TZ="*/ + (ptr?(strlen(ptr)+1):0);
357 	if ( (cpl_env.orig_tz.s=shm_malloc( cpl_env.orig_tz.len ))==0 ) {
358 		LM_ERR("no more shm mem. for saving TZ!\n");
359 		goto error;
360 	}
361 	memcpy(cpl_env.orig_tz.s,"TZ=",3);
362 	if (ptr)
363 		strcpy(cpl_env.orig_tz.s+3,ptr);
364 
365 	/* convert realm_prefix from string null terminated to str */
366 	if (cpl_env.realm_prefix.s) {
367 		/* convert the realm_prefix to lower cases */
368 		strlower( &cpl_env.realm_prefix );
369 	}
370 
371 	/* add space for one extra process */
372 	register_procs(1);
373 	/* add child to update local config framework structures */
374 	cfg_register_child(1);
375 
376 	return 0;
377 error:
378 	return -1;
379 }
380 
381 
382 
cpl_child_init(int rank)383 static int cpl_child_init(int rank)
384 {
385 	int pid;
386 
387 	if (rank==PROC_MAIN) {
388 		pid=fork_process(PROC_RPC, "CPL Aux", 1);
389 		if (pid<0)
390 			return -1; /* error */
391 		if(pid==0){
392 			/* child */
393 			/* initialize the config framework */
394 			if (cfg_child_init())
395 				return -1;
396 
397 			cpl_process(1);
398 		}
399 	}
400 
401 	if (rank==PROC_INIT || rank==PROC_MAIN || rank==PROC_TCP_MAIN)
402 		return 0; /* do nothing for the main process */
403 
404 	return cpl_db_init(&db_url, &db_table);
405 }
406 
407 
408 
cpl_process(int rank)409 static void cpl_process(int rank)
410 {
411 	cpl_aux_process( cpl_env.cmd_pipe[0], cpl_env.log_dir);
412 	exit(-1);
413 }
414 
415 
cpl_exit(void)416 static void cpl_exit(void)
417 {
418 	/* free the TZ orig */
419 	if (cpl_env.orig_tz.s)
420 		shm_free(cpl_env.orig_tz.s);
421 }
422 
423 
424 
build_user_AOR(str * username,str * domain,str * uh,int sip)425 static inline int build_user_AOR(str *username, str *domain, str *uh, int sip)
426 {
427 	unsigned char do_strip;
428 	char *p;
429 	int i;
430 
431 	/* calculate the len (without terminating \0) */
432 	uh->len = 4*(sip!=0) + username->len;
433 	do_strip = 0;
434 
435 	if (sip || cpl_env.use_domain) {
436 		/* do we need to strip realm prefix? */
437 		if (cpl_env.realm_prefix.len && cpl_env.realm_prefix.len<domain->len){
438 			for( i=cpl_env.realm_prefix.len-1 ; i>=0 ; i-- )
439 				if ( cpl_env.realm_prefix.s[i]!=tolower(domain->s[i]) )
440 					break;
441 			if (i==-1)
442 				do_strip = 1;
443 		}
444 		uh->len += 1 + domain->len - do_strip*cpl_env.realm_prefix.len;
445 	}
446 
447 	uh->s = (char*)shm_malloc( uh->len + 1 );
448 	if (!uh->s) {
449 		LM_ERR("no more shm memory.\n");
450 		return -1;
451 	}
452 
453 	/* build user@host */
454 	p = uh->s;
455 	if (sip) {
456 		memcpy( uh->s, "sip:", 4);
457 		p += 4;
458 	}
459 	/* user part */
460 	if (cpl_env.case_sensitive) {
461 		memcpy( p, username->s, username->len);
462 		p += username->len;
463 	} else {
464 		for(i=0;i<username->len;i++)
465 			*(p++) = tolower(username->s[i]);
466 	}
467 	if (sip || cpl_env.use_domain) {
468 		*(p++) = '@';
469 		/* host part in lower cases */
470 		for( i=do_strip*cpl_env.realm_prefix.len ; i< domain->len ; i++ )
471 			*(p++) = tolower(domain->s[i]);
472 	}
473 	*(p++) = 0;
474 
475 	/* sanity check */
476 	if (p-uh->s!=uh->len+1) {
477 		LM_CRIT("buffer overflow l=%d,w=%ld\n", uh->len,(long)(p-uh->s));
478 		return -1;
479 	}
480 	return 0;
481 }
482 
483 
484 
get_dest_user(struct sip_msg * msg,str * username,str * domain)485 static inline int get_dest_user(struct sip_msg *msg, str *username, str *domain)
486 {
487 	struct sip_uri uri;
488 
489 	/*  get the user_name from new_uri/RURI/To */
490 	LM_DBG("trying to get user from new_uri\n");
491 	if ( !msg->new_uri.s || parse_uri( msg->new_uri.s,msg->new_uri.len,&uri)<0
492 	|| !uri.user.len )
493 	{
494 		LM_DBG("trying to get user from R_uri\n");
495 		if ( parse_uri( msg->first_line.u.request.uri.s,
496 		msg->first_line.u.request.uri.len ,&uri)==-1 || !uri.user.len )
497 		{
498 			LM_DBG("trying to get user from To\n");
499 			if ( (!msg->to&&((parse_headers(msg,HDR_TO_F,0)==-1)||!msg->to))||
500 			parse_uri( get_to(msg)->uri.s, get_to(msg)->uri.len, &uri)<0
501 			|| !uri.user.len)
502 			{
503 				LM_ERR("unable to extract user name from RURI or To header!\n");
504 				return -1;
505 			}
506 		}
507 	}
508 	*username = uri.user;
509 	*domain = uri.host;
510 	return 0;
511 }
512 
513 
514 
get_orig_user(struct sip_msg * msg,str * username,str * domain)515 static inline int get_orig_user(struct sip_msg *msg, str *username, str *domain)
516 {
517 	struct to_body *from;
518 	struct sip_uri uri;
519 
520 	/* if it's outgoing -> get the user_name from From */
521 	/* parsing from header */
522 	LM_DBG("trying to get user from From\n");
523 	if ( parse_from_header( msg )==-1 ) {
524 		LM_ERR("unable to extract URI from FROM header\n");
525 		return -1;
526 	}
527 	from = (struct to_body*)msg->from->parsed;
528 	/* parse the extracted uri from From */
529 	if (parse_uri( from->uri.s, from->uri.len, &uri)||!uri.user.len) {
530 		LM_ERR("unable to extract user name from URI (From header)\n");
531 		return -1;
532 	}
533 	*username = uri.user;
534 	*domain = uri.host;
535 	return 0;
536 }
537 
538 
539 
540 /* Params:
541  *   str1 - as unsigned int - can be CPL_RUN_INCOMING or CPL_RUN_OUTGOING
542  *   str2 - as unsigned int - flags regarding state(less)|(ful)
543  *   str3 - URI in SPVE structure
544  */
cpl_invoke_script3(struct sip_msg * msg,char * str1,char * str2,char * str3)545 static int cpl_invoke_script3(struct sip_msg* msg, char* str1, char* str2, char *str3)
546 {
547 	struct cpl_interpreter  *cpl_intr;
548 	str  username = {0,0};
549 	str  domain = {0,0};
550 	str  uri = {0,0};
551 	sip_uri_t puri;
552 	str  loc;
553 	str  script;
554 
555 	/* get the user_name */
556 	if(str3==NULL) {
557 		if ( ((unsigned long)str1)&CPL_RUN_INCOMING ) {
558 			/* if it's incoming -> get the destination user name */
559 			if (get_dest_user( msg, &username, &domain)==-1)
560 				goto error0;
561 		} else {
562 			/* if it's outgoing -> get the origin user name */
563 			if (get_orig_user( msg, &username, &domain)==-1)
564 				goto error0;
565 		}
566 	} else {
567 		if(fixup_get_svalue(msg, (gparam_p)str3, &uri)!=0)
568 		{
569 			LM_ERR("invalid uri parameter");
570 			goto error0;
571 		}
572 		if (parse_uri(uri.s, uri.len, &puri) || !puri.user.len) {
573 			LM_ERR("unable to extract user name from URI param\n");
574 			return -1;
575 		}
576 		username = puri.user;
577 		domain = puri.host;
578 	}
579 
580 	/* get the script for this user */
581 	if (get_user_script(&username, cpl_env.use_domain?&domain:0,
582 	&script, &cpl_bin_col)==-1)
583 		goto error0;
584 
585 	/* has the user a non-empty script? if not, return normally, allowing the
586 	 * script execution to continue */
587 	if ( !script.s || !script.len )
588 		return 1;
589 
590 	/* build a new script interpreter */
591 	if ( (cpl_intr=new_cpl_interpreter(msg,&script))==0 )
592 		goto error1;
593 	/* set the flags */
594 	cpl_intr->flags =(unsigned int)((unsigned long)str1)|((unsigned long)str2);
595 	/* build user AOR */
596 	if (build_user_AOR( &username, &domain, &(cpl_intr->user), 0)!=0 )
597 		goto error2;
598 	/* for OUTGOING we need also the destination user for init. with him
599 	 * the location set */
600 	if ( ((unsigned long)str1)&CPL_RUN_OUTGOING ) {
601 		/* build user initial location -> get the destination user name */
602 		if (get_dest_user( msg, &username, &domain)==-1)
603 			goto error2;
604 		if (build_user_AOR( &username, &domain, &loc, 1)!=0 )
605 			goto error2;
606 		if (add_location( &(cpl_intr->loc_set), &loc, 0, 10, 0/*no dup*/)==-1)
607 			goto error2;
608 	}
609 
610 	/* run the script */
611 	switch (cpl_run_script( cpl_intr )) {
612 		case SCRIPT_DEFAULT:
613 			free_cpl_interpreter( cpl_intr );
614 			return 1; /* execution of ser's script will continue */
615 		case SCRIPT_END:
616 			free_cpl_interpreter( cpl_intr );
617 		case SCRIPT_TO_BE_CONTINUED:
618 			return 0; /* break the SER script */
619 		case SCRIPT_RUN_ERROR:
620 		case SCRIPT_FORMAT_ERROR:
621 			goto error2;
622 	}
623 
624 	return 1;
625 error2:
626 	free_cpl_interpreter( cpl_intr );
627 	return -1;
628 error1:
629 	shm_free(script.s);
630 error0:
631 	return -1;
632 }
633 
634 /* Params:
635  *   str1 - as unsigned int - can be CPL_RUN_INCOMING or CPL_RUN_OUTGOING
636  *   str2 - as unsigned int - flags regarding state(less)|(ful)
637  */
cpl_invoke_script(struct sip_msg * msg,char * str1,char * str2)638 static int cpl_invoke_script(struct sip_msg* msg, char* str1, char* str2)
639 {
640 	return  cpl_invoke_script3(msg, str1, str2, NULL);
641 }
642 
643 
644 #define CPL_SCRIPT          "script"
645 #define CPL_SCRIPT_LEN      (sizeof(CPL_SCRIPT)-1)
646 #define ACTION_PARAM        "action"
647 #define ACTION_PARAM_LEN    (sizeof(ACTION_PARAM)-1)
648 #define STORE_ACTION        "store"
649 #define STORE_ACTION_LEN    (sizeof(STORE_ACTION)-1)
650 #define REMOVE_ACTION       "remove"
651 #define REMOVE_ACTION_LEN   (sizeof(REMOVE_ACTION)-1)
652 
653 #define REMOVE_SCRIPT       0xcaca
654 #define STORE_SCRIPT        0xbebe
655 
656 #define CONTENT_TYPE_HDR      ("Content-Type: application/cpl-xml"CRLF)
657 #define CONTENT_TYPE_HDR_LEN  (sizeof(CONTENT_TYPE_HDR)-1)
658 
659 struct cpl_error {
660 	int   err_code;
661 	str   err_msg;
662 };
663 
664 static struct cpl_error bad_req = {400,str_init("Bad request")};
665 static struct cpl_error intern_err = {500,str_init("Internal server error")};
666 static struct cpl_error bad_cpl = {400,str_init("Bad CPL script")};
667 
668 static struct cpl_error *cpl_err = &bad_req;
669 
670 
do_script_action(struct sip_msg * msg,int action)671 static inline int do_script_action(struct sip_msg *msg, int action)
672 {
673 	str  body = {0,0};
674 	str  bin  = {0,0};
675 	str  log  = {0,0};
676 	str  username = {0,0};
677 	str  domain   = {0,0};
678 
679 	/* content-length (if present) */
680 	if ( !msg->content_length &&
681 	((parse_headers(msg,HDR_CONTENTLENGTH_F,0)==-1)||!msg->content_length)) {
682 		LM_ERR("no Content-Length hdr found!\n");
683 		goto error;
684 	}
685 	body.len = get_content_length( msg );
686 
687 	/* get the user name */
688 	if (get_dest_user( msg, &username, &domain)==-1)
689 		goto error;
690 
691 	/* we have the script and the user */
692 	switch (action) {
693 		case STORE_SCRIPT :
694 			/* check the len -> it must not be 0 */
695 			if (body.len==0) {
696 				LM_ERR("0 content-len found for store\n");
697 				goto error_1;
698 			}
699 			/* get the message's body */
700 			body.s = get_body( msg );
701 			if (body.s==0) {
702 				LM_ERR("cannot extract body from msg!\n");
703 				goto error_1;
704 			}
705 			/* now compile the script and place it into database */
706 			/* get the binary coding for the XML file */
707 			if ( encodeCPL( &body, &bin, &log)!=1) {
708 				cpl_err = &bad_cpl;
709 				goto error_1;
710 			}
711 
712 			/* write both the XML and binary formats into database */
713 			if (write_to_db( &username, cpl_env.use_domain?&domain:0,
714 			&body,&bin)!=1) {
715 				cpl_err = &intern_err;
716 				goto error_1;
717 			}
718 			break;
719 		case REMOVE_SCRIPT:
720 			/* check the len -> it must be 0 */
721 			if (body.len!=0) {
722 				LM_ERR("non-0 content-len found for remove\n");
723 				goto error_1;
724 			}
725 			/* remove the script for the user */
726 			if (rmv_from_db( &username, cpl_env.use_domain?&domain:0)!=1) {
727 				cpl_err = &intern_err;
728 				goto error_1;
729 			}
730 			break;
731 	}
732 
733 	if (log.s) pkg_free( log.s );
734 	return 0;
735 error_1:
736 	if (log.s) pkg_free( log.s );
737 error:
738 	return -1;
739 }
740 
741 
742 
do_script_download(struct sip_msg * msg)743 static inline int do_script_download(struct sip_msg *msg)
744 {
745 	str username  = {0,0};
746 	str domain = {0,0};
747 	str script = {0,0};
748 
749 	/* get the destination user name */
750 	if (get_dest_user( msg, &username, &domain)!=0)
751 		goto error;
752 
753 	/* get the user's xml script from the database */
754 	if (get_user_script( &username, cpl_env.use_domain?&domain:0,
755 	&script, &cpl_xml_col)==-1)
756 		goto error;
757 
758 	/* add a lump with content-type hdr */
759 	if (add_lump_rpl( msg, CONTENT_TYPE_HDR, CONTENT_TYPE_HDR_LEN,
760 	LUMP_RPL_HDR)==0) {
761 		LM_ERR("cannot build hdr lump\n");
762 		cpl_err = &intern_err;
763 		goto error;
764 	}
765 
766 	if (script.s!=0) {
767 		/* user has a script -> add a body lump */
768 		if ( add_lump_rpl( msg, script.s, script.len, LUMP_RPL_BODY)==0) {
769 			LM_ERR("cannot build body lump\n");
770 			cpl_err = &intern_err;
771 			goto error;
772 		}
773 		/* build_lump_rpl duplicates the added text, so free the original */
774 		shm_free( script.s );
775 	}
776 
777 	return 0;
778 error:
779 	if (script.s)
780 		shm_free(script.s);
781 	return -1;
782 }
783 
784 
785 
w_process_register(struct sip_msg * msg,char * str,char * str2)786 static int w_process_register(struct sip_msg* msg, char* str, char* str2)
787 {
788 	return cpl_process_register( msg, 0);
789 }
790 
791 
792 
w_process_register_norpl(struct sip_msg * msg,char * str,char * str2)793 static int w_process_register_norpl(struct sip_msg* msg, char* str,char* str2)
794 {
795 	return cpl_process_register( msg, 1);
796 }
797 
798 
799 
cpl_process_register(struct sip_msg * msg,int no_rpl)800 static int cpl_process_register(struct sip_msg* msg, int no_rpl)
801 {
802 	struct disposition *disp;
803 	struct disposition_param *param;
804 	int  ret;
805 	int  mime;
806 	int  *mimes;
807 
808 	/* make sure that is a REGISTER ??? */
809 
810 	/* here should be the CONTACT- hack */
811 
812 	/* is there a CONTENT-TYPE hdr ? */
813 	mime = parse_content_type_hdr( msg );
814 	if (mime==-1)
815 		goto error;
816 
817 	/* check the mime type */
818 	LM_DBG("Content-Type mime found %u, %u\n",
819 		mime>>16,mime&0x00ff);
820 	if ( mime && mime==(TYPE_APPLICATION<<16)+SUBTYPE_CPLXML ) {
821 		/* can be an upload or remove -> check for the content-purpose and
822 		 * content-action headers */
823 		LM_DBG("carrying CPL -> look at Content-Disposition\n");
824 		if (parse_content_disposition( msg )!=0) {
825 			LM_ERR("Content-Disposition missing or corrupted\n");
826 			goto error;
827 		}
828 		disp = get_content_disposition(msg);
829 		print_disposition( disp ); /* just for DEBUG */
830 		/* check if the type of disposition is SCRIPT */
831 		if (disp->type.len!=CPL_SCRIPT_LEN ||
832 		strncasecmp(disp->type.s,CPL_SCRIPT,CPL_SCRIPT_LEN) ) {
833 			LM_ERR("bogus message - Content-Type"
834 				"says CPL_SCRIPT, but Content-Disposition something else\n");
835 			goto error;
836 		}
837 		/* disposition type is OK -> look for action parameter */
838 		for(param=disp->params;param;param=param->next) {
839 			if (param->name.len==ACTION_PARAM_LEN &&
840 			!strncasecmp(param->name.s,ACTION_PARAM,ACTION_PARAM_LEN))
841 				break;
842 		}
843 		if (param==0) {
844 			LM_ERR("bogus message - "
845 				"Content-Disposition has no action param\n");
846 			goto error;
847 		}
848 		/* action param found -> check its value: store or remove */
849 		if (param->body.len==STORE_ACTION_LEN &&
850 		!strncasecmp( param->body.s, STORE_ACTION, STORE_ACTION_LEN)) {
851 			/* it's a store action -> get the script from body message and store
852 			 * it into database (CPL and BINARY format) */
853 			if (do_script_action( msg, STORE_SCRIPT)==-1)
854 				goto error;
855 		} else
856 		if (param->body.len==REMOVE_ACTION_LEN &&
857 		!strncasecmp( param->body.s, REMOVE_ACTION, REMOVE_ACTION_LEN)) {
858 			/* it's a remove action -> remove the script from database */
859 			if (do_script_action( msg, REMOVE_SCRIPT)==-1)
860 				goto error;
861 		} else {
862 			LM_ERR("unknown action <%.*s>\n",
863 				param->body.len,param->body.s);
864 			goto error;
865 		}
866 
867 		/* do I have to send to reply? */
868 		if (no_rpl)
869 			goto resume_script;
870 
871 		/* send a 200 OK reply back */
872 		cpl_fct.slb.freply( msg, 200, &cpl_ok_rpl);
873 		/* I send the reply and I don't want to return to script execution, so
874 		 * I return 0 to do break */
875 		goto stop_script;
876 	}
877 
878 	/* is there an ACCEPT hdr ? */
879 	if ( (ret=parse_accept_hdr(msg))<0)
880 		goto error;
881 	if (ret==0 || (mimes=get_accept(msg))==0 )
882 		/* accept header not present or no mimes found */
883 		goto resume_script;
884 
885 	/* looks if the REGISTER accepts cpl-xml or * */
886 	while (*mimes) {
887 		LM_DBG("accept mime found %u, %u\n",
888 			(*mimes)>>16,(*mimes)&0x00ff);
889 		if (*mimes==(TYPE_ALL<<16)+SUBTYPE_ALL ||
890 		*mimes==(TYPE_APPLICATION<<16)+SUBTYPE_CPLXML )
891 			break;
892 		mimes++;
893 	}
894 	if (*mimes==0)
895 		/* no accept mime that matched cpl */
896 		goto resume_script;
897 
898 	/* get the user name from msg, retrieve the script from db
899 	 * and appended to reply */
900 	if (do_script_download( msg )==-1)
901 		goto error;
902 
903 	/* do I have to send to reply? */
904 	if (no_rpl)
905 		goto resume_script;
906 
907 	/* send a 200 OK reply back */
908 	cpl_fct.slb.freply( msg, 200, &cpl_ok_rpl);
909 
910 stop_script:
911 	return 0;
912 resume_script:
913 	return 1;
914 error:
915 	/* send a error reply back */
916 	cpl_fct.slb.freply( msg, cpl_err->err_code, &cpl_err->err_msg);
917 	/* I don't want to return to script execution, so I return 0 to do break */
918 	return 0;
919 }
920