// -*-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
|
|
var b1, b2 string
|
|
var hex byte
|
|
|
|
%%{
|
|
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 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 param {
|
|
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 = alpha | digit | mark ;
|
|
ipv4 = digit | "." ;
|
|
ipv6 = xdigit | "." | ":" ;
|
|
hostname = alpha | digit | "-" | "." ;
|
|
tel = digit | "+" | "-" ;
|
|
schmchars = alpha | digit | "+" | "-" | "." ;
|
|
userchars = unreserved | "&" | "=" | "+" | "$" | "," | ";" | "?" | "/" ;
|
|
passchars = unreserved | "&" | "=" | "+" | "$" | "," ;
|
|
paramchars = unreserved | "[" | "]" | "/" | ":" | "&" | "+" | "$" ;
|
|
headerchars = unreserved | "[" | "]" | "/" | "?" | ":" | "+" | "$" ;
|
|
|
|
# Multibyte character definitions.
|
|
escaped = "%" ( xdigit @hexHi ) ( xdigit @hexLo ) ;
|
|
userchar = escaped | ( userchars @append ) ;
|
|
passchar = escaped | ( passchars @append ) ;
|
|
paramchar = escaped | ( paramchars @lower ) ;
|
|
headerchar = escaped | ( headerchars @append ) ;
|
|
|
|
# URI component definitions.
|
|
scheme = ( alpha schmchars* ) >start @lower %scheme ;
|
|
user = userchar+ >start %user ;
|
|
pass = passchar+ >start %pass ;
|
|
host6 = "[" ( ipv6+ >start @lower %host ) "]" ;
|
|
host = host6 | ( ( ipv4 | hostname | tel )+ >start @lower %host ) ;
|
|
port = digit+ @port ;
|
|
paramkey = paramchar+ >start >b2 %b1 ;
|
|
paramval = paramchar+ >start %b2 ;
|
|
param = space* ";" paramkey ( "=" paramval )? %param ;
|
|
headerkey = headerchar+ >start >b2 %b1 ;
|
|
headerval = headerchar+ >start %b2 ;
|
|
header = headerkey ( "=" headerval )? %header ;
|
|
headers = "?" header ( "&" header )* ;
|
|
userpass = user ( ":" pass )? ;
|
|
hostport = host ( ":" port )? ;
|
|
uriSansUser := space* scheme ":" hostport param* space* headers? space* ;
|
|
uriWithUser := space* scheme ":" userpass "@" hostport param* space* headers? space* ;
|
|
}%%
|
|
|
|
%% 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
|
|
}
|