First cut of Importing Legacy Profiles
continuous-integration/drone/push Build is pending
Details
continuous-integration/drone/push Build is pending
Details
This commit is contained in:
parent
e296c30818
commit
cfff858fe1
39
app/app.go
39
app/app.go
|
@ -157,7 +157,6 @@ func (ac *applicationCore) LoadProfiles(password string, timeline bool, loadProf
|
|||
}
|
||||
|
||||
for _, file := range files {
|
||||
|
||||
// Attempt to load an encrypted database
|
||||
profileDirectory := path.Join(ac.directory, "profiles", file.Name())
|
||||
profile, err := peer.FromEncryptedDatabase(profileDirectory, password)
|
||||
|
@ -167,7 +166,7 @@ func (ac *applicationCore) LoadProfiles(password string, timeline bool, loadProf
|
|||
loadProfileFn(profile)
|
||||
}
|
||||
|
||||
// On failure
|
||||
// On failure attempt to load a legacy profile
|
||||
if err != nil {
|
||||
eventBus := event.NewEventManager()
|
||||
|
||||
|
@ -176,19 +175,11 @@ func (ac *applicationCore) LoadProfiles(password string, timeline bool, loadProf
|
|||
continue
|
||||
}
|
||||
|
||||
profile := profileStore.GetProfileCopy(timeline)
|
||||
|
||||
_, exists := ac.eventBuses[profile.Onion]
|
||||
if exists {
|
||||
eventBus.Shutdown()
|
||||
log.Errorf("profile for onion %v already exists", profile.Onion)
|
||||
continue
|
||||
}
|
||||
|
||||
ac.coremutex.Lock()
|
||||
ac.eventBuses[profile.Onion] = eventBus
|
||||
ac.coremutex.Unlock()
|
||||
legacyProfile := profileStore.GetProfileCopy(timeline)
|
||||
|
||||
cps, err := peer.CreateEncryptedStore(profileDirectory, password)
|
||||
profile := peer.ImportLegacyProfile(legacyProfile, cps)
|
||||
loadProfileFn(profile)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -198,15 +189,21 @@ func (ac *applicationCore) LoadProfiles(password string, timeline bool, loadProf
|
|||
func (app *application) LoadProfiles(password string) {
|
||||
count := 0
|
||||
app.applicationCore.LoadProfiles(password, true, func(profile peer.CwtchPeer) {
|
||||
eventBus := event.NewEventManager()
|
||||
app.eventBuses[profile.GetOnion()] = eventBus
|
||||
profile.Init(app.eventBuses[profile.GetOnion()])
|
||||
app.appmutex.Lock()
|
||||
app.peers[profile.GetOnion()] = profile
|
||||
app.engines[profile.GetOnion()], _ = profile.GenerateProtocolEngine(app.acn, app.eventBuses[profile.GetOnion()])
|
||||
// Only attempt to finalize the profile if we don't have one loaded...
|
||||
if app.peers[profile.GetOnion()] == nil {
|
||||
eventBus := event.NewEventManager()
|
||||
app.eventBuses[profile.GetOnion()] = eventBus
|
||||
profile.Init(app.eventBuses[profile.GetOnion()])
|
||||
app.peers[profile.GetOnion()] = profile
|
||||
app.engines[profile.GetOnion()], _ = profile.GenerateProtocolEngine(app.acn, app.eventBuses[profile.GetOnion()])
|
||||
app.appBus.Publish(event.NewEvent(event.NewPeer, map[event.Field]string{event.Identity: profile.GetOnion(), event.Created: event.False}))
|
||||
count++
|
||||
} else {
|
||||
// Otherwise shutdown the connections
|
||||
profile.Shutdown()
|
||||
}
|
||||
app.appmutex.Unlock()
|
||||
app.appBus.Publish(event.NewEvent(event.NewPeer, map[event.Field]string{event.Identity: profile.GetOnion(), event.Created: event.False}))
|
||||
count++
|
||||
})
|
||||
if count == 0 {
|
||||
message := event.NewEventList(event.AppError, event.Error, event.AppErrLoaded0)
|
||||
|
|
|
@ -38,3 +38,11 @@ const AttrErr = "error"
|
|||
|
||||
// AttrSentTimestamp - conversation attribute for the time the message was (nominally) sent
|
||||
const AttrSentTimestamp = "sent"
|
||||
|
||||
// Legacy MessageFlags
|
||||
|
||||
// AttrRejected - conversation attribute for storing rejected prompts (for invites)
|
||||
const AttrRejected = "rejected-invite"
|
||||
|
||||
// AttrDownloaded - conversation attribute for storing downloaded prompts (for file downloads)
|
||||
const AttrDownloaded = "file-downloaded"
|
||||
|
|
|
@ -233,6 +233,7 @@ func NewProfileWithEncryptedStorage(name string, cps *CwtchProfileStorage) Cwtch
|
|||
cp := new(cwtchPeer)
|
||||
cp.shutdown = false
|
||||
cp.storage = cps
|
||||
cp.queue = event.NewQueue()
|
||||
cp.state = make(map[string]connections.ConnectionState)
|
||||
|
||||
pub, priv, _ := ed25519.GenerateKey(rand.Reader)
|
||||
|
@ -250,22 +251,106 @@ func FromEncryptedStorage(cps *CwtchProfileStorage) CwtchPeer {
|
|||
cp := new(cwtchPeer)
|
||||
cp.shutdown = false
|
||||
cp.storage = cps
|
||||
cp.queue = event.NewQueue()
|
||||
cp.state = make(map[string]connections.ConnectionState)
|
||||
// At some point we may want to populate caches here, for now we will assume hitting the
|
||||
// database directly is tolerable
|
||||
return cp
|
||||
}
|
||||
|
||||
// FromProfile generates a new peer from a profile.
|
||||
// ImportLegacyProfile generates a new peer from a profile.
|
||||
// Deprecated - Only to be used for importing new profiles
|
||||
func FromProfile(profile *model.Profile, cps *CwtchProfileStorage) CwtchPeer {
|
||||
func ImportLegacyProfile(profile *model.Profile, cps *CwtchProfileStorage) CwtchPeer {
|
||||
cp := new(cwtchPeer)
|
||||
cp.shutdown = false
|
||||
cp.storage = cps
|
||||
|
||||
cp.queue = event.NewQueue()
|
||||
// Store all the Necessary Base Attributes In The Database
|
||||
cp.SetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants.Name, profile.Name)
|
||||
cp.SetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants.Name, profile.Name)
|
||||
cp.storage.StoreProfileKeyValue(TypeAttribute, attr.PublicScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants.Onion)).ToString(), []byte(tor.GetTorV3Hostname(profile.Ed25519PublicKey)))
|
||||
cp.storage.StoreProfileKeyValue(TypePrivateKey, "Ed25519PrivateKey", profile.Ed25519PrivateKey)
|
||||
cp.storage.StoreProfileKeyValue(TypePublicKey, "Ed25519PublicKey", profile.Ed25519PublicKey)
|
||||
|
||||
for k, v := range profile.Attributes {
|
||||
parts := strings.SplitN(k, ".", 2)
|
||||
if len(parts) != 2 {
|
||||
scope := attr.IntoScope(parts[0])
|
||||
zone, path := attr.ParseZone(parts[1])
|
||||
cp.SetScopedZonedAttribute(scope, zone, path, v)
|
||||
} else {
|
||||
log.Errorf("could not import legacy style attribute %v", k)
|
||||
}
|
||||
}
|
||||
|
||||
for _, contact := range profile.Contacts {
|
||||
var conversationID int
|
||||
var err error
|
||||
if contact.Authorization == model.AuthApproved {
|
||||
conversationID, err = cp.NewContactConversation(contact.Onion, model.DefaultP2PAccessControl(), true)
|
||||
} else if contact.Authorization == model.AuthBlocked {
|
||||
conversationID, err = cp.NewContactConversation(contact.Onion, model.AccessControl{Blocked: true, Read: false, Append: false}, true)
|
||||
} else {
|
||||
conversationID, err = cp.NewContactConversation(contact.Onion, model.DefaultP2PAccessControl(), false)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
for key, value := range contact.Attributes {
|
||||
switch key {
|
||||
case "name":
|
||||
cp.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants.Name)), value)
|
||||
case "local.profile.name":
|
||||
cp.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants.Name)), value)
|
||||
case event.SaveHistoryKey:
|
||||
cp.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(event.SaveHistoryKey)), value)
|
||||
case string(model.BundleType):
|
||||
cp.AddServer(value)
|
||||
case string(model.KeyTypeTokenOnion):
|
||||
//ignore
|
||||
case string(model.KeyTypeServerOnion):
|
||||
// ignore
|
||||
case string(model.KeyTypePrivacyPass):
|
||||
// ignore
|
||||
default:
|
||||
log.Errorf("could not import conversation attribute %v %v", key, value)
|
||||
}
|
||||
}
|
||||
for _, message := range contact.Timeline.GetMessages() {
|
||||
// By definition anything stored in legacy timelines in acknowledged
|
||||
attr := model.Attributes{constants.AttrAck: event.True, constants.AttrSentTimestamp: message.Timestamp.Format(time.RFC3339Nano)}
|
||||
if message.Flags&0x01 == 0x01 {
|
||||
attr[constants.AttrRejected] = event.True
|
||||
}
|
||||
if message.Flags&0x02 == 0x02 {
|
||||
attr[constants.AttrDownloaded] = event.True
|
||||
}
|
||||
cp.storage.InsertMessage(conversationID, 0, message.Message, attr, model.GenerateRandomID(), model.CalculateContentHash(message.PeerID, message.Message))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, group := range profile.Groups {
|
||||
invite, err := group.Invite()
|
||||
if err == nil {
|
||||
// Automatically grab all the important fields...
|
||||
// Including Name...
|
||||
conversationID, err := cp.ImportGroup(invite)
|
||||
if err == nil {
|
||||
for _, message := range group.Timeline.GetMessages() {
|
||||
// By definition anything stored in legacy timelines in acknowledged
|
||||
attr := model.Attributes{constants.AttrAck: event.True, constants.AttrSentTimestamp: message.Timestamp.Format(time.RFC3339Nano)}
|
||||
if message.Flags&0x01 == 0x01 {
|
||||
attr[constants.AttrRejected] = event.True
|
||||
}
|
||||
if message.Flags&0x02 == 0x02 {
|
||||
attr[constants.AttrDownloaded] = event.True
|
||||
}
|
||||
cp.storage.InsertMessage(conversationID, 0, message.Message, attr, base64.StdEncoding.EncodeToString(message.Signature), model.CalculateContentHash(message.PeerID, message.Message))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cp
|
||||
}
|
||||
|
||||
|
@ -331,7 +416,6 @@ func (cp *cwtchPeer) Init(eventBus event.Manager) {
|
|||
// InitForEvents
|
||||
// Status: Ready for 1.5
|
||||
func (cp *cwtchPeer) InitForEvents(eventBus event.Manager, toBeHandled []event.Type) {
|
||||
cp.queue = event.NewQueue()
|
||||
go cp.eventHandler()
|
||||
|
||||
cp.eventBus = eventBus
|
||||
|
|
|
@ -113,6 +113,39 @@ func CreateEncryptedStorePeer(profileDirectory string, name string, password str
|
|||
return NewProfileWithEncryptedStorage(name, cps), nil
|
||||
}
|
||||
|
||||
// CreateEncryptedStore creates a encrypted datastore
|
||||
func CreateEncryptedStore(profileDirectory string, password string) (*CwtchProfileStorage, error) {
|
||||
log.Debugf("Initializing Encrypted Storage Directory")
|
||||
_, _, err := initV2Directory(profileDirectory, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debugf("Opening Encrypted Database")
|
||||
db, err := openEncryptedDatabase(profileDirectory, password)
|
||||
if db == nil || err != nil {
|
||||
return nil, fmt.Errorf("unable to open encrypted database: error: %v", err)
|
||||
}
|
||||
|
||||
log.Debugf("Initializing Database")
|
||||
err = initializeDatabase(db)
|
||||
|
||||
if err != nil {
|
||||
db.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debugf("Creating Cwtch Profile Backed By Encrypted Database")
|
||||
|
||||
cps, err := NewCwtchProfileStorage(db)
|
||||
if err != nil {
|
||||
db.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cps, nil
|
||||
}
|
||||
|
||||
// FromEncryptedDatabase constructs a Cwtch Profile from an existing Encrypted Database
|
||||
func FromEncryptedDatabase(profileDirectory string, password string) (CwtchPeer, error) {
|
||||
log.Debugf("Loading Encrypted Profile")
|
||||
|
|
Loading…
Reference in New Issue