From 0e7a19c6b7b329e707fdedef50f4c8b264b62788 Mon Sep 17 00:00:00 2001 From: Yiqun Zhang <guiyanakuang@gmail.com> Date: Tue, 21 Nov 2023 15:32:33 +0800 Subject: [PATCH] :sparkles: Create a tooltip for clipevery to interact with keychain --- .../clipevery/macos/MacosClipboardService.kt | 4 +- .../clipevery/macos/MacosKeychainHelper.kt | 22 +++++ .../clipevery/macos/api/MacClipboardApi.kt | 13 --- .../com/clipevery/macos/api/MacosApi.kt | 21 ++++ .../resources/darwin-x86-64/libMacosApi.dylib | Bin 0 -> 58936 bytes .../src/desktopMain/swift/MacosApi.swift | 93 ++++++++++++++++++ .../macos/MacosKeychainHelperTest.kt | 24 +++++ 7 files changed, 162 insertions(+), 15 deletions(-) create mode 100644 composeApp/src/desktopMain/kotlin/com/clipevery/macos/MacosKeychainHelper.kt delete mode 100644 composeApp/src/desktopMain/kotlin/com/clipevery/macos/api/MacClipboardApi.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/clipevery/macos/api/MacosApi.kt create mode 100755 composeApp/src/desktopMain/resources/darwin-x86-64/libMacosApi.dylib create mode 100644 composeApp/src/desktopMain/swift/MacosApi.swift create mode 100644 composeApp/src/desktopTest/kotlin/com/clipevery/macos/MacosKeychainHelperTest.kt diff --git a/composeApp/src/desktopMain/kotlin/com/clipevery/macos/MacosClipboardService.kt b/composeApp/src/desktopMain/kotlin/com/clipevery/macos/MacosClipboardService.kt index 133cdafd5..2e05c8369 100644 --- a/composeApp/src/desktopMain/kotlin/com/clipevery/macos/MacosClipboardService.kt +++ b/composeApp/src/desktopMain/kotlin/com/clipevery/macos/MacosClipboardService.kt @@ -1,7 +1,7 @@ package com.clipevery.macos import com.clipevery.clip.ClipboardService -import com.clipevery.macos.api.MacClipboard +import com.clipevery.macos.api.MacosApi import java.awt.Toolkit import java.awt.datatransfer.Clipboard import java.awt.datatransfer.Transferable @@ -22,7 +22,7 @@ class MacosClipboardService override fun run() { try { - MacClipboard.INSTANCE.clipboardChangeCount.let { currentChangeCount -> + MacosApi.INSTANCE.getClipboardChangeCount().let { currentChangeCount -> if (changeCount == currentChangeCount) { return } diff --git a/composeApp/src/desktopMain/kotlin/com/clipevery/macos/MacosKeychainHelper.kt b/composeApp/src/desktopMain/kotlin/com/clipevery/macos/MacosKeychainHelper.kt new file mode 100644 index 000000000..1d7f389cd --- /dev/null +++ b/composeApp/src/desktopMain/kotlin/com/clipevery/macos/MacosKeychainHelper.kt @@ -0,0 +1,22 @@ +package com.clipevery.macos + +import com.clipevery.macos.api.MacosApi + +object MacosKeychainHelper { + + fun getPassword(service: String, account: String): String? { + return MacosApi.INSTANCE.getPassword(service, account) + } + + fun setPassword(service: String, account: String, password: String): Boolean { + return MacosApi.INSTANCE.setPassword(service, account, password) + } + + fun updatePassword(service: String, account: String, password: String): Boolean { + return MacosApi.INSTANCE.updatePassword(service, account, password) + } + + fun deletePassword(service: String, account: String): Boolean { + return MacosApi.INSTANCE.deletePassword(service, account) + } +} \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/com/clipevery/macos/api/MacClipboardApi.kt b/composeApp/src/desktopMain/kotlin/com/clipevery/macos/api/MacClipboardApi.kt deleted file mode 100644 index f194412a8..000000000 --- a/composeApp/src/desktopMain/kotlin/com/clipevery/macos/api/MacClipboardApi.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.clipevery.macos.api - -import com.sun.jna.Library -import com.sun.jna.Native - - -interface MacClipboard : Library { - val clipboardChangeCount: Int - - companion object { - val INSTANCE: MacClipboard = Native.load("ClipboardHelper", MacClipboard::class.java) - } -} \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/com/clipevery/macos/api/MacosApi.kt b/composeApp/src/desktopMain/kotlin/com/clipevery/macos/api/MacosApi.kt new file mode 100644 index 000000000..adbdbeb78 --- /dev/null +++ b/composeApp/src/desktopMain/kotlin/com/clipevery/macos/api/MacosApi.kt @@ -0,0 +1,21 @@ +package com.clipevery.macos.api + +import com.sun.jna.Library +import com.sun.jna.Native + +interface MacosApi : Library { + + fun getClipboardChangeCount(): Int + + fun getPassword(service: String, account: String): String? + + fun setPassword(service: String, account: String, password: String): Boolean + + fun updatePassword(service: String, account: String, password: String): Boolean + + fun deletePassword(service: String, account: String): Boolean + + companion object { + val INSTANCE: MacosApi = Native.load("MacosApi", MacosApi::class.java) + } +} \ No newline at end of file diff --git a/composeApp/src/desktopMain/resources/darwin-x86-64/libMacosApi.dylib b/composeApp/src/desktopMain/resources/darwin-x86-64/libMacosApi.dylib new file mode 100755 index 0000000000000000000000000000000000000000..9ab0371f243faa65a2bcabdc8053c37b462eff63 GIT binary patch literal 58936 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 Jr)ZeK^RE<CsxANk literal 0 HcmV?d00001 diff --git a/composeApp/src/desktopMain/swift/MacosApi.swift b/composeApp/src/desktopMain/swift/MacosApi.swift new file mode 100644 index 000000000..4c0c1665e --- /dev/null +++ b/composeApp/src/desktopMain/swift/MacosApi.swift @@ -0,0 +1,93 @@ +import Cocoa +import Foundation +import Security + +@_cdecl("getClipboardChangeCount") +public func getClipboardChangeCount() -> Int { + let pasteboard = NSPasteboard.general + return pasteboard.changeCount +} + +@_cdecl("getPassword") +public func getPassword(service: UnsafePointer<CChar>, account: UnsafePointer<CChar>) -> UnsafePointer<CChar>? { + let serviceString = String(cString: service) + let accountString = String(cString: account) + + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrService as String: serviceString, + kSecAttrAccount as String: accountString, + kSecReturnData as String: kCFBooleanTrue!, + kSecMatchLimit as String: kSecMatchLimitOne + ] + + var item: CFTypeRef? + let status = SecItemCopyMatching(query as CFDictionary, &item) + + guard status == errSecSuccess else { return nil } + + if let data = item as? Data, let password = String(data: data, encoding: .utf8) { + return UnsafePointer<CChar>(strdup(password)) + } + + return nil +} + +@_cdecl("setPassword") +public func setPassword(service: UnsafePointer<CChar>, account: UnsafePointer<CChar>, password: UnsafePointer<CChar>) -> Bool { + let serviceString = String(cString: service) + let accountString = String(cString: account) + let passwordString = String(cString: password) + + let passwordData = passwordString.data(using: .utf8)! + + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrService as String: serviceString, + kSecAttrAccount as String: accountString, + kSecValueData as String: passwordData + ] + + // Try to add the item to the keychain + let status = SecItemAdd(query as CFDictionary, nil) + + // Check the result + return status == errSecSuccess +} + +@_cdecl("updatePassword") +public func updatePassword(service: UnsafePointer<CChar>, account: UnsafePointer<CChar>, newPassword: UnsafePointer<CChar>) -> Bool { + let serviceString = String(cString: service) + let accountString = String(cString: account) + let newPasswordString = String(cString: newPassword) + + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrService as String: serviceString, + kSecAttrAccount as String: accountString + ] + + let updateFields: [String: Any] = [ + kSecValueData as String: newPasswordString.data(using: .utf8)! + ] + + let status = SecItemUpdate(query as CFDictionary, updateFields as CFDictionary) + + return status == errSecSuccess +} + +@_cdecl("deletePassword") +public func deletePassword(service: UnsafePointer<CChar>, account: UnsafePointer<CChar>) -> Bool { + let serviceString = String(cString: service) + let accountString = String(cString: account) + + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrService as String: serviceString, + kSecAttrAccount as String: accountString + ] + + let status = SecItemDelete(query as CFDictionary) + + return status == errSecSuccess +} \ No newline at end of file diff --git a/composeApp/src/desktopTest/kotlin/com/clipevery/macos/MacosKeychainHelperTest.kt b/composeApp/src/desktopTest/kotlin/com/clipevery/macos/MacosKeychainHelperTest.kt new file mode 100644 index 000000000..259520de0 --- /dev/null +++ b/composeApp/src/desktopTest/kotlin/com/clipevery/macos/MacosKeychainHelperTest.kt @@ -0,0 +1,24 @@ +package com.clipevery.macos + +import com.clipevery.platform.currentPlatform +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class MacosKeychainHelperTest { + + @Test + fun testKeychain() { + if (currentPlatform().isMacos()) { + MacosKeychainHelper.setPassword("com.clipevery", "test", "test") + var password = MacosKeychainHelper.getPassword("com.clipevery", "test") + assertEquals("test", password) + MacosKeychainHelper.updatePassword("com.clipevery", "test", "test1") + password = MacosKeychainHelper.getPassword("com.clipevery", "test") + assertEquals("test1", password) + assertTrue(MacosKeychainHelper.deletePassword("com.clipevery", "test")) + val password1 = MacosKeychainHelper.getPassword("com.clipevery", "test") + assertTrue(password1 == null) + } + } +} \ No newline at end of file