/** * Copyright 2010 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.waveprotocol.box.server.rpc; import com.google.common.collect.Maps; import com.google.gxp.base.GxpContext; import com.google.inject.Inject; import com.google.inject.name.Named; import org.json.JSONException; import org.json.JSONObject; import org.waveprotocol.box.common.SessionConstants; import org.waveprotocol.box.server.CoreSettings; import org.waveprotocol.box.server.authentication.SessionManager; import org.waveprotocol.box.server.gxp.TopBar; import org.waveprotocol.box.server.gxp.WaveClientPage; import org.waveprotocol.box.server.util.RandomBase64Generator; import org.waveprotocol.wave.client.util.ClientFlagsBase; import org.waveprotocol.wave.common.bootstrap.FlagConstants; import org.waveprotocol.wave.model.wave.ParticipantId; import org.waveprotocol.wave.util.logging.Log; import java.io.IOException; import java.lang.reflect.Method; import java.util.Enumeration; import java.util.HashMap; import javax.inject.Singleton; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * The HTTP servlet for serving a wave client along with content generated on * the server. * * @author kalman@google.com (Benjamin Kalman) */ @SuppressWarnings("serial") @Singleton public class WaveClientServlet extends HttpServlet { private static final Log LOG = Log.get(WaveClientServlet.class); private static final HashMap<String, String> FLAG_MAP = Maps.newHashMap(); static { // __NAME_MAPPING__ is a map of name to obfuscated id for (int i = 0; i < FlagConstants.__NAME_MAPPING__.length; i += 2) { FLAG_MAP.put(FlagConstants.__NAME_MAPPING__[i], FlagConstants.__NAME_MAPPING__[i + 1]); } } private final String domain; private final Boolean useSocketIO; private final SessionManager sessionManager; /** * Creates a servlet for the wave client. */ @Inject public WaveClientServlet( @Named(CoreSettings.WAVE_SERVER_DOMAIN) String domain, @Named(CoreSettings.USE_SOCKETIO) Boolean useSocketIO, SessionManager sessionManager) { this.domain = domain; this.useSocketIO = useSocketIO; this.sessionManager = sessionManager; } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { ParticipantId id = sessionManager.getLoggedInUser(request.getSession(false)); // Eventually, it would be nice to show users who aren't logged in the public waves. // However, public waves aren't implemented yet. For now, we'll just redirect users // who haven't signed in to the sign in page. if (id == null) { // Check if there's a valid waveref specified. String path = request.getQueryString(); response.sendRedirect(sessionManager.getLoginUrl("/")); return; } String username = null; String userDomain = null; if (id != null) { String[] parts = id.getAddress().split("@"); username = parts[0]; userDomain = id.getDomain(); } try { WaveClientPage.write(response.getWriter(), new GxpContext(request.getLocale()), getSessionJson(request.getSession(false)), getClientFlags(request), TopBar.getGxpClosure(username, userDomain), useSocketIO); } catch (IOException e) { LOG.warning("Failed to write GXP for request " + request, e); response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return; } response.setContentType("text/html"); response.setStatus(HttpServletResponse.SC_OK); } private JSONObject getClientFlags(HttpServletRequest request) { try { JSONObject ret = new JSONObject(); Enumeration<?> iter = request.getParameterNames(); while (iter.hasMoreElements()) { String name = (String) iter.nextElement(); String value = request.getParameter(name); if (FLAG_MAP.containsKey(name)) { // Set using the correct type of data in the json using reflection try { Method getter = ClientFlagsBase.class.getMethod(name); Class<?> retType = getter.getReturnType(); if (retType.equals(String.class)) { ret.put(FLAG_MAP.get(name), value); } else if (retType.equals(Integer.class)) { ret.put(FLAG_MAP.get(name), Integer.parseInt(value)); } else if (retType.equals(Boolean.class)) { ret.put(FLAG_MAP.get(name), Boolean.parseBoolean(value)); } else if (retType.equals(Float.class)) { ret.put(FLAG_MAP.get(name), Float.parseFloat(value)); } else if (retType.equals(Double.class)) { ret.put(FLAG_MAP.get(name), Double.parseDouble(value)); } else { // Flag exists, but its type is unknown, so it can not be // properly encoded in JSON. LOG.warning("Ignoring flag [" + name + "] with unknown return type: " + retType); } // Ignore the flag on any exception } catch (SecurityException ex) { } catch (NoSuchMethodException ex) { LOG.warning("Failed to find the flag [" + name + "] in ClientFlagsBase."); } catch (NumberFormatException ex) { } } } return ret; } catch (JSONException ex) { LOG.severe("Failed to create flags JSON"); return new JSONObject(); } } private JSONObject getSessionJson(HttpSession session) { try { ParticipantId user = sessionManager.getLoggedInUser(session); String address = (user != null) ? user.getAddress() : null; // TODO(zdwang): Figure out a proper session id rather than generating a // random number String sessionId = (new RandomBase64Generator()).next(10); return new JSONObject() .put(SessionConstants.DOMAIN, domain) .putOpt(SessionConstants.ADDRESS, address) .putOpt(SessionConstants.ID_SEED, sessionId); } catch (JSONException e) { LOG.severe("Failed to create session JSON"); return new JSONObject(); } } }