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) { | ||||
| 	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) { | ||||
|  |  | |||
|  | @ -9,6 +9,10 @@ import ( | |||
| 	"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 { | ||||
| 	path     string | ||||
| 	filePath string | ||||
|  | @ -28,15 +32,24 @@ type ConfigRoot struct { | |||
| 
 | ||||
| type ConfigContact struct { | ||||
| 	Hostname      string | ||||
| 	LastConnected string | ||||
| 	LastConnected string `json:",omitempty"` | ||||
| 	Nickname      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 { | ||||
| 	DataDirectory     string | ||||
| 	HostnameBlacklist []string | ||||
| 	ServiceKey        string | ||||
| 	DataDirectory string `json:",omitempty"` | ||||
| 	ServiceKey    string | ||||
| } | ||||
| 
 | ||||
| func LoadConfig(configPath string) (*Config, error) { | ||||
|  |  | |||
|  | @ -2,9 +2,9 @@ package core | |||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	protocol "github.com/s-rah/go-ricochet" | ||||
| 	"github.com/ricochet-im/ricochet-go/core/utils" | ||||
| 	"github.com/ricochet-im/ricochet-go/rpc" | ||||
| 	protocol "github.com/s-rah/go-ricochet" | ||||
| 	"golang.org/x/net/context" | ||||
| 	"log" | ||||
| 	"strconv" | ||||
|  | @ -16,6 +16,9 @@ import ( | |||
| // XXX There is generally a lot of duplication and boilerplate between
 | ||||
| // 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 { | ||||
| 	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) | ||||
| 	} | ||||
| 
 | ||||
| 	// XXX Should have some global trigger that starts all contact connections
 | ||||
| 	// at the right time
 | ||||
| 	go contact.contactConnection() | ||||
| 	if data.Request.Pending { | ||||
| 		if data.Request.WhenRejected != "" { | ||||
| 			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 | ||||
| } | ||||
|  | @ -109,6 +123,15 @@ func (c *Contact) Data() *ricochet.Contact { | |||
| 		LastConnected: c.data.LastConnected, | ||||
| 		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 | ||||
| } | ||||
| 
 | ||||
|  | @ -298,10 +321,24 @@ func (c *Contact) setConnection(conn *protocol.OpenConnection) error { | |||
| 	// XXX implement this
 | ||||
| 
 | ||||
| 	c.connection = conn | ||||
| 	c.status = ricochet.Contact_ONLINE | ||||
| 	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
 | ||||
| 	config := c.core.Config.OpenWrite() | ||||
|  | @ -391,6 +428,20 @@ func (c *Contact) shouldReplaceConnection(conn *protocol.OpenConnection) bool { | |||
| 	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
 | ||||
| func (c *Contact) OnConnectionAuthenticated(conn *protocol.OpenConnection) { | ||||
| 	c.connChannel <- conn | ||||
|  |  | |||
|  | @ -7,9 +7,12 @@ import ( | |||
| 	"github.com/ricochet-im/ricochet-go/rpc" | ||||
| 	"strconv" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| type ContactList struct { | ||||
| 	core *Ricochet | ||||
| 
 | ||||
| 	mutex  sync.RWMutex | ||||
| 	events *utils.Publisher | ||||
| 
 | ||||
|  | @ -18,6 +21,7 @@ type ContactList struct { | |||
| 
 | ||||
| func LoadContactList(core *Ricochet) (*ContactList, error) { | ||||
| 	list := &ContactList{ | ||||
| 		core:   core, | ||||
| 		events: utils.CreatePublisher(), | ||||
| 	} | ||||
| 
 | ||||
|  | @ -76,8 +80,71 @@ func (this *ContactList) ContactByAddress(address string) *Contact { | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (this *ContactList) AddContact(address string, name string) (*Contact, error) { | ||||
| 	return nil, errors.New("Not implemented") | ||||
| func (this *ContactList) AddContactRequest(address, name, fromName, text string) (*Contact, error) { | ||||
| 	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 { | ||||
|  |  | |||
|  | @ -4,8 +4,8 @@ import ( | |||
| 	"crypto/rsa" | ||||
| 	"encoding/base64" | ||||
| 	"errors" | ||||
| 	protocol "github.com/s-rah/go-ricochet" | ||||
| 	"github.com/ricochet-im/ricochet-go/core/utils" | ||||
| 	protocol "github.com/s-rah/go-ricochet" | ||||
| 	"github.com/yawning/bulb/utils/pkcs1" | ||||
| 	"log" | ||||
| 	"sync" | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue