2016-09-16 21:26:14 +00:00
package main
import (
"fmt"
2016-10-17 04:26:35 +00:00
"github.com/ricochet-im/ricochet-go/rpc"
2016-09-16 21:26:14 +00:00
"golang.org/x/net/context"
"log"
)
type Client struct {
Backend ricochet . RicochetCoreClient
ServerStatus ricochet . ServerStatusReply
Identity ricochet . Identity
2016-10-16 04:57:16 +00:00
NetworkStatus ricochet . NetworkStatus
Contacts * ContactList
2016-10-16 03:57:07 +00:00
2016-10-16 21:50:34 +00:00
monitorsChannel chan interface { }
blockChannel chan struct { }
unblockChannel chan struct { }
populatedChannel chan struct { }
populatedNetwork bool
populatedContacts bool
populatedConversations bool
2016-09-16 21:26:14 +00:00
}
// XXX need to handle backend connection loss/reconnection..
func ( c * Client ) Initialize ( ) error {
2016-10-23 00:52:26 +00:00
c . Contacts = NewContactList ( c )
2016-10-16 03:57:07 +00:00
c . monitorsChannel = make ( chan interface { } , 10 )
2016-10-16 04:57:16 +00:00
c . blockChannel = make ( chan struct { } )
c . unblockChannel = make ( chan struct { } )
2016-10-16 21:50:34 +00:00
c . populatedChannel = make ( chan struct { } )
2016-10-16 03:13:02 +00:00
2016-09-16 21:26:14 +00:00
// Query server status and version
status , err := c . Backend . GetServerStatus ( context . Background ( ) , & ricochet . ServerStatusRequest {
RpcVersion : 1 ,
} )
if err != nil {
return err
}
c . ServerStatus = * status
if status . RpcVersion != 1 {
2016-10-16 21:50:34 +00:00
return fmt . Errorf ( "unsupported backend RPC version %d" , status . RpcVersion )
2016-09-16 21:26:14 +00:00
}
// Query identity
identity , err := c . Backend . GetIdentity ( context . Background ( ) , & ricochet . IdentityRequest { } )
if err != nil {
return err
}
c . Identity = * identity
// Spawn routines to query and monitor state changes
go c . monitorNetwork ( )
go c . monitorContacts ( )
2016-10-16 03:57:07 +00:00
// Conversation monitor isn't started until contacts are populated
2016-09-16 21:26:14 +00:00
2016-10-16 21:50:34 +00:00
// Spawn routine to handle all events
go c . Run ( )
// Block until all state is populated
<- c . populatedChannel
2016-09-16 21:26:14 +00:00
return nil
}
2016-10-16 03:57:07 +00:00
func ( c * Client ) Run ( ) {
for {
select {
case v := <- c . monitorsChannel :
switch event := v . ( type ) {
case * ricochet . NetworkStatus :
c . onNetworkStatus ( event )
case * ricochet . ContactEvent :
c . onContactEvent ( event )
case * ricochet . ConversationEvent :
c . onConversationEvent ( event )
default :
log . Panicf ( "Unknown event type on monitor channel: %v" , event )
}
2016-10-16 04:57:16 +00:00
case <- c . blockChannel :
<- c . unblockChannel
2016-10-16 03:57:07 +00:00
}
}
}
2016-10-16 04:57:16 +00:00
func ( c * Client ) Block ( ) {
c . blockChannel <- struct { } { }
}
func ( c * Client ) Unblock ( ) {
c . unblockChannel <- struct { } { }
2016-10-06 23:50:52 +00:00
}
2016-10-16 21:50:34 +00:00
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
}
}
2016-09-16 21:26:14 +00:00
func ( c * Client ) monitorNetwork ( ) {
stream , err := c . Backend . MonitorNetwork ( context . Background ( ) , & ricochet . MonitorNetworkRequest { } )
if err != nil {
log . Printf ( "Initializing network status monitor failed: %v" , err )
// XXX handle
return
}
for {
status , err := stream . Recv ( )
if err != nil {
log . Printf ( "Network status monitor error: %v" , err )
// XXX handle
break
}
2016-10-16 03:57:07 +00:00
c . monitorsChannel <- status
2016-09-16 21:26:14 +00:00
}
}
func ( c * Client ) monitorContacts ( ) {
stream , err := c . Backend . MonitorContacts ( context . Background ( ) , & ricochet . MonitorContactsRequest { } )
if err != nil {
log . Printf ( "Initializing contact status monitor failed: %v" , err )
// XXX handle
return
}
for {
event , err := stream . Recv ( )
if err != nil {
2016-10-16 03:57:07 +00:00
log . Printf ( "Contact monitor error: %v" , err )
2016-09-16 21:26:14 +00:00
// XXX handle
break
}
2016-10-16 03:57:07 +00:00
c . monitorsChannel <- event
2016-09-16 21:26:14 +00:00
}
2016-10-16 03:57:07 +00:00
}
2016-09-16 21:26:14 +00:00
2016-10-16 03:57:07 +00:00
func ( c * Client ) monitorConversations ( ) {
stream , err := c . Backend . MonitorConversations ( context . Background ( ) , & ricochet . MonitorConversationsRequest { } )
if err != nil {
log . Printf ( "Initializing conversations monitor failed: %v" , err )
// XXX handle
return
}
2016-09-16 21:26:14 +00:00
for {
event , err := stream . Recv ( )
if err != nil {
2016-10-16 03:57:07 +00:00
log . Printf ( "Conversations monitor error: %v" , err )
2016-09-16 21:26:14 +00:00
// XXX handle
break
}
2016-10-16 03:57:07 +00:00
c . monitorsChannel <- event
}
}
2016-09-16 21:26:14 +00:00
2016-10-16 03:57:07 +00:00
func ( c * Client ) onNetworkStatus ( status * ricochet . NetworkStatus ) {
log . Printf ( "Network status changed: %v" , status )
c . NetworkStatus = * status
2016-10-16 21:50:34 +00:00
c . populatedNetwork = true
c . checkIfPopulated ( )
2016-10-16 03:57:07 +00:00
}
2016-10-16 03:13:02 +00:00
2016-10-16 03:57:07 +00:00
func ( c * Client ) onContactEvent ( event * ricochet . ContactEvent ) {
if ! c . populatedContacts && event . Type != ricochet . ContactEvent_POPULATE {
log . Printf ( "Ignoring unexpected contact event during populate: %v" , event )
return
2017-10-15 03:21:02 +00:00
} else if c . populatedContacts && event . Type == ricochet . ContactEvent_POPULATE {
log . Printf ( "Ignoring unexpected contact populate event: %v" , event )
return
2016-10-16 03:57:07 +00:00
}
2016-09-16 21:26:14 +00:00
2017-10-15 03:21:02 +00:00
if cData := event . GetContact ( ) ; cData != nil {
switch event . Type {
case ricochet . ContactEvent_POPULATE :
c . Contacts . Populate ( cData )
2016-09-16 21:26:14 +00:00
2017-10-15 03:21:02 +00:00
case ricochet . ContactEvent_ADD :
c . Contacts . Added ( cData )
2016-09-16 21:26:14 +00:00
2017-10-15 03:21:02 +00:00
case ricochet . ContactEvent_UPDATE :
contact := c . Contacts . ByAddress ( cData . Address )
if contact == nil {
log . Printf ( "Ignoring contact update event for unknown contact: %v" , cData )
} else {
contact . Updated ( cData )
}
2016-10-16 03:57:07 +00:00
2017-10-15 03:21:02 +00:00
case ricochet . ContactEvent_DELETE :
contact , _ := c . Contacts . Deleted ( cData )
if Ui . CurrentContact == contact {
Ui . SetCurrentContact ( nil )
}
2016-10-06 23:50:52 +00:00
2017-10-15 03:21:02 +00:00
default :
log . Printf ( "Ignoring unknown contact event: %v" , event )
2016-09-16 21:26:14 +00:00
}
2017-10-15 03:21:02 +00:00
} else if reqData := event . GetRequest ( ) ; reqData != nil {
switch event . Type {
case ricochet . ContactEvent_POPULATE :
c . Contacts . Requests [ reqData . Address ] = reqData
case ricochet . ContactEvent_ADD :
fallthrough
case ricochet . ContactEvent_UPDATE :
c . Contacts . Requests [ reqData . Address ] = reqData
fmt . Fprintf ( Ui . Stdout , "\r\x1b[31m[[\x1b[0m \x1b[1m%s\x1b[0m wants to be your contact. Type \x1b[1m%s\x1b[0m to respond \x1b[31m]]\x1b[39m\n" , reqData . Address , Ui . PrefixForAddress ( reqData . Address ) )
case ricochet . ContactEvent_DELETE :
if c . Contacts . Requests [ reqData . Address ] != nil {
delete ( c . Contacts . Requests , reqData . Address )
}
2016-10-16 03:57:07 +00:00
}
2017-10-15 03:21:02 +00:00
} else if event . Subject == nil && event . Type == ricochet . ContactEvent_POPULATE && ! c . populatedContacts {
// Populate is terminated by a nil subject
c . populatedContacts = true
log . Printf ( "Loaded %d contacts and %d requests" , len ( c . Contacts . Contacts ) , len ( c . Contacts . Requests ) )
c . checkIfPopulated ( )
go c . monitorConversations ( )
} else {
log . Printf ( "Ignoring event with an unexpected subject" )
2016-09-16 21:26:14 +00:00
}
}
2016-10-05 21:38:41 +00:00
2016-10-16 03:57:07 +00:00
func ( c * Client ) onConversationEvent ( event * ricochet . ConversationEvent ) {
message := event . Msg
2016-10-16 21:50:34 +00:00
if event . Type == ricochet . ConversationEvent_POPULATE && message == nil {
c . populatedConversations = true
c . checkIfPopulated ( )
return
}
2016-10-26 21:17:40 +00:00
if message == nil || message . Recipient == nil || message . Sender == nil ||
( message . Sender . IsSelf && message . Recipient . IsSelf ) {
2016-10-16 03:57:07 +00:00
log . Printf ( "Ignoring invalid conversation event: %v" , event )
return
}
2016-10-05 21:38:41 +00:00
2016-10-16 03:57:07 +00:00
var remoteEntity * ricochet . Entity
if ! message . Sender . IsSelf {
remoteEntity = message . Sender
} else {
remoteEntity = message . Recipient
}
2016-10-06 23:50:52 +00:00
2017-09-24 21:55:47 +00:00
remoteContact := c . Contacts . ByAddress ( remoteEntity . Address )
2016-10-16 03:57:07 +00:00
if remoteContact == nil {
log . Printf ( "Ignoring conversation event with unknown contact: %v" , event )
return
}
2016-10-06 23:50:52 +00:00
2016-10-26 21:17:40 +00:00
switch event . Type {
case ricochet . ConversationEvent_POPULATE :
fallthrough
case ricochet . ConversationEvent_RECEIVE :
fallthrough
case ricochet . ConversationEvent_SEND :
remoteContact . Conversation . AddMessage ( message ,
event . Type == ricochet . ConversationEvent_POPULATE )
case ricochet . ConversationEvent_UPDATE :
remoteContact . Conversation . UpdateMessage ( message )
default :
log . Printf ( "Ignoring conversation event with unknown type: %v" , event )
}
2016-10-05 21:38:41 +00:00
}
2016-10-16 21:50:34 +00:00
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 { }
}
}