/* * Funambol is a mobile platform developed by Funambol, Inc. * Copyright (C) 2010 Funambol, Inc. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License version 3 as published by * the Free Software Foundation with the addition of the following permission * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE * WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. * * 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 Affero General Public License * along with this program; if not, see http://www.gnu.org/licenses or write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA. * * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com. * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU Affero General Public License version 3. * * In accordance with Section 7(b) of the GNU Affero General Public License * version 3, these Appropriate Legal Notices must retain the display of the * "Powered by Funambol" logo. If the display of the logo is not reasonably * feasible for technical reasons, the Appropriate Legal Notices must display * the words "Powered by Funambol". */ package com.funambol.client.controller; import java.io.IOException; import java.util.Vector; import com.funambol.client.customization.Customization; import com.funambol.client.localization.Localization; import com.funambol.client.ui.SignupScreen; import com.funambol.sapisync.sapi.SapiHandler; import com.funambol.platform.DeviceInfoInterface; import com.funambol.util.Log; import com.funambol.util.StringUtil; import org.json.me.JSONArray; import org.json.me.JSONException; import org.json.me.JSONObject; /** * Handles the signup process in a separated thread. Example: * new SignupHandler(SignupScreen, SignupScreenController).start(); */ public class SignupHandler extends Thread { private static final String TAG_LOG = "SignupHandler"; public static final int VALIDATION_MODE_NONE = 0; public static final int VALIDATION_MODE_CAPTCHA = 1; public static final int VALIDATION_MODE_SMS = 2; public static final int VALIDATION_MODE_SMS_CAPTCHA = 3; private static final String JSON_OBJECT_DATA = "data"; private static final String JSON_OBJECT_USER = "user"; private static final String JSON_OBJECT_ERROR = "error"; private static final String JSON_OBJECT_ERROR_FIELD_CODE = "code"; private static final String JSON_OBJECT_ERROR_FIELD_MESSAGE = "message"; private static final String JSON_OBJECT_ERROR_FIELD_CAUSE = "cause"; private static final String JSON_OBJECT_ERROR_FIELD_PARAMETERS = "parameters"; private static final String JSON_OBJECT_ERROR_FIELD_PARAM = "param"; private static final String JSON_OBJECT_USER_FIELD_ACTIVE = "active"; private static final String JSON_OBJECT_USER_FIELD_PHONE_NUMBER = "phonenumber"; private static final String JSON_OBJECT_USER_FIELD_PASSWORD = "password"; private static final String JSON_OBJECT_USER_FIELD_PLATFORM = "platform"; private static final String JSON_OBJECT_USER_FIELD_COUNTRY = "countrya2"; private static final String JSON_OBJECT_USER_FIELD_EMAIL = "useremail"; private static final String JSON_OBJECT_USER_FIELD_TIMEZONE = "timezone"; private static final String JSON_OBJECT_USER_FIELD_MANUFACTURER = "manufacturer"; private static final String JSON_OBJECT_USER_FIELD_MODEL = "model"; private static final String JSON_OBJECT_USER_FIELD_CARRIER = "carrier"; public static final String JSON_ERROR_CODE_PRO_1000 = "PRO-1000"; public static final String JSON_ERROR_CODE_PRO_1001 = "PRO-1001"; public static final String JSON_ERROR_CODE_PRO_1106 = "PRO-1106"; public static final String JSON_ERROR_CODE_PRO_1107 = "PRO-1107"; public static final String JSON_ERROR_CODE_PRO_1113 = "PRO-1113"; public static final String JSON_ERROR_CODE_PRO_1115 = "PRO-1115"; public static final String JSON_ERROR_CODE_PRO_1122 = "PRO-1122"; public static final String JSON_ERROR_CODE_PRO_1126 = "PRO-1126"; public static final String JSON_ERROR_CODE_PRO_1127 = "PRO-1127"; public static final String JSON_ERROR_CODE_PRO_1128 = "PRO-1128"; public static final String JSON_ERROR_CODE_COM_1006 = "COM-1006"; public static final String JSON_ERROR_CODE_COM_1008 = "COM-1008"; private SignupScreen signupScreen = null; private SignupScreenController signupScreenController = null; private Customization customization; private Localization localization; public SignupHandler(SignupScreen signupScreen, SignupScreenController signupScreenController) { this.signupScreen = signupScreen; this.signupScreenController = signupScreenController; this.localization = signupScreenController.localization; this.customization = signupScreenController.customization; } public void run() { if (Log.isLoggable(Log.INFO)) { Log.info(TAG_LOG, "Signing up"); } signupScreen.disableSignup(); signupScreenController.signupStarted(); // Get the current values from UI String serverUrl = signupScreen.getSyncUrl(); String phoneNumber = signupScreen.getUsername(); String password = signupScreen.getPassword(); String token = signupScreen.getCaptchaToken(); String signupUrl = StringUtil.extractAddressFromUrl(serverUrl); SapiHandler sapiHandler = new SapiHandler(signupUrl); String jsessionId = signupScreenController.getCurrentJSessionId(); if(jsessionId != null) { sapiHandler.enableJSessionAuthentication(true); sapiHandler.forceJSessionId(jsessionId); } try { if (Log.isLoggable(Log.DEBUG)) { Log.debug(TAG_LOG, "Sending Signup SAPI request"); } JSONObject request = createMobileSignupRequest(phoneNumber, password); Vector params = new Vector(); StringBuffer tokenParam = new StringBuffer(); tokenParam.append("token=").append(token); params.addElement(tokenParam.toString()); JSONObject res = sapiHandler.query("mobile", "signup", params, null, request); if(!userActivated(res)) { if (Log.isLoggable(Log.DEBUG)) { Log.debug(TAG_LOG, "Account not activated by the server"); } handleResponseError(res, signupUrl); return; } else { if (Log.isLoggable(Log.INFO)) { Log.info(TAG_LOG, "Account activated by the server"); } } } catch(IOException ex) { // This is a network failure Log.error(TAG_LOG, "Unable to signup", ex); signupScreenController.signupFailed( localization.getLanguage("signup_failed_network")); signupScreenController.promptCredentials(); return; } catch(Exception ex) { // This is a generic failure Log.error(TAG_LOG, "Unable to signup", ex); signupScreenController.signupFailed( localization.getLanguage("signup_failed_generic_message")); signupScreenController.promptCredentials(); return; } // Signup Succeeded signupScreenController.signupSucceeded(); } private JSONObject createMobileSignupRequest(String phoneNumber, String password) throws JSONException { JSONObject request = new JSONObject(); JSONObject data = new JSONObject(); JSONObject deviceInfo = new JSONObject(); DeviceInfoInterface devInfo = signupScreenController.getDeviceInfo(); deviceInfo.put(JSON_OBJECT_USER_FIELD_PHONE_NUMBER, phoneNumber); deviceInfo.put(JSON_OBJECT_USER_FIELD_PASSWORD, password); deviceInfo.put(JSON_OBJECT_USER_FIELD_PLATFORM, devInfo.getFunambolPlatform()); deviceInfo.put(JSON_OBJECT_USER_FIELD_COUNTRY, devInfo.getCountryCode()); deviceInfo.put(JSON_OBJECT_USER_FIELD_EMAIL, devInfo.getEmailAddress()); deviceInfo.put(JSON_OBJECT_USER_FIELD_TIMEZONE, devInfo.getTimezone()); deviceInfo.put(JSON_OBJECT_USER_FIELD_MANUFACTURER, devInfo.getManufacturer()); deviceInfo.put(JSON_OBJECT_USER_FIELD_MODEL, devInfo.getDeviceModel()); deviceInfo.put(JSON_OBJECT_USER_FIELD_CARRIER, devInfo.getCarrier()); data.put(JSON_OBJECT_USER, deviceInfo); request.put(JSON_OBJECT_DATA, data); return request; } private boolean userActivated(JSONObject response) { boolean active = false; try { JSONObject resData = response.getJSONObject(JSON_OBJECT_DATA); if(resData != null) { JSONObject resUser = resData.getJSONObject(JSON_OBJECT_USER); if(resUser != null) { active = resUser.getBoolean(JSON_OBJECT_USER_FIELD_ACTIVE); } } } catch(JSONException ex) { if (Log.isLoggable(Log.DEBUG)) { Log.debug(TAG_LOG, "Failed to retrieve user data json object"); } } return active; } /** * Handles the response error if any * @param response * @param signupUrl * @throws JSONException */ private void handleResponseError(JSONObject response, String signupUrl) throws Exception { try { // Check for errors JSONObject error = response.getJSONObject(JSON_OBJECT_ERROR); if(error != null) { String code = error.getString(JSON_OBJECT_ERROR_FIELD_CODE); String message = error.getString(JSON_OBJECT_ERROR_FIELD_MESSAGE); String cause = error.getString(JSON_OBJECT_ERROR_FIELD_CAUSE); StringBuffer logMsg = new StringBuffer( "Error in SAPI response").append("\r\n"); logMsg.append("code: ").append(code).append("\r\n"); logMsg.append("cause: ").append(cause).append("\r\n"); logMsg.append("message: ").append(message).append("\r\n"); JSONArray parameters = error.getJSONArray(JSON_OBJECT_ERROR_FIELD_PARAMETERS); for(int i=0; i<parameters.length(); i++) { JSONObject parameter = parameters.getJSONObject(i); String param = parameter.getString(JSON_OBJECT_ERROR_FIELD_PARAM); logMsg.append("param: ").append(param).append("\r\n"); } if (Log.isLoggable(Log.DEBUG)) { Log.debug(TAG_LOG, logMsg.toString()); } // Handle error codes boolean handled = false; if(JSON_ERROR_CODE_PRO_1000.equals(code)) { // Unknown exception in profile handling } else if(JSON_ERROR_CODE_PRO_1001.equals(code)) { // Missing the following mandatory parameter(s). } else if(JSON_ERROR_CODE_PRO_1106.equals(code)) { // The userid is not valid. Only letters (a-z), numbers (0-9), // and periods (.) are allowed. Userid must be less than 16 // characters and include at least one letter. } else if(JSON_ERROR_CODE_PRO_1107.equals(code)) { // You can not specify an existing e-mail address. } else if(JSON_ERROR_CODE_PRO_1113.equals(code)) { // You can not specify an existing username. String msg = localization.getLanguage("signup_failed_username_exists"); signupScreenController.signupFailed(msg); signupScreenController.promptCredentials(); handled = true; } else if(JSON_ERROR_CODE_PRO_1115.equals(code)) { // The password is not valid. Only letters (a-z) and numbers // (0-9) are allowed. Minimum 4, maximum 16 characters. signupScreenController.signupFailed(localization.getLanguage( "signup_failed_bad_password_message")); signupScreenController.promptCredentials(); handled = true; } else if(JSON_ERROR_CODE_PRO_1122.equals(code)) { // The e-mail address is not valid. } else if(JSON_ERROR_CODE_PRO_1126.equals(code) && customization.getDefaultMSUValidationMode() == VALIDATION_MODE_CAPTCHA || customization.getDefaultMSUValidationMode() == VALIDATION_MODE_SMS_CAPTCHA) { // Invalid CAPTCHA token signupScreenController.signupFailed(null, false); signupScreenController.requestNewCaptcha(true); handled = true; } else if(JSON_ERROR_CODE_PRO_1127.equals(code)) { // Phone number already exist String msg = localization.getLanguage("signup_failed_username_exists"); signupScreenController.signupFailed(msg); signupScreenController.promptCredentials(); handled = true; } else if(JSON_ERROR_CODE_PRO_1128.equals(code)) { // The phone number provided is not valid. signupScreenController.signupFailed(localization.getLanguage( "signup_failed_bad_phone_message")); signupScreenController.promptCredentials(); handled = true; } else if(JSON_ERROR_CODE_COM_1006.equals(code)) { // Invalid timezone. } else if(JSON_ERROR_CODE_COM_1008.equals(code)) { // Invalid data type or data format is not as expected. } if(!handled) { Log.error(TAG_LOG, "Unhandled error code: " + code); throw new Exception("Unhandled error code: " + code); } } } catch(JSONException ex) { if (Log.isLoggable(Log.DEBUG)) { Log.debug(TAG_LOG, "Failed to retrieve error json object"); } } } }