core: RPC and config for outbound contact requests
This commit is contained in:
		
							parent
							
								
									2fd3cd2ea0
								
							
						
					
					
						commit
						8baf1034f6
					
				| 
						 | 
					@ -120,7 +120,17 @@ func (s *RpcServer) MonitorContacts(req *rpc.MonitorContactsRequest, stream rpc.
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *RpcServer) AddContactRequest(ctx context.Context, req *rpc.ContactRequest) (*rpc.Contact, error) {
 | 
					func (s *RpcServer) AddContactRequest(ctx context.Context, req *rpc.ContactRequest) (*rpc.Contact, error) {
 | 
				
			||||||
	return nil, NotImplementedError
 | 
						contactList := s.core.Identity.ContactList()
 | 
				
			||||||
 | 
						if req.Direction != rpc.ContactRequest_OUTBOUND {
 | 
				
			||||||
 | 
							return nil, errors.New("Request must be outbound")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						contact, err := contactList.AddContactRequest(req.Address, req.Nickname, req.FromNickname, req.Text)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return contact.Data(), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *RpcServer) UpdateContact(ctx context.Context, req *rpc.Contact) (*rpc.Contact, error) {
 | 
					func (s *RpcServer) UpdateContact(ctx context.Context, req *rpc.Contact) (*rpc.Contact, error) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,10 @@ import (
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// XXX This is partially but not fully compatible with Ricochet's JSON
 | 
				
			||||||
 | 
					// configs. It might be better to be explicitly not compatible, but have
 | 
				
			||||||
 | 
					// an automatic import function.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Config struct {
 | 
					type Config struct {
 | 
				
			||||||
	path     string
 | 
						path     string
 | 
				
			||||||
	filePath string
 | 
						filePath string
 | 
				
			||||||
| 
						 | 
					@ -28,15 +32,24 @@ type ConfigRoot struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ConfigContact struct {
 | 
					type ConfigContact struct {
 | 
				
			||||||
	Hostname      string
 | 
						Hostname      string
 | 
				
			||||||
	LastConnected string
 | 
						LastConnected string `json:",omitempty"`
 | 
				
			||||||
	Nickname      string
 | 
						Nickname      string
 | 
				
			||||||
	WhenCreated   string
 | 
						WhenCreated   string
 | 
				
			||||||
 | 
						Request       ConfigContactRequest `json:",omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ConfigContactRequest struct {
 | 
				
			||||||
 | 
						Pending       bool
 | 
				
			||||||
 | 
						MyNickname    string
 | 
				
			||||||
 | 
						Message       string
 | 
				
			||||||
 | 
						WhenDelivered string `json:",omitempty"`
 | 
				
			||||||
 | 
						WhenRejected  string `json:",omitempty"`
 | 
				
			||||||
 | 
						RemoteError   string `json:",omitempty"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ConfigIdentity struct {
 | 
					type ConfigIdentity struct {
 | 
				
			||||||
	DataDirectory     string
 | 
						DataDirectory string `json:",omitempty"`
 | 
				
			||||||
	HostnameBlacklist []string
 | 
						ServiceKey    string
 | 
				
			||||||
	ServiceKey        string
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func LoadConfig(configPath string) (*Config, error) {
 | 
					func LoadConfig(configPath string) (*Config, error) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,9 +2,9 @@ package core
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	protocol "github.com/s-rah/go-ricochet"
 | 
					 | 
				
			||||||
	"github.com/ricochet-im/ricochet-go/core/utils"
 | 
						"github.com/ricochet-im/ricochet-go/core/utils"
 | 
				
			||||||
	"github.com/ricochet-im/ricochet-go/rpc"
 | 
						"github.com/ricochet-im/ricochet-go/rpc"
 | 
				
			||||||
 | 
						protocol "github.com/s-rah/go-ricochet"
 | 
				
			||||||
	"golang.org/x/net/context"
 | 
						"golang.org/x/net/context"
 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
| 
						 | 
					@ -16,6 +16,9 @@ import (
 | 
				
			||||||
// XXX There is generally a lot of duplication and boilerplate between
 | 
					// XXX There is generally a lot of duplication and boilerplate between
 | 
				
			||||||
// Contact, ConfigContact, and rpc.Contact. This should be reduced somehow.
 | 
					// Contact, ConfigContact, and rpc.Contact. This should be reduced somehow.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// XXX Consider replacing the config contact with the protobuf structure,
 | 
				
			||||||
 | 
					// and extending the protobuf structure for everything it needs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Contact struct {
 | 
					type Contact struct {
 | 
				
			||||||
	core *Ricochet
 | 
						core *Ricochet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -49,9 +52,20 @@ func ContactFromConfig(core *Ricochet, id int, data ConfigContact, events *utils
 | 
				
			||||||
		return nil, fmt.Errorf("Invalid contact hostname '%s", data.Hostname)
 | 
							return nil, fmt.Errorf("Invalid contact hostname '%s", data.Hostname)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// XXX Should have some global trigger that starts all contact connections
 | 
						if data.Request.Pending {
 | 
				
			||||||
	// at the right time
 | 
							if data.Request.WhenRejected != "" {
 | 
				
			||||||
	go contact.contactConnection()
 | 
								contact.status = ricochet.Contact_REJECTED
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								contact.status = ricochet.Contact_REQUEST
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// XXX Ugly and fragile way to inhibit connections
 | 
				
			||||||
 | 
						if contact.status != ricochet.Contact_REJECTED {
 | 
				
			||||||
 | 
							// XXX Should have some global trigger that starts all contact connections
 | 
				
			||||||
 | 
							// at the right time
 | 
				
			||||||
 | 
							go contact.contactConnection()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return contact, nil
 | 
						return contact, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -109,6 +123,15 @@ func (c *Contact) Data() *ricochet.Contact {
 | 
				
			||||||
		LastConnected: c.data.LastConnected,
 | 
							LastConnected: c.data.LastConnected,
 | 
				
			||||||
		Status:        c.status,
 | 
							Status:        c.status,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if c.data.Request.Pending {
 | 
				
			||||||
 | 
							data.Request = &ricochet.ContactRequest{
 | 
				
			||||||
 | 
								Direction:    ricochet.ContactRequest_OUTBOUND,
 | 
				
			||||||
 | 
								Address:      data.Address,
 | 
				
			||||||
 | 
								Nickname:     data.Nickname,
 | 
				
			||||||
 | 
								Text:         c.data.Request.Message,
 | 
				
			||||||
 | 
								FromNickname: c.data.Request.MyNickname,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return data
 | 
						return data
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -298,10 +321,24 @@ func (c *Contact) setConnection(conn *protocol.OpenConnection) error {
 | 
				
			||||||
	// XXX implement this
 | 
						// XXX implement this
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c.connection = conn
 | 
						c.connection = conn
 | 
				
			||||||
	c.status = ricochet.Contact_ONLINE
 | 
					 | 
				
			||||||
	log.Printf("Assigned connection %v to contact %v", c.connection, c)
 | 
						log.Printf("Assigned connection %v to contact %v", c.connection, c)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// XXX implicit accept contact requests
 | 
						if c.data.Request.Pending {
 | 
				
			||||||
 | 
							if conn.Client {
 | 
				
			||||||
 | 
								// XXX Need to check knownContact flag in authentication and implicit accept also
 | 
				
			||||||
 | 
								// Outbound connection for contact request; send request message
 | 
				
			||||||
 | 
								// XXX hardcoded channel ID
 | 
				
			||||||
 | 
								log.Printf("Sending outbound contact request to %v", c)
 | 
				
			||||||
 | 
								conn.SendContactRequest(5, c.data.Request.MyNickname, c.data.Request.Message)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// Inbound connection for contact request; implicitly accept request
 | 
				
			||||||
 | 
								// and continue as contact
 | 
				
			||||||
 | 
								log.Printf("Contact request implicitly accepted by incoming connection for contact %v", c)
 | 
				
			||||||
 | 
								c.requestAccepted()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							c.status = ricochet.Contact_ONLINE
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Update LastConnected time
 | 
						// Update LastConnected time
 | 
				
			||||||
	config := c.core.Config.OpenWrite()
 | 
						config := c.core.Config.OpenWrite()
 | 
				
			||||||
| 
						 | 
					@ -391,6 +428,20 @@ func (c *Contact) shouldReplaceConnection(conn *protocol.OpenConnection) bool {
 | 
				
			||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Assumes mutex is held, and assumes the caller will send the UPDATE event
 | 
				
			||||||
 | 
					func (c *Contact) requestAccepted() {
 | 
				
			||||||
 | 
						config := c.core.Config.OpenWrite()
 | 
				
			||||||
 | 
						c.data.Request = ConfigContactRequest{}
 | 
				
			||||||
 | 
						config.Contacts[strconv.Itoa(c.id)] = c.data
 | 
				
			||||||
 | 
						config.Save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if c.connection != nil {
 | 
				
			||||||
 | 
							c.status = ricochet.Contact_ONLINE
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							c.status = ricochet.Contact_UNKNOWN
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// XXX also will go away during protocol API rework
 | 
					// XXX also will go away during protocol API rework
 | 
				
			||||||
func (c *Contact) OnConnectionAuthenticated(conn *protocol.OpenConnection) {
 | 
					func (c *Contact) OnConnectionAuthenticated(conn *protocol.OpenConnection) {
 | 
				
			||||||
	c.connChannel <- conn
 | 
						c.connChannel <- conn
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,9 +7,12 @@ import (
 | 
				
			||||||
	"github.com/ricochet-im/ricochet-go/rpc"
 | 
						"github.com/ricochet-im/ricochet-go/rpc"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ContactList struct {
 | 
					type ContactList struct {
 | 
				
			||||||
 | 
						core *Ricochet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mutex  sync.RWMutex
 | 
						mutex  sync.RWMutex
 | 
				
			||||||
	events *utils.Publisher
 | 
						events *utils.Publisher
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,6 +21,7 @@ type ContactList struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func LoadContactList(core *Ricochet) (*ContactList, error) {
 | 
					func LoadContactList(core *Ricochet) (*ContactList, error) {
 | 
				
			||||||
	list := &ContactList{
 | 
						list := &ContactList{
 | 
				
			||||||
 | 
							core:   core,
 | 
				
			||||||
		events: utils.CreatePublisher(),
 | 
							events: utils.CreatePublisher(),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -76,8 +80,71 @@ func (this *ContactList) ContactByAddress(address string) *Contact {
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (this *ContactList) AddContact(address string, name string) (*Contact, error) {
 | 
					func (this *ContactList) AddContactRequest(address, name, fromName, text string) (*Contact, error) {
 | 
				
			||||||
	return nil, errors.New("Not implemented")
 | 
						this.mutex.Lock()
 | 
				
			||||||
 | 
						defer this.mutex.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// XXX check that address is valid before relying on format below
 | 
				
			||||||
 | 
						// XXX validity checks on name/text also useful
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, contact := range this.contacts {
 | 
				
			||||||
 | 
							if contact.Address() == address {
 | 
				
			||||||
 | 
								return nil, errors.New("Contact already exists with this address")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if contact.Nickname() == name {
 | 
				
			||||||
 | 
								return nil, errors.New("Contact already exists with this nickname")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// XXX check inbound requests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Write new contact into config
 | 
				
			||||||
 | 
						config := this.core.Config.OpenWrite()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						maxContactId := 0
 | 
				
			||||||
 | 
						for idstr, _ := range config.Contacts {
 | 
				
			||||||
 | 
							if id, err := strconv.Atoi(idstr); err == nil {
 | 
				
			||||||
 | 
								if maxContactId < id {
 | 
				
			||||||
 | 
									maxContactId = id
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						contactId := maxContactId + 1
 | 
				
			||||||
 | 
						configContact := ConfigContact{
 | 
				
			||||||
 | 
							Hostname:    address[9:] + ".onion",
 | 
				
			||||||
 | 
							Nickname:    name,
 | 
				
			||||||
 | 
							WhenCreated: time.Now().Format(time.RFC3339),
 | 
				
			||||||
 | 
							Request: ConfigContactRequest{
 | 
				
			||||||
 | 
								Pending:    true,
 | 
				
			||||||
 | 
								MyNickname: fromName,
 | 
				
			||||||
 | 
								Message:    text,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						config.Contacts[strconv.Itoa(contactId)] = configContact
 | 
				
			||||||
 | 
						if err := config.Save(); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create Contact
 | 
				
			||||||
 | 
						// XXX This starts connection immediately, which could cause contact update
 | 
				
			||||||
 | 
						// events before the add event in an unlikely race case
 | 
				
			||||||
 | 
						contact, err := ContactFromConfig(this.core, contactId, configContact, this.events)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						this.contacts[contactId] = contact
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						event := ricochet.ContactEvent{
 | 
				
			||||||
 | 
							Type: ricochet.ContactEvent_ADD,
 | 
				
			||||||
 | 
							Subject: &ricochet.ContactEvent_Contact{
 | 
				
			||||||
 | 
								Contact: contact.Data(),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						this.events.Publish(event)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return contact, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (this *ContactList) RemoveContact(contact *Contact) error {
 | 
					func (this *ContactList) RemoveContact(contact *Contact) error {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,8 +4,8 @@ import (
 | 
				
			||||||
	"crypto/rsa"
 | 
						"crypto/rsa"
 | 
				
			||||||
	"encoding/base64"
 | 
						"encoding/base64"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	protocol "github.com/s-rah/go-ricochet"
 | 
					 | 
				
			||||||
	"github.com/ricochet-im/ricochet-go/core/utils"
 | 
						"github.com/ricochet-im/ricochet-go/core/utils"
 | 
				
			||||||
 | 
						protocol "github.com/s-rah/go-ricochet"
 | 
				
			||||||
	"github.com/yawning/bulb/utils/pkcs1"
 | 
						"github.com/yawning/bulb/utils/pkcs1"
 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue