cli: Handle inbound contact requests
This commit is contained in:
parent
5475eeddba
commit
42a2cba547
|
@ -177,55 +177,29 @@ func (c *Client) onContactEvent(event *ricochet.ContactEvent) {
|
||||||
if !c.populatedContacts && event.Type != ricochet.ContactEvent_POPULATE {
|
if !c.populatedContacts && event.Type != ricochet.ContactEvent_POPULATE {
|
||||||
log.Printf("Ignoring unexpected contact event during populate: %v", event)
|
log.Printf("Ignoring unexpected contact event during populate: %v", event)
|
||||||
return
|
return
|
||||||
|
} else if c.populatedContacts && event.Type == ricochet.ContactEvent_POPULATE {
|
||||||
|
log.Printf("Ignoring unexpected contact populate event: %v", event)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
data := event.GetContact()
|
if cData := event.GetContact(); cData != nil {
|
||||||
|
|
||||||
switch event.Type {
|
switch event.Type {
|
||||||
case ricochet.ContactEvent_POPULATE:
|
case ricochet.ContactEvent_POPULATE:
|
||||||
if c.populatedContacts {
|
c.Contacts.Populate(cData)
|
||||||
log.Printf("Ignoring unexpected contact populate event: %v", event)
|
|
||||||
} else if event.Subject == nil {
|
|
||||||
// Populate is terminated by a nil subject
|
|
||||||
c.populatedContacts = true
|
|
||||||
log.Printf("Loaded %d contacts", len(c.Contacts.Contacts))
|
|
||||||
c.checkIfPopulated()
|
|
||||||
go c.monitorConversations()
|
|
||||||
} else if data != nil {
|
|
||||||
c.Contacts.Populate(data)
|
|
||||||
} else {
|
|
||||||
log.Printf("Invalid contact populate event: %v", event)
|
|
||||||
}
|
|
||||||
|
|
||||||
case ricochet.ContactEvent_ADD:
|
case ricochet.ContactEvent_ADD:
|
||||||
if data == nil {
|
c.Contacts.Added(cData)
|
||||||
log.Printf("Ignoring contact add event with null data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Contacts.Added(data)
|
|
||||||
|
|
||||||
case ricochet.ContactEvent_UPDATE:
|
case ricochet.ContactEvent_UPDATE:
|
||||||
if data == nil {
|
contact := c.Contacts.ByAddress(cData.Address)
|
||||||
log.Printf("Ignoring contact update event with null data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
contact := c.Contacts.ByAddress(data.Address)
|
|
||||||
if contact == nil {
|
if contact == nil {
|
||||||
log.Printf("Ignoring contact update event for unknown contact: %v", data)
|
log.Printf("Ignoring contact update event for unknown contact: %v", cData)
|
||||||
} else {
|
} else {
|
||||||
contact.Updated(data)
|
contact.Updated(cData)
|
||||||
}
|
}
|
||||||
|
|
||||||
case ricochet.ContactEvent_DELETE:
|
case ricochet.ContactEvent_DELETE:
|
||||||
if data == nil {
|
contact, _ := c.Contacts.Deleted(cData)
|
||||||
log.Printf("Ignoring contact delete event with null data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
contact, _ := c.Contacts.Deleted(data)
|
|
||||||
|
|
||||||
if Ui.CurrentContact == contact {
|
if Ui.CurrentContact == contact {
|
||||||
Ui.SetCurrentContact(nil)
|
Ui.SetCurrentContact(nil)
|
||||||
}
|
}
|
||||||
|
@ -233,6 +207,30 @@ func (c *Client) onContactEvent(event *ricochet.ContactEvent) {
|
||||||
default:
|
default:
|
||||||
log.Printf("Ignoring unknown contact event: %v", event)
|
log.Printf("Ignoring unknown contact event: %v", event)
|
||||||
}
|
}
|
||||||
|
} else if reqData := event.GetRequest(); reqData != nil {
|
||||||
|
switch event.Type {
|
||||||
|
case ricochet.ContactEvent_POPULATE:
|
||||||
|
c.Contacts.Requests[reqData.Address] = reqData
|
||||||
|
case ricochet.ContactEvent_ADD:
|
||||||
|
fallthrough
|
||||||
|
case ricochet.ContactEvent_UPDATE:
|
||||||
|
c.Contacts.Requests[reqData.Address] = reqData
|
||||||
|
fmt.Fprintf(Ui.Stdout, "\r\x1b[31m[[\x1b[0m \x1b[1m%s\x1b[0m wants to be your contact. Type \x1b[1m%s\x1b[0m to respond \x1b[31m]]\x1b[39m\n", reqData.Address, Ui.PrefixForAddress(reqData.Address))
|
||||||
|
|
||||||
|
case ricochet.ContactEvent_DELETE:
|
||||||
|
if c.Contacts.Requests[reqData.Address] != nil {
|
||||||
|
delete(c.Contacts.Requests, reqData.Address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if event.Subject == nil && event.Type == ricochet.ContactEvent_POPULATE && !c.populatedContacts {
|
||||||
|
// Populate is terminated by a nil subject
|
||||||
|
c.populatedContacts = true
|
||||||
|
log.Printf("Loaded %d contacts and %d requests", len(c.Contacts.Contacts), len(c.Contacts.Requests))
|
||||||
|
c.checkIfPopulated()
|
||||||
|
go c.monitorConversations()
|
||||||
|
} else {
|
||||||
|
log.Printf("Ignoring event with an unexpected subject")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) onConversationEvent(event *ricochet.ConversationEvent) {
|
func (c *Client) onConversationEvent(event *ricochet.ConversationEvent) {
|
||||||
|
|
|
@ -9,12 +9,14 @@ import (
|
||||||
type ContactList struct {
|
type ContactList struct {
|
||||||
Client *Client
|
Client *Client
|
||||||
Contacts map[string]*Contact
|
Contacts map[string]*Contact
|
||||||
|
Requests map[string]*ricochet.ContactRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewContactList(client *Client) *ContactList {
|
func NewContactList(client *Client) *ContactList {
|
||||||
return &ContactList{
|
return &ContactList{
|
||||||
Client: client,
|
Client: client,
|
||||||
Contacts: make(map[string]*Contact),
|
Contacts: make(map[string]*Contact),
|
||||||
|
Requests: make(map[string]*ricochet.ContactRequest),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -243,7 +243,7 @@ func (c *Conversation) printMessage(msg *ricochet.Message) {
|
||||||
if c.numUnread > 1 {
|
if c.numUnread > 1 {
|
||||||
messages += "s"
|
messages += "s"
|
||||||
}
|
}
|
||||||
fmt.Fprintf(Ui.Stdout, "\r\x1b[31m[[ \x1b[1;34m%s\x1b[0m from \x1b[1m%s\x1b[0m (\x1b[1m%s\x1b[0m) \x1b[31m]]\x1b[39m\n", messages, c.Contact.Data.Nickname, Ui.PrefixForContact(c.Contact))
|
fmt.Fprintf(Ui.Stdout, "\r\x1b[31m[[ \x1b[1;34m%s\x1b[0m from \x1b[1m%s\x1b[0m (\x1b[1m%s\x1b[0m) \x1b[31m]]\x1b[39m\n", messages, c.Contact.Data.Nickname, Ui.PrefixForAddress(c.Contact.Data.Address))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -110,9 +110,11 @@ func (ui *UI) Execute(line string) error {
|
||||||
ui.printHelp()
|
ui.printHelp()
|
||||||
|
|
||||||
default:
|
default:
|
||||||
contact := ui.ContactByPrefix(line)
|
contact, request := ui.EntityByPrefix(line)
|
||||||
if contact != nil {
|
if contact != nil {
|
||||||
ui.SetCurrentContact(contact)
|
ui.SetCurrentContact(contact)
|
||||||
|
} else if request != nil {
|
||||||
|
ui.ShowContactRequest(request)
|
||||||
} else {
|
} else {
|
||||||
ui.printHelp()
|
ui.printHelp()
|
||||||
}
|
}
|
||||||
|
@ -156,8 +158,26 @@ func (ui *UI) PrintStatus() {
|
||||||
|
|
||||||
fmt.Fprintf(ui.Stdout, "Your ricochet ID is %s\n", ui.Client.Identity.Address)
|
fmt.Fprintf(ui.Stdout, "Your ricochet ID is %s\n", ui.Client.Identity.Address)
|
||||||
|
|
||||||
// no. contacts, contact reqs, online contacts
|
var nContacts, nOnline int
|
||||||
// unread messages
|
for _, contact := range ui.Client.Contacts.Contacts {
|
||||||
|
nContacts++
|
||||||
|
if contact.Data.Status == ricochet.Contact_ONLINE {
|
||||||
|
nOnline++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if nContacts > 0 {
|
||||||
|
fmt.Fprintf(ui.Stdout, "%d of %d contacts are online\n", nOnline, nContacts)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(ui.Stdout, "You have no contacts :(\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
if reqs := len(ui.Client.Contacts.Requests); reqs > 0 {
|
||||||
|
plural := ""
|
||||||
|
if reqs > 1 {
|
||||||
|
plural = "s"
|
||||||
|
}
|
||||||
|
fmt.Fprintf(ui.Stdout, "%d new contact request%s are waiting\n", reqs, plural)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ui *UI) ListContacts() {
|
func (ui *UI) ListContacts() {
|
||||||
|
@ -176,12 +196,19 @@ func (ui *UI) ListContacts() {
|
||||||
for _, contact := range contacts {
|
for _, contact := range contacts {
|
||||||
unreadCount := contact.Conversation.UnreadCount()
|
unreadCount := contact.Conversation.UnreadCount()
|
||||||
if unreadCount > 0 {
|
if unreadCount > 0 {
|
||||||
fmt.Fprintf(ui.Stdout, " \x1b[1m%s\x1b[0m (\x1b[1m%s\x1b[0m) -- \x1b[34;1m%d new messages\x1b[0m\n", contact.Data.Nickname, ui.PrefixForContact(contact), unreadCount)
|
fmt.Fprintf(ui.Stdout, " \x1b[1m%s\x1b[0m (\x1b[1m%s\x1b[0m) -- \x1b[34;1m%d new messages\x1b[0m\n", contact.Data.Nickname, ui.PrefixForAddress(contact.Data.Address), unreadCount)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(ui.Stdout, " %s (\x1b[1m%s\x1b[0m)\n", contact.Data.Nickname, ui.PrefixForContact(contact))
|
fmt.Fprintf(ui.Stdout, " %s (\x1b[1m%s\x1b[0m)\n", contact.Data.Nickname, ui.PrefixForAddress(contact.Data.Address))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(ui.Client.Contacts.Requests) > 0 {
|
||||||
|
fmt.Fprintf(ui.Stdout, "\x1b[contact requests received\x1b[39m\n")
|
||||||
|
for _, request := range ui.Client.Contacts.Requests {
|
||||||
|
fmt.Fprintf(ui.Stdout, " %s (\x1b[1m%s\x1b[0m)\n", request.Address, ui.PrefixForAddress(request.Address))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ui *UI) AddContact(params []string) {
|
func (ui *UI) AddContact(params []string) {
|
||||||
|
@ -236,7 +263,7 @@ func (ui *UI) DeleteContact(params []string) {
|
||||||
}
|
}
|
||||||
contact := ui.Client.Contacts.ByAddress(params[0])
|
contact := ui.Client.Contacts.ByAddress(params[0])
|
||||||
if contact == nil {
|
if contact == nil {
|
||||||
contact = ui.ContactByPrefix(params[0])
|
contact, _ = ui.EntityByPrefix(params[0])
|
||||||
}
|
}
|
||||||
if contact == nil {
|
if contact == nil {
|
||||||
fmt.Fprintf(ui.Stdout, "No contact with address %s\n", params[0])
|
fmt.Fprintf(ui.Stdout, "No contact with address %s\n", params[0])
|
||||||
|
@ -388,38 +415,61 @@ func ColoredContactStatus(status ricochet.Contact_Status) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ui *UI) ContactByPrefix(prefix string) *Contact {
|
func (ui *UI) EntityByPrefix(prefix string) (*Contact, *ricochet.ContactRequest) {
|
||||||
if len(prefix) < MinContactPrefix {
|
if len(prefix) < MinContactPrefix || len(prefix) > 16 {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var contact *Contact
|
var contact *Contact
|
||||||
for _, c := range ui.Client.Contacts.Contacts {
|
for _, c := range ui.Client.Contacts.Contacts {
|
||||||
host, _ := core.PlainHostFromAddress(c.Data.Address)
|
host, _ := core.PlainHostFromAddress(c.Data.Address)
|
||||||
if prefix == host[:len(prefix)] {
|
if prefix == host[:len(prefix)] {
|
||||||
if contact != nil {
|
if contact != nil {
|
||||||
// Ambiguous prefix
|
// Ambiguous prefix
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
contact = c
|
contact = c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return contact
|
|
||||||
|
var request *ricochet.ContactRequest
|
||||||
|
for _, r := range ui.Client.Contacts.Requests {
|
||||||
|
host, _ := core.PlainHostFromAddress(r.Address)
|
||||||
|
if prefix == host[:len(prefix)] {
|
||||||
|
if contact != nil || request != nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
request = r
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ui *UI) PrefixForContact(contact *Contact) string {
|
return contact, request
|
||||||
host, _ := core.PlainHostFromAddress(contact.Data.Address)
|
}
|
||||||
|
|
||||||
|
func (ui *UI) PrefixForAddress(address string) string {
|
||||||
|
host, _ := core.PlainHostFromAddress(address)
|
||||||
prefix := host[:MinContactPrefix]
|
prefix := host[:MinContactPrefix]
|
||||||
|
|
||||||
for _, c := range ui.Client.Contacts.Contacts {
|
for _, c := range ui.Client.Contacts.Contacts {
|
||||||
if c == contact {
|
cHost, _ := core.PlainHostFromAddress(c.Data.Address)
|
||||||
|
if cHost == host {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
cHost, _ := core.PlainHostFromAddress(c.Data.Address)
|
|
||||||
for prefix == cHost[:len(prefix)] && len(prefix) < len(host) {
|
for prefix == cHost[:len(prefix)] && len(prefix) < len(host) {
|
||||||
prefix = host[:len(prefix)+1]
|
prefix = host[:len(prefix)+1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, r := range ui.Client.Contacts.Requests {
|
||||||
|
rHost, _ := core.PlainHostFromAddress(r.Address)
|
||||||
|
if rHost == host {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for prefix == rHost[:len(prefix)] && len(prefix) < len(host) {
|
||||||
|
prefix = host[:len(prefix)+1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return prefix
|
return prefix
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,3 +492,56 @@ func (ui *UI) SetCurrentContact(contact *Contact) {
|
||||||
ui.Input.SetConfig(ui.baseConfig)
|
ui.Input.SetConfig(ui.baseConfig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ui *UI) ShowContactRequest(request *ricochet.ContactRequest) {
|
||||||
|
fmt.Fprintf(ui.Stdout, "\nSomeone would like to add you as a contact:\n\n")
|
||||||
|
fmt.Fprintf(ui.Stdout, " Address:\t%s\n", request.Address)
|
||||||
|
// XXX Proper output sanitization here
|
||||||
|
if len(request.FromNickname) > 0 {
|
||||||
|
fmt.Fprintf(ui.Stdout, " Name:\t%s\n", request.FromNickname)
|
||||||
|
}
|
||||||
|
if len(request.Text) > 0 {
|
||||||
|
fmt.Fprintf(ui.Stdout, " Message:\t%s\n", request.Text)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(ui.Stdout, "\n")
|
||||||
|
action, err := readline.Line("(a)ccept, (r)eject, or (w)ait: ")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix("reject", action) {
|
||||||
|
_, err := ui.Client.Backend.RejectInboundRequest(context.Background(), request)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(ui.Stdout, "Failed: %s\n", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} else if !strings.HasPrefix("accept", action) {
|
||||||
|
// Anything other than accept is wait
|
||||||
|
fmt.Fprintf(ui.Stdout, "Doing nothing.\n")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
nickname, err := readline.Line("nickname: ")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
} else if nickname == "" {
|
||||||
|
fmt.Fprintf(ui.Stdout, "Aborted.\n")
|
||||||
|
return
|
||||||
|
} else if !core.IsNicknameAcceptable(nickname) {
|
||||||
|
fmt.Fprintf(ui.Stdout, "Invalid nickname '%s'\n", nickname)
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
request.FromNickname = nickname
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ui.Client.Backend.AcceptInboundRequest(context.Background(), request)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(ui.Stdout, "Failed: %s\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(ui.Stdout, "Accepted!\n")
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue