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"
|
rpc "github.com/special/notricochet/rpc"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"log"
|
"log"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var NotImplementedError error = errors.New("Not implemented")
|
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 {
|
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
|
return NotImplementedError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
17
cli/cli.go
17
cli/cli.go
|
@ -109,6 +109,23 @@ func main() {
|
||||||
} else {
|
} else {
|
||||||
log.Printf("network stopped: %v", status)
|
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":
|
case "help":
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -1,18 +1,59 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
type ContactStatus int
|
import (
|
||||||
|
"fmt"
|
||||||
const (
|
"strings"
|
||||||
ContactOnline ContactStatus = iota
|
"time"
|
||||||
ContactOffline
|
|
||||||
ContactRequestPending
|
|
||||||
ContactRequestRejected
|
|
||||||
ContactOutdated
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Contact struct {
|
// XXX There is generally a lot of duplication and boilerplate between
|
||||||
InternalId int
|
// Contact, ConfigContact, and rpc.Contact. This should be reduced somehow.
|
||||||
|
|
||||||
Name string
|
// XXX This is threadsafe only because it can't be modified right now.
|
||||||
Address string
|
|
||||||
|
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 (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ContactList struct {
|
type ContactList struct {
|
||||||
|
@ -10,6 +12,32 @@ type ContactList struct {
|
||||||
inboundRequests map[int]*InboundContactRequest
|
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 {
|
func (this *ContactList) Contacts() []*Contact {
|
||||||
re := make([]*Contact, 0, len(this.contacts))
|
re := make([]*Contact, 0, len(this.contacts))
|
||||||
for _, contact := range 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 {
|
func (this *ContactList) ContactByAddress(address string) *Contact {
|
||||||
for _, contact := range this.contacts {
|
for _, contact := range this.contacts {
|
||||||
if contact.Address == address {
|
if contact.Address() == address {
|
||||||
return contact
|
return contact
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Identity struct {
|
type Identity struct {
|
||||||
|
core Ricochet
|
||||||
|
|
||||||
address string
|
address string
|
||||||
privateKey *rsa.PrivateKey
|
privateKey *rsa.PrivateKey
|
||||||
|
|
||||||
|
@ -16,35 +18,51 @@ type Identity struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateIdentity(core Ricochet) (*Identity, error) {
|
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()
|
defer config.Close()
|
||||||
|
|
||||||
if config.Identity.ServiceKey != "" {
|
if config.Identity.ServiceKey != "" {
|
||||||
keyData, err := base64.StdEncoding.DecodeString(config.Identity.ServiceKey)
|
keyData, err := base64.StdEncoding.DecodeString(config.Identity.ServiceKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Decoding identity key failed: %v", err)
|
return err
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
me.privateKey, _, err = pkcs1.DecodePrivateKeyDER(keyData)
|
me.privateKey, _, err = pkcs1.DecodePrivateKeyDER(keyData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Decoding identity key failed: %v", err)
|
return err
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
me.address, err = pkcs1.OnionAddr(&me.privateKey.PublicKey)
|
me.address, err = pkcs1.OnionAddr(&me.privateKey.PublicKey)
|
||||||
if err != nil && me.address == "" {
|
|
||||||
err = errors.New("Cannot calculate onion address")
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Decoding identify key failed: %v", err)
|
return err
|
||||||
return nil, err
|
} else if me.address == "" {
|
||||||
|
return errors.New("Invalid onion address")
|
||||||
}
|
}
|
||||||
me.address = "ricochet:" + me.address
|
me.address = "ricochet:" + me.address
|
||||||
}
|
}
|
||||||
|
|
||||||
return me, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (me *Identity) Address() string {
|
func (me *Identity) Address() string {
|
||||||
|
|
|
@ -30,6 +30,11 @@ service RicochetCore {
|
||||||
// XXX Service status
|
// XXX Service status
|
||||||
rpc GetIdentity (IdentityRequest) returns (Identity);
|
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 MonitorContacts (MonitorContactsRequest) returns (stream ContactEvent);
|
||||||
rpc AddContactRequest (ContactRequest) returns (Contact);
|
rpc AddContactRequest (ContactRequest) returns (Contact);
|
||||||
rpc UpdateContact (Contact) returns (Contact);
|
rpc UpdateContact (Contact) returns (Contact);
|
||||||
|
|
Loading…
Reference in New Issue