modules_k/cpl-c/cpl.c

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