├── Manifest.txt ├── package.sh ├── cls └── static │ ├── loading.gif │ ├── style.css │ ├── index.html │ └── index.js ├── launch.sh ├── README.md ├── .gitignore └── src ├── org └── json │ ├── JSONString.java │ ├── JSONException.java │ ├── JSONPointerException.java │ ├── JSONPropertyIgnore.java │ ├── JSONPropertyName.java │ ├── HTTPTokener.java │ ├── Property.java │ ├── JSONStringer.java │ ├── CookieList.java │ ├── HTTP.java │ ├── Cookie.java │ ├── CDL.java │ ├── JSONPointer.java │ ├── XMLTokener.java │ ├── JSONWriter.java │ ├── JSONTokener.java │ ├── JSONML.java │ └── XML.java └── connect2 ├── GameInfo.java ├── Main.java ├── GuiApp.java ├── ECList.java ├── Utils.java ├── ECUtil.java ├── ECHost.java └── Web.java /Manifest.txt: -------------------------------------------------------------------------------- 1 | Main-Class: connect2.Main 2 | -------------------------------------------------------------------------------- /package.sh: -------------------------------------------------------------------------------- 1 | rm wc3connect.jar 2 | jar cvfm wc3connect.jar Manifest.txt -C cls . 3 | -------------------------------------------------------------------------------- /cls/static/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uakfdotb/wc3connect-java/HEAD/cls/static/loading.gif -------------------------------------------------------------------------------- /launch.sh: -------------------------------------------------------------------------------- 1 | rm cls/connect2/*.class 2 | javac -source 1.6 -target 1.6 -classpath src -d cls src/connect2/Main.java && java -classpath cls connect2.Main 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WC3Connect-Java 2 | =============== 3 | 4 | This is a cross-platform Java version of WC3Connect. 5 | 6 | To compile: 7 | 8 | $ ./launch.sh 9 | $ ./package.sh 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | -------------------------------------------------------------------------------- /cls/static/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #F5F7FA; 3 | } 4 | 5 | .container { 6 | margin-top: 25px; 7 | max-width: 700px; 8 | } 9 | 10 | tr.game { 11 | cursor: pointer; 12 | } 13 | 14 | tr.game:hover { 15 | background-color: grey; 16 | } 17 | 18 | tr.game.selected { 19 | background-color: green !important; 20 | } 21 | 22 | td.gameinfo h2 { 23 | font-weight: normal; 24 | font-size: 14px; 25 | } 26 | 27 | td.gameinfo table { 28 | border-collapse: separate; 29 | border-spacing: 15px 0px; 30 | } 31 | -------------------------------------------------------------------------------- /src/org/json/JSONString.java: -------------------------------------------------------------------------------- 1 | package org.json; 2 | /** 3 | * The JSONString interface allows a toJSONString() 4 | * method so that a class can change the behavior of 5 | * JSONObject.toString(), JSONArray.toString(), 6 | * and JSONWriter.value(Object). The 7 | * toJSONString method will be used instead of the default behavior 8 | * of using the Object's toString() method and quoting the result. 9 | */ 10 | public interface JSONString { 11 | /** 12 | * The toJSONString method allows a class to produce its own JSON 13 | * serialization. 14 | * 15 | * @return A strictly syntactically correct JSON text. 16 | */ 17 | public String toJSONString(); 18 | } 19 | -------------------------------------------------------------------------------- /src/connect2/GameInfo.java: -------------------------------------------------------------------------------- 1 | package connect2; 2 | 3 | import java.net.InetAddress; 4 | import java.net.UnknownHostException; 5 | 6 | public class GameInfo { 7 | int uid; 8 | InetAddress remoteAddress; 9 | int remotePort; 10 | int hostCounter; 11 | String gamename; 12 | String map; 13 | String extra; 14 | long millis; 15 | 16 | public GameInfo(int uid, byte[] addr, int port, int hostCounter, String gamename, String map, String extra) { 17 | this.uid = uid; 18 | this.remotePort = port; 19 | this.hostCounter = hostCounter; 20 | this.gamename = gamename; 21 | this.map = map; 22 | this.extra = extra; 23 | this.millis = System.currentTimeMillis(); 24 | 25 | try { 26 | remoteAddress = InetAddress.getByAddress(addr); 27 | } catch(UnknownHostException uhe) { 28 | System.out.println("[GameInfo] Error: unknown host on addr bytes: " + uhe.getLocalizedMessage()); 29 | remoteAddress = null; 30 | } 31 | } 32 | 33 | public boolean expired() { 34 | return (System.currentTimeMillis() - this.millis) > 30000; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/org/json/JSONException.java: -------------------------------------------------------------------------------- 1 | package org.json; 2 | 3 | /** 4 | * The JSONException is thrown by the JSON.org classes when things are amiss. 5 | * 6 | * @author JSON.org 7 | * @version 2015-12-09 8 | */ 9 | public class JSONException extends RuntimeException { 10 | /** Serialization ID */ 11 | private static final long serialVersionUID = 0; 12 | 13 | /** 14 | * Constructs a JSONException with an explanatory message. 15 | * 16 | * @param message 17 | * Detail about the reason for the exception. 18 | */ 19 | public JSONException(final String message) { 20 | super(message); 21 | } 22 | 23 | /** 24 | * Constructs a JSONException with an explanatory message and cause. 25 | * 26 | * @param message 27 | * Detail about the reason for the exception. 28 | * @param cause 29 | * The cause. 30 | */ 31 | public JSONException(final String message, final Throwable cause) { 32 | super(message, cause); 33 | } 34 | 35 | /** 36 | * Constructs a new JSONException with the specified cause. 37 | * 38 | * @param cause 39 | * The cause. 40 | */ 41 | public JSONException(final Throwable cause) { 42 | super(cause.getMessage(), cause); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/connect2/Main.java: -------------------------------------------------------------------------------- 1 | package connect2; 2 | 3 | import java.awt.*; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | public class Main { 8 | public static Map ThirdPartyBotMap = new HashMap(); 9 | public static boolean Debug = true; 10 | public static int Version = 2018102602; 11 | 12 | public static void main(String[] args) throws java.io.IOException { 13 | ThirdPartyBotMap.put("192.99.6.98", "MMH-USA"); 14 | ThirdPartyBotMap.put("85.10.199.252", "MMH-Euro"); 15 | 16 | new Thread(new LoadThirdPartyThread()).start(); 17 | new Web(); 18 | 19 | if(!GraphicsEnvironment.isHeadless()){ 20 | new GuiApp(); 21 | } else { 22 | System.out.println("Open your webbrowser and go to: 127.0.0.1:8033"); 23 | } 24 | } 25 | 26 | public static String GetThirdPartyBot(String ip) { 27 | synchronized(ThirdPartyBotMap) { 28 | return ThirdPartyBotMap.get(ip); 29 | } 30 | } 31 | 32 | static class LoadThirdPartyThread implements Runnable { 33 | public void run() { 34 | try { 35 | String[] lines = Utils.get("https://entgaming.net/entconnect/wc3connect_thirdpartymap.php").split("\n"); 36 | for(String line : lines) { 37 | line = line.trim(); 38 | String[] parts = line.split(" "); 39 | if(parts.length == 2) { 40 | System.out.println("[main] load third party bot: " + parts[0] + " -> " + parts[1]); 41 | synchronized(ThirdPartyBotMap) { 42 | ThirdPartyBotMap.put(parts[0], parts[1]); 43 | } 44 | } 45 | } 46 | } catch(Exception e) { 47 | e.printStackTrace(); 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/connect2/GuiApp.java: -------------------------------------------------------------------------------- 1 | package connect2; 2 | 3 | import javax.swing.*; 4 | import java.awt.*; 5 | import java.awt.event.ActionEvent; 6 | import java.awt.event.ActionListener; 7 | import java.io.IOException; 8 | import java.net.URI; 9 | 10 | public class GuiApp implements ActionListener { 11 | JButton launchBtn, exitBtn; 12 | 13 | public GuiApp(){ 14 | JFrame frame = new JFrame("WC3Connect-Java"); 15 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 16 | this.launchBtn = new JButton("Launch"); 17 | this.exitBtn = new JButton("Exit"); 18 | this.launchBtn.setFont(new Font("Arial", Font.PLAIN, 30)); 19 | this.exitBtn.setFont(new Font("Arial", Font.PLAIN, 30)); 20 | this.launchBtn.addActionListener(this); 21 | this.exitBtn.addActionListener(this); 22 | 23 | if(!Desktop.isDesktopSupported() || !Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)){ 24 | this.launchBtn.setEnabled(false); 25 | this.launchBtn.setText("Open your webbrowser and go to: 127.0.0.1:8033"); 26 | } 27 | 28 | frame.getContentPane().add(this.launchBtn, BorderLayout.PAGE_START); 29 | frame.getContentPane().add(this.exitBtn, BorderLayout.PAGE_END); 30 | frame.pack(); 31 | frame.setLocationRelativeTo(null); 32 | frame.setVisible(true); 33 | } 34 | 35 | private void launch(){ 36 | try { 37 | Desktop.getDesktop().browse(URI.create("http://127.0.0.1:8333")); 38 | } catch (IOException e1) { 39 | e1.printStackTrace(); 40 | } 41 | } 42 | 43 | public void actionPerformed(ActionEvent e) { 44 | if(e.getSource() == this.launchBtn) { 45 | this.launch(); 46 | } else if(e.getSource() == this.exitBtn) { 47 | System.exit(0); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/org/json/JSONPointerException.java: -------------------------------------------------------------------------------- 1 | package org.json; 2 | 3 | /* 4 | Copyright (c) 2002 JSON.org 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | The Software shall be used for Good, not Evil. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /** 28 | * The JSONPointerException is thrown by {@link JSONPointer} if an error occurs 29 | * during evaluating a pointer. 30 | * 31 | * @author JSON.org 32 | * @version 2016-05-13 33 | */ 34 | public class JSONPointerException extends JSONException { 35 | private static final long serialVersionUID = 8872944667561856751L; 36 | 37 | public JSONPointerException(String message) { 38 | super(message); 39 | } 40 | 41 | public JSONPointerException(String message, Throwable cause) { 42 | super(message, cause); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/org/json/JSONPropertyIgnore.java: -------------------------------------------------------------------------------- 1 | package org.json; 2 | 3 | /* 4 | Copyright (c) 2018 JSON.org 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | The Software shall be used for Good, not Evil. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | import static java.lang.annotation.ElementType.METHOD; 28 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 29 | 30 | import java.lang.annotation.Documented; 31 | import java.lang.annotation.Retention; 32 | import java.lang.annotation.Target; 33 | 34 | @Documented 35 | @Retention(RUNTIME) 36 | @Target({METHOD}) 37 | /** 38 | * Use this annotation on a getter method to override the Bean name 39 | * parser for Bean -> JSONObject mapping. If this annotation is 40 | * present at any level in the class hierarchy, then the method will 41 | * not be serialized from the bean into the JSONObject. 42 | */ 43 | public @interface JSONPropertyIgnore { } 44 | -------------------------------------------------------------------------------- /src/org/json/JSONPropertyName.java: -------------------------------------------------------------------------------- 1 | package org.json; 2 | 3 | /* 4 | Copyright (c) 2018 JSON.org 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | The Software shall be used for Good, not Evil. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | import static java.lang.annotation.ElementType.METHOD; 28 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 29 | 30 | import java.lang.annotation.Documented; 31 | import java.lang.annotation.Retention; 32 | import java.lang.annotation.Target; 33 | 34 | @Documented 35 | @Retention(RUNTIME) 36 | @Target({METHOD}) 37 | /** 38 | * Use this annotation on a getter method to override the Bean name 39 | * parser for Bean -> JSONObject mapping. A value set to empty string "" 40 | * will have the Bean parser fall back to the default field name processing. 41 | */ 42 | public @interface JSONPropertyName { 43 | /** 44 | * @return The name of the property as to be used in the JSON Object. 45 | */ 46 | String value(); 47 | } 48 | -------------------------------------------------------------------------------- /src/connect2/ECList.java: -------------------------------------------------------------------------------- 1 | package connect2; 2 | 3 | import org.json.JSONArray; 4 | 5 | import javax.net.ssl.HttpsURLConnection; 6 | import java.nio.ByteBuffer; 7 | import java.util.List; 8 | 9 | public class ECList implements Runnable { 10 | ECHost host; 11 | ByteBuffer buf; 12 | String filter; 13 | List preferred; //comma-separated list of preferred bots 14 | 15 | boolean terminate = false; 16 | 17 | public ECList(ECHost host) { 18 | this.host = host; 19 | this.buf = ByteBuffer.allocate(65536); 20 | Thread thread = new Thread(this); 21 | thread.start(); 22 | } 23 | 24 | public void deinit() { 25 | terminate = true; 26 | 27 | synchronized(this) { 28 | this.notifyAll(); 29 | } 30 | } 31 | 32 | public void setFilter(String filter) { 33 | synchronized(this) { 34 | this.filter = filter; 35 | } 36 | this.once(); 37 | } 38 | 39 | private void once() { 40 | synchronized(this) { 41 | HttpsURLConnection conn; 42 | String line; 43 | 44 | System.out.println("[ECList] Connecting to server..."); 45 | 46 | try { 47 | JSONArray arr = new JSONArray(Utils.get("https://connect.entgaming.net/games")); 48 | host.clearGames(); 49 | boolean matchedFilter = false; 50 | if(this.filter != null && !this.filter.isEmpty()) { 51 | for(int i = 0; i < arr.length(); i++) { 52 | byte[] data = ECUtil.hexDecode(arr.getString(i)); 53 | buf.clear(); 54 | buf.put(data); 55 | buf.position(0); 56 | matchedFilter = host.receivedUDP(buf, this.filter) || matchedFilter; 57 | } 58 | } 59 | if(!matchedFilter) { 60 | host.clearGames(); // ignore the games we just provided since we'll be sending them again 61 | for(int i = 0; i < arr.length(); i++) { 62 | byte[] data = ECUtil.hexDecode(arr.getString(i)); 63 | buf.clear(); 64 | buf.put(data); 65 | buf.position(0); 66 | host.receivedUDP(buf, null); 67 | } 68 | } 69 | 70 | System.out.println("[ECList] Listing complete (got " + arr.length() + " games"); 71 | } catch (Exception e) { 72 | System.out.println("[ECList] Error during listing: " + e.getLocalizedMessage()); 73 | e.printStackTrace(); 74 | } 75 | } 76 | } 77 | 78 | public void run() { 79 | while(!terminate) { 80 | this.once(); 81 | 82 | if(terminate) break; 83 | 84 | synchronized(this) { 85 | try { 86 | this.wait(10000); 87 | } catch(InterruptedException e) {} 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/org/json/HTTPTokener.java: -------------------------------------------------------------------------------- 1 | package org.json; 2 | 3 | /* 4 | Copyright (c) 2002 JSON.org 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | The Software shall be used for Good, not Evil. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /** 28 | * The HTTPTokener extends the JSONTokener to provide additional methods 29 | * for the parsing of HTTP headers. 30 | * @author JSON.org 31 | * @version 2015-12-09 32 | */ 33 | public class HTTPTokener extends JSONTokener { 34 | 35 | /** 36 | * Construct an HTTPTokener from a string. 37 | * @param string A source string. 38 | */ 39 | public HTTPTokener(String string) { 40 | super(string); 41 | } 42 | 43 | 44 | /** 45 | * Get the next token or string. This is used in parsing HTTP headers. 46 | * @throws JSONException 47 | * @return A String. 48 | */ 49 | public String nextToken() throws JSONException { 50 | char c; 51 | char q; 52 | StringBuilder sb = new StringBuilder(); 53 | do { 54 | c = next(); 55 | } while (Character.isWhitespace(c)); 56 | if (c == '"' || c == '\'') { 57 | q = c; 58 | for (;;) { 59 | c = next(); 60 | if (c < ' ') { 61 | throw syntaxError("Unterminated string."); 62 | } 63 | if (c == q) { 64 | return sb.toString(); 65 | } 66 | sb.append(c); 67 | } 68 | } 69 | for (;;) { 70 | if (c == 0 || Character.isWhitespace(c)) { 71 | return sb.toString(); 72 | } 73 | sb.append(c); 74 | c = next(); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/org/json/Property.java: -------------------------------------------------------------------------------- 1 | package org.json; 2 | 3 | /* 4 | Copyright (c) 2002 JSON.org 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | The Software shall be used for Good, not Evil. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | import java.util.Enumeration; 28 | import java.util.Properties; 29 | 30 | /** 31 | * Converts a Property file data into JSONObject and back. 32 | * @author JSON.org 33 | * @version 2015-05-05 34 | */ 35 | public class Property { 36 | /** 37 | * Converts a property file object into a JSONObject. The property file object is a table of name value pairs. 38 | * @param properties java.util.Properties 39 | * @return JSONObject 40 | * @throws JSONException 41 | */ 42 | public static JSONObject toJSONObject(java.util.Properties properties) throws JSONException { 43 | // can't use the new constructor for Android support 44 | // JSONObject jo = new JSONObject(properties == null ? 0 : properties.size()); 45 | JSONObject jo = new JSONObject(); 46 | if (properties != null && !properties.isEmpty()) { 47 | Enumeration enumProperties = properties.propertyNames(); 48 | while(enumProperties.hasMoreElements()) { 49 | String name = (String)enumProperties.nextElement(); 50 | jo.put(name, properties.getProperty(name)); 51 | } 52 | } 53 | return jo; 54 | } 55 | 56 | /** 57 | * Converts the JSONObject into a property file object. 58 | * @param jo JSONObject 59 | * @return java.util.Properties 60 | * @throws JSONException 61 | */ 62 | public static Properties toProperties(JSONObject jo) throws JSONException { 63 | Properties properties = new Properties(); 64 | if (jo != null) { 65 | // Don't use the new entrySet API to maintain Android support 66 | for (final String key : jo.keySet()) { 67 | Object value = jo.opt(key); 68 | if (!JSONObject.NULL.equals(value)) { 69 | properties.put(key, value.toString()); 70 | } 71 | } 72 | } 73 | return properties; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/org/json/JSONStringer.java: -------------------------------------------------------------------------------- 1 | package org.json; 2 | 3 | /* 4 | Copyright (c) 2006 JSON.org 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | The Software shall be used for Good, not Evil. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | import java.io.StringWriter; 28 | 29 | /** 30 | * JSONStringer provides a quick and convenient way of producing JSON text. 31 | * The texts produced strictly conform to JSON syntax rules. No whitespace is 32 | * added, so the results are ready for transmission or storage. Each instance of 33 | * JSONStringer can produce one JSON text. 34 | *

35 | * A JSONStringer instance provides a value method for appending 36 | * values to the 37 | * text, and a key 38 | * method for adding keys before values in objects. There are array 39 | * and endArray methods that make and bound array values, and 40 | * object and endObject methods which make and bound 41 | * object values. All of these methods return the JSONWriter instance, 42 | * permitting cascade style. For example,

43 |  * myString = new JSONStringer()
44 |  *     .object()
45 |  *         .key("JSON")
46 |  *         .value("Hello, World!")
47 |  *     .endObject()
48 |  *     .toString();
which produces the string
49 |  * {"JSON":"Hello, World!"}
50 | *

51 | * The first method called must be array or object. 52 | * There are no methods for adding commas or colons. JSONStringer adds them for 53 | * you. Objects and arrays can be nested up to 20 levels deep. 54 | *

55 | * This can sometimes be easier than using a JSONObject to build a string. 56 | * @author JSON.org 57 | * @version 2015-12-09 58 | */ 59 | public class JSONStringer extends JSONWriter { 60 | /** 61 | * Make a fresh JSONStringer. It can be used to build one JSON text. 62 | */ 63 | public JSONStringer() { 64 | super(new StringWriter()); 65 | } 66 | 67 | /** 68 | * Return the JSON text. This method is used to obtain the product of the 69 | * JSONStringer instance. It will return null if there was a 70 | * problem in the construction of the JSON text (such as the calls to 71 | * array were not properly balanced with calls to 72 | * endArray). 73 | * @return The JSON text. 74 | */ 75 | @Override 76 | public String toString() { 77 | return this.mode == 'd' ? this.writer.toString() : null; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/org/json/CookieList.java: -------------------------------------------------------------------------------- 1 | package org.json; 2 | 3 | /* 4 | Copyright (c) 2002 JSON.org 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | The Software shall be used for Good, not Evil. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /** 28 | * Convert a web browser cookie list string to a JSONObject and back. 29 | * @author JSON.org 30 | * @version 2015-12-09 31 | */ 32 | public class CookieList { 33 | 34 | /** 35 | * Convert a cookie list into a JSONObject. A cookie list is a sequence 36 | * of name/value pairs. The names are separated from the values by '='. 37 | * The pairs are separated by ';'. The names and the values 38 | * will be unescaped, possibly converting '+' and '%' sequences. 39 | * 40 | * To add a cookie to a cookie list, 41 | * cookielistJSONObject.put(cookieJSONObject.getString("name"), 42 | * cookieJSONObject.getString("value")); 43 | * @param string A cookie list string 44 | * @return A JSONObject 45 | * @throws JSONException 46 | */ 47 | public static JSONObject toJSONObject(String string) throws JSONException { 48 | JSONObject jo = new JSONObject(); 49 | JSONTokener x = new JSONTokener(string); 50 | while (x.more()) { 51 | String name = Cookie.unescape(x.nextTo('=')); 52 | x.next('='); 53 | jo.put(name, Cookie.unescape(x.nextTo(';'))); 54 | x.next(); 55 | } 56 | return jo; 57 | } 58 | 59 | /** 60 | * Convert a JSONObject into a cookie list. A cookie list is a sequence 61 | * of name/value pairs. The names are separated from the values by '='. 62 | * The pairs are separated by ';'. The characters '%', '+', '=', and ';' 63 | * in the names and values are replaced by "%hh". 64 | * @param jo A JSONObject 65 | * @return A cookie list string 66 | * @throws JSONException 67 | */ 68 | public static String toString(JSONObject jo) throws JSONException { 69 | boolean b = false; 70 | final StringBuilder sb = new StringBuilder(); 71 | // Don't use the new entrySet API to maintain Android support 72 | for (final String key : jo.keySet()) { 73 | final Object value = jo.opt(key); 74 | if (!JSONObject.NULL.equals(value)) { 75 | if (b) { 76 | sb.append(';'); 77 | } 78 | sb.append(Cookie.escape(key)); 79 | sb.append("="); 80 | sb.append(Cookie.escape(value.toString())); 81 | b = true; 82 | } 83 | } 84 | return sb.toString(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/connect2/Utils.java: -------------------------------------------------------------------------------- 1 | package connect2; 2 | 3 | import java.util.Map; 4 | import java.io.InputStream; 5 | import java.io.IOException; 6 | import java.io.ByteArrayOutputStream; 7 | import java.io.DataOutputStream; 8 | import java.io.OutputStream; 9 | import java.net.URL; 10 | import java.net.URLEncoder; 11 | import java.net.HttpURLConnection; 12 | import com.sun.net.httpserver.HttpExchange; 13 | import org.json.JSONObject; 14 | 15 | public class Utils { 16 | public static ByteArrayOutputStream streamToByteArrayOutputStream(InputStream in) throws IOException { 17 | byte[] buf = new byte[4096]; 18 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 19 | while(true) { 20 | int n = in.read(buf); 21 | if(n == -1) { 22 | break; 23 | } 24 | out.write(buf, 0, n); 25 | } 26 | return out; 27 | } 28 | 29 | public static byte[] streamToByteArray(InputStream in) throws IOException { 30 | return streamToByteArrayOutputStream(in).toByteArray(); 31 | } 32 | 33 | public static String streamToString(InputStream in) throws IOException { 34 | return streamToByteArrayOutputStream(in).toString("UTF-8"); 35 | } 36 | 37 | public static String post(String url, byte[] request) throws IOException { 38 | URL u = new URL(url); 39 | HttpURLConnection con = (HttpURLConnection) u.openConnection(); 40 | con.setRequestMethod("POST"); 41 | con.setRequestProperty("User-Agent", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2"); 42 | con.setConnectTimeout(5000); 43 | con.setDoOutput(true); 44 | DataOutputStream out = new DataOutputStream(con.getOutputStream()); 45 | out.write(request); 46 | out.flush(); 47 | out.close(); 48 | String response = Utils.streamToString(con.getInputStream()); 49 | return response; 50 | } 51 | 52 | public static String postForm(String url, Map request) throws IOException { 53 | StringBuilder result = new StringBuilder(); 54 | boolean first = true; 55 | for(Map.Entry entry : request.entrySet()) { 56 | if(first) { 57 | first = false; 58 | } else { 59 | result.append("&"); 60 | } 61 | result.append(URLEncoder.encode(entry.getKey(), "UTF-8")); 62 | result.append("="); 63 | result.append(URLEncoder.encode(entry.getValue(), "UTF-8")); 64 | } 65 | return Utils.post(url, result.toString().getBytes("UTF-8")); 66 | } 67 | 68 | public static String get(String url) throws IOException { 69 | URL u = new URL(url); 70 | HttpURLConnection con = (HttpURLConnection) u.openConnection(); 71 | con.setRequestMethod("GET"); 72 | con.setRequestProperty("User-Agent", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2"); 73 | con.setConnectTimeout(5000); 74 | String response = Utils.streamToString(con.getInputStream()); 75 | return response; 76 | } 77 | 78 | public static void respondString(HttpExchange t, String str) throws IOException { 79 | byte[] bytes = str.getBytes("UTF-8"); 80 | t.sendResponseHeaders(200, bytes.length); 81 | OutputStream os = t.getResponseBody(); 82 | os.write(bytes); 83 | os.close(); 84 | } 85 | 86 | public static void respondJSON(HttpExchange t, JSONObject object) throws IOException { 87 | t.getResponseHeaders().set("Content-Type", "application/json"); 88 | respondString(t, object.toString()); 89 | } 90 | 91 | public static void respondError(HttpExchange t, String msg) throws IOException { 92 | JSONObject obj = new JSONObject(); 93 | obj.put("error", msg); 94 | respondJSON(t, obj); 95 | } 96 | 97 | public static int unsignedByte(byte x) { 98 | return ((int) x) & 0xFF; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/connect2/ECUtil.java: -------------------------------------------------------------------------------- 1 | package connect2; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.nio.ByteBuffer; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public class ECUtil { 9 | static final char[] HEX_CHARS = "0123456789abcdef".toCharArray(); 10 | 11 | public static String hexEncode(byte[] buf) 12 | { 13 | char[] chars = new char[2 * buf.length]; 14 | for (int i = 0; i < buf.length; ++i) 15 | { 16 | chars[2 * i] = HEX_CHARS[(buf[i] & 0xF0) >>> 4]; 17 | chars[2 * i + 1] = HEX_CHARS[buf[i] & 0x0F]; 18 | } 19 | return new String(chars); 20 | } 21 | 22 | public static byte[] hexDecode(String s) { 23 | int len = s.length(); 24 | byte[] data = new byte[len / 2]; 25 | for (int i = 0; i < len - 1; i += 2) { 26 | data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) 27 | + Character.digit(s.charAt(i+1), 16)); 28 | } 29 | return data; 30 | } 31 | 32 | public static byte[] shortToByteArray(short s) { 33 | return new byte[] { (byte) ((s & 0xFF00) >> 8), (byte) (s & 0x00FF) }; 34 | } 35 | 36 | public static int unsignedShort(short s) { 37 | byte[] array = shortToByteArray(s); 38 | int b1 = (0x000000FF & ((int) array[0])); 39 | int b2 = (0x000000FF & ((int) array[1])); 40 | return (b1 << 8 | b2); 41 | } 42 | 43 | public static int unsignedByte(byte b) { 44 | return (0x000000FF & ((int)b)); 45 | } 46 | 47 | public static String getTerminatedString(ByteBuffer buf) { 48 | return new String(getTerminatedArray(buf)); 49 | } 50 | 51 | public static byte[] getTerminatedArray(ByteBuffer buf) { 52 | int start = buf.position(); 53 | 54 | while(buf.get() != 0) {} 55 | int end = buf.position(); 56 | 57 | byte[] bytes = new byte[end - start - 1]; //don't include terminator 58 | buf.position(start); 59 | buf.get(bytes); 60 | 61 | //put position after array 62 | buf.position(end); //skip terminator 63 | 64 | return bytes; 65 | } 66 | 67 | public static byte[] strToBytes(String str) { 68 | try { 69 | return str.getBytes("UTF-8"); 70 | } catch(UnsupportedEncodingException uee) { 71 | System.out.println("[ECUtil] UTF-8 is an unsupported encoding: " + uee.getLocalizedMessage()); 72 | return str.getBytes(); 73 | } 74 | } 75 | 76 | public static byte[] encodeStatString(byte[] data) 77 | { 78 | byte Mask = 1; 79 | List result = new ArrayList(); 80 | 81 | for(int i = 0; i < data.length; i++) { 82 | if(data[i] % 2 == 0) 83 | result.add((byte) (data[i] + 1)); 84 | else { 85 | result.add( data[i] ); 86 | Mask |= 1 << ((i % 7) + 1); 87 | } 88 | 89 | if(i % 7 == 6 || i == data.length - 1) { 90 | result.add(result.size() - 1 - (i % 7), Mask); 91 | Mask = 1; 92 | } 93 | } 94 | 95 | byte[] array = new byte[result.size()]; 96 | 97 | for(int i = 0; i < result.size(); i++) { 98 | array[i] = result.get(i); 99 | } 100 | 101 | return array; 102 | } 103 | 104 | public static byte[] decodeStatString(byte[] data) { 105 | byte Mask = 0; 106 | ByteBuffer result = ByteBuffer.allocate(data.length); 107 | 108 | for(int i = 0; i < data.length; i++) 109 | { 110 | if( ( i % 8 ) == 0 ) 111 | Mask = data[i]; 112 | else 113 | { 114 | if( ( Mask & ( 1 << ( i % 8 ) ) ) == 0 ) 115 | result.put( (byte) (data[i] - 1) ); 116 | else 117 | result.put( data[i] ); 118 | } 119 | } 120 | 121 | byte[] array = new byte[result.position()]; 122 | 123 | for(int i = 0; i < array.length; i++) { 124 | array[i] = result.get(i); 125 | } 126 | 127 | return array; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/org/json/HTTP.java: -------------------------------------------------------------------------------- 1 | package org.json; 2 | 3 | /* 4 | Copyright (c) 2002 JSON.org 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | The Software shall be used for Good, not Evil. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | import java.util.Locale; 28 | 29 | /** 30 | * Convert an HTTP header to a JSONObject and back. 31 | * @author JSON.org 32 | * @version 2015-12-09 33 | */ 34 | public class HTTP { 35 | 36 | /** Carriage return/line feed. */ 37 | public static final String CRLF = "\r\n"; 38 | 39 | /** 40 | * Convert an HTTP header string into a JSONObject. It can be a request 41 | * header or a response header. A request header will contain 42 | *

{
 43 |      *    Method: "POST" (for example),
 44 |      *    "Request-URI": "/" (for example),
 45 |      *    "HTTP-Version": "HTTP/1.1" (for example)
 46 |      * }
47 | * A response header will contain 48 | *
{
 49 |      *    "HTTP-Version": "HTTP/1.1" (for example),
 50 |      *    "Status-Code": "200" (for example),
 51 |      *    "Reason-Phrase": "OK" (for example)
 52 |      * }
53 | * In addition, the other parameters in the header will be captured, using 54 | * the HTTP field names as JSON names, so that
 55 |      *    Date: Sun, 26 May 2002 18:06:04 GMT
 56 |      *    Cookie: Q=q2=PPEAsg--; B=677gi6ouf29bn&b=2&f=s
 57 |      *    Cache-Control: no-cache
58 | * become 59 | *
{...
 60 |      *    Date: "Sun, 26 May 2002 18:06:04 GMT",
 61 |      *    Cookie: "Q=q2=PPEAsg--; B=677gi6ouf29bn&b=2&f=s",
 62 |      *    "Cache-Control": "no-cache",
 63 |      * ...}
64 | * It does no further checking or conversion. It does not parse dates. 65 | * It does not do '%' transforms on URLs. 66 | * @param string An HTTP header string. 67 | * @return A JSONObject containing the elements and attributes 68 | * of the XML string. 69 | * @throws JSONException 70 | */ 71 | public static JSONObject toJSONObject(String string) throws JSONException { 72 | JSONObject jo = new JSONObject(); 73 | HTTPTokener x = new HTTPTokener(string); 74 | String token; 75 | 76 | token = x.nextToken(); 77 | if (token.toUpperCase(Locale.ROOT).startsWith("HTTP")) { 78 | 79 | // Response 80 | 81 | jo.put("HTTP-Version", token); 82 | jo.put("Status-Code", x.nextToken()); 83 | jo.put("Reason-Phrase", x.nextTo('\0')); 84 | x.next(); 85 | 86 | } else { 87 | 88 | // Request 89 | 90 | jo.put("Method", token); 91 | jo.put("Request-URI", x.nextToken()); 92 | jo.put("HTTP-Version", x.nextToken()); 93 | } 94 | 95 | // Fields 96 | 97 | while (x.more()) { 98 | String name = x.nextTo(':'); 99 | x.next(':'); 100 | jo.put(name, x.nextTo('\0')); 101 | x.next(); 102 | } 103 | return jo; 104 | } 105 | 106 | 107 | /** 108 | * Convert a JSONObject into an HTTP header. A request header must contain 109 | *
{
110 |      *    Method: "POST" (for example),
111 |      *    "Request-URI": "/" (for example),
112 |      *    "HTTP-Version": "HTTP/1.1" (for example)
113 |      * }
114 | * A response header must contain 115 | *
{
116 |      *    "HTTP-Version": "HTTP/1.1" (for example),
117 |      *    "Status-Code": "200" (for example),
118 |      *    "Reason-Phrase": "OK" (for example)
119 |      * }
120 | * Any other members of the JSONObject will be output as HTTP fields. 121 | * The result will end with two CRLF pairs. 122 | * @param jo A JSONObject 123 | * @return An HTTP header string. 124 | * @throws JSONException if the object does not contain enough 125 | * information. 126 | */ 127 | public static String toString(JSONObject jo) throws JSONException { 128 | StringBuilder sb = new StringBuilder(); 129 | if (jo.has("Status-Code") && jo.has("Reason-Phrase")) { 130 | sb.append(jo.getString("HTTP-Version")); 131 | sb.append(' '); 132 | sb.append(jo.getString("Status-Code")); 133 | sb.append(' '); 134 | sb.append(jo.getString("Reason-Phrase")); 135 | } else if (jo.has("Method") && jo.has("Request-URI")) { 136 | sb.append(jo.getString("Method")); 137 | sb.append(' '); 138 | sb.append('"'); 139 | sb.append(jo.getString("Request-URI")); 140 | sb.append('"'); 141 | sb.append(' '); 142 | sb.append(jo.getString("HTTP-Version")); 143 | } else { 144 | throw new JSONException("Not enough material for an HTTP header."); 145 | } 146 | sb.append(CRLF); 147 | // Don't use the new entrySet API to maintain Android support 148 | for (final String key : jo.keySet()) { 149 | String value = jo.optString(key); 150 | if (!"HTTP-Version".equals(key) && !"Status-Code".equals(key) && 151 | !"Reason-Phrase".equals(key) && !"Method".equals(key) && 152 | !"Request-URI".equals(key) && !JSONObject.NULL.equals(value)) { 153 | sb.append(key); 154 | sb.append(": "); 155 | sb.append(jo.optString(key)); 156 | sb.append(CRLF); 157 | } 158 | } 159 | sb.append(CRLF); 160 | return sb.toString(); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/org/json/Cookie.java: -------------------------------------------------------------------------------- 1 | package org.json; 2 | 3 | /* 4 | Copyright (c) 2002 JSON.org 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | The Software shall be used for Good, not Evil. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /** 28 | * Convert a web browser cookie specification to a JSONObject and back. 29 | * JSON and Cookies are both notations for name/value pairs. 30 | * @author JSON.org 31 | * @version 2015-12-09 32 | */ 33 | public class Cookie { 34 | 35 | /** 36 | * Produce a copy of a string in which the characters '+', '%', '=', ';' 37 | * and control characters are replaced with "%hh". This is a gentle form 38 | * of URL encoding, attempting to cause as little distortion to the 39 | * string as possible. The characters '=' and ';' are meta characters in 40 | * cookies. By convention, they are escaped using the URL-encoding. This is 41 | * only a convention, not a standard. Often, cookies are expected to have 42 | * encoded values. We encode '=' and ';' because we must. We encode '%' and 43 | * '+' because they are meta characters in URL encoding. 44 | * @param string The source string. 45 | * @return The escaped result. 46 | */ 47 | public static String escape(String string) { 48 | char c; 49 | String s = string.trim(); 50 | int length = s.length(); 51 | StringBuilder sb = new StringBuilder(length); 52 | for (int i = 0; i < length; i += 1) { 53 | c = s.charAt(i); 54 | if (c < ' ' || c == '+' || c == '%' || c == '=' || c == ';') { 55 | sb.append('%'); 56 | sb.append(Character.forDigit((char)((c >>> 4) & 0x0f), 16)); 57 | sb.append(Character.forDigit((char)(c & 0x0f), 16)); 58 | } else { 59 | sb.append(c); 60 | } 61 | } 62 | return sb.toString(); 63 | } 64 | 65 | 66 | /** 67 | * Convert a cookie specification string into a JSONObject. The string 68 | * will contain a name value pair separated by '='. The name and the value 69 | * will be unescaped, possibly converting '+' and '%' sequences. The 70 | * cookie properties may follow, separated by ';', also represented as 71 | * name=value (except the secure property, which does not have a value). 72 | * The name will be stored under the key "name", and the value will be 73 | * stored under the key "value". This method does not do checking or 74 | * validation of the parameters. It only converts the cookie string into 75 | * a JSONObject. 76 | * @param string The cookie specification string. 77 | * @return A JSONObject containing "name", "value", and possibly other 78 | * members. 79 | * @throws JSONException 80 | */ 81 | public static JSONObject toJSONObject(String string) throws JSONException { 82 | String name; 83 | JSONObject jo = new JSONObject(); 84 | Object value; 85 | JSONTokener x = new JSONTokener(string); 86 | jo.put("name", x.nextTo('=')); 87 | x.next('='); 88 | jo.put("value", x.nextTo(';')); 89 | x.next(); 90 | while (x.more()) { 91 | name = unescape(x.nextTo("=;")); 92 | if (x.next() != '=') { 93 | if (name.equals("secure")) { 94 | value = Boolean.TRUE; 95 | } else { 96 | throw x.syntaxError("Missing '=' in cookie parameter."); 97 | } 98 | } else { 99 | value = unescape(x.nextTo(';')); 100 | x.next(); 101 | } 102 | jo.put(name, value); 103 | } 104 | return jo; 105 | } 106 | 107 | 108 | /** 109 | * Convert a JSONObject into a cookie specification string. The JSONObject 110 | * must contain "name" and "value" members. 111 | * If the JSONObject contains "expires", "domain", "path", or "secure" 112 | * members, they will be appended to the cookie specification string. 113 | * All other members are ignored. 114 | * @param jo A JSONObject 115 | * @return A cookie specification string 116 | * @throws JSONException 117 | */ 118 | public static String toString(JSONObject jo) throws JSONException { 119 | StringBuilder sb = new StringBuilder(); 120 | 121 | sb.append(escape(jo.getString("name"))); 122 | sb.append("="); 123 | sb.append(escape(jo.getString("value"))); 124 | if (jo.has("expires")) { 125 | sb.append(";expires="); 126 | sb.append(jo.getString("expires")); 127 | } 128 | if (jo.has("domain")) { 129 | sb.append(";domain="); 130 | sb.append(escape(jo.getString("domain"))); 131 | } 132 | if (jo.has("path")) { 133 | sb.append(";path="); 134 | sb.append(escape(jo.getString("path"))); 135 | } 136 | if (jo.optBoolean("secure")) { 137 | sb.append(";secure"); 138 | } 139 | return sb.toString(); 140 | } 141 | 142 | /** 143 | * Convert %hh sequences to single characters, and 144 | * convert plus to space. 145 | * @param string A string that may contain 146 | * + (plus) and 147 | * %hh sequences. 148 | * @return The unescaped string. 149 | */ 150 | public static String unescape(String string) { 151 | int length = string.length(); 152 | StringBuilder sb = new StringBuilder(length); 153 | for (int i = 0; i < length; ++i) { 154 | char c = string.charAt(i); 155 | if (c == '+') { 156 | c = ' '; 157 | } else if (c == '%' && i + 2 < length) { 158 | int d = JSONTokener.dehexchar(string.charAt(i + 1)); 159 | int e = JSONTokener.dehexchar(string.charAt(i + 2)); 160 | if (d >= 0 && e >= 0) { 161 | c = (char)(d * 16 + e); 162 | i += 2; 163 | } 164 | } 165 | sb.append(c); 166 | } 167 | return sb.toString(); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/connect2/ECHost.java: -------------------------------------------------------------------------------- 1 | package connect2; 2 | 3 | import java.awt.Color; 4 | import java.io.IOException; 5 | import java.net.DatagramPacket; 6 | import java.net.DatagramSocket; 7 | import java.net.InetAddress; 8 | import java.net.InetSocketAddress; 9 | import java.net.ServerSocket; 10 | import java.net.Socket; 11 | import java.net.SocketAddress; 12 | import java.net.UnknownHostException; 13 | import java.nio.ByteBuffer; 14 | import java.nio.ByteOrder; 15 | import java.util.ArrayList; 16 | import java.util.Map.Entry; 17 | import java.util.HashMap; 18 | import java.util.Iterator; 19 | import java.util.List; 20 | import java.util.Map; 21 | 22 | public class ECHost implements Runnable { 23 | Map games; 24 | List lastGames; 25 | List activeGames; 26 | 27 | ServerSocket server; 28 | ByteBuffer buf; 29 | 30 | //UDP broadcast socket 31 | DatagramSocket udpSocket; 32 | List udpTargets; 33 | 34 | int war3version; 35 | String name; 36 | String sessionKey; 37 | 38 | int serverPort = 7112; 39 | int counter = 0; //game counter 40 | 41 | boolean terminated = false; 42 | 43 | public ECHost(int war3Version, String name, String sessionKey) { 44 | this.war3version = war3Version; 45 | this.name = name; 46 | this.sessionKey = sessionKey; 47 | 48 | udpTargets = new ArrayList(); 49 | try { 50 | udpTargets.add(new InetSocketAddress(InetAddress.getLocalHost(), 6112)); 51 | udpTargets.add(new InetSocketAddress("255.255.255.255", 6112)); 52 | } catch(UnknownHostException uhe) { 53 | System.out.println("[ECHost] UDP broadcast target error: " + uhe.getLocalizedMessage()); 54 | } 55 | 56 | this.games = new HashMap(); 57 | this.lastGames = new ArrayList(); 58 | this.activeGames = new ArrayList(); 59 | this.buf = ByteBuffer.allocate(65536); 60 | } 61 | 62 | public void update(int war3Version, String name, String sessionKey) { 63 | synchronized(this) { 64 | this.war3version = war3Version; 65 | this.name = name; 66 | this.sessionKey = sessionKey; 67 | } 68 | } 69 | 70 | public void init() { 71 | System.out.println("[ECHost] Creating server socket..."); 72 | 73 | try { 74 | server = new ServerSocket(serverPort); 75 | } catch(IOException ioe) { 76 | System.out.println("[ECHost] Error while initiating server socket: " + ioe.getLocalizedMessage()); 77 | 78 | //increment port number and try again 79 | serverPort++; 80 | System.out.println("[ECHost] Trying again in three seconds on port: " + serverPort); 81 | new RestartThread().start(); 82 | return; 83 | } 84 | 85 | System.out.println("[ECHost] Creating UDP socket..."); 86 | try { 87 | udpSocket = new DatagramSocket(); 88 | } catch(IOException ioe) { 89 | System.out.println("[ECHost] Error while initiating UDP socket: " + ioe.getLocalizedMessage()); 90 | return; 91 | } 92 | 93 | new Thread(this).start(); 94 | } 95 | 96 | public void deinit() { 97 | terminated = true; 98 | 99 | try { 100 | server.close(); 101 | udpSocket.close(); 102 | } catch(IOException ioe) {} 103 | } 104 | 105 | public void decreateGame(int counter) { 106 | ByteBuffer lbuf = ByteBuffer.allocate(8); 107 | lbuf.order(ByteOrder.LITTLE_ENDIAN); 108 | 109 | lbuf.put((byte) 247); //W3GS constant 110 | lbuf.put((byte) 51); //DECREATE 111 | lbuf.putShort((short) 8); //packet length 112 | 113 | lbuf.putInt(counter); 114 | 115 | try { 116 | for(SocketAddress udpTarget : udpTargets) { 117 | DatagramPacket packet = new DatagramPacket(lbuf.array(), 8, udpTarget); 118 | udpSocket.send(packet); 119 | } 120 | } catch(IOException ioe) { 121 | System.out.println("[ECHost] Decreate error: " + ioe.getLocalizedMessage()); 122 | } 123 | } 124 | 125 | public void clearGames() { 126 | synchronized(this.games) { 127 | if(this.udpSocket != null) { 128 | for(GameInfo game : this.activeGames) { 129 | decreateGame(game.uid); 130 | } 131 | } 132 | 133 | this.activeGames.clear(); 134 | this.lastGames.clear(); 135 | 136 | Iterator> it = this.games.entrySet().iterator(); 137 | while(it.hasNext()) { 138 | Map.Entry entry = it.next(); 139 | if(entry.getValue().expired()) { 140 | it.remove(); 141 | } 142 | } 143 | } 144 | } 145 | 146 | public boolean receivedUDP(ByteBuffer lbuf, String gamenameFilter) { 147 | if(buf == null || udpSocket == null) { 148 | return false; 149 | } 150 | 151 | buf.clear(); //use buf to create our own packet 152 | lbuf.order(ByteOrder.LITTLE_ENDIAN); 153 | buf.order(ByteOrder.LITTLE_ENDIAN); 154 | 155 | lbuf.getShort(); //ignore header because this is from server directly 156 | lbuf.getShort(); //also ignore length 157 | 158 | buf.put((byte) 247); //W3GS 159 | buf.put((byte) 48); //GAMEINFO 160 | buf.putShort((short) 0); //packet size; do later 161 | 162 | byte[] addr = new byte[4]; 163 | lbuf.get(addr); //lbuf is special packet that includes IP address (not in W3GS GAMEINFO but used for entconnect) 164 | 165 | int productid = lbuf.getInt(); 166 | buf.putInt(productid); //product ID 167 | lbuf.getInt(); //ignore version in packet 168 | buf.putInt(war3version); //version 169 | 170 | int hostCounter = lbuf.getInt(); //hostcounter 171 | buf.putInt(counter); //replace hostcounter with uid 172 | 173 | buf.putInt(lbuf.getInt()); //unknown 174 | 175 | String gamename = ECUtil.getTerminatedString(lbuf); 176 | 177 | byte[] bytes = ECUtil.strToBytes(gamename); 178 | buf.put(bytes); 179 | buf.put((byte) 0); //null terminator 180 | 181 | buf.put(lbuf.get()); //unknown 182 | 183 | byte[] statString = ECUtil.getTerminatedArray(lbuf); 184 | buf.put(statString); //StatString 185 | buf.put((byte) 0); //null terminator 186 | 187 | byte[] decodedStatString = ECUtil.decodeStatString(statString); 188 | ByteBuffer ssbuf = ByteBuffer.wrap(decodedStatString); 189 | ssbuf.getInt(); 190 | ssbuf.get(); 191 | ssbuf.getInt(); 192 | ssbuf.getInt(); 193 | String mapPath = ECUtil.getTerminatedString(ssbuf); 194 | if(mapPath.endsWith(".w3x") || mapPath.endsWith(".w3m")) { 195 | mapPath = mapPath.substring(0, mapPath.length() - 4); 196 | } 197 | 198 | buf.putInt(lbuf.getInt()); //slots total 199 | buf.putInt(lbuf.getInt()); //game type 200 | buf.putInt(lbuf.getInt()); //unknown 201 | buf.putInt(lbuf.getInt()); //slots open 202 | buf.putInt(lbuf.getInt()); //up time 203 | 204 | //get the sender's port, but use our own server's port 205 | int senderPort = ECUtil.unsignedShort(lbuf.getShort()); 206 | buf.putShort((short) serverPort); //port 207 | 208 | // connect.entgaming.net adds username as extra field at the end 209 | String extra = ECUtil.getTerminatedString(lbuf); 210 | 211 | //assign length in little endian 212 | int length = buf.position(); 213 | buf.putShort(2, (short) length); 214 | 215 | //get bytes 216 | byte[] packetBytes = new byte[length]; 217 | buf.position(0); 218 | buf.get(packetBytes); 219 | 220 | //create new gameinfo 221 | GameInfo game = new GameInfo(counter++, addr, senderPort, hostCounter, gamename, mapPath, extra); 222 | 223 | synchronized(this.games) { 224 | this.games.put(game.uid, game); 225 | this.lastGames.add(game); 226 | } 227 | 228 | //filter by gamename if desired 229 | if(gamenameFilter != null && gamenameFilter.length() > 4 && !gamename.contains(gamenameFilter)) { 230 | return false; 231 | } 232 | 233 | //send packet to LAN, or to udpTarget 234 | System.out.println("[ECHost] Broadcasting with gamename [" + gamename + "]; version: " + war3version + 235 | "; productid: " + productid + "; senderport: " + senderPort); 236 | 237 | try { 238 | synchronized(this.games) { 239 | this.activeGames.add(game); 240 | } 241 | for(SocketAddress udpTarget : udpTargets) { 242 | DatagramPacket packet = new DatagramPacket(packetBytes, packetBytes.length, udpTarget); 243 | udpSocket.send(packet); 244 | } 245 | } catch(IOException ioe) { 246 | ioe.printStackTrace(); 247 | System.out.println("[ECHost] Error while broadcast UDP: " + ioe.getLocalizedMessage()); 248 | } 249 | 250 | return true; 251 | } 252 | 253 | public GameInfo searchGame(int uid) { 254 | synchronized(this.games) { 255 | return this.games.get(uid); 256 | } 257 | } 258 | 259 | public List getGames() { 260 | List games = new ArrayList(); 261 | synchronized(this.games) { 262 | for(GameInfo game : this.lastGames) { 263 | games.add(game); 264 | } 265 | } 266 | return games; 267 | } 268 | 269 | public void run() { 270 | while(!terminated) { 271 | try { 272 | Socket socket = server.accept(); 273 | System.out.println("[ECHost] Receiving connection from " + socket.getInetAddress().getHostAddress()); 274 | synchronized(this) { 275 | new ECConnection(this, socket, this.name, this.sessionKey); 276 | } 277 | } catch(IOException ioe) { 278 | System.out.println("[ECHost] Error while accepting connection: " + ioe.getLocalizedMessage()); 279 | break; 280 | } 281 | } 282 | } 283 | 284 | //used if we failed to bind on a certain server port 285 | class RestartThread extends Thread { 286 | public void run() { 287 | try { 288 | Thread.sleep(3000); 289 | } catch(InterruptedException e) {} 290 | 291 | init(); 292 | } 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /cls/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | WC3Connect 12 | 13 | 14 |
15 |

Login

16 |

Login with your WC3Connect account, or create a new one.

17 |

If you previously logged in with your entgaming.net forum account, you can login with the same username and password.

18 | 19 | 20 |
21 |
22 | 23 |
24 | 25 |
26 |
27 |
28 | 29 |
30 | 31 |
32 |
33 |
34 |
35 | 36 | 37 |
38 |
39 |
40 |
41 | 164 | 165 | 189 | 190 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | -------------------------------------------------------------------------------- /cls/static/index.js: -------------------------------------------------------------------------------- 1 | if(typeof console == 'undefined') { 2 | var console = {log: function() {}}; 3 | } 4 | 5 | $(document).ready(function() { 6 | $('#username').focus(); 7 | 8 | var showError = function(msg) { 9 | if(msg) { 10 | $('#loginerror').text(msg); 11 | $('#loginerror').css('display', ''); 12 | } else { 13 | $('#loginerror').css('display', 'none'); 14 | } 15 | }; 16 | var showMsg = function(msg) { 17 | if(msg) { 18 | $('#loginmsg').text(msg); 19 | $('#loginmsg').css('display', ''); 20 | } else { 21 | $('#loginmsg').css('display', 'none'); 22 | } 23 | }; 24 | 25 | $('#login_form').submit(function(e) { 26 | e.preventDefault(); 27 | var params = { 28 | 'username': $('#username').val(), 29 | 'password': $('#password').val(), 30 | }; 31 | $('.step1_btn').prop('disabled', true); 32 | $.post('/login', params, function(data) { 33 | $('#username').val(''); 34 | $('#password').val(''); 35 | if(data.error) { 36 | showError(data.error); 37 | $('#username').focus(); 38 | } else { 39 | $('#step1').css('display', 'none'); 40 | $('#step2').css('display', ''); 41 | onLogin(data.session_key); 42 | } 43 | }, 'json').always(function() { 44 | $('.step1_btn').prop('disabled', false); 45 | }); 46 | }); 47 | 48 | $('#signup_btn').click(function(e) { 49 | e.preventDefault(); 50 | var params = { 51 | 'username': $('#username').val(), 52 | 'password': $('#password').val(), 53 | }; 54 | $('.step1_btn').prop('disabled', true); 55 | $.post('/signup', params, function(data) { 56 | $('#username').val(''); 57 | $('#password').val(''); 58 | if(data.error) { 59 | showError(data.error); 60 | } else { 61 | showMsg('Your account has been created. You can now login below.'); 62 | } 63 | $('#username').focus(); 64 | }, 'json').always(function() { 65 | $('.step1_btn').prop('disabled', false); 66 | }); 67 | }); 68 | 69 | $('#username').keypress(function() { 70 | if($('#username').val().length > 0) { 71 | showError(); 72 | showMsg(); 73 | } 74 | }); 75 | }); 76 | 77 | var onLogin = function(sessionKey) { 78 | $.post('/version', function(data) { 79 | if(data.error) { 80 | $('#versiondiv').text('Error: ' + data.error + '.'); 81 | $('#versiondiv').css('display', ''); 82 | } else if(!data.up_to_date) { 83 | $('#versiondiv').text('A new version is available! Download it at https://entgaming.net/wc3connect'); 84 | $('#versiondiv').css('display', ''); 85 | } 86 | }, 'json'); 87 | 88 | $.post('/motd', function(data) { 89 | if(data.motd) { 90 | $('#motddiv').text(data.motd); 91 | $('#motddiv').css('display', ''); 92 | } 93 | }, 'json'); 94 | 95 | $.post('/get-config', function(data) { 96 | if(data.no_broadcast) { 97 | $('#cfgNoBroadcast').prop('checked', true); 98 | } 99 | if(data.enable_lan) { 100 | $('#cfgEnableLAN').prop('checked', true); 101 | } 102 | if(data.disable_in_game_refresh) { 103 | $('#cfgDisableInGameRefresh').prop('checked', true); 104 | } 105 | }, 'json'); 106 | 107 | var showWC3Alert = function(id) { 108 | $('.wc3alert').css('display', 'none'); 109 | $('#' + id).css('display', ''); 110 | }; 111 | 112 | $.post('/wc3', function(data) { 113 | if(data.installation && data.installation.correct_patch && data.installation.backup) { 114 | $('#wc3good_dir').text(data.installation.path); 115 | $('#wc3good_btn').data('path', data.installation.path) 116 | showWC3Alert('wc3gooddiv'); 117 | } else if(data.home_dir) { 118 | if(data.installation && data.installation.correct_patch) { 119 | $('#wc3backup_src').text(data.installation.path); 120 | $('#wc3backup_home').text(data.home_dir); 121 | $('#wc3backup_btn').data('path', data.installation.path); 122 | showWC3Alert('wc3backupdiv'); 123 | } else if(data.installation) { 124 | $('#wc3bad1_dir').text(data.installation.path); 125 | $('#wc3bad1_home').text(data.home_dir); 126 | showWC3Alert('wc3bad1div'); 127 | } else { 128 | $('#wc3bad2_home').text(data.home_dir); 129 | showWC3Alert('wc3bad2div'); 130 | } 131 | } 132 | }, 'json'); 133 | 134 | var setGames = function(tbody, games) { 135 | var existingRows = {}; 136 | tbody.children('tr.game').each(function() { 137 | var uiRow = $(this); 138 | var key = uiRow.data('key'); 139 | existingRows[key] = uiRow; 140 | }); 141 | var lastRow = null; 142 | for(var gameIdx = 0; gameIdx < games.length; gameIdx++) { 143 | var game = games[gameIdx]; 144 | 145 | if(game.app_game) { 146 | var cols = [ 147 | game.app_game.location, 148 | game.gamename, 149 | game.app_game.map, 150 | game.app_game.host, 151 | game.app_game.slots_taken + '/' + game.app_game.slots_total, 152 | ]; 153 | } else { 154 | var cols = [ 155 | game.location, 156 | game.gamename, 157 | game.map, 158 | '', 159 | '', 160 | ]; 161 | } 162 | var key = game.ip + '.' + game.gamename; 163 | var existingRow = existingRows[key]; 164 | if(existingRow) { 165 | existingRow.data('uid', game.uid); 166 | for(var i = 0; i < cols.length; i++) { 167 | var el = cols[i]; 168 | if(existingRow.children().eq(i).text() != el) { 169 | existingRow.children().eq(i).text(el); 170 | } 171 | } 172 | if(game.app_game) { 173 | existingRow.data('gameid', game.app_game.id); 174 | } 175 | lastRow = existingRow; 176 | delete existingRows[key]; 177 | } else { 178 | var uiRow = $('') 179 | .addClass('game') 180 | .data('key', key) 181 | .data('uid', game.uid); 182 | for(var i = 0; i < cols.length; i++) { 183 | var uiCol = $('').text(cols[i]); 184 | uiRow = uiRow.append(uiCol); 185 | } 186 | if(game.app_game) { 187 | uiRow = uiRow.data('gameid', game.app_game.id); 188 | } 189 | if(lastRow) { 190 | lastRow.after(uiRow); 191 | lastRow = uiRow; 192 | } else { 193 | tbody.prepend(uiRow); 194 | lastRow = uiRow; 195 | } 196 | } 197 | } 198 | for(var key in existingRows) { 199 | existingRows[key].remove(); 200 | } 201 | }; 202 | 203 | var getRefreshTime = function(counter) { 204 | if(counter && counter < 3) { 205 | return 1000; 206 | } else { 207 | return 3000; 208 | } 209 | }; 210 | 211 | var getGames = function(counter) { 212 | $.post('/games', function(data) { 213 | setGames($('#public-tbody'), data.publicGames); 214 | setGames($('#autohost-tbody'), data.autohostGames); 215 | setGames($('#others-tbody'), data.otherGames); 216 | setGames($('#unmoderated-tbody'), data.unmoderatedGames); 217 | }, 'json') 218 | .always(function() { 219 | setTimeout(function() { 220 | getGames(counter + 1); 221 | }, getRefreshTime(counter)); 222 | }); 223 | }; 224 | $('tbody').on('click', 'tr.game', function() { 225 | $('tr.game.selected').removeClass('selected'); 226 | $('tr.gameinfo').remove(); 227 | $(this).addClass('selected'); 228 | var uid = $(this).data('uid'); 229 | $.post('/show', {'uid': uid}); 230 | 231 | if($(this).data('gameid')) { 232 | var row = $('') 233 | .addClass('gameinfo') 234 | .data('gameid', $(this).data('gameid')); 235 | var col = $('') 236 | .addClass('gameinfo') 237 | .attr('colspan', 5); 238 | row = row.append(col); 239 | $(this).after(row); 240 | refreshInfo(true); 241 | } 242 | }); 243 | $('tbody').on('click', 'tr.gameinfo a', function(e) { 244 | e.preventDefault(); 245 | }); 246 | var refreshInfo = function(norefresh) { 247 | var row = $('tr.gameinfo'); 248 | if(row.length == 0) { 249 | setTimeout(function() { 250 | refreshInfo(); 251 | }, getRefreshTime()); 252 | return; 253 | } 254 | var gameid = row.data('gameid'); 255 | var post = $.post('/gameinfo', {'gameid': gameid}, function(data) { 256 | row.children().html(data); 257 | }); 258 | if(!norefresh) { 259 | post.always(function() { 260 | setTimeout(function() { 261 | refreshInfo(); 262 | }, getRefreshTime()); 263 | }); 264 | } 265 | }; 266 | getGames(1); 267 | setTimeout(function() { 268 | refreshInfo(); 269 | }, 3000); 270 | 271 | var setConfig = function() { 272 | var params = { 273 | 'no_broadcast': $('#cfgNoBroadcast').prop('checked') ? 'yes' : '', 274 | 'enable_lan': $('#cfgEnableLAN').prop('checked') ? 'yes' : '', 275 | 'disable_in_game_refresh': $('#cfgDisableInGameRefresh').prop('checked') ? 'yes' : '', 276 | }; 277 | $.post('/set-config', params); 278 | }; 279 | 280 | $('#cfgNoBroadcast').change(function() { 281 | setConfig(); 282 | }); 283 | 284 | $('#cfgEnableLAN').change(function() { 285 | setConfig(); 286 | }); 287 | 288 | $('#cfgDisableInGameRefresh').change(function() { 289 | setConfig(); 290 | }); 291 | 292 | var showSettingsInfo = function(msg) { 293 | $('#settingsinfo').text(msg); 294 | $('#settingsinfo').css('display', ''); 295 | setTimeout(function() { 296 | $('#settingsinfo').css('display', 'none'); 297 | }, 5000); 298 | }; 299 | 300 | $('#validate_btn').click(function(e) { 301 | e.preventDefault(); 302 | $('#settingsinfo').css('display', 'none'); 303 | var params = { 304 | key: $('#validate_key').val(), 305 | }; 306 | $('#validate_key').val(''); 307 | $.post('/validate', params, function(data) { 308 | if(data.error) { 309 | showSettingsInfo(data.error); 310 | } else { 311 | showSettingsInfo('The validation request was sent successfully! If the key is correct, your WC3Connect account should now be validated in your entgaming.net account.'); 312 | } 313 | }, 'json'); 314 | $('#validateModal').modal('hide') 315 | }); 316 | 317 | $('#wc3backup_btn').click(function(e) { 318 | e.preventDefault(); 319 | $('#progresserror').css('display', 'none'); 320 | $('#progressprogress').css('display', ''); 321 | $('#progressModal').modal('show'); 322 | var path = $('#wc3backup_btn').data('path'); 323 | $.post('/wc3-backup', {'path': path}, function(data) { 324 | if(data.error) { 325 | $('#progresserror').text(data.error); 326 | $('#progresserror').css('display', ''); 327 | $('#progressprogress').css('display', 'none'); 328 | return; 329 | } 330 | $('#progressModal').modal('hide'); 331 | $('#wc3good_dir').text(data.path); 332 | $('#wc3good_btn').data('path', data.path); 333 | showWC3Alert('wc3gooddiv'); 334 | }, 'json'); 335 | }); 336 | 337 | $('#wc3good_btn').click(function(e) { 338 | e.preventDefault(); 339 | var path = $('#wc3good_btn').data('path'); 340 | $.post('/wc3-start', {'path': path}); 341 | }); 342 | }; 343 | -------------------------------------------------------------------------------- /src/org/json/CDL.java: -------------------------------------------------------------------------------- 1 | package org.json; 2 | 3 | /* 4 | Copyright (c) 2002 JSON.org 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | The Software shall be used for Good, not Evil. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /** 28 | * This provides static methods to convert comma delimited text into a 29 | * JSONArray, and to convert a JSONArray into comma delimited text. Comma 30 | * delimited text is a very popular format for data interchange. It is 31 | * understood by most database, spreadsheet, and organizer programs. 32 | *

33 | * Each row of text represents a row in a table or a data record. Each row 34 | * ends with a NEWLINE character. Each row contains one or more values. 35 | * Values are separated by commas. A value can contain any character except 36 | * for comma, unless is is wrapped in single quotes or double quotes. 37 | *

38 | * The first row usually contains the names of the columns. 39 | *

40 | * A comma delimited list can be converted into a JSONArray of JSONObjects. 41 | * The names for the elements in the JSONObjects can be taken from the names 42 | * in the first row. 43 | * @author JSON.org 44 | * @version 2016-05-01 45 | */ 46 | public class CDL { 47 | 48 | /** 49 | * Get the next value. The value can be wrapped in quotes. The value can 50 | * be empty. 51 | * @param x A JSONTokener of the source text. 52 | * @return The value string, or null if empty. 53 | * @throws JSONException if the quoted string is badly formed. 54 | */ 55 | private static String getValue(JSONTokener x) throws JSONException { 56 | char c; 57 | char q; 58 | StringBuffer sb; 59 | do { 60 | c = x.next(); 61 | } while (c == ' ' || c == '\t'); 62 | switch (c) { 63 | case 0: 64 | return null; 65 | case '"': 66 | case '\'': 67 | q = c; 68 | sb = new StringBuffer(); 69 | for (;;) { 70 | c = x.next(); 71 | if (c == q) { 72 | //Handle escaped double-quote 73 | char nextC = x.next(); 74 | if(nextC != '\"') { 75 | // if our quote was the end of the file, don't step 76 | if(nextC > 0) { 77 | x.back(); 78 | } 79 | break; 80 | } 81 | } 82 | if (c == 0 || c == '\n' || c == '\r') { 83 | throw x.syntaxError("Missing close quote '" + q + "'."); 84 | } 85 | sb.append(c); 86 | } 87 | return sb.toString(); 88 | case ',': 89 | x.back(); 90 | return ""; 91 | default: 92 | x.back(); 93 | return x.nextTo(','); 94 | } 95 | } 96 | 97 | /** 98 | * Produce a JSONArray of strings from a row of comma delimited values. 99 | * @param x A JSONTokener of the source text. 100 | * @return A JSONArray of strings. 101 | * @throws JSONException 102 | */ 103 | public static JSONArray rowToJSONArray(JSONTokener x) throws JSONException { 104 | JSONArray ja = new JSONArray(); 105 | for (;;) { 106 | String value = getValue(x); 107 | char c = x.next(); 108 | if (value == null || 109 | (ja.length() == 0 && value.length() == 0 && c != ',')) { 110 | return null; 111 | } 112 | ja.put(value); 113 | for (;;) { 114 | if (c == ',') { 115 | break; 116 | } 117 | if (c != ' ') { 118 | if (c == '\n' || c == '\r' || c == 0) { 119 | return ja; 120 | } 121 | throw x.syntaxError("Bad character '" + c + "' (" + 122 | (int)c + ")."); 123 | } 124 | c = x.next(); 125 | } 126 | } 127 | } 128 | 129 | /** 130 | * Produce a JSONObject from a row of comma delimited text, using a 131 | * parallel JSONArray of strings to provides the names of the elements. 132 | * @param names A JSONArray of names. This is commonly obtained from the 133 | * first row of a comma delimited text file using the rowToJSONArray 134 | * method. 135 | * @param x A JSONTokener of the source text. 136 | * @return A JSONObject combining the names and values. 137 | * @throws JSONException 138 | */ 139 | public static JSONObject rowToJSONObject(JSONArray names, JSONTokener x) 140 | throws JSONException { 141 | JSONArray ja = rowToJSONArray(x); 142 | return ja != null ? ja.toJSONObject(names) : null; 143 | } 144 | 145 | /** 146 | * Produce a comma delimited text row from a JSONArray. Values containing 147 | * the comma character will be quoted. Troublesome characters may be 148 | * removed. 149 | * @param ja A JSONArray of strings. 150 | * @return A string ending in NEWLINE. 151 | */ 152 | public static String rowToString(JSONArray ja) { 153 | StringBuilder sb = new StringBuilder(); 154 | for (int i = 0; i < ja.length(); i += 1) { 155 | if (i > 0) { 156 | sb.append(','); 157 | } 158 | Object object = ja.opt(i); 159 | if (object != null) { 160 | String string = object.toString(); 161 | if (string.length() > 0 && (string.indexOf(',') >= 0 || 162 | string.indexOf('\n') >= 0 || string.indexOf('\r') >= 0 || 163 | string.indexOf(0) >= 0 || string.charAt(0) == '"')) { 164 | sb.append('"'); 165 | int length = string.length(); 166 | for (int j = 0; j < length; j += 1) { 167 | char c = string.charAt(j); 168 | if (c >= ' ' && c != '"') { 169 | sb.append(c); 170 | } 171 | } 172 | sb.append('"'); 173 | } else { 174 | sb.append(string); 175 | } 176 | } 177 | } 178 | sb.append('\n'); 179 | return sb.toString(); 180 | } 181 | 182 | /** 183 | * Produce a JSONArray of JSONObjects from a comma delimited text string, 184 | * using the first row as a source of names. 185 | * @param string The comma delimited text. 186 | * @return A JSONArray of JSONObjects. 187 | * @throws JSONException 188 | */ 189 | public static JSONArray toJSONArray(String string) throws JSONException { 190 | return toJSONArray(new JSONTokener(string)); 191 | } 192 | 193 | /** 194 | * Produce a JSONArray of JSONObjects from a comma delimited text string, 195 | * using the first row as a source of names. 196 | * @param x The JSONTokener containing the comma delimited text. 197 | * @return A JSONArray of JSONObjects. 198 | * @throws JSONException 199 | */ 200 | public static JSONArray toJSONArray(JSONTokener x) throws JSONException { 201 | return toJSONArray(rowToJSONArray(x), x); 202 | } 203 | 204 | /** 205 | * Produce a JSONArray of JSONObjects from a comma delimited text string 206 | * using a supplied JSONArray as the source of element names. 207 | * @param names A JSONArray of strings. 208 | * @param string The comma delimited text. 209 | * @return A JSONArray of JSONObjects. 210 | * @throws JSONException 211 | */ 212 | public static JSONArray toJSONArray(JSONArray names, String string) 213 | throws JSONException { 214 | return toJSONArray(names, new JSONTokener(string)); 215 | } 216 | 217 | /** 218 | * Produce a JSONArray of JSONObjects from a comma delimited text string 219 | * using a supplied JSONArray as the source of element names. 220 | * @param names A JSONArray of strings. 221 | * @param x A JSONTokener of the source text. 222 | * @return A JSONArray of JSONObjects. 223 | * @throws JSONException 224 | */ 225 | public static JSONArray toJSONArray(JSONArray names, JSONTokener x) 226 | throws JSONException { 227 | if (names == null || names.length() == 0) { 228 | return null; 229 | } 230 | JSONArray ja = new JSONArray(); 231 | for (;;) { 232 | JSONObject jo = rowToJSONObject(names, x); 233 | if (jo == null) { 234 | break; 235 | } 236 | ja.put(jo); 237 | } 238 | if (ja.length() == 0) { 239 | return null; 240 | } 241 | return ja; 242 | } 243 | 244 | 245 | /** 246 | * Produce a comma delimited text from a JSONArray of JSONObjects. The 247 | * first row will be a list of names obtained by inspecting the first 248 | * JSONObject. 249 | * @param ja A JSONArray of JSONObjects. 250 | * @return A comma delimited text. 251 | * @throws JSONException 252 | */ 253 | public static String toString(JSONArray ja) throws JSONException { 254 | JSONObject jo = ja.optJSONObject(0); 255 | if (jo != null) { 256 | JSONArray names = jo.names(); 257 | if (names != null) { 258 | return rowToString(names) + toString(names, ja); 259 | } 260 | } 261 | return null; 262 | } 263 | 264 | /** 265 | * Produce a comma delimited text from a JSONArray of JSONObjects using 266 | * a provided list of names. The list of names is not included in the 267 | * output. 268 | * @param names A JSONArray of strings. 269 | * @param ja A JSONArray of JSONObjects. 270 | * @return A comma delimited text. 271 | * @throws JSONException 272 | */ 273 | public static String toString(JSONArray names, JSONArray ja) 274 | throws JSONException { 275 | if (names == null || names.length() == 0) { 276 | return null; 277 | } 278 | StringBuffer sb = new StringBuffer(); 279 | for (int i = 0; i < ja.length(); i += 1) { 280 | JSONObject jo = ja.optJSONObject(i); 281 | if (jo != null) { 282 | sb.append(rowToString(jo.toJSONArray(names))); 283 | } 284 | } 285 | return sb.toString(); 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /src/org/json/JSONPointer.java: -------------------------------------------------------------------------------- 1 | package org.json; 2 | 3 | import static java.lang.String.format; 4 | 5 | import java.io.UnsupportedEncodingException; 6 | import java.net.URLDecoder; 7 | import java.net.URLEncoder; 8 | import java.util.ArrayList; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | /* 13 | Copyright (c) 2002 JSON.org 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in all 23 | copies or substantial portions of the Software. 24 | 25 | The Software shall be used for Good, not Evil. 26 | 27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 30 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 32 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 33 | SOFTWARE. 34 | */ 35 | 36 | /** 37 | * A JSON Pointer is a simple query language defined for JSON documents by 38 | * RFC 6901. 39 | * 40 | * In a nutshell, JSONPointer allows the user to navigate into a JSON document 41 | * using strings, and retrieve targeted objects, like a simple form of XPATH. 42 | * Path segments are separated by the '/' char, which signifies the root of 43 | * the document when it appears as the first char of the string. Array 44 | * elements are navigated using ordinals, counting from 0. JSONPointer strings 45 | * may be extended to any arbitrary number of segments. If the navigation 46 | * is successful, the matched item is returned. A matched item may be a 47 | * JSONObject, a JSONArray, or a JSON value. If the JSONPointer string building 48 | * fails, an appropriate exception is thrown. If the navigation fails to find 49 | * a match, a JSONPointerException is thrown. 50 | * 51 | * @author JSON.org 52 | * @version 2016-05-14 53 | */ 54 | public class JSONPointer { 55 | 56 | // used for URL encoding and decoding 57 | private static final String ENCODING = "utf-8"; 58 | 59 | /** 60 | * This class allows the user to build a JSONPointer in steps, using 61 | * exactly one segment in each step. 62 | */ 63 | public static class Builder { 64 | 65 | // Segments for the eventual JSONPointer string 66 | private final List refTokens = new ArrayList(); 67 | 68 | /** 69 | * Creates a {@code JSONPointer} instance using the tokens previously set using the 70 | * {@link #append(String)} method calls. 71 | */ 72 | public JSONPointer build() { 73 | return new JSONPointer(this.refTokens); 74 | } 75 | 76 | /** 77 | * Adds an arbitrary token to the list of reference tokens. It can be any non-null value. 78 | * 79 | * Unlike in the case of JSON string or URI fragment representation of JSON pointers, the 80 | * argument of this method MUST NOT be escaped. If you want to query the property called 81 | * {@code "a~b"} then you should simply pass the {@code "a~b"} string as-is, there is no 82 | * need to escape it as {@code "a~0b"}. 83 | * 84 | * @param token the new token to be appended to the list 85 | * @return {@code this} 86 | * @throws NullPointerException if {@code token} is null 87 | */ 88 | public Builder append(String token) { 89 | if (token == null) { 90 | throw new NullPointerException("token cannot be null"); 91 | } 92 | this.refTokens.add(token); 93 | return this; 94 | } 95 | 96 | /** 97 | * Adds an integer to the reference token list. Although not necessarily, mostly this token will 98 | * denote an array index. 99 | * 100 | * @param arrayIndex the array index to be added to the token list 101 | * @return {@code this} 102 | */ 103 | public Builder append(int arrayIndex) { 104 | this.refTokens.add(String.valueOf(arrayIndex)); 105 | return this; 106 | } 107 | } 108 | 109 | /** 110 | * Static factory method for {@link Builder}. Example usage: 111 | * 112 | *


113 |      * JSONPointer pointer = JSONPointer.builder()
114 |      *       .append("obj")
115 |      *       .append("other~key").append("another/key")
116 |      *       .append("\"")
117 |      *       .append(0)
118 |      *       .build();
119 |      * 
120 | * 121 | * @return a builder instance which can be used to construct a {@code JSONPointer} instance by chained 122 | * {@link Builder#append(String)} calls. 123 | */ 124 | public static Builder builder() { 125 | return new Builder(); 126 | } 127 | 128 | // Segments for the JSONPointer string 129 | private final List refTokens; 130 | 131 | /** 132 | * Pre-parses and initializes a new {@code JSONPointer} instance. If you want to 133 | * evaluate the same JSON Pointer on different JSON documents then it is recommended 134 | * to keep the {@code JSONPointer} instances due to performance considerations. 135 | * 136 | * @param pointer the JSON String or URI Fragment representation of the JSON pointer. 137 | * @throws IllegalArgumentException if {@code pointer} is not a valid JSON pointer 138 | */ 139 | public JSONPointer(final String pointer) { 140 | if (pointer == null) { 141 | throw new NullPointerException("pointer cannot be null"); 142 | } 143 | if (pointer.isEmpty() || pointer.equals("#")) { 144 | this.refTokens = Collections.emptyList(); 145 | return; 146 | } 147 | String refs; 148 | if (pointer.startsWith("#/")) { 149 | refs = pointer.substring(2); 150 | try { 151 | refs = URLDecoder.decode(refs, ENCODING); 152 | } catch (UnsupportedEncodingException e) { 153 | throw new RuntimeException(e); 154 | } 155 | } else if (pointer.startsWith("/")) { 156 | refs = pointer.substring(1); 157 | } else { 158 | throw new IllegalArgumentException("a JSON pointer should start with '/' or '#/'"); 159 | } 160 | this.refTokens = new ArrayList(); 161 | int slashIdx = -1; 162 | int prevSlashIdx = 0; 163 | do { 164 | prevSlashIdx = slashIdx + 1; 165 | slashIdx = refs.indexOf('/', prevSlashIdx); 166 | if(prevSlashIdx == slashIdx || prevSlashIdx == refs.length()) { 167 | // found 2 slashes in a row ( obj//next ) 168 | // or single slash at the end of a string ( obj/test/ ) 169 | this.refTokens.add(""); 170 | } else if (slashIdx >= 0) { 171 | final String token = refs.substring(prevSlashIdx, slashIdx); 172 | this.refTokens.add(unescape(token)); 173 | } else { 174 | // last item after separator, or no separator at all. 175 | final String token = refs.substring(prevSlashIdx); 176 | this.refTokens.add(unescape(token)); 177 | } 178 | } while (slashIdx >= 0); 179 | // using split does not take into account consecutive separators or "ending nulls" 180 | //for (String token : refs.split("/")) { 181 | // this.refTokens.add(unescape(token)); 182 | //} 183 | } 184 | 185 | public JSONPointer(List refTokens) { 186 | this.refTokens = new ArrayList(refTokens); 187 | } 188 | 189 | private String unescape(String token) { 190 | return token.replace("~1", "/").replace("~0", "~") 191 | .replace("\\\"", "\"") 192 | .replace("\\\\", "\\"); 193 | } 194 | 195 | /** 196 | * Evaluates this JSON Pointer on the given {@code document}. The {@code document} 197 | * is usually a {@link JSONObject} or a {@link JSONArray} instance, but the empty 198 | * JSON Pointer ({@code ""}) can be evaluated on any JSON values and in such case the 199 | * returned value will be {@code document} itself. 200 | * 201 | * @param document the JSON document which should be the subject of querying. 202 | * @return the result of the evaluation 203 | * @throws JSONPointerException if an error occurs during evaluation 204 | */ 205 | public Object queryFrom(Object document) throws JSONPointerException { 206 | if (this.refTokens.isEmpty()) { 207 | return document; 208 | } 209 | Object current = document; 210 | for (String token : this.refTokens) { 211 | if (current instanceof JSONObject) { 212 | current = ((JSONObject) current).opt(unescape(token)); 213 | } else if (current instanceof JSONArray) { 214 | current = readByIndexToken(current, token); 215 | } else { 216 | throw new JSONPointerException(format( 217 | "value [%s] is not an array or object therefore its key %s cannot be resolved", current, 218 | token)); 219 | } 220 | } 221 | return current; 222 | } 223 | 224 | /** 225 | * Matches a JSONArray element by ordinal position 226 | * @param current the JSONArray to be evaluated 227 | * @param indexToken the array index in string form 228 | * @return the matched object. If no matching item is found a 229 | * @throws JSONPointerException is thrown if the index is out of bounds 230 | */ 231 | private Object readByIndexToken(Object current, String indexToken) throws JSONPointerException { 232 | try { 233 | int index = Integer.parseInt(indexToken); 234 | JSONArray currentArr = (JSONArray) current; 235 | if (index >= currentArr.length()) { 236 | throw new JSONPointerException(format("index %s is out of bounds - the array has %d elements", indexToken, 237 | Integer.valueOf(currentArr.length()))); 238 | } 239 | try { 240 | return currentArr.get(index); 241 | } catch (JSONException e) { 242 | throw new JSONPointerException("Error reading value at index position " + index, e); 243 | } 244 | } catch (NumberFormatException e) { 245 | throw new JSONPointerException(format("%s is not an array index", indexToken), e); 246 | } 247 | } 248 | 249 | /** 250 | * Returns a string representing the JSONPointer path value using string 251 | * representation 252 | */ 253 | @Override 254 | public String toString() { 255 | StringBuilder rval = new StringBuilder(""); 256 | for (String token: this.refTokens) { 257 | rval.append('/').append(escape(token)); 258 | } 259 | return rval.toString(); 260 | } 261 | 262 | /** 263 | * Escapes path segment values to an unambiguous form. 264 | * The escape char to be inserted is '~'. The chars to be escaped 265 | * are ~, which maps to ~0, and /, which maps to ~1. Backslashes 266 | * and double quote chars are also escaped. 267 | * @param token the JSONPointer segment value to be escaped 268 | * @return the escaped value for the token 269 | */ 270 | private String escape(String token) { 271 | return token.replace("~", "~0") 272 | .replace("/", "~1") 273 | .replace("\\", "\\\\") 274 | .replace("\"", "\\\""); 275 | } 276 | 277 | /** 278 | * Returns a string representing the JSONPointer path value using URI 279 | * fragment identifier representation 280 | */ 281 | public String toURIFragment() { 282 | try { 283 | StringBuilder rval = new StringBuilder("#"); 284 | for (String token : this.refTokens) { 285 | rval.append('/').append(URLEncoder.encode(token, ENCODING)); 286 | } 287 | return rval.toString(); 288 | } catch (UnsupportedEncodingException e) { 289 | throw new RuntimeException(e); 290 | } 291 | } 292 | 293 | } 294 | -------------------------------------------------------------------------------- /src/org/json/XMLTokener.java: -------------------------------------------------------------------------------- 1 | package org.json; 2 | 3 | /* 4 | Copyright (c) 2002 JSON.org 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | The Software shall be used for Good, not Evil. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | import java.io.Reader; 28 | 29 | /** 30 | * The XMLTokener extends the JSONTokener to provide additional methods 31 | * for the parsing of XML texts. 32 | * @author JSON.org 33 | * @version 2015-12-09 34 | */ 35 | public class XMLTokener extends JSONTokener { 36 | 37 | 38 | /** The table of entity values. It initially contains Character values for 39 | * amp, apos, gt, lt, quot. 40 | */ 41 | public static final java.util.HashMap entity; 42 | 43 | static { 44 | entity = new java.util.HashMap(8); 45 | entity.put("amp", XML.AMP); 46 | entity.put("apos", XML.APOS); 47 | entity.put("gt", XML.GT); 48 | entity.put("lt", XML.LT); 49 | entity.put("quot", XML.QUOT); 50 | } 51 | 52 | /** 53 | * Construct an XMLTokener from a Reader. 54 | * @param r A source reader. 55 | */ 56 | public XMLTokener(Reader r) { 57 | super(r); 58 | } 59 | 60 | /** 61 | * Construct an XMLTokener from a string. 62 | * @param s A source string. 63 | */ 64 | public XMLTokener(String s) { 65 | super(s); 66 | } 67 | 68 | /** 69 | * Get the text in the CDATA block. 70 | * @return The string up to the ]]>. 71 | * @throws JSONException If the ]]> is not found. 72 | */ 73 | public String nextCDATA() throws JSONException { 74 | char c; 75 | int i; 76 | StringBuilder sb = new StringBuilder(); 77 | while (more()) { 78 | c = next(); 79 | sb.append(c); 80 | i = sb.length() - 3; 81 | if (i >= 0 && sb.charAt(i) == ']' && 82 | sb.charAt(i + 1) == ']' && sb.charAt(i + 2) == '>') { 83 | sb.setLength(i); 84 | return sb.toString(); 85 | } 86 | } 87 | throw syntaxError("Unclosed CDATA"); 88 | } 89 | 90 | 91 | /** 92 | * Get the next XML outer token, trimming whitespace. There are two kinds 93 | * of tokens: the '<' character which begins a markup tag, and the content 94 | * text between markup tags. 95 | * 96 | * @return A string, or a '<' Character, or null if there is no more 97 | * source text. 98 | * @throws JSONException 99 | */ 100 | public Object nextContent() throws JSONException { 101 | char c; 102 | StringBuilder sb; 103 | do { 104 | c = next(); 105 | } while (Character.isWhitespace(c)); 106 | if (c == 0) { 107 | return null; 108 | } 109 | if (c == '<') { 110 | return XML.LT; 111 | } 112 | sb = new StringBuilder(); 113 | for (;;) { 114 | if (c == 0) { 115 | return sb.toString().trim(); 116 | } 117 | if (c == '<') { 118 | back(); 119 | return sb.toString().trim(); 120 | } 121 | if (c == '&') { 122 | sb.append(nextEntity(c)); 123 | } else { 124 | sb.append(c); 125 | } 126 | c = next(); 127 | } 128 | } 129 | 130 | 131 | /** 132 | * Return the next entity. These entities are translated to Characters: 133 | * & ' > < ". 134 | * @param ampersand An ampersand character. 135 | * @return A Character or an entity String if the entity is not recognized. 136 | * @throws JSONException If missing ';' in XML entity. 137 | */ 138 | public Object nextEntity(@SuppressWarnings("unused") char ampersand) throws JSONException { 139 | StringBuilder sb = new StringBuilder(); 140 | for (;;) { 141 | char c = next(); 142 | if (Character.isLetterOrDigit(c) || c == '#') { 143 | sb.append(Character.toLowerCase(c)); 144 | } else if (c == ';') { 145 | break; 146 | } else { 147 | throw syntaxError("Missing ';' in XML entity: &" + sb); 148 | } 149 | } 150 | String string = sb.toString(); 151 | return unescapeEntity(string); 152 | } 153 | 154 | /** 155 | * Unescapes an XML entity encoding; 156 | * @param e entity (only the actual entity value, not the preceding & or ending ; 157 | * @return 158 | */ 159 | static String unescapeEntity(String e) { 160 | // validate 161 | if (e == null || e.isEmpty()) { 162 | return ""; 163 | } 164 | // if our entity is an encoded unicode point, parse it. 165 | if (e.charAt(0) == '#') { 166 | int cp; 167 | if (e.charAt(1) == 'x') { 168 | // hex encoded unicode 169 | cp = Integer.parseInt(e.substring(2), 16); 170 | } else { 171 | // decimal encoded unicode 172 | cp = Integer.parseInt(e.substring(1)); 173 | } 174 | return new String(new int[] {cp},0,1); 175 | } 176 | Character knownEntity = entity.get(e); 177 | if(knownEntity==null) { 178 | // we don't know the entity so keep it encoded 179 | return '&' + e + ';'; 180 | } 181 | return knownEntity.toString(); 182 | } 183 | 184 | 185 | /** 186 | * Returns the next XML meta token. This is used for skipping over 187 | * and structures. 188 | * @return Syntax characters (< > / = ! ?) are returned as 189 | * Character, and strings and names are returned as Boolean. We don't care 190 | * what the values actually are. 191 | * @throws JSONException If a string is not properly closed or if the XML 192 | * is badly structured. 193 | */ 194 | public Object nextMeta() throws JSONException { 195 | char c; 196 | char q; 197 | do { 198 | c = next(); 199 | } while (Character.isWhitespace(c)); 200 | switch (c) { 201 | case 0: 202 | throw syntaxError("Misshaped meta tag"); 203 | case '<': 204 | return XML.LT; 205 | case '>': 206 | return XML.GT; 207 | case '/': 208 | return XML.SLASH; 209 | case '=': 210 | return XML.EQ; 211 | case '!': 212 | return XML.BANG; 213 | case '?': 214 | return XML.QUEST; 215 | case '"': 216 | case '\'': 217 | q = c; 218 | for (;;) { 219 | c = next(); 220 | if (c == 0) { 221 | throw syntaxError("Unterminated string"); 222 | } 223 | if (c == q) { 224 | return Boolean.TRUE; 225 | } 226 | } 227 | default: 228 | for (;;) { 229 | c = next(); 230 | if (Character.isWhitespace(c)) { 231 | return Boolean.TRUE; 232 | } 233 | switch (c) { 234 | case 0: 235 | case '<': 236 | case '>': 237 | case '/': 238 | case '=': 239 | case '!': 240 | case '?': 241 | case '"': 242 | case '\'': 243 | back(); 244 | return Boolean.TRUE; 245 | } 246 | } 247 | } 248 | } 249 | 250 | 251 | /** 252 | * Get the next XML Token. These tokens are found inside of angle 253 | * brackets. It may be one of these characters: / > = ! ? or it 254 | * may be a string wrapped in single quotes or double quotes, or it may be a 255 | * name. 256 | * @return a String or a Character. 257 | * @throws JSONException If the XML is not well formed. 258 | */ 259 | public Object nextToken() throws JSONException { 260 | char c; 261 | char q; 262 | StringBuilder sb; 263 | do { 264 | c = next(); 265 | } while (Character.isWhitespace(c)); 266 | switch (c) { 267 | case 0: 268 | throw syntaxError("Misshaped element"); 269 | case '<': 270 | throw syntaxError("Misplaced '<'"); 271 | case '>': 272 | return XML.GT; 273 | case '/': 274 | return XML.SLASH; 275 | case '=': 276 | return XML.EQ; 277 | case '!': 278 | return XML.BANG; 279 | case '?': 280 | return XML.QUEST; 281 | 282 | // Quoted string 283 | 284 | case '"': 285 | case '\'': 286 | q = c; 287 | sb = new StringBuilder(); 288 | for (;;) { 289 | c = next(); 290 | if (c == 0) { 291 | throw syntaxError("Unterminated string"); 292 | } 293 | if (c == q) { 294 | return sb.toString(); 295 | } 296 | if (c == '&') { 297 | sb.append(nextEntity(c)); 298 | } else { 299 | sb.append(c); 300 | } 301 | } 302 | default: 303 | 304 | // Name 305 | 306 | sb = new StringBuilder(); 307 | for (;;) { 308 | sb.append(c); 309 | c = next(); 310 | if (Character.isWhitespace(c)) { 311 | return sb.toString(); 312 | } 313 | switch (c) { 314 | case 0: 315 | return sb.toString(); 316 | case '>': 317 | case '/': 318 | case '=': 319 | case '!': 320 | case '?': 321 | case '[': 322 | case ']': 323 | back(); 324 | return sb.toString(); 325 | case '<': 326 | case '"': 327 | case '\'': 328 | throw syntaxError("Bad character in a name"); 329 | } 330 | } 331 | } 332 | } 333 | 334 | 335 | /** 336 | * Skip characters until past the requested string. 337 | * If it is not found, we are left at the end of the source with a result of false. 338 | * @param to A string to skip past. 339 | */ 340 | // The Android implementation of JSONTokener has a public method of public void skipPast(String to) 341 | // even though ours does not have that method, to have API compatibility, our method in the subclass 342 | // should match. 343 | public void skipPast(String to) { 344 | boolean b; 345 | char c; 346 | int i; 347 | int j; 348 | int offset = 0; 349 | int length = to.length(); 350 | char[] circle = new char[length]; 351 | 352 | /* 353 | * First fill the circle buffer with as many characters as are in the 354 | * to string. If we reach an early end, bail. 355 | */ 356 | 357 | for (i = 0; i < length; i += 1) { 358 | c = next(); 359 | if (c == 0) { 360 | return; 361 | } 362 | circle[i] = c; 363 | } 364 | 365 | /* We will loop, possibly for all of the remaining characters. */ 366 | 367 | for (;;) { 368 | j = offset; 369 | b = true; 370 | 371 | /* Compare the circle buffer with the to string. */ 372 | 373 | for (i = 0; i < length; i += 1) { 374 | if (circle[j] != to.charAt(i)) { 375 | b = false; 376 | break; 377 | } 378 | j += 1; 379 | if (j >= length) { 380 | j -= length; 381 | } 382 | } 383 | 384 | /* If we exit the loop with b intact, then victory is ours. */ 385 | 386 | if (b) { 387 | return; 388 | } 389 | 390 | /* Get the next character. If there isn't one, then defeat is ours. */ 391 | 392 | c = next(); 393 | if (c == 0) { 394 | return; 395 | } 396 | /* 397 | * Shove the character in the circle buffer and advance the 398 | * circle offset. The offset is mod n. 399 | */ 400 | circle[offset] = c; 401 | offset += 1; 402 | if (offset >= length) { 403 | offset -= length; 404 | } 405 | } 406 | } 407 | } 408 | -------------------------------------------------------------------------------- /src/connect2/Web.java: -------------------------------------------------------------------------------- 1 | package connect2; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.logging.ConsoleHandler; 9 | import java.util.logging.Logger; 10 | import java.util.logging.Level; 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | import java.io.OutputStream; 14 | import java.net.InetSocketAddress; 15 | import java.net.URLDecoder; 16 | import com.sun.net.httpserver.HttpServer; 17 | import com.sun.net.httpserver.HttpExchange; 18 | import com.sun.net.httpserver.HttpHandler; 19 | import org.json.JSONObject; 20 | import org.json.JSONArray; 21 | 22 | public class Web { 23 | private LoginResult loginResult = null; 24 | private ECHost host = null; 25 | private ECList list = null; 26 | 27 | public Web() throws IOException { 28 | if(Main.Debug) { 29 | Logger logger = Logger.getLogger("com.sun.net.httpserver"); 30 | ConsoleHandler ch = new ConsoleHandler(); 31 | logger.setLevel(Level.FINER); 32 | ch.setLevel(Level.FINER); 33 | logger.addHandler(ch); 34 | } 35 | HttpServer server = HttpServer.create(new InetSocketAddress(8333), 0); 36 | server.createContext("/", new FileHandler()); 37 | server.createContext("/login", new LoginHandler(this)); 38 | server.createContext("/signup", new SignupHandler()); 39 | server.createContext("/games", new GamesHandler(this)); 40 | server.createContext("/motd", new MotdHandler()); 41 | server.createContext("/version", new VersionHandler()); 42 | server.createContext("/gameinfo", new GameInfoHandler()); 43 | server.createContext("/show", new ShowHandler(this)); 44 | server.createContext("/validate", new ValidateHandler(this)); 45 | server.setExecutor(null); 46 | server.start(); 47 | System.out.println("[web] started on :8333"); 48 | } 49 | 50 | static Map getPostForm(HttpExchange httpExchange) throws IOException { 51 | Map parameters = new HashMap(); 52 | String request = Utils.streamToString(httpExchange.getRequestBody()); 53 | String[] keyValuePairs = request.split("&"); 54 | for (String keyValuePair : keyValuePairs) { 55 | String[] keyValue = keyValuePair.split("="); 56 | if (keyValue.length != 2) { 57 | continue; 58 | } 59 | parameters.put(keyValue[0], URLDecoder.decode(keyValue[1], "UTF-8")); 60 | } 61 | return parameters; 62 | } 63 | 64 | static class LoginResult { 65 | int war3Version; 66 | String name; 67 | String sessionKey; 68 | 69 | LoginResult(int war3Version, String name, String sessionKey) { 70 | this.war3Version = war3Version; 71 | this.name = name; 72 | this.sessionKey = sessionKey; 73 | } 74 | } 75 | 76 | static class WebGame implements Comparable { 77 | int uid; 78 | String gamename; 79 | String ip; 80 | int slotsTaken; 81 | int slotsTotal; 82 | String map; 83 | String location; 84 | AppGame appGame; 85 | 86 | public JSONObject encode() { 87 | JSONObject obj = new JSONObject(); 88 | obj.put("uid", this.uid); 89 | obj.put("gamename", this.gamename); 90 | obj.put("ip", this.ip); 91 | obj.put("slots_taken", this.slotsTaken); 92 | obj.put("slots_total", this.slotsTotal); 93 | obj.put("map", this.map); 94 | obj.put("location", this.location); 95 | if(this.appGame == null) { 96 | obj.put("app_game", ""); 97 | } else { 98 | obj.put("app_game", this.appGame.encode()); 99 | } 100 | return obj; 101 | } 102 | 103 | public String sortKey() { 104 | // no app game => put at the end 105 | if(this.appGame == null) { 106 | return "z-" + this.location + "-" + this.gamename; 107 | } 108 | 109 | // autohost games come after others 110 | if(this.appGame.botID < 100) { 111 | return "y-" + this.gamename; 112 | } 113 | 114 | // order the rest by botid 115 | return "a-" + this.appGame.botID + "-" + this.gamename; 116 | } 117 | 118 | public int compareTo(WebGame other) { 119 | return this.sortKey().compareTo(other.sortKey()); 120 | } 121 | } 122 | 123 | static class AppGame { 124 | int id; 125 | int botID; 126 | String location; 127 | String gamename; 128 | String map; 129 | String host; 130 | int slotsTaken; 131 | int slotsTotal; 132 | String uptime; 133 | 134 | public JSONObject encode() { 135 | JSONObject obj = new JSONObject(); 136 | obj.put("id", this.id); 137 | obj.put("bot_id", this.botID); 138 | obj.put("location", this.location); 139 | obj.put("name", this.gamename); 140 | obj.put("map", this.map); 141 | obj.put("host", this.host); 142 | obj.put("slots_taken", this.slotsTaken); 143 | obj.put("slots_total", this.slotsTotal); 144 | obj.put("uptime", this.uptime); 145 | return obj; 146 | } 147 | 148 | public static AppGame decode(JSONObject obj) { 149 | AppGame g = new AppGame(); 150 | g.id = obj.getInt("id"); 151 | g.botID = obj.getInt("bot_id"); 152 | g.location = obj.getString("location"); 153 | g.gamename = obj.getString("name"); 154 | g.map = obj.getString("map"); 155 | g.host = obj.getString("host"); 156 | g.slotsTaken = obj.getInt("slots_taken"); 157 | g.slotsTotal = obj.getInt("slots_total"); 158 | g.uptime = obj.getString("uptime"); 159 | return g; 160 | } 161 | } 162 | 163 | static class WebGamelist { 164 | List pub; 165 | List autohost; 166 | List others; 167 | List unmoderated; 168 | 169 | public WebGamelist() { 170 | this.pub = new ArrayList(); 171 | this.autohost = new ArrayList(); 172 | this.others = new ArrayList(); 173 | this.unmoderated = new ArrayList(); 174 | } 175 | 176 | static JSONArray encodeList(List l) { 177 | JSONArray a = new JSONArray(); 178 | for(WebGame g : l) { 179 | a.put(g.encode()); 180 | } 181 | return a; 182 | } 183 | 184 | public JSONObject encode() { 185 | JSONObject obj = new JSONObject(); 186 | obj.put("publicGames", WebGamelist.encodeList(this.pub)); 187 | obj.put("autohostGames", WebGamelist.encodeList(this.autohost)); 188 | obj.put("otherGames", WebGamelist.encodeList(this.others)); 189 | obj.put("unmoderatedGames", WebGamelist.encodeList(this.unmoderated)); 190 | return obj; 191 | } 192 | } 193 | 194 | static class FileHandler implements HttpHandler { 195 | public void handle(HttpExchange t) throws IOException { 196 | String path = t.getRequestURI().getPath(); 197 | if(path.equals("/")) { 198 | path = "/index.html"; 199 | } 200 | InputStream in = getClass().getResourceAsStream("/static" + path); 201 | if(in == null) { 202 | t.sendResponseHeaders(404, 0); 203 | t.close(); 204 | return; 205 | } 206 | byte[] bytes = Utils.streamToByteArray(in); 207 | t.sendResponseHeaders(200, bytes.length); 208 | OutputStream os = t.getResponseBody(); 209 | os.write(bytes); 210 | os.close(); 211 | } 212 | } 213 | 214 | static class LoginHandler implements HttpHandler { 215 | private Web web; 216 | 217 | public LoginHandler(Web web) { 218 | this.web = web; 219 | } 220 | 221 | public void handle(HttpExchange t) throws IOException { 222 | Map postForm = Web.getPostForm(t); 223 | postForm.put("version", "2"); 224 | String response; 225 | try { 226 | response = Utils.postForm("https://connect.entgaming.net/gwc-login", postForm); 227 | } catch(Exception e) { 228 | Utils.respondError(t, "Error logging in: " + e.getMessage() + "."); 229 | return; 230 | } 231 | JSONObject obj = new JSONObject(response); 232 | if(obj.has("error") && obj.getString("error").length() > 0) { 233 | Utils.respondError(t, "Error logging in: " + obj.getString("error") + "."); 234 | return; 235 | } else if(obj.getLong("war3version") == 0) { 236 | Utils.respondError(t, "Error logging in: failed to login."); 237 | return; 238 | } 239 | synchronized(web) { 240 | web.loginResult = new LoginResult(obj.getInt("war3version"), obj.getString("name"), obj.getString("session_key2")); 241 | if(web.host == null) { 242 | web.host = new ECHost(web.loginResult.war3Version, web.loginResult.name, web.loginResult.sessionKey); 243 | web.host.init(); 244 | } else { 245 | web.host.update(web.loginResult.war3Version, web.loginResult.name, web.loginResult.sessionKey); 246 | } 247 | System.out.println("[web] logged in successfully as " + web.loginResult.name); 248 | web.list = new ECList(web.host); 249 | } 250 | Utils.respondJSON(t, obj); 251 | } 252 | } 253 | 254 | static class SignupHandler implements HttpHandler { 255 | public void handle(HttpExchange t) throws IOException { 256 | byte[] request = Utils.streamToByteArray(t.getRequestBody()); 257 | String response; 258 | try { 259 | response = Utils.post("https://connect.entgaming.net/gwc-signup", request); 260 | } catch(Exception e) { 261 | Utils.respondError(t, "Error signing up: " + e.getMessage() + "."); 262 | return; 263 | } 264 | JSONObject obj = new JSONObject(response); 265 | if(obj.has("error") && obj.getString("error").length() > 0) { 266 | Utils.respondError(t, "Error signing up: " + obj.getString("error") + "."); 267 | return; 268 | } 269 | System.out.println("[web] signed up a new account"); 270 | JSONObject resp = new JSONObject(); 271 | resp.put("success", true); 272 | Utils.respondJSON(t, resp); 273 | } 274 | } 275 | 276 | static class GamesHandler implements HttpHandler { 277 | private Web web; 278 | 279 | public GamesHandler(Web web) { 280 | this.web = web; 281 | } 282 | 283 | static Map getAppGames() throws IOException { 284 | JSONArray arr = new JSONArray(Utils.get("https://host.entgaming.net/allgames")); 285 | Map games = new HashMap(); 286 | for(int i = 0; i < arr.length(); i++) { 287 | AppGame game = AppGame.decode(arr.getJSONObject(i)); 288 | games.put(game.gamename, game); 289 | } 290 | return games; 291 | } 292 | 293 | public void handle(HttpExchange t) throws IOException { 294 | ECHost host; 295 | synchronized(web) { 296 | if(web.host == null) { 297 | Utils.respondJSON(t, new WebGamelist().encode()); 298 | return; 299 | } 300 | host = web.host; 301 | } 302 | Map appGames = GamesHandler.getAppGames(); 303 | WebGamelist games = new WebGamelist(); 304 | for(GameInfo game : host.getGames()) { 305 | WebGame webGame = new WebGame(); 306 | webGame.uid = game.uid; 307 | webGame.gamename = game.gamename; 308 | webGame.ip = game.remoteAddress.getHostAddress(); 309 | webGame.map = game.map; 310 | 311 | if(appGames.containsKey(webGame.gamename)) { 312 | webGame.appGame = appGames.get(webGame.gamename); 313 | } 314 | 315 | if(webGame.appGame != null) { 316 | if(webGame.appGame.botID >= 100) { 317 | games.pub.add(webGame); 318 | } else { 319 | games.autohost.add(webGame); 320 | } 321 | } else if(Main.GetThirdPartyBot(webGame.ip) != null) { 322 | webGame.location = Main.GetThirdPartyBot(webGame.ip); 323 | if(webGame.location.contains("MMH")) { 324 | games.pub.add(webGame); 325 | } else { 326 | games.others.add(webGame); 327 | } 328 | } else if(game.extra.length() > 0) { 329 | webGame.location = game.extra; 330 | games.unmoderated.add(webGame); 331 | } else { 332 | System.out.println("[web] ignoring game with ip=" + webGame.ip); 333 | } 334 | } 335 | 336 | Collections.sort(games.pub); 337 | Collections.sort(games.autohost); 338 | Collections.sort(games.others); 339 | Collections.sort(games.unmoderated); 340 | Utils.respondJSON(t, games.encode()); 341 | } 342 | } 343 | 344 | static class MotdHandler implements HttpHandler { 345 | public void handle(HttpExchange t) throws IOException { 346 | String motd = Utils.get("https://entgaming.net/entconnect/wc3connect_java_motd.php"); 347 | motd = motd.trim(); 348 | JSONObject obj = new JSONObject(); 349 | obj.put("motd", motd); 350 | Utils.respondJSON(t, obj); 351 | } 352 | } 353 | 354 | static class VersionHandler implements HttpHandler { 355 | public void handle(HttpExchange t) throws IOException { 356 | String versionStr = Utils.get("https://entgaming.net/entconnect/wc3connect_java_version.php"); 357 | Integer version = Integer.parseInt(versionStr.trim()); 358 | JSONObject obj = new JSONObject(); 359 | obj.put("up_to_date", version <= Main.Version); 360 | Utils.respondJSON(t, obj); 361 | } 362 | } 363 | 364 | static class GameInfoHandler implements HttpHandler { 365 | public void handle(HttpExchange t) throws IOException { 366 | Map postForm = Web.getPostForm(t); 367 | String gameID = postForm.get("gameid"); 368 | Utils.respondString(t, Utils.get("https://entgaming.net/forum/slots_fast.php?id=" + gameID + "&time=" + (System.currentTimeMillis()/1000))); 369 | } 370 | } 371 | 372 | static class ShowHandler implements HttpHandler { 373 | private Web web; 374 | 375 | public ShowHandler(Web web) { 376 | this.web = web; 377 | } 378 | 379 | public void handle(HttpExchange t) throws IOException { 380 | Map postForm = Web.getPostForm(t); 381 | long uid = Long.parseLong(postForm.get("uid")); 382 | ECHost host; 383 | ECList list; 384 | synchronized(web) { 385 | if(web.host == null) { 386 | Utils.respondError(t, "internal server error"); 387 | return; 388 | } 389 | host = web.host; 390 | list = web.list; 391 | } 392 | GameInfo gameInfo = host.searchGame((int) uid); 393 | if(gameInfo != null) { 394 | System.out.println("[web] set list filter to " + gameInfo.gamename); 395 | list.setFilter(gameInfo.gamename); 396 | } else { 397 | System.out.println("[web] no game found, did we click during an update?"); 398 | } 399 | JSONObject resp = new JSONObject(); 400 | resp.put("success", true); 401 | Utils.respondJSON(t, resp); 402 | } 403 | } 404 | 405 | static class ValidateHandler implements HttpHandler { 406 | private Web web; 407 | 408 | public ValidateHandler(Web web) { 409 | this.web = web; 410 | } 411 | 412 | public void handle(HttpExchange t) throws IOException { 413 | Map postForm = Web.getPostForm(t); 414 | String key = postForm.get("key"); 415 | if(key == null) { 416 | key = ""; 417 | } 418 | LoginResult loginResult; 419 | synchronized(web) { 420 | loginResult = web.loginResult; 421 | } 422 | Map m = new HashMap(); 423 | m.put("username", loginResult.name); 424 | m.put("sessionkey", loginResult.sessionKey); 425 | m.put("key", key); 426 | JSONObject obj = new JSONObject(Utils.postForm("https://connect.entgaming.net/gwc-validate", m)); 427 | if(obj.has("error")) { 428 | Utils.respondError(t, "Error validating account: " + obj.getString("error") + "."); 429 | return; 430 | } 431 | JSONObject resp = new JSONObject(); 432 | resp.put("success", true); 433 | Utils.respondJSON(t, resp); 434 | } 435 | } 436 | } 437 | -------------------------------------------------------------------------------- /src/org/json/JSONWriter.java: -------------------------------------------------------------------------------- 1 | package org.json; 2 | 3 | import java.io.IOException; 4 | import java.util.Collection; 5 | import java.util.Map; 6 | 7 | /* 8 | Copyright (c) 2006 JSON.org 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | The Software shall be used for Good, not Evil. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | SOFTWARE. 29 | */ 30 | 31 | /** 32 | * JSONWriter provides a quick and convenient way of producing JSON text. 33 | * The texts produced strictly conform to JSON syntax rules. No whitespace is 34 | * added, so the results are ready for transmission or storage. Each instance of 35 | * JSONWriter can produce one JSON text. 36 | *

37 | * A JSONWriter instance provides a value method for appending 38 | * values to the 39 | * text, and a key 40 | * method for adding keys before values in objects. There are array 41 | * and endArray methods that make and bound array values, and 42 | * object and endObject methods which make and bound 43 | * object values. All of these methods return the JSONWriter instance, 44 | * permitting a cascade style. For example,

 45 |  * new JSONWriter(myWriter)
 46 |  *     .object()
 47 |  *         .key("JSON")
 48 |  *         .value("Hello, World!")
 49 |  *     .endObject();
which writes
 50 |  * {"JSON":"Hello, World!"}
51 | *

52 | * The first method called must be array or object. 53 | * There are no methods for adding commas or colons. JSONWriter adds them for 54 | * you. Objects and arrays can be nested up to 200 levels deep. 55 | *

56 | * This can sometimes be easier than using a JSONObject to build a string. 57 | * @author JSON.org 58 | * @version 2016-08-08 59 | */ 60 | public class JSONWriter { 61 | private static final int maxdepth = 200; 62 | 63 | /** 64 | * The comma flag determines if a comma should be output before the next 65 | * value. 66 | */ 67 | private boolean comma; 68 | 69 | /** 70 | * The current mode. Values: 71 | * 'a' (array), 72 | * 'd' (done), 73 | * 'i' (initial), 74 | * 'k' (key), 75 | * 'o' (object). 76 | */ 77 | protected char mode; 78 | 79 | /** 80 | * The object/array stack. 81 | */ 82 | private final JSONObject stack[]; 83 | 84 | /** 85 | * The stack top index. A value of 0 indicates that the stack is empty. 86 | */ 87 | private int top; 88 | 89 | /** 90 | * The writer that will receive the output. 91 | */ 92 | protected Appendable writer; 93 | 94 | /** 95 | * Make a fresh JSONWriter. It can be used to build one JSON text. 96 | */ 97 | public JSONWriter(Appendable w) { 98 | this.comma = false; 99 | this.mode = 'i'; 100 | this.stack = new JSONObject[maxdepth]; 101 | this.top = 0; 102 | this.writer = w; 103 | } 104 | 105 | /** 106 | * Append a value. 107 | * @param string A string value. 108 | * @return this 109 | * @throws JSONException If the value is out of sequence. 110 | */ 111 | private JSONWriter append(String string) throws JSONException { 112 | if (string == null) { 113 | throw new JSONException("Null pointer"); 114 | } 115 | if (this.mode == 'o' || this.mode == 'a') { 116 | try { 117 | if (this.comma && this.mode == 'a') { 118 | this.writer.append(','); 119 | } 120 | this.writer.append(string); 121 | } catch (IOException e) { 122 | // Android as of API 25 does not support this exception constructor 123 | // however we won't worry about it. If an exception is happening here 124 | // it will just throw a "Method not found" exception instead. 125 | throw new JSONException(e); 126 | } 127 | if (this.mode == 'o') { 128 | this.mode = 'k'; 129 | } 130 | this.comma = true; 131 | return this; 132 | } 133 | throw new JSONException("Value out of sequence."); 134 | } 135 | 136 | /** 137 | * Begin appending a new array. All values until the balancing 138 | * endArray will be appended to this array. The 139 | * endArray method must be called to mark the array's end. 140 | * @return this 141 | * @throws JSONException If the nesting is too deep, or if the object is 142 | * started in the wrong place (for example as a key or after the end of the 143 | * outermost array or object). 144 | */ 145 | public JSONWriter array() throws JSONException { 146 | if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') { 147 | this.push(null); 148 | this.append("["); 149 | this.comma = false; 150 | return this; 151 | } 152 | throw new JSONException("Misplaced array."); 153 | } 154 | 155 | /** 156 | * End something. 157 | * @param m Mode 158 | * @param c Closing character 159 | * @return this 160 | * @throws JSONException If unbalanced. 161 | */ 162 | private JSONWriter end(char m, char c) throws JSONException { 163 | if (this.mode != m) { 164 | throw new JSONException(m == 'a' 165 | ? "Misplaced endArray." 166 | : "Misplaced endObject."); 167 | } 168 | this.pop(m); 169 | try { 170 | this.writer.append(c); 171 | } catch (IOException e) { 172 | // Android as of API 25 does not support this exception constructor 173 | // however we won't worry about it. If an exception is happening here 174 | // it will just throw a "Method not found" exception instead. 175 | throw new JSONException(e); 176 | } 177 | this.comma = true; 178 | return this; 179 | } 180 | 181 | /** 182 | * End an array. This method most be called to balance calls to 183 | * array. 184 | * @return this 185 | * @throws JSONException If incorrectly nested. 186 | */ 187 | public JSONWriter endArray() throws JSONException { 188 | return this.end('a', ']'); 189 | } 190 | 191 | /** 192 | * End an object. This method most be called to balance calls to 193 | * object. 194 | * @return this 195 | * @throws JSONException If incorrectly nested. 196 | */ 197 | public JSONWriter endObject() throws JSONException { 198 | return this.end('k', '}'); 199 | } 200 | 201 | /** 202 | * Append a key. The key will be associated with the next value. In an 203 | * object, every value must be preceded by a key. 204 | * @param string A key string. 205 | * @return this 206 | * @throws JSONException If the key is out of place. For example, keys 207 | * do not belong in arrays or if the key is null. 208 | */ 209 | public JSONWriter key(String string) throws JSONException { 210 | if (string == null) { 211 | throw new JSONException("Null key."); 212 | } 213 | if (this.mode == 'k') { 214 | try { 215 | JSONObject topObject = this.stack[this.top - 1]; 216 | // don't use the built in putOnce method to maintain Android support 217 | if(topObject.has(string)) { 218 | throw new JSONException("Duplicate key \"" + string + "\""); 219 | } 220 | topObject.put(string, true); 221 | if (this.comma) { 222 | this.writer.append(','); 223 | } 224 | this.writer.append(JSONObject.quote(string)); 225 | this.writer.append(':'); 226 | this.comma = false; 227 | this.mode = 'o'; 228 | return this; 229 | } catch (IOException e) { 230 | // Android as of API 25 does not support this exception constructor 231 | // however we won't worry about it. If an exception is happening here 232 | // it will just throw a "Method not found" exception instead. 233 | throw new JSONException(e); 234 | } 235 | } 236 | throw new JSONException("Misplaced key."); 237 | } 238 | 239 | 240 | /** 241 | * Begin appending a new object. All keys and values until the balancing 242 | * endObject will be appended to this object. The 243 | * endObject method must be called to mark the object's end. 244 | * @return this 245 | * @throws JSONException If the nesting is too deep, or if the object is 246 | * started in the wrong place (for example as a key or after the end of the 247 | * outermost array or object). 248 | */ 249 | public JSONWriter object() throws JSONException { 250 | if (this.mode == 'i') { 251 | this.mode = 'o'; 252 | } 253 | if (this.mode == 'o' || this.mode == 'a') { 254 | this.append("{"); 255 | this.push(new JSONObject()); 256 | this.comma = false; 257 | return this; 258 | } 259 | throw new JSONException("Misplaced object."); 260 | 261 | } 262 | 263 | 264 | /** 265 | * Pop an array or object scope. 266 | * @param c The scope to close. 267 | * @throws JSONException If nesting is wrong. 268 | */ 269 | private void pop(char c) throws JSONException { 270 | if (this.top <= 0) { 271 | throw new JSONException("Nesting error."); 272 | } 273 | char m = this.stack[this.top - 1] == null ? 'a' : 'k'; 274 | if (m != c) { 275 | throw new JSONException("Nesting error."); 276 | } 277 | this.top -= 1; 278 | this.mode = this.top == 0 279 | ? 'd' 280 | : this.stack[this.top - 1] == null 281 | ? 'a' 282 | : 'k'; 283 | } 284 | 285 | /** 286 | * Push an array or object scope. 287 | * @param jo The scope to open. 288 | * @throws JSONException If nesting is too deep. 289 | */ 290 | private void push(JSONObject jo) throws JSONException { 291 | if (this.top >= maxdepth) { 292 | throw new JSONException("Nesting too deep."); 293 | } 294 | this.stack[this.top] = jo; 295 | this.mode = jo == null ? 'a' : 'k'; 296 | this.top += 1; 297 | } 298 | 299 | /** 300 | * Make a JSON text of an Object value. If the object has an 301 | * value.toJSONString() method, then that method will be used to produce the 302 | * JSON text. The method is required to produce a strictly conforming text. 303 | * If the object does not contain a toJSONString method (which is the most 304 | * common case), then a text will be produced by other means. If the value 305 | * is an array or Collection, then a JSONArray will be made from it and its 306 | * toJSONString method will be called. If the value is a MAP, then a 307 | * JSONObject will be made from it and its toJSONString method will be 308 | * called. Otherwise, the value's toString method will be called, and the 309 | * result will be quoted. 310 | * 311 | *

312 | * Warning: This method assumes that the data structure is acyclical. 313 | * 314 | * @param value 315 | * The value to be serialized. 316 | * @return a printable, displayable, transmittable representation of the 317 | * object, beginning with { (left 318 | * brace) and ending with } (right 319 | * brace). 320 | * @throws JSONException 321 | * If the value is or contains an invalid number. 322 | */ 323 | public static String valueToString(Object value) throws JSONException { 324 | if (value == null || value.equals(null)) { 325 | return "null"; 326 | } 327 | if (value instanceof JSONString) { 328 | String object; 329 | try { 330 | object = ((JSONString) value).toJSONString(); 331 | } catch (Exception e) { 332 | throw new JSONException(e); 333 | } 334 | if (object != null) { 335 | return object; 336 | } 337 | throw new JSONException("Bad value from toJSONString: " + object); 338 | } 339 | if (value instanceof Number) { 340 | // not all Numbers may match actual JSON Numbers. i.e. Fractions or Complex 341 | final String numberAsString = JSONObject.numberToString((Number) value); 342 | if(JSONObject.NUMBER_PATTERN.matcher(numberAsString).matches()) { 343 | // Close enough to a JSON number that we will return it unquoted 344 | return numberAsString; 345 | } 346 | // The Number value is not a valid JSON number. 347 | // Instead we will quote it as a string 348 | return JSONObject.quote(numberAsString); 349 | } 350 | if (value instanceof Boolean || value instanceof JSONObject 351 | || value instanceof JSONArray) { 352 | return value.toString(); 353 | } 354 | if (value instanceof Map) { 355 | Map map = (Map) value; 356 | return new JSONObject(map).toString(); 357 | } 358 | if (value instanceof Collection) { 359 | Collection coll = (Collection) value; 360 | return new JSONArray(coll).toString(); 361 | } 362 | if (value.getClass().isArray()) { 363 | return new JSONArray(value).toString(); 364 | } 365 | if(value instanceof Enum){ 366 | return JSONObject.quote(((Enum)value).name()); 367 | } 368 | return JSONObject.quote(value.toString()); 369 | } 370 | 371 | /** 372 | * Append either the value true or the value 373 | * false. 374 | * @param b A boolean. 375 | * @return this 376 | * @throws JSONException 377 | */ 378 | public JSONWriter value(boolean b) throws JSONException { 379 | return this.append(b ? "true" : "false"); 380 | } 381 | 382 | /** 383 | * Append a double value. 384 | * @param d A double. 385 | * @return this 386 | * @throws JSONException If the number is not finite. 387 | */ 388 | public JSONWriter value(double d) throws JSONException { 389 | return this.value(Double.valueOf(d)); 390 | } 391 | 392 | /** 393 | * Append a long value. 394 | * @param l A long. 395 | * @return this 396 | * @throws JSONException 397 | */ 398 | public JSONWriter value(long l) throws JSONException { 399 | return this.append(Long.toString(l)); 400 | } 401 | 402 | 403 | /** 404 | * Append an object value. 405 | * @param object The object to append. It can be null, or a Boolean, Number, 406 | * String, JSONObject, or JSONArray, or an object that implements JSONString. 407 | * @return this 408 | * @throws JSONException If the value is out of sequence. 409 | */ 410 | public JSONWriter value(Object object) throws JSONException { 411 | return this.append(valueToString(object)); 412 | } 413 | } 414 | -------------------------------------------------------------------------------- /src/org/json/JSONTokener.java: -------------------------------------------------------------------------------- 1 | package org.json; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | import java.io.Reader; 8 | import java.io.StringReader; 9 | 10 | /* 11 | Copyright (c) 2002 JSON.org 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy 14 | of this software and associated documentation files (the "Software"), to deal 15 | in the Software without restriction, including without limitation the rights 16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | copies of the Software, and to permit persons to whom the Software is 18 | furnished to do so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in all 21 | copies or substantial portions of the Software. 22 | 23 | The Software shall be used for Good, not Evil. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | SOFTWARE. 32 | */ 33 | 34 | /** 35 | * A JSONTokener takes a source string and extracts characters and tokens from 36 | * it. It is used by the JSONObject and JSONArray constructors to parse 37 | * JSON source strings. 38 | * @author JSON.org 39 | * @version 2014-05-03 40 | */ 41 | public class JSONTokener { 42 | /** current read character position on the current line. */ 43 | private long character; 44 | /** flag to indicate if the end of the input has been found. */ 45 | private boolean eof; 46 | /** current read index of the input. */ 47 | private long index; 48 | /** current line of the input. */ 49 | private long line; 50 | /** previous character read from the input. */ 51 | private char previous; 52 | /** Reader for the input. */ 53 | private final Reader reader; 54 | /** flag to indicate that a previous character was requested. */ 55 | private boolean usePrevious; 56 | /** the number of characters read in the previous line. */ 57 | private long characterPreviousLine; 58 | 59 | 60 | /** 61 | * Construct a JSONTokener from a Reader. The caller must close the Reader. 62 | * 63 | * @param reader A reader. 64 | */ 65 | public JSONTokener(Reader reader) { 66 | this.reader = reader.markSupported() 67 | ? reader 68 | : new BufferedReader(reader); 69 | this.eof = false; 70 | this.usePrevious = false; 71 | this.previous = 0; 72 | this.index = 0; 73 | this.character = 1; 74 | this.characterPreviousLine = 0; 75 | this.line = 1; 76 | } 77 | 78 | 79 | /** 80 | * Construct a JSONTokener from an InputStream. The caller must close the input stream. 81 | * @param inputStream The source. 82 | */ 83 | public JSONTokener(InputStream inputStream) { 84 | this(new InputStreamReader(inputStream)); 85 | } 86 | 87 | 88 | /** 89 | * Construct a JSONTokener from a string. 90 | * 91 | * @param s A source string. 92 | */ 93 | public JSONTokener(String s) { 94 | this(new StringReader(s)); 95 | } 96 | 97 | 98 | /** 99 | * Back up one character. This provides a sort of lookahead capability, 100 | * so that you can test for a digit or letter before attempting to parse 101 | * the next number or identifier. 102 | * @throws JSONException Thrown if trying to step back more than 1 step 103 | * or if already at the start of the string 104 | */ 105 | public void back() throws JSONException { 106 | if (this.usePrevious || this.index <= 0) { 107 | throw new JSONException("Stepping back two steps is not supported"); 108 | } 109 | this.decrementIndexes(); 110 | this.usePrevious = true; 111 | this.eof = false; 112 | } 113 | 114 | /** 115 | * Decrements the indexes for the {@link #back()} method based on the previous character read. 116 | */ 117 | private void decrementIndexes() { 118 | this.index--; 119 | if(this.previous=='\r' || this.previous == '\n') { 120 | this.line--; 121 | this.character=this.characterPreviousLine ; 122 | } else if(this.character > 0){ 123 | this.character--; 124 | } 125 | } 126 | 127 | /** 128 | * Get the hex value of a character (base16). 129 | * @param c A character between '0' and '9' or between 'A' and 'F' or 130 | * between 'a' and 'f'. 131 | * @return An int between 0 and 15, or -1 if c was not a hex digit. 132 | */ 133 | public static int dehexchar(char c) { 134 | if (c >= '0' && c <= '9') { 135 | return c - '0'; 136 | } 137 | if (c >= 'A' && c <= 'F') { 138 | return c - ('A' - 10); 139 | } 140 | if (c >= 'a' && c <= 'f') { 141 | return c - ('a' - 10); 142 | } 143 | return -1; 144 | } 145 | 146 | /** 147 | * Checks if the end of the input has been reached. 148 | * 149 | * @return true if at the end of the file and we didn't step back 150 | */ 151 | public boolean end() { 152 | return this.eof && !this.usePrevious; 153 | } 154 | 155 | 156 | /** 157 | * Determine if the source string still contains characters that next() 158 | * can consume. 159 | * @return true if not yet at the end of the source. 160 | * @throws JSONException thrown if there is an error stepping forward 161 | * or backward while checking for more data. 162 | */ 163 | public boolean more() throws JSONException { 164 | if(this.usePrevious) { 165 | return true; 166 | } 167 | try { 168 | this.reader.mark(1); 169 | } catch (IOException e) { 170 | throw new JSONException("Unable to preserve stream position", e); 171 | } 172 | try { 173 | // -1 is EOF, but next() can not consume the null character '\0' 174 | if(this.reader.read() <= 0) { 175 | this.eof = true; 176 | return false; 177 | } 178 | this.reader.reset(); 179 | } catch (IOException e) { 180 | throw new JSONException("Unable to read the next character from the stream", e); 181 | } 182 | return true; 183 | } 184 | 185 | 186 | /** 187 | * Get the next character in the source string. 188 | * 189 | * @return The next character, or 0 if past the end of the source string. 190 | * @throws JSONException Thrown if there is an error reading the source string. 191 | */ 192 | public char next() throws JSONException { 193 | int c; 194 | if (this.usePrevious) { 195 | this.usePrevious = false; 196 | c = this.previous; 197 | } else { 198 | try { 199 | c = this.reader.read(); 200 | } catch (IOException exception) { 201 | throw new JSONException(exception); 202 | } 203 | } 204 | if (c <= 0) { // End of stream 205 | this.eof = true; 206 | return 0; 207 | } 208 | this.incrementIndexes(c); 209 | this.previous = (char) c; 210 | return this.previous; 211 | } 212 | 213 | /** 214 | * Increments the internal indexes according to the previous character 215 | * read and the character passed as the current character. 216 | * @param c the current character read. 217 | */ 218 | private void incrementIndexes(int c) { 219 | if(c > 0) { 220 | this.index++; 221 | if(c=='\r') { 222 | this.line++; 223 | this.characterPreviousLine = this.character; 224 | this.character=0; 225 | }else if (c=='\n') { 226 | if(this.previous != '\r') { 227 | this.line++; 228 | this.characterPreviousLine = this.character; 229 | } 230 | this.character=0; 231 | } else { 232 | this.character++; 233 | } 234 | } 235 | } 236 | 237 | /** 238 | * Consume the next character, and check that it matches a specified 239 | * character. 240 | * @param c The character to match. 241 | * @return The character. 242 | * @throws JSONException if the character does not match. 243 | */ 244 | public char next(char c) throws JSONException { 245 | char n = this.next(); 246 | if (n != c) { 247 | if(n > 0) { 248 | throw this.syntaxError("Expected '" + c + "' and instead saw '" + 249 | n + "'"); 250 | } 251 | throw this.syntaxError("Expected '" + c + "' and instead saw ''"); 252 | } 253 | return n; 254 | } 255 | 256 | 257 | /** 258 | * Get the next n characters. 259 | * 260 | * @param n The number of characters to take. 261 | * @return A string of n characters. 262 | * @throws JSONException 263 | * Substring bounds error if there are not 264 | * n characters remaining in the source string. 265 | */ 266 | public String next(int n) throws JSONException { 267 | if (n == 0) { 268 | return ""; 269 | } 270 | 271 | char[] chars = new char[n]; 272 | int pos = 0; 273 | 274 | while (pos < n) { 275 | chars[pos] = this.next(); 276 | if (this.end()) { 277 | throw this.syntaxError("Substring bounds error"); 278 | } 279 | pos += 1; 280 | } 281 | return new String(chars); 282 | } 283 | 284 | 285 | /** 286 | * Get the next char in the string, skipping whitespace. 287 | * @throws JSONException Thrown if there is an error reading the source string. 288 | * @return A character, or 0 if there are no more characters. 289 | */ 290 | public char nextClean() throws JSONException { 291 | for (;;) { 292 | char c = this.next(); 293 | if (c == 0 || c > ' ') { 294 | return c; 295 | } 296 | } 297 | } 298 | 299 | 300 | /** 301 | * Return the characters up to the next close quote character. 302 | * Backslash processing is done. The formal JSON format does not 303 | * allow strings in single quotes, but an implementation is allowed to 304 | * accept them. 305 | * @param quote The quoting character, either 306 | * " (double quote) or 307 | * ' (single quote). 308 | * @return A String. 309 | * @throws JSONException Unterminated string. 310 | */ 311 | public String nextString(char quote) throws JSONException { 312 | char c; 313 | StringBuilder sb = new StringBuilder(); 314 | for (;;) { 315 | c = this.next(); 316 | switch (c) { 317 | case 0: 318 | case '\n': 319 | case '\r': 320 | throw this.syntaxError("Unterminated string"); 321 | case '\\': 322 | c = this.next(); 323 | switch (c) { 324 | case 'b': 325 | sb.append('\b'); 326 | break; 327 | case 't': 328 | sb.append('\t'); 329 | break; 330 | case 'n': 331 | sb.append('\n'); 332 | break; 333 | case 'f': 334 | sb.append('\f'); 335 | break; 336 | case 'r': 337 | sb.append('\r'); 338 | break; 339 | case 'u': 340 | try { 341 | sb.append((char)Integer.parseInt(this.next(4), 16)); 342 | } catch (NumberFormatException e) { 343 | throw this.syntaxError("Illegal escape.", e); 344 | } 345 | break; 346 | case '"': 347 | case '\'': 348 | case '\\': 349 | case '/': 350 | sb.append(c); 351 | break; 352 | default: 353 | throw this.syntaxError("Illegal escape."); 354 | } 355 | break; 356 | default: 357 | if (c == quote) { 358 | return sb.toString(); 359 | } 360 | sb.append(c); 361 | } 362 | } 363 | } 364 | 365 | 366 | /** 367 | * Get the text up but not including the specified character or the 368 | * end of line, whichever comes first. 369 | * @param delimiter A delimiter character. 370 | * @return A string. 371 | * @throws JSONException Thrown if there is an error while searching 372 | * for the delimiter 373 | */ 374 | public String nextTo(char delimiter) throws JSONException { 375 | StringBuilder sb = new StringBuilder(); 376 | for (;;) { 377 | char c = this.next(); 378 | if (c == delimiter || c == 0 || c == '\n' || c == '\r') { 379 | if (c != 0) { 380 | this.back(); 381 | } 382 | return sb.toString().trim(); 383 | } 384 | sb.append(c); 385 | } 386 | } 387 | 388 | 389 | /** 390 | * Get the text up but not including one of the specified delimiter 391 | * characters or the end of line, whichever comes first. 392 | * @param delimiters A set of delimiter characters. 393 | * @return A string, trimmed. 394 | * @throws JSONException Thrown if there is an error while searching 395 | * for the delimiter 396 | */ 397 | public String nextTo(String delimiters) throws JSONException { 398 | char c; 399 | StringBuilder sb = new StringBuilder(); 400 | for (;;) { 401 | c = this.next(); 402 | if (delimiters.indexOf(c) >= 0 || c == 0 || 403 | c == '\n' || c == '\r') { 404 | if (c != 0) { 405 | this.back(); 406 | } 407 | return sb.toString().trim(); 408 | } 409 | sb.append(c); 410 | } 411 | } 412 | 413 | 414 | /** 415 | * Get the next value. The value can be a Boolean, Double, Integer, 416 | * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object. 417 | * @throws JSONException If syntax error. 418 | * 419 | * @return An object. 420 | */ 421 | public Object nextValue() throws JSONException { 422 | char c = this.nextClean(); 423 | String string; 424 | 425 | switch (c) { 426 | case '"': 427 | case '\'': 428 | return this.nextString(c); 429 | case '{': 430 | this.back(); 431 | return new JSONObject(this); 432 | case '[': 433 | this.back(); 434 | return new JSONArray(this); 435 | } 436 | 437 | /* 438 | * Handle unquoted text. This could be the values true, false, or 439 | * null, or it can be a number. An implementation (such as this one) 440 | * is allowed to also accept non-standard forms. 441 | * 442 | * Accumulate characters until we reach the end of the text or a 443 | * formatting character. 444 | */ 445 | 446 | StringBuilder sb = new StringBuilder(); 447 | while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) { 448 | sb.append(c); 449 | c = this.next(); 450 | } 451 | this.back(); 452 | 453 | string = sb.toString().trim(); 454 | if ("".equals(string)) { 455 | throw this.syntaxError("Missing value"); 456 | } 457 | return JSONObject.stringToValue(string); 458 | } 459 | 460 | 461 | /** 462 | * Skip characters until the next character is the requested character. 463 | * If the requested character is not found, no characters are skipped. 464 | * @param to A character to skip to. 465 | * @return The requested character, or zero if the requested character 466 | * is not found. 467 | * @throws JSONException Thrown if there is an error while searching 468 | * for the to character 469 | */ 470 | public char skipTo(char to) throws JSONException { 471 | char c; 472 | try { 473 | long startIndex = this.index; 474 | long startCharacter = this.character; 475 | long startLine = this.line; 476 | this.reader.mark(1000000); 477 | do { 478 | c = this.next(); 479 | if (c == 0) { 480 | // in some readers, reset() may throw an exception if 481 | // the remaining portion of the input is greater than 482 | // the mark size (1,000,000 above). 483 | this.reader.reset(); 484 | this.index = startIndex; 485 | this.character = startCharacter; 486 | this.line = startLine; 487 | return 0; 488 | } 489 | } while (c != to); 490 | this.reader.mark(1); 491 | } catch (IOException exception) { 492 | throw new JSONException(exception); 493 | } 494 | this.back(); 495 | return c; 496 | } 497 | 498 | /** 499 | * Make a JSONException to signal a syntax error. 500 | * 501 | * @param message The error message. 502 | * @return A JSONException object, suitable for throwing 503 | */ 504 | public JSONException syntaxError(String message) { 505 | return new JSONException(message + this.toString()); 506 | } 507 | 508 | /** 509 | * Make a JSONException to signal a syntax error. 510 | * 511 | * @param message The error message. 512 | * @param causedBy The throwable that caused the error. 513 | * @return A JSONException object, suitable for throwing 514 | */ 515 | public JSONException syntaxError(String message, Throwable causedBy) { 516 | return new JSONException(message + this.toString(), causedBy); 517 | } 518 | 519 | /** 520 | * Make a printable string of this JSONTokener. 521 | * 522 | * @return " at {index} [character {character} line {line}]" 523 | */ 524 | @Override 525 | public String toString() { 526 | return " at " + this.index + " [character " + this.character + " line " + 527 | this.line + "]"; 528 | } 529 | } 530 | -------------------------------------------------------------------------------- /src/org/json/JSONML.java: -------------------------------------------------------------------------------- 1 | package org.json; 2 | 3 | /* 4 | Copyright (c) 2008 JSON.org 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | The Software shall be used for Good, not Evil. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /** 28 | * This provides static methods to convert an XML text into a JSONArray or 29 | * JSONObject, and to covert a JSONArray or JSONObject into an XML text using 30 | * the JsonML transform. 31 | * 32 | * @author JSON.org 33 | * @version 2016-01-30 34 | */ 35 | public class JSONML { 36 | /** 37 | * Parse XML values and store them in a JSONArray. 38 | * @param x The XMLTokener containing the source string. 39 | * @param arrayForm true if array form, false if object form. 40 | * @param ja The JSONArray that is containing the current tag or null 41 | * if we are at the outermost level. 42 | * @param keepStrings Don't type-convert text nodes and attribute values 43 | * @return A JSONArray if the value is the outermost tag, otherwise null. 44 | * @throws JSONException 45 | */ 46 | private static Object parse( 47 | XMLTokener x, 48 | boolean arrayForm, 49 | JSONArray ja, 50 | boolean keepStrings 51 | ) throws JSONException { 52 | String attribute; 53 | char c; 54 | String closeTag = null; 55 | int i; 56 | JSONArray newja = null; 57 | JSONObject newjo = null; 58 | Object token; 59 | String tagName = null; 60 | 61 | // Test for and skip past these forms: 62 | // 63 | // 64 | // 65 | // 66 | 67 | while (true) { 68 | if (!x.more()) { 69 | throw x.syntaxError("Bad XML"); 70 | } 71 | token = x.nextContent(); 72 | if (token == XML.LT) { 73 | token = x.nextToken(); 74 | if (token instanceof Character) { 75 | if (token == XML.SLASH) { 76 | 77 | // Close tag "); 97 | } else { 98 | x.back(); 99 | } 100 | } else if (c == '[') { 101 | token = x.nextToken(); 102 | if (token.equals("CDATA") && x.next() == '[') { 103 | if (ja != null) { 104 | ja.put(x.nextCDATA()); 105 | } 106 | } else { 107 | throw x.syntaxError("Expected 'CDATA['"); 108 | } 109 | } else { 110 | i = 1; 111 | do { 112 | token = x.nextMeta(); 113 | if (token == null) { 114 | throw x.syntaxError("Missing '>' after ' 0); 121 | } 122 | } else if (token == XML.QUEST) { 123 | 124 | // "); 127 | } else { 128 | throw x.syntaxError("Misshaped tag"); 129 | } 130 | 131 | // Open tag < 132 | 133 | } else { 134 | if (!(token instanceof String)) { 135 | throw x.syntaxError("Bad tagName '" + token + "'."); 136 | } 137 | tagName = (String)token; 138 | newja = new JSONArray(); 139 | newjo = new JSONObject(); 140 | if (arrayForm) { 141 | newja.put(tagName); 142 | if (ja != null) { 143 | ja.put(newja); 144 | } 145 | } else { 146 | newjo.put("tagName", tagName); 147 | if (ja != null) { 148 | ja.put(newjo); 149 | } 150 | } 151 | token = null; 152 | for (;;) { 153 | if (token == null) { 154 | token = x.nextToken(); 155 | } 156 | if (token == null) { 157 | throw x.syntaxError("Misshaped tag"); 158 | } 159 | if (!(token instanceof String)) { 160 | break; 161 | } 162 | 163 | // attribute = value 164 | 165 | attribute = (String)token; 166 | if (!arrayForm && ("tagName".equals(attribute) || "childNode".equals(attribute))) { 167 | throw x.syntaxError("Reserved attribute."); 168 | } 169 | token = x.nextToken(); 170 | if (token == XML.EQ) { 171 | token = x.nextToken(); 172 | if (!(token instanceof String)) { 173 | throw x.syntaxError("Missing value"); 174 | } 175 | newjo.accumulate(attribute, keepStrings ? ((String)token) :XML.stringToValue((String)token)); 176 | token = null; 177 | } else { 178 | newjo.accumulate(attribute, ""); 179 | } 180 | } 181 | if (arrayForm && newjo.length() > 0) { 182 | newja.put(newjo); 183 | } 184 | 185 | // Empty tag <.../> 186 | 187 | if (token == XML.SLASH) { 188 | if (x.nextToken() != XML.GT) { 189 | throw x.syntaxError("Misshaped tag"); 190 | } 191 | if (ja == null) { 192 | if (arrayForm) { 193 | return newja; 194 | } 195 | return newjo; 196 | } 197 | 198 | // Content, between <...> and 199 | 200 | } else { 201 | if (token != XML.GT) { 202 | throw x.syntaxError("Misshaped tag"); 203 | } 204 | closeTag = (String)parse(x, arrayForm, newja, keepStrings); 205 | if (closeTag != null) { 206 | if (!closeTag.equals(tagName)) { 207 | throw x.syntaxError("Mismatched '" + tagName + 208 | "' and '" + closeTag + "'"); 209 | } 210 | tagName = null; 211 | if (!arrayForm && newja.length() > 0) { 212 | newjo.put("childNodes", newja); 213 | } 214 | if (ja == null) { 215 | if (arrayForm) { 216 | return newja; 217 | } 218 | return newjo; 219 | } 220 | } 221 | } 222 | } 223 | } else { 224 | if (ja != null) { 225 | ja.put(token instanceof String 226 | ? keepStrings ? XML.unescape((String)token) :XML.stringToValue((String)token) 227 | : token); 228 | } 229 | } 230 | } 231 | } 232 | 233 | 234 | /** 235 | * Convert a well-formed (but not necessarily valid) XML string into a 236 | * JSONArray using the JsonML transform. Each XML tag is represented as 237 | * a JSONArray in which the first element is the tag name. If the tag has 238 | * attributes, then the second element will be JSONObject containing the 239 | * name/value pairs. If the tag contains children, then strings and 240 | * JSONArrays will represent the child tags. 241 | * Comments, prologs, DTDs, and <[ [ ]]> are ignored. 242 | * @param string The source string. 243 | * @return A JSONArray containing the structured data from the XML string. 244 | * @throws JSONException Thrown on error converting to a JSONArray 245 | */ 246 | public static JSONArray toJSONArray(String string) throws JSONException { 247 | return (JSONArray)parse(new XMLTokener(string), true, null, false); 248 | } 249 | 250 | 251 | /** 252 | * Convert a well-formed (but not necessarily valid) XML string into a 253 | * JSONArray using the JsonML transform. Each XML tag is represented as 254 | * a JSONArray in which the first element is the tag name. If the tag has 255 | * attributes, then the second element will be JSONObject containing the 256 | * name/value pairs. If the tag contains children, then strings and 257 | * JSONArrays will represent the child tags. 258 | * As opposed to toJSONArray this method does not attempt to convert 259 | * any text node or attribute value to any type 260 | * but just leaves it as a string. 261 | * Comments, prologs, DTDs, and <[ [ ]]> are ignored. 262 | * @param string The source string. 263 | * @param keepStrings If true, then values will not be coerced into boolean 264 | * or numeric values and will instead be left as strings 265 | * @return A JSONArray containing the structured data from the XML string. 266 | * @throws JSONException Thrown on error converting to a JSONArray 267 | */ 268 | public static JSONArray toJSONArray(String string, boolean keepStrings) throws JSONException { 269 | return (JSONArray)parse(new XMLTokener(string), true, null, keepStrings); 270 | } 271 | 272 | 273 | /** 274 | * Convert a well-formed (but not necessarily valid) XML string into a 275 | * JSONArray using the JsonML transform. Each XML tag is represented as 276 | * a JSONArray in which the first element is the tag name. If the tag has 277 | * attributes, then the second element will be JSONObject containing the 278 | * name/value pairs. If the tag contains children, then strings and 279 | * JSONArrays will represent the child content and tags. 280 | * As opposed to toJSONArray this method does not attempt to convert 281 | * any text node or attribute value to any type 282 | * but just leaves it as a string. 283 | * Comments, prologs, DTDs, and <[ [ ]]> are ignored. 284 | * @param x An XMLTokener. 285 | * @param keepStrings If true, then values will not be coerced into boolean 286 | * or numeric values and will instead be left as strings 287 | * @return A JSONArray containing the structured data from the XML string. 288 | * @throws JSONException Thrown on error converting to a JSONArray 289 | */ 290 | public static JSONArray toJSONArray(XMLTokener x, boolean keepStrings) throws JSONException { 291 | return (JSONArray)parse(x, true, null, keepStrings); 292 | } 293 | 294 | 295 | /** 296 | * Convert a well-formed (but not necessarily valid) XML string into a 297 | * JSONArray using the JsonML transform. Each XML tag is represented as 298 | * a JSONArray in which the first element is the tag name. If the tag has 299 | * attributes, then the second element will be JSONObject containing the 300 | * name/value pairs. If the tag contains children, then strings and 301 | * JSONArrays will represent the child content and tags. 302 | * Comments, prologs, DTDs, and <[ [ ]]> are ignored. 303 | * @param x An XMLTokener. 304 | * @return A JSONArray containing the structured data from the XML string. 305 | * @throws JSONException Thrown on error converting to a JSONArray 306 | */ 307 | public static JSONArray toJSONArray(XMLTokener x) throws JSONException { 308 | return (JSONArray)parse(x, true, null, false); 309 | } 310 | 311 | 312 | /** 313 | * Convert a well-formed (but not necessarily valid) XML string into a 314 | * JSONObject using the JsonML transform. Each XML tag is represented as 315 | * a JSONObject with a "tagName" property. If the tag has attributes, then 316 | * the attributes will be in the JSONObject as properties. If the tag 317 | * contains children, the object will have a "childNodes" property which 318 | * will be an array of strings and JsonML JSONObjects. 319 | 320 | * Comments, prologs, DTDs, and <[ [ ]]> are ignored. 321 | * @param string The XML source text. 322 | * @return A JSONObject containing the structured data from the XML string. 323 | * @throws JSONException Thrown on error converting to a JSONObject 324 | */ 325 | public static JSONObject toJSONObject(String string) throws JSONException { 326 | return (JSONObject)parse(new XMLTokener(string), false, null, false); 327 | } 328 | 329 | 330 | /** 331 | * Convert a well-formed (but not necessarily valid) XML string into a 332 | * JSONObject using the JsonML transform. Each XML tag is represented as 333 | * a JSONObject with a "tagName" property. If the tag has attributes, then 334 | * the attributes will be in the JSONObject as properties. If the tag 335 | * contains children, the object will have a "childNodes" property which 336 | * will be an array of strings and JsonML JSONObjects. 337 | 338 | * Comments, prologs, DTDs, and <[ [ ]]> are ignored. 339 | * @param string The XML source text. 340 | * @param keepStrings If true, then values will not be coerced into boolean 341 | * or numeric values and will instead be left as strings 342 | * @return A JSONObject containing the structured data from the XML string. 343 | * @throws JSONException Thrown on error converting to a JSONObject 344 | */ 345 | public static JSONObject toJSONObject(String string, boolean keepStrings) throws JSONException { 346 | return (JSONObject)parse(new XMLTokener(string), false, null, keepStrings); 347 | } 348 | 349 | 350 | /** 351 | * Convert a well-formed (but not necessarily valid) XML string into a 352 | * JSONObject using the JsonML transform. Each XML tag is represented as 353 | * a JSONObject with a "tagName" property. If the tag has attributes, then 354 | * the attributes will be in the JSONObject as properties. If the tag 355 | * contains children, the object will have a "childNodes" property which 356 | * will be an array of strings and JsonML JSONObjects. 357 | 358 | * Comments, prologs, DTDs, and <[ [ ]]> are ignored. 359 | * @param x An XMLTokener of the XML source text. 360 | * @return A JSONObject containing the structured data from the XML string. 361 | * @throws JSONException Thrown on error converting to a JSONObject 362 | */ 363 | public static JSONObject toJSONObject(XMLTokener x) throws JSONException { 364 | return (JSONObject)parse(x, false, null, false); 365 | } 366 | 367 | 368 | /** 369 | * Convert a well-formed (but not necessarily valid) XML string into a 370 | * JSONObject using the JsonML transform. Each XML tag is represented as 371 | * a JSONObject with a "tagName" property. If the tag has attributes, then 372 | * the attributes will be in the JSONObject as properties. If the tag 373 | * contains children, the object will have a "childNodes" property which 374 | * will be an array of strings and JsonML JSONObjects. 375 | 376 | * Comments, prologs, DTDs, and <[ [ ]]> are ignored. 377 | * @param x An XMLTokener of the XML source text. 378 | * @param keepStrings If true, then values will not be coerced into boolean 379 | * or numeric values and will instead be left as strings 380 | * @return A JSONObject containing the structured data from the XML string. 381 | * @throws JSONException Thrown on error converting to a JSONObject 382 | */ 383 | public static JSONObject toJSONObject(XMLTokener x, boolean keepStrings) throws JSONException { 384 | return (JSONObject)parse(x, false, null, keepStrings); 385 | } 386 | 387 | 388 | /** 389 | * Reverse the JSONML transformation, making an XML text from a JSONArray. 390 | * @param ja A JSONArray. 391 | * @return An XML string. 392 | * @throws JSONException Thrown on error converting to a string 393 | */ 394 | public static String toString(JSONArray ja) throws JSONException { 395 | int i; 396 | JSONObject jo; 397 | int length; 398 | Object object; 399 | StringBuilder sb = new StringBuilder(); 400 | String tagName; 401 | 402 | // Emit = length) { 438 | sb.append('/'); 439 | sb.append('>'); 440 | } else { 441 | sb.append('>'); 442 | do { 443 | object = ja.get(i); 444 | i += 1; 445 | if (object != null) { 446 | if (object instanceof String) { 447 | sb.append(XML.escape(object.toString())); 448 | } else if (object instanceof JSONObject) { 449 | sb.append(toString((JSONObject)object)); 450 | } else if (object instanceof JSONArray) { 451 | sb.append(toString((JSONArray)object)); 452 | } else { 453 | sb.append(object.toString()); 454 | } 455 | } 456 | } while (i < length); 457 | sb.append('<'); 458 | sb.append('/'); 459 | sb.append(tagName); 460 | sb.append('>'); 461 | } 462 | return sb.toString(); 463 | } 464 | 465 | /** 466 | * Reverse the JSONML transformation, making an XML text from a JSONObject. 467 | * The JSONObject must contain a "tagName" property. If it has children, 468 | * then it must have a "childNodes" property containing an array of objects. 469 | * The other properties are attributes with string values. 470 | * @param jo A JSONObject. 471 | * @return An XML string. 472 | * @throws JSONException Thrown on error converting to a string 473 | */ 474 | public static String toString(JSONObject jo) throws JSONException { 475 | StringBuilder sb = new StringBuilder(); 476 | int i; 477 | JSONArray ja; 478 | int length; 479 | Object object; 480 | String tagName; 481 | Object value; 482 | 483 | //Emit '); 518 | } else { 519 | sb.append('>'); 520 | length = ja.length(); 521 | for (i = 0; i < length; i += 1) { 522 | object = ja.get(i); 523 | if (object != null) { 524 | if (object instanceof String) { 525 | sb.append(XML.escape(object.toString())); 526 | } else if (object instanceof JSONObject) { 527 | sb.append(toString((JSONObject)object)); 528 | } else if (object instanceof JSONArray) { 529 | sb.append(toString((JSONArray)object)); 530 | } else { 531 | sb.append(object.toString()); 532 | } 533 | } 534 | } 535 | sb.append('<'); 536 | sb.append('/'); 537 | sb.append(tagName); 538 | sb.append('>'); 539 | } 540 | return sb.toString(); 541 | } 542 | } 543 | -------------------------------------------------------------------------------- /src/org/json/XML.java: -------------------------------------------------------------------------------- 1 | package org.json; 2 | 3 | /* 4 | Copyright (c) 2015 JSON.org 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | The Software shall be used for Good, not Evil. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | import java.io.Reader; 28 | import java.io.StringReader; 29 | import java.util.Iterator; 30 | 31 | /** 32 | * This provides static methods to convert an XML text into a JSONObject, and to 33 | * covert a JSONObject into an XML text. 34 | * 35 | * @author JSON.org 36 | * @version 2016-08-10 37 | */ 38 | @SuppressWarnings("boxing") 39 | public class XML { 40 | /** The Character '&'. */ 41 | public static final Character AMP = '&'; 42 | 43 | /** The Character '''. */ 44 | public static final Character APOS = '\''; 45 | 46 | /** The Character '!'. */ 47 | public static final Character BANG = '!'; 48 | 49 | /** The Character '='. */ 50 | public static final Character EQ = '='; 51 | 52 | /** The Character '>'. */ 53 | public static final Character GT = '>'; 54 | 55 | /** The Character '<'. */ 56 | public static final Character LT = '<'; 57 | 58 | /** The Character '?'. */ 59 | public static final Character QUEST = '?'; 60 | 61 | /** The Character '"'. */ 62 | public static final Character QUOT = '"'; 63 | 64 | /** The Character '/'. */ 65 | public static final Character SLASH = '/'; 66 | 67 | /** 68 | * Creates an iterator for navigating Code Points in a string instead of 69 | * characters. Once Java7 support is dropped, this can be replaced with 70 | * 71 | * string.codePoints() 72 | * 73 | * which is available in Java8 and above. 74 | * 75 | * @see http://stackoverflow.com/a/21791059/6030888 77 | */ 78 | private static Iterable codePointIterator(final String string) { 79 | return new Iterable() { 80 | @Override 81 | public Iterator iterator() { 82 | return new Iterator() { 83 | private int nextIndex = 0; 84 | private int length = string.length(); 85 | 86 | @Override 87 | public boolean hasNext() { 88 | return this.nextIndex < this.length; 89 | } 90 | 91 | @Override 92 | public Integer next() { 93 | int result = string.codePointAt(this.nextIndex); 94 | this.nextIndex += Character.charCount(result); 95 | return result; 96 | } 97 | 98 | @Override 99 | public void remove() { 100 | throw new UnsupportedOperationException(); 101 | } 102 | }; 103 | } 104 | }; 105 | } 106 | 107 | /** 108 | * Replace special characters with XML escapes: 109 | * 110 | *

111 |      * & (ampersand) is replaced by &amp;
112 |      * < (less than) is replaced by &lt;
113 |      * > (greater than) is replaced by &gt;
114 |      * " (double quote) is replaced by &quot;
115 |      * ' (single quote / apostrophe) is replaced by &apos;
116 |      * 
117 | * 118 | * @param string 119 | * The string to be escaped. 120 | * @return The escaped string. 121 | */ 122 | public static String escape(String string) { 123 | StringBuilder sb = new StringBuilder(string.length()); 124 | for (final int cp : codePointIterator(string)) { 125 | switch (cp) { 126 | case '&': 127 | sb.append("&"); 128 | break; 129 | case '<': 130 | sb.append("<"); 131 | break; 132 | case '>': 133 | sb.append(">"); 134 | break; 135 | case '"': 136 | sb.append("""); 137 | break; 138 | case '\'': 139 | sb.append("'"); 140 | break; 141 | default: 142 | if (mustEscape(cp)) { 143 | sb.append("&#x"); 144 | sb.append(Integer.toHexString(cp)); 145 | sb.append(';'); 146 | } else { 147 | sb.appendCodePoint(cp); 148 | } 149 | } 150 | } 151 | return sb.toString(); 152 | } 153 | 154 | /** 155 | * @param cp code point to test 156 | * @return true if the code point is not valid for an XML 157 | */ 158 | private static boolean mustEscape(int cp) { 159 | /* Valid range from https://www.w3.org/TR/REC-xml/#charsets 160 | * 161 | * #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] 162 | * 163 | * any Unicode character, excluding the surrogate blocks, FFFE, and FFFF. 164 | */ 165 | // isISOControl is true when (cp >= 0 && cp <= 0x1F) || (cp >= 0x7F && cp <= 0x9F) 166 | // all ISO control characters are out of range except tabs and new lines 167 | return (Character.isISOControl(cp) 168 | && cp != 0x9 169 | && cp != 0xA 170 | && cp != 0xD 171 | ) || !( 172 | // valid the range of acceptable characters that aren't control 173 | (cp >= 0x20 && cp <= 0xD7FF) 174 | || (cp >= 0xE000 && cp <= 0xFFFD) 175 | || (cp >= 0x10000 && cp <= 0x10FFFF) 176 | ) 177 | ; 178 | } 179 | 180 | /** 181 | * Removes XML escapes from the string. 182 | * 183 | * @param string 184 | * string to remove escapes from 185 | * @return string with converted entities 186 | */ 187 | public static String unescape(String string) { 188 | StringBuilder sb = new StringBuilder(string.length()); 189 | for (int i = 0, length = string.length(); i < length; i++) { 190 | char c = string.charAt(i); 191 | if (c == '&') { 192 | final int semic = string.indexOf(';', i); 193 | if (semic > i) { 194 | final String entity = string.substring(i + 1, semic); 195 | sb.append(XMLTokener.unescapeEntity(entity)); 196 | // skip past the entity we just parsed. 197 | i += entity.length() + 1; 198 | } else { 199 | // this shouldn't happen in most cases since the parser 200 | // errors on unclosed entries. 201 | sb.append(c); 202 | } 203 | } else { 204 | // not part of an entity 205 | sb.append(c); 206 | } 207 | } 208 | return sb.toString(); 209 | } 210 | 211 | /** 212 | * Throw an exception if the string contains whitespace. Whitespace is not 213 | * allowed in tagNames and attributes. 214 | * 215 | * @param string 216 | * A string. 217 | * @throws JSONException Thrown if the string contains whitespace or is empty. 218 | */ 219 | public static void noSpace(String string) throws JSONException { 220 | int i, length = string.length(); 221 | if (length == 0) { 222 | throw new JSONException("Empty string."); 223 | } 224 | for (i = 0; i < length; i += 1) { 225 | if (Character.isWhitespace(string.charAt(i))) { 226 | throw new JSONException("'" + string 227 | + "' contains a space character."); 228 | } 229 | } 230 | } 231 | 232 | /** 233 | * Scan the content following the named tag, attaching it to the context. 234 | * 235 | * @param x 236 | * The XMLTokener containing the source string. 237 | * @param context 238 | * The JSONObject that will include the new material. 239 | * @param name 240 | * The tag name. 241 | * @return true if the close tag is processed. 242 | * @throws JSONException 243 | */ 244 | private static boolean parse(XMLTokener x, JSONObject context, String name, boolean keepStrings) 245 | throws JSONException { 246 | char c; 247 | int i; 248 | JSONObject jsonobject = null; 249 | String string; 250 | String tagName; 251 | Object token; 252 | 253 | // Test for and skip past these forms: 254 | // 255 | // 256 | // 257 | // 258 | // Report errors for these forms: 259 | // <> 260 | // <= 261 | // << 262 | 263 | token = x.nextToken(); 264 | 265 | // "); 272 | return false; 273 | } 274 | x.back(); 275 | } else if (c == '[') { 276 | token = x.nextToken(); 277 | if ("CDATA".equals(token)) { 278 | if (x.next() == '[') { 279 | string = x.nextCDATA(); 280 | if (string.length() > 0) { 281 | context.accumulate("content", string); 282 | } 283 | return false; 284 | } 285 | } 286 | throw x.syntaxError("Expected 'CDATA['"); 287 | } 288 | i = 1; 289 | do { 290 | token = x.nextMeta(); 291 | if (token == null) { 292 | throw x.syntaxError("Missing '>' after ' 0); 299 | return false; 300 | } else if (token == QUEST) { 301 | 302 | // "); 304 | return false; 305 | } else if (token == SLASH) { 306 | 307 | // Close tag 353 | if (x.nextToken() != GT) { 354 | throw x.syntaxError("Misshaped tag"); 355 | } 356 | if (jsonobject.length() > 0) { 357 | context.accumulate(tagName, jsonobject); 358 | } else { 359 | context.accumulate(tagName, ""); 360 | } 361 | return false; 362 | 363 | } else if (token == GT) { 364 | // Content, between <...> and 365 | for (;;) { 366 | token = x.nextContent(); 367 | if (token == null) { 368 | if (tagName != null) { 369 | throw x.syntaxError("Unclosed tag " + tagName); 370 | } 371 | return false; 372 | } else if (token instanceof String) { 373 | string = (String) token; 374 | if (string.length() > 0) { 375 | jsonobject.accumulate("content", 376 | keepStrings ? string : stringToValue(string)); 377 | } 378 | 379 | } else if (token == LT) { 380 | // Nested element 381 | if (parse(x, jsonobject, tagName,keepStrings)) { 382 | if (jsonobject.length() == 0) { 383 | context.accumulate(tagName, ""); 384 | } else if (jsonobject.length() == 1 385 | && jsonobject.opt("content") != null) { 386 | context.accumulate(tagName, 387 | jsonobject.opt("content")); 388 | } else { 389 | context.accumulate(tagName, jsonobject); 390 | } 391 | return false; 392 | } 393 | } 394 | } 395 | } else { 396 | throw x.syntaxError("Misshaped tag"); 397 | } 398 | } 399 | } 400 | } 401 | 402 | /** 403 | * This method is the same as {@link JSONObject#stringToValue(String)}. 404 | * 405 | * @param string String to convert 406 | * @return JSON value of this string or the string 407 | */ 408 | // To maintain compatibility with the Android API, this method is a direct copy of 409 | // the one in JSONObject. Changes made here should be reflected there. 410 | public static Object stringToValue(String string) { 411 | if (string.equals("")) { 412 | return string; 413 | } 414 | if (string.equalsIgnoreCase("true")) { 415 | return Boolean.TRUE; 416 | } 417 | if (string.equalsIgnoreCase("false")) { 418 | return Boolean.FALSE; 419 | } 420 | if (string.equalsIgnoreCase("null")) { 421 | return JSONObject.NULL; 422 | } 423 | 424 | /* 425 | * If it might be a number, try converting it. If a number cannot be 426 | * produced, then the value will just be a string. 427 | */ 428 | 429 | char initial = string.charAt(0); 430 | if ((initial >= '0' && initial <= '9') || initial == '-') { 431 | try { 432 | // if we want full Big Number support this block can be replaced with: 433 | // return stringToNumber(string); 434 | if (string.indexOf('.') > -1 || string.indexOf('e') > -1 435 | || string.indexOf('E') > -1 || "-0".equals(string)) { 436 | Double d = Double.valueOf(string); 437 | if (!d.isInfinite() && !d.isNaN()) { 438 | return d; 439 | } 440 | } else { 441 | Long myLong = Long.valueOf(string); 442 | if (string.equals(myLong.toString())) { 443 | if (myLong.longValue() == myLong.intValue()) { 444 | return Integer.valueOf(myLong.intValue()); 445 | } 446 | return myLong; 447 | } 448 | } 449 | } catch (Exception ignore) { 450 | } 451 | } 452 | return string; 453 | } 454 | 455 | /** 456 | * Convert a well-formed (but not necessarily valid) XML string into a 457 | * JSONObject. Some information may be lost in this transformation because 458 | * JSON is a data format and XML is a document format. XML uses elements, 459 | * attributes, and content text, while JSON uses unordered collections of 460 | * name/value pairs and arrays of values. JSON does not does not like to 461 | * distinguish between elements and attributes. Sequences of similar 462 | * elements are represented as JSONArrays. Content text may be placed in a 463 | * "content" member. Comments, prologs, DTDs, and <[ [ ]]> 464 | * are ignored. 465 | * 466 | * @param string 467 | * The source string. 468 | * @return A JSONObject containing the structured data from the XML string. 469 | * @throws JSONException Thrown if there is an errors while parsing the string 470 | */ 471 | public static JSONObject toJSONObject(String string) throws JSONException { 472 | return toJSONObject(string, false); 473 | } 474 | 475 | /** 476 | * Convert a well-formed (but not necessarily valid) XML into a 477 | * JSONObject. Some information may be lost in this transformation because 478 | * JSON is a data format and XML is a document format. XML uses elements, 479 | * attributes, and content text, while JSON uses unordered collections of 480 | * name/value pairs and arrays of values. JSON does not does not like to 481 | * distinguish between elements and attributes. Sequences of similar 482 | * elements are represented as JSONArrays. Content text may be placed in a 483 | * "content" member. Comments, prologs, DTDs, and <[ [ ]]> 484 | * are ignored. 485 | * 486 | * @param reader The XML source reader. 487 | * @return A JSONObject containing the structured data from the XML string. 488 | * @throws JSONException Thrown if there is an errors while parsing the string 489 | */ 490 | public static JSONObject toJSONObject(Reader reader) throws JSONException { 491 | return toJSONObject(reader, false); 492 | } 493 | 494 | /** 495 | * Convert a well-formed (but not necessarily valid) XML into a 496 | * JSONObject. Some information may be lost in this transformation because 497 | * JSON is a data format and XML is a document format. XML uses elements, 498 | * attributes, and content text, while JSON uses unordered collections of 499 | * name/value pairs and arrays of values. JSON does not does not like to 500 | * distinguish between elements and attributes. Sequences of similar 501 | * elements are represented as JSONArrays. Content text may be placed in a 502 | * "content" member. Comments, prologs, DTDs, and <[ [ ]]> 503 | * are ignored. 504 | * 505 | * All values are converted as strings, for 1, 01, 29.0 will not be coerced to 506 | * numbers but will instead be the exact value as seen in the XML document. 507 | * 508 | * @param reader The XML source reader. 509 | * @param keepStrings If true, then values will not be coerced into boolean 510 | * or numeric values and will instead be left as strings 511 | * @return A JSONObject containing the structured data from the XML string. 512 | * @throws JSONException Thrown if there is an errors while parsing the string 513 | */ 514 | public static JSONObject toJSONObject(Reader reader, boolean keepStrings) throws JSONException { 515 | JSONObject jo = new JSONObject(); 516 | XMLTokener x = new XMLTokener(reader); 517 | while (x.more()) { 518 | x.skipPast("<"); 519 | if(x.more()) { 520 | parse(x, jo, null, keepStrings); 521 | } 522 | } 523 | return jo; 524 | } 525 | 526 | /** 527 | * Convert a well-formed (but not necessarily valid) XML string into a 528 | * JSONObject. Some information may be lost in this transformation because 529 | * JSON is a data format and XML is a document format. XML uses elements, 530 | * attributes, and content text, while JSON uses unordered collections of 531 | * name/value pairs and arrays of values. JSON does not does not like to 532 | * distinguish between elements and attributes. Sequences of similar 533 | * elements are represented as JSONArrays. Content text may be placed in a 534 | * "content" member. Comments, prologs, DTDs, and <[ [ ]]> 535 | * are ignored. 536 | * 537 | * All values are converted as strings, for 1, 01, 29.0 will not be coerced to 538 | * numbers but will instead be the exact value as seen in the XML document. 539 | * 540 | * @param string 541 | * The source string. 542 | * @param keepStrings If true, then values will not be coerced into boolean 543 | * or numeric values and will instead be left as strings 544 | * @return A JSONObject containing the structured data from the XML string. 545 | * @throws JSONException Thrown if there is an errors while parsing the string 546 | */ 547 | public static JSONObject toJSONObject(String string, boolean keepStrings) throws JSONException { 548 | return toJSONObject(new StringReader(string), keepStrings); 549 | } 550 | 551 | /** 552 | * Convert a JSONObject into a well-formed, element-normal XML string. 553 | * 554 | * @param object 555 | * A JSONObject. 556 | * @return A string. 557 | * @throws JSONException Thrown if there is an error parsing the string 558 | */ 559 | public static String toString(Object object) throws JSONException { 560 | return toString(object, null); 561 | } 562 | 563 | /** 564 | * Convert a JSONObject into a well-formed, element-normal XML string. 565 | * 566 | * @param object 567 | * A JSONObject. 568 | * @param tagName 569 | * The optional name of the enclosing tag. 570 | * @return A string. 571 | * @throws JSONException Thrown if there is an error parsing the string 572 | */ 573 | public static String toString(final Object object, final String tagName) 574 | throws JSONException { 575 | StringBuilder sb = new StringBuilder(); 576 | JSONArray ja; 577 | JSONObject jo; 578 | String string; 579 | 580 | if (object instanceof JSONObject) { 581 | 582 | // Emit 583 | if (tagName != null) { 584 | sb.append('<'); 585 | sb.append(tagName); 586 | sb.append('>'); 587 | } 588 | 589 | // Loop thru the keys. 590 | // don't use the new entrySet accessor to maintain Android Support 591 | jo = (JSONObject) object; 592 | for (final String key : jo.keySet()) { 593 | Object value = jo.opt(key); 594 | if (value == null) { 595 | value = ""; 596 | } else if (value.getClass().isArray()) { 597 | value = new JSONArray(value); 598 | } 599 | 600 | // Emit content in body 601 | if ("content".equals(key)) { 602 | if (value instanceof JSONArray) { 603 | ja = (JSONArray) value; 604 | int jaLength = ja.length(); 605 | // don't use the new iterator API to maintain support for Android 606 | for (int i = 0; i < jaLength; i++) { 607 | if (i > 0) { 608 | sb.append('\n'); 609 | } 610 | Object val = ja.opt(i); 611 | sb.append(escape(val.toString())); 612 | } 613 | } else { 614 | sb.append(escape(value.toString())); 615 | } 616 | 617 | // Emit an array of similar keys 618 | 619 | } else if (value instanceof JSONArray) { 620 | ja = (JSONArray) value; 621 | int jaLength = ja.length(); 622 | // don't use the new iterator API to maintain support for Android 623 | for (int i = 0; i < jaLength; i++) { 624 | Object val = ja.opt(i); 625 | if (val instanceof JSONArray) { 626 | sb.append('<'); 627 | sb.append(key); 628 | sb.append('>'); 629 | sb.append(toString(val)); 630 | sb.append("'); 633 | } else { 634 | sb.append(toString(val, key)); 635 | } 636 | } 637 | } else if ("".equals(value)) { 638 | sb.append('<'); 639 | sb.append(key); 640 | sb.append("/>"); 641 | 642 | // Emit a new tag 643 | 644 | } else { 645 | sb.append(toString(value, key)); 646 | } 647 | } 648 | if (tagName != null) { 649 | 650 | // Emit the close tag 651 | sb.append("'); 654 | } 655 | return sb.toString(); 656 | 657 | } 658 | 659 | if (object != null && (object instanceof JSONArray || object.getClass().isArray())) { 660 | if(object.getClass().isArray()) { 661 | ja = new JSONArray(object); 662 | } else { 663 | ja = (JSONArray) object; 664 | } 665 | int jaLength = ja.length(); 666 | // don't use the new iterator API to maintain support for Android 667 | for (int i = 0; i < jaLength; i++) { 668 | Object val = ja.opt(i); 669 | // XML does not have good support for arrays. If an array 670 | // appears in a place where XML is lacking, synthesize an 671 | // element. 672 | sb.append(toString(val, tagName == null ? "array" : tagName)); 673 | } 674 | return sb.toString(); 675 | } 676 | 677 | string = (object == null) ? "null" : escape(object.toString()); 678 | return (tagName == null) ? "\"" + string + "\"" 679 | : (string.length() == 0) ? "<" + tagName + "/>" : "<" + tagName 680 | + ">" + string + ""; 681 | 682 | } 683 | } 684 | --------------------------------------------------------------------------------