// SIP Address Library // // For example: // // "J.A. Roberts Tunney" ;tag=deadbeef // // Roughly equates to: // // {Display: "J.A. Roberts Tunney", // Params: {"tag": "deadbeef"}, // Uri: {Scheme: "sip", // User: "jtunney", // Pass: "", // Host: "bsdtelecom.net", // Port: "", // Params: {"isup-oli": "29"}}} // package sip import ( "bytes" "errors" "github.com/jart/gosip/util" "log" "strings" ) // Represents a SIP Address Linked List type Addr struct { Uri *URI // never nil Display string // blank if not specified Params Params // these look like ;key=lol;rport;key=wut Next *Addr // for comma separated lists of addresses } // Parses a SIP address. func ParseAddr(s string) (addr *Addr, err error) { addr = new(Addr) l := len(s) if l == 0 { return nil, errors.New("empty addr") } // Extract display. switch n := strings.IndexAny(s, "\"<"); { case n < 0: return nil, errors.New("invalid address") case s[n] == '<': // Display is not quoted. addr.Display, s = strings.Trim(s[0:n], " "), s[n+1:] case s[n] == '"': // We found an opening quote. s = s[n+1:] LOL: for s != "" { switch s[0] { case '"': // Closing quote. s = s[1:] break LOL case '\\': // Escape sequence. if len(s) < 2 { return nil, errors.New("evil quote escape") } switch s[1] { case '"': addr.Display += "\"" case '\\': addr.Display += "\\" } s = s[2:] default: // Generic character. addr.Display += string(s[0]) s = s[1:] } } if s == "" { return nil, errors.New("no closing quote in display") } for s != "" { c := s[0] s = s[1:] if c == '<' { break } } } if n := strings.Index(s, ">"); n > 0 { addr.Uri, err = ParseURI(s[0:n]) if err != nil { return nil, err } s = s[n+1:] } else { addr.Uri, err = ParseURI(s) if err != nil { return nil, err } s = "" } // Extract semicolon delimited params. if s != "" && s[0] == ';' { addr.Params = parseParams(s[1:]) s = "" } // Is there another address? s = strings.TrimLeft(s, " \t") if s != "" && s[0] == ',' { s = strings.TrimLeft(s[1:], " \t") if s != "" { addr.Next, err = ParseAddr(s) if err != nil { log.Println("[NOTICE]", "dropping invalid bonus addr:", s, err) } } } return addr, nil } func (addr *Addr) String() string { if addr == nil { return "" } var b bytes.Buffer addr.Append(&b) return b.String() } // Returns self if non-nil, otherwise other. func (addr *Addr) Or(other *Addr) *Addr { if addr == nil { return other } return addr } // Sets newly generated tag ID and returns self. func (addr *Addr) Tag() *Addr { addr = addr.Copy() addr.Params["tag"] = util.GenerateTag() return addr } // Reassembles a SIP address into a buffer. func (addr *Addr) Append(b *bytes.Buffer) { if addr.Display != "" { b.WriteString("\"") b.WriteString(util.EscapeDisplay(addr.Display)) b.WriteString("\" ") } b.WriteString("<") addr.Uri.Append(b) b.WriteString(">") addr.Params.Append(b) if addr.Next != nil { b.WriteString(", ") addr.Next.Append(b) } } // Deep copies a new Addr object. func (addr *Addr) Copy() *Addr { if addr == nil { return nil } res := new(Addr) res.Uri = addr.Uri.Copy() res.Params = addr.Params.Copy() res.Next = addr.Next.Copy() return res } // Returns true if the host and port match. If a username is present in // `addr`, then the username is `other` must also match. func (addr *Addr) Compare(other *Addr) bool { if addr != nil && other != nil { return addr.Uri.Compare(other.Uri) } return false } // Returns pointer to last addr in linked list. func (addr *Addr) Last() *Addr { if addr != nil { for ; addr.Next != nil; addr = addr.Next { } } return addr }