core: Load contacts from config
This commit is contained in:
parent
b74073fd09
commit
2c4a1c8b37
|
@ -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
|
||||
}
|
||||
|
||||
|
|
17
cli/cli.go
17
cli/cli.go
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue