| 1 | /* -*-java-*- |
| 2 | * |
| 3 | * Declarations of C functions |
| 4 | * |
| 5 | * (c) 2018 Straylight/Edgeware |
| 6 | */ |
| 7 | |
| 8 | /*----- Licensing notice --------------------------------------------------* |
| 9 | * |
| 10 | * This file is part of the Trivial IP Encryption (TrIPE) Android app. |
| 11 | * |
| 12 | * TrIPE is free software: you can redistribute it and/or modify it under |
| 13 | * the terms of the GNU General Public License as published by the Free |
| 14 | * Software Foundation; either version 3 of the License, or (at your |
| 15 | * option) any later version. |
| 16 | * |
| 17 | * TrIPE is distributed in the hope that it will be useful, but WITHOUT |
| 18 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 19 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 20 | * for more details. |
| 21 | * |
| 22 | * You should have received a copy of the GNU General Public License |
| 23 | * along with TrIPE. If not, see <https://www.gnu.org/licenses/>. |
| 24 | */ |
| 25 | |
| 26 | package uk.org.distorted.tripe; package object jni { |
| 27 | |
| 28 | /*----- Imports -----------------------------------------------------------*/ |
| 29 | |
| 30 | import java.io.{Closeable, File}; |
| 31 | import java.util.Date; |
| 32 | import Magic._; |
| 33 | |
| 34 | /*----- Main code ---------------------------------------------------------*/ |
| 35 | |
| 36 | /* Import the native code library. */ |
| 37 | System.loadLibrary("toy"); |
| 38 | |
| 39 | /* Exception indicating that a wrapped native object has been clobbered. */ |
| 40 | class NativeObjectTypeException(msg: String) extends RuntimeException(msg); |
| 41 | type Wrapper = Array[Byte]; |
| 42 | |
| 43 | case class ErrorEntry(val tag: String, val err: Int); |
| 44 | @native def errtab: Array[ErrorEntry]; |
| 45 | @native def strerror(err: Int): CString; |
| 46 | |
| 47 | @native def hashsz(hash: String): Int; |
| 48 | /* Return the output hash size for the named HASH function, or -1. */ |
| 49 | |
| 50 | /* Flags for `close'. */ |
| 51 | val CF_CLOSERD = 1; |
| 52 | val CF_CLOSEWR = 2; |
| 53 | val CF_CLOSEMASK = CF_CLOSERD | CF_CLOSEWR; |
| 54 | |
| 55 | /* Flags for `lock'. */ |
| 56 | val LKF_EXCL = 1; |
| 57 | val LKF_WAIT = 2; |
| 58 | |
| 59 | /* Flags for `stat'. */ |
| 60 | val S_IFMT = 0xf000; |
| 61 | val S_IFIFO = 0x1000; |
| 62 | val S_IFCHR = 0x2000; |
| 63 | val S_IFDIR = 0x4000; |
| 64 | val S_IFBLK = 0x6000; |
| 65 | val S_IFREG = 0x8000; |
| 66 | val S_IFLNK = 0xa000; |
| 67 | val S_IFSOCK = 0xc000; |
| 68 | |
| 69 | object FileType extends Enumeration { |
| 70 | val FIFO, CHR, DIR, BLK, REG, LNK, SOCK, UNK = Value; |
| 71 | } |
| 72 | import FileType.{Value => _, _}; |
| 73 | |
| 74 | class FileInfo private[this](val devMajor: Int, val devMinor: Int, |
| 75 | val ino: Long, val mode: Int, val nlink: Int, |
| 76 | val uid: Int, val gid: Int, |
| 77 | _rdevMinor: Int, _rdevMajor: Int, |
| 78 | val size: Long, |
| 79 | val blksize: Int, val blocks: Long, |
| 80 | val atime: Date, val mtime: Date, |
| 81 | val ctime: Date) { |
| 82 | def this(devMajor: Int, devMinor: Int, ino: Long, |
| 83 | mode: Int, nlink: Int, uid: Int, gid: Int, |
| 84 | rdevMinor: Int, rdevMajor: Int, |
| 85 | size: Long, blksize: Int, blocks: Long, |
| 86 | atime: Long, mtime: Long, ctime: Long) { |
| 87 | this(devMajor, devMinor, ino, mode, nlink, uid, gid, |
| 88 | rdevMajor, rdevMinor, size, blksize, blocks, |
| 89 | new Date(atime), new Date(mtime), new Date(ctime)); |
| 90 | } |
| 91 | def perms: Int = mode&0xfff; |
| 92 | def ftype: FileType.Value = (mode&S_IFMT) match { |
| 93 | case S_IFIFO => FIFO |
| 94 | case S_IFCHR => CHR |
| 95 | case S_IFDIR => DIR |
| 96 | case S_IFBLK => BLK |
| 97 | case S_IFREG => REG |
| 98 | case S_IFLNK => LNK |
| 99 | case S_IFSOCK => SOCK |
| 100 | case _ => UNK |
| 101 | } |
| 102 | def isfifo: Boolean = ftype == FIFO |
| 103 | def ischr: Boolean = ftype == CHR |
| 104 | def isdir: Boolean = ftype == DIR |
| 105 | def isblk: Boolean = ftype == BLK |
| 106 | def isreg: Boolean = ftype == REG |
| 107 | def islnk: Boolean = ftype == LNK |
| 108 | def issock: Boolean = ftype == SOCK |
| 109 | def isdev: Boolean = ischr || isblk; |
| 110 | private[this] def mustBeDevice() { |
| 111 | if (!isdev) throw new IllegalArgumentException("Object is not a device"); |
| 112 | } |
| 113 | def rdevMajor: Int = { mustBeDevice(); _rdevMajor } |
| 114 | def rdevMinor: Int = { mustBeDevice(); _rdevMinor } |
| 115 | } |
| 116 | @native protected def unlink(path: CString); |
| 117 | def unlink(path: String) { unlink(path.toCString); } |
| 118 | def unlink(file: File) { unlink(file.getPath); } |
| 119 | @native protected def rmdir(path: CString); |
| 120 | def rmdir(path: String) { rmdir(path.toCString); } |
| 121 | def rmdir(file: File) { rmdir(file.getPath); } |
| 122 | @native protected def mkdir(path: CString, mode: Int); |
| 123 | def mkdir(path: String, mode: Int) { mkdir(path.toCString, mode); } |
| 124 | def mkdir(path: String) { mkdir(path, 0x1ff); } |
| 125 | def mkdir(file: File, mode: Int) { mkdir(file.getPath, mode); } |
| 126 | def mkdir(file: File) { mkdir(file.getPath); } |
| 127 | @native protected def mkfile(path: CString, mode: Int); |
| 128 | def mkfile(path: String, mode: Int) { mkfile(path.toCString, mode); } |
| 129 | def mkfile(path: String) { mkfile(path, 0x1b6); } |
| 130 | def mkfile(file: File, mode: Int) { mkfile(file.getPath, mode); } |
| 131 | def mkfile(file: File) { mkfile(file.getPath); } |
| 132 | @native protected def rename(from: CString, to: CString); |
| 133 | def rename(from: String, to: String) |
| 134 | { rename(from.toCString, to.toCString); } |
| 135 | def rename(from: File, to: File) |
| 136 | { rename(from.getPath, to.getPath); } |
| 137 | @native protected def stat(path: CString): FileInfo; |
| 138 | def stat(path: String): FileInfo = stat(path.toCString); |
| 139 | def stat(file: File): FileInfo = stat(file.getPath); |
| 140 | @native protected def lstat(path: CString): FileInfo; |
| 141 | def lstat(path: String): FileInfo = lstat(path.toCString); |
| 142 | def lstat(file: File): FileInfo = lstat(file.getPath); |
| 143 | |
| 144 | @native protected def opendir(path: CString): Wrapper; |
| 145 | @native protected def readdir(path: CString, dir: Wrapper): CString; |
| 146 | @native protected def closedir(path: CString, dir: Wrapper); |
| 147 | |
| 148 | abstract class BaseDirIterator[T](cpath: CString) |
| 149 | extends LookaheadIterator[T] with Closeable { |
| 150 | def this(path: String) { this(path.toCString); } |
| 151 | def this(dir: File) { this(dir.getPath); } |
| 152 | override def close() { closedir(cpath, dir); } |
| 153 | override protected def finalize() { super.finalize(); close(); } |
| 154 | private[this] val dir = opendir(cpath); |
| 155 | protected def mangle(file: String): T; |
| 156 | override protected def fetch(): Option[T] = readdir(cpath, dir) match { |
| 157 | case null => None |
| 158 | case f => f.toJString match { |
| 159 | case "." | ".." => fetch() |
| 160 | case jf => Some(mangle(jf)) |
| 161 | } |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | class DirIterator(val path: String) extends BaseDirIterator[String](path) { |
| 166 | def this(dir: File) { this(dir.getPath); } |
| 167 | override protected def mangle(file: String): String = file; |
| 168 | } |
| 169 | def listDir(path: String): List[String] = { |
| 170 | val iter = new DirIterator(path); |
| 171 | try { iter.toList } |
| 172 | finally { iter.close(); } |
| 173 | } |
| 174 | def listDir(dir: File): List[String] = listDir(dir.getPath); |
| 175 | |
| 176 | class DirFilesIterator private[this](val dir: File, cpath: CString) |
| 177 | extends BaseDirIterator[File](cpath) { |
| 178 | def this(dir: File) { this(dir, dir.getPath.toCString); } |
| 179 | def this(path: String) { this(new File(path), path.toCString); } |
| 180 | override protected def mangle(file: String): File = new File(dir, file); |
| 181 | } |
| 182 | def listDirFiles(path: String): List[File] = { |
| 183 | val iter = new DirFilesIterator(path); |
| 184 | try { iter.toList } |
| 185 | finally { iter.close(); } |
| 186 | } |
| 187 | def listDirFiles(dir: File): List[File] = listDirFiles(dir.getPath); |
| 188 | |
| 189 | @native protected def lock(path: CString, flags: Int): Wrapper; |
| 190 | @native protected def unlock(lock: Wrapper); |
| 191 | class FileLock(path: String, flags: Int) extends Closeable { |
| 192 | def this(file: File, flags: Int) { this(file.getPath, flags); } |
| 193 | def this(path: String) { this(path, LKF_EXCL); } |
| 194 | def this(file: File) { this(file.getPath, LKF_EXCL); } |
| 195 | private[this] val lk = lock(path.toCString, flags); |
| 196 | override def close() { unlock(lk); } |
| 197 | override protected def finalize() { super.finalize(); close(); } |
| 198 | } |
| 199 | def withLock[T](path: String, flags: Int)(body: => T): T = { |
| 200 | val lk = new FileLock(path, flags); |
| 201 | try { body; } finally { lk.close(); } |
| 202 | } |
| 203 | def withLock[T](file: File, flags: Int)(body: => T): T = |
| 204 | withLock(file.getPath, flags) { body } |
| 205 | def withLock[T](path: String)(body: => T): T = |
| 206 | withLock(path, LKF_EXCL) { body } |
| 207 | def withLock[T](file: File)(body: => T): T = |
| 208 | withLock(file.getPath, LKF_EXCL) { body } |
| 209 | |
| 210 | @native protected def connect(path: CString): Wrapper; |
| 211 | @native def send(conn: Wrapper, buf: CString, |
| 212 | start: Int, len: Int); |
| 213 | @native def recv(conn: Wrapper, buf: CString, |
| 214 | start: Int, len: Int): Int; |
| 215 | @native def close(conn: Wrapper, how: Int); |
| 216 | class Connection(path: String) extends Closeable { |
| 217 | def this(file: File) { this(file.getPath); } |
| 218 | private[this] val conn = connect(path.toCString); |
| 219 | override def close() { jni.close(conn, CF_CLOSEMASK); } |
| 220 | override protected def finalize() { super.finalize(); close(); } |
| 221 | class InputStream private[Connection] extends java.io.InputStream { |
| 222 | override def read(): Int = { |
| 223 | val buf = new Array[Byte](1); |
| 224 | val n = read(buf, 0, 1); |
| 225 | if (n < 0) -1 else buf(0)&0xff; |
| 226 | } |
| 227 | override def read(buf: Array[Byte]): Int = |
| 228 | read(buf, 0, buf.length); |
| 229 | override def read(buf: Array[Byte], start: Int, len: Int) = |
| 230 | recv(conn, buf, start, len); |
| 231 | override def close() { jni.close(conn, CF_CLOSERD); } |
| 232 | } |
| 233 | lazy val input = new InputStream; |
| 234 | class OutputStream private[Connection] extends java.io.OutputStream { |
| 235 | override def write(b: Int) { write(Array[Byte](b.toByte), 0, 1); } |
| 236 | override def write(buf: Array[Byte]) { write(buf, 0, buf.length); } |
| 237 | override def write(buf: Array[Byte], start: Int, len: Int) |
| 238 | { send(conn, buf, start, len); } |
| 239 | override def close() { jni.close(conn, CF_CLOSEWR); } |
| 240 | } |
| 241 | lazy val output = new OutputStream; |
| 242 | } |
| 243 | |
| 244 | /*----- That's all, folks -------------------------------------------------*/ |
| 245 | |
| 246 | } |