mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 10:30:38 +00:00
Update dependencies
This commit is contained in:
parent
9da6f3a702
commit
6e61bc2000
14 changed files with 55 additions and 98 deletions
|
@ -1,6 +1,7 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
**Attention**: Now requires native libsignal-client version 0.24.0
|
||||||
|
|
||||||
## [0.11.10] - 2023-05-11
|
## [0.11.10] - 2023-05-11
|
||||||
**Attention**: Now requires native libsignal-client version 0.23.1
|
**Attention**: Now requires native libsignal-client version 0.23.1
|
||||||
|
|
|
@ -170,6 +170,9 @@
|
||||||
{"name":"SENDING"}
|
{"name":"SENDING"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name":"org.signal.libsignal.protocol.state.KyberPreKeyStore"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name":"org.signal.libsignal.protocol.state.PreKeyRecord",
|
"name":"org.signal.libsignal.protocol.state.PreKeyRecord",
|
||||||
"fields":[{"name":"unsafeHandle"}]
|
"fields":[{"name":"unsafeHandle"}]
|
||||||
|
|
|
@ -1142,7 +1142,12 @@
|
||||||
"name":"org.asamk.signal.json.JsonTypingMessage",
|
"name":"org.asamk.signal.json.JsonTypingMessage",
|
||||||
"allDeclaredFields":true,
|
"allDeclaredFields":true,
|
||||||
"allDeclaredMethods":true,
|
"allDeclaredMethods":true,
|
||||||
"allDeclaredConstructors":true
|
"allDeclaredConstructors":true,
|
||||||
|
"methods":[
|
||||||
|
{"name":"action","parameterTypes":[] },
|
||||||
|
{"name":"groupId","parameterTypes":[] },
|
||||||
|
{"name":"timestamp","parameterTypes":[] }
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name":"org.asamk.signal.jsonrpc.JsonRpcBatchMessage",
|
"name":"org.asamk.signal.jsonrpc.JsonRpcBatchMessage",
|
||||||
|
|
|
@ -211,14 +211,11 @@
|
||||||
{
|
{
|
||||||
"pattern":"\\Qjni/x86_64-Linux/libjffi-1.2.so\\E"
|
"pattern":"\\Qjni/x86_64-Linux/libjffi-1.2.so\\E"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"pattern":"\\Qlibsignal_jni.so\\E"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"pattern":"\\Qlibsignal_jni.dylib\\E"
|
"pattern":"\\Qlibsignal_jni.dylib\\E"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"pattern":"\\Qsignal_jni.dll\\E"
|
"pattern":"\\Qlibsignal_jni.so\\E"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"pattern":"\\Qorg/asamk/signal/manager/config/ias.store\\E"
|
"pattern":"\\Qorg/asamk/signal/manager/config/ias.store\\E"
|
||||||
|
@ -232,6 +229,9 @@
|
||||||
{
|
{
|
||||||
"pattern":"\\Qorg/sqlite/native/Linux/x86_64/libsqlitejdbc.so\\E"
|
"pattern":"\\Qorg/sqlite/native/Linux/x86_64/libsqlitejdbc.so\\E"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"pattern":"\\Qsignal_jni.dll\\E"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"pattern":"\\Qsqlite-jdbc.properties\\E"
|
"pattern":"\\Qsqlite-jdbc.properties\\E"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
package org.asamk.signal.manager.actions;
|
|
||||||
|
|
||||||
import org.asamk.signal.manager.helper.Context;
|
|
||||||
|
|
||||||
public class SendPniIdentityKeyAction implements HandleAction {
|
|
||||||
|
|
||||||
private static final SendPniIdentityKeyAction INSTANCE = new SendPniIdentityKeyAction();
|
|
||||||
|
|
||||||
private SendPniIdentityKeyAction() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SendPniIdentityKeyAction create() {
|
|
||||||
return INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(Context context) throws Throwable {
|
|
||||||
context.getSyncHelper().sendPniIdentity();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -44,17 +44,7 @@ public class ServiceConfig {
|
||||||
|
|
||||||
public static AccountAttributes.Capabilities getCapabilities(boolean isPrimaryDevice) {
|
public static AccountAttributes.Capabilities getCapabilities(boolean isPrimaryDevice) {
|
||||||
final var giftBadges = !isPrimaryDevice;
|
final var giftBadges = !isPrimaryDevice;
|
||||||
return new AccountAttributes.Capabilities(false,
|
return new AccountAttributes.Capabilities(false, true, true, true, true, giftBadges, false, false);
|
||||||
true,
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
giftBadges,
|
|
||||||
false,
|
|
||||||
false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isSignalClientAvailable() {
|
public static boolean isSignalClientAvailable() {
|
||||||
|
|
|
@ -73,7 +73,7 @@ public class IdentityHelper {
|
||||||
final var recipientId = account.getRecipientResolver().resolveRecipient(serviceId);
|
final var recipientId = account.getRecipientResolver().resolveRecipient(serviceId);
|
||||||
final var address = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId);
|
final var address = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId);
|
||||||
|
|
||||||
if (account.getAccountCapabilities().getUuid()) {
|
if (false) {
|
||||||
if (serviceId.isUnknown()) {
|
if (serviceId.isUnknown()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import org.asamk.signal.manager.actions.RetrieveProfileAction;
|
||||||
import org.asamk.signal.manager.actions.RetrieveStorageDataAction;
|
import org.asamk.signal.manager.actions.RetrieveStorageDataAction;
|
||||||
import org.asamk.signal.manager.actions.SendGroupInfoAction;
|
import org.asamk.signal.manager.actions.SendGroupInfoAction;
|
||||||
import org.asamk.signal.manager.actions.SendGroupInfoRequestAction;
|
import org.asamk.signal.manager.actions.SendGroupInfoRequestAction;
|
||||||
import org.asamk.signal.manager.actions.SendPniIdentityKeyAction;
|
|
||||||
import org.asamk.signal.manager.actions.SendProfileKeyAction;
|
import org.asamk.signal.manager.actions.SendProfileKeyAction;
|
||||||
import org.asamk.signal.manager.actions.SendReceiptAction;
|
import org.asamk.signal.manager.actions.SendReceiptAction;
|
||||||
import org.asamk.signal.manager.actions.SendRetryMessageRequestAction;
|
import org.asamk.signal.manager.actions.SendRetryMessageRequestAction;
|
||||||
|
@ -35,7 +34,6 @@ import org.asamk.signal.manager.storage.groups.GroupInfoV1;
|
||||||
import org.asamk.signal.manager.storage.recipients.Profile;
|
import org.asamk.signal.manager.storage.recipients.Profile;
|
||||||
import org.asamk.signal.manager.storage.recipients.RecipientId;
|
import org.asamk.signal.manager.storage.recipients.RecipientId;
|
||||||
import org.asamk.signal.manager.storage.stickers.StickerPack;
|
import org.asamk.signal.manager.storage.stickers.StickerPack;
|
||||||
import org.asamk.signal.manager.util.KeyUtils;
|
|
||||||
import org.signal.libsignal.metadata.ProtocolInvalidKeyException;
|
import org.signal.libsignal.metadata.ProtocolInvalidKeyException;
|
||||||
import org.signal.libsignal.metadata.ProtocolInvalidKeyIdException;
|
import org.signal.libsignal.metadata.ProtocolInvalidKeyIdException;
|
||||||
import org.signal.libsignal.metadata.ProtocolInvalidMessageException;
|
import org.signal.libsignal.metadata.ProtocolInvalidMessageException;
|
||||||
|
@ -525,9 +523,6 @@ public final class IncomingMessageHandler {
|
||||||
if (rm.isConfigurationRequest()) {
|
if (rm.isConfigurationRequest()) {
|
||||||
actions.add(SendSyncConfigurationAction.create());
|
actions.add(SendSyncConfigurationAction.create());
|
||||||
}
|
}
|
||||||
if (rm.isPniIdentityRequest()) {
|
|
||||||
actions.add(SendPniIdentityKeyAction.create());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (syncMessage.getGroups().isPresent()) {
|
if (syncMessage.getGroups().isPresent()) {
|
||||||
try {
|
try {
|
||||||
|
@ -631,12 +626,6 @@ public final class IncomingMessageHandler {
|
||||||
.get());
|
.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (syncMessage.getPniIdentity().isPresent()) {
|
|
||||||
final var pniIdentity = syncMessage.getPniIdentity().get();
|
|
||||||
account.setPniIdentityKeyPair(KeyUtils.getIdentityKeyPair(pniIdentity.getPublicKey().toByteArray(),
|
|
||||||
pniIdentity.getPrivateKey().toByteArray()));
|
|
||||||
actions.add(RefreshPreKeysAction.create());
|
|
||||||
}
|
|
||||||
if (syncMessage.getPniChangeNumber().isPresent()) {
|
if (syncMessage.getPniChangeNumber().isPresent()) {
|
||||||
final var pniChangeNumber = syncMessage.getPniChangeNumber().get();
|
final var pniChangeNumber = syncMessage.getPniChangeNumber().get();
|
||||||
logger.debug("Received PNI change number sync message, applying.");
|
logger.debug("Received PNI change number sync message, applying.");
|
||||||
|
|
|
@ -2,7 +2,6 @@ package org.asamk.signal.manager.helper;
|
||||||
|
|
||||||
import org.asamk.signal.manager.api.IncorrectPinException;
|
import org.asamk.signal.manager.api.IncorrectPinException;
|
||||||
import org.asamk.signal.manager.api.Pair;
|
import org.asamk.signal.manager.api.Pair;
|
||||||
import org.asamk.signal.manager.util.PinHashing;
|
|
||||||
import org.signal.libsignal.protocol.InvalidKeyException;
|
import org.signal.libsignal.protocol.InvalidKeyException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -11,6 +10,7 @@ import org.whispersystems.signalservice.api.KeyBackupService;
|
||||||
import org.whispersystems.signalservice.api.KeyBackupServicePinException;
|
import org.whispersystems.signalservice.api.KeyBackupServicePinException;
|
||||||
import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException;
|
import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException;
|
||||||
import org.whispersystems.signalservice.api.kbs.MasterKey;
|
import org.whispersystems.signalservice.api.kbs.MasterKey;
|
||||||
|
import org.whispersystems.signalservice.api.kbs.PinHashUtil;
|
||||||
import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException;
|
import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException;
|
||||||
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
|
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
|
||||||
import org.whispersystems.signalservice.internal.push.LockedException;
|
import org.whispersystems.signalservice.internal.push.LockedException;
|
||||||
|
@ -37,7 +37,7 @@ public class PinHelper {
|
||||||
String pin, MasterKey masterKey
|
String pin, MasterKey masterKey
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
final var pinChangeSession = keyBackupService.newPinChangeSession();
|
final var pinChangeSession = keyBackupService.newPinChangeSession();
|
||||||
final var hashedPin = PinHashing.hashPin(pin, pinChangeSession);
|
final var hashedPin = PinHashUtil.hashPin(pin, pinChangeSession.hashSalt());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
pinChangeSession.setPin(hashedPin, masterKey);
|
pinChangeSession.setPin(hashedPin, masterKey);
|
||||||
|
@ -128,7 +128,7 @@ public class PinHelper {
|
||||||
var session = keyBackupService.newRegistrationSession(basicStorageCredentials, tokenResponse);
|
var session = keyBackupService.newRegistrationSession(basicStorageCredentials, tokenResponse);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var hashedPin = PinHashing.hashPin(pin, session);
|
var hashedPin = PinHashUtil.hashPin(pin, session.hashSalt());
|
||||||
var kbsData = session.restorePin(hashedPin);
|
var kbsData = session.restorePin(hashedPin);
|
||||||
if (kbsData == null) {
|
if (kbsData == null) {
|
||||||
throw new AssertionError("Null not expected");
|
throw new AssertionError("Null not expected");
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
package org.asamk.signal.manager.helper;
|
package org.asamk.signal.manager.helper;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
|
||||||
|
|
||||||
import org.asamk.signal.manager.api.TrustLevel;
|
import org.asamk.signal.manager.api.TrustLevel;
|
||||||
import org.asamk.signal.manager.groups.GroupId;
|
import org.asamk.signal.manager.groups.GroupId;
|
||||||
import org.asamk.signal.manager.storage.SignalAccount;
|
import org.asamk.signal.manager.storage.SignalAccount;
|
||||||
|
@ -233,15 +231,6 @@ public class SyncHelper {
|
||||||
context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forConfiguration(configurationMessage));
|
context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forConfiguration(configurationMessage));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendPniIdentity() {
|
|
||||||
final var pniIdentityKeyPair = account.getPniIdentityKeyPair();
|
|
||||||
var pniIdentity = SignalServiceProtos.SyncMessage.PniIdentity.newBuilder()
|
|
||||||
.setPrivateKey(ByteString.copyFrom(pniIdentityKeyPair.getPrivateKey().serialize()))
|
|
||||||
.setPublicKey(ByteString.copyFrom(pniIdentityKeyPair.getPublicKey().serialize()))
|
|
||||||
.build();
|
|
||||||
context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forPniIdentity(pniIdentity));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void handleSyncDeviceGroups(final InputStream input) {
|
public void handleSyncDeviceGroups(final InputStream input) {
|
||||||
final var s = new DeviceGroupsInputStream(input);
|
final var s = new DeviceGroupsInputStream(input);
|
||||||
DeviceGroup g;
|
DeviceGroup g;
|
||||||
|
|
|
@ -71,7 +71,8 @@ public class UnidentifiedAccessHelper {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(recipientUnidentifiedAccessKey,
|
return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(recipientUnidentifiedAccessKey,
|
||||||
senderCertificate), new UnidentifiedAccess(selfUnidentifiedAccessKey, senderCertificate)));
|
senderCertificate,
|
||||||
|
false), new UnidentifiedAccess(selfUnidentifiedAccessKey, senderCertificate, false)));
|
||||||
} catch (InvalidCertificateException e) {
|
} catch (InvalidCertificateException e) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
@ -87,8 +88,9 @@ public class UnidentifiedAccessHelper {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(selfUnidentifiedAccessKey,
|
return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(selfUnidentifiedAccessKey,
|
||||||
selfUnidentifiedAccessCertificate),
|
selfUnidentifiedAccessCertificate,
|
||||||
new UnidentifiedAccess(selfUnidentifiedAccessKey, selfUnidentifiedAccessCertificate)));
|
false),
|
||||||
|
new UnidentifiedAccess(selfUnidentifiedAccessKey, selfUnidentifiedAccessCertificate, false)));
|
||||||
} catch (InvalidCertificateException e) {
|
} catch (InvalidCertificateException e) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import org.signal.libsignal.protocol.NoSessionException;
|
||||||
import org.signal.libsignal.protocol.SignalProtocolAddress;
|
import org.signal.libsignal.protocol.SignalProtocolAddress;
|
||||||
import org.signal.libsignal.protocol.groups.state.SenderKeyRecord;
|
import org.signal.libsignal.protocol.groups.state.SenderKeyRecord;
|
||||||
import org.signal.libsignal.protocol.state.IdentityKeyStore;
|
import org.signal.libsignal.protocol.state.IdentityKeyStore;
|
||||||
|
import org.signal.libsignal.protocol.state.KyberPreKeyRecord;
|
||||||
import org.signal.libsignal.protocol.state.PreKeyRecord;
|
import org.signal.libsignal.protocol.state.PreKeyRecord;
|
||||||
import org.signal.libsignal.protocol.state.PreKeyStore;
|
import org.signal.libsignal.protocol.state.PreKeyStore;
|
||||||
import org.signal.libsignal.protocol.state.SessionRecord;
|
import org.signal.libsignal.protocol.state.SessionRecord;
|
||||||
|
@ -197,4 +198,32 @@ public class SignalProtocolStore implements SignalServiceAccountDataStore {
|
||||||
public boolean isMultiDevice() {
|
public boolean isMultiDevice() {
|
||||||
return isMultiDevice.get();
|
return isMultiDevice.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KyberPreKeyRecord loadKyberPreKey(final int kyberPreKeyId) throws InvalidKeyIdException {
|
||||||
|
// TODO
|
||||||
|
throw new InvalidKeyIdException("Missing kyber prekey with ID: $kyberPreKeyId");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<KyberPreKeyRecord> loadKyberPreKeys() {
|
||||||
|
// TODO
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void storeKyberPreKey(final int kyberPreKeyId, final KyberPreKeyRecord record) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKyberPreKey(final int kyberPreKeyId) {
|
||||||
|
// TODO
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markKyberPreKeyUsed(final int kyberPreKeyId) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
package org.asamk.signal.manager.util;
|
|
||||||
|
|
||||||
import org.bouncycastle.crypto.generators.Argon2BytesGenerator;
|
|
||||||
import org.bouncycastle.crypto.params.Argon2Parameters;
|
|
||||||
import org.whispersystems.signalservice.api.KeyBackupService;
|
|
||||||
import org.whispersystems.signalservice.api.kbs.HashedPin;
|
|
||||||
import org.whispersystems.signalservice.internal.registrationpin.PinHasher;
|
|
||||||
|
|
||||||
public final class PinHashing {
|
|
||||||
|
|
||||||
private PinHashing() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HashedPin hashPin(String pin, KeyBackupService.HashSession hashSession) {
|
|
||||||
final var params = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id).withParallelism(1)
|
|
||||||
.withIterations(32)
|
|
||||||
.withVersion(Argon2Parameters.ARGON2_VERSION_13)
|
|
||||||
.withMemoryAsKB(16 * 1024)
|
|
||||||
.withSalt(hashSession.hashSalt())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
final var generator = new Argon2BytesGenerator();
|
|
||||||
generator.init(params);
|
|
||||||
|
|
||||||
return PinHasher.hashPin(PinHasher.normalize(pin), password -> {
|
|
||||||
var output = new byte[64];
|
|
||||||
generator.generateBytes(password, output);
|
|
||||||
return output;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,7 +7,7 @@ dependencyResolutionManagement {
|
||||||
versionCatalogs {
|
versionCatalogs {
|
||||||
create("libs") {
|
create("libs") {
|
||||||
library("bouncycastle", "org.bouncycastle", "bcprov-jdk15on").version("1.70")
|
library("bouncycastle", "org.bouncycastle", "bcprov-jdk15on").version("1.70")
|
||||||
library("jackson.databind", "com.fasterxml.jackson.core", "jackson-databind").version("2.15.0")
|
library("jackson.databind", "com.fasterxml.jackson.core", "jackson-databind").version("2.15.1")
|
||||||
library("argparse4j", "net.sourceforge.argparse4j", "argparse4j").version("0.9.0")
|
library("argparse4j", "net.sourceforge.argparse4j", "argparse4j").version("0.9.0")
|
||||||
library("dbusjava", "com.github.hypfvieh", "dbus-java-transport-native-unixsocket").version("4.3.0")
|
library("dbusjava", "com.github.hypfvieh", "dbus-java-transport-native-unixsocket").version("4.3.0")
|
||||||
version("slf4j", "2.0.7")
|
version("slf4j", "2.0.7")
|
||||||
|
@ -16,7 +16,7 @@ dependencyResolutionManagement {
|
||||||
library("logback", "ch.qos.logback", "logback-classic").version("1.4.7")
|
library("logback", "ch.qos.logback", "logback-classic").version("1.4.7")
|
||||||
|
|
||||||
|
|
||||||
library("signalservice", "com.github.turasa", "signal-service-java").version("2.15.3_unofficial_70")
|
library("signalservice", "com.github.turasa", "signal-service-java").version("2.15.3_unofficial_71")
|
||||||
library("protobuf", "com.google.protobuf", "protobuf-javalite").version("3.23.0")
|
library("protobuf", "com.google.protobuf", "protobuf-javalite").version("3.23.0")
|
||||||
library("sqlite", "org.xerial", "sqlite-jdbc").version("3.41.2.1")
|
library("sqlite", "org.xerial", "sqlite-jdbc").version("3.41.2.1")
|
||||||
library("hikari", "com.zaxxer", "HikariCP").version("5.0.1")
|
library("hikari", "com.zaxxer", "HikariCP").version("5.0.1")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue