import java.io.IOException import java.math.BigInteger /** * This class represents a 9P connection. It provides a practical implementation with networking to the 9P methods * described in [NinePTranslator]. Details about methods related to 9P can be found in [NinePTranslator]. Remember to * disconnect using [disconnect] after use. * * Details about network-related topics can be found in [NetworkPacketTransporter] and the implementation of choice. * * Details about 9P messages and methods can be found in [NinePTranslator]. * * @param netPackTrans The networking API backend of choice. * * @throws UnresolvableHostException if the host resolution made by [netPackTrans] failed. */ class NinePConnection(netPackTrans: NetworkPacketTransporter) : NinePTranslator { /** * Networking API. */ val npt: NetworkPacketTransporter = netPackTrans /** * Tag generator. */ val tagGen = TagGenerator(TagGenerator.TagGenerationMethod.INCREMENTAL, 1u) /** * Maximum size for messages negotiated between the client and the server. */ var maxSize: UInt = 0u val NOTAG = 0.inv().toUShort() /** * Disconnect from the remote host, * * @throws IOException if an I/O error occurred while closing the socket. */ fun disconnect() { this.npt.close() } /** * Read an [nBytes]-long unsigned integer number from the connection. * * In 9P, binary numbers (non-textual) are specified in little-endian order (least significant byte first). * * @param nBytes The length of the integer number in bytes. It only works with unsigned numbers strictly greater * than zero (zero is excluded). * @return The number's value. * @throws IllegalArgumentException if [nBytes] is zero or negative. */ private fun readInteger(nBytes: Int): BigInteger { require(nBytes > 0) val bytes = this.npt.receiveFixed(nBytes.toULong()) var number: BigInteger = BigInteger.valueOf(0) for (i in 0.. { return Triple( readInteger(4).toInt().toUInt(), NinePMessageType.fromByte(readInteger(1).toByte()), readInteger(2).toInt().toUShort() ) } /** * Wait for a 9P message with the same tag from the server. * * All messages prior to the returned one are discarded. * * @param tag The tag to wait for. * @return A pair of (1) the size and (2) the type of the message that matches the given tag. */ private fun waitForTag(tag: UShort): Pair { var s = 0u var ty: NinePMessageType? = null var ta: UShort = 0u var found = false while (!found) { val stt = readSizeTypeTag() s = stt.first ty = stt.second ta = stt.third if (ta == tag) { found = true } } return Pair(s, ty ?: NinePMessageType.RERROR) } /** * Read a 9P message of type Rerror, after the message size and type have already been read. * * @return A pair of: (1) the message tag and (2) the error message */ private fun readError(): Pair { val tag = readInteger(2) val error = readString() return Pair(SizedMessageField(2, tag), error) } /** * Send 9P request and check for exceptions or a Rerror response. */ private fun responseError(msg: NinePMessage): String? { try { msg.write(this.npt) } catch (ex: Exception) { return "Exception thrown while sending message: (" + ex.javaClass.toString() + ") " + ex.message } val se = waitForTag(msg.tag) if (se.second == NinePMessageType.RERROR) { val error = readError() return error.second } return null } override fun version(msize: SizedMessageField, version: String): String? { val msg = NinePMessage(NinePMessageType.TVERSION, this.NOTAG, listOf("msize", "version"), mapOf( "msize" to msize ), mapOf( "version" to version ), this.maxSize ) val error = responseError(msg) if (error != null) { return error } val rmsize = readInteger(4) val rversion = readString() // this check should not be necessary, but you never know this.maxSize = ( if (rmsize < msize.value) msize.value else rmsize ).toInt().toUInt() if (rversion == "unknown") { return "Unknown version." } return null } override fun auth() { TODO("Not yet implemented") } override fun flush() { TODO("Not yet implemented") } override fun attach() { TODO("Not yet implemented") } override fun walk(path: String) { TODO("Not yet implemented") } override fun open(path: String) { TODO("Not yet implemented") } override fun create(path: String) { TODO("Not yet implemented") } override fun read(path: String) { TODO("Not yet implemented") } override fun write(path: String) { TODO("Not yet implemented") } override fun clunk(path: String) { TODO("Not yet implemented") } override fun remove(path: String) { TODO("Not yet implemented") } override fun stat(path: String) { TODO("Not yet implemented") } override fun wstat(path: String) { TODO("Not yet implemented") } }