// Package host définit le modèle d'un host SSH enregistré.
package host
import (
"errors"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
)
// Host représente une entrée SSH enregistrée.
type Host struct {
Name string `toml:"-"`
User string `toml:"user,omitempty"`
Host string `toml:"host"`
Port int `toml:"port,omitempty"`
Identity string `toml:"identity,omitempty"`
Jump string `toml:"jump,omitempty"`
Tags []string `toml:"tags,omitempty"`
}
// Parse construit un Host depuis une cible de la forme [user@]host[:port].
func Parse(name, target string) (Host, error) {
h := Host{Name: name}
if i := strings.LastIndex(target, "@"); i >= 0 {
h.User = target[:i]
target = target[i+1:]
}
if i := strings.LastIndex(target, ":"); i >= 0 && !strings.Contains(target, "]") {
port, err := strconv.Atoi(target[i+1:])
if err != nil {
return h, fmt.Errorf("port invalide dans %q", target)
}
h.Port = port
target = target[:i]
}
if target == "" {
return h, errors.New("hôte vide")
}
h.Host = target
return h, nil
}
// Target rend la cible ssh, ex. "inoval@51.210.14.32".
func (h Host) Target() string {
t := h.Host
if h.User != "" {
t = h.User + "@" + t
}
return t
}
// JumpSpec rend la forme [user@]host[:port] utilisable après -J.
func (h Host) JumpSpec() string {
s := h.Target()
if h.Port != 0 && h.Port != 22 {
s += ":" + strconv.Itoa(h.Port)
}
return s
}
// SSHArgs construit les arguments à passer au binaire ssh.
func (h Host) SSHArgs(extra ...string) []string {
var args []string
if h.Port != 0 && h.Port != 22 {
args = append(args, "-p", strconv.Itoa(h.Port))
}
if h.Identity != "" {
args = append(args, "-i", ExpandTilde(h.Identity))
}
if h.Jump != "" {
args = append(args, "-J", h.Jump)
}
args = append(args, h.Target())
return append(args, extra...)
}
// Describe rend une description courte pour les listes (TUI, sssh list).
func (h Host) Describe() string {
d := h.Target()
if h.Port != 0 && h.Port != 22 {
d += ":" + strconv.Itoa(h.Port)
}
if len(h.Tags) > 0 {
d += " #" + strings.Join(h.Tags, " #")
}
return d
}
// ExpandTilde remplace un préfixe ~/ par le home de l'utilisateur.
func ExpandTilde(p string) string {
if p == "~" || strings.HasPrefix(p, "~/") {
home, err := os.UserHomeDir()
if err != nil {
return p
}
return filepath.Join(home, strings.TrimPrefix(p[1:], "/"))
}
return p
}