diff --git a/core/config.go b/core/config.go deleted file mode 100644 index 0cdf358..0000000 --- a/core/config.go +++ /dev/null @@ -1,177 +0,0 @@ -package core - -import ( - "encoding/json" - "io/ioutil" - "log" - "os" - "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 { - filePath string - - root ConfigRoot - mutex sync.RWMutex -} - -type ConfigRoot struct { - Contacts map[string]ConfigContact - Identity ConfigIdentity - - // Not used by the permanent instance in Config - writable bool - config *Config -} - -type ConfigContact struct { - Hostname 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 `json:",omitempty"` - ServiceKey string -} - -func LoadConfig(filePath string) (*Config, error) { - config := &Config{ - filePath: filePath, - } - - data, err := ioutil.ReadFile(config.filePath) - if err != nil { - log.Printf("Config read error from %s: %v", config.filePath, err) - return nil, err - } - - if err := json.Unmarshal(data, &config.root); err != nil { - log.Printf("Config parse error: %v", err) - return nil, err - } - - return config, nil -} - -// Return a read-only snapshot of the current configuration. This object -// _must not_ be stored, and you must call Close() when finished with it. -// This function may block. -func (c *Config) OpenRead() *ConfigRoot { - c.mutex.RLock() - root := c.root.Clone() - root.writable = false - root.config = c - return root -} - -// Return a writable snapshot of the current configuration. This object -// _must not_ be stored, and you must call Save() or Discard() when -// finished with it. This function may block. -func (c *Config) OpenWrite() *ConfigRoot { - c.mutex.Lock() - root := c.root.Clone() - root.writable = true - root.config = c - return root -} - -func (root *ConfigRoot) Clone() *ConfigRoot { - re := *root - re.Contacts = make(map[string]ConfigContact) - for k, v := range root.Contacts { - re.Contacts[k] = v - } - return &re -} - -func (root *ConfigRoot) Close() { - if root.writable { - log.Panic("Close called on writable config object; use Save or Discard") - } - if root.config == nil { - log.Panic("Close called on closed config object") - } - root.config.mutex.RUnlock() - root.config = nil -} - -// Save writes the state to disk, and updates the Config object if -// successful. Changes to the object are discarded on error. -func (root *ConfigRoot) Save() error { - if !root.writable { - log.Panic("Save called on read-only config object") - } - if root.config == nil { - log.Panic("Save called on closed config object") - } - c := root.config - root.writable = false - root.config = nil - err := c.save(root) - c.mutex.Unlock() - return err -} - -// Discard closes a config write without saving any changes to disk -// or to the Config object. -func (root *ConfigRoot) Discard() { - if !root.writable { - log.Panic("Discard called on read-only config object") - } - if root.config == nil { - log.Panic("Discard called on closed config object") - } - c := root.config - root.config = nil - c.mutex.Unlock() -} - -func (c *Config) save(newRoot *ConfigRoot) error { - data, err := json.MarshalIndent(newRoot, "", " ") - if err != nil { - log.Printf("Config encoding error: %v", err) - return err - } - - // Make a pathetic attempt at atomic file write by writing into a - // temporary file and renaming over the original; this is probably - // imperfect as-implemented, but better than truncating and writing - // directly. - tempPath := c.filePath + ".new" - file, err := os.OpenFile(tempPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) - if err != nil { - log.Printf("Config save error: %v", err) - return err - } - - if _, err := file.Write(data); err != nil { - log.Printf("Config write error: %v", err) - file.Close() - return err - } - - file.Close() - if err := os.Rename(tempPath, c.filePath); err != nil { - log.Printf("Config replace error: %v", err) - return err - } - - c.root = *newRoot - return nil -} diff --git a/core/config/config.go b/core/config/config.go new file mode 100644 index 0000000..6043162 --- /dev/null +++ b/core/config/config.go @@ -0,0 +1,115 @@ +package config + +import ( + "github.com/golang/protobuf/jsonpb" + "github.com/golang/protobuf/proto" + "github.com/ricochet-im/ricochet-go/rpc" + "log" + "os" + "sync" + "sync/atomic" +) + +type ConfigFile struct { + filePath string + root *ricochet.Config + readSnapshot atomic.Value + mutex sync.Mutex +} + +func NewConfigFile(path string) (*ConfigFile, error) { + cfg := &ConfigFile{ + filePath: path, + root: &ricochet.Config{}, + } + cfg.readSnapshot.Store(cfg.root) + if err := cfg.save(); err != nil { + return nil, err + } + return cfg, nil +} + +func LoadConfigFile(path string) (*ConfigFile, error) { + cfg := &ConfigFile{ + filePath: path, + root: &ricochet.Config{}, + } + + file, err := os.Open(cfg.filePath) + if err != nil { + return nil, err + } + defer file.Close() + + json := jsonpb.Unmarshaler{ + AllowUnknownFields: true, + } + if err := json.Unmarshal(file, cfg.root); err != nil { + return nil, err + } + + cfg.readSnapshot.Store(cfg.root) + return cfg, nil +} + +// Read returns a **read-only** snapshot of the current configuration. This +// function is threadsafe, and the values in this instance of the configuration +// will not change when the configuration changes. +// +// Do not under any circumstances modify any part of this object. +func (cfg *ConfigFile) Read() *ricochet.Config { + return cfg.readSnapshot.Load().(*ricochet.Config) +} + +// Lock gains exclusive control of the configuration state, allowing the caller +// to safely make changes to the configuration. Concurrent readers will not see +// any changes until Unlock() is called, at which point they will atomically be +// visible in future calls to Read() as well as being saved persistently. +func (cfg *ConfigFile) Lock() *ricochet.Config { + cfg.mutex.Lock() + // Clone the current configuration for a mutable copy + cfg.root = proto.Clone(cfg.root).(*ricochet.Config) + return cfg.root +} + +func (cfg *ConfigFile) Unlock() { + // Clone cfg.root again to guarantee that any messages are detached from + // instances that exist elsewhere in the code. Inefficient but safe. + cfg.root = proto.Clone(cfg.root).(*ricochet.Config) + cfg.readSnapshot.Store(cfg.root) + err := cfg.save() + if err != nil { + log.Printf("WARNING: Unable to save configuration: %s", err) + } + cfg.mutex.Unlock() +} + +func (cfg *ConfigFile) save() error { + json := jsonpb.Marshaler{Indent: " "} + + // Make a pathetic attempt at atomic file write by writing into a + // temporary file and renaming over the original; this is probably + // imperfect as-implemented, but better than truncating and writing + // directly. + tempPath := cfg.filePath + ".new" + file, err := os.OpenFile(tempPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) + if err != nil { + log.Printf("Config save error: %v", err) + return err + } + + err = json.Marshal(file, cfg.root) + if err != nil { + log.Printf("Config encoding error: %v", err) + file.Close() + return err + } + + file.Close() + if err := os.Rename(tempPath, cfg.filePath); err != nil { + log.Printf("Config replace error: %v", err) + return err + } + + return nil +} diff --git a/core/contact.go b/core/contact.go index 8cdbc14..0987657 100644 --- a/core/contact.go +++ b/core/contact.go @@ -2,6 +2,7 @@ package core import ( "fmt" + "github.com/golang/protobuf/proto" "github.com/ricochet-im/ricochet-go/core/utils" "github.com/ricochet-im/ricochet-go/rpc" protocol "github.com/s-rah/go-ricochet" @@ -14,18 +15,11 @@ import ( "time" ) -// 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 - id int - data ConfigContact - status ricochet.Contact_Status + id int + data *ricochet.Contact mutex sync.Mutex events *utils.Publisher @@ -41,7 +35,7 @@ type Contact struct { conversation *Conversation } -func ContactFromConfig(core *Ricochet, id int, data ConfigContact, events *utils.Publisher) (*Contact, error) { +func ContactFromConfig(core *Ricochet, id int, data *ricochet.Contact, events *utils.Publisher) (*Contact, error) { contact := &Contact{ core: core, id: id, @@ -53,16 +47,18 @@ func ContactFromConfig(core *Ricochet, id int, data ConfigContact, events *utils if id < 0 { return nil, fmt.Errorf("Invalid contact ID '%d'", id) - } else if !IsOnionValid(data.Hostname) { - return nil, fmt.Errorf("Invalid contact hostname '%s", data.Hostname) + } else if !IsAddressValid(data.Address) { + return nil, fmt.Errorf("Invalid contact address '%s", data.Address) } - if data.Request.Pending { - if data.Request.WhenRejected != "" { - contact.status = ricochet.Contact_REJECTED + if data.Request != nil { + if data.Request.Rejected { + contact.data.Status = ricochet.Contact_REJECTED } else { - contact.status = ricochet.Contact_REQUEST + contact.data.Status = ricochet.Contact_REQUEST } + } else if contact.data.Status != ricochet.Contact_REJECTED { + contact.data.Status = ricochet.Contact_UNKNOWN } return contact, nil @@ -81,14 +77,14 @@ func (c *Contact) Nickname() string { func (c *Contact) Address() string { c.mutex.Lock() defer c.mutex.Unlock() - address, _ := AddressFromOnion(c.data.Hostname) - return address + return c.data.Address } func (c *Contact) Hostname() string { c.mutex.Lock() defer c.mutex.Unlock() - return c.data.Hostname + hostname, _ := OnionFromAddress(c.data.Address) + return hostname } func (c *Contact) LastConnected() time.Time { @@ -108,47 +104,28 @@ func (c *Contact) WhenCreated() time.Time { func (c *Contact) Status() ricochet.Contact_Status { c.mutex.Lock() defer c.mutex.Unlock() - return c.status + return c.data.Status } func (c *Contact) Data() *ricochet.Contact { c.mutex.Lock() defer c.mutex.Unlock() - address, _ := AddressFromOnion(c.data.Hostname) - data := &ricochet.Contact{ - Id: int32(c.id), - Address: address, - Nickname: c.data.Nickname, - WhenCreated: c.data.WhenCreated, - 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 + return proto.Clone(c.data).(*ricochet.Contact) } func (c *Contact) IsRequest() bool { c.mutex.Lock() defer c.mutex.Unlock() - return c.data.Request.Pending + return c.data.Request != nil } func (c *Contact) Conversation() *Conversation { c.mutex.Lock() defer c.mutex.Unlock() if c.conversation == nil { - address, _ := AddressFromOnion(c.data.Hostname) entity := &ricochet.Entity{ ContactId: int32(c.id), - Address: address, + Address: c.data.Address, } c.conversation = NewConversation(c, entity, c.core.Identity.ConversationStream) } @@ -187,7 +164,7 @@ func (c *Contact) shouldMakeOutboundConnections() bool { defer c.mutex.Unlock() // Don't make connections to contacts in the REJECTED state - if c.status == ricochet.Contact_REJECTED { + if c.data.Status == ricochet.Contact_REJECTED { return false } @@ -263,7 +240,7 @@ func (c *Contact) contactConnection() { // already closed. If there was an existing connection and this returns nil, // the old connection is closed but c.connection has not been reset. if err := c.considerUsingConnection(conn); err != nil { - log.Printf("Discarded new contact %s connection: %s", c.data.Hostname, err) + log.Printf("Discarded new contact %s connection: %s", c.data.Address, err) go closeUnhandledConnection(conn) c.mutex.Unlock() continue @@ -335,8 +312,8 @@ func (c *Contact) connectOutbound(ctx context.Context, connChannel chan *connect Network: c.core.Network, NeverGiveUp: true, } - hostname := c.data.Hostname - isRequest := c.data.Request.Pending + hostname, _ := OnionFromAddress(c.data.Address) + isRequest := c.data.Request != nil c.mutex.Unlock() for { @@ -446,8 +423,8 @@ func (c *Contact) sendContactRequest(conn *connection.Connection, ctx context.Co _, err := conn.RequestOpenChannel("im.ricochet.contact.request", &channels.ContactRequestChannel{ Handler: &requestChannelHandler{Response: responseChan}, - Name: c.data.Request.MyNickname, // XXX mutex - Message: c.data.Request.Message, + Name: c.data.Request.FromNickname, // XXX mutex + Message: c.data.Request.Text, }) return err }) @@ -502,9 +479,9 @@ func (c *Contact) considerUsingConnection(conn *connection.Connection) error { }() if conn.IsInbound { - log.Printf("Contact %s has a new inbound connection", c.data.Hostname) + log.Printf("Contact %s has a new inbound connection", c.data.Address) } else { - log.Printf("Contact %s has a new outbound connection", c.data.Hostname) + log.Printf("Contact %s has a new outbound connection", c.data.Address) } if conn == c.connection { @@ -515,8 +492,9 @@ func (c *Contact) considerUsingConnection(conn *connection.Connection) error { return fmt.Errorf("Connection %v is not authenticated", conn) } - if c.data.Hostname[0:16] != conn.RemoteHostname { - return fmt.Errorf("Connection hostname %s doesn't match contact hostname %s when assigning connection", conn.RemoteHostname, c.data.Hostname[0:16]) + plainHost, _ := PlainHostFromAddress(c.data.Address) + if plainHost != conn.RemoteHostname { + return fmt.Errorf("Connection hostname %s doesn't match contact hostname %s when assigning connection", conn.RemoteHostname, plainHost) } if c.connection != nil && !c.shouldReplaceConnection(conn) { @@ -539,28 +517,29 @@ func (c *Contact) considerUsingConnection(conn *connection.Connection) error { // Assumes c.mutex is held. func (c *Contact) onConnectionStateChanged() { if c.connection != nil { - if c.data.Request.Pending && c.connection.IsInbound { + if c.data.Request != nil && c.connection.IsInbound { // Inbound connection implicitly accepts the contact request and can continue as a contact // Outbound request logic is all handled by connectOutbound. log.Printf("Contact request implicitly accepted by contact %v", c) c.updateContactRequest("Accepted") } else { - c.status = ricochet.Contact_ONLINE + c.data.Status = ricochet.Contact_ONLINE } } else { - if c.status == ricochet.Contact_ONLINE { - c.status = ricochet.Contact_OFFLINE + if c.data.Status == ricochet.Contact_ONLINE { + c.data.Status = ricochet.Contact_OFFLINE } } // Update LastConnected time c.timeConnected = time.Now() - - config := c.core.Config.OpenWrite() c.data.LastConnected = c.timeConnected.Format(time.RFC3339) - config.Contacts[strconv.Itoa(c.id)] = c.data - config.Save() + config := c.core.Config.Lock() + config.Contacts[strconv.Itoa(c.id)] = c.data + c.core.Config.Unlock() + + // XXX I wonder if events and config updates can be combined now, and made safer... // _really_ assumes c.mutex was held c.mutex.Unlock() event := ricochet.ContactEvent{ @@ -615,7 +594,7 @@ func (c *Contact) UpdateContactRequest(status string) bool { c.mutex.Lock() defer c.mutex.Unlock() - if !c.data.Request.Pending { + if c.data.Request == nil { return false } @@ -635,7 +614,6 @@ func (c *Contact) UpdateContactRequest(status string) bool { // Same as above, but assumes the mutex is already held and that the caller // will send an UPDATE event func (c *Contact) updateContactRequest(status string) bool { - config := c.core.Config.OpenWrite() now := time.Now().Format(time.RFC3339) // Whether to keep the channel open var re bool @@ -646,11 +624,11 @@ func (c *Contact) updateContactRequest(status string) bool { re = true case "Accepted": - c.data.Request = ConfigContactRequest{} + c.data.Request = nil if c.connection != nil { - c.status = ricochet.Contact_ONLINE + c.data.Status = ricochet.Contact_ONLINE } else { - c.status = ricochet.Contact_UNKNOWN + c.data.Status = ricochet.Contact_UNKNOWN } case "Rejected": @@ -664,8 +642,9 @@ func (c *Contact) updateContactRequest(status string) bool { log.Printf("Unknown contact request status '%s'", status) } + config := c.core.Config.Lock() + defer c.core.Config.Unlock() config.Contacts[strconv.Itoa(c.id)] = c.data - config.Save() return re } diff --git a/core/contactlist.go b/core/contactlist.go index 34eb669..4e904b5 100644 --- a/core/contactlist.go +++ b/core/contactlist.go @@ -27,9 +27,7 @@ func LoadContactList(core *Ricochet) (*ContactList, error) { inboundRequests: make(map[string]*InboundContactRequest), } - config := core.Config.OpenRead() - defer config.Close() - + config := core.Config.Read() list.contacts = make(map[int]*Contact, len(config.Contacts)) for idStr, data := range config.Contacts { id, err := strconv.Atoi(idStr) @@ -99,20 +97,15 @@ func (cl *ContactList) InboundRequestByAddress(address string) *InboundContactRe // Generally, you will use AddContactRequest (for outbound requests) and // AddOrUpdateInboundContactRequest plus InboundContactRequest.Accept() instead of // using this function directly. -func (this *ContactList) AddNewContact(configContact ConfigContact) (*Contact, error) { +func (this *ContactList) AddNewContact(data *ricochet.Contact) (*Contact, error) { this.mutex.Lock() defer this.mutex.Unlock() - address, ok := AddressFromOnion(configContact.Hostname) - if !ok { - return nil, errors.New("Invalid ricochet address") - } - for _, contact := range this.contacts { - if contact.Address() == address { + if contact.Address() == data.Address { return nil, errors.New("Contact already exists with this address") } - if contact.Nickname() == configContact.Nickname { + if contact.Nickname() == data.Nickname { return nil, errors.New("Contact already exists with this nickname") } } @@ -120,7 +113,7 @@ func (this *ContactList) AddNewContact(configContact ConfigContact) (*Contact, e // XXX check inbound requests (but this can be called for an inbound req too) // Write new contact into config - config := this.core.Config.OpenWrite() + config := this.core.Config.Lock() maxContactId := 0 for idstr, _ := range config.Contacts { @@ -132,13 +125,15 @@ func (this *ContactList) AddNewContact(configContact ConfigContact) (*Contact, e } contactId := maxContactId + 1 - config.Contacts[strconv.Itoa(contactId)] = configContact - if err := config.Save(); err != nil { - return nil, err + + if config.Contacts == nil { + config.Contacts = make(map[string]*ricochet.Contact) } + config.Contacts[strconv.Itoa(contactId)] = data + this.core.Config.Unlock() // Create Contact - contact, err := ContactFromConfig(this.core, contactId, configContact, this.events) + contact, err := ContactFromConfig(this.core, contactId, data, this.events) if err != nil { return nil, err } @@ -163,8 +158,7 @@ func (this *ContactList) AddNewContact(configContact ConfigContact) (*Contact, e // If an inbound request already exists for this address, that request will be automatically // accepted, and the returned contact will already be fully established. func (cl *ContactList) AddContactRequest(address, name, fromName, text string) (*Contact, error) { - onion, valid := OnionFromAddress(address) - if !valid { + if !IsAddressValid(address) { return nil, errors.New("Invalid ricochet address") } if !IsNicknameAcceptable(name) { @@ -177,17 +171,20 @@ func (cl *ContactList) AddContactRequest(address, name, fromName, text string) ( return nil, errors.New("Invalid message") } - configContact := ConfigContact{ - Hostname: onion, + data := &ricochet.Contact{ + Address: address, Nickname: name, WhenCreated: time.Now().Format(time.RFC3339), - Request: ConfigContactRequest{ - Pending: true, - MyNickname: fromName, - Message: text, + Request: &ricochet.ContactRequest{ + Direction: ricochet.ContactRequest_OUTBOUND, + Address: address, + Nickname: name, + FromNickname: fromName, + Text: text, + WhenCreated: time.Now().Format(time.RFC3339), }, } - contact, err := cl.AddNewContact(configContact) + contact, err := cl.AddNewContact(data) if err != nil { return nil, err } @@ -213,11 +210,9 @@ func (this *ContactList) RemoveContact(contact *Contact) error { // leaves a goroutine up among other things. contact.StopConnection() - config := this.core.Config.OpenWrite() + config := this.core.Config.Lock() delete(config.Contacts, strconv.Itoa(contact.Id())) - if err := config.Save(); err != nil { - return err - } + this.core.Config.Unlock() delete(this.contacts, contact.Id()) diff --git a/core/identity.go b/core/identity.go index 9cc263a..c13905e 100644 --- a/core/identity.go +++ b/core/identity.go @@ -2,9 +2,9 @@ package core import ( "crypto/rsa" - "encoding/base64" "errors" "github.com/ricochet-im/ricochet-go/core/utils" + "github.com/ricochet-im/ricochet-go/rpc" protocol "github.com/s-rah/go-ricochet" connection "github.com/s-rah/go-ricochet/connection" "github.com/yawning/bulb/utils/pkcs1" @@ -51,15 +51,10 @@ func CreateIdentity(core *Ricochet) (*Identity, error) { } 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 { - return err - } + config := me.core.Config.Read() + if keyData := config.Secrets.GetServicePrivateKey(); keyData != nil { + var err error me.privateKey, _, err = pkcs1.DecodePrivateKeyDER(keyData) if err != nil { return err @@ -90,9 +85,12 @@ func (me *Identity) setPrivateKey(key *rsa.PrivateKey) error { if err != nil { return err } - config := me.core.Config.OpenWrite() - config.Identity.ServiceKey = base64.StdEncoding.EncodeToString(keyData) - config.Save() + config := me.core.Config.Lock() + if config.Secrets == nil { + config.Secrets = &ricochet.Secrets{} + } + config.Secrets.ServicePrivateKey = keyData + me.core.Config.Unlock() // Update Identity me.address, err = AddressFromKey(&key.PublicKey) diff --git a/core/inboundcontactrequest.go b/core/inboundcontactrequest.go index 0c8c740..62e4995 100644 --- a/core/inboundcontactrequest.go +++ b/core/inboundcontactrequest.go @@ -266,13 +266,12 @@ func (cr *InboundContactRequest) Accept() (*Contact, error) { log.Printf("Accepting contact request from %s", cr.data.Address) - onion, _ := OnionFromAddress(cr.data.Address) - configContact := ConfigContact{ - Hostname: onion, + data := &ricochet.Contact{ + Address: cr.data.Address, Nickname: cr.data.FromNickname, WhenCreated: cr.data.WhenCreated, } - contact, err := cr.core.Identity.ContactList().AddNewContact(configContact) + contact, err := cr.core.Identity.ContactList().AddNewContact(data) if err != nil { log.Printf("Error occurred in accepting contact request: %s", err) return nil, err diff --git a/core/ricochet.go b/core/ricochet.go index ad5395f..cca2c00 100644 --- a/core/ricochet.go +++ b/core/ricochet.go @@ -2,6 +2,7 @@ package core import ( cryptorand "crypto/rand" + "github.com/ricochet-im/ricochet-go/core/config" "log" "math" "math/big" @@ -11,12 +12,12 @@ import ( ) type Ricochet struct { - Config *Config + Config *config.ConfigFile Network *Network Identity *Identity } -func (core *Ricochet) Init(conf *Config) (err error) { +func (core *Ricochet) Init(conf *config.ConfigFile) (err error) { initRand() core.Config = conf diff --git a/ricochet-cli/main.go b/ricochet-cli/main.go index 6ad4cd0..73821b7 100644 --- a/ricochet-cli/main.go +++ b/ricochet-cli/main.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/chzyer/readline" ricochet "github.com/ricochet-im/ricochet-go/core" + "github.com/ricochet-im/ricochet-go/core/config" rpc "github.com/ricochet-im/ricochet-go/rpc" "google.golang.org/grpc" "log" @@ -175,13 +176,16 @@ func checkBackendAddressSafety(address string) error { } func startBackend() error { - config, err := ricochet.LoadConfig(configPath) + cfg, err := config.LoadConfigFile(configPath) + if err != nil && os.IsNotExist(err) { + cfg, err = config.NewConfigFile(configPath) + } if err != nil { return err } core := new(ricochet.Ricochet) - if err := core.Init(config); err != nil { + if err := core.Init(cfg); err != nil { return err } diff --git a/rpc/config.pb.go b/rpc/config.pb.go new file mode 100644 index 0000000..3af94c0 --- /dev/null +++ b/rpc/config.pb.go @@ -0,0 +1,89 @@ +// Code generated by protoc-gen-go. +// source: config.proto +// DO NOT EDIT! + +package ricochet + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +type Config struct { + Identity *Identity `protobuf:"bytes,1,opt,name=identity" json:"identity,omitempty"` + Contacts map[string]*Contact `protobuf:"bytes,2,rep,name=contacts" json:"contacts,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + Secrets *Secrets `protobuf:"bytes,3,opt,name=secrets" json:"secrets,omitempty"` +} + +func (m *Config) Reset() { *m = Config{} } +func (m *Config) String() string { return proto.CompactTextString(m) } +func (*Config) ProtoMessage() {} +func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor5, []int{0} } + +func (m *Config) GetIdentity() *Identity { + if m != nil { + return m.Identity + } + return nil +} + +func (m *Config) GetContacts() map[string]*Contact { + if m != nil { + return m.Contacts + } + return nil +} + +func (m *Config) GetSecrets() *Secrets { + if m != nil { + return m.Secrets + } + return nil +} + +// Secrets are not transmitted to frontend RPC clients +type Secrets struct { + ServicePrivateKey []byte `protobuf:"bytes,1,opt,name=servicePrivateKey,proto3" json:"servicePrivateKey,omitempty"` +} + +func (m *Secrets) Reset() { *m = Secrets{} } +func (m *Secrets) String() string { return proto.CompactTextString(m) } +func (*Secrets) ProtoMessage() {} +func (*Secrets) Descriptor() ([]byte, []int) { return fileDescriptor5, []int{1} } + +func (m *Secrets) GetServicePrivateKey() []byte { + if m != nil { + return m.ServicePrivateKey + } + return nil +} + +func init() { + proto.RegisterType((*Config)(nil), "ricochet.Config") + proto.RegisterType((*Secrets)(nil), "ricochet.Secrets") +} + +func init() { proto.RegisterFile("config.proto", fileDescriptor5) } + +var fileDescriptor5 = []byte{ + // 235 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x49, 0xce, 0xcf, 0x4b, + 0xcb, 0x4c, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x28, 0xca, 0x4c, 0xce, 0x4f, 0xce, + 0x48, 0x2d, 0x91, 0xe2, 0x4d, 0xce, 0xcf, 0x2b, 0x49, 0x4c, 0x2e, 0x81, 0x48, 0x48, 0xf1, 0x65, + 0xa6, 0xa4, 0xe6, 0x95, 0x64, 0x96, 0x54, 0x42, 0xf8, 0x4a, 0x1f, 0x19, 0xb9, 0xd8, 0x9c, 0xc1, + 0x3a, 0x85, 0xf4, 0xb8, 0x38, 0x60, 0x92, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0xdc, 0x46, 0x42, 0x7a, + 0x30, 0x63, 0xf4, 0x3c, 0xa1, 0x32, 0x41, 0x70, 0x35, 0x42, 0x56, 0x5c, 0x1c, 0x50, 0xb3, 0x8b, + 0x25, 0x98, 0x14, 0x98, 0x35, 0xb8, 0x8d, 0xe4, 0x10, 0xea, 0x21, 0x66, 0x82, 0x28, 0xb0, 0x02, + 0xd7, 0xbc, 0x92, 0xa2, 0xca, 0x20, 0xb8, 0x7a, 0x21, 0x6d, 0x2e, 0xf6, 0xe2, 0xd4, 0xe4, 0xa2, + 0xd4, 0x92, 0x62, 0x09, 0x66, 0xb0, 0x55, 0x82, 0x08, 0xad, 0xc1, 0x10, 0x89, 0x20, 0x98, 0x0a, + 0x29, 0x3f, 0x2e, 0x5e, 0x14, 0x73, 0x84, 0x04, 0xb8, 0x98, 0xb3, 0x53, 0x21, 0x8e, 0xe4, 0x0c, + 0x02, 0x31, 0x85, 0xd4, 0xb9, 0x58, 0xcb, 0x12, 0x73, 0x4a, 0x53, 0x25, 0x98, 0xd0, 0x4d, 0x83, + 0xea, 0x0c, 0x82, 0xc8, 0x5b, 0x31, 0x59, 0x30, 0x2a, 0x99, 0x73, 0xb1, 0x43, 0xed, 0x10, 0xd2, + 0xe1, 0x12, 0x2c, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0x0d, 0x28, 0xca, 0x2c, 0x4b, 0x2c, 0x49, + 0xf5, 0x86, 0x9a, 0xcb, 0x13, 0x84, 0x29, 0x91, 0xc4, 0x06, 0x0e, 0x33, 0x63, 0x40, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x42, 0xba, 0x23, 0x37, 0x6c, 0x01, 0x00, 0x00, +} diff --git a/rpc/config.proto b/rpc/config.proto new file mode 100644 index 0000000..f4a9673 --- /dev/null +++ b/rpc/config.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; +package ricochet; + +import "contact.proto"; +import "identity.proto"; + +message Config { + Identity identity = 1; + map contacts = 2; + Secrets secrets = 3; +} + +// Secrets are not transmitted to frontend RPC clients +message Secrets { + bytes servicePrivateKey = 1; +} + diff --git a/rpc/contact.pb.go b/rpc/contact.pb.go index 76efe4b..dcb08c7 100644 --- a/rpc/contact.pb.go +++ b/rpc/contact.pb.go @@ -11,6 +11,7 @@ It is generated from these files: core.proto identity.proto network.proto + config.proto It has these top-level messages: Contact @@ -38,6 +39,8 @@ It has these top-level messages: NetworkStatus StartNetworkRequest StopNetworkRequest + Config + Secrets */ package ricochet @@ -202,13 +205,16 @@ func (m *Contact) GetStatus() Contact_Status { } type ContactRequest struct { - Direction ContactRequest_Direction `protobuf:"varint,1,opt,name=direction,enum=ricochet.ContactRequest_Direction" json:"direction,omitempty"` - Address string `protobuf:"bytes,2,opt,name=address" json:"address,omitempty"` - Nickname string `protobuf:"bytes,3,opt,name=nickname" json:"nickname,omitempty"` - Text string `protobuf:"bytes,4,opt,name=text" json:"text,omitempty"` - FromNickname string `protobuf:"bytes,5,opt,name=fromNickname" json:"fromNickname,omitempty"` - WhenCreated string `protobuf:"bytes,6,opt,name=whenCreated" json:"whenCreated,omitempty"` - Rejected bool `protobuf:"varint,7,opt,name=rejected" json:"rejected,omitempty"` + Direction ContactRequest_Direction `protobuf:"varint,1,opt,name=direction,enum=ricochet.ContactRequest_Direction" json:"direction,omitempty"` + Address string `protobuf:"bytes,2,opt,name=address" json:"address,omitempty"` + Nickname string `protobuf:"bytes,3,opt,name=nickname" json:"nickname,omitempty"` + Text string `protobuf:"bytes,4,opt,name=text" json:"text,omitempty"` + FromNickname string `protobuf:"bytes,5,opt,name=fromNickname" json:"fromNickname,omitempty"` + WhenCreated string `protobuf:"bytes,6,opt,name=whenCreated" json:"whenCreated,omitempty"` + Rejected bool `protobuf:"varint,7,opt,name=rejected" json:"rejected,omitempty"` + WhenDelivered string `protobuf:"bytes,8,opt,name=whenDelivered" json:"whenDelivered,omitempty"` + WhenRejected string `protobuf:"bytes,9,opt,name=whenRejected" json:"whenRejected,omitempty"` + RemoteError string `protobuf:"bytes,10,opt,name=remoteError" json:"remoteError,omitempty"` } func (m *ContactRequest) Reset() { *m = ContactRequest{} } @@ -265,6 +271,27 @@ func (m *ContactRequest) GetRejected() bool { return false } +func (m *ContactRequest) GetWhenDelivered() string { + if m != nil { + return m.WhenDelivered + } + return "" +} + +func (m *ContactRequest) GetWhenRejected() string { + if m != nil { + return m.WhenRejected + } + return "" +} + +func (m *ContactRequest) GetRemoteError() string { + if m != nil { + return m.RemoteError + } + return "" +} + type MonitorContactsRequest struct { } @@ -467,39 +494,42 @@ func init() { func init() { proto.RegisterFile("contact.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 539 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x54, 0xd1, 0x72, 0x93, 0x40, - 0x14, 0x2d, 0x84, 0x02, 0xb9, 0x69, 0x23, 0xdd, 0xe9, 0x38, 0xd8, 0xbe, 0x30, 0x3b, 0x8e, 0x93, - 0x17, 0xd1, 0x89, 0xbe, 0xdb, 0x34, 0xd0, 0x31, 0x1a, 0x21, 0x6e, 0x61, 0x7c, 0x26, 0xb0, 0x4e, - 0xd1, 0x74, 0x89, 0xb0, 0x51, 0xf3, 0x43, 0x7e, 0x8b, 0x9f, 0xe3, 0x27, 0x38, 0xbb, 0x40, 0x6a, - 0x12, 0x75, 0x1c, 0xdf, 0xf6, 0x9e, 0x73, 0x2e, 0xbb, 0x7b, 0xee, 0x59, 0xe0, 0x38, 0x2d, 0x18, - 0x4f, 0x52, 0xee, 0x2e, 0xcb, 0x82, 0x17, 0xc8, 0x2c, 0xf3, 0xb4, 0x48, 0x6f, 0x28, 0xc7, 0xdf, - 0x55, 0x30, 0xc6, 0x35, 0x87, 0xfa, 0xa0, 0xe6, 0x99, 0xad, 0x38, 0xca, 0xe0, 0x90, 0xa8, 0x79, - 0x86, 0x6c, 0x30, 0x92, 0x2c, 0x2b, 0x69, 0x55, 0xd9, 0xaa, 0xa3, 0x0c, 0xba, 0xa4, 0x2d, 0xd1, - 0x19, 0x98, 0x2c, 0x4f, 0x3f, 0xb2, 0xe4, 0x96, 0xda, 0x1d, 0x49, 0x6d, 0x6a, 0xe4, 0x40, 0xef, - 0xcb, 0x0d, 0x65, 0xe3, 0x92, 0x26, 0x9c, 0x66, 0xb6, 0x26, 0xe9, 0x5f, 0x21, 0xf4, 0x10, 0x8e, - 0x17, 0x49, 0xc5, 0xc7, 0x05, 0x63, 0x34, 0x15, 0x9a, 0x43, 0xa9, 0xd9, 0x06, 0xd1, 0x10, 0x8c, - 0x92, 0x7e, 0x5a, 0xd1, 0x8a, 0xdb, 0xba, 0xa3, 0x0c, 0x7a, 0x43, 0xdb, 0x6d, 0x4f, 0xed, 0x36, - 0x27, 0x26, 0x35, 0x4f, 0x5a, 0x21, 0x7a, 0x0a, 0x7a, 0xc5, 0x13, 0xbe, 0xaa, 0x6c, 0x70, 0x94, - 0x41, 0xff, 0x37, 0x2d, 0xee, 0xb5, 0xe4, 0x49, 0xa3, 0xc3, 0x13, 0xd0, 0x6b, 0x04, 0xf5, 0xc0, - 0x88, 0x83, 0xd7, 0x41, 0xf8, 0x2e, 0xb0, 0x0e, 0x44, 0x11, 0x5e, 0x5d, 0x4d, 0x27, 0x81, 0x6f, - 0x29, 0x08, 0x40, 0x0f, 0x03, 0xb9, 0x56, 0x05, 0x41, 0xfc, 0xb7, 0xb1, 0x7f, 0x1d, 0x59, 0x1d, - 0x74, 0x04, 0x26, 0xf1, 0x5f, 0xf9, 0xe3, 0xc8, 0xf7, 0x2c, 0x0d, 0x7f, 0x53, 0xa1, 0xbf, 0x7d, - 0x30, 0x74, 0x01, 0xdd, 0x2c, 0x2f, 0x69, 0xca, 0xf3, 0x82, 0x49, 0x63, 0xfb, 0x43, 0xfc, 0xa7, - 0x5b, 0xb8, 0x5e, 0xab, 0x24, 0x77, 0x4d, 0xff, 0x39, 0x03, 0x04, 0x1a, 0xa7, 0x5f, 0x79, 0x63, - 0xbe, 0x5c, 0x23, 0x0c, 0x47, 0xef, 0xcb, 0xe2, 0x36, 0x68, 0x7b, 0x6a, 0xd3, 0xb7, 0xb0, 0xdd, - 0xd9, 0xe9, 0xfb, 0xb3, 0x3b, 0x03, 0xb3, 0xa4, 0x1f, 0xea, 0xb1, 0x19, 0x8e, 0x32, 0x30, 0xc9, - 0xa6, 0xc6, 0x8f, 0xa0, 0xbb, 0xb9, 0x83, 0x30, 0x6a, 0x12, 0x5c, 0x86, 0x71, 0xe0, 0x59, 0x07, - 0xc2, 0xa8, 0x30, 0x8e, 0xea, 0x4a, 0xc1, 0x36, 0xdc, 0x7f, 0x53, 0xb0, 0x9c, 0x17, 0x65, 0xe3, - 0x40, 0xd5, 0x58, 0x80, 0x7f, 0x28, 0x70, 0xd4, 0x60, 0xfe, 0x67, 0xca, 0x38, 0x7a, 0x02, 0x1a, - 0x5f, 0x2f, 0x69, 0xe3, 0xdd, 0xf9, 0x9e, 0x77, 0x52, 0xe5, 0x46, 0xeb, 0x25, 0x25, 0x52, 0x88, - 0x1e, 0x83, 0xd1, 0x44, 0x5d, 0xfa, 0xd5, 0x1b, 0x9e, 0xec, 0xf5, 0xbc, 0x3c, 0x20, 0xad, 0x06, - 0x3d, 0xbf, 0x0b, 0x59, 0xe7, 0xef, 0x21, 0x13, 0x5d, 0x8d, 0x14, 0xbf, 0x00, 0x4d, 0x6c, 0x89, - 0x4c, 0xd0, 0x82, 0x78, 0x3a, 0xad, 0x2f, 0x38, 0x0b, 0x67, 0xf1, 0x74, 0x14, 0x89, 0xc0, 0x18, - 0xd0, 0x19, 0x79, 0x9e, 0xa5, 0x8a, 0xe4, 0xc4, 0x33, 0x4f, 0x80, 0x1d, 0xb1, 0xf6, 0xfc, 0xa9, - 0x1f, 0xf9, 0x96, 0x76, 0xd9, 0x05, 0xa3, 0x5a, 0xcd, 0x85, 0x6d, 0xf8, 0x04, 0xee, 0x8d, 0xb2, - 0x6c, 0xb3, 0xd7, 0x72, 0xb1, 0xc6, 0x17, 0x70, 0xea, 0xd1, 0x05, 0xe5, 0x74, 0x27, 0x4d, 0xff, - 0xfc, 0x3e, 0xf1, 0x29, 0xa0, 0x9d, 0x2f, 0x88, 0xef, 0x9e, 0xc3, 0x03, 0x22, 0x67, 0x35, 0x61, - 0xf3, 0x62, 0xc5, 0xb2, 0xf6, 0xf9, 0x08, 0x72, 0xae, 0xcb, 0x3f, 0xc3, 0xb3, 0x9f, 0x01, 0x00, - 0x00, 0xff, 0xff, 0xe5, 0x62, 0x19, 0x3f, 0x2a, 0x04, 0x00, 0x00, + // 581 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x54, 0xdd, 0x6e, 0xd3, 0x30, + 0x14, 0x5e, 0xda, 0x2c, 0x3f, 0xa7, 0x5b, 0xc9, 0xac, 0x09, 0x85, 0xed, 0x26, 0xb2, 0x10, 0xea, + 0x0d, 0x01, 0x15, 0xee, 0xd9, 0xd6, 0x64, 0xa2, 0x50, 0x92, 0xe1, 0x25, 0xe2, 0x3a, 0x4b, 0x8c, + 0x16, 0xe8, 0xe2, 0xe2, 0xb8, 0x83, 0xbd, 0x06, 0x4f, 0xc5, 0xe3, 0xf0, 0x08, 0xc8, 0x4e, 0xd2, + 0xad, 0x2b, 0x20, 0xc4, 0x9d, 0xcf, 0x77, 0xbe, 0x63, 0x1f, 0x7f, 0xdf, 0xb1, 0x61, 0x37, 0x67, + 0x95, 0xc8, 0x72, 0xe1, 0x2f, 0x38, 0x13, 0x0c, 0x59, 0xbc, 0xcc, 0x59, 0x7e, 0x49, 0x05, 0xfe, + 0xd1, 0x03, 0x73, 0xd2, 0xe4, 0xd0, 0x10, 0x7a, 0x65, 0xe1, 0x6a, 0x9e, 0x36, 0xda, 0x26, 0xbd, + 0xb2, 0x40, 0x2e, 0x98, 0x59, 0x51, 0x70, 0x5a, 0xd7, 0x6e, 0xcf, 0xd3, 0x46, 0x36, 0xe9, 0x42, + 0x74, 0x00, 0x56, 0x55, 0xe6, 0x9f, 0xab, 0xec, 0x8a, 0xba, 0x7d, 0x95, 0x5a, 0xc5, 0xc8, 0x83, + 0xc1, 0xd7, 0x4b, 0x5a, 0x4d, 0x38, 0xcd, 0x04, 0x2d, 0x5c, 0x5d, 0xa5, 0xef, 0x42, 0xe8, 0x31, + 0xec, 0xce, 0xb3, 0x5a, 0x4c, 0x58, 0x55, 0xd1, 0x5c, 0x72, 0xb6, 0x15, 0x67, 0x1d, 0x44, 0x63, + 0x30, 0x39, 0xfd, 0xb2, 0xa4, 0xb5, 0x70, 0x0d, 0x4f, 0x1b, 0x0d, 0xc6, 0xae, 0xdf, 0x75, 0xed, + 0xb7, 0x1d, 0x93, 0x26, 0x4f, 0x3a, 0x22, 0x7a, 0x0e, 0x46, 0x2d, 0x32, 0xb1, 0xac, 0x5d, 0xf0, + 0xb4, 0xd1, 0xf0, 0x37, 0x25, 0xfe, 0xb9, 0xca, 0x93, 0x96, 0x87, 0xa7, 0x60, 0x34, 0x08, 0x1a, + 0x80, 0x99, 0x46, 0x6f, 0xa3, 0xf8, 0x43, 0xe4, 0x6c, 0xc9, 0x20, 0x3e, 0x3d, 0x9d, 0x4d, 0xa3, + 0xd0, 0xd1, 0x10, 0x80, 0x11, 0x47, 0x6a, 0xdd, 0x93, 0x09, 0x12, 0xbe, 0x4f, 0xc3, 0xf3, 0xc4, + 0xe9, 0xa3, 0x1d, 0xb0, 0x48, 0xf8, 0x26, 0x9c, 0x24, 0x61, 0xe0, 0xe8, 0xf8, 0x7b, 0x1f, 0x86, + 0xeb, 0x8d, 0xa1, 0x23, 0xb0, 0x8b, 0x92, 0xd3, 0x5c, 0x94, 0xac, 0x52, 0xc2, 0x0e, 0xc7, 0xf8, + 0x4f, 0xb7, 0xf0, 0x83, 0x8e, 0x49, 0x6e, 0x8b, 0xfe, 0xd3, 0x03, 0x04, 0xba, 0xa0, 0xdf, 0x44, + 0x2b, 0xbe, 0x5a, 0x23, 0x0c, 0x3b, 0x1f, 0x39, 0xbb, 0x8a, 0xba, 0x9a, 0x46, 0xf4, 0x35, 0xec, + 0xbe, 0x77, 0xc6, 0xa6, 0x77, 0x07, 0x60, 0x71, 0xfa, 0xa9, 0xb1, 0xcd, 0xf4, 0xb4, 0x91, 0x45, + 0x56, 0xb1, 0xf4, 0x55, 0x52, 0x03, 0x3a, 0x2f, 0xaf, 0x29, 0xa7, 0x85, 0x6b, 0x35, 0xbe, 0xae, + 0x81, 0xb2, 0x0f, 0x09, 0x90, 0x6e, 0x17, 0xbb, 0xe9, 0xe3, 0x2e, 0x26, 0xfb, 0xe0, 0xf4, 0x8a, + 0x09, 0x1a, 0x72, 0xce, 0xb8, 0x32, 0xd3, 0x26, 0x77, 0x21, 0xfc, 0x04, 0xec, 0x95, 0x5e, 0xd2, + 0x94, 0x69, 0x74, 0x12, 0xa7, 0x51, 0xe0, 0x6c, 0x49, 0x53, 0xe2, 0x34, 0x69, 0x22, 0x0d, 0xbb, + 0xf0, 0xf0, 0x1d, 0xab, 0x4a, 0xc1, 0x78, 0xab, 0x76, 0xdd, 0xca, 0x8d, 0x7f, 0x6a, 0xb0, 0xd3, + 0x62, 0xe1, 0x35, 0xad, 0x04, 0x7a, 0x06, 0xba, 0xb8, 0x59, 0xd0, 0xd6, 0xa7, 0xc3, 0x0d, 0x9f, + 0x14, 0xcb, 0x4f, 0x6e, 0x16, 0x94, 0x28, 0x22, 0x7a, 0x0a, 0x66, 0xfb, 0xac, 0x94, 0x37, 0x83, + 0xf1, 0xde, 0x46, 0xcd, 0xeb, 0x2d, 0xd2, 0x71, 0xd0, 0xcb, 0xdb, 0x81, 0xee, 0xff, 0x7d, 0xa0, + 0x65, 0x55, 0x4b, 0xc5, 0xaf, 0x40, 0x97, 0x47, 0x22, 0x0b, 0xf4, 0x28, 0x9d, 0xcd, 0x9a, 0x0b, + 0x9e, 0xc5, 0x67, 0xe9, 0xec, 0x38, 0x91, 0xc3, 0x69, 0x42, 0xff, 0x38, 0x08, 0x9c, 0x9e, 0x9c, + 0xd2, 0xf4, 0x2c, 0x90, 0x60, 0x5f, 0xae, 0x83, 0x70, 0x16, 0x26, 0xa1, 0xa3, 0x9f, 0xd8, 0x60, + 0xd6, 0xcb, 0x0b, 0x29, 0x2c, 0xde, 0x83, 0x07, 0xc7, 0x45, 0xb1, 0x3a, 0x6b, 0x31, 0xbf, 0xc1, + 0x47, 0xb0, 0x1f, 0xd0, 0x39, 0x15, 0xf4, 0xde, 0xe4, 0xfe, 0xf3, 0x5f, 0x80, 0xf7, 0x01, 0xdd, + 0xdb, 0x41, 0xee, 0x7b, 0x08, 0x8f, 0x1a, 0x37, 0xa7, 0xd5, 0x05, 0x5b, 0x56, 0x45, 0xf7, 0x54, + 0x65, 0xf2, 0xc2, 0x50, 0xbf, 0xd0, 0x8b, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x13, 0x18, 0x1c, + 0x90, 0x96, 0x04, 0x00, 0x00, } diff --git a/rpc/contact.proto b/rpc/contact.proto index 950f545..eeb3f37 100644 --- a/rpc/contact.proto +++ b/rpc/contact.proto @@ -34,6 +34,9 @@ message ContactRequest { string fromNickname = 5; string whenCreated = 6; bool rejected = 7; + string whenDelivered = 8; + string whenRejected = 9; + string remoteError = 10; } message MonitorContactsRequest { diff --git a/rpc/rpc.go b/rpc/rpc.go index 66f0181..5b4b645 100644 --- a/rpc/rpc.go +++ b/rpc/rpc.go @@ -1,3 +1,3 @@ package ricochet -//go:generate protoc --go_out=plugins=grpc:. contact.proto conversation.proto core.proto identity.proto network.proto +//go:generate protoc --go_out=plugins=grpc:. contact.proto conversation.proto core.proto identity.proto network.proto config.proto