libCwtch-go changes for storage engine refactor and profile api changes #47
|
@ -11,4 +11,3 @@ const (
|
||||||
// StatusError is an event response for event.Status signifying a call failed in error, ideally accompanied by a event.Error
|
// StatusError is an event response for event.Status signifying a call failed in error, ideally accompanied by a event.Error
|
||||||
StatusError = "error"
|
StatusError = "error"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
package contact
|
|
||||||
|
|
||||||
import (
|
|
||||||
"cwtch.im/cwtch/model"
|
|
||||||
"cwtch.im/cwtch/peer"
|
|
||||||
"git.openprivacy.ca/cwtch.im/libcwtch-go/features"
|
|
||||||
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Functionality groups some common UI triggered functions for contacts...
|
|
||||||
type Functionality struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
const addContactPrefix = "addcontact"
|
|
||||||
|
|
||||||
const sendMessagePrefix = "sendmessage"
|
|
||||||
|
|
||||||
// FunctionalityGate returns contact.Functionality always
|
|
||||||
func FunctionalityGate(experimentMap map[string]bool) (*Functionality, error) {
|
|
||||||
return new(Functionality), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendMessage handles sending messages to contacts
|
|
||||||
func (pf *Functionality) SendMessage(peer peer.SendMessages, handle string, message string) features.Response {
|
|
||||||
err := peer.SendMessage(handle, message)
|
|
||||||
if err == nil {
|
|
||||||
return features.ConstructResponse(sendMessagePrefix, "success")
|
|
||||||
}
|
|
||||||
return features.ConstructResponse(sendMessagePrefix, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleImportString handles contact import strings
|
|
||||||
func (pf *Functionality) HandleImportString(peer peer.ModifyContactsAndPeers, importString string) features.Response {
|
|
||||||
if tor.IsValidHostname(importString) {
|
|
||||||
if peer.GetContact(importString) == nil {
|
|
||||||
peer.AddContact(importString, importString, model.AuthApproved)
|
|
||||||
// Implicit Peer Attempt
|
|
||||||
peer.PeerWithOnion(importString)
|
|
||||||
return features.ConstructResponse(addContactPrefix, "success")
|
|
||||||
}
|
|
||||||
return features.ConstructResponse(addContactPrefix, "contact_already_exists")
|
|
||||||
}
|
|
||||||
return features.ConstructResponse(addContactPrefix, "invalid_import_string")
|
|
||||||
}
|
|
|
@ -1,124 +0,0 @@
|
||||||
package contact
|
|
||||||
|
|
||||||
import (
|
|
||||||
"cwtch.im/cwtch/model"
|
|
||||||
"git.openprivacy.ca/cwtch.im/libcwtch-go/features"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
const ValidHostname = "openpravyvc6spbd4flzn4g2iqu4sxzsizbtb5aqec25t76dnoo5w7yd"
|
|
||||||
|
|
||||||
type MockPeer struct {
|
|
||||||
hasContact bool
|
|
||||||
addContact bool
|
|
||||||
peerRequest bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m MockPeer) BlockUnknownConnections() {
|
|
||||||
panic("should never be called")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m MockPeer) AllowUnknownConnections() {
|
|
||||||
panic("should never be called")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m MockPeer) GetContacts() []string {
|
|
||||||
panic("should never be called")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m MockPeer) GetContact(s string) *model.PublicProfile {
|
|
||||||
if m.hasContact {
|
|
||||||
return &(model.GenerateNewProfile("").PublicProfile)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m MockPeer) GetContactAttribute(s string, s2 string) (string, bool) {
|
|
||||||
panic("should never be called")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockPeer) AddContact(nick, onion string, authorization model.Authorization) {
|
|
||||||
m.addContact = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m MockPeer) SetContactAuthorization(s string, authorization model.Authorization) error {
|
|
||||||
panic("should never be called")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m MockPeer) SetContactAttribute(s string, s2 string, s3 string) {
|
|
||||||
panic("should never be called")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m MockPeer) DeleteContact(s string) {
|
|
||||||
panic("should never be called")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockPeer) PeerWithOnion(s string) {
|
|
||||||
m.peerRequest = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m MockPeer) JoinServer(s string) error {
|
|
||||||
panic("should never be called")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContactFunctionality_InValidHostname(t *testing.T) {
|
|
||||||
cf, _ := FunctionalityGate(map[string]bool{})
|
|
||||||
|
|
||||||
peer := &MockPeer{
|
|
||||||
hasContact: false,
|
|
||||||
addContact: false,
|
|
||||||
peerRequest: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
response := cf.HandleImportString(peer, "")
|
|
||||||
|
|
||||||
if peer.addContact || peer.peerRequest {
|
|
||||||
t.Fatalf("HandleImportString for a malformed import string should have no resulted in addContact or a peerRequest: %v", peer)
|
|
||||||
}
|
|
||||||
|
|
||||||
if response.Error() != features.ConstructResponse(addContactPrefix, "invalid_import_string").Error() {
|
|
||||||
t.Fatalf("Response to a successful import is malformed: %v", response)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContactFunctionality_ValidHostnameExistingContact(t *testing.T) {
|
|
||||||
cf, _ := FunctionalityGate(map[string]bool{})
|
|
||||||
|
|
||||||
peer := &MockPeer{
|
|
||||||
hasContact: true,
|
|
||||||
addContact: false,
|
|
||||||
peerRequest: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
response := cf.HandleImportString(peer, ValidHostname)
|
|
||||||
|
|
||||||
if peer.addContact || peer.peerRequest {
|
|
||||||
t.Fatalf("HandleImportString for a valid string should not call addContact or a peerRequest when the contact already exists: %v", peer)
|
|
||||||
}
|
|
||||||
|
|
||||||
if response.Error() != features.ConstructResponse(addContactPrefix, "contact_already_exists").Error() {
|
|
||||||
t.Fatalf("Response to a successful import is malformed: %v", response)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContactFunctionality_ValidHostnameUnknownContact(t *testing.T) {
|
|
||||||
cf, _ := FunctionalityGate(map[string]bool{})
|
|
||||||
|
|
||||||
peer := &MockPeer{
|
|
||||||
hasContact: false,
|
|
||||||
addContact: false,
|
|
||||||
peerRequest: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
response := cf.HandleImportString(peer, ValidHostname)
|
|
||||||
|
|
||||||
if peer.addContact && peer.peerRequest {
|
|
||||||
if response.Error() != features.ConstructResponse(addContactPrefix, "success").Error() {
|
|
||||||
t.Fatalf("Response to a successful import is malformed: %v", response)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
t.Fatalf("HandleImportString for a valid import string should have resulted in addContact or a peerRequest: %v", peer)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,21 +3,14 @@ package groups
|
||||||
import (
|
import (
|
||||||
"cwtch.im/cwtch/event"
|
"cwtch.im/cwtch/event"
|
||||||
"cwtch.im/cwtch/model"
|
"cwtch.im/cwtch/model"
|
||||||
|
"cwtch.im/cwtch/model/attr"
|
||||||
"cwtch.im/cwtch/peer"
|
"cwtch.im/cwtch/peer"
|
||||||
"encoding/base64"
|
"cwtch.im/cwtch/protocol/connections"
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.openprivacy.ca/cwtch.im/libcwtch-go/features"
|
|
||||||
"git.openprivacy.ca/openprivacy/log"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const serverPrefix = "server:"
|
|
||||||
const tofuBundlePrefix = "tofubundle:"
|
|
||||||
const groupPrefix = "torv3"
|
|
||||||
const groupExperiment = "tapir-groups-experiment"
|
const groupExperiment = "tapir-groups-experiment"
|
||||||
|
|
||||||
const importBundlePrefix = "importBundle"
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// ServerList is a json encoded list of servers
|
// ServerList is a json encoded list of servers
|
||||||
ServerList = event.Field("ServerList")
|
ServerList = event.Field("ServerList")
|
||||||
|
@ -28,12 +21,6 @@ const (
|
||||||
UpdateServerInfo = event.Type("UpdateServerInfo")
|
UpdateServerInfo = event.Type("UpdateServerInfo")
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReadServerInfo is a meta-interface for reading information about servers..
|
|
||||||
type ReadServerInfo interface {
|
|
||||||
peer.ReadContacts
|
|
||||||
peer.ReadServers
|
|
||||||
}
|
|
||||||
|
|
||||||
// GroupFunctionality provides experiment gated server functionality
|
// GroupFunctionality provides experiment gated server functionality
|
||||||
type GroupFunctionality struct {
|
type GroupFunctionality struct {
|
||||||
}
|
}
|
||||||
|
@ -46,27 +33,8 @@ func ExperimentGate(experimentMap map[string]bool) (*GroupFunctionality, error)
|
||||||
return nil, fmt.Errorf("gated by %v", groupExperiment)
|
return nil, fmt.Errorf("gated by %v", groupExperiment)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendMessage is a deprecated api
|
|
||||||
func (gf *GroupFunctionality) SendMessage(peer peer.CwtchPeer, handle string, message string) (string, error) {
|
|
||||||
// TODO this auto accepting behaviour needs some thinking through
|
|
||||||
if !peer.GetGroup(handle).Accepted {
|
|
||||||
err := peer.AcceptInvite(handle)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("tried to mark a nonexistent group as existed. bad!")
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", peer.SendMessage(handle, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidPrefix returns true if an import string contains a prefix that indicates it contains information about a
|
|
||||||
// server or a group
|
|
||||||
func (gf *GroupFunctionality) ValidPrefix(importString string) bool {
|
|
||||||
return strings.HasPrefix(importString, tofuBundlePrefix) || strings.HasPrefix(importString, serverPrefix) || strings.HasPrefix(importString, groupPrefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetServerInfoList compiles all the information the UI might need regarding all servers..
|
// GetServerInfoList compiles all the information the UI might need regarding all servers..
|
||||||
func (gf *GroupFunctionality) GetServerInfoList(profile ReadServerInfo) []Server {
|
func (gf *GroupFunctionality) GetServerInfoList(profile peer.CwtchPeer) []Server {
|
||||||
var servers []Server
|
var servers []Server
|
||||||
for _, server := range profile.GetServers() {
|
for _, server := range profile.GetServers() {
|
||||||
servers = append(servers, gf.GetServerInfo(server, profile))
|
servers = append(servers, gf.GetServerInfo(server, profile))
|
||||||
|
@ -76,54 +44,15 @@ func (gf *GroupFunctionality) GetServerInfoList(profile ReadServerInfo) []Server
|
||||||
|
|
||||||
// GetServerInfo compiles all the information the UI might need regarding a particular server including any verified
|
// GetServerInfo compiles all the information the UI might need regarding a particular server including any verified
|
||||||
// cryptographic keys
|
// cryptographic keys
|
||||||
func (gf *GroupFunctionality) GetServerInfo(serverOnion string, profile peer.ReadContacts) Server {
|
func (gf *GroupFunctionality) GetServerInfo(serverOnion string, profile peer.CwtchPeer) Server {
|
||||||
serverInfo := profile.GetContact(serverOnion)
|
serverInfo, _ := profile.FetchConversationInfo(serverOnion)
|
||||||
keyTypes := []model.KeyType{model.KeyTypeServerOnion, model.KeyTypeTokenOnion, model.KeyTypePrivacyPass}
|
keyTypes := []model.KeyType{model.KeyTypeServerOnion, model.KeyTypeTokenOnion, model.KeyTypePrivacyPass}
|
||||||
var serverKeys []ServerKey
|
var serverKeys []ServerKey
|
||||||
|
|
||||||
for _, keyType := range keyTypes {
|
for _, keyType := range keyTypes {
|
||||||
if key, has := serverInfo.GetAttribute(string(keyType)); has {
|
if key, has := serverInfo.GetAttribute(attr.PublicScope, attr.ServerKeyZone, string(keyType)); has {
|
||||||
serverKeys = append(serverKeys, ServerKey{Type: string(keyType), Key: key})
|
serverKeys = append(serverKeys, ServerKey{Type: string(keyType), Key: key})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Server{Onion: serverOnion, Status: serverInfo.State, Keys: serverKeys}
|
return Server{Onion: serverOnion, Status: connections.ConnectionStateName[profile.GetPeerState(serverInfo.Handle)], Keys: serverKeys}
|
||||||
}
|
|
||||||
|
|
||||||
// HandleImportString handles import strings for groups and servers
|
|
||||||
func (gf *GroupFunctionality) HandleImportString(peer peer.CwtchPeer, importString string) error {
|
|
||||||
if strings.HasPrefix(importString, tofuBundlePrefix) {
|
|
||||||
bundle := strings.Split(importString, "||")
|
|
||||||
if len(bundle) == 2 {
|
|
||||||
err := gf.HandleImportString(peer, bundle[0][len(tofuBundlePrefix):])
|
|
||||||
// if the server import failed then abort the whole process..
|
|
||||||
if err != nil && !strings.HasSuffix(err.Error(), "success") {
|
|
||||||
return features.ConstructResponse(importBundlePrefix, err.Error())
|
|
||||||
}
|
|
||||||
return gf.HandleImportString(peer, bundle[1])
|
|
||||||
}
|
|
||||||
} else if strings.HasPrefix(importString, serverPrefix) {
|
|
||||||
// Server Key Bundles are prefixed with
|
|
||||||
bundle, err := base64.StdEncoding.DecodeString(importString[len(serverPrefix):])
|
|
||||||
if err == nil {
|
|
||||||
serverOnion, err := peer.AddServer(string(bundle))
|
|
||||||
if err != nil {
|
|
||||||
return features.ConstructResponse(importBundlePrefix, err.Error())
|
|
||||||
}
|
|
||||||
peer.JoinServer(serverOnion)
|
|
||||||
return features.ConstructResponse(importBundlePrefix, "success")
|
|
||||||
}
|
|
||||||
return features.ConstructResponse(importBundlePrefix, err.Error())
|
|
||||||
} else if strings.HasPrefix(importString, groupPrefix) {
|
|
||||||
//eg: torv3JFDWkXExBsZLkjvfkkuAxHsiLGZBk0bvoeJID9ItYnU=EsEBCiBhOWJhZDU1OTQ0NWI3YmM2N2YxYTM5YjkzMTNmNTczNRIgpHeNaG+6jy750eDhwLO39UX4f2xs0irK/M3P6mDSYQIaOTJjM2ttb29ibnlnaGoyenc2cHd2N2Q1N3l6bGQ3NTNhdW8zdWdhdWV6enB2ZmFrM2FoYzRiZHlkCiJAdVSSVgsksceIfHe41OJu9ZFHO8Kwv3G6F5OK3Hw4qZ6hn6SiZjtmJlJezoBH0voZlCahOU7jCOg+dsENndZxAA==
|
|
||||||
if gid, err := peer.ImportGroup(importString); err != nil {
|
|
||||||
return features.ConstructResponse(importBundlePrefix, err.Error())
|
|
||||||
} else {
|
|
||||||
// Auto accept the group here.
|
|
||||||
if peer.AcceptInvite(gid) != nil {
|
|
||||||
log.Errorf("Error accepting invite: %v", err)
|
|
||||||
}
|
|
||||||
return features.ConstructResponse(importBundlePrefix, "success")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return features.ConstructResponse(importBundlePrefix, "invalid_group_invite_prefix")
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,22 +2,6 @@ package groups
|
||||||
|
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestGroupFunctionality_ValidPrefix(t *testing.T) {
|
|
||||||
gf, _ := ExperimentGate(map[string]bool{groupExperiment: true})
|
|
||||||
if gf.ValidPrefix("torv3blahblahblah") == false {
|
|
||||||
t.Fatalf("torv3 should be a valid prefix")
|
|
||||||
}
|
|
||||||
if gf.ValidPrefix("tofubundle:32432423||3242342") == false {
|
|
||||||
t.Fatalf("tofubundle should be a valid prefix")
|
|
||||||
}
|
|
||||||
if gf.ValidPrefix("server:23541233t") == false {
|
|
||||||
t.Fatalf("server should be a valid prefix")
|
|
||||||
}
|
|
||||||
if gf.ValidPrefix("alice!24234") == true {
|
|
||||||
t.Fatalf("alice should be an invalid predix")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGroupFunctionality_IsEnabled(t *testing.T) {
|
func TestGroupFunctionality_IsEnabled(t *testing.T) {
|
||||||
|
|
||||||
_, err := ExperimentGate(map[string]bool{})
|
_, err := ExperimentGate(map[string]bool{})
|
||||||
|
|
|
@ -15,10 +15,10 @@ import (
|
||||||
const serversExperiment = "servers-experiment"
|
const serversExperiment = "servers-experiment"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ZeroServersLoaded = event.Type("ZeroServersLoaded")
|
ZeroServersLoaded = event.Type("ZeroServersLoaded")
|
||||||
NewServer = event.Type("NewServer")
|
NewServer = event.Type("NewServer")
|
||||||
ServerIntentUpdate = event.Type("ServerIntentUpdate")
|
ServerIntentUpdate = event.Type("ServerIntentUpdate")
|
||||||
ServerDeleted = event.Type("ServerDeleted")
|
ServerDeleted = event.Type("ServerDeleted")
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -31,12 +31,12 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServerInfo struct {
|
type ServerInfo struct {
|
||||||
Onion string
|
Onion string
|
||||||
ServerBundle string
|
ServerBundle string
|
||||||
Autostart bool
|
Autostart bool
|
||||||
Running bool
|
Running bool
|
||||||
Description string
|
Description string
|
||||||
StorageType string
|
StorageType string
|
||||||
}
|
}
|
||||||
|
|
||||||
var lock sync.Mutex
|
var lock sync.Mutex
|
||||||
|
@ -72,7 +72,7 @@ type ServersFunctionality struct {
|
||||||
func ExperimentGate(experimentMap map[string]bool) (*ServersFunctionality, error) {
|
func ExperimentGate(experimentMap map[string]bool) (*ServersFunctionality, error) {
|
||||||
if experimentMap[serversExperiment] {
|
if experimentMap[serversExperiment] {
|
||||||
lock.Lock()
|
lock.Lock()
|
||||||
defer lock.Unlock()
|
defer lock.Unlock()
|
||||||
return &ServersFunctionality{}, nil
|
return &ServersFunctionality{}, nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("gated by %v", serversExperiment)
|
return nil, fmt.Errorf("gated by %v", serversExperiment)
|
||||||
|
|
7
go.mod
|
@ -3,11 +3,12 @@ module git.openprivacy.ca/cwtch.im/libcwtch-go
|
||||||
go 1.15
|
go 1.15
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cwtch.im/cwtch v0.13.3
|
cwtch.im/cwtch v0.14.0
|
||||||
git.openprivacy.ca/cwtch.im/server v1.3.4
|
git.openprivacy.ca/cwtch.im/server v1.4.0
|
||||||
git.openprivacy.ca/openprivacy/connectivity v1.5.0
|
git.openprivacy.ca/openprivacy/connectivity v1.5.0
|
||||||
git.openprivacy.ca/openprivacy/log v1.0.3
|
git.openprivacy.ca/openprivacy/log v1.0.3
|
||||||
|
github.com/mutecomm/go-sqlcipher/v4 v4.4.2 // indirect
|
||||||
golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 // indirect
|
golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 // indirect
|
||||||
golang.org/x/mod v0.5.0 // indirect
|
golang.org/x/mod v0.5.0 // indirect
|
||||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect
|
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect
|
||||||
)
|
)
|
525
go.sum
|
@ -1,16 +1,30 @@
|
||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||||
|
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||||
|
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||||
|
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||||
|
cloud.google.com/go v0.46.3 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y=
|
||||||
|
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||||
|
cloud.google.com/go/bigquery v1.0.1 h1:hL+ycaJpVE9M7nLoiXb/Pn10ENE2u+oddxbD8uu0ZVU=
|
||||||
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
|
cloud.google.com/go/datastore v1.0.0 h1:Kt+gOPPp2LEPWp8CSfxhsM8ik9CcyE/gYu+0r+RnZvM=
|
||||||
|
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||||
|
cloud.google.com/go/firestore v1.1.0 h1:9x7Bx0A9R5/M9jibeJeZWqjeVEIxYW9fZYqB9a70/bY=
|
||||||
|
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||||
|
cloud.google.com/go/pubsub v1.0.1 h1:W9tAK3E57P75u0XLLR82LZyw8VpAnhmyTOxW9qzmyj8=
|
||||||
|
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||||
|
cloud.google.com/go/storage v1.0.0 h1:VV2nUM3wwLLGh9lSABFgZMjInyUbJeaRSE64WuAIQ+4=
|
||||||
|
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||||
cwtch.im/cwtch v0.12.2/go.mod h1:QpTkQK7MqNt0dQK9/pBk5VpkvFhy6xuoxJIn401B8fM=
|
cwtch.im/cwtch v0.12.2/go.mod h1:QpTkQK7MqNt0dQK9/pBk5VpkvFhy6xuoxJIn401B8fM=
|
||||||
cwtch.im/cwtch v0.13.3 h1:YMqb18msXmBW7gHAn2X/QgljoDag/HgWe6qdz9OgwlY=
|
cwtch.im/cwtch v0.14.0 h1:y/XiTjWzKm3Imuln03MVOy1YVxpdaX8twkks26LJwhg=
|
||||||
cwtch.im/cwtch v0.13.3/go.mod h1:QpTkQK7MqNt0dQK9/pBk5VpkvFhy6xuoxJIn401B8fM=
|
cwtch.im/cwtch v0.14.0/go.mod h1:/fLuoYLY/7JHw6RojFojpd245CiOcU24QpWqzh9FRDI=
|
||||||
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY=
|
||||||
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
|
filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
|
||||||
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
||||||
git.openprivacy.ca/cwtch.im/server v1.3.1 h1:Kt4TnlGxGPk1KTjvs1cXtnFWDx+hYqu8w+2eIaqt+F4=
|
git.openprivacy.ca/cwtch.im/server v1.4.0 h1:Gok/E6qFlpJI42gL+hWNq9QurtgY/1R2/xz+j40ssPY=
|
||||||
git.openprivacy.ca/cwtch.im/server v1.3.1/go.mod h1:gps6glXDt/ra66Do491csrm0TwatAc2lMLOAKCkh5Vw=
|
git.openprivacy.ca/cwtch.im/server v1.4.0/go.mod h1:zOtZjF0Vkirtjwr2O/23hOeZzkDwNS7XZ/pCVeKZQAA=
|
||||||
git.openprivacy.ca/cwtch.im/server v1.3.2 h1:dUNMM88IWER6cmgCfhekahkef7laH597hqTp9CE2tYg=
|
|
||||||
git.openprivacy.ca/cwtch.im/server v1.3.2/go.mod h1:gps6glXDt/ra66Do491csrm0TwatAc2lMLOAKCkh5Vw=
|
|
||||||
git.openprivacy.ca/cwtch.im/server v1.3.3 h1:LhHXlFAKzl2zM6Jd2Z3w3qMZM3UnRUA0qXWrlRBvKv8=
|
|
||||||
git.openprivacy.ca/cwtch.im/server v1.3.3/go.mod h1:gps6glXDt/ra66Do491csrm0TwatAc2lMLOAKCkh5Vw=
|
|
||||||
git.openprivacy.ca/cwtch.im/server v1.3.4 h1:l/iK1/PO2fZfhrTLL4FKi2ROT01etaY4YXMnqQ+eH90=
|
|
||||||
git.openprivacy.ca/cwtch.im/server v1.3.4/go.mod h1:gps6glXDt/ra66Do491csrm0TwatAc2lMLOAKCkh5Vw=
|
|
||||||
git.openprivacy.ca/cwtch.im/tapir v0.4.9 h1:LXonlztwvI1F1++0IyomIcDH1/Bxzo+oN8YjGonNvjM=
|
git.openprivacy.ca/cwtch.im/tapir v0.4.9 h1:LXonlztwvI1F1++0IyomIcDH1/Bxzo+oN8YjGonNvjM=
|
||||||
git.openprivacy.ca/cwtch.im/tapir v0.4.9/go.mod h1:p4bHo3DAO8wwimU6JAeZXbfPQ4jnoA2bV+4YvknWTNQ=
|
git.openprivacy.ca/cwtch.im/tapir v0.4.9/go.mod h1:p4bHo3DAO8wwimU6JAeZXbfPQ4jnoA2bV+4YvknWTNQ=
|
||||||
git.openprivacy.ca/openprivacy/bine v0.0.4 h1:CO7EkGyz+jegZ4ap8g5NWRuDHA/56KKvGySR6OBPW+c=
|
git.openprivacy.ca/openprivacy/bine v0.0.4 h1:CO7EkGyz+jegZ4ap8g5NWRuDHA/56KKvGySR6OBPW+c=
|
||||||
|
@ -20,92 +34,563 @@ git.openprivacy.ca/openprivacy/connectivity v1.5.0/go.mod h1:UjQiGBnWbotmBzIw59B
|
||||||
git.openprivacy.ca/openprivacy/log v1.0.2/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
git.openprivacy.ca/openprivacy/log v1.0.2/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
||||||
git.openprivacy.ca/openprivacy/log v1.0.3 h1:E/PMm4LY+Q9s3aDpfySfEDq/vYQontlvNj/scrPaga0=
|
git.openprivacy.ca/openprivacy/log v1.0.3 h1:E/PMm4LY+Q9s3aDpfySfEDq/vYQontlvNj/scrPaga0=
|
||||||
git.openprivacy.ca/openprivacy/log v1.0.3/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
git.openprivacy.ca/openprivacy/log v1.0.3/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
||||||
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
|
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||||
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
|
||||||
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
|
||||||
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA=
|
||||||
|
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||||
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
|
||||||
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||||
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to=
|
||||||
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
|
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
|
||||||
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
|
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
|
||||||
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
|
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c h1:+0HFd5KSZ/mm3JmhmrDukiId5iR6w4+BdFtfSy4yWIc=
|
||||||
|
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||||
|
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||||
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
|
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
||||||
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
|
||||||
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=
|
||||||
|
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||||
|
github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ=
|
||||||
|
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
|
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
|
||||||
|
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
|
||||||
|
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
github.com/cucumber/gherkin-go/v19 v19.0.3 h1:mMSKu1077ffLbTJULUfM5HPokgeBcIGboyeNUof1MdE=
|
||||||
|
github.com/cucumber/gherkin-go/v19 v19.0.3/go.mod h1:jY/NP6jUtRSArQQJ5h1FXOUgk5fZK24qtE7vKi776Vw=
|
||||||
|
github.com/cucumber/godog v0.12.0 h1:xVOc9ML+1joT0CqcdQTpfXiT7G1hOLbCmlUnYOyJ80w=
|
||||||
|
github.com/cucumber/godog v0.12.0/go.mod h1:u6SD7IXC49dLpPN35kal0oYEjsXZWee4pW6Tm9t5pIc=
|
||||||
|
github.com/cucumber/messages-go/v16 v16.0.0/go.mod h1:EJcyR5Mm5ZuDsKJnT2N9KRnBK30BGjtYotDKpwQ0v6g=
|
||||||
|
github.com/cucumber/messages-go/v16 v16.0.1 h1:fvkpwsLgnIm0qugftrw2YwNlio+ABe2Iu94Ap8GMYIY=
|
||||||
|
github.com/cucumber/messages-go/v16 v16.0.1/go.mod h1:EJcyR5Mm5ZuDsKJnT2N9KRnBK30BGjtYotDKpwQ0v6g=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||||
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954 h1:RMLoZVzv4GliuWafOuPuQDKSm1SJph7uCRnnS61JAn4=
|
||||||
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
|
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||||
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
|
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||||
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0=
|
||||||
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
|
github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
|
||||||
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
|
github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
|
||||||
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
|
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||||
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||||
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
|
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||||
|
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
|
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
|
||||||
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
|
||||||
|
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
|
||||||
|
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||||
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
|
||||||
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||||
|
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
|
||||||
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||||
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.9.0 h1:bM6ZAFZmc/wPFaRDi0d5L7hGEZEx/2u+Tmr2evNHDiI=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||||
github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is=
|
github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is=
|
||||||
github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s=
|
github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s=
|
||||||
github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc=
|
github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc=
|
||||||
github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o=
|
github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o=
|
||||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
github.com/hashicorp/consul/api v1.1.0 h1:BNQPM9ytxj6jbjjdRPioQ94T6YXriSopn0i8COv6SRA=
|
||||||
|
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||||
|
github.com/hashicorp/consul/sdk v0.1.1 h1:LnuDWGNsoajlhGyHJvuWW6FVqRl8JOTPqS6CPTsYjhY=
|
||||||
|
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||||
|
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||||
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
|
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||||
|
github.com/hashicorp/go-immutable-radix v1.3.0 h1:8exGP7ego3OmkfksihtSouGMZ+hQrhxx+FVELeXpVPE=
|
||||||
|
github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||||
|
github.com/hashicorp/go-memdb v1.3.0 h1:xdXq34gBOMEloa9rlGStLxmfX/dyIK8htOv36dQUwHU=
|
||||||
|
github.com/hashicorp/go-memdb v1.3.0/go.mod h1:Mluclgwib3R93Hk5fxEfiRhB+6Dar64wWh71LpNSe3g=
|
||||||
|
github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4=
|
||||||
|
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||||
|
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
|
||||||
|
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||||
|
github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI=
|
||||||
|
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs=
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||||
|
github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE=
|
||||||
|
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw=
|
||||||
|
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||||
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
|
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
|
||||||
|
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||||
|
github.com/hashicorp/mdns v1.0.0 h1:WhIgCr5a7AaVH6jPUwjtRuuE7/RDufnUvzIr48smyxs=
|
||||||
|
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||||
|
github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M=
|
||||||
|
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||||
|
github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0=
|
||||||
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
|
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI=
|
||||||
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
||||||
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
|
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
|
||||||
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc=
|
||||||
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
|
github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g=
|
||||||
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
|
github.com/kisielk/errcheck v1.1.0 h1:ZqfnKyx9KGpRcW04j5nnPDgRgoXUeLh2YFBeFzphcA0=
|
||||||
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
|
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
|
||||||
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
|
github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||||
|
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
|
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||||
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
|
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
|
||||||
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/mattn/go-sqlite3 v1.14.7 h1:fxWBnXkxfM6sRiuH3bqJ4CfzZojMOLVc0UTsTglEghA=
|
github.com/mattn/go-sqlite3 v1.14.7 h1:fxWBnXkxfM6sRiuH3bqJ4CfzZojMOLVc0UTsTglEghA=
|
||||||
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA=
|
||||||
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0=
|
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0=
|
||||||
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM=
|
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM=
|
||||||
|
github.com/mitchellh/cli v1.0.0 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y=
|
||||||
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
|
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
|
||||||
|
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||||
|
github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc=
|
||||||
|
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||||
|
github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=
|
||||||
|
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||||
|
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||||
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/mutecomm/go-sqlcipher/v4 v4.4.2 h1:eM10bFtI4UvibIsKr10/QT7Yfz+NADfjZYh0GKrXUNc=
|
||||||
|
github.com/mutecomm/go-sqlcipher/v4 v4.4.2/go.mod h1:mF2UmIpBnzFeBdu/ypTDb/LdbS0nk0dfSN1WUsWTjMA=
|
||||||
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc=
|
||||||
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
|
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||||
|
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||||
|
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||||
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||||
|
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||||
|
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||||
|
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||||
|
github.com/onsi/ginkgo/v2 v2.0.0-rc2 h1:2ukZwTHG/SAlJe4mm5xTdcUYH7IRvldIXhukE1pQBeY=
|
||||||
|
github.com/onsi/ginkgo/v2 v2.0.0-rc2/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||||
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
|
github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE=
|
||||||
|
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||||
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs=
|
||||||
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
|
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||||
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w=
|
||||||
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
|
github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
|
||||||
|
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
|
github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
|
||||||
|
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
|
github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA=
|
||||||
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af h1:gu+uRPtBe88sKxUCEXRoeCvVG90TJmwhiqRpvdhQFng=
|
||||||
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||||
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f h1:UFr9zpz4xgTnIE5yIMtWAMngCdZ9p/+q6lTbgelo80M=
|
||||||
|
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
|
||||||
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
|
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
|
||||||
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||||
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
|
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||||
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
|
github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
|
||||||
|
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||||
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
|
||||||
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
|
||||||
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
|
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||||
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
|
github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
|
||||||
|
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||||
|
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||||
|
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
|
||||||
|
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||||
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/struCoder/pidusage v0.2.1 h1:dFiEgUDkubeIj0XA1NpQ6+8LQmKrLi7NiIQl86E6BoY=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/struCoder/pidusage v0.2.1/go.mod h1:bewtP2KUA1TBUyza5+/PCpSQ6sc/H6jJbIKAzqW86BA=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||||
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
|
||||||
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
|
||||||
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.3.5 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs=
|
github.com/yuin/goldmark v1.3.5 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
|
go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
|
||||||
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||||
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
|
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
|
||||||
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
|
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
|
||||||
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
|
||||||
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
|
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
|
||||||
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee h1:4yd7jl+vXjalO5ztz6Vc1VADv+S/80LGJmyl1ROJ2AI=
|
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee h1:4yd7jl+vXjalO5ztz6Vc1VADv+S/80LGJmyl1ROJ2AI=
|
||||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||||
|
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||||
|
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8Xb6W51gRwfDBhkdcaw=
|
||||||
|
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 h1:3In5TnfvnuXTF/uflgpYxSCEGP2NdYT37KsPh3VjZYU=
|
golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 h1:3In5TnfvnuXTF/uflgpYxSCEGP2NdYT37KsPh3VjZYU=
|
||||||
golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554/go.mod h1:jFTmtFYCV0MFtXBU+J5V/+5AUeVS0ON/0WkE/KSrl6E=
|
golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554/go.mod h1:jFTmtFYCV0MFtXBU+J5V/+5AUeVS0ON/0WkE/KSrl6E=
|
||||||
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.5.0 h1:UG21uOlmZabA4fW5i7ZX6bjw1xELEGg/ZLgZq9auk/Q=
|
golang.org/x/mod v0.5.0 h1:UG21uOlmZabA4fW5i7ZX6bjw1xELEGg/ZLgZq9auk/Q=
|
||||||
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
|
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=
|
||||||
|
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k=
|
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k=
|
||||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||||
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA=
|
golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA=
|
||||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
|
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||||
|
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.13.0 h1:Q3Ui3V3/CVinFWFiW39Iw0kMuVrRzYX0wN6OPFp0lTA=
|
||||||
|
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
|
||||||
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||||
|
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4=
|
||||||
|
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
|
google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8=
|
||||||
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||||
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
|
||||||
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
||||||
|
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
|
gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI=
|
||||||
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
|
rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE=
|
||||||
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
|
|
449
lib.go
|
@ -8,8 +8,11 @@ import "C"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
constants2 "cwtch.im/cwtch/model/constants"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
// Import SQL Cipher
|
||||||
|
_ "github.com/mutecomm/go-sqlcipher/v4"
|
||||||
"os/user"
|
"os/user"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -23,7 +26,6 @@ import (
|
||||||
"cwtch.im/cwtch/model/attr"
|
"cwtch.im/cwtch/model/attr"
|
||||||
"cwtch.im/cwtch/peer"
|
"cwtch.im/cwtch/peer"
|
||||||
"git.openprivacy.ca/cwtch.im/libcwtch-go/constants"
|
"git.openprivacy.ca/cwtch.im/libcwtch-go/constants"
|
||||||
contact "git.openprivacy.ca/cwtch.im/libcwtch-go/features/contacts"
|
|
||||||
"git.openprivacy.ca/cwtch.im/libcwtch-go/features/groups"
|
"git.openprivacy.ca/cwtch.im/libcwtch-go/features/groups"
|
||||||
"git.openprivacy.ca/cwtch.im/libcwtch-go/features/servers"
|
"git.openprivacy.ca/cwtch.im/libcwtch-go/features/servers"
|
||||||
"git.openprivacy.ca/cwtch.im/server"
|
"git.openprivacy.ca/cwtch.im/server"
|
||||||
|
@ -195,7 +197,7 @@ func _startCwtch(appDir string, torPath string) {
|
||||||
|
|
||||||
peer.DefaultEventsToHandle = []event.Type{
|
peer.DefaultEventsToHandle = []event.Type{
|
||||||
event.EncryptedGroupMessage,
|
event.EncryptedGroupMessage,
|
||||||
event.NewMessageFromPeer,
|
event.NewMessageFromPeerEngine,
|
||||||
event.PeerAcknowledgement,
|
event.PeerAcknowledgement,
|
||||||
event.PeerError,
|
event.PeerError,
|
||||||
event.SendMessageToPeerError,
|
event.SendMessageToPeerError,
|
||||||
|
@ -246,34 +248,25 @@ func ReconnectCwtchForeground() {
|
||||||
}
|
}
|
||||||
|
|
||||||
settings := utils.ReadGlobalSettings()
|
settings := utils.ReadGlobalSettings()
|
||||||
|
groupHandler, _ := groups.ExperimentGate(settings.Experiments)
|
||||||
for _, profileOnion := range peerList {
|
for _, profileOnion := range peerList {
|
||||||
// fix peerpeercontact message counts
|
// fix peerpeercontact message counts
|
||||||
contactList := application.GetPeer(profileOnion).GetContacts()
|
profile := application.GetPeer(profileOnion)
|
||||||
for _, handle := range contactList {
|
conversations, _ := profile.FetchConversations()
|
||||||
totalMessages := application.GetPeer(profileOnion).GetContact(handle).Timeline.Len() + len(application.GetPeer(profileOnion).GetContact(handle).UnacknowledgedMessages)
|
for _, conversation := range conversations {
|
||||||
eventHandler.Push(event.NewEvent(event.MessageCounterResync, map[event.Field]string{
|
if (conversation.IsGroup() && groupHandler != nil) || conversation.IsServer() == false {
|
||||||
dan marked this conversation as resolved
Outdated
|
|||||||
event.Identity: profileOnion,
|
totalMessages, _ := profile.GetChannelMessageCount(conversation.ID, 0)
|
||||||
event.RemotePeer: handle,
|
|
||||||
event.Data: strconv.Itoa(totalMessages),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Group Experiment Refresh
|
|
||||||
groupHandler, err := groups.ExperimentGate(settings.Experiments)
|
|
||||||
if err == nil {
|
|
||||||
// fix peergroupcontact message counts
|
|
||||||
groupList := application.GetPeer(profileOnion).GetGroups()
|
|
||||||
for _, groupID := range groupList {
|
|
||||||
totalMessages := len(application.GetPeer(profileOnion).GetGroup(groupID).GetTimeline())
|
|
||||||
eventHandler.Push(event.NewEvent(event.MessageCounterResync, map[event.Field]string{
|
eventHandler.Push(event.NewEvent(event.MessageCounterResync, map[event.Field]string{
|
||||||
dan marked this conversation as resolved
dan
commented
we're still doing it for groups we're still doing it for groups
|
|||||||
event.Identity: profileOnion,
|
event.Identity: profileOnion,
|
||||||
event.GroupID: groupID,
|
event.ConversationID: strconv.Itoa(conversation.ID),
|
||||||
event.Data: strconv.Itoa(totalMessages),
|
event.Data: strconv.Itoa(totalMessages),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
serverListForOnion := groupHandler.GetServerInfoList(application.GetPeer(profileOnion))
|
// Group Experiment Server Refresh
|
||||||
|
if groupHandler != nil {
|
||||||
|
serverListForOnion := groupHandler.GetServerInfoList(profile)
|
||||||
serversListBytes, _ := json.Marshal(serverListForOnion)
|
serversListBytes, _ := json.Marshal(serverListForOnion)
|
||||||
eventHandler.Push(event.NewEvent(groups.UpdateServerInfo, map[event.Field]string{"ProfileOnion": profileOnion, groups.ServerList: string(serversListBytes)}))
|
eventHandler.Push(event.NewEvent(groups.UpdateServerInfo, map[event.Field]string{"ProfileOnion": profileOnion, groups.ServerList: string(serversListBytes)}))
|
||||||
}
|
}
|
||||||
|
@ -412,25 +405,6 @@ func SendProfileEvent(onion string, eventJson string) {
|
||||||
// We need to update the local cache
|
// We need to update the local cache
|
||||||
// Ideally I think this would be pushed back into Cwtch
|
// Ideally I think this would be pushed back into Cwtch
|
||||||
switch new_event.EventType {
|
switch new_event.EventType {
|
||||||
// DEPRECATED: use ImportBundle
|
|
||||||
case AddContact:
|
|
||||||
pf, _ := contact.FunctionalityGate(utils.ReadGlobalSettings().Experiments)
|
|
||||||
err := pf.HandleImportString(peer, new_event.Data[ImportString])
|
|
||||||
eventHandler.Push(event.NewEvent(event.AppError, map[event.Field]string{event.Data: err.Error()}))
|
|
||||||
// DEPRECATED: use SetProfileAttribute()
|
|
||||||
case event.SetAttribute:
|
|
||||||
log.Errorf("SetAttribute is deprecated.")
|
|
||||||
// DEPRECATED: use SetContactAttribute()
|
|
||||||
case event.SetPeerAttribute:
|
|
||||||
peer.SetContactAttribute(new_event.Data[event.RemotePeer], new_event.Data[event.Key], new_event.Data[event.Data])
|
|
||||||
// DEPRECATED: use AcceptContact() and BlockContact()
|
|
||||||
case event.SetPeerAuthorization:
|
|
||||||
peer.SetContactAuthorization(new_event.Data[event.RemotePeer], model.Authorization(new_event.Data[event.Authorization]))
|
|
||||||
|
|
||||||
// If approved (e.g. after an unblock) we want to kick off peering again...
|
|
||||||
if model.Authorization(new_event.Data[event.Authorization]) == model.AuthApproved {
|
|
||||||
peer.PeerWithOnion(new_event.Data[event.RemotePeer])
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
// rebroadcast catch all
|
// rebroadcast catch all
|
||||||
log.Infof("Received Event %v for %v but no libCwtch handler found, relaying the event directly", new_event, onion)
|
log.Infof("Received Event %v for %v but no libCwtch handler found, relaying the event directly", new_event, onion)
|
||||||
|
@ -480,99 +454,44 @@ func LoadProfiles(pass string) {
|
||||||
application.LoadProfiles(pass)
|
application.LoadProfiles(pass)
|
||||||
}
|
}
|
||||||
|
|
||||||
//export c_AcceptContact
|
//export c_AcceptConversation
|
||||||
func c_AcceptContact(profilePtr *C.char, profileLen C.int, handlePtr *C.char, handleLen C.int) {
|
func c_AcceptConversation(profilePtr *C.char, profileLen C.int, conversation_id C.int) {
|
||||||
AcceptContact(C.GoStringN(profilePtr, profileLen), C.GoStringN(handlePtr, handleLen))
|
AcceptConversation(C.GoStringN(profilePtr, profileLen), int(conversation_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
// AcceptContact takes in a profileOnion and a handle to either a group or a peer and authorizes the handle
|
// AcceptConversation takes in a profileOnion and a handle to either a group or a peer and authorizes the handle
|
||||||
// for further action (e.g. messaging / connecting to the server / joining the group etc.)
|
// for further action (e.g. messaging / connecting to the server / joining the group etc.)
|
||||||
func AcceptContact(profileOnion string, handle string) {
|
func AcceptConversation(profileOnion string, conversationID int) {
|
||||||
profile := application.GetPeer(profileOnion)
|
profile := application.GetPeer(profileOnion)
|
||||||
profileHandler := utils.NewPeerHelper(profile)
|
profile.AcceptConversation(conversationID)
|
||||||
if profileHandler.IsGroup(handle) {
|
|
||||||
profile.AcceptInvite(handle)
|
|
||||||
} else {
|
|
||||||
err := profile.SetContactAuthorization(handle, model.AuthApproved)
|
|
||||||
if err == nil {
|
|
||||||
eventHandler.Push(event.NewEvent(event.PeerStateChange, map[event.Field]string{
|
|
||||||
ProfileOnion: profileOnion,
|
|
||||||
event.RemotePeer: handle,
|
|
||||||
"authorization": string(model.AuthApproved),
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
log.Errorf("error accepting contact: %s", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//export c_RejectInvite
|
|
||||||
func c_RejectInvite(profilePtr *C.char, profileLen C.int, handlePtr *C.char, handleLen C.int) {
|
|
||||||
RejectInvite(C.GoStringN(profilePtr, profileLen), C.GoStringN(handlePtr, handleLen))
|
|
||||||
}
|
|
||||||
|
|
||||||
// RejectInvite rejects a group invite
|
|
||||||
dan marked this conversation as resolved
Outdated
dan
commented
How are we handeling rejecting invites? How are we handeling rejecting invites?
|
|||||||
func RejectInvite(profileOnion string, handle string) {
|
|
||||||
log.Debugf("rejecting invite %v for %v", handle, profileOnion)
|
|
||||||
profile := application.GetPeer(profileOnion)
|
|
||||||
profileHandler := utils.NewPeerHelper(profile)
|
|
||||||
if profileHandler.IsGroup(handle) {
|
|
||||||
profile.RejectInvite(handle)
|
|
||||||
log.Debugf("successfully rejected invite %v for %v", handle, profileOnion)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export c_BlockContact
|
//export c_BlockContact
|
||||||
func c_BlockContact(profilePtr *C.char, profileLen C.int, handlePtr *C.char, handleLen C.int) {
|
func c_BlockContact(profilePtr *C.char, profileLen C.int, conversation_id C.int) {
|
||||||
BlockContact(C.GoStringN(profilePtr, profileLen), C.GoStringN(handlePtr, handleLen))
|
BlockContact(C.GoStringN(profilePtr, profileLen), int(conversation_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
func BlockContact(profile, handle string) {
|
func BlockContact(profileOnion string, conversationID int) {
|
||||||
err := application.GetPeer(profile).SetContactAuthorization(handle, model.AuthBlocked)
|
|
||||||
if err == nil {
|
|
||||||
eventHandler.Push(event.NewEvent(event.PeerStateChange, map[event.Field]string{
|
|
||||||
ProfileOnion: profile,
|
|
||||||
event.RemotePeer: handle,
|
|
||||||
"authorization": string(model.AuthBlocked),
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
log.Errorf("error blocking contact: %s", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//export c_UpdateMessageFlags
|
|
||||||
func c_UpdateMessageFlags(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, mIdx C.int, message_flags C.ulong) {
|
|
||||||
profile := C.GoStringN(profile_ptr, profile_len)
|
|
||||||
handle := C.GoStringN(handle_ptr, handle_len)
|
|
||||||
UpdateMessageFlags(profile, handle, int(mIdx), int64(message_flags))
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateMessageFlags sets the messages flags on a given message for a given profile.
|
|
||||||
// gomobile doesn't support uint64...so here we are....
|
|
||||||
func UpdateMessageFlags(profileOnion, handle string, mIdx int, flags int64) {
|
|
||||||
profile := application.GetPeer(profileOnion)
|
profile := application.GetPeer(profileOnion)
|
||||||
if profile != nil {
|
profile.BlockConversation(conversationID)
|
||||||
profile.UpdateMessageFlags(handle, mIdx, uint64(flags))
|
|
||||||
} else {
|
|
||||||
log.Errorf("called updatemessageflags with invalid profile onion")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export c_GetMessage
|
//export c_GetMessage
|
||||||
// the pointer returned from this function **must** be Freed by c_Free
|
// the pointer returned from this function **must** be Freed by c_Free
|
||||||
func c_GetMessage(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, message_index C.int) *C.char {
|
func c_GetMessage(profile_ptr *C.char, profile_len C.int, conversation_id C.int, message_index C.int) *C.char {
|
||||||
profile := C.GoStringN(profile_ptr, profile_len)
|
profile := C.GoStringN(profile_ptr, profile_len)
|
||||||
handle := C.GoStringN(handle_ptr, handle_len)
|
return C.CString(GetMessage(profile, int(conversation_id), int(message_index)))
|
||||||
return C.CString(GetMessage(profile, handle, int(message_index)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnhancedMessage wraps a Cwtch model.Message with some additional data to reduce calls from the UI.
|
// EnhancedMessage wraps a Cwtch model.Message with some additional data to reduce calls from the UI.
|
||||||
type EnhancedMessage struct {
|
type EnhancedMessage struct {
|
||||||
model.Message
|
model.Message
|
||||||
|
ID int // the actual ID of the message in the database (not the row number)
|
||||||
ContactImage string
|
ContactImage string
|
||||||
|
Attributes map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMessage(profileOnion, handle string, messageIndex int) string {
|
func GetMessage(profileOnion string, conversationID int, messageIndex int) string {
|
||||||
var message EnhancedMessage
|
var message EnhancedMessage
|
||||||
// There is an edge case that can happen on Android when the app is shutdown while fetching messages...
|
// There is an edge case that can happen on Android when the app is shutdown while fetching messages...
|
||||||
// The worker threads that are spawned can become activated again when the app is opened attempt to finish their job...
|
// The worker threads that are spawned can become activated again when the app is opened attempt to finish their job...
|
||||||
|
@ -581,40 +500,54 @@ func GetMessage(profileOnion, handle string, messageIndex int) string {
|
||||||
// these requests complete almost immediately v.s. being stalled for seconds to minutes on large groups.
|
// these requests complete almost immediately v.s. being stalled for seconds to minutes on large groups.
|
||||||
if application != nil {
|
if application != nil {
|
||||||
profile := application.GetPeer(profileOnion)
|
profile := application.GetPeer(profileOnion)
|
||||||
ph := utils.NewPeerHelper(profile)
|
messages, err := profile.GetMostRecentMessages(conversationID, 0, messageIndex, 1)
|
||||||
if ph.IsGroup(handle) {
|
if err == nil && len(messages) == 1 {
|
||||||
if profile.GetGroup(handle) != nil {
|
time, _ := time.Parse(time.RFC3339Nano, messages[0].Attr[constants2.AttrSentTimestamp])
|
||||||
|
message.Message = model.Message{
|
||||||
|
Message: messages[0].Body,
|
||||||
|
Acknowledged: messages[0].Attr[constants2.AttrAck] == constants2.True,
|
||||||
|
Error: messages[0].Attr[constants2.AttrErr],
|
||||||
|
PeerID: messages[0].Attr[constants2.AttrAuthor],
|
||||||
|
Timestamp: time,
|
||||||
|
}
|
||||||
|
message.ID = messages[0].ID
|
||||||
|
message.Attributes = messages[0].Attr
|
||||||
|
message.ContactImage = utils.RandomProfileImage(message.PeerID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bytes, _ := json.Marshal(message)
|
||||||
|
return string(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
dan marked this conversation as resolved
Outdated
dan
commented
this could have more than 1 message returned? then what do we do? this could have more than 1 message returned? then what do we do?
dan
commented
seems like a missmatch always needing/only handing one message calling an interface that can return N? seems like a missmatch always needing/only handing one message calling an interface that can return N?
sarah
commented
This is the future-proofed getNMessages API.
It literally can't This is the future-proofed getNMessages API.
> this could have more than 1 message returned? then what do we do?
It literally can't ` profile.GetMostRecentMessages(conversationID, 0, messageIndex, **1**)`
|
|||||||
exists, timelineMessage, length := profile.GetGroup(handle).GetMessage(messageIndex)
|
//export c_GetMessageByID
|
||||||
if exists {
|
// the pointer returned from this function **must** be Freed by c_Free
|
||||||
message.Message = timelineMessage
|
func c_GetMessageByID(profile_ptr *C.char, profile_len C.int, conversation_id C.int, message_index C.int) *C.char {
|
||||||
message.ContactImage = ph.GetProfilePic(message.Message.PeerID)
|
profile := C.GoStringN(profile_ptr, profile_len)
|
||||||
} else {
|
return C.CString(GetMessageByID(profile, int(conversation_id), int(message_index)))
|
||||||
eventHandler.Push(event.NewEvent(event.MessageCounterResync, map[event.Field]string{
|
}
|
||||||
event.Identity: profileOnion,
|
|
||||||
event.GroupID: handle,
|
func GetMessageByID(profileOnion string, conversationID int, messageIndex int) string {
|
||||||
event.Data: strconv.Itoa(length),
|
var message EnhancedMessage
|
||||||
}))
|
// There is an edge case that can happen on Android when the app is shutdown while fetching messages...
|
||||||
log.Errorf("Couldn't find message in timeline %v / %v or unacked messages, probably transient threading issue, but logging for visibility..", messageIndex, length)
|
// The worker threads that are spawned can become activated again when the app is opened attempt to finish their job...
|
||||||
}
|
// In that case we skip processing and just return the empty message...
|
||||||
}
|
// Note: This is far less likely to happen now that the UI only requests messages *after* syncing has happened and
|
||||||
} else {
|
// these requests complete almost immediately v.s. being stalled for seconds to minutes on large groups.
|
||||||
if profile.GetContact(handle) != nil {
|
if application != nil {
|
||||||
// If we are safely within the limits of the timeline just grab the message at the index..
|
profile := application.GetPeer(profileOnion)
|
||||||
if len(profile.GetContact(handle).Timeline.Messages) > messageIndex {
|
dbmessage, attr, err := profile.GetChannelMessage(conversationID, 0, messageIndex)
|
||||||
message.Message = profile.GetContact(handle).Timeline.Messages[messageIndex]
|
if err == nil {
|
||||||
message.ContactImage = ph.GetProfilePic(handle)
|
time, _ := time.Parse(time.RFC3339Nano, attr[constants2.AttrSentTimestamp])
|
||||||
} else {
|
message.Message = model.Message{
|
||||||
// Otherwise Send a counter resync event...this shouldn't really happen for p2p messages so we
|
Message: dbmessage,
|
||||||
// throw an error.
|
Acknowledged: attr[constants2.AttrAck] == constants2.True,
|
||||||
log.Errorf("peerpeercontact getmessage out of range; sending counter resync just in case")
|
Error: attr[constants2.AttrErr],
|
||||||
eventHandler.Push(event.NewEvent(event.MessageCounterResync, map[event.Field]string{
|
PeerID: attr[constants2.AttrAuthor],
|
||||||
event.Identity: profileOnion,
|
Timestamp: time,
|
||||||
event.RemotePeer: handle,
|
|
||||||
event.Data: strconv.Itoa(len(profile.GetContact(handle).Timeline.Messages)),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
message.ID = messageIndex
|
||||||
|
message.Attributes = attr
|
||||||
|
message.ContactImage = utils.RandomProfileImage(message.PeerID)
|
||||||
sarah marked this conversation as resolved
Outdated
dan
commented
GetMessagesByContentHash why is it looping through the most recent 100 messages? GetMessagesByContentHash why is it looping through the most recent 100 messages?
peer.GetChannelMessageByContentHash doesnt return the message? just hte id? and then we fetch only the most recent 100 messages and iteratively search through them for it? seems like a bad API? can't we push this way into Cwtch and storage?
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
bytes, _ := json.Marshal(message)
|
bytes, _ := json.Marshal(message)
|
||||||
|
@ -623,11 +556,37 @@ func GetMessage(profileOnion, handle string, messageIndex int) string {
|
||||||
|
|
||||||
//export c_GetMessagesByContentHash
|
//export c_GetMessagesByContentHash
|
||||||
// the pointer returned from this function **must** be freed by calling c_Free
|
// the pointer returned from this function **must** be freed by calling c_Free
|
||||||
func c_GetMessagesByContentHash(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, contenthash_ptr *C.char, contenthash_len C.int) *C.char {
|
func c_GetMessagesByContentHash(profile_ptr *C.char, profile_len C.int, conversation_id C.int, contenthash_ptr *C.char, contenthash_len C.int) *C.char {
|
||||||
dan marked this conversation as resolved
Outdated
dan
commented
doesnt this make 100 coopies of message 0? doesnt this make 100 coopies of message 0?
why fo we get messages back in a format we have to process and migrate data from?
sarah
commented
UI renders messages by row numberCwtch understands messages by message id. For quoted messages we therefore need a mapping between the actual database row and the message ID. ContentHash gets us the ID. We don't want Row() to query the whole database and so we just look back over the most recent message - so 100 rows is chosen as a suitable limit. I think ultimately we are going to have to push back quoted messages into Cwtch and just do the rendered there instead of in UI but I didn't want to mess too much with the API in this PR. UI renders messages by row numberCwtch understands messages by message id.
For quoted messages we therefore need a mapping between the actual database row and the message ID. ContentHash gets us the ID. We don't want Row() to query the whole database and so we just look back over the most recent message - so 100 rows is chosen as a suitable limit.
I think ultimately we are going to have to push back quoted messages into Cwtch and just do the rendered there instead of in UI but I didn't want to mess too much with the API in this PR.
|
|||||||
profile := C.GoStringN(profile_ptr, profile_len)
|
profile := C.GoStringN(profile_ptr, profile_len)
|
||||||
handle := C.GoStringN(handle_ptr, handle_len)
|
|
||||||
contentHash := C.GoStringN(contenthash_ptr, contenthash_len)
|
contentHash := C.GoStringN(contenthash_ptr, contenthash_len)
|
||||||
return C.CString(GetMessagesByContentHash(profile, handle, contentHash))
|
return C.CString(GetMessagesByContentHash(profile, int(conversation_id), contentHash))
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMessagesByContentHash(profileOnion string, handle int, contentHash string) string {
|
||||||
|
var indexedMessages []model.LocallyIndexedMessage
|
||||||
|
if application != nil {
|
||||||
|
profile := application.GetPeer(profileOnion)
|
||||||
|
offset, err := profile.GetChannelMessageByContentHash(handle, 0, contentHash)
|
||||||
|
if err == nil {
|
||||||
|
messages, err := profile.GetMostRecentMessages(handle, 0, offset, 1)
|
||||||
|
if err == nil {
|
||||||
|
time, _ := time.Parse(time.RFC3339Nano, messages[0].Attr[constants2.AttrSentTimestamp])
|
||||||
|
msg := model.Message{
|
||||||
|
Message: messages[0].Body,
|
||||||
|
Acknowledged: messages[0].Attr[constants2.AttrAck] == constants2.True,
|
||||||
|
Error: messages[0].Attr[constants2.AttrErr],
|
||||||
|
PeerID: messages[0].Attr[constants2.AttrAuthor],
|
||||||
|
Timestamp: time,
|
||||||
|
}
|
||||||
|
// TODO this is mostly unused at this point, consider cleaning is up in 1.6
|
||||||
|
indexedMessages = append(indexedMessages, model.LocallyIndexedMessage{LocalIndex: offset, Message: msg})
|
||||||
|
} else {
|
||||||
|
log.Errorf("error fetching local index {} ", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bytes, _ := json.Marshal(indexedMessages)
|
||||||
|
return string(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
//export c_FreePointer
|
//export c_FreePointer
|
||||||
|
@ -636,97 +595,46 @@ func c_FreePointer(ptr *C.char) {
|
||||||
C.free(unsafe.Pointer(ptr))
|
C.free(unsafe.Pointer(ptr))
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMessagesByContentHash(profileOnion, handle string, contentHash string) string {
|
|
||||||
var indexedMessages []model.LocallyIndexedMessage
|
|
||||||
if application != nil {
|
|
||||||
profile := application.GetPeer(profileOnion)
|
|
||||||
ph := utils.NewPeerHelper(profile)
|
|
||||||
var err error
|
|
||||||
timeline := ph.GetTimeline(handle)
|
|
||||||
if timeline != nil {
|
|
||||||
indexedMessages, err = timeline.GetMessagesByHash(contentHash)
|
|
||||||
if err != nil {
|
|
||||||
indexedMessages = []model.LocallyIndexedMessage{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bytes, _ := json.Marshal(indexedMessages)
|
|
||||||
return string(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
//export c_SendMessage
|
//export c_SendMessage
|
||||||
func c_SendMessage(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, msg_ptr *C.char, msg_len C.int) {
|
func c_SendMessage(profile_ptr *C.char, profile_len C.int, conversation_id C.int, msg_ptr *C.char, msg_len C.int) {
|
||||||
profile := C.GoStringN(profile_ptr, profile_len)
|
profile := C.GoStringN(profile_ptr, profile_len)
|
||||||
handle := C.GoStringN(handle_ptr, handle_len)
|
|
||||||
msg := C.GoStringN(msg_ptr, msg_len)
|
msg := C.GoStringN(msg_ptr, msg_len)
|
||||||
SendMessage(profile, handle, msg)
|
SendMessage(profile, int(conversation_id), msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendMessage(profileOnion, handle, msg string) {
|
func SendMessage(profileOnion string, conversationID int, msg string) {
|
||||||
profile := application.GetPeer(profileOnion)
|
profile := application.GetPeer(profileOnion)
|
||||||
ph := utils.NewPeerHelper(profile)
|
profile.SendMessage(conversationID, msg)
|
||||||
if ph.IsGroup(handle) {
|
|
||||||
groupHandler, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments)
|
|
||||||
if err == nil {
|
|
||||||
groupHandler.SendMessage(profile, handle, msg)
|
|
||||||
profile.SetGroupAttribute(handle, attr.GetLocalScope(constants.Archived), event.False)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
contactHandler, _ := contact.FunctionalityGate(utils.ReadGlobalSettings().Experiments)
|
|
||||||
contactHandler.SendMessage(profile, handle, msg)
|
|
||||||
profile.SetContactAttribute(handle, attr.GetLocalScope(constants.Archived), event.False)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export c_SendInvitation
|
//export c_SendInvitation
|
||||||
func c_SendInvitation(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, target_ptr *C.char, target_len C.int) {
|
func c_SendInvitation(profile_ptr *C.char, profile_len C.int, conversation_id C.int, target_id C.int) {
|
||||||
profile := C.GoStringN(profile_ptr, profile_len)
|
profile := C.GoStringN(profile_ptr, profile_len)
|
||||||
handle := C.GoStringN(handle_ptr, handle_len)
|
SendInvitation(profile, int(conversation_id), int(target_id))
|
||||||
target := C.GoStringN(target_ptr, target_len)
|
|
||||||
SendInvitation(profile, handle, target)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send an invitation from `profileOnion` to contact `handle` (peer or group)
|
// SendInvitation sends an invitation from `profileOnion` to contact `handle` (peer or group)
|
||||||
// asking them to add the contact `target` (also peer or group).
|
// asking them to add the contact `target` (also peer or group).
|
||||||
// For groups, the profile must already have `target` as a contact.
|
// For groups, the profile must already have `target` as a contact.
|
||||||
func SendInvitation(profileOnion, handle, target string) {
|
func SendInvitation(profileOnion string, conversationID int, targetID int) {
|
||||||
profile := application.GetPeer(profileOnion)
|
profile := application.GetPeer(profileOnion)
|
||||||
ph := utils.NewPeerHelper(profile)
|
profile.SendInviteToConversation(conversationID, targetID)
|
||||||
|
|
||||||
var invite ChatMessage
|
|
||||||
if ph.IsGroup(target) {
|
|
||||||
bundle, _ := profile.GetContact(profile.GetGroup(target).GroupServer).GetAttribute(string(model.BundleType))
|
|
||||||
inviteStr, err := profile.GetGroup(target).Invite()
|
|
||||||
if err == nil {
|
|
||||||
invite = ChatMessage{O: 101, D: fmt.Sprintf("tofubundle:server:%s||%s", base64.StdEncoding.EncodeToString([]byte(bundle)), inviteStr)}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
invite = ChatMessage{O: 100, D: target}
|
|
||||||
}
|
|
||||||
|
|
||||||
inviteBytes, err := json.Marshal(invite)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("malformed invite: %v", err)
|
|
||||||
} else {
|
|
||||||
SendMessage(profileOnion, handle, string(inviteBytes))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export c_ShareFile
|
//export c_ShareFile
|
||||||
func c_ShareFile(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, filepath_ptr *C.char, filepath_len C.int) {
|
func c_ShareFile(profile_ptr *C.char, profile_len C.int, conversation_id C.int, filepath_ptr *C.char, filepath_len C.int) {
|
||||||
profile := C.GoStringN(profile_ptr, profile_len)
|
profile := C.GoStringN(profile_ptr, profile_len)
|
||||||
handle := C.GoStringN(handle_ptr, handle_len)
|
|
||||||
sharefilepath := C.GoStringN(filepath_ptr, filepath_len)
|
sharefilepath := C.GoStringN(filepath_ptr, filepath_len)
|
||||||
ShareFile(profile, handle, sharefilepath)
|
ShareFile(profile, int(conversation_id), sharefilepath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ShareFile(profileOnion, handle, sharefilepath string) {
|
func ShareFile(profileOnion string, conversationID int, sharefilepath string) {
|
||||||
profile := application.GetPeer(profileOnion)
|
profile := application.GetPeer(profileOnion)
|
||||||
fh, err := filesharing.FunctionalityGate(utils.ReadGlobalSettings().Experiments)
|
fh, err := filesharing.FunctionalityGate(utils.ReadGlobalSettings().Experiments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("file sharing error: %v", err)
|
log.Errorf("file sharing error: %v", err)
|
||||||
} else {
|
} else {
|
||||||
err = fh.ShareFile(sharefilepath, profile, handle)
|
err = fh.ShareFile(sharefilepath, profile, conversationID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("error sharing file: %v", err)
|
log.Errorf("error sharing file: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -734,22 +642,21 @@ func ShareFile(profileOnion, handle, sharefilepath string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//export c_DownloadFile
|
//export c_DownloadFile
|
||||||
func c_DownloadFile(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, filepath_ptr *C.char, filepath_len C.int, manifestpath_ptr *C.char, manifestpath_len C.int, filekey_ptr *C.char, filekey_len C.int) {
|
func c_DownloadFile(profile_ptr *C.char, profile_len C.int, conversation_id C.int, filepath_ptr *C.char, filepath_len C.int, manifestpath_ptr *C.char, manifestpath_len C.int, filekey_ptr *C.char, filekey_len C.int) {
|
||||||
profile := C.GoStringN(profile_ptr, profile_len)
|
profile := C.GoStringN(profile_ptr, profile_len)
|
||||||
handle := C.GoStringN(handle_ptr, handle_len)
|
|
||||||
downloadfilepath := C.GoStringN(filepath_ptr, filepath_len)
|
downloadfilepath := C.GoStringN(filepath_ptr, filepath_len)
|
||||||
manifestpath := C.GoStringN(manifestpath_ptr, manifestpath_len)
|
manifestpath := C.GoStringN(manifestpath_ptr, manifestpath_len)
|
||||||
filekey := C.GoStringN(filekey_ptr, filekey_len)
|
filekey := C.GoStringN(filekey_ptr, filekey_len)
|
||||||
DownloadFile(profile, handle, downloadfilepath, manifestpath, filekey)
|
DownloadFile(profile, int(conversation_id), downloadfilepath, manifestpath, filekey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func DownloadFile(profileOnion, handle, filepath, manifestpath, filekey string) {
|
func DownloadFile(profileOnion string, conversationID int, filepath, manifestpath, filekey string) {
|
||||||
profile := application.GetPeer(profileOnion)
|
profile := application.GetPeer(profileOnion)
|
||||||
fh, err := filesharing.FunctionalityGate(utils.ReadGlobalSettings().Experiments)
|
fh, err := filesharing.FunctionalityGate(utils.ReadGlobalSettings().Experiments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("file sharing error: %v", err)
|
log.Errorf("file sharing error: %v", err)
|
||||||
} else {
|
} else {
|
||||||
fh.DownloadFile(profile, handle, filepath, manifestpath, filekey)
|
fh.DownloadFile(profile, conversationID, filepath, manifestpath, filekey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -781,19 +688,18 @@ func CheckDownloadStatus(profileOnion, fileKey string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//export c_VerifyOrResumeDownload
|
//export c_VerifyOrResumeDownload
|
||||||
func c_VerifyOrResumeDownload(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, filekey_ptr *C.char, filekey_len C.int) {
|
func c_VerifyOrResumeDownload(profile_ptr *C.char, profile_len C.int, conversation_id C.int, filekey_ptr *C.char, filekey_len C.int) {
|
||||||
profile := C.GoStringN(profile_ptr, profile_len)
|
profile := C.GoStringN(profile_ptr, profile_len)
|
||||||
handle := C.GoStringN(handle_ptr, handle_len)
|
|
||||||
filekey := C.GoStringN(filekey_ptr, filekey_len)
|
filekey := C.GoStringN(filekey_ptr, filekey_len)
|
||||||
VerifyOrResumeDownload(profile, handle, filekey)
|
VerifyOrResumeDownload(profile, int(conversation_id), filekey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func VerifyOrResumeDownload(profileOnion, handle, fileKey string) {
|
func VerifyOrResumeDownload(profileOnion string, conversationID int, fileKey string) {
|
||||||
profile := application.GetPeer(profileOnion)
|
profile := application.GetPeer(profileOnion)
|
||||||
if manifestFilePath, exists := profile.GetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%v.manifest", fileKey)); exists {
|
if manifestFilePath, exists := profile.GetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%v.manifest", fileKey)); exists {
|
||||||
if downloadfilepath, exists := profile.GetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%s.path", fileKey)); exists {
|
if downloadfilepath, exists := profile.GetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%s.path", fileKey)); exists {
|
||||||
log.Infof("resuming %s", fileKey)
|
log.Infof("resuming %s", fileKey)
|
||||||
DownloadFile(profileOnion, handle, downloadfilepath, manifestFilePath, fileKey)
|
DownloadFile(profileOnion, conversationID, downloadfilepath, manifestFilePath, fileKey)
|
||||||
} else {
|
} else {
|
||||||
log.Errorf("found manifest path but not download path for %s", fileKey)
|
log.Errorf("found manifest path but not download path for %s", fileKey)
|
||||||
}
|
}
|
||||||
|
@ -808,7 +714,9 @@ func c_ResetTor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func ResetTor() {
|
func ResetTor() {
|
||||||
|
log.Infof("Restarting...")
|
||||||
globalACN.Restart()
|
globalACN.Restart()
|
||||||
|
log.Infof("Restarted")
|
||||||
}
|
}
|
||||||
|
|
||||||
//export c_CreateGroup
|
//export c_CreateGroup
|
||||||
|
@ -824,11 +732,9 @@ func CreateGroup(profileHandle string, server string, name string) {
|
||||||
profile := application.GetPeer(profileHandle)
|
profile := application.GetPeer(profileHandle)
|
||||||
_, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments)
|
_, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
gid, _, err := profile.StartGroup(server)
|
conversationID, err := profile.StartGroup(name, server)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
log.Debugf("created group %v on %v: $v", profileHandle, server, gid)
|
log.Debugf("created group %v on %v: $v", profileHandle, server, conversationID)
|
||||||
// set the group name
|
|
||||||
profile.SetGroupAttribute(gid, attr.GetLocalScope("name"), name)
|
|
||||||
} else {
|
} else {
|
||||||
log.Errorf("error creating group or %v on server %v: %v", profileHandle, server, err)
|
log.Errorf("error creating group or %v on server %v: %v", profileHandle, server, err)
|
||||||
}
|
}
|
||||||
|
@ -854,42 +760,27 @@ func DeleteProfile(profile string, password string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//export c_ArchiveConversation
|
//export c_ArchiveConversation
|
||||||
func c_ArchiveConversation(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int) {
|
func c_ArchiveConversation(profile_ptr *C.char, profile_len C.int, conversation_id C.int) {
|
||||||
profile := C.GoStringN(profile_ptr, profile_len)
|
profile := C.GoStringN(profile_ptr, profile_len)
|
||||||
handle := C.GoStringN(handle_ptr, handle_len)
|
ArchiveConversation(profile, int(conversation_id))
|
||||||
ArchiveConversation(profile, handle)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ArchiveConversation sets the conversation to archived
|
// ArchiveConversation sets the conversation to archived
|
||||||
func ArchiveConversation(profileHandle string, handle string) {
|
func ArchiveConversation(profileHandle string, conversationID int) {
|
||||||
profile := application.GetPeer(profileHandle)
|
profile := application.GetPeer(profileHandle)
|
||||||
ph := utils.NewPeerHelper(profile)
|
profile.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants.Archived)), constants2.True)
|
||||||
if ph.IsGroup(handle) {
|
|
||||||
profile.SetGroupAttribute(handle, attr.GetLocalScope(constants.Archived), event.True)
|
|
||||||
} else {
|
|
||||||
profile.SetContactAttribute(handle, attr.GetLocalScope(constants.Archived), event.True)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export c_DeleteContact
|
//export c_DeleteContact
|
||||||
func c_DeleteContact(profile_ptr *C.char, profile_len C.int, hanlde_ptr *C.char, handle_len C.int) {
|
func c_DeleteContact(profile_ptr *C.char, profile_len C.int, conversation_id C.int) {
|
||||||
profile := C.GoStringN(profile_ptr, profile_len)
|
profile := C.GoStringN(profile_ptr, profile_len)
|
||||||
groupID := C.GoStringN(hanlde_ptr, handle_len)
|
DeleteContact(profile, int(conversation_id))
|
||||||
DeleteContact(profile, groupID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteContact removes all trace of the contact from the profile
|
// DeleteContact removes all trace of the contact from the profile
|
||||||
func DeleteContact(profileHandle string, handle string) {
|
func DeleteContact(profileHandle string, conversationID int) {
|
||||||
profile := application.GetPeer(profileHandle)
|
profile := application.GetPeer(profileHandle)
|
||||||
ph := utils.NewPeerHelper(profile)
|
profile.DeleteConversation(conversationID)
|
||||||
if ph.IsGroup(handle) {
|
|
||||||
_, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments)
|
|
||||||
if err == nil {
|
|
||||||
profile.DeleteGroup(handle)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
profile.DeleteContact(handle)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export c_ImportBundle
|
//export c_ImportBundle
|
||||||
|
@ -903,21 +794,16 @@ func c_ImportBundle(profile_ptr *C.char, profile_len C.int, bundle_ptr *C.char,
|
||||||
// different formats (e.g. a peer address, a group invite, a server key bundle, or a combination)
|
// different formats (e.g. a peer address, a group invite, a server key bundle, or a combination)
|
||||||
func ImportBundle(profileOnion string, bundle string) {
|
func ImportBundle(profileOnion string, bundle string) {
|
||||||
profile := application.GetPeer(profileOnion)
|
profile := application.GetPeer(profileOnion)
|
||||||
peerHandler, _ := contact.FunctionalityGate(utils.ReadGlobalSettings().Experiments)
|
response := profile.ImportBundle(bundle)
|
||||||
response := peerHandler.HandleImportString(profile, bundle)
|
|
||||||
if strings.Contains(response.Error(), "invalid_import_string") {
|
|
||||||
groupHandler, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments)
|
|
||||||
if err == nil {
|
|
||||||
response = groupHandler.HandleImportString(profile, bundle)
|
|
||||||
eventHandler.Push(event.NewEvent(event.AppError, map[event.Field]string{event.Data: response.Error()}))
|
|
||||||
|
|
||||||
// We might have added a new server, so refresh the server list...
|
// We might have added a new server, so refresh the server list if applicable...
|
||||||
serverListForOnion := groupHandler.GetServerInfoList(profile)
|
groupHandler, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments)
|
||||||
serversListBytes, _ := json.Marshal(serverListForOnion)
|
if err == nil {
|
||||||
eventHandler.Push(event.NewEvent(groups.UpdateServerInfo, map[event.Field]string{"ProfileOnion": profileOnion, groups.ServerList: string(serversListBytes)}))
|
serverListForOnion := groupHandler.GetServerInfoList(profile)
|
||||||
return
|
serversListBytes, _ := json.Marshal(serverListForOnion)
|
||||||
}
|
eventHandler.Push(event.NewEvent(groups.UpdateServerInfo, map[event.Field]string{"ProfileOnion": profileOnion, groups.ServerList: string(serversListBytes)}))
|
||||||
}
|
}
|
||||||
|
|
||||||
eventHandler.Push(event.NewEvent(event.AppError, map[event.Field]string{event.Data: response.Error()}))
|
eventHandler.Push(event.NewEvent(event.AppError, map[event.Field]string{event.Data: response.Error()}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -936,7 +822,6 @@ func c_SetProfileAttribute(profile_ptr *C.char, profile_len C.int, key_ptr *C.ch
|
||||||
// Key must have the format zone.key where Zone is defined in Cwtch. Unknown zones are not permitted.
|
// Key must have the format zone.key where Zone is defined in Cwtch. Unknown zones are not permitted.
|
||||||
func SetProfileAttribute(profileOnion string, key string, value string) {
|
func SetProfileAttribute(profileOnion string, key string, value string) {
|
||||||
profile := application.GetPeer(profileOnion)
|
profile := application.GetPeer(profileOnion)
|
||||||
|
|
||||||
zone, key := attr.ParseZone(key)
|
zone, key := attr.ParseZone(key)
|
||||||
|
|
||||||
// TODO We only allow public.profile.zone to be set for now.
|
// TODO We only allow public.profile.zone to be set for now.
|
||||||
|
@ -948,36 +833,42 @@ func SetProfileAttribute(profileOnion string, key string, value string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//export c_SetContactAttribute
|
//export c_SetConversationAttribute
|
||||||
func c_SetContactAttribute(profile_ptr *C.char, profile_len C.int, contact_ptr *C.char, contact_len C.int, key_ptr *C.char, key_len C.int, val_ptr *C.char, val_len C.int) {
|
func c_SetConversationAttribute(profile_ptr *C.char, profile_len C.int, conversation_id C.int, key_ptr *C.char, key_len C.int, val_ptr *C.char, val_len C.int) {
|
||||||
profileOnion := C.GoStringN(profile_ptr, profile_len)
|
profileOnion := C.GoStringN(profile_ptr, profile_len)
|
||||||
contactHandle := C.GoStringN(contact_ptr, contact_len)
|
|
||||||
key := C.GoStringN(key_ptr, key_len)
|
key := C.GoStringN(key_ptr, key_len)
|
||||||
value := C.GoStringN(val_ptr, val_len)
|
value := C.GoStringN(val_ptr, val_len)
|
||||||
SetContactAttribute(profileOnion, contactHandle, key, value)
|
SetConversationAttribute(profileOnion, int(conversation_id), key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetContactAttribute provides a wrapper around profile.SetProfileAttribute
|
// SetConversationAttribute provides a wrapper around profile.SetProfileAttribute
|
||||||
func SetContactAttribute(profileOnion string, contactHandle string, key string, value string) {
|
func SetConversationAttribute(profileOnion string, conversationID int, key string, value string) {
|
||||||
profile := application.GetPeer(profileOnion)
|
profile := application.GetPeer(profileOnion)
|
||||||
profile.SetContactAttribute(contactHandle, key, value)
|
zone, key := attr.ParseZone(key)
|
||||||
|
profile.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(zone.ConstructZonedPath(key)), value)
|
||||||
}
|
}
|
||||||
|
|
||||||
//export c_SetGroupAttribute
|
//export c_SetMessageAttribute
|
||||||
func c_SetGroupAttribute(profile_ptr *C.char, profile_len C.int, group_ptr *C.char, group_len C.int, key_ptr *C.char, key_len C.int, val_ptr *C.char, val_len C.int) {
|
func c_SetMessageAttribute(profile_ptr *C.char, profile_len C.int, conversation_id C.int, channel_id C.int, message_id C.int, key_ptr *C.char, key_len C.int, val_ptr *C.char, val_len C.int) {
|
||||||
profileOnion := C.GoStringN(profile_ptr, profile_len)
|
profileOnion := C.GoStringN(profile_ptr, profile_len)
|
||||||
groupHandle := C.GoStringN(group_ptr, group_len)
|
|
||||||
key := C.GoStringN(key_ptr, key_len)
|
key := C.GoStringN(key_ptr, key_len)
|
||||||
value := C.GoStringN(val_ptr, val_len)
|
value := C.GoStringN(val_ptr, val_len)
|
||||||
SetGroupAttribute(profileOnion, groupHandle, key, value)
|
SetMessageAttribute(profileOnion, int(conversation_id), int(channel_id), int(message_id), key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetGroupAttribute provides a wrapper around profile.SetGroupAttribute, gated by global experiments...
|
// SetMessageAttribute is a wrapper around `UpdateMessageAttribute` on profile that allows the creation or update of a
|
||||||
func SetGroupAttribute(profileOnion string, groupHandle string, key string, value string) {
|
// given message attribute on a conversation/channel.
|
||||||
|
// Errors if `profileOnion` is not associated to an existing & loaded profile,
|
||||||
|
// of if `UpdateMessageAttribute` fails
|
||||||
|
func SetMessageAttribute(profileOnion string, conversationID int, channelID int, messageID int, attributeKey string, attributeValue string) {
|
||||||
profile := application.GetPeer(profileOnion)
|
profile := application.GetPeer(profileOnion)
|
||||||
_, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments)
|
if profile == nil {
|
||||||
if err == nil {
|
log.Errorf("called SetMessageAttribute with invalid profile handle: %v", profileOnion)
|
||||||
profile.SetGroupAttribute(groupHandle, key, value)
|
return
|
||||||
|
}
|
||||||
|
err := profile.UpdateMessageAttribute(conversationID, channelID, messageID, attributeKey, attributeValue)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error updating message attribute: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,6 @@
|
||||||
echo "Checking code quality (you want to see no output here)"
|
echo "Checking code quality (you want to see no output here)"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
echo "Vetting:"
|
|
||||||
go list ./... | xargs go vet
|
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Linting:"
|
echo "Linting:"
|
||||||
|
|
||||||
|
|
|
@ -13,4 +13,5 @@ type Contact struct {
|
||||||
IsGroup bool `json:"isGroup"`
|
IsGroup bool `json:"isGroup"`
|
||||||
GroupServer string `json:"groupServer"`
|
GroupServer string `json:"groupServer"`
|
||||||
IsArchived bool `json:"isArchived"`
|
IsArchived bool `json:"isArchived"`
|
||||||
|
Identifier int `json:"identifier"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"git.openprivacy.ca/cwtch.im/libcwtch-go/features/servers"
|
"git.openprivacy.ca/cwtch.im/libcwtch-go/features/servers"
|
||||||
"git.openprivacy.ca/openprivacy/log"
|
"git.openprivacy.ca/openprivacy/log"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
import "cwtch.im/cwtch/event"
|
import "cwtch.im/cwtch/event"
|
||||||
|
|
||||||
|
@ -61,14 +62,18 @@ func (eh *EventHandler) GetNextEvent() string {
|
||||||
select {
|
select {
|
||||||
case e := <-appChan:
|
case e := <-appChan:
|
||||||
return eh.handleAppBusEvent(&e)
|
return eh.handleAppBusEvent(&e)
|
||||||
case ev := <-eh.profileEvents:
|
default:
|
||||||
dan
commented
why a default in the select with appChan with a nested select with appChan again and profileEvents? why a default in the select with appChan with a nested select with appChan again and profileEvents?
|
|||||||
return eh.handleProfileEvent(&ev)
|
select {
|
||||||
|
case e := <-appChan:
|
||||||
|
return eh.handleAppBusEvent(&e)
|
||||||
|
case ev := <-eh.profileEvents:
|
||||||
|
return eh.handleProfileEvent(&ev)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleAppBusEvent enriches AppBus events so they are usable with out further data fetches
|
// handleAppBusEvent enriches AppBus events so they are usable with out further data fetches
|
||||||
func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
|
func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
|
||||||
log.Debugf("New AppBus Event to Handle: %v", e)
|
|
||||||
if eh.app != nil {
|
if eh.app != nil {
|
||||||
switch e.EventType {
|
switch e.EventType {
|
||||||
case event.ACNStatus:
|
case event.ACNStatus:
|
||||||
|
@ -117,106 +122,109 @@ func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
picVal, ok := profile.GetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants2.Picture)
|
|
||||||
if !ok {
|
|
||||||
picVal = ImageToString(NewImage(RandomProfileImage(onion), TypeImageDistro))
|
|
||||||
}
|
|
||||||
pic, err := StringToImage(picVal)
|
|
||||||
if err != nil {
|
|
||||||
pic = NewImage(RandomProfileImage(onion), TypeImageDistro)
|
|
||||||
}
|
|
||||||
picPath := GetPicturePath(pic)
|
|
||||||
|
|
||||||
// Set publicly scopes attributes
|
|
||||||
profile.SetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants2.Picture, picPath)
|
|
||||||
|
|
||||||
online, _ := profile.GetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants2.PeerOnline)
|
online, _ := profile.GetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants2.PeerOnline)
|
||||||
|
|
||||||
// Name always exists
|
// Name always exists
|
||||||
e.Data[constants.Name], _ = profile.GetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants.Name)
|
e.Data[constants.Name], _ = profile.GetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants.Name)
|
||||||
e.Data[constants2.Picture] = picPath
|
|
||||||
|
// Resolve the profile image of the profile.
|
||||||
|
e.Data[constants2.Picture] = RandomProfileImage(onion)
|
||||||
e.Data["Online"] = online
|
e.Data["Online"] = online
|
||||||
|
|
||||||
|
// Construct our conversations and our srever lists
|
||||||
var contacts []Contact
|
var contacts []Contact
|
||||||
var servers []groups.Server
|
var servers []groups.Server
|
||||||
for _, contact := range profile.GetContacts() {
|
|
||||||
|
|
||||||
// Only compile the server info if we have enabled the experiment...
|
conversations, err := profile.FetchConversations()
|
||||||
// Note that this means that this info can become stale if when first loaded the experiment
|
|
||||||
// has been disabled and then is later re-enabled. As such we need to ensure that this list is
|
if err == nil {
|
||||||
// re-fetched when the group experiment is enabled via a dedicated ListServerInfo event...
|
// We have conversations attached to this profile...
|
||||||
if profile.GetContact(contact).IsServer() {
|
for _, conversationInfo := range conversations {
|
||||||
groupHandler, err := groups.ExperimentGate(ReadGlobalSettings().Experiments)
|
// Only compile the server info if we have enabled the experiment...
|
||||||
if err == nil {
|
// Note that this means that this info can become stale if when first loaded the experiment
|
||||||
servers = append(servers, groupHandler.GetServerInfo(contact, profile))
|
// has been disabled and then is later re-enabled. As such we need to ensure that this list is
|
||||||
|
// re-fetched when the group experiment is enabled via a dedicated ListServerInfo event...
|
||||||
|
if conversationInfo.IsServer() {
|
||||||
|
groupHandler, err := groups.ExperimentGate(ReadGlobalSettings().Experiments)
|
||||||
|
if err == nil {
|
||||||
|
servers = append(servers, groupHandler.GetServerInfo(conversationInfo.Handle, profile))
|
||||||
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
contactInfo := profile.GetContact(contact)
|
// Prefer local override to public name...
|
||||||
ph := NewPeerHelper(profile)
|
name, exists := conversationInfo.GetAttribute(attr.LocalScope, attr.ProfileZone, constants.Name)
|
||||||
name := ph.GetNick(contact)
|
if !exists {
|
||||||
cpicPath := ph.GetProfilePic(contact)
|
name, exists = conversationInfo.GetAttribute(attr.PublicScope, attr.ProfileZone, constants.Name)
|
||||||
saveHistory, set := contactInfo.GetAttribute(event.SaveHistoryKey)
|
if !exists {
|
||||||
if !set {
|
name = conversationInfo.Handle
|
||||||
saveHistory = event.DeleteHistoryDefault
|
}
|
||||||
}
|
}
|
||||||
isArchived, set := contactInfo.GetAttribute(attr.GetLocalScope(constants2.Archived))
|
|
||||||
if !set {
|
|
||||||
isArchived = event.False
|
|
||||||
}
|
|
||||||
contacts = append(contacts, Contact{
|
|
||||||
Name: name,
|
|
||||||
Onion: contactInfo.Onion,
|
|
||||||
Status: contactInfo.State,
|
|
||||||
Picture: cpicPath,
|
|
||||||
Authorization: string(contactInfo.Authorization),
|
|
||||||
SaveHistory: saveHistory,
|
|
||||||
Messages: contactInfo.Timeline.Len(),
|
|
||||||
Unread: 0,
|
|
||||||
LastMessage: strconv.Itoa(getLastMessageTime(&contactInfo.Timeline)),
|
|
||||||
IsGroup: false,
|
|
||||||
IsArchived: isArchived == event.True,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// We compile and send the groups regardless of the experiment flag, and hide them in the UI
|
// Resolve the profile image of the contact
|
||||||
for _, groupId := range profile.GetGroups() {
|
var cpicPath string
|
||||||
group := profile.GetGroup(groupId)
|
if conversationInfo.IsGroup() {
|
||||||
|
cpicPath = RandomGroupImage(conversationInfo.Handle)
|
||||||
|
} else {
|
||||||
|
cpicPath = RandomProfileImage(conversationInfo.Handle)
|
||||||
|
}
|
||||||
|
|
||||||
// Check that the group is cryptographically valid
|
// Resolve Save History Setting
|
||||||
if !group.CheckGroup() {
|
saveHistory, set := conversationInfo.GetAttribute(attr.LocalScope, attr.ProfileZone, event.SaveHistoryKey)
|
||||||
continue
|
if !set {
|
||||||
|
saveHistory = event.DeleteHistoryDefault
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve Archived Setting
|
||||||
|
isArchived, set := conversationInfo.GetAttribute(attr.LocalScope, attr.ProfileZone, constants2.Archived)
|
||||||
|
if !set {
|
||||||
|
isArchived = event.False
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve Peer State (should probably be DISCONNECTED)
|
||||||
|
state := profile.GetPeerState(conversationInfo.Handle)
|
||||||
|
if !set {
|
||||||
|
state = connections.DISCONNECTED
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve Conversation Auth State
|
||||||
|
// TODO: Align this with ACLs
|
||||||
|
authorization := model.AuthUnknown
|
||||||
|
if conversationInfo.Accepted {
|
||||||
|
authorization = model.AuthApproved
|
||||||
|
}
|
||||||
|
|
||||||
|
// If ACL has blocked conversation then hide them...
|
||||||
|
if acl, exists := conversationInfo.ACL[conversationInfo.Handle]; exists && acl.Blocked {
|
||||||
|
authorization = model.AuthBlocked
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we are a server...
|
||||||
|
groupServer, _ := conversationInfo.GetAttribute(attr.LocalScope, attr.LegacyGroupZone, constants.GroupServer)
|
||||||
|
|
||||||
|
// Fetch the message count, and the time of the most recent message
|
||||||
|
count, err := profile.GetChannelMessageCount(conversationInfo.ID, 0)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error fetching channel message count %v %v", conversationInfo.ID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
lastMessage, _ := profile.GetMostRecentMessages(conversationInfo.ID, 0, 0, 1)
|
||||||
|
|
||||||
|
contacts = append(contacts, Contact{
|
||||||
|
Name: name,
|
||||||
|
Identifier: conversationInfo.ID,
|
||||||
|
Onion: conversationInfo.Handle,
|
||||||
|
Status: connections.ConnectionStateName[state],
|
||||||
|
Picture: cpicPath,
|
||||||
|
Authorization: string(authorization),
|
||||||
|
SaveHistory: saveHistory,
|
||||||
|
Messages: count,
|
||||||
|
Unread: 0,
|
||||||
|
LastMessage: strconv.Itoa(getLastMessageTime(lastMessage)),
|
||||||
|
IsGroup: conversationInfo.IsGroup(),
|
||||||
|
GroupServer: groupServer,
|
||||||
|
IsArchived: isArchived == event.True,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
ph := NewPeerHelper(profile)
|
|
||||||
cpicPath := ph.GetProfilePic(groupId)
|
|
||||||
|
|
||||||
authorization := model.AuthUnknown
|
|
||||||
if group.Accepted {
|
|
||||||
authorization = model.AuthApproved
|
|
||||||
}
|
|
||||||
isArchived, set := group.GetAttribute(attr.GetLocalScope(constants2.Archived))
|
|
||||||
if !set {
|
|
||||||
isArchived = event.False
|
|
||||||
}
|
|
||||||
// Use the server state when assessing group state
|
|
||||||
state := profile.GetContact(group.GroupServer).State
|
|
||||||
|
|
||||||
contacts = append(contacts, Contact{
|
|
||||||
Name: ph.GetNick(groupId),
|
|
||||||
Onion: group.GroupID,
|
|
||||||
Status: state,
|
|
||||||
Picture: cpicPath,
|
|
||||||
Authorization: string(authorization),
|
|
||||||
SaveHistory: event.SaveHistoryConfirmed,
|
|
||||||
Messages: group.Timeline.Len(),
|
|
||||||
Unread: 0,
|
|
||||||
LastMessage: strconv.Itoa(getLastMessageTime(&group.Timeline)),
|
|
||||||
IsGroup: true,
|
|
||||||
GroupServer: group.GroupServer,
|
|
||||||
IsArchived: isArchived == event.True,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes, _ := json.Marshal(contacts)
|
bytes, _ := json.Marshal(contacts)
|
||||||
|
@ -239,56 +247,95 @@ func (eh *EventHandler) handleProfileEvent(ev *EventProfileEnvelope) string {
|
||||||
if eh.app == nil {
|
if eh.app == nil {
|
||||||
log.Errorf("eh.app == nil in handleProfileEvent... this shouldnt happen?")
|
log.Errorf("eh.app == nil in handleProfileEvent... this shouldnt happen?")
|
||||||
} else {
|
} else {
|
||||||
dan marked this conversation as resolved
Outdated
dan
commented
ph.GetNick checks Local.Name first and then falls back to Peer.Name so that we can locally rename a contact. the current code now always uses the remote peer's name ignoring local renames. this breaks existing functionality ph.GetNick checks Local.Name first and then falls back to Peer.Name so that we can locally rename a contact. the current code now always uses the remote peer's name ignoring local renames. this breaks existing functionality
|
|||||||
peer := eh.app.GetPeer(ev.Profile)
|
profile := eh.app.GetPeer(ev.Profile)
|
||||||
ph := NewPeerHelper(peer)
|
|
||||||
log.Debugf("New Profile Event to Handle: %v", ev)
|
log.Debugf("New Profile Event to Handle: %v", ev)
|
||||||
switch ev.Event.EventType {
|
switch ev.Event.EventType {
|
||||||
|
|
||||||
/*
|
|
||||||
TODO: still handle this somewhere - network info from plugin Network check
|
|
||||||
case event.NetworkStatus:
|
|
||||||
online, _ := peer.GetAttribute(attr.GetLocalScope(constants.PeerOnline))
|
|
||||||
if e.Data[event.Status] == plugins.NetworkCheckSuccess && online == event.False {
|
|
||||||
peer.SetAttribute(attr.GetLocalScope(constants.PeerOnline), event.True)
|
|
||||||
uiManager.UpdateNetworkStatus(true)
|
|
||||||
// TODO we may have to reinitialize the peer
|
|
||||||
} else if e.Data[event.Status] == plugins.NetworkCheckError && online == event.True {
|
|
||||||
peer.SetAttribute(attr.GetLocalScope(constants.PeerOnline), event.False)
|
|
||||||
uiManager.UpdateNetworkStatus(false)
|
|
||||||
}*/
|
|
||||||
|
|
||||||
case event.NewMessageFromPeer: //event.TimestampReceived, event.RemotePeer, event.Data
|
case event.NewMessageFromPeer: //event.TimestampReceived, event.RemotePeer, event.Data
|
||||||
// only needs contact nickname and picture, for displaying on popup notifications
|
// only needs contact nickname and picture, for displaying on popup notifications
|
||||||
ev.Event.Data["Nick"] = ph.GetNick(ev.Event.Data["RemotePeer"])
|
ci, err := profile.FetchConversationInfo(ev.Event.Data["RemotePeer"])
|
||||||
ev.Event.Data["Picture"] = ph.GetProfilePic(ev.Event.Data["RemotePeer"])
|
if ci != nil && err == nil {
|
||||||
dan marked this conversation as resolved
Outdated
dan
commented
same. ph.Nick regressoin same. ph.Nick regressoin
|
|||||||
peer.SetContactAttribute(ev.Event.Data["RemotePeer"], attr.GetLocalScope(constants2.Archived), event.False)
|
ev.Event.Data[event.ConversationID] = strconv.Itoa(ci.ID)
|
||||||
case event.NewMessageFromGroup:
|
profile.SetConversationAttribute(ci.ID, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants2.Archived)), event.False)
|
||||||
// only needs contact nickname and picture, for displaying on popup notifications
|
} else {
|
||||||
ev.Event.Data["Nick"] = ph.GetNick(ev.Event.Data[event.GroupID])
|
// TODO This Conversation May Not Exist Yet...But we are not in charge of creating it...
|
||||||
ev.Event.Data["Picture"] = ph.GetProfilePic(ev.Event.Data[event.GroupID])
|
log.Errorf("todo wait for contact to be added before processing this event...")
|
||||||
peer.SetGroupAttribute(ev.Event.Data[event.GroupID], attr.GetLocalScope(constants2.Archived), event.False)
|
|
||||||
case event.PeerAcknowledgement:
|
|
||||||
// No enrichement required
|
|
||||||
case event.PeerCreated:
|
|
||||||
handle := ev.Event.Data[event.RemotePeer]
|
|
||||||
err := EnrichNewPeer(handle, ph, ev)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
var exists bool
|
||||||
|
ev.Event.Data["Nick"], exists = ci.GetAttribute(attr.LocalScope, attr.ProfileZone, constants.Name)
|
||||||
|
if !exists {
|
||||||
|
ev.Event.Data["Nick"], exists = ci.GetAttribute(attr.PublicScope, attr.ProfileZone, constants.Name)
|
||||||
dan marked this conversation as resolved
Outdated
dan
commented
fall back to handle if we don't have a public.profile.name? fall back to handle if we don't have a public.profile.name?
|
|||||||
|
if !exists {
|
||||||
|
ev.Event.Data["Nick"] = ev.Event.Data["RemotePeer"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ev.Event.Data["Picture"] = RandomProfileImage(ev.Event.Data["RemotePeer"])
|
||||||
|
|
||||||
|
case event.NewMessageFromGroup:
|
||||||
|
// only needs contact nickname and picture, for displaying on popup notifications
|
||||||
dan marked this conversation as resolved
Outdated
dan
commented
did we just copy the EnrichNewPeer function into place here? did we just copy the EnrichNewPeer function into place here?
sarah
commented
yes yes
|
|||||||
|
ci, err := profile.FetchConversationInfo(ev.Event.Data["RemotePeer"])
|
||||||
|
if ci != nil && err == nil {
|
||||||
|
var exists bool
|
||||||
dan marked this conversation as resolved
dan
commented
fall back to handle if neither exist. in a group setting we are very likely not to be peered with everyone (there for unable to fetch names) fall back to handle if neither exist. in a group setting we are very likely not to be peered with everyone (there for unable to fetch names)
|
|||||||
|
ev.Event.Data["Nick"], exists = ci.GetAttribute(attr.LocalScope, attr.ProfileZone, constants.Name)
|
||||||
|
if !exists {
|
||||||
|
ev.Event.Data["Nick"], exists = ci.GetAttribute(attr.PublicScope, attr.ProfileZone, constants.Name)
|
||||||
|
if !exists {
|
||||||
|
ev.Event.Data["Nick"] = ev.Event.Data["RemotePeer"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ev.Event.Data["Picture"] = RandomProfileImage(ev.Event.Data[event.RemotePeer])
|
||||||
|
conversationID, _ := strconv.Atoi(ev.Event.Data[event.ConversationID])
|
||||||
|
profile.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants2.Archived)), event.False)
|
||||||
|
case event.PeerAcknowledgement:
|
||||||
|
ci, err := profile.FetchConversationInfo(ev.Event.Data["RemotePeer"])
|
||||||
|
if ci != nil && err == nil {
|
||||||
|
ev.Event.Data[event.ConversationID] = strconv.Itoa(ci.ID)
|
||||||
|
}
|
||||||
|
case event.ContactCreated:
|
||||||
|
conversationID, _ := strconv.Atoi(ev.Event.Data[event.ConversationID])
|
||||||
|
count, err := profile.GetChannelMessageCount(conversationID, 0)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error fetching channel message count %v %v", conversationID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
conversationInfo, err := profile.GetConversationInfo(conversationID)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error fetching conversation info for %v %v", conversationID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve Conversation Auth State
|
||||||
|
// TODO: Align this with ACLs
|
||||||
|
authorization := model.AuthUnknown
|
||||||
|
if conversationInfo.Accepted {
|
||||||
|
authorization = model.AuthApproved
|
||||||
|
}
|
||||||
|
|
||||||
|
// If ACL has blocked conversation then hide them...
|
||||||
|
if acl, exists := conversationInfo.ACL[conversationInfo.Handle]; exists && acl.Blocked {
|
||||||
|
authorization = model.AuthBlocked
|
||||||
|
}
|
||||||
|
|
||||||
|
lastMessage, _ := profile.GetMostRecentMessages(conversationID, 0, 0, 1)
|
||||||
|
ev.Event.Data["unread"] = strconv.Itoa(count) // if this is a new contact with messages attached then by-definition these are unread...
|
||||||
|
ev.Event.Data["picture"] = RandomProfileImage(conversationInfo.Handle)
|
||||||
|
ev.Event.Data["numMessages"] = strconv.Itoa(count)
|
||||||
|
ev.Event.Data["nick"] = conversationInfo.Handle
|
||||||
|
ev.Event.Data["status"] = connections.ConnectionStateName[profile.GetPeerState(conversationInfo.Handle)]
|
||||||
|
ev.Event.Data["authorization"] = string(authorization)
|
||||||
|
ev.Event.Data["loading"] = "false"
|
||||||
|
ev.Event.Data["lastMsgTime"] = strconv.Itoa(getLastMessageTime(lastMessage))
|
||||||
case event.GroupCreated:
|
case event.GroupCreated:
|
||||||
// This event should only happen after we have validated the invite, as such the error
|
// This event should only happen after we have validated the invite, as such the error
|
||||||
// condition *should* never happen.
|
// condition *should* never happen.
|
||||||
|
groupPic := RandomGroupImage(ev.Event.Data[event.GroupID])
|
||||||
groupPic := ph.GetProfilePic(ev.Event.Data[event.GroupID])
|
|
||||||
ev.Event.Data["PicturePath"] = groupPic
|
ev.Event.Data["PicturePath"] = groupPic
|
||||||
ev.Event.Data["GroupName"] = ph.GetNick(ev.Event.Data[event.GroupID])
|
|
||||||
|
|
||||||
case event.NewGroup:
|
case event.NewGroup:
|
||||||
// This event should only happen after we have validated the invite, as such the error
|
// This event should only happen after we have validated the invite, as such the error
|
||||||
// condition *should* never happen.
|
// condition *should* never happen.
|
||||||
serializedInvite := ev.Event.Data[event.GroupInvite]
|
serializedInvite := ev.Event.Data[event.GroupInvite]
|
||||||
if invite, err := model.ValidateInvite(serializedInvite); err == nil {
|
if invite, err := model.ValidateInvite(serializedInvite); err == nil {
|
||||||
groupPic := ph.GetProfilePic(invite.GroupID)
|
groupPic := RandomGroupImage(invite.GroupID)
|
||||||
ev.Event.Data["PicturePath"] = groupPic
|
ev.Event.Data["PicturePath"] = groupPic
|
||||||
} else {
|
} else {
|
||||||
log.Errorf("received a new group event which contained an invalid invite %v. this should never happen and likely means there is a bug in cwtch. Please file a ticket @ https://git.openprivcy.ca/cwtch.im/cwtch", err)
|
log.Errorf("received a new group event which contained an invalid invite %v. this should never happen and likely means there is a bug in cwtch. Please file a ticket @ https://git.openprivcy.ca/cwtch.im/cwtch", err)
|
||||||
|
@ -296,10 +343,15 @@ func (eh *EventHandler) handleProfileEvent(ev *EventProfileEnvelope) string {
|
||||||
}
|
}
|
||||||
case event.PeerStateChange:
|
case event.PeerStateChange:
|
||||||
cxnState := connections.ConnectionStateToType()[ev.Event.Data[event.ConnectionState]]
|
cxnState := connections.ConnectionStateToType()[ev.Event.Data[event.ConnectionState]]
|
||||||
contact := peer.GetContact(ev.Event.Data[event.RemotePeer])
|
contact, _ := profile.FetchConversationInfo(ev.Event.Data[event.RemotePeer])
|
||||||
|
|
||||||
|
if ev.Event.Data[event.RemotePeer] == profile.GetOnion() {
|
||||||
|
return "" // suppress events from our own profile...
|
||||||
|
}
|
||||||
|
|
||||||
|
// We do not know who this is...don't send any event until we see a message from them
|
||||||
|
// (at that point the conversation will have been created...)
|
||||||
if cxnState == connections.AUTHENTICATED && contact == nil {
|
if cxnState == connections.AUTHENTICATED && contact == nil {
|
||||||
peer.AddContact(ev.Event.Data[event.RemotePeer], ev.Event.Data[event.RemotePeer], model.AuthUnknown)
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,21 +360,21 @@ func (eh *EventHandler) handleProfileEvent(ev *EventProfileEnvelope) string {
|
||||||
//uiManager.UpdateContactStatus(contact.Onion, int(cxnState), false)
|
//uiManager.UpdateContactStatus(contact.Onion, int(cxnState), false)
|
||||||
if cxnState == connections.AUTHENTICATED {
|
if cxnState == connections.AUTHENTICATED {
|
||||||
// if known and authed, get vars
|
// if known and authed, get vars
|
||||||
peer.SendScopedZonedGetValToContact(ev.Event.Data[event.RemotePeer], attr.PublicScope, attr.ProfileZone, constants.Name)
|
profile.SendScopedZonedGetValToContact(contact.ID, attr.PublicScope, attr.ProfileZone, constants.Name)
|
||||||
peer.SendScopedZonedGetValToContact(ev.Event.Data[event.RemotePeer], attr.PublicScope, attr.ProfileZone, constants2.Picture)
|
profile.SendScopedZonedGetValToContact(contact.ID, attr.PublicScope, attr.ProfileZone, constants2.Picture)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case event.NewRetValMessageFromPeer:
|
case event.NewRetValMessageFromPeer:
|
||||||
// auto handled event means the setting is already done, we're just deciding if we need to tell the UI
|
// auto handled event means the setting is already done, we're just deciding if we need to tell the UI
|
||||||
onion := ev.Event.Data[event.RemotePeer]
|
conversationID, _ := strconv.Atoi(ev.Event.Data[event.ConversationID])
|
||||||
scope := ev.Event.Data[event.Scope]
|
scope := ev.Event.Data[event.Scope]
|
||||||
path := ev.Event.Data[event.Path]
|
path := ev.Event.Data[event.Path]
|
||||||
//val := ev.Event.Data[event.Data]
|
|
||||||
exists, _ := strconv.ParseBool(ev.Event.Data[event.Exists])
|
exists, _ := strconv.ParseBool(ev.Event.Data[event.Exists])
|
||||||
|
|
||||||
if exists && attr.IntoScope(scope) == attr.PublicScope {
|
if exists && attr.IntoScope(scope) == attr.PublicScope {
|
||||||
if _, exists := peer.GetContactAttribute(onion, attr.GetLocalScope(path)); exists {
|
zone, path := attr.ParseZone(path)
|
||||||
|
if _, err := profile.GetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(zone.ConstructZonedPath(path))); err != nil {
|
||||||
// we have a locally set override, don't pass this remote set public scope update to UI
|
// we have a locally set override, don't pass this remote set public scope update to UI
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -343,12 +395,17 @@ func unwrap(original *EventProfileEnvelope) *event.Event {
|
||||||
func (eh *EventHandler) startHandlingPeer(onion string) {
|
func (eh *EventHandler) startHandlingPeer(onion string) {
|
||||||
eventBus := eh.app.GetEventBus(onion)
|
eventBus := eh.app.GetEventBus(onion)
|
||||||
q := event.NewQueue()
|
q := event.NewQueue()
|
||||||
|
|
||||||
|
// eventBus.Subscribe(event.NetworkStatus, q)
|
||||||
|
|
||||||
eventBus.Subscribe(event.NewMessageFromPeer, q)
|
eventBus.Subscribe(event.NewMessageFromPeer, q)
|
||||||
|
eventBus.Subscribe(event.UpdatedProfileAttribute, q)
|
||||||
eventBus.Subscribe(event.PeerAcknowledgement, q)
|
eventBus.Subscribe(event.PeerAcknowledgement, q)
|
||||||
eventBus.Subscribe(event.DeleteContact, q)
|
eventBus.Subscribe(event.DeleteContact, q)
|
||||||
eventBus.Subscribe(event.AppError, q)
|
eventBus.Subscribe(event.AppError, q)
|
||||||
eventBus.Subscribe(event.IndexedAcknowledgement, q)
|
eventBus.Subscribe(event.IndexedAcknowledgement, q)
|
||||||
eventBus.Subscribe(event.IndexedFailure, q)
|
eventBus.Subscribe(event.IndexedFailure, q)
|
||||||
|
eventBus.Subscribe(event.ContactCreated, q)
|
||||||
eventBus.Subscribe(event.NewMessageFromGroup, q)
|
eventBus.Subscribe(event.NewMessageFromGroup, q)
|
||||||
eventBus.Subscribe(event.MessageCounterResync, q)
|
eventBus.Subscribe(event.MessageCounterResync, q)
|
||||||
eventBus.Subscribe(event.GroupCreated, q)
|
eventBus.Subscribe(event.GroupCreated, q)
|
||||||
|
@ -361,8 +418,6 @@ func (eh *EventHandler) startHandlingPeer(onion string) {
|
||||||
eventBus.Subscribe(event.SendMessageToPeerError, q)
|
eventBus.Subscribe(event.SendMessageToPeerError, q)
|
||||||
eventBus.Subscribe(event.ServerStateChange, q)
|
eventBus.Subscribe(event.ServerStateChange, q)
|
||||||
eventBus.Subscribe(event.PeerStateChange, q)
|
eventBus.Subscribe(event.PeerStateChange, q)
|
||||||
eventBus.Subscribe(event.PeerCreated, q)
|
|
||||||
eventBus.Subscribe(event.NetworkStatus, q)
|
|
||||||
eventBus.Subscribe(event.ChangePasswordSuccess, q)
|
eventBus.Subscribe(event.ChangePasswordSuccess, q)
|
||||||
eventBus.Subscribe(event.ChangePasswordError, q)
|
eventBus.Subscribe(event.ChangePasswordError, q)
|
||||||
eventBus.Subscribe(event.NewRetValMessageFromPeer, q)
|
eventBus.Subscribe(event.NewRetValMessageFromPeer, q)
|
||||||
|
@ -395,3 +450,14 @@ func (eh *EventHandler) forwardProfileMessages(onion string, q event.Queue) {
|
||||||
func (eh *EventHandler) Push(newEvent event.Event) {
|
func (eh *EventHandler) Push(newEvent event.Event) {
|
||||||
eh.appBusQueue.Publish(newEvent)
|
eh.appBusQueue.Publish(newEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getLastMessageTime(conversationMessages []model.ConversationMessage) int {
|
||||||
|
if len(conversationMessages) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
time, err := time.Parse(time.RFC3339Nano, conversationMessages[0].Attr[constants.AttrSentTimestamp])
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return int(time.Unix())
|
||||||
|
}
|
||||||
|
|
251
utils/manager.go
|
@ -1,251 +0,0 @@
|
||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"cwtch.im/cwtch/model"
|
|
||||||
"cwtch.im/cwtch/model/attr"
|
|
||||||
"cwtch.im/cwtch/peer"
|
|
||||||
"cwtch.im/cwtch/protocol/connections"
|
|
||||||
"errors"
|
|
||||||
"git.openprivacy.ca/cwtch.im/libcwtch-go/constants"
|
|
||||||
"git.openprivacy.ca/openprivacy/log"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PeerHelper struct {
|
|
||||||
peer peer.CwtchPeer
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPeerHelper(profile peer.CwtchPeer) *PeerHelper {
|
|
||||||
return &PeerHelper{profile}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PeerHelper) IsGroup(id string) bool {
|
|
||||||
return len(id) == 32 && !p.IsServer(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PeerHelper) IsPeer(id string) bool {
|
|
||||||
return len(id) == 56 && !p.IsServer(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the id is associated with a contact with a KeyTypeServerOnion attribute (which indicates that this
|
|
||||||
// is a server, not a regular contact or a group
|
|
||||||
func (p *PeerHelper) IsServer(id string) bool {
|
|
||||||
_, ok := p.peer.GetContactAttribute(id, string(model.KeyTypeServerOnion))
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTimeline returns a pointer to the timeline associated with the conversation handle or nil if the handle
|
|
||||||
// does not exist (this can happen if the conversation has been deleted)
|
|
||||||
func (p *PeerHelper) GetTimeline(handle string) *model.Timeline {
|
|
||||||
if p.IsServer(handle) {
|
|
||||||
// This should *never* happen
|
|
||||||
log.Errorf("server accessed as contact when getting timeline...")
|
|
||||||
return &model.Timeline{}
|
|
||||||
}
|
|
||||||
// We return a pointer to the timeline to avoid copying, accessing Timeline is thread-safe
|
|
||||||
if p.IsGroup(handle) {
|
|
||||||
group := p.peer.GetGroup(handle)
|
|
||||||
if group == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &group.Timeline
|
|
||||||
}
|
|
||||||
contact := p.peer.GetContact(handle)
|
|
||||||
if contact == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &contact.Timeline
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
func getOrDefault(id, key string, defaultVal string) string {
|
|
||||||
var val string
|
|
||||||
var ok bool
|
|
||||||
if IsGroup(id) {
|
|
||||||
val, ok = the.Peer.GetGroupAttribute(id, key)
|
|
||||||
} else {
|
|
||||||
val, ok = the.Peer.GetContactAttribute(id, key)
|
|
||||||
}
|
|
||||||
if ok {
|
|
||||||
return val
|
|
||||||
} else {
|
|
||||||
return defaultVal
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
func (p *PeerHelper) GetWithSetDefault(id string, key string, defaultVal string) string {
|
|
||||||
var val string
|
|
||||||
var ok bool
|
|
||||||
if p.IsGroup(id) {
|
|
||||||
val, ok = p.peer.GetGroupAttribute(id, key)
|
|
||||||
} else {
|
|
||||||
val, ok = p.peer.GetContactAttribute(id, key)
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
val = defaultVal
|
|
||||||
if p.IsGroup(id) {
|
|
||||||
p.peer.SetGroupAttribute(id, key, defaultVal)
|
|
||||||
} else {
|
|
||||||
p.peer.SetContactAttribute(id, key, defaultVal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PeerHelper) GetNick(id string) string {
|
|
||||||
if p.IsGroup(id) {
|
|
||||||
nick, exists := p.peer.GetGroupAttribute(id, attr.GetLocalScope(constants.Name))
|
|
||||||
if !exists || nick == "" || nick == id {
|
|
||||||
nick, exists = p.peer.GetGroupAttribute(id, attr.GetPeerScope(constants.Name))
|
|
||||||
if !exists {
|
|
||||||
nick = "[" + id + "]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nick
|
|
||||||
} else {
|
|
||||||
nick, exists := p.peer.GetContactAttribute(id, attr.GetLocalScope(constants.Name))
|
|
||||||
if !exists || nick == "" || nick == id {
|
|
||||||
nick, exists = p.peer.GetContactAttribute(id, attr.GetPeerScope(constants.Name))
|
|
||||||
if !exists {
|
|
||||||
nick = "[" + id + "]"
|
|
||||||
// we do not have a canonical nick for this contact.
|
|
||||||
// re-request if authenticated
|
|
||||||
// TODO: This check probably doesn't belong here...
|
|
||||||
if contact := p.peer.GetContact(id); contact != nil && contact.State == connections.ConnectionStateName[connections.AUTHENTICATED] {
|
|
||||||
p.peer.SendScopedZonedGetValToContact(id, attr.PublicScope, attr.ProfileZone, constants.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nick
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitLastReadTime checks and gets the Attributable's LastRead time or sets it to now
|
|
||||||
func (p *PeerHelper) InitLastReadTime(id string) time.Time {
|
|
||||||
nowStr, _ := time.Now().MarshalText()
|
|
||||||
lastReadAttr := p.GetWithSetDefault(id, attr.GetLocalScope(constants.LastRead), string(nowStr))
|
|
||||||
var lastRead time.Time
|
|
||||||
lastRead.UnmarshalText([]byte(lastReadAttr))
|
|
||||||
return lastRead
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetProfilePic returns a string path to an image to display for hte given peer/group id
|
|
||||||
func (p *PeerHelper) GetProfilePic(id string) string {
|
|
||||||
if p.IsGroup(id) {
|
|
||||||
if picVal, exists := p.peer.GetGroupAttribute(id, attr.GetLocalScope(constants.Picture)); exists {
|
|
||||||
pic, err := StringToImage(picVal)
|
|
||||||
if err == nil {
|
|
||||||
return GetPicturePath(pic)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if picVal, exists := p.peer.GetGroupAttribute(id, attr.GetPeerScope(constants.Picture)); exists {
|
|
||||||
pic, err := StringToImage(picVal)
|
|
||||||
if err == nil {
|
|
||||||
return GetPicturePath(pic)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return GetPicturePath(NewImage(RandomGroupImage(id), TypeImageDistro))
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if picVal, exists := p.peer.GetContactAttribute(id, attr.GetLocalScope(constants.Picture)); exists {
|
|
||||||
pic, err := StringToImage(picVal)
|
|
||||||
if err == nil {
|
|
||||||
return GetPicturePath(pic)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if picVal, exists := p.peer.GetContactAttribute(id, attr.GetPeerScope(constants.Picture)); exists {
|
|
||||||
pic, err := StringToImage(picVal)
|
|
||||||
if err == nil {
|
|
||||||
return GetPicturePath(pic)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return RandomProfileImage(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// a lot of pics were stored full path + uri. remove all this to the relative path in images/
|
|
||||||
// fix for storing full paths introduced 2019.12
|
|
||||||
func profilePicRelativize(filename string) string {
|
|
||||||
parts := strings.Split(filename, "qml/images")
|
|
||||||
return parts[len(parts)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetPicturePath(pic *image) string {
|
|
||||||
switch pic.T {
|
|
||||||
case TypeImageDistro:
|
|
||||||
return profilePicRelativize(pic.Val)
|
|
||||||
default:
|
|
||||||
log.Errorf("Unhandled profile picture type of %v\n", pic.T)
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PeerHelper) CountUnread(messages []model.Message, lastRead time.Time) int {
|
|
||||||
count := 0
|
|
||||||
for i := len(messages) - 1; i >= 0; i-- {
|
|
||||||
if messages[i].Timestamp.After(lastRead) || messages[i].Timestamp.Equal(lastRead) {
|
|
||||||
count++
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
|
|
||||||
func getLastMessageTime(tl *model.Timeline) int {
|
|
||||||
if len(tl.Messages) == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return int(tl.Messages[len(tl.Messages)-1].Timestamp.Unix())
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnrichNewPeer populates required data for use by frontend
|
|
||||||
// uiManager.AddContact(onion)
|
|
||||||
// (handle string, displayName string, image string, badge int, status int, authorization string, loading bool, lastMsgTime int)
|
|
||||||
func EnrichNewPeer(handle string, ph *PeerHelper, ev *EventProfileEnvelope) error {
|
|
||||||
if ph.IsGroup(handle) {
|
|
||||||
group := ph.peer.GetGroup(handle)
|
|
||||||
if group != nil {
|
|
||||||
lastRead := ph.InitLastReadTime(group.GroupID)
|
|
||||||
ev.Event.Data["unread"] = strconv.Itoa(ph.CountUnread(group.Timeline.GetMessages(), lastRead))
|
|
||||||
ev.Event.Data["picture"] = ph.GetProfilePic(handle)
|
|
||||||
ev.Event.Data["numMessages"] = strconv.Itoa(group.Timeline.Len())
|
|
||||||
ev.Event.Data["nick"] = ph.GetNick(handle)
|
|
||||||
ev.Event.Data["status"] = group.State
|
|
||||||
ev.Event.Data["authorization"] = string(model.AuthApproved)
|
|
||||||
ev.Event.Data["loading"] = "false"
|
|
||||||
ev.Event.Data["lastMsgTime"] = strconv.Itoa(getLastMessageTime(&group.Timeline))
|
|
||||||
}
|
|
||||||
} else if ph.IsPeer(handle) {
|
|
||||||
contact := ph.peer.GetContact(handle)
|
|
||||||
if contact != nil {
|
|
||||||
lastRead := ph.InitLastReadTime(contact.Onion)
|
|
||||||
ev.Event.Data["unread"] = strconv.Itoa(ph.CountUnread(contact.Timeline.GetMessages(), lastRead))
|
|
||||||
ev.Event.Data["numMessages"] = strconv.Itoa(contact.Timeline.Len())
|
|
||||||
ev.Event.Data["picture"] = ph.GetProfilePic(handle)
|
|
||||||
|
|
||||||
ev.Event.Data["nick"] = ph.GetNick(handle)
|
|
||||||
|
|
||||||
// TODO Replace this if with a better flow that separates New Contacts and Peering Updates
|
|
||||||
if contact.State == "" {
|
|
||||||
// Will be disconnected to start
|
|
||||||
ev.Event.Data["status"] = connections.ConnectionStateName[connections.DISCONNECTED]
|
|
||||||
} else {
|
|
||||||
ev.Event.Data["status"] = contact.State
|
|
||||||
}
|
|
||||||
ev.Event.Data["authorization"] = string(contact.Authorization)
|
|
||||||
ev.Event.Data["loading"] = "false"
|
|
||||||
ev.Event.Data["lastMsgTime"] = strconv.Itoa(getLastMessageTime(&contact.Timeline))
|
|
||||||
} else {
|
|
||||||
log.Errorf("Failed to find contact: %v", handle)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// could be a server?
|
|
||||||
log.Debugf("sorry, unable to handle AddContact(%v)", handle)
|
|
||||||
return errors.New("not a peer or group")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -12,7 +12,7 @@ func RandomProfileImage(onion string) string {
|
||||||
choices := []string{"001-centaur", "002-kraken", "003-dinosaur", "004-tree-1", "005-hand", "006-echidna", "007-robot", "008-mushroom", "009-harpy", "010-phoenix", "011-dragon-1", "012-devil", "013-troll", "014-alien", "015-minotaur", "016-madre-monte", "017-satyr", "018-karakasakozou", "019-pirate", "020-werewolf", "021-scarecrow", "022-valkyrie", "023-curupira", "024-loch-ness-monster", "025-tree", "026-cerberus", "027-gryphon", "028-mermaid", "029-vampire", "030-goblin", "031-yeti", "032-leprechaun", "033-medusa", "034-chimera", "035-elf", "036-hydra", "037-cyclops", "038-pegasus", "039-narwhal", "040-woodcutter", "041-zombie", "042-dragon", "043-frankenstein", "044-witch", "045-fairy", "046-genie", "047-pinocchio", "048-ghost", "049-wizard", "050-unicorn"}
|
choices := []string{"001-centaur", "002-kraken", "003-dinosaur", "004-tree-1", "005-hand", "006-echidna", "007-robot", "008-mushroom", "009-harpy", "010-phoenix", "011-dragon-1", "012-devil", "013-troll", "014-alien", "015-minotaur", "016-madre-monte", "017-satyr", "018-karakasakozou", "019-pirate", "020-werewolf", "021-scarecrow", "022-valkyrie", "023-curupira", "024-loch-ness-monster", "025-tree", "026-cerberus", "027-gryphon", "028-mermaid", "029-vampire", "030-goblin", "031-yeti", "032-leprechaun", "033-medusa", "034-chimera", "035-elf", "036-hydra", "037-cyclops", "038-pegasus", "039-narwhal", "040-woodcutter", "041-zombie", "042-dragon", "043-frankenstein", "044-witch", "045-fairy", "046-genie", "047-pinocchio", "048-ghost", "049-wizard", "050-unicorn"}
|
||||||
barr, err := base32.StdEncoding.DecodeString(strings.ToUpper(onion))
|
barr, err := base32.StdEncoding.DecodeString(strings.ToUpper(onion))
|
||||||
if err != nil || len(barr) != 35 {
|
if err != nil || len(barr) != 35 {
|
||||||
log.Errorf("error: %v %v %v\n", onion, err, barr)
|
log.Errorf("error finding image for profile: %v %v %v\n", onion, err, barr)
|
||||||
return "extra/openprivacy.png"
|
return "extra/openprivacy.png"
|
||||||
}
|
}
|
||||||
return "profiles/" + choices[int(barr[33])%len(choices)] + ".png"
|
return "profiles/" + choices[int(barr[33])%len(choices)] + ".png"
|
||||||
|
@ -22,7 +22,7 @@ func RandomGroupImage(handle string) string {
|
||||||
choices := []string{"001-borobudur", "002-opera-house", "003-burj-al-arab", "004-chrysler", "005-acropolis", "006-empire-state-building", "007-temple", "008-indonesia-1", "009-new-zealand", "010-notre-dame", "011-space-needle", "012-seoul", "013-mosque", "014-milan", "015-statue", "016-pyramid", "017-cologne", "018-brandenburg-gate", "019-berlin-cathedral", "020-hungarian-parliament", "021-buckingham", "022-thailand", "023-independence", "024-angkor-wat", "025-vaticano", "026-christ-the-redeemer", "027-colosseum", "028-golden-gate-bridge", "029-sphinx", "030-statue-of-liberty", "031-cradle-of-humankind", "032-istanbul", "033-london-eye", "034-sagrada-familia", "035-tower-bridge", "036-burj-khalifa", "037-washington", "038-big-ben", "039-stonehenge", "040-white-house", "041-ahu-tongariki", "042-capitol", "043-eiffel-tower", "044-church-of-the-savior-on-spilled-blood", "045-arc-de-triomphe", "046-windmill", "047-louvre", "048-torii-gate", "049-petronas", "050-matsumoto-castle", "051-fuji", "052-temple-of-heaven", "053-pagoda", "054-chichen-itza", "055-forbidden-city", "056-merlion", "057-great-wall-of-china", "058-taj-mahal", "059-pisa", "060-indonesia"}
|
choices := []string{"001-borobudur", "002-opera-house", "003-burj-al-arab", "004-chrysler", "005-acropolis", "006-empire-state-building", "007-temple", "008-indonesia-1", "009-new-zealand", "010-notre-dame", "011-space-needle", "012-seoul", "013-mosque", "014-milan", "015-statue", "016-pyramid", "017-cologne", "018-brandenburg-gate", "019-berlin-cathedral", "020-hungarian-parliament", "021-buckingham", "022-thailand", "023-independence", "024-angkor-wat", "025-vaticano", "026-christ-the-redeemer", "027-colosseum", "028-golden-gate-bridge", "029-sphinx", "030-statue-of-liberty", "031-cradle-of-humankind", "032-istanbul", "033-london-eye", "034-sagrada-familia", "035-tower-bridge", "036-burj-khalifa", "037-washington", "038-big-ben", "039-stonehenge", "040-white-house", "041-ahu-tongariki", "042-capitol", "043-eiffel-tower", "044-church-of-the-savior-on-spilled-blood", "045-arc-de-triomphe", "046-windmill", "047-louvre", "048-torii-gate", "049-petronas", "050-matsumoto-castle", "051-fuji", "052-temple-of-heaven", "053-pagoda", "054-chichen-itza", "055-forbidden-city", "056-merlion", "057-great-wall-of-china", "058-taj-mahal", "059-pisa", "060-indonesia"}
|
||||||
barr, err := hex.DecodeString(handle)
|
barr, err := hex.DecodeString(handle)
|
||||||
if err != nil || len(barr) == 0 {
|
if err != nil || len(barr) == 0 {
|
||||||
log.Errorf("error: %v %v %v\n", handle, err, barr)
|
log.Errorf("error finding image for group: %v %v %v\n", handle, err, barr)
|
||||||
return "extra/openprivacy.png"
|
return "extra/openprivacy.png"
|
||||||
}
|
}
|
||||||
return "servers/" + choices[int(barr[0])%len(choices)] + ".png"
|
return "servers/" + choices[int(barr[0])%len(choices)] + ".png"
|
||||||
|
|
this event is still defined in cwtch/event/common but seems unused. is the UI migrating off this functionality? or do we need to port it into Cwtch?