commit b048de329d6201e31cccc543f60c7f0bf1fa0162
parent 04ca190f445629ae1c6fa4c6a7df3824196e6edd
Author: Marcello Stanisci <stanisci.m@gmail.com>
Date: Thu, 19 Mar 2020 19:10:04 +0100
Move zip/unzip to util.
Diffstat:
5 files changed, 88 insertions(+), 31 deletions(-)
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
@@ -28,16 +28,4 @@ fun chunkString(input: String): String {
fun expectId(param: String?): String {
return param ?: throw NexusError(HttpStatusCode.BadRequest, "Bad ID given")
-}
-
-fun unzipOrderData(od: ByteArray): String {
- val mem = SeekableInMemoryByteChannel(od)
- val zipFile = ZipFile(mem)
- val s = java.lang.StringBuilder()
- zipFile.getEntriesInPhysicalOrder().iterator().forEach { entry ->
- s.append("<=== File ${entry.name} ===>\n")
- s.append(zipFile.getInputStream(entry).readAllBytes().toString(Charsets.UTF_8))
- s.append("\n")
- }
- return s.toString()
-}
+}
+\ No newline at end of file
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
@@ -556,6 +556,44 @@ fun main() {
return@post
}
+ /**
+ * This function triggers the Nexus to perform all those un-submitted payments.
+ * Ideally, this logic will be moved into some more automatic mechanism.
+ * NOTE: payments are not yet marked as "done" after this function returns. This
+ * should be done AFTER the PAIN.002 data corresponding to a payment witnesses it.
+ */
+ post("/ebics/admin/execute-payments-ccc") {
+ val (paymentRowId, painDoc: String, debtorAccount) = transaction {
+ val entity = Pain001Entity.find {
+ (Pain001Table.submitted eq false) and (Pain001Table.invalid eq false)
+ }.firstOrNull() ?: throw NexusError(HttpStatusCode.Accepted, reason = "No ready payments found")
+ Triple(entity.id, createPain001document(entity), entity.debtorAccount)
+ }
+ logger.debug("Uploading PAIN.001: ${painDoc}")
+ val subscriberDetails = getSubscriberDetailsFromBankAccount(debtorAccount)
+ doEbicsUploadTransaction(
+ client,
+ subscriberDetails,
+ "CCT",
+ painDoc.toByteArray(Charsets.UTF_8),
+ EbicsStandardOrderParams()
+ )
+ /* flow here == no errors occurred */
+ transaction {
+ val payment = Pain001Entity.findById(paymentRowId) ?: throw NexusError(
+ HttpStatusCode.InternalServerError,
+ "Severe internal error: could not find payment in DB after having submitted it to the bank"
+ )
+ payment.submitted = true
+ }
+ call.respondText(
+ "CCT message submitted to the bank",
+ ContentType.Text.Plain,
+ HttpStatusCode.OK
+ )
+ return@post
+ }
+
post("/ebics/subscribers/{id}/fetch-payment-status") {
// FIXME(marcello?): Fetch pain.002 and mark transfers in it as "failed"
val id = expectId(call.parameters["id"])
@@ -609,7 +647,7 @@ fun main() {
when (response) {
is EbicsDownloadSuccessResult -> {
call.respondText(
- unzipOrderData(response.orderData),
+ response.orderData.unzip(),
ContentType.Text.Plain,
HttpStatusCode.OK
)
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
@@ -49,8 +49,6 @@ import java.util.zip.DeflaterInputStream
import java.util.zip.InflaterInputStream
import javax.sql.rowset.serial.SerialBlob
import javax.xml.datatype.DatatypeFactory
-import org.apache.commons.compress.archivers.ArchiveStreamFactory
-import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
import org.apache.commons.compress.utils.IOUtils
import org.joda.time.DateTime
import org.joda.time.Instant
@@ -271,23 +269,14 @@ private fun handleEbicsPTK(requestContext: RequestContext): ByteArray {
return "Hello I am a dummy PTK response.".toByteArray()
}
-
private fun handleEbicsC52(requestContext: RequestContext): ByteArray {
val subscriber = requestContext.subscriber
- val camt = constructCamtResponse(52, subscriber.bankCustomer.id.value, requestContext.requestObject.header)
-
- val baos = ByteArrayOutputStream()
- val asf = ArchiveStreamFactory().createArchiveOutputStream(ArchiveStreamFactory.ZIP, baos)
- val zae = ZipArchiveEntry("Singleton C5{2,3} Entry")
- asf.putArchiveEntry(zae)
-
- val bais = ByteArrayInputStream(camt.toByteArray())
- IOUtils.copy(bais, asf)
- bais.close()
- asf.closeArchiveEntry()
- asf.finish()
- baos.close()
- return baos.toByteArray()
+ val camt = constructCamtResponse(
+ 52,
+ subscriber.bankCustomer.id.value,
+ requestContext.requestObject.header
+ )
+ return camt.toByteArray().zip()
}
private suspend fun ApplicationCall.handleEbicsHia(header: EbicsUnsecuredRequest.Header, orderData: ByteArray) {
diff --git a/util/build.gradle b/util/build.gradle
@@ -40,10 +40,12 @@ dependencies {
implementation 'org.apache.santuario:xmlsec:2.1.4'
implementation group: 'org.bouncycastle', name: 'bcprov-jdk16', version: '1.45'
implementation group: 'org.xerial', name: 'sqlite-jdbc', version: '3.28.0'
+ implementation group: 'org.apache.commons', name: 'commons-compress', version: '1.20'
testImplementation group: 'junit', name: 'junit', version: '4.12'
testImplementation 'org.jetbrains.kotlin:kotlin-test-junit:1.3.50'
testImplementation 'org.jetbrains.kotlin:kotlin-test:1.3.50'
+
}
application {
diff --git a/util/src/main/kotlin/zip.kt b/util/src/main/kotlin/zip.kt
@@ -0,0 +1,38 @@
+package tech.libeufin.util
+
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import org.apache.commons.compress.archivers.ArchiveStreamFactory
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
+import org.apache.commons.compress.archivers.zip.ZipFile
+import org.apache.commons.compress.utils.IOUtils
+import org.apache.commons.compress.utils.SeekableInMemoryByteChannel
+
+
+fun ByteArray.zip(): ByteArray {
+
+ val baos = ByteArrayOutputStream()
+ val asf = ArchiveStreamFactory().createArchiveOutputStream(ArchiveStreamFactory.ZIP, baos)
+ val zae = ZipArchiveEntry("File 1")
+ asf.putArchiveEntry(zae) // link Zip archive to output stream.
+
+ val bais = ByteArrayInputStream(this)
+ IOUtils.copy(bais, asf)
+ bais.close()
+ asf.closeArchiveEntry()
+ asf.finish()
+ baos.close()
+ return baos.toByteArray()
+}
+
+fun ByteArray.unzip(): String {
+ val mem = SeekableInMemoryByteChannel(this)
+ val zipFile = ZipFile(mem)
+ val s = java.lang.StringBuilder()
+ zipFile.getEntriesInPhysicalOrder().iterator().forEach { entry ->
+ s.append("<=== File ${entry.name} ===>\n")
+ s.append(zipFile.getInputStream(entry).readAllBytes().toString(Charsets.UTF_8))
+ s.append("\n")
+ }
+ return s.toString()
+}
+\ No newline at end of file