[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[libeufin] branch master updated: shared config subcommand for nexus and
From: |
gnunet |
Subject: |
[libeufin] branch master updated: shared config subcommand for nexus and bank |
Date: |
Mon, 27 Nov 2023 19:46:44 +0100 |
This is an automated email from the git hooks/post-receive script.
dold pushed a commit to branch master
in repository libeufin.
The following commit(s) were added to refs/heads/master by this push:
new 87c42907 shared config subcommand for nexus and bank
87c42907 is described below
commit 87c42907a6b4d556f290985263f4a2ecd5b524c4
Author: Florian Dold <florian@dold.me>
AuthorDate: Mon Nov 27 19:46:44 2023 +0100
shared config subcommand for nexus and bank
---
.../kotlin/tech/libeufin/bank/Authentication.kt | 4 +
bank/src/main/kotlin/tech/libeufin/bank/Config.kt | 2 +-
bank/src/main/kotlin/tech/libeufin/bank/Main.kt | 74 +------------
.../tech/libeufin/bank/db/NotificationWatcher.kt | 4 +
.../kotlin/tech/libeufin/bank/db/TransactionDAO.kt | 4 +
nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt | 2 +-
.../kotlin/tech/libeufin/nexus/ebics/Ebics2.kt | 4 +
.../tech/libeufin/nexus/ebics/EbicsCommon.kt | 6 +-
util/build.gradle | 3 +-
util/src/main/kotlin/Config.kt | 3 -
util/src/main/kotlin/ConfigCli.kt | 119 +++++++++++++++++++++
util/src/main/kotlin/DB.kt | 1 +
util/src/main/kotlin/HTTP.kt | 4 +
util/src/main/kotlin/IbanPayto.kt | 4 +
util/src/main/kotlin/TalerConfig.kt | 5 +-
util/src/main/kotlin/UnixDomainSocket.kt | 8 +-
util/src/main/kotlin/XMLUtil.kt | 4 +
util/src/main/kotlin/startServer.kt | 4 +
util/src/main/kotlin/time.kt | 4 +
19 files changed, 173 insertions(+), 86 deletions(-)
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Authentication.kt
b/bank/src/main/kotlin/tech/libeufin/bank/Authentication.kt
index cbef0e1e..4a83e94e 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Authentication.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Authentication.kt
@@ -27,9 +27,13 @@ import io.ktor.util.pipeline.PipelineContext
import java.time.Instant
import net.taler.common.errorcodes.TalerErrorCode
import net.taler.wallet.crypto.Base32Crockford
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
import tech.libeufin.bank.AccountDAO.*
import tech.libeufin.util.*
+private val logger: Logger =
LoggerFactory.getLogger("tech.libeufin.bank.Authentication")
+
private val AUTH_IS_ADMIN = AttributeKey<Boolean>("is_admin");
/** Restrict route access to admin */
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Config.kt
b/bank/src/main/kotlin/tech/libeufin/bank/Config.kt
index ce0afb23..3acee798 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Config.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Config.kt
@@ -28,7 +28,7 @@ import org.slf4j.LoggerFactory
import tech.libeufin.util.DatabaseConfig
private val logger: Logger =
LoggerFactory.getLogger("tech.libeufin.bank.Config")
-private val BANK_CONFIG_SOURCE = ConfigSource("libeufin", "libeufin-bank",
"libeufin-bank")
+val BANK_CONFIG_SOURCE = ConfigSource("libeufin", "libeufin-bank",
"libeufin-bank")
/**
* Application the parsed configuration.
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
index 3f9cc132..cb380f9a 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
@@ -30,7 +30,6 @@ import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.http.content.*
import io.ktor.serialization.kotlinx.json.*
-import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.plugins.*
@@ -42,7 +41,6 @@ import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.utils.io.*
-import io.ktor.utils.io.jvm.javaio.*
import java.time.Duration
import java.util.zip.DataFormatException
import java.util.zip.Inflater
@@ -51,7 +49,6 @@ import java.io.File
import kotlin.system.exitProcess
import kotlinx.coroutines.*
import kotlinx.serialization.ExperimentalSerializationApi
-import kotlinx.serialization.descriptors.*
import kotlinx.serialization.json.*
import net.taler.common.errorcodes.TalerErrorCode
import org.slf4j.Logger
@@ -395,79 +392,10 @@ class ChangePw : CliktCommand("Change account password",
name = "passwd") {
}
}
-// TODO remove ?
-class BankConfigDump : CliktCommand("Dump the configuration", name = "dump") {
- private val configFile by option(
- "--config", "-c",
- help = "set the configuration file"
- )
-
- override fun run() {
- val config = talerConfig(configFile)
- println("# install path: ${config.getInstallPath()}")
- config.load(this.configFile)
- println(config.stringify())
- }
-}
-
-class BankConfigPathsub : CliktCommand("Substitute variables in a path", name
= "pathsub") {
- private val configFile by option(
- "--config", "-c",
- help = "set the configuration file"
- )
- private val pathExpr by argument()
-
- override fun run() {
- val config = talerConfig(configFile)
- println(config.pathsub(pathExpr))
- }
-}
-
-class BankConfigGet : CliktCommand("Lookup config value", name = "get") {
- private val configFile by option(
- "--config", "-c",
- help = "set the configuration file"
- )
- private val isPath by option(
- "--filename", "-f",
- help = "interpret value as path with dollar-expansion"
- ).flag()
- private val sectionName by argument()
- private val optionName by argument()
-
-
- override fun run() {
- val config = talerConfig(configFile)
- if (isPath) {
- val res = config.lookupPath(sectionName, optionName)
- if (res == null) {
- logger.error("value not found in config")
- exitProcess(2)
- }
- println(res)
- } else {
- val res = config.lookupString(sectionName, optionName)
- if (res == null) {
- logger.error("value not found in config")
- exitProcess(2)
- }
- println(res)
- }
- }
-}
-
-class BankConfigCmd : CliktCommand("Dump the configuration", name = "config") {
- init {
- subcommands(BankConfigDump(), BankConfigPathsub(), BankConfigGet())
- }
-
- override fun run() = Unit
-}
-
class LibeufinBankCommand : CliktCommand() {
init {
versionOption(getVersion())
- subcommands(ServeBank(), BankDbInit(), ChangePw(), BankConfigCmd())
+ subcommands(ServeBank(), BankDbInit(), ChangePw(),
CliConfigCmd(BANK_CONFIG_SOURCE))
}
override fun run() = Unit
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/NotificationWatcher.kt
b/bank/src/main/kotlin/tech/libeufin/bank/db/NotificationWatcher.kt
index 7dddf272..8fee5341 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/db/NotificationWatcher.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/db/NotificationWatcher.kt
@@ -24,8 +24,12 @@ import java.util.concurrent.ConcurrentHashMap
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import org.postgresql.ds.PGSimpleDataSource
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
import tech.libeufin.util.*
+private val logger: Logger =
LoggerFactory.getLogger("tech.libeufin.util.NotificationWatcher")
+
/** Postgres notification collector and distributor */
internal class NotificationWatcher(private val pgSource: PGSimpleDataSource) {
// ShareFlow that are manually counted for manual garbage collection
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/TransactionDAO.kt
b/bank/src/main/kotlin/tech/libeufin/bank/db/TransactionDAO.kt
index 024c37bd..38ab588b 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/db/TransactionDAO.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/db/TransactionDAO.kt
@@ -19,10 +19,14 @@
package tech.libeufin.bank
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
import tech.libeufin.util.*
import java.time.*
import java.sql.Types
+private val logger: Logger =
LoggerFactory.getLogger("tech.libeufin.util.TransactionDAO")
+
/** Data access logic for transactions */
class TransactionDAO(private val db: Database) {
/** Result status of bank transaction creation .*/
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
index 63a79102..482d6c31 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
@@ -378,7 +378,7 @@ fun TalerConfig.extractDbConfigOrFail(): DatabaseConfig =
class LibeufinNexusCommand : CliktCommand() {
init {
versionOption(getVersion())
- subcommands(EbicsSetup(), DbInit(), EbicsSubmit(), EbicsFetch())
+ subcommands(EbicsSetup(), DbInit(), EbicsSubmit(), EbicsFetch(),
CliConfigCmd(NEXUS_CONFIG_SOURCE))
}
override fun run() = Unit
}
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics2.kt
b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics2.kt
index 496ee8cd..e8705f85 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics2.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics2.kt
@@ -25,6 +25,8 @@ package tech.libeufin.nexus.ebics
import io.ktor.client.*
import org.bouncycastle.util.encoders.UTF8
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
import tech.libeufin.nexus.BankPublicKeysFile
import tech.libeufin.nexus.ClientPrivateKeysFile
import tech.libeufin.nexus.EbicsSetupConfig
@@ -37,6 +39,8 @@ import java.time.ZoneId
import java.util.*
import javax.xml.datatype.DatatypeFactory
+private val logger: Logger =
LoggerFactory.getLogger("tech.libeufin.nexus.Ebics2")
+
/**
* Convenience function to download via EBICS with a
* customer message type.
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt
b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt
index f539977c..cb0e6a63 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt
@@ -44,18 +44,20 @@ import io.ktor.client.statement.*
import io.ktor.http.*
import org.apache.commons.compress.archivers.zip.ZipFile
import org.apache.commons.compress.utils.SeekableInMemoryByteChannel
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
import tech.libeufin.nexus.*
import tech.libeufin.util.*
import tech.libeufin.util.ebics_h005.Ebics3Request
-import tech.libeufin.util.logger
import java.io.ByteArrayOutputStream
import java.security.interfaces.RSAPrivateCrtKey
-import java.time.Instant
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.*
import java.util.zip.DeflaterInputStream
+private val logger: Logger =
LoggerFactory.getLogger("tech.libeufin.nexus.EbicsCommon")
+
/**
* Available EBICS versions.
*/
diff --git a/util/build.gradle b/util/build.gradle
index 09c7fa45..dfb5967d 100644
--- a/util/build.gradle
+++ b/util/build.gradle
@@ -35,5 +35,6 @@ dependencies {
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
implementation("org.jetbrains.kotlin:kotlin-test:$kotlin_version")
-
+
+ implementation("com.github.ajalt.clikt:clikt:4.2.1")
}
\ No newline at end of file
diff --git a/util/src/main/kotlin/Config.kt b/util/src/main/kotlin/Config.kt
index afe695ce..065dc96f 100644
--- a/util/src/main/kotlin/Config.kt
+++ b/util/src/main/kotlin/Config.kt
@@ -1,10 +1,7 @@
package tech.libeufin.util
import ch.qos.logback.core.util.Loader
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-val logger: Logger = LoggerFactory.getLogger("tech.libeufin.util")
/**
* Putting those values into the 'attributes' container because they
* are needed by the util routines that do NOT have Sandbox and Nexus
diff --git a/util/src/main/kotlin/ConfigCli.kt
b/util/src/main/kotlin/ConfigCli.kt
new file mode 100644
index 00000000..3d484f50
--- /dev/null
+++ b/util/src/main/kotlin/ConfigCli.kt
@@ -0,0 +1,119 @@
+/*
+ * This file is part of LibEuFin.
+ * Copyright (C) 2023 Taler Systems S.A.
+
+ * LibEuFin is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation; either version 3, or
+ * (at your option) any later version.
+
+ * LibEuFin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General
+ * Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public
+ * License along with LibEuFin; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>
+ */
+
+package tech.libeufin.util
+
+import ConfigSource
+import TalerConfig
+import TalerConfigError
+import com.github.ajalt.clikt.core.CliktCommand
+import com.github.ajalt.clikt.core.subcommands
+import com.github.ajalt.clikt.parameters.arguments.argument
+import com.github.ajalt.clikt.parameters.options.flag
+import com.github.ajalt.clikt.parameters.options.option
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+import kotlin.system.exitProcess
+
+private val logger: Logger =
LoggerFactory.getLogger("tech.libeufin.util.ConfigCli")
+
+private fun <R> catchError(lambda: () -> R): R {
+ try {
+ return lambda()
+ } catch (e: TalerConfigError) {
+ logger.error(e.message)
+ exitProcess(1)
+ }
+}
+
+private fun talerConfig(configSource: ConfigSource, configPath: String?):
TalerConfig = catchError {
+ val config = TalerConfig(configSource)
+ config.load(configPath)
+ config
+}
+
+class CliConfigCmd(configSource: ConfigSource) : CliktCommand("Dump the
configuration", name = "config") {
+ init {
+ subcommands(CliConfigDump(configSource),
CliConfigPathsub(configSource), CliConfigGet(configSource))
+ }
+
+ override fun run() = Unit
+}
+
+private class CliConfigGet(private val configSource: ConfigSource) :
CliktCommand("Lookup config value", name = "get") {
+ private val configFile by option(
+ "--config", "-c",
+ help = "set the configuration file"
+ )
+ private val isPath by option(
+ "--filename", "-f",
+ help = "interpret value as path with dollar-expansion"
+ ).flag()
+ private val sectionName by argument()
+ private val optionName by argument()
+
+
+ override fun run() {
+ val config = talerConfig(configSource, configFile)
+ if (isPath) {
+ val res = config.lookupPath(sectionName, optionName)
+ if (res == null) {
+ logger.error("value not found in config")
+ exitProcess(2)
+ }
+ println(res)
+ } else {
+ val res = config.lookupString(sectionName, optionName)
+ if (res == null) {
+ logger.error("value not found in config")
+ exitProcess(2)
+ }
+ println(res)
+ }
+ }
+}
+
+
+
+private class CliConfigPathsub(private val configSource: ConfigSource) :
CliktCommand("Substitute variables in a path", name = "pathsub") {
+ private val configFile by option(
+ "--config", "-c",
+ help = "set the configuration file"
+ )
+ private val pathExpr by argument()
+
+ override fun run() {
+ val config = talerConfig(configSource, configFile)
+ println(config.pathsub(pathExpr))
+ }
+}
+
+private class CliConfigDump(private val configSource: ConfigSource) :
CliktCommand("Dump the configuration", name = "dump") {
+ private val configFile by option(
+ "--config", "-c",
+ help = "set the configuration file"
+ )
+
+ override fun run() {
+ val config = talerConfig(configSource, configFile)
+ println("# install path: ${config.getInstallPath()}")
+ config.load(this.configFile)
+ println(config.stringify())
+ }
+}
diff --git a/util/src/main/kotlin/DB.kt b/util/src/main/kotlin/DB.kt
index baaeb450..02591ad2 100644
--- a/util/src/main/kotlin/DB.kt
+++ b/util/src/main/kotlin/DB.kt
@@ -36,6 +36,7 @@ import java.sql.SQLException
fun getCurrentUser(): String = System.getProperty("user.name")
+private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.util.DB")
// Check GANA (https://docs.gnunet.org/gana/index.html) for numbers allowance.
diff --git a/util/src/main/kotlin/HTTP.kt b/util/src/main/kotlin/HTTP.kt
index d310c9ba..20ec3817 100644
--- a/util/src/main/kotlin/HTTP.kt
+++ b/util/src/main/kotlin/HTTP.kt
@@ -4,6 +4,10 @@ import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.server.util.*
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+
+private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.util.HTTP")
// Get the base URL of a request, returns null if any problem occurs.
fun ApplicationRequest.getBaseUrl(): String? {
diff --git a/util/src/main/kotlin/IbanPayto.kt
b/util/src/main/kotlin/IbanPayto.kt
index 07d331c9..8920aed9 100644
--- a/util/src/main/kotlin/IbanPayto.kt
+++ b/util/src/main/kotlin/IbanPayto.kt
@@ -1,8 +1,12 @@
package tech.libeufin.util
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
import java.net.URI
import java.net.URLDecoder
+private val logger: Logger =
LoggerFactory.getLogger("tech.libeufin.util.IbanPayto")
+
// Payto information.
data class IbanPayto(
// represent query param "sender-name" or "receiver-name".
diff --git a/util/src/main/kotlin/TalerConfig.kt
b/util/src/main/kotlin/TalerConfig.kt
index d468eaa0..acbdefc7 100644
--- a/util/src/main/kotlin/TalerConfig.kt
+++ b/util/src/main/kotlin/TalerConfig.kt
@@ -17,13 +17,16 @@
* <http://www.gnu.org/licenses/>
*/
-import tech.libeufin.util.logger
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
import java.io.File
import java.nio.file.Paths
import kotlin.io.path.Path
import kotlin.io.path.isReadable
import kotlin.io.path.listDirectoryEntries
+private val logger: Logger =
LoggerFactory.getLogger("tech.libeufin.util.TalerConfig")
+
private data class Section(
val entries: MutableMap<String, String>,
)
diff --git a/util/src/main/kotlin/UnixDomainSocket.kt
b/util/src/main/kotlin/UnixDomainSocket.kt
index 5207081c..01e825d3 100644
--- a/util/src/main/kotlin/UnixDomainSocket.kt
+++ b/util/src/main/kotlin/UnixDomainSocket.kt
@@ -2,12 +2,9 @@ import io.ktor.client.plugins.*
import io.ktor.client.request.*
import io.ktor.server.application.*
import io.ktor.client.statement.*
-import io.ktor.http.*
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpMethod
-import io.ktor.server.engine.*
import io.ktor.server.testing.*
-import io.ktor.utils.io.pool.*
import io.netty.bootstrap.ServerBootstrap
import io.netty.buffer.ByteBufInputStream
import io.netty.buffer.Unpooled
@@ -20,9 +17,12 @@ import io.netty.handler.codec.http.DefaultHttpResponse
import io.netty.handler.logging.LoggingHandler
import io.netty.handler.stream.ChunkedStream
import io.netty.handler.stream.ChunkedWriteHandler
-import tech.libeufin.util.logger
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
import java.io.ByteArrayInputStream
+private val logger: Logger =
LoggerFactory.getLogger("tech.libeufin.util.UnixDomainSocket")
+
fun startServer(
unixSocketPath: String,
app: Application.() -> Unit
diff --git a/util/src/main/kotlin/XMLUtil.kt b/util/src/main/kotlin/XMLUtil.kt
index 85e1ce14..67ab77dc 100644
--- a/util/src/main/kotlin/XMLUtil.kt
+++ b/util/src/main/kotlin/XMLUtil.kt
@@ -21,6 +21,8 @@ package tech.libeufin.util
import com.sun.xml.bind.marshaller.NamespacePrefixMapper
import io.ktor.http.*
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
import org.w3c.dom.Document
import org.w3c.dom.Node
import org.w3c.dom.NodeList
@@ -60,6 +62,8 @@ import javax.xml.xpath.XPath
import javax.xml.xpath.XPathConstants
import javax.xml.xpath.XPathFactory
+private val logger: Logger =
LoggerFactory.getLogger("tech.libeufin.util.XMLUtil")
+
class DefaultNamespaces : NamespacePrefixMapper() {
override fun getPreferredPrefix(namespaceUri: String?, suggestion:
String?, requirePrefix: Boolean): String? {
if (namespaceUri == "http://www.w3.org/2000/09/xmldsig#") return "ds"
diff --git a/util/src/main/kotlin/startServer.kt
b/util/src/main/kotlin/startServer.kt
index 06b1ee03..de6923a9 100644
--- a/util/src/main/kotlin/startServer.kt
+++ b/util/src/main/kotlin/startServer.kt
@@ -4,8 +4,12 @@ import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.netty.channel.unix.Errors
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
import kotlin.system.exitProcess
+private val logger: Logger =
LoggerFactory.getLogger("tech.libeufin.util.startServer")
+
const val EAFNOSUPPORT = -97 // Netty defines errors negatively.
class StartServerOptions(
var ipv4OnlyOpt: Boolean,
diff --git a/util/src/main/kotlin/time.kt b/util/src/main/kotlin/time.kt
index b164b0d0..25f5265f 100644
--- a/util/src/main/kotlin/time.kt
+++ b/util/src/main/kotlin/time.kt
@@ -19,10 +19,14 @@
package tech.libeufin.util
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
import java.time.*
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit
+private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.util.time")
+
/**
* Converts the 'this' Instant to the number of nanoseconds
* since the Epoch. It returns the result as Long, or null
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [libeufin] branch master updated: shared config subcommand for nexus and bank,
gnunet <=