modules_s/cpl-c/cpl.c

00001 /*
00002  * $Id$
00003  *
00004  * Copyright (C) 2001-2003 FhG Fokus
00005  *
00006  * This file is part of ser, a free SIP server.
00007  *
00008  * ser is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version
00012  *
00013  * For a license to use the ser software under conditions
00014  * other than those described here, or to purchase support for this
00015  * software, please contact iptel.org by e-mail at the following addresses:
00016  *    info@iptel.org
00017  *
00018  * ser is distributed in the hope that it will be useful,
00019  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00020  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00021  * GNU General Public License for more details.
00022  *
00023  * You should have received a copy of the GNU General Public License
00024  * along with this program; if not, write to the Free Software
00025  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00026  *
00027  * History:
00028  * -------
00029  * 2003-03-11: New module interface (janakj)
00030  * 2003-03-16: flags export parameter added (janakj)
00031  * 2003-11-11: build_lump_rpl() removed, add_lump_rpl() has flags (bogdan)
00032  * 2004-06-06  updated to the new DB api (andrei)
00033  * 2004-06-14: all global variables merged into cpl_env and cpl_fct;
00034  *             case_sensitive and realm_prefix added for building AORs - see
00035  *             build_userhost (bogdan)
00036  * 2004-10-09: added process_register_norpl to allow register processing
00037  *             without sending the reply(bogdan) - based on a patch sent by
00038  *             Christopher Crawford
00039  */
00040 
00041 
00042 #include <stdio.h>
00043 #include <string.h>
00044 #include <sys/types.h>
00045 #include <sys/stat.h>
00046 #include <unistd.h>
00047 #include <fcntl.h>
00048 #include <signal.h>
00049 
00050 #include "../../mem/shm_mem.h"
00051 #include "../../mem/mem.h"
00052 #include "../../sr_module.h"
00053 #include "../../str.h"
00054 #include "../../ut.h"
00055 #include "../../dprint.h"
00056 #include "../../data_lump_rpl.h"
00057 #include "../../usr_avp.h"
00058 #include "../../parser/parse_uri.h"
00059 #include "../../parser/parse_from.h"
00060 #include "../../parser/parse_content.h"
00061 #include "../../parser/parse_disposition.h"
00062 #include "../../lib/srdb2/db.h"
00063 #include "../../cfg/cfg_struct.h"
00064 #include "cpl_run.h"
00065 #include "cpl_env.h"
00066 #include "cpl_db.h"
00067 #include "cpl_loader.h"
00068 #include "cpl_parser.h"
00069 #include "cpl_nonsig.h"
00070 #include "cpl_rpc.h"
00071 #include "loc_set.h"
00072 
00073 
00074 #define MAX_PROXY_RECURSE  10
00075 #define MAX_USERHOST_LEN    256
00076 
00077 
00078 /* modules param variables */
00079 static char *DB_URL        = 0;  /* database url */
00080 static char *DB_TABLE      = 0;  /* */
00081 static char *dtd_file      = 0;  /* name of the DTD file for CPL parser */
00082 static char *lookup_domain = 0;
00083 static pid_t aux_process   = 0;  /* pid of the private aux. process */
00084 static char *timer_avp     = 0;  /* name of variable timer AVP */
00085 
00086 
00087 struct cpl_enviroment    cpl_env = {
00088                 0, /* no cpl logging */
00089                 0, /* recurse proxy level is 0 */
00090                 0, /* no script route to be run before proxy */
00091                 6, /* nat flag */
00092                 0, /* user part is not case sensitive */
00093                 {0,0},   /* no domain prefix to be ignored */
00094                 {-1,-1}, /* communication pipe to aux_process */
00095                 {0,0},   /* original TZ \0 terminated "TZ=value" format */
00096                 0, /* udomain */
00097                 0, /* no branches on lookup */
00098                 0, /* timer avp type */
00099                 /*(int_str)*/{ 0 } /* timer avp name/ID */
00100 };
00101 
00102 struct cpl_functions  cpl_fct;
00103 
00104 
00105 MODULE_VERSION
00106 
00107 
00108 static int cpl_invoke_script (struct sip_msg* msg, char* str, char* str2);
00109 static int w_process_register(struct sip_msg* msg, char* str, char* str2);
00110 static int w_process_register_norpl(struct sip_msg* msg, char* str,char* str2);
00111 static int cpl_process_register(struct sip_msg* msg, int no_rpl);
00112 static int fixup_cpl_run_script(void** param, int param_no);
00113 static int cpl_init(void);
00114 static int cpl_child_init(int rank);
00115 static int cpl_exit(void);
00116 
00117 
00118 /*
00119  * Exported functions
00120  */
00121 static cmd_export_t cmds[] = {
00122         {"cpl_run_script",cpl_invoke_script,2,fixup_cpl_run_script,REQUEST_ROUTE},
00123         {"cpl_process_register",w_process_register,0,0,REQUEST_ROUTE},
00124         {"cpl_process_register_norpl",w_process_register_norpl,0,0,REQUEST_ROUTE},
00125         {0, 0, 0, 0, 0}
00126 };
00127 
00128 
00129 /*
00130  * Exported parameters
00131  */
00132 static param_export_t params[] = {
00133         {"cpl_db",         PARAM_STRING, &DB_URL      },
00134         {"cpl_table",      PARAM_STRING, &DB_TABLE    },
00135         {"cpl_dtd_file",   PARAM_STRING, &dtd_file    },
00136         {"proxy_recurse",  PARAM_INT,    &cpl_env.proxy_recurse  },
00137         {"proxy_route",    PARAM_INT,    &cpl_env.proxy_route    },
00138         {"nat_flag",       PARAM_INT,    &cpl_env.nat_flag       },
00139         {"log_dir",        PARAM_STRING, &cpl_env.log_dir        },
00140         {"case_sensitive", PARAM_INT,    &cpl_env.case_sensitive },
00141         {"realm_prefix",   PARAM_STR,    &cpl_env.realm_prefix   },
00142         {"lookup_domain",  PARAM_STRING, &lookup_domain          },
00143         {"lookup_append_branches", PARAM_INT, &cpl_env.lu_append_branches},
00144         {"timer_avp",      PARAM_STRING, &timer_avp   },
00145         {0, 0, 0}
00146 };
00147 
00148 
00149 struct module_exports exports = {
00150         "cpl-c",
00151         cmds,             /* Exported functions */
00152         cpl_rpc_methods,  /* RPC methods */
00153         params,           /* Exported parameters */
00154         cpl_init,         /* Module initialization function */
00155         (response_function) 0,
00156         (destroy_function) cpl_exit,
00157         0,
00158         (child_init_function) cpl_child_init /* per-child init function */
00159 };
00160 
00161 
00162 
00163 static int fixup_cpl_run_script(void** param, int param_no)
00164 {
00165         long flag;
00166 
00167         if (param_no==1) {
00168                 if (!strcasecmp( "incoming", *param))
00169                         flag = CPL_RUN_INCOMING;
00170                 else if (!strcasecmp( "outgoing", *param))
00171                         flag = CPL_RUN_OUTGOING;
00172                 else {
00173                         LOG(L_ERR,"ERROR:fixup_cpl_run_script: script directive \"%s\""
00174                                 " unknown!\n",(char*)*param);
00175                         return E_UNSPEC;
00176                 }
00177                 pkg_free(*param);
00178                 *param=(void*)flag;
00179                 return 0;
00180         } else if (param_no==2) {
00181                 if ( !strcasecmp("is_stateless", *param) ) {
00182                         flag = 0;
00183                 } else if ( !strcasecmp("is_stateful", *param) ) {
00184                         flag = CPL_IS_STATEFUL;
00185                 } else if ( !strcasecmp("force_stateful", *param) ) {
00186                         flag = CPL_FORCE_STATEFUL;
00187                 } else {
00188                         LOG(L_ERR,"ERROR:fixup_cpl_run_script: flag \"%s\" (second param)"
00189                                 " unknown!\n",(char*)*param);
00190                         return E_UNSPEC;
00191                 }
00192                 pkg_free(*param);
00193                 *param=(void*)flag;
00194         }
00195         return 0;
00196 }
00197 
00198 
00199 
00200 static int cpl_init(void)
00201 {
00202         bind_usrloc_t bind_usrloc;
00203         load_tm_f     load_tm;
00204         struct stat   stat_t;
00205         char *ptr;
00206         int val;
00207         str foo;
00208 
00209         LOG(L_INFO,"CPL - initializing\n");
00210 
00211         /* check the module params */
00212         if (DB_URL==0) {
00213                 LOG(L_CRIT,"ERROR:cpl_init: mandatory parameter \"cpl_db\" "
00214                         "found empty\n");
00215                 goto error;
00216         }
00217 
00218         if (DB_TABLE==0) {
00219                 LOG(L_CRIT,"ERROR:cpl_init: mandatory parameter \"cpl_table\" "
00220                         "found empty\n");
00221                 goto error;
00222         }
00223 
00224         if (cpl_env.proxy_recurse>MAX_PROXY_RECURSE) {
00225                 LOG(L_CRIT,"ERROR:cpl_init: value of proxy_recurse param (%d) exceeds "
00226                         "the maximum safety value (%d)\n",
00227                         cpl_env.proxy_recurse,MAX_PROXY_RECURSE);
00228                 goto error;
00229         }
00230 
00231         /* fix the timer_avp name */
00232         if (timer_avp) {
00233                 foo.s = timer_avp;
00234                 foo.len = strlen(foo.s);
00235                 if (parse_avp_spec(&foo,&cpl_env.timer_avp_type,&cpl_env.timer_avp,0)<0){
00236                         LOG(L_CRIT,"ERROR:cpl_init: invalid timer AVP specs \"%s\"\n",
00237                                 timer_avp);
00238                         goto error;
00239                 }
00240                 if (cpl_env.timer_avp_type&AVP_NAME_STR && cpl_env.timer_avp.s.s==foo.s) {
00241                         cpl_env.timer_avp.s = foo;
00242                 }
00243         }
00244 
00245         if (dtd_file==0) {
00246                 LOG(L_CRIT,"ERROR:cpl_init: mandatory parameter \"cpl_dtd_file\" "
00247                         "found empty\n");
00248                 goto error;
00249         } else {
00250                 /* check if the dtd file exists */
00251                 if (stat( dtd_file, &stat_t)==-1) {
00252                         LOG(L_ERR,"ERROR:cpl_init: checking file \"%s\" status failed;"
00253                                 " stat returned %s\n",dtd_file,strerror(errno));
00254                         goto error;
00255                 }
00256                 if ( !S_ISREG( stat_t.st_mode ) ) {
00257                         LOG(L_ERR,"ERROR:cpl_init: dir \"%s\" is not a regular file!\n",
00258                                 dtd_file);
00259                         goto error;
00260                 }
00261                 if (access( dtd_file, R_OK )==-1) {
00262                         LOG(L_ERR,"ERROR:cpl_init: checking file \"%s\" for permissions "
00263                                 "failed; access returned %s\n",dtd_file,strerror(errno));
00264                         goto error;
00265                 }
00266         }
00267 
00268         if (cpl_env.log_dir==0) {
00269                 LOG(L_INFO,"INFO:cpl_init: log_dir param found void -> logging "
00270                         " disabled!\n");
00271         } else {
00272                 if ( strlen(cpl_env.log_dir)>MAX_LOG_DIR_SIZE ) {
00273                         LOG(L_ERR,"ERROR:cpl_init: dir \"%s\" has a too long name :-(!\n",
00274                                 cpl_env.log_dir);
00275                         goto error;
00276                 }
00277                 /* check if the dir exists */
00278                 if (stat( cpl_env.log_dir, &stat_t)==-1) {
00279                         LOG(L_ERR,"ERROR:cpl_init: checking dir \"%s\" status failed;"
00280                                 " stat returned %s\n",cpl_env.log_dir,strerror(errno));
00281                         goto error;
00282                 }
00283                 if ( !S_ISDIR( stat_t.st_mode ) ) {
00284                         LOG(L_ERR,"ERROR:cpl_init: dir \"%s\" is not a directory!\n",
00285                                 cpl_env.log_dir);
00286                         goto error;
00287                 }
00288                 if (access( cpl_env.log_dir, R_OK|W_OK )==-1) {
00289                         LOG(L_ERR,"ERROR:cpl_init: checking dir \"%s\" for permissions "
00290                                 "failed; access returned %s\n",
00291                                 cpl_env.log_dir, strerror(errno));
00292                         goto error;
00293                 }
00294         }
00295 
00296         /* import the TM auto-loading function */
00297         if ( !(load_tm=(load_tm_f)find_export("load_tm", NO_SCRIPT, 0))) {
00298                 LOG(L_ERR, "ERROR:cpl_c:cpl_init: cannot import load_tm\n");
00299                 goto error;
00300         }
00301         /* let the auto-loading function load all TM stuff */
00302         if (load_tm( &(cpl_fct.tmb) )==-1)
00303                 goto error;
00304 
00305         /* bind the SL API */
00306         if (sl_load_api(&cpl_fct.slb)!=0) {
00307                 LM_ERR("cannot bind to SL API\n");
00308                 return -1;
00309         }
00310 
00311         /* bind to usrloc module if requested */
00312         if (lookup_domain) {
00313                 /* import all usrloc functions */
00314                 bind_usrloc = (bind_usrloc_t)find_export("ul_bind_usrloc", 1, 0);
00315                 if (!bind_usrloc) {
00316                         LOG(L_ERR, "ERROR:cpl_c:cpl_init: Can't bind usrloc\n");
00317                         goto error;
00318                 }
00319                 if (bind_usrloc( &(cpl_fct.ulb) ) < 0) {
00320                         LOG(L_ERR, "ERROR:cpl_c:cpl_init: importing usrloc failed\n");
00321                         goto error;
00322                 }
00323                 /* convert lookup_domain from char* to udomain_t* pointer */
00324                 if (cpl_fct.ulb.register_udomain( lookup_domain, &cpl_env.lu_domain)
00325                 < 0) {
00326                         LOG(L_ERR, "ERROR:cpl_c:cpl_init: Error while registering domain "
00327                                 "<%s>\n",lookup_domain);
00328                         goto error;
00329                 }
00330         } else {
00331                 LOG(L_NOTICE,"NOTICE:cpl_init: no lookup_domain given -> disable "
00332                         " lookup node\n");
00333         }
00334 
00335         /* build a pipe for sending commands to aux process */
00336         if ( pipe( cpl_env.cmd_pipe )==-1 ) {
00337                 LOG(L_CRIT,"ERROR:cpl_init: cannot create command pipe: %s!\n",
00338                         strerror(errno) );
00339                 goto error;
00340         }
00341         /* set the writing non blocking */
00342         if ( (val=fcntl(cpl_env.cmd_pipe[1], F_GETFL, 0))<0 ) {
00343                 LOG(L_ERR,"ERROR:cpl_init: getting flags from pipe[1] failed: fcntl "
00344                         "said %s!\n",strerror(errno));
00345                 goto error;
00346         }
00347         if ( fcntl(cpl_env.cmd_pipe[1], F_SETFL, val|O_NONBLOCK) ) {
00348                 LOG(L_ERR,"ERROR:cpl_init: setting flags to pipe[1] failed: fcntl "
00349                         "said %s!\n",strerror(errno));
00350                 goto error;
00351         }
00352 
00353         /* init the CPL parser */
00354         if (init_CPL_parser( dtd_file )!=1 ) {
00355                 LOG(L_ERR,"ERROR:cpl_init: init_CPL_parser failed!\n");
00356                 goto error;
00357         }
00358 
00359         /* make a copy of the original TZ env. variable */
00360         ptr = getenv("TZ");
00361         cpl_env.orig_tz.len = 3/*"TZ="*/ + (ptr?(strlen(ptr)+1):0);
00362         if ( (cpl_env.orig_tz.s=shm_malloc( cpl_env.orig_tz.len ))==0 ) {
00363                 LOG(L_ERR,"ERROR:cpl_init: no more shm mem. for saving TZ!\n");
00364                 goto error;
00365         }
00366         memcpy(cpl_env.orig_tz.s,"TZ=",3);
00367         if (ptr)
00368                 strcpy(cpl_env.orig_tz.s+3,ptr);
00369 
00370         /* convert realm_prefix from string null terminated to str */
00371         if (cpl_env.realm_prefix.s) {
00372                 /* convert the realm_prefix to lower cases */
00373                 strlower( &cpl_env.realm_prefix );
00374         }
00375 
00376         /* Register a child process that will keep updating
00377          * its local configuration */
00378         cfg_register_child(1);
00379 
00380         return 0;
00381 error:
00382         return -1;
00383 }
00384 
00385 
00386 
00387 static int cpl_child_init(int rank)
00388 {
00389         pid_t pid;
00390 
00391         /* don't do anything for main process and TCP manager process */
00392         if (rank==PROC_INIT || rank==PROC_MAIN || rank==PROC_TCP_MAIN)
00393                 return 0;
00394 
00395         /* only child 1 will fork the aux process */
00396         if (rank==1) {
00397                 pid = fork();
00398                 if (pid==-1) {
00399                         LOG(L_CRIT,"ERROR:cpl_child_init(%d): cannot fork: %s!\n",
00400                                 rank, strerror(errno));
00401                         goto error;
00402                 } else if (pid==0) {
00403                         /* I'm the child */
00404 
00405                         /* initialize the config framework */
00406                         if (cfg_child_init()) goto error;
00407 
00408                         cpl_aux_process( cpl_env.cmd_pipe[0], cpl_env.log_dir);
00409                 } else {
00410                         LOG(L_INFO,"INFO:cpl_child_init(%d): I just gave birth to a child!"
00411                                 " I'm a PARENT!!\n",rank);
00412                         /* I'm the parent -> remember the pid */
00413                         aux_process = pid;
00414                 }
00415         }
00416 
00417         return cpl_db_init(DB_URL, DB_TABLE);
00418 error:
00419         return -1;
00420 }
00421 
00422 
00423 
00424 static int cpl_exit(void)
00425 {
00426         /* free the TZ orig */
00427         if (cpl_env.orig_tz.s)
00428                 shm_free(cpl_env.orig_tz.s);
00429 
00430         /* if still running, stop the aux process */
00431         if (!aux_process) {
00432                 LOG(L_INFO,"INFO:cpl_c:cpl_exit: aux process hasn't been created -> "
00433                         "nothing to kill :-(\n");
00434         } else {
00435                 /* kill the auxiliary process */
00436                 if (kill( aux_process, SIGKILL)!=0) {
00437                         if (errno==ESRCH) {
00438                                 LOG(L_INFO,"INFO:cpl_c:cpl_exit: seems that my child is "
00439                                         "already dead! :-((\n");
00440                         } else {
00441                                 LOG(L_ERR,"ERROR:cpl_c:cpl_exit: killing the aux. process "
00442                                         "failed! kill said: %s\n",strerror(errno));
00443                                 return -1;
00444                         }
00445                 } else {
00446                         LOG(L_INFO,"INFO:cl_c:cpl_exit: I have blood on my hands!! I just"
00447                                 " killed my own child!");
00448                 }
00449         }
00450         return 0;
00451 }
00452 
00453 
00454 
00455 #define BUILD_UH_SHM      (1<<0)
00456 #define BUILD_UH_ADDSIP   (1<<1)
00457 
00458 static inline int build_userhost(struct sip_uri *uri, str *uh, int flg)
00459 {
00460         static char buf[MAX_USERHOST_LEN];
00461         unsigned char do_strip;
00462         char *p;
00463         int i;
00464 
00465         /* do we need to strip realm prefix? */
00466         do_strip = 0;
00467         if (cpl_env.realm_prefix.len && cpl_env.realm_prefix.len<uri->host.len) {
00468                 for( i=cpl_env.realm_prefix.len-1 ; i>=0 ; i-- )
00469                         if ( cpl_env.realm_prefix.s[i]!=((uri->host.s[i])|(0x20)) )
00470                                 break;
00471                 if (i==-1)
00472                         do_strip = 1;
00473         }
00474 
00475         /* calculate the len (without terminating \0) */
00476         uh->len = 4*((flg&BUILD_UH_ADDSIP)!=0) + uri->user.len + 1 +
00477                 uri->host.len - do_strip*cpl_env.realm_prefix.len;
00478         if (flg&BUILD_UH_SHM) {
00479                 uh->s = (char*)shm_malloc( uh->len + 1 );
00480                 if (!uh->s) {
00481                         LOG(L_ERR,"ERROR:cpl-c:build_userhost: no more shm memory.\n");
00482                         return -1;
00483                 }
00484         } else {
00485                 uh->s = buf;
00486                 if ( uh->len > MAX_USERHOST_LEN ) {
00487                         LOG(L_ERR,"ERROR:cpl-c:build_userhost: user+host longer than %d\n",
00488                                 MAX_USERHOST_LEN);
00489                         return -1;
00490                 }
00491         }
00492 
00493         /* build user@host */
00494         p = uh->s;
00495         if (flg&BUILD_UH_ADDSIP) {
00496                 memcpy( uh->s, "sip:", 4);
00497                 p += 4;
00498         }
00499         /* user part */
00500         if (cpl_env.case_sensitive) {
00501                 memcpy( p, uri->user.s, uri->user.len);
00502                 p += uri->user.len;
00503         } else {
00504                 for(i=0;i<uri->user.len;i++)
00505                         *(p++) = (0x20)|(uri->user.s[i]);
00506         }
00507         *(p++) = '@';
00508         /* host part in lower cases */
00509         for( i=do_strip*cpl_env.realm_prefix.len ; i< uri->host.len ; i++ )
00510                 *(p++) = (0x20)|(uri->host.s[i]);
00511         *(p++) = 0;
00512 
00513         /* sanity check */
00514         if (p-uh->s!=uh->len+1) {
00515                 LOG(L_CRIT,"BUG:cpl-c:build_userhost: buffer overflow l=%d,w=%ld\n",
00516                         uh->len,(long)(p-uh->s));
00517                 return -1;
00518         }
00519         return 0;
00520 }
00521 
00522 
00523 
00524 static inline int get_dest_user(struct sip_msg *msg, str *uh, int flg)
00525 {
00526         struct sip_uri uri;
00527 
00528         /*  get the user_name from new_uri/RURI/To */
00529         DBG("DEBUG:cpl-c:get_dest_user: trying to get user from new_uri\n");
00530         if ( !msg->new_uri.s || parse_uri( msg->new_uri.s,msg->new_uri.len,&uri)==-1
00531         || !uri.user.len )
00532         {
00533                 DBG("DEBUG:cpl-c:get_dest_user: trying to get user from R_uri\n");
00534                 if ( parse_uri( msg->first_line.u.request.uri.s,
00535                 msg->first_line.u.request.uri.len ,&uri)==-1 || !uri.user.len )
00536                 {
00537                         DBG("DEBUG:cpl-c:get_dest_user: trying to get user from To\n");
00538                         if ( (!msg->to&&( (parse_headers(msg,HDR_TO_F,0)==-1) ||
00539                                         !msg->to)) ||
00540                                 parse_uri( get_to(msg)->uri.s, get_to(msg)->uri.len, &uri)==-1
00541                                 || !uri.user.len)
00542                         {
00543                                 LOG(L_ERR,"ERROR:cpl-c:get_dest_user: unable to extract user"
00544                                         " name from RURI or To header!\n");
00545                                 return -1;
00546                         }
00547                 }
00548         }
00549         return build_userhost( &uri, uh, flg);
00550 }
00551 
00552 
00553 
00554 static inline int get_orig_user(struct sip_msg *msg, str *uh, int flg)
00555 {
00556         struct to_body *from;
00557         struct sip_uri uri;
00558 
00559         /* if it's outgoing -> get the user_name from From */
00560         /* parsing from header */
00561         DBG("DEBUG:cpl-c:get_orig_user: trying to get user from From\n");
00562         if ( parse_from_header( msg )==-1 ) {
00563                 LOG(L_ERR,"ERROR:cpl-c:get_orig_user: unable to extract URI "
00564                         "from FROM header\n");
00565                 return -1;
00566         }
00567         from = (struct to_body*)msg->from->parsed;
00568         /* parse the extracted uri from From */
00569         if (parse_uri( from->uri.s, from->uri.len, &uri)||!uri.user.len) {
00570                 LOG(L_ERR,"ERROR:cpl-c:get_orig_user: unable to extract user name "
00571                         "from URI (From header)\n");
00572                 return -1;
00573         }
00574         return build_userhost( &uri, uh, flg);
00575 }
00576 
00577 
00578 
00579 /* Params:
00580  *   str1 - as unsigned int - can be CPL_RUN_INCOMING or CPL_RUN_OUTGOING
00581  *   str2 - as unsigned int - flags regarding state(less)|(ful)
00582  */
00583 static int cpl_invoke_script(struct sip_msg* msg, char* str1, char* str2)
00584 {
00585         struct cpl_interpreter  *cpl_intr;
00586         str  user;
00587         str  loc;
00588         str  script;
00589 
00590         /* get the user_name */
00591         if ( ((unsigned long)str1)&CPL_RUN_INCOMING ) {
00592                 /* if it's incoming -> get the destination user name */
00593                 if (get_dest_user( msg, &user, BUILD_UH_SHM)==-1)
00594                         goto error0;
00595         } else {
00596                 /* if it's outgoing -> get the origin user name */
00597                 if (get_orig_user( msg, &user, BUILD_UH_SHM)==-1)
00598                         goto error0;
00599         }
00600 
00601         /* get the script for this user */
00602         if (get_user_script(&user, &script, 1)==-1)
00603                 goto error1;
00604 
00605         /* has the user a non-empty script? if not, return normally, allowing ser to
00606          * continue its script */
00607         if ( !script.s || !script.len ) {
00608                 shm_free(user.s);
00609                 return 1;
00610         }
00611 
00612         /* build a new script interpreter */
00613         if ( (cpl_intr=new_cpl_interpreter(msg,&script))==0 )
00614                 goto error2;
00615         /* set the flags */
00616         cpl_intr->flags =(unsigned int)((unsigned long)str1)|((unsigned long)str2);
00617         /* attache the user */
00618         cpl_intr->user = user;
00619         /* for OUTGOING we need also the destination user for init. with him
00620          * the location set */
00621         if ( ((unsigned long)str1)&CPL_RUN_OUTGOING ) {
00622                 if (get_dest_user( msg, &loc,BUILD_UH_ADDSIP)==-1)
00623                         goto error3;
00624                 if (add_location( &(cpl_intr->loc_set), &loc,10,CPL_LOC_DUPL)==-1)
00625                         goto error3;
00626         }
00627 
00628         /* since the script interpretation can take some time, it will be better to
00629          * send a 100 back to prevent the UAC to retransmit
00630         if ( cpl_tmb.t_reply( msg, (int)100, "Running cpl script" )!=1 ) {
00631                 LOG(L_ERR,"ERROR:cpl_invoke_script: unable to send 100 reply!\n");
00632                 goto error3;
00633         }
00634         * this should be done from script - it's much sooner ;-) */
00635 
00636         /* run the script */
00637         switch (cpl_run_script( cpl_intr )) {
00638                 case SCRIPT_DEFAULT:
00639                         free_cpl_interpreter( cpl_intr );
00640                         return 1; /* execution of ser's script will continue */
00641                 case SCRIPT_END:
00642                         free_cpl_interpreter( cpl_intr );
00643                 case SCRIPT_TO_BE_CONTINUED:
00644                         return 0; /* break the SER script */
00645                 case SCRIPT_RUN_ERROR:
00646                 case SCRIPT_FORMAT_ERROR:
00647                         goto error3;
00648         }
00649 
00650         return 1;
00651 error3:
00652         free_cpl_interpreter( cpl_intr );
00653         return -1;
00654 error2:
00655         shm_free(script.s);
00656 error1:
00657         shm_free(user.s);
00658 error0:
00659         return -1;
00660 }
00661 
00662 
00663 
00664 #define CPL_SCRIPT          "script"
00665 #define CPL_SCRIPT_LEN      (sizeof(CPL_SCRIPT)-1)
00666 #define ACTION_PARAM        "action"
00667 #define ACTION_PARAM_LEN    (sizeof(ACTION_PARAM)-1)
00668 #define STORE_ACTION        "store"
00669 #define STORE_ACTION_LEN    (sizeof(STORE_ACTION)-1)
00670 #define REMOVE_ACTION       "remove"
00671 #define REMOVE_ACTION_LEN   (sizeof(REMOVE_ACTION)-1)
00672 
00673 #define REMOVE_SCRIPT       0xcaca
00674 #define STORE_SCRIPT        0xbebe
00675 
00676 #define CONTENT_TYPE_HDR      ("Content-Type: application/cpl-xml"CRLF)
00677 #define CONTENT_TYPE_HDR_LEN  (sizeof(CONTENT_TYPE_HDR)-1)
00678 
00679 struct cpl_error {
00680         int   err_code;
00681         char *err_msg;
00682 };
00683 
00684 static struct cpl_error bad_req = {400,"Bad request"};
00685 static struct cpl_error intern_err = {500,"Internal server error"};
00686 static struct cpl_error bad_cpl = {400,"Bad CPL script"};
00687 
00688 static struct cpl_error *cpl_err = &bad_req;
00689 
00690 
00691 static inline int do_script_action(struct sip_msg *msg, int action)
00692 {
00693         str  body = STR_NULL;
00694         str  user = STR_NULL;
00695         str  bin  = STR_NULL;
00696         str  log  = STR_NULL;
00697 
00698         /* content-length (if present) */
00699         if ( !msg->content_length &&
00700                         ((parse_headers(msg, HDR_CONTENTLENGTH_F, 0)==-1)
00701                          || !msg->content_length) )
00702         {
00703                 LOG(L_ERR,"ERROR:cpl-c:do_script_action: no Content-Length "
00704                         "hdr found!\n");
00705                 goto error;
00706         }
00707         body.len = get_content_length( msg );
00708 
00709         /* get the user name */
00710         if (get_dest_user( msg, &user, 0)==-1)
00711                 goto error;
00712 
00713         /* we have the script and the user */
00714         switch (action) {
00715                 case STORE_SCRIPT :
00716                         /* check the len -> it must not be 0 */
00717                         if (body.len==0) {
00718                                 LOG(L_ERR,"ERROR:cpl-c:do_script_action: 0 content-len found "
00719                                         "for store\n");
00720                                 goto error_1;
00721                         }
00722                         /* get the message's body */
00723                         body.s = get_body( msg );
00724                         if (body.s==0) {
00725                                 LOG(L_ERR,"ERROR:cpl-c:do_script_action: cannot extract "
00726                                         "body from msg!\n");
00727                                 goto error_1;
00728                         }
00729                         /* now compile the script and place it into database */
00730                         /* get the binary coding for the XML file */
00731                         if ( encodeCPL( &body, &bin, &log)!=1) {
00732                                 cpl_err = &bad_cpl;
00733                                 goto error_1;
00734                         }
00735 
00736                         /* write both the XML and binary formats into database */
00737                         if (write_to_db(user.s, &body, &bin)!=1) {
00738                                 cpl_err = &intern_err;
00739                                 goto error_1;
00740                         }
00741                         break;
00742                 case REMOVE_SCRIPT:
00743                         /* check the len -> it must be 0 */
00744                         if (body.len!=0) {
00745                                 LOG(L_ERR,"ERROR:cpl-c:do_script_action: non-0 content-len "
00746                                         "found for remove\n");
00747                                 goto error_1;
00748                         }
00749                         /* remove the script for the user */
00750                         if (rmv_from_db(user.s)!=1) {
00751                                 cpl_err = &intern_err;
00752                                 goto error_1;
00753                         }
00754                         break;
00755         }
00756 
00757         if (log.s) pkg_free( log.s );
00758         return 0;
00759 error_1:
00760         if (log.s) pkg_free( log.s );
00761 error:
00762         return -1;
00763 }
00764 
00765 
00766 
00767 static inline int do_script_download(struct sip_msg *msg)
00768 {
00769         str  user  = STR_NULL;
00770         str script = STR_NULL;
00771 
00772         /* get the destination user name */
00773         if (get_dest_user( msg, &user, 0)==-1)
00774                 goto error;
00775 
00776         /* get the user's xml script from the database */
00777         if (get_user_script(&user, &script, 0)==-1)
00778                 goto error;
00779 
00780         /* add a lump with content-type hdr */
00781         if (add_lump_rpl( msg, CONTENT_TYPE_HDR, CONTENT_TYPE_HDR_LEN,
00782         LUMP_RPL_HDR)==0) {
00783                 LOG(L_ERR,"ERROR:cpl-c:do_script_download: cannot build hdr lump\n");
00784                 cpl_err = &intern_err;
00785                 goto error;
00786         }
00787 
00788         if (script.s!=0) {
00789                 /*DBG("script len=%d\n--------\n%.*s\n--------\n",
00790                         script.len, script.len, script.s);*/
00791                 /* user has a script -> add a body lump */
00792                 if ( add_lump_rpl( msg, script.s, script.len, LUMP_RPL_BODY)==0) {
00793                         LOG(L_ERR,"ERROR:cpl-c:do_script_download: cannot build "
00794                                 "body lump\n");
00795                         cpl_err = &intern_err;
00796                         goto error;
00797                 }
00798                 /* build_lump_rpl duplicates the added text, so free the original */
00799                 shm_free( script.s );
00800         }
00801 
00802         return 0;
00803 error:
00804         if (script.s)
00805                 shm_free(script.s);
00806         return -1;
00807 }
00808 
00809 
00810 
00811 static int w_process_register(struct sip_msg* msg, char* str, char* str2)
00812 {
00813         return cpl_process_register( msg, 0);
00814 }
00815 
00816 
00817 
00818 static int w_process_register_norpl(struct sip_msg* msg, char* str,char* str2)
00819 {
00820         return cpl_process_register( msg, 1);
00821 }
00822 
00823 
00824 
00825 static int cpl_process_register(struct sip_msg* msg, int no_rpl)
00826 {
00827         struct disposition *disp;
00828         struct disposition_param *param;
00829         int  ret;
00830         int  mime;
00831         int  *mimes;
00832 
00833         /* make sure that is a REGISTER ??? */
00834 
00835         /* here should be the CONTACT- hack */
00836 
00837         /* is there a CONTENT-TYPE hdr ? */
00838         mime = parse_content_type_hdr( msg );
00839         if (mime==-1)
00840                 goto error;
00841 
00842         /* check the mime type */
00843         DBG("DEBUG:cpl_process_register: Content-Type mime found %u, %u\n",
00844                 mime>>16,mime&0x00ff);
00845         if ( mime && mime==(TYPE_APPLICATION<<16)+SUBTYPE_CPLXML ) {
00846                 /* can be an upload or remove -> check for the content-purpose and
00847                  * content-action headers */
00848                 DBG("DEBUG:cpl_process_register: carrying CPL -> look at "
00849                         "Content-Disposition\n");
00850                 if (parse_content_disposition( msg )!=0) {
00851                         LOG(L_ERR,"ERROR:cpl_process_register: Content-Disposition missing "
00852                                 "or corrupted\n");
00853                         goto error;
00854                 }
00855                 disp = get_content_disposition(msg);
00856                 print_disposition( disp ); /* just for DEBUG */
00857                 /* check if the type of disposition is SCRIPT */
00858                 if (disp->type.len!=CPL_SCRIPT_LEN ||
00859                 strncasecmp(disp->type.s,CPL_SCRIPT,CPL_SCRIPT_LEN) ) {
00860                         LOG(L_ERR,"ERROR:cpl_process_register: bogus message - Content-Type"
00861                                 "says CPL_SCRIPT, but Content-Disposition something else\n");
00862                         goto error;
00863                 }
00864                 /* disposition type is OK -> look for action parameter */
00865                 for(param=disp->params;param;param=param->next) {
00866                         if (param->name.len==ACTION_PARAM_LEN &&
00867                         !strncasecmp(param->name.s,ACTION_PARAM,ACTION_PARAM_LEN))
00868                                 break;
00869                 }
00870                 if (param==0) {
00871                         LOG(L_ERR,"ERROR:cpl_process_register: bogus message - "
00872                                 "Content-Disposition has no action param\n");
00873                         goto error;
00874                 }
00875                 /* action param found -> check its value: store or remove */
00876                 if (param->body.len==STORE_ACTION_LEN &&
00877                 !strncasecmp( param->body.s, STORE_ACTION, STORE_ACTION_LEN)) {
00878                         /* it's a store action -> get the script from body message and store
00879                          * it into database (CPL and BINARY format) */
00880                         if (do_script_action( msg, STORE_SCRIPT)==-1)
00881                                 goto error;
00882                 } else
00883                 if (param->body.len==REMOVE_ACTION_LEN &&
00884                 !strncasecmp( param->body.s, REMOVE_ACTION, REMOVE_ACTION_LEN)) {
00885                         /* it's a remove action -> remove the script from database */
00886                         if (do_script_action( msg, REMOVE_SCRIPT)==-1)
00887                                 goto error;
00888                 } else {
00889                         LOG(L_ERR,"ERROR:cpl_process_register: unknown action <%.*s>\n",
00890                                 param->body.len,param->body.s);
00891                         goto error;
00892                 }
00893 
00894                 /* do I have to send to reply? */
00895                 if (no_rpl)
00896                         goto resume_script;
00897 
00898                 /* send a 200 OK reply back */
00899                 cpl_fct.slb.zreply( msg, 200, "OK");
00900                 /* I send the reply and I don't want to return to script execution, so
00901                  * I return 0 to do break */
00902                 goto stop_script;
00903         }
00904 
00905         /* is there an ACCEPT hdr ? */
00906         if ( (ret=parse_accept_hdr(msg))==-1)
00907                 goto error;
00908         if (ret==0 || (mimes=get_accept(msg))==0 )
00909                 /* accept header not present or no mimes found */
00910                 goto resume_script;
00911 
00912         /* looks if the REGISTER accepts cpl-xml or * */
00913         while (*mimes) {
00914                 DBG("DEBUG: accept mime found %u, %u\n",
00915                         (*mimes)>>16,(*mimes)&0x00ff);
00916                 if (*mimes==(TYPE_ALL<<16)+SUBTYPE_ALL ||
00917                 *mimes==(TYPE_APPLICATION<<16)+SUBTYPE_CPLXML )
00918                         break;
00919                 mimes++;
00920         }
00921         if (*mimes==0)
00922                 /* no accept mime that matched cpl */
00923                 goto resume_script;
00924 
00925         /* get the user name from msg, retrieve the script from db
00926          * and appended to reply */
00927         if (do_script_download( msg )==-1)
00928                 goto error;
00929 
00930         /* do I have to send to reply? */
00931         if (no_rpl)
00932                 goto resume_script;
00933 
00934         /* send a 200 OK reply back */
00935         cpl_fct.slb.zreply( msg, 200, "OK");
00936 
00937 stop_script:
00938         return 0;
00939 resume_script:
00940         return 1;
00941 error:
00942         /* send a error reply back */
00943         cpl_fct.slb.zreply( msg, cpl_err->err_code, cpl_err->err_msg);
00944         /* I don't want to return to script execution, so I return 0 to do break */
00945         return 0;
00946 }
00947 
00948 
00949