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