summaryrefslogblamecommitdiffstats
path: root/route.c
blob: 3caed2bf69d97da3a111f8844cbf4731b0db4a35 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11










                                                                                






                                                                             
                                                       


                                                                             





                                                                                                                                    

                                                                                                     







































                                                                                                                                                               

                                                                                                                                                                                        


















                                                                                                      
                                                                                            

                                                                                   
                                                         
                                                                                          












                                                                                                                                





                                                                                                







                                                                                                                                                     
                                                                                                     



                                                                                                          

                                                                                                                                                                          
                       

                                                                                                   
































                                                                                                                                                     


                                                                                                                                                                        


















































                                                                                                                                   


                                                                                


                                                                            


                                                                                                     


                                                                           

                                          
                                                                               
                        

                                                                                    

                                          
                                                                                                            


                                                                                              


                                                                                      





                                                                                                                                         
                                                                                                                                                                                                                                                 






                                                                                                                                                           
                                                                                                                                                                                                                                                 
                              

                                                                                                                                         
                                                                                          
                                                                  





                                                                                                                                            
                                    























                                                                                                                          
                                                     


































                                                                                                                          
                                                                                                         








                                                                                                           



                                                                            










                                                                                               
                                                                                                           
                                                                                 























                                                                                                                
                                                                              










                                          
                   
                      
                                            



                                                                   
                                           












                                                                                              
                    


                                                             
                                          






                                                                       
                



                                                                        

                                                                                    


                                 
                                                                      




                                                         
                               




                                                                   
                                                                              
                              
                                       

















                                                                                                     
                              

                                              
                                            

                                                         
                                                 
                                                        
                                                         
                                                                    

                                                  
                                                            
                                                                       


                                                              
                                                                      

                                          
                                                   

                                                                  
                                                            

                                                                          
                                                                  
                                                                             

                                                          
                                                                                

                                                  
                                                                          

                                          
                                                              

                                  
                                                            













                                                       
                        

                                                         
                             


                                                
                                     








                                                                           
                                                          





















                                                                                                                                                               
                                                                          










                                                             
                                       



                                                                                            
                                              







                                                                                     
                                                                   



                                                                        

                                                                                                               
















                                                                                                            

                                                                                                             






















                                                                                                            

                                                                                                            
















                                                                                                            

                                                                                                       
















                                                                                                            

                                                                                                        










                                                                                                            
                                                                                                       































                                                                                                                                                                        
                                                                               


                   
                         

                           

                                         



                                            
                                         
                                    
                                                 


                                              
                                              









                                                                         

                                          


                                    
                                 
                            
                                         



                                                                           
                                      










































                                                                          
                                               


                                                                 

                                             






                                                                                              

                                            







                                                                                                     
                                                            











                                                       
                                             

                                


















                                         
                                







                                                      
                                         





                                                                  
                                                      


                                                                                      
                                                      





                                                                                    
                                                                                       
                                                                              














                                                                                
                                

                                         
                                        






                                                                                             
                                                            






















                                                                                            

                                                                  






                                                                                                                                                            
                                                                                               
                                  










                                                               
                                


                                         

                                                                              
                          







                                                       
                                            

                                            
                                                                                                     




                                                                                         
                                             


                                                                                                                   
                                                 





                                                                                                                                                                             
                                                      



                                                                        
                                                            




                                                                                          

                                                                      


                                                                                                                                                       
                                                



                                                                                                                                                   

                                                


                                                                                                                                                 
                                                







                                                                                                                                

                                                           






                                                                                                                           

                                                                                      
                                  



                                                             
                                                       
                                                    
                                              













                                                                               
                                                                     


                                                                                  
                                                           



                                                                      
                                                
                                            
                                                                             


                                                                                          
                                                                   




                                                                                    
                                                                              



                                                                                                            
                                                              









                                                                                                                                       
                                                                 






                                                                                                                                          

                                                                              




                                                                                                                       
                                                                            



                                                                                                                          

                                                                              




                                                                                                                     
                                                                           



                                                                                                                        

                                                                               




                                                                                                                
                                                                            



                                                                                                                   

                                                                              




                                                                                                                    
                                                                                 












                                                                                                                       
                                                                     



                                                                                                            
                                                              



                                                                                            
                                                
                                            
                                                                             



                                                                                                                    
                                                                         



                                                                                                       
                                                                            


                                                                                                                                          
                                                             




                                                                                              
                                                                                   

                                                              
                                                                            
                                                                                                 




















                                                                                             
                                      











                                                                                                        
                                      





                                                                                                         




































                                                                                                    


                                                                            
                                                                                



                                                                                   
                                                              


                                                                                               
                                                



                                                                                                                     
                                                           

                                                         
                                                                     











                                                                                                                                                   
                                        





                                                                        
                                        












                                                                                                                                       
                                















                                                                                  
                                                                                                          











                                                                                 

                                                                                  






                                                                  
                                                      


                                                                          
                                                           

                                             
                                                                                    
                          














                                                       
                                

                                             
                                                                                                     











                                                                                         
                                                       



                                                                         
                                                             



                                                                                 
                                                                     
                                                                       



                                                                                                         
                                                                               

                                                                        


                                                                                                        

                                                


                                                                                                                                           
                                                






                                                                                                                           

                                                      


                                                                                    
                                                                     



                                                                                  
                                                           


                                                                      
                                                  


                                                                                              
                                                          



                                                                                                                                 
                                                 





                                                           
                                                







                                                            

                                                            






                                                                                                                           

                                                                                         
                                  




                                                         
                                                        







                                                                    
                                                                     


                                                                                  
                                                           










                                                                       
                                                                     





                                                                                                                    

                                                                     





                                                                      
                                                    


                                                                                 

                                                                     













                                                                
                                                    



                                                          
                                                   
                                              
                                                                     



                                                                                                            
                                                              


                                                                                            
                                                                                        
                                                                               
                                                                          


                                                                                          
                                                                             


                                                                                             
                                                                           


                                                                                           
                                                                                





                                                                                                
                                                                              
                                                                               
                                                                       


                                                                                    
                                                                         





                                                                                         
                                                                                      
                                                                               
                                                                         


                                                                                      
                                                                        


                                                                                     
                                                                        


                                                                                     
                                                                                     


                                                                                              
                                                                                       


                                                                                                
                                                                                             





                                                                                                      
                                                                                 
                                                                               
                                                                              


                                                                                          
                                                                       





                                                                                            




























































                                                                                            
                                                                                     
                                                                               
                                                                      


                                                                                          
                                                                      


                                                                                          
                                                                       


                                                                                           
                                                                      





                                                                                             
                                                                                
                                                                               
                                                                          


                                                                                          
                                                                             


                                                                                             
                                                                           


                                                                                           
                                                                                





                                                                                                
                                                                                 
                                                                               
                                                                      


                                                                         
                                                                     





                                                                                                    
                                                                                    









                                                                                                                                                     
                                        










                                                                        
                                        











                                                                                                                                                     
                                        











                                                
                             














                                                                                                           
                             










                                      
                                                        


                                                      

                                                       























                                                                                   
                                         
                                      

                                                                                
                                                                         


                                        

                                                                       
                                           
                                       

                                        
                                            

                                  

                                 
                                                                                                                            
                                                                                                                               
                                                                                                                         

                                                                     
                     




                                                                              
                             



                                                                                                   
                                             
                                                  
                                                     



                                                         
                                                    


                                                         

                                                             
                                                                                                                













                                                                 



                                                         
                                                     
                                                      


                                                   
                                                          










































                                                                                  
                                                                               



                                                   
                                                                               







                                                         

                                                         



                                                                               

                                                         



                                                          

                                                         



                                                           

                                                         



                                                                 

                                                         

















                                                                                 
                                           







                                                      
                                      
     


                                                               
      








                                                                                                                                                        










                                                                                  
                                 







                                                   




                                                                                                            




                                                   
                                                                                                 
                                              
                                                                                      

                                                            
                                                  


                                                                                                                            
                                                                                        


                                                                      
                                                               






                                                                                  
                                                                

                                                                                             
                                        







                                                                                            
                                                  

                                                                                                                 
                                                                                      








                                                                    
                                                  

                                                                                                                 
                                                                                      








                                                                      
                                                

                                                            
                                                  





                                                                                                                 


                                                                          


                                                   


                                                     
                                              
                                                                                                                     
                                                      
                                                            
                                 
                                                                       
                                                   
                                                                           







                                                                                                                            
                                                                             




                                                                           
                                                                                   
                                                                                          
                                                                                           

















                                                                                                                    
                                                                               
                                                                                 
                                                                           

                                                                                  
                                                



                                                            

                                                                                             








                                                                                             
                                                                                  
                                                                                 

                                                                                                         





                                                                                                     
                                                                                                            



























                                                                                                                                 
                                             
                                                                     
                                                                          












                                                                                             






                                                                                                         

                                                              
                                 

                                                              
                                                           
                                                 
                                                                           

                              
                                 




                                                                


                                                                 
                                                                                                                     
         









                                                                                                  
                      










































                                                    
                                     

             
                      




























                                         
/*****************************************************************************\ 
**                                                                           **
** PBX4Linux                                                                 **
**                                                                           **
**---------------------------------------------------------------------------**
** Copyright: Andreas Eversberg                                              **
**                                                                           **
** match processing of routing configuration                                 **
**                                                                           **
\*****************************************************************************/ 

#include "main.h"


struct route_ruleset	*ruleset_first;		/* first entry */
struct route_ruleset	*ruleset_main;		/* pointer to main ruleset */

