/* * Catroid: An on-device visual programming system for Android devices * Copyright (C) 2010-2016 The Catrobat Team * (<http://developer.catrobat.org/credits>) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * An additional term exception under section 7 of the GNU Affero * General Public License, version 3, is available at * http://developer.catrobat.org/license_additional_term * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.catrobat.catroid.web; import android.content.Context; import android.content.SharedPreferences; import android.net.Uri; import android.os.ResultReceiver; import android.preference.PreferenceManager; import android.support.annotation.Nullable; import android.util.Log; import com.facebook.login.LoginBehavior; import com.google.android.gms.common.images.WebImage; import com.google.common.base.Preconditions; import com.google.common.io.Closeables; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import com.squareup.okhttp.ConnectionSpec; import com.squareup.okhttp.FormEncodingBuilder; import com.squareup.okhttp.Interceptor; import com.squareup.okhttp.MediaType; import com.squareup.okhttp.MultipartBuilder; import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.Request; import com.squareup.okhttp.RequestBody; import com.squareup.okhttp.Response; import org.catrobat.catroid.common.Constants; import org.catrobat.catroid.common.ScratchProgramData; import org.catrobat.catroid.common.ScratchSearchResult; import org.catrobat.catroid.common.ScratchVisibilityState; import org.catrobat.catroid.transfers.ProjectUploadService; import org.catrobat.catroid.utils.StatusBarNotificationManager; import org.catrobat.catroid.utils.Utils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import okio.BufferedSink; import okio.Okio; //web status codes are on: https://github.com/Catrobat/Catroweb/blob/master/statusCodes.php public final class ServerCalls implements ScratchDataFetcher { public static final String BASE_URL_TEST_HTTPS = "https://catroid-test.catrob.at/pocketcode/"; public static final String TEST_FILE_UPLOAD_URL_HTTP = BASE_URL_TEST_HTTPS + "api/upload/upload.json"; public static final String FILE_TAG_URL_HTTP = Constants.BASE_URL_HTTPS + "api/tags/getTags.json"; public static final int TOKEN_LENGTH = 32; public static final String TOKEN_CODE_INVALID = "-1"; private static final String TAG = ServerCalls.class.getSimpleName(); private static final String REGISTRATION_USERNAME_KEY = "registrationUsername"; private static final String REGISTRATION_PASSWORD_KEY = "registrationPassword"; private static final String REGISTRATION_COUNTRY_KEY = "registrationCountry"; private static final String REGISTRATION_EMAIL_KEY = "registrationEmail"; private static final String SIGNIN_USERNAME_KEY = "username"; private static final String SIGNIN_OAUTH_ID_KEY = "id"; private static final String SIGNIN_EMAIL_KEY = "email"; private static final String SIGNIN_LOCALE_KEY = "locale"; private static final String SIGNIN_FACEBOOK_CLIENT_TOKEN_KEY = "client_token"; private static final String SIGNIN_GOOGLE_CODE_KEY = "code"; private static final String SIGNIN_STATE = "state"; //not supported yet, but necessary argument for server call private static final String SIGNIN_ID_TOKEN = "id_token"; private static final String SIGNIN_TOKEN = "token"; private static final String OAUTH_TOKEN_AVAILABLE = "token_available"; private static final String EMAIL_AVAILABLE = "email_available"; private static final String USERNAME_AVAILABLE = "username_available"; private static final String FILE_UPLOAD_TAG = "upload"; private static final String PROJECT_NAME_TAG = "projectTitle"; private static final String PROJECT_DESCRIPTION_TAG = "projectDescription"; private static final String PROJECT_CHECKSUM_TAG = "fileChecksum"; private static final String USER_EMAIL = "userEmail"; private static final String DEVICE_LANGUAGE = "deviceLanguage"; private static final MediaType MEDIA_TYPE_ZIPFILE = MediaType.parse("application/zip"); private static final int SERVER_RESPONSE_TOKEN_OK = 200; private static final int SERVER_RESPONSE_REGISTER_OK = 201; private static final String FILE_UPLOAD_URL = Constants.BASE_URL_HTTPS + "api/upload/upload.json"; private static final String CHECK_TOKEN_URL = Constants.BASE_URL_HTTPS + "api/checkToken/check.json"; private static final String LOGIN_URL = Constants.BASE_URL_HTTPS + "api/login/Login.json"; private static final String REGISTRATION_URL = Constants.BASE_URL_HTTPS + "api/register/Register.json"; private static final String CHECK_GOOGLE_TOKEN_URL = Constants.BASE_URL_HTTPS + "api/GoogleServerTokenAvailable/GoogleServerTokenAvailable.json"; private static final String CHECK_FACEBOOK_TOKEN_URL = Constants.BASE_URL_HTTPS + "api/FacebookServerTokenAvailable/FacebookServerTokenAvailable.json"; private static final String GET_FACEBOOK_USER_INFO_URL = Constants.BASE_URL_HTTPS + "api/getFacebookUserInfo/getFacebookUserInfo.json"; private static final String CHECK_EMAIL_AVAILABLE_URL = Constants.BASE_URL_HTTPS + "api/EMailAvailable/EMailAvailable.json"; private static final String CHECK_USERNAME_AVAILABLE_URL = Constants.BASE_URL_HTTPS + "api/UsernameAvailable/UsernameAvailable.json"; private static final String EXCHANGE_GOOGLE_CODE_URL = Constants.BASE_URL_HTTPS + "api/exchangeGoogleCode/exchangeGoogleCode.json"; private static final String EXCHANGE_FACEBOOK_TOKEN_URL = Constants.BASE_URL_HTTPS + "api/exchangeFacebookToken/exchangeFacebookToken.json"; private static final String GOOGLE_LOGIN_URL = Constants.BASE_URL_HTTPS + "api/loginWithGoogle/loginWithGoogle.json"; private static final String FACEBOOK_LOGIN_URL = Constants.BASE_URL_HTTPS + "api/loginWithFacebook/loginWithFacebook.json"; private static final String FACEBOOK_CHECK_SERVER_TOKEN_VALIDITY = Constants.BASE_URL_HTTPS + "api/checkFacebookServerTokenValidity/checkFacebookServerTokenValidity.json"; private static final String TEST_CHECK_TOKEN_URL = BASE_URL_TEST_HTTPS + "api/checkToken/check.json"; private static final String TEST_LOGIN_URL = BASE_URL_TEST_HTTPS + "api/login/Login.json"; private static final String TEST_REGISTRATION_URL = BASE_URL_TEST_HTTPS + "api/register/Register.json"; private static final String TEST_CHECK_GOOGLE_TOKEN_URL = BASE_URL_TEST_HTTPS + "api/GoogleServerTokenAvailable/GoogleServerTokenAvailable.json"; private static final String TEST_CHECK_FACEBOOK_TOKEN_URL = BASE_URL_TEST_HTTPS + "api/FacebookServerTokenAvailable/FacebookServerTokenAvailable.json"; private static final String TEST_GET_FACEBOOK_USER_INFO_URL = BASE_URL_TEST_HTTPS + "api/getFacebookUserInfo/getFacebookUserInfo.json"; private static final String TEST_CHECK_EMAIL_AVAILABLE_URL = BASE_URL_TEST_HTTPS + "api/EMailAvailable/EMailAvailable.json"; private static final String TEST_CHECK_USERNAME_AVAILABLE_URL = BASE_URL_TEST_HTTPS + "api/UsernameAvailable/UsernameAvailable.json"; private static final String TEST_EXCHANGE_GOOGLE_CODE_URL = BASE_URL_TEST_HTTPS + "api/exchangeGoogleCode/exchangeGoogleCode.json"; private static final String TEST_EXCHANGE_FACEBOOK_TOKEN_URL = BASE_URL_TEST_HTTPS + "api/exchangeFacebookToken/exchangeFacebookToken.json"; private static final String TEST_GOOGLE_LOGIN_URL = BASE_URL_TEST_HTTPS + "api/loginWithGoogle/loginWithGoogle.json"; private static final String TEST_FACEBOOK_LOGIN_URL = BASE_URL_TEST_HTTPS + "api/loginWithFacebook/loginWithFacebook.json"; private static final String TEST_DELETE_TEST_USERS = BASE_URL_TEST_HTTPS + "api/deleteOAuthUserAccounts/deleteOAuthUserAccounts.json"; private static final String TEST_FACEBOOK_CHECK_SERVER_TOKEN_VALIDITY = BASE_URL_TEST_HTTPS + "api/checkFacebookServerTokenValidity/checkFacebookServerTokenValidity.json"; private static final String JSON_STATUS_CODE = "statusCode"; private static final String JSON_ANSWER = "answer"; private static final String JSON_TOKEN = "token"; private static final String FACEBOOK_SERVER_TOKEN_INVALID = "token_invalid"; private static LoginBehavior loginBehavior = LoginBehavior.NATIVE_WITH_FALLBACK; private static final ServerCalls INSTANCE = new ServerCalls(); public static boolean useTestUrl = false; private final OkHttpClient okHttpClient; private final Gson gson; public int oldNotificationId = 0; private String resultString; private String emailForUiTests; private int projectId; private ServerCalls() { okHttpClient = new OkHttpClient(); okHttpClient.setConnectionSpecs(Arrays.asList(ConnectionSpec.MODERN_TLS)); gson = new Gson(); } public static ServerCalls getInstance() { return INSTANCE; } public ScratchProgramData fetchScratchProgramDetails(final long programID) throws WebconnectionException, WebScratchProgramException, InterruptedIOException { final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", Locale.US); try { final String url = Constants.SCRATCH_CONVERTER_API_DEFAULT_PROJECTS_URL + programID; Log.d(TAG, "URL to use: " + url); resultString = getRequestInterruptable(url); Log.d(TAG, "Result string: " + resultString); final JSONObject jsonObject = new JSONObject(resultString); if (jsonObject.length() == 0) { return null; } if (!jsonObject.getBoolean("accessible")) { throw new WebScratchProgramException(WebScratchProgramException.ERROR_PROGRAM_NOT_ACCESSIBLE, "Program not accessible!"); } final JSONObject jsonData = jsonObject.getJSONObject("projectData"); final String title = jsonData.getString("title"); final String owner = jsonData.getString("owner"); final String imageURL = jsonData.isNull("image_url") ? null : jsonData.getString("image_url"); final String instructions = jsonData.isNull("instructions") ? null : jsonData.getString("instructions"); final String notesAndCredits = jsonData.isNull("notes_and_credits") ? null : jsonData.getString("notes_and_credits"); final String sharedDateString = jsonData.getString("shared_date"); final String modifiedDateString = jsonData.getString("modified_date"); final int views = jsonData.getInt("views"); final int favorites = jsonData.getInt("favorites"); final int loves = jsonData.getInt("loves"); final ScratchVisibilityState visibilityState = ScratchVisibilityState.valueOf(jsonObject.getInt("visibility")); final JSONArray jsonTags = jsonData.getJSONArray("tags"); Date sharedDate; try { sharedDate = formatter.parse(sharedDateString); } catch (ParseException ex) { sharedDate = null; } Date modifiedDate; try { modifiedDate = formatter.parse(modifiedDateString); } catch (ParseException ex) { modifiedDate = null; } WebImage image = null; if (imageURL != null) { image = new WebImage(Uri.parse(imageURL), Constants.SCRATCH_IMAGE_DEFAULT_WIDTH, Constants.SCRATCH_IMAGE_DEFAULT_HEIGHT); } final ScratchProgramData programData = new ScratchProgramData(programID, title, owner, image); programData.setInstructions(instructions); programData.setNotesAndCredits(notesAndCredits); programData.setModifiedDate(modifiedDate); programData.setSharedDate(sharedDate); programData.setViews(views); programData.setLoves(loves); programData.setFavorites(favorites); programData.setVisibilityState(visibilityState); for (int i = 0; i < jsonTags.length(); i++) { programData.addTag(jsonTags.getString(i)); } JSONArray remixes = jsonData.getJSONArray("remixes"); for (int i = 0; i < remixes.length(); ++i) { JSONObject remixJson = remixes.getJSONObject(i); long remixId = remixJson.getLong("id"); String remixTitle = remixJson.getString("title"); String remixOwner = remixJson.getString("owner"); String remixImageURL = remixJson.isNull("image") ? null : remixJson.getString("image"); WebImage remixImage = null; if (remixImageURL != null) { remixImage = new WebImage(Uri.parse(remixImageURL), Constants.SCRATCH_IMAGE_DEFAULT_WIDTH, Constants.SCRATCH_IMAGE_DEFAULT_HEIGHT); } programData.addRemixProgram(new ScratchProgramData(remixId, remixTitle, remixOwner, remixImage)); } return programData; } catch (WebScratchProgramException exception) { throw exception; } catch (InterruptedIOException exception) { Log.d(TAG, "OK! Request cancelled"); throw exception; } catch (Exception exception) { throw new WebconnectionException(WebconnectionException.ERROR_JSON, resultString); } } public ScratchSearchResult fetchDefaultScratchPrograms() throws WebconnectionException, InterruptedIOException { try { final String url = Constants.SCRATCH_CONVERTER_API_DEFAULT_PROJECTS_URL; Log.d(TAG, "URL to use: " + url); resultString = getRequestInterruptable(url); Log.d(TAG, "Result string: " + resultString); final JSONObject jsonObject = new JSONObject(resultString); final JSONArray results = jsonObject.getJSONArray("results"); final List<ScratchProgramData> programDataList = extractScratchProgramDataListFromJson(results); return new ScratchSearchResult(programDataList, null, 0); } catch (InterruptedIOException exception) { Log.d(TAG, "OK! Request cancelled"); throw exception; } catch (Exception exception) { Log.e(TAG, Log.getStackTraceString(exception)); throw new WebconnectionException(WebconnectionException.ERROR_JSON, resultString); } } public ScratchSearchResult scratchSearch(final String query, final int numberOfItems, final int pageNumber) throws WebconnectionException, InterruptedIOException { Preconditions.checkNotNull(query, "Parameter query cannot be null!"); Preconditions.checkArgument(numberOfItems > 0, "Parameter numberOfItems must be greater than 0"); Preconditions.checkArgument(pageNumber >= 0, "Parameter page must be greater or equal than 0"); try { final HashMap<String, String> httpGetParams = new HashMap<String, String>() { { put("limit", Integer.toString(numberOfItems)); put("offset", Integer.toString(pageNumber * numberOfItems)); put("language", Locale.getDefault().getLanguage()); put("q", URLEncoder.encode(query, "UTF-8")); } }; StringBuilder urlStringBuilder = new StringBuilder(Constants.SCRATCH_SEARCH_URL); urlStringBuilder.append('?'); for (Map.Entry<String, String> entry : httpGetParams.entrySet()) { urlStringBuilder.append(entry.getKey()); urlStringBuilder.append('='); urlStringBuilder.append(entry.getValue()); urlStringBuilder.append('&'); } urlStringBuilder.setLength(urlStringBuilder.length() - 1); // removes trailing "&" or "?" character final String url = urlStringBuilder.toString(); Log.d(TAG, "URL to use: " + url); resultString = getRequestInterruptable(url); Log.d(TAG, "Result string: " + resultString); final JSONArray results = new JSONArray(resultString); final List<ScratchProgramData> programDataList = extractScratchProgramDataListFromJson(results); return new ScratchSearchResult(programDataList, query, pageNumber); } catch (InterruptedIOException exception) { Log.d(TAG, "OK! Request cancelled"); throw exception; } catch (Exception exception) { Log.e(TAG, Log.getStackTraceString(exception)); throw new WebconnectionException(WebconnectionException.ERROR_JSON, resultString); } } private List<ScratchProgramData> extractScratchProgramDataListFromJson(final JSONArray jsonArray) throws JSONException, ParseException { final DateFormat iso8601LocalDateFormat = new SimpleDateFormat(Constants.DATE_FORMAT_ISO_8601, Locale.US); ArrayList<ScratchProgramData> programDataList = new ArrayList<>(); for (int i = 0; i < jsonArray.length(); ++i) { JSONObject programJsonData = jsonArray.getJSONObject(i); final long id = programJsonData.getLong("id"); final String title = programJsonData.getString("title"); final String notesAndCredits = programJsonData.getString("description"); final String instructions = programJsonData.getString("instructions"); final String imageURL = programJsonData.isNull("image") ? null : programJsonData.getString("image"); final JSONObject authorJsonData = programJsonData.getJSONObject("author"); // final String ownerUserID = authorJsonData.getString("id"); // reserved for later use! final String ownerUserName = authorJsonData.getString("username"); final JSONObject historyJsonData = programJsonData.getJSONObject("history"); final String createdDateString = historyJsonData.getString("created"); final String modifiedDateString = historyJsonData.getString("modified"); final String sharedDateString = historyJsonData.getString("shared"); final Date createdDate = iso8601LocalDateFormat.parse(createdDateString); final Date modifiedDate = iso8601LocalDateFormat.parse(modifiedDateString); final Date sharedDate = iso8601LocalDateFormat.parse(sharedDateString); final JSONObject statisticsJsonData = programJsonData.getJSONObject("stats"); final int views = statisticsJsonData.getInt("views"); final int loves = statisticsJsonData.getInt("loves"); final int favorites = statisticsJsonData.getInt("favorites"); // final long comments = statisticsJsonData.getLong("comments"); // reserved for later use! WebImage image = null; if (imageURL != null) { image = new WebImage(Uri.parse(imageURL), Constants.SCRATCH_IMAGE_DEFAULT_WIDTH, Constants.SCRATCH_IMAGE_DEFAULT_HEIGHT); } final ScratchProgramData programData = new ScratchProgramData(id, title, ownerUserName, image); programData.setInstructions(instructions); programData.setNotesAndCredits(notesAndCredits); programData.setCreatedDate(createdDate); programData.setModifiedDate(modifiedDate); programData.setSharedDate(sharedDate); programData.setViews(views); programData.setLoves(loves); programData.setFavorites(favorites); programDataList.add(programData); } return programDataList; } public void uploadProject(String projectName, String projectDescription, String zipFileString, String userEmail, String language, String token, String username, ResultReceiver receiver, Integer notificationId, Context context) throws WebconnectionException { Preconditions.checkNotNull(context, "Context cannot be null!"); userEmail = emailForUiTests == null ? userEmail : emailForUiTests; userEmail = userEmail == null ? "" : userEmail; try { String md5Checksum = Utils.md5Checksum(new File(zipFileString)); final String serverUrl = useTestUrl ? TEST_FILE_UPLOAD_URL_HTTP : FILE_UPLOAD_URL; Log.v(TAG, "Url to upload: " + serverUrl); File file = new File(zipFileString); RequestBody requestBody = new MultipartBuilder() .type(MultipartBuilder.FORM) .addFormDataPart( FILE_UPLOAD_TAG, ProjectUploadService.UPLOAD_FILE_NAME, RequestBody.create(MEDIA_TYPE_ZIPFILE, file)) .addFormDataPart( PROJECT_NAME_TAG, projectName) .addFormDataPart( PROJECT_DESCRIPTION_TAG, projectDescription) .addFormDataPart( USER_EMAIL, userEmail) .addFormDataPart( PROJECT_CHECKSUM_TAG, md5Checksum) .addFormDataPart( Constants.TOKEN, token) .addFormDataPart( Constants.USERNAME, username) .addFormDataPart( DEVICE_LANGUAGE, language) .build(); Request request = new Request.Builder() .url(serverUrl) .post(requestBody) .build(); Response response = okHttpClient.newCall(request).execute(); if (response.isSuccessful()) { Log.v(TAG, "Upload successful"); StatusBarNotificationManager.getInstance().showOrUpdateNotification(notificationId, 100); } else { Log.v(TAG, "Upload not successful"); throw new WebconnectionException(response.code(), "Upload failed! HTTP Status code was " + response.code()); } UploadResponse uploadResponse = gson.fromJson(response.body().string(), UploadResponse.class); String newToken = uploadResponse.token; String answer = uploadResponse.answer; int status = uploadResponse.statusCode; projectId = uploadResponse.projectId; if (status != SERVER_RESPONSE_TOKEN_OK) { throw new WebconnectionException(status, "Upload failed! JSON Response was " + status); } if (token.length() != TOKEN_LENGTH || token.isEmpty() || token.equals(TOKEN_CODE_INVALID)) { throw new WebconnectionException(status, answer); } SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); sharedPreferences.edit().putString(Constants.TOKEN, newToken).commit(); sharedPreferences.edit().putString(Constants.USERNAME, username).commit(); } catch (JsonSyntaxException jsonSyntaxException) { Log.e(TAG, Log.getStackTraceString(jsonSyntaxException)); throw new WebconnectionException(WebconnectionException.ERROR_JSON, "JsonSyntaxException"); } catch (IOException ioException) { Log.e(TAG, Log.getStackTraceString(ioException)); throw new WebconnectionException(WebconnectionException.ERROR_NETWORK, "I/O Exception"); } } public void downloadProject(final String url, final String filePath, final String programName, final ResultReceiver receiver, final int notificationId) throws IOException, WebconnectionException { File file = new File(filePath); if (!(file.getParentFile().mkdirs() || file.getParentFile().isDirectory())) { throw new IOException("Directory not created"); } Request request = new Request.Builder() .url(url) .build(); // do not use default client for NON-HTTPS requests! OkHttpClient httpClient = okHttpClient; if (url.startsWith("http://")) { httpClient = new OkHttpClient(); // allow HTTP instead of HTTPS! httpClient.setConnectionSpecs(Arrays.asList(ConnectionSpec.CLEARTEXT)); } httpClient.networkInterceptors().add(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Response originalResponse = chain.proceed(chain.request()); if (notificationId >= oldNotificationId) { oldNotificationId = notificationId; return originalResponse.newBuilder() .body(new ProgressResponseBody( originalResponse.body(), receiver, notificationId, programName, url)) .build(); } else { return originalResponse; } } }); try { Response response = httpClient.newCall(request).execute(); BufferedSink bufferedSink = Okio.buffer(Okio.sink(file)); bufferedSink.writeAll(response.body().source()); bufferedSink.close(); } catch (IOException ioException) { Log.e(TAG, Log.getStackTraceString(ioException)); throw new WebconnectionException(WebconnectionException.ERROR_NETWORK, "Connection could not be established!"); } } public void downloadMedia(final String url, final String filePath, final ResultReceiver receiver) throws IOException, WebconnectionException { File file = new File(filePath); if (!(file.getParentFile().mkdirs() || file.getParentFile().isDirectory())) { throw new IOException("Directory not created"); } Request request = new Request.Builder() .url(url) .build(); okHttpClient.networkInterceptors().add(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Response originalResponse = chain.proceed(chain.request()); return originalResponse.newBuilder() .body(new ProgressResponseBody( originalResponse.body(), receiver, 0, null, url)) .build(); } }); try { Response response = okHttpClient.newCall(request).execute(); BufferedSink bufferedSink = Okio.buffer(Okio.sink(file)); bufferedSink.writeAll(response.body().source()); bufferedSink.close(); } catch (IOException ioException) { Log.e(TAG, Log.getStackTraceString(ioException)); throw new WebconnectionException(WebconnectionException.ERROR_NETWORK, "Connection could not be established!"); } } @Nullable public byte[] downloadFile(final String url) throws Throwable { InputStream inputStream = null; try { Log.d(TAG, "Downloading file from URL: " + url); URL imageUrl = new URL(url); HttpURLConnection connection = (HttpURLConnection) imageUrl.openConnection(); connection.setConnectTimeout(Constants.DOWNLOAD_FILE_HTTP_TIMEOUT); connection.setReadTimeout(Constants.DOWNLOAD_FILE_HTTP_TIMEOUT); connection.setInstanceFollowRedirects(true); inputStream = connection.getInputStream(); if (inputStream == null) { return null; } return Utils.convertInputStreamToByteArray(inputStream); } catch (Throwable ex) { Log.e(TAG, ex.getMessage()); throw ex; } finally { Closeables.closeQuietly(inputStream); } } public boolean checkToken(String token, String username) throws WebconnectionException { try { HashMap<String, String> postValues = new HashMap<>(); postValues.put(Constants.TOKEN, token); postValues.put(SIGNIN_USERNAME_KEY, username); String serverUrl = useTestUrl ? TEST_CHECK_TOKEN_URL : CHECK_TOKEN_URL; Log.v(TAG, "post values - token:" + token + "user: " + username); Log.v(TAG, "url to upload: " + serverUrl); resultString = httpFormUpload(serverUrl, postValues); Log.v(TAG, "result string: " + resultString); JSONObject jsonObject = new JSONObject(resultString); int statusCode = jsonObject.getInt(JSON_STATUS_CODE); String serverAnswer = jsonObject.optString(JSON_ANSWER); if (statusCode == SERVER_RESPONSE_TOKEN_OK) { return true; } else { throw new WebconnectionException(statusCode, "server response token ok, but error: " + serverAnswer); } } catch (JSONException jsonException) { Log.e(TAG, Log.getStackTraceString(jsonException)); throw new WebconnectionException(WebconnectionException.ERROR_JSON, "JSON-Exception"); } } public String httpFormUpload(String url, Map<String, String> postValues) throws WebconnectionException { FormEncodingBuilder formEncodingBuilder = new FormEncodingBuilder(); if (postValues != null) { for (Map.Entry<String, String> entry : postValues.entrySet()) { formEncodingBuilder.add(entry.getKey(), entry.getValue()); } } Request request = new Request.Builder() .url(url) .post(formEncodingBuilder.build()) .build(); try { Response response = okHttpClient.newCall(request).execute(); return response.body().string(); } catch (IOException ioException) { Log.e(TAG, Log.getStackTraceString(ioException)); throw new WebconnectionException(WebconnectionException.ERROR_NETWORK, "Connection could not be established!"); } } public String getRequest(String url) throws WebconnectionException { Request request = new Request.Builder() .url(url) .build(); try { Response response = okHttpClient.newCall(request).execute(); return response.body().string(); } catch (IOException ioException) { Log.e(TAG, Log.getStackTraceString(ioException)); throw new WebconnectionException(WebconnectionException.ERROR_NETWORK, "Connection could not be established!"); } } public String getRequestInterruptable(String url) throws InterruptedIOException, WebconnectionException { Request request = new Request.Builder() .url(url) .build(); try { OkHttpClient httpClient = okHttpClient; // do not use default client for NON-HTTPS requests! if (url.startsWith("http://")) { httpClient = new OkHttpClient(); // allows HTTP instead of HTTPS! httpClient.setConnectionSpecs(Arrays.asList(ConnectionSpec.CLEARTEXT)); } Response response = httpClient.newCall(request).execute(); return response.body().string(); } catch (InterruptedIOException interruptedException) { Log.d(TAG, "Request cancelled"); throw interruptedException; } catch (IOException ioException) { Log.e(TAG, Log.getStackTraceString(ioException)); throw new WebconnectionException(WebconnectionException.ERROR_NETWORK, "Connection could not be established!"); } } public String getTags(String language) { try { String serverUrl = FILE_TAG_URL_HTTP; if (language != null) { serverUrl = serverUrl.concat("?language=" + language); } Log.v(TAG, "TAGURL to use: " + serverUrl); String response = getRequest(serverUrl); Log.d(TAG, "TAG-RESPONSE: " + response); return response; } catch (WebconnectionException exception) { Log.e(TAG, Log.getStackTraceString(exception)); return ""; } } public boolean register(String username, String password, String userEmail, String language, String country, String token, Context context) throws WebconnectionException { Preconditions.checkNotNull(context, "Context cannot be null!"); if (emailForUiTests != null) { userEmail = emailForUiTests; } if (userEmail == null) { userEmail = Constants.RESTRICTED_USER; SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); sharedPreferences.edit().putBoolean(Constants.RESTRICTED_USER, true).commit(); } try { HashMap<String, String> postValues = new HashMap<>(); postValues.put(REGISTRATION_USERNAME_KEY, username); postValues.put(REGISTRATION_PASSWORD_KEY, password); postValues.put(REGISTRATION_EMAIL_KEY, userEmail); if (!token.equals(Constants.NO_TOKEN)) { postValues.put(Constants.TOKEN, token); } if (country != null) { postValues.put(REGISTRATION_COUNTRY_KEY, country); } if (language != null) { postValues.put(DEVICE_LANGUAGE, language); } String serverUrl = useTestUrl ? TEST_REGISTRATION_URL : REGISTRATION_URL; Log.v(TAG, "URL to use: " + serverUrl); resultString = httpFormUpload(serverUrl, postValues); Log.v(TAG, "Result string: " + resultString); JSONObject jsonObject = new JSONObject(resultString); int statusCode = jsonObject.getInt(JSON_STATUS_CODE); String serverAnswer = jsonObject.optString(JSON_ANSWER); if (statusCode == SERVER_RESPONSE_TOKEN_OK || statusCode == SERVER_RESPONSE_REGISTER_OK) { String tokenReceived = jsonObject.getString(JSON_TOKEN); if (isInvalidToken(tokenReceived)) { throw new WebconnectionException(statusCode, serverAnswer); } SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); sharedPreferences.edit().putString(Constants.TOKEN, tokenReceived).commit(); sharedPreferences.edit().putString(Constants.USERNAME, username).commit(); sharedPreferences.edit().putString(Constants.EMAIL, userEmail).commit(); } boolean registered; if (statusCode == SERVER_RESPONSE_TOKEN_OK) { registered = false; } else if (statusCode == SERVER_RESPONSE_REGISTER_OK) { registered = true; } else { throw new WebconnectionException(statusCode, serverAnswer); } return registered; } catch (JSONException jsonException) { Log.e(TAG, Log.getStackTraceString(jsonException)); throw new WebconnectionException(WebconnectionException.ERROR_JSON, resultString); } } public boolean login(String username, String password, String token, Context context) throws WebconnectionException { Preconditions.checkNotNull(context, "Context cannot be null!"); try { HashMap<String, String> postValues = new HashMap<>(); postValues.put(REGISTRATION_USERNAME_KEY, username); postValues.put(REGISTRATION_PASSWORD_KEY, password); if (!token.equals(Constants.NO_TOKEN)) { postValues.put(Constants.TOKEN, token); } Log.d(TAG, "token:" + token); String serverUrl = useTestUrl ? TEST_LOGIN_URL : LOGIN_URL; Log.v(TAG, "URL to use: " + serverUrl); resultString = httpFormUpload(serverUrl, postValues); Log.v(TAG, "Result string: " + resultString); JSONObject jsonObject = new JSONObject(resultString); int statusCode = jsonObject.getInt(JSON_STATUS_CODE); String serverAnswer = jsonObject.optString(JSON_ANSWER); SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); if (statusCode == SERVER_RESPONSE_TOKEN_OK || statusCode == SERVER_RESPONSE_REGISTER_OK) { String tokenReceived = jsonObject.getString(JSON_TOKEN); if (isInvalidToken(tokenReceived)) { throw new WebconnectionException(statusCode, serverAnswer); } sharedPreferences.edit().putString(Constants.TOKEN, tokenReceived).commit(); sharedPreferences.edit().putString(Constants.USERNAME, username).commit(); } String eMail = jsonObject.optString(Constants.EMAIL); if (!eMail.isEmpty()) { sharedPreferences.edit().putString(Constants.EMAIL, eMail).commit(); } if (statusCode != SERVER_RESPONSE_TOKEN_OK) { throw new WebconnectionException(statusCode, serverAnswer); } return true; } catch (JSONException jsonException) { Log.e(TAG, Log.getStackTraceString(jsonException)); throw new WebconnectionException(WebconnectionException.ERROR_JSON, resultString); } } private boolean isInvalidToken(String token) { return token.length() != TOKEN_LENGTH || token.equals("") || token.equals(TOKEN_CODE_INVALID); } public Boolean checkOAuthToken(String id, String oauthProvider, Context context) throws WebconnectionException { Preconditions.checkNotNull(context, "Context cannot be null!"); try { HashMap<String, String> postValues = new HashMap<>(); postValues.put(SIGNIN_OAUTH_ID_KEY, id); String serverUrl; switch (oauthProvider) { case Constants.FACEBOOK: serverUrl = useTestUrl ? TEST_CHECK_FACEBOOK_TOKEN_URL : CHECK_FACEBOOK_TOKEN_URL; break; case Constants.GOOGLE_PLUS: serverUrl = useTestUrl ? TEST_CHECK_GOOGLE_TOKEN_URL : CHECK_GOOGLE_TOKEN_URL; break; default: throw new WebconnectionException(-1, "OAuth provider not supported!"); } Log.v(TAG, "URL to use: " + serverUrl); resultString = httpFormUpload(serverUrl, postValues); Log.v(TAG, "Result string: " + resultString); JSONObject jsonObject = new JSONObject(resultString); checkStatusCode200(jsonObject.getInt(JSON_STATUS_CODE)); String serverEmail = jsonObject.optString(SIGNIN_EMAIL_KEY); String serverUsername = jsonObject.optString(SIGNIN_USERNAME_KEY); boolean tokenAvailable = jsonObject.getBoolean(OAUTH_TOKEN_AVAILABLE); if (tokenAvailable) { SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); if (oauthProvider.equals(Constants.FACEBOOK)) { sharedPreferences.edit().putString(Constants.FACEBOOK_USERNAME, serverUsername).commit(); sharedPreferences.edit().putString(Constants.FACEBOOK_EMAIL, serverEmail).commit(); } else if (oauthProvider.equals(Constants.GOOGLE_PLUS)) { sharedPreferences.edit().putString(Constants.GOOGLE_USERNAME, serverUsername).commit(); sharedPreferences.edit().putString(Constants.GOOGLE_EMAIL, serverEmail).commit(); } } return tokenAvailable; } catch (JSONException jsonException) { Log.e(TAG, Log.getStackTraceString(jsonException)); throw new WebconnectionException(WebconnectionException.ERROR_JSON, resultString); } } public Boolean checkEMailAvailable(String email) throws WebconnectionException { try { HashMap<String, String> postValues = new HashMap<>(); postValues.put(SIGNIN_EMAIL_KEY, email); String serverUrl = useTestUrl ? TEST_CHECK_EMAIL_AVAILABLE_URL : CHECK_EMAIL_AVAILABLE_URL; Log.v(TAG, "URL to use: " + serverUrl); resultString = httpFormUpload(serverUrl, postValues); Log.v(TAG, "Result string: " + resultString); JSONObject jsonObject = new JSONObject(resultString); checkStatusCode200(jsonObject.getInt(JSON_STATUS_CODE)); return jsonObject.getBoolean(EMAIL_AVAILABLE); } catch (JSONException jsonException) { Log.e(TAG, Log.getStackTraceString(jsonException)); throw new WebconnectionException(WebconnectionException.ERROR_JSON, resultString); } } public Boolean checkUserNameAvailable(String username) throws WebconnectionException { try { HashMap<String, String> postValues = new HashMap<>(); postValues.put(SIGNIN_USERNAME_KEY, username); String serverUrl = useTestUrl ? TEST_CHECK_USERNAME_AVAILABLE_URL : CHECK_USERNAME_AVAILABLE_URL; Log.v(TAG, "URL to use: " + serverUrl); resultString = httpFormUpload(serverUrl, postValues); Log.v(TAG, "Result string: " + resultString); JSONObject jsonObject = new JSONObject(resultString); checkStatusCode200(jsonObject.getInt(JSON_STATUS_CODE)); return jsonObject.getBoolean(USERNAME_AVAILABLE); } catch (JSONException jsonException) { Log.e(TAG, Log.getStackTraceString(jsonException)); throw new WebconnectionException(WebconnectionException.ERROR_JSON, resultString); } } public JSONObject getFacebookUserInfo(String facebookId, String token) throws WebconnectionException { try { HashMap<String, String> postValues = new HashMap<>(); postValues.put(SIGNIN_OAUTH_ID_KEY, facebookId); if (token != null) { postValues.put(SIGNIN_TOKEN, token); } String serverUrl = useTestUrl ? TEST_GET_FACEBOOK_USER_INFO_URL : GET_FACEBOOK_USER_INFO_URL; Log.v(TAG, "URL to use: " + serverUrl); resultString = httpFormUpload(serverUrl, postValues); Log.v(TAG, "Result string: " + resultString); JSONObject jsonObject = new JSONObject(resultString); if (jsonObject.has(Constants.JSON_ERROR_CODE)) { return jsonObject; } checkStatusCode200(jsonObject.getInt(JSON_STATUS_CODE)); return jsonObject; } catch (JSONException jsonException) { Log.e(TAG, Log.getStackTraceString(jsonException)); throw new WebconnectionException(WebconnectionException.ERROR_JSON, resultString); } } public boolean facebookLogin(String mail, String username, String id, String locale, Context context) throws WebconnectionException { Preconditions.checkNotNull(context, "Context cannot be null!"); try { HashMap<String, String> postValues = new HashMap<>(); postValues.put(SIGNIN_USERNAME_KEY, username); postValues.put(SIGNIN_OAUTH_ID_KEY, id); postValues.put(SIGNIN_EMAIL_KEY, mail); postValues.put(SIGNIN_LOCALE_KEY, locale); String serverUrl = useTestUrl ? TEST_FACEBOOK_LOGIN_URL : FACEBOOK_LOGIN_URL; Log.v(TAG, "URL to use: " + serverUrl); resultString = httpFormUpload(serverUrl, postValues); Log.v(TAG, "Result string: " + resultString); JSONObject jsonObject = new JSONObject(resultString); int statusCode = jsonObject.getInt(JSON_STATUS_CODE); String serverAnswer = jsonObject.optString(JSON_ANSWER); if (statusCode == SERVER_RESPONSE_TOKEN_OK || statusCode == SERVER_RESPONSE_REGISTER_OK) { String tokenReceived = jsonObject.getString(JSON_TOKEN); if (tokenReceived.length() != TOKEN_LENGTH || tokenReceived.equals("") || tokenReceived.equals(TOKEN_CODE_INVALID)) { throw new WebconnectionException(statusCode, serverAnswer); } refreshUploadTokenAndUsername(tokenReceived, username, context); } return true; } catch (JSONException jsonException) { Log.e(TAG, Log.getStackTraceString(jsonException)); throw new WebconnectionException(WebconnectionException.ERROR_JSON, resultString); } } public boolean facebookExchangeToken(String accessToken, String id, String username, String mail, String locale) throws WebconnectionException { try { HashMap<String, String> postValues = new HashMap<>(); postValues.put(SIGNIN_FACEBOOK_CLIENT_TOKEN_KEY, accessToken); postValues.put(SIGNIN_OAUTH_ID_KEY, id); postValues.put(SIGNIN_USERNAME_KEY, username); postValues.put(SIGNIN_EMAIL_KEY, mail); postValues.put(SIGNIN_LOCALE_KEY, locale); postValues.put(SIGNIN_STATE, ""); postValues.put(Constants.REQUEST_MOBILE, "Android"); String serverUrl = useTestUrl ? TEST_EXCHANGE_FACEBOOK_TOKEN_URL : EXCHANGE_FACEBOOK_TOKEN_URL; Log.v(TAG, "URL to use: " + serverUrl); resultString = httpFormUpload(serverUrl, postValues); Log.v(TAG, "Result string: " + resultString); JSONObject jsonObject = new JSONObject(resultString); int statusCode = jsonObject.getInt(JSON_STATUS_CODE); if (!(statusCode == SERVER_RESPONSE_TOKEN_OK || statusCode == SERVER_RESPONSE_REGISTER_OK)) { throw new WebconnectionException(statusCode, resultString); } return true; } catch (JSONException jsonException) { Log.e(TAG, Log.getStackTraceString(jsonException)); throw new WebconnectionException(WebconnectionException.ERROR_JSON, resultString); } } public boolean googleLogin(String mail, String username, String id, String locale, Context context) throws WebconnectionException { Preconditions.checkNotNull(context, "Context cannot be null!"); try { HashMap<String, String> postValues = new HashMap<>(); postValues.put(SIGNIN_EMAIL_KEY, mail); postValues.put(SIGNIN_USERNAME_KEY, username); postValues.put(SIGNIN_OAUTH_ID_KEY, id); postValues.put(SIGNIN_LOCALE_KEY, locale); String serverUrl = useTestUrl ? TEST_GOOGLE_LOGIN_URL : GOOGLE_LOGIN_URL; Log.v(TAG, "URL to use: " + serverUrl); resultString = httpFormUpload(serverUrl, postValues); Log.v(TAG, "Result string: " + resultString); JSONObject jsonObject = new JSONObject(resultString); checkStatusCode200(jsonObject.getInt(JSON_STATUS_CODE)); refreshUploadTokenAndUsername(jsonObject.getString(Constants.TOKEN), username, context); return true; } catch (JSONException jsonException) { Log.e(TAG, Log.getStackTraceString(jsonException)); throw new WebconnectionException(WebconnectionException.ERROR_JSON, resultString); } } public boolean googleExchangeCode(String code, String id, String username, String mail, String locale, String idToken) throws WebconnectionException { try { HashMap<String, String> postValues = new HashMap<>(); postValues.put(SIGNIN_GOOGLE_CODE_KEY, code); postValues.put(SIGNIN_OAUTH_ID_KEY, id); postValues.put(SIGNIN_USERNAME_KEY, username); postValues.put(SIGNIN_EMAIL_KEY, mail); postValues.put(SIGNIN_LOCALE_KEY, locale); postValues.put(SIGNIN_ID_TOKEN, idToken); postValues.put(Constants.REQUEST_MOBILE, "Android"); Log.d(TAG, "ID token: " + idToken); String serverUrl = useTestUrl ? TEST_EXCHANGE_GOOGLE_CODE_URL : EXCHANGE_GOOGLE_CODE_URL; Log.v(TAG, "URL to use: " + serverUrl); resultString = httpFormUpload(serverUrl, postValues); Log.v(TAG, "Result string: " + resultString); JSONObject jsonObject = new JSONObject(resultString); int statusCode = jsonObject.getInt(JSON_STATUS_CODE); if (!(statusCode == SERVER_RESPONSE_TOKEN_OK || statusCode == SERVER_RESPONSE_REGISTER_OK)) { throw new WebconnectionException(statusCode, resultString); } return true; } catch (JSONException jsonException) { Log.e(TAG, Log.getStackTraceString(jsonException)); throw new WebconnectionException(WebconnectionException.ERROR_JSON, resultString); } } private void checkStatusCode200(int statusCode) throws WebconnectionException { if (statusCode != SERVER_RESPONSE_TOKEN_OK) { throw new WebconnectionException(statusCode, resultString); } } private void refreshUploadTokenAndUsername(String newToken, String username, Context context) { SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); sharedPreferences.edit().putString(Constants.TOKEN, newToken).commit(); sharedPreferences.edit().putString(Constants.USERNAME, username).commit(); } public boolean deleteTestUserAccountsOnServer() throws WebconnectionException { try { String serverUrl = TEST_DELETE_TEST_USERS; Log.v(TAG, "URL to use: " + serverUrl); resultString = getRequest(serverUrl); Log.v(TAG, "Result string: " + resultString); JSONObject jsonObject = new JSONObject(resultString); checkStatusCode200(jsonObject.getInt(JSON_STATUS_CODE)); return true; } catch (JSONException jsonException) { Log.e(TAG, Log.getStackTraceString(jsonException)); throw new WebconnectionException(WebconnectionException.ERROR_JSON, resultString); } } public Boolean checkFacebookServerTokenValidity(String id) throws WebconnectionException { try { HashMap<String, String> postValues = new HashMap<>(); postValues.put(SIGNIN_OAUTH_ID_KEY, id); String serverUrl = useTestUrl ? TEST_FACEBOOK_CHECK_SERVER_TOKEN_VALIDITY : FACEBOOK_CHECK_SERVER_TOKEN_VALIDITY; Log.v(TAG, "URL to use: " + serverUrl); resultString = httpFormUpload(serverUrl, postValues); Log.v(TAG, "Result string: " + resultString); JSONObject jsonObject = new JSONObject(resultString); checkStatusCode200(jsonObject.getInt(JSON_STATUS_CODE)); return jsonObject.getBoolean(FACEBOOK_SERVER_TOKEN_INVALID); } catch (JSONException jsonException) { Log.e(TAG, Log.getStackTraceString(jsonException)); throw new WebconnectionException(WebconnectionException.ERROR_JSON, resultString); } } public void logout(String userName) { try { String serverUrl = Constants.CATROBAT_TOKEN_LOGIN_URL + userName + Constants .CATROBAT_TOKEN_LOGIN_AMP_TOKEN + Constants.NO_TOKEN; Log.v(TAG, "URL to use: " + serverUrl); getRequest(serverUrl); } catch (WebconnectionException exception) { Log.e(TAG, Log.getStackTraceString(exception)); } } public LoginBehavior getLoginBehavior() { return loginBehavior; } public void setLoginBehavior(LoginBehavior loginBehavior) { ServerCalls.loginBehavior = loginBehavior; } public int getProjectId() { return projectId; } static class UploadResponse { int projectId; int statusCode; String answer; String token; // String preHeaderMessages; } }