more fix for whitelist mode

This commit is contained in:
tateisu 2021-05-09 12:17:11 +09:00
parent 955b6e4850
commit 963d9003c1
6 changed files with 150 additions and 186 deletions

View File

@ -1954,8 +1954,8 @@ class ActMain : AsyncActivity(), View.OnClickListener,
)?.also { )?.also {
this.ta = parser.account(it.jsonObject) this.ta = parser.account(it.jsonObject)
if( ta != null){ if( ta != null){
val (ti, r2) = TootInstance.get(client, forceAccessToken = refToken.get()) val (ti, ri) = TootInstance.getEx(client, forceAccessToken = refToken.get())
this.ti = ti ?: return r2 this.ti = ti ?: return ri
} }
} }
} }
@ -2124,15 +2124,10 @@ class ActMain : AsyncActivity(), View.OnClickListener,
override suspend fun background(client: TootApiClient): TootApiResult? { override suspend fun background(client: TootApiClient): TootApiResult? {
val (instance, instanceResult) = TootInstance.get( val (ti,ri) = TootInstance.getEx(client,forceAccessToken = access_token)
client, this.ti = ti ?: return ri
apiHost,
forceAccessToken = access_token
)
instance ?: return instanceResult
this.ti = instance
val misskeyVersion = instance.misskeyVersion val misskeyVersion = ti.misskeyVersion
val result = client.getUserCredential(access_token, misskeyVersion = misskeyVersion) val result = client.getUserCredential(access_token, misskeyVersion = misskeyVersion)
@ -2140,7 +2135,7 @@ class ActMain : AsyncActivity(), View.OnClickListener,
this@ActMain, this@ActMain,
LinkHelper.create( LinkHelper.create(
apiHost, apiHost,
apDomainArg = instance.uri?.let { Host.parse(it) }, apDomainArg = ti.uri?.let { Host.parse(it) },
misskeyVersion = misskeyVersion misskeyVersion = misskeyVersion
) )
).account(result?.jsonObject) ).account(result?.jsonObject)

View File

@ -1215,31 +1215,17 @@ enum class ColumnType(
headerType = HeaderType.Instance, headerType = HeaderType.Instance,
loading = { client -> loading = { client ->
val (instance, instanceResult) = TootInstance.get( val (ti,ri) = TootInstance.getEx(
client, client,
Host.parse(column.instance_uri), Host.parse(column.instance_uri),
allowPixelfed = true, allowPixelfed = true,
forceUpdate = true forceUpdate = true
) )
if (instance != null) { if (ti != null) {
column.instance_information = instance column.instance_information = ti
column.handshake = instanceResult?.response?.handshake column.handshake = ri?.response?.handshake
} }
instanceResult ri
//
// // 「インスタンス情報」カラムをNAアカウントで開く場合
// instance_name != null -> client.instance = instance_name
//
// val (result, ti) = client.parseInstanceInformation(client.getInstanceInformation())
// instance_tmp = ti
// return result
// }
//
// val result = getInstanceInformation(client, column.instance_uri)
// if(instance_tmp != null) {
//
// }
// result
} }
), ),

View File