struct cond_defs cond_defs[] = {
	{ "extern",	MATCH_EXTERN,	COND_TYPE_NULL,
	  "extern", "Matches if call is from external port (no extension)."},
	{ "intern",	MATCH_INTERN,COND_TYPE_NULL,
	  "intern", "Matches if call is from an extension."},
	{ "port",	MATCH_PORT,	COND_TYPE_INTEGER,
	  "port=<number>[-<number>][,...]", "Matches if call is received from given port(s). NOT INTERFACE!"},
	{ "interface",	MATCH_INTERFACE,COND_TYPE_STRING,
	  "interface=<interface>[,...]", "Matches if call is received from given interface(s). NOT PORTS!"},
	{ "callerid",	MATCH_CALLERID,	COND_TYPE_STRING,
	  "callerid=<digits>[-<digits>][,...]", "Matches if caller ID matches or begins with the given (range(s) of) prefixes(s)."},
	{ "callerid2",	MATCH_CALLERID2,COND_TYPE_STRING,
	  "callerid2=<digits>[-<digits>][,...]", "Matches the second caller ID (network provided)."},
	{ "extension",	MATCH_EXTENSION,COND_TYPE_STRING,
	  "extension=<digits>[-<digits>][,...]", "Matches if caller calls from given (range(s) of) extension(s)."},
	{ "dialing",	MATCH_DIALING,	COND_TYPE_STRING,
	  "dialing=<digits>[-<digits>][,...]", "Matches if caller has dialed the given (range(s) of) digits at least."},
	{ "enblock",	MATCH_ENBLOCK,	COND_TYPE_NULL,
	  "enblock", "Matches if caller dialed en block. (Dial the number before pick up.)"},
	{ "overlap",	MATCH_OVERLAP,	COND_TYPE_NULL,
	  "overlap", "Matches if caller dialed digit by digit. (Dial the number after pick up.)"},
	{ "anonymous",	MATCH_ANONYMOUS,COND_TYPE_NULL,
	  "anonymous", "Matches if caller uses restricted caller ID or if not available."},
	{ "visible",	MATCH_VISIBLE,	COND_TYPE_NULL,
	  "visible", "Matches if caller ID is presented and if available."},
	{ "unknown",	MATCH_UNKNOWN,	COND_TYPE_NULL,
	  "unknown", "Matches if no ID is available from caller."},
	{ "available",	MATCH_AVAILABLE,COND_TYPE_NULL,
	  "available", "Matches if ID is available from caller."},
	{ "fake",	MATCH_FAKE,	COND_TYPE_NULL,
	  "fake", "Matches if caller ID is not screened and may be faked by caller."},
	{ "real",	MATCH_REAL,	COND_TYPE_NULL,
	  "real", "Matches if caller ID is screend and so it is the real caller's ID."},
	{ "redirected",	MATCH_REDIRECTED,COND_TYPE_NULL,
	  "redirected", "Matches if caller has been redirected."},
	{ "direct",	MATCH_DIRECT	,COND_TYPE_NULL,
	  "direct", "Matches if caller did not come from an redirection."},
	{ "redirid",	MATCH_REDIRID	,COND_TYPE_STRING,
	  "redirid=<digits>[-<digits>][,...]", "Matches if the caller has been redirected by the given (range(s) of) ID(s) or prefix(es))"},
	{ "time",	MATCH_TIME,	COND_TYPE_TIME,
	  "time=<minutes>[-<minutes>][,...]", "Matches if the caller calls within the given (range(s) of) time(s). (e.g. 0700-1900)"},
	{ "mday",	MATCH_MDAY,	COND_TYPE_MDAY,
	  "mday=<day>[-<day>][,...]", "Matches if the caller calls within the given (range(s) of) day(s) of the month. (1..31)"},
	{ "month",	MATCH_MONTH,	COND_TYPE_MONTH,
	  "month=<month>[-<month>][,...]", "Matches if the caller calls within the given (range(s) of) month(s). (1=January..12=December)"},
	{ "year",	MATCH_YEAR,	COND_TYPE_YEAR,
	  "year=<year>[-<year>][,...]", "Matches if the caller calls within the given (range(s) of) year(s). (1970..2106)"},
	{ "wday",	MATCH_WDAY,	COND_TYPE_WDAY,
	  "wday=<day>[-<day>][,...]", "Matches if the caller calls within the given (range(s) of) weekday(s). (1=Monday..7=Sunday)"},
	{ "capability",	MATCH_CAPABILITY, COND_TYPE_CAPABILITY,
	  "capability=speech|audio|video|digital-restricted|digital-unrestricted|digital-unrestricted-tones[,...]", "Matches the given bearer capability(s)."},
	{ "infolayer1",	MATCH_INFOLAYER1, COND_TYPE_INTEGER,
	  "infolayer1=<value>[,...]", "Matches the given information layer 1. (2=u-Law, 3=a-law, see info layer 1 in bearer capability.)"},
	{ "hlc",	MATCH_HLC,	COND_TYPE_HLC,
	  "hlc=telephony|faxg2g3|faxg4|teletex1|teletex2|teletex3|videotex1|videotex2|telex|mhs|osi|maintenance|management|audiovisual[,...]", "Matches the high layer capability(s)."},
	{ "file",	MATCH_FILE,	COND_TYPE_STRING,
	  "file=<path>[,...]", "Mathes is the given file exists and if the first character is '1'."},
	{ "execute",	MATCH_EXECUTE,	COND_TYPE_STRING,
	  "execute=<command>[,...]","Matches if the return value of the given command is greater 0."},
	{ "default",	MATCH_DEFAULT,	COND_TYPE_NULL,
	  "default","Matches if no further dialing could match."},
	{ "timeout",	MATCH_TIMEOUT,	COND_TYPE_INTEGER,
	  "timeout=<seconds>","Matches if the ruleset was entered AFTER given seconds."},
	{ "free",	MATCH_FREE,	COND_TYPE_IFATTR,
	  "free=<interface>:<channel>","Matches if the given minimum of channels are free."},
	{ "notfree",	MATCH_NOTFREE,	COND_TYPE_IFATTR,
	  "notfree=<interface>:<channel>","Matches if NOT the given minimum of channels are free."},
	{ "blocked",	MATCH_DOWN,	COND_TYPE_STRING,
	  "blocked=<interfaces>[,...]","Matches if all of the given interfaces are blocked."},
	{ "idle",	MATCH_UP,	COND_TYPE_STRING,
	  "idle=<interface>[,...]","Matches if any of the given interfaces is idle."},
	{ "busy",	MATCH_BUSY,	COND_TYPE_STRING,
	  "busy=<extension>[,...]","Matches if any of the given extension is busy."},
	{ "notbusy",	MATCH_IDLE,	COND_TYPE_STRING,
	  "notbusy=<extension>[,...]","Matches if any of the given extension is not busy."},
	{ "remote",	MATCH_REMOTE,	COND_TYPE_STRING,
	  "remote=<application name>","Matches if remote application is running."},
	{ "notremote",	MATCH_NOTREMOTE,COND_TYPE_STRING,
	  "notremote=<application name>","Matches if remote application is not running."},
	{ NULL, 0, 0, NULL}
};

struct param_defs param_defs[] = {
	{ PARAM_PROCEEDING,
	  "proceeding",	PARAM_TYPE_NULL,
	  "proceeding", "Will set the call into 'proceeding' state to prevent dial timeout."},
	{ PARAM_ALERTING,
	  "alerting",	PARAM_TYPE_NULL,
	  "alerting", "Will set the call into 'altering' state."},
	{ PARAM_CONNECT,
	  "connect",	PARAM_TYPE_NULL,
	  "connect", "Will complete the call before processing the action. Audio path for external calls will be established."},
	{ PARAM_EXTENSION,
	  "extension",	PARAM_TYPE_STRING,
	  "extension=<digits>", "Give extension name (digits) to relate this action to."},
	{ PARAM_EXTENSIONS,
	  "extensions",	PARAM_TYPE_STRING,
	  "extensions=<extension>[,<extension>[,...]]", "One or more extensions may be given."},
	{ PARAM_PREFIX,
	  "prefix",	PARAM_TYPE_STRING,
	  "prefix=<digits>", "Add prefix in front of the dialed number."},
	{ PARAM_CAPA,
	  "capability",	PARAM_TYPE_CAPABILITY,
	  "capability=speech|audio|video|digital-restricted|digital-unrestricted|digital-unrestricted-tones", "Alter the service type of the call."},
	{ PARAM_BMODE,
	  "bmode",	PARAM_TYPE_BMODE,
	  "bmode=transparent|hdlc", "Alter the bchannel mode of the call. Use hdlc for data calls."},
	{ PARAM_INFO1,
	  "infolayer1",	PARAM_TYPE_INTEGER,
	  "infolayer1=<value>", "Alter the layer 1 information of a call. Use 3 for ALAW or 2 for uLAW."},
	{ PARAM_HLC,
	  "hlc",	PARAM_TYPE_HLC,
	  "hlc=telephony|faxg2g3|faxg4|teletex1|teletex2|teletex3|videotex1|videotex2|telex|mhs|osi|maintenance|management|audiovisual", "Alter the HLC identification."},
	{ PARAM_EXTHLC,
	  "exthlc",	PARAM_TYPE_HLC,
	  "exthlc=<value>", "Alter extended HLC value, see hlc. (Mainenance only, don't use it.)"},
	{ PARAM_PRESENT,
	  "present",	PARAM_TYPE_YESNO,
	  "present=yes|no", "Allow or restrict caller ID regardless what the caller wants."},
	{ PARAM_DIVERSION,
	  "diversion",	PARAM_TYPE_DIVERSION,
	  "diversion=cfu|cfnr|cfb|cfp", "Set diversion type."},
	{ PARAM_DEST,
	  "dest",	PARAM_TYPE_DESTIN,
	  "dest=<string>", "Destination number to divert to. Use 'vbox' to divert to vbox. (cfu,cfnr,cfb only)"},
	{ PARAM_SELECT,
	  "select",	PARAM_TYPE_NULL,
	  "select", "Lets the caller select the history of calls using keys '1'/'3' or '*'/'#'."},
	{ PARAM_DELAY,
	  "delay",	PARAM_TYPE_INTEGER,
	  "delay=<seconds>", "Number of seconds to delay."},
	{ PARAM_LIMIT,
	  "limit",	PARAM_TYPE_INTEGER,
	  "limit=<retries>", "Number of maximum retries."},
	{ PARAM_HOST,
	  "host",	PARAM_TYPE_STRING,
	  "host=<string>", "Name of remote VoIP host."},
	{ PARAM_PORT,
	  "port",	PARAM_TYPE_STRING,
	  "port=<value>", "Alternate port to use if 'host' is given."},
	{ PARAM_INTERFACES,
	  "interfaces",	PARAM_TYPE_STRING,
	  "interfaces=<interface>[,<interface>[,...]]", "Give one or a list of Interfaces to select a free channel from."},
	{ PARAM_ADDRESS,
	  "address",	PARAM_TYPE_STRING,
	  "address=<string>", "Complete VoIP address. ( [user@]host[:port] )"},
	{ PARAM_SAMPLE,
	  "sample",	PARAM_TYPE_STRING,
	  "sample=<file prefix>", "Filename of sample (current tone's dir) or full path to sample. ('.wav'/'.wave'/'.isdn' is added automatically."},
	{ PARAM_ANNOUNCEMENT,
	  "announcement",PARAM_TYPE_STRING,
	  "announcement=<file prefix>", "Filename of announcement (inside vbox recording dir) or full path to sample. ('.wav'/'.wave'/'.isdn' is added automatically."},
	{ PARAM_RULESET,
	  "ruleset",	PARAM_TYPE_STRING,
	  "ruleset=<name>", "Ruleset to go to."},
	{ PARAM_CAUSE,
	  "cause",	PARAM_TYPE_INTEGER,
	  "cause=<cause value>", "Cause value when disconnecting. (21=reject 1=unassigned 63=service not available)"},
	{ PARAM_LOCATION,
	  "location",	PARAM_TYPE_INTEGER,
	  "location=<location value>", "Location of cause value when disconnecting. (0=user 1=private network sering local user)"},
	{ PARAM_DISPLAY,
	  "display",	PARAM_TYPE_STRING,
	  "display=<text>", "Message to display on the caller's telephone. (internal only)"},
	{ PARAM_PORTS,
	  "ports",	PARAM_TYPE_INTEGER,
	  "ports=<port>[,<port>[,...]]", "ISDN port[s] to use."},
	{ PARAM_TPRESET,
	  "tpreset",	PARAM_TYPE_INTEGER,
	  "tpreset=<seconds>", "Preset of countdown timer."},
	{ PARAM_FILE,
	  "file",	PARAM_TYPE_STRING,
	  "file=<full path>", "Full path to file name."},
	{ PARAM_CONTENT,
	  "content",	PARAM_TYPE_STRING,
	  "content=<string>", "Content to write into file."},
	{ PARAM_APPEND,
	  "append",	PARAM_TYPE_NULL,
	  "append", "Will append to given file, rather than overwriting it."},
	{ PARAM_EXECUTE,
	  "execute",	PARAM_TYPE_STRING,
	  "execute=<full path>", "Full path to script/command name. (Dialed digits are the argument 1.)"},
	{ PARAM_PARAM,
	  "param",	PARAM_TYPE_STRING,
	  "param=<string>", "Optionally this parameter can be inserted as argument 1, others are shifted."},
	{ PARAM_TYPE,
	  "type",	PARAM_TYPE_TYPE,
	  "type=unknown|subscriber|national|international", "Type of number to dial, default is 'unknown'."},
	{ PARAM_COMPLETE,
	  "complete",	PARAM_TYPE_NULL,
	  "complete", "Indicates complete number as given by prefix. Proceeding of long distance calls may be faster."},
	{ PARAM_CALLERID,
	  "callerid",	PARAM_TYPE_STRING,
	  "callerid=<digits>", "Change caller ID to given string."},
	{ PARAM_CALLERIDTYPE,
	  "calleridtype",PARAM_TYPE_CALLERIDTYPE,
	  "calleridtype=[unknown|subscriber|national|international]", "Type of caller ID. For normal MSN use 'unknown'"},
	{ PARAM_CALLTO,
	  "callto",	PARAM_TYPE_STRING,
	  "callto=<digits>", "Where to call back. By default the caller ID is used."},
	{ PARAM_ROOM,
	  "room",	PARAM_TYPE_INTEGER,
	  "room=<digits>", "Conference room number, must be greater 0, as in real life."},
	{ PARAM_JINGLE,
	  "jingle",	PARAM_TYPE_NULL,
	  "jingle", "Conference members will hear a jingle if a member joins."},
	{ PARAM_TIMEOUT,
	  "timeout",	PARAM_TYPE_INTEGER,
	  "timeout=<seconds>", "Timeout before continue with next action."},
	{ PARAM_NOPASSWORD,
	  "nopassword",	PARAM_TYPE_NULL,
	  "nopassword", "Don't ask for password. Be sure to authenticate right via real caller ID."},
	{ PARAM_STRIP,
	  "strip",	PARAM_TYPE_NULL,
	  "strip", "Remove digits that were required to match this rule."},
	{ PARAM_APPLICATION,
	  "application",PARAM_TYPE_STRING,
	  "application=<name>", "Name of remote application to make call to."},
	{ PARAM_CONTEXT,
	  "context",	PARAM_TYPE_STRING,
	  "context=<context>", "Give context parameter to the remote application."},
	{ PARAM_EXTEN,
	  "exten",	PARAM_TYPE_STRING,
	  "exten=<extension>", "Give exten parameter to the remote application. (overrides dialed number)"},
	{ PARAM_ON,
	  "on",	PARAM_TYPE_STRING,
	  "on=[init|hangup]", "Defines if the action is executed on call init or on hangup."},
	{ PARAM_KEYPAD,
	  "keypad",	PARAM_TYPE_NULL,
	  "keypad", "Use 'keypad facility' for dialing, instead of 'called number'."},
	{ 0, NULL, 0, NULL, NULL}
};

struct action_defs action_defs[] = {
	{ ACTION_EXTERNAL,
	  "extern",	&EndpointAppPBX::action_init_call, &EndpointAppPBX::action_dialing_external, &EndpointAppPBX::action_hangup_call,
	  PARAM_CONNECT | PARAM_PREFIX | PARAM_COMPLETE | PARAM_TYPE | PARAM_CAPA | PARAM_BMODE | PARAM_INFO1 | PARAM_HLC | PARAM_EXTHLC | PARAM_PRESENT | PARAM_INTERFACES | PARAM_CALLERID | PARAM_CALLERIDTYPE | PARAM_KEYPAD | PARAM_TIMEOUT,
	  "Call is routed to extern number as dialed."},
	{ ACTION_INTERNAL,
	  "intern",	&EndpointAppPBX::action_init_call, &EndpointAppPBX::action_dialing_internal, &EndpointAppPBX::action_hangup_call,
	  PARAM_CONNECT | PARAM_EXTENSION | PARAM_TYPE | PARAM_CAPA | PARAM_BMODE | PARAM_INFO1 | PARAM_HLC | PARAM_EXTHLC | PARAM_PRESENT | PARAM_TIMEOUT,
	  "Call is routed to intern extension as given by the dialed number or specified by option."},
	{ ACTION_OUTDIAL,
	  "outdial",	&EndpointAppPBX::action_init_call, &EndpointAppPBX::action_dialing_external, &EndpointAppPBX::action_hangup_call,
	  PARAM_CONNECT | PARAM_PREFIX | PARAM_COMPLETE | PARAM_TYPE | PARAM_CAPA | PARAM_BMODE | PARAM_INFO1 | PARAM_HLC | PARAM_EXTHLC | PARAM_PRESENT | PARAM_INTERFACES | PARAM_CALLERID | PARAM_CALLERIDTYPE | PARAM_KEYPAD | PARAM_TIMEOUT,
	  "Same as 'extern'"},
	{ ACTION_REMOTE,
	  "remote",	&EndpointAppPBX::action_init_remote, &EndpointAppPBX::action_dialing_remote, &EndpointAppPBX::action_hangup_call,
	  PARAM_CONNECT | PARAM_APPLICATION | PARAM_CONTEXT | PARAM_EXTEN | PARAM_TIMEOUT,
	  "Call is routed to Remote application, like Asterisk."},
	{ ACTION_VBOX_RECORD,
	  "vbox-record",&EndpointAppPBX::action_init_call, &EndpointAppPBX::action_dialing_vbox_record, &EndpointAppPBX::action_hangup_call,
	  PARAM_CONNECT | PARAM_EXTENSION | PARAM_ANNOUNCEMENT | PARAM_TIMEOUT,
	  "Caller is routed to the voice box of given extension."},
	{ ACTION_PARTYLINE,
	  "partyline",&EndpointAppPBX::action_init_partyline, NULL, &EndpointAppPBX::action_hangup_call,
	  PARAM_ROOM | PARAM_JINGLE,
	  "Caller is participating the conference with the given room number."},
	{ ACTION_LOGIN,
	  "login",	NULL, &EndpointAppPBX::action_dialing_login, NULL,
	  PARAM_CONNECT | PARAM_EXTENSION | PARAM_NOPASSWORD,
	  "Log into the given extension. Password required."},
	{ ACTION_CALLERID,
	  "callerid",	&EndpointAppPBX::action_init_change_callerid, &EndpointAppPBX::action_dialing_callerid, NULL,
	  PARAM_CONNECT | PARAM_CALLERID | PARAM_CALLERIDTYPE | PARAM_PRESENT,
	  "Caller changes the caller ID for all calls."},
	{ ACTION_CALLERIDNEXT,
	  "calleridnext",&EndpointAppPBX::action_init_change_callerid, &EndpointAppPBX::action_dialing_calleridnext, NULL,
	  PARAM_CONNECT | PARAM_CALLERID | PARAM_CALLERIDTYPE | PARAM_PRESENT,
	  "Caller changes the caller ID for the next call."},
	{ ACTION_FORWARD,
	  "forward",	&EndpointAppPBX::action_init_change_forward, &EndpointAppPBX::action_dialing_forward, NULL,
	  PARAM_CONNECT | PARAM_DIVERSION | PARAM_DEST | PARAM_DELAY,
	  "Caller changes the diversion of given type to the given destination or voice box."},
	{ ACTION_REDIAL,
	  "redial",	&EndpointAppPBX::action_init_redial_reply, &EndpointAppPBX::action_dialing_redial, NULL,
	  PARAM_CONNECT | PARAM_SELECT,
	  "Caller redials. (last outgoing call(s))"},
	{ ACTION_REPLY,
	  "reply",	&EndpointAppPBX::action_init_redial_reply, &EndpointAppPBX::action_dialing_reply, NULL,
	  PARAM_CONNECT | PARAM_SELECT,
	  "Caller replies. (last incoming call(s))"},
	{ ACTION_POWERDIAL,
	  "powerdial",	NULL, &EndpointAppPBX::action_dialing_powerdial, NULL,
	  PARAM_CONNECT | PARAM_DELAY | PARAM_LIMIT | PARAM_TIMEOUT,
	  "Caller redials using powerdialing."},
	{ ACTION_CALLBACK,
	  "callback",	NULL, &EndpointAppPBX::action_dialing_callback, &EndpointAppPBX::action_hangup_callback,
	  PARAM_PROCEEDING | PARAM_ALERTING | PARAM_CONNECT | PARAM_EXTENSION | PARAM_DELAY | PARAM_CALLTO | PARAM_PREFIX,
	  "Caller will use the callback service. After disconnecting, the callback is triggered."},
	{ ACTION_ABBREV,
	  "abbrev",	NULL, &EndpointAppPBX::action_dialing_abbrev, NULL,
	  PARAM_CONNECT,
	  "Caller dials abbreviation."},
	{ ACTION_TEST,
	  "test",	NULL, &EndpointAppPBX::action_dialing_test, NULL,
	  PARAM_CONNECT | PARAM_PREFIX | PARAM_TIMEOUT,
	  "Caller dials test mode."},
	{ ACTION_PLAY,
	  "play",	&EndpointAppPBX::action_init_play, NULL, NULL,
	  PARAM_PROCEEDING | PARAM_ALERTING | PARAM_CONNECT | PARAM_SAMPLE | PARAM_TIMEOUT,
	  "Plays the given sample."},
	{ ACTION_VBOX_PLAY,
	  "vbox-play",	&EndpointAppPBX::action_init_vbox_play, &EndpointAppPBX::action_dialing_vbox_play, NULL,
	  PARAM_EXTENSION,
	  "Caller listens to her voice box or to given extension."},
	{ ACTION_CALCULATOR,
	  "calculator",	NULL, &EndpointAppPBX::action_dialing_calculator, NULL,
	  PARAM_CONNECT,
	  "Caller calls the calculator."},
	{ ACTION_TIMER,
	  "timer",	NULL, &EndpointAppPBX::action_dialing_timer, NULL,
	  PARAM_CONNECT | PARAM_TPRESET | PARAM_TIMEOUT,
	  NULL},
//	  "Caller calls the timer."},
	{ ACTION_GOTO,
	  "goto",	NULL, &EndpointAppPBX::action_dialing_goto, NULL,
	  PARAM_PROCEEDING | PARAM_ALERTING | PARAM_CONNECT | PARAM_RULESET | PARAM_STRIP | PARAM_SAMPLE,
	  "Jump to given ruleset and optionally play sample. Dialed digits are not flushed."},
	{ ACTION_MENU,
	  "menu",	NULL, &EndpointAppPBX::action_dialing_menu, NULL,
	  PARAM_CONNECT | PARAM_RULESET | PARAM_SAMPLE,
	  "Same as 'goto', but flushes all digits dialed so far."},
	{ ACTION_DISCONNECT,
	  "disconnect",	NULL, &EndpointAppPBX::action_dialing_disconnect, NULL,
	  PARAM_CONNECT | PARAM_CAUSE | PARAM_LOCATION | PARAM_SAMPLE | PARAM_DISPLAY,
	  "Caller gets disconnected optionally with given cause and given sample and given display text."},
	{ ACTION_RELEASE,
	  "release",	NULL, &EndpointAppPBX::action_dialing_release, NULL,
	  PARAM_CONNECT | PARAM_CAUSE | PARAM_LOCATION | PARAM_DISPLAY,
	  "Same as 'disconnect', but using RELEASE message on ISDN."},
	{ ACTION_DEFLECT,
	  "deflect",	NULL, &EndpointAppPBX::action_dialing_deflect, NULL,
	  PARAM_DEST,
	  NULL},
//	  "External call is deflected to the given destination within the telephone network."},
	{ ACTION_SETFORWARD,
	  "setforward",	NULL, &EndpointAppPBX::action_dialing_setforward, NULL,
	  PARAM_CONNECT | PARAM_DIVERSION | PARAM_DEST | PARAM_PORT,
	  NULL},
//	  "The call forward is set within the telephone network of the external line."},
	{ ACTION_EXECUTE,
	  "execute",	&EndpointAppPBX::action_init_execute, NULL, &EndpointAppPBX::action_hangup_execute,
	  PARAM_CONNECT | PARAM_EXECUTE | PARAM_PARAM | PARAM_ON | PARAM_TIMEOUT,
	  "Executes the given script file. The file must terminate quickly, because it will halt the PBX."},
	{ ACTION_FILE,
	  "file",	NULL, NULL, &EndpointAppPBX::action_hangup_file,
	  PARAM_CONNECT | PARAM_FILE | PARAM_CONTENT | PARAM_APPEND,
	  "Writes givent content to given file. If content is not given, the dialed digits are written."},
	{ ACTION_PICK,
	  "pick",	&EndpointAppPBX::action_init_pick, NULL, NULL,
	  PARAM_EXTENSIONS,
	  "Pick up a call that is ringing on any phone. Extensions may be given to limit the picking ability."},
	{ ACTION_PASSWORD,
	  "password",	NULL, &EndpointAppPBX::action_dialing_password, NULL,
	  0,
	  NULL},
	{ ACTION_PASSWORD_WRITE,
	  "password_wr",NULL, &EndpointAppPBX::action_dialing_password_wr, NULL,
	  0,
	  NULL},
	{ ACTION_NOTHING,
	  "nothing",	NULL, NULL, NULL,
	  PARAM_PROCEEDING | PARAM_ALERTING | PARAM_CONNECT | PARAM_TIMEOUT,
	  "does nothing. Usefull to wait for calls to be released completely, by giving timeout value."},
	{ ACTION_EFI,
	  "efi",	&EndpointAppPBX::action_init_efi, NULL, NULL,
	  PARAM_PROCEEDING | PARAM_ALERTING | PARAM_CONNECT,
	  "Elektronische Fernsprecher Identifikation - announces caller ID."},
	{ -1,
	  NULL, NULL, NULL, NULL, 0, NULL}
};


/* display documentation of rules */

void doc_rules(const char *name)
{
	int i, j;

	if (name) {
		i = 0;
		while(action_defs[i].name) {
			if (!strcasecmp(action_defs[i].name, name))
				break;
			i++;
		}
		if (!action_defs[i].name) {
			fprintf(stderr, "Given action '%s' unknown.\n", name);
			return;
		}
		name = action_defs[i].name;
	}

	printf("Syntax overview:\n");
	printf("----------------\n\n");
	printf("[ruleset]\n");
	printf("<condition> ...   : <action> [parameter ...]   [timeout=X : <action> ...]\n");
	printf("...\n");
	printf("Please refer to the documentation for description on rule format.\n\n");

	if (!name) {
		printf("Available conditions to match:\n");
		printf("------------------------------\n\n");
		i = 0;
		while(cond_defs[i].name) {
			printf("Usage: %s\n", cond_defs[i].doc);
			printf("%s\n\n", cond_defs[i].help);
			i++;
		}

		printf("Available actions with their parameters:\n");
		printf("----------------------------------------\n\n");
	} else {
		printf("Detailes parameter description of action:\n");
		printf("-----------------------------------------\n\n");
	}
	i = 0;
	while(action_defs[i].name) {
		if (name && !!strcmp(action_defs[i].name,name)) { /* not selected */
			i++;
			continue;
		}
		if (!action_defs[i].help) { /* not internal actions */
			i++;
			continue;
		}
		printf("Usage: %s", action_defs[i].name);
		j = 0;
		while(j < 64) {
			if ((1LL<<j) & action_defs[i].params)
				printf(" [%s]", param_defs[j].doc);
			j++;
		}
		printf("\n%s\n\n", action_defs[i].help);
		if (name) { /* only show parameter help for specific action */
			j = 0;
			while(j < 64) {
				if ((1LL<<j) & action_defs[i].params)
					printf("%s:\n\t%s\n", param_defs[j].doc, param_defs[j].help);
				j++;
			}
			printf("\n");
		}
		i++;
	}
}

void ruleset_free(struct route_ruleset *ruleset_start)
{
	struct route_ruleset *ruleset;
	struct route_rule *rule;
	struct route_cond *cond;
	struct route_action *action;
	struct route_param *param;

	while(ruleset_start) {
		ruleset = ruleset_start;
		ruleset_start = ruleset->next;
		while(ruleset->rule_first) {
			rule = ruleset->rule_first;
			ruleset->rule_first = rule->next;
			while(rule->cond_first) {
				cond = rule->cond_first;
				if (cond->string_value) {
					FREE(cond->string_value, 0);
					rmemuse--;
				}
				if (cond->string_value_to) {
					FREE(cond->string_value_to, 0);
					rmemuse--;
				}
				rule->cond_first = cond->next;
				FREE(cond, sizeof(struct route_cond));
				rmemuse--;
			}
			while(rule->action_first) {
				action = rule->action_first;
				rule->action_first = action->next;
				while(action->param_first) {
					param = action->param_first;
					action->param_first = param->next;
					if (param->string_value) {
						FREE(param->string_value, 0);
						rmemuse--;
					}
					FREE(param, sizeof(struct route_param));
					rmemuse--;
				}
				FREE(action, sizeof(struct route_action));
				rmemuse--;
			}
			FREE(rule, sizeof(struct route_rule));
			rmemuse--;
		}
		FREE(ruleset, sizeof(struct route_ruleset));
		rmemuse--;
	}
}

void ruleset_debug(struct route_ruleset *ruleset_start)
{
	struct route_ruleset	*ruleset;
	struct route_rule	*rule;
	struct route_cond	*cond;
	struct route_action	*action;
	struct route_param	*param;
	int			first;

	ruleset = ruleset_start;
	while(ruleset) {
		printf("Ruleset: '%s'\n", ruleset->name);
		rule = ruleset->rule_first;
		while(rule) {
			/* CONDITION */
			first = 1;
			cond = rule->cond_first;
			while(cond) {
				if (first)
					printf("    Condition:");
				else
					printf("    and       ");
				first = 0;
				printf(" %s", cond_defs[cond->index].name);
				if (cond->value_type != VALUE_TYPE_NULL)
					printf(" = ");
				next_cond_value:
				switch(cond->value_type) {
					case VALUE_TYPE_NULL:
					break;

					case VALUE_TYPE_INTEGER:
					printf("%d", cond->integer_value);
					break;

					case VALUE_TYPE_INTEGER_RANGE:
					printf("%d-%d", cond->integer_value, cond->integer_value_to);
					break;

					case VALUE_TYPE_STRING:
					printf("'%s'", cond->string_value);
					break;

					case VALUE_TYPE_STRING_RANGE:
					printf("'%s'-'%s'", cond->string_value, cond->string_value_to);
					break;

					default:
					printf("Software error: VALUE_TYPE_* %d not known in function '%s' line=%d", cond->value_type, __FUNCTION__, __LINE__);
				}
				if (cond->value_extension && cond->next) {
					cond = cond->next;
					printf(" or ");
					goto next_cond_value;
				}

				cond = cond->next;
				printf("\n");
			}

			/* ACTION */
			action = rule->action_first;
			while(action) {
				printf("    Action: %s\n", action_defs[action->index].name);
				/* PARAM */
				first = 1;
				param = action->param_first;
				while(param) {
					if (first)
						printf("    Param:");
					else
						printf("          ");
					first = 0;
					printf(" %s", param_defs[param->index].name);
					if (param->value_type != VALUE_TYPE_NULL)
						printf(" = ");
					switch(param->value_type) {
						case VALUE_TYPE_NULL:
						break;

						case VALUE_TYPE_INTEGER:
						if (param_defs[param->index].type == PARAM_TYPE_CALLERIDTYPE) {
							switch(param->integer_value) {
								case INFO_NTYPE_UNKNOWN:
								printf("unknown");
								break;
								case INFO_NTYPE_SUBSCRIBER:
								printf("subscriber");
								break;
								case INFO_NTYPE_NATIONAL:
								printf("national");
								break;
								case INFO_NTYPE_INTERNATIONAL:
								printf("international");
								break;
								default:
								printf("unknown(%d)", param->integer_value);
							}
							break;
						}
						if (param_defs[param->index].type == PARAM_TYPE_CAPABILITY) {
							switch(param->integer_value) {
								case INFO_BC_SPEECH:
								printf("speech");
								break;
								case INFO_BC_AUDIO:
								printf("audio");
								break;
								case INFO_BC_VIDEO:
								printf("video");
								break;
								case INFO_BC_DATARESTRICTED:
								printf("digital-restricted");
								break;
								case INFO_BC_DATAUNRESTRICTED:
								printf("digital-unrestricted");
								break;
								case INFO_BC_DATAUNRESTRICTED_TONES:
								printf("digital-unrestricted-tones");
								break;
								default:
								printf("unknown(%d)", param->integer_value);
							}
							break;
						}
						if (param_defs[param->index].type == PARAM_TYPE_DIVERSION) {
							switch(param->integer_value) {
								case INFO_DIVERSION_CFU:
								printf("cfu");
								break;
								case INFO_DIVERSION_CFNR:
								printf("cfnr");
								break;
								case INFO_DIVERSION_CFB:
								printf("cfb");
								break;
								case INFO_DIVERSION_CFP:
								printf("cfp");
								break;
								default:
								printf("unknown(%d)", param->integer_value);
							}
							break;
						}
						if (param_defs[param->index].type == PARAM_TYPE_TYPE) {
							switch(param->integer_value) {
								case INFO_NTYPE_UNKNOWN:
								printf("unknown");
								break;
								case INFO_NTYPE_SUBSCRIBER:
								printf("subscriber");
								break;
								case INFO_NTYPE_NATIONAL:
								printf("national");
								break;
								case INFO_NTYPE_INTERNATIONAL:
								printf("international");
								break;
								default:
								printf("unknown(%d)", param->integer_value);
							}
							break;
						}
						if (param_defs[param->index].type == PARAM_TYPE_YESNO) {
							switch(param->integer_value) {
								case 1:
								printf("yes");
								break;
								case 0:
								printf("no");
								break;
								default:
								printf("unknown(%d)", param->integer_value);
							}
							break;
						}
						if (param_defs[param->index].type == PARAM_TYPE_NULL) {
							break;
						}
						printf("%d", param->integer_value);
						break;

						case VALUE_TYPE_STRING:
						printf("'%s'", param->string_value);
						break;

						default:
						printf("Software error: VALUE_TYPE_* %d not known in function '%s' line=%d", param->value_type, __FUNCTION__, __LINE__);
					}
					param = param->next;
					printf("\n");
				}
				/* TIMEOUT */
				if (action->timeout)
					printf("    Timeout: %d\n", action->timeout);
				action = action->next;
			}
			printf("\n");
			rule = rule->next;
		}
		printf("\n");
		ruleset = ruleset->next;
	}
}


/*
 * parse ruleset
 */
static char *read_string(char *p, char *key, int key_size, const char *special)
{
	key[0] = 0;

	if (*p == '\"') {
		p++;
		/* quote */
		while(*p) {
			if (*p == '\"') {
				p++;
				*key = '\0';
				return(p);
			}
			if (*p == '\\') {
				p++;
				if (*p == '\0') {
					break;
				}
			}
			if (--key_size == 0) {
				UPRINT(key, "\001String too long.");
				return(p);
			}
			*key++ = *p++;
		}
		UPRINT(key, "\001Unexpected end of line inside quotes.");
		return(p);
	}

	/* no quote */
	while(*p) {
		if (strchr(special, *p)) {
			*key = '\0';
			return(p);
		}
		if (*p == '\\') {
			p++;
			if (*p == '\0') {
				UPRINT(key, "\001Unexpected end of line.");
				return(p);
			}
		}
		if (--key_size == 0) {
			UPRINT(key, "\001String too long.");
			return(p);
		}
		*key++ = *p++;
	}
	*key = '\0';
	return(p);
}
char ruleset_error[256];
struct route_ruleset *ruleset_parse(void)
{
//	char			from[128];
//	char			to[128];
	int			i;
	unsigned long long	j;
//	int			a,
//				b;
	#define			MAXNESTING 8
	FILE			*fp[MAXNESTING];
	char			filename[MAXNESTING][256];
	int			line[MAXNESTING];
	int			nesting = -1;
	char			buffer[1024],
				key[1024],
				key_to[1024],
				pointer[1024+1],
				*p;
	int			expecting = 1; /* 1 = expecting ruleset */
	int			index,
				value_type,
				integer,
				integer_to; /* condition index, .. */
	struct route_ruleset	*ruleset_start = NULL, *ruleset;
	struct route_ruleset	**ruleset_pointer = &ruleset_start;
	struct route_rule	*rule;
	struct route_rule	**rule_pointer = NULL;
	struct route_cond	*cond;
	struct route_cond	**cond_pointer = NULL;
	struct route_action	*action;
	struct route_action	**action_pointer = NULL;
	struct route_param	*param;
	struct route_param	**param_pointer = NULL;
	char			failure[256];
	unsigned long long	allowed_params;

	/* check the integrity of IDs for ACTION_* and PARAM_* */
	i = 0;
	while(action_defs[i].name) {
		if (action_defs[i].id != i) {
			PERROR("Software Error action '%s' must have id of %d, but has %d.\n",
				action_defs[i].name, i, action_defs[i].id);
			goto openerror;
		}
		i++;
	}
	i = 0; j = 1;
	while(param_defs[i].name) {
		if (param_defs[i].id != j) {
			PERROR("Software Error param '%s' must have id of 0x%llx, but has 0x%llx.\n",
				param_defs[i].name, j, param_defs[i].id);
			goto openerror;
		}
		i++;
		j<<=1;
	}

        SPRINT(filename[0], "%s/routing.conf", CONFIG_DATA);

        if (!(fp[0]=fopen(filename[0],"r")))
        {
                PERROR("Cannot open %s\n",filename[0]);
               	goto openerror;
        }
	nesting++;
	fduse++;

	go_leaf:
        line[nesting]=0;
	go_root:
        while((GETLINE(buffer, fp[nesting])))
        {
                line[nesting]++;
                p = buffer;

		/* remove tabs */
		while(*p) {
			if (*p < 32)
				*p = 32;
			p++;
		} 
		p = buffer;

                /* skip spaces, if any */
                while(*p == 32)
                {
                        if (*p == 0)
                                break;
                        p++;
		}

		/* skip comments */
		if (*p == '#') {
			p++;
			/* don't skip "define" */
			if (!!strncmp(p, "define", 6))
				continue;
			p+=6;
			if (*p != 32)
				continue;
			/* skip spaces */
			while(*p == 32) {
				if (*p == 0)
					break;
				p++;
			}
			p++;
			p = read_string(p, key, sizeof(key), " ");
			if (key[0] == 1) { /* error */
				SPRINT(failure, "Parsing Filename failed: %s", key+1);
				goto parse_error;
			}
			if (nesting == MAXNESTING-1) {
				SPRINT(failure, "'include' is nesting too deep.\n");
				goto parse_error;
			}
			if (key[0] == '/')
				SCPY(filename[nesting+1], key);
			else
	        		SPRINT(filename[nesting+1], "%s/%s", CONFIG_DATA, key);
		        if (!(fp[nesting+1]=fopen(filename[nesting+1],"r"))) {
				PERROR("Cannot open %s\n", filename[nesting+1]);
				goto parse_error;
			}
			fduse++;
			nesting++;
			goto go_leaf;
		}
		if (*p == '/') if (p[1] == '/')
			continue;

		/* skip empty lines */
		if (*p == 0)
			continue;

		/* expecting ruleset */
		if (expecting) {
			new_ruleset:
			/* expecting [ */
			if (*p != '[') {
				SPRINT(failure, "Expecting ruleset name starting with '['.");
				goto parse_error;
			}
			p++;

			/* reading ruleset name text */
			i = 0;
			while(*p>' ' && *p<127 && *p!=']') {
				if (*p>='A' && *p<='Z') *p = *p-'A'+'a'; /* lower case */
				key[i++] = *p++;
				if (i == sizeof(key)) i--; /* limit */
			}
			key[i] = 0;
			if (key[0] == '\0') {
				SPRINT(failure, "Missing ruleset name after '['.");
				goto parse_error;
			}

			/* expecting ] and nothing more */
			if (*p != ']') {
				SPRINT(failure, "Expecting ']' after ruleset name.");
				goto parse_error;
			}
			p++;
			if (*p != 0) {
				SPRINT(failure, "Unexpected character after ruleset name.");
				goto parse_error;
			}

			/* check for duplicate rulesets */
			ruleset = ruleset_start;
			while(ruleset) {
				if (!strcmp(ruleset->name, key)) {
					SPRINT(failure, "Duplicate ruleset '%s', already defined in file '%s' line %d.", key, ruleset->file, ruleset->line);
					goto parse_error;
				}
				ruleset = ruleset->next;
			}

			/* create ruleset */
			ruleset = (struct route_ruleset *)MALLOC(sizeof(struct route_ruleset));
			rmemuse++;
			*ruleset_pointer = ruleset;
			ruleset_pointer = &(ruleset->next);
			SCPY(ruleset->name, key);
			SCPY(ruleset->file, filename[nesting]);
			ruleset->line = line[nesting];
			rule_pointer = &(ruleset->rule_first);
			expecting = 0;
			continue;
		}

		/* for new ruleset [ */
		if (*p == '[') {
			goto new_ruleset;
		}

		/* Alloc memory for rule */
		rule = (struct route_rule *)MALLOC(sizeof(struct route_rule));
		rmemuse++;
		*rule_pointer = rule;
		rule_pointer = &(rule->next);
		cond_pointer = &(rule->cond_first);
		action_pointer = &(rule->action_first);
		SCPY(rule->file, filename[nesting]);
		rule->line = line[nesting];

		/* loop CONDITIONS */
		while(*p!=':' && *p!='\0') {
			/* read item text */
			i = 0;
			while((*p>='a' && *p<='z') || (*p>='A' && *p<='Z') || (*p>='0' && *p<='9')) {
				if (*p>='A' && *p<='Z') *p = *p-'A'+'a'; /* lower case */
				key[i++] = *p++;
				if (i == sizeof(key)) i--; /* limit */
			}
			key[i] = 0;
			if (key[0] == '\0') {
				SPRINT(failure, "Expecting condition item name or ':' for end of condition list.");
				goto parse_error;
			}
			if (*p!=' ' && *p!='=') {
				SPRINT(failure, "Illegal character '%c' after condition name '%s'. Expecting '=' for equation or ' ' to seperate condition items.", *p, key);
				goto parse_error;
			}

			/* check if condition exists */
			index = 0;
			while(cond_defs[index].name) {
				if (!strcmp(cond_defs[index].name, key))
					break;
				index++;
			}
			if (cond_defs[index].name == NULL) {
				SPRINT(failure, "Unknown condition item name '%s'.", key);
				goto parse_error;
			}

			/* items without values must not have any parameter */
			if (cond_defs[index].type == COND_TYPE_NULL) {
				if (*p == '=') {
					SPRINT(failure, "Condition item '%s' must not have any value. Don't use '=' for this type of condition.", key);
					goto parse_error;
				}
				if (*p != ' ') {
					SPRINT(failure, "Condition item '%s' must not have any value. Expecting ' ' or tab after item name.", key);
					goto parse_error;
				}
//				p++;
			} else {
				if (*p == ' ') {
					SPRINT(failure, "Condition item '%s' must have at least one value, '=' expected, and not a space.", key);
					goto parse_error;
				}
				if (*p != '=') {
					SPRINT(failure, "Condition item '%s' must have at least one value, '=' expected.", key);
					goto parse_error;
				}
				p++;
			}

			/* check for duplicate condition */
			cond = rule->cond_first;
			while(cond) {
				if (cond->index == index) {
					SPRINT(failure, "Duplicate condition '%s', use ',' to give multiple values.", key);
					goto parse_error;
				}
				cond = cond->next;
			}

			nextcondvalue:
			/* Alloc memory for item */
			cond = (struct route_cond *)MALLOC(sizeof(struct route_cond));
			rmemuse++;
			*cond_pointer = cond;
			cond_pointer = &(cond->next);
			cond->index = index;
			cond->match = cond_defs[index].match;
			switch(cond_defs[index].type) {
				case COND_TYPE_NULL:
				if (*p=='=') {
					SPRINT(failure, "Expecting no value.");
					goto parse_error;
				}
				value_type = VALUE_TYPE_NULL;
				break;

				/* parse all integer values/ranges */
				case COND_TYPE_INTEGER:
				case COND_TYPE_TIME:
				case COND_TYPE_MDAY:
				case COND_TYPE_MONTH:
				case COND_TYPE_WDAY:
				case COND_TYPE_YEAR:
				integer = integer_to = 0;
				if (*p==',' || *p==' ' || *p=='\0') {
					SPRINT(failure, "Missing integer value.");
					goto parse_error;
				}
				while(*p>='0' && *p<='9') {
					integer = integer*10 + *p-'0';
					p++;
				}
				value_type = VALUE_TYPE_INTEGER;
				if (*p == '-') {
					p++;
					if (*p==',' || *p==' ' || *p=='\0') {
						SPRINT(failure, "Missing integer value.");
						goto parse_error;
					}
					while(*p>='0' && *p<='9') {
						integer_to = integer_to*10 + *p-'0';
						p++;
					}
					value_type = VALUE_TYPE_INTEGER_RANGE;
				}
				if (cond_defs[index].type == COND_TYPE_TIME) {
					// Simon: i store the time as decimal, later i compare it correctly:
					// hours * 100 + minutes
					if (integer == 2400)
						integer = 0;
					if (integer >= 2400) {
						timeoutofrange1:
						SPRINT(failure, "Given time '%d' not in range 0000..2359 (or 2400 for 0000)", integer);
						goto parse_error;
					}
					if (integer%100 >= 60)
						goto timeoutofrange1;
					if (value_type == VALUE_TYPE_INTEGER)
						goto integer_done;
					if (integer_to == 2400)
						integer_to = 0;
					if (integer_to >= 2400) {
						timeoutofrange2:
						SPRINT(failure, "Given time '%d' not in range 0000..2359 (or 2400 for 0000)", integer_to);
						goto parse_error;
					}
					if (integer_to%100 >= 60)
						goto timeoutofrange2;
				}
				if (cond_defs[index].type == COND_TYPE_MDAY) {
					if (integer<1 || integer>31) {
						SPRINT(failure, "Given day-of-month '%d' not in range 1..31", integer);
						goto parse_error;
					} 
					if (value_type == VALUE_TYPE_INTEGER)
						goto integer_done;
					if (integer_to<1 || integer_to>31) {
						SPRINT(failure, "Given day-of-month '%d' not in range 1..31", integer_to);
						goto parse_error;
					} 
				}
				if (cond_defs[index].type == COND_TYPE_WDAY) {
					if (integer<1 || integer>7) {
						SPRINT(failure, "Given day-of-week '%d' not in range 1..7", integer);
						goto parse_error;
					} 
					if (value_type == VALUE_TYPE_INTEGER)
						goto integer_done;
					if (integer_to<1 || integer_to>7) {
						SPRINT(failure, "Given day-of-week '%d' not in range 1..7", integer_to);
						goto parse_error;
					} 
				}
				if (cond_defs[index].type == COND_TYPE_MONTH) {
					if (integer<1 || integer>12) {
						SPRINT(failure, "Given month '%d' not in range 1..12", integer);
						goto parse_error;
					} 
					if (value_type == VALUE_TYPE_INTEGER)
						goto integer_done;
					if (integer_to<1 || integer_to>12) {
						SPRINT(failure, "Given month '%d' not in range 1..12", integer_to);
						goto parse_error;
					} 
				}
				if (cond_defs[index].type == COND_TYPE_YEAR) {
					if (integer<1970 || integer>2106) {
						SPRINT(failure, "Given year '%d' not in range 1970..2106", integer);
						goto parse_error;
					} 
					if (value_type == VALUE_TYPE_INTEGER)
						goto integer_done;
					if (integer_to<1970 || integer_to>2106) {
						SPRINT(failure, "Given year '%d' not in range 1970..2106", integer_to);
						goto parse_error;
					} 
				}
				integer_done:
				cond->integer_value = integer;
				cond->integer_value_to = integer_to;
				cond->value_type = value_type;
				break;

				/* parse all string values/ranges */
				case COND_TYPE_STRING:
				key[0] = key_to[0] = '\0';
				if (*p==',' || *p==' ' || *p=='\0') {
					SPRINT(failure, "Missing string value, use \"\" for empty string.");
					goto parse_error;
				}
				p = read_string(p, key, sizeof(key), "-, ");
				if (key[0] == 1) { /* error */
					SPRINT(failure, "Parsing String failed: %s", key+1);
					goto parse_error;
				}
				value_type = VALUE_TYPE_STRING;
				if (*p == '-') {
					p++;
					if (*p==',' || *p==' ' || *p=='\0') {
						SPRINT(failure, "Missing string value, use \"\" for empty string.");
						goto parse_error;
					}
					p = read_string(p, key_to, sizeof(key_to), "-, ");
					if (key_to[0] == 1) { /* error */
						SPRINT(failure, "Parsing string failed: %s", key_to+1);
						goto parse_error;
					}
					value_type = VALUE_TYPE_STRING_RANGE;
					if (strlen(key) != strlen(key_to)) {
						SPRINT(failure, "Given range of strings \"%s\"-\"%s\" have unequal length.", key, key_to);
						goto parse_error;
					}
					if (key[0] == '\0') {
						SPRINT(failure, "Given range has no length.");
						goto parse_error;
					}
				}
				alloc_string:
				cond->string_value = (char *)MALLOC(strlen(key)+1);
				rmemuse++;
				UCPY(cond->string_value, key);
				if (value_type == VALUE_TYPE_STRING_RANGE) {
					cond->string_value_to = (char *)MALLOC(strlen(key_to)+1);
					rmemuse++;
					UCPY(cond->string_value_to, key_to);
					cond->comp_string = strcmp(key, key_to);
				}
				cond->value_type = value_type;
				break;

				/* parse service value */
				case COND_TYPE_CAPABILITY:
				if (!strncasecmp("speech", p, 6))
					cond->integer_value = INFO_BC_SPEECH;
				else if (!strncasecmp("audio", p, 5))
					cond->integer_value = INFO_BC_AUDIO;
				else if (!strncasecmp("video", p, 5))
					cond->integer_value = INFO_BC_VIDEO;
				else if (!strncasecmp("digital-restricted", p, 18))
					cond->integer_value = INFO_BC_DATARESTRICTED;
				else if (!strncasecmp("digital-unrestricted", p, 20))
					cond->integer_value = INFO_BC_DATAUNRESTRICTED;
				else if (!strncasecmp("digital-unrestricted-tones", p, 26))
					cond->integer_value = INFO_BC_DATAUNRESTRICTED_TONES;
				else {
					SPRINT(failure, "Given service type is invalid or misspelled.");
					goto parse_error;
				}
				cond->value_type = VALUE_TYPE_INTEGER;
				break;

				/* parse bmode value */
				case COND_TYPE_BMODE:
				if (!strncasecmp("transparent", p, 11))
					cond->integer_value = INFO_BMODE_CIRCUIT;
				else if (!strncasecmp("hdlc", p, 4))
					cond->integer_value = INFO_BMODE_PACKET;
				else {
					SPRINT(failure, "Given bchannel mode is invalid or misspelled.");
					goto parse_error;
				}
				cond->value_type = VALUE_TYPE_INTEGER;
				break;

				/* parse service value */
				case COND_TYPE_HLC:
				if (!strncasecmp("telephony", p, 9))
					cond->integer_value = INFO_HLC_TELEPHONY;
				else if (!strncasecmp("faxg2g3", p, 7))
					cond->integer_value = INFO_HLC_FAXG2G3;
				else if (!strncasecmp("faxg4", p, 5))
					cond->integer_value = INFO_HLC_FAXG4;
				else if (!strncasecmp("teletex1", p, 8))
					cond->integer_value = INFO_HLC_TELETEX1;
				else if (!strncasecmp("teletex2", p, 8))
					cond->integer_value = INFO_HLC_TELETEX2;
				else if (!strncasecmp("teletex3", p, 8))
					cond->integer_value = INFO_HLC_TELETEX3;
				else if (!strncasecmp("videotex1", p, 9))
					cond->integer_value = INFO_HLC_VIDEOTEX1;
				else if (!strncasecmp("videotex2", p, 9))
					cond->integer_value = INFO_HLC_VIDEOTEX2;
				else if (!strncasecmp("telex", p, 5))
					cond->integer_value = INFO_HLC_TELEX;
				else if (!strncasecmp("mhs", p, 3))
					cond->integer_value = INFO_HLC_MHS;
				else if (!strncasecmp("osi", p, 3))
					cond->integer_value = INFO_HLC_OSI;
				else if (!strncasecmp("maintenance", p, 11))
					cond->integer_value = INFO_HLC_MAINTENANCE;
				else if (!strncasecmp("management", p, 10))
					cond->integer_value = INFO_HLC_MANAGEMENT;
				else if (!strncasecmp("audiovisual", p, 11))
					cond->integer_value = INFO_HLC_AUDIOVISUAL;
				else {
					SPRINT(failure, "Given HLC type is invalid or misspelled.");
					goto parse_error;
				}
				cond->value_type = VALUE_TYPE_INTEGER;
				break;

				/* parse interface attribute <if>:<value> */
				case COND_TYPE_IFATTR:
				key[0] = key_to[0] = '\0';
				if (*p==':' || *p==',' || *p==' ' || *p=='\0') {
					SPRINT(failure, "Missing interface name.");
					goto parse_error;
				}
				p = read_string(p, key, sizeof(key), ":-, ");
				if (key[0] == 1) { /* error */
					SPRINT(failure, "Parsing interface failed: %s", key+1);
					goto parse_error;
				}
				if (*p != ':') {
					SPRINT(failure, "Expeciting kolon to seperate value behind interface name.");
					goto parse_error;
				}
				SCCAT(key, *p++);
				while(*p>='0' && *p<='9') {
					SCCAT(key, *p++);
				}
				if (*p!=',' && *p!=' ' && *p!='\0') {
					SPRINT(failure, "Invalid characters behind value.");
					goto parse_error;
				}
				value_type = VALUE_TYPE_STRING;
				goto alloc_string;
				break;

				default:
				SPRINT(failure, "Software error: COND_TYPE_* %d not parsed in function '%s'", cond_defs[index].type, __FUNCTION__);
				goto parse_error;
			}
			/* if we have another value for that item, we attach it */
			if (*p == ',') {
				p++;
				/* next item */
				cond->value_extension = 1;
				goto nextcondvalue;
			}
			/* to seperate the items, a space is required */
			if (*p != ' ') {
				SPRINT(failure, "Character '%c' not expected here. Use ',' to seperate multiple possible values.", *p);
				goto parse_error;
			}
                	/* skip spaces */
	                while(*p == 32)
	                {
	                        if (*p == 0)
	                                break;
	                        p++;
			}
		}

		/* we are done with CONDITIONS, so we expect the ACTION */
		if (*p != ':') {
			SPRINT(failure, "Expecting ':' after condition item(s).");
			goto parse_error;
		}
		p++;

		nextaction:
                /* skip spaces, if any */
                while(*p == 32)
                {
                        if (*p == 0)
                                break;
                        p++;
		}

		/* read action name */
		i = 0;
		while((*p>='a' && *p<='z') || (*p>='A' && *p<='Z') || (*p>='0' && *p<='9') || *p == '-') {
			if (*p>='A' && *p<='Z') *p = *p-'A'+'a'; /* lower case */
			key[i++] = *p++;
			if (i == sizeof(key)) i--; /* limit */
		}
		key[i] = 0;
		if (key[0] == '\0') {
			SPRINT(failure, "Expecting action name.");
			goto parse_error;
		}

		/* check if item exists */
		index = 0;
		while(action_defs[index].name) {
			if (!action_defs[index].help) { /* not internal actions */
				index++;
				continue;
			}
			if (!strcmp(action_defs[index].name, key))
				break;
			index++;
		}
		if (action_defs[index].name == NULL) {
			SPRINT(failure, "Unknown action name '%s'.", key);
			goto parse_error;
		}
		allowed_params = action_defs[index].params;

		/* alloc memory for action */
		action = (struct route_action *)MALLOC(sizeof(struct route_action));
		rmemuse++;
		*action_pointer = action;
		action_pointer = &(action->next);
		param_pointer = &(action->param_first);
		action->index = index;
		action->line = line[nesting];

               	/* skip spaces after param name */
	        while(*p == 32)
	        {
	        	if (*p == 0)
				break;
			p++;
		}

		/* loop PARAMS */
		while(*p != 0) {
			/* read param text */
			i = 0;
			while((*p>='a' && *p<='z') || (*p>='A' && *p<='Z') || (*p>='0' && *p<='9')) {
				if (*p>='A' && *p<='Z') *p = *p-'A'+'a'; /* lower case */
				key[i++] = *p++;
				if (i == sizeof(key)) i--; /* limit */
			}
			key[i] = 0;
			if (key[0] == '\0') {
				SPRINT(failure, "Expecting parameter name.");
				goto parse_error;
			}

			/* check if item exists */
			index = 0;
			while(param_defs[index].name) {
				if (!strcmp(param_defs[index].name, key))
					break;
				index++;
			}
			if (param_defs[index].name == NULL) {
				SPRINT(failure, "Unknown param name '%s'.", key);
				goto parse_error;
			}

			/* check if item is allowed for the action */
			if (!(param_defs[index].id & allowed_params)) {
				SPRINT(failure, "Param name '%s' exists, but not for this action.", key);
				goto parse_error;
			}

			/* params without values must not have any parameter */
			if (param_defs[index].type == PARAM_TYPE_NULL) {
				if (*p!=' ' && *p!='\0') {
					SPRINT(failure, "Parameter '%s' must not have any value.", key);
					goto parse_error;
				}
			} else {
				if (*p == ' ') {
					SPRINT(failure, "Parameter '%s' must have at least one value, '=' expected and not a space.", key);
					goto parse_error;
				}
				if (*p != '=') {
					SPRINT(failure, "Parameter '%s' must have at least one value, '=' expected.", key);
					goto parse_error;
				}
				p++;
			}

			/* special timeout value */
			if (!strcmp("timeout", key)) {
				if (action->timeout) {
					SPRINT(failure, "Duplicate timeout value.");
					goto parse_error;
				}
				if (*p==',' || *p==' ' || *p=='\0') {
					SPRINT(failure, "Missing integer value.");
					goto parse_error;
				}
				integer = 0;
				while(*p>='0' && *p<='9') {
					integer = integer*10 + *p-'0';
					p++;
				}
				if (integer < 1) {
					SPRINT(failure, "Expecting timeout value greater 0.");
					goto parse_error;
				}
				if (*p!=' ' && *p!='\0') {
					SPRINT(failure, "Character '%c' not expected here. Use ' ' to seperate parameters.", *p);
					goto parse_error;
				}
				/* skip spaces */
				while(*p == 32) {
					if (*p == 0)
						break;
					p++;
				}
				action->timeout = integer;
				/* check for next ACTION */
				if (*p == ':') {
					p++;
					goto nextaction;
				}
				continue;
			}

			/* check for duplicate parameters */
			param = action->param_first;
			while(param) {
				if (param->index == index) {
					SPRINT(failure, "Duplicate parameter '%s', use ',' to give multiple values.", key);
					goto parse_error;
				}
				param = param->next;
			}

			nextparamvalue:
			/* Alloc memory for param */
			param = (struct route_param *)MALLOC(sizeof(struct route_param));
			rmemuse++;
			*param_pointer = param;
			param_pointer = &(param->next);
			param->index = index;
			param->id = param_defs[index].id;

			switch(param_defs[index].type) {
				/* parse null value */
				case PARAM_TYPE_NULL:
				param->value_type = VALUE_TYPE_NULL;
				break;

				/* parse integer value */
				case PARAM_TYPE_INTEGER:
				integer = 0;
				if (*p==',' || *p==' ' || *p=='\0') {
					SPRINT(failure, "Missing integer value.");
					goto parse_error;
				}
				while(*p>='0' && *p<='9') {
					integer = integer*10 + *p-'0';
					p++;
				}
				param->integer_value = integer;
				param->value_type = VALUE_TYPE_INTEGER;
				break;

#if 0
				/* parse ports value */
				case PARAM_TYPE_PORTS:
				key[0] = '\0';
				if (*p==',' || *p==' ' || *p=='\0') {
					SPRINT(failure, "Missing port number, omit parameter or give port number.");
					goto parse_error;
				}
				i = 0;
				nextport:
				integer = 0;
				while(*p>='0' && *p<='9') {
					if (i < (int)sizeof(key)-1) {
						key[i] = *p;
						key[i++] = '\0';
					}
					integer = integer*10 + *p-'0';
					p++;
				}
				if (integer > 255) {
					SPRINT(failure, "Port number too high.");
					goto parse_error;
				}
				if (*p==',') {
					if (i < (int)sizeof(key)-1) {
						key[i] = *p;
						key[i++] = '\0';
					}
					p++;
					goto nextport;
				}
				goto mallocstring;
#endif

				/* parse string value */
				case PARAM_TYPE_STRING:
				case PARAM_TYPE_CALLERIDTYPE:
				case PARAM_TYPE_CAPABILITY:
				case PARAM_TYPE_BMODE:
				case PARAM_TYPE_HLC:
				case PARAM_TYPE_DIVERSION:
				case PARAM_TYPE_DESTIN:
				case PARAM_TYPE_TYPE:
				case PARAM_TYPE_YESNO:
				case PARAM_TYPE_ON:
				key[0] = '\0';
				if (*p==',' || *p==' ' || *p=='\0') {
					SPRINT(failure, "Missing string value, use \"\" for empty string.");
					goto parse_error;
				}
				p = read_string(p, key, sizeof(key), " ");
				if (key[0] == 1) { /* error */
					SPRINT(failure, "Parsing string failed: %s", key+1);
					goto parse_error;
				}
				if (param_defs[index].type == PARAM_TYPE_CALLERIDTYPE) {
					param->value_type = VALUE_TYPE_INTEGER;
					if (!strcasecmp(key, "unknown")) {
						param->integer_value = INFO_NTYPE_UNKNOWN;
						break;
					}
					if (!strcasecmp(key, "subscriber")) {
						param->integer_value = INFO_NTYPE_SUBSCRIBER;
						break;
					}
					if (!strcasecmp(key, "national")) {
						param->integer_value = INFO_NTYPE_NATIONAL;
						break;
					}
					if (!strcasecmp(key, "international")) {
						param->integer_value = INFO_NTYPE_INTERNATIONAL;
						break;
					}
					SPRINT(failure, "Caller ID type '%s' unknown.", key);
					goto parse_error;
				}
				if (param_defs[index].type == PARAM_TYPE_ON) {
					param->value_type = VALUE_TYPE_INTEGER;
					if (!strcasecmp(key, "init")) {
						param->integer_value = INFO_ON_INIT;
						break;
					}
					if (!strcasecmp(key, "hangup")) {
						param->integer_value = INFO_ON_HANGUP;
						break;
					}
					SPRINT(failure, "Execute on '%s' unknown.", key);
					goto parse_error;
				}
				if (param_defs[index].type == PARAM_TYPE_CAPABILITY) {
					param->value_type = VALUE_TYPE_INTEGER;
					if (!strcasecmp(key, "speech")) {
						param->integer_value = INFO_BC_SPEECH;
						break;
					}
					if (!strcasecmp(key, "audio")) {
						param->integer_value = INFO_BC_AUDIO;
						break;
					}
					if (!strcasecmp(key, "video")) {
						param->integer_value = INFO_BC_VIDEO;
						break;
					}
					if (!strcasecmp(key, "digital-restricted")) {
						param->integer_value = INFO_BC_DATARESTRICTED;
						break;
					}
					if (!strcasecmp(key, "digital-unrestricted")) {
						param->integer_value = INFO_BC_DATAUNRESTRICTED;
						break;
					}
					if (!strcasecmp(key, "digital-unrestricted-tones")) {
						param->integer_value = INFO_BC_DATAUNRESTRICTED_TONES;
						break;
					}
					SPRINT(failure, "Service type '%s' unknown.", key);
					goto parse_error;
				}
				if (param_defs[index].type == PARAM_TYPE_BMODE) {
					param->value_type = VALUE_TYPE_INTEGER;
					if (!strcasecmp(key, "transparent")) {
						param->integer_value = INFO_BMODE_CIRCUIT;
						break;
					}
					if (!strcasecmp(key, "hdlc")) {
						param->integer_value = INFO_BMODE_PACKET;
						break;
					}
					SPRINT(failure, "Bchannel mode '%s' unknown.", key);
					goto parse_error;
				}
				if (param_defs[index].type == PARAM_TYPE_HLC) {
					param->value_type = VALUE_TYPE_INTEGER;
					if (!strcasecmp(key, "telephony")) {
						param->integer_value = INFO_HLC_TELEPHONY;
						break;
					}
					if (!strcasecmp(key, "faxg2g3")) {
						param->integer_value = INFO_HLC_FAXG2G3;
						break;
					}
					if (!strcasecmp(key, "faxg4")) {
						param->integer_value = INFO_HLC_FAXG4;
						break;
					}
					if (!strcasecmp(key, "teletex1")) {
						param->integer_value = INFO_HLC_TELETEX1;
						break;
					}
					if (!strcasecmp(key, "teletex2")) {
						param->integer_value = INFO_HLC_TELETEX2;
						break;
					}
					if (!strcasecmp(key, "teletex3")) {
						param->integer_value = INFO_HLC_TELETEX3;
						break;
					}
					if (!strcasecmp(key, "videotex1")) {
						param->integer_value = INFO_HLC_VIDEOTEX1;
						break;
					}
					if (!strcasecmp(key, "videotex2")) {
						param->integer_value = INFO_HLC_VIDEOTEX2;
						break;
					}
					if (!strcasecmp(key, "telex")) {
						param->integer_value = INFO_HLC_TELEX;
						break;
					}
					if (!strcasecmp(key, "mhs")) {
						param->integer_value = INFO_HLC_MHS;
						break;
					}
					if (!strcasecmp(key, "osi")) {
						param->integer_value = INFO_HLC_OSI;
						break;
					}
					if (!strcasecmp(key, "maintenance")) {
						param->integer_value = INFO_HLC_MAINTENANCE;
						break;
					}
					if (!strcasecmp(key, "management")) {
						param->integer_value = INFO_HLC_MANAGEMENT;
						break;
					}
					if (!strcasecmp(key, "audiovisual")) {
						param->integer_value = INFO_HLC_AUDIOVISUAL;
						break;
					}
					SPRINT(failure, "HLC type '%s' unknown.", key);
					goto parse_error;
				}
				if (param_defs[index].type == PARAM_TYPE_DIVERSION) {
					param->value_type = VALUE_TYPE_INTEGER;
					if (!strcasecmp(key, "cfu")) {
						param->integer_value = INFO_DIVERSION_CFU;
						break;
					}
					if (!strcasecmp(key, "cfb")) {
						param->integer_value = INFO_DIVERSION_CFB;
						break;
					}
					if (!strcasecmp(key, "cfnr")) {
						param->integer_value = INFO_DIVERSION_CFNR;
						break;
					}
					if (!strcasecmp(key, "cfp")) {
						param->integer_value = INFO_DIVERSION_CFP;
						break;
					}
					SPRINT(failure, "Diversion type '%s' unknown.", key);
					goto parse_error;
				}
				if (param_defs[index].type == PARAM_TYPE_TYPE) {
					param->value_type = VALUE_TYPE_INTEGER;
					if (!strcasecmp(key, "unknown")) {
						param->integer_value = INFO_NTYPE_UNKNOWN;
						break;
					}
					if (!strcasecmp(key, "subscriber")) {
						param->integer_value = INFO_NTYPE_SUBSCRIBER;
						break;
					}
					if (!strcasecmp(key, "national")) {
						param->integer_value = INFO_NTYPE_NATIONAL;
						break;
					}
					if (!strcasecmp(key, "international")) {
						param->integer_value = INFO_NTYPE_INTERNATIONAL;
						break;
					}
					SPRINT(failure, "Number type '%s' unknown.", key);
					goto parse_error;
				}
				if (param_defs[index].type == PARAM_TYPE_YESNO) {
					param->value_type = VALUE_TYPE_INTEGER;
					if (!strcasecmp(key, "yes")) {
						param->integer_value = 1;
						break;
					}
					if (!strcasecmp(key, "no")) {
						param->integer_value = 0;
						break;
					}
					SPRINT(failure, "Value '%s' unknown. ('yes' or 'no')", key);
					goto parse_error;
				}
				param->string_value = (char *)MALLOC(strlen(key)+1);
				rmemuse++;
				UCPY(param->string_value, key);
				param->value_type = VALUE_TYPE_STRING;
				break;

				default:
				SPRINT(failure, "Software error: PARAM_TYPE_* %d not parsed in function '%s'", param_defs[index].type, __FUNCTION__);
				goto parse_error;
			}

			if (*p == ',') {
				p++;
				/* next item */
				param->value_extension = 1;
				goto nextparamvalue;
			}

			/* end of line */
			if (*p == '\0')
				break;

			/* to seperate the items, a space is required */
			if (*p != ' ') {
				SPRINT(failure, "Character '%c' not expected here. Use ' ' to seperate parameters, or ',' for multiple values.", *p);
				goto parse_error;
			}
                	/* skip spaces */
	                while(*p == 32)
	                {
	                        if (*p == 0)
	                                break;
	                        p++;
			}

			/* check for next ACTION */
			if (*p == ':') {
				p++;
				goto nextaction;
			}
		}
	}

