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

View File

@ -1215,31 +1215,17 @@ enum class ColumnType(
headerType = HeaderType.Instance,
loading = { client ->
val (instance, instanceResult) = TootInstance.get(
val (ti,ri) = TootInstance.getEx(
client,
Host.parse(column.instance_uri),
allowPixelfed = true,
forceUpdate = true
)
if (instance != null) {
column.instance_information = instance
column.handshake = instanceResult?.response?.handshake
if (ti != null) {
column.instance_information = ti
column.handshake = ri?.response?.handshake
}
instanceResult
//
// // 「インスタンス情報」カラムを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
ri
}
),

View File

@ -25,7 +25,7 @@ object Action_Instance {
instance == null -> TootTaskRunner(activity).run(host, object : TootTask {
var targetInstance : TootInstance? = null
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
return ri
}

View File

@ -120,7 +120,7 @@ class TootApiClient(
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以降)によりサーバ情報を取得できなかった
else -> "read+write+follow+push"
@ -551,8 +551,7 @@ class TootApiClient(
suspend fun request(
path: String,
request_builder: Request.Builder = Request.Builder(),
withoutToken: Boolean = false,
forceAccessToken:String? =null,
forceAccessToken: String? = null,
): TootApiResult? {
val result = TootApiResult.makeWithCaption(apiHost?.pretty)
if (result.error != null) return result
@ -566,12 +565,8 @@ class TootApiClient(
request_builder.url(url)
if (!withoutToken) {
val access_token = forceAccessToken ?: account?.getAccessToken()
if (access_token?.isNotEmpty() == true) {
request_builder.header("Authorization", "Bearer $access_token")
}
}
(forceAccessToken ?: account?.getAccessToken())
?.notEmpty()?.let { request_builder.header("Authorization", "Bearer $it") }
request_builder.build()
.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.util.LinkHelper
import jp.juggler.subwaytooter.util.VersionString
import jp.juggler.subwaytooter.util.matchHost
import jp.juggler.util.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import okhttp3.Request
import java.lang.NullPointerException
import java.util.*
import java.util.regex.Pattern
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)
if (result.error != null) return 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
}
// misskeyの事は忘れて本来のエラー情報を返す
return result
}
// 疑似アカウントの追加時に、インスタンスの検証を行う
private suspend fun TootApiClient.getInstanceInformationMisskey(): TootApiResult? {
private suspend fun TootApiClient.getInstanceInformationMisskey(
forceAccessToken: String? = null
): TootApiResult? {
val result = TootApiResult.makeWithCaption(apiHost?.pretty)
if (result.error != null) return result
if (sendRequest(result) {
JsonObject().apply {
jsonObject {
put("dummy", 1)
}
.toPostRequestBuilder()
(forceAccessToken ?: account?.misskeyApiToken )
?.notEmpty()?.let { put("i", it) }
}.toPostRequestBuilder()
.url("https://${apiHost?.ascii}/api/meta")
.build()
}) {
}
) {
parseJson(result) ?: return null
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のインスタンス
val r2 = getInstanceInformationMisskey() ?: return null
val r2 = getInstanceInformationMisskey(forceAccessToken) ?: return null
if (r2.jsonObject != null) return r2
// マストドンのインスタンス情報を読めたら、それはマストドンのインスタンス
val r1 = getInstanceInformationMastodon() ?: return null
val r1 = getInstanceInformationMastodon(forceAccessToken) ?: return null
if (r1.jsonObject != null) return r1
return r1 // 通信エラーの表示ならr1でもr2でも構わないはず
return r1 // ホワイトリストモードの問題があるのでマストドン側のエラーを返す
}
class RequestInfo(
val client: TootApiClient,
val account: SavedAccount?,
class QueuedRequest(
val allowPixelfed: Boolean,
val forceUpdate: Boolean,
val forceAccessToken: String?,
val get: suspend (cached: TootInstance?) -> 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 {
// インスタンス情報のキャッシュ
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つしか実行しない、インスタンス情報更新キュー
val requestQueue = Channel<RequestInfo>(capacity = Channel.UNLIMITED)
val requestQueue = Channel<QueuedRequest>(capacity = Channel.UNLIMITED)
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."))
)
}
private suspend fun handleRequest(req: QueuedRequest) = try {
val pair = req.get(cacheData)
pair.first?.let { cacheData = it }
when {
pair.first?.instanceType == InstanceType.Pixelfed &&
!Pref.bpEnablePixelfed(App1.pref) &&
!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 {
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
fun getCached(host: String) = Host.parse(host).getCacheEntry().cacheData
suspend fun get(
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(client: TootApiClient): Pair<TootInstance?, TootApiResult?> = getEx(client)
suspend fun get(
suspend fun getEx(
client: TootApiClient,
hostArg: Host? = null,
account: SavedAccount? = if (hostArg == client.apiHost) client.account else null,
account: SavedAccount? = null,
allowPixelfed: Boolean = false,
forceUpdate: Boolean = false,
forceAccessToken: String? = null, // マストドンのwhitelist modeでアカウント追加時に必要
): Pair<TootInstance?, TootApiResult?> {
val tmpInstance = client.apiHost
val tmpAccount = client.account
try {
// this may write client.apiHost
if (account != null) client.account = account
// update client.apiHost
if (hostArg != null) client.apiHost = hostArg
val cacheEntry = (hostArg ?: account?.apiHost ?: client.apiHost)?.getCacheEntry()
?: return Pair(null, TootApiResult("missing host."))
val host = client.apiHost
?: throw NullPointerException("missing host to get server information.")
// ホスト名ごとに用意したオブジェクトで同期する
return queuedRequest(allowPixelfed) { cached ->
// ホスト名ごとに用意したオブジェクトで同期する
return RequestInfo(
client = client,
account = account,
allowPixelfed = allowPixelfed,
forceUpdate = forceUpdate,
forceAccessToken = forceAccessToken
// may use cached item.
if (!forceUpdate && forceAccessToken == null && cached!=null) {
val now = SystemClock.elapsedRealtime()
if ( now - cached.time_parse <= EXPIRE)
return@queuedRequest Pair(cached, TootApiResult())
}
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 {
client.account = tmpAccount
client.apiHost = tmpInstance // must be last.
Pair(item, result)
}
.also { cacheEntry.requestQueue.send(it) }
.result.receive()
}
}
}

View File

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