/*
* Copyright 2016 Nathan Howard
*
* This file is part of OpenGrave
*
* OpenGrave is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenGrave is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenGrave. If not, see <http://www.gnu.org/licenses/>.
*/
package com.opengrave.common.xml;
import java.awt.Desktop;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.net.*;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import javax.net.ssl.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.opengrave.common.DebugExceptionHandler;
import com.opengrave.common.ServerData;
import com.opengrave.common.event.*;
import com.opengrave.og.MainThread;
import com.opengrave.og.states.ErrorState;
import com.opengrave.og.states.ProfileState;
public class HGXMLThread implements Runnable, EventListener {
private static ArrayList<HGXMLRequest> requestList = new ArrayList<HGXMLRequest>();
public static String authURL;
public static String sendXMLRequest(String urlString, Document XML) {
try {
URL url = new URL(urlString);
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setSSLSocketFactory(sslSocketFactory);
// specify that we will send output and accept input
con.setDoInput(true);
con.setDoOutput(true);
con.setConnectTimeout(20000); // long timeout, but not infinite
con.setReadTimeout(20000);
con.setUseCaches(false);
con.setDefaultUseCaches(false);
// tell the web server what we are sending
con.setRequestProperty("Content-Type", "text/xml");
OutputStreamWriter writer = new OutputStreamWriter(con.getOutputStream());
writer.write(docToString(XML));
writer.flush();
writer.close();
// reading the response
InputStreamReader reader = new InputStreamReader(con.getInputStream());
StringBuilder buf = new StringBuilder();
char[] cbuf = new char[2048];
int num;
while (-1 != (num = reader.read(cbuf))) {
buf.append(cbuf, 0, num);
}
String result = buf.toString();
return result;
} catch (NoRouteToHostException e) {
new DebugExceptionHandler(e, urlString, docToString(XML));
} catch (MalformedURLException e) {
new DebugExceptionHandler(e, urlString, docToString(XML));
} catch (IOException e) {
new DebugExceptionHandler(e, urlString, docToString(XML));
}
return null;
}
@EventHandler(priority = EventHandlerPriority.LATE)
public void onXMLReturned(XMLReturnedEvent event) {
Document doc = event.getDocument();
Element api = XML.getChild(doc, "api");
if (api == null) {
System.out.println("Error reading returned XML");
System.out.println(event.getDocument().toString());
} else {
for (Element response : XML.getChildren(api, "response")) {
String type = response.getAttribute("type");
Element result = null;
String mods = "";
switch (type.toLowerCase()) {
case "serverstart":
result = XML.getChild(response, "result");
boolean wanAccess = false;
if (result == null || (!result.getTextContent().equalsIgnoreCase("OK"))) {
// Server did not get outside WAN.
} else {
// Server connects on WAN with given IP and port
wanAccess = true;
}
EventDispatcher.dispatchEvent(new ServerStartedEvent(wanAccess));
break;
case "clientstarted":
Element token = XML.getChild(response, "token");
if (token == null) {
MainThread.changeState(new ErrorState(
"Login details are incorrect. Please download and use a fresh copy of the Launcher from the website"));
break;
}
Element modlist = XML.getChild(response, "mods");
mods = "1";
if (modlist != null) {
mods = modlist.getTextContent();
} else {
mods = "1";
}
String accessToken = token.getTextContent();
ProfileState.state.isOnline = true;
ProfileState.state.standing = ProfileState.Standing.UNKNOWN;
ProfileState.state.accessToken = accessToken;
ProfileState.state.mods = mods;
EventDispatcher.dispatchEvent(new ClientAuthStatusEvent(mods));
break;
case "clientbug":
Element bugNumberEle = XML.getChild(response, "bugid");
Element reportNumberEle = XML.getChild(response, "bugrep");
if (bugNumberEle == null || reportNumberEle == null) {
break;
}
try {
int bugId = Integer.parseInt(bugNumberEle.getTextContent());
int repId = Integer.parseInt(reportNumberEle.getTextContent());
Desktop.getDesktop().browse(new URI("https://aperistudios.co.uk/bug.php?bug=" + bugId + "#report" + repId));
} catch (NumberFormatException nfe) {
} catch (IOException e) {
} catch (URISyntaxException e) {
}
break;
case "serverlist":
ArrayList<ServerData> sd = new ArrayList<ServerData>();
for (Element child : XML.getChildren(response, "server")) {
String names = child.getAttribute("names");
String ip = child.getAttribute("ip");
String port = child.getAttribute("port");
String id = child.getAttribute("id");
mods = child.getAttribute("mods");
ServerData s = new ServerData(names, ip, port, id, mods);
sd.add(s);
}
EventDispatcher.dispatchEvent(new ServerListEvent(sd));
break;
case "clientjoin":
result = XML.getChild(response, "result");
Element name = XML.getChild(response, "name");
token = XML.getChild(response, "token");
Element id = XML.getChild(response, "id");
if (id == null) {
break;
}
if (token == null || result == null || name == null || result.getTextContent().equalsIgnoreCase("bad")) {
EventDispatcher.dispatchEvent(new ServerAuthFailedEvent(id.getTextContent()));
break;
}
EventDispatcher.dispatchEvent(new ServerAuthEvent(name.getTextContent(), id.getTextContent(), token.getTextContent()));
break;
}
}
}
}
public static void requestClientCheckIn() {
String host = "";
int port = -1;
String idHost = "-1";
if (MainThread.getConnection() != null) {
host = MainThread.getConnection().getHost();
port = MainThread.getConnection().getPort();
idHost = MainThread.getConnection().getId();
}
System.out.println("Reporting connection as " + host + ":" + port + ":" + idHost);
Document doc = createDoc();
Element root = doc.createElement("api");
doc.appendChild(root);
Element request = doc.createElement("request");
root.appendChild(request);
request.setAttribute("type", "clientping");
Element login = doc.createElement("userlogin");
login.setTextContent(MainThread.USERNAME);
Element password = doc.createElement("userpassword");
password.setTextContent(MainThread.PASSWORD);
Element ip = doc.createElement("ip");
ip.setTextContent(host);
Element portE = doc.createElement("port");
portE.setTextContent(port + "");
Element id = doc.createElement("id");
id.setTextContent(idHost);
request.appendChild(login);
request.appendChild(password);
request.appendChild(ip);
request.appendChild(portE);
String url = "https://api.aperistudios.co.uk/auth/";
HGXMLThread.addRequest(new HGXMLRequest(url, doc));
}
public static void requestServerlist() {
Document doc = createDoc();
Element root = doc.createElement("api");
doc.appendChild(root);
Element request = doc.createElement("request");
root.appendChild(request);
request.setAttribute("type", "serverlist");
root.appendChild(request);
HGXMLThread.addRequest(new HGXMLRequest(authURL, doc));
}
public static void requestServerRegister(int port) {
Document doc = createDoc();
Element root = doc.createElement("api");
doc.appendChild(root);
Element request = doc.createElement("request");
root.appendChild(request);
request.setAttribute("type", "serverstart");
Element login = doc.createElement("userlogin");
login.setTextContent(MainThread.USERNAME);
Element password = doc.createElement("userpassword");
password.setTextContent(MainThread.PASSWORD);
Element ip = doc.createElement("ip");
ip.setTextContent("None");
Element portE = doc.createElement("port");
portE.setTextContent(port + "");
request.appendChild(login);
request.appendChild(password);
request.appendChild(ip);
request.appendChild(portE);
HGXMLThread.addRequest(new HGXMLRequest(authURL, doc));
}
public static void requestAuthClientFromServer(String userId, String userToken) {
Document doc = createDoc();
Element root = doc.createElement("api");
doc.appendChild(root);
Element request = doc.createElement("request");
root.appendChild(request);
request.setAttribute("type", "clientjoin");
Element login = doc.createElement("userlogin");
login.setTextContent(userId);
Element password = doc.createElement("usertoken");
password.setTextContent(userToken);
request.appendChild(login);
request.appendChild(password);
HGXMLThread.addRequest(new HGXMLRequest(authURL, doc));
}
public static void requestAuthClient() {
Document doc = createDoc();
Element root = doc.createElement("api");
doc.appendChild(root);
Element request = doc.createElement("request");
root.appendChild(request);
request.setAttribute("type", "clientstarted");
Element login = doc.createElement("userlogin");
login.setTextContent(MainThread.USERNAME);
Element password = doc.createElement("userpassword");
password.setTextContent(MainThread.PASSWORD);
request.appendChild(login);
request.appendChild(password);
HGXMLThread.addRequest(new HGXMLRequest(authURL, doc));
}
private static void addRequest(HGXMLRequest hgxmlCallback) {
synchronized (requestList) {
requestList.add(hgxmlCallback);
requestList.notifyAll();
}
}
static String docToString(Document doc) {
TransformerFactory transfac = TransformerFactory.newInstance();
Transformer trans;
StringWriter sw = null;
try {
trans = transfac.newTransformer();
trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
trans.setOutputProperty(OutputKeys.INDENT, "yes");
// create string from xml tree
sw = new StringWriter();
StreamResult result = new StreamResult(sw);
DOMSource source = new DOMSource(doc);
trans.transform(source, result);
} catch (TransformerConfigurationException e) {
new DebugExceptionHandler(e);
} catch (TransformerException e) {
new DebugExceptionHandler(e);
}
return sw != null ? sw.toString() : null;
}
private static Document createDoc() {
try {
DocumentBuilderFactory dFact = DocumentBuilderFactory.newInstance();
DocumentBuilder build = dFact.newDocumentBuilder();
Document doc = build.newDocument();
return doc;
} catch (ParserConfigurationException e) {
new DebugExceptionHandler(e);
}
return null;
}
public HGXMLThread() {
EventDispatcher.addHandler(this);
}
private SSLContext sslContext;
private static SSLSocketFactory sslSocketFactory;
@Override
public void run() {
try {
final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
@Override
public void checkClientTrusted(final X509Certificate[] chain, final String authType) {
}
@Override
public void checkServerTrusted(final X509Certificate[] chain, final String authType) {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
} };
sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
sslSocketFactory = sslContext.getSocketFactory();
} catch (NoSuchAlgorithmException e) {
new DebugExceptionHandler(e);
} catch (KeyManagementException e) {
new DebugExceptionHandler(e);
}
while (MainThread.running) {
synchronized (requestList) {
while (requestList.size() > 0) {
HGXMLRequest cb = requestList.remove(0); // Earlier first
String ret = sendXMLRequest(cb.url, cb.originalXml);
cb.returnXML(ret);
}
}
synchronized (requestList) {
try {
requestList.wait();
} catch (InterruptedException e) {
}
}
}
}
public static void requestBugReport(String sTrace, String title) {
System.out.println("Filing bug report");
Document doc = createDoc();
Element root = doc.createElement("api");
doc.appendChild(root);
Element request = doc.createElement("request");
root.appendChild(request);
request.setAttribute("type", "clientbug");
Element login = doc.createElement("userlogin");
login.setTextContent(MainThread.USERNAME);
Element password = doc.createElement("userpassword");
password.setTextContent(MainThread.PASSWORD);
Element strace = doc.createElement("strace");
strace.setTextContent(sTrace);
Element prod1 = doc.createElement("prod1");
prod1.setTextContent("hgjava");
Element rev1 = doc.createElement("rev1");
rev1.setTextContent("" + MainThread.hgver);
Element prod2 = doc.createElement("prod2");
prod2.setTextContent("hgasset");
Element rev2 = doc.createElement("rev2");
rev2.setTextContent("" + MainThread.assetver);
Element titleE = doc.createElement("title");
titleE.setTextContent(title);
request.appendChild(login);
request.appendChild(password);
request.appendChild(strace);
request.appendChild(prod1);
request.appendChild(prod2);
request.appendChild(rev1);
request.appendChild(rev2);
request.appendChild(titleE);
String url = "https://api.aperistudios.co.uk/auth/";
HGXMLThread.addRequest(new HGXMLRequest(url, doc));
}
}