	fclose(fp[nesting--]);
	fduse--;

	if (nesting >= 0)
		goto go_root;

	if (!ruleset_start) {
		SPRINT(failure, "No ruleset defined.");
	}
	return(ruleset_start);

	parse_error:
	printf("While parsing %s, an error occurred in line %d:\n", filename[nesting], line[nesting]);
	printf("-> %s\n", buffer);
	memset(pointer, ' ', sizeof(pointer));
	pointer[p-buffer] = '^';
	pointer[p-buffer+1] = '\0';
	printf("   %s\n", pointer);
	printf("%s\n", failure);
	SPRINT(ruleset_error, "Error in file %s, line %d: %s",  filename[nesting], line[nesting], failure);

	openerror:
	while(nesting >= 0) {
		fclose(fp[nesting--]);
		fduse--;
	}

	ruleset_free(ruleset_start);
	return(NULL);
}

/*
 * return ruleset by name
 */
struct route_ruleset *getrulesetbyname(const char *name)
{
	struct route_ruleset *ruleset = ruleset_first;

	while(ruleset) {
		if (!strcasecmp(name, ruleset->name)) {
			break;
		}
		ruleset = ruleset->next;
	}
	PDEBUG(DEBUG_ROUTE, "ruleset %s %s.\n", name, ruleset?"found":"not found");
	return(ruleset);
}

