138 lines
3.1 KiB
Go
138 lines
3.1 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"flag"
|
|
"fmt"
|
|
"github.com/chzyer/readline"
|
|
ricochet "github.com/ricochet-im/ricochet-go/core"
|
|
rpc "github.com/ricochet-im/ricochet-go/rpc"
|
|
"google.golang.org/grpc"
|
|
"log"
|
|
"net"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
LogBuffer bytes.Buffer
|
|
|
|
// Flags
|
|
backendAddress string
|
|
unsafeBackend bool
|
|
)
|
|
|
|
func main() {
|
|
flag.StringVar(&backendAddress, "backend", "", "Connect to the client backend running on `address`")
|
|
flag.BoolVar(&unsafeBackend, "allow-unsafe-backend", false, "Allow a remote backend address. This is NOT RECOMMENDED and may harm your security or privacy. Do not use without a secure, trusted link")
|
|
flag.Parse()
|
|
|
|
// Set up readline
|
|
input, err := readline.NewEx(&readline.Config{
|
|
InterruptPrompt: "^C",
|
|
EOFPrompt: "exit",
|
|
})
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
os.Exit(1)
|
|
}
|
|
defer input.Close()
|
|
log.SetOutput(&LogBuffer)
|
|
|
|
// Connect to RPC backend, start in-process backend if necessary
|
|
conn, err := connectClientBackend()
|
|
if err != nil {
|
|
fmt.Printf("backend failed: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
defer conn.Close()
|
|
|
|
// Configure client and UI
|
|
client := &Client{
|
|
Backend: rpc.NewRicochetCoreClient(conn),
|
|
}
|
|
Ui = UI{
|
|
Input: input,
|
|
Stdout: input.Stdout(),
|
|
Client: client,
|
|
}
|
|
|
|
// Initialize data from backend and start UI command loop
|
|
fmt.Print("Connecting to backend...\n")
|
|
go func() {
|
|
if err := client.Initialize(); err != nil {
|
|
fmt.Printf("Error: %s\n", err)
|
|
os.Exit(1)
|
|
}
|
|
client.Block()
|
|
Ui.PrintStatus()
|
|
client.Unblock()
|
|
}()
|
|
|
|
Ui.CommandLoop()
|
|
}
|
|
|
|
func connectClientBackend() (*grpc.ClientConn, error) {
|
|
if backendAddress == "" {
|
|
// In-process backend, using 'InnerNet' as a fake socket
|
|
address, err := startLocalBackend()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return grpc.Dial(address, grpc.WithInsecure(), grpc.WithDialer(DialInnerNet))
|
|
} else {
|
|
// External backend
|
|
if strings.HasPrefix(backendAddress, "unix:") {
|
|
return grpc.Dial(backendAddress[5:], grpc.WithInsecure(),
|
|
grpc.WithDialer(func(address string, timeout time.Duration) (net.Conn, error) {
|
|
return net.DialTimeout("unix", address, timeout)
|
|
}))
|
|
} else {
|
|
host, _, err := net.SplitHostPort(backendAddress)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ip := net.ParseIP(host)
|
|
if !unsafeBackend && (ip == nil || !ip.IsLoopback()) {
|
|
return nil, fmt.Errorf("Host '%s' is not a loopback address.\nRead the warnings and use -allow-unsafe-backend for non-local addresses", host)
|
|
}
|
|
|
|
return grpc.Dial(backendAddress, grpc.WithInsecure())
|
|
}
|
|
}
|
|
}
|
|
|
|
func startLocalBackend() (string, error) {
|
|
config, err := ricochet.LoadConfig(".")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
core := new(ricochet.Ricochet)
|
|
if err := core.Init(config); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
listener, err := ListenInnerNet("ricochet.rpc")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
server := &ricochet.RpcServer{
|
|
Core: core,
|
|
}
|
|
|
|
go func() {
|
|
grpcServer := grpc.NewServer()
|
|
rpc.RegisterRicochetCoreServer(grpcServer, server)
|
|
err := grpcServer.Serve(listener)
|
|
if err != nil {
|
|
log.Printf("backend exited: %v", err)
|
|
os.Exit(1)
|
|
}
|
|
}()
|
|
|
|
return "ricochet.rpc", nil
|
|
}
|