From 911758582818d1d88f9aadf939b53c0a2244f253 Mon Sep 17 00:00:00 2001 From: althafvly Date: Fri, 10 Apr 2026 16:40:13 +0530 Subject: [PATCH 1/3] Fix protocol test failures --- .../fsck/k9/mail/store/imap/mockserver/MockImapServer.java | 7 +++---- .../java/com/fsck/k9/mail/store/pop3/Pop3Connection.java | 2 ++ .../java/com/fsck/k9/mail/store/pop3/MockPop3Server.java | 7 +++---- .../fsck/k9/mail/transport/mockServer/MockSmtpServer.java | 7 +++---- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/mockserver/MockImapServer.java b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/mockserver/MockImapServer.java index 140fa61df7..a547ed0be9 100644 --- a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/mockserver/MockImapServer.java +++ b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/mockserver/MockImapServer.java @@ -282,8 +282,6 @@ public class MockImapServer { while (!shouldStop) { readAdditionalCommands(); } - - waitForConnectionClosed.countDown(); } catch (UnexpectedCommandException e) { unexpectedCommandException = e; } catch (IOException e) { @@ -293,10 +291,11 @@ public class MockImapServer { } catch (KeyStoreException | CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyManagementException e) { throw new RuntimeException(e); + } finally { + waitForConnectionClosed.countDown(); + IOUtils.closeQuietly(socket); } - IOUtils.closeQuietly(socket); - logger.log("Exiting"); } diff --git a/mail/protocols/pop3/src/main/java/com/fsck/k9/mail/store/pop3/Pop3Connection.java b/mail/protocols/pop3/src/main/java/com/fsck/k9/mail/store/pop3/Pop3Connection.java index bbcca76308..cc30af1f55 100644 --- a/mail/protocols/pop3/src/main/java/com/fsck/k9/mail/store/pop3/Pop3Connection.java +++ b/mail/protocols/pop3/src/main/java/com/fsck/k9/mail/store/pop3/Pop3Connection.java @@ -82,12 +82,14 @@ class Pop3Connection { performAuthentication(settings.getAuthType(), serverGreeting); } catch (SSLException e) { + close(); if (e.getCause() instanceof CertificateException) { throw new CertificateValidationException(e.getMessage(), e); } else { throw new MessagingException("Unable to connect", e); } } catch (GeneralSecurityException gse) { + close(); throw new MessagingException( "Unable to open connection to POP server due to security error.", gse); } catch (IOException ioe) { diff --git a/mail/protocols/pop3/src/test/java/com/fsck/k9/mail/store/pop3/MockPop3Server.java b/mail/protocols/pop3/src/test/java/com/fsck/k9/mail/store/pop3/MockPop3Server.java index a5142829db..36b0e48ac4 100644 --- a/mail/protocols/pop3/src/test/java/com/fsck/k9/mail/store/pop3/MockPop3Server.java +++ b/mail/protocols/pop3/src/test/java/com/fsck/k9/mail/store/pop3/MockPop3Server.java @@ -282,8 +282,6 @@ public class MockPop3Server { while (!shouldStop) { readAdditionalCommands(); } - - waitForConnectionClosed.countDown(); } catch (UnexpectedCommandException e) { unexpectedCommandException = e; } catch (IOException e) { @@ -293,10 +291,11 @@ public class MockPop3Server { } catch (KeyStoreException | CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyManagementException e) { throw new RuntimeException(e); + } finally { + waitForConnectionClosed.countDown(); + IOUtils.closeQuietly(socket); } - IOUtils.closeQuietly(socket); - logger.log("Exiting"); } diff --git a/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/mockServer/MockSmtpServer.java b/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/mockServer/MockSmtpServer.java index 35ece7ab7c..b8fb14aefc 100644 --- a/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/mockServer/MockSmtpServer.java +++ b/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/mockServer/MockSmtpServer.java @@ -284,8 +284,6 @@ public class MockSmtpServer { while (!shouldStop) { readAdditionalCommands(); } - - waitForConnectionClosed.countDown(); } catch (UnexpectedCommandException e) { unexpectedCommandException = e; } catch (IOException e) { @@ -295,10 +293,11 @@ public class MockSmtpServer { } catch (KeyStoreException | CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyManagementException e) { throw new RuntimeException(e); + } finally { + waitForConnectionClosed.countDown(); + IOUtils.closeQuietly(socket); } - IOUtils.closeQuietly(socket); - logger.log("Exiting"); } -- GitLab From 9bf2c305f4813b1272a5dac27c6acc3851609528 Mon Sep 17 00:00:00 2001 From: althafvly Date: Fri, 10 Apr 2026 16:46:55 +0530 Subject: [PATCH 2/3] ci: build release APK only --- .gitlab-ci.yml | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e0a4f000e0..5ca09ac836 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,7 +2,7 @@ image: "registry.gitlab.e.foundation/e/os/docker-android-apps-cicd:master" variables: APK_PATH: "app/k9mail/build/outputs/apk/release" - UNSIGNED_APK: "Mail-unsigned.apk" + UNSIGNED_APK: "k9mail-release-unsigned.apk" COMMUNITY_APK: "Mail-community.apk" OFFICIAL_APK: "Mail-official.apk" TEST_APK: "Mail-test.apk" @@ -29,19 +29,10 @@ test: build: stage: build script: - - ./gradlew :app:k9mail:build - - cd app/k9mail/build/outputs/apk/ - - | - if [[ ! -d "release" ]]; then - echo "$APK_PATH does not exist." - exit 1 - fi - cd "release" - unsigned_build=$(ls *.apk | grep "unsigned") - cp $unsigned_build $UNSIGNED_APK + - ./gradlew :app:k9mail:assembleRelease artifacts: paths: - - app/k9mail/build/outputs/apk/ + - $APK_PATH init_submodules: stage: gitlab_release -- GitLab From 47c20b33ccd1ee4586cb09b72507d1bf5f5e2d6f Mon Sep 17 00:00:00 2001 From: althafvly Date: Fri, 10 Apr 2026 14:25:46 +0530 Subject: [PATCH 3/3] feat: enable full backup support for Seedvault --- app/k9mail/src/main/AndroidManifest.xml | 6 +- .../main/java/com/fsck/k9/MailBackupAgent.kt | 90 +++++++++++++++++++ app/k9mail/src/main/res/xml/backup_rules.xml | 24 +++++ .../main/res/xml/data_extraction_rules.xml | 33 +++++++ 4 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 app/k9mail/src/main/java/com/fsck/k9/MailBackupAgent.kt create mode 100644 app/k9mail/src/main/res/xml/backup_rules.xml create mode 100644 app/k9mail/src/main/res/xml/data_extraction_rules.xml diff --git a/app/k9mail/src/main/AndroidManifest.xml b/app/k9mail/src/main/AndroidManifest.xml index 096d894489..d534fcbcde 100644 --- a/app/k9mail/src/main/AndroidManifest.xml +++ b/app/k9mail/src/main/AndroidManifest.xml @@ -36,7 +36,11 @@ android:label="@string/app_name" android:theme="@style/Theme.K9.Startup" android:resizeableActivity="true" - android:allowBackup="false" + android:allowBackup="true" + android:backupAgent="com.fsck.k9.MailBackupAgent" + android:fullBackupContent="@xml/backup_rules" + android:dataExtractionRules="@xml/data_extraction_rules" + android:fullBackupOnly="true" android:supportsRtl="true" android:hasFragileUserData="false" tools:replace="android:theme" diff --git a/app/k9mail/src/main/java/com/fsck/k9/MailBackupAgent.kt b/app/k9mail/src/main/java/com/fsck/k9/MailBackupAgent.kt new file mode 100644 index 0000000000..4d8e9c7194 --- /dev/null +++ b/app/k9mail/src/main/java/com/fsck/k9/MailBackupAgent.kt @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2026 e Foundation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.fsck.k9 + +import android.app.backup.BackupAgent +import android.app.backup.BackupDataInput +import android.app.backup.BackupDataOutput +import android.app.backup.FullBackupDataOutput +import android.os.Build +import android.os.ParcelFileDescriptor +import androidx.annotation.RequiresApi +import java.io.File +import java.io.IOException +import timber.log.Timber + +/* + * Note: + * - We rely only on full backup. + * - Backup is allowed only for encrypted or device-to-device transports. + * - Only selected app data is backed up (databases + shared preferences). + */ +class MailBackupAgent : BackupAgent() { + override fun onBackup( + oldState: ParcelFileDescriptor?, + data: BackupDataOutput?, + newState: ParcelFileDescriptor? + ) = Unit + + override fun onRestore( + data: BackupDataInput?, + appVersionCode: Int, + newState: ParcelFileDescriptor? + ) = Unit + + @RequiresApi(Build.VERSION_CODES.P) + override fun onFullBackup(data: FullBackupDataOutput) { + val flags = data.transportFlags + val allowed = flags and FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED != 0 || + flags and FLAG_DEVICE_TO_DEVICE_TRANSFER != 0 + + if (!allowed) return + + BACKUP_DIRS.forEach { backupDir(File(dataDir, it), data) } + } + + private fun backupDir(dir: File, data: FullBackupDataOutput) { + if (!dir.isDirectory) return + + try { + dir.listFiles()?.forEach { file -> + if (file.isDirectory) { + backupDir(file, data) + } else if (shouldBackupFile(file)) { + fullBackupFile(file, data) + } + } + } catch (e: SecurityException) { + Timber.e(e, "Skipping restricted") + } catch (e: IOException) { + Timber.e(e, "I/O error during backup") + } + } + + private fun shouldBackupFile(file: File) = + EXCLUDED_SUFFIXES.none(file.name::endsWith) + + companion object { + private val EXCLUDED_SUFFIXES = listOf("-journal", "-shm", "-wal") + + private val BACKUP_DIRS = listOf( + "databases", + "shared_prefs" + ) + } +} diff --git a/app/k9mail/src/main/res/xml/backup_rules.xml b/app/k9mail/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000000..187cb6b432 --- /dev/null +++ b/app/k9mail/src/main/res/xml/backup_rules.xml @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/app/k9mail/src/main/res/xml/data_extraction_rules.xml b/app/k9mail/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000000..e04cdd1370 --- /dev/null +++ b/app/k9mail/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + -- GitLab