diff --git a/bindings/contact.go b/bindings/contact.go index cfebb078270de5a36baa7f341e4cbbfdfb5d39a9..1cdef91938445b1e401b0be58d6e6d32de315115 100644 --- a/bindings/contact.go +++ b/bindings/contact.go @@ -8,12 +8,27 @@ package bindings import ( - "errors" "gitlab.com/elixxir/client/interfaces/contact" "gitlab.com/elixxir/primitives/fact" ) /* fact object*/ +//creates a new fact. The factType must be either: +// 0 - Username +// 1 - Email +// 2 - Phone Number +// The fact must be well formed for the type and must not include commas or +// semicolons. If it is not well formed, it will be rejected. Phone numbers +// must have the two letter country codes appended. For the complete set of +// validation, see /elixxir/primitives/fact/fact.go +func NewFact(factType int, factStr string)(*Fact, error){ + f, err := fact.NewFact(fact.FactType(factType), factStr) + if err!=nil{ + return nil, err + } + return &Fact{f:&f}, nil +} + type Fact struct { f *fact.Fact } @@ -26,6 +41,10 @@ func (f *Fact) Type() int { return int(f.f.T) } +func (f *Fact) Stringify() string { + return f.f.Stringify() +} + /* contact object*/ type Contact struct { c *contact.Contact @@ -54,32 +73,3 @@ func (c *Contact) GetFactList() *FactList { func (c *Contact) Marshal() ([]byte, error) { return c.c.Marshal() } - -/* FactList object*/ -type FactList struct { - c *contact.Contact -} - -func (fl *FactList) Num() int { - return len(fl.c.Facts) -} - -func (fl *FactList) Get(i int) Fact { - return Fact{f: &(fl.c.Facts)[i]} -} - -func (fl *FactList) Add(factData string, factType int) error { - ft := fact.FactType(factType) - if !ft.IsValid() { - return errors.New("Invalid fact type") - } - fl.c.Facts = append(fl.c.Facts, fact.Fact{ - Fact: factData, - T: ft, - }) - return nil -} - -func (fl *FactList) Marshal() ([]byte, error) { - return []byte(fl.c.Facts.Stringify()), nil -} diff --git a/bindings/interfaces.go b/bindings/interfaces.go deleted file mode 100644 index f827aefb50265cbec7b9f7463e86f737febd0394..0000000000000000000000000000000000000000 --- a/bindings/interfaces.go +++ /dev/null @@ -1,202 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// Copyright © 2020 xx network SEZC // -// // -// Use of this source code is governed by a license that can be found in the // -// LICENSE file // -/////////////////////////////////////////////////////////////////////////////// - -package bindings - -/* -// Client is defined inside the api package. At minimum, it implements all of -// functionality defined here. A Client handles all network connectivity, key -// generation, and storage for a given cryptographic identity on the cmix -// network. -// These threads may become a significant drain on battery when offline, ensure -// they are stopped if there is no internet access -type client interface { - // ----- Network ----- - // StartNetworkFollower kicks off the tracking of the network. It starts - // long running network client threads and returns an object for checking - // state and stopping those threads. - // Call this when returning from sleep and close when going back to - // sleep. - StartNetworkFollower() error - - // StopNetworkFollower stops the network follower if it is running. - // It returns errors if the Follower is in the wrong status to stop or if it - // fails to stop it. - // if the network follower is running and this fails, the client object will - // most likely be in an unrecoverable state and need to be trashed. - StopNetworkFollower(timeoutMS int) error - - // NetworkFollowerStatus gets the state of the network follower. - // Returns: - // Stopped - 0 - // Starting - 1000 - // Running - 2000 - // Stopping - 3000 - NetworkFollowerStatus() int - - // Returns true if the following of the network is in a state where messages - // can be sent, false otherwise - IsNetworkHealthy() bool - - // Registers a callback which gets triggered every time network health - // changes - RegisterNetworkHealthCB(func(bool)) - - // ----- Reception ----- - - // RegisterListener records and installs a listener for messages - // matching specific uid, msgType, and/or username - RegisterListener(uid []byte, msgType int, username string, - listener Listener) - - // ----- Transmission ----- - - // SendE2E sends an end-to-end payload to the provided recipient with - // the provided msgType. Returns the list of rounds in which parts of - // the message were sent or an error if it fails. - SendE2E(payload, recipient []byte, msgType int) (RoundList, error) - // SendUnsafe sends an unencrypted payload to the provided recipient - // with the provided msgType. Returns the list of rounds in which parts - // of the message were sent or an error if it fails. - // NOTE: Do not use this function unless you know what you are doing. - // This function always produces an error message in client logging. - SendUnsafe(payload, recipient []byte, msgType int) (RoundList, error) - // SendCMIX sends a "raw" CMIX message payload to the provided - // recipient. Note that both SendE2E and SendUnsafe call SendCMIX. - // Returns the round ID of the round the payload was sent or an error - // if it fails. - SendCMIX(payload, recipient []byte) (int, error) - - // ----- Notifications ----- - - // RegisterForNotifications allows a client to register for push - // notifications. - // Note that clients are not required to register for push notifications - // especially as these rely on third parties (i.e., Firebase *cough* - // *cough* google's palantir *cough*) that may represent a security - // risk to the user. - RegisterForNotifications(token []byte) error - // UnregisterForNotifications turns of notifications for this client - UnregisterForNotifications() error - - // ----- Registration ----- - - // Returns true if the cryptographic identity has been registered with - // the CMIX user discovery agent. - // Note that clients do not need to perform this step if they use - // out of band methods to exchange cryptographic identities - // (e.g., QR codes), but failing to be registered precludes usage - // of the user discovery mechanism (this may be preferred by user). - IsRegistered() bool - - // RegisterIdentity registers an arbitrary username with the user - // discovery protocol. Returns an error when it cannot connect or - // the username is already registered. - RegisterIdentity(username string) error - // RegisterEmail makes the users email searchable after confirmation. - // It returns a registration confirmation token to be used with - // ConfirmRegistration or an error on failure. - RegisterEmail(email string) ([]byte, error) - // RegisterPhone makes the users phone searchable after confirmation. - // It returns a registration confirmation token to be used with - // ConfirmRegistration or an error on failure. - RegisterPhone(phone string) ([]byte, error) - // ConfirmRegistration sends the user discovery agent a confirmation - // token (from Register Email/Phone) and code (string sent via Email - // or SMS to confirm ownership) to confirm ownership. - ConfirmRegistration(token, code []byte) error - - // ----- Contacts ----- - - // GetUser returns the current user Identity for this client. This - // can be serialized into a byte stream for out-of-band sharing. - GetUser() (bind.Contact, error) - - // ----- User Discovery ----- - - // Search accepts a "separator" separated list of search elements with - // an associated list of searchTypes. It returns a ContactList which - // allows you to iterate over the found contact objects. - Search(data, separator string, searchTypes []byte) ContactList - // SearchWithHandler is a non-blocking search that also registers - // a callback interface for user disovery events. - SearchWithHandler(data, separator string, searchTypes []byte, - hdlr UserDiscoveryHandler) - - // ----- Key Exchange ----- - - // CreateAuthenticatedChannel creates a 1-way authenticated channel - // so this user can send messages to the desired recipient Contact. - // To receive confirmation from the remote user, clients must - // register a listener to do that. - CreateAuthenticatedChannel(recipient bind.Contact, payload []byte) error - // RegierAuthEventsHandler registers a callback interface for channel - // authentication events. - RegisterAuthEventsHandler(hdlr AuthEventHandler) - - // ----- Network ----- - - // RegisterRoundEventsHandler registers a callback interface for round - // events. - RegisterRoundEventsHandler() -} - -// ContactList contains a list of contacts -type ContactList interface { - // GetLen returns the number of contacts in the list - GetLen() int - // GetContact returns the contact at index i - GetContact(i int) bind.Contact -} - -// ----- Callback interfaces ----- - - - -// AuthEventHandler handles authentication requests initiated by -// CreateAuthenticatedChannel -type AuthEventHandler interface { - // HandleConfirmation handles AuthEvents received after - // the client has called CreateAuthenticatedChannel for - // the provided contact. Payload is typically empty but - // may include a small introductory message. - HandleConfirmation(contact bind.Contact, payload []byte) - // HandleRequest handles AuthEvents received before - // the client has called CreateAuthenticatedChannel for - // the provided contact. It should prompt the user to accept - // the channel creation "request" and, if approved, - // call CreateAuthenticatedChannel for this Contact. - HandleRequest(contact bind.Contact, payload []byte) -} - -// UserDiscoveryHandler handles search results against the user discovery agent. -type UserDiscoveryHandler interface { - HandleSearchResults(results ContactList) -} - - -// Message is a message received from the cMix network in the clear -// or that has been decrypted using established E2E keys. -type Message interface { - //Returns the id of the message - GetID() []byte - - // Returns the message's sender ID, if available - GetSender() []byte - - // Returns the message payload/contents - // Parse this with protobuf/whatever according to the message type - GetPayload() []byte - - // Returns the message's type - GetMessageType() int - - // Returns the message's timestamp in milliseconds since unix epoc - GetTimestampMS() int - // Returns the message's timestamp in ns since unix epoc - GetTimestampNano() int -}*/ diff --git a/bindings/list.go b/bindings/list.go index 5657bcb456706c28905a9c58f33e891eae61fba3..fda887f93f12860d90792405a6ef0e3864f3a30c 100644 --- a/bindings/list.go +++ b/bindings/list.go @@ -9,9 +9,13 @@ package bindings import ( "errors" + "gitlab.com/elixxir/client/interfaces/contact" + "gitlab.com/elixxir/primitives/fact" "gitlab.com/xx_network/primitives/id" ) +/*IntList*/ + type IntList struct { lst []int } @@ -35,6 +39,8 @@ func (il *IntList) Get(i int) (int, error) { return il.lst[i], nil } +/*RoundList*/ + type RoundList struct { list []id.Round } @@ -53,3 +59,59 @@ func (rl *RoundList) Get(i int) (int, error) { return int(rl.list[i]), nil } + +/*ContactList*/ + +type ContactList struct { + list []contact.Contact +} + +// Gets the number of round IDs stored +func (cl *ContactList) Len() int { + return len(cl.list) +} + +// Gets a stored round ID at the given index +func (cl *ContactList) Get(i int) (*Contact, error) { + if i < 0 || i > len(cl.list) { + return nil, errors.New("contact cannot be under 0 or over" + + " list len") + } + + return &Contact{c:&cl.list[i]}, nil +} + +/*FactList*/ +func NewFactList()*FactList{ + return &FactList{ c: &contact.Contact{ + ID: nil, + DhPubKey: nil, + OwnershipProof: nil, + Facts: make([]fact.Fact,0), + }} +} + +type FactList struct { + c *contact.Contact +} + +func (fl *FactList) Num() int { + return len(fl.c.Facts) +} + +func (fl *FactList) Get(i int) Fact { + return Fact{f: &(fl.c.Facts)[i]} +} + +func (fl *FactList) Add(factData string, factType int) error { + f, err := fact.NewFact(fact.FactType(factType), factData) + if err!=nil{ + return err + } + fl.c.Facts = append(fl.c.Facts, f) + return nil +} + +func (fl *FactList) Stringify() (string, error) { + return fl.c.Facts.Stringify(), nil +} diff --git a/bindings/ud.go b/bindings/ud.go new file mode 100644 index 0000000000000000000000000000000000000000..bae84a46f6b040a7a366cd51396f0c433278da0a --- /dev/null +++ b/bindings/ud.go @@ -0,0 +1,184 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package bindings + +import ( + "github.com/pkg/errors" + "gitlab.com/elixxir/client/interfaces/contact" + "gitlab.com/elixxir/client/ud" + "gitlab.com/elixxir/primitives/fact" + "gitlab.com/xx_network/primitives/id" + "time" +) + +//This package wraps the user discovery system + +// User Discovery object +type UserDiscovery struct{ + ud *ud.Manager +} + +// Returns a new user discovery object. Only call this once. It must be called +// after StartNetworkFollower is called and will fail if the network has never +// been contacted. +// This function technically has a memory leak because it causes both sides of +// the bindings to think the other is in charge of the client object. +// In general this is not an issue because the client object should exist +// for the life of the program. +func NewUserDiscovery(client *Client)(*UserDiscovery, error){ + m, err := ud.NewManager(&client.api) + + if err!=nil{ + return nil, err + }else{ + return &UserDiscovery{ud:m}, nil + } +} + +// Register registers a user with user discovery. Will return an error if the +// network signatures are malformed or if the username is taken. Usernames +// cannot be changed after registration at this time. Will fail if the user is +// already registered. +// Registration does not go over cmix, it occurs over normal communications +func (ud *UserDiscovery)Register(username string)error{ + return ud.ud.Register(username) +} + +// Adds a fact for the user to user discovery. Will only succeed if the +// user is already registered and the system does not have the fact currently +// registered for any user. +// Will fail if the fact string is not well formed. +// This does not complete the fact registration process, it returns a +// confirmation id instead. Over the communications system the fact is +// associated with, a code will be sent. This confirmation ID needs to be +// called along with the code to finalize the fact. +func (ud *UserDiscovery)AddFact(fStr string)(string, error){ + f, err := fact.UnstringifyFact(fStr) + if err !=nil{ + return "", errors.WithMessage(err, "Failed to add due to " + + "malformed fact") + } + + return ud.ud.SendRegisterFact(f) +} + +// Confirms a fact first registered via AddFact. The confirmation ID comes from +// AddFact while the code will come over the associated communications system +func (ud *UserDiscovery)ConfirmFact(confirmationID, code string)error{ + return ud.ud.SendConfirmFact(confirmationID, code) +} + +// Removes a previously confirmed fact. Will fail if the passed fact string is +// not well formed or if the fact is not associated with this client. +func (ud *UserDiscovery)RemoveFact(fStr string)error{ + f, err := fact.UnstringifyFact(fStr) + if err !=nil{ + return errors.WithMessage(err, "Failed to remove due to " + + "malformed fact") + } + return ud.ud.RemoveFact(f) +} + +// SearchCallback returns the result of a search +type SearchCallback interface { + Callback(contacts *ContactList, error string) +} + +// Searches for the passed Facts. The factList is the stringification of a +// fact list object, look at /bindings/list.go for more on that object. +// This will reject if that object is malformed. The SearchCallback will return +// a list of contacts, each having the facts it hit against. +// This is NOT intended to be used to search for multiple users at once, that +// can have a privacy reduction. Instead, it is intended to be used to search +// for a user where multiple pieces of information is known. +func (ud UserDiscovery)Search(fl string, callback SearchCallback, + timeoutMS int)error{ + factList, _, err := fact.UnstringifyFactList(fl) + if err!=nil{ + return errors.WithMessage(err, "Failed to search due to " + + "malformed fact list") + } + timeout := time.Duration(timeoutMS)*time.Millisecond + cb := func(cl []contact.Contact, err error){ + var contactList *ContactList + var errStr string + if err==nil{ + contactList = &ContactList{list:cl} + }else{ + errStr = err.Error() + } + callback.Callback(contactList, errStr) + } + return ud.ud.Search(factList, cb, timeout) +} + +// SingleSearchCallback returns the result of a single search +type SingleSearchCallback interface { + Callback(contact *Contact, error string) +} + +// Searches for the passed Facts. The fact is the stringification of a +// fact object, look at /bindings/contact.go for more on that object. +// This will reject if that object is malformed. The SearchCallback will return +// a list of contacts, each having the facts it hit against. +// This only searches for a single fact at a time. It is intended to make some +// simple use cases of the API easier. +func (ud UserDiscovery)SearchSingle(f string, callback SingleSearchCallback, + timeoutMS int)error{ + fObj, err := fact.UnstringifyFact(f) + if err!=nil{ + return errors.WithMessage(err, "Failed to single search due " + + "to malformed fact") + } + timeout := time.Duration(timeoutMS)*time.Millisecond + cb := func(cl []contact.Contact, err error){ + var contact *Contact + var errStr string + if err==nil{ + contact = &Contact{c:&cl[0]} + }else{ + errStr = err.Error() + } + callback.Callback(contact, errStr) + } + return ud.ud.Search([]fact.Fact{fObj}, cb, timeout) +} + +// SingleSearchCallback returns the result of a single search +type LookupCallback interface { + Callback(contact *Contact, error string) +} + +// Looks for the contact object associated with the given userID. The +// id is the byte representation of an id. +// This will reject if that id is malformed. The LookupCallback will return +// the associated contact if it exists. +func (ud UserDiscovery)Lookup(idBytes []byte, callback LookupCallback, + timeoutMS int)error { + + uid, err := id.Unmarshal(idBytes) + if err!=nil{ + return errors.WithMessage(err, "Failed to lookup due to " + + "malformed id") + } + + timeout := time.Duration(timeoutMS)*time.Millisecond + cb := func(cl contact.Contact, err error){ + var contact *Contact + var errStr string + if err==nil{ + contact = &Contact{c:&cl} + }else{ + errStr = err.Error() + } + callback.Callback(contact, errStr) + } + + return ud.ud.Lookup(uid, cb, timeout) + +} \ No newline at end of file diff --git a/ud/addFact.go b/ud/addFact.go index a20626303b0baebe980bfe02777ad2ecebf9af76..11bf3395658b8e07073a44e6dc0ec1c4edd1ea83 100644 --- a/ud/addFact.go +++ b/ud/addFact.go @@ -10,27 +10,37 @@ import ( "gitlab.com/xx_network/comms/connect" "gitlab.com/xx_network/crypto/signature/rsa" "gitlab.com/xx_network/primitives/id" + jww "github.com/spf13/jwalterweatherman" ) type addFactComms interface { SendRegisterFact(host *connect.Host, message *pb.FactRegisterRequest) (*pb.FactRegisterResponse, error) } -func (m *Manager) SendRegisterFact(fact fact.Fact) (*pb.FactRegisterResponse, error) { +// Adds a fact for the user to user discovery. Will only succeed if the +// user is already registered and the system does not have the fact currently +// registered for any user. +// This does not complete the fact registration process, it returns a +// confirmation id instead. Over the communications system the fact is +// associated with, a code will be sent. This confirmation ID needs to be +// called along with the code to finalize the fact. +func (m *Manager) SendRegisterFact(fact fact.Fact) (string, error) { + jww.INFO.Printf("ud.SendRegisterFact(%s)", fact.Stringify()) uid := m.storage.User().GetCryptographicIdentity().GetUserID() return m.addFact(fact, uid, m.comms) } -func (m *Manager) addFact(inFact fact.Fact, uid *id.ID, aFC addFactComms) (*pb.FactRegisterResponse, error) { +func (m *Manager) addFact(inFact fact.Fact, uid *id.ID, aFC addFactComms) (string, error) { + if !m.IsRegistered() { - return nil, errors.New("Failed to add fact: " + + return "", errors.New("Failed to add fact: " + "client is not registered") } // Create a primitives Fact so we can hash it f, err := fact.NewFact(inFact.T, inFact.Fact) if err != nil { - return &pb.FactRegisterResponse{}, err + return "", err } // Create a hash of our fact @@ -39,7 +49,7 @@ func (m *Manager) addFact(inFact fact.Fact, uid *id.ID, aFC addFactComms) (*pb.F // Sign our inFact for putting into the request fsig, err := rsa.Sign(rand.Reader, m.privKey, hash.CMixHash, fhash, nil) if err != nil { - return &pb.FactRegisterResponse{}, err + return "", err } // Create our Fact Removal Request message data @@ -55,6 +65,11 @@ func (m *Manager) addFact(inFact fact.Fact, uid *id.ID, aFC addFactComms) (*pb.F // Send the message response, err := aFC.SendRegisterFact(m.host, &remFactMsg) + confirmationID := "" + if response!=nil{ + confirmationID=response.ConfirmationID + } + // Return the error - return response, err + return confirmationID, err } diff --git a/ud/confirmFact.go b/ud/confirmFact.go index de56805f668bd8deac98620dc9ccd88a20bb5c65..b715a620d19af1c3f3cdb9f4c25599334f70c277 100644 --- a/ud/confirmFact.go +++ b/ud/confirmFact.go @@ -5,13 +5,17 @@ import ( pb "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/xx_network/comms/connect" "gitlab.com/xx_network/comms/messages" + jww "github.com/spf13/jwalterweatherman" ) type confirmFactComm interface { SendConfirmFact(host *connect.Host, message *pb.FactConfirmRequest) (*messages.Ack, error) } +// Confirms a fact first registered via AddFact. The confirmation ID comes from +// AddFact while the code will come over the associated communications system func (m *Manager) SendConfirmFact(confirmationID, code string) error { + jww.INFO.Printf("ud.SendConfirmFact(%s, %s)", confirmationID, code) if err := m.confirmFact(confirmationID, code, m.comms); err!=nil{ return errors.WithMessage(err, "Failed to confirm fact") } diff --git a/ud/lookup.go b/ud/lookup.go index 15eff9f0dd9b0d55ac665eff64bea35ef32bb66c..3d57fcda5023bb6f6d9dfa2efa291a0cf7cfe10f 100644 --- a/ud/lookup.go +++ b/ud/lookup.go @@ -56,7 +56,7 @@ func (m *Manager) lookupProcess(c chan message.Receive, quitCh <-chan struct{}) // Lookup returns the public key of the passed ID as known by the user discovery // system or returns by the timeout. func (m *Manager) Lookup(uid *id.ID, callback lookupCallback, timeout time.Duration) error { - + jww.INFO.Printf("ud.Lookup(%s, %s)", uid, timeout) if !m.IsRegistered(){ return errors.New("Failed to lookup: " + "client is not registered") diff --git a/ud/manager.go b/ud/manager.go index 7cbce3d6b793ea26a9a47fee81afff4137cd7c33..733a2149b262c1198a12347f1e90a82f070cf35a 100644 --- a/ud/manager.go +++ b/ud/manager.go @@ -14,6 +14,7 @@ import ( "gitlab.com/xx_network/crypto/signature/rsa" "gitlab.com/xx_network/primitives/id" "sync" + jww "github.com/spf13/jwalterweatherman" ) type Manager struct { @@ -48,7 +49,7 @@ type Manager struct { // New manager builds a new user discovery manager. It requires that an // updated NDF is available and will error if one is not. func NewManager(client *api.Client)(*Manager, error){ - + jww.INFO.Println("ud.NewManager()") if !client.GetHealth().IsHealthy(){ return nil, errors.New("cannot start UD Manager when network " + "is not healthy") diff --git a/ud/register.go b/ud/register.go index bac83720391e896f154dc8e9e6f9825048dbaf5c..90832d6e5d9a036b260a920a284fe0c897c84e68 100644 --- a/ud/register.go +++ b/ud/register.go @@ -9,14 +9,20 @@ import ( "gitlab.com/xx_network/comms/connect" "gitlab.com/xx_network/comms/messages" "gitlab.com/xx_network/crypto/signature/rsa" + jww "github.com/spf13/jwalterweatherman" ) type registerUserComms interface { SendRegisterUser(*connect.Host, *pb.UDBUserRegistration) (*messages.Ack, error) } -// Register registers a user with user discovery. +// Register registers a user with user discovery. Will return an error if the +// network signatures are malformed or if the username is taken. Usernames cannot +// be changed after registration at this time. Will fail if the user is already +// registered. +// Registration does not go over cmix, it occurs over normal communications func (m *Manager) Register(username string) error { + jww.INFO.Printf("ud.Register(%s)", username) return m.register(username, m.comms) } diff --git a/ud/remove.go b/ud/remove.go index 147cf49642a0852f005eafa1e4ceb78f411dc9bb..d679a1abd479230ae2baa9e6dc59a6d49c07fcd5 100644 --- a/ud/remove.go +++ b/ud/remove.go @@ -6,13 +6,17 @@ import ( "gitlab.com/elixxir/primitives/fact" "gitlab.com/xx_network/comms/connect" "gitlab.com/xx_network/comms/messages" + jww "github.com/spf13/jwalterweatherman" ) type removeFactComms interface { SendDeleteMessage(host *connect.Host, message *mixmessages.FactRemovalRequest) (*messages.Ack, error) } +// Removes a previously confirmed fact. Will fail if the fact is not +// associated with this client. func (m *Manager) RemoveFact(fact fact.Fact) error { + jww.INFO.Printf("ud.RemoveFact(%s)", fact.Stringify()) return m.removeFact(fact, nil) } diff --git a/ud/search.go b/ud/search.go index ad180a64d1ed8c2b17dab80a108e3dbc5d230c04..be2321322ec324942ee9759bb5a5e3e80368fdeb 100644 --- a/ud/search.go +++ b/ud/search.go @@ -16,7 +16,7 @@ import ( "time" ) -type searchCallback func([]contact.Contact, error) +type SearchCallback func([]contact.Contact, error) func (m *Manager) searchProcess(c chan message.Receive, quitCh <-chan struct{}) { for true { @@ -54,8 +54,13 @@ func (m *Manager) searchProcess(c chan message.Receive, quitCh <-chan struct{}) } } -// Search... -func (m *Manager) Search(list fact.FactList, callback searchCallback, timeout time.Duration) error { +// Searches for the passed Facts. The SearchCallback will return +// a list of contacts, each having the facts it hit against. +// This is NOT intended to be used to search for multiple users at once, that +// can have a privacy reduction. Instead, it is intended to be used to search +// for a user where multiple pieces of information is known. +func (m *Manager) Search(list fact.FactList, callback SearchCallback, timeout time.Duration) error { + jww.INFO.Printf("ud.Search(%s, %s)", list.Stringify(), timeout) if !m.IsRegistered() { return errors.New("Failed to search: " + "client is not registered")