From ce1aa62e3cc3667cc56b266958700f31063693e8 Mon Sep 17 00:00:00 2001 From: Janne Koschinski <janne@kuschku.de> Date: Mon, 28 Jan 2019 23:42:58 +0100 Subject: [PATCH] Implement UI for TLS certificate details --- .tx/config | 11 + app/src/main/AndroidManifest.xml | 5 + .../quasseldroid/dagger/ActivityModule.kt | 6 + .../ssl/custom/QuasselCertificateManager.kt | 4 +- .../ssl/custom/QuasselHostnameManager.kt | 4 +- .../quasseldroid/ui/chat/ChatActivity.kt | 14 +- .../quasseldroid/ui/chat/ToolbarFragment.kt | 2 +- .../chat/buffers/BufferViewConfigFragment.kt | 2 +- .../ui/chat/input/ChatlineFragment.kt | 2 +- .../ui/chat/messages/MessageListFragment.kt | 2 +- .../ui/chat/nicks/NickListFragment.kt | 2 +- .../ui/chat/topic/TopicFragment.kt | 2 +- .../certificate/CertificateInfoActivity.kt | 37 +++ .../certificate/CertificateInfoFragment.kt | 152 +++++++++++ .../CertificateInfoFragmentProvider.kt | 34 +++ .../ui/info/channel/ChannelInfoFragment.kt | 2 +- .../info/channellist/ChannelListFragment.kt | 2 +- .../ui/info/core/CoreInfoFragment.kt | 75 ++++-- .../ui/info/user/UserInfoFragment.kt | 2 +- .../util/helper/X509CertificateHelper.kt | 6 +- app/src/main/res/drawable/ic_domain.xml | 28 ++ app/src/main/res/drawable/ic_fingerprint.xml | 30 +++ app/src/main/res/layout-land/layout_main.xml | 4 +- .../res/layout-sw600dp-land/layout_main.xml | 4 +- .../res/layout-sw720dp-land/activity_main.xml | 4 +- app/src/main/res/layout/activity_main.xml | 4 +- ...ragment_chatline.xml => chat_chatline.xml} | 0 ...agment_chat_list.xml => chat_chatlist.xml} | 0 ...ragment_messages.xml => chat_messages.xml} | 0 ...agment_nick_list.xml => chat_nicklist.xml} | 0 ...{fragment_toolbar.xml => chat_toolbar.xml} | 0 app/src/main/res/layout/info_certificate.xml | 250 ++++++++++++++++++ ...ment_info_channel.xml => info_channel.xml} | 0 ...o_channellist.xml => info_channellist.xml} | 0 .../{fragment_info_core.xml => info_core.xml} | 54 +++- ...fragment_info_topic.xml => info_topic.xml} | 0 .../{fragment_info_user.xml => info_user.xml} | 0 app/src/main/res/layout/layout_main.xml | 4 +- app/src/main/res/layout/layout_toolbar.xml | 2 +- .../main/res/layout/widget_channel_search.xml | 4 +- .../res/layout/widget_chatmessage_error.xml | 2 +- .../res/layout/widget_chatmessage_info.xml | 2 +- .../res/layout/widget_chatmessage_notice.xml | 2 +- .../res/layout/widget_chatmessage_plain.xml | 2 +- .../res/layout/widget_chatmessage_server.xml | 2 +- app/src/main/res/layout/widget_nick.xml | 2 +- .../main/res/menu/activity_channellist.xml | 27 +- app/src/main/res/values-de/strings.xml | 1 + .../res/values-de/strings_certificates.xml | 39 +++ app/src/main/res/values-de/strings_info.xml | 2 + .../values-fr-rCA/strings_certificates.xml | 20 ++ app/src/main/res/values-it/strings.xml | 7 +- .../res/values-it/strings_certificates.xml | 20 ++ .../res/values-lt/strings_certificates.xml | 20 ++ .../res/values-pt/strings_certificates.xml | 20 ++ .../res/values-sr/strings_certificates.xml | 20 ++ .../main/res/values-v17/styles_widgets.xml | 29 ++ app/src/main/res/values/strings.xml | 1 + .../main/res/values/strings_certificates.xml | 39 +++ app/src/main/res/values/strings_info.xml | 2 + app/src/main/res/values/styles_widgets.xml | 31 +++ .../libquassel/ssl/X500PrincipalHelper.kt | 40 +++ .../de/kuschku/libquassel/ssl/X509Helper.kt | 21 +- .../viewmodel/QuasselViewModel.kt | 6 + 64 files changed, 1019 insertions(+), 92 deletions(-) create mode 100644 app/src/main/java/de/kuschku/quasseldroid/ui/info/certificate/CertificateInfoActivity.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid/ui/info/certificate/CertificateInfoFragment.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid/ui/info/certificate/CertificateInfoFragmentProvider.kt create mode 100644 app/src/main/res/drawable/ic_domain.xml create mode 100644 app/src/main/res/drawable/ic_fingerprint.xml rename app/src/main/res/layout/{fragment_chatline.xml => chat_chatline.xml} (100%) rename app/src/main/res/layout/{fragment_chat_list.xml => chat_chatlist.xml} (100%) rename app/src/main/res/layout/{fragment_messages.xml => chat_messages.xml} (100%) rename app/src/main/res/layout/{fragment_nick_list.xml => chat_nicklist.xml} (100%) rename app/src/main/res/layout/{fragment_toolbar.xml => chat_toolbar.xml} (100%) create mode 100644 app/src/main/res/layout/info_certificate.xml rename app/src/main/res/layout/{fragment_info_channel.xml => info_channel.xml} (100%) rename app/src/main/res/layout/{fragment_info_channellist.xml => info_channellist.xml} (100%) rename app/src/main/res/layout/{fragment_info_core.xml => info_core.xml} (70%) rename app/src/main/res/layout/{fragment_info_topic.xml => info_topic.xml} (100%) rename app/src/main/res/layout/{fragment_info_user.xml => info_user.xml} (100%) create mode 100644 app/src/main/res/values-de/strings_certificates.xml create mode 100644 app/src/main/res/values-fr-rCA/strings_certificates.xml create mode 100644 app/src/main/res/values-it/strings_certificates.xml create mode 100644 app/src/main/res/values-lt/strings_certificates.xml create mode 100644 app/src/main/res/values-pt/strings_certificates.xml create mode 100644 app/src/main/res/values-sr/strings_certificates.xml create mode 100644 app/src/main/res/values/strings_certificates.xml create mode 100644 lib/src/main/java/de/kuschku/libquassel/ssl/X500PrincipalHelper.kt diff --git a/.tx/config b/.tx/config index 3252fe358..07b89e733 100644 --- a/.tx/config +++ b/.tx/config @@ -1,6 +1,17 @@ [main] host = https://www.transifex.com +[quasseldroid-1.strings_certificatesxml] +type = ANDROID +source_file = app/src/main/res/values/strings_certificates.xml +source_lang = en +trans.de = app/src/main/res/values-de/strings_certificates.xml +trans.fr_CA = app/src/main/res/values-fr-rCA/strings_certificates.xml +trans.it = app/src/main/res/values-it/strings_certificates.xml +trans.lt = app/src/main/res/values-lt/strings_certificates.xml +trans.pt = app/src/main/res/values-pt/strings_certificates.xml +trans.sr = app/src/main/res/values-sr/strings_certificates.xml + [quasseldroid-1.strings_statusxml] type = ANDROID source_file = app/src/main/res/values/strings_status.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 33dc97251..bfad59094 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -108,6 +108,11 @@ android:exported="false" android:label="@string/label_info_channellist" android:windowSoftInputMode="adjustResize" /> + <activity + android:name="de.kuschku.quasseldroid.ui.info.certificate.CertificateInfoActivity" + android:exported="false" + android:label="@string/label_info_certificate" + android:windowSoftInputMode="adjustResize" /> <!-- Core Settings --> <activity diff --git a/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt b/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt index fef17c67f..eb5e6e12a 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt @@ -69,6 +69,8 @@ import de.kuschku.quasseldroid.ui.coresettings.networkserver.NetworkServerActivi import de.kuschku.quasseldroid.ui.coresettings.networkserver.NetworkServerFragmentProvider import de.kuschku.quasseldroid.ui.coresettings.passwordchange.PasswordChangeActivity import de.kuschku.quasseldroid.ui.coresettings.passwordchange.PasswordChangeFragmentProvider +import de.kuschku.quasseldroid.ui.info.certificate.CertificateInfoActivity +import de.kuschku.quasseldroid.ui.info.certificate.CertificateInfoFragmentProvider import de.kuschku.quasseldroid.ui.info.channel.ChannelInfoActivity import de.kuschku.quasseldroid.ui.info.channel.ChannelInfoFragmentProvider import de.kuschku.quasseldroid.ui.info.channellist.ChannelListActivity @@ -117,6 +119,10 @@ abstract class ActivityModule { @ContributesAndroidInjector(modules = [ChannelListFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) abstract fun bindChannelListActivity(): ChannelListActivity + @ActivityScope + @ContributesAndroidInjector(modules = [CertificateInfoFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) + abstract fun bindCertificateInfoActivity(): CertificateInfoActivity + // Client Settings @ActivityScope diff --git a/app/src/main/java/de/kuschku/quasseldroid/ssl/custom/QuasselCertificateManager.kt b/app/src/main/java/de/kuschku/quasseldroid/ssl/custom/QuasselCertificateManager.kt index 771097cc3..da115f23f 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ssl/custom/QuasselCertificateManager.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ssl/custom/QuasselCertificateManager.kt @@ -20,8 +20,8 @@ package de.kuschku.quasseldroid.ssl.custom import de.kuschku.quasseldroid.persistence.QuasselDatabase -import de.kuschku.quasseldroid.util.helper.fingerprint import de.kuschku.quasseldroid.util.helper.isValid +import de.kuschku.quasseldroid.util.helper.sha1Fingerprint import java.security.cert.X509Certificate class QuasselCertificateManager( @@ -36,7 +36,7 @@ class QuasselCertificateManager( private fun isServerTrusted(leafCertificate: X509Certificate): Boolean { // Verify if a whitelist entry exists - return validityWhitelist.find(leafCertificate.fingerprint)?.let { + return validityWhitelist.find(leafCertificate.sha1Fingerprint)?.let { it.ignoreDate || leafCertificate.isValid } ?: false } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ssl/custom/QuasselHostnameManager.kt b/app/src/main/java/de/kuschku/quasseldroid/ssl/custom/QuasselHostnameManager.kt index ecc364b37..7472b5cdf 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ssl/custom/QuasselHostnameManager.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ssl/custom/QuasselHostnameManager.kt @@ -21,7 +21,7 @@ package de.kuschku.quasseldroid.ssl.custom import de.kuschku.libquassel.connection.SocketAddress import de.kuschku.quasseldroid.persistence.QuasselDatabase -import de.kuschku.quasseldroid.util.helper.fingerprint +import de.kuschku.quasseldroid.util.helper.sha1Fingerprint import java.security.cert.X509Certificate class QuasselHostnameManager( @@ -29,7 +29,7 @@ class QuasselHostnameManager( ) { fun isValid(address: SocketAddress, chain: Array<out X509Certificate>): Boolean { val leafCertificate = chain.firstOrNull() ?: return false - val whitelistEntry = hostnameWhitelist.find(leafCertificate.fingerprint, address.host) + val whitelistEntry = hostnameWhitelist.find(leafCertificate.sha1Fingerprint, address.host) return whitelistEntry != null } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt index 660e893e0..3f55b80e6 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt @@ -453,7 +453,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc Html.fromHtml( getString( R.string.label_error_certificate_invalid, - leafCertificate.fingerprint, + leafCertificate.sha1Fingerprint, dateTimeFormatter.format(Instant.ofEpochMilli(leafCertificate.notBefore.time) .atZone(ZoneId.systemDefault())), dateTimeFormatter.format(Instant.ofEpochMilli(leafCertificate.notAfter.time) @@ -470,7 +470,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc runInBackground { database.validityWhitelist().save( QuasselDatabase.SslValidityWhitelistEntry( - fingerprint = leafCertificate.fingerprint, + fingerprint = leafCertificate.sha1Fingerprint, ignoreDate = true ) ) @@ -494,7 +494,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc Html.fromHtml( getString( R.string.label_error_certificate_untrusted, - leafCertificate.fingerprint + leafCertificate.sha1Fingerprint ) ) ) @@ -507,14 +507,14 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc runInBackground { database.validityWhitelist().save( QuasselDatabase.SslValidityWhitelistEntry( - fingerprint = leafCertificate.fingerprint, + fingerprint = leafCertificate.sha1Fingerprint, ignoreDate = !leafCertificate.isValid ) ) accountDatabase.accounts().findById(accountId)?.let { database.hostnameWhitelist().save( QuasselDatabase.SslHostnameWhitelistEntry( - fingerprint = leafCertificate.fingerprint, + fingerprint = leafCertificate.sha1Fingerprint, hostname = it.host ) ) @@ -539,7 +539,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc Html.fromHtml( getString( R.string.label_error_certificate_no_match, - leafCertificate.fingerprint, + leafCertificate.sha1Fingerprint, it.address.host ) ) @@ -553,7 +553,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc runInBackground { database.hostnameWhitelist().save( QuasselDatabase.SslHostnameWhitelistEntry( - fingerprint = leafCertificate.fingerprint, + fingerprint = leafCertificate.sha1Fingerprint, hostname = it.address.host ) ) diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ToolbarFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ToolbarFragment.kt index b12d68196..5ba0e602c 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ToolbarFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ToolbarFragment.kt @@ -89,7 +89,7 @@ class ToolbarFragment : ServiceBoundFragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View? { - val view = inflater.inflate(R.layout.fragment_toolbar, container, false) + val view = inflater.inflate(R.layout.chat_toolbar, container, false) ButterKnife.bind(this, view) fun colorizeDescription(description: String?) = ircFormatDeserializer.formatString( diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt index b16056bd5..60cfaeacd 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt @@ -244,7 +244,7 @@ class BufferViewConfigFragment : ServiceBoundFragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - val view = inflater.inflate(R.layout.fragment_chat_list, container, false) + val view = inflater.inflate(R.layout.chat_chatlist, container, false) ButterKnife.bind(this, view) val adapter = BufferViewConfigAdapter() diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/ChatlineFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/ChatlineFragment.kt index 075515323..9365bcaa5 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/ChatlineFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/ChatlineFragment.kt @@ -110,7 +110,7 @@ class ChatlineFragment : ServiceBoundFragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val view = inflater.inflate(R.layout.fragment_chatline, container, false) + val view = inflater.inflate(R.layout.chat_chatline, container, false) ButterKnife.bind(this, view) editorViewModel.quasselViewModel.onNext(viewModel) diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt index f273ecabc..0dbfd3d77 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt @@ -236,7 +236,7 @@ class MessageListFragment : ServiceBoundFragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - val view = inflater.inflate(R.layout.fragment_messages, container, false) + val view = inflater.inflate(R.layout.chat_messages, container, false) ButterKnife.bind(this, view) linearLayoutManager = LinearLayoutManager(context) diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListFragment.kt index 2fbe1b9ca..082863605 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListFragment.kt @@ -73,7 +73,7 @@ class NickListFragment : ServiceBoundFragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val view = inflater.inflate(R.layout.fragment_nick_list, container, false) + val view = inflater.inflate(R.layout.chat_nicklist, container, false) ButterKnife.bind(this, view) val nickListAdapter = NickListAdapter(messageSettings, clickListener) diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt index 3db97c344..c3cda191e 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt @@ -79,7 +79,7 @@ class TopicFragment : ServiceBoundSettingsFragment(), Savable { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val view = inflater.inflate(R.layout.fragment_info_topic, container, false) + val view = inflater.inflate(R.layout.info_topic, container, false) ButterKnife.bind(this, view) editorViewModel.quasselViewModel.onNext(viewModel) diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/certificate/CertificateInfoActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/certificate/CertificateInfoActivity.kt new file mode 100644 index 000000000..52061fbe3 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/certificate/CertificateInfoActivity.kt @@ -0,0 +1,37 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid.ui.info.certificate + +import android.content.Context +import android.content.Intent +import de.kuschku.quasseldroid.util.ui.settings.ServiceBoundSettingsActivity + +class CertificateInfoActivity : ServiceBoundSettingsActivity(CertificateInfoFragment()) { + companion object { + fun launch( + context: Context + ) = context.startActivity(intent(context)) + + fun intent( + context: Context + ) = Intent(context, CertificateInfoActivity::class.java).apply { + } + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/certificate/CertificateInfoFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/certificate/CertificateInfoFragment.kt new file mode 100644 index 000000000..02179e48b --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/certificate/CertificateInfoFragment.kt @@ -0,0 +1,152 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid.ui.info.certificate + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.lifecycle.Observer +import butterknife.BindView +import butterknife.ButterKnife +import de.kuschku.libquassel.ssl.X509Helper +import de.kuschku.libquassel.ssl.commonName +import de.kuschku.libquassel.ssl.organization +import de.kuschku.libquassel.ssl.organizationalUnit +import de.kuschku.quasseldroid.R +import de.kuschku.quasseldroid.util.helper.sha1Fingerprint +import de.kuschku.quasseldroid.util.helper.sha256Fingerprint +import de.kuschku.quasseldroid.util.helper.toLiveData +import de.kuschku.quasseldroid.util.helper.visibleIf +import de.kuschku.quasseldroid.util.ui.settings.fragment.ServiceBoundSettingsFragment +import org.threeten.bp.Instant +import org.threeten.bp.ZoneId +import org.threeten.bp.format.DateTimeFormatter +import org.threeten.bp.format.FormatStyle + +class CertificateInfoFragment : ServiceBoundSettingsFragment() { + + @BindView(R.id.content) + lateinit var content: View + @BindView(R.id.error) + lateinit var error: View + + @BindView(R.id.subject_common_name_wrapper) + lateinit var subjectCommonNameWrapper: View + @BindView(R.id.subject_common_name) + lateinit var subjectCommonName: TextView + + @BindView(R.id.subject_hostnames_wrapper) + lateinit var subjectHostnamesWrapper: View + @BindView(R.id.subject_hostnames) + lateinit var subjectHostnames: TextView + + @BindView(R.id.subject_organization_wrapper) + lateinit var subjectOrganizationWrapper: View + @BindView(R.id.subject_organization) + lateinit var subjectOrganization: TextView + + @BindView(R.id.subject_organizational_unit_wrapper) + lateinit var subjectOrganizationalUnitWrapper: View + @BindView(R.id.subject_organizational_unit) + lateinit var subjectOrganizationalUnit: TextView + + @BindView(R.id.issuer_common_name_wrapper) + lateinit var issuerCommonNameWrapper: View + @BindView(R.id.issuer_common_name) + lateinit var issuerCommonName: TextView + + @BindView(R.id.issuer_organization_wrapper) + lateinit var issuerOrganizationWrapper: View + @BindView(R.id.issuer_organization) + lateinit var issuerOrganization: TextView + + @BindView(R.id.issuer_organizational_unit_wrapper) + lateinit var issuerOrganizationalUnitWrapper: View + @BindView(R.id.issuer_organizational_unit) + lateinit var issuerOrganizationalUnit: TextView + + @BindView(R.id.not_before) + lateinit var notBefore: TextView + @BindView(R.id.not_after) + lateinit var notAfter: TextView + + @BindView(R.id.fingerprint_sha256) + lateinit var fingerprintSha256: TextView + + @BindView(R.id.fingerprint_sha1) + lateinit var fingerprintSha1: TextView + + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + val view = inflater.inflate(R.layout.info_certificate, container, false) + ButterKnife.bind(this, view) + + val dateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM) + + viewModel.peerCertificateChain.toLiveData().observe(this, Observer { + val leafCertificate = it.firstOrNull() + if (leafCertificate != null) { + content.visibility = View.VISIBLE + error.visibility = View.GONE + + subjectCommonName.text = leafCertificate.subjectX500Principal.commonName + subjectCommonNameWrapper.visibleIf(!subjectCommonName.text.isNullOrBlank()) + + subjectHostnames.text = X509Helper.hostnames(leafCertificate).joinToString(", ") + subjectHostnamesWrapper.visibleIf(!subjectHostnames.text.isNullOrBlank()) + + subjectOrganization.text = leafCertificate.subjectX500Principal.organization + subjectOrganizationWrapper.visibleIf(!subjectOrganization.text.isNullOrBlank()) + + subjectOrganizationalUnit.text = leafCertificate.subjectX500Principal.organizationalUnit + subjectOrganizationalUnitWrapper.visibleIf(!subjectOrganizationalUnit.text.isNullOrBlank()) + + issuerCommonName.text = leafCertificate.issuerX500Principal.commonName + issuerCommonNameWrapper.visibleIf(!issuerCommonName.text.isNullOrBlank()) + + issuerOrganization.text = leafCertificate.issuerX500Principal.organization + issuerOrganizationWrapper.visibleIf(!issuerOrganization.text.isNullOrBlank()) + + issuerOrganizationalUnit.text = leafCertificate.issuerX500Principal.organizationalUnit + issuerOrganizationalUnitWrapper.visibleIf(!issuerOrganizationalUnit.text.isNullOrBlank()) + + notBefore.text = dateFormatter.format( + Instant.ofEpochMilli(leafCertificate.notBefore.time) + .atZone(ZoneId.systemDefault()) + ) + notAfter.text = dateFormatter.format( + Instant.ofEpochMilli(leafCertificate.notAfter.time) + .atZone(ZoneId.systemDefault()) + ) + + fingerprintSha256.text = leafCertificate.sha256Fingerprint + fingerprintSha1.text = leafCertificate.sha1Fingerprint + } else { + content.visibility = View.GONE + error.visibility = View.VISIBLE + } + }) + + return view + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/certificate/CertificateInfoFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/certificate/CertificateInfoFragmentProvider.kt new file mode 100644 index 000000000..cc2fe676e --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/certificate/CertificateInfoFragmentProvider.kt @@ -0,0 +1,34 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid.ui.info.certificate + +import androidx.fragment.app.FragmentActivity +import dagger.Binds +import dagger.Module +import dagger.android.ContributesAndroidInjector + +@Module +abstract class CertificateInfoFragmentProvider { + @Binds + abstract fun bindFragmentActivity(activity: CertificateInfoActivity): FragmentActivity + + @ContributesAndroidInjector + abstract fun bindCertificateInfoFragment(): CertificateInfoFragment +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoFragment.kt index 85c2f7d9f..3ab0bca26 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoFragment.kt @@ -75,7 +75,7 @@ class ChannelInfoFragment : ServiceBoundFragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val view = inflater.inflate(R.layout.fragment_info_channel, container, false) + val view = inflater.inflate(R.layout.info_channel, container, false) ButterKnife.bind(this, view) val openBuffer = arguments?.getBoolean("openBuffer") diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListFragment.kt index b90a1cd49..8ce88f5ff 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListFragment.kt @@ -106,7 +106,7 @@ class ChannelListFragment : ServiceBoundSettingsFragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val view = inflater.inflate(R.layout.fragment_info_channellist, container, false) + val view = inflater.inflate(R.layout.info_channellist, container, false) ButterKnife.bind(this, view) val networkId = arguments?.getInt("network_id", -1) ?: -1 diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/core/CoreInfoFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/core/CoreInfoFragment.kt index 79f65707e..b9d1d6c24 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/core/CoreInfoFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/core/CoreInfoFragment.kt @@ -34,8 +34,10 @@ import butterknife.BindView import butterknife.ButterKnife import de.kuschku.libquassel.quassel.QuasselFeatures import de.kuschku.libquassel.ssl.X509Helper +import de.kuschku.libquassel.ssl.commonName import de.kuschku.libquassel.util.helpers.value import de.kuschku.quasseldroid.R +import de.kuschku.quasseldroid.ui.info.certificate.CertificateInfoActivity import de.kuschku.quasseldroid.util.helper.* import de.kuschku.quasseldroid.util.missingfeatures.MissingFeature import de.kuschku.quasseldroid.util.missingfeatures.MissingFeaturesDialog @@ -65,11 +67,20 @@ class CoreInfoFragment : ServiceBoundFragment() { @BindView(R.id.uptime) lateinit var uptime: TextView - @BindView(R.id.secure) - lateinit var secureText: TextView + @BindView(R.id.secure_certificate) + lateinit var secureCertificate: TextView - @BindView(R.id.secure_icon) - lateinit var secureIcon: ImageView + @BindView(R.id.secure_certificate_icon) + lateinit var secureCertificateIcon: ImageView + + @BindView(R.id.secure_connection_protocol) + lateinit var secureConnectionProtocol: TextView + + @BindView(R.id.secure_connection_ciphersuite) + lateinit var secureConnectionCiphersuite: TextView + + @BindView(R.id.secure_details) + lateinit var secureDetails: Button @BindView(R.id.clients_title) lateinit var clientsTitle: View @@ -82,13 +93,15 @@ class CoreInfoFragment : ServiceBoundFragment() { private val movementMethod = BetterLinkMovementMethod.newInstance() + private val cipherSuiteRegex = Regex("TLS_(.*)_WITH_(.*)") + init { movementMethod.setOnLinkLongClickListener(LinkLongClickMenuHelper()) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val view = inflater.inflate(R.layout.fragment_info_core, container, false) + val view = inflater.inflate(R.layout.info_core, container, false) ButterKnife.bind(this, view) var missingFeatureList: List<MissingFeature> = emptyList() @@ -137,20 +150,48 @@ class CoreInfoFragment : ServiceBoundFragment() { insecure?.tint(getColor(2, 0)) } + secureDetails.setOnClickListener { + CertificateInfoActivity.launch(it.context) + } + viewModel.sslSession.toLiveData().observe(this, Observer { - val sslSession = it?.orNull() - val leafCertificate = sslSession?.peerCertificateChain?.firstOrNull() - val issuerName = leafCertificate?.issuerDN?.name?.let(X509Helper::commonName) - ?: requireContext().getString(R.string.label_core_connection_verified_by_unknown) - - if (sslSession == null) { - secureText.text = requireContext().getString(R.string.label_core_connection_insecure) - secureIcon.setImageDrawable(insecure) - } else { - secureText.text = requireContext().getString( - R.string.label_core_connection_verified_by, issuerName + val certificateChain = it?.orNull()?.peerCertificateChain?.map(X509Helper::convert).orEmpty() + val leafCertificate = certificateChain.firstOrNull() + if (leafCertificate != null) { + secureCertificate.text = requireContext().getString( + R.string.label_core_connection_verified_by, + leafCertificate.issuerX500Principal.commonName ) - secureIcon.setImageDrawable(secure) + if (leafCertificate.isValid) { + secureCertificateIcon.setImageDrawable(secure) + } else { + secureCertificateIcon.setImageDrawable(partiallySecure) + } + secureDetails.visibility = View.VISIBLE + } else { + secureCertificate.text = context?.getString(R.string.label_core_connection_insecure) + secureCertificateIcon.setImageDrawable(insecure) + secureDetails.visibility = View.GONE + } + + val (keyExchangeMechanism, cipherSuite) = it.orNull()?.cipherSuite?.let { cipherSuite -> + cipherSuiteRegex.matchEntire(cipherSuite)?.destructured + }?.let { (keyExchangeMechanism, cipherSuite) -> + Pair(keyExchangeMechanism, cipherSuite) + } ?: Pair(null, null) + + val protocol = it.orNull()?.protocol + if (cipherSuite != null && keyExchangeMechanism != null && protocol != null) { + secureConnectionProtocol.text = context?.getString(R.string.label_core_connection_protocol, + protocol) + secureConnectionCiphersuite.text = context?.getString(R.string.label_core_connection_ciphersuite, + cipherSuite, + keyExchangeMechanism) + secureConnectionProtocol.visibility = View.VISIBLE + secureConnectionCiphersuite.visibility = View.VISIBLE + } else { + secureConnectionProtocol.visibility = View.GONE + secureConnectionCiphersuite.visibility = View.GONE } }) diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoFragment.kt index 5f5069bb1..a1def4e56 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoFragment.kt @@ -125,7 +125,7 @@ class UserInfoFragment : ServiceBoundFragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val view = inflater.inflate(R.layout.fragment_info_user, container, false) + val view = inflater.inflate(R.layout.info_user, container, false) ButterKnife.bind(this, view) val openBuffer = arguments?.getBoolean("openBuffer") diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/helper/X509CertificateHelper.kt b/app/src/main/java/de/kuschku/quasseldroid/util/helper/X509CertificateHelper.kt index db060c070..47cc68921 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/helper/X509CertificateHelper.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/helper/X509CertificateHelper.kt @@ -34,12 +34,12 @@ val X509Certificate.isValid: Boolean false } -val X509Certificate.fingerprint: String +val X509Certificate.sha1Fingerprint: String get() = DigestUtils.sha1(encoded).joinToString(":") { (it.toInt() and 0xff).toString(16) } -val javax.security.cert.X509Certificate.fingerprint: String - get() = DigestUtils.sha1(encoded).joinToString(":") { +val X509Certificate.sha256Fingerprint: String + get() = DigestUtils.sha256(encoded).joinToString(":") { (it.toInt() and 0xff).toString(16) } diff --git a/app/src/main/res/drawable/ic_domain.xml b/app/src/main/res/drawable/ic_domain.xml new file mode 100644 index 000000000..1690b2265 --- /dev/null +++ b/app/src/main/res/drawable/ic_domain.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + Quasseldroid - Quassel client for Android + + Copyright (c) 2019 Janne Koschinski + Copyright (c) 2019 The Quassel Project + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License version 3 as published + by the Free Software Foundation. + + 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 <http://www.gnu.org/licenses/>. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="#000" + android:pathData="M18,15H16V17H18M18,11H16V13H18M20,19H12V17H14V15H12V13H14V11H12V9H20M10,7H8V5H10M10,11H8V9H10M10,15H8V13H10M10,19H8V17H10M6,7H4V5H6M6,11H4V9H6M6,15H4V13H6M6,19H4V17H6M12,7V3H2V21H22V7H12Z" /> +</vector> diff --git a/app/src/main/res/drawable/ic_fingerprint.xml b/app/src/main/res/drawable/ic_fingerprint.xml new file mode 100644 index 000000000..80f321cf3 --- /dev/null +++ b/app/src/main/res/drawable/ic_fingerprint.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + Quasseldroid - Quassel client for Android + + Copyright (c) 2019 Janne Koschinski + Copyright (c) 2019 The Quassel Project + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License version 3 as published + by the Free Software Foundation. + + 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 <http://www.gnu.org/licenses/>. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + tools:ignore="VectorPath"> + <path + android:fillColor="#000" + android:pathData="M17.81,4.47C17.73,4.47 17.65,4.45 17.58,4.41C15.66,3.42 14,3 12,3C10.03,3 8.15,3.47 6.44,4.41C6.2,4.54 5.9,4.45 5.76,4.21C5.63,3.97 5.72,3.66 5.96,3.53C7.82,2.5 9.86,2 12,2C14.14,2 16,2.47 18.04,3.5C18.29,3.65 18.38,3.95 18.25,4.19C18.16,4.37 18,4.47 17.81,4.47M3.5,9.72C3.4,9.72 3.3,9.69 3.21,9.63C3,9.47 2.93,9.16 3.09,8.93C4.08,7.53 5.34,6.43 6.84,5.66C10,4.04 14,4.03 17.15,5.65C18.65,6.42 19.91,7.5 20.9,8.9C21.06,9.12 21,9.44 20.78,9.6C20.55,9.76 20.24,9.71 20.08,9.5C19.18,8.22 18.04,7.23 16.69,6.54C13.82,5.07 10.15,5.07 7.29,6.55C5.93,7.25 4.79,8.25 3.89,9.5C3.81,9.65 3.66,9.72 3.5,9.72M9.75,21.79C9.62,21.79 9.5,21.74 9.4,21.64C8.53,20.77 8.06,20.21 7.39,19C6.7,17.77 6.34,16.27 6.34,14.66C6.34,11.69 8.88,9.27 12,9.27C15.12,9.27 17.66,11.69 17.66,14.66A0.5,0.5 0 0,1 17.16,15.16A0.5,0.5 0 0,1 16.66,14.66C16.66,12.24 14.57,10.27 12,10.27C9.43,10.27 7.34,12.24 7.34,14.66C7.34,16.1 7.66,17.43 8.27,18.5C8.91,19.66 9.35,20.15 10.12,20.93C10.31,21.13 10.31,21.44 10.12,21.64C10,21.74 9.88,21.79 9.75,21.79M16.92,19.94C15.73,19.94 14.68,19.64 13.82,19.05C12.33,18.04 11.44,16.4 11.44,14.66A0.5,0.5 0 0,1 11.94,14.16A0.5,0.5 0 0,1 12.44,14.66C12.44,16.07 13.16,17.4 14.38,18.22C15.09,18.7 15.92,18.93 16.92,18.93C17.16,18.93 17.56,18.9 17.96,18.83C18.23,18.78 18.5,18.96 18.54,19.24C18.59,19.5 18.41,19.77 18.13,19.82C17.56,19.93 17.06,19.94 16.92,19.94M14.91,22C14.87,22 14.82,22 14.78,22C13.19,21.54 12.15,20.95 11.06,19.88C9.66,18.5 8.89,16.64 8.89,14.66C8.89,13.04 10.27,11.72 11.97,11.72C13.67,11.72 15.05,13.04 15.05,14.66C15.05,15.73 16,16.6 17.13,16.6C18.28,16.6 19.21,15.73 19.21,14.66C19.21,10.89 15.96,7.83 11.96,7.83C9.12,7.83 6.5,9.41 5.35,11.86C4.96,12.67 4.76,13.62 4.76,14.66C4.76,15.44 4.83,16.67 5.43,18.27C5.53,18.53 5.4,18.82 5.14,18.91C4.88,19 4.59,18.87 4.5,18.62C4,17.31 3.77,16 3.77,14.66C3.77,13.46 4,12.37 4.45,11.42C5.78,8.63 8.73,6.82 11.96,6.82C16.5,6.82 20.21,10.33 20.21,14.65C20.21,16.27 18.83,17.59 17.13,17.59C15.43,17.59 14.05,16.27 14.05,14.65C14.05,13.58 13.12,12.71 11.97,12.71C10.82,12.71 9.89,13.58 9.89,14.65C9.89,16.36 10.55,17.96 11.76,19.16C12.71,20.1 13.62,20.62 15.03,21C15.3,21.08 15.45,21.36 15.38,21.62C15.33,21.85 15.12,22 14.91,22Z" /> +</vector> diff --git a/app/src/main/res/layout-land/layout_main.xml b/app/src/main/res/layout-land/layout_main.xml index 9105f7698..32ca88aea 100644 --- a/app/src/main/res/layout-land/layout_main.xml +++ b/app/src/main/res/layout-land/layout_main.xml @@ -42,7 +42,7 @@ android:name="de.kuschku.quasseldroid.ui.chat.messages.MessageListFragment" android:layout_width="match_parent" android:layout_height="match_parent" - tools:layout="@layout/fragment_messages" /> + tools:layout="@layout/chat_messages" /> <de.kuschku.quasseldroid.util.ui.WarningBarView android:id="@+id/connection_status" @@ -88,6 +88,6 @@ app:behavior_hideable="false" app:behavior_peekHeight="?actionBarSize" app:layout_behavior="@string/drag_intercept_bottom_sheet_behavior" - tools:layout="@layout/fragment_chatline" /> + tools:layout="@layout/chat_chatline" /> </androidx.coordinatorlayout.widget.CoordinatorLayout> diff --git a/app/src/main/res/layout-sw600dp-land/layout_main.xml b/app/src/main/res/layout-sw600dp-land/layout_main.xml index 610cf367f..544a7eba4 100644 --- a/app/src/main/res/layout-sw600dp-land/layout_main.xml +++ b/app/src/main/res/layout-sw600dp-land/layout_main.xml @@ -48,7 +48,7 @@ android:name="de.kuschku.quasseldroid.ui.chat.messages.MessageListFragment" android:layout_width="match_parent" android:layout_height="match_parent" - tools:layout="@layout/fragment_messages" /> + tools:layout="@layout/chat_messages" /> <de.kuschku.quasseldroid.util.ui.WarningBarView android:id="@+id/connection_status" @@ -94,7 +94,7 @@ app:behavior_hideable="false" app:behavior_peekHeight="?actionBarSize" app:layout_behavior="@string/drag_intercept_bottom_sheet_behavior" - tools:layout="@layout/fragment_chatline" /> + tools:layout="@layout/chat_chatline" /> </androidx.coordinatorlayout.widget.CoordinatorLayout> diff --git a/app/src/main/res/layout-sw720dp-land/activity_main.xml b/app/src/main/res/layout-sw720dp-land/activity_main.xml index 97d17ddb8..78f93190e 100644 --- a/app/src/main/res/layout-sw720dp-land/activity_main.xml +++ b/app/src/main/res/layout-sw720dp-land/activity_main.xml @@ -35,7 +35,7 @@ android:name="de.kuschku.quasseldroid.ui.chat.buffers.BufferViewConfigFragment" android:layout_width="320dp" android:layout_height="match_parent" - tools:layout="@layout/fragment_chat_list" /> + tools:layout="@layout/chat_chatlist" /> <LinearLayout android:layout_width="1dp" @@ -71,5 +71,5 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="end" - tools:layout="@layout/fragment_nick_list" /> + tools:layout="@layout/chat_nicklist" /> </androidx.drawerlayout.widget.DrawerLayout> diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index e4d161154..26d3f2772 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -33,7 +33,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="end" - tools:layout="@layout/fragment_nick_list" /> + tools:layout="@layout/chat_nicklist" /> <de.kuschku.quasseldroid.util.ui.NavigationDrawerLayout android:layout_width="match_parent" @@ -48,6 +48,6 @@ android:name="de.kuschku.quasseldroid.ui.chat.buffers.BufferViewConfigFragment" android:layout_width="match_parent" android:layout_height="match_parent" - tools:layout="@layout/fragment_chat_list" /> + tools:layout="@layout/chat_chatlist" /> </de.kuschku.quasseldroid.util.ui.NavigationDrawerLayout> </androidx.drawerlayout.widget.DrawerLayout> diff --git a/app/src/main/res/layout/fragment_chatline.xml b/app/src/main/res/layout/chat_chatline.xml similarity index 100% rename from app/src/main/res/layout/fragment_chatline.xml rename to app/src/main/res/layout/chat_chatline.xml diff --git a/app/src/main/res/layout/fragment_chat_list.xml b/app/src/main/res/layout/chat_chatlist.xml similarity index 100% rename from app/src/main/res/layout/fragment_chat_list.xml rename to app/src/main/res/layout/chat_chatlist.xml diff --git a/app/src/main/res/layout/fragment_messages.xml b/app/src/main/res/layout/chat_messages.xml similarity index 100% rename from app/src/main/res/layout/fragment_messages.xml rename to app/src/main/res/layout/chat_messages.xml diff --git a/app/src/main/res/layout/fragment_nick_list.xml b/app/src/main/res/layout/chat_nicklist.xml similarity index 100% rename from app/src/main/res/layout/fragment_nick_list.xml rename to app/src/main/res/layout/chat_nicklist.xml diff --git a/app/src/main/res/layout/fragment_toolbar.xml b/app/src/main/res/layout/chat_toolbar.xml similarity index 100% rename from app/src/main/res/layout/fragment_toolbar.xml rename to app/src/main/res/layout/chat_toolbar.xml diff --git a/app/src/main/res/layout/info_certificate.xml b/app/src/main/res/layout/info_certificate.xml new file mode 100644 index 000000000..d53fb9ab6 --- /dev/null +++ b/app/src/main/res/layout/info_certificate.xml @@ -0,0 +1,250 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + Quasseldroid - Quassel client for Android + + Copyright (c) 2019 Janne Koschinski + Copyright (c) 2019 The Quassel Project + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License version 3 as published + by the Free Software Foundation. + + 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 <http://www.gnu.org/licenses/>. + --> + + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <androidx.core.widget.NestedScrollView + android:id="@+id/content" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <LinearLayout style="@style/Widget.CoreSettings.PrimaryItemGroupHeader"> + + <androidx.appcompat.widget.AppCompatImageView + style="@style/Widget.CoreSettings.PrimaryItemIcon" + app:srcCompat="@drawable/ic_account" /> + + <TextView + style="@style/Widget.CoreSettings.PrimaryItemSwitch" + android:text="@string/certificate_subject" /> + </LinearLayout> + + <LinearLayout + android:id="@+id/subject_common_name_wrapper" + style="@style/Widget.CoreSettings.PrimaryItemGroup"> + + <TextView + style="@style/Widget.CoreSettings.SubHeader" + android:text="@string/certificate_common_name" /> + + <TextView + android:id="@+id/subject_common_name" + style="@style/Widget.CoreSettings.SelectableTextView" + tools:text="*.reddit.com" /> + </LinearLayout> + + <LinearLayout + android:id="@+id/subject_hostnames_wrapper" + style="@style/Widget.CoreSettings.PrimaryItemGroup"> + + <TextView + style="@style/Widget.CoreSettings.SubHeader" + android:text="@string/certificate_hostnames" /> + + <TextView + android:id="@+id/subject_hostnames" + style="@style/Widget.CoreSettings.SelectableTextView" + tools:text="*.reddit.com, reddit.com, *.redditmedia.com, redditmedia.com, *.redd.it, redd.it, www.redditstatic.com, i.reddituploads.com, *.thumbs.redditmedia.com, www.redditinc.com, redditinc.com" /> + </LinearLayout> + + <LinearLayout + android:id="@+id/subject_organization_wrapper" + style="@style/Widget.CoreSettings.PrimaryItemGroup"> + + <TextView + style="@style/Widget.CoreSettings.SubHeader" + android:text="@string/certificate_organization" /> + + <TextView + android:id="@+id/subject_organization" + style="@style/Widget.CoreSettings.SelectableTextView" + tools:text="Reddit Inc." /> + </LinearLayout> + + <LinearLayout + android:id="@+id/subject_organizational_unit_wrapper" + style="@style/Widget.CoreSettings.PrimaryItemGroup"> + + <TextView + style="@style/Widget.CoreSettings.SubHeader" + android:text="@string/certificate_organizational_unit" /> + + <TextView + android:id="@+id/subject_organizational_unit" + style="@style/Widget.CoreSettings.SelectableTextView" + tools:text="<Not Part Of Certificate>" /> + </LinearLayout> + + <LinearLayout style="@style/Widget.CoreSettings.PrimaryItemGroupHeader"> + + <androidx.appcompat.widget.AppCompatImageView + style="@style/Widget.CoreSettings.PrimaryItemIcon" + app:srcCompat="@drawable/ic_domain" /> + + <TextView + style="@style/Widget.CoreSettings.PrimaryItemSwitch" + android:text="@string/certificate_issuer" /> + </LinearLayout> + + <LinearLayout + android:id="@+id/issuer_common_name_wrapper" + style="@style/Widget.CoreSettings.PrimaryItemGroup"> + + <TextView + style="@style/Widget.CoreSettings.SubHeader" + android:text="@string/certificate_common_name" /> + + <TextView + android:id="@+id/issuer_common_name" + style="@style/Widget.CoreSettings.SelectableTextView" + tools:text="DigiCert SHA2 Secure Server CA" /> + </LinearLayout> + + <LinearLayout + android:id="@+id/issuer_organization_wrapper" + style="@style/Widget.CoreSettings.PrimaryItemGroup"> + + <TextView + style="@style/Widget.CoreSettings.SubHeader" + android:text="@string/certificate_organization" /> + + <TextView + android:id="@+id/issuer_organization" + style="@style/Widget.CoreSettings.SelectableTextView" + tools:text="DigiCert Inc" /> + </LinearLayout> + + <LinearLayout + android:id="@+id/issuer_organizational_unit_wrapper" + style="@style/Widget.CoreSettings.PrimaryItemGroup"> + + <TextView + style="@style/Widget.CoreSettings.SubHeader" + android:text="@string/certificate_organizational_unit" /> + + <TextView + android:id="@+id/issuer_organizational_unit" + style="@style/Widget.CoreSettings.SelectableTextView" + tools:text="<Not Part Of Certificate>" /> + </LinearLayout> + + <LinearLayout style="@style/Widget.CoreSettings.PrimaryItemGroupHeader"> + + <androidx.appcompat.widget.AppCompatImageView + style="@style/Widget.CoreSettings.PrimaryItemIcon" + app:srcCompat="@drawable/ic_clock" /> + + <TextView + style="@style/Widget.CoreSettings.PrimaryItemSwitch" + android:text="@string/certificate_validity" /> + </LinearLayout> + + <LinearLayout style="@style/Widget.CoreSettings.PrimaryItemGroup"> + + <TextView + style="@style/Widget.CoreSettings.SubHeader" + android:text="@string/certificate_not_before" /> + + <TextView + android:id="@+id/not_before" + style="@style/Widget.CoreSettings.SelectableTextView" + tools:text="17. August 2018" /> + </LinearLayout> + + <LinearLayout style="@style/Widget.CoreSettings.PrimaryItemGroup"> + + <TextView + style="@style/Widget.CoreSettings.SubHeader" + android:text="@string/certificate_not_after" /> + + <TextView + android:id="@+id/not_after" + style="@style/Widget.CoreSettings.SelectableTextView" + tools:text="2. September 2020" /> + </LinearLayout> + + <LinearLayout style="@style/Widget.CoreSettings.PrimaryItemGroupHeader"> + + <androidx.appcompat.widget.AppCompatImageView + style="@style/Widget.CoreSettings.PrimaryItemIcon" + app:srcCompat="@drawable/ic_fingerprint" /> + + <TextView + style="@style/Widget.CoreSettings.PrimaryItemSwitch" + android:text="@string/certificate_fingerprints" /> + </LinearLayout> + + <LinearLayout style="@style/Widget.CoreSettings.PrimaryItemGroup"> + + <TextView + style="@style/Widget.CoreSettings.SubHeader" + android:text="@string/certificate_fingerprint_sha256" /> + + <TextView + android:id="@+id/fingerprint_sha256" + style="@style/Widget.CoreSettings.SelectableTextView" + tools:text="77:C7:6C:11:70:33:25:EE:F0:6C:3B:E3:0F:15:C2:CB:2A:73:7A:56:F3:40:FD:76:29:1E:06:CB:0D:45:48:2C" /> + </LinearLayout> + + <LinearLayout style="@style/Widget.CoreSettings.PrimaryItemGroup"> + + <TextView + style="@style/Widget.CoreSettings.SubHeader" + android:text="@string/certificate_fingerprint_sha1" /> + + <TextView + android:id="@+id/fingerprint_sha1" + style="@style/Widget.CoreSettings.SelectableTextView" + tools:text="E3:C0:F1:CF:CB:A4:61:09:02:1A:74:06:71:83:CD:A8:59:28:B4:0D" /> + </LinearLayout> + + <Space + android:layout_width="match_parent" + android:layout_height="16dp" /> + </LinearLayout> + </androidx.core.widget.NestedScrollView> + + <TextView + android:id="@+id/error" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:gravity="center" + android:minHeight="?android:attr/listPreferredItemHeightSmall" + android:paddingStart="?listPreferredItemPaddingLeft" + android:paddingLeft="?listPreferredItemPaddingLeft" + android:paddingEnd="?listPreferredItemPaddingRight" + android:paddingRight="?listPreferredItemPaddingRight" + android:text="@string/label_error_certificate_no_certificate" + android:textColor="?colorTextSecondary" + android:textStyle="italic" + android:visibility="gone" + tools:visibility="visible" /> +</FrameLayout> diff --git a/app/src/main/res/layout/fragment_info_channel.xml b/app/src/main/res/layout/info_channel.xml similarity index 100% rename from app/src/main/res/layout/fragment_info_channel.xml rename to app/src/main/res/layout/info_channel.xml diff --git a/app/src/main/res/layout/fragment_info_channellist.xml b/app/src/main/res/layout/info_channellist.xml similarity index 100% rename from app/src/main/res/layout/fragment_info_channellist.xml rename to app/src/main/res/layout/info_channellist.xml diff --git a/app/src/main/res/layout/fragment_info_core.xml b/app/src/main/res/layout/info_core.xml similarity index 70% rename from app/src/main/res/layout/fragment_info_core.xml rename to app/src/main/res/layout/info_core.xml index 8947f80b9..3fac5c09b 100644 --- a/app/src/main/res/layout/fragment_info_core.xml +++ b/app/src/main/res/layout/info_core.xml @@ -88,22 +88,48 @@ <LinearLayout style="@style/Widget.Info.Item" - android:orientation="horizontal"> + android:orientation="vertical"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <TextView + android:id="@+id/secure_certificate" + style="@style/Widget.CoreSettings.TextView" + android:layout_width="0dip" + android:layout_weight="1" + tools:text="Connection verified by Let’s Encrypt" /> + + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/secure_certificate_icon" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_gravity="center" + android:layout_marginStart="16dp" + android:layout_marginLeft="16dp" + app:srcCompat="@drawable/ic_lock" /> + + </LinearLayout> <TextView - android:id="@+id/secure" - style="@style/Widget.Info.Item.Content" - android:layout_width="0dip" - android:layout_weight="1" - tools:text="Connection verified by Let’s Encrypt" /> - - <androidx.appcompat.widget.AppCompatImageView - android:id="@+id/secure_icon" - android:layout_width="24dp" - android:layout_height="24dp" - android:layout_marginStart="16dp" - android:layout_marginLeft="16dp" - app:srcCompat="@drawable/ic_lock" /> + android:id="@+id/secure_connection_protocol" + style="@style/Widget.CoreSettings.TextView" + tools:text="The connection uses TLS 1.2" /> + + <TextView + android:id="@+id/secure_connection_ciphersuite" + style="@style/Widget.CoreSettings.TextView" + tools:text="The connection is encrypted and authenticated using CHACHA20_POLY1305 and uses ECDHE_RSA as the key exchange mechanism" /> + + <Button + android:id="@+id/secure_details" + style="@style/Widget.Button.Borderless.Colored" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="end" + android:text="@string/label_info" /> </LinearLayout> <View diff --git a/app/src/main/res/layout/fragment_info_topic.xml b/app/src/main/res/layout/info_topic.xml similarity index 100% rename from app/src/main/res/layout/fragment_info_topic.xml rename to app/src/main/res/layout/info_topic.xml diff --git a/app/src/main/res/layout/fragment_info_user.xml b/app/src/main/res/layout/info_user.xml similarity index 100% rename from app/src/main/res/layout/fragment_info_user.xml rename to app/src/main/res/layout/info_user.xml diff --git a/app/src/main/res/layout/layout_main.xml b/app/src/main/res/layout/layout_main.xml index 2d2e3a95e..814ffe99a 100644 --- a/app/src/main/res/layout/layout_main.xml +++ b/app/src/main/res/layout/layout_main.xml @@ -48,7 +48,7 @@ android:name="de.kuschku.quasseldroid.ui.chat.messages.MessageListFragment" android:layout_width="match_parent" android:layout_height="match_parent" - tools:layout="@layout/fragment_messages" /> + tools:layout="@layout/chat_messages" /> <de.kuschku.quasseldroid.util.ui.WarningBarView android:id="@+id/connection_status" @@ -95,7 +95,7 @@ app:behavior_hideable="false" app:behavior_peekHeight="?actionBarSize" app:layout_behavior="@string/drag_intercept_bottom_sheet_behavior" - tools:layout="@layout/fragment_chatline" /> + tools:layout="@layout/chat_chatline" /> </androidx.coordinatorlayout.widget.CoordinatorLayout> diff --git a/app/src/main/res/layout/layout_toolbar.xml b/app/src/main/res/layout/layout_toolbar.xml index 4f55ccb1c..a76ad7d5a 100644 --- a/app/src/main/res/layout/layout_toolbar.xml +++ b/app/src/main/res/layout/layout_toolbar.xml @@ -41,7 +41,7 @@ android:name="de.kuschku.quasseldroid.ui.chat.ToolbarFragment" android:layout_width="fill_parent" android:layout_height="fill_parent" - tools:layout="@layout/fragment_toolbar" /> + tools:layout="@layout/chat_toolbar" /> </androidx.appcompat.widget.Toolbar> diff --git a/app/src/main/res/layout/widget_channel_search.xml b/app/src/main/res/layout/widget_channel_search.xml index 235744279..77a6c86b9 100644 --- a/app/src/main/res/layout/widget_channel_search.xml +++ b/app/src/main/res/layout/widget_channel_search.xml @@ -71,10 +71,10 @@ style="@style/Widget.RtlConformTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:layout_marginLeft="8dp" android:ellipsize="marquee" android:singleLine="true" - android:layout_marginLeft="8dp" - android:layout_marginStart="8dp" android:textColor="?attr/colorTextSecondary" android:textSize="12sp" tools:text="@sample/channels.json/data/users" diff --git a/app/src/main/res/layout/widget_chatmessage_error.xml b/app/src/main/res/layout/widget_chatmessage_error.xml index de14e2031..ab8642beb 100644 --- a/app/src/main/res/layout/widget_chatmessage_error.xml +++ b/app/src/main/res/layout/widget_chatmessage_error.xml @@ -22,7 +22,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - tools:showIn="@layout/fragment_messages"> + tools:showIn="@layout/chat_messages"> <include layout="@layout/widget_chatmessage_daychange" /> diff --git a/app/src/main/res/layout/widget_chatmessage_info.xml b/app/src/main/res/layout/widget_chatmessage_info.xml index 470f4b2a9..a0fdb5664 100644 --- a/app/src/main/res/layout/widget_chatmessage_info.xml +++ b/app/src/main/res/layout/widget_chatmessage_info.xml @@ -22,7 +22,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - tools:showIn="@layout/fragment_messages"> + tools:showIn="@layout/chat_messages"> <include layout="@layout/widget_chatmessage_daychange" /> diff --git a/app/src/main/res/layout/widget_chatmessage_notice.xml b/app/src/main/res/layout/widget_chatmessage_notice.xml index 96c93232d..18c602811 100644 --- a/app/src/main/res/layout/widget_chatmessage_notice.xml +++ b/app/src/main/res/layout/widget_chatmessage_notice.xml @@ -22,7 +22,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - tools:showIn="@layout/fragment_messages"> + tools:showIn="@layout/chat_messages"> <include layout="@layout/widget_chatmessage_daychange" /> diff --git a/app/src/main/res/layout/widget_chatmessage_plain.xml b/app/src/main/res/layout/widget_chatmessage_plain.xml index 2ee291d66..78871b4b7 100644 --- a/app/src/main/res/layout/widget_chatmessage_plain.xml +++ b/app/src/main/res/layout/widget_chatmessage_plain.xml @@ -22,7 +22,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - tools:showIn="@layout/fragment_messages"> + tools:showIn="@layout/chat_messages"> <include layout="@layout/widget_chatmessage_daychange" /> diff --git a/app/src/main/res/layout/widget_chatmessage_server.xml b/app/src/main/res/layout/widget_chatmessage_server.xml index b927b7640..2d86eabcb 100644 --- a/app/src/main/res/layout/widget_chatmessage_server.xml +++ b/app/src/main/res/layout/widget_chatmessage_server.xml @@ -22,7 +22,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - tools:showIn="@layout/fragment_messages"> + tools:showIn="@layout/chat_messages"> <include layout="@layout/widget_chatmessage_daychange" /> diff --git a/app/src/main/res/layout/widget_nick.xml b/app/src/main/res/layout/widget_nick.xml index ca9949877..403d4ce2b 100644 --- a/app/src/main/res/layout/widget_nick.xml +++ b/app/src/main/res/layout/widget_nick.xml @@ -28,7 +28,7 @@ android:paddingRight="16dp" android:paddingBottom="4dp" android:textAppearance="?android:attr/textAppearanceListItemSmall" - tools:showIn="@layout/fragment_nick_list"> + tools:showIn="@layout/chat_nicklist"> <ImageView android:id="@+id/avatar" diff --git a/app/src/main/res/menu/activity_channellist.xml b/app/src/main/res/menu/activity_channellist.xml index 315483593..0ea816b68 100644 --- a/app/src/main/res/menu/activity_channellist.xml +++ b/app/src/main/res/menu/activity_channellist.xml @@ -25,30 +25,39 @@ android:title="@string/label_sort" app:showAsAction="always"> <menu> - <item android:id="@+id/channel_name" + <item + android:id="@+id/channel_name" android:title="@string/label_channel_name"> <menu> - <item android:id="@+id/channel_name_asc" + <item + android:id="@+id/channel_name_asc" android:title="@string/label_ascending" /> - <item android:id="@+id/channel_name_desc" + <item + android:id="@+id/channel_name_desc" android:title="@string/label_descending" /> </menu> </item> - <item android:id="@+id/user_count" + <item + android:id="@+id/user_count" android:title="@string/label_user_count"> <menu> - <item android:id="@+id/user_count_asc" + <item + android:id="@+id/user_count_asc" android:title="@string/label_ascending" /> - <item android:id="@+id/user_count_desc" + <item + android:id="@+id/user_count_desc" android:title="@string/label_descending" /> </menu> </item> - <item android:id="@+id/topic" + <item + android:id="@+id/topic" android:title="@string/label_topic"> <menu> - <item android:id="@+id/topic_asc" + <item + android:id="@+id/topic_asc" android:title="@string/label_ascending" /> - <item android:id="@+id/topic_desc" + <item + android:id="@+id/topic_desc" android:title="@string/label_descending" /> </menu> </item> diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index f3af62058..31554946c 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -60,6 +60,7 @@ <string name="label_ignore">Ignorieren</string> <string name="label_ignore_long">Add/remove user to/from ignore list</string> <string name="label_info">Info</string> + <string name="label_info_certificate">Zertifikat</string> <string name="label_info_channel">Kanalinformationen</string> <string name="label_info_channellist">Kanalliste</string> <string name="label_info_core">Core-Details</string> diff --git a/app/src/main/res/values-de/strings_certificates.xml b/app/src/main/res/values-de/strings_certificates.xml new file mode 100644 index 000000000..0ebf4a36e --- /dev/null +++ b/app/src/main/res/values-de/strings_certificates.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + Quasseldroid - Quassel client for Android + + Copyright (c) 2019 Janne Koschinski + Copyright (c) 2019 The Quassel Project + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License version 3 as published + by the Free Software Foundation. + + 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 <http://www.gnu.org/licenses/>. + --> + +<resources> + <string name="certificate_subject">Ausgestellt für</string> + <string name="certificate_issuer">Ausgestellt von</string> + + <string name="certificate_common_name">Allgemeiner Name (CN)</string> + <string name="certificate_organization">Organisation (O)</string> + <string name="certificate_organizational_unit">Organisationseinheit (OU)</string> + + <string name="certificate_hostnames">Hostnamen</string> + + <string name="certificate_validity">Gültigkeitsdauer</string> + + <string name="certificate_not_before">Beginnt mit</string> + <string name="certificate_not_after">Gültig bis</string> + + <string name="certificate_fingerprints">Fingerabdrücke</string> + + <string name="certificate_fingerprint_sha256">SHA-256 Fingerabdruck</string> + <string name="certificate_fingerprint_sha1">SHA-1 Fingerabdruck</string> +</resources> diff --git a/app/src/main/res/values-de/strings_info.xml b/app/src/main/res/values-de/strings_info.xml index 03cf886c9..8defc1908 100644 --- a/app/src/main/res/values-de/strings_info.xml +++ b/app/src/main/res/values-de/strings_info.xml @@ -33,6 +33,8 @@ <string name="label_core_online_since">Online seit %1$s</string> <string name="label_core_connected_since">Verbunden seit %1$s</string> <string name="label_core_connection_verified_by">Verbindung verifiziert von %1$s</string> + <string name="label_core_connection_protocol">Die Verbindung verwendet %1$s</string> + <string name="label_core_connection_ciphersuite">Die Verbindung ist mit %1$s verschlüsselt und authentifiziert und verwendet %2$s als Mechanismus für den Schlüsselaustausch</string> <string name="label_core_connection_verified_by_unknown">Unbekannt</string> <string name="label_core_connection_insecure">Unsichere Verbindung</string> diff --git a/app/src/main/res/values-fr-rCA/strings_certificates.xml b/app/src/main/res/values-fr-rCA/strings_certificates.xml new file mode 100644 index 000000000..c985cffec --- /dev/null +++ b/app/src/main/res/values-fr-rCA/strings_certificates.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + Quasseldroid - Quassel client for Android + + Copyright (c) 2019 Janne Koschinski + Copyright (c) 2019 The Quassel Project + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License version 3 as published + by the Free Software Foundation. + + 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 <http://www.gnu.org/licenses/>. + --> + +<resources></resources> diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 70a48487b..f43527f1e 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -128,7 +128,7 @@ <string name="label_unknown_sender"><Sconosciuto></string> <string name="label_update_user_password">Aggiorna il nome utente e la password</string> <string name="label_use_default">Utilizza i valori predefiniti</string> - <string name="label_user_count">Utenti totali</string> + <string name="label_user_count">Numero di utenti</string> <string name="label_website">Sito web</string> <string name="label_whitelist">Ignora</string> <string name="label_whitelist_certificates">Certificati</string> @@ -142,6 +142,11 @@ <string name="label_whois_long">Aggiorna le informazioni dell’utente</string> <string name="label_yes">Sì</string> + <plurals name="label_user_count"> + <item quantity="one">%1$d utente</item> + <item quantity="other">%1$d utenti</item> + </plurals> + <string name="label_feature_synchronizedmarkerline">Necessario per sincronizzare l’ultima posizione nei canali</string> <string name="label_feature_saslauthentication">Necessario per SASL</string> <string name="label_feature_saslexternal">Necessario per SASL con certificati</string> diff --git a/app/src/main/res/values-it/strings_certificates.xml b/app/src/main/res/values-it/strings_certificates.xml new file mode 100644 index 000000000..c985cffec --- /dev/null +++ b/app/src/main/res/values-it/strings_certificates.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + Quasseldroid - Quassel client for Android + + Copyright (c) 2019 Janne Koschinski + Copyright (c) 2019 The Quassel Project + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License version 3 as published + by the Free Software Foundation. + + 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 <http://www.gnu.org/licenses/>. + --> + +<resources></resources> diff --git a/app/src/main/res/values-lt/strings_certificates.xml b/app/src/main/res/values-lt/strings_certificates.xml new file mode 100644 index 000000000..c985cffec --- /dev/null +++ b/app/src/main/res/values-lt/strings_certificates.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + Quasseldroid - Quassel client for Android + + Copyright (c) 2019 Janne Koschinski + Copyright (c) 2019 The Quassel Project + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License version 3 as published + by the Free Software Foundation. + + 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 <http://www.gnu.org/licenses/>. + --> + +<resources></resources> diff --git a/app/src/main/res/values-pt/strings_certificates.xml b/app/src/main/res/values-pt/strings_certificates.xml new file mode 100644 index 000000000..c985cffec --- /dev/null +++ b/app/src/main/res/values-pt/strings_certificates.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + Quasseldroid - Quassel client for Android + + Copyright (c) 2019 Janne Koschinski + Copyright (c) 2019 The Quassel Project + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License version 3 as published + by the Free Software Foundation. + + 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 <http://www.gnu.org/licenses/>. + --> + +<resources></resources> diff --git a/app/src/main/res/values-sr/strings_certificates.xml b/app/src/main/res/values-sr/strings_certificates.xml new file mode 100644 index 000000000..c985cffec --- /dev/null +++ b/app/src/main/res/values-sr/strings_certificates.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + Quasseldroid - Quassel client for Android + + Copyright (c) 2019 Janne Koschinski + Copyright (c) 2019 The Quassel Project + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License version 3 as published + by the Free Software Foundation. + + 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 <http://www.gnu.org/licenses/>. + --> + +<resources></resources> diff --git a/app/src/main/res/values-v17/styles_widgets.xml b/app/src/main/res/values-v17/styles_widgets.xml index 05758623d..e1face6db 100644 --- a/app/src/main/res/values-v17/styles_widgets.xml +++ b/app/src/main/res/values-v17/styles_widgets.xml @@ -89,6 +89,35 @@ <item name="android:textColor">?colorTextSecondary</item> </style> + <style name="Widget.CoreSettings.SubHeader" parent="Widget.RtlConformTextView"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_gravity">center_vertical</item> + <item name="android:paddingTop">10dp</item> + <item name="android:paddingBottom">10dp</item> + <item name="android:paddingRight">16dp</item> + <item name="android:paddingEnd">16dp</item> + <item name="android:gravity">center_vertical</item> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:textSize">14sp</item> + <item name="android:textColor">?colorTextPrimary</item> + <item name="android:textStyle">bold</item> + </style> + + <style name="Widget.CoreSettings.TextView" parent="Widget.RtlConformTextView"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_gravity">center_vertical</item> + <item name="android:paddingTop">10dp</item> + <item name="android:paddingBottom">10dp</item> + <item name="android:paddingRight">16dp</item> + <item name="android:paddingEnd">16dp</item> + <item name="android:gravity">center_vertical</item> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:textSize">14sp</item> + <item name="android:textColor">?colorTextSecondary</item> + </style> + <style name="Widget.Info.Header" parent=""> <item name="android:layout_width">match_parent</item> <item name="android:layout_height">wrap_content</item> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4de34c032..a25af0b5b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -60,6 +60,7 @@ <string name="label_ignore">Ignore</string> <string name="label_ignore_long">Add/remove user to/from ignore list</string> <string name="label_info">Details</string> + <string name="label_info_certificate">Certificate</string> <string name="label_info_channel">Channel Details</string> <string name="label_info_channellist">Channel List</string> <string name="label_info_core">Core Details</string> diff --git a/app/src/main/res/values/strings_certificates.xml b/app/src/main/res/values/strings_certificates.xml new file mode 100644 index 000000000..00d1c4fab --- /dev/null +++ b/app/src/main/res/values/strings_certificates.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + Quasseldroid - Quassel client for Android + + Copyright (c) 2019 Janne Koschinski + Copyright (c) 2019 The Quassel Project + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License version 3 as published + by the Free Software Foundation. + + 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 <http://www.gnu.org/licenses/>. + --> + +<resources> + <string name="certificate_subject">Issued To</string> + <string name="certificate_issuer">Issued By</string> + + <string name="certificate_common_name">Common Name (CN)</string> + <string name="certificate_organization">Organization (O)</string> + <string name="certificate_organizational_unit">Organizational Unit (OU)</string> + + <string name="certificate_hostnames">Hostnames</string> + + <string name="certificate_validity">Period of Validity</string> + + <string name="certificate_not_before">Not Before</string> + <string name="certificate_not_after">Not After</string> + + <string name="certificate_fingerprints">Fingerprints</string> + + <string name="certificate_fingerprint_sha256">SHA-256 Fingerprint</string> + <string name="certificate_fingerprint_sha1">SHA-1 Fingerprint</string> +</resources> diff --git a/app/src/main/res/values/strings_info.xml b/app/src/main/res/values/strings_info.xml index 37bd66cce..769aa5047 100644 --- a/app/src/main/res/values/strings_info.xml +++ b/app/src/main/res/values/strings_info.xml @@ -33,6 +33,8 @@ <string name="label_core_online_since">Online since %1$s</string> <string name="label_core_connected_since">Connected since %1$s</string> <string name="label_core_connection_verified_by">Connection verified by %1$s</string> + <string name="label_core_connection_protocol">The connection uses %1$s</string> + <string name="label_core_connection_ciphersuite">The connection is encrypted and authenticated using %1$s and uses %2$s as the key exchange mechanism</string> <string name="label_core_connection_verified_by_unknown">Unknown</string> <string name="label_core_connection_insecure">Insecure Connection</string> diff --git a/app/src/main/res/values/styles_widgets.xml b/app/src/main/res/values/styles_widgets.xml index 79b998d6f..27b65b4be 100644 --- a/app/src/main/res/values/styles_widgets.xml +++ b/app/src/main/res/values/styles_widgets.xml @@ -210,6 +210,37 @@ <item name="android:textSize">12sp</item> </style> + <style name="Widget.CoreSettings.SubHeader" parent="Widget.RtlConformTextView"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_gravity">center_vertical</item> + <item name="android:paddingTop">10dp</item> + <item name="android:paddingBottom">10dp</item> + <item name="android:paddingRight">16dp</item> + <item name="android:gravity">center_vertical</item> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:textSize">14sp</item> + <item name="android:textColor">?colorTextPrimary</item> + <item name="android:textStyle">bold</item> + </style> + + <style name="Widget.CoreSettings.TextView" parent="Widget.RtlConformTextView"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_gravity">center_vertical</item> + <item name="android:paddingTop">10dp</item> + <item name="android:paddingBottom">10dp</item> + <item name="android:paddingRight">16dp</item> + <item name="android:gravity">center_vertical</item> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:textSize">14sp</item> + <item name="android:textColor">?colorTextSecondary</item> + </style> + + <style name="Widget.CoreSettings.SelectableTextView" parent="Widget.CoreSettings.TextView"> + <item name="android:textIsSelectable">true</item> + </style> + <style name="Widget.Info.Header" parent=""> <item name="android:layout_width">match_parent</item> <item name="android:layout_height">wrap_content</item> diff --git a/lib/src/main/java/de/kuschku/libquassel/ssl/X500PrincipalHelper.kt b/lib/src/main/java/de/kuschku/libquassel/ssl/X500PrincipalHelper.kt new file mode 100644 index 000000000..66d50378b --- /dev/null +++ b/lib/src/main/java/de/kuschku/libquassel/ssl/X500PrincipalHelper.kt @@ -0,0 +1,40 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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 <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.ssl + +import javax.security.auth.x500.X500Principal + +val X500Principal.commonName + get() = commonName(name) + +fun commonName(distinguishedName: String) = + X509Helper.COMMON_NAME.find(distinguishedName)?.groups?.get(1)?.value + +val X500Principal.organization + get() = organization(name) + +fun organization(distinguishedName: String) = + X509Helper.ORGANIZATION.find(distinguishedName)?.groups?.get(1)?.value + +val X500Principal.organizationalUnit + get() = organizationalUnit(name) + +fun organizationalUnit(distinguishedName: String) = + X509Helper.ORGANIZATIONAL_UNIT.find(distinguishedName)?.groups?.get(1)?.value diff --git a/lib/src/main/java/de/kuschku/libquassel/ssl/X509Helper.kt b/lib/src/main/java/de/kuschku/libquassel/ssl/X509Helper.kt index a2562c2a2..a19290708 100644 --- a/lib/src/main/java/de/kuschku/libquassel/ssl/X509Helper.kt +++ b/lib/src/main/java/de/kuschku/libquassel/ssl/X509Helper.kt @@ -19,22 +19,19 @@ package de.kuschku.libquassel.ssl +import java.io.ByteArrayInputStream +import java.security.cert.CertificateFactory import java.security.cert.X509Certificate // FIXME: re-read RFC and check it's actually secure object X509Helper { + val certificateFactory = CertificateFactory.getInstance("X.509") + fun hostnames(certificate: X509Certificate): Sequence<String> = - (sequenceOf(commonName(certificate)) + subjectAlternativeNames( - certificate)) + (sequenceOf(certificate.subjectX500Principal.commonName) + subjectAlternativeNames(certificate)) .filterNotNull() .distinct() - fun commonName(certificate: X509Certificate) = - commonName(certificate.subjectX500Principal.name) - - fun commonName(distinguishedName: String) = - COMMON_NAME.find(distinguishedName)?.groups?.get(1)?.value - fun subjectAlternativeNames(certificate: X509Certificate): Sequence<String> = certificate.subjectAlternativeNames.orEmpty().asSequence().mapNotNull { val type = it[0] as? Int @@ -48,5 +45,11 @@ object X509Helper { name } - private val COMMON_NAME = Regex("""(?:^|,\s?)(?:CN=("(?:[^"]|"")+"|[^,]+))""") + fun convert(certificate: javax.security.cert.X509Certificate) = + certificateFactory.generateCertificate(ByteArrayInputStream(certificate.encoded)) as? X509Certificate + + + val COMMON_NAME = Regex("""(?:^|,\s?)(?:CN=("(?:[^"]|"")+"|[^,]+))""") + val ORGANIZATION = Regex("""(?:^|,\s?)(?:O=("(?:[^"]|"")+"|[^,]+))""") + val ORGANIZATIONAL_UNIT = Regex("""(?:^|,\s?)(?:OU=("(?:[^"]|"")+"|[^,]+))""") } diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt index 688ebb282..619f58cc8 100644 --- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt +++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt @@ -29,6 +29,7 @@ import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork import de.kuschku.libquassel.session.Backend import de.kuschku.libquassel.session.ISession import de.kuschku.libquassel.session.SessionManager +import de.kuschku.libquassel.ssl.X509Helper import de.kuschku.libquassel.util.Optional import de.kuschku.libquassel.util.flag.and import de.kuschku.libquassel.util.flag.hasFlag @@ -40,6 +41,7 @@ import io.reactivex.Observable import io.reactivex.subjects.BehaviorSubject import io.reactivex.subjects.PublishSubject import java.util.concurrent.TimeUnit +import javax.net.ssl.SSLSession class QuasselViewModel : ViewModel() { fun resetAccount() { @@ -128,6 +130,10 @@ class QuasselViewModel : ViewModel() { } val sslSession = session.flatMapSwitchMap(ISession::sslSession) + val peerCertificateChain = sslSession.mapMap(SSLSession::getPeerCertificateChain).mapMap { + it.mapNotNull(X509Helper::convert) + }.mapOrElse(emptyList()) + val leafCertificate = peerCertificateChain.map { Optional.ofNullable(it.firstOrNull()) } val coreInfo = session.mapMapNullable(ISession::coreInfo).mapSwitchMap(CoreInfo::liveInfo) val coreInfoClients = coreInfo.mapMap(CoreInfo.CoreData::sessionConnectedClientData) -- GitLab