/*
*/
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();
}
}
}