From 30c0f2d4a475e8fbdc0d1a1dd3f1bc8a9c3656cc Mon Sep 17 00:00:00 2001 From: anushkavidanage Date: Wed, 14 Jan 2026 09:53:46 +1100 Subject: [PATCH 1/4] A function to generate custom folders at the initialisation stage --- lib/solidpod.dart | 1 + lib/src/solid/utils/misc.dart | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/lib/solidpod.dart b/lib/solidpod.dart index 15dd4106..3020d37d 100644 --- a/lib/solidpod.dart +++ b/lib/solidpod.dart @@ -119,6 +119,7 @@ export 'src/solid/utils/misc.dart' logoutPod, setAppDirName, generateDefaultFolders, + generateCustomFolders, generateDefaultFiles, initPod; diff --git a/lib/src/solid/utils/misc.dart b/lib/src/solid/utils/misc.dart index 52da4786..ffcfa747 100644 --- a/lib/src/solid/utils/misc.dart +++ b/lib/src/solid/utils/misc.dart @@ -394,6 +394,27 @@ Future> generateDefaultFolders() async { return folders; } +/// Generates a list of custom folder paths for a given application. +/// +/// This function takes the custom folder list given by the developer and +/// creates paths for those folders. Each custom folder will be created +/// inside the "data" folder. Following are few examples. +/// - Custom folder 'myDir1' will be created as '/data/myDir1' +/// - Custom folder 'myDir1/myDir3' will be created as '/data/myDir1/myDir3' +/// - Custom folder 'data' will be created as '/data/data' + +List generateCustomFolders(List customFolderPaths) { + List customFolderList = []; + if (customFolderPaths.isNotEmpty) { + for (String folderPath in customFolderPaths) { + // Remove any leading '/' characters + folderPath = folderPath.replaceFirst(RegExp(r'^/+'), ''); + customFolderList.add([appDirName, dataDir, folderPath].join('/')); + } + } + return customFolderList; +} + /// Generates a list of default folder paths for a given application. /// /// This function takes the name of an application as input and returns a list of strings. From eef72805b68719aaac8413065691847acba51497 Mon Sep 17 00:00:00 2001 From: anushkavidanage Date: Wed, 14 Jan 2026 12:13:19 +1100 Subject: [PATCH 2/4] Relocate misc functions to separate files --- lib/solidpod.dart | 16 +- lib/src/solid/api/grant_permission_api.dart | 1 + lib/src/solid/api/rest_api.dart | 1 + lib/src/solid/api/revoke_permission_api.dart | 1 + lib/src/solid/chk_exists_and_has_acl.dart | 1 + lib/src/solid/common_func.dart | 2 + lib/src/solid/get_resources.dart | 1 + lib/src/solid/grant_permission.dart | 1 + lib/src/solid/read_permission.dart | 1 + lib/src/solid/read_pod.dart | 1 + lib/src/solid/revoke_permission.dart | 1 + .../revoke_permission_to_deleted_file.dart | 1 + lib/src/solid/shared_resources.dart | 1 + lib/src/solid/utils/data_encryption.dart | 45 ++++ lib/src/solid/utils/get_url_helper.dart | 149 ++++++++++++ .../solid/utils/init_pod_gen_resources.dart | 102 +++++++++ lib/src/solid/utils/io_helper.dart | 2 + lib/src/solid/utils/key_helper.dart | 16 ++ lib/src/solid/utils/key_manager.dart | 2 + lib/src/solid/utils/large_file_helper.dart | 3 +- lib/src/solid/utils/misc.dart | 214 +----------------- lib/src/solid/write_external_pod.dart | 1 + 22 files changed, 345 insertions(+), 218 deletions(-) create mode 100644 lib/src/solid/utils/data_encryption.dart create mode 100644 lib/src/solid/utils/get_url_helper.dart create mode 100644 lib/src/solid/utils/init_pod_gen_resources.dart diff --git a/lib/solidpod.dart b/lib/solidpod.dart index 3020d37d..10457d7a 100644 --- a/lib/solidpod.dart +++ b/lib/solidpod.dart @@ -107,22 +107,26 @@ export 'src/solid/utils/misc.dart' deleteExternalFile, deleteFile, deleteLogIn, - filenameToResourceUrl, getWebId, getDataDirPath, getAppNameVersion, - getFileUrl, - getDirUrl, getTokensForResource, getDateTime, getEncKeyPath, logoutPod, setAppDirName, - generateDefaultFolders, - generateCustomFolders, - generateDefaultFiles, initPod; +/// Utility functions for generating resource URLs + +export 'src/solid/utils/get_url_helper.dart' + show filenameToResourceUrl, getFileUrl, getDirUrl; + +/// Utility functions for generating resources at POD initialisation + +export 'src/solid/utils/init_pod_gen_resources.dart' + show generateDefaultFolders, generateCustomFolders, generateDefaultFiles; + /// Change security key popup widget export 'src/widgets/change_key_dialog.dart'; diff --git a/lib/src/solid/api/grant_permission_api.dart b/lib/src/solid/api/grant_permission_api.dart index 4dea940e..70a97c91 100644 --- a/lib/src/solid/api/grant_permission_api.dart +++ b/lib/src/solid/api/grant_permission_api.dart @@ -41,6 +41,7 @@ import 'package:solidpod/src/solid/api/rest_api.dart'; import 'package:solidpod/src/solid/constants/common.dart'; import 'package:solidpod/src/solid/constants/schema.dart'; import 'package:solidpod/src/solid/constants/web_acl.dart'; +import 'package:solidpod/src/solid/utils/get_url_helper.dart'; import 'package:solidpod/src/solid/utils/misc.dart'; import 'package:solidpod/src/solid/utils/permission.dart'; diff --git a/lib/src/solid/api/rest_api.dart b/lib/src/solid/api/rest_api.dart index fdecf606..d376602f 100644 --- a/lib/src/solid/api/rest_api.dart +++ b/lib/src/solid/api/rest_api.dart @@ -44,6 +44,7 @@ import 'package:rdflib/rdflib.dart'; import 'package:solidpod/src/solid/constants/common.dart'; import 'package:solidpod/src/solid/utils/authdata_manager.dart'; import 'package:solidpod/src/solid/utils/exceptions.dart'; +import 'package:solidpod/src/solid/utils/get_url_helper.dart'; import 'package:solidpod/src/solid/utils/key_manager.dart'; import 'package:solidpod/src/solid/utils/misc.dart'; diff --git a/lib/src/solid/api/revoke_permission_api.dart b/lib/src/solid/api/revoke_permission_api.dart index b5addf7a..67a92f39 100644 --- a/lib/src/solid/api/revoke_permission_api.dart +++ b/lib/src/solid/api/revoke_permission_api.dart @@ -40,6 +40,7 @@ import 'package:solidpod/src/solid/api/rest_api.dart'; import 'package:solidpod/src/solid/constants/common.dart'; import 'package:solidpod/src/solid/constants/schema.dart'; import 'package:solidpod/src/solid/constants/web_acl.dart'; +import 'package:solidpod/src/solid/utils/get_url_helper.dart'; import 'package:solidpod/src/solid/utils/misc.dart'; import 'package:solidpod/src/solid/utils/permission.dart'; diff --git a/lib/src/solid/chk_exists_and_has_acl.dart b/lib/src/solid/chk_exists_and_has_acl.dart index d9d83acf..808ccc75 100644 --- a/lib/src/solid/chk_exists_and_has_acl.dart +++ b/lib/src/solid/chk_exists_and_has_acl.dart @@ -36,6 +36,7 @@ import 'package:solidpod/src/solid/api/common_permission.dart'; import 'package:solidpod/src/solid/api/rest_api.dart'; import 'package:solidpod/src/solid/constants/common.dart'; import 'package:solidpod/src/solid/solid_func_call_status.dart'; +import 'package:solidpod/src/solid/utils/get_url_helper.dart'; import 'package:solidpod/src/solid/utils/misc.dart'; /// Check [fileName] exists and has the associated ACL file. Requires user diff --git a/lib/src/solid/common_func.dart b/lib/src/solid/common_func.dart index 3806394f..e55a3747 100644 --- a/lib/src/solid/common_func.dart +++ b/lib/src/solid/common_func.dart @@ -35,6 +35,8 @@ import 'package:solidpod/src/solid/constants/common.dart'; import 'package:solidpod/src/solid/constants/schema.dart' show appsTerms; import 'package:solidpod/src/solid/utils/alert.dart'; import 'package:solidpod/src/solid/utils/exceptions.dart'; +import 'package:solidpod/src/solid/utils/get_url_helper.dart'; +import 'package:solidpod/src/solid/utils/init_pod_gen_resources.dart'; import 'package:solidpod/src/solid/utils/misc.dart'; import 'package:solidpod/src/solid/utils/rdf.dart' show parseTTLMap; diff --git a/lib/src/solid/get_resources.dart b/lib/src/solid/get_resources.dart index 43498884..70e6c1db 100644 --- a/lib/src/solid/get_resources.dart +++ b/lib/src/solid/get_resources.dart @@ -37,6 +37,7 @@ import 'package:flutter/material.dart' hide Key; import 'package:solidpod/src/solid/api/rest_api.dart'; import 'package:solidpod/src/solid/constants/common.dart'; import 'package:solidpod/src/solid/utils/exceptions.dart'; +import 'package:solidpod/src/solid/utils/get_url_helper.dart'; import 'package:solidpod/src/solid/utils/misc.dart'; /// Get the list of files created by the user in their POD by querying the data directory of the POD. diff --git a/lib/src/solid/grant_permission.dart b/lib/src/solid/grant_permission.dart index cc64411d..646ba88a 100644 --- a/lib/src/solid/grant_permission.dart +++ b/lib/src/solid/grant_permission.dart @@ -41,6 +41,7 @@ import 'package:solidpod/src/solid/models/log_entry.dart'; import 'package:solidpod/src/solid/solid_func_call_status.dart'; import 'package:solidpod/src/solid/utils/authdata_manager.dart'; import 'package:solidpod/src/solid/utils/exceptions.dart'; +import 'package:solidpod/src/solid/utils/get_url_helper.dart'; import 'package:solidpod/src/solid/utils/key_helper.dart' show RecipientPubKey; import 'package:solidpod/src/solid/utils/key_manager.dart' show KeyManager; import 'package:solidpod/src/solid/utils/misc.dart'; diff --git a/lib/src/solid/read_permission.dart b/lib/src/solid/read_permission.dart index ca5d3132..6d0e4c87 100644 --- a/lib/src/solid/read_permission.dart +++ b/lib/src/solid/read_permission.dart @@ -30,6 +30,7 @@ library; import 'dart:core'; +import 'package:solidpod/src/solid/utils/get_url_helper.dart'; import 'package:solidpod/src/solid/utils/misc.dart'; import 'package:solidpod/src/solid/utils/permission.dart'; diff --git a/lib/src/solid/read_pod.dart b/lib/src/solid/read_pod.dart index abd96e29..d5e2fa99 100644 --- a/lib/src/solid/read_pod.dart +++ b/lib/src/solid/read_pod.dart @@ -37,6 +37,7 @@ import 'package:encrypter_plus/encrypter_plus.dart'; import 'package:solidpod/src/solid/api/rest_api.dart'; import 'package:solidpod/src/solid/constants/common.dart'; import 'package:solidpod/src/solid/constants/path_type.dart'; +import 'package:solidpod/src/solid/utils/data_encryption.dart'; import 'package:solidpod/src/solid/utils/exceptions.dart'; import 'package:solidpod/src/solid/utils/key_helper.dart'; import 'package:solidpod/src/solid/utils/key_inheritance.dart'; diff --git a/lib/src/solid/revoke_permission.dart b/lib/src/solid/revoke_permission.dart index 0e3aeeb7..91238923 100644 --- a/lib/src/solid/revoke_permission.dart +++ b/lib/src/solid/revoke_permission.dart @@ -38,6 +38,7 @@ import 'package:solidpod/src/solid/constants/web_acl.dart'; import 'package:solidpod/src/solid/models/log_entry.dart'; import 'package:solidpod/src/solid/solid_func_call_status.dart'; import 'package:solidpod/src/solid/utils/authdata_manager.dart'; +import 'package:solidpod/src/solid/utils/get_url_helper.dart'; import 'package:solidpod/src/solid/utils/misc.dart'; import 'package:solidpod/src/solid/utils/permission.dart'; diff --git a/lib/src/solid/revoke_permission_to_deleted_file.dart b/lib/src/solid/revoke_permission_to_deleted_file.dart index 433706dc..7224155f 100644 --- a/lib/src/solid/revoke_permission_to_deleted_file.dart +++ b/lib/src/solid/revoke_permission_to_deleted_file.dart @@ -37,6 +37,7 @@ import 'package:solidpod/src/solid/api/rest_api.dart'; import 'package:solidpod/src/solid/api/revoke_permission_api.dart'; import 'package:solidpod/src/solid/models/log_entry.dart'; import 'package:solidpod/src/solid/solid_func_call_status.dart'; +import 'package:solidpod/src/solid/utils/get_url_helper.dart'; import 'package:solidpod/src/solid/utils/misc.dart'; /// Revoke permissions to non-existent [fileName] (ie already deleted) diff --git a/lib/src/solid/shared_resources.dart b/lib/src/solid/shared_resources.dart index a2e26ba4..2e116a3b 100644 --- a/lib/src/solid/shared_resources.dart +++ b/lib/src/solid/shared_resources.dart @@ -34,6 +34,7 @@ import 'package:solidpod/src/solid/api/common_permission.dart'; import 'package:solidpod/src/solid/api/rest_api.dart'; import 'package:solidpod/src/solid/utils/authdata_manager.dart'; import 'package:solidpod/src/solid/utils/exceptions.dart'; +import 'package:solidpod/src/solid/utils/get_url_helper.dart'; import 'package:solidpod/src/solid/utils/misc.dart'; import 'package:solidpod/src/solid/utils/rdf.dart'; diff --git a/lib/src/solid/utils/data_encryption.dart b/lib/src/solid/utils/data_encryption.dart new file mode 100644 index 00000000..4d8ac38d --- /dev/null +++ b/lib/src/solid/utils/data_encryption.dart @@ -0,0 +1,45 @@ +/// Data encryption utility functions used across the package. +/// +/// +/// Copyright (C) 2026, Software Innovation Institute, ANU. +/// +/// Licensed under the MIT License (the "License"). +/// +/// License: https://choosealicense.com/licenses/mit/. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +/// +/// Authors: Anushka Vidanage + +library; + +import 'package:encrypter_plus/encrypter_plus.dart'; + +/// Encrypt data using AES with the specified key +String encryptData(String data, Key key, IV iv, {AESMode mode = AESMode.sic}) => + Encrypter(AES(key, mode: mode)).encrypt(data, iv: iv).base64; + +/// Decrypt a ciphertext value +String decryptData( + String encData, + Key key, + IV iv, { + AESMode mode = AESMode.sic, +}) => + Encrypter(AES(key, mode: mode)).decrypt(Encrypted.from64(encData), iv: iv); diff --git a/lib/src/solid/utils/get_url_helper.dart b/lib/src/solid/utils/get_url_helper.dart new file mode 100644 index 00000000..7dc12b7d --- /dev/null +++ b/lib/src/solid/utils/get_url_helper.dart @@ -0,0 +1,149 @@ +/// Get URLs related utility functions used across the package. +/// +/// +/// Copyright (C) 2026, Software Innovation Institute, ANU. +/// +/// Licensed under the MIT License (the "License"). +/// +/// License: https://choosealicense.com/licenses/mit/. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +/// +/// Authors: Anushka Vidanage, Dawei Chen + +library; + +import 'dart:convert'; + +import 'package:crypto/crypto.dart'; + +import 'package:solidpod/src/solid/constants/common.dart'; +import 'package:solidpod/src/solid/utils/authdata_manager.dart'; +import 'package:solidpod/src/solid/utils/misc.dart'; + +/// Generate unique ID for the resource URL +/// In order for the ID to be unique across all webIDs and no one can +/// reverse engineer the resource being shared we also use receiver webId +/// in the ID generation process +String getUniqueIdResUrl(String resourceUrl, String receiverWebId) { + return sha256.convert(utf8.encode(resourceUrl + receiverWebId)).toString(); +} + +/// From a given resource path [resourcePath] create its URL +/// [resourcePath] should be relative to the POD, e.g., `myapp/data/abc.ttl`. +/// [isFile] should be true if the resource is a file, otherwise false if it +/// is a directory. +/// [webId] (optionally) specifies the resource's POD (could be external). +/// returns the full resource URL + +Future _getResourceUrl( + String resourcePath, { + required bool isFile, + String? webId, +}) async { + final wId = webId ?? await AuthDataManager.getWebId(); + + assert(wId != null); + assert(wId!.contains(profCard)); + + final resourceUrl = wId!.replaceAll(profCard, resourcePath); + + if (!isFile && !resourceUrl.endsWith('/')) { + return '$resourceUrl/'; + } + + return resourceUrl; +} + +/// Create the URL for a file +Future getFileUrl(String filePath, {String? webId}) async => + await _getResourceUrl(filePath, isFile: true, webId: webId); + +/// Create the URL for a directory (container) +Future getDirUrl(String dirPath, {String? webId}) async => + await _getResourceUrl(dirPath, isFile: false, webId: webId); + +/// Get resource Url from a filename, with different options for how +/// the filename is provided. If [isExternalRes] or [isFileUrl] is +/// set to true, the filename is already the resource url and is +/// returned unchanged. +/// +/// Parameters: +/// - [fileName] - name of file of interest. +/// - [isFile] - flag describing whether the resources is a file or not. +/// If false the [fileName] is a directory. (Default: true). +/// - [isFilePath] - flag describing whether [fileName] includes the +/// directory data path. (Default: false). +/// - [isFileUrl] - flag describing whether [fileName] is the url of the +/// file. (Default: false). +/// - [isExternalRes] - flag describing whether the file is an external +/// file shared with the user. In which case the [fileName] should be +/// the full URL of the file. (Default: false). + +Future filenameToResourceUrl({ + required String fileName, + bool isFile = true, + bool isFileUrl = false, + bool isExternalRes = false, +}) async { + final String resourceUrl; + final String filePath; + + // Note: external resources always specified by url + if (isExternalRes) { + isFileUrl = true; + } + + // If not already a url, get url + if (!isExternalRes && !isFileUrl) { + // Get the file path + // Ensure path uses correct path separators and + // has app data dir prepended. + filePath = await normalizeFilePath(fileName, null); + + // Get the url of the resource by prepending the + // user = owner webID (excluding profCard) + if (isFile) { + resourceUrl = await getFileUrl(filePath); + } else { + resourceUrl = await getDirUrl(filePath); + } + } else { + // Use fileName, fileName already the resource url + resourceUrl = fileName; + } + + return resourceUrl; +} + +/// Derive external POD inherited parent directory url from the resource url +/// and parent directory path +String getExtDirUrl(String resourceUrl, String dirPath) { + int pathIndex = resourceUrl.indexOf(dirPath); + + String dirUrl = ''; + if (pathIndex != -1) { + dirUrl = resourceUrl.substring(0, pathIndex + dirPath.length); + if (!dirUrl.endsWith('/')) { + dirUrl += '/'; + } + } + + return dirUrl; +} diff --git a/lib/src/solid/utils/init_pod_gen_resources.dart b/lib/src/solid/utils/init_pod_gen_resources.dart new file mode 100644 index 00000000..954c3968 --- /dev/null +++ b/lib/src/solid/utils/init_pod_gen_resources.dart @@ -0,0 +1,102 @@ +/// Utility functions for generating folders and files at the initilisation stage. +/// +/// +/// Copyright (C) 2026, Software Innovation Institute, ANU. +/// +/// Licensed under the MIT License (the "License"). +/// +/// License: https://choosealicense.com/licenses/mit/. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +/// +/// Authors: Anushka Vidanage, Dawei Chen + +library; + +import 'package:solidpod/src/solid/constants/common.dart'; + +/// Generates a list of default folder paths for a given application. +/// +/// This function takes the name of an application as input and returns a list of strings. +/// Each string in the list represents a path to a default folder for the application. + +Future> generateDefaultFolders() async { + final dataDirLoc = [appDirName, dataDir].join('/'); + final sharingDirLoc = [appDirName, sharingDir].join('/'); + final sharedDirLoc = [appDirName, sharedDir].join('/'); + final encDirLoc = [appDirName, encDir].join('/'); + final logDirLoc = [appDirName, logsDir].join('/'); + + final folders = [ + appDirName, + sharingDirLoc, + sharedDirLoc, + dataDirLoc, + encDirLoc, + logDirLoc, + ]; + return folders; +} + +/// Generates a list of custom folder paths for a given application. +/// +/// This function takes the custom folder list given by the developer and +/// creates paths for those folders. Each custom folder will be created +/// inside the "data" folder. Following are few examples. +/// - Custom folder 'myDir1' will be created as '/data/myDir1' +/// - Custom folder 'myDir1/myDir3' will be created as '/data/myDir1/myDir3' +/// - Custom folder 'data' will be created as '/data/data' + +List generateCustomFolders(List customFolderPaths) { + List customFolderList = []; + if (customFolderPaths.isNotEmpty) { + for (String folderPath in customFolderPaths) { + // Remove any leading '/' characters + folderPath = folderPath.replaceFirst(RegExp(r'^/+'), ''); + customFolderList.add([appDirName, dataDir, folderPath].join('/')); + } + } + return customFolderList; +} + +/// Generates a list of default folder paths for a given application. +/// +/// This function takes the name of an application as input and returns a list of strings. +/// Each string in the list represents a path to a default folder for the application. + +Future> generateDefaultFiles() async { + final sharingDirLoc = [appDirName, sharingDir].join('/'); + final sharedDirLoc = [appDirName, sharedDir].join('/'); + final encDirLoc = [appDirName, encDir].join('/'); + final logDirLoc = [appDirName, logsDir].join('/'); + + final files = { + sharingDirLoc: [ + pubKeyFile, + '$pubKeyFile.acl', + ], + logDirLoc: [ + permLogFile, + '$permLogFile.acl', + ], + sharedDirLoc: ['.acl'], + encDirLoc: [encKeyFile, indKeyFile], + }; + return files; +} diff --git a/lib/src/solid/utils/io_helper.dart b/lib/src/solid/utils/io_helper.dart index 6285a830..69dfda35 100644 --- a/lib/src/solid/utils/io_helper.dart +++ b/lib/src/solid/utils/io_helper.dart @@ -28,6 +28,8 @@ library; +import 'package:solidpod/src/solid/utils/get_url_helper.dart'; +import 'package:solidpod/src/solid/utils/init_pod_gen_resources.dart'; import 'package:solidpod/src/solid/utils/misc.dart'; final _protectedFiles = {}; diff --git a/lib/src/solid/utils/key_helper.dart b/lib/src/solid/utils/key_helper.dart index 8eeac67a..227d0148 100644 --- a/lib/src/solid/utils/key_helper.dart +++ b/lib/src/solid/utils/key_helper.dart @@ -39,10 +39,26 @@ import 'package:rdflib/rdflib.dart'; import 'package:solidpod/src/solid/api/rest_api.dart'; import 'package:solidpod/src/solid/constants/common.dart'; import 'package:solidpod/src/solid/constants/schema.dart'; +import 'package:solidpod/src/solid/utils/data_encryption.dart'; +import 'package:solidpod/src/solid/utils/get_url_helper.dart'; import 'package:solidpod/src/solid/utils/misc.dart'; import 'package:solidpod/src/solid/utils/rdf.dart' show tripleMapToTurtle, turtleToTripleMap; +/// Generates a public key block from a given key content. +String genPubKeyStr(String pubKeyContent) => + '''-----BEGIN RSA PUBLIC KEY-----\n$pubKeyContent\n-----END RSA PUBLIC KEY-----'''; + +/// Get unique bit of the webId +String getUniqueStrWebId(String webId) { + var uniqueStr = webId.replaceAll('https://', ''); + uniqueStr = uniqueStr.replaceAll('http://', ''); + uniqueStr = uniqueStr.replaceAll('/$profCard', ''); + uniqueStr = uniqueStr.replaceAll('/', '-'); + + return uniqueStr; +} + /// Derive the master key from the security key Key genMasterKey(String securityKey) => Key.fromUtf8( sha256.convert(utf8.encode(securityKey)).toString().substring(0, 32), diff --git a/lib/src/solid/utils/key_manager.dart b/lib/src/solid/utils/key_manager.dart index 049b5b8c..8d4accc6 100644 --- a/lib/src/solid/utils/key_manager.dart +++ b/lib/src/solid/utils/key_manager.dart @@ -42,6 +42,8 @@ import 'package:pointycastle/asymmetric/api.dart'; import 'package:solidpod/src/solid/api/rest_api.dart'; import 'package:solidpod/src/solid/constants/common.dart'; +import 'package:solidpod/src/solid/utils/data_encryption.dart'; +import 'package:solidpod/src/solid/utils/get_url_helper.dart'; import 'package:solidpod/src/solid/utils/key_helper.dart'; import 'package:solidpod/src/solid/utils/misc.dart'; diff --git a/lib/src/solid/utils/large_file_helper.dart b/lib/src/solid/utils/large_file_helper.dart index f48aa36c..0a2d4113 100644 --- a/lib/src/solid/utils/large_file_helper.dart +++ b/lib/src/solid/utils/large_file_helper.dart @@ -47,10 +47,11 @@ import 'package:solidpod/src/solid/constants/schema.dart' import 'package:solidpod/src/solid/read_external_pod.dart' show readExternalPod; import 'package:solidpod/src/solid/read_pod.dart' show readPod; import 'package:solidpod/src/solid/utils/authdata_manager.dart'; +import 'package:solidpod/src/solid/utils/get_url_helper.dart'; import 'package:solidpod/src/solid/utils/key_helper.dart' show genRandIndividualKey, genRandIV; import 'package:solidpod/src/solid/utils/misc.dart' - show deleteAclForResource, getDataDirPath, getDirUrl, getFileUrl; + show deleteAclForResource, getDataDirPath; import 'package:solidpod/src/solid/utils/permission.dart' show genAclTurtle; import 'package:solidpod/src/solid/utils/rdf.dart' show tripleMapToTurtle, turtleToTripleMap; diff --git a/lib/src/solid/utils/misc.dart b/lib/src/solid/utils/misc.dart index ffcfa747..ca322f70 100644 --- a/lib/src/solid/utils/misc.dart +++ b/lib/src/solid/utils/misc.dart @@ -30,11 +30,8 @@ library; -import 'dart:convert'; - import 'package:flutter/foundation.dart' show debugPrint; -import 'package:crypto/crypto.dart'; import 'package:encrypter_plus/encrypter_plus.dart'; import 'package:fast_rsa/fast_rsa.dart' show KeyPair; import 'package:intl/intl.dart'; @@ -51,7 +48,10 @@ import 'package:solidpod/src/solid/constants/web_acl.dart'; import 'package:solidpod/src/solid/revoke_permission_to_recipients.dart'; import 'package:solidpod/src/solid/utils/app_info.dart'; import 'package:solidpod/src/solid/utils/authdata_manager.dart'; +import 'package:solidpod/src/solid/utils/data_encryption.dart'; import 'package:solidpod/src/solid/utils/exceptions.dart'; +import 'package:solidpod/src/solid/utils/get_url_helper.dart'; +import 'package:solidpod/src/solid/utils/init_pod_gen_resources.dart'; import 'package:solidpod/src/solid/utils/key_manager.dart'; import 'package:solidpod/src/solid/utils/permission.dart'; import 'package:solidpod/src/solid/utils/rdf.dart'; @@ -85,19 +85,6 @@ Future writeToSecureStorage(String key, String value) async { ); } -/// Encrypt data using AES with the specified key -String encryptData(String data, Key key, IV iv, {AESMode mode = AESMode.sic}) => - Encrypter(AES(key, mode: mode)).encrypt(data, iv: iv).base64; - -/// Decrypt a ciphertext value -String decryptData( - String encData, - Key key, - IV iv, { - AESMode mode = AESMode.sic, -}) => - Encrypter(AES(key, mode: mode)).decrypt(Encrypted.from64(encData), iv: iv); - /// Load and parse a private TTL file from POD // Future> loadPrvTTL(String fileUrl) async { // // final fileUrl = await getFileUrl(filePath); @@ -138,131 +125,6 @@ String decryptData( // } // } -/// Generates a public key block from a given key content. -String genPubKeyStr(String pubKeyContent) => - '''-----BEGIN RSA PUBLIC KEY-----\n$pubKeyContent\n-----END RSA PUBLIC KEY-----'''; - -/// Get unique bit of the webId -String getUniqueStrWebId(String webId) { - var uniqueStr = webId.replaceAll('https://', ''); - uniqueStr = uniqueStr.replaceAll('http://', ''); - uniqueStr = uniqueStr.replaceAll('/$profCard', ''); - uniqueStr = uniqueStr.replaceAll('/', '-'); - - return uniqueStr; -} - -/// Generate unique ID for the resource URL -/// In order for the ID to be unique across all webIDs and no one can -/// reverse engineer the resource being shared we also use receiver webId -/// in the ID generation process -String getUniqueIdResUrl(String resourceUrl, String receiverWebId) { - return sha256.convert(utf8.encode(resourceUrl + receiverWebId)).toString(); -} - -/// From a given resource path [resourcePath] create its URL -/// [resourcePath] should be relative to the POD, e.g., `myapp/data/abc.ttl`. -/// [isFile] should be true if the resource is a file, otherwise false if it -/// is a directory. -/// [webId] (optionally) specifies the resource's POD (could be external). -/// returns the full resource URL - -Future _getResourceUrl( - String resourcePath, { - required bool isFile, - String? webId, -}) async { - final wId = webId ?? await AuthDataManager.getWebId(); - - assert(wId != null); - assert(wId!.contains(profCard)); - - final resourceUrl = wId!.replaceAll(profCard, resourcePath); - - if (!isFile && !resourceUrl.endsWith('/')) { - return '$resourceUrl/'; - } - - return resourceUrl; -} - -/// Create the URL for a file -Future getFileUrl(String filePath, {String? webId}) async => - await _getResourceUrl(filePath, isFile: true, webId: webId); - -/// Create the URL for a directory (container) -Future getDirUrl(String dirPath, {String? webId}) async => - await _getResourceUrl(dirPath, isFile: false, webId: webId); - -/// Get resource Url from a filename, with different options for how -/// the filename is provided. If [isExternalRes] or [isFileUrl] is -/// set to true, the filename is already the resource url and is -/// returned unchanged. -/// -/// Parameters: -/// - [fileName] - name of file of interest. -/// - [isFile] - flag describing whether the resources is a file or not. -/// If false the [fileName] is a directory. (Default: true). -/// - [isFilePath] - flag describing whether [fileName] includes the -/// directory data path. (Default: false). -/// - [isFileUrl] - flag describing whether [fileName] is the url of the -/// file. (Default: false). -/// - [isExternalRes] - flag describing whether the file is an external -/// file shared with the user. In which case the [fileName] should be -/// the full URL of the file. (Default: false). - -Future filenameToResourceUrl({ - required String fileName, - bool isFile = true, - bool isFileUrl = false, - bool isExternalRes = false, -}) async { - final String resourceUrl; - final String filePath; - - // Note: external resources always specified by url - if (isExternalRes) { - isFileUrl = true; - } - - // If not already a url, get url - if (!isExternalRes && !isFileUrl) { - // Get the file path - // Ensure path uses correct path separators and - // has app data dir prepended. - filePath = await normalizeFilePath(fileName, null); - - // Get the url of the resource by prepending the - // user = owner webID (excluding profCard) - if (isFile) { - resourceUrl = await getFileUrl(filePath); - } else { - resourceUrl = await getDirUrl(filePath); - } - } else { - // Use fileName, fileName already the resource url - resourceUrl = fileName; - } - - return resourceUrl; -} - -/// Derive external POD inherited parent directory url from the resource url -/// and parent directory path -String getExtDirUrl(String resourceUrl, String dirPath) { - int pathIndex = resourceUrl.indexOf(dirPath); - - String dirUrl = ''; - if (pathIndex != -1) { - dirUrl = resourceUrl.substring(0, pathIndex + dirPath.length); - if (!dirUrl.endsWith('/')) { - dirUrl += '/'; - } - } - - return dirUrl; -} - /// Encrypt a given data string and format to TTL Future getEncTTLStr( String filePath, @@ -371,76 +233,6 @@ Future createDir(String dirUrl) async { Future deleteLogIn() async => AuthDataManager.removeAuthData(); -/// Generates a list of default folder paths for a given application. -/// -/// This function takes the name of an application as input and returns a list of strings. -/// Each string in the list represents a path to a default folder for the application. - -Future> generateDefaultFolders() async { - final dataDirLoc = [appDirName, dataDir].join('/'); - final sharingDirLoc = [appDirName, sharingDir].join('/'); - final sharedDirLoc = [appDirName, sharedDir].join('/'); - final encDirLoc = [appDirName, encDir].join('/'); - final logDirLoc = [appDirName, logsDir].join('/'); - - final folders = [ - appDirName, - sharingDirLoc, - sharedDirLoc, - dataDirLoc, - encDirLoc, - logDirLoc, - ]; - return folders; -} - -/// Generates a list of custom folder paths for a given application. -/// -/// This function takes the custom folder list given by the developer and -/// creates paths for those folders. Each custom folder will be created -/// inside the "data" folder. Following are few examples. -/// - Custom folder 'myDir1' will be created as '/data/myDir1' -/// - Custom folder 'myDir1/myDir3' will be created as '/data/myDir1/myDir3' -/// - Custom folder 'data' will be created as '/data/data' - -List generateCustomFolders(List customFolderPaths) { - List customFolderList = []; - if (customFolderPaths.isNotEmpty) { - for (String folderPath in customFolderPaths) { - // Remove any leading '/' characters - folderPath = folderPath.replaceFirst(RegExp(r'^/+'), ''); - customFolderList.add([appDirName, dataDir, folderPath].join('/')); - } - } - return customFolderList; -} - -/// Generates a list of default folder paths for a given application. -/// -/// This function takes the name of an application as input and returns a list of strings. -/// Each string in the list represents a path to a default folder for the application. - -Future> generateDefaultFiles() async { - final sharingDirLoc = [appDirName, sharingDir].join('/'); - final sharedDirLoc = [appDirName, sharedDir].join('/'); - final encDirLoc = [appDirName, encDir].join('/'); - final logDirLoc = [appDirName, logsDir].join('/'); - - final files = { - sharingDirLoc: [ - pubKeyFile, - '$pubKeyFile.acl', - ], - logDirLoc: [ - permLogFile, - '$permLogFile.acl', - ], - sharedDirLoc: ['.acl'], - encDirLoc: [encKeyFile, indKeyFile], - }; - return files; -} - /// Set directory name for the app for storing the POD data /// /// If not initially set the app name will be taken by default. diff --git a/lib/src/solid/write_external_pod.dart b/lib/src/solid/write_external_pod.dart index 10174035..ba06b144 100644 --- a/lib/src/solid/write_external_pod.dart +++ b/lib/src/solid/write_external_pod.dart @@ -39,6 +39,7 @@ import 'package:solidpod/src/solid/api/rest_api.dart'; import 'package:solidpod/src/solid/common_func.dart'; import 'package:solidpod/src/solid/constants/common.dart'; import 'package:solidpod/src/solid/utils/exceptions.dart'; +import 'package:solidpod/src/solid/utils/get_url_helper.dart'; import 'package:solidpod/src/solid/utils/key_helper.dart' show genRandIV; import 'package:solidpod/src/solid/utils/key_inheritance.dart'; import 'package:solidpod/src/solid/utils/key_manager.dart' show KeyManager; From 9380ec2538ef1b29a4e0f13233f3e73fb7b3dd0e Mon Sep 17 00:00:00 2001 From: anushkavidanage Date: Wed, 14 Jan 2026 13:15:34 +1100 Subject: [PATCH 3/4] remove unused imports --- lib/src/solid/revoke_permission.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/src/solid/revoke_permission.dart b/lib/src/solid/revoke_permission.dart index 9eb34bf8..cab41bd9 100644 --- a/lib/src/solid/revoke_permission.dart +++ b/lib/src/solid/revoke_permission.dart @@ -37,7 +37,6 @@ import 'package:solidpod/src/solid/constants/common.dart'; import 'package:solidpod/src/solid/constants/web_acl.dart'; import 'package:solidpod/src/solid/models/log_entry.dart'; import 'package:solidpod/src/solid/solid_func_call_status.dart'; -import 'package:solidpod/src/solid/utils/authdata_manager.dart'; import 'package:solidpod/src/solid/utils/get_url_helper.dart'; import 'package:solidpod/src/solid/utils/misc.dart'; import 'package:solidpod/src/solid/utils/permission.dart'; From 03c0c88196610ac0b55c5ac2b43546ca647c9981 Mon Sep 17 00:00:00 2001 From: Graham Williams Date: Wed, 14 Jan 2026 14:15:49 +1100 Subject: [PATCH 4/4] Bump version 0.9.7 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 5c341286..3e8d1208 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: solidpod description: Support access to private data from PODs on Solid servers. -version: 0.9.3 +version: 0.9.7 homepage: https://github.com/anusii/solidpod environment: