Skip to content

Commit

Permalink
v0.12.0 (580)
Browse files Browse the repository at this point in the history
  • Loading branch information
tbaigner committed Jan 24, 2023
1 parent 7abef0b commit d81fb8b
Show file tree
Hide file tree
Showing 242 changed files with 11,417 additions and 4,118 deletions.
21 changes: 21 additions & 0 deletions CHANGELOG.en.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# Changelog

## [0.12.0 (580)] - 2022-10-22

- Say hello to new discussion groups! They can now be configured with as many administrators as you want.
- Backup are now faster and even more reliable.
- The name of a contact who deletes a message for all discussion participants is now indicated right within the discussion.
- csv files can now be shared with Olvid using AirDrop. Yepee!
- Typing a new message now automatically puts the discussion on top of the list of recent discussions.
- You are now notified when a contact takes a screenshot of sensitive messages (either read-once or with a limited visibility).
- Download and upload progresses can now be accessed from the information panel of the associated message.
- Opening a discussion under iPad would not always scroll to the latest message. This is fixed.
- User notifications now show a small preview of the attachment when possible.
- You are notified if a message fails to be sent after retrying for a long period of time.
- A new option allows to automatically download all attachments regardless of their size.
- Keycloak revocations now work even when push notifications are deactivated.
- Fixes a visual bug occurring when sending an audio message under iOS 16.
- Fixes a potential crash occurring under iOS 16 when performing a Keycloak search.
- Fixes an issue occurring under iOS 16 where the copy/paste would systematically request the user's permission.
- Fixes an issue preventing the display of certain "missed call" notifications.
- Olvid starts even faster than before.
- In order to never miss a secure call, you can now grant access to the microphone during the onboarding procedure.

## [0.11.1 (564)] - 2022-09-22

- It is now possible to define a personalized passcode (either PIN or password) to unlock Olvid!
Expand Down
21 changes: 21 additions & 0 deletions CHANGELOG.fr.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# Changelog

## [0.12.0 (580)] - 2022-10-22

- Bienvenue aux nouvelles discussions de groupe ! Vous pouvez maintenant les configurer avec autant d'administrateurs que vous voulez.
- Les sauvegardes sont plus rapides et encore plus robustes qu'avant.
- Le nom d'un contact qui supprime un message pour tous est maintenant affiché directement dans la discussion.
- Les fichiers csv peuvent maintenant être partagés vers Olvid avec AirDrop. Youpi !
- Taper un nouveau message place la discussion au sommet de la liste des discussions récentes.
- Vous êtes maintenant notifié quand un contact fait une capture d'écran affichant des messages sensibles (à lecture unique ou à visibilité limitée).
- La progression des téléchargements peut être consultée depuis le panel d'information du message associé.
- Sous iPad, il arrivait que le défilement jusqu'au message le plus récent ne fonctionne pas à l'ouverture d'une discussion. C'est corrigé.
- Les notifications utilisateur affichent une petite prévisualisation des pièces jointes lorsque cela est possible.
- Vous êtes maintenant notifié lorsqu'un message ne peut être envoyé après de nombreuses tentatives.
- Une nouvelle option permet de télécharger automatiquement toutes les pièces jointes, indépendamment de leur taille.
- Les révocations envoyées par Keycloak sont maintenant reçues même si les notifications push sont désactivées.
- Corrige un bug visuel se produisant à l'envoi d'un message audio.
- Corrige un potentiel crash sous iOS 16 se produisant pendant une recherche Keycloak.
- Corrige un problème rencontré sous iOS 16 concernant les autorisations systématiques demandées au moment de faire un copier/coller.
- Corrige un bug empêchant l'affichage de certaines notifications d'appel manqué.
- Le démarrage d'Olvid est encore plus rapide qu'avant.
- Afin de ne jamais rater un appel sécurisé, vous avez maintenant la possibilité d'accorder l'accès au micro pendant l'onboarding.

## [0.11.1 (564)] - 2022-09-22

- Il est maintenant possible de choisir un PIN ou un mot de passe personnalisé pour déverrouiller Olvid !
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1320"
LastUpgradeVersion = "1400"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
228 changes: 222 additions & 6 deletions CoreDataStack/CoreDataStack/DataMigrationManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import Foundation
import CoreData
import OlvidUtils
import os.log
import SQLite3

open class DataMigrationManager<PersistentContainerType: NSPersistentContainer> {

Expand Down Expand Up @@ -160,7 +161,7 @@ open class DataMigrationManager<PersistentContainerType: NSPersistentContainer>
return res
}

private func getSourceStoreMetadata() throws -> [String: Any] {
private func getSourceStoreMetadata(storeURL: URL) throws -> [String: Any] {
let dict = try NSPersistentStoreCoordinator.metadataForPersistentStore(ofType: NSSQLiteStoreType,
at: storeURL,
options: nil)
Expand Down Expand Up @@ -206,8 +207,8 @@ open class DataMigrationManager<PersistentContainerType: NSPersistentContainer>
}


private func getStoreManagedObjectModel() throws -> NSManagedObjectModel {
let storeMetadata = try getSourceStoreMetadata()
private func getStoreManagedObjectModel(storeURL: URL) throws -> NSManagedObjectModel {
let storeMetadata = try getSourceStoreMetadata(storeURL: storeURL)
let allModels = try getAllManagedObjectModels()
for model in allModels {
if model.isConfiguration(withName: nil, compatibleWithStoreMetadata: storeMetadata) {
Expand All @@ -227,7 +228,19 @@ open class DataMigrationManager<PersistentContainerType: NSPersistentContainer>
migrationRunningLog.addEvent(message: "Destination Managed Object Model: \(destinationManagedObjectModel.versionIdentifier)")
os_log("Destination Managed Object Model: %{public}@", log: log, type: .info, destinationManagedObjectModel.versionIdentifier)

let sourceStoreMetadata = try getSourceStoreMetadata()
let sourceStoreMetadata: [String: Any]
do {
sourceStoreMetadata = try getSourceStoreMetadata(storeURL: self.storeURL)
} catch {
migrationRunningLog.addEvent(message: "Failed to get source store metadata: \(error.localizedDescription)")
os_log("Failed to get source store metadata: %{public}@", log: log, type: .fault, error.localizedDescription)
logDebugInformation()
throw error
}

migrationRunningLog.addEvent(message: "Just got the source store metada")
os_log("Just got the source store metada", log: log, type: .info)

if let sourceVersionIdentifier = (sourceStoreMetadata[NSStoreModelVersionIdentifiersKey] as? [Any])?.first as? String {
migrationRunningLog.addEvent(message: "Source Store Model Version Identifier: \(sourceVersionIdentifier)")
os_log("Source Store Model Version Identifier: %{public}@", log: log, type: .info, sourceVersionIdentifier)
Expand All @@ -237,6 +250,209 @@ open class DataMigrationManager<PersistentContainerType: NSPersistentContainer>
compatibleWithStoreMetadata: sourceStoreMetadata)
}


// MARK: - Logging debug informations


private let byteCountFormatter: ByteCountFormatter = {
var bcf = ByteCountFormatter()
return bcf
}()


private let dateFormatter: DateFormatter = {
var df = DateFormatter()
df.dateStyle = .short
df.timeStyle = .short
return df
}()


/// As for now, this method is called when we fail to obtain (metada) information about the current version of the database and thus, fail to migrate to a new version.
private func logDebugInformation() {

migrationRunningLog.addEvent(message: "[DEBUG] Source Store URL: \(storeURL.debugDescription)")
migrationRunningLog.addEvent(message: "[DEBUG] Model name: \(modelName)")

// List the files in the source store URL (and remember sqlite files)
var sqliteFiles = [URL]()
do {
var isDirectory: ObjCBool = false
let resourceKeys = [URLResourceKey.fileSizeKey, .creationDateKey, .contentModificationDateKey, .attributeModificationDateKey]
if FileManager.default.fileExists(atPath: storeURL.path, isDirectory: &isDirectory) {
if isDirectory.boolValue {
migrationRunningLog.addEvent(message: "[DEBUG] The storeURL is a directory, which is not expected")
} else if let directoryContents = try? FileManager.default.contentsOfDirectory(at: storeURL.deletingLastPathComponent(), includingPropertiesForKeys: resourceKeys) {
migrationRunningLog.addEvent(message: "[DEBUG] Listing the files in \(storeURL.deletingLastPathComponent().path)")
for value in directoryContents.enumerated() {
var resourceString = [String]()
if let resourceValues = try? value.element.resourceValues(forKeys: Set(resourceKeys)) {
if let fileSize = resourceValues.fileSize {
resourceString.append("File size: \(byteCountFormatter.string(fromByteCount: Int64(fileSize)))")
}
if let creationDate = resourceValues.creationDate {
resourceString.append("Creation date: \(dateFormatter.string(from: creationDate))")
}
if let contentModificationDate = resourceValues.contentModificationDate {
resourceString.append("Content modification date: \(dateFormatter.string(from: contentModificationDate))")
}
if let attributeModificationDate = resourceValues.attributeModificationDate {
resourceString.append("Attribute modification date: \(dateFormatter.string(from: attributeModificationDate))")
}
}
let allResources = resourceString.joined(separator: ", ")
migrationRunningLog.addEvent(message: "[DEBUG] File \(value.offset): \(value.element.lastPathComponent) (\(allResources))")
if value.element.pathExtension == "sqlite" {
sqliteFiles.append(value.element)
}
}
}
}
}

// List all sqlite files

migrationRunningLog.addEvent(message: "[DEBUG] Found \(sqliteFiles.count) sqlite files")

for (offset, url) in sqliteFiles.enumerated() {
migrationRunningLog.addEvent(message: "[DEBUG][\(offset)] \(url.path)")
do {
try logMetadaQueryOn(sqliteFile: url)
if let model = try? getStoreManagedObjectModel(storeURL: url) {
migrationRunningLog.addEvent(message: "[DEBUG][\(offset)] The model version identifier is \(model.versionIdentifier)")
} else {
migrationRunningLog.addEvent(message: "[DEBUG][\(offset)] Failed to determine the model version identifier")
}
} catch {
// If we reach this point, we could not log metada informations about the sqlite file at `url`
migrationRunningLog.addEvent(message: "[DEBUG][\(offset)] Failed to log metada on \(url.debugDescription): \(error.localizedDescription)")
}

}

}


private func createBackupOf(sqliteFile: URL) throws -> URL {

// Open the database

var db: OpaquePointer?
do {
let res = sqlite3_open(sqliteFile.path, &db)
guard res == SQLITE_OK else {
migrationRunningLog.addEvent(message: "[DEBUG] Could not open sqlite file \(sqliteFile.path). Error is \(res.description)")
throw Self.makeError(message: res.description)
}
}

guard let db else {
migrationRunningLog.addEvent(message: "[DEBUG] Could not open sqlite file \(sqliteFile.path). The db point is nil")
throw Self.makeError(message: "Unexpected error")
}

defer { sqlite3_close(db) }

// Create the database to which we will backup records

let backupSqliteFile = sqliteFile.deletingLastPathComponent().appendingPathComponent("backup-\(UUID().uuidString).sqlite")

var backupDb: OpaquePointer?
do {
let res = sqlite3_open(backupSqliteFile.path, &backupDb)
guard res == SQLITE_OK else {
migrationRunningLog.addEvent(message: "[DEBUG] Could not open backup sqlite file \(sqliteFile.path). Error is \(res.description)")
throw Self.makeError(message: res.description)
}
}

guard let backupDb else {
migrationRunningLog.addEvent(message: "[DEBUG] Could not open backup sqlite file \(backupSqliteFile.path). The backupDb point is nil")
throw Self.makeError(message: "Unexpected error")
}

defer { sqlite3_close(backupDb) }

// Initiate the backup

let sql3Backup = sqlite3_backup_init(backupDb, "main" , db, "main")

guard let sql3Backup else {
migrationRunningLog.addEvent(message: "[DEBUG] Could not initiate backup of file \(sqliteFile.path)")
throw Self.makeError(message: "[DEBUG] Could not initiate backup of file \(sqliteFile.path)")
}

// Perform the backup

do {
let res = sqlite3_backup_step(sql3Backup, -1)
guard res == SQLITE_DONE else {
migrationRunningLog.addEvent(message: "[DEBUG] Could not perform backup of sqlite file \(sqliteFile.path). Error is \(res.description)")
throw Self.makeError(message: res.description)
}
}

// Finish the backup

do {
let res = sqlite3_backup_finish(sql3Backup)
guard res == SQLITE_OK else {
migrationRunningLog.addEvent(message: "[DEBUG] Could not finish backup of sqlite file \(sqliteFile.path). Error is \(res.description)")
throw Self.makeError(message: res.description)
}
}

return backupSqliteFile
}


private func logMetadaQueryOn(sqliteFile: URL) throws {

migrationRunningLog.addEvent(message: "[DEBUG] Performing a raw SQL query on \(sqliteFile.path)")

// Open the database

var db: OpaquePointer?
do {
let res = sqlite3_open(sqliteFile.path, &db)
guard res == SQLITE_OK else {
migrationRunningLog.addEvent(message: "[DEBUG] Could not open sqlite file \(sqliteFile.path). Error is \(res.description)")
throw Self.makeError(message: res.description)
}
}

guard let db else {
migrationRunningLog.addEvent(message: "[DEBUG] Could not open sqlite file \(sqliteFile.path). The db point is nil")
return
}

defer { sqlite3_close(db) }

let statementString = "SELECT * FROM Z_METADATA;"
var statement: OpaquePointer?
do {
let res = sqlite3_prepare_v2(db, statementString, -1, &statement, nil)
guard res == SQLITE_OK else {
migrationRunningLog.addEvent(message: "[DEBUG] Could not prepare statement for sqlite file \(sqliteFile.path). Error is \(res.description)")
throw Self.makeError(message: res.description)
}
}

while sqlite3_step(statement) == SQLITE_ROW {
migrationRunningLog.addEvent(message: "[DEBUG] Found a metadata row")
let version = sqlite3_column_int(statement, 0)
migrationRunningLog.addEvent(message: "[DEBUG] Version is \(version)")
if let rawBlob = sqlite3_column_blob(statement, 2) {
let count = sqlite3_column_bytes(statement, 2)
let blob = Data(bytes: rawBlob, count: Int(count))
migrationRunningLog.addEvent(message: "[DEBUG] Blob length is \(blob.debugDescription) (typical is 3726 bytes)")
}
}

sqlite3_finalize(statement)

}


// MARK: - Migrating

Expand All @@ -259,7 +475,7 @@ open class DataMigrationManager<PersistentContainerType: NSPersistentContainer>

migrationRunningLog.addEvent(message: "Trying to determine the model of the store on disk...")

var currentStoreModel = try getStoreManagedObjectModel()
var currentStoreModel = try getStoreManagedObjectModel(storeURL: self.storeURL)

migrationRunningLog.addEvent(message: "The current model of the store on disk is \(currentStoreModel.versionIdentifier)")

Expand Down Expand Up @@ -389,7 +605,7 @@ open class DataMigrationManager<PersistentContainerType: NSPersistentContainer>


migrationRunningLog.addEvent(message: "Determining the new store model...")
currentStoreModel = try getStoreManagedObjectModel()
currentStoreModel = try getStoreManagedObjectModel(storeURL: self.storeURL)

migrationRunningLog.addEvent(message: "The (new) store model on disk is \(currentStoreModel.versionIdentifier)")

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1330"
LastUpgradeVersion = "1400"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Loading

0 comments on commit d81fb8b

Please sign in to comment.