core: Load contacts from config

This commit is contained in:
John Brooks 2016-08-29 20:46:41 -06:00
parent b74073fd09
commit 2c4a1c8b37
6 changed files with 165 additions and 25 deletions

View File

@ -6,6 +6,7 @@ import (
rpc "github.com/special/notricochet/rpc"
"golang.org/x/net/context"
"log"
"time"
)
var NotImplementedError error = errors.New("Not implemented")
@ -92,6 +93,36 @@ func (core *RicochetCore) GetIdentity(ctx context.Context, req *rpc.IdentityRequ
}
func (core *RicochetCore) MonitorContacts(req *rpc.MonitorContactsRequest, stream rpc.RicochetCore_MonitorContactsServer) error {
// Populate
contacts := core.Identity().ContactList().Contacts()
for _, contact := range contacts {
data := &rpc.Contact{
Id: int32(contact.Id()),
Address: contact.Address(),
Nickname: contact.Nickname(),
WhenCreated: contact.WhenCreated().Format(time.RFC3339),
LastConnected: contact.LastConnected().Format(time.RFC3339),
}
event := &rpc.ContactEvent{
Type: rpc.ContactEvent_POPULATE,
Subject: &rpc.ContactEvent_Contact{
Contact: data,
},
}
if err := stream.Send(event); err != nil {
return err
}
}
// Terminate populate list with a null subject
{
event := &rpc.ContactEvent{
Type: rpc.ContactEvent_POPULATE,
}
if err := stream.Send(event); err != nil {
return err
}
}
return NotImplementedError
}

View File

@ -109,6 +109,23 @@ func main() {
} else {
log.Printf("network stopped: %v", status)
}
case "contacts":
stream, err := c.Backend.MonitorContacts(context.Background(), &rpc.MonitorContactsRequest{})
if err != nil {
log.Printf("contacts error: %v", err)
} else {
for {
event, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
log.Printf("contacts error: %v", err)
break
}
log.Printf("contact event: %v", event)
}
}
case "help":
fallthrough
default:

View File

@ -1,18 +1,59 @@
package core
type ContactStatus int
const (
ContactOnline ContactStatus = iota
ContactOffline
ContactRequestPending
ContactRequestRejected
ContactOutdated
import (
"fmt"
"strings"
"time"
)
type Contact struct {
InternalId int
// XXX There is generally a lot of duplication and boilerplate between
// Contact, ConfigContact, and rpc.Contact. This should be reduced somehow.
Name string
Address string
// XXX This is threadsafe only because it can't be modified right now.
type Contact struct {
id int
data ConfigContact
}
func ContactFromConfig(id int, data ConfigContact) (*Contact, error) {
contact := &Contact{
id: id,
data: data,
}
if id < 0 {
return nil, fmt.Errorf("Invalid contact ID '%d'", id)
} else if len(data.Hostname) != 22 || !strings.HasSuffix(data.Hostname, ".onion") {
return nil, fmt.Errorf("Invalid contact hostname '%s", data.Hostname)
}
return contact, nil
}
func (c *Contact) Id() int {
return c.id
}
func (c *Contact) Nickname() string {
return c.data.Nickname
}
func (c *Contact) Address() string {
return "ricochet:" + c.data.Hostname[0:16]
}
func (c *Contact) Hostname() string {
return c.data.Hostname
}
func (c *Contact) LastConnected() time.Time {
time, _ := time.Parse(time.RFC3339, c.data.LastConnected)
return time
}
func (c *Contact) WhenCreated() time.Time {
time, _ := time.Parse(time.RFC3339, c.data.WhenCreated)
return time
}

View File

@ -2,6 +2,8 @@ package core
import (
"errors"
"fmt"
"strconv"
)
type ContactList struct {
@ -10,6 +12,32 @@ type ContactList struct {
inboundRequests map[int]*InboundContactRequest
}
func LoadContactList(core Ricochet) (*ContactList, error) {
list := &ContactList{}
config := core.Config().OpenRead()
defer config.Close()
list.contacts = make(map[int]*Contact, len(config.Contacts))
for idStr, data := range config.Contacts {
id, err := strconv.Atoi(idStr)
if err != nil {
return nil, fmt.Errorf("Invalid contact id '%s'", idStr)
}
if _, exists := list.contacts[id]; exists {
return nil, fmt.Errorf("Duplicate contact id '%d'", id)
}
contact, err := ContactFromConfig(id, data)
if err != nil {
return nil, err
}
list.contacts[id] = contact
}
// XXX Requests aren't implemented
return list, nil
}
func (this *ContactList) Contacts() []*Contact {
re := make([]*Contact, 0, len(this.contacts))
for _, contact := range this.contacts {
@ -40,7 +68,7 @@ func (this *ContactList) ContactById(id int) *Contact {
func (this *ContactList) ContactByAddress(address string) *Contact {
for _, contact := range this.contacts {
if contact.Address == address {
if contact.Address() == address {
return contact
}
}

View File

@ -9,6 +9,8 @@ import (
)
type Identity struct {
core Ricochet
address string
privateKey *rsa.PrivateKey
@ -16,35 +18,51 @@ type Identity struct {
}
func CreateIdentity(core Ricochet) (*Identity, error) {
me := &Identity{}
me := &Identity{
core: core,
}
config := core.Config().OpenRead()
err := me.loadIdentity()
if err != nil {
log.Printf("Loading identity failed: %v", err)
return nil, err
}
contactList, err := LoadContactList(core)
if err != nil {
log.Printf("Loading contact list failed: %v", err)
return nil, err
}
me.contactList = contactList
return me, nil
}
func (me *Identity) loadIdentity() error {
config := me.core.Config().OpenRead()
defer config.Close()
if config.Identity.ServiceKey != "" {
keyData, err := base64.StdEncoding.DecodeString(config.Identity.ServiceKey)
if err != nil {
log.Printf("Decoding identity key failed: %v", err)
return nil, err
return err
}
me.privateKey, _, err = pkcs1.DecodePrivateKeyDER(keyData)
if err != nil {
log.Printf("Decoding identity key failed: %v", err)
return nil, err
return err
}
me.address, err = pkcs1.OnionAddr(&me.privateKey.PublicKey)
if err != nil && me.address == "" {
err = errors.New("Cannot calculate onion address")
}
if err != nil {
log.Printf("Decoding identify key failed: %v", err)
return nil, err
return err
} else if me.address == "" {
return errors.New("Invalid onion address")
}
me.address = "ricochet:" + me.address
}
return me, nil
return nil
}
func (me *Identity) Address() string {

View File

@ -30,6 +30,11 @@ service RicochetCore {
// XXX Service status
rpc GetIdentity (IdentityRequest) returns (Identity);
// Query contacts and monitor for contact changes. The full contact list
// is sent in POPULATE events, terminated by a POPULATE event with no
// subject. Any new, removed, or modified contacts, including changes in
// the state of contacts, are sent as ADD, UPDATE, or DELETE events until
// the stream is closed.
rpc MonitorContacts (MonitorContactsRequest) returns (stream ContactEvent);
rpc AddContactRequest (ContactRequest) returns (Contact);
rpc UpdateContact (Contact) returns (Contact);