/*
 * parses the current ruleset and returns action
 */
struct route_action *EndpointAppPBX::route(struct route_ruleset *ruleset)
{
	int			match,
				couldmatch = 0, /* any rule could match */
				istrue,
				couldbetrue,
				condition,
				dialing_required,
				avail,
				any;
	struct route_rule	*rule = ruleset->rule_first;
	struct route_cond	*cond;
	struct route_action	*action = NULL;
	unsigned long		comp_len;
	int			j, jj;
	char			isdn_port[10];
	char			*argv[11]; /* check also number of args below */
	char			callerid[64], callerid2[64], redirid[64];
	int			integer;
	char			*string;
	FILE			*tfp;
	long long		timeout, now_ll = 0, match_timeout = 0;
	struct timeval		current_time;
	struct mISDNport	*mISDNport;
	struct admin_list	*admin;
	time_t			now;
	struct tm		*now_tm;
	int			pid, status;

	/* reset timeout action */
	e_match_to_action = NULL;

	SCPY(callerid, numberrize_callerinfo(e_callerinfo.id, e_callerinfo.ntype, options.national, options.international));
	SCPY(callerid2, numberrize_callerinfo(e_callerinfo.id2, e_callerinfo.ntype2, options.national, options.international));
	SCPY(redirid, numberrize_callerinfo(e_redirinfo.id, e_redirinfo.ntype, options.national, options.international));
	
