1
0
mirror of https://github.com/tateisu/SubwayTooter synced 2025-01-26 16:56:28 +01:00

fix #155, Unable to log into whitelist_mode instance

This commit is contained in:
tateisu 2021-05-09 10:18:56 +09:00
parent 91d0451e9a
commit 955b6e4850
4 changed files with 146 additions and 81 deletions

View File

@ -1051,7 +1051,7 @@ class TestTootApiClient {
println(url)
// ブラウザからコールバックで受け取ったcodeを処理する
result = client.authentication2(clientName, "DUMMY_CODE")
result = client.authentication2Mastodon(clientName, "DUMMY_CODE")
jsonObject = result?.jsonObject
assertNotNull(jsonObject)
if (jsonObject == null) return@runBlocking

View File

@ -49,6 +49,7 @@ import java.io.FileOutputStream
import java.io.InputStreamReader
import java.lang.ref.WeakReference
import java.util.*
import java.util.concurrent.atomic.AtomicReference
import java.util.zip.ZipInputStream
import kotlin.math.abs
import kotlin.math.max
@ -579,7 +580,11 @@ class ActMain : AsyncActivity(), View.OnClickListener,
// アカウント設定から戻ってきたら、カラムを消す必要があるかもしれない
val new_order = app_state.columnList
.mapIndexedNotNull { index, column ->
if (!column.access_info.isNA && null == SavedAccount.loadAccount(this@ActMain, column.access_info.db_id)) {
if (!column.access_info.isNA && null == SavedAccount.loadAccount(
this@ActMain,
column.access_info.db_id
)
) {
null
} else {
index
@ -639,7 +644,12 @@ class ActMain : AsyncActivity(), View.OnClickListener,
if (te - ts >= 100L) log.w("onStart: ${te - ts}ms :updateColumnStripSelection")
ts = SystemClock.elapsedRealtime()
app_state.columnList.forEach { it.fireShowContent(reason = "ActMain onStart", reset = true) }
app_state.columnList.forEach {
it.fireShowContent(
reason = "ActMain onStart",
reset = true
)
}
te = SystemClock.elapsedRealtime()
if (te - ts >= 100L) log.w("onStart: ${te - ts}ms :fireShowContent")
@ -823,7 +833,7 @@ class ActMain : AsyncActivity(), View.OnClickListener,
// 新しいカラムをどこに挿入するか
// カラムの次の位置か、現在のページの次の位置か、終端
fun nextPosition(column: Column?): Int =
app_state.columnIndex(column)?.let{ it+1 } ?: defaultInsertPosition
app_state.columnIndex(column)?.let { it + 1 } ?: defaultInsertPosition
private fun showQuickTootVisibility() {
btnQuickTootMenu.imageResource =
@ -868,7 +878,7 @@ class ActMain : AsyncActivity(), View.OnClickListener,
val refresh_after_toot = Pref.ipRefreshAfterToot(pref)
if (refresh_after_toot != Pref.RAT_DONT_REFRESH) {
app_state.columnList
.filter{ it.access_info.acct == posted_acct}
.filter { it.access_info.acct == posted_acct }
.forEach {
it.startRefreshForPost(
refresh_after_toot,
@ -970,7 +980,7 @@ class ActMain : AsyncActivity(), View.OnClickListener,
private fun isOrderChanged(new_order: List<Int>): Boolean {
if (new_order.size != app_state.columnCount) return true
for(i in new_order.indices){
for (i in new_order.indices) {
if (new_order[i] != i) return true
}
return false
@ -1027,7 +1037,7 @@ class ActMain : AsyncActivity(), View.OnClickListener,
REQUEST_CODE_COLUMN_COLOR -> if (data != null) {
app_state.saveColumnList()
val idx = data.getIntExtra(ActColumnCustomize.EXTRA_COLUMN_INDEX, 0)
app_state.column(idx)?.let{
app_state.column(idx)?.let {
it.fireColumnColor()
it.fireShowContent(
reason = "ActMain column color changed",
@ -1074,7 +1084,10 @@ class ActMain : AsyncActivity(), View.OnClickListener,
REQUEST_CODE_TEXT -> when (resultCode) {
ActText.RESULT_SEARCH_MSP -> searchFromActivityResult(data, ColumnType.SEARCH_MSP)
ActText.RESULT_SEARCH_TS -> searchFromActivityResult(data, ColumnType.SEARCH_TS)
ActText.RESULT_SEARCH_NOTESTOCK -> searchFromActivityResult(data, ColumnType.SEARCH_NOTESTOCK)
ActText.RESULT_SEARCH_NOTESTOCK -> searchFromActivityResult(
data,
ColumnType.SEARCH_NOTESTOCK
)
}
}
@ -1090,7 +1103,7 @@ class ActMain : AsyncActivity(), View.OnClickListener,
}
// カラムが0個ならアプリを終了する
if (app_state.columnCount == 0 ) {
if (app_state.columnCount == 0) {
finish()
return
}
@ -1504,12 +1517,12 @@ class ActMain : AsyncActivity(), View.OnClickListener,
val c = env.pager.currentItem
c == idx
}, { env ->
idx >= 0 && idx in env.visibleColumnsIndices
}
idx >= 0 && idx in env.visibleColumnsIndices
}
)
private fun updateColumnStrip() {
llEmpty.vg(app_state.columnCount==0)
llEmpty.vg(app_state.columnCount == 0)
val iconSize = stripIconSize
val rootW = (iconSize * 1.25f + 0.5f).toInt()
@ -1588,7 +1601,7 @@ class ActMain : AsyncActivity(), View.OnClickListener,
handler.post(Runnable {
if (isFinishing) return@Runnable
if (app_state.columnCount == 0 ) {
if (app_state.columnCount == 0) {
llColumnStrip.setVisibleRange(-1, -1, 0f)
} else {
phoneTab({ env ->
@ -1788,7 +1801,7 @@ class ActMain : AsyncActivity(), View.OnClickListener,
PollingWorker.queueNotificationClicked(this, uri)
val columnList = app_state.columnList
val column =columnList.firstOrNull {
val column = columnList.firstOrNull {
it.type == ColumnType.NOTIFICATIONS &&
it.access_info == account &&
!it.system_notification_not_related
@ -1883,8 +1896,10 @@ class ActMain : AsyncActivity(), View.OnClickListener,
val error = uri.getQueryParameter("error")
val error_description = uri.getQueryParameter("error_description")
if (error != null || error_description != null)
return TootApiResult(error_description.notBlank() ?: error.notBlank()
?: "?")
return TootApiResult(
error_description.notBlank() ?: error.notBlank()
?: "?"
)
// subwaytooter://oauth(\d*)/
// ?code=113cc036e078ac500d3d0d3ad345cd8181456ab087abc67270d40f40a4e9e3c2
@ -1923,22 +1938,26 @@ class ActMain : AsyncActivity(), View.OnClickListener,
val instance = client.apiHost
?: return TootApiResult("missing instance in callback url.")
val (ti, r2) = TootInstance.get(client)
ti ?: return r2
this.ti = ti
this.host = instance
val parser = TootParser(
this@ActMain,
linkHelper = LinkHelper.create(instance)
)
return client.authentication2(
val refToken = AtomicReference<String>(null)
return client.authentication2Mastodon(
Pref.spClientName(this@ActMain),
code
)?.also { this.ta = parser.account(it.jsonObject) }
code,
outAccessToken = refToken
)?.also {
this.ta = parser.account(it.jsonObject)
if( ta != null){
val (ti, r2) = TootInstance.get(client, forceAccessToken = refToken.get())
this.ti = ti ?: return r2
}
}
}
}
@ -2105,7 +2124,11 @@ class ActMain : AsyncActivity(), View.OnClickListener,
override suspend fun background(client: TootApiClient): TootApiResult? {
val (instance, instanceResult) = TootInstance.get(client, apiHost)
val (instance, instanceResult) = TootInstance.get(
client,
apiHost,
forceAccessToken = access_token
)
instance ?: return instanceResult
this.ti = instance
@ -2193,19 +2216,19 @@ class ActMain : AsyncActivity(), View.OnClickListener,
return
}
app_state.columnIndex(column)?.let{ page_delete ->
app_state.columnIndex(column)?.let { page_delete ->
phoneTab({ env ->
val page_showing = env.pager.currentItem
removeColumn(column)
if ( page_showing == page_delete) {
scrollAndLoad(page_showing-1)
if (page_showing == page_delete) {
scrollAndLoad(page_showing - 1)
}
}, {
removeColumn(column)
scrollAndLoad( page_delete - 1)
scrollAndLoad(page_delete - 1)
})
}
}
@ -2253,8 +2276,8 @@ class ActMain : AsyncActivity(), View.OnClickListener,
scrollAndLoad(lastColumnIndex)
}
private fun scrollAndLoad(idx:Int){
val c = app_state.column(idx) ?:return
private fun scrollAndLoad(idx: Int) {
val c = app_state.column(idx) ?: return
scrollToColumn(idx)
if (!c.bFirstInitialized) c.startLoading()
}

View File

@ -9,6 +9,7 @@ import jp.juggler.subwaytooter.util.*
import jp.juggler.util.*
import okhttp3.*
import java.util.*
import java.util.concurrent.atomic.AtomicReference
class TootApiClient(
val context: Context,
@ -117,10 +118,14 @@ class TootApiClient(
return sb.toString().replace("\n+".toRegex(), "\n")
}
fun getScopeString(ti: TootInstance) = when {
fun getScopeString(ti: TootInstance?) = when {
// 古いサーバ
ti?.versionGE(TootInstance.VERSION_2_4_0_rc1) == false ->"read+write+follow"
// 新しいサーバか、AUTHORIZED_FETCH(3.0.0以降)によりサーバ情報を取得できなかった
else -> "read+write+follow+push"
// 過去の試行錯誤かな
// ti.versionGE(TootInstance.VERSION_2_7_0_rc1) -> "read+write+follow+push+create"
ti.versionGE(TootInstance.VERSION_2_4_0_rc1) -> "read+write+follow+push"
else -> "read+write+follow"
}
fun getScopeArrayMisskey(@Suppress("UNUSED_PARAMETER") ti: TootInstance) =
@ -547,6 +552,7 @@ class TootApiClient(
path: String,
request_builder: Request.Builder = Request.Builder(),
withoutToken: Boolean = false,
forceAccessToken:String? =null,
): TootApiResult? {
val result = TootApiResult.makeWithCaption(apiHost?.pretty)
if (result.error != null) return result
@ -556,19 +562,19 @@ class TootApiClient(
try {
if (!sendRequest(result) {
val url = "https://${apiHost?.ascii}$path"
val url = "https://${apiHost?.ascii}$path"
request_builder.url(url)
if(!withoutToken){
val access_token = account?.getAccessToken()
if (!withoutToken) {
val access_token = forceAccessToken ?: account?.getAccessToken()
if (access_token?.isNotEmpty() == true) {
request_builder.header("Authorization", "Bearer $access_token")
}
}
request_builder.build()
.also{ log.d("request: ${it.method} $url") }
.also { log.d("request: ${it.method} $url") }
}) return result
@ -684,7 +690,10 @@ class TootApiClient(
return result
}
private suspend fun authentication1Misskey(clientNameArg: String, ti: TootInstance): TootApiResult? {
private suspend fun authentication1Misskey(
clientNameArg: String,
ti: TootInstance
): TootApiResult? {
val result = TootApiResult.makeWithCaption(this.apiHost?.pretty)
if (result.error != null) return result
val instance = result.caption // same to instance
@ -954,7 +963,7 @@ class TootApiClient(
private suspend fun prepareClientMastodon(
clientNameArg: String,
ti: TootInstance,
ti: TootInstance?,
forceUpdateClient: Boolean = false
): TootApiResult? {
// 前準備
@ -1046,11 +1055,11 @@ class TootApiClient(
private suspend fun authentication1Mastodon(
clientNameArg: String,
ti: TootInstance,
ti: TootInstance?,
forceUpdateClient: Boolean = false
): TootApiResult? {
if (ti.instanceType == InstanceType.Pixelfed) {
if (ti?.instanceType == InstanceType.Pixelfed) {
return TootApiResult("currently Pixelfed instance is not supported.")
}
@ -1069,15 +1078,24 @@ class TootApiClient(
): TootApiResult? {
val (ti, ri) = TootInstance.get(this)
ti ?: return ri
return when {
log.i("authentication1: instance info version=${ti?.version} misskeyVersion=${ti?.misskeyVersion} responseCode=${ri?.response?.code}")
return if (ti == null) when (ri?.response?.code) {
// https://github.com/tateisu/SubwayTooter/issues/155
// Mastodon's WHITELIST_MODE
401 -> authentication1Mastodon(clientNameArg, null, forceUpdateClient)
else -> ri
} else when {
ti.misskeyVersion > 0 -> authentication1Misskey(clientNameArg, ti)
else -> authentication1Mastodon(clientNameArg, ti, forceUpdateClient)
}
}
// oAuth2認証の続きを行う
suspend fun authentication2(clientNameArg: String, code: String): TootApiResult? {
suspend fun authentication2Mastodon(
clientNameArg: String,
code: String,
outAccessToken: AtomicReference<String>
): TootApiResult? {
val result = TootApiResult.makeWithCaption(apiHost?.pretty)
if (result.error != null) return result
@ -1116,8 +1134,8 @@ class TootApiClient(
if (access_token?.isEmpty() != false) {
return result.setError("missing access_token in the response.")
}
outAccessToken.set(access_token)
return getUserCredential(access_token, token_info)
}
// アクセストークン手動入力でアカウントを更新する場合、アカウントの情報を取得する
@ -1282,13 +1300,13 @@ class TootApiClient(
val request_builder = Request.Builder()
if( account.isMisskey){
if (account.isMisskey) {
val accessToken = account.misskeyApiToken
if (accessToken?.isNotEmpty() == true) {
val delm = if (-1 != url.indexOf('?')) '&' else '?'
url = "$url${delm}i=${accessToken.encodePercent()}"
}
}else {
} else {
val access_token = account.getAccessToken()
if (access_token?.isNotEmpty() == true) {
val delm = if (-1 != url.indexOf('?')) '&' else '?'

View File

@ -30,30 +30,34 @@ enum class InstanceType {
Pleroma
}
enum class CapabilitySource{
enum class CapabilitySource {
Fedibird,
}
enum class InstanceCapability(private val capabilitySource:CapabilitySource,private val id: String) {
FavouriteHashtag(CapabilitySource.Fedibird,"favourite_hashtag"),
FavouriteDomain(CapabilitySource.Fedibird,"favourite_domain"),
StatusExpire(CapabilitySource.Fedibird,"status_expire"),
FollowNoDelivery(CapabilitySource.Fedibird,"follow_no_delivery"),
FollowHashtag(CapabilitySource.Fedibird,"follow_hashtag"),
SubscribeAccount(CapabilitySource.Fedibird,"subscribe_account"),
SubscribeDomain(CapabilitySource.Fedibird,"subscribe_domain"),
SubscribeKeyword(CapabilitySource.Fedibird,"subscribe_keyword"),
TimelineNoLocal(CapabilitySource.Fedibird,"timeline_no_local"),
TimelineDomain(CapabilitySource.Fedibird,"timeline_domain"),
TimelineGroup(CapabilitySource.Fedibird,"timeline_group"),
TimelineGroupDirectory(CapabilitySource.Fedibird,"timeline_group_directory"),
VisibilityMutual(CapabilitySource.Fedibird,"visibility_mutual"),
VisibilityLimited(CapabilitySource.Fedibird,"visibility_limited"),
enum class InstanceCapability(
private val capabilitySource: CapabilitySource,
private val id: String
) {
FavouriteHashtag(CapabilitySource.Fedibird, "favourite_hashtag"),
FavouriteDomain(CapabilitySource.Fedibird, "favourite_domain"),
StatusExpire(CapabilitySource.Fedibird, "status_expire"),
FollowNoDelivery(CapabilitySource.Fedibird, "follow_no_delivery"),
FollowHashtag(CapabilitySource.Fedibird, "follow_hashtag"),
SubscribeAccount(CapabilitySource.Fedibird, "subscribe_account"),
SubscribeDomain(CapabilitySource.Fedibird, "subscribe_domain"),
SubscribeKeyword(CapabilitySource.Fedibird, "subscribe_keyword"),
TimelineNoLocal(CapabilitySource.Fedibird, "timeline_no_local"),
TimelineDomain(CapabilitySource.Fedibird, "timeline_domain"),
TimelineGroup(CapabilitySource.Fedibird, "timeline_group"),
TimelineGroupDirectory(CapabilitySource.Fedibird, "timeline_group_directory"),
VisibilityMutual(CapabilitySource.Fedibird, "visibility_mutual"),
VisibilityLimited(CapabilitySource.Fedibird, "visibility_limited"),
;
fun hasCapability(instance:TootInstance):Boolean{
when(capabilitySource){
CapabilitySource.Fedibird->{
if( instance.fedibird_capabilities?.any{ it == id} ==true ) return true
fun hasCapability(instance: TootInstance): Boolean {
when (capabilitySource) {
CapabilitySource.Fedibird -> {
if (instance.fedibird_capabilities?.any { it == id } == true) return true
}
}
// XXX: もし機能がMastodon公式に取り込まれたならバージョン番号で判断できるはず
@ -220,7 +224,7 @@ class TootInstance(parser: TootParser, src: JsonObject) {
return i >= 0
}
fun hasCapability(cap:InstanceCapability) = cap.hasCapability(this)
fun hasCapability(cap: InstanceCapability) = cap.hasCapability(this)
companion object {
@ -320,6 +324,7 @@ class TootInstance(parser: TootParser, src: JsonObject) {
val account: SavedAccount?,
val allowPixelfed: Boolean,
val forceUpdate: Boolean,
val forceAccessToken: String?,
) {
val result = Channel<Pair<TootInstance?, TootApiResult?>>()
}
@ -333,7 +338,7 @@ class TootInstance(parser: TootParser, src: JsonObject) {
var item: TootInstance?
if (!ri.forceUpdate) {
if (!ri.forceUpdate && ri.forceAccessToken == null) {
// re-use cached item.
val now = SystemClock.elapsedRealtime()
item = cacheData
@ -353,9 +358,15 @@ class TootInstance(parser: TootParser, src: JsonObject) {
// get new information
val result = when {
ri.account == null -> {
// マストドンのホワイトリストモード用
ri.forceAccessToken != null ->
ri.client.request(
"/api/v1/instance",
forceAccessToken = ri.forceAccessToken
)
ri.account == null ->
ri.client.getInstanceInformation()
}
ri.account.isMisskey ->
ri.client.request(
@ -409,11 +420,16 @@ class TootInstance(parser: TootParser, src: JsonObject) {
private suspend fun loop() {
while (true) {
requestQueue.receive().let { req ->
req.result.send(try {
getImpl(req)
} catch (ex: Throwable) {
Pair(null, TootApiResult(ex.withCaption("can't get server information.")))
})
req.result.send(
try {
getImpl(req)
} catch (ex: Throwable) {
Pair(
null,
TootApiResult(ex.withCaption("can't get server information."))
)
}
)
}
}
}
@ -454,7 +470,8 @@ class TootInstance(parser: TootParser, src: JsonObject) {
hostArg: Host? = null,
account: SavedAccount? = if (hostArg == client.apiHost) client.account else null,
allowPixelfed: Boolean = false,
forceUpdate: Boolean = false
forceUpdate: Boolean = false,
forceAccessToken: String? = null, // マストドンのwhitelist modeでアカウント追加時に必要
): Pair<TootInstance?, TootApiResult?> {
val tmpInstance = client.apiHost
@ -465,11 +482,18 @@ class TootInstance(parser: TootParser, src: JsonObject) {
// update client.apiHost
if (hostArg != null) client.apiHost = hostArg
if (client.apiHost == null) throw NullPointerException("missing host to get server information.")
val host = client.apiHost
?: throw NullPointerException("missing host to get server information.")
// ホスト名ごとに用意したオブジェクトで同期する
return RequestInfo(client, account, allowPixelfed, forceUpdate)
.also { client.apiHost!!.getCacheEntry().requestQueue.send(it) }
return RequestInfo(
client = client,
account = account,
allowPixelfed = allowPixelfed,
forceUpdate = forceUpdate,
forceAccessToken = forceAccessToken
)
.also { host.getCacheEntry().requestQueue.send(it) }
.result.receive()
} finally {