106 lines
3.2 KiB
Go
106 lines
3.2 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package speech
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"os/user"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type serverAddress struct {
|
|
network string
|
|
address string
|
|
}
|
|
|
|
// default_socket_path follows Python speechd, retaining Fenrir's sudo-user
|
|
// runtime directory selection for console sessions.
|
|
func default_socket_path() string {
|
|
if uid := os.Getenv("SUDO_UID"); uid != "" {
|
|
if parsed, err := strconv.ParseUint(uid, 10, 32); err == nil {
|
|
runtimeDirectory := os.Getenv("XDG_RUNTIME_DIR")
|
|
if !owned_directory(runtimeDirectory, uint32(parsed)) {
|
|
runtimeDirectory = filepath.Join("/run/user", uid)
|
|
}
|
|
if !owned_directory(runtimeDirectory, uint32(parsed)) {
|
|
if account, err := user.LookupId(uid); err == nil {
|
|
runtimeDirectory = filepath.Join(account.HomeDir, ".cache")
|
|
}
|
|
}
|
|
return filepath.Join(runtimeDirectory, "speech-dispatcher/speechd.sock")
|
|
}
|
|
}
|
|
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")
|
|
}
|
|
|
|
// resolve_server_address gives an internal socket override priority over speechd's
|
|
// standard address variables. SPEECHD_SOCKET is the older Unix-only setting.
|
|
func resolve_server_address(socket string) (serverAddress, error) {
|
|
if socket != "" {
|
|
return serverAddress{network: "unix", address: socket}, nil
|
|
}
|
|
if address := os.Getenv("SPEECHD_ADDRESS"); address != "" {
|
|
return parse_server_address(address)
|
|
}
|
|
if socket := os.Getenv("SPEECHD_SOCKET"); socket != "" {
|
|
return serverAddress{network: "unix", address: socket}, nil
|
|
}
|
|
return serverAddress{network: "unix", address: default_socket_path()}, nil
|
|
}
|
|
|
|
func parse_server_address(address string) (serverAddress, error) {
|
|
method, value, _ := strings.Cut(address, ":")
|
|
switch method {
|
|
case "unix_socket":
|
|
if value == "" {
|
|
value = default_socket_path()
|
|
}
|
|
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)
|
|
}
|
|
}
|
|
|
|
// Explicit settings select exactly one server; only defaults use fallback.
|
|
func automatic_server_address(socket string) bool {
|
|
return socket == "" && os.Getenv("SPEECHD_ADDRESS") == "" && os.Getenv("SPEECHD_SOCKET") == ""
|
|
}
|