	PDEBUG(DEBUG_ROUTE, "parsing ruleset '%s'\n", ruleset->name);
	while(rule) {
		PDEBUG(DEBUG_ROUTE, "checking rule in line %d\n", rule->line);
		match = 1; /* this rule matches */
		dialing_required = 0;
		timeout = 0; /* timeout time */
		cond = rule->cond_first;
		while(cond) {
			condition = 0; /* any condition element is true (1) or could be true (2) */
			checkextension:
			istrue = 0; /* this condition-element is true */
			couldbetrue = 0; /* this conditions-element could be true */
			switch(cond->match) {
				case MATCH_EXTERN:
				if (!e_ext.number[0])
					istrue = 1;	 
				break;

				case MATCH_INTERN:
				if (e_ext.number[0])
					istrue = 1;	 
				break;

				case MATCH_PORT:
				if (ea_endpoint->ep_portlist)
				if ((ea_endpoint->ep_portlist->port_type & PORT_CLASS_MASK) != PORT_CLASS_mISDN)
					break;
				integer = e_callerinfo.isdn_port;
				goto match_integer;

				case MATCH_INTERFACE:
				if (!e_callerinfo.interface[0])
					break;
				string = e_callerinfo.interface;
				goto match_string_prefix;

				case MATCH_CALLERID:
				string = callerid;
				goto match_string_prefix;

				case MATCH_CALLERID2:
				string = callerid2;
				goto match_string_prefix;

				case MATCH_EXTENSION:
				string = e_ext.number;
				goto match_string;

				case MATCH_DIALING:
				string = e_dialinginfo.id;
				goto match_string_prefix;

				case MATCH_ENBLOCK:
				if (!e_overlap)
					istrue = 1;
				break;

				case MATCH_OVERLAP:
				if (e_overlap)
					istrue = 1;
				break;

				case MATCH_ANONYMOUS:
				if (e_callerinfo.present != INFO_PRESENT_ALLOWED)
					istrue = 1;
				break;

				case MATCH_VISIBLE:
				if (e_callerinfo.present == INFO_PRESENT_ALLOWED)
					istrue = 1;
				break;

				case MATCH_UNKNOWN:
				if (e_callerinfo.present == INFO_PRESENT_NOTAVAIL)
					istrue = 1;
				break;

				case MATCH_AVAILABLE:
				if (e_callerinfo.present != INFO_PRESENT_NOTAVAIL)
					istrue = 1;
				break;

				case MATCH_FAKE:
				if (e_callerinfo.screen == INFO_SCREEN_USER)
					istrue = 1;
				break;

				case MATCH_REAL:
				if (e_callerinfo.screen != INFO_SCREEN_USER)
					istrue = 1;
				break;

				case MATCH_REDIRECTED:
				if (e_redirinfo.ntype != INFO_NTYPE_NOTPRESENT)
					istrue = 1;
				break;

				case MATCH_DIRECT:
				if (e_redirinfo.ntype == INFO_NTYPE_NOTPRESENT)
					istrue = 1;
				break;

				case MATCH_REDIRID:
				string = redirid;
				goto match_string_prefix;

				case MATCH_TIME:
				time(&now);
				now_tm = localtime(&now);
				integer = now_tm->tm_hour*100 + now_tm->tm_min;
				goto match_integer;

				case MATCH_MDAY:
				time(&now);
				now_tm = localtime(&now);
				integer = now_tm->tm_mday;
				goto match_integer;

				case MATCH_MONTH:
				time(&now);
				now_tm = localtime(&now);
				integer = now_tm->tm_mon+1;
				goto match_integer;

				case MATCH_YEAR:
				time(&now);
				now_tm = localtime(&now);
				integer = now_tm->tm_year + 1900;
				goto match_integer;

				case MATCH_WDAY:
				time(&now);
				now_tm = localtime(&now);
				integer = now_tm->tm_wday;
				integer = integer?integer:7; /* correct sunday */
				goto match_integer;

				case MATCH_CAPABILITY:
				integer = e_capainfo.bearer_capa;
				goto match_integer;
	
				case MATCH_INFOLAYER1:
				integer = e_capainfo.bearer_info1;
				goto match_integer;

				case MATCH_HLC:
				integer = e_capainfo.hlc;
				goto match_integer;

				case MATCH_FILE:
				tfp = fopen(cond->string_value, "r");
				if (!tfp) {
					break;
				}
				if (fgetc(tfp) == '1')
					istrue = 1;
				fclose(tfp);
				break;

				case MATCH_EXECUTE:
				j = 0;
#if 0
				argv[j++] = (char *)"/bin/sh";
				argv[j++] = (char *)"-c";
				argv[j++] = cond->string_value;
#endif
				argv[j++] = cond->string_value;
				argv[j++] = e_extdialing;
				argv[j++] = (char *)numberrize_callerinfo(e_callerinfo.id, e_callerinfo.ntype, options.national, options.international);
				argv[j++] = e_callerinfo.extension;
				argv[j++] = e_callerinfo.name;
				SPRINT(isdn_port, "%d", e_callerinfo.isdn_port);
				argv[j++] = isdn_port;
				argv[j++] = e_callerinfo.imsi;
				argv[j++] = NULL; /* check also number of args above */
				switch ((pid = fork())) {
				case 0:
					execve(cond->string_value, argv, environ);
					perror("execve");
					exit(1);
				case -1:
					break;
				default:
					waitpid(pid, &status, 0);
					if (0 == WEXITSTATUS(status))
						istrue = 1;
				}
				break;

				case MATCH_DEFAULT:
				if (!couldmatch)
					istrue = 1;
				break;

				case MATCH_TIMEOUT:
				if (!now_ll) {
					gettimeofday(&current_time, NULL);
					now_ll = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec;
				}
				timeout = now_ll + (cond->integer_value * MICRO_SECONDS);
				istrue = 1;
				break;

				case MATCH_FREE:
				case MATCH_NOTFREE:
				if (!(comp_len = (unsigned long)strchr(cond->string_value, ':')))
					break;
				comp_len = comp_len-(unsigned long)cond->string_value;
				avail = 0;
				mISDNport = mISDNport_first;
				while(mISDNport) {
					if (mISDNport->ifport)
					if (strlen(mISDNport->ifport->interface->name) == comp_len)
					if (!strncasecmp(mISDNport->ifport->interface->name, cond->string_value, comp_len)) 
					if (!mISDNport->l2hold || mISDNport->l2link>0) {
						j = 0;
						jj = mISDNport->b_num;
						avail += jj;
						while(j < jj) {
							if (mISDNport->b_state[j])
								avail--;
							j++;
						}
					}
					mISDNport = mISDNport->next;
				}
				if (cond->match == MATCH_FREE) {
					if (avail >= atoi(cond->string_value + comp_len + 1))
						istrue = 1;
				} else {
					if (avail < atoi(cond->string_value + comp_len + 1))
						istrue = 1;
				}
				break;


				case MATCH_DOWN:
				mISDNport = mISDNport_first;
				while(mISDNport) {
					if (mISDNport->ifport)
					if (!strcasecmp(mISDNport->ifport->interface->name, cond->string_value)) 
					if (!mISDNport->l2hold || mISDNport->l2link>0)
						break;
					mISDNport = mISDNport->next;
				}
				if (!mISDNport) /* all down */
					istrue = 1;
				break;

				case MATCH_UP:
				mISDNport = mISDNport_first;
				while(mISDNport) {
					if (mISDNport->ifport)
					if (!strcasecmp(mISDNport->ifport->interface->name, cond->string_value)) 
					if (!mISDNport->l2hold || mISDNport->l2link>0)
						break;
					
					mISDNport = mISDNport->next;
				}
				if (mISDNport) /* one link at least */
					istrue = 1;
				break;

				case MATCH_BUSY:
				case MATCH_IDLE:
				any = 0;
				mISDNport = mISDNport_first;
				while(mISDNport) {
					if (mISDNport->ifport)
					if (!strcasecmp(mISDNport->ifport->interface->name, cond->string_value)) 
					if (mISDNport->use) /* break if in use */
						break;
					mISDNport = mISDNport->next;
				}
				if (mISDNport && cond->match==MATCH_BUSY)
					istrue = 1;
				if (!mISDNport && cond->match==MATCH_IDLE)
					istrue = 1;
				break;

				case MATCH_REMOTE:
				case MATCH_NOTREMOTE:
				admin = admin_first;
				while(admin) {
					if (admin->remote_name[0] && !strcmp(cond->string_value, admin->remote_name))
						break;
					admin = admin->next;
				}
				if (admin && cond->match==MATCH_REMOTE)
					istrue = 1;
				if (!admin && cond->match==MATCH_NOTREMOTE)
					istrue = 1;
				break;

				default:
				PERROR("Software error: MATCH_* %d not parsed in function '%s'", cond->match, __FUNCTION__);
				break;

				match_integer:
				if (cond->value_type == VALUE_TYPE_INTEGER) {
					if (integer != cond->integer_value)
						break;
					istrue = 1;
					break;
				}
				if (cond->value_type == VALUE_TYPE_INTEGER_RANGE) {
					/* check if negative range (2100 - 700 o'clock) */
					if (cond->integer_value > cond->integer_value_to) {
						if (integer>=cond->integer_value && integer<=cond->integer_value_to)
							istrue = 1;
						break;
					}
					/* range is positive */
					if (integer>=cond->integer_value && integer<=cond->integer_value_to)
						istrue = 1;
					break;
				}
				break;

				match_string:
				if (strlen(cond->string_value) != strlen(string))
					break;
				/* fall through */
				match_string_prefix:
				comp_len = strlen(cond->string_value); /* because we must reach value's length */
				/* we must have greater or equal length to values */
				if ((unsigned long)strlen(string) < comp_len) {
					/* special case for unfinished dialing */
					if (cond->match == MATCH_DIALING) {
						couldbetrue = 1; /* could match */
						comp_len = strlen(string);
					} else {
						break;
					}
				}
				/* on single string match */
				if (cond->value_type == VALUE_TYPE_STRING) {
					if (!strncmp(string, cond->string_value, comp_len)) {
						istrue = 1;
						/* must be set for changing 'e_extdialing' */
						if (cond->match == MATCH_DIALING)
							dialing_required = comp_len;
						break;
					}
					break;
				}
				/* on range match */
				if (cond->value_type == VALUE_TYPE_STRING_RANGE) {
					/* check if negative range ("55"-"22") */
					if (cond->comp_string > 0) {
						if (strncmp(string, cond->string_value, comp_len) >= 0) {
							istrue = 1;
							/* must be set for changing 'e_extdialing' */
							if (cond->match == MATCH_DIALING)
								dialing_required = comp_len;
							break;
						}
						if (strncmp(string, cond->string_value_to, comp_len) <= 0) {
							/* must be set for changing 'e_extdialing' */
							istrue = 1;
							if (cond->match == MATCH_DIALING)
								dialing_required = comp_len;
							break;
						}
						break;
					}
					/* range is positive */
					if (strncmp(string, cond->string_value, comp_len) < 0)
						break;
					if (strncmp(string, cond->string_value_to, comp_len) > 0)
						break;
					istrue = 1;
					if (cond->match == MATCH_DIALING)
						dialing_required = comp_len;
					break;
				}
				break;
			}

			/* set current condition */
			if (istrue && !couldbetrue)
				condition = 1; /* element matches, so condition matches */
			if (istrue && couldbetrue && !condition)
				condition = 2; /* element could match and other elements don't match, so condition could match */

			/* if not matching or could match */
			if (condition != 1) {
				/* if we have more values to check */
				if (cond->value_extension && cond->next) {
					cond = cond->next;
					goto checkextension;
				}
				match = condition;
				break;
			}
			
			/* skip exteded values, beacuse we already have one matching value */
			while(cond->value_extension && cond->next)
				cond = cond->next;

			cond = cond->next;
		}
		if (timeout>now_ll && match==1) { /* the matching rule with timeout in the future */
			if (match_timeout == 0 || timeout < match_timeout) { /* first timeout or lower */
				/* set timeout in the furture */
				match_timeout = timeout;
				e_match_to_action = rule->action_first;
				e_match_to_extdialing = e_dialinginfo.id + dialing_required;
			}
			match = 0; /* matches in the future */
		}
		if (match == 1) {
			/* matching, we return first action */
			action = rule->action_first;
			match_timeout = 0; /* no timeout */
			e_match_to_action = NULL;
			e_extdialing = e_dialinginfo.id + dialing_required;
			break;
		}
		if (match == 2) {
			/* rule could match if more is dialed */
			couldmatch = 1;
		}
		rule = rule->next;
	}
	if (match_timeout == 0)
		unsched_timer(&e_match_timeout); /* no timeout */
	else {
		schedule_timer(&e_match_timeout, (match_timeout-now_ll) / 1000000, (match_timeout-now_ll) % 1000000);
	}
	return(action);
}

/*
 * parses the current action's parameters and return them
 */
struct route_param *EndpointAppPBX::routeparam(struct route_action *action, unsigned long long id)
{
	struct route_param *param = action->param_first;

	while(param) {
		if (param->id == id)
			break;
		param = param->next;
	}
	return(param);
}


/*
 * internal rules that are not defined by route.conf
 */
struct route_action action_password = {
	NULL,
	NULL,
	ACTION_PASSWORD,
	0,
	0,
};

struct route_action action_password_write = {
	NULL,
	NULL,
	ACTION_PASSWORD_WRITE,
	0,
	0,
};

struct route_action action_external = {
	NULL,
	NULL,
	ACTION_EXTERNAL,
	0,
	0,
};

struct route_action action_internal = {
	NULL,
	NULL,
	ACTION_INTERNAL,
	0,
	0,
};

struct route_action action_remote = {
	NULL,
	NULL,
	ACTION_REMOTE,
	0,
	0,
};

struct route_action action_vbox = {
	NULL,
	NULL,
	ACTION_VBOX_RECORD,
	0,
	0,
};

struct route_action action_partyline = {
	NULL,
	NULL,
	ACTION_PARTYLINE,
	0,
	0,
};

struct route_action action_disconnect = {
	NULL,
	NULL,
	ACTION_DISCONNECT,
	0,
	0,
};