From 569e174b78041634f7a95b35a7cc16ee30ad7c06 Mon Sep 17 00:00:00 2001 From: Adrian Malacoda Date: Thu, 22 Oct 2020 04:01:33 -0500 Subject: [PATCH] commit initial version of PieCannon library and desktop app. Documentation is sparse right now, I'm working on getting the desktop and android apps working first. --- .gitignore | 1 + README.md | 9 ++ desktop/piecannon | 3 + desktop/pom.xml | 67 +++++++++++++++ .../java/net/monarchpass/piecannon/App.java | 84 +++++++++++++++++++ .../monarchpass/piecannon/ServerFactory.java | 31 +++++++ .../net/monarchpass/piecannon/AppTest.java | 10 +++ lib/pom.xml | 57 +++++++++++++ .../net/monarchpass/piecannon/PieCannon.java | 39 +++++++++ .../net/monarchpass/piecannon/Server.java | 12 +++ .../piecannon/impl/SftpServer.java | 53 ++++++++++++ .../net/monarchpass/piecannon/SftpTest.java | 30 +++++++ 12 files changed, 396 insertions(+) create mode 100644 .gitignore create mode 100755 desktop/piecannon create mode 100644 desktop/pom.xml create mode 100644 desktop/src/main/java/net/monarchpass/piecannon/App.java create mode 100644 desktop/src/main/java/net/monarchpass/piecannon/ServerFactory.java create mode 100644 desktop/src/test/java/net/monarchpass/piecannon/AppTest.java create mode 100644 lib/pom.xml create mode 100644 lib/src/main/java/net/monarchpass/piecannon/PieCannon.java create mode 100644 lib/src/main/java/net/monarchpass/piecannon/Server.java create mode 100644 lib/src/main/java/net/monarchpass/piecannon/impl/SftpServer.java create mode 100644 lib/src/test/java/net/monarchpass/piecannon/SftpTest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target diff --git a/README.md b/README.md index e69de29..c1be26d 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,9 @@ +# Project Pie Cannon +Pie Cannon is an easy way to upload files to the internet and get a shareable link. File goes in, link comes out. + +It doesn't come with a backend because you're expected to bring your own. A plain old FTP/SFTP+HTTP server will work fine, but support for more backends is on the way. + +This project is separated into a library and a desktop application (targeting mainly GNU/Linux), with an Android application in the works. + +## How to use +TODO diff --git a/desktop/piecannon b/desktop/piecannon new file mode 100755 index 0000000..9b502c5 --- /dev/null +++ b/desktop/piecannon @@ -0,0 +1,3 @@ +#!/bin/sh +RESULT=$(mvn -B exec:java -Dexec.mainClass=net.monarchpass.piecannon.App -Dexec.arguments=$1 -Dorg.slf4j.simpleLogger.defaultLogLevel=WARN | tail -n 1) +xdg-open $RESULT diff --git a/desktop/pom.xml b/desktop/pom.xml new file mode 100644 index 0000000..3f8aea0 --- /dev/null +++ b/desktop/pom.xml @@ -0,0 +1,67 @@ + + 4.0.0 + + net.monarchpass + piecannon-desktop-app + 0.0.1-SNAPSHOT + + + 1.8 + 1.8 + + + + + net.monarchpass + libpiecannon + 0.0.1-SNAPSHOT + + + com.google.guava + guava + 30.0-jre + + + net.kothar + xdg-java + 0.1.1 + + + com.google.code.gson + gson + 2.8.6 + + + org.projectlombok + lombok + 1.18.16 + provided + + + org.junit.jupiter + junit-jupiter + 5.7.0 + test + + + com.google.truth + truth + 1.1 + test + + + + + + + maven-compiler-plugin + 3.8.1 + + + maven-surefire-plugin + 2.22.2 + + + + diff --git a/desktop/src/main/java/net/monarchpass/piecannon/App.java b/desktop/src/main/java/net/monarchpass/piecannon/App.java new file mode 100644 index 0000000..0eea3a7 --- /dev/null +++ b/desktop/src/main/java/net/monarchpass/piecannon/App.java @@ -0,0 +1,84 @@ +package net.monarchpass.piecannon; + +import java.net.URI; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.InputStreamReader; + +import java.util.Collections; +import java.util.Random; +import java.util.List; +import java.util.ArrayList; +import java.util.stream.Collectors; + +import org.freedesktop.BaseDirectory; + +import com.google.common.collect.Streams; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import lombok.extern.java.Log; +import java.util.logging.Level; + +@Log +public class App { + public static void main (final String... args) throws Exception { + final File serversJson = getServersJson(); + final List servers = loadServersFrom(serversJson); + log.log(Level.INFO, "{0} servers loaded from {1}", new Object[] { + servers.size(), serversJson + }); + + if (servers.isEmpty()) { + log.log(Level.SEVERE, "No servers defined, please define at least one in {0}", serversJson); + System.exit(1); + } + + final Server server = servers.get(new Random().nextInt(servers.size())); + log.log(Level.INFO, "Randomly selected server: {0}", server.getLabel()); + + if (args.length == 0) { + log.log(Level.SEVERE, "No filename provided"); + System.exit(1); + } + + final File source = new File(args[0]); + if (!source.exists()) { + log.log(Level.SEVERE, "No such file: {0}", source); + System.exit(1); + } + + final URI result = server.upload(source); + System.out.println(result); + } + + public static List loadServersFrom (final File serversJson) throws IOException { + if (!serversJson.exists()) { + return Collections.EMPTY_LIST; + } + + final ServerFactory factory = new ServerFactory(); + try (final InputStream in = new FileInputStream(serversJson)) { + final JsonParser parser = new JsonParser(); + return Streams.stream(parser.parse(new InputStreamReader(in)).getAsJsonArray()) + .filter(JsonElement::isJsonObject) + .map(JsonObject.class::cast) + .map(factory) + .collect(Collectors.toList()); + } + } + + public static File getServersJson () { + return new File(getDataDirectory(), "servers.json"); + } + + public static File getDataDirectory () { + return new File(BaseDirectory.get(BaseDirectory.XDG_DATA_HOME), "piecannon"); + } +} diff --git a/desktop/src/main/java/net/monarchpass/piecannon/ServerFactory.java b/desktop/src/main/java/net/monarchpass/piecannon/ServerFactory.java new file mode 100644 index 0000000..a84e51b --- /dev/null +++ b/desktop/src/main/java/net/monarchpass/piecannon/ServerFactory.java @@ -0,0 +1,31 @@ +package net.monarchpass.piecannon; + +import java.net.URI; + +import java.util.function.Function; + +import com.google.gson.JsonObject; +import net.monarchpass.piecannon.impl.SftpServer; + +import com.google.common.base.MoreObjects; + +public class ServerFactory implements Function { + public Server apply (final JsonObject object) { + return makeSftpServer(object); + } + + private Server makeSftpServer (final JsonObject object) { + final String host = object.getAsJsonPrimitive("host").getAsString(); + final String label = object.has("label") ? object.getAsJsonPrimitive("label").getAsString() : host; + final int port = object.has("port") ? object.getAsJsonPrimitive("port").getAsInt() : 22; + final String username = object.getAsJsonPrimitive("username").getAsString(); + final String password = object.getAsJsonPrimitive("password").getAsString(); + final String path = object.getAsJsonPrimitive("path").getAsString(); + final String url = object.getAsJsonPrimitive("url").getAsString(); + + return new SftpServer( + label, host, port, username, password, + path, URI.create(url) + ); + } +} diff --git a/desktop/src/test/java/net/monarchpass/piecannon/AppTest.java b/desktop/src/test/java/net/monarchpass/piecannon/AppTest.java new file mode 100644 index 0000000..bf6f223 --- /dev/null +++ b/desktop/src/test/java/net/monarchpass/piecannon/AppTest.java @@ -0,0 +1,10 @@ +package net.monarchpass.piecannon; + +import org.junit.jupiter.api.Test; + +import static com.google.common.truth.Truth.assertThat; + +public class AppTest { + @Test + public void test () throws Exception {} +} diff --git a/lib/pom.xml b/lib/pom.xml new file mode 100644 index 0000000..e096e10 --- /dev/null +++ b/lib/pom.xml @@ -0,0 +1,57 @@ + + 4.0.0 + + net.monarchpass + libpiecannon + 0.0.1-SNAPSHOT + + + 1.8 + 1.8 + + + + + com.github.mwiede + jsch + 0.1.60 + + + com.google.guava + guava + 30.0-android + + + org.projectlombok + lombok + 1.18.16 + provided + + + org.junit.jupiter + junit-jupiter + 5.7.0 + test + + + com.google.truth + truth + 1.1 + test + + + + + + + maven-compiler-plugin + 3.8.1 + + + maven-surefire-plugin + 2.22.2 + + + + diff --git a/lib/src/main/java/net/monarchpass/piecannon/PieCannon.java b/lib/src/main/java/net/monarchpass/piecannon/PieCannon.java new file mode 100644 index 0000000..04a0fc7 --- /dev/null +++ b/lib/src/main/java/net/monarchpass/piecannon/PieCannon.java @@ -0,0 +1,39 @@ +package net.monarchpass.piecannon; + +import java.net.URI; + +import java.io.InputStream; +import java.io.IOException; + +import java.util.List; +import java.util.ArrayList; +import java.util.Collections; + +import com.google.common.base.Charsets; +import com.google.common.io.CharSource; +import com.google.common.io.ByteStreams; + +public class PieCannon { + private final List servers = new ArrayList<>(); + + public void addServer (final Server server) { + servers.add(server); + } + + public List getServers () { + return Collections.unmodifiableList(servers); + } + + public static boolean testServer (final Server server) { + final String testString = "piecannon-test-" + System.currentTimeMillis(); + final URI result = server.upload("piecannon.test", CharSource.wrap(testString).asByteSource(Charsets.UTF_8)); + + try (final InputStream in = result.toURL().openStream()) { + return new String(ByteStreams.toByteArray(in), Charsets.UTF_8).equals(testString); + } catch (final IOException exception) { + exception.printStackTrace(); + } + + return false; + } +} diff --git a/lib/src/main/java/net/monarchpass/piecannon/Server.java b/lib/src/main/java/net/monarchpass/piecannon/Server.java new file mode 100644 index 0000000..29cb451 --- /dev/null +++ b/lib/src/main/java/net/monarchpass/piecannon/Server.java @@ -0,0 +1,12 @@ +package net.monarchpass.piecannon; + +import java.io.File; +import java.net.URI; +import com.google.common.io.Files; +import com.google.common.io.ByteSource; + +public interface Server { + public String getLabel (); + public URI upload (String name, ByteSource source); + public default URI upload (File file) {return upload(file.getName(), Files.asByteSource(file));} +} diff --git a/lib/src/main/java/net/monarchpass/piecannon/impl/SftpServer.java b/lib/src/main/java/net/monarchpass/piecannon/impl/SftpServer.java new file mode 100644 index 0000000..f1e6cc0 --- /dev/null +++ b/lib/src/main/java/net/monarchpass/piecannon/impl/SftpServer.java @@ -0,0 +1,53 @@ +package net.monarchpass.piecannon.impl; + +import net.monarchpass.piecannon.Server; +import com.google.common.io.ByteSource; + +import java.io.InputStream; +import java.io.IOException; +import java.net.URI; + +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.SftpException; + +import lombok.Data; + +@Data +public class SftpServer implements Server { + private final String label; + private final String host; + private final int port; + private final String username; + private final String password; + private final String path; + private final URI uri; + + public URI upload (String name, ByteSource source) { + try { + final JSch jsch = new JSch(); + final Session session = jsch.getSession(username, host, port); + session.setPassword(password); + session.setConfig("StrictHostKeyChecking", "no"); + session.connect(); + + final Channel channel = session.openChannel("sftp"); + channel.connect(); + + final ChannelSftp sftp = (ChannelSftp) channel; + sftp.cd(path); + + try (InputStream in = source.openStream()) { + sftp.put(in, name); + } + + session.disconnect(); + return URI.create(uri.toString() + "/" + name); + } catch (final JSchException | IOException | SftpException exception) { + throw new RuntimeException(exception); + } + } +} diff --git a/lib/src/test/java/net/monarchpass/piecannon/SftpTest.java b/lib/src/test/java/net/monarchpass/piecannon/SftpTest.java new file mode 100644 index 0000000..c9cb1f8 --- /dev/null +++ b/lib/src/test/java/net/monarchpass/piecannon/SftpTest.java @@ -0,0 +1,30 @@ +package net.monarchpass.piecannon; + +import java.net.URI; +import org.junit.jupiter.api.Test; +import net.monarchpass.piecannon.impl.SftpServer; + +import static com.google.common.truth.Truth.assertThat; + +public class SftpTest { + @Test + public void test () throws Exception { + final String testServerHost = System.getProperty("piecannon.test.host"); + if (testServerHost == null) { + return; + } + + final String testServerUser = System.getProperty("piecannon.test.user"); + final String testServerPassword = System.getProperty("piecannon.test.password"); + final String testServerPath = System.getProperty("piecannon.test.path"); + final String testServerURL = System.getProperty("piecannon.test.url"); + + final Server server = new SftpServer( + "Test Server Instance", testServerHost, 22, + testServerUser, testServerPassword, testServerPath, + URI.create(testServerURL) + ); + + assertThat(PieCannon.testServer(server)).isTrue(); + } +}