/* */ package com.lvlstudios.gtmessage.server; import java.io.IOException; import java.io.Reader; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.jdo.JDOObjectNotFoundException; import javax.jdo.PersistenceManager; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.google.android.c2dm.server.C2DMessaging; import com.google.appengine.api.datastore.Key; import com.google.appengine.api.oauth.OAuthService; import com.google.appengine.api.oauth.OAuthServiceFactory; import com.google.appengine.api.users.User; import com.google.appengine.api.users.UserService; import com.google.appengine.api.users.UserServiceFactory; import com.google.appengine.repackaged.org.json.JSONException; import com.google.appengine.repackaged.org.json.JSONObject; /** * Common code and helpers to handle a request and manipulate device info. * */ public class RequestInfo { private static final Logger log = Logger.getLogger(RequestInfo.class.getName()); private static final String ERROR_STATUS = "ERROR"; private static final String LOGIN_REQUIRED_STATUS = "LOGIN_REQUIRED"; public List<DeviceInfo> devices = new ArrayList<DeviceInfo>(); public String userName; private ServletContext ctx; public String deviceRegistrationID; // Request parameters - transitioning to JSON, but need to support existing // code. Map<String, String[]> parameterMap; JSONObject jsonParams; public boolean isAuth() { return userName != null; } /** * Authenticate the user, check headers and pull the registration data. * * @return null if authentication fails. * @throws IOException */ public static RequestInfo processRequest(HttpServletRequest req, HttpServletResponse resp, ServletContext ctx) throws IOException { // Basic XSRF protection if (req.getHeader("X-Same-Domain") == null) { resp.setStatus(400); resp.getWriter().println(ERROR_STATUS + " (Missing X-Same-Domain header)"); log.warning("Missing X-Same-Domain"); return null; } User user = null; RequestInfo ri = new RequestInfo(); ri.ctx= ctx; OAuthService oauthService = OAuthServiceFactory.getOAuthService(); try { user = oauthService.getCurrentUser(); if (user != null) { ri.userName = user.getEmail(); } } catch (Throwable t) { log.log(Level.INFO, "Non-OAuth request"); user = null; } if (user == null) { // Try ClientLogin UserService userService = UserServiceFactory.getUserService(); user = userService.getCurrentUser(); if (user != null) { ri.userName = user.getEmail(); } } if (req.getContentType().startsWith("application/json")) { Reader reader = req.getReader(); // where is readFully ? char[] tmp = new char[2048]; StringBuffer body = new StringBuffer(); while (true) { int cnt = reader.read(tmp); if (cnt <= 0) { break; } body.append(tmp, 0, cnt); } try { ri.jsonParams = new JSONObject(body.toString()); } catch (JSONException e) { resp.setStatus(500); return null; } } else { ri.parameterMap = req.getParameterMap(); } ri.deviceRegistrationID = ri.getParameter("devregid"); if (ri.deviceRegistrationID != null) { ri.deviceRegistrationID = ri.deviceRegistrationID.trim(); if ("".equals(ri.deviceRegistrationID)) { ri.deviceRegistrationID = null; } } if (ri.userName == null) { resp.setStatus(200); resp.getWriter().println(LOGIN_REQUIRED_STATUS); log.info("Missing user, login required"); return null; } if (ctx != null) { ri.initDevices(ctx); } return ri; } public String getParameter(String name) { if (jsonParams != null) { return jsonParams.optString(name, null); } else { String res[] = parameterMap.get(name); if (res == null || res.length == 0) { return null; } return res[0]; } } /** * Authenticate using the req, fetch devices. */ private RequestInfo() { } @Override public String toString() { return userName + " " + devices.size() + " " + jsonParams; } public RequestInfo(String userN, ServletContext ctx) { this.userName = userN; this.ctx= ctx; if (ctx != null) { initDevices(ctx); } } private void initDevices(ServletContext ctx) { // Context-shared PMF. PersistenceManager pm = C2DMessaging.getPMF(ctx).getPersistenceManager(); try { devices = DeviceInfo.getDeviceInfoForUser(pm, userName); // cleanup for multi-device if (devices.size() > 1) { // Make sure there is no 'bare' registration // Keys are sorted - check the first DeviceInfo first = devices.get(0); Key oldKey = first.getKey(); if (oldKey.toString().indexOf("#") < 0) { log.warning("Removing old-style key " + oldKey.toString()); // multiple devices, first is old-style. devices.remove(0); pm.deletePersistent(first); } } } catch (Exception e) { log.log(Level.WARNING, "Error loading registrations ", e); } finally { pm.close(); } } // We need to iterate again - can be avoided with a query. // delete will fail if the pm is different than the one used to // load the object - we must close the object when we're done public void deleteRegistration(String regId) { if (ctx == null) { return; } PersistenceManager pm = C2DMessaging.getPMF(ctx).getPersistenceManager(); try { List<DeviceInfo> registrations = DeviceInfo.getDeviceInfoForUser(pm, userName); for (int i = 0; i < registrations.size(); i++) { DeviceInfo deviceInfo = registrations.get(i); if (deviceInfo.getDeviceRegistrationID().equals(regId)) { pm.deletePersistent(deviceInfo); // Keep looping in case of duplicates } } } catch (JDOObjectNotFoundException e) { log.warning("User unknown"); } catch (Exception e) { log.warning("Error unregistering device: " + e.getMessage()); } finally { pm.close(); } } }