blob: 19a72a1088cc9b3409cae129568e93d2b92d3115 [file] [log] [blame]
// Copyright (C) 2008 The Android Open Source Project
//
// 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 com.google.gerrit.server;
import com.google.gerrit.client.Link;
import com.google.gerrit.client.reviewdb.Account;
import com.google.gerrit.client.reviewdb.Change;
import com.google.gerrit.client.reviewdb.PatchSet;
import com.google.gerrit.client.reviewdb.RevId;
import com.google.gerrit.client.reviewdb.ReviewDb;
import com.google.gerrit.client.rpc.Common;
import com.google.gwtjsonrpc.server.XsrfException;
import com.google.gwtorm.client.OrmException;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/** Rewrites Gerrit 1 style URLs to Gerrit 2 style URLs. */
public class UrlRewriteFilter implements Filter {
private static final Pattern CHANGE_ID = Pattern.compile("^/(\\d+)/?$");
private static final Pattern REV_ID =
Pattern.compile("^/r/([0-9a-fA-F]{4," + RevId.LEN + "})/?$");
private static final Pattern USER_PAGE = Pattern.compile("^/user/(.*)/?$");
private static final Map<String, String> staticLinks;
private static final Set<String> staticExtensions;
static {
staticLinks = new HashMap<String, String>();
staticLinks.put("", "");
staticLinks.put("/", "");
staticLinks.put("/mine", Link.MINE);
staticLinks.put("/starred", Link.MINE_STARRED);
staticLinks.put("/settings", Link.SETTINGS);
staticLinks.put("/all_unclaimed", Link.ALL_UNCLAIMED);
staticLinks.put("/all", Link.ALL_MERGED);
staticLinks.put("/open", Link.ALL_OPEN);
staticExtensions = new HashSet<String>();
staticExtensions.add(".css");
staticExtensions.add(".gif");
staticExtensions.add(".html");
staticExtensions.add(".js");
staticExtensions.add(".png");
}
private FilterConfig config;
public void init(final FilterConfig config) throws ServletException {
this.config = config;
try {
GerritServer.getInstance();
} catch (OrmException e) {
throw new ServletException("Cannot initialize GerritServer", e);
} catch (XsrfException e) {
throw new ServletException("Cannot initialize GerritServer", e);
}
}
public void destroy() {
}
public void doFilter(final ServletRequest sreq, final ServletResponse srsp,
final FilterChain chain) throws IOException, ServletException {
final HttpServletRequest req = (HttpServletRequest) sreq;
final HttpServletResponse rsp = (HttpServletResponse) srsp;
final String pathInfo = pathInfo(req);
if (pathInfo.startsWith("/gerrit/rpc/")) {
// RPC requests are very common in Gerrit 2, we want to make sure
// they run quickly by jumping through the chain as fast as we can.
//
chain.doFilter(req, rsp);
} else if (staticExtension(pathInfo, req, rsp, chain)) {
} else if (staticLink(pathInfo, req, rsp)) {
} else if (bareChangeId(pathInfo, req, rsp)) {
} else if (bareRevisionId(pathInfo, req, rsp)) {
} else if (bareUserEmailDashboard(pathInfo, req, rsp)) {
} else {
// Anything else is either a static resource request (which the container
// can do for us) or is going to be a 404 error when the container cannot
// find the resource. Either form of request is not very common compared
// to the above cases.
//
chain.doFilter(req, rsp);
}
}
private static boolean staticExtension(final String pathInfo,
final HttpServletRequest req, final HttpServletResponse rsp,
final FilterChain chain) throws IOException, ServletException {
final int d = pathInfo.lastIndexOf('.');
if (d > 0 && staticExtensions.contains(pathInfo.substring(d + 1))) {
// Any URL which ends in this static extension is meant to be handled
// by the servlet container, by returning a resource from the WAR.
// We don't need to evaluate it any further.
//
chain.doFilter(req, rsp);
return true;
}
return false;
}
private static boolean staticLink(final String pathInfo,
final HttpServletRequest req, final HttpServletResponse rsp)
throws IOException {
final String newLink = staticLinks.get(pathInfo);
if (newLink == null) {
return false;
}
// A static link (one with no parameters).
//
final StringBuffer url = toGerrit(req);
if (newLink.length() > 0) {
url.append('#');
url.append(newLink);
}
rsp.sendRedirect(url.toString());
return true;
}
private static boolean bareChangeId(final String pathInfo,
final HttpServletRequest req, final HttpServletResponse rsp)
throws IOException {
final Matcher m = CHANGE_ID.matcher(pathInfo);
if (!m.matches()) {
return false;
}
final Change.Id id = Change.Id.parse(m.group(1));
final StringBuffer url = toGerrit(req);
url.append('#');
url.append(Link.toChange(id));
rsp.sendRedirect(url.toString());
return true;
}
private boolean bareRevisionId(final String pathInfo,
final HttpServletRequest req, final HttpServletResponse rsp)
throws IOException {
final Matcher m = REV_ID.matcher(pathInfo);
if (!m.matches()) {
return false;
}
final String rev = m.group(1).toLowerCase();
if (rev.length() > RevId.LEN) {
rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
return true;
}
final RevId id = new RevId(rev);
final List<PatchSet> patches;
try {
final ReviewDb db = Common.getSchemaFactory().open();
try {
if (id.isComplete()) {
patches = db.patchSets().byRevision(id).toList();
} else {
patches = db.patchSets().byRevisionRange(id, id.max()).toList();
}
} finally {
db.close();
}
} catch (OrmException e) {
config.getServletContext().log("Unable to query for " + rev, e);
rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return true;
}
if (patches.size() == 0) {
rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
} else if (patches.size() == 1) {
final StringBuffer url = toGerrit(req);
url.append('#');
url.append(Link.toChange(patches.get(0).getId().getParentKey()));
rsp.sendRedirect(url.toString());
} else {
// TODO Someday this should be a menu of choices.
rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
return true;
}
private boolean bareUserEmailDashboard(final String pathInfo,
final HttpServletRequest req, final HttpServletResponse rsp)
throws IOException {
final Matcher m = USER_PAGE.matcher(pathInfo);
if (!m.matches()) {
return false;
}
final String email = cleanEmail(m.group(1));
final List<Account> people;
try {
final ReviewDb db = Common.getSchemaFactory().open();
try {
people = db.accounts().byPreferredEmail(email).toList();
} finally {
db.close();
}
} catch (OrmException e) {
config.getServletContext().log("Unable to query for " + email, e);
rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return true;
}
if (people.size() == 0) {
rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
} else if (people.size() == 1) {
final StringBuffer url = toGerrit(req);
url.append('#');
url.append(Link.toAccountDashboard(people.get(0).getId()));
rsp.sendRedirect(url.toString());
} else {
// TODO Someday this should be a menu of choices.
rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
return true;
}
private static String cleanEmail(final String email) {
int dc = email.indexOf(",,");
if (dc >= 0) {
return email.substring(0, dc) + "@" + email.substring(dc + 2);
}
dc = email.indexOf(',');
if (dc >= 0) {
return email.substring(0, dc) + "@" + email.substring(dc + 1);
}
return email;
}
private static String pathInfo(final HttpServletRequest req) {
final String uri = req.getRequestURI();
final String ctx = req.getContextPath();
return uri.startsWith(ctx) ? uri.substring(ctx.length()) : uri;
}
private static StringBuffer toGerrit(final HttpServletRequest req) {
final StringBuffer url = new StringBuffer();
url.append(req.getContextPath());
url.append("/Gerrit");
return url;
}
}