cli: Improve startup process
This commit is contained in:
		
							parent
							
								
									c8bfe4663e
								
							
						
					
					
						commit
						e9f22c64ac
					
				
							
								
								
									
										35
									
								
								cli/cli.go
								
								
								
								
							
							
						
						
									
										35
									
								
								cli/cli.go
								
								
								
								
							| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	rpc "github.com/special/notricochet/rpc"
 | 
			
		||||
	"google.golang.org/grpc"
 | 
			
		||||
| 
						 | 
				
			
			@ -13,21 +14,23 @@ const (
 | 
			
		|||
	defaultAddress = "127.0.0.1:58281"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	conn, err := grpc.Dial(defaultAddress, grpc.WithInsecure())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("connection failed: %v\n", err)
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
	defer conn.Close()
 | 
			
		||||
var LogBuffer bytes.Buffer
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	input, err := readline.NewEx(&readline.Config{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println(err)
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
	defer input.Close()
 | 
			
		||||
	log.SetOutput(input.Stdout())
 | 
			
		||||
	log.SetOutput(&LogBuffer)
 | 
			
		||||
 | 
			
		||||
	conn, err := grpc.Dial(defaultAddress, grpc.WithInsecure())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("connection failed: %v\n", err)
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
	defer conn.Close()
 | 
			
		||||
 | 
			
		||||
	client := &Client{
 | 
			
		||||
		Backend: rpc.NewRicochetCoreClient(conn),
 | 
			
		||||
| 
						 | 
				
			
			@ -38,11 +41,17 @@ func main() {
 | 
			
		|||
	}
 | 
			
		||||
	client.Ui = ui
 | 
			
		||||
 | 
			
		||||
	if err := client.Initialize(); err != nil {
 | 
			
		||||
		fmt.Println(err)
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
	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()
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	go client.Run()
 | 
			
		||||
	ui.CommandLoop()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,10 +17,14 @@ type Client struct {
 | 
			
		|||
	NetworkStatus ricochet.NetworkStatus
 | 
			
		||||
	Contacts      *ContactList
 | 
			
		||||
 | 
			
		||||
	monitorsChannel   chan interface{}
 | 
			
		||||
	blockChannel      chan struct{}
 | 
			
		||||
	unblockChannel    chan struct{}
 | 
			
		||||
	populatedContacts bool
 | 
			
		||||
	monitorsChannel chan interface{}
 | 
			
		||||
	blockChannel    chan struct{}
 | 
			
		||||
	unblockChannel  chan struct{}
 | 
			
		||||
 | 
			
		||||
	populatedChannel       chan struct{}
 | 
			
		||||
	populatedNetwork       bool
 | 
			
		||||
	populatedContacts      bool
 | 
			
		||||
	populatedConversations bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// XXX need to handle backend connection loss/reconnection..
 | 
			
		||||
| 
						 | 
				
			
			@ -29,6 +33,7 @@ func (c *Client) Initialize() error {
 | 
			
		|||
	c.monitorsChannel = make(chan interface{}, 10)
 | 
			
		||||
	c.blockChannel = make(chan struct{})
 | 
			
		||||
	c.unblockChannel = make(chan struct{})
 | 
			
		||||
	c.populatedChannel = make(chan struct{})
 | 
			
		||||
 | 
			
		||||
	// Query server status and version
 | 
			
		||||
	status, err := c.Backend.GetServerStatus(context.Background(), &ricochet.ServerStatusRequest{
 | 
			
		||||
| 
						 | 
				
			
			@ -39,7 +44,7 @@ func (c *Client) Initialize() error {
 | 
			
		|||
	}
 | 
			
		||||
	c.ServerStatus = *status
 | 
			
		||||
	if status.RpcVersion != 1 {
 | 
			
		||||
		return fmt.Errorf("Unsupported backend RPC version %d", status.RpcVersion)
 | 
			
		||||
		return fmt.Errorf("unsupported backend RPC version %d", status.RpcVersion)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Query identity
 | 
			
		||||
| 
						 | 
				
			
			@ -54,7 +59,11 @@ func (c *Client) Initialize() error {
 | 
			
		|||
	go c.monitorContacts()
 | 
			
		||||
	// Conversation monitor isn't started until contacts are populated
 | 
			
		||||
 | 
			
		||||
	// XXX block until populated/initialized?
 | 
			
		||||
	// Spawn routine to handle all events
 | 
			
		||||
	go c.Run()
 | 
			
		||||
 | 
			
		||||
	// Block until all state is populated
 | 
			
		||||
	<-c.populatedChannel
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -86,6 +95,18 @@ func (c *Client) Unblock() {
 | 
			
		|||
	c.unblockChannel <- struct{}{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) IsInitialized() bool {
 | 
			
		||||
	return c.populatedChannel == nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) checkIfPopulated() {
 | 
			
		||||
	if c.populatedChannel != nil && c.populatedContacts &&
 | 
			
		||||
		c.populatedConversations && c.populatedNetwork {
 | 
			
		||||
		close(c.populatedChannel)
 | 
			
		||||
		c.populatedChannel = nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) monitorNetwork() {
 | 
			
		||||
	stream, err := c.Backend.MonitorNetwork(context.Background(), &ricochet.MonitorNetworkRequest{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -149,6 +170,8 @@ func (c *Client) monitorConversations() {
 | 
			
		|||
func (c *Client) onNetworkStatus(status *ricochet.NetworkStatus) {
 | 
			
		||||
	log.Printf("Network status changed: %v", status)
 | 
			
		||||
	c.NetworkStatus = *status
 | 
			
		||||
	c.populatedNetwork = true
 | 
			
		||||
	c.checkIfPopulated()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) onContactEvent(event *ricochet.ContactEvent) {
 | 
			
		||||
| 
						 | 
				
			
			@ -161,10 +184,13 @@ func (c *Client) onContactEvent(event *ricochet.ContactEvent) {
 | 
			
		|||
 | 
			
		||||
	switch event.Type {
 | 
			
		||||
	case ricochet.ContactEvent_POPULATE:
 | 
			
		||||
		// Populate is terminated by a nil subject
 | 
			
		||||
		if event.Subject == nil {
 | 
			
		||||
		if c.populatedContacts {
 | 
			
		||||
			log.Printf("Ignoring unexpected contact populate event: %v", event)
 | 
			
		||||
		} else if event.Subject == nil {
 | 
			
		||||
			// Populate is terminated by a nil subject
 | 
			
		||||
			c.populatedContacts = true
 | 
			
		||||
			log.Printf("Loaded %d contacts", len(c.Contacts.Contacts))
 | 
			
		||||
			c.checkIfPopulated()
 | 
			
		||||
			go c.monitorConversations()
 | 
			
		||||
		} else if data != nil {
 | 
			
		||||
			c.Contacts.Populate(data)
 | 
			
		||||
| 
						 | 
				
			
			@ -218,6 +244,13 @@ func (c *Client) onConversationEvent(event *ricochet.ConversationEvent) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	message := event.Msg
 | 
			
		||||
 | 
			
		||||
	if event.Type == ricochet.ConversationEvent_POPULATE && message == nil {
 | 
			
		||||
		c.populatedConversations = true
 | 
			
		||||
		c.checkIfPopulated()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if message == nil || message.Recipient == nil || message.Sender == nil {
 | 
			
		||||
		log.Printf("Ignoring invalid conversation event: %v", event)
 | 
			
		||||
		return
 | 
			
		||||
| 
						 | 
				
			
			@ -236,8 +269,11 @@ func (c *Client) onConversationEvent(event *ricochet.ConversationEvent) {
 | 
			
		|||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.Ui.PrintMessage(remoteContact, message.Sender.IsSelf, message.Text)
 | 
			
		||||
	if event.Type != ricochet.ConversationEvent_POPULATE {
 | 
			
		||||
		c.Ui.PrintMessage(remoteContact, message.Sender.IsSelf, message.Text)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// XXX Shouldn't mark until displayed
 | 
			
		||||
	if !message.Sender.IsSelf {
 | 
			
		||||
		backend := c.Backend
 | 
			
		||||
		message := message
 | 
			
		||||
| 
						 | 
				
			
			@ -252,3 +288,19 @@ func (c *Client) onConversationEvent(event *ricochet.ConversationEvent) {
 | 
			
		|||
		}()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) NetworkControlStatus() ricochet.TorControlStatus {
 | 
			
		||||
	if c.NetworkStatus.Control != nil {
 | 
			
		||||
		return *c.NetworkStatus.Control
 | 
			
		||||
	} else {
 | 
			
		||||
		return ricochet.TorControlStatus{}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) NetworkConnectionStatus() ricochet.TorConnectionStatus {
 | 
			
		||||
	if c.NetworkStatus.Connection != nil {
 | 
			
		||||
		return *c.NetworkStatus.Connection
 | 
			
		||||
	} else {
 | 
			
		||||
		return ricochet.TorConnectionStatus{}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										40
									
								
								cli/ui.go
								
								
								
								
							
							
						
						
									
										40
									
								
								cli/ui.go
								
								
								
								
							| 
						 | 
				
			
			@ -87,16 +87,54 @@ func (ui *UI) Execute(line string) error {
 | 
			
		|||
	case "contacts":
 | 
			
		||||
		ui.ListContacts()
 | 
			
		||||
 | 
			
		||||
	case "log":
 | 
			
		||||
		fmt.Print(LogBuffer.String())
 | 
			
		||||
 | 
			
		||||
	case "help":
 | 
			
		||||
		fallthrough
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		fmt.Println("Commands: clear, quit, status, connect, disconnect, contacts, help")
 | 
			
		||||
		fmt.Println("Commands: clear, quit, status, connect, disconnect, contacts, log, help")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ui *UI) PrintStatus() {
 | 
			
		||||
	controlStatus := ui.Client.NetworkControlStatus()
 | 
			
		||||
	connectionStatus := ui.Client.NetworkConnectionStatus()
 | 
			
		||||
 | 
			
		||||
	switch controlStatus.Status {
 | 
			
		||||
	case ricochet.TorControlStatus_STOPPED:
 | 
			
		||||
		fmt.Fprintf(ui.Input.Stdout(), "Network is stopped -- type 'connect' to go online\n")
 | 
			
		||||
 | 
			
		||||
	case ricochet.TorControlStatus_ERROR:
 | 
			
		||||
		fmt.Fprintf(ui.Input.Stdout(), "Network error: %s\n", controlStatus.ErrorMessage)
 | 
			
		||||
 | 
			
		||||
	case ricochet.TorControlStatus_CONNECTING:
 | 
			
		||||
		fmt.Fprintf(ui.Input.Stdout(), "Network connecting...\n")
 | 
			
		||||
 | 
			
		||||
	case ricochet.TorControlStatus_CONNECTED:
 | 
			
		||||
		switch connectionStatus.Status {
 | 
			
		||||
		case ricochet.TorConnectionStatus_UNKNOWN:
 | 
			
		||||
			fallthrough
 | 
			
		||||
		case ricochet.TorConnectionStatus_OFFLINE:
 | 
			
		||||
			fmt.Fprintf(ui.Input.Stdout(), "Network is offline\n")
 | 
			
		||||
 | 
			
		||||
		case ricochet.TorConnectionStatus_BOOTSTRAPPING:
 | 
			
		||||
			fmt.Fprintf(ui.Input.Stdout(), "Network bootstrapping: %s\n", connectionStatus.BootstrapProgress)
 | 
			
		||||
 | 
			
		||||
		case ricochet.TorConnectionStatus_READY:
 | 
			
		||||
			fmt.Fprintf(ui.Input.Stdout(), "Network is online\n")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fmt.Fprintf(ui.Input.Stdout(), "Your ricochet ID is %s\n", ui.Client.Identity.Address)
 | 
			
		||||
 | 
			
		||||
	// no. contacts, contact reqs, online contacts
 | 
			
		||||
	// unread messages
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ui *UI) PrintMessage(contact *Contact, outbound bool, text string) {
 | 
			
		||||
	if contact == ui.CurrentContact {
 | 
			
		||||
		if outbound {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue