package asterisk import ( "encoding/hex" "errors" "fmt" "os" "path/filepath" "github.com/CyCoreSystems/ari/v5" "github.com/CyCoreSystems/ari/v5/client/native" ) // ariOptions uses the credential issued to the Agent on the same Cell as // native Asterisk. It never reads a historical local test credential. func (l Loader) ariOptions() (*native.Options, error) { if l.ConfigDir == "" { return nil, errors.New("native Asterisk configuration directory required") } path := filepath.Join(l.ConfigDir, "ari-secret") info, err := os.Lstat(path) if err != nil { return nil, fmt.Errorf("inspect issued Agent ARI credential: %T", err) } if !info.Mode().IsRegular() || info.Mode().Perm() != 0600 { return nil, errors.New("issued Agent ARI credential must be a private regular file (0600)") } secret, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("read issued Agent ARI credential: %T", err) } if len(secret) != 64 { return nil, errors.New("issued Agent ARI credential has an invalid length") } if _, err := hex.DecodeString(string(secret)); err != nil { return nil, errors.New("issued Agent ARI credential has an invalid format") } return &native.Options{ URL: "http://127.0.0.1:8088/ari", WebsocketURL: "ws://127.0.0.1:8088/ari/events", Application: "go-sip-agent", Username: "go-sip-agent", Password: string(secret), }, nil } // OpenARI proves that the native server accepts this Agent credential without // creating a channel or dialing. The caller owns and must close the client. func (l Loader) OpenARI() (ari.Client, error) { options, err := l.ariOptions() if err != nil { return nil, err } client, err := native.Connect(options) if err != nil { return nil, fmt.Errorf("connect native Agent ARI: %T", err) } info, err := client.Asterisk().Info(nil) if err != nil { client.Close() return nil, fmt.Errorf("read native Agent ARI status: %T", err) } if info == nil { client.Close() return nil, errors.New("native Agent ARI returned an empty status") } return client, nil }