cli: Handle inbound contact requests
This commit is contained in:
		
							parent
							
								
									5475eeddba
								
							
						
					
					
						commit
						42a2cba547
					
				|  | @ -177,61 +177,59 @@ func (c *Client) onContactEvent(event *ricochet.ContactEvent) { | |||
| 	if !c.populatedContacts && event.Type != ricochet.ContactEvent_POPULATE { | ||||
| 		log.Printf("Ignoring unexpected contact event during populate: %v", event) | ||||
| 		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 { | ||||
| 		case ricochet.ContactEvent_POPULATE: | ||||
| 			c.Contacts.Populate(cData) | ||||
| 
 | ||||
| 	switch event.Type { | ||||
| 	case ricochet.ContactEvent_POPULATE: | ||||
| 		if c.populatedContacts { | ||||
| 			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: | ||||
| 			c.Contacts.Added(cData) | ||||
| 
 | ||||
| 		case ricochet.ContactEvent_UPDATE: | ||||
| 			contact := c.Contacts.ByAddress(cData.Address) | ||||
| 			if contact == nil { | ||||
| 				log.Printf("Ignoring contact update event for unknown contact: %v", cData) | ||||
| 			} else { | ||||
| 				contact.Updated(cData) | ||||
| 			} | ||||
| 
 | ||||
| 		case ricochet.ContactEvent_DELETE: | ||||
| 			contact, _ := c.Contacts.Deleted(cData) | ||||
| 			if Ui.CurrentContact == contact { | ||||
| 				Ui.SetCurrentContact(nil) | ||||
| 			} | ||||
| 
 | ||||
| 		default: | ||||
| 			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_ADD: | ||||
| 		if data == nil { | ||||
| 			log.Printf("Ignoring contact add event with null data") | ||||
| 			return | ||||
| 		case ricochet.ContactEvent_DELETE: | ||||
| 			if c.Contacts.Requests[reqData.Address] != nil { | ||||
| 				delete(c.Contacts.Requests, reqData.Address) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		c.Contacts.Added(data) | ||||
| 
 | ||||
| 	case ricochet.ContactEvent_UPDATE: | ||||
| 		if data == nil { | ||||
| 			log.Printf("Ignoring contact update event with null data") | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		contact := c.Contacts.ByAddress(data.Address) | ||||
| 		if contact == nil { | ||||
| 			log.Printf("Ignoring contact update event for unknown contact: %v", data) | ||||
| 		} else { | ||||
| 			contact.Updated(data) | ||||
| 		} | ||||
| 
 | ||||
| 	case ricochet.ContactEvent_DELETE: | ||||
| 		if data == nil { | ||||
| 			log.Printf("Ignoring contact delete event with null data") | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		contact, _ := c.Contacts.Deleted(data) | ||||
| 
 | ||||
| 		if Ui.CurrentContact == contact { | ||||
| 			Ui.SetCurrentContact(nil) | ||||
| 		} | ||||
| 
 | ||||
| 	default: | ||||
| 		log.Printf("Ignoring unknown contact event: %v", event) | ||||
| 	} 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") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -9,12 +9,14 @@ import ( | |||
| type ContactList struct { | ||||
| 	Client   *Client | ||||
| 	Contacts map[string]*Contact | ||||
| 	Requests map[string]*ricochet.ContactRequest | ||||
| } | ||||
| 
 | ||||
| func NewContactList(client *Client) *ContactList { | ||||
| 	return &ContactList{ | ||||
| 		Client:   client, | ||||
| 		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 { | ||||
| 			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 | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -110,9 +110,11 @@ func (ui *UI) Execute(line string) error { | |||
| 		ui.printHelp() | ||||
| 
 | ||||
| 	default: | ||||
| 		contact := ui.ContactByPrefix(line) | ||||
| 		contact, request := ui.EntityByPrefix(line) | ||||
| 		if contact != nil { | ||||
| 			ui.SetCurrentContact(contact) | ||||
| 		} else if request != nil { | ||||
| 			ui.ShowContactRequest(request) | ||||
| 		} else { | ||||
| 			ui.printHelp() | ||||
| 		} | ||||
|  | @ -156,8 +158,26 @@ func (ui *UI) PrintStatus() { | |||
| 
 | ||||
| 	fmt.Fprintf(ui.Stdout, "Your ricochet ID is %s\n", ui.Client.Identity.Address) | ||||
| 
 | ||||
| 	// no. contacts, contact reqs, online contacts
 | ||||
| 	// unread messages
 | ||||
| 	var nContacts, nOnline int | ||||
| 	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() { | ||||
|  | @ -176,12 +196,19 @@ func (ui *UI) ListContacts() { | |||
| 		for _, contact := range contacts { | ||||
| 			unreadCount := contact.Conversation.UnreadCount() | ||||
| 			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 { | ||||
| 				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) { | ||||
|  | @ -236,7 +263,7 @@ func (ui *UI) DeleteContact(params []string) { | |||
| 	} | ||||
| 	contact := ui.Client.Contacts.ByAddress(params[0]) | ||||
| 	if contact == nil { | ||||
| 		contact = ui.ContactByPrefix(params[0]) | ||||
| 		contact, _ = ui.EntityByPrefix(params[0]) | ||||
| 	} | ||||
| 	if contact == nil { | ||||
| 		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 { | ||||
| 	if len(prefix) < MinContactPrefix { | ||||
| 		return nil | ||||
| func (ui *UI) EntityByPrefix(prefix string) (*Contact, *ricochet.ContactRequest) { | ||||
| 	if len(prefix) < MinContactPrefix || len(prefix) > 16 { | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 
 | ||||
| 	var contact *Contact | ||||
| 	for _, c := range ui.Client.Contacts.Contacts { | ||||
| 		host, _ := core.PlainHostFromAddress(c.Data.Address) | ||||
| 		if prefix == host[:len(prefix)] { | ||||
| 			if contact != nil { | ||||
| 				// Ambiguous prefix
 | ||||
| 				return nil | ||||
| 				return nil, nil | ||||
| 			} | ||||
| 			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 | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return contact, request | ||||
| } | ||||
| 
 | ||||
| func (ui *UI) PrefixForContact(contact *Contact) string { | ||||
| 	host, _ := core.PlainHostFromAddress(contact.Data.Address) | ||||
| func (ui *UI) PrefixForAddress(address string) string { | ||||
| 	host, _ := core.PlainHostFromAddress(address) | ||||
| 	prefix := host[:MinContactPrefix] | ||||
| 
 | ||||
| 	for _, c := range ui.Client.Contacts.Contacts { | ||||
| 		if c == contact { | ||||
| 		cHost, _ := core.PlainHostFromAddress(c.Data.Address) | ||||
| 		if cHost == host { | ||||
| 			continue | ||||
| 		} | ||||
| 		cHost, _ := core.PlainHostFromAddress(c.Data.Address) | ||||
| 		for prefix == cHost[:len(prefix)] && len(prefix) < len(host) { | ||||
| 			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 | ||||
| } | ||||
| 
 | ||||
|  | @ -442,3 +492,56 @@ func (ui *UI) SetCurrentContact(contact *Contact) { | |||
| 		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