@ -25,7 +25,7 @@ object Action_Instance {
instance == null -> TootTaskRunner(activity).run(host, object : TootTask { instance == null -> TootTaskRunner(activity).run(host, object : TootTask {
var targetInstance : TootInstance? = null var targetInstance : TootInstance? = null
override suspend fun background(client : TootApiClient) : TootApiResult? { override suspend fun background(client : TootApiClient) : TootApiResult? {
val (ti, ri) = TootInstance.get(client, host, allowPixelfed = true) val (ti, ri) = TootInstance.getEx(client, host, allowPixelfed = true)
targetInstance = ti targetInstance = ti
return ri return ri
} }

View File

@ -120,7 +120,7 @@ class TootApiClient(
fun getScopeString(ti: TootInstance?) = when { fun getScopeString(ti: TootInstance?) = when {
// 古いサーバ // 古いサーバ
ti?.versionGE(TootInstance.VERSION_2_4_0_rc1) == false ->"read+write+follow" ti?.versionGE(TootInstance.VERSION_2_4_0_rc1) == false -> "read+write+follow"
// 新しいサーバか、AUTHORIZED_FETCH(3.0.0以降)によりサーバ情報を取得できなかった // 新しいサーバか、AUTHORIZED_FETCH(3.0.0以降)によりサーバ情報を取得できなかった
else -> "read+write+follow+push" else -> "read+write+follow+push"
@ -551,8 +551,7 @@ class TootApiClient(
suspend fun request( suspend fun request(
path: String, path: String,
request_builder: Request.Builder = Request.Builder(), request_builder: Request.Builder = Request.Builder(),
withoutToken: Boolean = false, forceAccessToken: String? = null,
forceAccessToken:String? =null,
): TootApiResult? { ): TootApiResult? {
val result = TootApiResult.makeWithCaption(apiHost?.pretty) val result = TootApiResult.makeWithCaption(apiHost?.pretty)
if (result.error != null) return result if (result.error != null) return result
@ -566,12 +565,8 @@ class TootApiClient(
request_builder.url(url) request_builder.url(url)
if (!withoutToken) { (forceAccessToken ?: account?.getAccessToken())
val access_token = forceAccessToken ?: account?.getAccessToken() ?.notEmpty()?.let { request_builder.header("Authorization", "Bearer $it") }
if (access_token?.isNotEmpty() == true) {
request_builder.header("Authorization", "Bearer $access_token")
}
}
request_builder.build() request_builder.build()
.also { log.d("request: ${it.method} $url") } .also { log.d("request: ${it.method} $url") }

View File

@ -9,14 +9,12 @@ import jp.juggler.subwaytooter.api.TootParser
import jp.juggler.subwaytooter.table.SavedAccount import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.subwaytooter.util.LinkHelper import jp.juggler.subwaytooter.util.LinkHelper
import jp.juggler.subwaytooter.util.VersionString import jp.juggler.subwaytooter.util.VersionString
import jp.juggler.subwaytooter.util.matchHost
import jp.juggler.util.* import jp.juggler.util.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import okhttp3.Request import okhttp3.Request
import java.lang.NullPointerException
import java.util.* import java.util.*
import java.util.regex.Pattern import java.util.regex.Pattern
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@ -266,33 +264,43 @@ class TootInstance(parser: TootParser, src: JsonObject) {
} }
// 疑似アカウントの追加時に、インスタンスの検証を行う // 疑似アカウントの追加時に、インスタンスの検証を行う
private suspend fun TootApiClient.getInstanceInformationMastodon(): TootApiResult? { private suspend fun TootApiClient.getInstanceInformationMastodon(
forceAccessToken: String? = null
): TootApiResult? {
val result = TootApiResult.makeWithCaption(apiHost?.pretty) val result = TootApiResult.makeWithCaption(apiHost?.pretty)
if (result.error != null) return result if (result.error != null) return result
if (sendRequest(result) { if (sendRequest(result) {
Request.Builder().url("https://${apiHost?.ascii}/api/v1/instance").build() val builder = Request.Builder().url("https://${apiHost?.ascii}/api/v1/instance")
(forceAccessToken ?: account?.getAccessToken() )
?.notEmpty()?.let { builder.header("Authorization", "Bearer $it") }
builder.build()
} }
) { ) {
parseJson(result) ?: return null parseJson(result) ?: return null
} }
// misskeyの事は忘れて本来のエラー情報を返す
return result return result
} }
// 疑似アカウントの追加時に、インスタンスの検証を行う // 疑似アカウントの追加時に、インスタンスの検証を行う
private suspend fun TootApiClient.getInstanceInformationMisskey(): TootApiResult? { private suspend fun TootApiClient.getInstanceInformationMisskey(
forceAccessToken: String? = null
): TootApiResult? {
val result = TootApiResult.makeWithCaption(apiHost?.pretty) val result = TootApiResult.makeWithCaption(apiHost?.pretty)
if (result.error != null) return result if (result.error != null) return result
if (sendRequest(result) { if (sendRequest(result) {
JsonObject().apply { jsonObject {
put("dummy", 1) put("dummy", 1)
} (forceAccessToken ?: account?.misskeyApiToken )
.toPostRequestBuilder() ?.notEmpty()?.let { put("i", it) }
}.toPostRequestBuilder()
.url("https://${apiHost?.ascii}/api/meta") .url("https://${apiHost?.ascii}/api/meta")
.build() .build()
}) { }
) {
parseJson(result) ?: return null parseJson(result) ?: return null
result.jsonObject?.apply { result.jsonObject?.apply {
@ -306,136 +314,71 @@ class TootInstance(parser: TootParser, src: JsonObject) {
} }
// 疑似アカウントの追加時に、インスタンスの検証を行う // 疑似アカウントの追加時に、インスタンスの検証を行う
private suspend fun TootApiClient.getInstanceInformation(): TootApiResult? { private suspend fun TootApiClient.getInstanceInformation(
forceAccessToken: String? = null
): TootApiResult? {
// misskeyのインスタンス情報を読めたら、それはmisskeyのインスタンス // misskeyのインスタンス情報を読めたら、それはmisskeyのインスタンス
val r2 = getInstanceInformationMisskey() ?: return null val r2 = getInstanceInformationMisskey(forceAccessToken) ?: return null
if (r2.jsonObject != null) return r2 if (r2.jsonObject != null) return r2
// マストドンのインスタンス情報を読めたら、それはマストドンのインスタンス // マストドンのインスタンス情報を読めたら、それはマストドンのインスタンス
val r1 = getInstanceInformationMastodon() ?: return null val r1 = getInstanceInformationMastodon(forceAccessToken) ?: return null
if (r1.jsonObject != null) return r1 if (r1.jsonObject != null) return r1
return r1 // 通信エラーの表示ならr1でもr2でも構わないはず return r1 // ホワイトリストモードの問題があるのでマストドン側のエラーを返す
} }
class RequestInfo( class QueuedRequest(
val client: TootApiClient,
val account: SavedAccount?,
val allowPixelfed: Boolean, val allowPixelfed: Boolean,
val forceUpdate: Boolean, val get: suspend (cached: TootInstance?) -> Pair<TootInstance?, TootApiResult?>,
val forceAccessToken: String?,
) { ) {
val result = Channel<Pair<TootInstance?, TootApiResult?>>() val result = Channel<Pair<TootInstance?, TootApiResult?>>()
} }
fun queuedRequest(
allowPixelfed: Boolean,
get: suspend (cached: TootInstance?) -> Pair<TootInstance?, TootApiResult?>
) = QueuedRequest(allowPixelfed, get)
// インスタンス情報のキャッシュ。同期オブジェクトを兼ねる // インスタンス情報のキャッシュ。同期オブジェクトを兼ねる
class CacheEntry { class CacheEntry {
// インスタンス情報のキャッシュ // インスタンス情報のキャッシュ
var cacheData: TootInstance? = null var cacheData: TootInstance? = null
private suspend fun getImpl(ri: RequestInfo): Pair<TootInstance?, TootApiResult?> {
var item: TootInstance?
if (!ri.forceUpdate && ri.forceAccessToken == null) {
// re-use cached item.
val now = SystemClock.elapsedRealtime()
item = cacheData
if (item != null && now - item.time_parse <= EXPIRE) {
if (item.instanceType == InstanceType.Pixelfed &&
!Pref.bpEnablePixelfed(App1.pref) &&
!ri.allowPixelfed
) {
return Pair(
null,
TootApiResult("currently Pixelfed instance is not supported.")
)
}
return Pair(item, TootApiResult())
}
}
// get new information
val result = when {
// マストドンのホワイトリストモード用
ri.forceAccessToken != null ->
ri.client.request(
"/api/v1/instance",
forceAccessToken = ri.forceAccessToken
)
ri.account == null ->
ri.client.getInstanceInformation()
ri.account.isMisskey ->
ri.client.request(
"/api/meta",
JsonObject().apply { put("dummy", 1) }.toPostRequestBuilder(),
withoutToken = true
)
else ->
ri.client.request(
"/api/v1/instance",
withoutToken = true
)
}
val json = result?.jsonObject ?: return Pair(null, result)
item = parseItem(
::TootInstance,
TootParser(
ri.client.context,
linkHelper = ri.account ?: LinkHelper.create(
ri.client.apiHost!!,
misskeyVersion = parseMisskeyVersion(json)
)
),
json
)
return when {
item == null -> Pair(
null,
result.setError("instance information parse error.")
)
item.instanceType == InstanceType.Pixelfed &&
!Pref.bpEnablePixelfed(App1.pref) &&
!ri.allowPixelfed ->
Pair(
null,
result.setError("currently Pixelfed instance is not supported.")
)
else -> Pair(item.also { cacheData = it }, result)
}
}
// ホストごとに同時に1つしか実行しない、インスタンス情報更新キュー // ホストごとに同時に1つしか実行しない、インスタンス情報更新キュー
val requestQueue = Channel<RequestInfo>(capacity = Channel.UNLIMITED) val requestQueue = Channel<QueuedRequest>(capacity = Channel.UNLIMITED)
private suspend fun loop() { private suspend fun handleRequest(req: QueuedRequest) = try {
while (true) { val pair = req.get(cacheData)
requestQueue.receive().let { req ->
req.result.send( pair.first?.let { cacheData = it }
try {
getImpl(req) when {
} catch (ex: Throwable) {
Pair( pair.first?.instanceType == InstanceType.Pixelfed &&
null, !Pref.bpEnablePixelfed(App1.pref) &&
TootApiResult(ex.withCaption("can't get server information.")) !req.allowPixelfed ->
) Pair(
} null, TootApiResult("currently Pixelfed instance is not supported.")
) )
}
else -> pair
} }
} catch (ex: Throwable) {
Pair(
null,
TootApiResult(ex.withCaption("can't get server information."))
)
} }
init { init {
GlobalScope.launch(Dispatchers.IO) { loop() } GlobalScope.launch(Dispatchers.IO) {
while (true) {
requestQueue.receive().let { it.result.send(handleRequest(it)) }
}
}
} }
} }
@ -456,50 +399,95 @@ class TootInstance(parser: TootParser, src: JsonObject) {
// no request, no expiration check // no request, no expiration check
fun getCached(host: String) = Host.parse(host).getCacheEntry().cacheData fun getCached(host: String) = Host.parse(host).getCacheEntry().cacheData
suspend fun get( suspend fun get(client: TootApiClient): Pair<TootInstance?, TootApiResult?> = getEx(client)
client: TootApiClient,
host: String,
account: SavedAccount? = client.account?.takeIf { it.matchHost(host) },
allowPixelfed: Boolean = false,
forceUpdate: Boolean = false
): Pair<TootInstance?, TootApiResult?> =
get(client, Host.parse(host), account, allowPixelfed, forceUpdate)
suspend fun get( suspend fun getEx(
client: TootApiClient, client: TootApiClient,
hostArg: Host? = null, hostArg: Host? = null,
account: SavedAccount? = if (hostArg == client.apiHost) client.account else null, account: SavedAccount? = null,
allowPixelfed: Boolean = false, allowPixelfed: Boolean = false,
forceUpdate: Boolean = false, forceUpdate: Boolean = false,
forceAccessToken: String? = null, // マストドンのwhitelist modeでアカウント追加時に必要 forceAccessToken: String? = null, // マストドンのwhitelist modeでアカウント追加時に必要
): Pair<TootInstance?, TootApiResult?> { ): Pair<TootInstance?, TootApiResult?> {
val tmpInstance = client.apiHost val cacheEntry = (hostArg ?: account?.apiHost ?: client.apiHost)?.getCacheEntry()
val tmpAccount = client.account ?: return Pair(null, TootApiResult("missing host."))
try {
// this may write client.apiHost
if (account != null) client.account = account
// update client.apiHost
if (hostArg != null) client.apiHost = hostArg
val host = client.apiHost // ホスト名ごとに用意したオブジェクトで同期する
?: throw NullPointerException("missing host to get server information.") return queuedRequest(allowPixelfed) { cached ->
// ホスト名ごとに用意したオブジェクトで同期する // may use cached item.
return RequestInfo( if (!forceUpdate && forceAccessToken == null && cached!=null) {
client = client, val now = SystemClock.elapsedRealtime()
account = account, if ( now - cached.time_parse <= EXPIRE)
allowPixelfed = allowPixelfed, return@queuedRequest Pair(cached, TootApiResult())
forceUpdate = forceUpdate, }
forceAccessToken = forceAccessToken
val tmpInstance = client.apiHost
val tmpAccount = client.account
val linkHelper: LinkHelper?
// get new information
val result = when {
// ストリームマネジャから呼ばれる
account != null -> try {
linkHelper = account
client.account = account // this may change client.apiHost
if (account.isMisskey) {
client.getInstanceInformationMisskey()
} else {
client.getInstanceInformationMastodon()
}
} finally {
client.account = tmpAccount
client.apiHost = tmpInstance // must be last.
}
// サーバ情報カラムやProfileDirectoryを開く場合
hostArg != null && hostArg != tmpInstance -> try {
linkHelper = null
client.account = null // don't use access token.
client.apiHost = hostArg
client.getInstanceInformation()
} finally {
client.account = tmpAccount
client.apiHost = tmpInstance // must be last.
}
// client にすでにあるアクセス情報でサーバ情報を取得する
// マストドンのホワイトリストモード用にアクセストークンを指定できる
else -> {
linkHelper = client.account // may null
client.getInstanceInformation(
forceAccessToken = forceAccessToken
)
}
}
val json = result?.jsonObject
?: return@queuedRequest Pair(null, result)
val item = parseItem(
::TootInstance,
TootParser(
client.context,
linkHelper = linkHelper ?: LinkHelper.create(
hostArg!!,
misskeyVersion = parseMisskeyVersion(json)
)
),
json
) ?: return@queuedRequest Pair(
null,
result.setError("instance information parse error.")
) )
.also { host.getCacheEntry().requestQueue.send(it) }
.result.receive()
} finally { Pair(item, result)
client.account = tmpAccount
client.apiHost = tmpInstance // must be last.
} }
.also { cacheEntry.requestQueue.send(it) }
.result.receive()
} }
} }
} }

View File

@ -56,7 +56,7 @@ class StreamManager(val appState: AppState) {
if (errorAcct.contains(acct)) return null if (errorAcct.contains(acct)) return null
var acctGroup = newMap[acct] var acctGroup = newMap[acct]
if (acctGroup == null) { if (acctGroup == null) {
var (ti, ri) = TootInstance.get(client, account = accessInfo) var (ti, ri) = TootInstance.getEx(client, account = accessInfo)
if (ti == null) { if (ti == null) {
log.d("can't get server info. ${ri?.error}") log.d("can't get server info. ${ri?.error}")
val tiOld = acctGroups[acct]?.ti val tiOld = acctGroups[acct]?.ti