You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

189 lines
4.2 KiB

// -*-go-*-
package sip
import (
"bytes"
"errors"
"fmt"
)
%% machine uri;
%% write data;
// ParseURI turns a a SIP URI into a data structure.
func ParseURI(s string) (uri *URI, err error) {
if s == "" {
return nil, errors.New("Empty URI")
}
return ParseURIBytes([]byte(s))
}
// ParseURI turns a a SIP URI byte slice into a data structure.
func ParseURIBytes(data []byte) (uri *URI, err error) {
if data == nil {
return nil, nil
}
uri = new(URI)
cs := 0
p := 0
pe := len(data)
eof := len(data)
buf := make([]byte, len(data))
amt := 0
// mark := 0
var b1, b2 string
var hex byte
%%{
action mark {
mark = p
}
action backtrack {
fexec mark;
}
action start {
amt = 0
}
action append {
buf[amt] = fc
amt++
}
action hexHi {
hex = unhex(fc) * 16
}
action hexLo {
hex += unhex(fc)
buf[amt] = hex
amt++
}
action goto_uriSansUser {
fgoto uriSansUser;
}
action goto_uriWithUser {
fgoto uriWithUser;
}
action scheme {
uri.Scheme = string(buf[0:amt])
}
action user {
uri.User = string(buf[0:amt])
}
action pass {
uri.Pass = string(buf[0:amt])
}
action host {
uri.Host = string(buf[0:amt])
}
action port {
uri.Port = uri.Port * 10 + uint16(fc - 0x30)
}
action b1 {
b1 = string(buf[0:amt])
amt = 0
}
action b2 {
b2 = string(buf[0:amt])
amt = 0
}
action lower {
if 'A' <= fc && fc <= 'Z' {
buf[amt] = fc + 0x20
} else {
buf[amt] = fc
}
amt++
}
action uparam {
if uri.Params == nil {
uri.Params = Params{}
}
uri.Params[b1] = b2
}
action header {
if uri.Headers == nil {
uri.Headers = URIHeaders{}
}
uri.Headers[b1] = b2
}
# Byte character definitions.
mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")";
reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ",";
unreserved = alnum | mark;
ipv4c = digit | ".";
ipv6c = xdigit | "." | ":";
hostc = alnum | "-" | ".";
telc = digit | "+" | "-";
schemec = alnum | "+" | "-" | ".";
uric = reserved | unreserved | "%" | "[" | "]";
userc = unreserved | "&" | "=" | "+" | "$" | "," | ";" | "?" | "/";
passc = unreserved | "&" | "=" | "+" | "$" | ",";
uparamc = unreserved | "[" | "]" | "/" | ":" | "&" | "+" | "$";
headerc = unreserved | "[" | "]" | "/" | "?" | ":" | "+" | "$";
# Multibyte character definitions.
escaped = "%" ( xdigit @hexHi ) ( xdigit @hexLo );
userchar = escaped | ( userc @append );
passchar = escaped | ( passc @append );
uparamchar = escaped | ( uparamc @lower );
headerchar = escaped | ( headerc @append );
# URI component definitions.
scheme = ( alpha schemec* ) >start @lower %scheme;
user = userchar+ >start %user;
pass = passchar+ >start %pass;
host6 = "[" ( ipv6c+ >start @lower %host ) "]";
host = host6 | ( ( ipv4c | hostc | telc )+ >start @lower %host );
port = digit+ @port;
uparamkey = uparamchar+ >start >b2 %b1;
uparamval = uparamchar+ >start %b2;
uparam = ";" uparamkey ( "=" uparamval )? %uparam;
headerkey = headerchar+ >start >b2 %b1;
headerval = headerchar+ >start %b2;
header = headerkey ( "=" headerval )? %header;
headers = "?" header ( "&" header )*;
userpass = user ( ":" pass )?;
hostport = host ( ":" port )?;
uriSansUser := scheme ":" hostport uparam* headers?;
uriWithUser := scheme ":" userpass "@" hostport uparam* headers?;
# XXX: This backtracking solution causes a weird Ragel bug.
# uri := any+ >mark %backtrack %goto_uriSansUser
# | any+ >mark :> "@" @backtrack @goto_uriWithUser;
}%%
%% write init;
if bytes.IndexByte(data, '@') == -1 {
cs = uri_en_uriSansUser;
} else {
cs = uri_en_uriWithUser;
}
%% write exec;
if cs < uri_first_final {
if p == pe {
return nil, errors.New(fmt.Sprintf("Incomplete URI: %s", data))
} else {
return nil, errors.New(fmt.Sprintf("Error in URI at pos %d: %s", p, data))
}
}
return uri, nil
}