// Copyright (c) 2026 Storm Dragon // Authors: Storm Dragon, Angus Kola // SPDX-License-Identifier: GPL-3.0-or-later package speech import ( "fmt" "net" "os" "path/filepath" "strconv" "strings" ) type serverAddress struct { network string address string } // DefaultSocketAddress follows the default used by the Python speechd client. func DefaultSocketAddress() string { runtimeDirectory := os.Getenv("XDG_RUNTIME_DIR") if runtimeDirectory == "" { runtimeDirectory = os.Getenv("XDG_CACHE_HOME") } if runtimeDirectory == "" { cacheDirectory, err := os.UserCacheDir() if err == nil { runtimeDirectory = cacheDirectory } } return filepath.Join(runtimeDirectory, "speech-dispatcher", "speechd.sock") } // resolveServerAddress gives an explicit CLI socket priority over speechd's // standard address variables. SPEECHD_SOCKET is the older Unix-only setting. func resolveServerAddress(socket string) (serverAddress, error) { if socket != "" { return serverAddress{network: "unix", address: socket}, nil } if address := os.Getenv("SPEECHD_ADDRESS"); address != "" { return parseServerAddress(address) } if socket := os.Getenv("SPEECHD_SOCKET"); socket != "" { return serverAddress{network: "unix", address: socket}, nil } return serverAddress{network: "unix", address: DefaultSocketAddress()}, nil } func parseServerAddress(address string) (serverAddress, error) { method, value, _ := strings.Cut(address, ":") switch method { case "unix_socket": if value == "" { value = DefaultSocketAddress() } if !filepath.IsAbs(value) { return serverAddress{}, fmt.Errorf("Speech Dispatcher Unix socket must be an absolute path: %q", value) } return serverAddress{network: "unix", address: value}, nil case "inet_socket": host, port := "127.0.0.1", 6560 if value != "" { parts := strings.Split(value, ":") if len(parts) > 2 { return serverAddress{}, fmt.Errorf("invalid Speech Dispatcher address %q", address) } if parts[0] != "" { host = parts[0] } if len(parts) == 2 { parsed, err := strconv.Atoi(parts[1]) if err != nil || parsed < 1 || parsed > 65535 { return serverAddress{}, fmt.Errorf("invalid Speech Dispatcher port in %q", address) } port = parsed } } return serverAddress{network: "tcp4", address: net.JoinHostPort(host, strconv.Itoa(port))}, nil default: return serverAddress{}, fmt.Errorf("unsupported Speech Dispatcher address %q", address) } }