summaryrefslogtreecommitdiff
path: root/app/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java')
-rw-r--r--app/src/main/java/org/terst/feedreader/data/AppDatabase.kt40
-rw-r--r--app/src/main/java/org/terst/feedreader/ui/AddFeedScreen.kt20
2 files changed, 53 insertions, 7 deletions
diff --git a/app/src/main/java/org/terst/feedreader/data/AppDatabase.kt b/app/src/main/java/org/terst/feedreader/data/AppDatabase.kt
index 3367a71..da68d3d 100644
--- a/app/src/main/java/org/terst/feedreader/data/AppDatabase.kt
+++ b/app/src/main/java/org/terst/feedreader/data/AppDatabase.kt
@@ -9,7 +9,7 @@ import androidx.room.migration.Migration
@Database(
entities = [FeedEntity::class, ArticleEntity::class, FilterRuleEntity::class],
- version = 6,
+ version = 7,
exportSchema = false
)
abstract class AppDatabase : RoomDatabase() {
@@ -99,12 +99,48 @@ abstract class AppDatabase : RoomDatabase() {
}
}
+ // v7: this app now upgrades http:// feed URLs to https:// at add/import time (see
+ // AddFeedScreen.preferHttps), but that only ever runs on the way IN -- feeds already
+ // subscribed to before this change stay http:// forever otherwise. Android blocks
+ // cleartext by default (targetSdk 28+), so every one of these was failing every fetch
+ // ("CLEARTEXT communication ... not permitted"). Checked live (curl) which of this
+ // user's http feeds actually serve valid content over https and rewrite just those --
+ // three hosts genuinely have no usable HTTPS (no TLS listener at all, a broken TLS
+ // handshake, or a certificate that doesn't match the hostname) and are deliberately
+ // left alone, since switching them would just trade one permanent failure for another.
+ private val MIGRATION_6_7 = object : Migration(6, 7) {
+ override fun migrate(db: SupportSQLiteDatabase) {
+ val httpToHttps = mapOf(
+ "http://blog.cryptographyengineering.com/feeds/posts/default" to
+ "https://blog.cryptographyengineering.com/feeds/posts/default",
+ "http://mybiasedcoin.blogspot.com/feeds/posts/default?alt=rss" to
+ "https://mybiasedcoin.blogspot.com/feeds/posts/default?alt=rss",
+ "http://processalgebra.blogspot.com/feeds/posts/default?alt=rss" to
+ "https://processalgebra.blogspot.com/feeds/posts/default?alt=rss",
+ "http://www.rowetel.com/blog/?feed=rss2" to
+ "https://www.rowetel.com/blog/?feed=rss2",
+ "http://www.thecodelesscode.com/rss" to
+ "https://www.thecodelesscode.com/rss",
+ // This one's own https endpoint redirects through an insecure (http)
+ // feedburner hop before landing on real content -- point straight at
+ // feedburner's own https endpoint instead of a URL that would still fail
+ // cleartext-blocked partway through the redirect chain.
+ "http://www.divingforpearlsblog.com/feeds/posts/default" to
+ "https://feeds.feedburner.com/blogspot/sjomU"
+ )
+ for ((oldUrl, newUrl) in httpToHttps) {
+ db.execSQL("UPDATE articles SET feedId = ? WHERE feedId = ?", arrayOf(newUrl, oldUrl))
+ db.execSQL("UPDATE feeds SET id = ? WHERE id = ?", arrayOf(newUrl, oldUrl))
+ }
+ }
+ }
+
fun get(context: Context): AppDatabase = instance ?: synchronized(this) {
instance ?: Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"feedreader.db"
- ).addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6).build().also { instance = it }
+ ).addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6, MIGRATION_6_7).build().also { instance = it }
}
}
}
diff --git a/app/src/main/java/org/terst/feedreader/ui/AddFeedScreen.kt b/app/src/main/java/org/terst/feedreader/ui/AddFeedScreen.kt
index e531fb7..38b0f32 100644
--- a/app/src/main/java/org/terst/feedreader/ui/AddFeedScreen.kt
+++ b/app/src/main/java/org/terst/feedreader/ui/AddFeedScreen.kt
@@ -61,7 +61,7 @@ fun AddFeedScreen(onDone: () -> Unit) {
// exported from a reader with folders/labels) appears once per folder here --
// dedupe by feed URL before reporting a count, or "108 feeds" would be reported
// as some larger number even though upsert already collapses them to one row each.
- val uniqueFeeds = imported.distinctBy { it.xmlUrl }
+ val uniqueFeeds = imported.map { it.copy(xmlUrl = preferHttps(it.xmlUrl)) }.distinctBy { it.xmlUrl }
for (feed in uniqueFeeds) {
// upsert() REPLACEs the whole row -- for a feed that's already subscribed,
// building a bare FeedEntity here would silently wipe readerModeDisabled
@@ -147,20 +147,30 @@ fun AddFeedScreen(onDone: () -> Unit) {
}
}
+// Feeds get added as https:// even if the user (or an OPML export) gave an http:// URL --
+// Android blocks cleartext by default (targetSdk 28+), so an http feed would just fail every
+// fetch forever. Most sites serve identical content either way; this is a plain scheme swap.
+// If a site genuinely doesn't support https, the very next fetch attempt fails immediately and
+// shows up the normal way (the feed list's "Last fetch failed" section) rather than silently
+// adding a feed that looks fine until you notice it never updates.
+private fun preferHttps(url: String): String =
+ if (url.startsWith("http://", ignoreCase = true)) "https://" + url.removePrefix("http://") else url
+
private suspend fun addFeedByUrl(context: android.content.Context, db: AppDatabase, feedUrl: String): String =
withContext(Dispatchers.IO) {
+ val url = preferHttps(feedUrl)
try {
- val response = Net.client.newCall(Request.Builder().url(feedUrl).build()).execute()
+ val response = Net.client.newCall(Request.Builder().url(url).build()).execute()
response.use {
if (!it.isSuccessful) return@withContext "Couldn't fetch feed: HTTP ${it.code}"
val body = it.body ?: return@withContext "Empty response"
val parsed = FeedParser.parse(body.byteStream())
// Same REPLACE-wipes-other-fields concern as the OPML import path.
- val existing = db.feedDao().getById(feedUrl)
+ val existing = db.feedDao().getById(url)
val entity = existing?.copy(
- title = parsed.title.ifBlank { feedUrl },
+ title = parsed.title.ifBlank { url },
siteUrl = parsed.siteUrl ?: existing.siteUrl
- ) ?: FeedEntity(id = feedUrl, title = parsed.title.ifBlank { feedUrl }, siteUrl = parsed.siteUrl)
+ ) ?: FeedEntity(id = url, title = parsed.title.ifBlank { url }, siteUrl = parsed.siteUrl)
db.feedDao().upsert(entity)
}
FeedFetchWorker.fetchNow(context)