/** * Copyright 2010 The University of Nottingham * * This file is part of lobbyservice. * * lobbyservice 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. * * lobbyservice 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 lobbyservice. If not, see <http://www.gnu.org/licenses/>. * */ package uk.ac.horizon.ug.lobby.user; import java.io.BufferedReader; import java.io.IOException; import java.io.Writer; import java.util.List; import java.util.logging.Logger; import javax.persistence.EntityManager; import javax.persistence.EntityTransaction; import javax.persistence.Query; import javax.servlet.http.*; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONWriter; import com.google.appengine.api.datastore.Key; import uk.ac.horizon.ug.lobby.Constants; import uk.ac.horizon.ug.lobby.RequestException; import uk.ac.horizon.ug.lobby.model.Account; import uk.ac.horizon.ug.lobby.model.AccountAuditRecordType; import uk.ac.horizon.ug.lobby.model.AuditRecordLevel; import uk.ac.horizon.ug.lobby.model.EMF; import uk.ac.horizon.ug.lobby.model.GameClient; import uk.ac.horizon.ug.lobby.model.GameClientStatus; import uk.ac.horizon.ug.lobby.model.GameInstance; import uk.ac.horizon.ug.lobby.protocol.ClientManagementRequest; import uk.ac.horizon.ug.lobby.protocol.ClientManagementResponse; import uk.ac.horizon.ug.lobby.protocol.ClientManagementResponseStatus; import uk.ac.horizon.ug.lobby.protocol.GameJoinRequest; import uk.ac.horizon.ug.lobby.protocol.GameJoinRequestType; import uk.ac.horizon.ug.lobby.protocol.JSONUtils; import uk.ac.horizon.ug.lobby.server.AuditUtils; import uk.ac.horizon.ug.lobby.user.AddGameInstanceServlet.GameInstanceInfo; /** * Get all GameClients (user view). * * @author cmg * */ @SuppressWarnings("serial") public class ClientManagementServlet extends HttpServlet implements Constants { static Logger logger = Logger.getLogger(ClientManagementServlet.class.getName()); @Override public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { ClientManagementRequest cmreq = null; try { BufferedReader r = req.getReader(); String line = r.readLine(); if (line==null) { resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "No data supplied"); return; } // why does this seem to read {} ?? //JSONObject json = new JSONObject(req.getReader()); JSONObject json = new JSONObject(line); cmreq = JSONUtils.parseClientManagementRequest(json); } catch (JSONException je) { logger.warning("Bad request: "+je.toString()); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, je.toString()); return; } try { Account account = AccountUtils.getAccount(req); ClientManagementResponse cmresp = handleClientManagement(cmreq, account, req.getRemoteAddr()); logger.info("Handled "+cmreq+" -> "+cmresp); JSONUtils.sendClientManagementResponse(resp, cmresp); }catch (RequestException re) { logger.warning("Error: "+re.getErrorCode()+": "+re.getMessage()); resp.sendError(re.getErrorCode(), re.getMessage()); return; } } private static ClientManagementResponse handleClientManagement( ClientManagementRequest cmreq, Account account, String clientIp) throws RequestException { // validate request String clientId = cmreq.getClientId(); if (clientId==null) throw new RequestException(HttpServletResponse.SC_BAD_REQUEST, "clientId not specified"); GameClientStatus newStatus = cmreq.getNewStatus(); if (newStatus==null) throw new RequestException(HttpServletResponse.SC_BAD_REQUEST, "newStatus not specified"); if (newStatus!=GameClientStatus.TRUSTED && newStatus!=GameClientStatus.BLOCKED) throw new RequestException(HttpServletResponse.SC_BAD_REQUEST, "Cannot change status to "+newStatus); // TODO authenticate client request (clientHmac, clientTime)! ClientManagementResponse cmresp = new ClientManagementResponse(); cmresp.setClientId(clientId); cmresp.setVersion(ClientManagementRequest.VERSION); // get client, ready to update atomically EntityManager em = EMF.get().createEntityManager(); EntityTransaction et = em.getTransaction(); try { // key Key clientKey = GameClient.idToKey(clientId); et.begin(); GameClient gc = em.find(GameClient.class, clientKey); if (gc==null) { cmresp.setStatus(ClientManagementResponseStatus.ERROR_UNKNOWN_CLIENT); cmresp.setMessage("Client is unknown"); return cmresp; } if (gc.getAccountKey()!=null && !gc.getAccountKey().equals(account.getKey())) { cmresp.setStatus(ClientManagementResponseStatus.ERROR_CLIENT_NOT_PERMITTED); cmresp.setMessage("Client is owned by another user"); return cmresp; } else if (gc.getAccountKey()==null) { // fix? gc.setStatus(GameClientStatus.ANONYMOUS); } if (cmreq.getNewStatus()==gc.getStatus()) { logger.info("Client status unchanged ("+gc.getStatus()+")"); // no op cmresp.setStatus(ClientManagementResponseStatus.OK); return cmresp; } logger.info("Client status "+gc.getStatus()+" -> "+cmreq.getNewStatus()); if (gc.getStatus()==GameClientStatus.BLOCKED) { // can't do anything to a BLOCKED client cmresp.setStatus(ClientManagementResponseStatus.ERROR_CLIENT_BLOCKED); cmresp.setMessage("Client is already BLOCKED"); return cmresp; } // update long now = System.currentTimeMillis(); gc.setStatus(cmreq.getNewStatus()); gc.setAccountKey(account.getKey()); if (gc.getStatus()==GameClientStatus.TRUSTED) gc.setTrustedFromTime(now); else if (gc.getStatus()==GameClientStatus.BLOCKED) gc.setTrustedToTime(now); // commit! et.commit(); // audit if (gc.getStatus()==GameClientStatus.TRUSTED) AuditUtils.logAccountAuditRecord(gc.getKey(), account.getKey(), clientIp, now, AccountAuditRecordType.USER_TRUSTED_CLIENT, AuditRecordLevel.NORMAL, /*detailsJson*/"{}", "User trusted client "+clientId); else if (gc.getStatus()==GameClientStatus.BLOCKED) AuditUtils.logAccountAuditRecord(gc.getKey(), account.getKey(), clientIp, now, AccountAuditRecordType.USER_BLOCKED_CLIENT, AuditRecordLevel.NORMAL, /*detailsJson*/"{}", "User trusted client "+clientId); cmresp.setStatus(ClientManagementResponseStatus.OK); return cmresp; } finally { if (et.isActive()) et.rollback(); em.close(); } } }