Compare commits
16 Commits
b004320ffe
...
ssh_agent_
Author | SHA1 | Date | |
---|---|---|---|
b8fd687ec2 | |||
4e1156d4bc | |||
e4ea50ac66 | |||
33491e3904 | |||
80cc767c25 | |||
be5b64789a | |||
b894978f92 | |||
ef252f5a19 | |||
21a7b6e870 | |||
6ed87b738f | |||
c0cb1b4f2e | |||
212996521a | |||
0ffe462710 | |||
cf920c57c6 | |||
77cf4f5820 | |||
0d29b03dce |
14
README.md
14
README.md
@@ -28,4 +28,16 @@ TODO
|
||||
## How to use
|
||||
Place a `servers.json` in the Pie Cannon data directory. For GNU/Linux this is `XDG_DATA_HOME` (by default this would be `~/.local/share/piecannon`) (create this directory if it does not exist. For Android this would be the app's "externalFilesDir" (probably something like `Android/data/net.monarchpass.piecannon/files` - the app will tell you when you launch it). See `servers.example.json` for an example of such a file. Whenever you initiate a file upload, Pie Cannon will select one of your defined servers at random and upload the file.
|
||||
|
||||
For SFTP servers (the only supported type as of now), the `path` is relative to your home directory and should be where `url` points to. i.e. a file uploaded to `path` should be downloadable at `url`. In the future Pie Cannon should be able to get SSH credentials from your agent so you won't need to put a password in this file.
|
||||
#### Server Types
|
||||
The `type` field in the server configuration tells Pie Cannon what type of server to use. The default value is `webdav`.
|
||||
|
||||
##### WebDAV (`webdav`)
|
||||
For WebDAV servers, `url` is where the file is uploaded to and served from. `username` and `password`, if given, are HTTP Basic authentication credentials.
|
||||
|
||||
For setting this up with nginx, see [ngx_http_dav_module](https://nginx.org/en/docs/http/ngx_http_dav_module.html) documentation and also [HTTP Basic Authentication](https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/) guide.
|
||||
|
||||
##### FTP/SFTP (`ftp`/`sftp`)
|
||||
For FTP/SFTP servers, the `path` is relative to your home directory and should be where `url` points to. i.e. a file uploaded to `path` should be downloadable at `url`. In the future Pie Cannon should be able to get SSH credentials from your agent so you won't need to put a password in this file.
|
||||
|
||||
##### GoFile.io (`gofile`)
|
||||
No configuration options are supported for this currently, however in the future there may be support for using a token. Note that it's not possible to get a direct download link for this service without visiting a page first, so Pie Cannon returns the URL for that page instead of a direct download link.
|
||||
|
@@ -2,6 +2,8 @@
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/net/monarchpass/libpiecannon/0.0.1-SNAPSHOT/libpiecannon-0.0.1-SNAPSHOT.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/github/mwiede/jsch/0.1.60/jsch-0.1.60.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/google/code/gson/gson/2.8.6/gson-2.8.6.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/jodd/jodd-http/6.0.3/jodd-http-6.0.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/jodd/jodd-util/6.0.0/jodd-util-6.0.0.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/google/guava/guava/30.0-jre/guava-30.0-jre.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" enabled="true" runInBatchMode="false"/>
|
||||
@@ -10,7 +12,10 @@
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/google/errorprone/error_prone_annotations/2.3.4/error_prone_annotations-2.3.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/net/kothar/xdg-java/0.1.1/xdg-java-0.1.1.jar" enabled="true" runInBatchMode="false"/>
|
||||
<<<<<<< HEAD
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/jcraft/jsch.agentproxy.jsch/0.0.9/jsch.agentproxy.jsch-0.0.9.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/jcraft/jsch.agentproxy.core/0.0.9/jsch.agentproxy.core-0.0.9.jar" enabled="true" runInBatchMode="false"/>
|
||||
=======
|
||||
>>>>>>> master
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/projectlombok/lombok/1.18.16/lombok-1.18.16.jar" enabled="true" runInBatchMode="false"/>
|
||||
</factorypath>
|
||||
|
@@ -4,8 +4,6 @@ import java.net.URI;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.freedesktop.BaseDirectory;
|
||||
|
||||
import lombok.extern.java.Log;
|
||||
@@ -36,15 +34,21 @@ public class App {
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
final Server server = cannon.selectServer();
|
||||
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]);
|
||||
final Server server = cannon.selectServer();
|
||||
log.log(Level.INFO, "Randomly selected server: {0}", server.getLabel());
|
||||
|
||||
final String arg = args[0];
|
||||
if (arg.equals("--test")) {
|
||||
testServers(servers);
|
||||
return;
|
||||
}
|
||||
|
||||
final File source = new File(arg);
|
||||
if (!source.exists()) {
|
||||
log.log(Level.SEVERE, "No such file: {0}", source);
|
||||
System.exit(1);
|
||||
@@ -54,6 +58,23 @@ public class App {
|
||||
System.out.println(result);
|
||||
}
|
||||
|
||||
public static void testServers (final List<Server> servers) {
|
||||
log.log(Level.INFO, "Testing all defined servers");
|
||||
for (final Server server : servers) {
|
||||
Object result;
|
||||
try {
|
||||
result = PieCannon.testServer(server);
|
||||
}
|
||||
catch (final Exception exception) {
|
||||
result = exception;
|
||||
}
|
||||
|
||||
log.log(Level.INFO, "{0} {1}: {2}", new Object[] {
|
||||
server.getClass().getSimpleName(), server.getLabel(), result
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static File getServersJson () {
|
||||
return new File(getDataDirectory(), "servers.json");
|
||||
}
|
||||
|
@@ -8,5 +8,7 @@
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/google/errorprone/error_prone_annotations/2.3.4/error_prone_annotations-2.3.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/google/code/gson/gson/2.8.6/gson-2.8.6.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/jodd/jodd-http/6.0.3/jodd-http-6.0.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/jodd/jodd-util/6.0.0/jodd-util-6.0.0.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/projectlombok/lombok/1.18.16/lombok-1.18.16.jar" enabled="true" runInBatchMode="false"/>
|
||||
</factorypath>
|
||||
|
@@ -27,6 +27,11 @@
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jodd</groupId>
|
||||
<artifactId>jodd-http</artifactId>
|
||||
<version>6.0.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
|
@@ -0,0 +1,45 @@
|
||||
package net.monarchpass.piecannon.impl;
|
||||
|
||||
import net.monarchpass.piecannon.Server;
|
||||
import com.google.common.io.ByteSource;
|
||||
import com.google.common.io.ByteStreams;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class FtpServer 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) {
|
||||
name = name.replace(" ", "%20");
|
||||
|
||||
try {
|
||||
final URI target = URI.create(uri.toString() + "/" + name);
|
||||
final URL ftpUrl = new URL(String.format("ftp://%s:%s@%s:%d/%s/%s;type=i", username, password, host, port, path, name));
|
||||
final URLConnection connection = ftpUrl.openConnection();
|
||||
|
||||
try (InputStream in = source.openStream()) {
|
||||
try (OutputStream out = connection.getOutputStream()) {
|
||||
ByteStreams.copy(in, out);
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
} catch (final IOException exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
package net.monarchpass.piecannon.impl;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.io.ByteSource;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import jodd.http.HttpRequest;
|
||||
import lombok.Data;
|
||||
import net.monarchpass.piecannon.Server;
|
||||
import net.monarchpass.piecannon.util.ByteSourceUploadable;
|
||||
|
||||
@Data
|
||||
public class GoFileServer implements Server {
|
||||
public static final String DEFAULT_HOST = "gofile.io";
|
||||
public static final String DEFAULT_API_HOST = "apiv2." + DEFAULT_HOST;
|
||||
|
||||
private final Gson gson = new Gson();
|
||||
private final String label;
|
||||
private String host = DEFAULT_HOST;
|
||||
private String apiHost = DEFAULT_API_HOST;
|
||||
|
||||
@Override
|
||||
public URI upload (String name, ByteSource source) {
|
||||
Response response = request(HttpRequest.get(String.format("https://%s/getServer", apiHost)));
|
||||
|
||||
final String serverId = (String)response.getData().get("server");
|
||||
response = request(HttpRequest.post(String.format("https://%s.%s/uploadFile", serverId, host))
|
||||
.form("file", new ByteSourceUploadable(name, source)));
|
||||
|
||||
final String code = (String)response.getData().get("code");
|
||||
final URI uri = URI.create(String.format("https://%s/d/%s", host, code));
|
||||
return uri;
|
||||
}
|
||||
|
||||
private Response request (final HttpRequest request) {
|
||||
return gson.fromJson(request.send().bodyText(), Response.class);
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Response {
|
||||
public final String status;
|
||||
public final Map<String, Object> data;
|
||||
}
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
package net.monarchpass.piecannon.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
||||
import com.google.common.io.ByteSource;
|
||||
|
||||
import jodd.http.HttpRequest;
|
||||
import jodd.http.HttpResponse;
|
||||
import lombok.Data;
|
||||
import net.monarchpass.piecannon.Server;
|
||||
|
||||
@Data
|
||||
public class WebDavServer implements Server {
|
||||
private final String label;
|
||||
private final URI uri;
|
||||
|
||||
private final String username;
|
||||
private final String password;
|
||||
|
||||
@Override
|
||||
public URI upload (String name, ByteSource source) {
|
||||
try {
|
||||
final URI target = URI.create(uri.toString() + "/" + name.replace(" ", "%20"));
|
||||
final HttpRequest request = HttpRequest.put(target.toString())
|
||||
.body(source.read(), "");
|
||||
|
||||
if (username != null && password != null) {
|
||||
request.basicAuthentication(username, password);
|
||||
}
|
||||
|
||||
final HttpResponse response = request.send();
|
||||
if (response.statusCode() >= 400) {
|
||||
throw new RuntimeException(response.statusCode() + ": " + response.statusPhrase());
|
||||
}
|
||||
|
||||
return target;
|
||||
} catch (final IOException exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
package net.monarchpass.piecannon.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import com.google.common.io.ByteSource;
|
||||
|
||||
import jodd.http.HttpException;
|
||||
import jodd.http.upload.Uploadable;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ByteSourceUploadable implements Uploadable<ByteSource> {
|
||||
private final String fileName;
|
||||
private final ByteSource source;
|
||||
|
||||
@Override
|
||||
public ByteSource getContent () {
|
||||
return source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBytes () {
|
||||
try {
|
||||
return source.read();
|
||||
} catch (final IOException exception) {
|
||||
throw new HttpException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileName () {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMimeType () {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize () {
|
||||
try {
|
||||
return (int) source.size();
|
||||
} catch (final IOException exception) {
|
||||
throw new HttpException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream openInputStream () throws IOException {
|
||||
return source.openStream();
|
||||
}
|
||||
}
|
@@ -1,14 +1,18 @@
|
||||
package net.monarchpass.piecannon.util;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.jcraft.jsch.IdentityRepository;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
|
||||
import net.monarchpass.piecannon.Server;
|
||||
import net.monarchpass.piecannon.impl.FtpServer;
|
||||
import net.monarchpass.piecannon.impl.GoFileServer;
|
||||
import net.monarchpass.piecannon.impl.SftpServer;
|
||||
import net.monarchpass.piecannon.impl.WebDavServer;
|
||||
|
||||
import lombok.Setter;
|
||||
|
||||
@@ -16,9 +20,40 @@ public class ServerFactory implements Function<JsonObject, Server> {
|
||||
private @Setter IdentityRepository identityRepository;
|
||||
|
||||
public Server apply (final JsonObject object) {
|
||||
final String type = Optional.ofNullable(object.getAsJsonPrimitive("type"))
|
||||
.map(JsonPrimitive::getAsString)
|
||||
.orElse("webdav");
|
||||
|
||||
if (type.equals("webdav")) {
|
||||
return makeWebDavServer(object);
|
||||
} else if (type.equalsIgnoreCase("gofile")) {
|
||||
return new GoFileServer(Optional.ofNullable(object.getAsJsonPrimitive("label")).map(JsonPrimitive::getAsString).orElse("GoFile"));
|
||||
} else if (type.equalsIgnoreCase("ftp")) {
|
||||
return makeFtpServer(object);
|
||||
} else if (type.equalsIgnoreCase("sftp")) {
|
||||
return makeSftpServer(object);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Invalid server type: " + type);
|
||||
}
|
||||
|
||||
private Server makeWebDavServer (final JsonObject object) {
|
||||
final String label = Optional.ofNullable(object.getAsJsonPrimitive("label"))
|
||||
.map(JsonPrimitive::getAsString)
|
||||
.orElse("WebDav");
|
||||
final String username = Optional.ofNullable(object.getAsJsonPrimitive("username"))
|
||||
.map(JsonPrimitive::getAsString)
|
||||
.orElse(null);
|
||||
final String password = Optional.ofNullable(object.getAsJsonPrimitive("password"))
|
||||
.map(JsonPrimitive::getAsString)
|
||||
.orElse(null);
|
||||
final String url = object.getAsJsonPrimitive("url").getAsString();
|
||||
|
||||
return new WebDavServer(
|
||||
label, URI.create(url), username, password
|
||||
);
|
||||
}
|
||||
|
||||
private Server makeSftpServer (final JsonObject object) {
|
||||
final String host = object.getAsJsonPrimitive("host").getAsString();
|
||||
final String label = object.has("label") ? object.getAsJsonPrimitive("label").getAsString() : host;
|
||||
@@ -33,4 +68,19 @@ public class ServerFactory implements Function<JsonObject, Server> {
|
||||
path, URI.create(url), identityRepository
|
||||
);
|
||||
}
|
||||
|
||||
private Server makeFtpServer (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() : 21;
|
||||
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 FtpServer(
|
||||
label, host, port, username, password,
|
||||
path, URI.create(url)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
27
lib/src/test/java/net/monarchpass/piecannon/GoFileTest.java
Normal file
27
lib/src/test/java/net/monarchpass/piecannon/GoFileTest.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package net.monarchpass.piecannon;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import lombok.extern.java.Log;
|
||||
import net.monarchpass.piecannon.impl.GoFileServer;
|
||||
|
||||
@Log
|
||||
public class GoFileTest {
|
||||
@Test
|
||||
@Disabled
|
||||
public void test () throws Exception {
|
||||
try {
|
||||
System.setProperty("https.protocols", "TLSv1.2");
|
||||
final GoFileServer server = new GoFileServer("GoFileTest");
|
||||
assertThat(PieCannon.testServer(server)).isTrue();
|
||||
} catch (final Exception exception) {
|
||||
log.log(Level.SEVERE, "", exception);
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
[{
|
||||
"type": "ftp",
|
||||
"host": "example.com",
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
|
Reference in New Issue
Block a user