work
This commit is contained in:
parent
2da318ef44
commit
6572a3d317
58
pom.xml
58
pom.xml
@ -7,19 +7,6 @@
|
|||||||
<groupId>de.gurkengewuerz</groupId>
|
<groupId>de.gurkengewuerz</groupId>
|
||||||
<artifactId>postfix-rest-send</artifactId>
|
<artifactId>postfix-rest-send</artifactId>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<version>1.0-SNAPSHOT</version>
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
|
||||||
<configuration>
|
|
||||||
<source>1.8</source>
|
|
||||||
<target>1.8</target>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.simplejavamail</groupId>
|
<groupId>org.simplejavamail</groupId>
|
||||||
@ -46,5 +33,50 @@
|
|||||||
<artifactId>json</artifactId>
|
<artifactId>json</artifactId>
|
||||||
<version>20170516</version>
|
<version>20170516</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
|
<version>5.1.39</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.5.1</version>
|
||||||
|
<configuration>
|
||||||
|
<source>1.8</source>
|
||||||
|
<target>1.8</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<version>2.6</version>
|
||||||
|
<configuration>
|
||||||
|
<appendAssemblyId>false</appendAssemblyId>
|
||||||
|
<finalName>${project.artifactId}</finalName>
|
||||||
|
<descriptorRefs>
|
||||||
|
<descriptorRef>
|
||||||
|
jar-with-dependencies
|
||||||
|
</descriptorRef>
|
||||||
|
</descriptorRefs>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<mainClass>${project.groupId}.postfix_rest_send.Main</mainClass>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>assamble</id>
|
||||||
|
<goals>
|
||||||
|
<goal>single</goal>
|
||||||
|
</goals>
|
||||||
|
<phase>package</phase>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
</project>
|
</project>
|
136
src/main/java/de/gurkengewuerz/postfix_rest_send/Config.java
Normal file
136
src/main/java/de/gurkengewuerz/postfix_rest_send/Config.java
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
package de.gurkengewuerz.postfix_rest_send;
|
||||||
|
|
||||||
|
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.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by gurkengewuerz.de on 02.07.2017.
|
||||||
|
*/
|
||||||
|
public class Config extends JSONObject {
|
||||||
|
private File file;
|
||||||
|
private JSONObject database;
|
||||||
|
private boolean firstRun = false;
|
||||||
|
|
||||||
|
public Config(File file) throws IOException {
|
||||||
|
this.file = file;
|
||||||
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
file.createNewFile();
|
||||||
|
firstRun = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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("debug", true);
|
||||||
|
this.put("disable_bruteforcefilter", false);
|
||||||
|
this.put("token_expire", 730); // houres/1M
|
||||||
|
this.put("http_port", 8081);
|
||||||
|
this.put("postfixadmin_encryption", "md5crypt");
|
||||||
|
this.put("postfixadmin_path", "/var/www/html/postfixadmin/");
|
||||||
|
|
||||||
|
JSONObject database = new JSONObject();
|
||||||
|
database.put("port", 3306);
|
||||||
|
database.put("host", "127.0.0.1");
|
||||||
|
database.put("db", "mail");
|
||||||
|
database.put("username", "postfixadmin");
|
||||||
|
database.put("password", "abc123");
|
||||||
|
this.put("database", database);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 '}':
|
||||||
|
save();
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
throw jt.syntaxError("Expected a ',' or '}'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean debug() {
|
||||||
|
return getBoolean("debug");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadRestData() {
|
||||||
|
database = getJSONObject("database");
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSONObject getDatabaseConfig() {
|
||||||
|
return database;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDBURL() {
|
||||||
|
return "jdbc:mysql://" + getDatabaseConfig().getString("host") + ":" + getDatabaseConfig().getInt("port") + "/" + getDatabaseConfig().getString("db");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFirstRun() {
|
||||||
|
return firstRun;
|
||||||
|
}
|
||||||
|
}
|
@ -1,104 +1,254 @@
|
|||||||
package de.gurkengewuerz.postfix_rest_send;
|
package de.gurkengewuerz.postfix_rest_send;
|
||||||
|
|
||||||
|
import de.gurkengewuerz.postfix_rest_send.objects.BruteforceFilter;
|
||||||
|
import de.gurkengewuerz.postfix_rest_send.objects.ReturnHolder;
|
||||||
|
import de.gurkengewuerz.postfix_rest_send.utils.BashUtils;
|
||||||
|
import de.gurkengewuerz.postfix_rest_send.utils.HtmlToPlainText;
|
||||||
|
import de.gurkengewuerz.postfix_rest_send.utils.RandomUtils;
|
||||||
import fi.iki.elonen.NanoHTTPD;
|
import fi.iki.elonen.NanoHTTPD;
|
||||||
|
import org.hazlewood.connor.bottema.emailaddress.EmailAddressCriteria;
|
||||||
|
import org.hazlewood.connor.bottema.emailaddress.EmailAddressValidator;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
import org.simplejavamail.email.Email;
|
||||||
|
import org.simplejavamail.mailer.Mailer;
|
||||||
|
import org.simplejavamail.mailer.config.ServerConfig;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import javax.mail.Message;
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.EnumSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by gurkengewuerz.de on 12.07.2017.
|
* Created by gurkengewuerz.de on 12.07.2017.
|
||||||
*/
|
*/
|
||||||
public class Main extends NanoHTTPD {
|
public class Main extends NanoHTTPD {
|
||||||
|
private static MySQL db;
|
||||||
|
private static Config conf;
|
||||||
|
private static Mailer mailer;
|
||||||
|
private static BruteforceFilter filter;
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO:
|
||||||
|
Main umbennen
|
||||||
|
Configurierbare Domains/IPs
|
||||||
|
Post JSON Support
|
||||||
|
*/
|
||||||
|
|
||||||
public Main() throws IOException {
|
public Main() throws IOException {
|
||||||
super(8081);
|
super(conf.getInt("http_port"));
|
||||||
|
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Started Server :" + getListeningPort());
|
||||||
start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
|
start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// http://www.simplejavamail.org/#/debugging
|
public static void main(String[] args) throws IOException, SQLException {
|
||||||
|
String settingsFile = "." + File.separator + "settings.json";
|
||||||
public static void main(String[] args) {
|
if (args.length >= 1) {
|
||||||
// Mailer m = new Mailer(new ServerConfig("localhost", 25));
|
settingsFile = args[0];
|
||||||
// m.setDebug(true);
|
|
||||||
//
|
|
||||||
// Email email = new Email();
|
|
||||||
// email.setFromAddress("admin@gurkengewuerz.de", "admin@gurkengewuerz.de");
|
|
||||||
// email.setReplyToAddress("support@gurkengewuerz.de", "support@gurkengewuerz.de");
|
|
||||||
// email.addRecipient("developer@the-town.net", "developer@the-town.net", Message.RecipientType.TO);
|
|
||||||
// email.setSubject("Email Test");
|
|
||||||
// email.setText("Dies ist ein Email Test von Java.\nHoffentlich bald mit Rest API");
|
|
||||||
// email.setTextHTML("<img src='cid:wink1'><b>We should meet up!</b><img src='cid:wink2'>");
|
|
||||||
//
|
|
||||||
// m.sendMail(email);
|
|
||||||
|
|
||||||
// HtmlToPlainText formatter = new HtmlToPlainText();
|
|
||||||
// formatter.getPlainText()
|
|
||||||
try {
|
|
||||||
System.out.println("Started");
|
|
||||||
new Main();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
File f = new File(settingsFile);
|
||||||
|
|
||||||
|
conf = new Config(f);
|
||||||
|
conf.load();
|
||||||
|
|
||||||
|
if (conf.debug())
|
||||||
|
Logger.getLogger(Main.class.getName()).log(Level.INFO, "Using Settingsfile " + f.getAbsolutePath());
|
||||||
|
|
||||||
|
if (conf.isFirstRun()) System.exit(0);
|
||||||
|
|
||||||
|
db = new MySQL(MySQL.Type.MYSQL, conf.getDBURL(), conf.getDatabaseConfig().getString("username"), conf.getDatabaseConfig().getString("password"));
|
||||||
|
db.executeUpdate("CREATE TABLE IF NOT EXISTS token (" +
|
||||||
|
" tokenid int(11) NOT NULL AUTO_INCREMENT," +
|
||||||
|
" username VARCHAR(255) NOT NULL," +
|
||||||
|
" token VARCHAR(255) NOT NULL," +
|
||||||
|
" created INT NOT NULL," +
|
||||||
|
" expire INT NOT NULL," +
|
||||||
|
" PRIMARY KEY (tokenid), " +
|
||||||
|
" UNIQUE (token)" +
|
||||||
|
" ); ");
|
||||||
|
db.executeUpdate("CREATE TABLE IF NOT EXISTS token_bruteforce (" +
|
||||||
|
" id int(11) NOT NULL AUTO_INCREMENT," +
|
||||||
|
" occurred INT NOT NULL," +
|
||||||
|
" ip VARCHAR(255) NOT NULL," +
|
||||||
|
" PRIMARY KEY (id)" +
|
||||||
|
" ); ");
|
||||||
|
mailer = new Mailer(new ServerConfig("localhost", 25));
|
||||||
|
mailer.setDebug(conf.debug());
|
||||||
|
|
||||||
|
filter = new BruteforceFilter(6);
|
||||||
|
|
||||||
|
new Main();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response serve(IHTTPSession session) {
|
public Response serve(IHTTPSession session) {
|
||||||
|
Map<String, String> headers = session.getHeaders();
|
||||||
|
String ip = headers.get("remote-addr");
|
||||||
|
Logger.getLogger(Main.class.getName()).log(Level.INFO, session.getMethod().name() + ": " + session.getUri() + " " + ip);
|
||||||
Map<String, String> parms = session.getParms();
|
Map<String, String> parms = session.getParms();
|
||||||
|
|
||||||
JSONObject json = new JSONObject("{'error':'not found'}");
|
JSONObject json = new JSONObject("{'error':'not found'}");
|
||||||
Response.Status status = Response.Status.NOT_FOUND;
|
Response.Status status = Response.Status.NOT_IMPLEMENTED;
|
||||||
|
|
||||||
|
if (filter.banned(ip))
|
||||||
|
return getError(Response.Status.FORBIDDEN, "you are temporary banned");
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (session.getMethod().equals(Method.POST))
|
||||||
|
session.parseBody(parms);
|
||||||
|
} catch (IOException | ResponseException e) {
|
||||||
|
return getError(Response.Status.INTERNAL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (db == null) return getError(Response.Status.INTERNAL_ERROR, "Database Error");
|
||||||
|
|
||||||
|
try {
|
||||||
if (session.getUri().startsWith("/authorize")) {
|
if (session.getUri().startsWith("/authorize")) {
|
||||||
if (session.getUri().startsWith("/authorize/add")) {
|
if (session.getUri().startsWith("/authorize/add")) {
|
||||||
String username = parms.get("username");
|
String username = parms.get("username");
|
||||||
String password = parms.get("password");
|
String password = parms.get("password");
|
||||||
|
|
||||||
if (username != null && password != null) {
|
if (username == null || password == null) return getError(Response.Status.UNAUTHORIZED);
|
||||||
System.out.println(checkPassword("/var/www/html/tools/functions.inc.php", "md5", "EMailFam.Schuetrumpf@123", "460b7d128333a4be972d4c7bdb0ee142"));
|
|
||||||
|
|
||||||
|
boolean usernameValid = EmailAddressValidator.isValid(username, EnumSet.of(EmailAddressCriteria.ALLOW_DOMAIN_LITERALS));
|
||||||
status = Response.Status.OK;
|
if (!usernameValid) {
|
||||||
json = new JSONObject("{'token':''}");
|
filter.failed(ip);
|
||||||
} else {
|
return getError(Response.Status.UNAUTHORIZED, "invalid user");
|
||||||
json = new JSONObject("{'error':'username/password is null'}");
|
|
||||||
}
|
}
|
||||||
} else if (session.getUri().startsWith("/authorize/revoke")) {
|
|
||||||
|
|
||||||
|
ResultSet rs = db.executeQuery("SELECT * FROM mailbox WHERE username = ? AND active = 1;", username);
|
||||||
|
int rowcount = 0;
|
||||||
|
while (rs.next()) {
|
||||||
|
rowcount++;
|
||||||
|
}
|
||||||
|
rs.beforeFirst();
|
||||||
|
rs.next();
|
||||||
|
if (rowcount <= 0) {
|
||||||
|
filter.failed(ip);
|
||||||
|
return getError(Response.Status.UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean correct = checkPassword(conf.getString("postfixadmin_encryption"), password, rs.getString("password"));
|
||||||
|
if (!correct) {
|
||||||
|
filter.failed(ip);
|
||||||
|
return getError(Response.Status.UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
String token = RandomUtils.rndToken(username);
|
||||||
|
long expires = (System.currentTimeMillis() / 1000) + (conf.getInt("token_expire") * 60 * 60);
|
||||||
|
db.executeUpdate("INSERT INTO token (username, token, created, expire) VALUES (?,?,?,?);", username, token, (System.currentTimeMillis() / 1000), expires);
|
||||||
|
status = Response.Status.OK;
|
||||||
|
json = new JSONObject("{'token':'" + token + "', 'expires': " + expires + "}");
|
||||||
|
} else if (session.getUri().startsWith("/authorize/revoke")) {
|
||||||
|
String token = parms.get("token");
|
||||||
|
if (token == null) return getError(Response.Status.BAD_REQUEST);
|
||||||
|
db.executeUpdate("DELETE FROM token WHERE token = ?;", token);
|
||||||
|
status = Response.Status.OK;
|
||||||
|
json = new JSONObject("{'status':'ok'}");
|
||||||
}
|
}
|
||||||
} else if (session.getUri().startsWith("/validate/address")) {
|
} else if (session.getUri().startsWith("/validate/address")) {
|
||||||
|
String mail = parms.get("mail");
|
||||||
|
if (mail == null) return getError(Response.Status.BAD_REQUEST);
|
||||||
|
boolean valid = EmailAddressValidator.isValid(mail, EnumSet.of(EmailAddressCriteria.ALLOW_DOMAIN_LITERALS));
|
||||||
|
status = Response.Status.OK;
|
||||||
|
json = new JSONObject("{'valid':" + valid + "}");
|
||||||
} else if (session.getUri().startsWith("/send")) {
|
} else if (session.getUri().startsWith("/send")) {
|
||||||
|
String token = parms.get("token");
|
||||||
|
String to = parms.get("to");
|
||||||
|
String subject = parms.get("to");
|
||||||
|
String text = parms.get("text");
|
||||||
|
String html = parms.get("html");
|
||||||
|
|
||||||
|
if (token == null) return getError(Response.Status.UNAUTHORIZED);
|
||||||
|
|
||||||
|
ResultSet rs = db.executeQuery("SELECT * FROM token WHERE token = ?;", token);
|
||||||
|
int rowcount = 0;
|
||||||
|
while (rs.next()) {
|
||||||
|
rowcount++;
|
||||||
|
}
|
||||||
|
rs.beforeFirst();
|
||||||
|
rs.next();
|
||||||
|
if (rowcount <= 0){
|
||||||
|
filter.banned(ip);
|
||||||
|
return getError(Response.Status.UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rs.getLong("expire") <= System.currentTimeMillis() / 1000)
|
||||||
|
return getError(Response.Status.UNAUTHORIZED, "token expired");
|
||||||
|
|
||||||
|
String username = rs.getString("username");
|
||||||
|
|
||||||
|
if (to == null || subject == null || (text == null && html == null))
|
||||||
|
return getError(Response.Status.BAD_REQUEST);
|
||||||
|
|
||||||
|
ArrayList<String> toList = new ArrayList<>();
|
||||||
|
|
||||||
|
if (to.contains(";")) {
|
||||||
|
for (String mailTo : to.split(";")) {
|
||||||
|
if (!EmailAddressValidator.isValid(mailTo, EnumSet.of(EmailAddressCriteria.ALLOW_DOMAIN_LITERALS)))
|
||||||
|
return getError(Response.Status.BAD_REQUEST, "invalid recipient");
|
||||||
|
toList.add(mailTo);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!EmailAddressValidator.isValid(to, EnumSet.of(EmailAddressCriteria.ALLOW_DOMAIN_LITERALS)))
|
||||||
|
return getError(Response.Status.BAD_REQUEST, "invalid recipient");
|
||||||
|
toList.add(to);
|
||||||
|
}
|
||||||
|
|
||||||
|
Email email = new Email();
|
||||||
|
email.setFromAddress(username, username);
|
||||||
|
toList.forEach(s -> email.addRecipient(s, s, Message.RecipientType.TO));
|
||||||
|
email.setSubject(subject);
|
||||||
|
if (text != null) email.setText(text);
|
||||||
|
if (html != null) email.setTextHTML(html);
|
||||||
|
if (text == null && html != null) {
|
||||||
|
HtmlToPlainText formatter = new HtmlToPlainText();
|
||||||
|
email.setText(formatter.getPlainText(html));
|
||||||
|
}
|
||||||
|
|
||||||
|
mailer.sendMail(email);
|
||||||
|
status = Response.Status.OK;
|
||||||
|
json = new JSONObject("{'status':'send'}");
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, e);
|
||||||
|
return getError(Response.Status.INTERNAL_ERROR);
|
||||||
}
|
}
|
||||||
return newFixedLengthResponse(status, "application/json", json.toString());
|
return newFixedLengthResponse(status, "application/json", json.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean checkPassword(String path, String encryption, String plain, String dbPW) {
|
public boolean checkPassword(String encryption, String plain, String dbPW) {
|
||||||
String crypted = execPHP("-r '$CONF = array(); $CONF[\"encrypt\"] = \"" + encryption + "\"; include \"" + path + "\"; echo(pacrypt(\"" + plain + "\", \"" + dbPW + "\")).\"\\n\";'");
|
ReturnHolder crypted = BashUtils.run(conf.debug(), "php", "-r", "$CONF = array(); $CONF[\"encrypt\"] = \"" + encryption + "\"; include \"" + conf.getString("postfixadmin_path") + "/functions.inc.php" + "\"; echo(pacrypt(\"" + plain + "\", \"" + dbPW + "\")).\"\\n\";");
|
||||||
System.out.println(crypted);
|
return String.join("", crypted.output).equals(dbPW);
|
||||||
return crypted.equals(dbPW);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String execPHP(String args) {
|
|
||||||
try {
|
public Response getError(Response.Status status, String error) {
|
||||||
System.out.println(args);
|
JSONObject json = new JSONObject("{'error':'unknown error'}");
|
||||||
String line;
|
json.put("error", error);
|
||||||
StringBuilder output = new StringBuilder();
|
return newFixedLengthResponse(status, "application/json", json.toString());
|
||||||
Process p = Runtime.getRuntime().exec("php " + args);
|
|
||||||
BufferedReader input =
|
|
||||||
new BufferedReader
|
|
||||||
(new InputStreamReader(p.getInputStream()));
|
|
||||||
while ((line = input.readLine()) != null) {
|
|
||||||
output.append(line);
|
|
||||||
System.out.println(line);
|
|
||||||
}
|
}
|
||||||
input.close();
|
|
||||||
return output.toString();
|
public Response getError(Response.Status status) {
|
||||||
} catch (Exception err) {
|
switch (status) {
|
||||||
err.printStackTrace();
|
case INTERNAL_ERROR:
|
||||||
|
return getError(status, "internal server rror");
|
||||||
|
case BAD_REQUEST:
|
||||||
|
return getError(status, "bad request");
|
||||||
|
case UNAUTHORIZED:
|
||||||
|
return getError(status, "unauthorized");
|
||||||
}
|
}
|
||||||
return "";
|
return getError(status, "unknown error");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MySQL getDatabase() {
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Config getConfig() {
|
||||||
|
return conf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
252
src/main/java/de/gurkengewuerz/postfix_rest_send/MySQL.java
Normal file
252
src/main/java/de/gurkengewuerz/postfix_rest_send/MySQL.java
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
package de.gurkengewuerz.postfix_rest_send;
|
||||||
|
|
||||||
|
import java.sql.*;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple wrapper for database abstraction.
|
||||||
|
*
|
||||||
|
* @author Nijiko
|
||||||
|
* @url https://raw.githubusercontent.com/iConomy/SimpleShop/master/com/nijiko/simpleshop/database/Wrapper.java
|
||||||
|
*/
|
||||||
|
public class MySQL {
|
||||||
|
private static final Logger log = Logger.getLogger("MySQL");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Database Types
|
||||||
|
*/
|
||||||
|
public enum Type {
|
||||||
|
SQLITE,
|
||||||
|
MYSQL
|
||||||
|
}
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch type from string.
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Type getType(String type) {
|
||||||
|
for (Type dbType : Type.values()) {
|
||||||
|
if (dbType.toString().equalsIgnoreCase(type)) {
|
||||||
|
return dbType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Type.SQLITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Database Settings
|
||||||
|
*/
|
||||||
|
public Type database = null;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Database Connection Settings
|
||||||
|
*/
|
||||||
|
private String db;
|
||||||
|
private String user;
|
||||||
|
private String pass;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Database Memory
|
||||||
|
*/
|
||||||
|
private Connection connection;
|
||||||
|
private Statement Stmt;
|
||||||
|
private PreparedStatement Statement;
|
||||||
|
private ResultSet ResultSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance of the wrapper for usage.
|
||||||
|
*
|
||||||
|
* @param database
|
||||||
|
* @param db
|
||||||
|
* @param user
|
||||||
|
* @param pass
|
||||||
|
*/
|
||||||
|
public MySQL(Type database, String db, String user, String pass) {
|
||||||
|
this.database = database;
|
||||||
|
this.db = db;
|
||||||
|
this.user = user;
|
||||||
|
this.pass = pass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the wrapper
|
||||||
|
*
|
||||||
|
* @return Connection
|
||||||
|
* @throws ClassNotFoundException
|
||||||
|
* @throws SQLException
|
||||||
|
*/
|
||||||
|
public void initialize() {
|
||||||
|
try {
|
||||||
|
this.connection();
|
||||||
|
} catch (SQLException ex) {
|
||||||
|
log.severe("[" + this.database.toString() + " Database] Failed to connect: " + ex);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
log.severe("[" + this.database.toString() + " Database] Connector not found: " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to the database, return connection.
|
||||||
|
*
|
||||||
|
* @return Connection
|
||||||
|
* @throws ClassNotFoundException
|
||||||
|
* @throws SQLException
|
||||||
|
*/
|
||||||
|
private Connection connection() throws ClassNotFoundException, SQLException {
|
||||||
|
if (this.database.equals(database.SQLITE)) {
|
||||||
|
Class.forName("org.sqlite.JDBC");
|
||||||
|
this.connection = DriverManager.getConnection(this.db);
|
||||||
|
} else {
|
||||||
|
Class.forName("com.mysql.jdbc.Driver");
|
||||||
|
this.connection = DriverManager.getConnection(this.db, this.user, this.pass);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to see if table exists.
|
||||||
|
*
|
||||||
|
* @param table
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public boolean checkTable(String table) {
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
try {
|
||||||
|
DatabaseMetaData dbm = this.connection.getMetaData();
|
||||||
|
this.ResultSet = dbm.getTables(null, null, table, null);
|
||||||
|
return this.ResultSet.next();
|
||||||
|
} catch (SQLException ex) {
|
||||||
|
log.severe("[" + this.database.toString() + " Database] Table check failed: " + ex);
|
||||||
|
} finally {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute pure SQL String.
|
||||||
|
*
|
||||||
|
* @param query
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public ResultSet executeQuery(String query) {
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.Statement = this.connection.prepareStatement(query);
|
||||||
|
|
||||||
|
return this.Statement.executeQuery();
|
||||||
|
} catch (SQLException ex) {
|
||||||
|
log.severe("[" + this.database.toString() + " Database] Could not execute query: " + ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute Query with variables.
|
||||||
|
*
|
||||||
|
* @param query
|
||||||
|
* @param variables
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public ResultSet executeQuery(String query, Object... variables) {
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.Statement = this.connection.prepareStatement(query);
|
||||||
|
int i = 1;
|
||||||
|
for (Object obj : variables) {
|
||||||
|
this.Statement.setObject(i, obj);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return this.Statement.executeQuery();
|
||||||
|
} catch (SQLException ex) {
|
||||||
|
log.severe("[" + this.database.toString() + " Database] Could not execute query: " + ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute pure SQL String.
|
||||||
|
*
|
||||||
|
* @param query
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int executeUpdate(String query) {
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.Stmt = this.connection.createStatement();
|
||||||
|
return this.Stmt.executeUpdate(query);
|
||||||
|
} catch (SQLException ex) {
|
||||||
|
log.severe("[" + this.database.toString() + " Database] Could not execute query: " + ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute Query with variables.
|
||||||
|
*
|
||||||
|
* @param query
|
||||||
|
* @param variables
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int executeUpdate(String query, Object... variables) {
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.Statement = this.connection.prepareStatement(query);
|
||||||
|
int i = 1;
|
||||||
|
|
||||||
|
for (Object obj : variables) {
|
||||||
|
this.Statement.setObject(i, obj);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.Statement.executeUpdate();
|
||||||
|
} catch (SQLException ex) {
|
||||||
|
log.severe("[" + this.database.toString() + " Database] Could not execute query: " + ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
try {
|
||||||
|
if (this.Statement != null) {
|
||||||
|
this.Statement.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.ResultSet != null) {
|
||||||
|
this.ResultSet.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.connection != null) {
|
||||||
|
this.connection.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (SQLException ex) {
|
||||||
|
log.severe("[" + this.database.toString() + " Database] Failed to close connection: " + ex);
|
||||||
|
|
||||||
|
// Close anyway.
|
||||||
|
this.connection = null;
|
||||||
|
this.Statement = null;
|
||||||
|
this.ResultSet = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void finalize() {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
package de.gurkengewuerz.postfix_rest_send.objects;
|
||||||
|
|
||||||
|
import de.gurkengewuerz.postfix_rest_send.Main;
|
||||||
|
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by gurkengewuerz.de on 13.07.2017.
|
||||||
|
*/
|
||||||
|
public class BruteforceFilter {
|
||||||
|
private final HashMap<String, Integer> attempts = new HashMap<>();
|
||||||
|
private int maxAttemps = 3;
|
||||||
|
|
||||||
|
public BruteforceFilter(int maxAttemps) {
|
||||||
|
this.maxAttemps = maxAttemps;
|
||||||
|
Timer t = new Timer();
|
||||||
|
t.scheduleAtFixedRate(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
refresh();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
Logger.getLogger(BruteforceFilter.class.getName()).log(Level.SEVERE, null, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 0, 2 * 60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BruteforceFilter() {
|
||||||
|
this(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refresh() throws SQLException {
|
||||||
|
ResultSet rs = Main.getDatabase().executeQuery("SELECT ip, COUNT(*) count FROM token_bruteforce WHERE occurred > ? GROUP BY ip ORDER BY COUNT(*) DESC;", (System.currentTimeMillis() / 1000) - (24 * 60 * 60));
|
||||||
|
synchronized (attempts) {
|
||||||
|
attempts.clear();
|
||||||
|
while (rs.next()) {
|
||||||
|
attempts.put(rs.getString("ip"), rs.getInt("count"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Main.getConfig().debug())
|
||||||
|
Logger.getLogger(getClass().getName()).log(Level.INFO, "refreshed bans (" + attempts.size() + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void failed(String ip) {
|
||||||
|
if (Main.getConfig().getBoolean("disable_bruteforcefilter")) return;
|
||||||
|
Main.getDatabase().executeUpdate("INSERT INTO token_bruteforce (ip, occurred) VALUES (?, ?);", ip, System.currentTimeMillis() / 1000);
|
||||||
|
synchronized (attempts) {
|
||||||
|
if (attempts.containsKey(ip)) {
|
||||||
|
attempts.replace(ip, attempts.get(ip) + 1);
|
||||||
|
} else {
|
||||||
|
attempts.put(ip, 1);
|
||||||
|
}
|
||||||
|
if (Main.getConfig().debug())
|
||||||
|
Logger.getLogger(getClass().getName()).log(Level.INFO, "banned " + ip + " attemp " + attempts.get(ip));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean banned(String ip) {
|
||||||
|
if (Main.getConfig().getBoolean("disable_bruteforcefilter")) return false;
|
||||||
|
synchronized (attempts) {
|
||||||
|
return attempts.containsKey(ip) && attempts.get(ip) >= maxAttemps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package de.gurkengewuerz.postfix_rest_send.objects;
|
||||||
|
|
||||||
|
public class ReturnHolder {
|
||||||
|
public final String[] output;
|
||||||
|
public final String[] error;
|
||||||
|
|
||||||
|
public ReturnHolder(String[] output, String[] error) {
|
||||||
|
this.output = output;
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
package de.gurkengewuerz.postfix_rest_send.utils;
|
||||||
|
|
||||||
|
import de.gurkengewuerz.postfix_rest_send.objects.ReturnHolder;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by gurkengewuerz.de on 13.07.2017.
|
||||||
|
*/
|
||||||
|
public class BashUtils {
|
||||||
|
public static ReturnHolder run(boolean directPrint, String... command) {
|
||||||
|
try {
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(command);
|
||||||
|
Process p = pb.start();
|
||||||
|
return getOutput(directPrint, p);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Logger.getLogger(BashUtils.class.getName()).log(Level.SEVERE, "error executing command", ex);
|
||||||
|
}
|
||||||
|
return new ReturnHolder(new String[0], new String[]{"exception"});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ReturnHolder getOutput(boolean print, Process P) {
|
||||||
|
try {
|
||||||
|
BufferedReader stdInput = new BufferedReader(new InputStreamReader(P.getInputStream()));
|
||||||
|
BufferedReader stdError = new BufferedReader(new InputStreamReader(P.getErrorStream()));
|
||||||
|
ArrayList<String> output = new ArrayList<>();
|
||||||
|
ArrayList<String> error = new ArrayList<>();
|
||||||
|
Thread t = null;
|
||||||
|
if (print) {
|
||||||
|
t = new Thread(() -> {
|
||||||
|
String s;
|
||||||
|
try {
|
||||||
|
while ((s = stdError.readLine()) != null) {
|
||||||
|
error.add(s);
|
||||||
|
System.out.println("ERR: " + s);
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Logger.getLogger(BashUtils.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
String s;
|
||||||
|
while ((s = stdInput.readLine()) != null) {
|
||||||
|
output.add(s);
|
||||||
|
if (print) {
|
||||||
|
System.out.println("OUT: " + s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (t != null) {
|
||||||
|
if (t.isAlive()) {
|
||||||
|
t.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while ((s = stdError.readLine()) != null) {
|
||||||
|
error.add(s);
|
||||||
|
if (print) {
|
||||||
|
System.out.println("ERR: " + s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ReturnHolder(output.toArray(new String[output.size()]), error.toArray(new String[error.size()]));
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Logger.getLogger(BashUtils.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
}
|
||||||
|
return new ReturnHolder(new String[0], new String[]{"exception"});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package de.gurkengewuerz.postfix_rest_send.utils;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by gurkengewuerz.de on 13.07.2017.
|
||||||
|
*/
|
||||||
|
public class RandomUtils {
|
||||||
|
public static String rndToken() {
|
||||||
|
return rndToken("");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String rndToken(String extraSalt) {
|
||||||
|
String uuidFront = UUID.randomUUID().toString();
|
||||||
|
String uuidBack = UUID.randomUUID().toString();
|
||||||
|
String salted = uuidFront + System.currentTimeMillis() + uuidBack + extraSalt;
|
||||||
|
return DigestUtils.sha256Hex(salted);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user