package de.tud.kom.socom.components.user;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import de.tud.kom.socom.GlobalConfig;
import de.tud.kom.socom.SocomComponent;
import de.tud.kom.socom.components.game.GameInstance;
import de.tud.kom.socom.components.social.SNConnection;
import de.tud.kom.socom.components.social.SocialNetworkManager;
import de.tud.kom.socom.database.HSQLAccess;
import de.tud.kom.socom.database.game.GameDatabase;
import de.tud.kom.socom.database.game.HSQLGameDatabase;
import de.tud.kom.socom.database.user.HSQLUserDatabase;
import de.tud.kom.socom.database.user.HSQLUserGameInfoDatabase;
import de.tud.kom.socom.database.user.UserDatabase;
import de.tud.kom.socom.database.user.UserGameInfoDatabase;
import de.tud.kom.socom.util.EasyEncrypter;
import de.tud.kom.socom.util.JSONUtils;
import de.tud.kom.socom.util.ResourceLoader;
import de.tud.kom.socom.util.SocomRequest;
import de.tud.kom.socom.util.datatypes.JournalEntry;
import de.tud.kom.socom.util.datatypes.SimpleGameContext;
import de.tud.kom.socom.util.datatypes.User;
import de.tud.kom.socom.util.datatypes.UserMetadata;
import de.tud.kom.socom.util.exceptions.ContextNotFoundException;
import de.tud.kom.socom.util.exceptions.CurrentContextNotFoundException;
import de.tud.kom.socom.util.exceptions.CurrentGameInstanceNotIncludedException;
import de.tud.kom.socom.util.exceptions.SocomException;
import de.tud.kom.socom.util.exceptions.IllegalParameterException;
import de.tud.kom.socom.util.exceptions.ParseException;
import de.tud.kom.socom.util.exceptions.UIDNotIncludedException;
import de.tud.kom.socom.util.exceptions.UserNotFoundException;
import de.tud.kom.socom.util.playerstate.ObservedUIDs;
/**
*
* @author rhaban
*
*/
public class UserManager extends SocomComponent {
/*
* first character letter
* min 4 characters, max 15
* only letters, numbers, underscore
*/
private static final String PASSWORD_REGEX = "^[a-zA-Z]\\w{3,14}$";
private static final String URL_PATTERN = "user";
private static UserManager instance = new UserManager();
private UserDatabase udb;
private UserGameInfoDatabase uidb;
private GameDatabase gamedb;
private UserManager() {
this.udb = HSQLUserDatabase.getInstance();
this.uidb = HSQLUserGameInfoDatabase.getInstance();
this.gamedb = HSQLGameDatabase.getInstance();
}
public static UserManager getInstance() {
return instance;
}
/**
* Shows information about the current user or the user with the given id
* (iff the profile is visible)
*
* @param uid
* in session
* @param Optional
* : id (long) socom id of the user to show
* @return Userinformation
* @throws SQLException
* @throws JSONException
* @throws SocomException
*/
public int getUser(SocomRequest req) throws SQLException, JSONException, SocomException {
long uid = req.getUid();
long profileId;
try {
profileId = Long.parseLong(req.getParam("id"));
} catch (IllegalParameterException e) {
profileId = uid;
}
User user = udb.fetchUser(uid, profileId);
if (user == null)
throw new UserNotFoundException(profileId);
req.addOutput(user.toJSONString());
return 0;
}
/**
* Loggs a user in. Starts a new session with his id.
*
* @param username
* Name of the user
* @param password
* User's password
* @param game
* Name of the current game
* @param version
* Current game's version
* @param gamepassword
* Game's Password
* @return The users id.
* @throws SQLException
* @throws JSONException
*/
public int loginUser(SocomRequest req) throws SQLException, JSONException, SocomException {
// User already logged id?
try {
req.getUid();
return 11;
} catch (UIDNotIncludedException e) {
}
String gamename = req.getParam("game");
String gameversion = req.getParam("version");
String gamepassword = req.getParam("gamepassword");
String username = req.getParam("username");
String password = req.getParam("password");
long gameinstanceid = gamedb.authenticateGameInstance(gamename, gameversion, gamepassword);
password = EasyEncrypter.getSHA(password);
long[] ids = udb.validateUser(gameinstanceid, username, password);
udb.updateUsersGame(ids[0], gameinstanceid);
ObservedUIDs.getInstance().setOnline(ids[0]);
req.setUid(ids[0]);
req.setCurrentGameInst(ids[1]);
req.addOutput(JSONUtils.JSONToString(new JSONObject().put("uid", ids[0])));
return 0;
}
/**
* Logs the current user out. Does not delete anything except the session..
*
* @param uid
* in session
* @return success boolean
* @throws JSONException
*/
public int logout(SocomRequest req) throws SQLException, JSONException, SocomException {
udb.setUserOffline(req.getUid());
udb.updateUsersGame(req.getUid(), req.getCurrentGameInst());
req.removeUid();
req.removeGameInst();
req.addOutput(JSONUtils.getSuccessJsonString());
return 0;
}
/**
* Creates a new user. After this the user is logged in and bound to the
* current game instance.
*
* @param username
* @param password
* @param game
* @param version
* Game's version
* @param gamepassword
* @param visibility
* (int, 0 = private, 1 = friends, 2 = public)
* @return uid of the new user
* @throws SQLException
* @throws JSONException
*/
public int createUser(SocomRequest req) throws SQLException, JSONException, SocomException {
// User already logged id?
try {
req.getUid();
return 11;
} catch (UIDNotIncludedException e) {
}
String username = req.getParam("username");
if (username.startsWith("GeneratedUser")) // forbidden username
throw new IllegalParameterException("username must not start with GeneratedUser..");
String password = req.getParam("password");
if(!password.matches(PASSWORD_REGEX))
throw new IllegalParameterException("password",
"Password must start with letter, have at least 4 " +
"and at maximum 14 characters " +
"and contain at least one number");
password = EasyEncrypter.getSHA(password);
String gamename = req.getParam("game");
String gameversion = req.getParam("version");
String gamepassword = req.getParam("gamepassword");
int visibility = Integer.parseInt(req.getParam("visibility"));
long gameinstanceid = gamedb.authenticateGameInstance(gamename, gameversion, gamepassword);
long[] ids = udb.createUser(gameinstanceid, username, password, visibility);
udb.updateUsersGame(ids[0], gameinstanceid);
req.setUid(ids[0]);
req.setCurrentGameInst(ids[1]);
req.addOutput(JSONUtils.JSONToString(new JSONObject().put("uid", ids[0])));
return 0;
}
public int changeUser(SocomRequest req) throws SQLException, JSONException, SocomException {
// User already logged id?
long uid = req.getUid();
String username = req.getParam("username", null);
if (username != null && username.startsWith("GeneratedUser")) // forbidden username
throw new IllegalParameterException("username must not start with GeneratedUser..");
String password = req.getParam("password");
int visibility = req.getParam("visibility", -1);
if(username != null)
{
udb.changeUsername(uid, password, username);
}
if(visibility != -1)
{
udb.changeVisibility(uid, password, visibility);
}
req.addOutput(JSONUtils.getSuccessJsonString());
return 0;
}
/**
* Generates a user and returns a url to login via facebook.
*
* @param game
* @param version
* @param gamepassword
* @param visibility
* (int, 0 = private, 1 = friends, 2 = public)
* @param network
* (Facebook, VZNet, Google+) where the user wants to login with
* @return uid of the new user, his username and password, a loginUrl
* leading to the social network login
* @throws SQLException
* @throws JSONException
*/
public int createUserWithSocialNetwork(SocomRequest req) throws SQLException, JSONException, SocomException {
try {
req.getUid();
return 11;
} catch (UIDNotIncludedException e) {
}
String gamename = req.getParam("game");
String version = req.getParam("version");
String gamepassword = req.getParam("gamepassword");
long gameinstanceid = gamedb.authenticateGameInstance(gamename, version, gamepassword);
int visibility = Integer.parseInt(req.getParam("visibility"));
String network = req.getParam("network");
SNConnection nwconn = SocialNetworkManager.getInstance().getConnection(network, gameinstanceid);
if (nwconn == null)
return 4;
String username = udb.getNextGeneratableUserName();
String password = EasyEncrypter.getInstance().getRandomPassword();
String passwordHash = EasyEncrypter.getSHA(password);
long[] ids = udb.createUser(gameinstanceid, username, passwordHash, visibility);
long uid = ids[0];
String loginUrl = nwconn.getLoginURL(uid, gameinstanceid);
udb.updateUsersGame(uid, gameinstanceid);
req.setUid(uid);
req.setCurrentGameInst(ids[1]);
req.addOutput(JSONUtils.JSONToString(new JSONObject().put("uid", uid).put("username", username).put("password", password).put("loginUrl", loginUrl)));
return 0;
}
/**
* Deletes a user. (Can be re-activated in the web-application)
*
* @param password User's password
* @return success boolean
* @throws SQLException
* @throws JSONException
*/
public int deleteUser(SocomRequest req) throws SQLException, JSONException, SocomException {
long uid = req.getUid();
String password = req.getParam("password");
password = EasyEncrypter.getSHA(password);
boolean success = udb.deleteUser(uid, password);
logout(req);
req.setOutput(JSONUtils.getSuccessJsonString(success));
return 0;
}
/**
* Sets the current user as admin
* @param password User's password
* @param mastersecret
* @return success boolean
* @throws SQLException
* @throws JSONException
*/
public int becomeAdmin(SocomRequest req) throws SQLException, JSONException, SocomException {
if (req.getParam("mastersecret").equals(ResourceLoader.getResource("mastersecret"))) {
long uid = req.getUid();
String password = req.getParam("password");
password = EasyEncrypter.getSHA(password);
udb.becomeAdmin(uid, password);
req.addOutput(JSONUtils.getSuccessJsonString());
} else
return 19; // illegal access
return 0;
}
/**
* Shows a list of the users game instances he is/was playing
*
* @throws SQLException
* @throws JSONException
*/
public int getUsersGames(SocomRequest req) throws SQLException, JSONException, SocomException {
long uid = req.getUid();
List<GameInstance> result = udb.getUsersGames(uid);
req.addOutput(JSONUtils.JSONToString(new JSONObject().put("games", new JSONArray(result))));
return 0;
}
/**
* Sets the current context for the player (contexts may be: scenes, level, mapparts, ..)
*
* If the context does not exist it will be created, furthermore a relation between two contexts will be created
*
* @param context External ID of the current context
* @param isNewGame true if the game is currently restarted to
* prevent autogenerated relations to beginning scene (default: false)
* @return success boolean
* @throws SQLException
* @throws JSONException
*/
public int setCurrentContext(SocomRequest req) throws SQLException, JSONException, SocomException {
long uid = req.getUid();
String context = req.getParam("context");
boolean isNewGame = req.getParam("isNewGame", false);
long gameInstance = req.getCurrentGameInst();
long contextId = -1L;
long lastContextId = -1L;
while (true) {
try {
// get new context
contextId = gamedb.getGameContextId(context, gameInstance);
// save last context
lastContextId = uidb.getCurrentContext(uid, gameInstance);
// autogenerate relation (if it doesn't already exist, and its not game restart)
if (lastContextId != contextId && !isNewGame)
gamedb.addContextRelation(gameInstance, lastContextId, contextId, true);
// go further
break;
} catch (ContextNotFoundException e) {
// if the new context does not exist -> autogenerate it and use given id
context = gamedb.autogenerateContext(gameInstance, context);
// try again
continue;
} catch (CurrentContextNotFoundException e) {
// current context not present -> skip autogen relation
break;
}
}
uidb.setCurrentContext(uid, lastContextId, contextId);
req.addOutput(JSONUtils.getSuccessJsonString());
return 0;
}
/**
* Show all contexts the user ever visited.
*
* @return List of contexts
* @throws SQLException
* @throws JSONException
*/
public int getVisitedContexts(SocomRequest req) throws SQLException, JSONException, SocomException {
long uid = req.getUid();
long gameInst = req.getCurrentGameInst();
List<SimpleGameContext> contexts = uidb.getVisitedContexts(uid, gameInst);
req.addOutput(JSONUtils.JSONToString(new JSONObject().put("contexts", contexts)));
return 0;
}
/**
* Adds a gamelog (mostly debug-msgs for game-developers)
*
* @param type
* @param message
* @throws SQLException
* @throws JSONException
* @throws SocomException
*/
public int addLog(SocomRequest req) throws SQLException, JSONException, SocomException {
int visibility = GlobalConfig.VISIBILITY_NON_USER;
return createLog(req, visibility);
}
/**
* Adds a journal entry for the user
*
* @param type
* @param message
* @param Optional: visibility
* @throws SQLException
* @throws JSONException
* @throws IllegalAccessException
*/
public int addJournalEntry(SocomRequest req) throws SQLException, JSONException, SocomException {
int visibility = GlobalConfig.VISIBILITY_PUBLIC;
if (req.getParams().containsKey("visibility")) {
visibility = Integer.parseInt(req.getParam("visibility"));
}
return createLog(req, visibility);
}
private int createLog(SocomRequest req, int visibility) throws SocomException, SQLException, JSONException {
long uid = req.getUid();
long currentGameInst = req.getCurrentGameInst();
String type = req.getParam("type").toUpperCase();
String message = req.getParam("message");
uidb.addJournalEntry(uid, currentGameInst, new JournalEntry(type, message, visibility));
req.addOutput(JSONUtils.getSuccessJsonString());
return 0;
}
/**
* Shows all Journal Entries for the current user.
*
* @param Optional: limit (max. entries to show)
* @param Optional: offset (sql-like limit & offset)
* @param Optional: type Which types should only be shown
* @return List of Journal-Entries
* @throws SQLException
* @throws JSONException
*/
public int getJournalEntries(SocomRequest req) throws SQLException, JSONException, SocomException {
boolean gameLogs = false;
return getLogs(req, gameLogs);
}
/**
* Shows all GameLogs for the current user (only accessible for the game itself)
*
* @param gamepassword
* @param Optional: limit (max. entries to show)
* @param Optional: offset (sql-like limit & offset)
* @param Optional: type Which types should only be shown
* @throws SocomException
* @throws SQLException
* @throws JSONException
*/
public int getLogs(SocomRequest req) throws SocomException, SQLException, JSONException{
String gamepassword = req.getParam("gamepassword");
long gameinstid = req.getCurrentGameInst();
HSQLGameDatabase.getInstance().authenticateGameInstance(gameinstid, gamepassword);
boolean gameLogs = true;
return getLogs(req, gameLogs);
}
private int getLogs(SocomRequest req, boolean gameLogs) throws UIDNotIncludedException,
CurrentGameInstanceNotIncludedException, ParseException, SQLException, JSONException,
IllegalParameterException {
String debugParamString = "limit";
try {
long uid = req.getUid();
long gameinstid = req.getCurrentGameInst();
Map<String, String> params = req.getParams();
String limitParam = params.get("limit");
String offsetParam = params.get("offset");
String type = params.get("type");
int limit = limitParam == null ? 0 : Integer.parseInt(limitParam);
debugParamString = "offset";
int offset = offsetParam == null ? 0 : Integer.parseInt(offsetParam);
if (limit < 0 || offset < 0)
return 4;
if (type == null)
type = "all";
else
type = type.toUpperCase();
List<JournalEntry> logs = uidb.getUserJournal(uid, gameinstid, limit, offset, type, gameLogs);
req.addOutput(JSONUtils.JSONToString(new JSONObject().put("logs", logs)));
} catch (NumberFormatException e) {
throw new IllegalParameterException(debugParamString + " must be of type integer.");
}
return 0;
}
/**
* Adds time (in seconds) to the 'already played time' for the player in his current context
*
* @param time in s
* @return success boolean
* @throws SQLException
* @throws JSONException
*/
public int addTimePlayed(SocomRequest req) throws SQLException, JSONException, SocomException {
try {
long uid = req.getUid();
long currentGameInst = req.getCurrentGameInst();
long contextId = uidb.getCurrentContext(uid, currentGameInst);
long time = Long.parseLong(req.getParam("time"));
uidb.addTimePlayed(uid, contextId, time);
req.addOutput(JSONUtils.getSuccessJsonString());
} catch (NumberFormatException e) {
throw new IllegalParameterException("time must be of type long.");
}
return 0;
}
/**
* Resets the 'time already played' (=0) for the player in his current context
*
* @return success boolean
* @throws SQLException
* @throws JSONException
*/
public int resetTimePlayed(SocomRequest req) throws SQLException, JSONException, SocomException {
long uid = req.getUid();
long currentGameInst = req.getCurrentGameInst();
long contextId = uidb.getCurrentContext(uid, currentGameInst);
uidb.setTimePlayed(uid, contextId, 0);
req.addOutput(JSONUtils.getSuccessJsonString());
return 0;
}
/**
* Sets the 'already played time' (in seconds) for the player in his current context
*
* @param time in s
* @return success boolean
* @throws SQLException
* @throws JSONException
*/
public int setTimePlayed(SocomRequest req) throws SQLException, JSONException, SocomException {
try {
long uid = req.getUid();
long currentGameInst = req.getCurrentGameInst();
long contextId = uidb.getCurrentContext(uid, currentGameInst);
long time = Long.parseLong(req.getParam("time"));
uidb.setTimePlayed(uid, contextId, time);
req.addOutput(JSONUtils.getSuccessJsonString());
} catch (NumberFormatException e) {
throw new IllegalParameterException("time must be of type long.");
}
return 0;
}
/**
* Show the time played in the current context
*
* @return time in s
* @throws SQLException
* @throws JSONException
*/
public int getTimePlayed(SocomRequest req) throws SQLException, JSONException, SocomException {
long uid = req.getUid();
long currentGameInst = req.getCurrentGameInst();
long contextId = uidb.getCurrentContext(uid, currentGameInst);
long timeplayed = uidb.getTimePlayed(uid, contextId);
req.addOutput(JSONUtils.JSONToString(new JSONObject().put("timeplayed", timeplayed)));
return 0;
}
/**
* Creates a new Metadata key-value-Pair for the user
* The Metadata have public visibility if nothing else is defined
*
* @param key
* @param value
* @param Optional: visibility (Integer) (if not specified its 0 (private)
* @return success boolean
* @throws SQLException
* @throws JSONException
*/
public int createMetadata(SocomRequest req) throws SQLException, JSONException, SocomException {
long uid = req.getUid();
String key = req.getParam("key");
String value = req.getParam("value");
int visibility = GlobalConfig.VISIBILITY_PRIVATE;
if(req.containsParam("visibility"))
visibility = Integer.parseInt(req.getParam("visibility"));
udb.createMetadata(uid, key, value, visibility);
req.addOutput(JSONUtils.getSuccessJsonString());
return 0;
}
/**
* Changes an existing Metadata
*
* @param key Existing key
* @param value
* @param Optional: visibility (Integer)
* @return success boolean
* @throws SQLException
* @throws JSONException
*/
public int updateMetadata(SocomRequest req) throws SQLException, JSONException, SocomException {
long uid = req.getUid();
String key = req.getParam("key");
String value = req.getParam("value");
int visibility = -1;
if(req.containsParam("visibility"))
visibility = Integer.parseInt(req.getParam("visibility"));
udb.updateMetadata(uid, key, value, visibility);
req.addOutput(JSONUtils.getSuccessJsonString());
return 0;
}
/**
* Delete an existing Metadata
*
* @param key Existing metadata-key
* @param Optional: deleted The deleted value
* @return success boolean
* @throws SQLException
* @throws JSONException
*/
public int deleteMetadata(SocomRequest req) throws SQLException, JSONException, SocomException {
long uid = req.getUid();
String key = req.getParam("key");
int deleted = 1;
if (req.getParams().containsKey("deleted"))
deleted = Integer.parseInt(req.getParam("deleted"));
udb.deleteMetadata(uid, key, deleted);
req.addOutput(JSONUtils.getSuccessJsonString());
return 0;
}
/**
* Shows existing, visible Metadata
*
* @return The metadata
* @throws SQLException
* @throws JSONException
*/
public int getMetadata(SocomRequest req) throws SQLException, JSONException, SocomException {
long currentUid = req.getUid();
long ofUid = req.containsParam("of") ? Long.parseLong(req.getParam("of")) : currentUid;
List<UserMetadata> result = udb.fetchMetadata(currentUid, ofUid);
req.addOutput(JSONUtils.JSONToString(new JSONObject().put("data", result)));
return 0;
}
/**
* Changes the username
* @param username The new usersname the user wishes to have
* @param password the actual password to authenticate again
* @return success boolean
* @throws JSONException
*/
public int changeUsername(SocomRequest req) throws SocomException, SQLException, JSONException {
long uid = req.getUid();
String newUsername = req.getParam("username");
String password = req.getParam("password");
password = EasyEncrypter.getSHA(password);
udb.changeUsername(uid, password, newUsername);
req.addOutput(JSONUtils.getSuccessJsonString());
return 0;
}
/**
* Changes the users password
* @param password the actual password to authenticate again
* @param newPassword the new password the users wishes to have
* @return success boolean
* @throws JSONException
*/
public int changeUserPassword(SocomRequest req) throws SocomException, SQLException, JSONException {
long uid = req.getUid();
String password = req.getParam("password");
String newPassword = req.getParam("newpassword");
if(!newPassword.matches(PASSWORD_REGEX))
throw new IllegalParameterException("newpassword",
"Password must start with letter, have at least 4 " +
"and at maximum 14 characters " +
"and contain at least one number");
password = EasyEncrypter.getSHA(password);
newPassword = EasyEncrypter.getSHA(newPassword);
udb.changePassword(uid, password, newPassword);
req.addOutput(JSONUtils.getSuccessJsonString());
return 0;
}
/*
* hacked in method to stop socom (when someone forgets to shutdown the server ;) )
*/
public int exit(SocomRequest req) throws SocomException, SQLException {
if (req.getParam("mastersecret").equals(ResourceLoader.getResource("mastersecret")))
{
HSQLAccess.getInstance().execQuery("SHUTDOWN");
System.exit(0);
return 0;
}
return 19; // illegal access
}
/**
* URL PATTERN IS "user"
*/
@Override
public String getUrlPattern() {
return URL_PATTERN;
}
}