edu.mit.scratch.Scratch.java Source code

Java tutorial

Introduction

Here is the source code for edu.mit.scratch.Scratch.java

Source

package edu.mit.scratch;

/*
 *
 * +------+----------------+------+
 * |######|  [ScratchAPI]  |######|
 * +------+----------------+------+
 *
 * Copyright (c) 2016 ScratchAPI Developers
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * "ScratchAPI Developers" means anybody who contributed code to the
 * project.
 *
 */

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;

import org.apache.http.Header;
import org.apache.http.client.CookieStore;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.cookie.Cookie;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.json.JSONArray;
import org.json.JSONObject;

import edu.mit.scratch.exceptions.ScratchLoginException;
import edu.mit.scratch.exceptions.ScratchUserException;

public class Scratch {
    protected static String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64)"
            + " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/" + "537.36";
public static final char CLOUD = '?';

    public static String CLOUD_SERVER = "cloud.scratch.mit.edu";
    public static int CLOUD_PORT = 531;

    public static ScratchSession createSession(final String username, String password)
            throws ScratchLoginException {
        try {
            final RequestConfig globalConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.DEFAULT) // Changed due to deprecation
                    .build();

            final CookieStore cookieStore = new BasicCookieStore();
            final BasicClientCookie lang = new BasicClientCookie("scratchlanguage", "en");
            lang.setDomain(".scratch.mit.edu");
            lang.setPath("/");
            cookieStore.addCookie(lang);

            final CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(globalConfig)
                    .setUserAgent(Scratch.USER_AGENT).setDefaultCookieStore(cookieStore).build();
            CloseableHttpResponse resp;

            final HttpUriRequest csrf = RequestBuilder.get().setUri("https://scratch.mit.edu/csrf_token/")
                    .addHeader("Accept", "*/*").addHeader("Referer", "https://scratch.mit.edu")
                    .addHeader("X-Requested-With", "XMLHttpRequest").build();
            resp = httpClient.execute(csrf);
            resp.close();

            String csrfToken = null;
            for (final Cookie c : cookieStore.getCookies())
                if (c.getName().equals("scratchcsrftoken"))
                    csrfToken = c.getValue();

            final JSONObject loginObj = new JSONObject();
            loginObj.put("username", username);
            loginObj.put("password", password);
            loginObj.put("captcha_challenge", "");
            loginObj.put("captcha_response", "");
            loginObj.put("embed_captcha", false);
            loginObj.put("timezone", "America/New_York");
            loginObj.put("csrfmiddlewaretoken", csrfToken);
            final HttpUriRequest login = RequestBuilder.post().setUri("https://scratch.mit.edu/accounts/login/")
                    .addHeader("Accept", "application/json, text/javascript, */*; q=0.01")
                    .addHeader("Referer", "https://scratch.mit.edu").addHeader("Origin", "https://scratch.mit.edu")
                    .addHeader("Accept-Encoding", "gzip, deflate").addHeader("Accept-Language", "en-US,en;q=0.8")
                    .addHeader("Content-Type", "application/json").addHeader("X-Requested-With", "XMLHttpRequest")
                    .addHeader("X-CSRFToken", csrfToken).setEntity(new StringEntity(loginObj.toString())).build();
            resp = httpClient.execute(login);
            password = null;
            final BufferedReader rd = new BufferedReader(new InputStreamReader(resp.getEntity().getContent()));

            final StringBuffer result = new StringBuffer();
            String line = "";
            while ((line = rd.readLine()) != null)
                result.append(line);
            final JSONObject jsonOBJ = new JSONObject(
                    result.toString().substring(1, result.toString().length() - 1));
            if ((int) jsonOBJ.get("success") != 1)
                throw new ScratchLoginException();
            String ssi = null;
            String sct = null;
            String e = null;
            final Header[] headers = resp.getAllHeaders();
            for (final Header header : headers)
                if (header.getName().equals("Set-Cookie")) {
                    final String value = header.getValue();
                    final String[] split = value.split(Pattern.quote("; "));
                    for (final String s : split) {
                        if (s.contains("=")) {
                            final String[] split2 = s.split(Pattern.quote("="));
                            final String key = split2[0];
                            final String val = split2[1];
                            if (key.equals("scratchsessionsid"))
                                ssi = val;
                            else if (key.equals("scratchcsrftoken"))
                                sct = val;
                            else if (key.equals("expires"))
                                e = val;
                        }
                    }
                }
            resp.close();

            return new ScratchSession(ssi, sct, e, username);
        } catch (final IOException e) {
            e.printStackTrace();
            throw new ScratchLoginException();
        }
    }

    public static ScratchSession register(final String username, final String password, final String gender,
            final int birthMonth, final String birthYear, final String country, final String email)
            throws ScratchUserException {
        // Long if statement to verify all fields are valid
        if ((username.length() < 3) || (username.length() > 20) || (password.length() < 6) || (gender.length() < 2)
                || (birthMonth < 1) || (birthMonth > 12) || (birthYear.length() != 4) || (country.length() == 0)
                || (email.length() < 5)) {
            throw new ScratchUserException(); // TDL: Specify reason for failure
        } else {
            try {
                final RequestConfig globalConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.DEFAULT)
                        .build();

                final CookieStore cookieStore = new BasicCookieStore();
                final BasicClientCookie lang = new BasicClientCookie("scratchlanguage", "en");
                lang.setDomain(".scratch.mit.edu");
                lang.setPath("/");
                cookieStore.addCookie(lang);

                CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(globalConfig)
                        .setUserAgent(Scratch.USER_AGENT).setDefaultCookieStore(cookieStore).build();
                CloseableHttpResponse resp;

                final HttpUriRequest csrf = RequestBuilder.get().setUri("https://scratch.mit.edu/csrf_token/")
                        .addHeader("Accept", "*/*").addHeader("Referer", "https://scratch.mit.edu")
                        .addHeader("X-Requested-With", "XMLHttpRequest").build();
                try {
                    resp = httpClient.execute(csrf);
                } catch (final IOException e) {
                    e.printStackTrace();
                    throw new ScratchUserException();
                }
                try {
                    resp.close();
                } catch (final IOException e) {
                    throw new ScratchUserException();
                }

                String csrfToken = null;
                for (final Cookie c : cookieStore.getCookies())
                    if (c.getName().equals("scratchcsrftoken"))
                        csrfToken = c.getValue();

                /*
                 * try {
                 * username = URLEncoder.encode(username, "UTF-8");
                 * password = URLEncoder.encode(password, "UTF-8");
                 * birthMonth = Integer.parseInt(URLEncoder.encode("" +
                 * birthMonth, "UTF-8"));
                 * birthYear = URLEncoder.encode(birthYear, "UTF-8");
                 * gender = URLEncoder.encode(gender, "UTF-8");
                 * country = URLEncoder.encode(country, "UTF-8");
                 * email = URLEncoder.encode(email, "UTF-8");
                 * } catch (UnsupportedEncodingException e1) {
                 * e1.printStackTrace();
                 * }
                 */

                final BasicClientCookie csrfCookie = new BasicClientCookie("scratchcsrftoken", csrfToken);
                csrfCookie.setDomain(".scratch.mit.edu");
                csrfCookie.setPath("/");
                cookieStore.addCookie(csrfCookie);
                final BasicClientCookie debug = new BasicClientCookie("DEBUG", "true");
                debug.setDomain(".scratch.mit.edu");
                debug.setPath("/");
                cookieStore.addCookie(debug);

                httpClient = HttpClients.custom().setDefaultRequestConfig(globalConfig)
                        .setUserAgent(Scratch.USER_AGENT).setDefaultCookieStore(cookieStore).build();

                /*
                 * final String data = "username=" + username + "&password=" +
                 * password + "&birth_month=" + birthMonth + "&birth_year=" +
                 * birthYear + "&gender=" + gender + "&country=" + country +
                 * "&email=" + email +
                 * "&is_robot=false&should_generate_admin_ticket=false&usernames_and_messages=%3Ctable+class%3D'banhistory'%3E%0A++++%3Cthead%3E%0A++++++++%3Ctr%3E%0A++++++++++++%3Ctd%3EAccount%3C%2Ftd%3E%0A++++++++++++%3Ctd%3EEmail%3C%2Ftd%3E%0A++++++++++++%3Ctd%3EReason%3C%2Ftd%3E%0A++++++++++++%3Ctd%3EDate%3C%2Ftd%3E%0A++++++++%3C%2Ftr%3E%0A++++%3C%2Fthead%3E%0A++++%0A%3C%2Ftable%3E%0A&csrfmiddlewaretoken="
                 * + csrfToken;
                 * System.out.println(data);
                 */

                final MultipartEntityBuilder builder = MultipartEntityBuilder.create();
                builder.addTextBody("birth_month", birthMonth + "", ContentType.TEXT_PLAIN);
                builder.addTextBody("birth_year", birthYear, ContentType.TEXT_PLAIN);
                builder.addTextBody("country", country, ContentType.TEXT_PLAIN);
                builder.addTextBody("csrfmiddlewaretoken", csrfToken, ContentType.TEXT_PLAIN);
                builder.addTextBody("email", email, ContentType.TEXT_PLAIN);
                builder.addTextBody("gender", gender, ContentType.TEXT_PLAIN);
                builder.addTextBody("is_robot", "false", ContentType.TEXT_PLAIN);
                builder.addTextBody("password", password, ContentType.TEXT_PLAIN);
                builder.addTextBody("should_generate_admin_ticket", "false", ContentType.TEXT_PLAIN);
                builder.addTextBody("username", username, ContentType.TEXT_PLAIN);
                builder.addTextBody("usernames_and_messages",
                        "<table class=\"banhistory\"> <thead> <tr> <td>Account</td> <td>Email</td> <td>Reason</td> <td>Date</td> </tr> </thead> </table>",
                        ContentType.TEXT_PLAIN);

                final HttpUriRequest createAccount = RequestBuilder.post()
                        .setUri("https://scratch.mit.edu/accounts/register_new_user/")
                        .addHeader("Accept", "application/json, text/javascript, */*; q=0.01")
                        .addHeader("Referer", "https://scratch.mit.edu/accounts/standalone-registration/")
                        .addHeader("Origin", "https://scratch.mit.edu")
                        .addHeader("Accept-Encoding", "gzip, deflate").addHeader("DNT", "1")
                        .addHeader("Accept-Language", "en-US,en;q=0.8")
                        .addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
                        .addHeader("X-Requested-With", "XMLHttpRequest").addHeader("X-CSRFToken", csrfToken)
                        .addHeader("X-DevTools-Emulate-Network-Conditions-Client-Id",
                                "54255D9A-9771-4CAC-9052-50C8AB7469E0")
                        .setEntity(builder.build()).build();
                resp = httpClient.execute(createAccount);
                System.out.println("REGISTER:" + resp.getStatusLine());
                final BufferedReader rd = new BufferedReader(new InputStreamReader(resp.getEntity().getContent()));

                final StringBuffer result = new StringBuffer();
                String line = "";
                while ((line = rd.readLine()) != null)
                    result.append(line);
                System.out.println("exact:" + result.toString() + "\n" + resp.getStatusLine().getReasonPhrase()
                        + "\n" + resp.getStatusLine());
                if (resp.getStatusLine().getStatusCode() != 200)
                    throw new ScratchUserException();
                resp.close();
            } catch (final Exception e) {
                e.printStackTrace();
                throw new ScratchUserException();
            }
            try {
                return Scratch.createSession(username, password);
            } catch (final Exception e) {
                e.printStackTrace();
                throw new ScratchUserException();
            }
        }
    }

    /*
     * TODO:
     * - Get list of featured projects
     * - Get list of newest projects
     * - Get list of curated projects
     * - Get list of team members (?)
     * - Get list of top-remixed projects
     * - Get list of top-loved projects
     * - Get list of featured studios
     * - Get current project curator
     * - Get Scratch design studio
     * - Get list of projects by following
     * - Get list of projects by following's loved projects
     * - Get list of projects in studios I'm following
     */
    protected static String consume(final CloseableHttpResponse r) throws IllegalStateException, IOException {
        final InputStream in = r.getEntity().getContent();
        final StringBuffer str = new StringBuffer();
        final byte[] b = new byte[64];
        int len;
        while ((len = in.read(b)) != -1)
            str.append(new String(b, 0, len));
        in.close();
        return str.toString();
    }

    public static String setUserAgent(final String user_agent) {
        Scratch.USER_AGENT = user_agent;
        return Scratch.USER_AGENT;
    }

    public static List<ScratchUser> getUsers(final int limit, final int offset) throws ScratchUserException {
        if ((offset < 0) || (limit < 0))
            throw new ScratchUserException();

        final List<ScratchUser> users = new ArrayList<>();

        try {
            final RequestConfig globalConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.DEFAULT).build();

            final CookieStore cookieStore = new BasicCookieStore();
            final BasicClientCookie lang = new BasicClientCookie("scratchlanguage", "en");
            final BasicClientCookie debug = new BasicClientCookie("DEBUG", "true");
            debug.setDomain(".scratch.mit.edu");
            debug.setPath("/");
            lang.setPath("/");
            lang.setDomain(".scratch.mit.edu");
            cookieStore.addCookie(lang);
            cookieStore.addCookie(debug);

            final CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(globalConfig)
                    .setUserAgent(Scratch.USER_AGENT).setDefaultCookieStore(cookieStore).build();
            CloseableHttpResponse resp;

            final HttpUriRequest update = RequestBuilder.get()
                    .setUri("https://scratch.mit.edu/api/v1/user/?format=json&limit=" + limit + "&offset=" + offset)
                    .addHeader("Accept",
                            "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
                    .addHeader("Referer", "https://scratch.mit.edu").addHeader("Origin", "https://scratch.mit.edu")
                    .addHeader("Accept-Encoding", "gzip, deflate, sdch")
                    .addHeader("Accept-Language", "en-US,en;q=0.8").addHeader("Content-Type", "application/json")
                    .addHeader("X-Requested-With", "XMLHttpRequest").build();
            try {
                resp = httpClient.execute(update);
            } catch (final IOException e) {
                e.printStackTrace();
                throw new ScratchUserException();
            }

            BufferedReader rd;
            try {
                rd = new BufferedReader(new InputStreamReader(resp.getEntity().getContent()));
            } catch (UnsupportedOperationException | IOException e) {
                e.printStackTrace();
                throw new ScratchUserException();
            }

            final StringBuffer result = new StringBuffer();
            String line = "";
            while ((line = rd.readLine()) != null)
                result.append(line);
            final JSONObject jsonOBJ = new JSONObject(result.toString().trim());

            final Iterator<?> keys = jsonOBJ.keys();

            while (keys.hasNext()) {
                final String key = "" + keys.next();
                final Object o = jsonOBJ.get(key);
                final String val = "" + o;

                if (key.equals("objects")) {
                    final JSONArray jsonArray = (JSONArray) o;
                    for (int i = 0; i < jsonArray.length(); i++) {
                        final JSONObject jsonOBJ2 = jsonArray.getJSONObject(i);
                        users.add(new ScratchUser("" + jsonOBJ2.get("username")));
                    }
                }
            }

            return users;
        } catch (final UnsupportedEncodingException e) {
            e.printStackTrace();
            throw new ScratchUserException();
        } catch (final Exception e) {
            e.printStackTrace();
            throw new ScratchUserException();
        }
    }
}