init repo
This commit is contained in:
commit
bbf9f1151f
70
.gitignore
vendored
Normal file
70
.gitignore
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
*.db
|
||||
*.sqlite*
|
||||
settings.json
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Manifest created by build
|
||||
MANIFEST.MF
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear on external disk
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
nbproject/
|
||||
dist/
|
||||
build/
|
||||
/bin/
|
||||
|
||||
# Files created by the bot inside resources folder
|
||||
resources/bannedUsers.bin
|
||||
resources/botlogin.txt
|
||||
resources/phantombot.db
|
||||
resources/stacktrace.txt
|
||||
resources/stdio.txt
|
||||
resources/web/default.html
|
||||
resources/web/default.txt
|
||||
resources/scripts/*
|
||||
|
||||
# Do not include the class files for GenerateTwitterTokens
|
||||
genTwitterTokens/classes/generatetwittertokens
|
||||
|
||||
# IntelliJ Project files
|
||||
.idea
|
||||
out/
|
||||
*.iml
|
||||
|
||||
# Netbeans Project files
|
||||
nbproject
|
||||
|
||||
# Eclipse Project files
|
148
src/de/gurkengewuerz/termbin/Config.java
Normal file
148
src/de/gurkengewuerz/termbin/Config.java
Normal file
@ -0,0 +1,148 @@
|
||||
package de.gurkengewuerz.termbin;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONTokener;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.AccessDeniedException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Created by gurkengewuerz.de on 02.07.2017.
|
||||
*/
|
||||
public class Config extends JSONObject {
|
||||
private File file;
|
||||
private static List<String> ban = new ArrayList<>();
|
||||
private static List<String> whitelist = new ArrayList<>();
|
||||
|
||||
public Config(File file) throws IOException {
|
||||
this.file = file;
|
||||
|
||||
if (!file.exists()) {
|
||||
file.createNewFile();
|
||||
}
|
||||
|
||||
if (!file.isFile()) {
|
||||
throw new FileNotFoundException(file.getAbsolutePath() + " not found");
|
||||
}
|
||||
|
||||
if (!file.canRead() || !file.canWrite()) {
|
||||
throw new AccessDeniedException(file.getAbsolutePath() + " is not accessable");
|
||||
}
|
||||
|
||||
this.put("log", "termvin.log");
|
||||
this.put("database", "db.sqlite3");
|
||||
this.put("domain", "http://localhost/");
|
||||
|
||||
JSONObject uploadserver = new JSONObject();
|
||||
uploadserver.put("port", 8888);
|
||||
uploadserver.put("bind", "0.0.0.0");
|
||||
this.put("uploadserver", uploadserver);
|
||||
|
||||
JSONObject dataserver = new JSONObject();
|
||||
dataserver.put("port", 8080);
|
||||
this.put("dataserver", dataserver);
|
||||
|
||||
JSONObject apiserver = new JSONObject();
|
||||
apiserver.put("port", 9090);
|
||||
this.put("apiserver", apiserver);
|
||||
|
||||
JSONArray ban_list = new JSONArray();
|
||||
ban_list.put("125.38.39.40");
|
||||
ban_list.put("27.46.74.43");
|
||||
ban_list.put("210.35.171.4");
|
||||
this.put("ban_list", ban_list);
|
||||
|
||||
JSONArray white_list = new JSONArray();
|
||||
white_list.put("127.0.0.1");
|
||||
white_list.put("192.168.1.36");
|
||||
this.put("white_list", white_list);
|
||||
}
|
||||
|
||||
public void save() {
|
||||
try {
|
||||
FileWriter fw = new FileWriter(file.getAbsolutePath());
|
||||
fw.write(this.toString(4));
|
||||
fw.close();
|
||||
loadRestData();
|
||||
} catch (IOException e) {
|
||||
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void load() {
|
||||
try {
|
||||
String content = new String(Files.readAllBytes(file.toPath()), "UTF-8");
|
||||
if (content.isEmpty()) {
|
||||
save();
|
||||
return;
|
||||
}
|
||||
JSONTokener jt = new JSONTokener(content);
|
||||
if (jt.nextClean() != 123) {
|
||||
throw jt.syntaxError("A JSONObject text must begin with '{'");
|
||||
} else {
|
||||
while (jt.more()) {
|
||||
char c = jt.nextClean();
|
||||
switch (c) {
|
||||
case '\u0000':
|
||||
throw jt.syntaxError("A JSONObject text must end with '}'");
|
||||
case '}':
|
||||
return;
|
||||
default:
|
||||
jt.back();
|
||||
String key = jt.nextValue().toString();
|
||||
c = jt.nextClean();
|
||||
if (c != 58) {
|
||||
throw jt.syntaxError("Expected a ':' after a key");
|
||||
}
|
||||
|
||||
this.remove(key);
|
||||
this.putOnce(key, jt.nextValue());
|
||||
switch (jt.nextClean()) {
|
||||
case ',':
|
||||
case ';':
|
||||
if (jt.nextClean() == 125) {
|
||||
return;
|
||||
}
|
||||
|
||||
jt.back();
|
||||
break;
|
||||
case '}':
|
||||
loadRestData();
|
||||
return;
|
||||
default:
|
||||
throw jt.syntaxError("Expected a ',' or '}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadRestData() {
|
||||
JSONArray json_whitelist = getJSONArray("white_list");
|
||||
for (int i = 0; i < json_whitelist.length(); i++) {
|
||||
whitelist.add(json_whitelist.getString(i));
|
||||
}
|
||||
|
||||
JSONArray json_ban = getJSONArray("ban_list");
|
||||
for (int i = 0; i < json_ban.length(); i++) {
|
||||
if (whitelist.contains(json_ban.getString(i))) continue;
|
||||
ban.add(json_ban.getString(i));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isBanned(String ipv4) {
|
||||
return ban.contains(ipv4);
|
||||
}
|
||||
}
|
61
src/de/gurkengewuerz/termbin/Database.java
Normal file
61
src/de/gurkengewuerz/termbin/Database.java
Normal file
@ -0,0 +1,61 @@
|
||||
package de.gurkengewuerz.termbin;
|
||||
|
||||
import java.sql.*;
|
||||
|
||||
/**
|
||||
* Created by gurkengewuerz.de on 08.04.2017.
|
||||
*/
|
||||
public class Database {
|
||||
|
||||
private String sUrl = null;
|
||||
private int iTimeout = 30;
|
||||
private Connection conn = null;
|
||||
private Statement statement = null;
|
||||
|
||||
|
||||
public Database(String sUrlToLoad) throws Exception {
|
||||
setUrl(sUrlToLoad);
|
||||
setConnection();
|
||||
setStatement();
|
||||
}
|
||||
|
||||
private void setUrl(String sUrlVar) {
|
||||
sUrl = sUrlVar;
|
||||
}
|
||||
|
||||
private void setConnection() throws Exception {
|
||||
conn = DriverManager.getConnection("jdbc:sqlite:" + sUrl);
|
||||
}
|
||||
|
||||
|
||||
public Connection getConnection() {
|
||||
return conn;
|
||||
}
|
||||
|
||||
private void setStatement() throws Exception {
|
||||
if (conn == null) {
|
||||
setConnection();
|
||||
}
|
||||
statement = conn.createStatement();
|
||||
statement.setQueryTimeout(iTimeout); // set timeout to 30 sec.
|
||||
}
|
||||
|
||||
public PreparedStatement getPreparedStatement(String sql) throws SQLException {
|
||||
return conn.prepareStatement(sql);
|
||||
}
|
||||
|
||||
public void executeUpdate(String instruction) throws SQLException {
|
||||
statement.executeUpdate(instruction);
|
||||
}
|
||||
|
||||
public ResultSet executeQuery(String instruction) throws SQLException {
|
||||
return statement.executeQuery(instruction);
|
||||
}
|
||||
|
||||
public void closeConnection() {
|
||||
try {
|
||||
conn.close();
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
}
|
111
src/de/gurkengewuerz/termbin/Server/APIHandler.java
Normal file
111
src/de/gurkengewuerz/termbin/Server/APIHandler.java
Normal file
@ -0,0 +1,111 @@
|
||||
package de.gurkengewuerz.termbin.Server;
|
||||
|
||||
import de.gurkengewuerz.termbin.Termbin;
|
||||
import de.gurkengewuerz.termbin.Utils.ImageUtils;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Created by gurkengewuerz.de on 02.07.2017.
|
||||
*/
|
||||
public class APIHandler extends AbstractHandler {
|
||||
|
||||
@Override
|
||||
public void handle(String s, Request request, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException, ServletException {
|
||||
Logger.getLogger(getClass().getName()).log(Level.INFO, "API Request by " + request.getRemoteAddr() + "@" + s);
|
||||
|
||||
if(Termbin.getConfig().isBanned(request.getRemoteAddr())) {
|
||||
request.setHandled(true);
|
||||
Logger.getLogger(getClass().getName()).log(Level.INFO, "API Request by " + request.getRemoteAddr() + "@" + s + " closed BANNED");
|
||||
return;
|
||||
}
|
||||
|
||||
JSONObject returnObject = null;
|
||||
JSONArray returnArray = null;
|
||||
|
||||
request.setCharacterEncoding("UTF-8");
|
||||
httpServletResponse.setCharacterEncoding("UTF-8");
|
||||
|
||||
if (s.equals("/")) { // Describe yourself
|
||||
httpServletResponse.setStatus(HttpServletResponse.SC_OK);
|
||||
returnObject = new JSONObject();
|
||||
returnObject.put("self", "/");
|
||||
returnObject.put("upload", "/upload/");
|
||||
} else if (s.startsWith("/upload")) {
|
||||
returnObject = new JSONObject();
|
||||
httpServletResponse.setStatus(HttpServletResponse.SC_OK);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
boolean breaked = false;
|
||||
byte[] buff = new byte[1024];
|
||||
while (true) {
|
||||
int n = httpServletRequest.getInputStream().read(buff);
|
||||
if (n < 0) break;
|
||||
baos.write(buff, 0, n);
|
||||
if (baos.size() > 1024 * 1024 * 2) {// 2 MB
|
||||
breaked = true;
|
||||
returnObject.put("error", "File too big");
|
||||
Logger.getLogger(getClass().getName()).log(Level.INFO, "API Request by " + request.getRemoteAddr() + "@" + s + " closed FILE TOO BIG");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!breaked) {
|
||||
byte[] data = baos.toByteArray();
|
||||
|
||||
if (data.length > 3) {
|
||||
String dataString = new String(data, "UTF-8");
|
||||
|
||||
Termbin.FileType ft = Termbin.FileType.TXT;
|
||||
|
||||
if (ImageUtils.isValidPNG(data))
|
||||
ft = Termbin.FileType.PNG;
|
||||
else if (ImageUtils.isValidJPEG(data))
|
||||
ft = Termbin.FileType.JPG;
|
||||
else if (ImageUtils.isValidGIF(data))
|
||||
ft = Termbin.FileType.GIF;
|
||||
|
||||
try {
|
||||
String uploadID = Termbin.upload(request.getRemoteAddr(), dataString, data, ft);
|
||||
returnObject.put("key", uploadID);
|
||||
Logger.getLogger(getClass().getName()).log(Level.INFO, "API Request by " + request.getRemoteAddr() + "@" + s + " closed SUCCESSFULL");
|
||||
} catch (SQLException e) {
|
||||
Logger.getLogger(getClass().getName()).log(Level.INFO, "API Request by " + request.getRemoteAddr() + "@" + s + " closed SERVER ERROR");
|
||||
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
|
||||
returnObject.put("error", "Server error");
|
||||
}
|
||||
} else {
|
||||
returnObject.put("error", "data is empty");
|
||||
Logger.getLogger(getClass().getName()).log(Level.INFO, "API Request by " + request.getRemoteAddr() + "@" + s + " closed EMPTY");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
httpServletResponse.setContentType("application/json; charset=utf-8");
|
||||
|
||||
PrintWriter out = httpServletResponse.getWriter();
|
||||
if (returnObject != null) {
|
||||
out.write(returnObject.toString());
|
||||
} else if (returnArray != null) {
|
||||
out.write(returnArray.toString());
|
||||
} else {
|
||||
returnObject = new JSONObject();
|
||||
returnObject.put("error", "not found");
|
||||
out.write(returnObject.toString());
|
||||
}
|
||||
|
||||
request.setHandled(true);
|
||||
}
|
||||
}
|
64
src/de/gurkengewuerz/termbin/Server/DataHandler.java
Normal file
64
src/de/gurkengewuerz/termbin/Server/DataHandler.java
Normal file
@ -0,0 +1,64 @@
|
||||
package de.gurkengewuerz.termbin.Server;
|
||||
|
||||
import de.gurkengewuerz.termbin.Termbin;
|
||||
import de.gurkengewuerz.termbin.Utils.SQLInjectionEscaper;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Created by gurkengewuerz.de on 02.07.2017.
|
||||
*/
|
||||
public class DataHandler extends AbstractHandler {
|
||||
|
||||
@Override
|
||||
public void handle(String s, Request request, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException, ServletException {
|
||||
Logger.getLogger(getClass().getName()).log(Level.INFO, "Request by " + request.getRemoteAddr() + "@" + s);
|
||||
|
||||
if (Termbin.getConfig().isBanned(request.getRemoteAddr())) {
|
||||
request.setHandled(true);
|
||||
Logger.getLogger(getClass().getName()).log(Level.INFO, "Request by " + request.getRemoteAddr() + "@" + s + " closed BANNED");
|
||||
return;
|
||||
}
|
||||
|
||||
request.setCharacterEncoding("UTF-8");
|
||||
httpServletResponse.setCharacterEncoding("UTF-8");
|
||||
try {
|
||||
ResultSet rs = Termbin.getDatabase().executeQuery("SELECT * FROM data WHERE uniqueid = '" + SQLInjectionEscaper.escapeString(s.substring(1), false) + "' LIMIT 1;");
|
||||
|
||||
boolean found = false;
|
||||
httpServletResponse.setStatus(HttpServletResponse.SC_OK);
|
||||
while (rs.next()) {
|
||||
found = true;
|
||||
httpServletResponse.setContentType(rs.getString("filetype"));
|
||||
if (rs.getString("filetype").equals("text/plain")) {
|
||||
httpServletResponse.getOutputStream().write(rs.getString("text").getBytes("UTF-8"));
|
||||
} else {
|
||||
httpServletResponse.setContentLength(rs.getBytes("rawData").length);
|
||||
httpServletResponse.getOutputStream().write(rs.getBytes("rawData"));
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
httpServletResponse.getOutputStream().write("<html><body><img src='https://http.cat/404'/></body></html>".getBytes("UTF-8"));
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
|
||||
httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
httpServletResponse.getOutputStream().write("<html><body><img src='https://http.cat/500'/></body></html>".getBytes("UTF-8"));
|
||||
|
||||
}
|
||||
|
||||
request.setHandled(true);
|
||||
}
|
||||
}
|
131
src/de/gurkengewuerz/termbin/Server/UploadServer.java
Normal file
131
src/de/gurkengewuerz/termbin/Server/UploadServer.java
Normal file
@ -0,0 +1,131 @@
|
||||
package de.gurkengewuerz.termbin.Server;
|
||||
|
||||
import de.gurkengewuerz.termbin.Termbin;
|
||||
import de.gurkengewuerz.termbin.Utils.ImageUtils;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.*;
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Created by gurkengewuerz.de on 02.07.2017.
|
||||
*/
|
||||
public class UploadServer {
|
||||
|
||||
public UploadServer(String ipv4, int port) throws IOException {
|
||||
Logger.getLogger("Main").log(Level.INFO, "Starting...");
|
||||
int clientNumber = 1;
|
||||
|
||||
ServerSocket listener = new ServerSocket(port);
|
||||
if (!ipv4.isEmpty() && !ipv4.equals("0.0.0.0")) {
|
||||
listener.bind(new InetSocketAddress(ipv4, port));
|
||||
}
|
||||
Logger.getLogger(getClass().getName()).log(Level.INFO, "Started. Waiting for Clients.");
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
Socket socketOfServer = listener.accept();
|
||||
new ServiceThread(socketOfServer, clientNumber++).start();
|
||||
}
|
||||
} finally {
|
||||
listener.close();
|
||||
}
|
||||
}
|
||||
|
||||
private class ServiceThread extends Thread {
|
||||
|
||||
private Socket socketOfServer;
|
||||
private String client;
|
||||
private String clientIP;
|
||||
|
||||
public ServiceThread(Socket socketOfServer, int clientNumber) {
|
||||
this.socketOfServer = socketOfServer;
|
||||
this.clientIP = socketOfServer.getInetAddress().toString().substring(1);
|
||||
this.client = "client#" + clientNumber + "@" + clientIP + ":" + socketOfServer.getPort();
|
||||
try {
|
||||
this.socketOfServer.setSoTimeout(2000);
|
||||
} catch (SocketException e) {
|
||||
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
|
||||
}
|
||||
Logger.getLogger(getClass().getName()).log(Level.INFO, "opened " + client + " at " + socketOfServer.getLocalAddress() + ":" + socketOfServer.getLocalPort());
|
||||
|
||||
if (Termbin.getConfig().isBanned(clientIP)) {
|
||||
Logger.getLogger(getClass().getName()).log(Level.INFO, "closed " + client + " with error banned");
|
||||
try {
|
||||
socketOfServer.close();
|
||||
} catch (IOException e) {
|
||||
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (socketOfServer.isClosed()) return;
|
||||
BufferedWriter os = new BufferedWriter(new OutputStreamWriter(socketOfServer.getOutputStream()));
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
try {
|
||||
byte[] buff = new byte[1024];
|
||||
while (true) {
|
||||
int n = socketOfServer.getInputStream().read(buff);
|
||||
if (n < 0) break;
|
||||
baos.write(buff, 0, n);
|
||||
if (baos.size() > 1024 * 1024 * 2) {// 2 MB
|
||||
Logger.getLogger(getClass().getName()).log(Level.INFO, "closed " + client + " with file to big");
|
||||
os.write("File to big!");
|
||||
os.newLine();
|
||||
os.flush();
|
||||
socketOfServer.close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (SocketTimeoutException e) {
|
||||
Logger.getLogger(getClass().getName()).log(Level.INFO, "closed " + client + " with error invalid data");
|
||||
os.write("Invalid data");
|
||||
os.newLine();
|
||||
os.flush();
|
||||
socketOfServer.close();
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] data = baos.toByteArray();
|
||||
if (data.length > 3) {
|
||||
String dataString = new String(data, "UTF-8");
|
||||
|
||||
Termbin.FileType ft = Termbin.FileType.TXT;
|
||||
|
||||
if (ImageUtils.isValidPNG(data))
|
||||
ft = Termbin.FileType.PNG;
|
||||
else if (ImageUtils.isValidJPEG(data))
|
||||
ft = Termbin.FileType.JPG;
|
||||
else if (ImageUtils.isValidGIF(data))
|
||||
ft = Termbin.FileType.GIF;
|
||||
|
||||
try {
|
||||
String uploadID = Termbin.upload(clientIP, dataString, data, ft);
|
||||
os.write(Termbin.getConfig().get("domain") + uploadID);
|
||||
Logger.getLogger(getClass().getName()).log(Level.INFO, "closed " + client + " successfully with ID#" + uploadID);
|
||||
} catch (SQLException e) {
|
||||
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
|
||||
os.write("Server Error");
|
||||
}
|
||||
} else {
|
||||
os.write("Invalid data");
|
||||
Logger.getLogger(getClass().getName()).log(Level.INFO, "closed " + client + " with error invalid data");
|
||||
}
|
||||
os.newLine();
|
||||
os.flush();
|
||||
socketOfServer.close();
|
||||
} catch (IOException e) {
|
||||
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
158
src/de/gurkengewuerz/termbin/Termbin.java
Normal file
158
src/de/gurkengewuerz/termbin/Termbin.java
Normal file
@ -0,0 +1,158 @@
|
||||
package de.gurkengewuerz.termbin;
|
||||
|
||||
import de.gurkengewuerz.termbin.Server.APIHandler;
|
||||
import de.gurkengewuerz.termbin.Server.DataHandler;
|
||||
import de.gurkengewuerz.termbin.Server.UploadServer;
|
||||
import de.gurkengewuerz.termbin.Utils.HashUtils;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Created by gurkengewuerz.de on 02.07.2017.
|
||||
*/
|
||||
public class Termbin {
|
||||
|
||||
private static Database db;
|
||||
private static Config conf;
|
||||
|
||||
public static void main(String args[]) {
|
||||
File f = new File("." + File.separator + "settings.json");
|
||||
|
||||
try {
|
||||
conf = new Config(f);
|
||||
} catch (IOException e) {
|
||||
Logger.getLogger(Termbin.class.getName()).log(Level.SEVERE, null, e);
|
||||
System.exit(1);
|
||||
}
|
||||
conf.load();
|
||||
|
||||
try {
|
||||
db = new Database(conf.getString("database"));
|
||||
createDatabase();
|
||||
} catch (Exception e) {
|
||||
Logger.getLogger(Termbin.class.getName()).log(Level.SEVERE, null, e);
|
||||
}
|
||||
|
||||
Runnable uploadServerTask = () -> {
|
||||
try {
|
||||
JSONObject uploadServerConf = conf.getJSONObject("uploadserver");
|
||||
new UploadServer(uploadServerConf.getString("bind"), uploadServerConf.getInt("port"));
|
||||
} catch (IOException e) {
|
||||
Logger.getLogger(Termbin.class.getName()).log(Level.SEVERE, null, e);
|
||||
}
|
||||
};
|
||||
Thread uploadServerThread = new Thread(uploadServerTask);
|
||||
uploadServerThread.start();
|
||||
|
||||
|
||||
Runnable dataServerTask = () -> {
|
||||
try {
|
||||
JSONObject dataServerConf = conf.getJSONObject("dataserver");
|
||||
Server dataServer = new Server(dataServerConf.getInt("port"));
|
||||
dataServer.setHandler(new DataHandler());
|
||||
|
||||
dataServer.start();
|
||||
dataServer.join();
|
||||
} catch (Exception e) {
|
||||
Logger.getLogger(Termbin.class.getName()).log(Level.SEVERE, null, e);
|
||||
}
|
||||
};
|
||||
Thread dataServerThread = new Thread(dataServerTask);
|
||||
dataServerThread.start();
|
||||
|
||||
|
||||
Runnable apiServerTask = () -> {
|
||||
try {
|
||||
JSONObject apiServerConf = conf.getJSONObject("apiserver");
|
||||
Server apiServer = new Server(apiServerConf.getInt("port"));
|
||||
apiServer.setHandler(new APIHandler());
|
||||
|
||||
apiServer.start();
|
||||
apiServer.join();
|
||||
} catch (Exception e) {
|
||||
Logger.getLogger(Termbin.class.getName()).log(Level.SEVERE, null, e);
|
||||
}
|
||||
};
|
||||
Thread apiServerThread = new Thread(apiServerTask);
|
||||
apiServerThread.start();
|
||||
|
||||
// TODO: Check if all threads started successfully
|
||||
// TODO: log to file
|
||||
// TODO: Arguments (Settings file)
|
||||
// TODO: print start values (all ports etc.)
|
||||
// TODO: Config Max lifetime => Check insert time on request
|
||||
// TODO: Maven
|
||||
}
|
||||
|
||||
public static Database getDatabase() {
|
||||
return db;
|
||||
}
|
||||
|
||||
|
||||
public static Config getConfig() {
|
||||
return conf;
|
||||
}
|
||||
|
||||
public static String upload(String ip, String text, byte[] rawData, FileType fileType) throws SQLException {
|
||||
String answerID = HashUtils.getSha256(System.currentTimeMillis() + "").substring(0, 8);
|
||||
PreparedStatement ps = getDatabase().getPreparedStatement("INSERT INTO data (uniqueid, timestamp, fromClient, filetype, text, rawData) VALUES (?, ?, ?, ?, ?, ?);");
|
||||
ps.setString(1, answerID);
|
||||
ps.setInt(2, (int) (System.currentTimeMillis() / 1000));
|
||||
ps.setString(3, ip);
|
||||
ps.setString(4, fileType.toString());
|
||||
if (fileType.equals(FileType.TXT)) {
|
||||
ps.setString(5, text);
|
||||
ps.setBytes(6, null);
|
||||
} else {
|
||||
ps.setString(5, null);
|
||||
ps.setBytes(6, rawData);
|
||||
}
|
||||
|
||||
ps.execute();
|
||||
return answerID;
|
||||
}
|
||||
|
||||
public enum FileType {
|
||||
TXT,
|
||||
PNG,
|
||||
JPG,
|
||||
GIF;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
switch (this) {
|
||||
case TXT:
|
||||
return "text/plain";
|
||||
case PNG:
|
||||
return "image/png";
|
||||
case JPG:
|
||||
return "image/jpeg";
|
||||
case GIF:
|
||||
return "image/gif";
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void createDatabase() throws SQLException {
|
||||
getDatabase().executeUpdate(
|
||||
"CREATE TABLE IF NOT EXISTS data (" +
|
||||
" id INTEGER PRIMARY KEY AUTOINCREMENT," +
|
||||
" uniqueid char(255) NOT NULL UNIQUE," +
|
||||
" text text NULL," +
|
||||
" rawData blob NULL," +
|
||||
" timestamp float NOT NULL," +
|
||||
" fromClient char(255) NOT NULL," +
|
||||
" filetype char(255) NOT NULL" +
|
||||
");"
|
||||
);
|
||||
}
|
||||
}
|
24
src/de/gurkengewuerz/termbin/Utils/HashUtils.java
Normal file
24
src/de/gurkengewuerz/termbin/Utils/HashUtils.java
Normal file
@ -0,0 +1,24 @@
|
||||
package de.gurkengewuerz.termbin.Utils;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
|
||||
/**
|
||||
* Created by gurkengewuerz.de on 02.07.2017.
|
||||
*/
|
||||
public class HashUtils {
|
||||
public static String getSha256(String value) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
md.update(value.getBytes());
|
||||
return bytesToHex(md.digest());
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static String bytesToHex(byte[] bytes) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (byte b : bytes) result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
|
||||
return result.toString();
|
||||
}
|
||||
}
|
78
src/de/gurkengewuerz/termbin/Utils/ImageUtils.java
Normal file
78
src/de/gurkengewuerz/termbin/Utils/ImageUtils.java
Normal file
@ -0,0 +1,78 @@
|
||||
package de.gurkengewuerz.termbin.Utils;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ImageUtils {
|
||||
|
||||
static byte[] PNG_HEADER = hexStringToByteArray("89504e470d0a1a0a");
|
||||
/**
|
||||
* Check if the image is a PNG. The first eight bytes of a PNG file always
|
||||
* contain the following (decimal) values: 137 80 78 71 13 10 26 10 / Hex:
|
||||
* 89 50 4e 47 0d 0a 1a 0a
|
||||
*/
|
||||
public static boolean isValidPNG(byte[] is) {
|
||||
try {
|
||||
byte[] b = Arrays.copyOfRange(is, 0, 8);
|
||||
if (Arrays.equals(b, PNG_HEADER)) {
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
//Ignore
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the image is a JPEG. JPEG image files begin with FF D8 and end
|
||||
* with FF D9
|
||||
*/
|
||||
public static boolean isValidJPEG(byte[] is) {
|
||||
try {
|
||||
// check first 2 bytes:
|
||||
byte[] b = Arrays.copyOfRange(is, 0, 2);
|
||||
if ((b[0]&0xff) != 0xff || (b[1]&0xff) != 0xd8) {
|
||||
return false;
|
||||
}
|
||||
// check last 2 bytes:
|
||||
b = Arrays.copyOfRange(is, is.length - 2, is.length);
|
||||
if ((b[0]&0xff) != 0xff || (b[1]&0xff) != 0xd9) {
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Ignore
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Check if the image is a valid GIF. GIF files start with GIF and 87a or 89a.
|
||||
* http://www.onicos.com/staff/iz/formats/gif.html
|
||||
*/
|
||||
public static boolean isValidGIF(byte[] is) {
|
||||
try {
|
||||
byte[] b = Arrays.copyOfRange(is, 0, 6);
|
||||
//check 1st 3 bytes
|
||||
if(b[0]!='G' || b[1]!='I' || b[2]!='F') {
|
||||
return false;
|
||||
}
|
||||
if(b[3]!='8' || !(b[4]=='7' || b[4]=='9') || b[5]!='a') {
|
||||
return false;
|
||||
}
|
||||
} catch(Exception e) {
|
||||
// Ignore
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static byte[] hexStringToByteArray(String s) {
|
||||
int len = s.length();
|
||||
byte[] data = new byte[len / 2];
|
||||
for (int i = 0; i < len; i += 2) {
|
||||
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
|
||||
+ Character.digit(s.charAt(i+1), 16));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
71
src/de/gurkengewuerz/termbin/Utils/SQLInjectionEscaper.java
Normal file
71
src/de/gurkengewuerz/termbin/Utils/SQLInjectionEscaper.java
Normal file
@ -0,0 +1,71 @@
|
||||
package de.gurkengewuerz.termbin.Utils;
|
||||
|
||||
public class SQLInjectionEscaper {
|
||||
|
||||
public static String escapeString(String x, boolean escapeDoubleQuotes) {
|
||||
StringBuilder sBuilder = new StringBuilder(x.length() * 11 / 10);
|
||||
|
||||
int stringLength = x.length();
|
||||
|
||||
for (int i = 0; i < stringLength; ++i) {
|
||||
char c = x.charAt(i);
|
||||
|
||||
switch (c) {
|
||||
case 0: /* Must be escaped for 'mysql' */
|
||||
sBuilder.append('\\');
|
||||
sBuilder.append('0');
|
||||
|
||||
break;
|
||||
|
||||
case '\n': /* Must be escaped for logs */
|
||||
sBuilder.append('\\');
|
||||
sBuilder.append('n');
|
||||
|
||||
break;
|
||||
|
||||
case '\r':
|
||||
sBuilder.append('\\');
|
||||
sBuilder.append('r');
|
||||
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
sBuilder.append('\\');
|
||||
sBuilder.append('\\');
|
||||
|
||||
break;
|
||||
|
||||
case '\'':
|
||||
sBuilder.append('\\');
|
||||
sBuilder.append('\'');
|
||||
|
||||
break;
|
||||
|
||||
case '"': /* Better safe than sorry */
|
||||
if (escapeDoubleQuotes) {
|
||||
sBuilder.append('\\');
|
||||
}
|
||||
|
||||
sBuilder.append('"');
|
||||
|
||||
break;
|
||||
|
||||
case '\032': /* This gives problems on Win32 */
|
||||
sBuilder.append('\\');
|
||||
sBuilder.append('Z');
|
||||
|
||||
break;
|
||||
|
||||
case '\u00a5':
|
||||
case '\u20a9':
|
||||
// escape characters interpreted as backslash by mysql
|
||||
// fall through
|
||||
|
||||
default:
|
||||
sBuilder.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return sBuilder.toString();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user