Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions autocomplete/fish_autocomplete
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,7 @@ complete -x -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcomman
complete -x -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from inbound in inbound-trunk; and not __fish_seen_subcommand_from list create update delete help h' -a 'create' -d 'Create an inbound SIP Trunk'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from inbound in inbound-trunk; and __fish_seen_subcommand_from create' -f -l name -r -d 'Sets a new name for the trunk'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from inbound in inbound-trunk; and __fish_seen_subcommand_from create' -f -l numbers -r -d 'Sets a list of numbers for the trunk'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from inbound in inbound-trunk; and __fish_seen_subcommand_from create' -f -l media-enc -r -d 'Sets media encryption for the trunk'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from inbound in inbound-trunk; and __fish_seen_subcommand_from create' -f -l media-enc -r -d 'Sets media encryption for outbound call'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from inbound in inbound-trunk; and __fish_seen_subcommand_from create' -f -l auth-user -r -d 'Set username for authentication'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from inbound in inbound-trunk; and __fish_seen_subcommand_from create' -f -l auth-pass -r -d 'Set password for authentication'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from inbound in inbound-trunk; and __fish_seen_subcommand_from create' -f -l help -s h -d 'show help'
Expand Down Expand Up @@ -641,7 +641,7 @@ complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_f
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from outbound out outbound-trunk; and __fish_seen_subcommand_from create' -f -l address -r -d 'Sets a destination address for the trunk'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from outbound out outbound-trunk; and __fish_seen_subcommand_from create' -f -l transport -r -d 'Sets a transport for the trunk'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from outbound out outbound-trunk; and __fish_seen_subcommand_from create' -f -l destination-country -r -d 'Sets a destination country for the trunk as ISO 3166-1 alpha-2 (https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from outbound out outbound-trunk; and __fish_seen_subcommand_from create' -f -l media-enc -r -d 'Sets media encryption for the trunk'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from outbound out outbound-trunk; and __fish_seen_subcommand_from create' -f -l media-enc -r -d 'Sets media encryption for outbound call'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from outbound out outbound-trunk; and __fish_seen_subcommand_from create' -f -l numbers -r -d 'Sets a list of numbers for the trunk'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from outbound out outbound-trunk; and __fish_seen_subcommand_from create' -f -l auth-user -r -d 'Set username for authentication'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from outbound out outbound-trunk; and __fish_seen_subcommand_from create' -f -l auth-pass -r -d 'Set password for authentication'
Expand Down Expand Up @@ -671,6 +671,9 @@ complete -x -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcomman
complete -x -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from dispatch dispatch-rule; and not __fish_seen_subcommand_from list create update delete help h' -a 'create' -d 'Create a SIP Dispatch Rule'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from dispatch dispatch-rule; and __fish_seen_subcommand_from create' -f -l name -r -d 'Sets a new name for the dispatch rule'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from dispatch dispatch-rule; and __fish_seen_subcommand_from create' -f -l trunks -r -d 'Sets a list of trunks for the dispatch rule'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from dispatch dispatch-rule; and __fish_seen_subcommand_from create' -f -l media-enc -r -d 'Sets media encryption for outbound call'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from dispatch dispatch-rule; and __fish_seen_subcommand_from create' -f -l no-default-codecs -d 'Disables a builtin list of default SIP codecs'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from dispatch dispatch-rule; and __fish_seen_subcommand_from create' -f -l codecs -r -d 'Sets a list of SIP codecs for outbound call'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from dispatch dispatch-rule; and __fish_seen_subcommand_from create' -f -l help -s h -d 'show help'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from dispatch dispatch-rule; and __fish_seen_subcommand_from create' -f -l direct -r -d 'Sets a direct dispatch to a specified room'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from dispatch dispatch-rule; and __fish_seen_subcommand_from create' -f -l caller -s individual -r -d 'Sets a individual caller dispatch to a new room with a specific prefix'
Expand Down Expand Up @@ -705,6 +708,9 @@ complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_f
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from participant; and __fish_seen_subcommand_from create' -f -l wait -d 'wait for the call to dial (overrides json config)'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from participant; and __fish_seen_subcommand_from create' -f -l timeout -r -d 'timeout for the call to dial'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from participant; and __fish_seen_subcommand_from create' -f -l header -r -d 'Custom SIP header in format \'Key:Value\' (can be specified multiple times)'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from participant; and __fish_seen_subcommand_from create' -f -l media-enc -r -d 'Sets media encryption for outbound call'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from participant; and __fish_seen_subcommand_from create' -f -l no-default-codecs -d 'Disables a builtin list of default SIP codecs'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from participant; and __fish_seen_subcommand_from create' -f -l codecs -r -d 'Sets a list of SIP codecs for outbound call'
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from participant; and __fish_seen_subcommand_from create' -f -l help -s h -d 'show help'
complete -x -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from participant; and __fish_seen_subcommand_from create; and not __fish_seen_subcommand_from help h' -a 'help' -d 'Shows a list of commands or help for one command'
complete -x -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from participant; and not __fish_seen_subcommand_from create transfer help h' -a 'transfer' -d 'Transfer a SIP Participant'
Expand Down
171 changes: 137 additions & 34 deletions cmd/lk/sip.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@ import (
"fmt"
"maps"
"slices"
"strconv"
"strings"
"time"

"github.com/urfave/cli/v3"
"google.golang.org/protobuf/types/known/durationpb"

"github.com/livekit/livekit-cli/v2/pkg/util"
"github.com/livekit/protocol/livekit"
lksdk "github.com/livekit/server-sdk-go/v2"
"github.com/urfave/cli/v3"
"google.golang.org/protobuf/types/known/durationpb"
)

//lint:file-ignore SA1019 we still support older APIs for compatibility
Expand Down Expand Up @@ -63,10 +65,7 @@ var (
Name: "numbers",
Usage: "Sets a list of numbers for the trunk",
},
&cli.StringFlag{
Name: "media-enc",
Usage: "Sets media encryption for the trunk",
},
sipMediaEncFlag(),
&cli.StringFlag{
Name: "auth-user",
Usage: "Set username for authentication",
Expand Down Expand Up @@ -146,10 +145,7 @@ var (
Name: "destination-country",
Usage: "Sets a destination country for the trunk as ISO 3166-1 alpha-2 (https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)",
},
&cli.StringFlag{
Name: "media-enc",
Usage: "Sets media encryption for the trunk",
},
sipMediaEncFlag(),
&cli.StringSliceFlag{
Name: "numbers",
Usage: "Sets a list of numbers for the trunk",
Expand Down Expand Up @@ -228,7 +224,7 @@ var (
Usage: "Create a SIP Dispatch Rule",
Action: createSIPDispatchRule,
ArgsUsage: RequestDesc[livekit.CreateSIPDispatchRuleRequest](),
Flags: []cli.Flag{
Flags: appendSIPMediaFlags([]cli.Flag{
&cli.StringFlag{
Name: "name",
Usage: "Sets a new name for the dispatch rule",
Expand All @@ -237,7 +233,7 @@ var (
Name: "trunks",
Usage: "Sets a list of trunks for the dispatch rule",
},
},
}),
MutuallyExclusiveFlags: []cli.MutuallyExclusiveFlags{
{
Flags: [][]cli.Flag{
Expand Down Expand Up @@ -334,7 +330,7 @@ var (
Usage: "Create a SIP Participant",
Action: createSIPParticipant,
ArgsUsage: RequestDesc[livekit.CreateSIPParticipantRequest](),
Flags: []cli.Flag{
Flags: appendSIPMediaFlags([]cli.Flag{
optional(roomFlag),
optional(identityFlag),
&cli.StringFlag{
Expand Down Expand Up @@ -374,7 +370,7 @@ var (
Name: "header",
Usage: "Custom SIP header in format 'Key:Value' (can be specified multiple times)",
},
},
}),
},
{
Name: "transfer",
Expand Down Expand Up @@ -481,6 +477,105 @@ func listSetFlag(cmd *cli.Command, setName string) ([]string, bool) {
return val, true
}

func optBoolFlag(cmd *cli.Command, setName string) (bool, bool) {
if !cmd.IsSet(setName) {
return false, false
}
return cmd.Bool(setName), true
}

func sipMediaEncFlag() cli.Flag {
return &cli.StringFlag{
Name: "media-enc",
Usage: "Sets media encryption for outbound call",
}
}

func appendSIPMediaFlags(flags []cli.Flag) []cli.Flag {
flags = append(flags,
sipMediaEncFlag(),
&cli.BoolFlag{
Name: "no-default-codecs",
Usage: "Disables a builtin list of default SIP codecs",
},
&cli.StringSliceFlag{
Name: "codecs",
Usage: "Sets a list of SIP codecs for outbound call",
},
)
return flags
}

func parseSIPMediaEnc(cmd *cli.Command) (*livekit.SIPMediaEncryption, error) {
val := cmd.String("media-enc")
if val == "" {
return nil, nil
}
val = strings.ToUpper(val)
v, ok := livekit.SIPMediaEncryption_value[val]
if !ok {
v, ok = livekit.SIPMediaEncryption_value["SIP_MEDIA_ENCRYPT_"+val]
}
if !ok {
return nil, fmt.Errorf("invalid value for SIP media encryption: %q", val)
}
return new(livekit.SIPMediaEncryption(v)), nil
}

func parseSIPCodecs(vals []string) ([]*livekit.SIPCodec, error) {
var out []*livekit.SIPCodec
for _, s := range vals {
sub := strings.SplitN(s, "/", 3)
if len(sub) > 2 {
return out, fmt.Errorf("invalid media codec: %q, expected: <name> or <name>/<sample-rate>", s)
}
name := sub[0]
if name == "" {
return out, fmt.Errorf("invalid media codec: %q: empty name", s)
}
var rate uint64
if len(sub) >= 2 {
var err error
rate, err = strconv.ParseUint(sub[1], 10, 32)
if err != nil {
return out, fmt.Errorf("invalid media codec: %q: %w", s, err)
}
}
out = append(out, &livekit.SIPCodec{
Name: name,
Rate: uint32(rate),
})
}
return out, nil
}

func parseSIPMediaConfig(cmd *cli.Command) (*livekit.SIPMediaConfig, error) {
var m *livekit.SIPMediaConfig
getMedia := func() *livekit.SIPMediaConfig {
if m == nil {
m = new(livekit.SIPMediaConfig)
}
return m
}
if enc, err := parseSIPMediaEnc(cmd); err == nil && enc != nil {
getMedia().Encryption = enc
} else if err != nil {
return nil, err
}
if vals, ok := listSetFlag(cmd, "codecs"); ok {
codecs, err := parseSIPCodecs(vals)
if err != nil {
return nil, err
}
m := getMedia()
m.Codecs = codecs
if val, ok := optBoolFlag(cmd, "no-default-codecs"); ok {
m.OnlyListedCodecs = val
}
Comment on lines +572 to +574

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I personally feel like the indirection here doesn't make this simpler.

Suggested change
if val, ok := optBoolFlag(cmd, "no-default-codecs"); ok {
m.OnlyListedCodecs = val
}
noDefaultCodecsFlag := "no-default-codecs"
if cmd.isSet(noDefaultCodecsFlag) {
m.OnlyListedCodecs = cmd.Bool(noDefaultCodecsFlag)
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't require an extra var with the flag name, though.

}
return m, nil
}

func createSIPClient(ctx context.Context, cmd *cli.Command) (*lksdk.SIPClient, error) {
_, err := requireProject(ctx, cmd)
if err != nil {
Expand All @@ -505,16 +600,10 @@ func createSIPInboundTrunk(ctx context.Context, cmd *cli.Command) error {
if val, ok := listSetFlag(cmd, "numbers"); ok {
p.Numbers = val
}
if val := cmd.String("media-enc"); val != "" {
val = strings.ToUpper(val)
v, ok := livekit.SIPMediaEncryption_value[val]
if !ok {
v, ok = livekit.SIPMediaEncryption_value["SIP_MEDIA_ENCRYPT_"+val]
}
if !ok {
return fmt.Errorf("invalid value for SIP media encryption: %q", val)
}
p.MediaEncryption = livekit.SIPMediaEncryption(v)
if enc, err := parseSIPMediaEnc(cmd); err == nil && enc != nil {
p.MediaEncryption = *enc
} else if err != nil {
return err
}
if val := cmd.String("auth-user"); val != "" {
p.AuthUsername = val
Expand Down Expand Up @@ -618,16 +707,10 @@ func createSIPOutboundTrunk(ctx context.Context, cmd *cli.Command) error {
if val := cmd.String("destination-country"); val != "" {
p.DestinationCountry = val
}
if val := cmd.String("media-enc"); val != "" {
val = strings.ToUpper(val)
v, ok := livekit.SIPMediaEncryption_value[val]
if !ok {
v, ok = livekit.SIPMediaEncryption_value["SIP_MEDIA_ENCRYPT_"+val]
}
if !ok {
return fmt.Errorf("invalid value for SIP media encryption: %q", val)
}
p.MediaEncryption = livekit.SIPMediaEncryption(v)
if enc, err := parseSIPMediaEnc(cmd); err == nil && enc != nil {
p.MediaEncryption = *enc
} else if err != nil {
return err
}
if val, ok := listSetFlag(cmd, "numbers"); ok {
p.Numbers = val
Expand Down Expand Up @@ -898,6 +981,16 @@ func createSIPDispatchRule(ctx context.Context, cmd *cli.Command) error {
if val, ok := listSetFlag(cmd, "trunks"); ok {
p.TrunkIds = val
}
if m, err := parseSIPMediaConfig(cmd); err == nil {
p.Media = m
} else if err != nil {
return err
}
if enc, err := parseSIPMediaEnc(cmd); err == nil && enc != nil {
p.MediaEncryption = *enc
} else if err != nil {
return err
}
if val := cmd.String("direct"); val != "" {
if p.Rule != nil {
return fmt.Errorf("only one dispatch rule type is allowed")
Expand Down Expand Up @@ -1155,6 +1248,16 @@ func createSIPParticipant(ctx context.Context, cmd *cli.Command) error {
if cmd.Bool("wait") {
req.WaitUntilAnswered = true
}
if m, err := parseSIPMediaConfig(cmd); err == nil {
req.Media = m
} else if err != nil {
return err
}
if enc, err := parseSIPMediaEnc(cmd); err == nil && enc != nil {
req.MediaEncryption = *enc
} else if err != nil {
return err
}

// Parse headers from repeatable "header" flag
if headers := cmd.StringSlice("header"); len(headers) > 0 {
Expand Down
Loading