summaryrefslogtreecommitdiff
path: root/src/main/kotlin
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/kotlin')
-rw-r--r--src/main/kotlin/Authenticator.kt18
-rw-r--r--src/main/kotlin/Authenticator9PAnyV2DP9IK.kt12
-rw-r--r--src/main/kotlin/Connection.kt321
-rw-r--r--src/main/kotlin/FileMode.kt32
-rw-r--r--src/main/kotlin/FilePermissions.kt92
-rw-r--r--src/main/kotlin/IPAddress.kt73
-rw-r--r--src/main/kotlin/NinePMacros.kt6
-rw-r--r--src/main/kotlin/NinePMessageType.kt39
-rw-r--r--src/main/kotlin/PathInfo.kt93
-rw-r--r--src/main/kotlin/ProtocolTranslator.kt167
-rw-r--r--src/main/kotlin/QID.kt105
-rw-r--r--src/main/kotlin/SizedInteger.kt36
-rw-r--r--src/main/kotlin/Stat.kt151
-rw-r--r--src/main/kotlin/TagGenerator.kt63
-rw-r--r--src/main/kotlin/Utils.kt38
-rw-r--r--src/main/kotlin/demo/Main.kt23
-rw-r--r--src/main/kotlin/except/FailedAuthenticationException.kt8
-rw-r--r--src/main/kotlin/except/InvalidMessageException.kt8
-rw-r--r--src/main/kotlin/except/MsizeValueTooBigException.kt10
-rw-r--r--src/main/kotlin/except/RErrorException.kt8
-rw-r--r--src/main/kotlin/except/UnaccessibleFileException.kt11
-rw-r--r--src/main/kotlin/except/UnknownVersionException.kt9
-rw-r--r--src/main/kotlin/except/UnresolvableHostException.kt8
-rw-r--r--src/main/kotlin/net/InMessage.kt167
-rw-r--r--src/main/kotlin/net/OutMessage.kt127
-rw-r--r--src/main/kotlin/net/TransportLayer.kt59
-rw-r--r--src/main/kotlin/net/TransportLayerJavaNet.kt95
27 files changed, 0 insertions, 1779 deletions
diff --git a/src/main/kotlin/Authenticator.kt b/src/main/kotlin/Authenticator.kt
deleted file mode 100644
index 932004f..0000000
--- a/src/main/kotlin/Authenticator.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
- * The Authenticator interface provides methods for authenticating a user over an established protocol connection.
- */
-interface Authenticator {
- /**
- * Authenticate a user identified by the given [username] and whose authenticity is confirmed by the given
- * [password]. The authentication protocol can read and write data within the underlying connection using [readFun]
- * and [writeFun].
- *
- * @param username The name the user goes by.
- * @param password The confirmation of the user's authenticity.
- * @param readFun A function to read incoming data from the underlying connection.
- * @param writeFun A function to write outgoing data into the underlying connection.
- * @throws except.FailedAuthenticationException if the authentication could not be performed. A human-readable
- * reason for the failure can be provided if necessary.
- */
- fun authenticate(username: String, password: String, readFun: () -> List<UByte>, writeFun: (b: List<UByte>) -> Unit)
-} \ No newline at end of file
diff --git a/src/main/kotlin/Authenticator9PAnyV2DP9IK.kt b/src/main/kotlin/Authenticator9PAnyV2DP9IK.kt
deleted file mode 100644
index 99c52a0..0000000
--- a/src/main/kotlin/Authenticator9PAnyV2DP9IK.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-/**
- * This class (with an ugly ass name) implements the authentication procedure for the p9any meta-protocol version 2,
- * hinting at the usage of dp9ik during negotiation and failing if it's unavailable.
- *
- * The 9P protocol does not provide a default authentication method. However, since NineKt must work with 9front's
- * default authenticated 9P service, it must implement the p9any meta-protocol, preferably version 2.
- */
-class Authenticator9PAnyV2DP9IK : Authenticator {
- override fun authenticate(username: String, password: String, readFun: () -> List<UByte>, writeFun: (List<UByte>) -> Unit) {
-
- }
-} \ No newline at end of file
diff --git a/src/main/kotlin/Connection.kt b/src/main/kotlin/Connection.kt
deleted file mode 100644
index f2cdd15..0000000
--- a/src/main/kotlin/Connection.kt
+++ /dev/null
@@ -1,321 +0,0 @@
-import except.MsizeValueTooBigException
-import except.RErrorException
-import except.UnaccessibleFileException
-import except.UnknownVersionException
-import net.InMessage
-import net.OutMessage
-import net.TransportLayer
-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 [ProtocolTranslator]. Details about methods related to 9P can be found in [ProtocolTranslator]. Remember
- * to disconnect using [disconnect] after use.
- *
- * Details about network-related topics can be found in [net.TransportLayer] and the implementation of choice.
- *
- * Details about 9P messages and methods can be found in [ProtocolTranslator].
- *
- * @param transLay The networking API backend of choice.
- * @throws except.UnresolvableHostException if the host resolution made by [transLay] failed.
- */
-class Connection(transLay: TransportLayer) : ProtocolTranslator {
- /**
- * Networking API.
- */
- private val tl: TransportLayer = transLay
-
- /**
- * Tag generator.
- */
- private val tagGen = TagGenerator(TagGenerator.TagGenerationMethod.INCREMENTAL, 1u)
-
- /**
- * Maximum size for messages negotiated between the client and the server.
- */
- private var maxSize: UInt = 0u
-
- // 9P constants.
- val DEFAULT_VERSION = "9P2000"
- val NOTAG = 0.toUShort().inv()
- val NOFID = 0.toUInt().inv()
-
-
- /**
- * Disconnect from the remote host,
- *
- * @throws IOException if an I/O error occurred while closing the socket.
- */
- fun disconnect() {
- this.tl.close()
- }
-
- /**
- * Handy function to create an [net.InMessage] instance and check for errors. After successfully using this
- * function, it is guaranteed that both no error occurred while reading the incoming message and the message is not
- * of type R-error.
- *
- * It uses [tl] and [maxSize] for instancing the [net.InMessage] class.
- *
- * @return A pair of: (1) a nullable string (which can be: `null` if no error occurred, empty if an error occurred
- * with no message, or non-empty with the error message) and (2) the optional [net.InMessage] instance (null if an
- * error occurred).
- * @throws except.InvalidMessageException if the received message is invalid.
- * @throws except.RErrorException if the received message is an R-error message.
- */
- private fun checkedInMessage(reqTag: UShort): InMessage {
- val imsg = InMessage(this.tl, this.maxSize, reqTag)
- if (imsg.type == NinePMessageType.RERROR) {
- imsg.applyField(InMessage.Field("ename", InMessage.Field.Type.STRING, 0u))
- throw RErrorException(imsg.fieldsStr["ename"])
- }
- return imsg
- }
-
- override fun version(msize: UInt, version: String) {
- val omsg = OutMessage(NinePMessageType.TVERSION, this.NOTAG, listOf("msize", "version"),
- mapOf(
- "msize" to Pair(BigInteger(msize.toString()), 4u)
- ),
- mapOf(
- "version" to version
- ),
- emptyMap(),
- this.maxSize
- )
- omsg.write(this.tl)
- val imsg = checkedInMessage(omsg.tag)
- imsg.applySchema(listOf(
- InMessage.Field("msize", InMessage.Field.Type.INTEGER, 4u),
- InMessage.Field("version", InMessage.Field.Type.STRING, 0u)
- ))
- val remoteMaxSize = imsg.fieldsInt["msize"]!!.toInt().toUInt()
- if (remoteMaxSize > this.maxSize) {
- throw MsizeValueTooBigException(msize, remoteMaxSize)
- }
- this.maxSize = remoteMaxSize
- if (!imsg.fieldsStr["version"]!!.startsWith("9P2000")) {
- throw UnknownVersionException(imsg.fieldsStr["version"]!!)
- }
- }
-
- override fun auth(afid: UInt, uname: String, aname: String) {
- // TODO: Leave this unimplemented until p9any and TLS are implemented
- TODO("Not yet implemented")
- }
-
- override fun flush(oldtag: UShort) {
- val omsg = OutMessage(NinePMessageType.TFLUSH, this.tagGen.generate(), listOf("oldtag"),
- mapOf(
- "oldtag" to Pair(BigInteger(oldtag.toString()), 2u)
- ),
- emptyMap(),
- emptyMap(),
- this.maxSize
- )
- omsg.write(this.tl)
- val imsg = checkedInMessage(omsg.tag)
- }
-
- override fun attach(fid: UInt, afid: UInt, uname: String, aname: String): QID {
- val omsg = OutMessage(NinePMessageType.TATTACH, this.tagGen.generate(), listOf("fid", "afid", "uname", "aname"),
- mapOf(
- "fid" to Pair(BigInteger(fid.toString()), 4u),
- "afid" to Pair(BigInteger(afid.toString()), 4u)
- ),
- mapOf(
- "uname" to uname,
- "aname" to aname
- ),
- emptyMap(),
- this.maxSize
- )
- omsg.write(this.tl)
- val imsg = checkedInMessage(omsg.tag)
-
- imsg.applyField(InMessage.Field("qid", InMessage.Field.Type.RAW, 13u))
- val qid = QID(imsg.fieldsRaw["qid"]!!.toList())
- return qid
- }
-
- override fun walk(fid: UInt, newfid: UInt, wname: List<String>): List<QID> {
- val nwname = wname.size
- val omsg = OutMessage(NinePMessageType.TWALK, this.tagGen.generate(), listOf("fid", "newfid", "nwname"),
- mapOf(
- "fid" to Pair(BigInteger(fid.toString()), 4u),
- "newfid" to Pair(BigInteger(newfid.toString()), 4u),
- "nwname" to Pair(BigInteger(nwname.toString()), 2u)
- ),
- emptyMap(),
- emptyMap(),
- this.maxSize
- )
- omsg.write(this.tl)
- for (wn in wname) { // write wname elements
- this.tl.transmit(OutMessage.convStringToBytes(wn))
- }
- val imsg = checkedInMessage(omsg.tag)
-
- imsg.applyField(InMessage.Field("nwqid", InMessage.Field.Type.INTEGER, 2u))
- val nwqid = imsg.fieldsInt["nwqid"]!!.toInt()
- if (nwqid < nwname) {
- throw UnaccessibleFileException(wname.slice(0..nwqid))
- }
- imsg.applyField(InMessage.Field("qids", InMessage.Field.Type.RAW, (nwqid * 13).toUInt()))
- val rawQids = imsg.fieldsRaw["qids"]!!
- val qids: MutableList<QID> = mutableListOf()
- for (i in 0..<nwqid/13) {
- val start = i * 13
- val end = ((i+1) * 13) - 1
- val rawQid = rawQids.slice(start..end)
- qids += QID(rawQid)
- }
- return qids.toList()
- }
-
- override fun open(fid: UInt, mode: FileMode): Pair<QID, UInt> {
- val omsg = OutMessage(NinePMessageType.TOPEN, this.tagGen.generate(), listOf("fid", "mode"),
- mapOf(
- "fid" to Pair(BigInteger(fid.toString()), 4u),
- "mode" to Pair(BigInteger(mode.toModeByte().toString()), 1u)
- ),
- emptyMap(),
- emptyMap(),
- this.maxSize
- )
- omsg.write(this.tl)
- val imsg = checkedInMessage(omsg.tag)
- imsg.applySchema(listOf(
- InMessage.Field("qid", InMessage.Field.Type.RAW, 13u),
- InMessage.Field("iounit", InMessage.Field.Type.INTEGER, 4u)
- ))
- val qid = QID(imsg.fieldsRaw["qid"]!!.toList())
- val iounit = imsg.fieldsInt["iounit"]!!.toInt().toUInt()
- return Pair(qid, iounit)
- }
-
- override fun create(fid: UInt, name: String, perm: FilePermissions, mode: FileMode): Pair<QID, UInt> {
- val omsg = OutMessage(NinePMessageType.TCREATE, this.tagGen.generate(), listOf("fid", "name", "perm", "mode"),
- mapOf(
- "fid" to Pair(BigInteger(fid.toString()), 4u),
- "perm" to Pair(BigInteger(perm.toPermissionInt().toString()), 4u),
- "mode" to Pair(BigInteger(mode.toModeByte().toString()), 1u)
- ),
- mapOf(
- "name" to name
- ),
- emptyMap(),
- this.maxSize
- )
- omsg.write(this.tl)
- val imsg = checkedInMessage(omsg.tag)
- imsg.applySchema(listOf(
- InMessage.Field("qid", InMessage.Field.Type.RAW, 13u),
- InMessage.Field("iounit", InMessage.Field.Type.INTEGER, 4u)
- ))
- val qid = QID(imsg.fieldsRaw["qid"]!!.toList())
- val iounit = imsg.fieldsInt["iounit"]!!.toInt().toUInt()
- return Pair(qid, iounit)
- }
-
- override fun read(fid: UInt, offset: ULong, count: UInt): Array<UByte> {
- val omsg = OutMessage(NinePMessageType.TREAD, this.tagGen.generate(), listOf("fid", "offset", "count"),
- mapOf(
- "fid" to Pair(BigInteger(fid.toString()), 4u),
- "offset" to Pair(BigInteger(offset.toString()), 8u),
- "count" to Pair(BigInteger(count.toString()), 4u)
- ),
- emptyMap(),
- emptyMap(),
- this.maxSize
- )
- omsg.write(this.tl)
- val imsg = checkedInMessage(omsg.tag)
- imsg.applyField(InMessage.Field("count", InMessage.Field.Type.INTEGER, 4u))
- val count = imsg.fieldsInt["count"]!!.toInt().toUInt()
- imsg.applyField(InMessage.Field("data", InMessage.Field.Type.RAW, count))
-
- return imsg.fieldsRaw["data"]!!
- }
-
- override fun write(fid: UInt, offset: ULong, count: UInt, data: Iterable<UByte>): UInt {
- val data = data.take(count.toInt()).toTypedArray()
- val omsg = OutMessage(NinePMessageType.TWRITE, this.tagGen.generate(), listOf("offset", "count", "data"),
- mapOf(
- "offset" to Pair(BigInteger(offset.toString()), 8u),
- "count" to Pair(BigInteger(count.toString()), 4u)
- ),
- emptyMap(),
- mapOf(
- "data" to data.toList()
- ),
- this.maxSize
- )
- omsg.write(this.tl)
- val imsg = checkedInMessage(omsg.tag)
- imsg.applyField(InMessage.Field("count", InMessage.Field.Type.INTEGER, 4u))
- val count = imsg.fieldsInt["count"]!!
-
- return count.toInt().toUInt()
- }
-
- override fun clunk(fid: UInt) {
- val omsg = OutMessage(NinePMessageType.TCLUNK, this.tagGen.generate(), listOf("fid"),
- mapOf(
- "fid" to Pair(BigInteger(fid.toString()), 4u)
- ),
- emptyMap(),
- emptyMap(),
- this.maxSize
- )
- omsg.write(this.tl)
- val imsg = checkedInMessage(omsg.tag)
- }
-
- override fun remove(fid: UInt) {
- val omsg = OutMessage(NinePMessageType.TREMOVE, this.tagGen.generate(), listOf("fid"),
- mapOf(
- "fid" to Pair(BigInteger(fid.toString()), 4u)
- ),
- emptyMap(),
- emptyMap(),
- this.maxSize
- )
- omsg.write(this.tl)
- val imsg = checkedInMessage(omsg.tag)
- }
-
- override fun stat(fid: UInt): Stat {
- val omsg = OutMessage(NinePMessageType.TSTAT, this.tagGen.generate(), listOf("fid"),
- mapOf(
- "fid" to Pair(BigInteger(fid.toString()), 4u)
- ),
- emptyMap(),
- emptyMap(),
- this.maxSize
- )
- omsg.write(this.tl)
- val imsg = checkedInMessage(omsg.tag)
- imsg.applyField(InMessage.Field("nstat", InMessage.Field.Type.INTEGER, 2u))
- val nstat = imsg.fieldsInt["nstat"]!!.toInt().toUInt()
- imsg.applyField(InMessage.Field("stat", InMessage.Field.Type.RAW, nstat))
- val rawStat = imsg.fieldsRaw["stat"]!!.toList()
- return Stat(fid, rawStat)
- }
-
- override fun wstat(fid: UInt, stat: Stat) {
- val omsg = OutMessage(NinePMessageType.TWSTAT, this.tagGen.generate(), listOf("fid", "stat"),
- mapOf(
- "fid" to Pair(BigInteger(fid.toString()), 4u)
- ),
- emptyMap(),
- mapOf(
- "stat" to stat.toRaw()
- ),
- this.maxSize
- )
- omsg.write(this.tl)
- val imsg = checkedInMessage(omsg.tag)
- }
-} \ No newline at end of file
diff --git a/src/main/kotlin/FileMode.kt b/src/main/kotlin/FileMode.kt
deleted file mode 100644
index 81712e6..0000000
--- a/src/main/kotlin/FileMode.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * The mode in which to open or create a file. For directories, it's illegal for directories to be written, truncated,
- * or removed on close.
- *
- * @param mode The actual mode, as described by the [Mode] enum class.
- * @param truncate Set or unset the truncation bit. It requires the write permission.
- * @param removeClose Set or unset the "remove on close" bit. It requires the remove permission in the parent directory.
- */
-data class FileMode(val mode: Mode, val truncate: Boolean, val removeClose: Boolean) {
- enum class Mode(val value: UByte) {
- READ(0u),
- WRITE(1u),
- READ_WRITE(2u),
- EXECUTE(3u)
- }
-
- /**
- * Turn the mode described by the [FileMode] fields into a mode byte.
- */
- fun toModeByte(): UByte {
- var byte: UByte = 0u
- byte = byte.or(this.mode.value)
- if (truncate) {
- byte = byte.or(0x10u)
- }
- if (removeClose) {
- byte = byte.or(0x40u)
- }
-
- return byte
- }
-} \ No newline at end of file
diff --git a/src/main/kotlin/FilePermissions.kt b/src/main/kotlin/FilePermissions.kt
deleted file mode 100644
index 84a7944..0000000
--- a/src/main/kotlin/FilePermissions.kt
+++ /dev/null
@@ -1,92 +0,0 @@
-/**
- * The permissions of a newly created file.
- */
-class FilePermissions {
-
- /**
- * The permissions for the file's owning user.
- */
- val userPerms: Permissions
-
- /**
- * The permissions for the file's owning group.
- */
- val groupPerms: Permissions
-
- /**
- * The permissions for everyone else.
- */
- val elsePerms: Permissions
-
- /**
- * Is the file a directory? If not, it's a regular file.
- */
- val isDirectory: Boolean
-
- private val DMDIR: UInt = 0x80000000u
-
- /**
- * Constructor for file permissions with separate fields.
- *
- * @param userPerms The permissions for the file's owning user.
- * @param groupPerms The permissions for the file's owning group.
- * @param elsePerms The permissions for everyone else.
- * @param isDirectory Is the file a directory? If not, it's a regular file.
- */
- constructor(userPerms: Permissions, groupPerms: Permissions, elsePerms: Permissions, isDirectory: Boolean) {
- this.userPerms = userPerms
- this.groupPerms = groupPerms
- this.elsePerms = elsePerms
- this.isDirectory = isDirectory
- }
-
- /**
- * Constructor for raw file permission data. Only the first 4 elements are read.
- *
- * @param raw The raw file permission data.
- * @throws IllegalArgumentException if [raw] does not have at least 4 elements.
- */
- constructor(raw: List<UByte>) {
- require(raw.size >= 4)
- val raw = raw.slice(0..4)
- val dirValue = raw[0].toUInt().xor(this.DMDIR)
- this.isDirectory = dirValue > 0u
- this.userPerms = Permissions.fromByte(raw[1])
- this.groupPerms = Permissions.fromByte(raw[2])
- this.elsePerms = Permissions.fromByte(raw[3])
- }
-
- enum class Permissions(val bits: UByte) {
- READ(0x4u),
- WRITE(0x2u),
- EXECUTE(0x1u),
- READ_WRITE(READ.bits.or(WRITE.bits)),
- READ_EXECUTE(READ.bits.or(EXECUTE.bits)),
- WRITE_EXECUTE(WRITE.bits.or(EXECUTE.bits)),
- READ_WRITE_EXECUTE(READ.bits.or(WRITE.bits.or(EXECUTE.bits)));
-
- companion object {
- /**
- * Obtain a [Permissions] instance by matching its value.
- *
- * @throws NoSuchElementException if no such element has the provided value.
- */
- fun fromByte(bits: UByte) = Permissions.entries.first { it.bits == bits }
- }
- }
-
- /**
- * Turn the permissions described by the [FilePermissions] fields into a permission integer (4 bytes).
- */
- fun toPermissionInt(): UInt {
- val permFileds = listOf(userPerms, groupPerms, elsePerms)
- val perms: UInt = 0u
- for (i in 0..permFileds.size) {
- perms.or(permFileds[i].bits.toUInt().shl(8 * (permFileds.size - 1 - i)))
- }
- if (isDirectory) {
- perms.or(this.DMDIR)
- }
- return perms
- }
-} \ No newline at end of file
diff --git a/src/main/kotlin/IPAddress.kt b/src/main/kotlin/IPAddress.kt
deleted file mode 100644
index 8eb7414..0000000
--- a/src/main/kotlin/IPAddress.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-/**
- * An IP address (v4 or v6).
- */
-class IPAddress {
- private var address: Array<UByte>
- private var is4: Boolean
-
- /**
- * @throws NumberFormatException if the IP address follows neither the IPv4 syntax, nor the IPv6 syntax.
- */
- constructor(address: String) {
- if (isAddressV4(address)) {
- this.is4 = true
- } else if (isAddressV6(address)) {
- this.is4 = false
- } else {
- throw NumberFormatException()
- }
-
- val split4 = address.split('.')
- val split6 = address.split(':')
- val bytes: List<UByte>
- if (this.is4) {
- bytes = split4.map { it.toUByte(10) }
- } else {
- val shorts = split6.map { it.toShort(16) }
- bytes = shorts.flatMap {
- listOf(
- it.toInt().and(0xFF00).shr(0x08).toUByte(),
- it.toInt().and(0x00FF).toUByte()
- )
- }
- }
- this.address = bytes.toTypedArray()
- }
-
- private fun isAddressV4(address: String): Boolean {
- val split4 = address.split('.')
- var isOK = split4.size == 4
- isOK = isOK && split4.size == split4.filter { it.matches(Regex("^[0-9]+$")) }.size
- return isOK
- }
-
- private fun isAddressV6(address: String): Boolean {
- val split6 = address.split(':')
- var isOK = split6.size == 8
- isOK = isOK && split6.size == split6.filter { it.contains(Regex("^[0-9][a-f]+$")) }.size
- if (isOK) {
- return true
- }
-
- // try with "::"
- val split6Double = address.split("::")
- if (split6Double.size != 2) {
- return false
- }
- val omitted = 8 - split6Double[0].split(':').size - split6Double[1].split(':').size
- if (omitted < 0) {
- return false
- }
- return true
- }
-
- override fun toString(): String {
- val str: String
- if (this.is4) {
- str = this.address.joinToString(separator = ".") { it.toString() }
- } else {
- str = this.address.joinToString(separator = ":") { it.toString() }
- }
- return str
- }
-} \ No newline at end of file
diff --git a/src/main/kotlin/NinePMacros.kt b/src/main/kotlin/NinePMacros.kt
deleted file mode 100644
index 738732b..0000000
--- a/src/main/kotlin/NinePMacros.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-/**
- * Handy function that initializes the 9P connection.
- */
-fun initNineP(npconn: NinePConnection) {
- // TODO: implement
-} \ No newline at end of file
diff --git a/src/main/kotlin/NinePMessageType.kt b/src/main/kotlin/NinePMessageType.kt
deleted file mode 100644
index 4c8c60f..0000000
--- a/src/main/kotlin/NinePMessageType.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-enum class NinePMessageType(val value: UByte) {
- TVERSION(100u),
- RVERSION(101u),
- TAUTH(102u),
- RAUTH(103u),
- TATTACH(104u),
- RATTACH(105u),
- //TERROR(106), <--- illegal
- RERROR(107u),
- TFLUSH(108u),
- RFLUSH(109u),
- TWALK(110u),
- RWALK(111u),
- TOPEN(112u),
- ROPEN(113u),
- TCREATE(114u),
- RCREATE(115u),
- TREAD(116u),
- RREAD(117u),
- TWRITE(118u),
- RWRITE(119u),
- TCLUNK(120u),
- RCLUNK(121u),
- TREMOVE(122u),
- RREMOVE(123u),
- TSTAT(124u),
- RSTAT(125u),
- TWSTAT(126u),
- RWSTAT(127u);
-
- companion object {
- /**
- * Obtain a [NinePMessageType] instance by matching its value.
- *
- * @throws NoSuchElementException if no such element has the provided value.
- */
- fun fromByte(value: UByte) = NinePMessageType.entries.first { it.value == value }
- }
-} \ No newline at end of file
diff --git a/src/main/kotlin/PathInfo.kt b/src/main/kotlin/PathInfo.kt
deleted file mode 100644
index 2aee406..0000000
--- a/src/main/kotlin/PathInfo.kt
+++ /dev/null
@@ -1,93 +0,0 @@
-/**
- * This class holds all info about paths, their FIDs, and their QIDs.
- */
-class PathInfo() {
- private val paths: MutableSet<Path> = mutableSetOf()
-
- /**
- * Information about a path. Each FID maps to one path but not all paths have FIDs (injective). On the other hand,
- * one QID can be shared with different paths and FIDs.
- *
- * FIDs are optional because some R-messages only return QIDs for certain files (e.g. walk).
- *
- * @param path The path.
- * @param fid The optional FID associated with the path.
- * @param qid The QID associated with the path.
- */
- data class Path(val path: List<String>, val fid: UInt?, val qid: QID)
-
- /**
- * Add a path.
- */
- fun addPath(path: Path) {
- this.paths.add(path)
- }
-
- /**
- * Add a QID to an existing FID. Do nothing if the given FID does not exist.
- */
- fun addQIDToFID(fid: UInt, qid: QID) {
- findByFID(fid)?.qid = qid
- }
-
- /**
- * Remove a FID.
- */
- fun removeFID(fid: UInt) {
- this.paths.removeIf { x -> x.fid == fid }
- }
-
- fun find(predicate: (Path) -> Boolean) = this.paths.find(predicate)
-
- /**
- * Find [Path] object by path.
- *
- * @param path The path to search for.
- * @return A path object if [path] exists, or null otherwise.
- */
- fun findByPath(path: List<String>): Path? {
- return this.paths.find { x -> x.path == path }
- }
-
- /**
- * Find [Path] object by FID.
- *
- * @param fid The FID to search for.
- * @return A path object if [fid] exists, or null otherwise.
- */
- fun findByFID(fid: UInt): Path? {
- return this.paths.find { x -> x.fid == fid }
- }
-
- /**
- * Find [Path] object by QID.
- *
- * @param qid The path to search for.
- * @return A path object if [qid] exists, or null otherwise.
- */
- fun findByQID(qid: QID): Path? {
- return this.paths.find { x -> x.qid == qid }
- }
-
- /**
- * Retrieve all paths
- */
- fun getAllPaths(): Set<Path> {
- return this.paths.toSet()
- }
-
- /**
- * Generate a new FID which is not already in use.
- *
- * @return The new FID.
- * @throws IllegalStateException if there is no available FID.
- */
- fun genFID(): UInt {
- for (newFid in 0u..UInt.MAX_VALUE) {
- if (findByFID(newFid) == null) {
- return newFid
- }
- }
- throw IllegalStateException()
- }
-} \ No newline at end of file
diff --git a/src/main/kotlin/ProtocolTranslator.kt b/src/main/kotlin/ProtocolTranslator.kt
deleted file mode 100644
index fee52f4..0000000
--- a/src/main/kotlin/ProtocolTranslator.kt
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
-TODO:
- - add arguments to methods
- - switch from returned strings to exceptions
-*/
-
-/**
- * The [ProtocolTranslator] interface provides methods that coincide 1:1 with each request type in the 9P protocol.
- * Every method that can fail, that is, every request that can receive a response with `Rerror` type instead of the same
- * type as itself, returns a non-null `String` that contains the error message received in the response.
- *
- * Tags and the `msize` value are supposed to be managed internally by the implementing class.
- *
- * When compared to 9P's formal message descriptions, like those which can be read in Plan 9's manual pages, some of the
- * methods might lack parameters. Those which can be inferred from the existing parameters are purposefully omitted. An
- * example is [walk], which omits `nwname` because it can be obtained by calculating the size of `wname`.
- *
- * Trivia: comments for each method are taken from each message type's manual page in section 5.
- */
-interface ProtocolTranslator {
- /**
- * Negotiate protocol version.
- *
- * This must be the first message sent on the 9P connection and no other requests can be issued until a response has
- * been received.
- *
- * @param msize The maximum length, in bytes, that the client will ever generate or expect to receive in a single
- * 9P message.
- * @param version Should be "9P2000", which is the only defined value.
- * @throws except.InvalidMessageException if the received message is invalid.
- * @throws except.RErrorException if the received message is an R-error message.
- * @throws except.MsizeValueTooBigException if the received `msize` value is bigger than what the client requested.
- * @throws except.UnknownVersionException if the version negotiation failed.
- */
- fun version(msize: UInt, version: String)
-
- /**
- * Perform authentication.
- */
- fun auth(afid: UInt, uname: String, aname: String)
-
- /**
- * Abort a message.
- *
- * @param oldtag The tag of the message that needs to be purged.
- * @throws except.InvalidMessageException if the received message is invalid.
- * @throws except.RErrorException if the received message is an R-error message.
- */
- fun flush(oldtag: UShort)
-
- /**
- * Establish a connection.
- *
- * @param fid FID that represents the root directory of the desired file tree.
- * @param afid A FID previously established by an auth message.
- * @param uname A user identifier.
- * @param aname The desired file tree to access.
- * @return The QID of the file tree's root.
- * @throws except.InvalidMessageException if the received message is invalid.
- * @throws except.RErrorException if the received message is an R-error message.
- */
- fun attach(fid: UInt, afid: UInt, uname: String, aname: String): QID
-
- /**
- * Descend a directory hierarchy.
- *
- * @param fid The existing FID. It must represent a directory.
- * @param newfid The proposed FID, which is going to be associated with the result of walking the hierarchy. It must
- * be not in use, unless it is the same as [fid].
- * @param wname The successive path name elements which describe the file to walk to.
- * @return The QID of each path element that has been walked through.
- * @throws except.InvalidMessageException if the received message is invalid.
- * @throws except.RErrorException if the received message is an R-error message.
- * @throws except.UnaccessibleFileException if [wname] contains one or more path elements that do not exist.
- */
- fun walk(fid: UInt, newfid: UInt, wname: List<String>): List<QID>
-
- /**
- * Prepare an FID for I/O on an existing file.
- *
- * @param fid The FID of the file to open.
- * @param mode The mode in which the file is opened.
- * @return A pair of: (1) the returned QID, and (2) a value called `iounit` that indicates, if non-zero, the maximum
- * number of bytes that are guaranteed to be read from or written to the file without breaking the I/O transfer into
- * multiple 9P messages.
- * @throws except.InvalidMessageException if the received message is invalid.
- * @throws except.RErrorException if the received message is an R-error message.
- */
- fun open(fid: UInt, mode: FileMode): Pair<QID, UInt>
-
- /**
- * Prepare an FID for I/O on a new file.
- *
- * @param fid The FID of the directory that is going to contain the file. The specified directory requires write
- * permission.
- * @param name The file name.
- * @param perm The permissions of the new file.
- * @param mode The open mode after successful creation.
- * @return A pair of: (1) the QID of the newly created file, and (2) a value called `iounit` that indicates, if
- * non-zero, the maximum number of bytes that are guaranteed to be read from or written to the file without breaking
- * the I/O transfer into multiple 9P messages.
- * @throws except.InvalidMessageException if the received message is invalid.
- * @throws except.RErrorException if the received message is an R-error message.
- */
- fun create(fid: UInt, name: String, perm: FilePermissions, mode: FileMode): Pair<QID, UInt>
-
- /**
- * Transfer data from file. Due to the negotiated maximum size of 9P messages, called `msize`, one is supposed to
- * call this method multiple times, unless the content is smaller than `msize`.
- *
- * @return The content read with the call just made.
- * @throws except.InvalidMessageException if the received message is invalid.
- * @throws except.RErrorException if the received message is an R-error message.
- */
- fun read(fid: UInt, offset: ULong, count: UInt): Array<UByte>
-
- /**
- * Transfer data to file. Due to the negotiated maximum size of 9P messages, called `msize`, this method is supposed
- * to be called multiple times, unless the content is smaller than `msize`.
- *
- * @param fid The FID to write to.
- * @param offset The distance between the beginning of the file and the first written byte.
- * @param data The raw bytes that are going to be written.
- * @return The amount of bytes written with the call just made.
- * @throws except.InvalidMessageException if the received message is invalid.
- * @throws except.RErrorException if the received message is an R-error message.
- */
- fun write(fid: UInt, offset: ULong, count: UInt, data: Iterable<UByte>): UInt
-
- /**
- * Forget about a FID.
- *
- * @param fid The FID to forget.
- * @throws except.InvalidMessageException if the received message is invalid.
- * @throws except.RErrorException if the received message is an R-error message.
- */
- fun clunk(fid: UInt)
-
- /**
- * Remove a file from a server.
- *
- * @param fid The FID of the file to remove.
- * @throws except.InvalidMessageException if the received message is invalid.
- * @throws except.RErrorException if the received message is an R-error message.
- */
- fun remove(fid: UInt)
-
- /**
- * Inquire file attributes.
- *
- * @param fid The FID of the file to inquire.
- * @return All the file attributes of the file associated with [fid].
- * @throws except.InvalidMessageException if the received message is invalid.
- * @throws except.RErrorException if the received message is an R-error message.
- */
- fun stat(fid: UInt): Stat
-
- /**
- * Change file attributes.
- *
- * @param fid The FID of the file to set attributes of.
- * @param stat The attributes to set.
- * @throws except.InvalidMessageException if the received message is invalid.
- * @throws except.RErrorException if the received message is an R-error message.
- */
- fun wstat(fid: UInt, stat: Stat)
-} \ No newline at end of file
diff --git a/src/main/kotlin/QID.kt b/src/main/kotlin/QID.kt
deleted file mode 100644
index 4d0bd6a..0000000
--- a/src/main/kotlin/QID.kt
+++ /dev/null
@@ -1,105 +0,0 @@
-import net.InMessage
-import net.OutMessage
-import java.math.BigInteger
-
-/**
- * This class holds information about a single QID.
- */
-class QID {
- /**
- * Does the QID represent a directory?
- */
- val isDirectory get() = getIsDirectory()
-
- /**
- * Does the QID represent an append-only file?
- */
- val isAppendOnly get() = getIsAppendOnly()
-
- /**
- * Does the QID represent an exclusive-use file?
- */
- val isExclusive get() = getIsExclusive()
-
- /**
- * Does the QID represent a temporary file?
- */
- val isTemporary get() = getIsTemporary()
-
- /**
- * The QID type.
- */
- val type: UByte
-
- /**
- * The QID file version.
- */
- val version: UInt
-
- /**
- * The QID path.
- */
- val path: ULong
-
- /**
- * Constructor for a QID with separate fields. See the `stat(5)` manual page for more information about [type],
- * [version], and [path].
- *
- * @param type The QID type.
- * @param version The QID file version.
- * @param path The QID path.
- */
- constructor(type: UByte, version: UInt, path: ULong) {
- this.type = type
- this.version = version
- this.path = path
- }
-
- /**
- * Constructor for raw QID data. Only the first 13 elements are read.
- *
- * @param raw The raw QID data.
- * @throws IllegalArgumentException if [raw] does not have at least 13 elements.
- */
- constructor(raw: List<UByte>) {
- require(raw.size >= 13)
- this.type = raw.first()
- val rawVersion = raw.slice(1..4)
- val rawPath = raw.slice(5..12)
- this.version = InMessage.convInteger(rawVersion, 0, rawVersion.size).toInt().toUInt()
- this.path = InMessage.convInteger(rawPath, 0, rawPath.size).toLong().toULong()
- }
-
- /**
- * Check bit values in [type]. In case of multiple bits, the method returns true if at least one of them is 1.
- *
- * @param bits A byte whose bits set to 1 are checked.
- */
- private fun checkTypeBits(bits: UByte): Boolean {
- return this.type.and(bits) != 0u.toUByte()
- }
-
- private fun getIsDirectory(): Boolean {
- return checkTypeBits(0x80u)
- }
-
- private fun getIsAppendOnly(): Boolean {
- return checkTypeBits(0x40u)
- }
-
- private fun getIsExclusive(): Boolean {
- return checkTypeBits(0x20u)
- }
-
- private fun getIsTemporary(): Boolean {
- return checkTypeBits(0x04u)
- }
-
- fun toRaw(): List<UByte> {
- var bytes: List<UByte> = emptyList()
- bytes += this.type
- bytes += OutMessage.convIntegerToBytes(BigInteger(this.version.toString()), 4u)
- bytes += OutMessage.convIntegerToBytes(BigInteger(this.path.toString()), 8u)
- return bytes
- }
-} \ No newline at end of file
diff --git a/src/main/kotlin/SizedInteger.kt b/src/main/kotlin/SizedInteger.kt
deleted file mode 100644
index 48e4e25..0000000
--- a/src/main/kotlin/SizedInteger.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-import java.math.BigInteger
-
-/**
- * [SizedInteger] represents an unsigned integer number of arbitrary yet fixed length. It's useful for storing integers
- * got from a connection as message fields.
- *
- * @param size The size of the field, measured in bytes.
- * @param value The value of the field.
- *
- * @throws IllegalArgumentException if the size required to store the provided value is bigger than the provided size.
- */
-class SizedInteger(val size: ULong, value: BigInteger) {
- init {
- if (!this.sizeOk(size, value)) {
- throw IllegalArgumentException()
- }
- }
-
- constructor(size: ULong) : this(size, 0.toBigInteger())
-
- /**
- * @throws IllegalStateException on set if the declared size is not enough for the new value.
- */
- var value = value
- set(value) {
- if (!this.sizeOk(value)) {
- throw IllegalStateException()
- }
- }
-
- private fun sizeOk(size: ULong, value: BigInteger): Boolean {
- val requiredSize = value.toByteArray().size.toUInt()
- return requiredSize <= size
- }
- private fun sizeOk(value: BigInteger): Boolean = sizeOk(this.size, value)
-} \ No newline at end of file
diff --git a/src/main/kotlin/Stat.kt b/src/main/kotlin/Stat.kt
deleted file mode 100644
index 20692c5..0000000
--- a/src/main/kotlin/Stat.kt
+++ /dev/null
@@ -1,151 +0,0 @@
-import net.InMessage
-import net.OutMessage
-import java.math.BigInteger
-
-// TODO: add time conversion methods
-/**
- * File attributes. The `type` and `dev` attributes are ignored since they are for kernel use only. Time is measured in
- * seconds, since the epoch (Jan 1 00:00 1970 GMT).
- */
-class Stat {
- /**
- * The FID sent the T-stat message.
- */
- val fid: UInt
-
- /**
- * The QID of the file.
- */
- val qid: QID
-
- /**
- * Permissions and flags.
- */
- val mode: FilePermissions
-
- /**
- * Last acces time.
- */
- val atime: UInt
-
- /**
- * Last modification time.
- */
- val mtime: UInt
-
- /**
- * The length of the file in bytes.
- */
- val length: ULong
-
- /**
- * The file name, which is `/` if the file is the root directory.
- */
- val name: String
-
- /**
- * The owner's name.
- */
- val uid: String
-
- /**
- * The group's name.
- */
- val gid: String
-
- /**
- * The name of the user who last modified the file.
- */
- val muid: String
-
- /**
- * Make an instance of [Stat] from each of its fields.
- *
- * @param fid The FID sent the T-stat message.
- * @param qid The QID of the file.
- * @param mode Permissions and flags.
- * @param atime Last acces time.
- * @param mtime Last modification time.
- * @param length The length of the file in bytes.
- * @param name The file name, which is `/` if the file is the root directory.
- * @param uid The owner's name.
- * @param gid The group's name.
- * @param muid The name of the user who last modified the file.
- */
- constructor(fid: UInt, qid: QID, mode: FilePermissions, atime: UInt, mtime: UInt, length: ULong, name: String, uid: String, gid: String, muid: String) {
- this.fid = fid
- this.qid = qid
- this.mode = mode
- this.atime = atime
- this.mtime = mtime
- this.length = length
- this.name = name
- this.uid = uid
- this.gid = gid
- this.muid = muid
- }
-
- /**
- * Make an instance of [Stat] from raw data.
- *
- * @param fid The FID of the file associated with the stat instance.
- * @param raw The raw stat data.
- */
- constructor(fid: UInt, raw: List<UByte>) {
- var offset = 0
- val qid = QID(raw.slice(0..<13))
- offset += 13
- val mode = FilePermissions(raw.slice(offset+0..<offset+4))
- offset += 4
-
- val intFielSizes = listOf(4, 4, 8)
- val intFields: MutableList<BigInteger> = mutableListOf()
- for (size in intFielSizes) {
- intFields.add(InMessage.convInteger(raw, offset, size))
- offset += size
- }
- val atime = intFields[0].toInt().toUInt()
- val mtime = intFields[1].toInt().toUInt()
- val length = intFields[2].toLong().toULong()
-
- val strAmount = 4
- val strFields: MutableList<String> = mutableListOf()
- for (i in 0..strAmount) {
- val str = InMessage.convString(raw, offset)
- strFields.add(str)
- offset += str.length
- }
- val name = strFields[0]
- val uid = strFields[1]
- val gid = strFields[2]
- val muid = strFields[3]
-
- this.fid = fid
- this.qid = qid
- this.mode = mode
- this.atime = atime
- this.mtime = mtime
- this.length = length
- this.name = name
- this.uid = uid
- this.gid = gid
- this.muid = muid
- }
-
- /**
- * Turn a [Stat] instance into raw data. This leaves out the [fid] field.
- */
- fun toRaw(): List<UByte> {
- var bytes: List<UByte> = emptyList()
- bytes += this.qid.toRaw()
- bytes += OutMessage.convIntegerToBytes(BigInteger(this.mode.toPermissionInt().toString()), 4u)
- bytes += OutMessage.convIntegerToBytes(BigInteger(this.atime.toString()), 4u)
- bytes += OutMessage.convIntegerToBytes(BigInteger(this.mtime.toString()), 4u)
- bytes += OutMessage.convIntegerToBytes(BigInteger(this.length.toString()), 8u)
- bytes += OutMessage.convStringToBytes(this.name)
- bytes += OutMessage.convStringToBytes(this.uid)
- bytes += OutMessage.convStringToBytes(this.gid)
- bytes += OutMessage.convStringToBytes(this.muid)
- return bytes
- }
-} \ No newline at end of file
diff --git a/src/main/kotlin/TagGenerator.kt b/src/main/kotlin/TagGenerator.kt
deleted file mode 100644
index c386b17..0000000
--- a/src/main/kotlin/TagGenerator.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-import kotlin.random.Random
-
-/**
- * Generate tags for 9P messages.
- */
-class TagGenerator(val method: TagGenerationMethod, val initial: UShort) {
-
- private var current: UShort = initial
- private val rng: Random = Random(initial.toInt())
- private val randomReturned: Set<UShort> = emptySet()
-
- private val generationFunctions: Map<TagGenerationMethod, () -> UShort> = mapOf(
- TagGenerationMethod.INCREMENTAL to this::generateIncremental,
- TagGenerationMethod.RANDOM to this::generateRandom,
- TagGenerationMethod.RANDOM_CHECKED to this::generateRandomChecked
- )
-
- /**
- * How are tags generated?
- */
- enum class TagGenerationMethod {
- /**
- * Return the initial value on the first generation. Increment the value on each generation.
- */
- INCREMENTAL,
-
- /**
- * Use the initial value as a seed and generate random values from it.
- */
- RANDOM,
-
- /**
- * Same as [RANDOM], but checks are added to avoid generating the same value twice.
- */
- RANDOM_CHECKED,
- }
-
- /**
- * Generate a new tag.
- */
- fun generate(): UShort {
- return this.generationFunctions.getValue(method).invoke()
- }
-
- private fun generateIncremental(): UShort {
- val tmp = this.current
- this.current++
- return tmp
- }
-
- private fun generateRandom(): UShort {
- return this.rng.nextBits(16).toUShort()
- }
-
- private fun generateRandomChecked(): UShort {
- var v: UShort
- do {
- v = generateRandom()
- } while (v in randomReturned)
- randomReturned.plus(v)
- return v
- }
-} \ No newline at end of file
diff --git a/src/main/kotlin/Utils.kt b/src/main/kotlin/Utils.kt
deleted file mode 100644
index 45185de..0000000
--- a/src/main/kotlin/Utils.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-const val DEFAULT_9P_PORT: UShort = 564u
-const val DEFAULT_9P_TLS_PORT: UShort = 17020u
-
-/**
- * The [nineAddressToValues] function translates an address from one of the dial(2) formats into the respective data.
- * For the sake of my sanity, the following list highlights differences from the typical dial(2) formats:
- * 1. The "network" part is ignored.
- * 2. If nothing is specified in the place of "service", the default unencrypted 9P port is used: 564.
- * 3. "Service" can only contain port numbers.
- * All the rest is the same.
- *
- * @return A Pair (A, P) containing the host address (A) and the port (P).
- *
- * @throws IllegalArgumentException If the address is improperly formatted or has invalid values.
- * @throws NumberFormatException If the port's value can't be parsed into an [UShort].
- */
-fun nineAddressToValues(nineAddress: String): Pair<String, UShort> {
- val parts = nineAddress.split('!', limit = 3)
- val address: String
- val port: UShort
- when (parts.size) {
- 1 -> {
- address = parts[0]
- port = DEFAULT_9P_PORT
- }
- 2 -> {
- address = parts[1]
- port = DEFAULT_9P_PORT
- }
- 3 -> {
- address = parts[1]
- port = parts[2].toUShort()
- }
- else -> throw IllegalArgumentException()
- }
-
- return Pair(address, port)
-} \ No newline at end of file
diff --git a/src/main/kotlin/demo/Main.kt b/src/main/kotlin/demo/Main.kt
deleted file mode 100644
index 640ec25..0000000
--- a/src/main/kotlin/demo/Main.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package demo
-
-import NetworkPacketTransporterJavaNet
-import NinePConnection
-import UnresolvableHostException
-import kotlin.system.exitProcess
-
-fun main(args: Array<String>) {
- val myUsername = ""
- val myPassword = ""
- val npconn: NinePConnection
-
- try {
- npconn = NinePConnection(NetworkPacketTransporterJavaNet("net!9p.mydomain.com!564"))
- } catch (uhe: UnresolvableHostException) {
- println("failed to connect :(")
- exitProcess(1)
- }
-
- // TODO: add walk and stat
-
- npconn.disconnect()
-} \ No newline at end of file
diff --git a/src/main/kotlin/except/FailedAuthenticationException.kt b/src/main/kotlin/except/FailedAuthenticationException.kt
deleted file mode 100644
index 94935c3..0000000
--- a/src/main/kotlin/except/FailedAuthenticationException.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package except
-
-/**
- * The authentication with the remote part failed.
- *
- * @param reason A human-readable reason for which the authentication failed.
- */
-class FailedAuthenticationException(val reason: String) : Exception("Authentication with remote host failed: $reason") \ No newline at end of file
diff --git a/src/main/kotlin/except/InvalidMessageException.kt b/src/main/kotlin/except/InvalidMessageException.kt
deleted file mode 100644
index 02a3cc4..0000000
--- a/src/main/kotlin/except/InvalidMessageException.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package except
-
-/**
- * The packet that is currently being read is not valid.
- *
- * @param reason The reason for which the packet is invalid.
- */
-class InvalidMessageException(val reason: String) : Exception("Invalid packet: $reason") \ No newline at end of file
diff --git a/src/main/kotlin/except/MsizeValueTooBigException.kt b/src/main/kotlin/except/MsizeValueTooBigException.kt
deleted file mode 100644
index f1f6da1..0000000
--- a/src/main/kotlin/except/MsizeValueTooBigException.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package except
-
-/**
- * This exception is thrown when the `msize` value sent by the server is bigger than that sent by the client during the
- * version transaction.
- *
- * @param maxClientValue The value requested by the client.
- * @param receivedValue The value sent by the server.
- */
-class MsizeValueTooBigException(val maxClientValue: UInt, val receivedValue: UInt) : Exception("Msize value too big: $receivedValue > $maxClientValue") \ No newline at end of file
diff --git a/src/main/kotlin/except/RErrorException.kt b/src/main/kotlin/except/RErrorException.kt
deleted file mode 100644
index c15cbc2..0000000
--- a/src/main/kotlin/except/RErrorException.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package except
-
-/**
- * This exception represents an error sent by the remote server as an R-error message.
- *
- * @param message The message sent by the server.
- */
-class RErrorException(val rErrorMessage: String?) : Exception("R-error message received: $rErrorMessage") \ No newline at end of file
diff --git a/src/main/kotlin/except/UnaccessibleFileException.kt b/src/main/kotlin/except/UnaccessibleFileException.kt
deleted file mode 100644
index 07d5d13..0000000
--- a/src/main/kotlin/except/UnaccessibleFileException.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package except
-
-/**
- * This exception is thrown when the file that the client is trying to open (or walk through, in case of a directory)
- * cannot be accessed.
- *
- * @param path The path, as a list of path elements, that the client tried to access, up to and including the first
- * element that cannot be accessed (e.g. if the path the user wants to access is `["usr", "foo", "bar", "zib"]` but
- * `bar` does not exist, then [path] must be `["usr", "foo", "bar"]`).
- */
-class UnaccessibleFileException(val path: List<String>) : Exception("Could not walk to file ${path.joinToString(separator = "/")}.") \ No newline at end of file
diff --git a/src/main/kotlin/except/UnknownVersionException.kt b/src/main/kotlin/except/UnknownVersionException.kt
deleted file mode 100644
index 5023bc5..0000000
--- a/src/main/kotlin/except/UnknownVersionException.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package except
-
-/**
- * This exception is thrown when the remote server sent either an "unknown" version back during the version negotiation
- * procedure or a version unknown to this client implementation.
- *
- * @param version The version sent by the server.
- */
-class UnknownVersionException(val version: String) : Exception("Unknown version: $version") \ No newline at end of file
diff --git a/src/main/kotlin/except/UnresolvableHostException.kt b/src/main/kotlin/except/UnresolvableHostException.kt
deleted file mode 100644
index 19dd3b6..0000000
--- a/src/main/kotlin/except/UnresolvableHostException.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package except
-
-/**
- * The specified domain, which identifies the host's address, could not be resolved.
- *
- * @param address The unresolvable address.
- */
-class UnresolvableHostException(val address: String) : Exception("Hostname $address unresolvable.") \ No newline at end of file
diff --git a/src/main/kotlin/net/InMessage.kt b/src/main/kotlin/net/InMessage.kt
deleted file mode 100644
index 771c670..0000000
--- a/src/main/kotlin/net/InMessage.kt
+++ /dev/null
@@ -1,167 +0,0 @@
-package net
-
-import NinePMessageType
-import except.InvalidMessageException
-import java.math.BigInteger
-
-/**
- * An incoming 9P message. Upon instancing this class only one message is read, and it's represented in a way similar to
- * that of [OutMessage]. This class is supposed to be complementary, and opposite, to [OutMessage].
- *
- * @param tl The transport layer API.
- * @param maxSize The maximum message size negotiated with the remote part.
- * @param reqTag The required tag.
- * @throws InvalidMessageException if the message that is currently being read is invalid.
- */
-class InMessage(val tl: TransportLayer, maxSize: UInt, val reqTag: UShort) {
- /**
- * The total size of the message.
- */
- val size: UInt
-
- /**
- * The message type.
- */
- val type: NinePMessageType
-
- /**
- * The message tag.
- */
- val tag: UShort
-
- /**
- * A map of each integer field's name to its value.
- */
- var fieldsInt: MutableMap<String, BigInteger> = mutableMapOf()
- private set
-
- /**
- * A map of each string field's name to its value.
- */
- var fieldsStr: MutableMap<String, String> = mutableMapOf()
- private set
-
- /**
- * A map of each raw field's name to its value.
- */
- var fieldsRaw: MutableMap<String, Array<UByte>> = mutableMapOf()
- private set
-
- /**
- * An ordered collection of raw bytes that still need to be interpreted as values.
- */
- private var rawData: List<UByte>
-
- init {
- size = convInteger(this.tl.receiver(), 0, 4).toInt().toUInt()
- if (this.size > maxSize) {
- throw InvalidMessageException("Size greater than maximum size (${this.size} > ${maxSize}).")
- }
- try {
- this.type = NinePMessageType.fromByte(convInteger(this.tl.receiver(), 0, 1).toInt().toUByte())
- } catch (_: NoSuchElementException) {
- throw InvalidMessageException("Invalid 9P message type.")
- }
- tag = convInteger(this.tl.receiver(), 0, 2).toInt().toUShort()
- if (tag != reqTag) {
- // TODO: what do we do now?
- }
- this.rawData = this.tl.receive((size - (4u + 1u + 2u)).toULong()).toList()
- }
-
- /**
- * Field of an incoming 9P message. An ordered collection of fields makes a schema.
- *
- * @param name The field's name. It's typically the same you can find in the manual pages.
- * @param type The field's type.
- * @param size The field's size in bytes. If the type is [Type.STRING], this parameter is ignored.
- */
- data class Field(val name: String, val type: Type, val size: UInt) {
-
- enum class Type {
- INTEGER,
- STRING,
- RAW
- }
- }
-
- /**
- * Apply the given field to the raw data and put it in one of [fieldsInt], [fieldsStr], or [fieldsRaw]. Fields must
- * be applied strictly in order, as their application is not commutative.
- *
- * Each time a field is applied, the initial part of raw data that coincides with that field is removed.
- *
- * @param field The given field.
- */
- fun applyField(field: Field) {
- val size: Int
- when (field.type) {
- Field.Type.STRING -> {
- val str = convString(this.rawData.toList(), 0)
- size = 2 + str.length
- this.fieldsStr[field.name] = str
- }
- Field.Type.INTEGER -> {
- size = field.size.toInt()
- this.fieldsInt[field.name] = convInteger(this.rawData.toList(), 0, size)
- }
- Field.Type.RAW -> {
- size = field.size.toInt()
- this.fieldsRaw[field.name] = this.rawData.take(size).toTypedArray()
- }
- }
- this.rawData = this.rawData.drop(size)
- }
-
- /**
- * Apply the given message schema to the raw data and fill [fieldsInt], [fieldsStr], and [fieldsRaw].
- *
- * Note: This method could have been avoided by making a giant `when` block in the class constructor. However, I'd
- * rather let the caller, which is usually a method that makes a request and reads its response, decide the schema.
- * In this way, each method that needs to read a response of a specific type (and there is usually one method per
- * response type) declares its own schema, while those which cannot be easily represented by a schema (e.g. `Rwalk`)
- * are simply going to be read in a field-by-field fashion.
- *
- * @param schema The desired ordered collection of fields.
- */
- fun applySchema(schema: Iterable<Field>) {
- for (field in schema) {
- applyField(field)
- }
- }
-
- companion object {
- /**
- * Convert an [len] bytes long unsigned integer number from raw bytes.
- *
- * In 9P, binary numbers (non-textual) are specified in little-endian order (least significant byte first).
- *
- * @param len The length of the integer number in bytes. If zero, nothing is read.
- * @return the number's value.
- * @throws IllegalArgumentException if either [offset] or [len] are negative.
- */
- fun convInteger(bytes: Iterable<UByte>, offset: Int, len: Int): BigInteger {
- val bytes = bytes.drop(offset).take(len)
- var value = 0.toBigInteger()
- for (i in 0..<bytes.size) {
- value += bytes[i].toInt().toBigInteger().shl(i*8)
- }
- return value
- }
-
- /**
- * Convert a string from raw bytes.
- *
- * In 9P, strings are represented as a 2-byte integer (the string's size) followed by the actual UTF-8 string. The
- * null terminator is forbidden in 9P messages.
- *
- * @return the string.
- * @throws IllegalArgumentException if either [offset] is negative.
- */
- fun convString(bytes: Iterable<UByte>, offset: Int): String {
- val length = convInteger(bytes, 0, 2).toInt()
- val bytes = bytes.drop(offset).take(length)
- return String(ByteArray(bytes.size) { i -> bytes[i].toByte() })
- }
- }
-} \ No newline at end of file
diff --git a/src/main/kotlin/net/OutMessage.kt b/src/main/kotlin/net/OutMessage.kt
deleted file mode 100644
index c9ae879..0000000
--- a/src/main/kotlin/net/OutMessage.kt
+++ /dev/null
@@ -1,127 +0,0 @@
-package net
-
-import NinePMessageType
-import java.math.BigInteger
-import kotlin.math.pow
-
-/**
- * An outgoing 9P message with the given type, tag, and fields. The message size is calculated automatically.
- *
- * Important note: the field names in [fieldValuesInt], [fieldValuesStr], and [fieldValuesRaw] (i.e. the keys of their
- * maps) must be mutually exclusive and the union of these two maps' keys must result in a subset of (or a set equal to)
- * [fieldNames]. Calling [write] when these conditions are not met throws an exception.
- *
- * @param type The 9P message type.
- * @param tag The tag given to the message.
- * @param fieldNames The names of the message fields, in the same order they are expected to be sent.
- * @param fieldValuesInt A map of each integer field's name into its value and size in bytes.
- * @param fieldValuesStr A map of each string field's name into its value.
- * @param fieldValuesRaw A map of each raw field's name into its value.
- * @param maxSize The maximum message size.
- */
-class OutMessage(val type: NinePMessageType, val tag: UShort, val fieldNames: List<String>, val fieldValuesInt: Map<String, Pair<BigInteger, UInt>>, val fieldValuesStr: Map<String, String>, val fieldValuesRaw: Map<String, List<UByte>>, val maxSize: UInt) {
- /**
- * Intersection between [fieldNames] and [fieldValuesInt]. In other words: the integer fields that are going to be
- * used when writing the message.
- */
- private val insecInts = fieldNames.intersect(fieldValuesInt.keys)
-
- /**
- * Intersection between [fieldNames] and [fieldValuesStr]. In other words: the string fields that are going to be
- * used when writing the message.
- */
- private val insecStrs = fieldNames.intersect(fieldValuesStr.keys)
-
- /**
- * Intersection between [fieldNames] and [fieldValuesRaw]. In other words: the raw fields that are going to be used
- * when writing the message.
- */
- private val insecRaws = fieldNames.intersect(fieldValuesRaw.keys)
-
- /**
- * Send the message using the given networking API.
- *
- * @param tl The networking API.
- * @throws IllegalArgumentException if [fieldNames], [fieldValuesInt], and [fieldValuesStr] are incoherent or the
- * final size of the message exceeds the negotiated value.
- */
- fun write(tl: TransportLayer) {
- // check that names in fieldNames exist as keys in either fieldValuesInt or fieldValuesStr but not both
- require(fieldNames.size == insecInts.size + insecStrs.size + insecRaws.size)
-
- val totalSize = size()
- if (totalSize > this.maxSize) {
- throw IllegalArgumentException("Message size exceeded.")
- }
- writeMessageSizeTypeTag(tl, totalSize, type, tag)
- for (field in fieldNames) {
- tl.transmit(
- if (field in insecInts) {
- val valsize = fieldValuesInt[field]!!
- convIntegerToBytes(valsize.first, valsize.second)
- } else if (field in insecStrs) {
- convStringToBytes(fieldValuesStr[field]!!)
- } else {
- fieldValuesRaw[field]!!.toList()
- }
- )
- }
- }
-
- /**
- * Write the message size and type.
- *
- * @param tl The networking API.
- * @param size The total message size, including the 4 bytes of this parameter and the type's byte.
- * @param type The 9P message type as a [NinePMessageType] constant.
- * @param tag The 9P message tag.
- */
- private fun writeMessageSizeTypeTag(tl: TransportLayer, size: UInt, type: NinePMessageType, tag: UShort) {
- var bytes: List<UByte> = emptyList()
- bytes += convIntegerToBytes(BigInteger(size.toString()), 4u)
- bytes += convIntegerToBytes(BigInteger(type.value.toString()), 1u)
- bytes += convIntegerToBytes(BigInteger(tag.toString()), 2u)
- tl.transmit(bytes)
- }
-
- /**
- * Calculate the expected size of the message.
- */
- fun size(): UInt {
- return 4u + 1u + 2u + this.insecInts.sumOf { this.fieldValuesInt[it]!!.second } + this.insecStrs.sumOf { 2u + this.fieldValuesStr[it]!!.length.toUInt() } + this.insecRaws.sumOf { this.fieldValuesRaw[it]!!.size.toUInt() }
- }
-
- companion object {
- // TODO: Add size that the value is required to fit in
-
- /**
- * Convert an integer number to its byte representation.
- *
- * In 9P, binary numbers (non-textual) are specified in little-endian order (least significant byte first).
- *
- * @param value The number's value.
- * @param size The number's size in bytes.
- */
- fun convIntegerToBytes(value: BigInteger, size: UInt): List<UByte> {
- var bytes: List<UByte> = value.toByteArray().toList().map { x -> x.toUByte() }
- bytes += List(size.toInt() - bytes.size, {0u}) // add padding for missing bytes
- return bytes
- }
-
- /**
- * Write a string to the connection.
- *
- * In 9P, strings are represented as a 2-byte integer (the string's size) followed by the actual UTF-8 string. The
- * null terminator is forbidden in 9P messages.
- *
- * @param value The string.
- * @throws IllegalArgumentException if the value of the string's size does not fit into 2 bytes.
- */
- fun convStringToBytes(value: String): List<UByte> {
- require(value.length <= 2.0.pow(16.0) - 1)
- var bytes = convIntegerToBytes(value.length.toBigInteger(), 2u)
- bytes += value.toByteArray().toList().map { x -> x.toUByte() }
- return bytes
- }
- }
-} \ No newline at end of file
diff --git a/src/main/kotlin/net/TransportLayer.kt b/src/main/kotlin/net/TransportLayer.kt
deleted file mode 100644
index 90dcd11..0000000
--- a/src/main/kotlin/net/TransportLayer.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-package net
-
-import java.io.Closeable
-
-/**
- * [TransportLayer] is an interface for network transport-layer operations. A class that implements these methods, once
- * instantiated, establishes and manages a connection with a remote endpoint defined by an address and a port and allows
- * to send and receive network messages (also called "payloads").
- *
- * The address of the remote endpoint can be an IP address (v4 or v6) or a domain name, in which case it is resolved to
- * an IP address right before initializing the connection. Every constructor should throw an
- * [except.UnresolvableHostException] if the remote address is formatted as an actual domain name, but it cannot be
- * resolved (e.g. it doesn't exist, or it contains forbidden characters).
- *
- * Depending on the specific given implementation, the constructor of this class might throw other exceptions (e.g. the
- * [java.net.Socket] constructor in [TransportLayerJavaNet]).
- */
-interface TransportLayer : Closeable {
- /**
- * Close the connection.
- */
- abstract override fun close()
-
- /**
- * Transmit a payload.
- *
- * @throws java.io.IOException if the message could not be correctly transmitted.
- */
- fun transmit(payload: Iterable<UByte>)
-/*
- /**
- * Receive a payload until a byte occurs, which marks the end of the message. The byte is discarded after being read
- * and is not returned.
- *
- * If you know both which byte marks the end of the message and the message length, it is advised to use
- * [receiveFixed] instead, which is usually more efficient.
- *
- * @param untilByte The byte that marks the end of the message.
- * @return the received payload.
- * @throws java.io.IOException if the message could not be correctly received.
- */
- abstract fun receiveUntil(untilByte: UByte): Array<UByte>
-*/
- /**
- * Receive a payload with fixed length. If zero, nothing is read.
- *
- * @param length The length of the message in bytes.
- * @return the received payload.
- * @throws java.io.IOException if the message could not be correctly received.
- */
- fun receive(length: ULong): Array<UByte>
-
- /**
- * Gives the caller a "receiver" (i.e. an instance of Iterable) from which raw data of any length can be read.
- *
- * @return The receiver.
- */
- fun receiver(): Iterable<UByte>
-} \ No newline at end of file
diff --git a/src/main/kotlin/net/TransportLayerJavaNet.kt b/src/main/kotlin/net/TransportLayerJavaNet.kt
deleted file mode 100644
index 3d2867a..0000000
--- a/src/main/kotlin/net/TransportLayerJavaNet.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-package net
-
-import nineAddressToValues
-import java.io.InputStream
-import java.io.OutputStream
-import java.net.Socket
-import kotlin.math.min
-
-/*
-TODO:
- - add TLS support
-*/
-
-/**
- * An implementation of [TransportLayer] written using the [java.net] package.
- */
-class TransportLayerJavaNet(val address: String, val port: UShort) : TransportLayer {
- /**
- * The connection's socket.
- */
- private val socket: Socket = Socket(this.address, this.port.toInt())
-
- /**
- * The connection's input stream.
- */
- private val inStream: InputStream = this.socket.inputStream
-
- /**
- * The connection's output stream.
- */
- private val outStream: OutputStream = this.socket.outputStream
-
- constructor(fullAddress: String) : this(nineAddressToValues(fullAddress).first, nineAddressToValues(fullAddress).second)
-
- private class InStreamIterator(val inStream: InputStream) : Iterator<UByte> {
- override fun next(): UByte {
- return this.inStream.readNBytes(1).first().toUByte()
- }
-
- override fun hasNext(): Boolean {
- return this.inStream.available() > 0
- }
- }
-
- override fun close() {
- if (this.socket.isClosed) {
- return
- }
- this.socket.close()
- }
-
- override fun transmit(payload: Iterable<UByte>) {
- val payload = payload.toList()
- val bytes = ByteArray(payload.size, { i -> payload[i].toByte() })
- this.outStream.write(bytes)
- }
-
-/*
- override fun receiveUntil(untilByte: UByte): Array<UByte> {
- var stop = false
- val payload: Array<UByte> = MutableList(0, { 0 })
- while (!stop) {
- val b = this.inStream.readNBytes(1)[0]
- if (b == untilByte) {
- stop = true
- continue
- } else {
- payload.add(b)
- }
- }
- return payload
- }
-*/
-
- override fun receive(length: ULong): Array<UByte> {
- var length = length
- val intMax = Int.MAX_VALUE.toULong()
- val bytes: MutableList<Byte> = MutableList(0) { 0 }
- // readNBytes only takes Int values, so it must be called multiple times if the length is greater than Int's
- // maximum value
- while (length > 0u) {
- // the min function ensures that the value passed to readNBytes never exceeds Int's maximum value while also
- // switching to the length variable when its value eventually becomes less than Int's maximum value, which
- // avoids writing duplicated readNBytes calls in the code
- val lenMin = min(length, intMax)
- bytes += this.inStream.readNBytes(lenMin.toInt()).toMutableList()
- length -= intMax
- }
- return Array(bytes.size) { i -> bytes[i].toUByte() }
- }
-
- override fun receiver(): Iterable<UByte> {
- return Iterable { InStreamIterator(this.inStream) }
- }
-} \ No newline at end of file