-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8140 from thunderbird/qr_code_parser
Add QR code payload reader
- Loading branch information
Showing
13 changed files
with
1,697 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
plugins { | ||
id(ThunderbirdPlugins.Library.android) | ||
} | ||
|
||
android { | ||
namespace = "app.k9mail.feature.migration.qrcode" | ||
resourcePrefix = "migration_qrcode_" | ||
} | ||
|
||
dependencies { | ||
implementation(projects.core.common) | ||
implementation(libs.moshi) | ||
implementation(libs.timber) | ||
} |
105 changes: 105 additions & 0 deletions
105
feature/migration/qrcode/src/main/kotlin/app/k9mail/feature/migration/qrcode/AccountData.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
package app.k9mail.feature.migration.qrcode | ||
|
||
import app.k9mail.core.common.mail.EmailAddress | ||
import app.k9mail.core.common.net.Hostname | ||
import app.k9mail.core.common.net.Port | ||
|
||
internal data class AccountData( | ||
val sequenceNumber: Int, | ||
val sequenceEnd: Int, | ||
val accounts: List<Account>, | ||
) { | ||
data class Account( | ||
val accountName: String, | ||
val incomingServer: IncomingServer, | ||
val outgoingServerGroups: List<OutgoingServerGroup>, | ||
) | ||
|
||
data class IncomingServer( | ||
val protocol: IncomingServerProtocol, | ||
val hostname: Hostname, | ||
val port: Port, | ||
val connectionSecurity: ConnectionSecurity, | ||
val authenticationType: AuthenticationType, | ||
val username: String, | ||
val password: String?, | ||
) | ||
|
||
data class OutgoingServer( | ||
val protocol: OutgoingServerProtocol, | ||
val hostname: Hostname, | ||
val port: Port, | ||
val connectionSecurity: ConnectionSecurity, | ||
val authenticationType: AuthenticationType, | ||
val username: String, | ||
val password: String?, | ||
) | ||
|
||
data class OutgoingServerGroup( | ||
val outgoingServer: OutgoingServer, | ||
val identities: List<Identity>, | ||
) | ||
|
||
data class Identity( | ||
val emailAddress: EmailAddress, | ||
val displayName: String, | ||
) | ||
|
||
@Suppress("MagicNumber") | ||
enum class IncomingServerProtocol(val intValue: Int) { | ||
Imap(0), | ||
Pop3(1), | ||
; | ||
|
||
companion object { | ||
fun fromInt(value: Int): IncomingServerProtocol { | ||
return requireNotNull(entries.find { it.intValue == value }) { "Unsupported value: $value" } | ||
} | ||
} | ||
} | ||
|
||
@Suppress("MagicNumber") | ||
enum class OutgoingServerProtocol(val intValue: Int) { | ||
Smtp(0), | ||
; | ||
|
||
companion object { | ||
fun fromInt(value: Int): OutgoingServerProtocol { | ||
return requireNotNull(entries.find { it.intValue == value }) { "Unsupported value: $value" } | ||
} | ||
} | ||
} | ||
|
||
@Suppress("MagicNumber") | ||
enum class ConnectionSecurity(val intValue: Int) { | ||
Plain(0), | ||
TryStartTls(1), | ||
AlwaysStartTls(2), | ||
Tls(3), | ||
; | ||
|
||
companion object { | ||
fun fromInt(value: Int): ConnectionSecurity { | ||
return requireNotNull(entries.find { it.intValue == value }) { "Unsupported value: $value" } | ||
} | ||
} | ||
} | ||
|
||
@Suppress("MagicNumber") | ||
enum class AuthenticationType(val intValue: Int) { | ||
None(0), | ||
PasswordCleartext(1), | ||
PasswordEncrypted(2), | ||
Gssapi(3), | ||
Ntlm(4), | ||
TlsCertificate(5), | ||
OAuth2(6), | ||
; | ||
|
||
companion object { | ||
fun fromInt(value: Int): AuthenticationType { | ||
return requireNotNull(entries.find { it.intValue == value }) { "Unsupported value: $value" } | ||
} | ||
} | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
feature/migration/qrcode/src/main/kotlin/app/k9mail/feature/migration/qrcode/QrCodeData.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package app.k9mail.feature.migration.qrcode | ||
|
||
internal data class QrCodeData( | ||
val version: Int, | ||
val misc: Misc, | ||
val accounts: List<Account>, | ||
) { | ||
data class Misc( | ||
val sequenceNumber: Int, | ||
val sequenceEnd: Int, | ||
) | ||
|
||
data class Account( | ||
val incomingServer: IncomingServer, | ||
val outgoingServers: List<OutgoingServer>, | ||
) | ||
|
||
data class IncomingServer( | ||
val protocol: Int, | ||
val hostname: String, | ||
val port: Int, | ||
val connectionSecurity: Int, | ||
val authenticationType: Int, | ||
val username: String, | ||
val accountName: String?, | ||
val password: String?, | ||
) | ||
|
||
data class OutgoingServer( | ||
val protocol: Int, | ||
val hostname: String, | ||
val port: Int, | ||
val connectionSecurity: Int, | ||
val authenticationType: Int, | ||
val username: String, | ||
val password: String?, | ||
val identities: List<Identity>, | ||
) | ||
|
||
data class Identity( | ||
val emailAddress: String, | ||
val displayName: String, | ||
) | ||
} |
153 changes: 153 additions & 0 deletions
153
...ration/qrcode/src/main/kotlin/app/k9mail/feature/migration/qrcode/QrCodePayloadAdapter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
package app.k9mail.feature.migration.qrcode | ||
|
||
import com.squareup.moshi.JsonAdapter | ||
import com.squareup.moshi.JsonReader | ||
import com.squareup.moshi.JsonWriter | ||
import timber.log.Timber | ||
|
||
internal class QrCodePayloadAdapter : JsonAdapter<QrCodeData>() { | ||
override fun fromJson(jsonReader: JsonReader): QrCodeData? { | ||
jsonReader.beginArray() | ||
|
||
val version = jsonReader.nextInt() | ||
if (version != 1) { | ||
// We don't even attempt to read something that is newer than version 1. | ||
Timber.d("Unsupported version: %s", version) | ||
return null | ||
} | ||
|
||
val misc = readMiscellaneousData(jsonReader) | ||
|
||
val accounts = buildList { | ||
do { | ||
add(readAccount(jsonReader)) | ||
} while (jsonReader.hasNext()) | ||
} | ||
|
||
jsonReader.endArray() | ||
|
||
return QrCodeData(version, misc, accounts) | ||
} | ||
|
||
private fun readMiscellaneousData(jsonReader: JsonReader): QrCodeData.Misc { | ||
jsonReader.beginArray() | ||
|
||
val sequenceNumber = jsonReader.nextInt() | ||
val sequenceEnd = jsonReader.nextInt() | ||
|
||
skipAdditionalArrayEntries(jsonReader) | ||
jsonReader.endArray() | ||
|
||
return QrCodeData.Misc( | ||
sequenceNumber, | ||
sequenceEnd, | ||
) | ||
} | ||
|
||
private fun readAccount(jsonReader: JsonReader): QrCodeData.Account { | ||
val incomingServer = readIncomingServer(jsonReader) | ||
val outgoingServers = readOutgoingServers(jsonReader) | ||
|
||
return QrCodeData.Account(incomingServer, outgoingServers) | ||
} | ||
|
||
private fun readIncomingServer(jsonReader: JsonReader): QrCodeData.IncomingServer { | ||
jsonReader.beginArray() | ||
|
||
val protocol = jsonReader.nextInt() | ||
val hostname = jsonReader.nextString() | ||
val port = jsonReader.nextInt() | ||
val connectionSecurity = jsonReader.nextInt() | ||
val authenticationType = jsonReader.nextInt() | ||
val username = jsonReader.nextString() | ||
val accountName = if (jsonReader.hasNext()) jsonReader.nextString() else null | ||
val password = if (jsonReader.hasNext()) jsonReader.nextString() else null | ||
|
||
skipAdditionalArrayEntries(jsonReader) | ||
jsonReader.endArray() | ||
|
||
return QrCodeData.IncomingServer( | ||
protocol, | ||
hostname, | ||
port, | ||
connectionSecurity, | ||
authenticationType, | ||
username, | ||
accountName, | ||
password, | ||
) | ||
} | ||
|
||
private fun readOutgoingServers(jsonReader: JsonReader): List<QrCodeData.OutgoingServer> { | ||
jsonReader.beginArray() | ||
|
||
val outgoingServers = buildList { | ||
do { | ||
add(readOutgoingServer(jsonReader)) | ||
} while (jsonReader.hasNext()) | ||
} | ||
|
||
jsonReader.endArray() | ||
|
||
return outgoingServers | ||
} | ||
|
||
private fun readOutgoingServer(jsonReader: JsonReader): QrCodeData.OutgoingServer { | ||
jsonReader.beginArray() | ||
|
||
jsonReader.beginArray() | ||
|
||
val protocol = jsonReader.nextInt() | ||
val hostname = jsonReader.nextString() | ||
val port = jsonReader.nextInt() | ||
val connectionSecurity = jsonReader.nextInt() | ||
val authenticationType = jsonReader.nextInt() | ||
val username = jsonReader.nextString() | ||
val password = if (jsonReader.hasNext()) jsonReader.nextString() else null | ||
|
||
skipAdditionalArrayEntries(jsonReader) | ||
jsonReader.endArray() | ||
|
||
val identities = buildList { | ||
do { | ||
add(readIdentity(jsonReader)) | ||
} while (jsonReader.hasNext()) | ||
} | ||
|
||
jsonReader.endArray() | ||
|
||
return QrCodeData.OutgoingServer( | ||
protocol, | ||
hostname, | ||
port, | ||
connectionSecurity, | ||
authenticationType, | ||
username, | ||
password, | ||
identities, | ||
) | ||
} | ||
|
||
private fun readIdentity(jsonReader: JsonReader): QrCodeData.Identity { | ||
jsonReader.beginArray() | ||
|
||
val emailAddress = jsonReader.nextString() | ||
val displayName = jsonReader.nextString() | ||
|
||
skipAdditionalArrayEntries(jsonReader) | ||
jsonReader.endArray() | ||
|
||
return QrCodeData.Identity(emailAddress, displayName) | ||
} | ||
|
||
private fun skipAdditionalArrayEntries(jsonReader: JsonReader) { | ||
// For forward compatibility allow additional array elements. | ||
while (jsonReader.hasNext()) { | ||
jsonReader.readJsonValue() | ||
} | ||
} | ||
|
||
override fun toJson(jsonWriter: JsonWriter, value: QrCodeData?) { | ||
throw UnsupportedOperationException("not implemented") | ||
} | ||
} |
Oops, something went wrong.