goirc/server/config/parser.rl

242 lines
6.1 KiB
Plaintext
Raw Normal View History

2010-11-28 16:39:25 +00:00
package config
import (
"io"
"os"
"fmt"
"net"
"strconv"
)
%% machine config;
%% write data;
// some helpers
2010-11-28 16:39:25 +00:00
var booleans = map[string]bool {
"true": true,
"yes": true,
"on": true,
"1": true,
"false": false,
"no": false,
"off": false,
"0": false,
}
func getbool(val []byte) bool {
return booleans[string(val)]
}
func getint(val []byte) int {
if v, err := strconv.Atoi(string(val)); err == nil {
return v
}
return 0
}
2010-11-28 16:39:25 +00:00
%%{
###############################################################################
# basic parser bits
2010-11-28 16:39:25 +00:00
# mark the current position for acquiring data
action mark { mark = p }
# acceptable boolean values
true = "yes" | "on" | "1" | "true" ;
false = "no" | "off" | "0" | "false" ;
boolean = true | false ;
# IP addresses, v4 and v6
octet = digit # 0-9
| digit digit # 00-99
| [01] digit digit # 000-199
| "2" [0-4] digit # 200-249
| "2" "5" [0-5] ; # 250-255
ipv4addr = octet "." octet "." octet "." octet ;
ipv6addr = ( xdigit{1,4} ":" ){7} xdigit{1,4} # all 8 blocks
| xdigit{0,4} "::" # special case
| ( xdigit{1,4} ":" ){1,6} ":" # first 1-6, ::
| ":" ( ":" xdigit{1,4} ){1,6} # ::, final 1-6
| xdigit{1,4} ":" ( ":" xdigit{1,4} ){1,6} # 1::1-6
| ( xdigit{1,4} ":" ){2}( ":" xdigit{1,4} ){1,5} # 2::1-5
| ( xdigit{1,4} ":" ){3}( ":" xdigit{1,4} ){1,4} # 3::1-4
| ( xdigit{1,4} ":" ){4}( ":" xdigit{1,4} ){1,5} # 4::1-3
| ( xdigit{1,4} ":" ){5}( ":" xdigit{1,4} ){1,2} # 5::1-2
| ( xdigit{1,4} ":" ){6}( ":" xdigit{1,4} ) ; # 6::1
# acceptable password chars:
# anything in the normal ascii set apart from spaces and control chars
passchar = ascii -- ( space | cntrl ) ;
# acceptable hostmask chars:
# alphanumeric, plus "*", "?" and "."
hostchar = ( alnum | "*" | "?" | "." ) ;
###############################################################################
# "port" configuration parser
2010-11-28 16:39:25 +00:00
# Actions to create a cPort and save it into the Config struct
action p_new { cur = defaultPort() }
action p_save {
2010-11-28 16:39:25 +00:00
port := cur.(*cPort)
conf.Ports[port.Port] = port
cur = nil
}
# parse and save the port number
action px_port {
cur.(*cPort).Port = getint(data[mark:p])
2010-11-28 16:39:25 +00:00
}
pv_port = digit+ >mark %px_port ;
2010-11-28 16:39:25 +00:00
# parse a bind_ip statement and save the IP
action px_bind_ip {
2010-11-28 16:39:25 +00:00
cur.(*cPort).BindIP = net.ParseIP(string(data[mark:p]))
}
pv_bind_ip = (ipv4addr | ipv6addr) >mark %px_bind_ip ;
ps_bind_ip = "bind_ip" " "+ "=" " "+ pv_bind_ip ;
2010-11-28 16:39:25 +00:00
# parse a class statement and save it
action px_class {
2010-11-28 16:39:25 +00:00
cur.(*cPort).Class = string(data[mark:p])
}
pv_class = ( "server" | "client" ) >mark %px_class ;
ps_class = "class" " "+ "=" " "+ pv_class ;
2010-11-28 16:39:25 +00:00
# parse SSL and Zip booleans
action px_ssl {
cur.(*cPort).SSL = getbool(data[mark:p])
2010-11-28 16:39:25 +00:00
}
pv_ssl = boolean >mark %px_ssl ;
ps_ssl = "ssl" " "+ "=" " "+ pv_ssl ;
action px_zip {
cur.(*cPort).Zip = getbool(data[mark:p])
2010-11-28 16:39:25 +00:00
}
pv_zip = boolean >mark %px_zip ;
ps_zip = "zip" " "+ "=" " "+ pv_zip ;
2010-11-28 16:39:25 +00:00
# a port statement can be any one of the above statements
p_stmt = ( ps_bind_ip | ps_class | ps_ssl | ps_zip ) ;
# and a port block combines one or more statements
p_block = "{" space* ( p_stmt | (p_stmt " "* "\n" space*)+ ) space* "}" ;
2010-11-28 16:39:25 +00:00
# a port configuration can either be:
# port <port>\n
# port <port> { <statement> ... }
p_line = "port" >p_new " "+ pv_port " "* "\n" %p_save ;
p_defn = "port" >p_new " "+ pv_port " "+ p_block %p_save ;
port_config = space* ( p_line | p_defn ) space* ;
###############################################################################
# "oper" configuration parser
# actions to create a new cOper and save it into the Config struct
action o_new { cur = defaultOper() }
action o_save {
oper := cur.(*cOper)
conf.Opers[oper.Username] = oper
cur = nil
}
# parse and save the username
action ox_username {
cur.(*cOper).Username = string(data[mark:p])
}
ov_username = alpha >mark alnum* %ox_username ;
# parse a password statement and save it
action ox_password {
cur.(*cOper).Password = string(data[mark:p])
}
ov_password = passchar+ >mark %ox_password ;
os_password = "password" " "+ "=" " "+ ov_password ;
# parse a hostmask statement and save it
action ox_hostmask {
cur.(*cOper).HostMask = append(
cur.(*cOper).HostMask, string(data[mark:p]))
}
ov_hostmask = hostchar+ >mark "@" hostchar+ %ox_hostmask ;
os_hostmask = "hostmask" " "+ "=" " "+ ov_hostmask ;
# parse and save the various oper permissions
action ox_kill {
cur.(*cOper).CanKill = getbool(data[mark:p])
}
ov_kill = boolean >mark %ox_kill ;
os_kill = "kill" " "+ "=" " "+ ov_kill ;
action ox_ban {
cur.(*cOper).CanBan = getbool(data[mark:p])
}
ov_ban = boolean >mark %ox_ban ;
os_ban = "ban" " "+ "=" " "+ ov_ban ;
action ox_renick {
cur.(*cOper).CanRenick = getbool(data[mark:p])
}
ov_renick = boolean >mark %ox_renick ;
os_renick = "renick" " "+ "=" " "+ ov_renick ;
action ox_link {
cur.(*cOper).CanLink = getbool(data[mark:p])
}
ov_link = boolean >mark %ox_link ;
os_link = "link" " "+ "=" " "+ ov_link ;
# an oper statement can be any of the above statements
o_stmt = ( os_password
| os_hostmask
| os_kill
| os_ban
| os_renick
| os_link ) ;
# and an oper block combines one or more statements
o_block = "{" space* ( o_stmt | (o_stmt " "* "\n" space*)+ ) space* "}" ;
# an oper configuration looks like:
# oper <username> { <statement> ... }
oper_config = "oper" >o_new " "+ ov_username
" "+ o_block %o_save space* ;
2010-11-28 16:39:25 +00:00
# config = portconfig+
# | operconfig+
# | linkconfig*
# | infoconfig
# | settings ;
main := ( port_config | oper_config )+ ;
2010-11-28 16:39:25 +00:00
}%%
func (conf *Config) Parse(r io.Reader) {
cs, p, mark, pe, eof, buflen := 0, 0, 0, 0, 0, 16384
done := false
var cur interface{}
data := make([]byte, buflen)
%% write init;
for !done {
n, err := r.Read(data)
pe = p + n
if err == os.EOF {
fmt.Println("yeahhhhh.")
done = true
eof = pe
}
%% write exec;
}
if cs < config_first_final {
fmt.Printf("Parse error at %d near '%s'\n", p, data[p:p+10])
}
for _, port := range conf.Ports {
fmt.Println(port.String())
}
for _, oper := range conf.Opers {
fmt.Println(oper.String())
}
2010-11-28 16:39:25 +00:00
}