fr.outadev.skinswitch.network.MojangConnectionHandler.java Source code

Java tutorial

Introduction

Here is the source code for fr.outadev.skinswitch.network.MojangConnectionHandler.java

Source

/*
 * SkinSwitch - MojangConnectionHandler
 * Copyright (C) 2014-2015  Baptiste Candellier
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package fr.outadev.skinswitch.network;

import android.content.Context;
import android.text.Html;
import android.util.Log;

import com.github.kevinsawicki.http.HttpRequest;

import org.json.JSONObject;
import org.json.JSONTokener;

import java.io.File;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import fr.outadev.skinswitch.BasicSkin;

/**
 * Manages the connection to minecraft.net. Allows you to sign in successfully,
 * and send a skin.
 *
 * @author outadoc
 */
public class MojangConnectionHandler extends ConnectionHandler {

    private static final String BASE_URL = "https://minecraft.net";

    public MojangConnectionHandler(Context context) {
        super(context);
        CookieManager cookieManager = new CookieManager();
        CookieHandler.setDefault(cookieManager);
    }

    /**
     * Login with this user's credentials to the website.
     *
     * @param user the username and password that will be used to login.
     * @throws InvalidMojangCredentialsException if the username or password isn't right.
     * @throws ChallengeRequirementException     if the user has to complete a challenge in order to log in
     *                                           from this device.
     */
    public void loginWithCredentials(User user) throws InvalidMojangCredentialsException,
            ChallengeRequirementException, HttpRequest.HttpRequestException {

        Map<String, String> data = new HashMap<String, String>();

        data.put("username", user.getUsername());
        data.put("password", user.getPassword());
        data.put("remember", "false");

        String body = HttpRequest.post(BASE_URL + "/login").userAgent(getUserAgent()).followRedirects(true)
                .form(data).body();

        if (body.isEmpty() || body.contains("<h1>Login</h1>")) {
            Log.e(ConnectionHandler.TAG, "could not log in as " + user.getUsername());
            throw new InvalidMojangCredentialsException();
        } else if (body.contains("<h1>Confirm your identity</h1>")) {
            Log.e(ConnectionHandler.TAG, "challenge required for " + user.getUsername());
            throw new ChallengeRequirementException(new LoginChallenge(body));
        }

        Log.i(ConnectionHandler.TAG, "logged in as " + user.getUsername());
    }

    /**
     * Send a completed challenge to the website for validation.
     *
     * @param challenge the challenge that was answered.
     * @param answer    the answer.
     * @throws InvalidMojangChallengeAnswerException if the answer wasn't the right one.
     */
    public void validateChallenge(LoginChallenge challenge, String answer)
            throws InvalidMojangChallengeAnswerException, HttpRequest.HttpRequestException {
        Map<String, String> data = new HashMap<String, String>();

        data.put("answer", answer);
        data.put("questionId", challenge.getId());
        data.put("authenticityToken", challenge.getAuthToken());

        String body = HttpRequest.post(BASE_URL + "/challenge").userAgent(getUserAgent()).followRedirects(false)
                .form(data).body();

        if (body.equals("Security challenge passed.")) {
            Log.i(ConnectionHandler.TAG, "challenge validated");
        } else {
            String error;

            try {
                JSONObject errorObject = (JSONObject) new JSONTokener(body).nextValue();

                if (errorObject != null && errorObject.getString("error") != null) {
                    error = Html.fromHtml(errorObject.getString("error")).toString().trim();
                } else {
                    throw new Exception();
                }
            } catch (Exception e) {
                error = Html.fromHtml(body).toString().trim();
            }

            Log.e(ConnectionHandler.TAG, "challenge error: " + error);
            throw new InvalidMojangChallengeAnswerException(error);
        }
    }

    /**
     * Uploads a skin to the website.
     *
     * @param skin the skin to send.
     * @throws SkinUploadException if the upload failed.
     */
    public void uploadSkinToMojang(BasicSkin skin, Context context)
            throws SkinUploadException, HttpRequest.HttpRequestException {
        // first off, we need an authenticity token to upload the skin ;-;
        String profileBody = HttpRequest.get(BASE_URL + "/profile").userAgent(getUserAgent()).body();
        String authToken = null;

        // parse the request and get the auth token
        Pattern patternAuthToken = Pattern
                .compile("<input type=\"hidden\" name=\"authenticityToken\" value=\"([0-9a-f]*)\">");
        Matcher matcher = patternAuthToken.matcher(profileBody);

        if (matcher.find()) {
            authToken = matcher.group(1);
        }

        File skinFile = skin.getRawSkinFile(context);

        // once we have that, send the actual skin
        HttpRequest skinRequest = HttpRequest.post(BASE_URL + "/profile/skin").userAgent(getUserAgent())
                .followRedirects(true).part("authenticityToken", authToken).part("model", skin.getModelString())
                .part("skin", skinFile.getName(), "image/png", skinFile);

        String body = skinRequest.body();

        //check for upload errors
        if (body != null) {
            Pattern errorPattern = Pattern.compile("<span class=\"error\">(.+)</span>");
            matcher = errorPattern.matcher(body);

            if (matcher.find()) {
                String error = matcher.group(1);
                Log.e(ConnectionHandler.TAG, "skin couldn't be uploaded: " + error);
                throw new SkinUploadException(error);
            }
        }

        Log.i(ConnectionHandler.TAG, "skin uploaded successfully");

    }
}