org.crazyt.xgogdownloader.Api.java Source code

Java tutorial

Introduction

Here is the source code for org.crazyt.xgogdownloader.Api.java

Source

package org.crazyt.xgogdownloader;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.rmi.AccessException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.IOUtils;
import org.jsoup.Jsoup;
import org.jsoup.select.Elements;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

/* This program is free software. It comes without any warranty, to
 * the extent permitted by applicable law. You can redistribute it
 * and/or modify it under the terms of the Do What The Fuck You Want
 * To Public License, Version 2, as published by Sam Hocevar. See
 * http://www.wtfpl.net/ for more details. */
public class Api implements IApi {
    private final UserDetails user = new UserDetails();
    private final ApiConfig config = new ApiConfig();
    private boolean error;
    private String error_message;

    public Api(String token, String secret) {
        this.error = false;
        this.config.oauth_token = token;
        this.config.oauth_secret = secret;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.crazyt.xgogdownloader.IApi#init()
     */
    @Override
    public final boolean init() {
        boolean res = false;
        this.getAPIConfig();

        // Check if we already have token and secret
        if ((this.config.oauth_token != null) && (this.config.oauth_secret != "")) {
            // Test authorization by getting user details
            res = this.getUserDetails();
        }

        return res;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.crazyt.xgogdownloader.IApi#getAPIConfig()
     */
    @Override
    public final boolean getAPIConfig() {
        String url = "https://api.gog.com/en/downloader2/status/stable/"; // Stable
        // API
        boolean res = false;
        String json;
        try {
            json = this.getResponse(url);
        } catch (AccessException e1) {
            return false;
        }
        if (json != "") {
            try {
                ObjectMapper mapper = new ObjectMapper();
                JsonNode root = mapper.readTree(json);

                this.config.oauth_authorize_temp_token = root.get("config").get("oauth_authorize_temp_token")
                        .textValue() + "/";
                this.config.oauth_get_temp_token = root.get("config").get("oauth_get_temp_token").textValue() + "/";
                this.config.oauth_get_token = root.get("config").get("oauth_get_token").textValue() + "/";
                this.config.get_user_games = root.get("config").get("get_user_games").textValue() + "/";
                this.config.get_user_details = root.get("config").get("get_user_details").textValue() + "/";
                this.config.get_installer_link = root.get("config").get("get_installer_link").textValue() + "/";
                this.config.get_game_details = root.get("config").get("get_game_details").textValue() + "/";
                this.config.get_extra_link = root.get("config").get("get_extra_link").textValue() + "/";
                this.config.set_app_status = root.get("config").get("set_app_status").textValue() + "/";
                res = true;
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else {
            this.setError("Found nothing in " + url);
            res = false;
        }
        return res;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.crazyt.xgogdownloader.IApi#login(java.lang.String,
     * java.lang.String)
     */
    @Override
    public final boolean login(String email, String password) {
        boolean res = false;
        String url;
        try {
            password = URLEncoder.encode(password, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        try {
            email = URLEncoder.encode(email, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        // Get temporary request token
        url = this.config.oauth_get_temp_token + "?oauth_consumer_key=" + Global.CONSUMER_KEY
                + "&oauth_consumer_key_secret=" + Global.CONSUMER_SECRET + "&oauth_timestamp="
                + (System.currentTimeMillis() / 1000) + "&oauth_nonce=" + UUID.randomUUID()
                + "&oauth_signature_method=HMAC-SHA1&oauth_version=1.0";
        String request_token_resp;
        try {
            request_token_resp = this.getResponse(url);
        } catch (AccessException e) {
            return false;
        }

        // Authorize temporary token and get verifier
        url = this.config.oauth_authorize_temp_token + "?oauth_consumer_key=" + Global.CONSUMER_KEY
                + "&oauth_consumer_key_secret=" + Global.CONSUMER_SECRET + "&oauth_timestamp="
                + (System.currentTimeMillis() / 1000) + "&oauth_nonce=" + UUID.randomUUID()
                + "&oauth_signature_method=HMAC-SHA1&oauth_version=1.0&" + request_token_resp + "&username=" + email
                + "&password=" + password;
        String authorize_resp;
        try {
            authorize_resp = this.getResponse(url);
        } catch (AccessException e) {
            return false;
        }

        // Get final token and secret
        url = this.config.oauth_get_token + "" + authorize_resp
                + "&oauth_signature_method=HMAC-SHA1&oauth_version=1.0&oauth_consumer_key=" + Global.CONSUMER_KEY
                + "&oauth_timestamp=" + (System.currentTimeMillis() / 1000) + "&oauth_nonce=" + UUID.randomUUID()
                + "&oauth_consumer_key_secret=" + Global.CONSUMER_SECRET;
        String token_resp;
        try {
            token_resp = this.getResponse(url);
        } catch (AccessException e) {
            return false;
        }

        String[] token_resp_array = token_resp.split("&");
        if (token_resp_array.length < 2) {
            throw new RuntimeException(token_resp);
        }

        this.config.oauth_token = token_resp_array[0].replace("oauth_token=", "");
        this.config.oauth_secret = token_resp_array[1].replace("oauth_token_secret=", "");
        res = true;
        return res;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.crazyt.xgogdownloader.IApi#getUserDetails()
     */
    @Override
    public final boolean getUserDetails() {
        boolean res = false;
        String url = this.config.get_user_details;
        String json = this.getResponseOAuth(url);

        if (json != "") {
            try {
                ObjectMapper mapper = new ObjectMapper();
                JsonNode root = mapper.readTree(fixjson(json));

                this.user.id = root.get("user").get("id").longValue();
                this.user.username = root.get("user").get("xywka").textValue();
                this.user.email = root.get("user").get("email").textValue();
                this.user.avatar_big = root.get("user").get("avatar").get("big").textValue();
                this.user.avatar_small = root.get("user").get("avatar").get("small").textValue();
                this.user.notifications_forum = root.get("user").get("notifications").get("forum").intValue();
                this.user.notifications_games = root.get("user").get("notifications").get("games").intValue();
                this.user.notifications_messages = root.get("user").get("notifications").get("messages").intValue();
                res = true;
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else {
            this.setError("Found nothing in " + url);
            res = false;
        }
        return res;
    }

    private String fixjson(String json) {
        json = json.replaceAll("\"\\{", "{").replaceAll("\\}\"", "}");
        Pattern p = Pattern.compile("\"html\":\"<(.+)>\"");
        Matcher m = p.matcher(json);
        while (m.find()) {
            String orgHtml = m.group(1);
            String html = orgHtml.replace("\"", "\\\"");
            json = json.replace(orgHtml, html);
        }
        return json;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.crazyt.xgogdownloader.IApi#getResponse(java.lang.String)
     */
    @Override
    public final String getResponse(String url) throws AccessException {
        String response = "";
        try {
            response = IOUtils.toString(new InputStreamReader(new URL(url).openStream()));
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new AccessException("access to url denied:" + url);
        }
        return org.apache.commons.lang3.StringEscapeUtils.unescapeJson(response);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.crazyt.xgogdownloader.IApi#getResponseOAuth(java.lang.String)
     */
    @Override
    public final String getResponseOAuth(String url) {
        String response = "";
        String url_oauth = url + "?oauth_consumer_key=" + Global.CONSUMER_KEY + "&oauth_consumer_key_secret="
                + Global.CONSUMER_SECRET + "&oauth_token=" + this.config.oauth_token + "&oauth_token_secret="
                + this.config.oauth_secret + "&oauth_timestamp=" + (System.currentTimeMillis() / 1000)
                + "&oauth_nonce=" + UUID.randomUUID() + "&oauth_signature_method=HMAC-SHA1&oauth_version=1.0";
        try {
            response = this.getResponse(url_oauth);
        } catch (AccessException e) {
            return "";
        }
        return response;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.crazyt.xgogdownloader.IApi#getGameDetails(java.lang.String, int,
     * int, boolean)
     */
    @Override
    public final GameDetails getGameDetails(String game_name, int type, int lang, boolean useDuplicateHandler) {
        String url;
        GameDetails game = new GameDetails();

        url = this.config.get_game_details + game_name + "/" + "installer_win_en"; // can't get game details without file id,
        // any file id seems to return all
        // details which is good for us
        String json = this.getResponseOAuth(url);
        if (!json.isEmpty()) {
            try {
                ObjectMapper mapper = new ObjectMapper();
                JsonNode root = mapper.readTree(json);

                game.gamename = game_name;
                game.title = root.get("game").get("title").textValue();
                game.icon = root.get("game").get("icon").textValue();

                // Installer details
                // Create a list of installers from JSON
                // java.util.ArrayList<std.pair<Json.Value,Integer>> installers
                // = new java.util.ArrayList<std.pair<Json.Value,Integer>>();
                List<Installer> installers = new ArrayList<Installer>();
                for (int i = 0; i < GlobalConstants.PLATFORMS.size(); ++i) {
                    if ((type & GlobalConstants.PLATFORMS.get(i).platformId) != 0) {
                        String installer = "installer_" + GlobalConstants.PLATFORMS.get(i).platformCode + "_";
                        for (int j = 0; j < GlobalConstants.LANGUAGES.size(); ++j) {
                            if ((lang & GlobalConstants.LANGUAGES.get(j).languageId) != 0) {

                                if (installer != null) {
                                    JsonNode installerNode = root.get("game")
                                            .get(installer + GlobalConstants.LANGUAGES.get(j).languageCode);
                                    for (JsonNode node : installerNode) {
                                        installers.add(new Installer(node.get("id").textValue(),
                                                node.get("name").textValue(), node.get("link").textValue(),
                                                node.get("size").asLong(),
                                                GlobalConstants.LANGUAGES.get(j).languageId,
                                                node.get("notificated").asBoolean(), node.get("silent").asInt()));
                                    }
                                }

                            }
                        }
                    }
                }

                // Check for duplicate installers in different languages
                // and add languageId of duplicate installer to the original
                // installer
                // https://secure.gog.com/forum/general/introducing_the_beta_release_of_the_new_gogcom_downloader/post1483
                if (useDuplicateHandler) {
                    Iterator<Installer> it1 = installers.iterator();
                    while (it1.hasNext()) {
                        Installer installer = it1.next();
                        Iterator<Installer> it2 = installers.iterator();
                        while (it2.hasNext()) {
                            Installer installer2 = it2.next();
                            if (installer != installer2) {
                                if (installer.getUrl().equals(installer2.getUrl())) {
                                    it2.remove();
                                }
                            }
                        }
                    }
                }

                for (Installer installer : installers) {
                    game.installers.add(new GameFile(installer.getNotificated(), installer.getId(),
                            installer.getName(), installer.getUrl(), installer.getSize(), installer.getLanguageId(),
                            installer.getSilent()));
                }

                // Extra details
                JsonNode extras = root.get("game").get("extras");
                for (int index = 0; index < extras.size(); ++index) {
                    JsonNode extra = extras.get(index);

                    game.extras.add(new GameFile(false, extra.get("id").textValue(), extra.get("name").textValue(),
                            extra.get("link").textValue(), extra.get("size_mb").asLong(), 0, 0)); // extras don't have "updated"
                    // flag
                }

                // Patch details
                for (int i = 0; i < GlobalConstants.LANGUAGES.size(); ++i) { // Check
                    // against
                    // the
                    // specified
                    // languages
                    if ((lang & GlobalConstants.LANGUAGES.get(i).languageId) != 0) {
                        // Try to find a patch
                        int patch_number = 0;
                        final int maxTries = 8;
                        java.util.ArrayList<String> patchnames = new java.util.ArrayList<String>();
                        while (patch_number < maxTries) {
                            int patch_number_file = 0;
                            while (patch_number_file < maxTries) {
                                String patchname = GlobalConstants.LANGUAGES.get(i).languageCode + patch_number
                                        + "patch" + patch_number_file;
                                /*
                                 * if (root.get("game").isMember(patchname)) //
                                 * Check that patch node exists {
                                 * patchnames.add(patchname); }
                                 */
                                patch_number_file++;
                            }
                            patch_number++;
                        }

                        if (patchnames.size() > 0) // found at least one patch
                        {
                            for (int j = 0; j < patchnames.size(); ++j) {
                                JsonNode patchnode = root.get("game").get(patchnames.get(j));
                                if (patchnode.isArray()) // Patch has multiple
                                // files
                                {
                                    for (int index = 0; index < patchnode.size(); ++index) {
                                        JsonNode patch = patchnode.get(index);

                                        game.patches.add(new GameFile(false, patch.get("id").textValue(),
                                                patch.get("name").textValue(), patch.get("link").textValue(),
                                                patch.get("size").asLong(),
                                                GlobalConstants.LANGUAGES.get(j).languageId, 0)); // patches
                                        // don't
                                        // have
                                        // "updated"
                                        // flag
                                    }
                                } else // Patch is a single file
                                {
                                    game.patches.add(new GameFile(false, patchnode.get("id").textValue(),
                                            patchnode.get("name").textValue(), patchnode.get("link").textValue(),
                                            patchnode.get("size").asLong(),
                                            GlobalConstants.LANGUAGES.get(j).languageId, 0)); // patches don't have
                                                                                                                                                                                                                                                                          // "updated" flag
                                }
                            }
                        }
                    }
                }

                // Language pack details
                for (int i = 0; i < GlobalConstants.LANGUAGES.size(); ++i) { // Check
                    // against
                    // the
                    // specified
                    // languages
                    if ((lang & GlobalConstants.LANGUAGES.get(i).languageId) != 0) {
                        // Try to find a language pack
                        int lang_pack_number = 0;
                        final int maxTries = 4;
                        java.util.ArrayList<String> langpacknames = new java.util.ArrayList<String>();
                        while (lang_pack_number < maxTries) {
                            int lang_pack_number_file = 0;
                            while (lang_pack_number_file < maxTries) {
                                String langpackname = GlobalConstants.LANGUAGES.get(i).languageCode
                                        + lang_pack_number + "langpack" + lang_pack_number_file;
                                /*
                                 * if (root.get("game").isMember(langpackname))
                                 * // Check that language pack node exists {
                                 * langpacknames.add(langpackname); }
                                 */
                                lang_pack_number_file++;
                            }
                            lang_pack_number++;
                        }

                        if (langpacknames.size() > 0) // found at least one
                        // language pack
                        {
                            for (int j = 0; j < langpacknames.size(); ++j) {
                                JsonNode langpack = root.get("game").get(langpacknames.get(j));
                                game.languagepacks.add(new GameFile(false, langpack.get("id").textValue(),
                                        langpack.get("name").textValue(), langpack.get("link").textValue(),
                                        langpack.get("size").asLong(), GlobalConstants.LANGUAGES.get(j).languageId,
                                        0)); // language
                                // packs
                                // don't
                                // have
                                // "updated"
                                // flag
                            }
                        }
                    }
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else {
            this.setError("Found nothing in " + url);
        }
        return game;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.crazyt.xgogdownloader.IApi#getInstallerLink(java.lang.String,
     * java.lang.String)
     */
    @Override
    public final String getInstallerLink(String game_name, String id) {
        String url;
        String link = "";
        url = this.config.get_installer_link + game_name + "/" + id + "/";
        String json = this.getResponseOAuth(url);

        if (json != "") {
            try {
                ObjectMapper mapper = new ObjectMapper();
                JsonNode root = mapper.readTree(json);

                int available = root.get("file").get("available").intValue();
                if (available != 0) {
                    link = root.get("file").get("link").textValue();
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else {
            this.setError("Found nothing in " + url);
        }
        return link;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.crazyt.xgogdownloader.IApi#getExtraLink(java.lang.String,
     * java.lang.String)
     */
    @Override
    public final String getExtraLink(String game_name, String id) {
        String url;
        String link = "";
        url = this.config.get_extra_link + game_name + "/" + id + "/";
        String json = this.getResponseOAuth(url);

        if (json != "") {
            try {
                ObjectMapper mapper = new ObjectMapper();
                JsonNode root = mapper.readTree(json);

                int available = root.get("file").get("available").intValue();
                if (available != 0) {
                    link = root.get("file").get("link").textValue();
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else {
            this.setError("Found nothing in " + url);
        }
        return link;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.crazyt.xgogdownloader.IApi#getPatchLink(java.lang.String,
     * java.lang.String)
     */
    @Override
    public final String getPatchLink(String game_name, String id) {
        return this.getInstallerLink(game_name, id);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.crazyt.xgogdownloader.IApi#getLanguagePackLink(java.lang.String,
     * java.lang.String)
     */
    @Override
    public final String getLanguagePackLink(String game_name, String id) {
        return this.getInstallerLink(game_name, id);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.crazyt.xgogdownloader.IApi#getXML(java.lang.String,
     * java.lang.String)
     */
    @Override
    public final String getXML(String game_name, String id) {
        String url = "";
        String XML = "";
        url = this.config.get_installer_link + game_name + "/" + id + "/crc/";
        String json = this.getResponseOAuth(url);

        if (json != "") {
            try {
                ObjectMapper mapper = new ObjectMapper();
                JsonNode root = mapper.readTree(json);

                int available = root.get("file").get("available").intValue();
                if (available != 0) {
                    url = root.get("file").get("link").textValue();
                    XML = this.getResponse(url);
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else {
            this.setError("Found nothing in " + url);
        }
        return XML;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.crazyt.xgogdownloader.IApi#clearError()
     */
    @Override
    public final void clearError() {
        this.error = false;
        this.error_message = "";
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.crazyt.xgogdownloader.IApi#setError(java.lang.String)
     */
    @Override
    public final void setError(String err) {
        this.error = true;
        if (this.error_message == null) {
            this.error_message = err;
        } else {
            this.error_message += "\n" + err;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.crazyt.xgogdownloader.IApi#getError()
     */
    @Override
    public final boolean getError() {
        return this.error;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.crazyt.xgogdownloader.IApi#getErrorMessage()
     */
    @Override
    public final String getErrorMessage() {
        return this.error_message;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.crazyt.xgogdownloader.IApi#getToken()
     */
    @Override
    public final String getToken() {
        return this.config.oauth_token;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.crazyt.xgogdownloader.IApi#getSecret()
     */
    @Override
    public final String getSecret() {
        return this.config.oauth_secret;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.crazyt.xgogdownloader.IApi#searchGames(java.lang.String)
     */
    @Override
    public List<Game> searchGames(String search) {
        if ((search == null) || (search.isEmpty())) {
            throw new RuntimeException("wrong search parameter!");
        }
        try {
            String url = "http://www.gog.com/games/ajax?a=search&f=%7B%22search%22%3A%22"
                    + URLEncoder.encode(URLEncoder.encode(search, "UTF-8"), "UTF-8")
                    + "%22%2C%22sort%22%3A%22rating%22%7D&p=1&t=all";
            ObjectMapper mapper = new ObjectMapper();
            String json = this.getResponse(url);
            json = json.replaceAll("\\=\"([^\"]+)\"", "=\\\\\"$1\\\\\"");
            JsonNode root = mapper.readTree(json);
            JsonNode result = root.get("result");
            String html = result.get("html").textValue();
            org.jsoup.nodes.Document doc = Jsoup.parse(html);
            Elements links = doc.select("div.gog-price.game-owned");
            List<Game> games = new ArrayList<>();
            for (org.jsoup.nodes.Element link : links) {
                String game = link.attr("data-gameindex");
                String id = link.attr("data-gameid");
                if (game != "" && id != "") {
                    games.add(new Game(id, game));
                }
            }
            return games;
        } catch (IOException e) {
            throw new RuntimeException("Wrong search parameter or output!", e);
        }

    }

    /*
     * (non-Javadoc)
     * 
     * @see org.crazyt.xgogdownloader.IApi#getFreeGames()
     */
    @Override
    public final List<Game> getFreeGames() {
        List<Game> games = new ArrayList<Game>();
        try {
            JsonNode root;
            ObjectMapper jsonparser = new ObjectMapper();
            String json = this.getResponse(
                    "https://secure.gog.com/games/ajax?a=search&f={\"price\":[\"free\"],\"sort\":\"title\"}&p=1&t=all");
            // Parse JSON
            root = jsonparser.readTree(fixjson(json));
            String html = root.get("result").get("html").textValue();
            // Parse HTML to get game names
            org.jsoup.nodes.Document doc = Jsoup.parse(html);
            Elements links = doc.select("span.gog-price");
            for (org.jsoup.nodes.Element link : links) {
                String game = link.attr("data-gameindex");
                String id = link.attr("data-gameid");
                if (game != "" && id != "") {
                    games.add(new Game(id, game));
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return games;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.crazyt.xgogdownloader.IApi#getGames()
     */
    @Override
    public final List<Game> getGames() {
        List<Game> games = new ArrayList<Game>();
        JsonNode root;
        ObjectMapper jsonparser = new ObjectMapper();
        int i = 1;
        String html = "";
        String page_html = "";
        do {
            try {
                String response = this.getResponse(
                        "https://secure.gog.com/en/account/ajax?a=gamesShelfMore&s=title&q=&t=0&p=" + i);
                root = jsonparser.readTree(response);
                // Parse JSON
                page_html = root.get("html").textValue();
                html += page_html;
                i++;
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } while (page_html != "");
        // Parse HTML to get game names
        org.jsoup.nodes.Document doc = Jsoup.parse(html);
        Elements links = doc.select("div.shelf_game");
        for (org.jsoup.nodes.Element link : links) {
            String game = link.attr("data-gameindex");
            String gameid = link.attr("data-gameid");
            if (game != "" && gameid != "") {
                games.add(new Game(gameid, game));
            }
        }
        return games;
    }

    @Override
    public UserDetails getUser() {
        return user;
    }

}