Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

ServerState.kt 6.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. package com.dmdirc.ktirc.model
  2. import com.dmdirc.ktirc.io.CaseMapping
  3. import com.dmdirc.ktirc.sasl.SaslMechanism
  4. import com.dmdirc.ktirc.sasl.supportedSaslMechanisms
  5. import com.dmdirc.ktirc.util.logger
  6. import kotlin.reflect.KClass
  7. /**
  8. * Contains the current state of a single IRC server.
  9. */
  10. class ServerState internal constructor(
  11. initialNickname: String,
  12. initialServerName: String,
  13. saslMechanisms: Collection<SaslMechanism> = supportedSaslMechanisms) {
  14. private val log by logger()
  15. /** Whether we've received the 'Welcome to IRC' (001) message. */
  16. internal var receivedWelcome = false
  17. /** The current status of the server. */
  18. var status = ServerStatus.Disconnected
  19. internal set
  20. /**
  21. * What we believe our current nickname to be on the server.
  22. *
  23. * Initially this will be the nickname provided in the [Profile]. It will be updated to the actual nickname
  24. * in use when connecting. Once you have received a [com.dmdirc.ktirc.events.ServerWelcome] event you can
  25. * rely on this value being current.
  26. * */
  27. var localNickname: String = initialNickname
  28. internal set
  29. /**
  30. * The name of the server we are connected to.
  31. *
  32. * Initially this will be the hostname or IP address provided in the [Server]. It will be updated to the server's
  33. * self-reported hostname when connecting. Once you have received a [com.dmdirc.ktirc.events.ServerWelcome] event
  34. * you can rely on this value being current.
  35. */
  36. var serverName: String = initialServerName
  37. internal set
  38. /** The features that the server has declared it supports (from the 005 header). */
  39. val features = ServerFeatureMap()
  40. /** The capabilities we have negotiated with the server (from IRCv3). */
  41. val capabilities = CapabilitiesState()
  42. /** The current state of SASL authentication. */
  43. internal val sasl = SaslState(saslMechanisms)
  44. /**
  45. * Convenience accessor for the [ServerFeature.ModePrefixes] feature, which will always have a value.
  46. */
  47. val channelModePrefixes
  48. get() = features[ServerFeature.ModePrefixes] ?: throw IllegalStateException("lost mode prefixes")
  49. /**
  50. * Determines if the given mode is one applied to a user of a channel, such as 'o' for operator.
  51. */
  52. fun isChannelUserMode(mode: Char) = channelModePrefixes.isMode(mode)
  53. /**
  54. * Determines what type of channel mode the given character is, based on the server features.
  55. *
  56. * If the mode isn't found, or the server hasn't provided modes, it is presumed to be [ChannelModeType.NoParameter].
  57. */
  58. fun channelModeType(mode: Char): ChannelModeType {
  59. features[ServerFeature.ChannelModes]?.forEachIndexed { index, modes ->
  60. if (mode in modes) {
  61. return ChannelModeType.values()[index]
  62. }
  63. }
  64. log.warning { "Unknown channel mode $mode, assuming it's boolean" }
  65. return ChannelModeType.NoParameter
  66. }
  67. }
  68. /**
  69. * Maps known features onto their values, enforcing type safety.
  70. */
  71. class ServerFeatureMap {
  72. private val features = HashMap<ServerFeature<*>, Any?>()
  73. /**
  74. * Gets the value, or the default value, of the given feature.
  75. */
  76. @Suppress("UNCHECKED_CAST")
  77. operator fun <T : Any> get(feature: ServerFeature<T>) = features.getOrDefault(feature, feature.default) as? T? ?: feature.default
  78. internal operator fun set(feature: ServerFeature<*>, value: Any) {
  79. require(feature.type.isInstance(value)) {
  80. "Value given for feature ${feature::class} must be type ${feature.type} but was ${value::class}"
  81. }
  82. features[feature] = value
  83. }
  84. internal fun setAll(featureMap: ServerFeatureMap) = featureMap.features.forEach { feature, value -> features[feature] = value }
  85. internal fun reset(feature: ServerFeature<*>) = features.put(feature, null)
  86. }
  87. /**
  88. * Stores the mapping of mode prefixes received from the server.
  89. */
  90. data class ModePrefixMapping(val modes: String, val prefixes: String) {
  91. /** Determines whether the given character is a mode prefix (e.g. "@", "+"). */
  92. fun isPrefix(char: Char) = prefixes.contains(char)
  93. /** Determines whether the given character is a channel user mode (e.g. "o", "v"). */
  94. fun isMode(char: Char) = modes.contains(char)
  95. /** Gets the mode corresponding to the given prefix (e.g. "@" -> "o"). */
  96. fun getMode(prefix: Char) = modes[prefixes.indexOf(prefix)]
  97. /** Gets the modes corresponding to the given prefixes (e.g. "@+" -> "ov"). */
  98. fun getModes(prefixes: String) = String(prefixes.map(this::getMode).toCharArray())
  99. }
  100. /**
  101. * Describes a server feature determined from the 005 response.
  102. */
  103. sealed class ServerFeature<T : Any>(val name: String, val type: KClass<T>, val default: T? = null) {
  104. /** The network the server says it belongs to. */
  105. object Network : ServerFeature<String>("NETWORK", String::class)
  106. /** The case mapping the server uses, defaulting to RFC. */
  107. object ServerCaseMapping : ServerFeature<CaseMapping>("CASEMAPPING", CaseMapping::class, CaseMapping.Rfc)
  108. /** The mode prefixes the server uses, defaulting to ov/@+. */
  109. object ModePrefixes : ServerFeature<ModePrefixMapping>("PREFIX", ModePrefixMapping::class, ModePrefixMapping("ov", "@+"))
  110. /** The maximum number of channels a client may join. */
  111. object MaximumChannels : ServerFeature<Int>("MAXCHANNELS", Int::class) // TODO: CHANLIMIT also exists
  112. /** The modes supported in channels. */
  113. object ChannelModes : ServerFeature<Array<String>>("CHANMODES", Array<String>::class)
  114. /** The maximum length of a channel name, defaulting to 200. */
  115. object MaximumChannelNameLength : ServerFeature<Int>("CHANNELLEN", Int::class, 200)
  116. /** Whether or not the server supports extended who. */
  117. object WhoxSupport : ServerFeature<Boolean>("WHOX", Boolean::class, false)
  118. }
  119. internal val serverFeatures: Map<String, ServerFeature<*>> by lazy {
  120. ServerFeature::class.nestedClasses.map { it.objectInstance as ServerFeature<*> }.associateBy { it.name }
  121. }
  122. /**
  123. * Enumeration of the possible states of a server.
  124. */
  125. enum class ServerStatus {
  126. /** The server is not connected. */
  127. Disconnected,
  128. /** We are attempting to connect to the server. It is not yet ready for use. */
  129. Connecting,
  130. /** We are logging in, dealing with capabilities, etc. The server is not yet ready for use. */
  131. Negotiating,
  132. /** We are connected and commands can be sent. */
  133. Ready,
  134. }