package com.tylerstonge.honeypot.ftp import java.io.File import java.net.InetSocketAddress import java.nio.file.{Files, StandardOpenOption} import java.util.UUID import akka.actor.{Actor, ActorRef, Props} import akka.event.{Logging, LoggingAdapter} import akka.io.Tcp._ import akka.io.{IO, Tcp} import akka.util.{ByteString, ByteStringBuilder} import com.tylerstonge.honeypot.messages.MFoundFile object FtpFileReceiver { def props(port: Int, controller: ActorRef): Props = Props(new FtpFileReceiver(port, controller)) } class FtpFileReceiver(port: Int, controller: ActorRef) extends Actor { val path = "/tmp/files/" val log: LoggingAdapter = Logging(context.system, this) IO(Tcp)(context.system) ! Bind(self, new InetSocketAddress("127.0.0.1", port)) val fileData: ByteStringBuilder = ByteString.newBuilder var name: String = UUID.randomUUID().toString override def postStop { log.debug("shutting down") } override def receive: Receive = { case Bound(localAddress) => log.info("listening on {}", localAddress) case CommandFailed(_: Bind) => context.stop(self) case Connected(_, _) => val connection = sender() connection ! Register(self) context.become { case Received(data) => log.debug("read {} bytes", data.length) fileData.addAll(data) case PeerClosed => log.debug("peer closed connection, writing file to disk") val out = Files.newByteChannel(new File(this.path + this.name).toPath, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE) out.write(fileData.result().toByteBuffer) out.close() controller.tell(Write(ByteString.apply("226 File transferred.\n")), context.parent) context.system.eventStream.publish(MFoundFile(this.path + this.name)) context.stop(self) case SetName(name) => log.info("file accepted as {}", name) this.name = name case msg@_ => log.warning("unknown message: {}", msg) } } } case class Ready() case class SetName(name: String)