+++ /dev/null
-/*\r
- * Copyright 2011 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.io.IOException;\r
-import java.text.MessageFormat;\r
-\r
-import javax.servlet.FilterChain;\r
-import javax.servlet.ServletException;\r
-import javax.servlet.ServletRequest;\r
-import javax.servlet.ServletResponse;\r
-import javax.servlet.http.HttpServletRequest;\r
-import javax.servlet.http.HttpServletResponse;\r
-\r
-import com.gitblit.manager.IRepositoryManager;\r
-import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.manager.ISessionManager;\r
-import com.gitblit.models.RepositoryModel;\r
-import com.gitblit.models.UserModel;\r
-import com.gitblit.utils.StringUtils;\r
-\r
-/**\r
- * The AccessRestrictionFilter is an AuthenticationFilter that confirms that the\r
- * requested repository can be accessed by the anonymous or named user.\r
- *\r
- * The filter extracts the name of the repository from the url and determines if\r
- * the requested action for the repository requires a Basic authentication\r
- * prompt. If authentication is required and no credentials are stored in the\r
- * "Authorization" header, then a basic authentication challenge is issued.\r
- *\r
- * http://en.wikipedia.org/wiki/Basic_access_authentication\r
- *\r
- * @author James Moger\r
- *\r
- */\r
-public abstract class AccessRestrictionFilter extends AuthenticationFilter {\r
-\r
- protected final IRuntimeManager runtimeManager;\r
-\r
- protected final IRepositoryManager repositoryManager;\r
-\r
- protected AccessRestrictionFilter(\r
- IRuntimeManager runtimeManager,\r
- ISessionManager sessionManager,\r
- IRepositoryManager repositoryManager) {\r
- super(sessionManager);\r
- this.runtimeManager = runtimeManager;\r
- this.repositoryManager = repositoryManager;\r
- }\r
-\r
- /**\r
- * Extract the repository name from the url.\r
- *\r
- * @param url\r
- * @return repository name\r
- */\r
- protected abstract String extractRepositoryName(String url);\r
-\r
- /**\r
- * Analyze the url and returns the action of the request.\r
- *\r
- * @param url\r
- * @return action of the request\r
- */\r
- protected abstract String getUrlRequestAction(String url);\r
-\r
- /**\r
- * Determine if a non-existing repository can be created using this filter.\r
- *\r
- * @return true if the filter allows repository creation\r
- */\r
- protected abstract boolean isCreationAllowed();\r
-\r
- /**\r
- * Determine if the action may be executed on the repository.\r
- *\r
- * @param repository\r
- * @param action\r
- * @return true if the action may be performed\r
- */\r
- protected abstract boolean isActionAllowed(RepositoryModel repository, String action);\r
-\r
- /**\r
- * Determine if the repository requires authentication.\r
- *\r
- * @param repository\r
- * @param action\r
- * @return true if authentication required\r
- */\r
- protected abstract boolean requiresAuthentication(RepositoryModel repository, String action);\r
-\r
- /**\r
- * Determine if the user can access the repository and perform the specified\r
- * action.\r
- *\r
- * @param repository\r
- * @param user\r
- * @param action\r
- * @return true if user may execute the action on the repository\r
- */\r
- protected abstract boolean canAccess(RepositoryModel repository, UserModel user, String action);\r
-\r
- /**\r
- * Allows a filter to create a repository, if one does not exist.\r
- *\r
- * @param user\r
- * @param repository\r
- * @param action\r
- * @return the repository model, if it is created, null otherwise\r
- */\r
- protected RepositoryModel createRepository(UserModel user, String repository, String action) {\r
- return null;\r
- }\r
-\r
- /**\r
- * doFilter does the actual work of preprocessing the request to ensure that\r
- * the user may proceed.\r
- *\r
- * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,\r
- * javax.servlet.ServletResponse, javax.servlet.FilterChain)\r
- */\r
- @Override\r
- public void doFilter(final ServletRequest request, final ServletResponse response,\r
- final FilterChain chain) throws IOException, ServletException {\r
-\r
- HttpServletRequest httpRequest = (HttpServletRequest) request;\r
- HttpServletResponse httpResponse = (HttpServletResponse) response;\r
-\r
- String fullUrl = getFullUrl(httpRequest);\r
- String repository = extractRepositoryName(fullUrl);\r
-\r
- if (repositoryManager.isCollectingGarbage(repository)) {\r
- logger.info(MessageFormat.format("ARF: Rejecting request for {0}, busy collecting garbage!", repository));\r
- httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);\r
- return;\r
- }\r
-\r
- // Determine if the request URL is restricted\r
- String fullSuffix = fullUrl.substring(repository.length());\r
- String urlRequestType = getUrlRequestAction(fullSuffix);\r
-\r
- UserModel user = getUser(httpRequest);\r
-\r
- // Load the repository model\r
- RepositoryModel model = repositoryManager.getRepositoryModel(repository);\r
- if (model == null) {\r
- if (isCreationAllowed()) {\r
- if (user == null) {\r
- // challenge client to provide credentials for creation. send 401.\r
- if (runtimeManager.isDebugMode()) {\r
- logger.info(MessageFormat.format("ARF: CREATE CHALLENGE {0}", fullUrl));\r
- }\r
- httpResponse.setHeader("WWW-Authenticate", CHALLENGE);\r
- httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);\r
- return;\r
- } else {\r
- // see if we can create a repository for this request\r
- model = createRepository(user, repository, urlRequestType);\r
- }\r
- }\r
-\r
- if (model == null) {\r
- // repository not found. send 404.\r
- logger.info(MessageFormat.format("ARF: {0} ({1})", fullUrl,\r
- HttpServletResponse.SC_NOT_FOUND));\r
- httpResponse.sendError(HttpServletResponse.SC_NOT_FOUND);\r
- return;\r
- }\r
- }\r
-\r
- // Confirm that the action may be executed on the repository\r
- if (!isActionAllowed(model, urlRequestType)) {\r
- logger.info(MessageFormat.format("ARF: action {0} on {1} forbidden ({2})",\r
- urlRequestType, model, HttpServletResponse.SC_FORBIDDEN));\r
- httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);\r
- return;\r
- }\r
-\r
- // Wrap the HttpServletRequest with the AccessRestrictionRequest which\r
- // overrides the servlet container user principal methods.\r
- // JGit requires either:\r
- //\r
- // 1. servlet container authenticated user\r
- // 2. http.receivepack = true in each repository's config\r
- //\r
- // Gitblit must conditionally authenticate users per-repository so just\r
- // enabling http.receivepack is insufficient.\r
- AuthenticatedRequest authenticatedRequest = new AuthenticatedRequest(httpRequest);\r
- if (user != null) {\r
- authenticatedRequest.setUser(user);\r
- }\r
-\r
- // BASIC authentication challenge and response processing\r
- if (!StringUtils.isEmpty(urlRequestType) && requiresAuthentication(model, urlRequestType)) {\r
- if (user == null) {\r
- // challenge client to provide credentials. send 401.\r
- if (runtimeManager.isDebugMode()) {\r
- logger.info(MessageFormat.format("ARF: CHALLENGE {0}", fullUrl));\r
- }\r
- httpResponse.setHeader("WWW-Authenticate", CHALLENGE);\r
- httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);\r
- return;\r
- } else {\r
- // check user access for request\r
- if (user.canAdmin() || canAccess(model, user, urlRequestType)) {\r
- // authenticated request permitted.\r
- // pass processing to the restricted servlet.\r
- newSession(authenticatedRequest, httpResponse);\r
- logger.info(MessageFormat.format("ARF: {0} ({1}) authenticated", fullUrl,\r
- HttpServletResponse.SC_CONTINUE));\r
- chain.doFilter(authenticatedRequest, httpResponse);\r
- return;\r
- }\r
- // valid user, but not for requested access. send 403.\r
- if (runtimeManager.isDebugMode()) {\r
- logger.info(MessageFormat.format("ARF: {0} forbidden to access {1}",\r
- user.username, fullUrl));\r
- }\r
- httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);\r
- return;\r
- }\r
- }\r
-\r
- if (runtimeManager.isDebugMode()) {\r
- logger.info(MessageFormat.format("ARF: {0} ({1}) unauthenticated", fullUrl,\r
- HttpServletResponse.SC_CONTINUE));\r
- }\r
- // unauthenticated request permitted.\r
- // pass processing to the restricted servlet.\r
- chain.doFilter(authenticatedRequest, httpResponse);\r
- }\r
-}
\ No newline at end of file
+++ /dev/null
-/*\r
- * Copyright 2011 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.io.IOException;\r
-import java.security.Principal;\r
-import java.util.Enumeration;\r
-import java.util.HashMap;\r
-import java.util.Map;\r
-\r
-import javax.servlet.Filter;\r
-import javax.servlet.FilterChain;\r
-import javax.servlet.FilterConfig;\r
-import javax.servlet.ServletException;\r
-import javax.servlet.ServletRequest;\r
-import javax.servlet.ServletResponse;\r
-import javax.servlet.http.HttpServletRequest;\r
-import javax.servlet.http.HttpServletRequestWrapper;\r
-import javax.servlet.http.HttpServletResponse;\r
-import javax.servlet.http.HttpSession;\r
-\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.gitblit.manager.ISessionManager;\r
-import com.gitblit.models.UserModel;\r
-import com.gitblit.utils.DeepCopier;\r
-import com.gitblit.utils.StringUtils;\r
-\r
-/**\r
- * The AuthenticationFilter is a servlet filter that preprocesses requests that\r
- * match its url pattern definition in the web.xml file.\r
- *\r
- * http://en.wikipedia.org/wiki/Basic_access_authentication\r
- *\r
- * @author James Moger\r
- *\r
- */\r
-public abstract class AuthenticationFilter implements Filter {\r
-\r
- protected static final String CHALLENGE = "Basic realm=\"" + Constants.NAME + "\"";\r
-\r
- protected static final String SESSION_SECURED = "com.gitblit.secured";\r
-\r
- protected transient Logger logger = LoggerFactory.getLogger(getClass());\r
-\r
- protected final ISessionManager sessionManager;\r
-\r
- protected AuthenticationFilter(ISessionManager sessionManager) {\r
- this.sessionManager = sessionManager;\r
- }\r
-\r
- /**\r
- * doFilter does the actual work of preprocessing the request to ensure that\r
- * the user may proceed.\r
- *\r
- * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,\r
- * javax.servlet.ServletResponse, javax.servlet.FilterChain)\r
- */\r
- @Override\r
- public abstract void doFilter(final ServletRequest request, final ServletResponse response,\r
- final FilterChain chain) throws IOException, ServletException;\r
-\r
- /**\r
- * Allow the filter to require a client certificate to continue processing.\r
- *\r
- * @return true, if a client certificate is required\r
- */\r
- protected boolean requiresClientCertificate() {\r
- return false;\r
- }\r
-\r
- /**\r
- * Returns the full relative url of the request.\r
- *\r
- * @param httpRequest\r
- * @return url\r
- */\r
- protected String getFullUrl(HttpServletRequest httpRequest) {\r
- String servletUrl = httpRequest.getContextPath() + httpRequest.getServletPath();\r
- String url = httpRequest.getRequestURI().substring(servletUrl.length());\r
- String params = httpRequest.getQueryString();\r
- if (url.length() > 0 && url.charAt(0) == '/') {\r
- url = url.substring(1);\r
- }\r
- String fullUrl = url + (StringUtils.isEmpty(params) ? "" : ("?" + params));\r
- return fullUrl;\r
- }\r
-\r
- /**\r
- * Returns the user making the request, if the user has authenticated.\r
- *\r
- * @param httpRequest\r
- * @return user\r
- */\r
- protected UserModel getUser(HttpServletRequest httpRequest) {\r
- UserModel user = sessionManager.authenticate(httpRequest, requiresClientCertificate());\r
- return user;\r
- }\r
-\r
- /**\r
- * Taken from Jetty's LoginAuthenticator.renewSessionOnAuthentication()\r
- */\r
- protected void newSession(HttpServletRequest request, HttpServletResponse response) {\r
- HttpSession oldSession = request.getSession(false);\r
- if (oldSession != null && oldSession.getAttribute(SESSION_SECURED) == null) {\r
- synchronized (this) {\r
- Map<String, Object> attributes = new HashMap<String, Object>();\r
- Enumeration<String> e = oldSession.getAttributeNames();\r
- while (e.hasMoreElements()) {\r
- String name = e.nextElement();\r
- attributes.put(name, oldSession.getAttribute(name));\r
- oldSession.removeAttribute(name);\r
- }\r
- oldSession.invalidate();\r
-\r
- HttpSession newSession = request.getSession(true);\r
- newSession.setAttribute(SESSION_SECURED, Boolean.TRUE);\r
- for (Map.Entry<String, Object> entry : attributes.entrySet()) {\r
- newSession.setAttribute(entry.getKey(), entry.getValue());\r
- }\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)\r
- */\r
- @Override\r
- public void init(final FilterConfig config) throws ServletException {\r
- }\r
-\r
- /**\r
- * @see javax.servlet.Filter#destroy()\r
- */\r
- @Override\r
- public void destroy() {\r
- }\r
-\r
- /**\r
- * Wraps a standard HttpServletRequest and overrides user principal methods.\r
- */\r
- public static class AuthenticatedRequest extends HttpServletRequestWrapper {\r
-\r
- private UserModel user;\r
-\r
- public AuthenticatedRequest(HttpServletRequest req) {\r
- super(req);\r
- user = DeepCopier.copy(UserModel.ANONYMOUS);\r
- }\r
-\r
- UserModel getUser() {\r
- return user;\r
- }\r
-\r
- void setUser(UserModel user) {\r
- this.user = user;\r
- }\r
-\r
- @Override\r
- public String getRemoteUser() {\r
- return user.username;\r
- }\r
-\r
- @Override\r
- public boolean isUserInRole(String role) {\r
- if (role.equals(Constants.ADMIN_ROLE)) {\r
- return user.canAdmin();\r
- }\r
- // Gitblit does not currently use actual roles in the traditional\r
- // servlet container sense. That is the reason this is marked\r
- // deprecated, but I may want to revisit this.\r
- return user.canAccessRepository(role);\r
- }\r
-\r
- @Override\r
- public Principal getUserPrincipal() {\r
- return user;\r
- }\r
- }\r
-}
\ No newline at end of file
+++ /dev/null
-/*\r
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\r
- * Copyright 2013 gitblit.com.\r
- * and other copyright owners as documented in the project's IP log.\r
- *\r
- * This program and the accompanying materials are made available\r
- * under the terms of the Eclipse Distribution License v1.0 which\r
- * accompanies this distribution, is reproduced below, and is\r
- * available at http://www.eclipse.org/org/documents/edl-v10.php\r
- *\r
- * All rights reserved.\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.awt.BasicStroke;\r
-import java.awt.Color;\r
-import java.awt.Graphics;\r
-import java.awt.Graphics2D;\r
-import java.awt.RenderingHints;\r
-import java.awt.Stroke;\r
-import java.awt.image.BufferedImage;\r
-import java.io.IOException;\r
-import java.io.InputStream;\r
-import java.io.OutputStream;\r
-import java.io.Serializable;\r
-import java.util.ArrayList;\r
-import java.util.LinkedList;\r
-import java.util.List;\r
-import java.util.Set;\r
-import java.util.TreeSet;\r
-\r
-import javax.imageio.ImageIO;\r
-import javax.inject.Inject;\r
-import javax.inject.Singleton;\r
-import javax.servlet.ServletException;\r
-import javax.servlet.http.HttpServlet;\r
-import javax.servlet.http.HttpServletRequest;\r
-import javax.servlet.http.HttpServletResponse;\r
-\r
-import org.eclipse.jgit.lib.Ref;\r
-import org.eclipse.jgit.lib.Repository;\r
-import org.eclipse.jgit.revplot.AbstractPlotRenderer;\r
-import org.eclipse.jgit.revplot.PlotCommit;\r
-import org.eclipse.jgit.revplot.PlotCommitList;\r
-import org.eclipse.jgit.revplot.PlotLane;\r
-import org.eclipse.jgit.revplot.PlotWalk;\r
-import org.eclipse.jgit.revwalk.RevCommit;\r
-\r
-import com.gitblit.manager.IRepositoryManager;\r
-import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.utils.JGitUtils;\r
-import com.gitblit.utils.StringUtils;\r
-\r
-/**\r
- * Handles requests for branch graphs\r
- *\r
- * @author James Moger\r
- *\r
- */\r
-@Singleton\r
-public class BranchGraphServlet extends HttpServlet {\r
-\r
- private static final long serialVersionUID = 1L;\r
-\r
- private static final int LANE_WIDTH = 14;\r
-\r
- // must match tr.commit css height\r
- private static final int ROW_HEIGHT = 24;\r
-\r
- private static final int RIGHT_PAD = 2;\r
-\r
- private final Stroke[] strokeCache;\r
-\r
- private final IStoredSettings settings;\r
-\r
- private final IRepositoryManager repositoryManager;\r
-\r
- @Inject\r
- public BranchGraphServlet(\r
- IRuntimeManager runtimeManager,\r
- IRepositoryManager repositoryManager) {\r
-\r
- super();\r
- this.settings = runtimeManager.getSettings();\r
- this.repositoryManager = repositoryManager;\r
-\r
- strokeCache = new Stroke[4];\r
- for (int i = 1; i < strokeCache.length; i++)\r
- strokeCache[i] = new BasicStroke(i);\r
- }\r
-\r
- /**\r
- * Returns an url to this servlet for the specified parameters.\r
- *\r
- * @param baseURL\r
- * @param repository\r
- * @param objectId\r
- * @param numberCommits\r
- * @return an url\r
- */\r
- public static String asLink(String baseURL, String repository, String objectId, int numberCommits) {\r
- if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {\r
- baseURL = baseURL.substring(0, baseURL.length() - 1);\r
- }\r
- return baseURL + Constants.BRANCH_GRAPH_PATH + "?r=" + repository\r
- + (objectId == null ? "" : ("&h=" + objectId))\r
- + (numberCommits > 0 ? ("&l=" + numberCommits) : "");\r
- }\r
-\r
- @Override\r
- protected long getLastModified(HttpServletRequest req) {\r
- String repository = req.getParameter("r");\r
- String objectId = req.getParameter("h");\r
- Repository r = null;\r
- try {\r
- r = repositoryManager.getRepository(repository);\r
- if (StringUtils.isEmpty(objectId)) {\r
- objectId = JGitUtils.getHEADRef(r);\r
- }\r
- RevCommit commit = JGitUtils.getCommit(r, objectId);\r
- return JGitUtils.getCommitDate(commit).getTime();\r
- } finally {\r
- if (r != null) {\r
- r.close();\r
- }\r
- }\r
- }\r
-\r
- @Override\r
- protected void doGet(HttpServletRequest request, HttpServletResponse response)\r
- throws ServletException, IOException {\r
- InputStream is = null;\r
- Repository r = null;\r
- PlotWalk rw = null;\r
- try {\r
- String repository = request.getParameter("r");\r
- String objectId = request.getParameter("h");\r
- String length = request.getParameter("l");\r
-\r
- r = repositoryManager.getRepository(repository);\r
-\r
- rw = new PlotWalk(r);\r
- if (StringUtils.isEmpty(objectId)) {\r
- objectId = JGitUtils.getHEADRef(r);\r
- }\r
-\r
- rw.markStart(rw.lookupCommit(r.resolve(objectId)));\r
-\r
- // default to the items-per-page setting, unless specified\r
- int maxCommits = settings.getInteger(Keys.web.itemsPerPage, 50);\r
- int requestedCommits = maxCommits;\r
- if (!StringUtils.isEmpty(length)) {\r
- int l = Integer.parseInt(length);\r
- if (l > 0) {\r
- requestedCommits = l;\r
- }\r
- }\r
-\r
- // fetch the requested commits plus some extra so that the last\r
- // commit displayed *likely* has correct lane assignments\r
- CommitList commitList = new CommitList();\r
- commitList.source(rw);\r
- commitList.fillTo(2*Math.max(requestedCommits, maxCommits));\r
-\r
- // determine the appropriate width for the image\r
- int numLanes = 1;\r
- int numCommits = Math.min(requestedCommits, commitList.size());\r
- if (numCommits > 1) {\r
- // determine graph width\r
- Set<String> parents = new TreeSet<String>();\r
- for (int i = 0; i < commitList.size(); i++) {\r
- PlotCommit<Lane> commit = commitList.get(i);\r
- boolean checkLane = false;\r
-\r
- if (i < numCommits) {\r
- // commit in visible list\r
- checkLane = true;\r
-\r
- // remember parents\r
- for (RevCommit p : commit.getParents()) {\r
- parents.add(p.getName());\r
- }\r
- } else if (parents.contains(commit.getName())) {\r
- // commit outside visible list, but it is a parent of a\r
- // commit in the visible list so we need to know it's lane\r
- // assignment\r
- checkLane = true;\r
- }\r
-\r
- if (checkLane) {\r
- int pos = commit.getLane().getPosition();\r
- numLanes = Math.max(numLanes, pos + 1);\r
- }\r
- }\r
- }\r
-\r
- int graphWidth = numLanes * LANE_WIDTH + RIGHT_PAD;\r
- int rowHeight = ROW_HEIGHT;\r
-\r
- // create an image buffer and render the lanes\r
- BufferedImage image = new BufferedImage(graphWidth, rowHeight*numCommits, BufferedImage.TYPE_INT_ARGB);\r
-\r
- Graphics2D g = null;\r
- try {\r
- g = image.createGraphics();\r
- g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);\r
- LanesRenderer renderer = new LanesRenderer();\r
- for (int i = 0; i < commitList.size(); i++) {\r
- PlotCommit<Lane> commit = commitList.get(i);\r
- Graphics row = g.create(0, i*rowHeight, graphWidth, rowHeight);\r
- try {\r
- renderer.paint(row, commit, rowHeight, graphWidth);\r
- } finally {\r
- row.dispose();\r
- row = null;\r
- }\r
- }\r
- } finally {\r
- if (g != null) {\r
- g.dispose();\r
- g = null;\r
- }\r
- }\r
-\r
- // write the image buffer to the client\r
- response.setContentType("image/png");\r
- if (numCommits > 1) {\r
- response.setHeader("Cache-Control", "public, max-age=60, must-revalidate");\r
- response.setDateHeader("Last-Modified", JGitUtils.getCommitDate(commitList.get(0)).getTime());\r
- }\r
- OutputStream os = response.getOutputStream();\r
- ImageIO.write(image, "png", os);\r
- os.flush();\r
- image.flush();\r
- image = null;\r
- } catch (Exception e) {\r
- e.printStackTrace();\r
- } finally {\r
- if (is != null) {\r
- is.close();\r
- is = null;\r
- }\r
- if (rw != null) {\r
- rw.dispose();\r
- rw = null;\r
- }\r
- if (r != null) {\r
- r.close();\r
- r = null;\r
- }\r
- }\r
- }\r
-\r
- private Stroke stroke(final int width) {\r
- if (width < strokeCache.length)\r
- return strokeCache[width];\r
- return new BasicStroke(width);\r
- }\r
-\r
- static class CommitList extends PlotCommitList<Lane> {\r
- final List<Color> laneColors;\r
- final LinkedList<Color> colors;\r
-\r
- CommitList() {\r
- laneColors = new ArrayList<Color>();\r
- laneColors.add(new Color(133, 166, 214));\r
- laneColors.add(new Color(221, 205, 93));\r
- laneColors.add(new Color(199, 134, 57));\r
- laneColors.add(new Color(131, 150, 98));\r
- laneColors.add(new Color(197, 123, 127));\r
- laneColors.add(new Color(139, 136, 140));\r
- laneColors.add(new Color(48, 135, 144));\r
- laneColors.add(new Color(190, 93, 66));\r
- laneColors.add(new Color(143, 163, 54));\r
- laneColors.add(new Color(180, 148, 74));\r
- laneColors.add(new Color(101, 101, 217));\r
- laneColors.add(new Color(72, 153, 119));\r
- laneColors.add(new Color(23, 101, 160));\r
- laneColors.add(new Color(132, 164, 118));\r
- laneColors.add(new Color(255, 230, 59));\r
- laneColors.add(new Color(136, 176, 70));\r
- laneColors.add(new Color(255, 138, 1));\r
- laneColors.add(new Color(123, 187, 95));\r
- laneColors.add(new Color(233, 88, 98));\r
- laneColors.add(new Color(93, 158, 254));\r
- laneColors.add(new Color(175, 215, 0));\r
- laneColors.add(new Color(140, 134, 142));\r
- laneColors.add(new Color(232, 168, 21));\r
- laneColors.add(new Color(0, 172, 191));\r
- laneColors.add(new Color(251, 58, 4));\r
- laneColors.add(new Color(63, 64, 255));\r
- laneColors.add(new Color(27, 194, 130));\r
- laneColors.add(new Color(0, 104, 183));\r
-\r
- colors = new LinkedList<Color>();\r
- repackColors();\r
- }\r
-\r
- private void repackColors() {\r
- colors.addAll(laneColors);\r
- }\r
-\r
- @Override\r
- protected Lane createLane() {\r
- final Lane lane = new Lane();\r
- if (colors.isEmpty())\r
- repackColors();\r
- lane.color = colors.removeFirst();\r
- return lane;\r
- }\r
-\r
- @Override\r
- protected void recycleLane(final Lane lane) {\r
- colors.add(lane.color);\r
- }\r
- }\r
-\r
- static class Lane extends PlotLane {\r
-\r
- private static final long serialVersionUID = 1L;\r
-\r
- Color color;\r
-\r
- @Override\r
- public boolean equals(Object o) {\r
- return super.equals(o) && color.equals(((Lane)o).color);\r
- }\r
-\r
- @Override\r
- public int hashCode() {\r
- return super.hashCode() ^ color.hashCode();\r
- }\r
- }\r
-\r
- class LanesRenderer extends AbstractPlotRenderer<Lane, Color> implements Serializable {\r
-\r
- private static final long serialVersionUID = 1L;\r
-\r
- final Color commitDotFill = new Color(220, 220, 220);\r
-\r
- final Color commitDotOutline = new Color(110, 110, 110);\r
-\r
- transient Graphics2D g;\r
-\r
- void paint(Graphics in, PlotCommit<Lane> commit, int h, int w) {\r
- g = (Graphics2D) in.create();\r
- try {\r
- if (commit != null)\r
- paintCommit(commit, h);\r
- } finally {\r
- g.dispose();\r
- g = null;\r
- }\r
- }\r
-\r
- @Override\r
- protected void drawLine(Color color, int x1, int y1, int x2, int y2, int width) {\r
- if (y1 == y2) {\r
- x1 -= width / 2;\r
- x2 -= width / 2;\r
- } else if (x1 == x2) {\r
- y1 -= width / 2;\r
- y2 -= width / 2;\r
- }\r
-\r
- g.setColor(color);\r
- g.setStroke(stroke(width));\r
- g.drawLine(x1, y1, x2, y2);\r
- }\r
-\r
- @Override\r
- protected void drawCommitDot(int x, int y, int w, int h) {\r
- g.setColor(commitDotFill);\r
- g.setStroke(strokeCache[2]);\r
- g.fillOval(x + 2, y + 1, w - 2, h - 2);\r
- g.setColor(commitDotOutline);\r
- g.drawOval(x + 2, y + 1, w - 2, h - 2);\r
- }\r
-\r
- @Override\r
- protected void drawBoundaryDot(int x, int y, int w, int h) {\r
- drawCommitDot(x, y, w, h);\r
- }\r
-\r
- @Override\r
- protected void drawText(String msg, int x, int y) {\r
- }\r
-\r
- @Override\r
- protected Color laneColor(Lane myLane) {\r
- return myLane != null ? myLane.color : Color.black;\r
- }\r
-\r
- @Override\r
- protected int drawLabel(int x, int y, Ref ref) {\r
- return 0;\r
- }\r
- }\r
-}\r
public static enum SearchObjectType {\r
commit, blob;\r
\r
- static SearchObjectType fromName(String name) {\r
+ public static SearchObjectType fromName(String name) {\r
for (SearchObjectType value : values()) {\r
if (value.name().equals(name)) {\r
return value;\r
import com.gitblit.manager.ServicesManager;
import com.gitblit.manager.SessionManager;
import com.gitblit.manager.UserManager;
+import com.gitblit.servlet.BranchGraphServlet;
+import com.gitblit.servlet.DownloadZipFilter;
+import com.gitblit.servlet.DownloadZipServlet;
+import com.gitblit.servlet.EnforceAuthenticationFilter;
+import com.gitblit.servlet.FederationServlet;
+import com.gitblit.servlet.GitFilter;
+import com.gitblit.servlet.LogoServlet;
+import com.gitblit.servlet.PagesFilter;
+import com.gitblit.servlet.PagesServlet;
+import com.gitblit.servlet.RobotsTxtServlet;
+import com.gitblit.servlet.RpcFilter;
+import com.gitblit.servlet.RpcServlet;
+import com.gitblit.servlet.SparkleShareInviteServlet;
+import com.gitblit.servlet.SyndicationFilter;
+import com.gitblit.servlet.SyndicationServlet;
import com.gitblit.wicket.GitBlitWebApp;
import com.gitblit.wicket.GitblitWicketFilter;
+++ /dev/null
-/*\r
- * Copyright 2011 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import javax.inject.Inject;\r
-import javax.inject.Singleton;\r
-\r
-import com.gitblit.Constants.AccessRestrictionType;\r
-import com.gitblit.manager.IRepositoryManager;\r
-import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.manager.ISessionManager;\r
-import com.gitblit.models.RepositoryModel;\r
-import com.gitblit.models.UserModel;\r
-\r
-/**\r
- * The DownloadZipFilter is an AccessRestrictionFilter which ensures that zip\r
- * requests for view-restricted repositories have proper authentication\r
- * credentials and are authorized.\r
- *\r
- * @author James Moger\r
- *\r
- */\r
-@Singleton\r
-public class DownloadZipFilter extends AccessRestrictionFilter {\r
-\r
- @Inject\r
- public DownloadZipFilter(\r
- IRuntimeManager runtimeManager,\r
- ISessionManager sessionManager,\r
- IRepositoryManager repositoryManager) {\r
-\r
- super(runtimeManager, sessionManager, repositoryManager);\r
- }\r
-\r
- /**\r
- * Extract the repository name from the url.\r
- *\r
- * @param url\r
- * @return repository name\r
- */\r
- @Override\r
- protected String extractRepositoryName(String url) {\r
- int a = url.indexOf("r=");\r
- String repository = url.substring(a + 2);\r
- if (repository.indexOf('&') > -1) {\r
- repository = repository.substring(0, repository.indexOf('&'));\r
- }\r
- return repository;\r
- }\r
-\r
- /**\r
- * Analyze the url and returns the action of the request.\r
- *\r
- * @param url\r
- * @return action of the request\r
- */\r
- @Override\r
- protected String getUrlRequestAction(String url) {\r
- return "DOWNLOAD";\r
- }\r
-\r
- /**\r
- * Determine if a non-existing repository can be created using this filter.\r
- *\r
- * @return true if the filter allows repository creation\r
- */\r
- @Override\r
- protected boolean isCreationAllowed() {\r
- return false;\r
- }\r
-\r
- /**\r
- * Determine if the action may be executed on the repository.\r
- *\r
- * @param repository\r
- * @param action\r
- * @return true if the action may be performed\r
- */\r
- @Override\r
- protected boolean isActionAllowed(RepositoryModel repository, String action) {\r
- return true;\r
- }\r
-\r
- /**\r
- * Determine if the repository requires authentication.\r
- *\r
- * @param repository\r
- * @param action\r
- * @return true if authentication required\r
- */\r
- @Override\r
- protected boolean requiresAuthentication(RepositoryModel repository, String action) {\r
- return repository.accessRestriction.atLeast(AccessRestrictionType.VIEW);\r
- }\r
-\r
- /**\r
- * Determine if the user can access the repository and perform the specified\r
- * action.\r
- *\r
- * @param repository\r
- * @param user\r
- * @param action\r
- * @return true if user may execute the action on the repository\r
- */\r
- @Override\r
- protected boolean canAccess(RepositoryModel repository, UserModel user, String action) {\r
- return user.canView(repository);\r
- }\r
-\r
-}\r
+++ /dev/null
-/*\r
- * Copyright 2011 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.io.IOException;\r
-import java.text.MessageFormat;\r
-import java.text.ParseException;\r
-import java.util.Date;\r
-\r
-import javax.inject.Inject;\r
-import javax.inject.Singleton;\r
-import javax.servlet.ServletException;\r
-import javax.servlet.http.HttpServlet;\r
-import javax.servlet.http.HttpServletResponse;\r
-\r
-import org.eclipse.jgit.lib.Repository;\r
-import org.eclipse.jgit.revwalk.RevCommit;\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.gitblit.manager.IRepositoryManager;\r
-import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.utils.CompressionUtils;\r
-import com.gitblit.utils.JGitUtils;\r
-import com.gitblit.utils.MarkdownUtils;\r
-import com.gitblit.utils.StringUtils;\r
-\r
-/**\r
- * Streams out a zip file from the specified repository for any tree path at any\r
- * revision.\r
- *\r
- * @author James Moger\r
- *\r
- */\r
-@Singleton\r
-public class DownloadZipServlet extends HttpServlet {\r
-\r
- private static final long serialVersionUID = 1L;\r
-\r
- private transient Logger logger = LoggerFactory.getLogger(DownloadZipServlet.class);\r
-\r
- private final IStoredSettings settings;\r
-\r
- private final IRepositoryManager repositoryManager;\r
-\r
- public static enum Format {\r
- zip(".zip"), tar(".tar"), gz(".tar.gz"), xz(".tar.xz"), bzip2(".tar.bzip2");\r
-\r
- public final String extension;\r
-\r
- Format(String ext) {\r
- this.extension = ext;\r
- }\r
-\r
- public static Format fromName(String name) {\r
- for (Format format : values()) {\r
- if (format.name().equalsIgnoreCase(name)) {\r
- return format;\r
- }\r
- }\r
- return zip;\r
- }\r
- }\r
-\r
- @Inject\r
- public DownloadZipServlet(\r
- IRuntimeManager runtimeManager,\r
- IRepositoryManager repositoryManager) {\r
-\r
- super();\r
- this.settings = runtimeManager.getSettings();\r
- this.repositoryManager = repositoryManager;\r
- }\r
-\r
- /**\r
- * Returns an url to this servlet for the specified parameters.\r
- *\r
- * @param baseURL\r
- * @param repository\r
- * @param objectId\r
- * @param path\r
- * @param format\r
- * @return an url\r
- */\r
- public static String asLink(String baseURL, String repository, String objectId, String path, Format format) {\r
- if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {\r
- baseURL = baseURL.substring(0, baseURL.length() - 1);\r
- }\r
- return baseURL + Constants.ZIP_PATH + "?r=" + repository\r
- + (path == null ? "" : ("&p=" + path))\r
- + (objectId == null ? "" : ("&h=" + objectId))\r
- + (format == null ? "" : ("&format=" + format.name()));\r
- }\r
-\r
- /**\r
- * Creates a zip stream from the repository of the requested data.\r
- *\r
- * @param request\r
- * @param response\r
- * @throws javax.servlet.ServletException\r
- * @throws java.io.IOException\r
- */\r
- private void processRequest(javax.servlet.http.HttpServletRequest request,\r
- javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,\r
- java.io.IOException {\r
- if (!settings.getBoolean(Keys.web.allowZipDownloads, true)) {\r
- logger.warn("Zip downloads are disabled");\r
- response.sendError(HttpServletResponse.SC_FORBIDDEN);\r
- return;\r
- }\r
-\r
- Format format = Format.zip;\r
- String repository = request.getParameter("r");\r
- String basePath = request.getParameter("p");\r
- String objectId = request.getParameter("h");\r
- String f = request.getParameter("format");\r
- if (!StringUtils.isEmpty(f)) {\r
- format = Format.fromName(f);\r
- }\r
-\r
- try {\r
- String name = repository;\r
- if (name.indexOf('/') > -1) {\r
- name = name.substring(name.lastIndexOf('/') + 1);\r
- }\r
- name = StringUtils.stripDotGit(name);\r
-\r
- if (!StringUtils.isEmpty(basePath)) {\r
- name += "-" + basePath.replace('/', '_');\r
- }\r
- if (!StringUtils.isEmpty(objectId)) {\r
- name += "-" + objectId;\r
- }\r
-\r
- Repository r = repositoryManager.getRepository(repository);\r
- if (r == null) {\r
- if (repositoryManager.isCollectingGarbage(repository)) {\r
- error(response, MessageFormat.format("# Error\nGitblit is busy collecting garbage in {0}", repository));\r
- return;\r
- } else {\r
- error(response, MessageFormat.format("# Error\nFailed to find repository {0}", repository));\r
- return;\r
- }\r
- }\r
- RevCommit commit = JGitUtils.getCommit(r, objectId);\r
- if (commit == null) {\r
- error(response, MessageFormat.format("# Error\nFailed to find commit {0}", objectId));\r
- r.close();\r
- return;\r
- }\r
- Date date = JGitUtils.getCommitDate(commit);\r
-\r
- String contentType = "application/octet-stream";\r
- response.setContentType(contentType + "; charset=" + response.getCharacterEncoding());\r
- response.setHeader("Content-Disposition", "attachment; filename=\"" + name + format.extension + "\"");\r
- response.setDateHeader("Last-Modified", date.getTime());\r
- response.setHeader("Cache-Control", "no-cache");\r
- response.setHeader("Pragma", "no-cache");\r
- response.setDateHeader("Expires", 0);\r
-\r
- try {\r
- switch (format) {\r
- case zip:\r
- CompressionUtils.zip(r, basePath, objectId, response.getOutputStream());\r
- break;\r
- case tar:\r
- CompressionUtils.tar(r, basePath, objectId, response.getOutputStream());\r
- break;\r
- case gz:\r
- CompressionUtils.gz(r, basePath, objectId, response.getOutputStream());\r
- break;\r
- case xz:\r
- CompressionUtils.xz(r, basePath, objectId, response.getOutputStream());\r
- break;\r
- case bzip2:\r
- CompressionUtils.bzip2(r, basePath, objectId, response.getOutputStream());\r
- break;\r
- }\r
-\r
- response.flushBuffer();\r
- } catch (IOException t) {\r
- String message = t.getMessage() == null ? "" : t.getMessage().toLowerCase();\r
- if (message.contains("reset") || message.contains("broken pipe")) {\r
- logger.error("Client aborted zip download: " + message);\r
- } else {\r
- logger.error("Failed to write attachment to client", t);\r
- }\r
- } catch (Throwable t) {\r
- logger.error("Failed to write attachment to client", t);\r
- }\r
-\r
- // close the repository\r
- r.close();\r
- } catch (Throwable t) {\r
- logger.error("Failed to write attachment to client", t);\r
- }\r
- }\r
-\r
- private void error(HttpServletResponse response, String mkd) throws ServletException,\r
- IOException, ParseException {\r
- String content = MarkdownUtils.transformMarkdown(mkd);\r
- response.setContentType("text/html; charset=" + Constants.ENCODING);\r
- response.getWriter().write(content);\r
- }\r
-\r
- @Override\r
- protected void doPost(javax.servlet.http.HttpServletRequest request,\r
- javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,\r
- java.io.IOException {\r
- processRequest(request, response);\r
- }\r
-\r
- @Override\r
- protected void doGet(javax.servlet.http.HttpServletRequest request,\r
- javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,\r
- java.io.IOException {\r
- processRequest(request, response);\r
- }\r
-}\r
+++ /dev/null
-/*
- * Copyright 2013 Laurens Vrijnsen
- * Copyright 2013 gitblit.com.
- *
- * 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.gitblit;
-
-import java.io.IOException;
-import java.text.MessageFormat;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-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;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.gitblit.manager.IRuntimeManager;
-import com.gitblit.manager.ISessionManager;
-import com.gitblit.models.UserModel;
-
-/**
- * This filter enforces authentication via HTTP Basic Authentication, if the settings indicate so.
- * It looks at the settings "web.authenticateViewPages" and "web.enforceHttpBasicAuthentication"; if
- * both are true, any unauthorized access will be met with a HTTP Basic Authentication header.
- *
- * @author Laurens Vrijnsen
- *
- */
-@Singleton
-public class EnforceAuthenticationFilter implements Filter {
-
- protected transient Logger logger = LoggerFactory.getLogger(getClass());
-
- private final IStoredSettings settings;
-
- private final ISessionManager sessionManager;
-
- @Inject
- public EnforceAuthenticationFilter(
- IRuntimeManager runtimeManager,
- ISessionManager sessionManager) {
-
- super();
- this.settings = runtimeManager.getSettings();
- this.sessionManager = sessionManager;
- }
-
- /*
- * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
- */
- @Override
- public void init(FilterConfig filterConfig) throws ServletException {
- }
-
- /*
- * This does the actual filtering: is the user authenticated? If not, enforce HTTP authentication (401)
- *
- * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
- */
- @Override
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
-
- Boolean mustForceAuth = settings.getBoolean(Keys.web.authenticateViewPages, false)
- && settings.getBoolean(Keys.web.enforceHttpBasicAuthentication, false);
-
- HttpServletRequest httpRequest = (HttpServletRequest) request;
- HttpServletResponse httpResponse = (HttpServletResponse) response;
- UserModel user = sessionManager.authenticate(httpRequest);
-
- if (mustForceAuth && (user == null)) {
- // not authenticated, enforce now:
- logger.debug(MessageFormat.format("EnforceAuthFilter: user not authenticated for URL {0}!", request.toString()));
- String challenge = MessageFormat.format("Basic realm=\"{0}\"", settings.getString(Keys.web.siteName, ""));
- httpResponse.setHeader("WWW-Authenticate", challenge);
- httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
- return;
-
- } else {
- // user is authenticated, or don't care, continue handling
- chain.doFilter(request, response);
- }
- }
-
-
- /*
- * @see javax.servlet.Filter#destroy()
- */
- @Override
- public void destroy() {
- }
-}
import com.gitblit.manager.RuntimeManager;\r
import com.gitblit.manager.UserManager;\r
import com.gitblit.models.FederationModel;\r
+import com.gitblit.service.FederationPullService;\r
import com.gitblit.utils.FederationUtils;\r
import com.gitblit.utils.StringUtils;\r
\r
RepositoryManager repositories = new RepositoryManager(runtime, users).start();\r
FederationManager federation = new FederationManager(runtime, notifications, users, repositories).start();\r
\r
- FederationPullExecutor puller = new FederationPullExecutor(federation.getFederationRegistrations()) {\r
+ FederationPullService puller = new FederationPullService(federation.getFederationRegistrations()) {\r
@Override\r
public void reschedule(FederationModel registration) {\r
// NOOP\r
+++ /dev/null
-package com.gitblit;
-
-import static org.eclipse.jgit.lib.Constants.DOT_GIT_EXT;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.net.InetAddress;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.StoredConfig;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.transport.CredentialsProvider;
-import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.gitblit.Constants.AccessPermission;
-import com.gitblit.Constants.FederationPullStatus;
-import com.gitblit.Constants.FederationStrategy;
-import com.gitblit.GitBlitException.ForbiddenException;
-import com.gitblit.models.FederationModel;
-import com.gitblit.models.RefModel;
-import com.gitblit.models.RepositoryModel;
-import com.gitblit.models.TeamModel;
-import com.gitblit.models.UserModel;
-import com.gitblit.utils.ArrayUtils;
-import com.gitblit.utils.FederationUtils;
-import com.gitblit.utils.FileUtils;
-import com.gitblit.utils.JGitUtils;
-import com.gitblit.utils.JGitUtils.CloneResult;
-import com.gitblit.utils.StringUtils;
-
-public abstract class FederationPullExecutor implements Runnable {
-
- Logger logger = LoggerFactory.getLogger(getClass());
-
- Gitblit gitblit;
-
- private final List<FederationModel> registrations;
-
- /**
- * Constructor for specifying a single federation registration. This
- * constructor is used to schedule the next pull execution.
- *
- * @param provider
- * @param registration
- */
- public FederationPullExecutor(FederationModel registration) {
- this(Arrays.asList(registration));
- }
-
- /**
- * Constructor to specify a group of federation registrations. This is
- * normally used at startup to pull and then schedule the next update based
- * on each registrations frequency setting.
- *
- * @param provider
- * @param registrations
- * @param isDaemon
- * if true, registrations are rescheduled in perpetuity. if
- * false, the federation pull operation is executed once.
- */
- public FederationPullExecutor(List<FederationModel> registrations) {
- this.registrations = registrations;
- }
-
- public abstract void reschedule(FederationModel registration);
-
- /**
- * Run method for this pull executor.
- */
- @Override
- public void run() {
- for (FederationModel registration : registrations) {
- FederationPullStatus was = registration.getLowestStatus();
- try {
- Date now = new Date(System.currentTimeMillis());
- pull(registration);
- sendStatusAcknowledgment(registration);
- registration.lastPull = now;
- FederationPullStatus is = registration.getLowestStatus();
- if (is.ordinal() < was.ordinal()) {
- // the status for this registration has downgraded
- logger.warn("Federation pull status of {0} is now {1}", registration.name,
- is.name());
- if (registration.notifyOnError) {
- String message = "Federation pull of " + registration.name + " @ "
- + registration.url + " is now at " + is.name();
- gitblit.sendMailToAdministrators(
- "Pull Status of " + registration.name + " is " + is.name(),
- message);
- }
- }
- } catch (Throwable t) {
- logger.error(MessageFormat.format(
- "Failed to pull from federated gitblit ({0} @ {1})", registration.name,
- registration.url), t);
- } finally {
- reschedule(registration);
- }
- }
- }
-
- /**
- * Mirrors a repository and, optionally, the server's users, and/or
- * configuration settings from a origin Gitblit instance.
- *
- * @param registration
- * @throws Exception
- */
- private void pull(FederationModel registration) throws Exception {
- Map<String, RepositoryModel> repositories = FederationUtils.getRepositories(registration,
- true);
- String registrationFolder = registration.folder.toLowerCase().trim();
- // confirm valid characters in server alias
- Character c = StringUtils.findInvalidCharacter(registrationFolder);
- if (c != null) {
- logger.error(MessageFormat
- .format("Illegal character ''{0}'' in folder name ''{1}'' of federation registration {2}!",
- c, registrationFolder, registration.name));
- return;
- }
- File repositoriesFolder = gitblit.getRepositoriesFolder();
- File registrationFolderFile = new File(repositoriesFolder, registrationFolder);
- registrationFolderFile.mkdirs();
-
- // Clone/Pull the repository
- for (Map.Entry<String, RepositoryModel> entry : repositories.entrySet()) {
- String cloneUrl = entry.getKey();
- RepositoryModel repository = entry.getValue();
- if (!repository.hasCommits) {
- logger.warn(MessageFormat.format(
- "Skipping federated repository {0} from {1} @ {2}. Repository is EMPTY.",
- repository.name, registration.name, registration.url));
- registration.updateStatus(repository, FederationPullStatus.SKIPPED);
- continue;
- }
-
- // Determine local repository name
- String repositoryName;
- if (StringUtils.isEmpty(registrationFolder)) {
- repositoryName = repository.name;
- } else {
- repositoryName = registrationFolder + "/" + repository.name;
- }
-
- if (registration.bare) {
- // bare repository, ensure .git suffix
- if (!repositoryName.toLowerCase().endsWith(DOT_GIT_EXT)) {
- repositoryName += DOT_GIT_EXT;
- }
- } else {
- // normal repository, strip .git suffix
- if (repositoryName.toLowerCase().endsWith(DOT_GIT_EXT)) {
- repositoryName = repositoryName.substring(0,
- repositoryName.indexOf(DOT_GIT_EXT));
- }
- }
-
- // confirm that the origin of any pre-existing repository matches
- // the clone url
- String fetchHead = null;
- Repository existingRepository = gitblit.getRepository(repositoryName);
-
- if (existingRepository == null && gitblit.isCollectingGarbage(repositoryName)) {
- logger.warn(MessageFormat.format("Skipping local repository {0}, busy collecting garbage", repositoryName));
- continue;
- }
-
- if (existingRepository != null) {
- StoredConfig config = existingRepository.getConfig();
- config.load();
- String origin = config.getString("remote", "origin", "url");
- RevCommit commit = JGitUtils.getCommit(existingRepository,
- org.eclipse.jgit.lib.Constants.FETCH_HEAD);
- if (commit != null) {
- fetchHead = commit.getName();
- }
- existingRepository.close();
- if (!origin.startsWith(registration.url)) {
- logger.warn(MessageFormat
- .format("Skipping federated repository {0} from {1} @ {2}. Origin does not match, consider EXCLUDING.",
- repository.name, registration.name, registration.url));
- registration.updateStatus(repository, FederationPullStatus.SKIPPED);
- continue;
- }
- }
-
- // clone/pull this repository
- CredentialsProvider credentials = new UsernamePasswordCredentialsProvider(
- Constants.FEDERATION_USER, registration.token);
- logger.info(MessageFormat.format("Pulling federated repository {0} from {1} @ {2}",
- repository.name, registration.name, registration.url));
-
- CloneResult result = JGitUtils.cloneRepository(registrationFolderFile, repository.name,
- cloneUrl, registration.bare, credentials);
- Repository r = gitblit.getRepository(repositoryName);
- RepositoryModel rm = gitblit.getRepositoryModel(repositoryName);
- repository.isFrozen = registration.mirror;
- if (result.createdRepository) {
- // default local settings
- repository.federationStrategy = FederationStrategy.EXCLUDE;
- repository.isFrozen = registration.mirror;
- repository.showRemoteBranches = !registration.mirror;
- logger.info(MessageFormat.format(" cloning {0}", repository.name));
- registration.updateStatus(repository, FederationPullStatus.MIRRORED);
- } else {
- // fetch and update
- boolean fetched = false;
- RevCommit commit = JGitUtils.getCommit(r, org.eclipse.jgit.lib.Constants.FETCH_HEAD);
- String newFetchHead = commit.getName();
- fetched = fetchHead == null || !fetchHead.equals(newFetchHead);
-
- if (registration.mirror) {
- // mirror
- if (fetched) {
- // update local branches to match the remote tracking branches
- for (RefModel ref : JGitUtils.getRemoteBranches(r, false, -1)) {
- if (ref.displayName.startsWith("origin/")) {
- String branch = org.eclipse.jgit.lib.Constants.R_HEADS
- + ref.displayName.substring(ref.displayName.indexOf('/') + 1);
- String hash = ref.getReferencedObjectId().getName();
-
- JGitUtils.setBranchRef(r, branch, hash);
- logger.info(MessageFormat.format(" resetting {0} of {1} to {2}", branch,
- repository.name, hash));
- }
- }
-
- String newHead;
- if (StringUtils.isEmpty(repository.HEAD)) {
- newHead = newFetchHead;
- } else {
- newHead = repository.HEAD;
- }
- JGitUtils.setHEADtoRef(r, newHead);
- logger.info(MessageFormat.format(" resetting HEAD of {0} to {1}",
- repository.name, newHead));
- registration.updateStatus(repository, FederationPullStatus.MIRRORED);
- } else {
- // indicate no commits pulled
- registration.updateStatus(repository, FederationPullStatus.NOCHANGE);
- }
- } else {
- // non-mirror
- if (fetched) {
- // indicate commits pulled to origin/master
- registration.updateStatus(repository, FederationPullStatus.PULLED);
- } else {
- // indicate no commits pulled
- registration.updateStatus(repository, FederationPullStatus.NOCHANGE);
- }
- }
-
- // preserve local settings
- repository.isFrozen = rm.isFrozen;
- repository.federationStrategy = rm.federationStrategy;
-
- // merge federation sets
- Set<String> federationSets = new HashSet<String>();
- if (rm.federationSets != null) {
- federationSets.addAll(rm.federationSets);
- }
- if (repository.federationSets != null) {
- federationSets.addAll(repository.federationSets);
- }
- repository.federationSets = new ArrayList<String>(federationSets);
-
- // merge indexed branches
- Set<String> indexedBranches = new HashSet<String>();
- if (rm.indexedBranches != null) {
- indexedBranches.addAll(rm.indexedBranches);
- }
- if (repository.indexedBranches != null) {
- indexedBranches.addAll(repository.indexedBranches);
- }
- repository.indexedBranches = new ArrayList<String>(indexedBranches);
-
- }
- // only repositories that are actually _cloned_ from the origin
- // Gitblit repository are marked as federated. If the origin
- // is from somewhere else, these repositories are not considered
- // "federated" repositories.
- repository.isFederated = cloneUrl.startsWith(registration.url);
-
- gitblit.updateConfiguration(r, repository);
- r.close();
- }
-
- IUserService userService = null;
-
- try {
- // Pull USERS
- // TeamModels are automatically pulled because they are contained
- // within the UserModel. The UserService creates unknown teams
- // and updates existing teams.
- Collection<UserModel> users = FederationUtils.getUsers(registration);
- if (users != null && users.size() > 0) {
- File realmFile = new File(registrationFolderFile, registration.name + "_users.conf");
- realmFile.delete();
- userService = new ConfigUserService(realmFile);
- for (UserModel user : users) {
- userService.updateUserModel(user.username, user);
-
- // merge the origin permissions and origin accounts into
- // the user accounts of this Gitblit instance
- if (registration.mergeAccounts) {
- // reparent all repository permissions if the local
- // repositories are stored within subfolders
- if (!StringUtils.isEmpty(registrationFolder)) {
- if (user.permissions != null) {
- // pulling from >= 1.2 version
- Map<String, AccessPermission> copy = new HashMap<String, AccessPermission>(user.permissions);
- user.permissions.clear();
- for (Map.Entry<String, AccessPermission> entry : copy.entrySet()) {
- user.setRepositoryPermission(registrationFolder + "/" + entry.getKey(), entry.getValue());
- }
- } else {
- // pulling from <= 1.1 version
- List<String> permissions = new ArrayList<String>(user.repositories);
- user.repositories.clear();
- for (String permission : permissions) {
- user.addRepositoryPermission(registrationFolder + "/" + permission);
- }
- }
- }
-
- // insert new user or update local user
- UserModel localUser = gitblit.getUserModel(user.username);
- if (localUser == null) {
- // create new local user
- gitblit.updateUserModel(user.username, user, true);
- } else {
- // update repository permissions of local user
- if (user.permissions != null) {
- // pulling from >= 1.2 version
- Map<String, AccessPermission> copy = new HashMap<String, AccessPermission>(user.permissions);
- for (Map.Entry<String, AccessPermission> entry : copy.entrySet()) {
- localUser.setRepositoryPermission(entry.getKey(), entry.getValue());
- }
- } else {
- // pulling from <= 1.1 version
- for (String repository : user.repositories) {
- localUser.addRepositoryPermission(repository);
- }
- }
- localUser.password = user.password;
- localUser.canAdmin = user.canAdmin;
- gitblit.updateUserModel(localUser.username, localUser, false);
- }
-
- for (String teamname : gitblit.getAllTeamNames()) {
- TeamModel team = gitblit.getTeamModel(teamname);
- if (user.isTeamMember(teamname) && !team.hasUser(user.username)) {
- // new team member
- team.addUser(user.username);
- gitblit.updateTeamModel(teamname, team);
- } else if (!user.isTeamMember(teamname) && team.hasUser(user.username)) {
- // remove team member
- team.removeUser(user.username);
- gitblit.updateTeamModel(teamname, team);
- }
-
- // update team repositories
- TeamModel remoteTeam = user.getTeam(teamname);
- if (remoteTeam != null) {
- if (remoteTeam.permissions != null) {
- // pulling from >= 1.2
- for (Map.Entry<String, AccessPermission> entry : remoteTeam.permissions.entrySet()){
- team.setRepositoryPermission(entry.getKey(), entry.getValue());
- }
- gitblit.updateTeamModel(teamname, team);
- } else if (!ArrayUtils.isEmpty(remoteTeam.repositories)) {
- // pulling from <= 1.1
- team.addRepositoryPermissions(remoteTeam.repositories);
- gitblit.updateTeamModel(teamname, team);
- }
- }
- }
- }
- }
- }
- } catch (ForbiddenException e) {
- // ignore forbidden exceptions
- } catch (IOException e) {
- logger.warn(MessageFormat.format(
- "Failed to retrieve USERS from federated gitblit ({0} @ {1})",
- registration.name, registration.url), e);
- }
-
- try {
- // Pull TEAMS
- // We explicitly pull these even though they are embedded in
- // UserModels because it is possible to use teams to specify
- // mailing lists or push scripts without specifying users.
- if (userService != null) {
- Collection<TeamModel> teams = FederationUtils.getTeams(registration);
- if (teams != null && teams.size() > 0) {
- for (TeamModel team : teams) {
- userService.updateTeamModel(team);
- }
- }
- }
- } catch (ForbiddenException e) {
- // ignore forbidden exceptions
- } catch (IOException e) {
- logger.warn(MessageFormat.format(
- "Failed to retrieve TEAMS from federated gitblit ({0} @ {1})",
- registration.name, registration.url), e);
- }
-
- try {
- // Pull SETTINGS
- Map<String, String> settings = FederationUtils.getSettings(registration);
- if (settings != null && settings.size() > 0) {
- Properties properties = new Properties();
- properties.putAll(settings);
- FileOutputStream os = new FileOutputStream(new File(registrationFolderFile,
- registration.name + "_" + Constants.PROPERTIES_FILE));
- properties.store(os, null);
- os.close();
- }
- } catch (ForbiddenException e) {
- // ignore forbidden exceptions
- } catch (IOException e) {
- logger.warn(MessageFormat.format(
- "Failed to retrieve SETTINGS from federated gitblit ({0} @ {1})",
- registration.name, registration.url), e);
- }
-
- try {
- // Pull SCRIPTS
- Map<String, String> scripts = FederationUtils.getScripts(registration);
- if (scripts != null && scripts.size() > 0) {
- for (Map.Entry<String, String> script : scripts.entrySet()) {
- String scriptName = script.getKey();
- if (scriptName.endsWith(".groovy")) {
- scriptName = scriptName.substring(0, scriptName.indexOf(".groovy"));
- }
- File file = new File(registrationFolderFile, registration.name + "_"
- + scriptName + ".groovy");
- FileUtils.writeContent(file, script.getValue());
- }
- }
- } catch (ForbiddenException e) {
- // ignore forbidden exceptions
- } catch (IOException e) {
- logger.warn(MessageFormat.format(
- "Failed to retrieve SCRIPTS from federated gitblit ({0} @ {1})",
- registration.name, registration.url), e);
- }
- }
-
- /**
- * Sends a status acknowledgment to the origin Gitblit instance. This
- * includes the results of the federated pull.
- *
- * @param registration
- * @throws Exception
- */
- private void sendStatusAcknowledgment(FederationModel registration) throws Exception {
- if (!registration.sendStatus) {
- // skip status acknowledgment
- return;
- }
- InetAddress addr = InetAddress.getLocalHost();
- String federationName = gitblit.getSettings().getString(Keys.federation.name, null);
- if (StringUtils.isEmpty(federationName)) {
- federationName = addr.getHostName();
- }
- FederationUtils.acknowledgeStatus(addr.getHostAddress(), registration);
- logger.info(MessageFormat.format("Pull status sent to {0}", registration.url));
- }
-}
\ No newline at end of file
+++ /dev/null
-/*\r
- * Copyright 2011 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.io.File;\r
-import java.text.MessageFormat;\r
-import java.util.ArrayList;\r
-import java.util.Date;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Set;\r
-\r
-import javax.inject.Inject;\r
-import javax.inject.Singleton;\r
-import javax.servlet.http.HttpServletResponse;\r
-\r
-import com.gitblit.Constants.FederationRequest;\r
-import com.gitblit.manager.IFederationManager;\r
-import com.gitblit.manager.IRepositoryManager;\r
-import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.manager.IUserManager;\r
-import com.gitblit.models.FederationModel;\r
-import com.gitblit.models.FederationProposal;\r
-import com.gitblit.models.TeamModel;\r
-import com.gitblit.models.UserModel;\r
-import com.gitblit.utils.FederationUtils;\r
-import com.gitblit.utils.FileUtils;\r
-import com.gitblit.utils.HttpUtils;\r
-import com.gitblit.utils.StringUtils;\r
-import com.gitblit.utils.TimeUtils;\r
-\r
-/**\r
- * Handles federation requests.\r
- *\r
- * @author James Moger\r
- *\r
- */\r
-@Singleton\r
-public class FederationServlet extends JsonServlet {\r
-\r
- private static final long serialVersionUID = 1L;\r
-\r
- private final IStoredSettings settings;\r
-\r
- private final IUserManager userManager;\r
-\r
- private final IRepositoryManager repositoryManager;\r
-\r
- private final IFederationManager federationManager;\r
-\r
- @Inject\r
- public FederationServlet(\r
- IRuntimeManager runtimeManager,\r
- IUserManager userManager,\r
- IRepositoryManager repositoryManager,\r
- IFederationManager federationManager) {\r
-\r
- super();\r
- this.settings = runtimeManager.getSettings();\r
- this.userManager = userManager;\r
- this.repositoryManager = repositoryManager;\r
- this.federationManager = federationManager;\r
- }\r
-\r
- /**\r
- * Processes a federation request.\r
- *\r
- * @param request\r
- * @param response\r
- * @throws javax.servlet.ServletException\r
- * @throws java.io.IOException\r
- */\r
-\r
- @Override\r
- protected void processRequest(javax.servlet.http.HttpServletRequest request,\r
- javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,\r
- java.io.IOException {\r
-\r
- FederationRequest reqType = FederationRequest.fromName(request.getParameter("req"));\r
- logger.info(MessageFormat.format("Federation {0} request from {1}", reqType,\r
- request.getRemoteAddr()));\r
-\r
- if (FederationRequest.POKE.equals(reqType)) {\r
- // Gitblit always responds to POKE requests to verify a connection\r
- logger.info("Received federation POKE from " + request.getRemoteAddr());\r
- return;\r
- }\r
-\r
- if (!settings.getBoolean(Keys.git.enableGitServlet, true)) {\r
- logger.warn(Keys.git.enableGitServlet + " must be set TRUE for federation requests.");\r
- response.sendError(HttpServletResponse.SC_FORBIDDEN);\r
- return;\r
- }\r
-\r
- String uuid = settings.getString(Keys.federation.passphrase, "");\r
- if (StringUtils.isEmpty(uuid)) {\r
- logger.warn(Keys.federation.passphrase\r
- + " is not properly set! Federation request denied.");\r
- response.sendError(HttpServletResponse.SC_FORBIDDEN);\r
- return;\r
- }\r
-\r
- if (FederationRequest.PROPOSAL.equals(reqType)) {\r
- // Receive a gitblit federation proposal\r
- FederationProposal proposal = deserialize(request, response, FederationProposal.class);\r
- if (proposal == null) {\r
- return;\r
- }\r
-\r
- // reject proposal, if not receipt prohibited\r
- if (!settings.getBoolean(Keys.federation.allowProposals, false)) {\r
- logger.error(MessageFormat.format("Rejected {0} federation proposal from {1}",\r
- proposal.tokenType.name(), proposal.url));\r
- response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);\r
- return;\r
- }\r
-\r
- // poke the origin Gitblit instance that is proposing federation\r
- boolean poked = false;\r
- try {\r
- poked = FederationUtils.poke(proposal.url);\r
- } catch (Exception e) {\r
- logger.error("Failed to poke origin", e);\r
- }\r
- if (!poked) {\r
- logger.error(MessageFormat.format("Failed to send federation poke to {0}",\r
- proposal.url));\r
- response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);\r
- return;\r
- }\r
-\r
- String url = HttpUtils.getGitblitURL(request);\r
- federationManager.submitFederationProposal(proposal, url);\r
- logger.info(MessageFormat.format(\r
- "Submitted {0} federation proposal to pull {1} repositories from {2}",\r
- proposal.tokenType.name(), proposal.repositories.size(), proposal.url));\r
- response.setStatus(HttpServletResponse.SC_OK);\r
- return;\r
- }\r
-\r
- if (FederationRequest.STATUS.equals(reqType)) {\r
- // Receive a gitblit federation status acknowledgment\r
- String remoteId = StringUtils.decodeFromHtml(request.getParameter("url"));\r
- String identification = MessageFormat.format("{0} ({1})", remoteId,\r
- request.getRemoteAddr());\r
-\r
- // deserialize the status data\r
- FederationModel results = deserialize(request, response, FederationModel.class);\r
- if (results == null) {\r
- return;\r
- }\r
-\r
- // setup the last and netx pull dates\r
- results.lastPull = new Date();\r
- int mins = TimeUtils.convertFrequencyToMinutes(results.frequency);\r
- results.nextPull = new Date(System.currentTimeMillis() + (mins * 60 * 1000L));\r
-\r
- // acknowledge the receipt of status\r
- federationManager.acknowledgeFederationStatus(identification, results);\r
- logger.info(MessageFormat.format(\r
- "Received status of {0} federated repositories from {1}", results\r
- .getStatusList().size(), identification));\r
- response.setStatus(HttpServletResponse.SC_OK);\r
- return;\r
- }\r
-\r
- // Determine the federation tokens for this gitblit instance\r
- String token = request.getParameter("token");\r
- List<String> tokens = federationManager.getFederationTokens();\r
- if (!tokens.contains(token)) {\r
- logger.warn(MessageFormat.format(\r
- "Received Federation token ''{0}'' does not match the server tokens", token));\r
- response.sendError(HttpServletResponse.SC_FORBIDDEN);\r
- return;\r
- }\r
-\r
- Object result = null;\r
- if (FederationRequest.PULL_REPOSITORIES.equals(reqType)) {\r
- String gitblitUrl = HttpUtils.getGitblitURL(request);\r
- result = federationManager.getRepositories(gitblitUrl, token);\r
- } else {\r
- if (FederationRequest.PULL_SETTINGS.equals(reqType)) {\r
- // pull settings\r
- if (!federationManager.validateFederationRequest(reqType, token)) {\r
- // invalid token to pull users or settings\r
- logger.warn(MessageFormat.format(\r
- "Federation token from {0} not authorized to pull SETTINGS",\r
- request.getRemoteAddr()));\r
- response.sendError(HttpServletResponse.SC_FORBIDDEN);\r
- return;\r
- }\r
- Map<String, String> map = new HashMap<String, String>();\r
- List<String> keys = settings.getAllKeys(null);\r
- for (String key : keys) {\r
- map.put(key, settings.getString(key, ""));\r
- }\r
- result = map;\r
- } else if (FederationRequest.PULL_USERS.equals(reqType)) {\r
- // pull users\r
- if (!federationManager.validateFederationRequest(reqType, token)) {\r
- // invalid token to pull users or settings\r
- logger.warn(MessageFormat.format(\r
- "Federation token from {0} not authorized to pull USERS",\r
- request.getRemoteAddr()));\r
- response.sendError(HttpServletResponse.SC_FORBIDDEN);\r
- return;\r
- }\r
- List<String> usernames = userManager.getAllUsernames();\r
- List<UserModel> users = new ArrayList<UserModel>();\r
- for (String username : usernames) {\r
- UserModel user = userManager.getUserModel(username);\r
- if (!user.excludeFromFederation) {\r
- users.add(user);\r
- }\r
- }\r
- result = users;\r
- } else if (FederationRequest.PULL_TEAMS.equals(reqType)) {\r
- // pull teams\r
- if (!federationManager.validateFederationRequest(reqType, token)) {\r
- // invalid token to pull teams\r
- logger.warn(MessageFormat.format(\r
- "Federation token from {0} not authorized to pull TEAMS",\r
- request.getRemoteAddr()));\r
- response.sendError(HttpServletResponse.SC_FORBIDDEN);\r
- return;\r
- }\r
- List<String> teamnames = userManager.getAllTeamNames();\r
- List<TeamModel> teams = new ArrayList<TeamModel>();\r
- for (String teamname : teamnames) {\r
- TeamModel user = userManager.getTeamModel(teamname);\r
- teams.add(user);\r
- }\r
- result = teams;\r
- } else if (FederationRequest.PULL_SCRIPTS.equals(reqType)) {\r
- // pull scripts\r
- if (!federationManager.validateFederationRequest(reqType, token)) {\r
- // invalid token to pull script\r
- logger.warn(MessageFormat.format(\r
- "Federation token from {0} not authorized to pull SCRIPTS",\r
- request.getRemoteAddr()));\r
- response.sendError(HttpServletResponse.SC_FORBIDDEN);\r
- return;\r
- }\r
- Map<String, String> scripts = new HashMap<String, String>();\r
-\r
- Set<String> names = new HashSet<String>();\r
- names.addAll(settings.getStrings(Keys.groovy.preReceiveScripts));\r
- names.addAll(settings.getStrings(Keys.groovy.postReceiveScripts));\r
- for (TeamModel team : userManager.getAllTeams()) {\r
- names.addAll(team.preReceiveScripts);\r
- names.addAll(team.postReceiveScripts);\r
- }\r
- File scriptsFolder = repositoryManager.getHooksFolder();\r
- for (String name : names) {\r
- File file = new File(scriptsFolder, name);\r
- if (!file.exists() && !file.getName().endsWith(".groovy")) {\r
- file = new File(scriptsFolder, name + ".groovy");\r
- }\r
- if (file.exists()) {\r
- // read the script\r
- String content = FileUtils.readContent(file, "\n");\r
- scripts.put(name, content);\r
- } else {\r
- // missing script?!\r
- logger.warn(MessageFormat.format("Failed to find push script \"{0}\"", name));\r
- }\r
- }\r
- result = scripts;\r
- }\r
- }\r
-\r
- // send the result of the request\r
- serialize(response, result);\r
- }\r
-}\r
+++ /dev/null
-/*\r
- * Copyright 2012 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.lang.reflect.Field;\r
-import java.text.MessageFormat;\r
-import java.util.Calendar;\r
-import java.util.Date;\r
-import java.util.Map;\r
-import java.util.Properties;\r
-import java.util.concurrent.ConcurrentHashMap;\r
-import java.util.concurrent.atomic.AtomicBoolean;\r
-import java.util.concurrent.atomic.AtomicInteger;\r
-\r
-import org.eclipse.jgit.api.GarbageCollectCommand;\r
-import org.eclipse.jgit.api.Git;\r
-import org.eclipse.jgit.lib.Repository;\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.gitblit.manager.IRepositoryManager;\r
-import com.gitblit.models.RepositoryModel;\r
-import com.gitblit.utils.FileUtils;\r
-\r
-/**\r
- * The GC executor handles periodic garbage collection in repositories.\r
- *\r
- * @author James Moger\r
- *\r
- */\r
-public class GCExecutor implements Runnable {\r
-\r
- public static enum GCStatus {\r
- READY, COLLECTING;\r
-\r
- public boolean exceeds(GCStatus s) {\r
- return ordinal() > s.ordinal();\r
- }\r
- }\r
- private final Logger logger = LoggerFactory.getLogger(GCExecutor.class);\r
-\r
- private final IStoredSettings settings;\r
-\r
- private final IRepositoryManager repositoryManager;\r
-\r
- private AtomicBoolean running = new AtomicBoolean(false);\r
-\r
- private AtomicBoolean forceClose = new AtomicBoolean(false);\r
-\r
- private final Map<String, GCStatus> gcCache = new ConcurrentHashMap<String, GCStatus>();\r
-\r
- public GCExecutor(\r
- IStoredSettings settings,\r
- IRepositoryManager repositoryManager) {\r
-\r
- this.settings = settings;\r
- this.repositoryManager = repositoryManager;\r
- }\r
-\r
- /**\r
- * Indicates if the GC executor is ready to process repositories.\r
- *\r
- * @return true if the GC executor is ready to process repositories\r
- */\r
- public boolean isReady() {\r
- return settings.getBoolean(Keys.git.enableGarbageCollection, false);\r
- }\r
-\r
- public boolean isRunning() {\r
- return running.get();\r
- }\r
-\r
- public boolean lock(String repositoryName) {\r
- return setGCStatus(repositoryName, GCStatus.COLLECTING);\r
- }\r
-\r
- /**\r
- * Tries to set a GCStatus for the specified repository.\r
- *\r
- * @param repositoryName\r
- * @return true if the status has been set\r
- */\r
- private boolean setGCStatus(String repositoryName, GCStatus status) {\r
- String key = repositoryName.toLowerCase();\r
- if (gcCache.containsKey(key)) {\r
- if (gcCache.get(key).exceeds(GCStatus.READY)) {\r
- // already collecting or blocked\r
- return false;\r
- }\r
- }\r
- gcCache.put(key, status);\r
- return true;\r
- }\r
-\r
- /**\r
- * Returns true if Gitblit is actively collecting garbage in this repository.\r
- *\r
- * @param repositoryName\r
- * @return true if actively collecting garbage\r
- */\r
- public boolean isCollectingGarbage(String repositoryName) {\r
- String key = repositoryName.toLowerCase();\r
- return gcCache.containsKey(key) && GCStatus.COLLECTING.equals(gcCache.get(key));\r
- }\r
-\r
- /**\r
- * Resets the GC status to ready.\r
- *\r
- * @param repositoryName\r
- */\r
- public void releaseLock(String repositoryName) {\r
- gcCache.put(repositoryName.toLowerCase(), GCStatus.READY);\r
- }\r
-\r
- public void close() {\r
- forceClose.set(true);\r
- }\r
-\r
- @Override\r
- public void run() {\r
- if (!isReady()) {\r
- return;\r
- }\r
-\r
- running.set(true);\r
- Date now = new Date();\r
-\r
- for (String repositoryName : repositoryManager.getRepositoryList()) {\r
- if (forceClose.get()) {\r
- break;\r
- }\r
- if (isCollectingGarbage(repositoryName)) {\r
- logger.warn(MessageFormat.format("Already collecting garbage from {0}?!?", repositoryName));\r
- continue;\r
- }\r
- boolean garbageCollected = false;\r
- RepositoryModel model = null;\r
- Repository repository = null;\r
- try {\r
- model = repositoryManager.getRepositoryModel(repositoryName);\r
- repository = repositoryManager.getRepository(repositoryName);\r
- if (repository == null) {\r
- logger.warn(MessageFormat.format("GCExecutor is missing repository {0}?!?", repositoryName));\r
- continue;\r
- }\r
-\r
- if (!isRepositoryIdle(repository)) {\r
- logger.debug(MessageFormat.format("GCExecutor is skipping {0} because it is not idle", repositoryName));\r
- continue;\r
- }\r
-\r
- // By setting the GCStatus to COLLECTING we are\r
- // disabling *all* access to this repository from Gitblit.\r
- // Think of this as a clutch in a manual transmission vehicle.\r
- if (!setGCStatus(repositoryName, GCStatus.COLLECTING)) {\r
- logger.warn(MessageFormat.format("Can not acquire GC lock for {0}, skipping", repositoryName));\r
- continue;\r
- }\r
-\r
- logger.debug(MessageFormat.format("GCExecutor locked idle repository {0}", repositoryName));\r
-\r
- Git git = new Git(repository);\r
- GarbageCollectCommand gc = git.gc();\r
- Properties stats = gc.getStatistics();\r
-\r
- // determine if this is a scheduled GC\r
- Calendar cal = Calendar.getInstance();\r
- cal.setTime(model.lastGC);\r
- cal.set(Calendar.HOUR_OF_DAY, 0);\r
- cal.set(Calendar.MINUTE, 0);\r
- cal.set(Calendar.SECOND, 0);\r
- cal.set(Calendar.MILLISECOND, 0);\r
- cal.add(Calendar.DATE, model.gcPeriod);\r
- Date gcDate = cal.getTime();\r
- boolean shouldCollectGarbage = now.after(gcDate);\r
-\r
- // determine if filesize triggered GC\r
- long gcThreshold = FileUtils.convertSizeToLong(model.gcThreshold, 500*1024L);\r
- long sizeOfLooseObjects = (Long) stats.get("sizeOfLooseObjects");\r
- boolean hasEnoughGarbage = sizeOfLooseObjects >= gcThreshold;\r
-\r
- // if we satisfy one of the requirements, GC\r
- boolean hasGarbage = sizeOfLooseObjects > 0;\r
- if (hasGarbage && (hasEnoughGarbage || shouldCollectGarbage)) {\r
- long looseKB = sizeOfLooseObjects/1024L;\r
- logger.info(MessageFormat.format("Collecting {1} KB of loose objects from {0}", repositoryName, looseKB));\r
-\r
- // do the deed\r
- gc.call();\r
-\r
- garbageCollected = true;\r
- }\r
- } catch (Exception e) {\r
- logger.error("Error collecting garbage in " + repositoryName, e);\r
- } finally {\r
- // cleanup\r
- if (repository != null) {\r
- if (garbageCollected) {\r
- // update the last GC date\r
- model.lastGC = new Date();\r
- repositoryManager.updateConfiguration(repository, model);\r
- }\r
-\r
- repository.close();\r
- }\r
-\r
- // reset the GC lock\r
- releaseLock(repositoryName);\r
- logger.debug(MessageFormat.format("GCExecutor released GC lock for {0}", repositoryName));\r
- }\r
- }\r
-\r
- running.set(false);\r
- }\r
-\r
- private boolean isRepositoryIdle(Repository repository) {\r
- try {\r
- // Read the use count.\r
- // An idle use count is 2:\r
- // +1 for being in the cache\r
- // +1 for the repository parameter in this method\r
- Field useCnt = Repository.class.getDeclaredField("useCnt");\r
- useCnt.setAccessible(true);\r
- int useCount = ((AtomicInteger) useCnt.get(repository)).get();\r
- return useCount == 2;\r
- } catch (Exception e) {\r
- logger.warn(MessageFormat\r
- .format("Failed to reflectively determine use count for repository {0}",\r
- repository.getDirectory().getPath()), e);\r
- }\r
- return false;\r
- }\r
-}\r
+++ /dev/null
-/*
- * Copyright 2011 gitblit.com.
- *
- * 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.gitblit;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.naming.Context;
-import javax.naming.InitialContext;
-import javax.naming.NamingException;
-import javax.servlet.ServletContext;
-import javax.servlet.annotation.WebListener;
-
-import com.gitblit.dagger.DaggerContextListener;
-import com.gitblit.git.GitServlet;
-import com.gitblit.manager.IFederationManager;
-import com.gitblit.manager.IGitblitManager;
-import com.gitblit.manager.IManager;
-import com.gitblit.manager.INotificationManager;
-import com.gitblit.manager.IProjectManager;
-import com.gitblit.manager.IRepositoryManager;
-import com.gitblit.manager.IRuntimeManager;
-import com.gitblit.manager.IServicesManager;
-import com.gitblit.manager.ISessionManager;
-import com.gitblit.manager.IUserManager;
-import com.gitblit.utils.ContainerUtils;
-import com.gitblit.utils.StringUtils;
-import com.gitblit.wicket.GitblitWicketFilter;
-
-import dagger.ObjectGraph;
-
-/**
- * This class is the main entry point for the entire webapp. It is a singleton
- * created manually by Gitblit GO or dynamically by the WAR/Express servlet
- * container. This class instantiates and starts all managers followed by
- * instantiating and registering all servlets and filters.
- *
- * Leveraging Servlet 3 and Dagger static dependency injection allows Gitblit to
- * be modular and completely code-driven rather then relying on the fragility of
- * a web.xml descriptor and the static & monolithic design previously used.
- *
- * @author James Moger
- *
- */
-@WebListener
-public class GitBlit extends DaggerContextListener {
-
- private static GitBlit gitblit;
-
- private final List<IManager> managers = new ArrayList<IManager>();
-
- private final IStoredSettings goSettings;
-
- private final File goBaseFolder;
-
- /**
- * Construct a Gitblit WAR/Express context.
- */
- public GitBlit() {
- this.goSettings = null;
- this.goBaseFolder = null;
- gitblit = this;
- }
-
- /**
- * Construct a Gitblit GO context.
- *
- * @param settings
- * @param baseFolder
- */
- public GitBlit(IStoredSettings settings, File baseFolder) {
- this.goSettings = settings;
- this.goBaseFolder = baseFolder;
- gitblit = this;
- }
-
- /**
- * This method is only used for unit and integration testing.
- *
- * @param managerClass
- * @return a manager
- */
- @SuppressWarnings("unchecked")
- public static <X extends IManager> X getManager(Class<X> managerClass) {
- for (IManager manager : gitblit.managers) {
- if (managerClass.isAssignableFrom(manager.getClass())) {
- return (X) manager;
- }
- }
- return null;
- }
-
- /**
- * Returns Gitblit's Dagger injection modules.
- */
- @Override
- protected Object [] getModules() {
- return new Object [] { new DaggerModule() };
- }
-
- /**
- * Prepare runtime settings and start all manager instances.
- */
- @Override
- protected void beforeServletInjection(ServletContext context) {
- ObjectGraph injector = getInjector(context);
-
- // create the runtime settings object
- IStoredSettings runtimeSettings = injector.get(IStoredSettings.class);
- final File baseFolder;
-
- if (goSettings != null) {
- // Gitblit GO
- baseFolder = configureGO(context, goSettings, goBaseFolder, runtimeSettings);
- } else {
- // servlet container
- WebXmlSettings webxmlSettings = new WebXmlSettings(context);
- String contextRealPath = context.getRealPath("/");
- File contextFolder = (contextRealPath != null) ? new File(contextRealPath) : null;
-
- if (!StringUtils.isEmpty(System.getenv("OPENSHIFT_DATA_DIR"))) {
- // RedHat OpenShift
- baseFolder = configureExpress(context, webxmlSettings, contextFolder, runtimeSettings);
- } else {
- // standard WAR
- baseFolder = configureWAR(context, webxmlSettings, contextFolder, runtimeSettings);
- }
-
- // Test for Tomcat forward-slash/%2F issue and auto-adjust settings
- ContainerUtils.CVE_2007_0450.test(runtimeSettings);
- }
-
- // Manually configure IRuntimeManager
- logManager(IRuntimeManager.class);
- IRuntimeManager runtime = injector.get(IRuntimeManager.class);
- runtime.setBaseFolder(baseFolder);
- runtime.getStatus().isGO = goSettings != null;
- runtime.getStatus().servletContainer = context.getServerInfo();
- runtime.start();
- managers.add(runtime);
-
- // start all other managers
- startManager(injector, INotificationManager.class);
- startManager(injector, IUserManager.class);
- startManager(injector, ISessionManager.class);
- startManager(injector, IRepositoryManager.class);
- startManager(injector, IProjectManager.class);
- startManager(injector, IGitblitManager.class);
- startManager(injector, IFederationManager.class);
- startManager(injector, IServicesManager.class);
-
- logger.info("");
- logger.info("All managers started.");
- logger.info("");
- }
-
- protected <X extends IManager> X startManager(ObjectGraph injector, Class<X> clazz) {
- logManager(clazz);
- X x = injector.get(clazz);
- x.start();
- managers.add(x);
- return x;
- }
-
- protected void logManager(Class<? extends IManager> clazz) {
- logger.info("");
- logger.info("----[{}]----", clazz.getName());
- }
-
- /**
- * Instantiate and inject all filters and servlets into the container using
- * the servlet 3 specification.
- */
- @Override
- protected void injectServlets(ServletContext context) {
- // access restricted servlets
- serve(context, Constants.GIT_PATH, GitServlet.class, GitFilter.class);
- serve(context, Constants.PAGES, PagesServlet.class, PagesFilter.class);
- serve(context, Constants.RPC_PATH, RpcServlet.class, RpcFilter.class);
- serve(context, Constants.ZIP_PATH, DownloadZipServlet.class, DownloadZipFilter.class);
- serve(context, Constants.SYNDICATION_PATH, SyndicationServlet.class, SyndicationFilter.class);
-
- // servlets
- serve(context, Constants.FEDERATION_PATH, FederationServlet.class);
- serve(context, Constants.SPARKLESHARE_INVITE_PATH, SparkleShareInviteServlet.class);
- serve(context, Constants.BRANCH_GRAPH_PATH, BranchGraphServlet.class);
- file(context, "/robots.txt", RobotsTxtServlet.class);
- file(context, "/logo.png", LogoServlet.class);
-
- // optional force basic authentication
- filter(context, "/*", EnforceAuthenticationFilter.class, null);
-
- // Wicket
- String toIgnore = StringUtils.flattenStrings(getRegisteredPaths(), ",");
- Map<String, String> params = new HashMap<String, String>();
- params.put(GitblitWicketFilter.FILTER_MAPPING_PARAM, "/*");
- params.put(GitblitWicketFilter.IGNORE_PATHS_PARAM, toIgnore);
- filter(context, "/*", GitblitWicketFilter.class, params);
- }
-
- /**
- * Gitblit is being shutdown either because the servlet container is
- * shutting down or because the servlet container is re-deploying Gitblit.
- */
- @Override
- protected void destroyContext(ServletContext context) {
- logger.info("Gitblit context destroyed by servlet container.");
- for (IManager manager : managers) {
- logger.debug("stopping {}", manager.getClass().getSimpleName());
- manager.stop();
- }
- }
-
- /**
- * Configures Gitblit GO
- *
- * @param context
- * @param settings
- * @param baseFolder
- * @param runtimeSettings
- * @return the base folder
- */
- protected File configureGO(
- ServletContext context,
- IStoredSettings goSettings,
- File goBaseFolder,
- IStoredSettings runtimeSettings) {
-
- logger.debug("configuring Gitblit GO");
-
- // merge the stored settings into the runtime settings
- //
- // if runtimeSettings is also a FileSettings w/o a specified target file,
- // the target file for runtimeSettings is set to "localSettings".
- runtimeSettings.merge(goSettings);
- File base = goBaseFolder;
- return base;
- }
-
-
- /**
- * Configures a standard WAR instance of Gitblit.
- *
- * @param context
- * @param webxmlSettings
- * @param contextFolder
- * @param runtimeSettings
- * @return the base folder
- */
- protected File configureWAR(
- ServletContext context,
- WebXmlSettings webxmlSettings,
- File contextFolder,
- IStoredSettings runtimeSettings) {
-
- // Gitblit is running in a standard servlet container
- logger.debug("configuring Gitblit WAR");
- logger.info("WAR contextFolder is " + ((contextFolder != null) ? contextFolder.getAbsolutePath() : "<empty>"));
-
- String path = webxmlSettings.getString(Constants.baseFolder, Constants.contextFolder$ + "/WEB-INF/data");
-
- if (path.contains(Constants.contextFolder$) && contextFolder == null) {
- // warn about null contextFolder (issue-199)
- logger.error("");
- logger.error(MessageFormat.format("\"{0}\" depends on \"{1}\" but \"{2}\" is returning NULL for \"{1}\"!",
- Constants.baseFolder, Constants.contextFolder$, context.getServerInfo()));
- logger.error(MessageFormat.format("Please specify a non-parameterized path for <context-param> {0} in web.xml!!", Constants.baseFolder));
- logger.error(MessageFormat.format("OR configure your servlet container to specify a \"{0}\" parameter in the context configuration!!", Constants.baseFolder));
- logger.error("");
- }
-
- try {
- // try to lookup JNDI env-entry for the baseFolder
- InitialContext ic = new InitialContext();
- Context env = (Context) ic.lookup("java:comp/env");
- String val = (String) env.lookup("baseFolder");
- if (!StringUtils.isEmpty(val)) {
- path = val;
- }
- } catch (NamingException n) {
- logger.error("Failed to get JNDI env-entry: " + n.getExplanation());
- }
-
- File base = com.gitblit.utils.FileUtils.resolveParameter(Constants.contextFolder$, contextFolder, path);
- base.mkdirs();
-
- // try to extract the data folder resource to the baseFolder
- File localSettings = new File(base, "gitblit.properties");
- if (!localSettings.exists()) {
- extractResources(context, "/WEB-INF/data/", base);
- }
-
- // delegate all config to baseFolder/gitblit.properties file
- FileSettings fileSettings = new FileSettings(localSettings.getAbsolutePath());
-
- // merge the stored settings into the runtime settings
- //
- // if runtimeSettings is also a FileSettings w/o a specified target file,
- // the target file for runtimeSettings is set to "localSettings".
- runtimeSettings.merge(fileSettings);
-
- return base;
- }
-
- /**
- * Configures an OpenShift instance of Gitblit.
- *
- * @param context
- * @param webxmlSettings
- * @param contextFolder
- * @param runtimeSettings
- * @return the base folder
- */
- private File configureExpress(
- ServletContext context,
- WebXmlSettings webxmlSettings,
- File contextFolder,
- IStoredSettings runtimeSettings) {
-
- // Gitblit is running in OpenShift/JBoss
- logger.debug("configuring Gitblit Express");
- String openShift = System.getenv("OPENSHIFT_DATA_DIR");
- File base = new File(openShift);
- logger.info("EXPRESS contextFolder is " + contextFolder.getAbsolutePath());
-
- // Copy the included scripts to the configured groovy folder
- String path = webxmlSettings.getString(Keys.groovy.scriptsFolder, "groovy");
- File localScripts = com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, base, path);
- if (!localScripts.exists()) {
- File warScripts = new File(contextFolder, "/WEB-INF/data/groovy");
- if (!warScripts.equals(localScripts)) {
- try {
- com.gitblit.utils.FileUtils.copy(localScripts, warScripts.listFiles());
- } catch (IOException e) {
- logger.error(MessageFormat.format(
- "Failed to copy included Groovy scripts from {0} to {1}",
- warScripts, localScripts));
- }
- }
- }
-
- // merge the WebXmlSettings into the runtime settings (for backwards-compatibilty)
- runtimeSettings.merge(webxmlSettings);
-
- // settings are to be stored in openshift/gitblit.properties
- File localSettings = new File(base, "gitblit.properties");
- FileSettings fileSettings = new FileSettings(localSettings.getAbsolutePath());
-
- // merge the stored settings into the runtime settings
- //
- // if runtimeSettings is also a FileSettings w/o a specified target file,
- // the target file for runtimeSettings is set to "localSettings".
- runtimeSettings.merge(fileSettings);
-
- return base;
- }
-
- protected void extractResources(ServletContext context, String path, File toDir) {
- for (String resource : context.getResourcePaths(path)) {
- // extract the resource to the directory if it does not exist
- File f = new File(toDir, resource.substring(path.length()));
- if (!f.exists()) {
- InputStream is = null;
- OutputStream os = null;
- try {
- if (resource.charAt(resource.length() - 1) == '/') {
- // directory
- f.mkdirs();
- extractResources(context, resource, f);
- } else {
- // file
- f.getParentFile().mkdirs();
- is = context.getResourceAsStream(resource);
- os = new FileOutputStream(f);
- byte [] buffer = new byte[4096];
- int len = 0;
- while ((len = is.read(buffer)) > -1) {
- os.write(buffer, 0, len);
- }
- }
- } catch (FileNotFoundException e) {
- logger.error("Failed to find resource \"" + resource + "\"", e);
- } catch (IOException e) {
- logger.error("Failed to copy resource \"" + resource + "\" to " + f, e);
- } finally {
- if (is != null) {
- try {
- is.close();
- } catch (IOException e) {
- // ignore
- }
- }
- if (os != null) {
- try {
- os.close();
- } catch (IOException e) {
- // ignore
- }
- }
- }
- }
- }
- }
-}
import com.beust.jcommander.Parameters;\r
import com.gitblit.authority.GitblitAuthority;\r
import com.gitblit.authority.NewCertificateConfig;\r
+import com.gitblit.servlet.GitblitContext;\r
import com.gitblit.utils.StringUtils;\r
import com.gitblit.utils.TimeUtils;\r
import com.gitblit.utils.X509Utils;\r
}\r
\r
// Setup the Gitblit context\r
- GitBlit gitblit = newGitblit(settings, baseFolder);\r
+ GitblitContext gitblit = newGitblit(settings, baseFolder);\r
rootContext.addEventListener(gitblit);\r
\r
try {\r
}\r
}\r
\r
- protected GitBlit newGitblit(IStoredSettings settings, File baseFolder) {\r
- return new GitBlit(settings, baseFolder);\r
+ protected GitblitContext newGitblit(IStoredSettings settings, File baseFolder) {\r
+ return new GitblitContext(settings, baseFolder);\r
}\r
\r
/**\r
+++ /dev/null
-/*\r
- * Copyright 2011 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.text.MessageFormat;\r
-\r
-import javax.inject.Inject;\r
-import javax.inject.Singleton;\r
-\r
-import com.gitblit.Constants.AccessRestrictionType;\r
-import com.gitblit.Constants.AuthorizationControl;\r
-import com.gitblit.manager.IRepositoryManager;\r
-import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.manager.ISessionManager;\r
-import com.gitblit.models.RepositoryModel;\r
-import com.gitblit.models.UserModel;\r
-import com.gitblit.utils.StringUtils;\r
-\r
-/**\r
- * The GitFilter is an AccessRestrictionFilter which ensures that Git client\r
- * requests for push, clone, or view restricted repositories are authenticated\r
- * and authorized.\r
- *\r
- * @author James Moger\r
- *\r
- */\r
-@Singleton\r
-public class GitFilter extends AccessRestrictionFilter {\r
-\r
- protected static final String gitReceivePack = "/git-receive-pack";\r
-\r
- protected static final String gitUploadPack = "/git-upload-pack";\r
-\r
- protected static final String[] suffixes = { gitReceivePack, gitUploadPack, "/info/refs", "/HEAD",\r
- "/objects" };\r
-\r
- private final IStoredSettings settings;\r
-\r
- @Inject\r
- public GitFilter(\r
- IRuntimeManager runtimeManager,\r
- ISessionManager sessionManager,\r
- IRepositoryManager repositoryManager) {\r
-\r
- super(runtimeManager, sessionManager, repositoryManager);\r
- this.settings = runtimeManager.getSettings();\r
- }\r
-\r
- /**\r
- * Extract the repository name from the url.\r
- *\r
- * @param cloneUrl\r
- * @return repository name\r
- */\r
- public static String getRepositoryName(String value) {\r
- String repository = value;\r
- // get the repository name from the url by finding a known url suffix\r
- for (String urlSuffix : suffixes) {\r
- if (repository.indexOf(urlSuffix) > -1) {\r
- repository = repository.substring(0, repository.indexOf(urlSuffix));\r
- }\r
- }\r
- return repository;\r
- }\r
-\r
- /**\r
- * Extract the repository name from the url.\r
- *\r
- * @param url\r
- * @return repository name\r
- */\r
- @Override\r
- protected String extractRepositoryName(String url) {\r
- return GitFilter.getRepositoryName(url);\r
- }\r
-\r
- /**\r
- * Analyze the url and returns the action of the request. Return values are\r
- * either "/git-receive-pack" or "/git-upload-pack".\r
- *\r
- * @param serverUrl\r
- * @return action of the request\r
- */\r
- @Override\r
- protected String getUrlRequestAction(String suffix) {\r
- if (!StringUtils.isEmpty(suffix)) {\r
- if (suffix.startsWith(gitReceivePack)) {\r
- return gitReceivePack;\r
- } else if (suffix.startsWith(gitUploadPack)) {\r
- return gitUploadPack;\r
- } else if (suffix.contains("?service=git-receive-pack")) {\r
- return gitReceivePack;\r
- } else if (suffix.contains("?service=git-upload-pack")) {\r
- return gitUploadPack;\r
- } else {\r
- return gitUploadPack;\r
- }\r
- }\r
- return null;\r
- }\r
-\r
- /**\r
- * Determine if a non-existing repository can be created using this filter.\r
- *\r
- * @return true if the server allows repository creation on-push\r
- */\r
- @Override\r
- protected boolean isCreationAllowed() {\r
- return settings.getBoolean(Keys.git.allowCreateOnPush, true);\r
- }\r
-\r
- /**\r
- * Determine if the repository can receive pushes.\r
- *\r
- * @param repository\r
- * @param action\r
- * @return true if the action may be performed\r
- */\r
- @Override\r
- protected boolean isActionAllowed(RepositoryModel repository, String action) {\r
- // the log here has been moved into ReceiveHook to provide clients with\r
- // error messages\r
- return true;\r
- }\r
-\r
- @Override\r
- protected boolean requiresClientCertificate() {\r
- return settings.getBoolean(Keys.git.requiresClientCertificate, false);\r
- }\r
-\r
- /**\r
- * Determine if the repository requires authentication.\r
- *\r
- * @param repository\r
- * @param action\r
- * @return true if authentication required\r
- */\r
- @Override\r
- protected boolean requiresAuthentication(RepositoryModel repository, String action) {\r
- if (gitUploadPack.equals(action)) {\r
- // send to client\r
- return repository.accessRestriction.atLeast(AccessRestrictionType.CLONE);\r
- } else if (gitReceivePack.equals(action)) {\r
- // receive from client\r
- return repository.accessRestriction.atLeast(AccessRestrictionType.PUSH);\r
- }\r
- return false;\r
- }\r
-\r
- /**\r
- * Determine if the user can access the repository and perform the specified\r
- * action.\r
- *\r
- * @param repository\r
- * @param user\r
- * @param action\r
- * @return true if user may execute the action on the repository\r
- */\r
- @Override\r
- protected boolean canAccess(RepositoryModel repository, UserModel user, String action) {\r
- if (!settings.getBoolean(Keys.git.enableGitServlet, true)) {\r
- // Git Servlet disabled\r
- return false;\r
- }\r
- if (action.equals(gitReceivePack)) {\r
- // Push request\r
- if (user.canPush(repository)) {\r
- return true;\r
- } else {\r
- // user is unauthorized to push to this repository\r
- logger.warn(MessageFormat.format("user {0} is not authorized to push to {1}",\r
- user.username, repository));\r
- return false;\r
- }\r
- } else if (action.equals(gitUploadPack)) {\r
- // Clone request\r
- if (user.canClone(repository)) {\r
- return true;\r
- } else {\r
- // user is unauthorized to clone this repository\r
- logger.warn(MessageFormat.format("user {0} is not authorized to clone {1}",\r
- user.username, repository));\r
- return false;\r
- }\r
- }\r
- return true;\r
- }\r
-\r
- /**\r
- * An authenticated user with the CREATE role can create a repository on\r
- * push.\r
- *\r
- * @param user\r
- * @param repository\r
- * @param action\r
- * @return the repository model, if it is created, null otherwise\r
- */\r
- @Override\r
- protected RepositoryModel createRepository(UserModel user, String repository, String action) {\r
- boolean isPush = !StringUtils.isEmpty(action) && gitReceivePack.equals(action);\r
- if (isPush) {\r
- if (user.canCreate(repository)) {\r
- // user is pushing to a new repository\r
- // validate name\r
- if (repository.startsWith("../")) {\r
- logger.error(MessageFormat.format("Illegal relative path in repository name! {0}", repository));\r
- return null;\r
- }\r
- if (repository.contains("/../")) {\r
- logger.error(MessageFormat.format("Illegal relative path in repository name! {0}", repository));\r
- return null;\r
- }\r
-\r
- // confirm valid characters in repository name\r
- Character c = StringUtils.findInvalidCharacter(repository);\r
- if (c != null) {\r
- logger.error(MessageFormat.format("Invalid character '{0}' in repository name {1}!", c, repository));\r
- return null;\r
- }\r
-\r
- // create repository\r
- RepositoryModel model = new RepositoryModel();\r
- model.name = repository;\r
- model.addOwner(user.username);\r
- model.projectPath = StringUtils.getFirstPathElement(repository);\r
- if (model.isUsersPersonalRepository(user.username)) {\r
- // personal repository, default to private for user\r
- model.authorizationControl = AuthorizationControl.NAMED;\r
- model.accessRestriction = AccessRestrictionType.VIEW;\r
- } else {\r
- // common repository, user default server settings\r
- model.authorizationControl = AuthorizationControl.fromName(settings.getString(Keys.git.defaultAuthorizationControl, ""));\r
- model.accessRestriction = AccessRestrictionType.fromName(settings.getString(Keys.git.defaultAccessRestriction, "PUSH"));\r
- }\r
-\r
- // create the repository\r
- try {\r
- repositoryManager.updateRepositoryModel(model.name, model, true);\r
- logger.info(MessageFormat.format("{0} created {1} ON-PUSH", user.username, model.name));\r
- return repositoryManager.getRepositoryModel(model.name);\r
- } catch (GitBlitException e) {\r
- logger.error(MessageFormat.format("{0} failed to create repository {1} ON-PUSH!", user.username, model.name), e);\r
- }\r
- } else {\r
- logger.warn(MessageFormat.format("{0} is not permitted to create repository {1} ON-PUSH!", user.username, repository));\r
- }\r
- }\r
-\r
- // repository could not be created or action was not a push\r
- return null;\r
- }\r
-}\r
+++ /dev/null
-/*
- * Copyright 2013 gitblit.com.
- *
- * 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.gitblit;
-
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Map;
-
-import javax.servlet.DispatcherType;
-import javax.servlet.Filter;
-import javax.servlet.FilterRegistration;
-import javax.servlet.Servlet;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletContextEvent;
-import javax.servlet.ServletContextListener;
-import javax.servlet.ServletRegistration;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Injection context listener instantiates and injects servlets, filters, and
- * anything else you might want into a servlet context. This class provides
- * convenience methods for servlet & filter registration and also tracks
- * registered paths.
- *
- * @author James Moger
- *
- */
-public abstract class InjectionContextListener implements ServletContextListener {
-
- protected final Logger logger = LoggerFactory.getLogger(getClass());
-
- private final List<String> registeredPaths = new ArrayList<String>();
-
- protected final List<String> getRegisteredPaths() {
- return registeredPaths;
- }
-
- /**
- * Hook for subclasses to manipulate context initialization before
- * standard initialization procedure.
- *
- * @param context
- */
- protected void beforeServletInjection(ServletContext context) {
- // NOOP
- }
-
- /**
- * Hook for subclasses to instantiate and inject servlets and filters
- * into the servlet context.
- *
- * @param context
- */
- protected abstract void injectServlets(ServletContext context);
-
- /**
- * Hook for subclasses to manipulate context initialization after
- * servlet registration.
- *
- * @param context
- */
- protected void afterServletInjection(ServletContext context) {
- // NOOP
- }
-
- /**
- * Configure Gitblit from the web.xml, if no configuration has already been
- * specified.
- *
- * @see ServletContextListener.contextInitialize(ServletContextEvent)
- */
- @Override
- public final void contextInitialized(ServletContextEvent contextEvent) {
- ServletContext context = contextEvent.getServletContext();
- beforeServletInjection(context);
- injectServlets(context);
- afterServletInjection(context);
- }
-
-
- /**
- * Registers a file path.
- *
- * @param context
- * @param file
- * @param servletClass
- */
- protected void file(ServletContext context, String file, Class<? extends Servlet> servletClass) {
- file(context, file, servletClass, null);
- }
-
- /**
- * Registers a file path with init parameters.
- *
- * @param context
- * @param file
- * @param servletClass
- * @param initParams
- */
- protected void file(ServletContext context, String file, Class<? extends Servlet> servletClass, Map<String, String> initParams) {
- Servlet servlet = instantiate(context, servletClass);
- ServletRegistration.Dynamic d = context.addServlet(sanitize(servletClass.getSimpleName() + file), servlet);
- d.addMapping(file);
- if (initParams != null) {
- d.setInitParameters(initParams);
- }
- registeredPaths.add(file);
- }
-
- /**
- * Serves a path (trailing wildcard will be appended).
- *
- * @param context
- * @param route
- * @param servletClass
- */
- protected void serve(ServletContext context, String route, Class<? extends Servlet> servletClass) {
- serve(context, route, servletClass, (Class<Filter>) null);
- }
-
- /**
- * Serves a path (trailing wildcard will be appended) with init parameters.
- *
- * @param context
- * @param route
- * @param servletClass
- * @param initParams
- */
- protected void serve(ServletContext context, String route, Class<? extends Servlet> servletClass, Map<String, String> initParams) {
- Servlet servlet = instantiate(context, servletClass);
- ServletRegistration.Dynamic d = context.addServlet(sanitize(servletClass.getSimpleName() + route), servlet);
- d.addMapping(route + "*");
- if (initParams != null) {
- d.setInitParameters(initParams);
- }
- registeredPaths.add(route);
- }
-
- /**
- * Serves a path (trailing wildcard will be appended) and also maps a filter
- * to that path.
- *
- * @param context
- * @param route
- * @param servletClass
- * @param filterClass
- */
- protected void serve(ServletContext context, String route, Class<? extends Servlet> servletClass, Class<? extends Filter> filterClass) {
- Servlet servlet = instantiate(context, servletClass);
- ServletRegistration.Dynamic d = context.addServlet(sanitize(servletClass.getSimpleName() + route), servlet);
- d.addMapping(route + "*");
- if (filterClass != null) {
- filter(context, route + "*", filterClass);
- }
- registeredPaths.add(route);
- }
-
- /**
- * Registers a path filter.
- *
- * @param context
- * @param route
- * @param filterClass
- */
- protected void filter(ServletContext context, String route, Class<? extends Filter> filterClass) {
- filter(context, route, filterClass, null);
- }
-
- /**
- * Registers a path filter with init parameters.
- *
- * @param context
- * @param route
- * @param filterClass
- * @param initParams
- */
- protected void filter(ServletContext context, String route, Class<? extends Filter> filterClass, Map<String, String> initParams) {
- Filter filter = instantiate(context, filterClass);
- FilterRegistration.Dynamic d = context.addFilter(sanitize(filterClass.getSimpleName() + route), filter);
- d.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, route);
- if (initParams != null) {
- d.setInitParameters(initParams);
- }
- }
-
- /**
- * Limit the generated servlet/filter names to alpha-numeric values with a
- * handful of acceptable other characters.
- *
- * @param name
- * @return a sanitized name
- */
- protected String sanitize(String name) {
- StringBuilder sb = new StringBuilder();
- for (char c : name.toCharArray()) {
- if (Character.isLetterOrDigit(c)) {
- sb.append(c);
- } else if ('-' == c) {
- sb.append(c);
- } else if ('*' == c) {
- sb.append("all");
- } else if ('.' == c) {
- sb.append('.');
- } else {
- sb.append('_');
- }
- }
- return sb.toString();
- }
-
- /**
- * Instantiates an object.
- *
- * @param clazz
- * @return the object
- */
- protected <X> X instantiate(ServletContext context, Class<X> clazz) {
- try {
- return clazz.newInstance();
- } catch (Throwable t) {
- logger.error(null, t);
- }
- return null;
- }
-}
+++ /dev/null
-/*\r
- * Copyright 2011 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.io.BufferedReader;\r
-import java.io.IOException;\r
-import java.lang.reflect.Type;\r
-import java.text.MessageFormat;\r
-\r
-import javax.servlet.ServletException;\r
-import javax.servlet.http.HttpServlet;\r
-import javax.servlet.http.HttpServletRequest;\r
-import javax.servlet.http.HttpServletResponse;\r
-\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.gitblit.utils.JsonUtils;\r
-import com.gitblit.utils.StringUtils;\r
-\r
-/**\r
- * Servlet class for interpreting json requests.\r
- *\r
- * @author James Moger\r
- *\r
- */\r
-public abstract class JsonServlet extends HttpServlet {\r
-\r
- private static final long serialVersionUID = 1L;\r
-\r
- protected final int forbiddenCode = HttpServletResponse.SC_FORBIDDEN;\r
-\r
- protected final int notAllowedCode = HttpServletResponse.SC_METHOD_NOT_ALLOWED;\r
-\r
- protected final int failureCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;\r
-\r
- protected final Logger logger;\r
-\r
- public JsonServlet() {\r
- super();\r
- logger = LoggerFactory.getLogger(getClass());\r
- }\r
-\r
- /**\r
- * Processes an gson request.\r
- *\r
- * @param request\r
- * @param response\r
- * @throws javax.servlet.ServletException\r
- * @throws java.io.IOException\r
- */\r
- protected abstract void processRequest(HttpServletRequest request, HttpServletResponse response)\r
- throws ServletException, IOException;\r
-\r
- @Override\r
- protected void doPost(HttpServletRequest request, HttpServletResponse response)\r
- throws ServletException, java.io.IOException {\r
- processRequest(request, response);\r
- }\r
-\r
- @Override\r
- protected void doGet(HttpServletRequest request, HttpServletResponse response)\r
- throws ServletException, IOException {\r
- processRequest(request, response);\r
- }\r
-\r
- protected <X> X deserialize(HttpServletRequest request, HttpServletResponse response,\r
- Class<X> clazz) throws IOException {\r
- String json = readJson(request, response);\r
- if (StringUtils.isEmpty(json)) {\r
- return null;\r
- }\r
-\r
- X object = JsonUtils.fromJsonString(json.toString(), clazz);\r
- return object;\r
- }\r
-\r
- protected <X> X deserialize(HttpServletRequest request, HttpServletResponse response, Type type)\r
- throws IOException {\r
- String json = readJson(request, response);\r
- if (StringUtils.isEmpty(json)) {\r
- return null;\r
- }\r
-\r
- X object = JsonUtils.fromJsonString(json.toString(), type);\r
- return object;\r
- }\r
-\r
- private String readJson(HttpServletRequest request, HttpServletResponse response)\r
- throws IOException {\r
- BufferedReader reader = request.getReader();\r
- StringBuilder json = new StringBuilder();\r
- String line = null;\r
- while ((line = reader.readLine()) != null) {\r
- json.append(line);\r
- }\r
- reader.close();\r
-\r
- if (json.length() == 0) {\r
- logger.error(MessageFormat.format("Failed to receive json data from {0}",\r
- request.getRemoteAddr()));\r
- response.setStatus(HttpServletResponse.SC_BAD_REQUEST);\r
- return null;\r
- }\r
- return json.toString();\r
- }\r
-\r
- protected void serialize(HttpServletResponse response, Object o) throws IOException {\r
- if (o != null) {\r
- // Send JSON response\r
- String json = JsonUtils.toJsonString(o);\r
- response.setCharacterEncoding(Constants.ENCODING);\r
- response.setContentType("application/json");\r
- response.getWriter().append(json);\r
- }\r
- }\r
-}\r
+++ /dev/null
-/*\r
- * Copyright 2013 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.io.File;\r
-import java.io.FileInputStream;\r
-import java.io.IOException;\r
-import java.io.InputStream;\r
-import java.io.OutputStream;\r
-\r
-import javax.inject.Inject;\r
-import javax.inject.Singleton;\r
-import javax.servlet.ServletContext;\r
-import javax.servlet.ServletException;\r
-import javax.servlet.http.HttpServlet;\r
-import javax.servlet.http.HttpServletRequest;\r
-import javax.servlet.http.HttpServletResponse;\r
-\r
-import com.gitblit.manager.IRuntimeManager;\r
-\r
-/**\r
- * Handles requests for logo.png\r
- *\r
- * @author James Moger\r
- *\r
- */\r
-@Singleton\r
-public class LogoServlet extends HttpServlet {\r
-\r
- private static final long serialVersionUID = 1L;\r
-\r
- private static final long lastModified = System.currentTimeMillis();\r
-\r
- private final IRuntimeManager runtimeManager;\r
-\r
- @Inject\r
- public LogoServlet(IRuntimeManager runtimeManager) {\r
- super();\r
- this.runtimeManager = runtimeManager;\r
- }\r
-\r
- @Override\r
- protected long getLastModified(HttpServletRequest req) {\r
- File file = runtimeManager.getFileOrFolder(Keys.web.headerLogo, "${baseFolder}/logo.png");\r
- if (file.exists()) {\r
- return Math.max(lastModified, file.lastModified());\r
- } else {\r
- return lastModified;\r
- }\r
- }\r
-\r
- @Override\r
- protected void doGet(HttpServletRequest request, HttpServletResponse response)\r
- throws ServletException, IOException {\r
- InputStream is = null;\r
- try {\r
- String contentType = null;\r
- File file = runtimeManager.getFileOrFolder(Keys.web.headerLogo, "${baseFolder}/logo.png");\r
- if (file.exists()) {\r
- // custom logo\r
- ServletContext context = request.getSession().getServletContext();\r
- contentType = context.getMimeType(file.getName());\r
- response.setContentLength((int) file.length());\r
- response.setDateHeader("Last-Modified", Math.max(lastModified, file.lastModified()));\r
- is = new FileInputStream(file);\r
- } else {\r
- // default logo\r
- response.setDateHeader("Last-Modified", lastModified);\r
- is = getClass().getResourceAsStream("/logo.png");\r
- }\r
- if (contentType == null) {\r
- contentType = "image/png";\r
- }\r
- response.setContentType(contentType);\r
- response.setHeader("Cache-Control", "public, max-age=3600, must-revalidate");\r
- OutputStream os = response.getOutputStream();\r
- byte[] buf = new byte[4096];\r
- int bytesRead = is.read(buf);\r
- while (bytesRead != -1) {\r
- os.write(buf, 0, bytesRead);\r
- bytesRead = is.read(buf);\r
- }\r
- os.flush();\r
- } catch (Exception e) {\r
- e.printStackTrace();\r
- } finally {\r
- if (is != null) {\r
- is.close();\r
- }\r
- }\r
- }\r
-}\r
+++ /dev/null
-/*\r
- * Copyright 2012 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import static org.eclipse.jgit.treewalk.filter.TreeFilter.ANY_DIFF;\r
-\r
-import java.io.ByteArrayOutputStream;\r
-import java.io.File;\r
-import java.io.IOException;\r
-import java.io.InputStream;\r
-import java.lang.reflect.Method;\r
-import java.text.MessageFormat;\r
-import java.text.ParseException;\r
-import java.util.ArrayList;\r
-import java.util.Collections;\r
-import java.util.Comparator;\r
-import java.util.HashMap;\r
-import java.util.LinkedHashSet;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Set;\r
-import java.util.TreeMap;\r
-import java.util.TreeSet;\r
-import java.util.concurrent.ConcurrentHashMap;\r
-\r
-import org.apache.lucene.analysis.Analyzer;\r
-import org.apache.lucene.analysis.standard.StandardAnalyzer;\r
-import org.apache.lucene.document.DateTools;\r
-import org.apache.lucene.document.DateTools.Resolution;\r
-import org.apache.lucene.document.Document;\r
-import org.apache.lucene.document.Field;\r
-import org.apache.lucene.document.Field.Index;\r
-import org.apache.lucene.document.Field.Store;\r
-import org.apache.lucene.index.IndexReader;\r
-import org.apache.lucene.index.IndexWriter;\r
-import org.apache.lucene.index.IndexWriterConfig;\r
-import org.apache.lucene.index.IndexWriterConfig.OpenMode;\r
-import org.apache.lucene.index.MultiReader;\r
-import org.apache.lucene.index.Term;\r
-import org.apache.lucene.queryParser.QueryParser;\r
-import org.apache.lucene.search.BooleanClause.Occur;\r
-import org.apache.lucene.search.BooleanQuery;\r
-import org.apache.lucene.search.IndexSearcher;\r
-import org.apache.lucene.search.Query;\r
-import org.apache.lucene.search.ScoreDoc;\r
-import org.apache.lucene.search.TopScoreDocCollector;\r
-import org.apache.lucene.search.highlight.Fragmenter;\r
-import org.apache.lucene.search.highlight.Highlighter;\r
-import org.apache.lucene.search.highlight.InvalidTokenOffsetsException;\r
-import org.apache.lucene.search.highlight.QueryScorer;\r
-import org.apache.lucene.search.highlight.SimpleHTMLFormatter;\r
-import org.apache.lucene.search.highlight.SimpleSpanFragmenter;\r
-import org.apache.lucene.store.Directory;\r
-import org.apache.lucene.store.FSDirectory;\r
-import org.apache.lucene.util.Version;\r
-import org.eclipse.jgit.diff.DiffEntry.ChangeType;\r
-import org.eclipse.jgit.lib.Constants;\r
-import org.eclipse.jgit.lib.FileMode;\r
-import org.eclipse.jgit.lib.ObjectId;\r
-import org.eclipse.jgit.lib.ObjectLoader;\r
-import org.eclipse.jgit.lib.ObjectReader;\r
-import org.eclipse.jgit.lib.Repository;\r
-import org.eclipse.jgit.lib.RepositoryCache.FileKey;\r
-import org.eclipse.jgit.revwalk.RevCommit;\r
-import org.eclipse.jgit.revwalk.RevTree;\r
-import org.eclipse.jgit.revwalk.RevWalk;\r
-import org.eclipse.jgit.storage.file.FileBasedConfig;\r
-import org.eclipse.jgit.treewalk.EmptyTreeIterator;\r
-import org.eclipse.jgit.treewalk.TreeWalk;\r
-import org.eclipse.jgit.util.FS;\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.gitblit.Constants.SearchObjectType;\r
-import com.gitblit.manager.IRepositoryManager;\r
-import com.gitblit.models.PathModel.PathChangeModel;\r
-import com.gitblit.models.RefModel;\r
-import com.gitblit.models.RepositoryModel;\r
-import com.gitblit.models.SearchResult;\r
-import com.gitblit.utils.ArrayUtils;\r
-import com.gitblit.utils.JGitUtils;\r
-import com.gitblit.utils.StringUtils;\r
-\r
-/**\r
- * The Lucene executor handles indexing and searching repositories.\r
- *\r
- * @author James Moger\r
- *\r
- */\r
-public class LuceneExecutor implements Runnable {\r
-\r
-\r
- private static final int INDEX_VERSION = 5;\r
-\r
- private static final String FIELD_OBJECT_TYPE = "type";\r
- private static final String FIELD_PATH = "path";\r
- private static final String FIELD_COMMIT = "commit";\r
- private static final String FIELD_BRANCH = "branch";\r
- private static final String FIELD_SUMMARY = "summary";\r
- private static final String FIELD_CONTENT = "content";\r
- private static final String FIELD_AUTHOR = "author";\r
- private static final String FIELD_COMMITTER = "committer";\r
- private static final String FIELD_DATE = "date";\r
- private static final String FIELD_TAG = "tag";\r
-\r
- private static final String CONF_FILE = "lucene.conf";\r
- private static final String LUCENE_DIR = "lucene";\r
- private static final String CONF_INDEX = "index";\r
- private static final String CONF_VERSION = "version";\r
- private static final String CONF_ALIAS = "aliases";\r
- private static final String CONF_BRANCH = "branches";\r
-\r
- private static final Version LUCENE_VERSION = Version.LUCENE_35;\r
-\r
- private final Logger logger = LoggerFactory.getLogger(LuceneExecutor.class);\r
-\r
- private final IStoredSettings storedSettings;\r
- private final IRepositoryManager repositoryManager;\r
- private final File repositoriesFolder;\r
-\r
- private final Map<String, IndexSearcher> searchers = new ConcurrentHashMap<String, IndexSearcher>();\r
- private final Map<String, IndexWriter> writers = new ConcurrentHashMap<String, IndexWriter>();\r
-\r
- private final String luceneIgnoreExtensions = "7z arc arj bin bmp dll doc docx exe gif gz jar jpg lib lzh odg odf odt pdf ppt png so swf xcf xls xlsx zip";\r
- private Set<String> excludedExtensions;\r
-\r
- public LuceneExecutor(\r
- IStoredSettings settings,\r
- IRepositoryManager repositoryManager) {\r
-\r
- this.storedSettings = settings;\r
- this.repositoryManager = repositoryManager;\r
- this.repositoriesFolder = repositoryManager.getRepositoriesFolder();\r
- String exts = luceneIgnoreExtensions;\r
- if (settings != null) {\r
- exts = settings.getString(Keys.web.luceneIgnoreExtensions, exts);\r
- }\r
- excludedExtensions = new TreeSet<String>(StringUtils.getStringsFromValue(exts));\r
- }\r
-\r
- /**\r
- * Run is executed by the Gitblit executor service. Because this is called\r
- * by an executor service, calls will queue - i.e. there can never be\r
- * concurrent execution of repository index updates.\r
- */\r
- @Override\r
- public void run() {\r
- if (!storedSettings.getBoolean(Keys.web.allowLuceneIndexing, true)) {\r
- // Lucene indexing is disabled\r
- return;\r
- }\r
- // reload the excluded extensions\r
- String exts = storedSettings.getString(Keys.web.luceneIgnoreExtensions, luceneIgnoreExtensions);\r
- excludedExtensions = new TreeSet<String>(StringUtils.getStringsFromValue(exts));\r
-\r
- if (repositoryManager.isCollectingGarbage()) {\r
- // busy collecting garbage, try again later\r
- return;\r
- }\r
-\r
- for (String repositoryName: repositoryManager.getRepositoryList()) {\r
- RepositoryModel model = repositoryManager.getRepositoryModel(repositoryName);\r
- if (model.hasCommits && !ArrayUtils.isEmpty(model.indexedBranches)) {\r
- Repository repository = repositoryManager.getRepository(model.name);\r
- if (repository == null) {\r
- if (repositoryManager.isCollectingGarbage(model.name)) {\r
- logger.info(MessageFormat.format("Skipping Lucene index of {0}, busy garbage collecting", repositoryName));\r
- }\r
- continue;\r
- }\r
- index(model, repository);\r
- repository.close();\r
- System.gc();\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Synchronously indexes a repository. This may build a complete index of a\r
- * repository or it may update an existing index.\r
- *\r
- * @param name\r
- * the name of the repository\r
- * @param repository\r
- * the repository object\r
- */\r
- private void index(RepositoryModel model, Repository repository) {\r
- try {\r
- if (shouldReindex(repository)) {\r
- // (re)build the entire index\r
- IndexResult result = reindex(model, repository);\r
-\r
- if (result.success) {\r
- if (result.commitCount > 0) {\r
- String msg = "Built {0} Lucene index from {1} commits and {2} files across {3} branches in {4} secs";\r
- logger.info(MessageFormat.format(msg, model.name, result.commitCount,\r
- result.blobCount, result.branchCount, result.duration()));\r
- }\r
- } else {\r
- String msg = "Could not build {0} Lucene index!";\r
- logger.error(MessageFormat.format(msg, model.name));\r
- }\r
- } else {\r
- // update the index with latest commits\r
- IndexResult result = updateIndex(model, repository);\r
- if (result.success) {\r
- if (result.commitCount > 0) {\r
- String msg = "Updated {0} Lucene index with {1} commits and {2} files across {3} branches in {4} secs";\r
- logger.info(MessageFormat.format(msg, model.name, result.commitCount,\r
- result.blobCount, result.branchCount, result.duration()));\r
- }\r
- } else {\r
- String msg = "Could not update {0} Lucene index!";\r
- logger.error(MessageFormat.format(msg, model.name));\r
- }\r
- }\r
- } catch (Throwable t) {\r
- logger.error(MessageFormat.format("Lucene indexing failure for {0}", model.name), t);\r
- }\r
- }\r
-\r
- /**\r
- * Close the writer/searcher objects for a repository.\r
- *\r
- * @param repositoryName\r
- */\r
- public synchronized void close(String repositoryName) {\r
- try {\r
- IndexSearcher searcher = searchers.remove(repositoryName);\r
- if (searcher != null) {\r
- searcher.getIndexReader().close();\r
- }\r
- } catch (Exception e) {\r
- logger.error("Failed to close index searcher for " + repositoryName, e);\r
- }\r
-\r
- try {\r
- IndexWriter writer = writers.remove(repositoryName);\r
- if (writer != null) {\r
- writer.close();\r
- }\r
- } catch (Exception e) {\r
- logger.error("Failed to close index writer for " + repositoryName, e);\r
- }\r
- }\r
-\r
- /**\r
- * Close all Lucene indexers.\r
- *\r
- */\r
- public synchronized void close() {\r
- // close all writers\r
- for (String writer : writers.keySet()) {\r
- try {\r
- writers.get(writer).close(true);\r
- } catch (Throwable t) {\r
- logger.error("Failed to close Lucene writer for " + writer, t);\r
- }\r
- }\r
- writers.clear();\r
-\r
- // close all searchers\r
- for (String searcher : searchers.keySet()) {\r
- try {\r
- searchers.get(searcher).getIndexReader().close();\r
- } catch (Throwable t) {\r
- logger.error("Failed to close Lucene searcher for " + searcher, t);\r
- }\r
- }\r
- searchers.clear();\r
- }\r
-\r
-\r
- /**\r
- * Deletes the Lucene index for the specified repository.\r
- *\r
- * @param repositoryName\r
- * @return true, if successful\r
- */\r
- public boolean deleteIndex(String repositoryName) {\r
- try {\r
- // close any open writer/searcher\r
- close(repositoryName);\r
-\r
- // delete the index folder\r
- File repositoryFolder = FileKey.resolve(new File(repositoriesFolder, repositoryName), FS.DETECTED);\r
- File luceneIndex = new File(repositoryFolder, LUCENE_DIR);\r
- if (luceneIndex.exists()) {\r
- org.eclipse.jgit.util.FileUtils.delete(luceneIndex,\r
- org.eclipse.jgit.util.FileUtils.RECURSIVE);\r
- }\r
- // delete the config file\r
- File luceneConfig = new File(repositoryFolder, CONF_FILE);\r
- if (luceneConfig.exists()) {\r
- luceneConfig.delete();\r
- }\r
- return true;\r
- } catch (IOException e) {\r
- throw new RuntimeException(e);\r
- }\r
- }\r
-\r
- /**\r
- * Returns the author for the commit, if this information is available.\r
- *\r
- * @param commit\r
- * @return an author or unknown\r
- */\r
- private String getAuthor(RevCommit commit) {\r
- String name = "unknown";\r
- try {\r
- name = commit.getAuthorIdent().getName();\r
- if (StringUtils.isEmpty(name)) {\r
- name = commit.getAuthorIdent().getEmailAddress();\r
- }\r
- } catch (NullPointerException n) {\r
- }\r
- return name;\r
- }\r
-\r
- /**\r
- * Returns the committer for the commit, if this information is available.\r
- *\r
- * @param commit\r
- * @return an committer or unknown\r
- */\r
- private String getCommitter(RevCommit commit) {\r
- String name = "unknown";\r
- try {\r
- name = commit.getCommitterIdent().getName();\r
- if (StringUtils.isEmpty(name)) {\r
- name = commit.getCommitterIdent().getEmailAddress();\r
- }\r
- } catch (NullPointerException n) {\r
- }\r
- return name;\r
- }\r
-\r
- /**\r
- * Get the tree associated with the given commit.\r
- *\r
- * @param walk\r
- * @param commit\r
- * @return tree\r
- * @throws IOException\r
- */\r
- private RevTree getTree(final RevWalk walk, final RevCommit commit)\r
- throws IOException {\r
- final RevTree tree = commit.getTree();\r
- if (tree != null) {\r
- return tree;\r
- }\r
- walk.parseHeaders(commit);\r
- return commit.getTree();\r
- }\r
-\r
- /**\r
- * Construct a keyname from the branch.\r
- *\r
- * @param branchName\r
- * @return a keyname appropriate for the Git config file format\r
- */\r
- private String getBranchKey(String branchName) {\r
- return StringUtils.getSHA1(branchName);\r
- }\r
-\r
- /**\r
- * Returns the Lucene configuration for the specified repository.\r
- *\r
- * @param repository\r
- * @return a config object\r
- */\r
- private FileBasedConfig getConfig(Repository repository) {\r
- File file = new File(repository.getDirectory(), CONF_FILE);\r
- FileBasedConfig config = new FileBasedConfig(file, FS.detect());\r
- return config;\r
- }\r
-\r
- /**\r
- * Reads the Lucene config file for the repository to check the index\r
- * version. If the index version is different, then rebuild the repository\r
- * index.\r
- *\r
- * @param repository\r
- * @return true of the on-disk index format is different than INDEX_VERSION\r
- */\r
- private boolean shouldReindex(Repository repository) {\r
- try {\r
- FileBasedConfig config = getConfig(repository);\r
- config.load();\r
- int indexVersion = config.getInt(CONF_INDEX, CONF_VERSION, 0);\r
- // reindex if versions do not match\r
- return indexVersion != INDEX_VERSION;\r
- } catch (Throwable t) {\r
- }\r
- return true;\r
- }\r
-\r
-\r
- /**\r
- * This completely indexes the repository and will destroy any existing\r
- * index.\r
- *\r
- * @param repositoryName\r
- * @param repository\r
- * @return IndexResult\r
- */\r
- public IndexResult reindex(RepositoryModel model, Repository repository) {\r
- IndexResult result = new IndexResult();\r
- if (!deleteIndex(model.name)) {\r
- return result;\r
- }\r
- try {\r
- String [] encodings = storedSettings.getStrings(Keys.web.blobEncodings).toArray(new String[0]);\r
- FileBasedConfig config = getConfig(repository);\r
- Set<String> indexedCommits = new TreeSet<String>();\r
- IndexWriter writer = getIndexWriter(model.name);\r
- // build a quick lookup of tags\r
- Map<String, List<String>> tags = new HashMap<String, List<String>>();\r
- for (RefModel tag : JGitUtils.getTags(repository, false, -1)) {\r
- if (!tag.isAnnotatedTag()) {\r
- // skip non-annotated tags\r
- continue;\r
- }\r
- if (!tags.containsKey(tag.getObjectId())) {\r
- tags.put(tag.getReferencedObjectId().getName(), new ArrayList<String>());\r
- }\r
- tags.get(tag.getReferencedObjectId().getName()).add(tag.displayName);\r
- }\r
-\r
- ObjectReader reader = repository.newObjectReader();\r
-\r
- // get the local branches\r
- List<RefModel> branches = JGitUtils.getLocalBranches(repository, true, -1);\r
-\r
- // sort them by most recently updated\r
- Collections.sort(branches, new Comparator<RefModel>() {\r
- @Override\r
- public int compare(RefModel ref1, RefModel ref2) {\r
- return ref2.getDate().compareTo(ref1.getDate());\r
- }\r
- });\r
-\r
- // reorder default branch to first position\r
- RefModel defaultBranch = null;\r
- ObjectId defaultBranchId = JGitUtils.getDefaultBranch(repository);\r
- for (RefModel branch : branches) {\r
- if (branch.getObjectId().equals(defaultBranchId)) {\r
- defaultBranch = branch;\r
- break;\r
- }\r
- }\r
- branches.remove(defaultBranch);\r
- branches.add(0, defaultBranch);\r
-\r
- // walk through each branch\r
- for (RefModel branch : branches) {\r
-\r
- boolean indexBranch = false;\r
- if (model.indexedBranches.contains(com.gitblit.Constants.DEFAULT_BRANCH)\r
- && branch.equals(defaultBranch)) {\r
- // indexing "default" branch\r
- indexBranch = true;\r
- } else if (branch.getName().startsWith(com.gitblit.Constants.R_GITBLIT)) {\r
- // skip Gitblit internal branches\r
- indexBranch = false;\r
- } else {\r
- // normal explicit branch check\r
- indexBranch = model.indexedBranches.contains(branch.getName());\r
- }\r
-\r
- // if this branch is not specifically indexed then skip\r
- if (!indexBranch) {\r
- continue;\r
- }\r
-\r
- String branchName = branch.getName();\r
- RevWalk revWalk = new RevWalk(reader);\r
- RevCommit tip = revWalk.parseCommit(branch.getObjectId());\r
- String tipId = tip.getId().getName();\r
-\r
- String keyName = getBranchKey(branchName);\r
- config.setString(CONF_ALIAS, null, keyName, branchName);\r
- config.setString(CONF_BRANCH, null, keyName, tipId);\r
-\r
- // index the blob contents of the tree\r
- TreeWalk treeWalk = new TreeWalk(repository);\r
- treeWalk.addTree(tip.getTree());\r
- treeWalk.setRecursive(true);\r
-\r
- Map<String, ObjectId> paths = new TreeMap<String, ObjectId>();\r
- while (treeWalk.next()) {\r
- // ensure path is not in a submodule\r
- if (treeWalk.getFileMode(0) != FileMode.GITLINK) {\r
- paths.put(treeWalk.getPathString(), treeWalk.getObjectId(0));\r
- }\r
- }\r
-\r
- ByteArrayOutputStream os = new ByteArrayOutputStream();\r
- byte[] tmp = new byte[32767];\r
-\r
- RevWalk commitWalk = new RevWalk(reader);\r
- commitWalk.markStart(tip);\r
-\r
- RevCommit commit;\r
- while ((paths.size() > 0) && (commit = commitWalk.next()) != null) {\r
- TreeWalk diffWalk = new TreeWalk(reader);\r
- int parentCount = commit.getParentCount();\r
- switch (parentCount) {\r
- case 0:\r
- diffWalk.addTree(new EmptyTreeIterator());\r
- break;\r
- case 1:\r
- diffWalk.addTree(getTree(commitWalk, commit.getParent(0)));\r
- break;\r
- default:\r
- // skip merge commits\r
- continue;\r
- }\r
- diffWalk.addTree(getTree(commitWalk, commit));\r
- diffWalk.setFilter(ANY_DIFF);\r
- diffWalk.setRecursive(true);\r
- while ((paths.size() > 0) && diffWalk.next()) {\r
- String path = diffWalk.getPathString();\r
- if (!paths.containsKey(path)) {\r
- continue;\r
- }\r
-\r
- // remove path from set\r
- ObjectId blobId = paths.remove(path);\r
- result.blobCount++;\r
-\r
- // index the blob metadata\r
- String blobAuthor = getAuthor(commit);\r
- String blobCommitter = getCommitter(commit);\r
- String blobDate = DateTools.timeToString(commit.getCommitTime() * 1000L,\r
- Resolution.MINUTE);\r
-\r
- Document doc = new Document();\r
- doc.add(new Field(FIELD_OBJECT_TYPE, SearchObjectType.blob.name(), Store.YES, Index.NOT_ANALYZED_NO_NORMS));\r
- doc.add(new Field(FIELD_BRANCH, branchName, Store.YES, Index.ANALYZED));\r
- doc.add(new Field(FIELD_COMMIT, commit.getName(), Store.YES, Index.ANALYZED));\r
- doc.add(new Field(FIELD_PATH, path, Store.YES, Index.ANALYZED));\r
- doc.add(new Field(FIELD_DATE, blobDate, Store.YES, Index.NO));\r
- doc.add(new Field(FIELD_AUTHOR, blobAuthor, Store.YES, Index.ANALYZED));\r
- doc.add(new Field(FIELD_COMMITTER, blobCommitter, Store.YES, Index.ANALYZED));\r
-\r
- // determine extension to compare to the extension\r
- // blacklist\r
- String ext = null;\r
- String name = path.toLowerCase();\r
- if (name.indexOf('.') > -1) {\r
- ext = name.substring(name.lastIndexOf('.') + 1);\r
- }\r
-\r
- // index the blob content\r
- if (StringUtils.isEmpty(ext) || !excludedExtensions.contains(ext)) {\r
- ObjectLoader ldr = repository.open(blobId, Constants.OBJ_BLOB);\r
- InputStream in = ldr.openStream();\r
- int n;\r
- while ((n = in.read(tmp)) > 0) {\r
- os.write(tmp, 0, n);\r
- }\r
- in.close();\r
- byte[] content = os.toByteArray();\r
- String str = StringUtils.decodeString(content, encodings);\r
- doc.add(new Field(FIELD_CONTENT, str, Store.YES, Index.ANALYZED));\r
- os.reset();\r
- }\r
-\r
- // add the blob to the index\r
- writer.addDocument(doc);\r
- }\r
- }\r
-\r
- os.close();\r
-\r
- // index the tip commit object\r
- if (indexedCommits.add(tipId)) {\r
- Document doc = createDocument(tip, tags.get(tipId));\r
- doc.add(new Field(FIELD_BRANCH, branchName, Store.YES, Index.ANALYZED));\r
- writer.addDocument(doc);\r
- result.commitCount += 1;\r
- result.branchCount += 1;\r
- }\r
-\r
- // traverse the log and index the previous commit objects\r
- RevWalk historyWalk = new RevWalk(reader);\r
- historyWalk.markStart(historyWalk.parseCommit(tip.getId()));\r
- RevCommit rev;\r
- while ((rev = historyWalk.next()) != null) {\r
- String hash = rev.getId().getName();\r
- if (indexedCommits.add(hash)) {\r
- Document doc = createDocument(rev, tags.get(hash));\r
- doc.add(new Field(FIELD_BRANCH, branchName, Store.YES, Index.ANALYZED));\r
- writer.addDocument(doc);\r
- result.commitCount += 1;\r
- }\r
- }\r
- }\r
-\r
- // finished\r
- reader.release();\r
-\r
- // commit all changes and reset the searcher\r
- config.setInt(CONF_INDEX, null, CONF_VERSION, INDEX_VERSION);\r
- config.save();\r
- writer.commit();\r
- resetIndexSearcher(model.name);\r
- result.success();\r
- } catch (Exception e) {\r
- logger.error("Exception while reindexing " + model.name, e);\r
- }\r
- return result;\r
- }\r
-\r
- /**\r
- * Incrementally update the index with the specified commit for the\r
- * repository.\r
- *\r
- * @param repositoryName\r
- * @param repository\r
- * @param branch\r
- * the fully qualified branch name (e.g. refs/heads/master)\r
- * @param commit\r
- * @return true, if successful\r
- */\r
- private IndexResult index(String repositoryName, Repository repository,\r
- String branch, RevCommit commit) {\r
- IndexResult result = new IndexResult();\r
- try {\r
- String [] encodings = storedSettings.getStrings(Keys.web.blobEncodings).toArray(new String[0]);\r
- List<PathChangeModel> changedPaths = JGitUtils.getFilesInCommit(repository, commit);\r
- String revDate = DateTools.timeToString(commit.getCommitTime() * 1000L,\r
- Resolution.MINUTE);\r
- IndexWriter writer = getIndexWriter(repositoryName);\r
- for (PathChangeModel path : changedPaths) {\r
- if (path.isSubmodule()) {\r
- continue;\r
- }\r
- // delete the indexed blob\r
- deleteBlob(repositoryName, branch, path.name);\r
-\r
- // re-index the blob\r
- if (!ChangeType.DELETE.equals(path.changeType)) {\r
- result.blobCount++;\r
- Document doc = new Document();\r
- doc.add(new Field(FIELD_OBJECT_TYPE, SearchObjectType.blob.name(), Store.YES,\r
- Index.NOT_ANALYZED));\r
- doc.add(new Field(FIELD_BRANCH, branch, Store.YES, Index.ANALYZED));\r
- doc.add(new Field(FIELD_COMMIT, commit.getName(), Store.YES, Index.ANALYZED));\r
- doc.add(new Field(FIELD_PATH, path.path, Store.YES, Index.ANALYZED));\r
- doc.add(new Field(FIELD_DATE, revDate, Store.YES, Index.NO));\r
- doc.add(new Field(FIELD_AUTHOR, getAuthor(commit), Store.YES, Index.ANALYZED));\r
- doc.add(new Field(FIELD_COMMITTER, getCommitter(commit), Store.YES, Index.ANALYZED));\r
-\r
- // determine extension to compare to the extension\r
- // blacklist\r
- String ext = null;\r
- String name = path.name.toLowerCase();\r
- if (name.indexOf('.') > -1) {\r
- ext = name.substring(name.lastIndexOf('.') + 1);\r
- }\r
-\r
- if (StringUtils.isEmpty(ext) || !excludedExtensions.contains(ext)) {\r
- // read the blob content\r
- String str = JGitUtils.getStringContent(repository, commit.getTree(),\r
- path.path, encodings);\r
- if (str != null) {\r
- doc.add(new Field(FIELD_CONTENT, str, Store.YES, Index.ANALYZED));\r
- writer.addDocument(doc);\r
- }\r
- }\r
- }\r
- }\r
- writer.commit();\r
-\r
- // get any annotated commit tags\r
- List<String> commitTags = new ArrayList<String>();\r
- for (RefModel ref : JGitUtils.getTags(repository, false, -1)) {\r
- if (ref.isAnnotatedTag() && ref.getReferencedObjectId().equals(commit.getId())) {\r
- commitTags.add(ref.displayName);\r
- }\r
- }\r
-\r
- // create and write the Lucene document\r
- Document doc = createDocument(commit, commitTags);\r
- doc.add(new Field(FIELD_BRANCH, branch, Store.YES, Index.ANALYZED));\r
- result.commitCount++;\r
- result.success = index(repositoryName, doc);\r
- } catch (Exception e) {\r
- logger.error(MessageFormat.format("Exception while indexing commit {0} in {1}", commit.getId().getName(), repositoryName), e);\r
- }\r
- return result;\r
- }\r
-\r
- /**\r
- * Delete a blob from the specified branch of the repository index.\r
- *\r
- * @param repositoryName\r
- * @param branch\r
- * @param path\r
- * @throws Exception\r
- * @return true, if deleted, false if no record was deleted\r
- */\r
- public boolean deleteBlob(String repositoryName, String branch, String path) throws Exception {\r
- String pattern = MessageFormat.format("{0}:'{'0} AND {1}:\"'{'1'}'\" AND {2}:\"'{'2'}'\"", FIELD_OBJECT_TYPE, FIELD_BRANCH, FIELD_PATH);\r
- String q = MessageFormat.format(pattern, SearchObjectType.blob.name(), branch, path);\r
-\r
- BooleanQuery query = new BooleanQuery();\r
- StandardAnalyzer analyzer = new StandardAnalyzer(LUCENE_VERSION);\r
- QueryParser qp = new QueryParser(LUCENE_VERSION, FIELD_SUMMARY, analyzer);\r
- query.add(qp.parse(q), Occur.MUST);\r
-\r
- IndexWriter writer = getIndexWriter(repositoryName);\r
- int numDocsBefore = writer.numDocs();\r
- writer.deleteDocuments(query);\r
- writer.commit();\r
- int numDocsAfter = writer.numDocs();\r
- if (numDocsBefore == numDocsAfter) {\r
- logger.debug(MessageFormat.format("no records found to delete {0}", query.toString()));\r
- return false;\r
- } else {\r
- logger.debug(MessageFormat.format("deleted {0} records with {1}", numDocsBefore - numDocsAfter, query.toString()));\r
- return true;\r
- }\r
- }\r
-\r
- /**\r
- * Updates a repository index incrementally from the last indexed commits.\r
- *\r
- * @param model\r
- * @param repository\r
- * @return IndexResult\r
- */\r
- private IndexResult updateIndex(RepositoryModel model, Repository repository) {\r
- IndexResult result = new IndexResult();\r
- try {\r
- FileBasedConfig config = getConfig(repository);\r
- config.load();\r
-\r
- // build a quick lookup of annotated tags\r
- Map<String, List<String>> tags = new HashMap<String, List<String>>();\r
- for (RefModel tag : JGitUtils.getTags(repository, false, -1)) {\r
- if (!tag.isAnnotatedTag()) {\r
- // skip non-annotated tags\r
- continue;\r
- }\r
- if (!tags.containsKey(tag.getObjectId())) {\r
- tags.put(tag.getReferencedObjectId().getName(), new ArrayList<String>());\r
- }\r
- tags.get(tag.getReferencedObjectId().getName()).add(tag.displayName);\r
- }\r
-\r
- // detect branch deletion\r
- // first assume all branches are deleted and then remove each\r
- // existing branch from deletedBranches during indexing\r
- Set<String> deletedBranches = new TreeSet<String>();\r
- for (String alias : config.getNames(CONF_ALIAS)) {\r
- String branch = config.getString(CONF_ALIAS, null, alias);\r
- deletedBranches.add(branch);\r
- }\r
-\r
- // get the local branches\r
- List<RefModel> branches = JGitUtils.getLocalBranches(repository, true, -1);\r
-\r
- // sort them by most recently updated\r
- Collections.sort(branches, new Comparator<RefModel>() {\r
- @Override\r
- public int compare(RefModel ref1, RefModel ref2) {\r
- return ref2.getDate().compareTo(ref1.getDate());\r
- }\r
- });\r
-\r
- // reorder default branch to first position\r
- RefModel defaultBranch = null;\r
- ObjectId defaultBranchId = JGitUtils.getDefaultBranch(repository);\r
- for (RefModel branch : branches) {\r
- if (branch.getObjectId().equals(defaultBranchId)) {\r
- defaultBranch = branch;\r
- break;\r
- }\r
- }\r
- branches.remove(defaultBranch);\r
- branches.add(0, defaultBranch);\r
-\r
- // walk through each branches\r
- for (RefModel branch : branches) {\r
- String branchName = branch.getName();\r
-\r
- boolean indexBranch = false;\r
- if (model.indexedBranches.contains(com.gitblit.Constants.DEFAULT_BRANCH)\r
- && branch.equals(defaultBranch)) {\r
- // indexing "default" branch\r
- indexBranch = true;\r
- } else if (branch.getName().startsWith(com.gitblit.Constants.R_GITBLIT)) {\r
- // ignore internal Gitblit branches\r
- indexBranch = false;\r
- } else {\r
- // normal explicit branch check\r
- indexBranch = model.indexedBranches.contains(branch.getName());\r
- }\r
-\r
- // if this branch is not specifically indexed then skip\r
- if (!indexBranch) {\r
- continue;\r
- }\r
-\r
- // remove this branch from the deletedBranches set\r
- deletedBranches.remove(branchName);\r
-\r
- // determine last commit\r
- String keyName = getBranchKey(branchName);\r
- String lastCommit = config.getString(CONF_BRANCH, null, keyName);\r
-\r
- List<RevCommit> revs;\r
- if (StringUtils.isEmpty(lastCommit)) {\r
- // new branch/unindexed branch, get all commits on branch\r
- revs = JGitUtils.getRevLog(repository, branchName, 0, -1);\r
- } else {\r
- // pre-existing branch, get changes since last commit\r
- revs = JGitUtils.getRevLog(repository, lastCommit, branchName);\r
- }\r
-\r
- if (revs.size() > 0) {\r
- result.branchCount += 1;\r
- }\r
-\r
- // reverse the list of commits so we start with the first commit\r
- Collections.reverse(revs);\r
- for (RevCommit commit : revs) {\r
- // index a commit\r
- result.add(index(model.name, repository, branchName, commit));\r
- }\r
-\r
- // update the config\r
- config.setInt(CONF_INDEX, null, CONF_VERSION, INDEX_VERSION);\r
- config.setString(CONF_ALIAS, null, keyName, branchName);\r
- config.setString(CONF_BRANCH, null, keyName, branch.getObjectId().getName());\r
- config.save();\r
- }\r
-\r
- // the deletedBranches set will normally be empty by this point\r
- // unless a branch really was deleted and no longer exists\r
- if (deletedBranches.size() > 0) {\r
- for (String branch : deletedBranches) {\r
- IndexWriter writer = getIndexWriter(model.name);\r
- writer.deleteDocuments(new Term(FIELD_BRANCH, branch));\r
- writer.commit();\r
- }\r
- }\r
- result.success = true;\r
- } catch (Throwable t) {\r
- logger.error(MessageFormat.format("Exception while updating {0} Lucene index", model.name), t);\r
- }\r
- return result;\r
- }\r
-\r
- /**\r
- * Creates a Lucene document for a commit\r
- *\r
- * @param commit\r
- * @param tags\r
- * @return a Lucene document\r
- */\r
- private Document createDocument(RevCommit commit, List<String> tags) {\r
- Document doc = new Document();\r
- doc.add(new Field(FIELD_OBJECT_TYPE, SearchObjectType.commit.name(), Store.YES,\r
- Index.NOT_ANALYZED));\r
- doc.add(new Field(FIELD_COMMIT, commit.getName(), Store.YES, Index.ANALYZED));\r
- doc.add(new Field(FIELD_DATE, DateTools.timeToString(commit.getCommitTime() * 1000L,\r
- Resolution.MINUTE), Store.YES, Index.NO));\r
- doc.add(new Field(FIELD_AUTHOR, getAuthor(commit), Store.YES, Index.ANALYZED));\r
- doc.add(new Field(FIELD_COMMITTER, getCommitter(commit), Store.YES, Index.ANALYZED));\r
- doc.add(new Field(FIELD_SUMMARY, commit.getShortMessage(), Store.YES, Index.ANALYZED));\r
- doc.add(new Field(FIELD_CONTENT, commit.getFullMessage(), Store.YES, Index.ANALYZED));\r
- if (!ArrayUtils.isEmpty(tags)) {\r
- doc.add(new Field(FIELD_TAG, StringUtils.flattenStrings(tags), Store.YES, Index.ANALYZED));\r
- }\r
- return doc;\r
- }\r
-\r
- /**\r
- * Incrementally index an object for the repository.\r
- *\r
- * @param repositoryName\r
- * @param doc\r
- * @return true, if successful\r
- */\r
- private boolean index(String repositoryName, Document doc) {\r
- try {\r
- IndexWriter writer = getIndexWriter(repositoryName);\r
- writer.addDocument(doc);\r
- writer.commit();\r
- resetIndexSearcher(repositoryName);\r
- return true;\r
- } catch (Exception e) {\r
- logger.error(MessageFormat.format("Exception while incrementally updating {0} Lucene index", repositoryName), e);\r
- }\r
- return false;\r
- }\r
-\r
- private SearchResult createSearchResult(Document doc, float score, int hitId, int totalHits) throws ParseException {\r
- SearchResult result = new SearchResult();\r
- result.hitId = hitId;\r
- result.totalHits = totalHits;\r
- result.score = score;\r
- result.date = DateTools.stringToDate(doc.get(FIELD_DATE));\r
- result.summary = doc.get(FIELD_SUMMARY);\r
- result.author = doc.get(FIELD_AUTHOR);\r
- result.committer = doc.get(FIELD_COMMITTER);\r
- result.type = SearchObjectType.fromName(doc.get(FIELD_OBJECT_TYPE));\r
- result.branch = doc.get(FIELD_BRANCH);\r
- result.commitId = doc.get(FIELD_COMMIT);\r
- result.path = doc.get(FIELD_PATH);\r
- if (doc.get(FIELD_TAG) != null) {\r
- result.tags = StringUtils.getStringsFromValue(doc.get(FIELD_TAG));\r
- }\r
- return result;\r
- }\r
-\r
- private synchronized void resetIndexSearcher(String repository) throws IOException {\r
- IndexSearcher searcher = searchers.remove(repository);\r
- if (searcher != null) {\r
- searcher.getIndexReader().close();\r
- }\r
- }\r
-\r
- /**\r
- * Gets an index searcher for the repository.\r
- *\r
- * @param repository\r
- * @return\r
- * @throws IOException\r
- */\r
- private IndexSearcher getIndexSearcher(String repository) throws IOException {\r
- IndexSearcher searcher = searchers.get(repository);\r
- if (searcher == null) {\r
- IndexWriter writer = getIndexWriter(repository);\r
- searcher = new IndexSearcher(IndexReader.open(writer, true));\r
- searchers.put(repository, searcher);\r
- }\r
- return searcher;\r
- }\r
-\r
- /**\r
- * Gets an index writer for the repository. The index will be created if it\r
- * does not already exist or if forceCreate is specified.\r
- *\r
- * @param repository\r
- * @return an IndexWriter\r
- * @throws IOException\r
- */\r
- private IndexWriter getIndexWriter(String repository) throws IOException {\r
- IndexWriter indexWriter = writers.get(repository);\r
- File repositoryFolder = FileKey.resolve(new File(repositoriesFolder, repository), FS.DETECTED);\r
- File indexFolder = new File(repositoryFolder, LUCENE_DIR);\r
- Directory directory = FSDirectory.open(indexFolder);\r
-\r
- if (indexWriter == null) {\r
- if (!indexFolder.exists()) {\r
- indexFolder.mkdirs();\r
- }\r
- StandardAnalyzer analyzer = new StandardAnalyzer(LUCENE_VERSION);\r
- IndexWriterConfig config = new IndexWriterConfig(LUCENE_VERSION, analyzer);\r
- config.setOpenMode(OpenMode.CREATE_OR_APPEND);\r
- indexWriter = new IndexWriter(directory, config);\r
- writers.put(repository, indexWriter);\r
- }\r
- return indexWriter;\r
- }\r
-\r
- /**\r
- * Searches the specified repositories for the given text or query\r
- *\r
- * @param text\r
- * if the text is null or empty, null is returned\r
- * @param page\r
- * the page number to retrieve. page is 1-indexed.\r
- * @param pageSize\r
- * the number of elements to return for this page\r
- * @param repositories\r
- * a list of repositories to search. if no repositories are\r
- * specified null is returned.\r
- * @return a list of SearchResults in order from highest to the lowest score\r
- *\r
- */\r
- public List<SearchResult> search(String text, int page, int pageSize, List<String> repositories) {\r
- if (ArrayUtils.isEmpty(repositories)) {\r
- return null;\r
- }\r
- return search(text, page, pageSize, repositories.toArray(new String[0]));\r
- }\r
-\r
- /**\r
- * Searches the specified repositories for the given text or query\r
- *\r
- * @param text\r
- * if the text is null or empty, null is returned\r
- * @param page\r
- * the page number to retrieve. page is 1-indexed.\r
- * @param pageSize\r
- * the number of elements to return for this page\r
- * @param repositories\r
- * a list of repositories to search. if no repositories are\r
- * specified null is returned.\r
- * @return a list of SearchResults in order from highest to the lowest score\r
- *\r
- */\r
- public List<SearchResult> search(String text, int page, int pageSize, String... repositories) {\r
- if (StringUtils.isEmpty(text)) {\r
- return null;\r
- }\r
- if (ArrayUtils.isEmpty(repositories)) {\r
- return null;\r
- }\r
- Set<SearchResult> results = new LinkedHashSet<SearchResult>();\r
- StandardAnalyzer analyzer = new StandardAnalyzer(LUCENE_VERSION);\r
- try {\r
- // default search checks summary and content\r
- BooleanQuery query = new BooleanQuery();\r
- QueryParser qp;\r
- qp = new QueryParser(LUCENE_VERSION, FIELD_SUMMARY, analyzer);\r
- qp.setAllowLeadingWildcard(true);\r
- query.add(qp.parse(text), Occur.SHOULD);\r
-\r
- qp = new QueryParser(LUCENE_VERSION, FIELD_CONTENT, analyzer);\r
- qp.setAllowLeadingWildcard(true);\r
- query.add(qp.parse(text), Occur.SHOULD);\r
-\r
- IndexSearcher searcher;\r
- if (repositories.length == 1) {\r
- // single repository search\r
- searcher = getIndexSearcher(repositories[0]);\r
- } else {\r
- // multiple repository search\r
- List<IndexReader> readers = new ArrayList<IndexReader>();\r
- for (String repository : repositories) {\r
- IndexSearcher repositoryIndex = getIndexSearcher(repository);\r
- readers.add(repositoryIndex.getIndexReader());\r
- }\r
- IndexReader[] rdrs = readers.toArray(new IndexReader[readers.size()]);\r
- MultiSourceReader reader = new MultiSourceReader(rdrs);\r
- searcher = new IndexSearcher(reader);\r
- }\r
-\r
- Query rewrittenQuery = searcher.rewrite(query);\r
- logger.debug(rewrittenQuery.toString());\r
-\r
- TopScoreDocCollector collector = TopScoreDocCollector.create(5000, true);\r
- searcher.search(rewrittenQuery, collector);\r
- int offset = Math.max(0, (page - 1) * pageSize);\r
- ScoreDoc[] hits = collector.topDocs(offset, pageSize).scoreDocs;\r
- int totalHits = collector.getTotalHits();\r
- for (int i = 0; i < hits.length; i++) {\r
- int docId = hits[i].doc;\r
- Document doc = searcher.doc(docId);\r
- SearchResult result = createSearchResult(doc, hits[i].score, offset + i + 1, totalHits);\r
- if (repositories.length == 1) {\r
- // single repository search\r
- result.repository = repositories[0];\r
- } else {\r
- // multi-repository search\r
- MultiSourceReader reader = (MultiSourceReader) searcher.getIndexReader();\r
- int index = reader.getSourceIndex(docId);\r
- result.repository = repositories[index];\r
- }\r
- String content = doc.get(FIELD_CONTENT);\r
- result.fragment = getHighlightedFragment(analyzer, query, content, result);\r
- results.add(result);\r
- }\r
- } catch (Exception e) {\r
- logger.error(MessageFormat.format("Exception while searching for {0}", text), e);\r
- }\r
- return new ArrayList<SearchResult>(results);\r
- }\r
-\r
- /**\r
- *\r
- * @param analyzer\r
- * @param query\r
- * @param content\r
- * @param result\r
- * @return\r
- * @throws IOException\r
- * @throws InvalidTokenOffsetsException\r
- */\r
- private String getHighlightedFragment(Analyzer analyzer, Query query,\r
- String content, SearchResult result) throws IOException, InvalidTokenOffsetsException {\r
- if (content == null) {\r
- content = "";\r
- }\r
-\r
- int fragmentLength = SearchObjectType.commit == result.type ? 512 : 150;\r
-\r
- QueryScorer scorer = new QueryScorer(query, "content");\r
- Fragmenter fragmenter = new SimpleSpanFragmenter(scorer, fragmentLength);\r
-\r
- // use an artificial delimiter for the token\r
- String termTag = "!!--[";\r
- String termTagEnd = "]--!!";\r
- SimpleHTMLFormatter formatter = new SimpleHTMLFormatter(termTag, termTagEnd);\r
- Highlighter highlighter = new Highlighter(formatter, scorer);\r
- highlighter.setTextFragmenter(fragmenter);\r
-\r
- String [] fragments = highlighter.getBestFragments(analyzer, "content", content, 3);\r
- if (ArrayUtils.isEmpty(fragments)) {\r
- if (SearchObjectType.blob == result.type) {\r
- return "";\r
- }\r
- // clip commit message\r
- String fragment = content;\r
- if (fragment.length() > fragmentLength) {\r
- fragment = fragment.substring(0, fragmentLength) + "...";\r
- }\r
- return "<pre class=\"text\">" + StringUtils.escapeForHtml(fragment, true) + "</pre>";\r
- }\r
-\r
- // make sure we have unique fragments\r
- Set<String> uniqueFragments = new LinkedHashSet<String>();\r
- for (String fragment : fragments) {\r
- uniqueFragments.add(fragment);\r
- }\r
- fragments = uniqueFragments.toArray(new String[uniqueFragments.size()]);\r
-\r
- StringBuilder sb = new StringBuilder();\r
- for (int i = 0, len = fragments.length; i < len; i++) {\r
- String fragment = fragments[i];\r
- String tag = "<pre class=\"text\">";\r
-\r
- // resurrect the raw fragment from removing the artificial delimiters\r
- String raw = fragment.replace(termTag, "").replace(termTagEnd, "");\r
-\r
- // determine position of the raw fragment in the content\r
- int pos = content.indexOf(raw);\r
-\r
- // restore complete first line of fragment\r
- int c = pos;\r
- while (c > 0) {\r
- c--;\r
- if (content.charAt(c) == '\n') {\r
- break;\r
- }\r
- }\r
- if (c > 0) {\r
- // inject leading chunk of first fragment line\r
- fragment = content.substring(c + 1, pos) + fragment;\r
- }\r
-\r
- if (SearchObjectType.blob == result.type) {\r
- // count lines as offset into the content for this fragment\r
- int line = Math.max(1, StringUtils.countLines(content.substring(0, pos)));\r
-\r
- // create fragment tag with line number and language\r
- String lang = "";\r
- String ext = StringUtils.getFileExtension(result.path).toLowerCase();\r
- if (!StringUtils.isEmpty(ext)) {\r
- // maintain leading space!\r
- lang = " lang-" + ext;\r
- }\r
- tag = MessageFormat.format("<pre class=\"prettyprint linenums:{0,number,0}{1}\">", line, lang);\r
-\r
- }\r
-\r
- sb.append(tag);\r
-\r
- // replace the artificial delimiter with html tags\r
- String html = StringUtils.escapeForHtml(fragment, false);\r
- html = html.replace(termTag, "<span class=\"highlight\">").replace(termTagEnd, "</span>");\r
- sb.append(html);\r
- sb.append("</pre>");\r
- if (i < len - 1) {\r
- sb.append("<span class=\"ellipses\">...</span><br/>");\r
- }\r
- }\r
- return sb.toString();\r
- }\r
-\r
- /**\r
- * Simple class to track the results of an index update.\r
- */\r
- private class IndexResult {\r
- long startTime = System.currentTimeMillis();\r
- long endTime = startTime;\r
- boolean success;\r
- int branchCount;\r
- int commitCount;\r
- int blobCount;\r
-\r
- void add(IndexResult result) {\r
- this.branchCount += result.branchCount;\r
- this.commitCount += result.commitCount;\r
- this.blobCount += result.blobCount;\r
- }\r
-\r
- void success() {\r
- success = true;\r
- endTime = System.currentTimeMillis();\r
- }\r
-\r
- float duration() {\r
- return (endTime - startTime)/1000f;\r
- }\r
- }\r
-\r
- /**\r
- * Custom subclass of MultiReader to identify the source index for a given\r
- * doc id. This would not be necessary of there was a public method to\r
- * obtain this information.\r
- *\r
- */\r
- private class MultiSourceReader extends MultiReader {\r
-\r
- final Method method;\r
-\r
- MultiSourceReader(IndexReader[] subReaders) {\r
- super(subReaders);\r
- Method m = null;\r
- try {\r
- m = MultiReader.class.getDeclaredMethod("readerIndex", int.class);\r
- m.setAccessible(true);\r
- } catch (Exception e) {\r
- logger.error("Error getting readerIndex method", e);\r
- }\r
- method = m;\r
- }\r
-\r
- int getSourceIndex(int docId) {\r
- int index = -1;\r
- try {\r
- Object o = method.invoke(this, docId);\r
- index = (Integer) o;\r
- } catch (Exception e) {\r
- logger.error("Error getting source index", e);\r
- }\r
- return index;\r
- }\r
- }\r
-}\r
+++ /dev/null
-/*\r
- * Copyright 2011 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.Date;\r
-import java.util.HashSet;\r
-import java.util.List;\r
-import java.util.Properties;\r
-import java.util.Queue;\r
-import java.util.Set;\r
-import java.util.concurrent.ConcurrentLinkedQueue;\r
-import java.util.regex.Pattern;\r
-\r
-import javax.mail.Authenticator;\r
-import javax.mail.Message;\r
-import javax.mail.PasswordAuthentication;\r
-import javax.mail.Session;\r
-import javax.mail.Transport;\r
-import javax.mail.internet.InternetAddress;\r
-import javax.mail.internet.MimeMessage;\r
-\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.gitblit.utils.StringUtils;\r
-\r
-/**\r
- * The mail executor handles sending email messages asynchronously from queue.\r
- *\r
- * @author James Moger\r
- *\r
- */\r
-public class MailExecutor implements Runnable {\r
-\r
- private final Logger logger = LoggerFactory.getLogger(MailExecutor.class);\r
-\r
- private final Queue<Message> queue = new ConcurrentLinkedQueue<Message>();\r
-\r
- private final Session session;\r
-\r
- private final IStoredSettings settings;\r
-\r
- public MailExecutor(IStoredSettings settings) {\r
- this.settings = settings;\r
-\r
- final String mailUser = settings.getString(Keys.mail.username, null);\r
- final String mailPassword = settings.getString(Keys.mail.password, null);\r
- final boolean smtps = settings.getBoolean(Keys.mail.smtps, false);\r
- boolean authenticate = !StringUtils.isEmpty(mailUser) && !StringUtils.isEmpty(mailPassword);\r
- String server = settings.getString(Keys.mail.server, "");\r
- if (StringUtils.isEmpty(server)) {\r
- session = null;\r
- return;\r
- }\r
- int port = settings.getInteger(Keys.mail.port, 25);\r
- boolean isGMail = false;\r
- if (server.equals("smtp.gmail.com")) {\r
- port = 465;\r
- isGMail = true;\r
- }\r
-\r
- Properties props = new Properties();\r
- props.setProperty("mail.smtp.host", server);\r
- props.setProperty("mail.smtp.port", String.valueOf(port));\r
- props.setProperty("mail.smtp.auth", String.valueOf(authenticate));\r
- props.setProperty("mail.smtp.auths", String.valueOf(authenticate));\r
-\r
- if (isGMail || smtps) {\r
- props.setProperty("mail.smtp.starttls.enable", "true");\r
- props.put("mail.smtp.socketFactory.port", String.valueOf(port));\r
- props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");\r
- props.put("mail.smtp.socketFactory.fallback", "false");\r
- }\r
-\r
- if (!StringUtils.isEmpty(mailUser) && !StringUtils.isEmpty(mailPassword)) {\r
- // SMTP requires authentication\r
- session = Session.getInstance(props, new Authenticator() {\r
- @Override\r
- protected PasswordAuthentication getPasswordAuthentication() {\r
- PasswordAuthentication passwordAuthentication = new PasswordAuthentication(\r
- mailUser, mailPassword);\r
- return passwordAuthentication;\r
- }\r
- });\r
- } else {\r
- // SMTP does not require authentication\r
- session = Session.getInstance(props);\r
- }\r
- }\r
-\r
- /**\r
- * Indicates if the mail executor can send emails.\r
- *\r
- * @return true if the mail executor is ready to send emails\r
- */\r
- public boolean isReady() {\r
- return session != null;\r
- }\r
-\r
-\r
- /**\r
- * Create a message.\r
- *\r
- * @param toAddresses\r
- * @return a message\r
- */\r
- public Message createMessage(String... toAddresses) {\r
- return createMessage(Arrays.asList(toAddresses));\r
- }\r
-\r
- /**\r
- * Create a message.\r
- *\r
- * @param toAddresses\r
- * @return a message\r
- */\r
- public Message createMessage(List<String> toAddresses) {\r
- MimeMessage message = new MimeMessage(session);\r
- try {\r
- String fromAddress = settings.getString(Keys.mail.fromAddress, null);\r
- if (StringUtils.isEmpty(fromAddress)) {\r
- fromAddress = "gitblit@gitblit.com";\r
- }\r
- InternetAddress from = new InternetAddress(fromAddress, "Gitblit");\r
- message.setFrom(from);\r
-\r
- // determine unique set of addresses\r
- Set<String> uniques = new HashSet<String>();\r
- for (String address : toAddresses) {\r
- uniques.add(address.toLowerCase());\r
- }\r
-\r
- Pattern validEmail = Pattern\r
- .compile("^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$");\r
- List<InternetAddress> tos = new ArrayList<InternetAddress>();\r
- for (String address : uniques) {\r
- if (StringUtils.isEmpty(address)) {\r
- continue;\r
- }\r
- if (validEmail.matcher(address).find()) {\r
- try {\r
- tos.add(new InternetAddress(address));\r
- } catch (Throwable t) {\r
- }\r
- }\r
- }\r
- message.setRecipients(Message.RecipientType.BCC,\r
- tos.toArray(new InternetAddress[tos.size()]));\r
- message.setSentDate(new Date());\r
- } catch (Exception e) {\r
- logger.error("Failed to properly create message", e);\r
- }\r
- return message;\r
- }\r
-\r
- /**\r
- * Returns the status of the mail queue.\r
- *\r
- * @return true, if the queue is empty\r
- */\r
- public boolean hasEmptyQueue() {\r
- return queue.isEmpty();\r
- }\r
-\r
- /**\r
- * Queue's an email message to be sent.\r
- *\r
- * @param message\r
- * @return true if the message was queued\r
- */\r
- public boolean queue(Message message) {\r
- if (!isReady()) {\r
- return false;\r
- }\r
- try {\r
- message.saveChanges();\r
- } catch (Throwable t) {\r
- logger.error("Failed to save changes to message!", t);\r
- }\r
- queue.add(message);\r
- return true;\r
- }\r
-\r
- @Override\r
- public void run() {\r
- if (!queue.isEmpty()) {\r
- if (session != null) {\r
- // send message via mail server\r
- List<Message> failures = new ArrayList<Message>();\r
- Message message = null;\r
- while ((message = queue.poll()) != null) {\r
- try {\r
- if (settings.getBoolean(Keys.mail.debug, false)) {\r
- logger.info("send: " + StringUtils.trimString(message.getSubject(), 60));\r
- }\r
- Transport.send(message);\r
- } catch (Throwable e) {\r
- logger.error("Failed to send message", e);\r
- failures.add(message);\r
- }\r
- }\r
-\r
- // push the failures back onto the queue for the next cycle\r
- queue.addAll(failures);\r
- }\r
- }\r
- }\r
-\r
- public void sendNow(Message message) throws Exception {\r
- Transport.send(message);\r
- }\r
-}\r
+++ /dev/null
-/*\r
- * Copyright 2013 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.text.MessageFormat;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.HashSet;\r
-import java.util.List;\r
-import java.util.Set;\r
-import java.util.concurrent.atomic.AtomicBoolean;\r
-\r
-import org.eclipse.jgit.api.Git;\r
-import org.eclipse.jgit.lib.RefUpdate.Result;\r
-import org.eclipse.jgit.lib.Repository;\r
-import org.eclipse.jgit.lib.StoredConfig;\r
-import org.eclipse.jgit.transport.FetchResult;\r
-import org.eclipse.jgit.transport.RemoteConfig;\r
-import org.eclipse.jgit.transport.TrackingRefUpdate;\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.gitblit.manager.IRepositoryManager;\r
-import com.gitblit.models.RepositoryModel;\r
-import com.gitblit.models.UserModel;\r
-import com.gitblit.utils.JGitUtils;\r
-\r
-/**\r
- * The Mirror executor handles periodic fetching of mirrored repositories.\r
- *\r
- * @author James Moger\r
- *\r
- */\r
-public class MirrorExecutor implements Runnable {\r
-\r
- private final Logger logger = LoggerFactory.getLogger(MirrorExecutor.class);\r
-\r
- private final Set<String> repairAttempted = Collections.synchronizedSet(new HashSet<String>());\r
-\r
- private final IStoredSettings settings;\r
-\r
- private final IRepositoryManager repositoryManager;\r
-\r
- private AtomicBoolean running = new AtomicBoolean(false);\r
-\r
- private AtomicBoolean forceClose = new AtomicBoolean(false);\r
-\r
- private final UserModel gitblitUser;\r
-\r
- public MirrorExecutor(\r
- IStoredSettings settings,\r
- IRepositoryManager repositoryManager) {\r
-\r
- this.settings = settings;\r
- this.repositoryManager = repositoryManager;\r
- this.gitblitUser = new UserModel("gitblit");\r
- this.gitblitUser.displayName = "Gitblit";\r
- }\r
-\r
- public boolean isReady() {\r
- return settings.getBoolean(Keys.git.enableMirroring, false);\r
- }\r
-\r
- public boolean isRunning() {\r
- return running.get();\r
- }\r
-\r
- public void close() {\r
- forceClose.set(true);\r
- }\r
-\r
- @Override\r
- public void run() {\r
- if (!isReady()) {\r
- return;\r
- }\r
-\r
- running.set(true);\r
-\r
- for (String repositoryName : repositoryManager.getRepositoryList()) {\r
- if (forceClose.get()) {\r
- break;\r
- }\r
- if (repositoryManager.isCollectingGarbage(repositoryName)) {\r
- logger.debug("mirror is skipping {} garbagecollection", repositoryName);\r
- continue;\r
- }\r
- RepositoryModel model = null;\r
- Repository repository = null;\r
- try {\r
- model = repositoryManager.getRepositoryModel(repositoryName);\r
- if (!model.isMirror && !model.isBare) {\r
- // repository must be a valid bare git mirror\r
- logger.debug("mirror is skipping {} !mirror !bare", repositoryName);\r
- continue;\r
- }\r
-\r
- repository = repositoryManager.getRepository(repositoryName);\r
- if (repository == null) {\r
- logger.warn(MessageFormat.format("MirrorExecutor is missing repository {0}?!?", repositoryName));\r
- continue;\r
- }\r
-\r
- // automatically repair (some) invalid fetch ref specs\r
- if (!repairAttempted.contains(repositoryName)) {\r
- repairAttempted.add(repositoryName);\r
- JGitUtils.repairFetchSpecs(repository);\r
- }\r
-\r
- // find the first mirror remote - there should only be one\r
- StoredConfig rc = repository.getConfig();\r
- RemoteConfig mirror = null;\r
- List<RemoteConfig> configs = RemoteConfig.getAllRemoteConfigs(rc);\r
- for (RemoteConfig config : configs) {\r
- if (config.isMirror()) {\r
- mirror = config;\r
- break;\r
- }\r
- }\r
-\r
- if (mirror == null) {\r
- // repository does not have a mirror remote\r
- logger.debug("mirror is skipping {} no mirror remote found", repositoryName);\r
- continue;\r
- }\r
-\r
- logger.debug("checking {} remote {} for ref updates", repositoryName, mirror.getName());\r
- final boolean testing = false;\r
- Git git = new Git(repository);\r
- FetchResult result = git.fetch().setRemote(mirror.getName()).setDryRun(testing).call();\r
- Collection<TrackingRefUpdate> refUpdates = result.getTrackingRefUpdates();\r
- if (refUpdates.size() > 0) {\r
- for (TrackingRefUpdate ru : refUpdates) {\r
- StringBuilder sb = new StringBuilder();\r
- sb.append("updated mirror ");\r
- sb.append(repositoryName);\r
- sb.append(" ");\r
- sb.append(ru.getRemoteName());\r
- sb.append(" -> ");\r
- sb.append(ru.getLocalName());\r
- if (ru.getResult() == Result.FORCED) {\r
- sb.append(" (forced)");\r
- }\r
- sb.append(" ");\r
- sb.append(ru.getOldObjectId() == null ? "" : ru.getOldObjectId().abbreviate(7).name());\r
- sb.append("..");\r
- sb.append(ru.getNewObjectId() == null ? "" : ru.getNewObjectId().abbreviate(7).name());\r
- logger.info(sb.toString());\r
- }\r
- }\r
- } catch (Exception e) {\r
- logger.error("Error updating mirror " + repositoryName, e);\r
- } finally {\r
- // cleanup\r
- if (repository != null) {\r
- repository.close();\r
- }\r
- }\r
- }\r
-\r
- running.set(false);\r
- }\r
-}\r
+++ /dev/null
-/*\r
- * Copyright 2012 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import javax.inject.Inject;\r
-import javax.inject.Singleton;\r
-\r
-import org.eclipse.jgit.lib.Repository;\r
-\r
-import com.gitblit.Constants.AccessRestrictionType;\r
-import com.gitblit.manager.IRepositoryManager;\r
-import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.manager.ISessionManager;\r
-import com.gitblit.models.RepositoryModel;\r
-import com.gitblit.models.UserModel;\r
-\r
-/**\r
- * The PagesFilter is an AccessRestrictionFilter which ensures the gh-pages\r
- * requests for a view-restricted repository are authenticated and authorized.\r
- *\r
- * @author James Moger\r
- *\r
- */\r
-@Singleton\r
-public class PagesFilter extends AccessRestrictionFilter {\r
-\r
- @Inject\r
- public PagesFilter(IRuntimeManager runtimeManager,\r
- ISessionManager sessionManager,\r
- IRepositoryManager repositoryManager) {\r
-\r
- super(runtimeManager, sessionManager, repositoryManager);\r
- }\r
-\r
- /**\r
- * Extract the repository name from the url.\r
- *\r
- * @param url\r
- * @return repository name\r
- */\r
- @Override\r
- protected String extractRepositoryName(String url) {\r
- // get the repository name from the url by finding a known url suffix\r
- String repository = "";\r
- Repository r = null;\r
- int offset = 0;\r
- while (r == null) {\r
- int slash = url.indexOf('/', offset);\r
- if (slash == -1) {\r
- repository = url;\r
- } else {\r
- repository = url.substring(0, slash);\r
- }\r
- r = repositoryManager.getRepository(repository, false);\r
- if (r == null) {\r
- // try again\r
- offset = slash + 1;\r
- } else {\r
- // close the repo\r
- r.close();\r
- }\r
- if (repository.equals(url)) {\r
- // either only repository in url or no repository found\r
- break;\r
- }\r
- }\r
- return repository;\r
- }\r
-\r
- /**\r
- * Analyze the url and returns the action of the request.\r
- *\r
- * @param cloneUrl\r
- * @return action of the request\r
- */\r
- @Override\r
- protected String getUrlRequestAction(String suffix) {\r
- return "VIEW";\r
- }\r
-\r
- /**\r
- * Determine if a non-existing repository can be created using this filter.\r
- *\r
- * @return true if the filter allows repository creation\r
- */\r
- @Override\r
- protected boolean isCreationAllowed() {\r
- return false;\r
- }\r
-\r
- /**\r
- * Determine if the action may be executed on the repository.\r
- *\r
- * @param repository\r
- * @param action\r
- * @return true if the action may be performed\r
- */\r
- @Override\r
- protected boolean isActionAllowed(RepositoryModel repository, String action) {\r
- return true;\r
- }\r
-\r
- /**\r
- * Determine if the repository requires authentication.\r
- *\r
- * @param repository\r
- * @param action\r
- * @return true if authentication required\r
- */\r
- @Override\r
- protected boolean requiresAuthentication(RepositoryModel repository, String action) {\r
- return repository.accessRestriction.atLeast(AccessRestrictionType.VIEW);\r
- }\r
-\r
- /**\r
- * Determine if the user can access the repository and perform the specified\r
- * action.\r
- *\r
- * @param repository\r
- * @param user\r
- * @param action\r
- * @return true if user may execute the action on the repository\r
- */\r
- @Override\r
- protected boolean canAccess(RepositoryModel repository, UserModel user, String action) {\r
- return user.canView(repository);\r
- }\r
-}\r
+++ /dev/null
-/*\r
- * Copyright 2012 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.io.IOException;\r
-import java.text.MessageFormat;\r
-import java.text.ParseException;\r
-import java.util.ArrayList;\r
-import java.util.List;\r
-import java.util.Set;\r
-import java.util.TreeSet;\r
-\r
-import javax.inject.Inject;\r
-import javax.inject.Singleton;\r
-import javax.servlet.ServletContext;\r
-import javax.servlet.ServletException;\r
-import javax.servlet.http.HttpServlet;\r
-import javax.servlet.http.HttpServletRequest;\r
-import javax.servlet.http.HttpServletResponse;\r
-\r
-import org.eclipse.jgit.lib.Repository;\r
-import org.eclipse.jgit.revwalk.RevCommit;\r
-import org.eclipse.jgit.revwalk.RevTree;\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.gitblit.manager.IRepositoryManager;\r
-import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.models.PathModel;\r
-import com.gitblit.models.RefModel;\r
-import com.gitblit.utils.ArrayUtils;\r
-import com.gitblit.utils.ByteFormat;\r
-import com.gitblit.utils.JGitUtils;\r
-import com.gitblit.utils.MarkdownUtils;\r
-import com.gitblit.utils.StringUtils;\r
-import com.gitblit.wicket.MarkupProcessor;\r
-import com.gitblit.wicket.MarkupProcessor.MarkupDocument;\r
-\r
-/**\r
- * Serves the content of a gh-pages branch.\r
- *\r
- * @author James Moger\r
- *\r
- */\r
-@Singleton\r
-public class PagesServlet extends HttpServlet {\r
-\r
- private static final long serialVersionUID = 1L;\r
-\r
- private transient Logger logger = LoggerFactory.getLogger(PagesServlet.class);\r
-\r
- private final IStoredSettings settings;\r
-\r
- private final IRepositoryManager repositoryManager;\r
-\r
- @Inject\r
- public PagesServlet(\r
- IRuntimeManager runtimeManager,\r
- IRepositoryManager repositoryManager) {\r
-\r
- super();\r
- this.settings = runtimeManager.getSettings();\r
- this.repositoryManager = repositoryManager;\r
- }\r
-\r
- /**\r
- * Returns an url to this servlet for the specified parameters.\r
- *\r
- * @param baseURL\r
- * @param repository\r
- * @param path\r
- * @return an url\r
- */\r
- public static String asLink(String baseURL, String repository, String path) {\r
- if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {\r
- baseURL = baseURL.substring(0, baseURL.length() - 1);\r
- }\r
- return baseURL + Constants.PAGES + repository + "/" + (path == null ? "" : ("/" + path));\r
- }\r
-\r
- /**\r
- * Retrieves the specified resource from the gh-pages branch of the\r
- * repository.\r
- *\r
- * @param request\r
- * @param response\r
- * @throws javax.servlet.ServletException\r
- * @throws java.io.IOException\r
- */\r
- private void processRequest(HttpServletRequest request, HttpServletResponse response)\r
- throws ServletException, IOException {\r
- String path = request.getPathInfo();\r
- if (path.toLowerCase().endsWith(".git")) {\r
- // forward to url with trailing /\r
- // this is important for relative pages links\r
- response.sendRedirect(request.getServletPath() + path + "/");\r
- return;\r
- }\r
- if (path.charAt(0) == '/') {\r
- // strip leading /\r
- path = path.substring(1);\r
- }\r
-\r
- // determine repository and resource from url\r
- String repository = "";\r
- String resource = "";\r
- Repository r = null;\r
- int offset = 0;\r
- while (r == null) {\r
- int slash = path.indexOf('/', offset);\r
- if (slash == -1) {\r
- repository = path;\r
- } else {\r
- repository = path.substring(0, slash);\r
- }\r
- r = repositoryManager.getRepository(repository, false);\r
- offset = slash + 1;\r
- if (offset > 0) {\r
- resource = path.substring(offset);\r
- }\r
- if (repository.equals(path)) {\r
- // either only repository in url or no repository found\r
- break;\r
- }\r
- }\r
-\r
- ServletContext context = request.getSession().getServletContext();\r
-\r
- try {\r
- if (r == null) {\r
- // repository not found!\r
- String mkd = MessageFormat.format(\r
- "# Error\nSorry, no valid **repository** specified in this url: {0}!",\r
- repository);\r
- error(response, mkd);\r
- return;\r
- }\r
-\r
- // retrieve the content from the repository\r
- RefModel pages = JGitUtils.getPagesBranch(r);\r
- RevCommit commit = JGitUtils.getCommit(r, pages.getObjectId().getName());\r
-\r
- if (commit == null) {\r
- // branch not found!\r
- String mkd = MessageFormat.format(\r
- "# Error\nSorry, the repository {0} does not have a **gh-pages** branch!",\r
- repository);\r
- error(response, mkd);\r
- r.close();\r
- return;\r
- }\r
-\r
- MarkupProcessor processor = new MarkupProcessor(settings);\r
- String [] encodings = settings.getStrings(Keys.web.blobEncodings).toArray(new String[0]);\r
-\r
- RevTree tree = commit.getTree();\r
-\r
- String res = resource;\r
- if (res.endsWith("/")) {\r
- res = res.substring(0, res.length() - 1);\r
- }\r
- Set<String> names = new TreeSet<String>();\r
- for (PathModel entry : JGitUtils.getFilesInPath(r, res, commit)) {\r
- names.add(entry.name);\r
- }\r
-\r
- byte[] content = null;\r
- if (names.isEmpty()) {\r
- // not a path, a specific resource\r
- try {\r
- String contentType = context.getMimeType(resource);\r
- if (contentType == null) {\r
- contentType = "text/plain";\r
- }\r
- if (contentType.startsWith("text")) {\r
- content = JGitUtils.getStringContent(r, tree, resource, encodings).getBytes(\r
- Constants.ENCODING);\r
- } else {\r
- content = JGitUtils.getByteContent(r, tree, resource, false);\r
- }\r
- response.setContentType(contentType);\r
- } catch (Exception e) {\r
- }\r
- } else {\r
- // path\r
- List<String> extensions = new ArrayList<String>();\r
- extensions.add("html");\r
- extensions.add("htm");\r
- extensions.addAll(processor.getMarkupExtensions());\r
- for (String ext : extensions) {\r
- String file = "index." + ext;\r
-\r
- if (names.contains(file)) {\r
- String stringContent = JGitUtils.getStringContent(r, tree, file, encodings);\r
- if (stringContent == null) {\r
- continue;\r
- }\r
- content = stringContent.getBytes(Constants.ENCODING);\r
- if (content != null) {\r
- resource = file;\r
- // assume text/html unless the servlet container\r
- // overrides\r
- response.setContentType("text/html; charset=" + Constants.ENCODING);\r
- break;\r
- }\r
- }\r
- }\r
- }\r
-\r
- // no content, try custom 404 page\r
- if (ArrayUtils.isEmpty(content)) {\r
- String ext = StringUtils.getFileExtension(resource);\r
- if (StringUtils.isEmpty(ext)) {\r
- // document list\r
- response.setContentType("text/html");\r
- response.getWriter().append("<style>table th, table td { min-width: 150px; text-align: left; }</style>");\r
- response.getWriter().append("<table>");\r
- response.getWriter().append("<thead><tr><th>path</th><th>mode</th><th>size</th></tr>");\r
- response.getWriter().append("</thead>");\r
- response.getWriter().append("<tbody>");\r
- String pattern = "<tr><td><a href=\"{0}\">{0}</a></td><td>{1}</td><td>{2}</td></tr>";\r
- final ByteFormat byteFormat = new ByteFormat();\r
- List<PathModel> entries = JGitUtils.getFilesInPath(r, resource, commit);\r
- for (PathModel entry : entries) {\r
- response.getWriter().append(MessageFormat.format(pattern, entry.name, JGitUtils.getPermissionsFromMode(entry.mode), byteFormat.format(entry.size)));\r
- }\r
- response.getWriter().append("</tbody>");\r
- response.getWriter().append("</table>");\r
- } else {\r
- // 404\r
- String custom404 = JGitUtils.getStringContent(r, tree, "404.html", encodings);\r
- if (!StringUtils.isEmpty(custom404)) {\r
- content = custom404.getBytes(Constants.ENCODING);\r
- }\r
-\r
- // still no content\r
- if (ArrayUtils.isEmpty(content)) {\r
- String str = MessageFormat.format(\r
- "# Error\nSorry, the requested resource **{0}** was not found.",\r
- resource);\r
- content = MarkdownUtils.transformMarkdown(str).getBytes(Constants.ENCODING);\r
- }\r
-\r
- try {\r
- // output the content\r
- logger.warn("Pages 404: " + resource);\r
- response.setStatus(HttpServletResponse.SC_NOT_FOUND);\r
- response.getOutputStream().write(content);\r
- response.flushBuffer();\r
- } catch (Throwable t) {\r
- logger.error("Failed to write page to client", t);\r
- }\r
- }\r
- return;\r
- }\r
-\r
- // check to see if we should transform markup files\r
- String ext = StringUtils.getFileExtension(resource);\r
- if (processor.getMarkupExtensions().contains(ext)) {\r
- String markup = new String(content, Constants.ENCODING);\r
- MarkupDocument markupDoc = processor.parse(repository, commit.getName(), resource, markup);\r
- content = markupDoc.html.getBytes("UTF-8");\r
- response.setContentType("text/html; charset=" + Constants.ENCODING);\r
- }\r
-\r
- try {\r
- // output the content\r
- response.setHeader("Cache-Control", "public, max-age=3600, must-revalidate");\r
- response.setDateHeader("Last-Modified", JGitUtils.getCommitDate(commit).getTime());\r
- response.getOutputStream().write(content);\r
- response.flushBuffer();\r
- } catch (Throwable t) {\r
- logger.error("Failed to write page to client", t);\r
- }\r
-\r
- // close the repository\r
- r.close();\r
- } catch (Throwable t) {\r
- logger.error("Failed to write page to client", t);\r
- }\r
- }\r
-\r
- private void error(HttpServletResponse response, String mkd) throws ServletException,\r
- IOException, ParseException {\r
- String content = MarkdownUtils.transformMarkdown(mkd);\r
- response.setContentType("text/html; charset=" + Constants.ENCODING);\r
- response.getWriter().write(content);\r
- }\r
-\r
- @Override\r
- protected void doPost(HttpServletRequest request, HttpServletResponse response)\r
- throws ServletException, IOException {\r
- processRequest(request, response);\r
- }\r
-\r
- @Override\r
- protected void doGet(HttpServletRequest request, HttpServletResponse response)\r
- throws ServletException, IOException {\r
- processRequest(request, response);\r
- }\r
-}\r
+++ /dev/null
-/*\r
- * Copyright 2012 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.io.File;\r
-import java.io.IOException;\r
-\r
-import javax.inject.Inject;\r
-import javax.inject.Singleton;\r
-import javax.servlet.ServletException;\r
-import javax.servlet.http.HttpServlet;\r
-import javax.servlet.http.HttpServletRequest;\r
-import javax.servlet.http.HttpServletResponse;\r
-\r
-import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.utils.FileUtils;\r
-\r
-/**\r
- * Handles requests for robots.txt\r
- *\r
- * @author James Moger\r
- *\r
- */\r
-@Singleton\r
-public class RobotsTxtServlet extends HttpServlet {\r
-\r
- private static final long serialVersionUID = 1L;\r
-\r
- private final IRuntimeManager runtimeManager;\r
-\r
- @Inject\r
- public RobotsTxtServlet(IRuntimeManager runtimeManager) {\r
- super();\r
- this.runtimeManager = runtimeManager;\r
- }\r
-\r
- @Override\r
- protected void doPost(HttpServletRequest request, HttpServletResponse response)\r
- throws ServletException, java.io.IOException {\r
- processRequest(request, response);\r
- }\r
-\r
- @Override\r
- protected void doGet(HttpServletRequest request, HttpServletResponse response)\r
- throws ServletException, IOException {\r
- processRequest(request, response);\r
- }\r
-\r
- protected void processRequest(javax.servlet.http.HttpServletRequest request,\r
- javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,\r
- java.io.IOException {\r
- File file = runtimeManager.getFileOrFolder(Keys.web.robots.txt, null);\r
- String content = "";\r
- if (file.exists()) {\r
- content = FileUtils.readContent(file, "\n");\r
- }\r
- response.getWriter().append(content);\r
- }\r
-}\r
+++ /dev/null
-/*\r
- * Copyright 2011 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.io.IOException;\r
-import java.text.MessageFormat;\r
-\r
-import javax.inject.Inject;\r
-import javax.inject.Singleton;\r
-import javax.servlet.FilterChain;\r
-import javax.servlet.ServletException;\r
-import javax.servlet.ServletRequest;\r
-import javax.servlet.ServletResponse;\r
-import javax.servlet.http.HttpServletRequest;\r
-import javax.servlet.http.HttpServletResponse;\r
-\r
-import com.gitblit.Constants.RpcRequest;\r
-import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.manager.ISessionManager;\r
-import com.gitblit.models.UserModel;\r
-\r
-/**\r
- * The RpcFilter is a servlet filter that secures the RpcServlet.\r
- *\r
- * The filter extracts the rpc request type from the url and determines if the\r
- * requested action requires a Basic authentication prompt. If authentication is\r
- * required and no credentials are stored in the "Authorization" header, then a\r
- * basic authentication challenge is issued.\r
- *\r
- * http://en.wikipedia.org/wiki/Basic_access_authentication\r
- *\r
- * @author James Moger\r
- *\r
- */\r
-@Singleton\r
-public class RpcFilter extends AuthenticationFilter {\r
-\r
- private final IStoredSettings settings;\r
-\r
- private final IRuntimeManager runtimeManager;\r
-\r
- @Inject\r
- public RpcFilter(\r
- IRuntimeManager runtimeManager,\r
- ISessionManager sessionManager) {\r
-\r
- super(sessionManager);\r
- this.settings = runtimeManager.getSettings();\r
- this.runtimeManager = runtimeManager;\r
- }\r
-\r
- /**\r
- * doFilter does the actual work of preprocessing the request to ensure that\r
- * the user may proceed.\r
- *\r
- * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,\r
- * javax.servlet.ServletResponse, javax.servlet.FilterChain)\r
- */\r
- @Override\r
- public void doFilter(final ServletRequest request, final ServletResponse response,\r
- final FilterChain chain) throws IOException, ServletException {\r
-\r
- HttpServletRequest httpRequest = (HttpServletRequest) request;\r
- HttpServletResponse httpResponse = (HttpServletResponse) response;\r
-\r
- String fullUrl = getFullUrl(httpRequest);\r
- RpcRequest requestType = RpcRequest.fromName(httpRequest.getParameter("req"));\r
- if (requestType == null) {\r
- httpResponse.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);\r
- return;\r
- }\r
-\r
- boolean adminRequest = requestType.exceeds(RpcRequest.LIST_SETTINGS);\r
-\r
- // conditionally reject all rpc requests\r
- if (!settings.getBoolean(Keys.web.enableRpcServlet, true)) {\r
- logger.warn(Keys.web.enableRpcServlet + " must be set TRUE for rpc requests.");\r
- httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);\r
- return;\r
- }\r
-\r
- boolean authenticateView = settings.getBoolean(Keys.web.authenticateViewPages, false);\r
- boolean authenticateAdmin = settings.getBoolean(Keys.web.authenticateAdminPages, true);\r
-\r
- // Wrap the HttpServletRequest with the RpcServletRequest which\r
- // overrides the servlet container user principal methods.\r
- AuthenticatedRequest authenticatedRequest = new AuthenticatedRequest(httpRequest);\r
- UserModel user = getUser(httpRequest);\r
- if (user != null) {\r
- authenticatedRequest.setUser(user);\r
- }\r
-\r
- // conditionally reject rpc management/administration requests\r
- if (adminRequest && !settings.getBoolean(Keys.web.enableRpcManagement, false)) {\r
- logger.warn(MessageFormat.format("{0} must be set TRUE for {1} rpc requests.",\r
- Keys.web.enableRpcManagement, requestType.toString()));\r
- httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);\r
- return;\r
- }\r
-\r
- // BASIC authentication challenge and response processing\r
- if ((adminRequest && authenticateAdmin) || (!adminRequest && authenticateView)) {\r
- if (user == null) {\r
- // challenge client to provide credentials. send 401.\r
- if (runtimeManager.isDebugMode()) {\r
- logger.info(MessageFormat.format("RPC: CHALLENGE {0}", fullUrl));\r
-\r
- }\r
- httpResponse.setHeader("WWW-Authenticate", CHALLENGE);\r
- httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);\r
- return;\r
- } else {\r
- // check user access for request\r
- if (user.canAdmin() || canAccess(user, requestType)) {\r
- // authenticated request permitted.\r
- // pass processing to the restricted servlet.\r
- newSession(authenticatedRequest, httpResponse);\r
- logger.info(MessageFormat.format("RPC: {0} ({1}) authenticated", fullUrl,\r
- HttpServletResponse.SC_CONTINUE));\r
- chain.doFilter(authenticatedRequest, httpResponse);\r
- return;\r
- }\r
- // valid user, but not for requested access. send 403.\r
- if (runtimeManager.isDebugMode()) {\r
- logger.info(MessageFormat.format("RPC: {0} forbidden to access {1}",\r
- user.username, fullUrl));\r
- }\r
- httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);\r
- return;\r
- }\r
- }\r
-\r
- if (runtimeManager.isDebugMode()) {\r
- logger.info(MessageFormat.format("RPC: {0} ({1}) unauthenticated", fullUrl,\r
- HttpServletResponse.SC_CONTINUE));\r
- }\r
- // unauthenticated request permitted.\r
- // pass processing to the restricted servlet.\r
- chain.doFilter(authenticatedRequest, httpResponse);\r
- }\r
-\r
- private boolean canAccess(UserModel user, RpcRequest requestType) {\r
- switch (requestType) {\r
- case GET_PROTOCOL:\r
- return true;\r
- case LIST_REPOSITORIES:\r
- return true;\r
- default:\r
- return user.canAdmin();\r
- }\r
- }\r
-}
\ No newline at end of file
+++ /dev/null
-/*\r
- * Copyright 2011 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.io.IOException;\r
-import java.text.MessageFormat;\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.HashMap;\r
-import java.util.List;\r
-import java.util.Map;\r
-\r
-import javax.inject.Inject;\r
-import javax.inject.Singleton;\r
-import javax.servlet.ServletException;\r
-import javax.servlet.http.HttpServletRequest;\r
-import javax.servlet.http.HttpServletResponse;\r
-\r
-import org.eclipse.jgit.lib.Repository;\r
-\r
-import com.gitblit.Constants.RpcRequest;\r
-import com.gitblit.manager.IFederationManager;\r
-import com.gitblit.manager.IGitblitManager;\r
-import com.gitblit.manager.IRepositoryManager;\r
-import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.manager.IUserManager;\r
-import com.gitblit.models.RefModel;\r
-import com.gitblit.models.RegistrantAccessPermission;\r
-import com.gitblit.models.RepositoryModel;\r
-import com.gitblit.models.ServerSettings;\r
-import com.gitblit.models.TeamModel;\r
-import com.gitblit.models.UserModel;\r
-import com.gitblit.utils.DeepCopier;\r
-import com.gitblit.utils.HttpUtils;\r
-import com.gitblit.utils.JGitUtils;\r
-import com.gitblit.utils.RpcUtils;\r
-import com.gitblit.utils.StringUtils;\r
-\r
-/**\r
- * Handles remote procedure calls.\r
- *\r
- * @author James Moger\r
- *\r
- */\r
-@Singleton\r
-public class RpcServlet extends JsonServlet {\r
-\r
- private static final long serialVersionUID = 1L;\r
-\r
- public static final int PROTOCOL_VERSION = 6;\r
-\r
- private final IStoredSettings settings;\r
-\r
- private final IRuntimeManager runtimeManager;\r
-\r
- private final IUserManager userManager;\r
-\r
- private final IRepositoryManager repositoryManager;\r
-\r
- private final IFederationManager federationManager;\r
-\r
- private final IGitblitManager gitblitManager;\r
-\r
- @Inject\r
- public RpcServlet(\r
- IRuntimeManager runtimeManager,\r
- IUserManager userManager,\r
- IRepositoryManager repositoryManager,\r
- IFederationManager federationManager,\r
- IGitblitManager gitblitManager) {\r
-\r
- super();\r
-\r
- this.settings = runtimeManager.getSettings();\r
- this.runtimeManager = runtimeManager;\r
- this.userManager = userManager;\r
- this.repositoryManager = repositoryManager;\r
- this.federationManager = federationManager;\r
- this.gitblitManager = gitblitManager;\r
- }\r
-\r
- /**\r
- * Processes an rpc request.\r
- *\r
- * @param request\r
- * @param response\r
- * @throws javax.servlet.ServletException\r
- * @throws java.io.IOException\r
- */\r
- @Override\r
- protected void processRequest(HttpServletRequest request, HttpServletResponse response)\r
- throws ServletException, IOException {\r
- RpcRequest reqType = RpcRequest.fromName(request.getParameter("req"));\r
- String objectName = request.getParameter("name");\r
- logger.info(MessageFormat.format("Rpc {0} request from {1}", reqType,\r
- request.getRemoteAddr()));\r
-\r
- UserModel user = (UserModel) request.getUserPrincipal();\r
-\r
- boolean allowManagement = user != null && user.canAdmin()\r
- && settings.getBoolean(Keys.web.enableRpcManagement, false);\r
-\r
- boolean allowAdmin = user != null && user.canAdmin()\r
- && settings.getBoolean(Keys.web.enableRpcAdministration, false);\r
-\r
- Object result = null;\r
- if (RpcRequest.GET_PROTOCOL.equals(reqType)) {\r
- // Return the protocol version\r
- result = PROTOCOL_VERSION;\r
- } else if (RpcRequest.LIST_REPOSITORIES.equals(reqType)) {\r
- // Determine the Gitblit clone url\r
- String gitblitUrl = HttpUtils.getGitblitURL(request);\r
- StringBuilder sb = new StringBuilder();\r
- sb.append(gitblitUrl);\r
- sb.append(Constants.GIT_PATH);\r
- sb.append("{0}");\r
- String cloneUrl = sb.toString();\r
-\r
- // list repositories\r
- List<RepositoryModel> list = repositoryManager.getRepositoryModels(user);\r
- Map<String, RepositoryModel> repositories = new HashMap<String, RepositoryModel>();\r
- for (RepositoryModel model : list) {\r
- String url = MessageFormat.format(cloneUrl, model.name);\r
- repositories.put(url, model);\r
- }\r
- result = repositories;\r
- } else if (RpcRequest.LIST_BRANCHES.equals(reqType)) {\r
- // list all local branches in all repositories accessible to user\r
- Map<String, List<String>> localBranches = new HashMap<String, List<String>>();\r
- List<RepositoryModel> models = repositoryManager.getRepositoryModels(user);\r
- for (RepositoryModel model : models) {\r
- if (!model.hasCommits) {\r
- // skip empty repository\r
- continue;\r
- }\r
- if (model.isCollectingGarbage) {\r
- // skip garbage collecting repository\r
- logger.warn(MessageFormat.format("Temporarily excluding {0} from RPC, busy collecting garbage", model.name));\r
- continue;\r
- }\r
- // get local branches\r
- Repository repository = repositoryManager.getRepository(model.name);\r
- List<RefModel> refs = JGitUtils.getLocalBranches(repository, false, -1);\r
- if (model.showRemoteBranches) {\r
- // add remote branches if repository displays them\r
- refs.addAll(JGitUtils.getRemoteBranches(repository, false, -1));\r
- }\r
- if (refs.size() > 0) {\r
- List<String> branches = new ArrayList<String>();\r
- for (RefModel ref : refs) {\r
- branches.add(ref.getName());\r
- }\r
- localBranches.put(model.name, branches);\r
- }\r
- repository.close();\r
- }\r
- result = localBranches;\r
- } else if (RpcRequest.GET_USER.equals(reqType)) {\r
- if (StringUtils.isEmpty(objectName)) {\r
- if (UserModel.ANONYMOUS.equals(user)) {\r
- response.sendError(forbiddenCode);\r
- } else {\r
- // return the current user, reset credentials\r
- UserModel requestedUser = DeepCopier.copy(user);\r
- result = requestedUser;\r
- }\r
- } else {\r
- if (user.canAdmin() || objectName.equals(user.username)) {\r
- // return the specified user\r
- UserModel requestedUser = userManager.getUserModel(objectName);\r
- if (requestedUser == null) {\r
- response.setStatus(failureCode);\r
- } else {\r
- result = requestedUser;\r
- }\r
- } else {\r
- response.sendError(forbiddenCode);\r
- }\r
- }\r
- } else if (RpcRequest.LIST_USERS.equals(reqType)) {\r
- // list users\r
- List<String> names = userManager.getAllUsernames();\r
- List<UserModel> users = new ArrayList<UserModel>();\r
- for (String name : names) {\r
- users.add(userManager.getUserModel(name));\r
- }\r
- result = users;\r
- } else if (RpcRequest.LIST_TEAMS.equals(reqType)) {\r
- // list teams\r
- List<String> names = userManager.getAllTeamNames();\r
- List<TeamModel> teams = new ArrayList<TeamModel>();\r
- for (String name : names) {\r
- teams.add(userManager.getTeamModel(name));\r
- }\r
- result = teams;\r
- } else if (RpcRequest.CREATE_REPOSITORY.equals(reqType)) {\r
- // create repository\r
- RepositoryModel model = deserialize(request, response, RepositoryModel.class);\r
- try {\r
- repositoryManager.updateRepositoryModel(model.name, model, true);\r
- } catch (GitBlitException e) {\r
- response.setStatus(failureCode);\r
- }\r
- } else if (RpcRequest.EDIT_REPOSITORY.equals(reqType)) {\r
- // edit repository\r
- RepositoryModel model = deserialize(request, response, RepositoryModel.class);\r
- // name specifies original repository name in event of rename\r
- String repoName = objectName;\r
- if (repoName == null) {\r
- repoName = model.name;\r
- }\r
- try {\r
- repositoryManager.updateRepositoryModel(repoName, model, false);\r
- } catch (GitBlitException e) {\r
- response.setStatus(failureCode);\r
- }\r
- } else if (RpcRequest.DELETE_REPOSITORY.equals(reqType)) {\r
- // delete repository\r
- RepositoryModel model = deserialize(request, response, RepositoryModel.class);\r
- repositoryManager.deleteRepositoryModel(model);\r
- } else if (RpcRequest.CREATE_USER.equals(reqType)) {\r
- // create user\r
- UserModel model = deserialize(request, response, UserModel.class);\r
- try {\r
- gitblitManager.updateUserModel(model.username, model, true);\r
- } catch (GitBlitException e) {\r
- response.setStatus(failureCode);\r
- }\r
- } else if (RpcRequest.EDIT_USER.equals(reqType)) {\r
- // edit user\r
- UserModel model = deserialize(request, response, UserModel.class);\r
- // name parameter specifies original user name in event of rename\r
- String username = objectName;\r
- if (username == null) {\r
- username = model.username;\r
- }\r
- try {\r
- gitblitManager.updateUserModel(username, model, false);\r
- } catch (GitBlitException e) {\r
- response.setStatus(failureCode);\r
- }\r
- } else if (RpcRequest.DELETE_USER.equals(reqType)) {\r
- // delete user\r
- UserModel model = deserialize(request, response, UserModel.class);\r
- if (!userManager.deleteUser(model.username)) {\r
- response.setStatus(failureCode);\r
- }\r
- } else if (RpcRequest.CREATE_TEAM.equals(reqType)) {\r
- // create team\r
- TeamModel model = deserialize(request, response, TeamModel.class);\r
- try {\r
- gitblitManager.updateTeamModel(model.name, model, true);\r
- } catch (GitBlitException e) {\r
- response.setStatus(failureCode);\r
- }\r
- } else if (RpcRequest.EDIT_TEAM.equals(reqType)) {\r
- // edit team\r
- TeamModel model = deserialize(request, response, TeamModel.class);\r
- // name parameter specifies original team name in event of rename\r
- String teamname = objectName;\r
- if (teamname == null) {\r
- teamname = model.name;\r
- }\r
- try {\r
- gitblitManager.updateTeamModel(teamname, model, false);\r
- } catch (GitBlitException e) {\r
- response.setStatus(failureCode);\r
- }\r
- } else if (RpcRequest.DELETE_TEAM.equals(reqType)) {\r
- // delete team\r
- TeamModel model = deserialize(request, response, TeamModel.class);\r
- if (!userManager.deleteTeam(model.name)) {\r
- response.setStatus(failureCode);\r
- }\r
- } else if (RpcRequest.LIST_REPOSITORY_MEMBERS.equals(reqType)) {\r
- // get repository members\r
- RepositoryModel model = repositoryManager.getRepositoryModel(objectName);\r
- result = repositoryManager.getRepositoryUsers(model);\r
- } else if (RpcRequest.SET_REPOSITORY_MEMBERS.equals(reqType)) {\r
- // rejected since 1.2.0\r
- response.setStatus(failureCode);\r
- } else if (RpcRequest.LIST_REPOSITORY_MEMBER_PERMISSIONS.equals(reqType)) {\r
- // get repository member permissions\r
- RepositoryModel model = repositoryManager.getRepositoryModel(objectName);\r
- result = repositoryManager.getUserAccessPermissions(model);\r
- } else if (RpcRequest.SET_REPOSITORY_MEMBER_PERMISSIONS.equals(reqType)) {\r
- // set the repository permissions for the specified users\r
- RepositoryModel model = repositoryManager.getRepositoryModel(objectName);\r
- Collection<RegistrantAccessPermission> permissions = deserialize(request, response, RpcUtils.REGISTRANT_PERMISSIONS_TYPE);\r
- result = repositoryManager.setUserAccessPermissions(model, permissions);\r
- } else if (RpcRequest.LIST_REPOSITORY_TEAMS.equals(reqType)) {\r
- // get repository teams\r
- RepositoryModel model = repositoryManager.getRepositoryModel(objectName);\r
- result = repositoryManager.getRepositoryTeams(model);\r
- } else if (RpcRequest.SET_REPOSITORY_TEAMS.equals(reqType)) {\r
- // rejected since 1.2.0\r
- response.setStatus(failureCode);\r
- } else if (RpcRequest.LIST_REPOSITORY_TEAM_PERMISSIONS.equals(reqType)) {\r
- // get repository team permissions\r
- RepositoryModel model = repositoryManager.getRepositoryModel(objectName);\r
- result = repositoryManager.getTeamAccessPermissions(model);\r
- } else if (RpcRequest.SET_REPOSITORY_TEAM_PERMISSIONS.equals(reqType)) {\r
- // set the repository permissions for the specified teams\r
- RepositoryModel model = repositoryManager.getRepositoryModel(objectName);\r
- Collection<RegistrantAccessPermission> permissions = deserialize(request, response, RpcUtils.REGISTRANT_PERMISSIONS_TYPE);\r
- result = repositoryManager.setTeamAccessPermissions(model, permissions);\r
- } else if (RpcRequest.LIST_FEDERATION_REGISTRATIONS.equals(reqType)) {\r
- // return the list of federation registrations\r
- if (allowAdmin) {\r
- result = federationManager.getFederationRegistrations();\r
- } else {\r
- response.sendError(notAllowedCode);\r
- }\r
- } else if (RpcRequest.LIST_FEDERATION_RESULTS.equals(reqType)) {\r
- // return the list of federation result registrations\r
- if (allowAdmin && federationManager.canFederate()) {\r
- result = federationManager.getFederationResultRegistrations();\r
- } else {\r
- response.sendError(notAllowedCode);\r
- }\r
- } else if (RpcRequest.LIST_FEDERATION_PROPOSALS.equals(reqType)) {\r
- // return the list of federation proposals\r
- if (allowAdmin && federationManager.canFederate()) {\r
- result = federationManager.getPendingFederationProposals();\r
- } else {\r
- response.sendError(notAllowedCode);\r
- }\r
- } else if (RpcRequest.LIST_FEDERATION_SETS.equals(reqType)) {\r
- // return the list of federation sets\r
- if (allowAdmin && federationManager.canFederate()) {\r
- String gitblitUrl = HttpUtils.getGitblitURL(request);\r
- result = federationManager.getFederationSets(gitblitUrl);\r
- } else {\r
- response.sendError(notAllowedCode);\r
- }\r
- } else if (RpcRequest.LIST_SETTINGS.equals(reqType)) {\r
- // return the server's settings\r
- ServerSettings serverSettings = runtimeManager.getSettingsModel();\r
- if (allowAdmin) {\r
- // return all settings\r
- result = serverSettings;\r
- } else {\r
- // anonymous users get a few settings to allow browser launching\r
- List<String> keys = new ArrayList<String>();\r
- keys.add(Keys.web.siteName);\r
- keys.add(Keys.web.mountParameters);\r
- keys.add(Keys.web.syndicationEntries);\r
-\r
- if (allowManagement) {\r
- // keys necessary for repository and/or user management\r
- keys.add(Keys.realm.minPasswordLength);\r
- keys.add(Keys.realm.passwordStorage);\r
- keys.add(Keys.federation.sets);\r
- }\r
- // build the settings\r
- ServerSettings managementSettings = new ServerSettings();\r
- for (String key : keys) {\r
- managementSettings.add(serverSettings.get(key));\r
- }\r
- if (allowManagement) {\r
- managementSettings.pushScripts = serverSettings.pushScripts;\r
- }\r
- result = managementSettings;\r
- }\r
- } else if (RpcRequest.EDIT_SETTINGS.equals(reqType)) {\r
- // update settings on the server\r
- if (allowAdmin) {\r
- Map<String, String> map = deserialize(request, response,\r
- RpcUtils.SETTINGS_TYPE);\r
- runtimeManager.updateSettings(map);\r
- } else {\r
- response.sendError(notAllowedCode);\r
- }\r
- } else if (RpcRequest.LIST_STATUS.equals(reqType)) {\r
- // return the server's status information\r
- if (allowAdmin) {\r
- result = runtimeManager.getStatus();\r
- } else {\r
- response.sendError(notAllowedCode);\r
- }\r
- } else if (RpcRequest.CLEAR_REPOSITORY_CACHE.equals(reqType)) {\r
- // clear the repository list cache\r
- if (allowManagement) {\r
- repositoryManager.resetRepositoryListCache();\r
- } else {\r
- response.sendError(notAllowedCode);\r
- }\r
- }\r
-\r
- // send the result of the request\r
- serialize(response, result);\r
- }\r
-}\r
+++ /dev/null
-/*\r
- * Copyright 2013 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.io.IOException;\r
-import java.text.MessageFormat;\r
-\r
-import javax.inject.Inject;\r
-import javax.inject.Singleton;\r
-import javax.servlet.ServletException;\r
-import javax.servlet.http.HttpServlet;\r
-import javax.servlet.http.HttpServletRequest;\r
-import javax.servlet.http.HttpServletResponse;\r
-\r
-import com.gitblit.manager.IRepositoryManager;\r
-import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.manager.ISessionManager;\r
-import com.gitblit.manager.IUserManager;\r
-import com.gitblit.models.RepositoryModel;\r
-import com.gitblit.models.UserModel;\r
-import com.gitblit.utils.StringUtils;\r
-\r
-/**\r
- * Handles requests for Sparkleshare Invites\r
- *\r
- * @author James Moger\r
- *\r
- */\r
-@Singleton\r
-public class SparkleShareInviteServlet extends HttpServlet {\r
-\r
- private static final long serialVersionUID = 1L;\r
-\r
- private final IStoredSettings settings;\r
-\r
- private final IUserManager userManager;\r
-\r
- private final ISessionManager sessionManager;\r
-\r
- private final IRepositoryManager repositoryManager;\r
-\r
- @Inject\r
- public SparkleShareInviteServlet(\r
- IRuntimeManager runtimeManager,\r
- IUserManager userManager,\r
- ISessionManager sessionManager,\r
- IRepositoryManager repositoryManager) {\r
-\r
- super();\r
- this.settings = runtimeManager.getSettings();\r
- this.userManager = userManager;\r
- this.sessionManager = sessionManager;\r
- this.repositoryManager = repositoryManager;\r
- }\r
-\r
- @Override\r
- protected void doPost(HttpServletRequest request, HttpServletResponse response)\r
- throws ServletException, java.io.IOException {\r
- processRequest(request, response);\r
- }\r
-\r
- @Override\r
- protected void doGet(HttpServletRequest request, HttpServletResponse response)\r
- throws ServletException, IOException {\r
- processRequest(request, response);\r
- }\r
-\r
- protected void processRequest(javax.servlet.http.HttpServletRequest request,\r
- javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,\r
- java.io.IOException {\r
-\r
- // extract repo name from request\r
- String repoUrl = request.getPathInfo().substring(1);\r
-\r
- // trim trailing .xml\r
- if (repoUrl.endsWith(".xml")) {\r
- repoUrl = repoUrl.substring(0, repoUrl.length() - 4);\r
- }\r
-\r
- String servletPath = Constants.GIT_PATH;\r
-\r
- int schemeIndex = repoUrl.indexOf("://") + 3;\r
- String host = repoUrl.substring(0, repoUrl.indexOf('/', schemeIndex));\r
- String path = repoUrl.substring(repoUrl.indexOf(servletPath) + servletPath.length());\r
- String username = null;\r
- int fetchIndex = repoUrl.indexOf('@');\r
- if (fetchIndex > -1) {\r
- username = repoUrl.substring(schemeIndex, fetchIndex);\r
- }\r
- UserModel user;\r
- if (StringUtils.isEmpty(username)) {\r
- user = sessionManager.authenticate(request);\r
- } else {\r
- user = userManager.getUserModel(username);\r
- }\r
- if (user == null) {\r
- user = UserModel.ANONYMOUS;\r
- username = "";\r
- }\r
-\r
- // ensure that the requested repository exists\r
- RepositoryModel model = repositoryManager.getRepositoryModel(path);\r
- if (model == null) {\r
- response.setStatus(HttpServletResponse.SC_NOT_FOUND);\r
- response.getWriter().append(MessageFormat.format("Repository \"{0}\" not found!", path));\r
- return;\r
- }\r
-\r
- StringBuilder sb = new StringBuilder();\r
- sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");\r
- sb.append("<sparkleshare><invite>\n");\r
- sb.append(MessageFormat.format("<address>{0}</address>\n", host));\r
- sb.append(MessageFormat.format("<remote_path>{0}{1}</remote_path>\n", servletPath, model.name));\r
- if (settings.getInteger(Keys.fanout.port, 0) > 0) {\r
- // Gitblit is running it's own fanout service for pubsub notifications\r
- sb.append(MessageFormat.format("<announcements_url>tcp://{0}:{1}</announcements_url>\n", request.getServerName(), settings.getString(Keys.fanout.port, "")));\r
- }\r
- sb.append("</invite></sparkleshare>\n");\r
-\r
- // write invite to client\r
- response.setContentType("application/xml");\r
- response.setContentLength(sb.length());\r
- response.getWriter().append(sb.toString());\r
- }\r
-}\r
+++ /dev/null
-/*\r
- * Copyright 2011 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.io.IOException;\r
-import java.text.MessageFormat;\r
-\r
-import javax.inject.Inject;\r
-import javax.inject.Singleton;\r
-import javax.servlet.FilterChain;\r
-import javax.servlet.ServletException;\r
-import javax.servlet.ServletRequest;\r
-import javax.servlet.ServletResponse;\r
-import javax.servlet.http.HttpServletRequest;\r
-import javax.servlet.http.HttpServletResponse;\r
-\r
-import com.gitblit.Constants.AccessRestrictionType;\r
-import com.gitblit.manager.IProjectManager;\r
-import com.gitblit.manager.IRepositoryManager;\r
-import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.manager.ISessionManager;\r
-import com.gitblit.models.ProjectModel;\r
-import com.gitblit.models.RepositoryModel;\r
-import com.gitblit.models.UserModel;\r
-\r
-/**\r
- * The SyndicationFilter is an AuthenticationFilter which ensures that feed\r
- * requests for projects or view-restricted repositories have proper authentication\r
- * credentials and are authorized for the requested feed.\r
- *\r
- * @author James Moger\r
- *\r
- */\r
-@Singleton\r
-public class SyndicationFilter extends AuthenticationFilter {\r
-\r
- private final IRuntimeManager runtimeManager;\r
- private final IRepositoryManager repositoryManager;\r
- private final IProjectManager projectManager;\r
-\r
- @Inject\r
- public SyndicationFilter(\r
- IRuntimeManager runtimeManager,\r
- ISessionManager sessionManager,\r
- IRepositoryManager repositoryManager,\r
- IProjectManager projectManager) {\r
-\r
- super(sessionManager);\r
- this.runtimeManager = runtimeManager;\r
- this.repositoryManager = repositoryManager;\r
- this.projectManager = projectManager;\r
- }\r
-\r
- /**\r
- * Extract the repository name from the url.\r
- *\r
- * @param url\r
- * @return repository name\r
- */\r
- protected String extractRequestedName(String url) {\r
- if (url.indexOf('?') > -1) {\r
- return url.substring(0, url.indexOf('?'));\r
- }\r
- return url;\r
- }\r
-\r
- /**\r
- * doFilter does the actual work of preprocessing the request to ensure that\r
- * the user may proceed.\r
- *\r
- * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,\r
- * javax.servlet.ServletResponse, javax.servlet.FilterChain)\r
- */\r
- @Override\r
- public void doFilter(final ServletRequest request, final ServletResponse response,\r
- final FilterChain chain) throws IOException, ServletException {\r
-\r
- HttpServletRequest httpRequest = (HttpServletRequest) request;\r
- HttpServletResponse httpResponse = (HttpServletResponse) response;\r
-\r
- String fullUrl = getFullUrl(httpRequest);\r
- String name = extractRequestedName(fullUrl);\r
-\r
- ProjectModel project = projectManager.getProjectModel(name);\r
- RepositoryModel model = null;\r
-\r
- if (project == null) {\r
- // try loading a repository model\r
- model = repositoryManager.getRepositoryModel(name);\r
- if (model == null) {\r
- // repository not found. send 404.\r
- logger.info(MessageFormat.format("ARF: {0} ({1})", fullUrl,\r
- HttpServletResponse.SC_NOT_FOUND));\r
- httpResponse.sendError(HttpServletResponse.SC_NOT_FOUND);\r
- return;\r
- }\r
- }\r
-\r
- // Wrap the HttpServletRequest with the AccessRestrictionRequest which\r
- // overrides the servlet container user principal methods.\r
- // JGit requires either:\r
- //\r
- // 1. servlet container authenticated user\r
- // 2. http.receivepack = true in each repository's config\r
- //\r
- // Gitblit must conditionally authenticate users per-repository so just\r
- // enabling http.receivepack is insufficient.\r
- AuthenticatedRequest authenticatedRequest = new AuthenticatedRequest(httpRequest);\r
- UserModel user = getUser(httpRequest);\r
- if (user != null) {\r
- authenticatedRequest.setUser(user);\r
- }\r
-\r
- // BASIC authentication challenge and response processing\r
- if (model != null) {\r
- if (model.accessRestriction.atLeast(AccessRestrictionType.VIEW)) {\r
- if (user == null) {\r
- // challenge client to provide credentials. send 401.\r
- if (runtimeManager.isDebugMode()) {\r
- logger.info(MessageFormat.format("ARF: CHALLENGE {0}", fullUrl));\r
- }\r
- httpResponse.setHeader("WWW-Authenticate", CHALLENGE);\r
- httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);\r
- return;\r
- } else {\r
- // check user access for request\r
- if (user.canView(model)) {\r
- // authenticated request permitted.\r
- // pass processing to the restricted servlet.\r
- newSession(authenticatedRequest, httpResponse);\r
- logger.info(MessageFormat.format("ARF: {0} ({1}) authenticated", fullUrl,\r
- HttpServletResponse.SC_CONTINUE));\r
- chain.doFilter(authenticatedRequest, httpResponse);\r
- return;\r
- }\r
- // valid user, but not for requested access. send 403.\r
- if (runtimeManager.isDebugMode()) {\r
- logger.info(MessageFormat.format("ARF: {0} forbidden to access {1}",\r
- user.username, fullUrl));\r
- }\r
- httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);\r
- return;\r
- }\r
- }\r
- }\r
-\r
- if (runtimeManager.isDebugMode()) {\r
- logger.info(MessageFormat.format("ARF: {0} ({1}) unauthenticated", fullUrl,\r
- HttpServletResponse.SC_CONTINUE));\r
- }\r
- // unauthenticated request permitted.\r
- // pass processing to the restricted servlet.\r
- chain.doFilter(authenticatedRequest, httpResponse);\r
- }\r
-}\r
+++ /dev/null
-/*\r
- * Copyright 2011 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.text.MessageFormat;\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.Collections;\r
-import java.util.List;\r
-import java.util.Map;\r
-\r
-import javax.inject.Inject;\r
-import javax.inject.Singleton;\r
-import javax.servlet.http.HttpServlet;\r
-\r
-import org.eclipse.jgit.lib.ObjectId;\r
-import org.eclipse.jgit.lib.Repository;\r
-import org.eclipse.jgit.revwalk.RevCommit;\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.gitblit.AuthenticationFilter.AuthenticatedRequest;\r
-import com.gitblit.manager.IProjectManager;\r
-import com.gitblit.manager.IRepositoryManager;\r
-import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.models.FeedEntryModel;\r
-import com.gitblit.models.ProjectModel;\r
-import com.gitblit.models.RefModel;\r
-import com.gitblit.models.RepositoryModel;\r
-import com.gitblit.models.UserModel;\r
-import com.gitblit.utils.HttpUtils;\r
-import com.gitblit.utils.JGitUtils;\r
-import com.gitblit.utils.MessageProcessor;\r
-import com.gitblit.utils.StringUtils;\r
-import com.gitblit.utils.SyndicationUtils;\r
-\r
-/**\r
- * SyndicationServlet generates RSS 2.0 feeds and feed links.\r
- *\r
- * Access to this servlet is protected by the SyndicationFilter.\r
- *\r
- * @author James Moger\r
- *\r
- */\r
-@Singleton\r
-public class SyndicationServlet extends HttpServlet {\r
-\r
- private static final long serialVersionUID = 1L;\r
-\r
- private transient Logger logger = LoggerFactory.getLogger(SyndicationServlet.class);\r
-\r
- private final IStoredSettings settings;\r
-\r
- private final IRepositoryManager repositoryManager;\r
-\r
- private final IProjectManager projectManager;\r
-\r
- @Inject\r
- public SyndicationServlet(\r
- IRuntimeManager runtimeManager,\r
- IRepositoryManager repositoryManager,\r
- IProjectManager projectManager) {\r
-\r
- super();\r
- this.settings = runtimeManager.getSettings();\r
- this.repositoryManager = repositoryManager;\r
- this.projectManager = projectManager;\r
- }\r
-\r
- /**\r
- * Create a feed link for the specified repository and branch/tag/commit id.\r
- *\r
- * @param baseURL\r
- * @param repository\r
- * the repository name\r
- * @param objectId\r
- * the branch, tag, or first commit for the feed\r
- * @param length\r
- * the number of commits to include in the feed\r
- * @return an RSS feed url\r
- */\r
- public static String asLink(String baseURL, String repository, String objectId, int length) {\r
- if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {\r
- baseURL = baseURL.substring(0, baseURL.length() - 1);\r
- }\r
- StringBuilder url = new StringBuilder();\r
- url.append(baseURL);\r
- url.append(Constants.SYNDICATION_PATH);\r
- url.append(repository);\r
- if (!StringUtils.isEmpty(objectId) || length > 0) {\r
- StringBuilder parameters = new StringBuilder("?");\r
- if (StringUtils.isEmpty(objectId)) {\r
- parameters.append("l=");\r
- parameters.append(length);\r
- } else {\r
- parameters.append("h=");\r
- parameters.append(objectId);\r
- if (length > 0) {\r
- parameters.append("&l=");\r
- parameters.append(length);\r
- }\r
- }\r
- url.append(parameters);\r
- }\r
- return url.toString();\r
- }\r
-\r
- /**\r
- * Determines the appropriate title for a feed.\r
- *\r
- * @param repository\r
- * @param objectId\r
- * @return title of the feed\r
- */\r
- public static String getTitle(String repository, String objectId) {\r
- String id = objectId;\r
- if (!StringUtils.isEmpty(id)) {\r
- if (id.startsWith(org.eclipse.jgit.lib.Constants.R_HEADS)) {\r
- id = id.substring(org.eclipse.jgit.lib.Constants.R_HEADS.length());\r
- } else if (id.startsWith(org.eclipse.jgit.lib.Constants.R_REMOTES)) {\r
- id = id.substring(org.eclipse.jgit.lib.Constants.R_REMOTES.length());\r
- } else if (id.startsWith(org.eclipse.jgit.lib.Constants.R_TAGS)) {\r
- id = id.substring(org.eclipse.jgit.lib.Constants.R_TAGS.length());\r
- }\r
- }\r
- return MessageFormat.format("{0} ({1})", repository, id);\r
- }\r
-\r
- /**\r
- * Generates the feed content.\r
- *\r
- * @param request\r
- * @param response\r
- * @throws javax.servlet.ServletException\r
- * @throws java.io.IOException\r
- */\r
- private void processRequest(javax.servlet.http.HttpServletRequest request,\r
- javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,\r
- java.io.IOException {\r
-\r
- String servletUrl = request.getContextPath() + request.getServletPath();\r
- String url = request.getRequestURI().substring(servletUrl.length());\r
- if (url.charAt(0) == '/' && url.length() > 1) {\r
- url = url.substring(1);\r
- }\r
- String repositoryName = url;\r
- String objectId = request.getParameter("h");\r
- String l = request.getParameter("l");\r
- String page = request.getParameter("pg");\r
- String searchString = request.getParameter("s");\r
- Constants.SearchType searchType = Constants.SearchType.COMMIT;\r
- if (!StringUtils.isEmpty(request.getParameter("st"))) {\r
- Constants.SearchType type = Constants.SearchType.forName(request.getParameter("st"));\r
- if (type != null) {\r
- searchType = type;\r
- }\r
- }\r
- int length = settings.getInteger(Keys.web.syndicationEntries, 25);\r
- if (StringUtils.isEmpty(objectId)) {\r
- objectId = org.eclipse.jgit.lib.Constants.HEAD;\r
- }\r
- if (!StringUtils.isEmpty(l)) {\r
- try {\r
- length = Integer.parseInt(l);\r
- } catch (NumberFormatException x) {\r
- }\r
- }\r
- int offset = 0;\r
- if (!StringUtils.isEmpty(page)) {\r
- try {\r
- offset = length * Integer.parseInt(page);\r
- } catch (NumberFormatException x) {\r
- }\r
- }\r
-\r
- response.setContentType("application/rss+xml; charset=UTF-8");\r
-\r
- boolean isProjectFeed = false;\r
- String feedName = null;\r
- String feedTitle = null;\r
- String feedDescription = null;\r
-\r
- List<String> repositories = null;\r
- if (repositoryName.indexOf('/') == -1 && !repositoryName.toLowerCase().endsWith(".git")) {\r
- // try to find a project\r
- UserModel user = null;\r
- if (request instanceof AuthenticatedRequest) {\r
- user = ((AuthenticatedRequest) request).getUser();\r
- }\r
- ProjectModel project = projectManager.getProjectModel(repositoryName, user);\r
- if (project != null) {\r
- isProjectFeed = true;\r
- repositories = new ArrayList<String>(project.repositories);\r
-\r
- // project feed\r
- feedName = project.name;\r
- feedTitle = project.title;\r
- feedDescription = project.description;\r
- }\r
- }\r
-\r
- if (repositories == null) {\r
- // could not find project, assume this is a repository\r
- repositories = Arrays.asList(repositoryName);\r
- }\r
-\r
-\r
- boolean mountParameters = settings.getBoolean(Keys.web.mountParameters, true);\r
- String urlPattern;\r
- if (mountParameters) {\r
- // mounted parameters\r
- urlPattern = "{0}/commit/{1}/{2}";\r
- } else {\r
- // parameterized parameters\r
- urlPattern = "{0}/commit/?r={1}&h={2}";\r
- }\r
- String gitblitUrl = HttpUtils.getGitblitURL(request);\r
- char fsc = settings.getChar(Keys.web.forwardSlashCharacter, '/');\r
-\r
- List<FeedEntryModel> entries = new ArrayList<FeedEntryModel>();\r
-\r
- for (String name : repositories) {\r
- Repository repository = repositoryManager.getRepository(name);\r
- RepositoryModel model = repositoryManager.getRepositoryModel(name);\r
-\r
- if (repository == null) {\r
- if (model.isCollectingGarbage) {\r
- logger.warn(MessageFormat.format("Temporarily excluding {0} from feed, busy collecting garbage", name));\r
- }\r
- continue;\r
- }\r
- if (!isProjectFeed) {\r
- // single-repository feed\r
- feedName = model.name;\r
- feedTitle = model.name;\r
- feedDescription = model.description;\r
- }\r
-\r
- List<RevCommit> commits;\r
- if (StringUtils.isEmpty(searchString)) {\r
- // standard log/history lookup\r
- commits = JGitUtils.getRevLog(repository, objectId, offset, length);\r
- } else {\r
- // repository search\r
- commits = JGitUtils.searchRevlogs(repository, objectId, searchString, searchType,\r
- offset, length);\r
- }\r
- Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository, model.showRemoteBranches);\r
- MessageProcessor processor = new MessageProcessor(settings);\r
-\r
- // convert RevCommit to SyndicatedEntryModel\r
- for (RevCommit commit : commits) {\r
- FeedEntryModel entry = new FeedEntryModel();\r
- entry.title = commit.getShortMessage();\r
- entry.author = commit.getAuthorIdent().getName();\r
- entry.link = MessageFormat.format(urlPattern, gitblitUrl,\r
- StringUtils.encodeURL(model.name.replace('/', fsc)), commit.getName());\r
- entry.published = commit.getCommitterIdent().getWhen();\r
- entry.contentType = "text/html";\r
- String message = processor.processCommitMessage(model, commit.getFullMessage());\r
- entry.content = message;\r
- entry.repository = model.name;\r
- entry.branch = objectId;\r
- entry.tags = new ArrayList<String>();\r
-\r
- // add commit id and parent commit ids\r
- entry.tags.add("commit:" + commit.getName());\r
- for (RevCommit parent : commit.getParents()) {\r
- entry.tags.add("parent:" + parent.getName());\r
- }\r
-\r
- // add refs to tabs list\r
- List<RefModel> refs = allRefs.get(commit.getId());\r
- if (refs != null && refs.size() > 0) {\r
- for (RefModel ref : refs) {\r
- entry.tags.add("ref:" + ref.getName());\r
- }\r
- }\r
- entries.add(entry);\r
- }\r
- }\r
-\r
- // sort & truncate the feed\r
- Collections.sort(entries);\r
- if (entries.size() > length) {\r
- // clip the list\r
- entries = entries.subList(0, length);\r
- }\r
-\r
- String feedLink;\r
- if (isProjectFeed) {\r
- // project feed\r
- if (mountParameters) {\r
- // mounted url\r
- feedLink = MessageFormat.format("{0}/project/{1}", gitblitUrl,\r
- StringUtils.encodeURL(feedName));\r
- } else {\r
- // parameterized url\r
- feedLink = MessageFormat.format("{0}/project/?p={1}", gitblitUrl,\r
- StringUtils.encodeURL(feedName));\r
- }\r
- } else {\r
- // repository feed\r
- if (mountParameters) {\r
- // mounted url\r
- feedLink = MessageFormat.format("{0}/summary/{1}", gitblitUrl,\r
- StringUtils.encodeURL(feedName));\r
- } else {\r
- // parameterized url\r
- feedLink = MessageFormat.format("{0}/summary/?r={1}", gitblitUrl,\r
- StringUtils.encodeURL(feedName));\r
- }\r
- }\r
-\r
- try {\r
- SyndicationUtils.toRSS(gitblitUrl, feedLink, getTitle(feedTitle, objectId),\r
- feedDescription, entries, response.getOutputStream());\r
- } catch (Exception e) {\r
- logger.error("An error occurred during feed generation", e);\r
- }\r
- }\r
-\r
- @Override\r
- protected void doPost(javax.servlet.http.HttpServletRequest request,\r
- javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,\r
- java.io.IOException {\r
- processRequest(request, response);\r
- }\r
-\r
- @Override\r
- protected void doGet(javax.servlet.http.HttpServletRequest request,\r
- javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,\r
- java.io.IOException {\r
- processRequest(request, response);\r
- }\r
-}\r
import com.gitblit.IStoredSettings;\r
import com.gitblit.IUserService;\r
import com.gitblit.Keys;\r
-import com.gitblit.MailExecutor;\r
import com.gitblit.client.HeaderPanel;\r
import com.gitblit.client.Translation;\r
import com.gitblit.models.UserModel;\r
+import com.gitblit.service.MailService;\r
import com.gitblit.utils.ArrayUtils;\r
import com.gitblit.utils.FileUtils;\r
import com.gitblit.utils.StringUtils;\r
\r
private TableRowSorter<UserCertificateTableModel> defaultSorter;\r
\r
- private MailExecutor mail;\r
+ private MailService mail;\r
\r
private JButton certificateDefaultsButton;\r
\r
return null;\r
}\r
gitblitSettings = new FileSettings(file.getAbsolutePath());\r
- mail = new MailExecutor(gitblitSettings);\r
+ mail = new MailService(gitblitSettings);\r
String us = gitblitSettings.getString(Keys.realm.userService, "${baseFolder}/users.conf");\r
String ext = us.substring(us.lastIndexOf(".") + 1).toLowerCase();\r
IUserService service = null;\r
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
-import com.gitblit.InjectionContextListener;
+import com.gitblit.servlet.InjectionContextListener;
import dagger.ObjectGraph;
import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
-import com.gitblit.MailExecutor;
+import com.gitblit.service.MailService;
/**
* The notification manager dispatches notifications. Currently, email is the
private final IStoredSettings settings;
- private final MailExecutor mailExecutor;
+ private final MailService mailExecutor;
public NotificationManager(IStoredSettings settings) {
this.settings = settings;
- this.mailExecutor = new MailExecutor(settings);
+ this.mailExecutor = new MailService(settings);
}
@Override
import com.gitblit.Constants.FederationStrategy;
import com.gitblit.Constants.PermissionType;
import com.gitblit.Constants.RegistrantType;
-import com.gitblit.GCExecutor;
import com.gitblit.GitBlitException;
import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
-import com.gitblit.LuceneExecutor;
-import com.gitblit.MirrorExecutor;
import com.gitblit.models.ForkModel;
import com.gitblit.models.Metric;
import com.gitblit.models.RefModel;
import com.gitblit.models.SearchResult;
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
+import com.gitblit.service.GarbageCollectorService;
+import com.gitblit.service.LuceneService;
+import com.gitblit.service.MirrorService;
import com.gitblit.utils.ArrayUtils;
import com.gitblit.utils.ByteFormat;
import com.gitblit.utils.CommitCache;
private final File repositoriesFolder;
- private LuceneExecutor luceneExecutor;
+ private LuceneService luceneExecutor;
- private GCExecutor gcExecutor;
+ private GarbageCollectorService gcExecutor;
- private MirrorExecutor mirrorExecutor;
+ private MirrorService mirrorExecutor;
public RepositoryManager(
IRuntimeManager runtimeManager,
}
protected void configureLuceneIndexing() {
- luceneExecutor = new LuceneExecutor(settings, this);
+ luceneExecutor = new LuceneService(settings, this);
int period = 2;
scheduledExecutor.scheduleAtFixedRate(luceneExecutor, 1, period, TimeUnit.MINUTES);
logger.info("Lucene will process indexed branches every {} minutes.", period);
protected void configureGarbageCollector() {
// schedule gc engine
- gcExecutor = new GCExecutor(settings, this);
+ gcExecutor = new GarbageCollectorService(settings, this);
if (gcExecutor.isReady()) {
logger.info("Garbage Collector (GC) will scan repositories every 24 hours.");
Calendar c = Calendar.getInstance();
}
protected void configureMirrorExecutor() {
- mirrorExecutor = new MirrorExecutor(settings, this);
+ mirrorExecutor = new MirrorService(settings, this);
if (mirrorExecutor.isReady()) {
int mins = TimeUtils.convertFrequencyToMinutes(settings.getString(Keys.git.mirrorPeriod, "30 mins"));
if (mins < 5) {
import org.slf4j.LoggerFactory;
import com.gitblit.Constants.FederationToken;
-import com.gitblit.FederationPullExecutor;
import com.gitblit.Gitblit;
import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
import com.gitblit.fanout.FanoutSocketService;
import com.gitblit.git.GitDaemon;
import com.gitblit.models.FederationModel;
+import com.gitblit.service.FederationPullService;
import com.gitblit.utils.StringUtils;
import com.gitblit.utils.TimeUtils;
}
}
- private class FederationPuller extends FederationPullExecutor {
+ private class FederationPuller extends FederationPullService {
public FederationPuller(FederationModel registration) {
super(Arrays.asList(registration));
--- /dev/null
+package com.gitblit.service;
+
+import static org.eclipse.jgit.lib.Constants.DOT_GIT_EXT;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.gitblit.ConfigUserService;
+import com.gitblit.Constants;
+import com.gitblit.Constants.AccessPermission;
+import com.gitblit.Constants.FederationPullStatus;
+import com.gitblit.Constants.FederationStrategy;
+import com.gitblit.GitBlitException.ForbiddenException;
+import com.gitblit.Gitblit;
+import com.gitblit.IUserService;
+import com.gitblit.Keys;
+import com.gitblit.models.FederationModel;
+import com.gitblit.models.RefModel;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.TeamModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.ArrayUtils;
+import com.gitblit.utils.FederationUtils;
+import com.gitblit.utils.FileUtils;
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.utils.JGitUtils.CloneResult;
+import com.gitblit.utils.StringUtils;
+
+public abstract class FederationPullService implements Runnable {
+
+ Logger logger = LoggerFactory.getLogger(getClass());
+
+ Gitblit gitblit;
+
+ private final List<FederationModel> registrations;
+
+ /**
+ * Constructor for specifying a single federation registration. This
+ * constructor is used to schedule the next pull execution.
+ *
+ * @param provider
+ * @param registration
+ */
+ public FederationPullService(FederationModel registration) {
+ this(Arrays.asList(registration));
+ }
+
+ /**
+ * Constructor to specify a group of federation registrations. This is
+ * normally used at startup to pull and then schedule the next update based
+ * on each registrations frequency setting.
+ *
+ * @param provider
+ * @param registrations
+ * @param isDaemon
+ * if true, registrations are rescheduled in perpetuity. if
+ * false, the federation pull operation is executed once.
+ */
+ public FederationPullService(List<FederationModel> registrations) {
+ this.registrations = registrations;
+ }
+
+ public abstract void reschedule(FederationModel registration);
+
+ /**
+ * Run method for this pull service.
+ */
+ @Override
+ public void run() {
+ for (FederationModel registration : registrations) {
+ FederationPullStatus was = registration.getLowestStatus();
+ try {
+ Date now = new Date(System.currentTimeMillis());
+ pull(registration);
+ sendStatusAcknowledgment(registration);
+ registration.lastPull = now;
+ FederationPullStatus is = registration.getLowestStatus();
+ if (is.ordinal() < was.ordinal()) {
+ // the status for this registration has downgraded
+ logger.warn("Federation pull status of {0} is now {1}", registration.name,
+ is.name());
+ if (registration.notifyOnError) {
+ String message = "Federation pull of " + registration.name + " @ "
+ + registration.url + " is now at " + is.name();
+ gitblit.sendMailToAdministrators(
+ "Pull Status of " + registration.name + " is " + is.name(),
+ message);
+ }
+ }
+ } catch (Throwable t) {
+ logger.error(MessageFormat.format(
+ "Failed to pull from federated gitblit ({0} @ {1})", registration.name,
+ registration.url), t);
+ } finally {
+ reschedule(registration);
+ }
+ }
+ }
+
+ /**
+ * Mirrors a repository and, optionally, the server's users, and/or
+ * configuration settings from a origin Gitblit instance.
+ *
+ * @param registration
+ * @throws Exception
+ */
+ private void pull(FederationModel registration) throws Exception {
+ Map<String, RepositoryModel> repositories = FederationUtils.getRepositories(registration,
+ true);
+ String registrationFolder = registration.folder.toLowerCase().trim();
+ // confirm valid characters in server alias
+ Character c = StringUtils.findInvalidCharacter(registrationFolder);
+ if (c != null) {
+ logger.error(MessageFormat
+ .format("Illegal character ''{0}'' in folder name ''{1}'' of federation registration {2}!",
+ c, registrationFolder, registration.name));
+ return;
+ }
+ File repositoriesFolder = gitblit.getRepositoriesFolder();
+ File registrationFolderFile = new File(repositoriesFolder, registrationFolder);
+ registrationFolderFile.mkdirs();
+
+ // Clone/Pull the repository
+ for (Map.Entry<String, RepositoryModel> entry : repositories.entrySet()) {
+ String cloneUrl = entry.getKey();
+ RepositoryModel repository = entry.getValue();
+ if (!repository.hasCommits) {
+ logger.warn(MessageFormat.format(
+ "Skipping federated repository {0} from {1} @ {2}. Repository is EMPTY.",
+ repository.name, registration.name, registration.url));
+ registration.updateStatus(repository, FederationPullStatus.SKIPPED);
+ continue;
+ }
+
+ // Determine local repository name
+ String repositoryName;
+ if (StringUtils.isEmpty(registrationFolder)) {
+ repositoryName = repository.name;
+ } else {
+ repositoryName = registrationFolder + "/" + repository.name;
+ }
+
+ if (registration.bare) {
+ // bare repository, ensure .git suffix
+ if (!repositoryName.toLowerCase().endsWith(DOT_GIT_EXT)) {
+ repositoryName += DOT_GIT_EXT;
+ }
+ } else {
+ // normal repository, strip .git suffix
+ if (repositoryName.toLowerCase().endsWith(DOT_GIT_EXT)) {
+ repositoryName = repositoryName.substring(0,
+ repositoryName.indexOf(DOT_GIT_EXT));
+ }
+ }
+
+ // confirm that the origin of any pre-existing repository matches
+ // the clone url
+ String fetchHead = null;
+ Repository existingRepository = gitblit.getRepository(repositoryName);
+
+ if (existingRepository == null && gitblit.isCollectingGarbage(repositoryName)) {
+ logger.warn(MessageFormat.format("Skipping local repository {0}, busy collecting garbage", repositoryName));
+ continue;
+ }
+
+ if (existingRepository != null) {
+ StoredConfig config = existingRepository.getConfig();
+ config.load();
+ String origin = config.getString("remote", "origin", "url");
+ RevCommit commit = JGitUtils.getCommit(existingRepository,
+ org.eclipse.jgit.lib.Constants.FETCH_HEAD);
+ if (commit != null) {
+ fetchHead = commit.getName();
+ }
+ existingRepository.close();
+ if (!origin.startsWith(registration.url)) {
+ logger.warn(MessageFormat
+ .format("Skipping federated repository {0} from {1} @ {2}. Origin does not match, consider EXCLUDING.",
+ repository.name, registration.name, registration.url));
+ registration.updateStatus(repository, FederationPullStatus.SKIPPED);
+ continue;
+ }
+ }
+
+ // clone/pull this repository
+ CredentialsProvider credentials = new UsernamePasswordCredentialsProvider(
+ Constants.FEDERATION_USER, registration.token);
+ logger.info(MessageFormat.format("Pulling federated repository {0} from {1} @ {2}",
+ repository.name, registration.name, registration.url));
+
+ CloneResult result = JGitUtils.cloneRepository(registrationFolderFile, repository.name,
+ cloneUrl, registration.bare, credentials);
+ Repository r = gitblit.getRepository(repositoryName);
+ RepositoryModel rm = gitblit.getRepositoryModel(repositoryName);
+ repository.isFrozen = registration.mirror;
+ if (result.createdRepository) {
+ // default local settings
+ repository.federationStrategy = FederationStrategy.EXCLUDE;
+ repository.isFrozen = registration.mirror;
+ repository.showRemoteBranches = !registration.mirror;
+ logger.info(MessageFormat.format(" cloning {0}", repository.name));
+ registration.updateStatus(repository, FederationPullStatus.MIRRORED);
+ } else {
+ // fetch and update
+ boolean fetched = false;
+ RevCommit commit = JGitUtils.getCommit(r, org.eclipse.jgit.lib.Constants.FETCH_HEAD);
+ String newFetchHead = commit.getName();
+ fetched = fetchHead == null || !fetchHead.equals(newFetchHead);
+
+ if (registration.mirror) {
+ // mirror
+ if (fetched) {
+ // update local branches to match the remote tracking branches
+ for (RefModel ref : JGitUtils.getRemoteBranches(r, false, -1)) {
+ if (ref.displayName.startsWith("origin/")) {
+ String branch = org.eclipse.jgit.lib.Constants.R_HEADS
+ + ref.displayName.substring(ref.displayName.indexOf('/') + 1);
+ String hash = ref.getReferencedObjectId().getName();
+
+ JGitUtils.setBranchRef(r, branch, hash);
+ logger.info(MessageFormat.format(" resetting {0} of {1} to {2}", branch,
+ repository.name, hash));
+ }
+ }
+
+ String newHead;
+ if (StringUtils.isEmpty(repository.HEAD)) {
+ newHead = newFetchHead;
+ } else {
+ newHead = repository.HEAD;
+ }
+ JGitUtils.setHEADtoRef(r, newHead);
+ logger.info(MessageFormat.format(" resetting HEAD of {0} to {1}",
+ repository.name, newHead));
+ registration.updateStatus(repository, FederationPullStatus.MIRRORED);
+ } else {
+ // indicate no commits pulled
+ registration.updateStatus(repository, FederationPullStatus.NOCHANGE);
+ }
+ } else {
+ // non-mirror
+ if (fetched) {
+ // indicate commits pulled to origin/master
+ registration.updateStatus(repository, FederationPullStatus.PULLED);
+ } else {
+ // indicate no commits pulled
+ registration.updateStatus(repository, FederationPullStatus.NOCHANGE);
+ }
+ }
+
+ // preserve local settings
+ repository.isFrozen = rm.isFrozen;
+ repository.federationStrategy = rm.federationStrategy;
+
+ // merge federation sets
+ Set<String> federationSets = new HashSet<String>();
+ if (rm.federationSets != null) {
+ federationSets.addAll(rm.federationSets);
+ }
+ if (repository.federationSets != null) {
+ federationSets.addAll(repository.federationSets);
+ }
+ repository.federationSets = new ArrayList<String>(federationSets);
+
+ // merge indexed branches
+ Set<String> indexedBranches = new HashSet<String>();
+ if (rm.indexedBranches != null) {
+ indexedBranches.addAll(rm.indexedBranches);
+ }
+ if (repository.indexedBranches != null) {
+ indexedBranches.addAll(repository.indexedBranches);
+ }
+ repository.indexedBranches = new ArrayList<String>(indexedBranches);
+
+ }
+ // only repositories that are actually _cloned_ from the origin
+ // Gitblit repository are marked as federated. If the origin
+ // is from somewhere else, these repositories are not considered
+ // "federated" repositories.
+ repository.isFederated = cloneUrl.startsWith(registration.url);
+
+ gitblit.updateConfiguration(r, repository);
+ r.close();
+ }
+
+ IUserService userService = null;
+
+ try {
+ // Pull USERS
+ // TeamModels are automatically pulled because they are contained
+ // within the UserModel. The UserService creates unknown teams
+ // and updates existing teams.
+ Collection<UserModel> users = FederationUtils.getUsers(registration);
+ if (users != null && users.size() > 0) {
+ File realmFile = new File(registrationFolderFile, registration.name + "_users.conf");
+ realmFile.delete();
+ userService = new ConfigUserService(realmFile);
+ for (UserModel user : users) {
+ userService.updateUserModel(user.username, user);
+
+ // merge the origin permissions and origin accounts into
+ // the user accounts of this Gitblit instance
+ if (registration.mergeAccounts) {
+ // reparent all repository permissions if the local
+ // repositories are stored within subfolders
+ if (!StringUtils.isEmpty(registrationFolder)) {
+ if (user.permissions != null) {
+ // pulling from >= 1.2 version
+ Map<String, AccessPermission> copy = new HashMap<String, AccessPermission>(user.permissions);
+ user.permissions.clear();
+ for (Map.Entry<String, AccessPermission> entry : copy.entrySet()) {
+ user.setRepositoryPermission(registrationFolder + "/" + entry.getKey(), entry.getValue());
+ }
+ } else {
+ // pulling from <= 1.1 version
+ List<String> permissions = new ArrayList<String>(user.repositories);
+ user.repositories.clear();
+ for (String permission : permissions) {
+ user.addRepositoryPermission(registrationFolder + "/" + permission);
+ }
+ }
+ }
+
+ // insert new user or update local user
+ UserModel localUser = gitblit.getUserModel(user.username);
+ if (localUser == null) {
+ // create new local user
+ gitblit.updateUserModel(user.username, user, true);
+ } else {
+ // update repository permissions of local user
+ if (user.permissions != null) {
+ // pulling from >= 1.2 version
+ Map<String, AccessPermission> copy = new HashMap<String, AccessPermission>(user.permissions);
+ for (Map.Entry<String, AccessPermission> entry : copy.entrySet()) {
+ localUser.setRepositoryPermission(entry.getKey(), entry.getValue());
+ }
+ } else {
+ // pulling from <= 1.1 version
+ for (String repository : user.repositories) {
+ localUser.addRepositoryPermission(repository);
+ }
+ }
+ localUser.password = user.password;
+ localUser.canAdmin = user.canAdmin;
+ gitblit.updateUserModel(localUser.username, localUser, false);
+ }
+
+ for (String teamname : gitblit.getAllTeamNames()) {
+ TeamModel team = gitblit.getTeamModel(teamname);
+ if (user.isTeamMember(teamname) && !team.hasUser(user.username)) {
+ // new team member
+ team.addUser(user.username);
+ gitblit.updateTeamModel(teamname, team);
+ } else if (!user.isTeamMember(teamname) && team.hasUser(user.username)) {
+ // remove team member
+ team.removeUser(user.username);
+ gitblit.updateTeamModel(teamname, team);
+ }
+
+ // update team repositories
+ TeamModel remoteTeam = user.getTeam(teamname);
+ if (remoteTeam != null) {
+ if (remoteTeam.permissions != null) {
+ // pulling from >= 1.2
+ for (Map.Entry<String, AccessPermission> entry : remoteTeam.permissions.entrySet()){
+ team.setRepositoryPermission(entry.getKey(), entry.getValue());
+ }
+ gitblit.updateTeamModel(teamname, team);
+ } else if (!ArrayUtils.isEmpty(remoteTeam.repositories)) {
+ // pulling from <= 1.1
+ team.addRepositoryPermissions(remoteTeam.repositories);
+ gitblit.updateTeamModel(teamname, team);
+ }
+ }
+ }
+ }
+ }
+ }
+ } catch (ForbiddenException e) {
+ // ignore forbidden exceptions
+ } catch (IOException e) {
+ logger.warn(MessageFormat.format(
+ "Failed to retrieve USERS from federated gitblit ({0} @ {1})",
+ registration.name, registration.url), e);
+ }
+
+ try {
+ // Pull TEAMS
+ // We explicitly pull these even though they are embedded in
+ // UserModels because it is possible to use teams to specify
+ // mailing lists or push scripts without specifying users.
+ if (userService != null) {
+ Collection<TeamModel> teams = FederationUtils.getTeams(registration);
+ if (teams != null && teams.size() > 0) {
+ for (TeamModel team : teams) {
+ userService.updateTeamModel(team);
+ }
+ }
+ }
+ } catch (ForbiddenException e) {
+ // ignore forbidden exceptions
+ } catch (IOException e) {
+ logger.warn(MessageFormat.format(
+ "Failed to retrieve TEAMS from federated gitblit ({0} @ {1})",
+ registration.name, registration.url), e);
+ }
+
+ try {
+ // Pull SETTINGS
+ Map<String, String> settings = FederationUtils.getSettings(registration);
+ if (settings != null && settings.size() > 0) {
+ Properties properties = new Properties();
+ properties.putAll(settings);
+ FileOutputStream os = new FileOutputStream(new File(registrationFolderFile,
+ registration.name + "_" + Constants.PROPERTIES_FILE));
+ properties.store(os, null);
+ os.close();
+ }
+ } catch (ForbiddenException e) {
+ // ignore forbidden exceptions
+ } catch (IOException e) {
+ logger.warn(MessageFormat.format(
+ "Failed to retrieve SETTINGS from federated gitblit ({0} @ {1})",
+ registration.name, registration.url), e);
+ }
+
+ try {
+ // Pull SCRIPTS
+ Map<String, String> scripts = FederationUtils.getScripts(registration);
+ if (scripts != null && scripts.size() > 0) {
+ for (Map.Entry<String, String> script : scripts.entrySet()) {
+ String scriptName = script.getKey();
+ if (scriptName.endsWith(".groovy")) {
+ scriptName = scriptName.substring(0, scriptName.indexOf(".groovy"));
+ }
+ File file = new File(registrationFolderFile, registration.name + "_"
+ + scriptName + ".groovy");
+ FileUtils.writeContent(file, script.getValue());
+ }
+ }
+ } catch (ForbiddenException e) {
+ // ignore forbidden exceptions
+ } catch (IOException e) {
+ logger.warn(MessageFormat.format(
+ "Failed to retrieve SCRIPTS from federated gitblit ({0} @ {1})",
+ registration.name, registration.url), e);
+ }
+ }
+
+ /**
+ * Sends a status acknowledgment to the origin Gitblit instance. This
+ * includes the results of the federated pull.
+ *
+ * @param registration
+ * @throws Exception
+ */
+ private void sendStatusAcknowledgment(FederationModel registration) throws Exception {
+ if (!registration.sendStatus) {
+ // skip status acknowledgment
+ return;
+ }
+ InetAddress addr = InetAddress.getLocalHost();
+ String federationName = gitblit.getSettings().getString(Keys.federation.name, null);
+ if (StringUtils.isEmpty(federationName)) {
+ federationName = addr.getHostName();
+ }
+ FederationUtils.acknowledgeStatus(addr.getHostAddress(), registration);
+ logger.info(MessageFormat.format("Pull status sent to {0}", registration.url));
+ }
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * Copyright 2012 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.service;\r
+\r
+import java.lang.reflect.Field;\r
+import java.text.MessageFormat;\r
+import java.util.Calendar;\r
+import java.util.Date;\r
+import java.util.Map;\r
+import java.util.Properties;\r
+import java.util.concurrent.ConcurrentHashMap;\r
+import java.util.concurrent.atomic.AtomicBoolean;\r
+import java.util.concurrent.atomic.AtomicInteger;\r
+\r
+import org.eclipse.jgit.api.GarbageCollectCommand;\r
+import org.eclipse.jgit.api.Git;\r
+import org.eclipse.jgit.lib.Repository;\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+import com.gitblit.IStoredSettings;\r
+import com.gitblit.Keys;\r
+import com.gitblit.Keys.git;\r
+import com.gitblit.manager.IRepositoryManager;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.utils.FileUtils;\r
+\r
+/**\r
+ * The Garbage Collector Service handles periodic garbage collection in repositories.\r
+ *\r
+ * @author James Moger\r
+ *\r
+ */\r
+public class GarbageCollectorService implements Runnable {\r
+\r
+ public static enum GCStatus {\r
+ READY, COLLECTING;\r
+\r
+ public boolean exceeds(GCStatus s) {\r
+ return ordinal() > s.ordinal();\r
+ }\r
+ }\r
+ private final Logger logger = LoggerFactory.getLogger(GarbageCollectorService.class);\r
+\r
+ private final IStoredSettings settings;\r
+\r
+ private final IRepositoryManager repositoryManager;\r
+\r
+ private AtomicBoolean running = new AtomicBoolean(false);\r
+\r
+ private AtomicBoolean forceClose = new AtomicBoolean(false);\r
+\r
+ private final Map<String, GCStatus> gcCache = new ConcurrentHashMap<String, GCStatus>();\r
+\r
+ public GarbageCollectorService(\r
+ IStoredSettings settings,\r
+ IRepositoryManager repositoryManager) {\r
+\r
+ this.settings = settings;\r
+ this.repositoryManager = repositoryManager;\r
+ }\r
+\r
+ /**\r
+ * Indicates if the GC executor is ready to process repositories.\r
+ *\r
+ * @return true if the GC executor is ready to process repositories\r
+ */\r
+ public boolean isReady() {\r
+ return settings.getBoolean(Keys.git.enableGarbageCollection, false);\r
+ }\r
+\r
+ public boolean isRunning() {\r
+ return running.get();\r
+ }\r
+\r
+ public boolean lock(String repositoryName) {\r
+ return setGCStatus(repositoryName, GCStatus.COLLECTING);\r
+ }\r
+\r
+ /**\r
+ * Tries to set a GCStatus for the specified repository.\r
+ *\r
+ * @param repositoryName\r
+ * @return true if the status has been set\r
+ */\r
+ private boolean setGCStatus(String repositoryName, GCStatus status) {\r
+ String key = repositoryName.toLowerCase();\r
+ if (gcCache.containsKey(key)) {\r
+ if (gcCache.get(key).exceeds(GCStatus.READY)) {\r
+ // already collecting or blocked\r
+ return false;\r
+ }\r
+ }\r
+ gcCache.put(key, status);\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Returns true if Gitblit is actively collecting garbage in this repository.\r
+ *\r
+ * @param repositoryName\r
+ * @return true if actively collecting garbage\r
+ */\r
+ public boolean isCollectingGarbage(String repositoryName) {\r
+ String key = repositoryName.toLowerCase();\r
+ return gcCache.containsKey(key) && GCStatus.COLLECTING.equals(gcCache.get(key));\r
+ }\r
+\r
+ /**\r
+ * Resets the GC status to ready.\r
+ *\r
+ * @param repositoryName\r
+ */\r
+ public void releaseLock(String repositoryName) {\r
+ gcCache.put(repositoryName.toLowerCase(), GCStatus.READY);\r
+ }\r
+\r
+ public void close() {\r
+ forceClose.set(true);\r
+ }\r
+\r
+ @Override\r
+ public void run() {\r
+ if (!isReady()) {\r
+ return;\r
+ }\r
+\r
+ running.set(true);\r
+ Date now = new Date();\r
+\r
+ for (String repositoryName : repositoryManager.getRepositoryList()) {\r
+ if (forceClose.get()) {\r
+ break;\r
+ }\r
+ if (isCollectingGarbage(repositoryName)) {\r
+ logger.warn(MessageFormat.format("Already collecting garbage from {0}?!?", repositoryName));\r
+ continue;\r
+ }\r
+ boolean garbageCollected = false;\r
+ RepositoryModel model = null;\r
+ Repository repository = null;\r
+ try {\r
+ model = repositoryManager.getRepositoryModel(repositoryName);\r
+ repository = repositoryManager.getRepository(repositoryName);\r
+ if (repository == null) {\r
+ logger.warn(MessageFormat.format("GCExecutor is missing repository {0}?!?", repositoryName));\r
+ continue;\r
+ }\r
+\r
+ if (!isRepositoryIdle(repository)) {\r
+ logger.debug(MessageFormat.format("GCExecutor is skipping {0} because it is not idle", repositoryName));\r
+ continue;\r
+ }\r
+\r
+ // By setting the GCStatus to COLLECTING we are\r
+ // disabling *all* access to this repository from Gitblit.\r
+ // Think of this as a clutch in a manual transmission vehicle.\r
+ if (!setGCStatus(repositoryName, GCStatus.COLLECTING)) {\r
+ logger.warn(MessageFormat.format("Can not acquire GC lock for {0}, skipping", repositoryName));\r
+ continue;\r
+ }\r
+\r
+ logger.debug(MessageFormat.format("GCExecutor locked idle repository {0}", repositoryName));\r
+\r
+ Git git = new Git(repository);\r
+ GarbageCollectCommand gc = git.gc();\r
+ Properties stats = gc.getStatistics();\r
+\r
+ // determine if this is a scheduled GC\r
+ Calendar cal = Calendar.getInstance();\r
+ cal.setTime(model.lastGC);\r
+ cal.set(Calendar.HOUR_OF_DAY, 0);\r
+ cal.set(Calendar.MINUTE, 0);\r
+ cal.set(Calendar.SECOND, 0);\r
+ cal.set(Calendar.MILLISECOND, 0);\r
+ cal.add(Calendar.DATE, model.gcPeriod);\r
+ Date gcDate = cal.getTime();\r
+ boolean shouldCollectGarbage = now.after(gcDate);\r
+\r
+ // determine if filesize triggered GC\r
+ long gcThreshold = FileUtils.convertSizeToLong(model.gcThreshold, 500*1024L);\r
+ long sizeOfLooseObjects = (Long) stats.get("sizeOfLooseObjects");\r
+ boolean hasEnoughGarbage = sizeOfLooseObjects >= gcThreshold;\r
+\r
+ // if we satisfy one of the requirements, GC\r
+ boolean hasGarbage = sizeOfLooseObjects > 0;\r
+ if (hasGarbage && (hasEnoughGarbage || shouldCollectGarbage)) {\r
+ long looseKB = sizeOfLooseObjects/1024L;\r
+ logger.info(MessageFormat.format("Collecting {1} KB of loose objects from {0}", repositoryName, looseKB));\r
+\r
+ // do the deed\r
+ gc.call();\r
+\r
+ garbageCollected = true;\r
+ }\r
+ } catch (Exception e) {\r
+ logger.error("Error collecting garbage in " + repositoryName, e);\r
+ } finally {\r
+ // cleanup\r
+ if (repository != null) {\r
+ if (garbageCollected) {\r
+ // update the last GC date\r
+ model.lastGC = new Date();\r
+ repositoryManager.updateConfiguration(repository, model);\r
+ }\r
+\r
+ repository.close();\r
+ }\r
+\r
+ // reset the GC lock\r
+ releaseLock(repositoryName);\r
+ logger.debug(MessageFormat.format("GCExecutor released GC lock for {0}", repositoryName));\r
+ }\r
+ }\r
+\r
+ running.set(false);\r
+ }\r
+\r
+ private boolean isRepositoryIdle(Repository repository) {\r
+ try {\r
+ // Read the use count.\r
+ // An idle use count is 2:\r
+ // +1 for being in the cache\r
+ // +1 for the repository parameter in this method\r
+ Field useCnt = Repository.class.getDeclaredField("useCnt");\r
+ useCnt.setAccessible(true);\r
+ int useCount = ((AtomicInteger) useCnt.get(repository)).get();\r
+ return useCount == 2;\r
+ } catch (Exception e) {\r
+ logger.warn(MessageFormat\r
+ .format("Failed to reflectively determine use count for repository {0}",\r
+ repository.getDirectory().getPath()), e);\r
+ }\r
+ return false;\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2012 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.service;\r
+\r
+import static org.eclipse.jgit.treewalk.filter.TreeFilter.ANY_DIFF;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.lang.reflect.Method;\r
+import java.text.MessageFormat;\r
+import java.text.ParseException;\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.Comparator;\r
+import java.util.HashMap;\r
+import java.util.LinkedHashSet;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Set;\r
+import java.util.TreeMap;\r
+import java.util.TreeSet;\r
+import java.util.concurrent.ConcurrentHashMap;\r
+\r
+import org.apache.lucene.analysis.Analyzer;\r
+import org.apache.lucene.analysis.standard.StandardAnalyzer;\r
+import org.apache.lucene.document.DateTools;\r
+import org.apache.lucene.document.DateTools.Resolution;\r
+import org.apache.lucene.document.Document;\r
+import org.apache.lucene.document.Field;\r
+import org.apache.lucene.document.Field.Index;\r
+import org.apache.lucene.document.Field.Store;\r
+import org.apache.lucene.index.IndexReader;\r
+import org.apache.lucene.index.IndexWriter;\r
+import org.apache.lucene.index.IndexWriterConfig;\r
+import org.apache.lucene.index.IndexWriterConfig.OpenMode;\r
+import org.apache.lucene.index.MultiReader;\r
+import org.apache.lucene.index.Term;\r
+import org.apache.lucene.queryParser.QueryParser;\r
+import org.apache.lucene.search.BooleanClause.Occur;\r
+import org.apache.lucene.search.BooleanQuery;\r
+import org.apache.lucene.search.IndexSearcher;\r
+import org.apache.lucene.search.Query;\r
+import org.apache.lucene.search.ScoreDoc;\r
+import org.apache.lucene.search.TopScoreDocCollector;\r
+import org.apache.lucene.search.highlight.Fragmenter;\r
+import org.apache.lucene.search.highlight.Highlighter;\r
+import org.apache.lucene.search.highlight.InvalidTokenOffsetsException;\r
+import org.apache.lucene.search.highlight.QueryScorer;\r
+import org.apache.lucene.search.highlight.SimpleHTMLFormatter;\r
+import org.apache.lucene.search.highlight.SimpleSpanFragmenter;\r
+import org.apache.lucene.store.Directory;\r
+import org.apache.lucene.store.FSDirectory;\r
+import org.apache.lucene.util.Version;\r
+import org.eclipse.jgit.diff.DiffEntry.ChangeType;\r
+import org.eclipse.jgit.lib.Constants;\r
+import org.eclipse.jgit.lib.FileMode;\r
+import org.eclipse.jgit.lib.ObjectId;\r
+import org.eclipse.jgit.lib.ObjectLoader;\r
+import org.eclipse.jgit.lib.ObjectReader;\r
+import org.eclipse.jgit.lib.Repository;\r
+import org.eclipse.jgit.lib.RepositoryCache.FileKey;\r
+import org.eclipse.jgit.revwalk.RevCommit;\r
+import org.eclipse.jgit.revwalk.RevTree;\r
+import org.eclipse.jgit.revwalk.RevWalk;\r
+import org.eclipse.jgit.storage.file.FileBasedConfig;\r
+import org.eclipse.jgit.treewalk.EmptyTreeIterator;\r
+import org.eclipse.jgit.treewalk.TreeWalk;\r
+import org.eclipse.jgit.util.FS;\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+import com.gitblit.Constants.SearchObjectType;\r
+import com.gitblit.IStoredSettings;\r
+import com.gitblit.Keys;\r
+import com.gitblit.manager.IRepositoryManager;\r
+import com.gitblit.models.PathModel.PathChangeModel;\r
+import com.gitblit.models.RefModel;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.SearchResult;\r
+import com.gitblit.utils.ArrayUtils;\r
+import com.gitblit.utils.JGitUtils;\r
+import com.gitblit.utils.StringUtils;\r
+\r
+/**\r
+ * The Lucene service handles indexing and searching repositories.\r
+ *\r
+ * @author James Moger\r
+ *\r
+ */\r
+public class LuceneService implements Runnable {\r
+\r
+\r
+ private static final int INDEX_VERSION = 5;\r
+\r
+ private static final String FIELD_OBJECT_TYPE = "type";\r
+ private static final String FIELD_PATH = "path";\r
+ private static final String FIELD_COMMIT = "commit";\r
+ private static final String FIELD_BRANCH = "branch";\r
+ private static final String FIELD_SUMMARY = "summary";\r
+ private static final String FIELD_CONTENT = "content";\r
+ private static final String FIELD_AUTHOR = "author";\r
+ private static final String FIELD_COMMITTER = "committer";\r
+ private static final String FIELD_DATE = "date";\r
+ private static final String FIELD_TAG = "tag";\r
+\r
+ private static final String CONF_FILE = "lucene.conf";\r
+ private static final String LUCENE_DIR = "lucene";\r
+ private static final String CONF_INDEX = "index";\r
+ private static final String CONF_VERSION = "version";\r
+ private static final String CONF_ALIAS = "aliases";\r
+ private static final String CONF_BRANCH = "branches";\r
+\r
+ private static final Version LUCENE_VERSION = Version.LUCENE_35;\r
+\r
+ private final Logger logger = LoggerFactory.getLogger(LuceneService.class);\r
+\r
+ private final IStoredSettings storedSettings;\r
+ private final IRepositoryManager repositoryManager;\r
+ private final File repositoriesFolder;\r
+\r
+ private final Map<String, IndexSearcher> searchers = new ConcurrentHashMap<String, IndexSearcher>();\r
+ private final Map<String, IndexWriter> writers = new ConcurrentHashMap<String, IndexWriter>();\r
+\r
+ private final String luceneIgnoreExtensions = "7z arc arj bin bmp dll doc docx exe gif gz jar jpg lib lzh odg odf odt pdf ppt png so swf xcf xls xlsx zip";\r
+ private Set<String> excludedExtensions;\r
+\r
+ public LuceneService(\r
+ IStoredSettings settings,\r
+ IRepositoryManager repositoryManager) {\r
+\r
+ this.storedSettings = settings;\r
+ this.repositoryManager = repositoryManager;\r
+ this.repositoriesFolder = repositoryManager.getRepositoriesFolder();\r
+ String exts = luceneIgnoreExtensions;\r
+ if (settings != null) {\r
+ exts = settings.getString(Keys.web.luceneIgnoreExtensions, exts);\r
+ }\r
+ excludedExtensions = new TreeSet<String>(StringUtils.getStringsFromValue(exts));\r
+ }\r
+\r
+ /**\r
+ * Run is executed by the Gitblit executor service. Because this is called\r
+ * by an executor service, calls will queue - i.e. there can never be\r
+ * concurrent execution of repository index updates.\r
+ */\r
+ @Override\r
+ public void run() {\r
+ if (!storedSettings.getBoolean(Keys.web.allowLuceneIndexing, true)) {\r
+ // Lucene indexing is disabled\r
+ return;\r
+ }\r
+ // reload the excluded extensions\r
+ String exts = storedSettings.getString(Keys.web.luceneIgnoreExtensions, luceneIgnoreExtensions);\r
+ excludedExtensions = new TreeSet<String>(StringUtils.getStringsFromValue(exts));\r
+\r
+ if (repositoryManager.isCollectingGarbage()) {\r
+ // busy collecting garbage, try again later\r
+ return;\r
+ }\r
+\r
+ for (String repositoryName: repositoryManager.getRepositoryList()) {\r
+ RepositoryModel model = repositoryManager.getRepositoryModel(repositoryName);\r
+ if (model.hasCommits && !ArrayUtils.isEmpty(model.indexedBranches)) {\r
+ Repository repository = repositoryManager.getRepository(model.name);\r
+ if (repository == null) {\r
+ if (repositoryManager.isCollectingGarbage(model.name)) {\r
+ logger.info(MessageFormat.format("Skipping Lucene index of {0}, busy garbage collecting", repositoryName));\r
+ }\r
+ continue;\r
+ }\r
+ index(model, repository);\r
+ repository.close();\r
+ System.gc();\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Synchronously indexes a repository. This may build a complete index of a\r
+ * repository or it may update an existing index.\r
+ *\r
+ * @param name\r
+ * the name of the repository\r
+ * @param repository\r
+ * the repository object\r
+ */\r
+ private void index(RepositoryModel model, Repository repository) {\r
+ try {\r
+ if (shouldReindex(repository)) {\r
+ // (re)build the entire index\r
+ IndexResult result = reindex(model, repository);\r
+\r
+ if (result.success) {\r
+ if (result.commitCount > 0) {\r
+ String msg = "Built {0} Lucene index from {1} commits and {2} files across {3} branches in {4} secs";\r
+ logger.info(MessageFormat.format(msg, model.name, result.commitCount,\r
+ result.blobCount, result.branchCount, result.duration()));\r
+ }\r
+ } else {\r
+ String msg = "Could not build {0} Lucene index!";\r
+ logger.error(MessageFormat.format(msg, model.name));\r
+ }\r
+ } else {\r
+ // update the index with latest commits\r
+ IndexResult result = updateIndex(model, repository);\r
+ if (result.success) {\r
+ if (result.commitCount > 0) {\r
+ String msg = "Updated {0} Lucene index with {1} commits and {2} files across {3} branches in {4} secs";\r
+ logger.info(MessageFormat.format(msg, model.name, result.commitCount,\r
+ result.blobCount, result.branchCount, result.duration()));\r
+ }\r
+ } else {\r
+ String msg = "Could not update {0} Lucene index!";\r
+ logger.error(MessageFormat.format(msg, model.name));\r
+ }\r
+ }\r
+ } catch (Throwable t) {\r
+ logger.error(MessageFormat.format("Lucene indexing failure for {0}", model.name), t);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Close the writer/searcher objects for a repository.\r
+ *\r
+ * @param repositoryName\r
+ */\r
+ public synchronized void close(String repositoryName) {\r
+ try {\r
+ IndexSearcher searcher = searchers.remove(repositoryName);\r
+ if (searcher != null) {\r
+ searcher.getIndexReader().close();\r
+ }\r
+ } catch (Exception e) {\r
+ logger.error("Failed to close index searcher for " + repositoryName, e);\r
+ }\r
+\r
+ try {\r
+ IndexWriter writer = writers.remove(repositoryName);\r
+ if (writer != null) {\r
+ writer.close();\r
+ }\r
+ } catch (Exception e) {\r
+ logger.error("Failed to close index writer for " + repositoryName, e);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Close all Lucene indexers.\r
+ *\r
+ */\r
+ public synchronized void close() {\r
+ // close all writers\r
+ for (String writer : writers.keySet()) {\r
+ try {\r
+ writers.get(writer).close(true);\r
+ } catch (Throwable t) {\r
+ logger.error("Failed to close Lucene writer for " + writer, t);\r
+ }\r
+ }\r
+ writers.clear();\r
+\r
+ // close all searchers\r
+ for (String searcher : searchers.keySet()) {\r
+ try {\r
+ searchers.get(searcher).getIndexReader().close();\r
+ } catch (Throwable t) {\r
+ logger.error("Failed to close Lucene searcher for " + searcher, t);\r
+ }\r
+ }\r
+ searchers.clear();\r
+ }\r
+\r
+\r
+ /**\r
+ * Deletes the Lucene index for the specified repository.\r
+ *\r
+ * @param repositoryName\r
+ * @return true, if successful\r
+ */\r
+ public boolean deleteIndex(String repositoryName) {\r
+ try {\r
+ // close any open writer/searcher\r
+ close(repositoryName);\r
+\r
+ // delete the index folder\r
+ File repositoryFolder = FileKey.resolve(new File(repositoriesFolder, repositoryName), FS.DETECTED);\r
+ File luceneIndex = new File(repositoryFolder, LUCENE_DIR);\r
+ if (luceneIndex.exists()) {\r
+ org.eclipse.jgit.util.FileUtils.delete(luceneIndex,\r
+ org.eclipse.jgit.util.FileUtils.RECURSIVE);\r
+ }\r
+ // delete the config file\r
+ File luceneConfig = new File(repositoryFolder, CONF_FILE);\r
+ if (luceneConfig.exists()) {\r
+ luceneConfig.delete();\r
+ }\r
+ return true;\r
+ } catch (IOException e) {\r
+ throw new RuntimeException(e);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Returns the author for the commit, if this information is available.\r
+ *\r
+ * @param commit\r
+ * @return an author or unknown\r
+ */\r
+ private String getAuthor(RevCommit commit) {\r
+ String name = "unknown";\r
+ try {\r
+ name = commit.getAuthorIdent().getName();\r
+ if (StringUtils.isEmpty(name)) {\r
+ name = commit.getAuthorIdent().getEmailAddress();\r
+ }\r
+ } catch (NullPointerException n) {\r
+ }\r
+ return name;\r
+ }\r
+\r
+ /**\r
+ * Returns the committer for the commit, if this information is available.\r
+ *\r
+ * @param commit\r
+ * @return an committer or unknown\r
+ */\r
+ private String getCommitter(RevCommit commit) {\r
+ String name = "unknown";\r
+ try {\r
+ name = commit.getCommitterIdent().getName();\r
+ if (StringUtils.isEmpty(name)) {\r
+ name = commit.getCommitterIdent().getEmailAddress();\r
+ }\r
+ } catch (NullPointerException n) {\r
+ }\r
+ return name;\r
+ }\r
+\r
+ /**\r
+ * Get the tree associated with the given commit.\r
+ *\r
+ * @param walk\r
+ * @param commit\r
+ * @return tree\r
+ * @throws IOException\r
+ */\r
+ private RevTree getTree(final RevWalk walk, final RevCommit commit)\r
+ throws IOException {\r
+ final RevTree tree = commit.getTree();\r
+ if (tree != null) {\r
+ return tree;\r
+ }\r
+ walk.parseHeaders(commit);\r
+ return commit.getTree();\r
+ }\r
+\r
+ /**\r
+ * Construct a keyname from the branch.\r
+ *\r
+ * @param branchName\r
+ * @return a keyname appropriate for the Git config file format\r
+ */\r
+ private String getBranchKey(String branchName) {\r
+ return StringUtils.getSHA1(branchName);\r
+ }\r
+\r
+ /**\r
+ * Returns the Lucene configuration for the specified repository.\r
+ *\r
+ * @param repository\r
+ * @return a config object\r
+ */\r
+ private FileBasedConfig getConfig(Repository repository) {\r
+ File file = new File(repository.getDirectory(), CONF_FILE);\r
+ FileBasedConfig config = new FileBasedConfig(file, FS.detect());\r
+ return config;\r
+ }\r
+\r
+ /**\r
+ * Reads the Lucene config file for the repository to check the index\r
+ * version. If the index version is different, then rebuild the repository\r
+ * index.\r
+ *\r
+ * @param repository\r
+ * @return true of the on-disk index format is different than INDEX_VERSION\r
+ */\r
+ private boolean shouldReindex(Repository repository) {\r
+ try {\r
+ FileBasedConfig config = getConfig(repository);\r
+ config.load();\r
+ int indexVersion = config.getInt(CONF_INDEX, CONF_VERSION, 0);\r
+ // reindex if versions do not match\r
+ return indexVersion != INDEX_VERSION;\r
+ } catch (Throwable t) {\r
+ }\r
+ return true;\r
+ }\r
+\r
+\r
+ /**\r
+ * This completely indexes the repository and will destroy any existing\r
+ * index.\r
+ *\r
+ * @param repositoryName\r
+ * @param repository\r
+ * @return IndexResult\r
+ */\r
+ public IndexResult reindex(RepositoryModel model, Repository repository) {\r
+ IndexResult result = new IndexResult();\r
+ if (!deleteIndex(model.name)) {\r
+ return result;\r
+ }\r
+ try {\r
+ String [] encodings = storedSettings.getStrings(Keys.web.blobEncodings).toArray(new String[0]);\r
+ FileBasedConfig config = getConfig(repository);\r
+ Set<String> indexedCommits = new TreeSet<String>();\r
+ IndexWriter writer = getIndexWriter(model.name);\r
+ // build a quick lookup of tags\r
+ Map<String, List<String>> tags = new HashMap<String, List<String>>();\r
+ for (RefModel tag : JGitUtils.getTags(repository, false, -1)) {\r
+ if (!tag.isAnnotatedTag()) {\r
+ // skip non-annotated tags\r
+ continue;\r
+ }\r
+ if (!tags.containsKey(tag.getObjectId())) {\r
+ tags.put(tag.getReferencedObjectId().getName(), new ArrayList<String>());\r
+ }\r
+ tags.get(tag.getReferencedObjectId().getName()).add(tag.displayName);\r
+ }\r
+\r
+ ObjectReader reader = repository.newObjectReader();\r
+\r
+ // get the local branches\r
+ List<RefModel> branches = JGitUtils.getLocalBranches(repository, true, -1);\r
+\r
+ // sort them by most recently updated\r
+ Collections.sort(branches, new Comparator<RefModel>() {\r
+ @Override\r
+ public int compare(RefModel ref1, RefModel ref2) {\r
+ return ref2.getDate().compareTo(ref1.getDate());\r
+ }\r
+ });\r
+\r
+ // reorder default branch to first position\r
+ RefModel defaultBranch = null;\r
+ ObjectId defaultBranchId = JGitUtils.getDefaultBranch(repository);\r
+ for (RefModel branch : branches) {\r
+ if (branch.getObjectId().equals(defaultBranchId)) {\r
+ defaultBranch = branch;\r
+ break;\r
+ }\r
+ }\r
+ branches.remove(defaultBranch);\r
+ branches.add(0, defaultBranch);\r
+\r
+ // walk through each branch\r
+ for (RefModel branch : branches) {\r
+\r
+ boolean indexBranch = false;\r
+ if (model.indexedBranches.contains(com.gitblit.Constants.DEFAULT_BRANCH)\r
+ && branch.equals(defaultBranch)) {\r
+ // indexing "default" branch\r
+ indexBranch = true;\r
+ } else if (branch.getName().startsWith(com.gitblit.Constants.R_GITBLIT)) {\r
+ // skip Gitblit internal branches\r
+ indexBranch = false;\r
+ } else {\r
+ // normal explicit branch check\r
+ indexBranch = model.indexedBranches.contains(branch.getName());\r
+ }\r
+\r
+ // if this branch is not specifically indexed then skip\r
+ if (!indexBranch) {\r
+ continue;\r
+ }\r
+\r
+ String branchName = branch.getName();\r
+ RevWalk revWalk = new RevWalk(reader);\r
+ RevCommit tip = revWalk.parseCommit(branch.getObjectId());\r
+ String tipId = tip.getId().getName();\r
+\r
+ String keyName = getBranchKey(branchName);\r
+ config.setString(CONF_ALIAS, null, keyName, branchName);\r
+ config.setString(CONF_BRANCH, null, keyName, tipId);\r
+\r
+ // index the blob contents of the tree\r
+ TreeWalk treeWalk = new TreeWalk(repository);\r
+ treeWalk.addTree(tip.getTree());\r
+ treeWalk.setRecursive(true);\r
+\r
+ Map<String, ObjectId> paths = new TreeMap<String, ObjectId>();\r
+ while (treeWalk.next()) {\r
+ // ensure path is not in a submodule\r
+ if (treeWalk.getFileMode(0) != FileMode.GITLINK) {\r
+ paths.put(treeWalk.getPathString(), treeWalk.getObjectId(0));\r
+ }\r
+ }\r
+\r
+ ByteArrayOutputStream os = new ByteArrayOutputStream();\r
+ byte[] tmp = new byte[32767];\r
+\r
+ RevWalk commitWalk = new RevWalk(reader);\r
+ commitWalk.markStart(tip);\r
+\r
+ RevCommit commit;\r
+ while ((paths.size() > 0) && (commit = commitWalk.next()) != null) {\r
+ TreeWalk diffWalk = new TreeWalk(reader);\r
+ int parentCount = commit.getParentCount();\r
+ switch (parentCount) {\r
+ case 0:\r
+ diffWalk.addTree(new EmptyTreeIterator());\r
+ break;\r
+ case 1:\r
+ diffWalk.addTree(getTree(commitWalk, commit.getParent(0)));\r
+ break;\r
+ default:\r
+ // skip merge commits\r
+ continue;\r
+ }\r
+ diffWalk.addTree(getTree(commitWalk, commit));\r
+ diffWalk.setFilter(ANY_DIFF);\r
+ diffWalk.setRecursive(true);\r
+ while ((paths.size() > 0) && diffWalk.next()) {\r
+ String path = diffWalk.getPathString();\r
+ if (!paths.containsKey(path)) {\r
+ continue;\r
+ }\r
+\r
+ // remove path from set\r
+ ObjectId blobId = paths.remove(path);\r
+ result.blobCount++;\r
+\r
+ // index the blob metadata\r
+ String blobAuthor = getAuthor(commit);\r
+ String blobCommitter = getCommitter(commit);\r
+ String blobDate = DateTools.timeToString(commit.getCommitTime() * 1000L,\r
+ Resolution.MINUTE);\r
+\r
+ Document doc = new Document();\r
+ doc.add(new Field(FIELD_OBJECT_TYPE, SearchObjectType.blob.name(), Store.YES, Index.NOT_ANALYZED_NO_NORMS));\r
+ doc.add(new Field(FIELD_BRANCH, branchName, Store.YES, Index.ANALYZED));\r
+ doc.add(new Field(FIELD_COMMIT, commit.getName(), Store.YES, Index.ANALYZED));\r
+ doc.add(new Field(FIELD_PATH, path, Store.YES, Index.ANALYZED));\r
+ doc.add(new Field(FIELD_DATE, blobDate, Store.YES, Index.NO));\r
+ doc.add(new Field(FIELD_AUTHOR, blobAuthor, Store.YES, Index.ANALYZED));\r
+ doc.add(new Field(FIELD_COMMITTER, blobCommitter, Store.YES, Index.ANALYZED));\r
+\r
+ // determine extension to compare to the extension\r
+ // blacklist\r
+ String ext = null;\r
+ String name = path.toLowerCase();\r
+ if (name.indexOf('.') > -1) {\r
+ ext = name.substring(name.lastIndexOf('.') + 1);\r
+ }\r
+\r
+ // index the blob content\r
+ if (StringUtils.isEmpty(ext) || !excludedExtensions.contains(ext)) {\r
+ ObjectLoader ldr = repository.open(blobId, Constants.OBJ_BLOB);\r
+ InputStream in = ldr.openStream();\r
+ int n;\r
+ while ((n = in.read(tmp)) > 0) {\r
+ os.write(tmp, 0, n);\r
+ }\r
+ in.close();\r
+ byte[] content = os.toByteArray();\r
+ String str = StringUtils.decodeString(content, encodings);\r
+ doc.add(new Field(FIELD_CONTENT, str, Store.YES, Index.ANALYZED));\r
+ os.reset();\r
+ }\r
+\r
+ // add the blob to the index\r
+ writer.addDocument(doc);\r
+ }\r
+ }\r
+\r
+ os.close();\r
+\r
+ // index the tip commit object\r
+ if (indexedCommits.add(tipId)) {\r
+ Document doc = createDocument(tip, tags.get(tipId));\r
+ doc.add(new Field(FIELD_BRANCH, branchName, Store.YES, Index.ANALYZED));\r
+ writer.addDocument(doc);\r
+ result.commitCount += 1;\r
+ result.branchCount += 1;\r
+ }\r
+\r
+ // traverse the log and index the previous commit objects\r
+ RevWalk historyWalk = new RevWalk(reader);\r
+ historyWalk.markStart(historyWalk.parseCommit(tip.getId()));\r
+ RevCommit rev;\r
+ while ((rev = historyWalk.next()) != null) {\r
+ String hash = rev.getId().getName();\r
+ if (indexedCommits.add(hash)) {\r
+ Document doc = createDocument(rev, tags.get(hash));\r
+ doc.add(new Field(FIELD_BRANCH, branchName, Store.YES, Index.ANALYZED));\r
+ writer.addDocument(doc);\r
+ result.commitCount += 1;\r
+ }\r
+ }\r
+ }\r
+\r
+ // finished\r
+ reader.release();\r
+\r
+ // commit all changes and reset the searcher\r
+ config.setInt(CONF_INDEX, null, CONF_VERSION, INDEX_VERSION);\r
+ config.save();\r
+ writer.commit();\r
+ resetIndexSearcher(model.name);\r
+ result.success();\r
+ } catch (Exception e) {\r
+ logger.error("Exception while reindexing " + model.name, e);\r
+ }\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * Incrementally update the index with the specified commit for the\r
+ * repository.\r
+ *\r
+ * @param repositoryName\r
+ * @param repository\r
+ * @param branch\r
+ * the fully qualified branch name (e.g. refs/heads/master)\r
+ * @param commit\r
+ * @return true, if successful\r
+ */\r
+ private IndexResult index(String repositoryName, Repository repository,\r
+ String branch, RevCommit commit) {\r
+ IndexResult result = new IndexResult();\r
+ try {\r
+ String [] encodings = storedSettings.getStrings(Keys.web.blobEncodings).toArray(new String[0]);\r
+ List<PathChangeModel> changedPaths = JGitUtils.getFilesInCommit(repository, commit);\r
+ String revDate = DateTools.timeToString(commit.getCommitTime() * 1000L,\r
+ Resolution.MINUTE);\r
+ IndexWriter writer = getIndexWriter(repositoryName);\r
+ for (PathChangeModel path : changedPaths) {\r
+ if (path.isSubmodule()) {\r
+ continue;\r
+ }\r
+ // delete the indexed blob\r
+ deleteBlob(repositoryName, branch, path.name);\r
+\r
+ // re-index the blob\r
+ if (!ChangeType.DELETE.equals(path.changeType)) {\r
+ result.blobCount++;\r
+ Document doc = new Document();\r
+ doc.add(new Field(FIELD_OBJECT_TYPE, SearchObjectType.blob.name(), Store.YES,\r
+ Index.NOT_ANALYZED));\r
+ doc.add(new Field(FIELD_BRANCH, branch, Store.YES, Index.ANALYZED));\r
+ doc.add(new Field(FIELD_COMMIT, commit.getName(), Store.YES, Index.ANALYZED));\r
+ doc.add(new Field(FIELD_PATH, path.path, Store.YES, Index.ANALYZED));\r
+ doc.add(new Field(FIELD_DATE, revDate, Store.YES, Index.NO));\r
+ doc.add(new Field(FIELD_AUTHOR, getAuthor(commit), Store.YES, Index.ANALYZED));\r
+ doc.add(new Field(FIELD_COMMITTER, getCommitter(commit), Store.YES, Index.ANALYZED));\r
+\r
+ // determine extension to compare to the extension\r
+ // blacklist\r
+ String ext = null;\r
+ String name = path.name.toLowerCase();\r
+ if (name.indexOf('.') > -1) {\r
+ ext = name.substring(name.lastIndexOf('.') + 1);\r
+ }\r
+\r
+ if (StringUtils.isEmpty(ext) || !excludedExtensions.contains(ext)) {\r
+ // read the blob content\r
+ String str = JGitUtils.getStringContent(repository, commit.getTree(),\r
+ path.path, encodings);\r
+ if (str != null) {\r
+ doc.add(new Field(FIELD_CONTENT, str, Store.YES, Index.ANALYZED));\r
+ writer.addDocument(doc);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ writer.commit();\r
+\r
+ // get any annotated commit tags\r
+ List<String> commitTags = new ArrayList<String>();\r
+ for (RefModel ref : JGitUtils.getTags(repository, false, -1)) {\r
+ if (ref.isAnnotatedTag() && ref.getReferencedObjectId().equals(commit.getId())) {\r
+ commitTags.add(ref.displayName);\r
+ }\r
+ }\r
+\r
+ // create and write the Lucene document\r
+ Document doc = createDocument(commit, commitTags);\r
+ doc.add(new Field(FIELD_BRANCH, branch, Store.YES, Index.ANALYZED));\r
+ result.commitCount++;\r
+ result.success = index(repositoryName, doc);\r
+ } catch (Exception e) {\r
+ logger.error(MessageFormat.format("Exception while indexing commit {0} in {1}", commit.getId().getName(), repositoryName), e);\r
+ }\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * Delete a blob from the specified branch of the repository index.\r
+ *\r
+ * @param repositoryName\r
+ * @param branch\r
+ * @param path\r
+ * @throws Exception\r
+ * @return true, if deleted, false if no record was deleted\r
+ */\r
+ public boolean deleteBlob(String repositoryName, String branch, String path) throws Exception {\r
+ String pattern = MessageFormat.format("{0}:'{'0} AND {1}:\"'{'1'}'\" AND {2}:\"'{'2'}'\"", FIELD_OBJECT_TYPE, FIELD_BRANCH, FIELD_PATH);\r
+ String q = MessageFormat.format(pattern, SearchObjectType.blob.name(), branch, path);\r
+\r
+ BooleanQuery query = new BooleanQuery();\r
+ StandardAnalyzer analyzer = new StandardAnalyzer(LUCENE_VERSION);\r
+ QueryParser qp = new QueryParser(LUCENE_VERSION, FIELD_SUMMARY, analyzer);\r
+ query.add(qp.parse(q), Occur.MUST);\r
+\r
+ IndexWriter writer = getIndexWriter(repositoryName);\r
+ int numDocsBefore = writer.numDocs();\r
+ writer.deleteDocuments(query);\r
+ writer.commit();\r
+ int numDocsAfter = writer.numDocs();\r
+ if (numDocsBefore == numDocsAfter) {\r
+ logger.debug(MessageFormat.format("no records found to delete {0}", query.toString()));\r
+ return false;\r
+ } else {\r
+ logger.debug(MessageFormat.format("deleted {0} records with {1}", numDocsBefore - numDocsAfter, query.toString()));\r
+ return true;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Updates a repository index incrementally from the last indexed commits.\r
+ *\r
+ * @param model\r
+ * @param repository\r
+ * @return IndexResult\r
+ */\r
+ private IndexResult updateIndex(RepositoryModel model, Repository repository) {\r
+ IndexResult result = new IndexResult();\r
+ try {\r
+ FileBasedConfig config = getConfig(repository);\r
+ config.load();\r
+\r
+ // build a quick lookup of annotated tags\r
+ Map<String, List<String>> tags = new HashMap<String, List<String>>();\r
+ for (RefModel tag : JGitUtils.getTags(repository, false, -1)) {\r
+ if (!tag.isAnnotatedTag()) {\r
+ // skip non-annotated tags\r
+ continue;\r
+ }\r
+ if (!tags.containsKey(tag.getObjectId())) {\r
+ tags.put(tag.getReferencedObjectId().getName(), new ArrayList<String>());\r
+ }\r
+ tags.get(tag.getReferencedObjectId().getName()).add(tag.displayName);\r
+ }\r
+\r
+ // detect branch deletion\r
+ // first assume all branches are deleted and then remove each\r
+ // existing branch from deletedBranches during indexing\r
+ Set<String> deletedBranches = new TreeSet<String>();\r
+ for (String alias : config.getNames(CONF_ALIAS)) {\r
+ String branch = config.getString(CONF_ALIAS, null, alias);\r
+ deletedBranches.add(branch);\r
+ }\r
+\r
+ // get the local branches\r
+ List<RefModel> branches = JGitUtils.getLocalBranches(repository, true, -1);\r
+\r
+ // sort them by most recently updated\r
+ Collections.sort(branches, new Comparator<RefModel>() {\r
+ @Override\r
+ public int compare(RefModel ref1, RefModel ref2) {\r
+ return ref2.getDate().compareTo(ref1.getDate());\r
+ }\r
+ });\r
+\r
+ // reorder default branch to first position\r
+ RefModel defaultBranch = null;\r
+ ObjectId defaultBranchId = JGitUtils.getDefaultBranch(repository);\r
+ for (RefModel branch : branches) {\r
+ if (branch.getObjectId().equals(defaultBranchId)) {\r
+ defaultBranch = branch;\r
+ break;\r
+ }\r
+ }\r
+ branches.remove(defaultBranch);\r
+ branches.add(0, defaultBranch);\r
+\r
+ // walk through each branches\r
+ for (RefModel branch : branches) {\r
+ String branchName = branch.getName();\r
+\r
+ boolean indexBranch = false;\r
+ if (model.indexedBranches.contains(com.gitblit.Constants.DEFAULT_BRANCH)\r
+ && branch.equals(defaultBranch)) {\r
+ // indexing "default" branch\r
+ indexBranch = true;\r
+ } else if (branch.getName().startsWith(com.gitblit.Constants.R_GITBLIT)) {\r
+ // ignore internal Gitblit branches\r
+ indexBranch = false;\r
+ } else {\r
+ // normal explicit branch check\r
+ indexBranch = model.indexedBranches.contains(branch.getName());\r
+ }\r
+\r
+ // if this branch is not specifically indexed then skip\r
+ if (!indexBranch) {\r
+ continue;\r
+ }\r
+\r
+ // remove this branch from the deletedBranches set\r
+ deletedBranches.remove(branchName);\r
+\r
+ // determine last commit\r
+ String keyName = getBranchKey(branchName);\r
+ String lastCommit = config.getString(CONF_BRANCH, null, keyName);\r
+\r
+ List<RevCommit> revs;\r
+ if (StringUtils.isEmpty(lastCommit)) {\r
+ // new branch/unindexed branch, get all commits on branch\r
+ revs = JGitUtils.getRevLog(repository, branchName, 0, -1);\r
+ } else {\r
+ // pre-existing branch, get changes since last commit\r
+ revs = JGitUtils.getRevLog(repository, lastCommit, branchName);\r
+ }\r
+\r
+ if (revs.size() > 0) {\r
+ result.branchCount += 1;\r
+ }\r
+\r
+ // reverse the list of commits so we start with the first commit\r
+ Collections.reverse(revs);\r
+ for (RevCommit commit : revs) {\r
+ // index a commit\r
+ result.add(index(model.name, repository, branchName, commit));\r
+ }\r
+\r
+ // update the config\r
+ config.setInt(CONF_INDEX, null, CONF_VERSION, INDEX_VERSION);\r
+ config.setString(CONF_ALIAS, null, keyName, branchName);\r
+ config.setString(CONF_BRANCH, null, keyName, branch.getObjectId().getName());\r
+ config.save();\r
+ }\r
+\r
+ // the deletedBranches set will normally be empty by this point\r
+ // unless a branch really was deleted and no longer exists\r
+ if (deletedBranches.size() > 0) {\r
+ for (String branch : deletedBranches) {\r
+ IndexWriter writer = getIndexWriter(model.name);\r
+ writer.deleteDocuments(new Term(FIELD_BRANCH, branch));\r
+ writer.commit();\r
+ }\r
+ }\r
+ result.success = true;\r
+ } catch (Throwable t) {\r
+ logger.error(MessageFormat.format("Exception while updating {0} Lucene index", model.name), t);\r
+ }\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * Creates a Lucene document for a commit\r
+ *\r
+ * @param commit\r
+ * @param tags\r
+ * @return a Lucene document\r
+ */\r
+ private Document createDocument(RevCommit commit, List<String> tags) {\r
+ Document doc = new Document();\r
+ doc.add(new Field(FIELD_OBJECT_TYPE, SearchObjectType.commit.name(), Store.YES,\r
+ Index.NOT_ANALYZED));\r
+ doc.add(new Field(FIELD_COMMIT, commit.getName(), Store.YES, Index.ANALYZED));\r
+ doc.add(new Field(FIELD_DATE, DateTools.timeToString(commit.getCommitTime() * 1000L,\r
+ Resolution.MINUTE), Store.YES, Index.NO));\r
+ doc.add(new Field(FIELD_AUTHOR, getAuthor(commit), Store.YES, Index.ANALYZED));\r
+ doc.add(new Field(FIELD_COMMITTER, getCommitter(commit), Store.YES, Index.ANALYZED));\r
+ doc.add(new Field(FIELD_SUMMARY, commit.getShortMessage(), Store.YES, Index.ANALYZED));\r
+ doc.add(new Field(FIELD_CONTENT, commit.getFullMessage(), Store.YES, Index.ANALYZED));\r
+ if (!ArrayUtils.isEmpty(tags)) {\r
+ doc.add(new Field(FIELD_TAG, StringUtils.flattenStrings(tags), Store.YES, Index.ANALYZED));\r
+ }\r
+ return doc;\r
+ }\r
+\r
+ /**\r
+ * Incrementally index an object for the repository.\r
+ *\r
+ * @param repositoryName\r
+ * @param doc\r
+ * @return true, if successful\r
+ */\r
+ private boolean index(String repositoryName, Document doc) {\r
+ try {\r
+ IndexWriter writer = getIndexWriter(repositoryName);\r
+ writer.addDocument(doc);\r
+ writer.commit();\r
+ resetIndexSearcher(repositoryName);\r
+ return true;\r
+ } catch (Exception e) {\r
+ logger.error(MessageFormat.format("Exception while incrementally updating {0} Lucene index", repositoryName), e);\r
+ }\r
+ return false;\r
+ }\r
+\r
+ private SearchResult createSearchResult(Document doc, float score, int hitId, int totalHits) throws ParseException {\r
+ SearchResult result = new SearchResult();\r
+ result.hitId = hitId;\r
+ result.totalHits = totalHits;\r
+ result.score = score;\r
+ result.date = DateTools.stringToDate(doc.get(FIELD_DATE));\r
+ result.summary = doc.get(FIELD_SUMMARY);\r
+ result.author = doc.get(FIELD_AUTHOR);\r
+ result.committer = doc.get(FIELD_COMMITTER);\r
+ result.type = SearchObjectType.fromName(doc.get(FIELD_OBJECT_TYPE));\r
+ result.branch = doc.get(FIELD_BRANCH);\r
+ result.commitId = doc.get(FIELD_COMMIT);\r
+ result.path = doc.get(FIELD_PATH);\r
+ if (doc.get(FIELD_TAG) != null) {\r
+ result.tags = StringUtils.getStringsFromValue(doc.get(FIELD_TAG));\r
+ }\r
+ return result;\r
+ }\r
+\r
+ private synchronized void resetIndexSearcher(String repository) throws IOException {\r
+ IndexSearcher searcher = searchers.remove(repository);\r
+ if (searcher != null) {\r
+ searcher.getIndexReader().close();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Gets an index searcher for the repository.\r
+ *\r
+ * @param repository\r
+ * @return\r
+ * @throws IOException\r
+ */\r
+ private IndexSearcher getIndexSearcher(String repository) throws IOException {\r
+ IndexSearcher searcher = searchers.get(repository);\r
+ if (searcher == null) {\r
+ IndexWriter writer = getIndexWriter(repository);\r
+ searcher = new IndexSearcher(IndexReader.open(writer, true));\r
+ searchers.put(repository, searcher);\r
+ }\r
+ return searcher;\r
+ }\r
+\r
+ /**\r
+ * Gets an index writer for the repository. The index will be created if it\r
+ * does not already exist or if forceCreate is specified.\r
+ *\r
+ * @param repository\r
+ * @return an IndexWriter\r
+ * @throws IOException\r
+ */\r
+ private IndexWriter getIndexWriter(String repository) throws IOException {\r
+ IndexWriter indexWriter = writers.get(repository);\r
+ File repositoryFolder = FileKey.resolve(new File(repositoriesFolder, repository), FS.DETECTED);\r
+ File indexFolder = new File(repositoryFolder, LUCENE_DIR);\r
+ Directory directory = FSDirectory.open(indexFolder);\r
+\r
+ if (indexWriter == null) {\r
+ if (!indexFolder.exists()) {\r
+ indexFolder.mkdirs();\r
+ }\r
+ StandardAnalyzer analyzer = new StandardAnalyzer(LUCENE_VERSION);\r
+ IndexWriterConfig config = new IndexWriterConfig(LUCENE_VERSION, analyzer);\r
+ config.setOpenMode(OpenMode.CREATE_OR_APPEND);\r
+ indexWriter = new IndexWriter(directory, config);\r
+ writers.put(repository, indexWriter);\r
+ }\r
+ return indexWriter;\r
+ }\r
+\r
+ /**\r
+ * Searches the specified repositories for the given text or query\r
+ *\r
+ * @param text\r
+ * if the text is null or empty, null is returned\r
+ * @param page\r
+ * the page number to retrieve. page is 1-indexed.\r
+ * @param pageSize\r
+ * the number of elements to return for this page\r
+ * @param repositories\r
+ * a list of repositories to search. if no repositories are\r
+ * specified null is returned.\r
+ * @return a list of SearchResults in order from highest to the lowest score\r
+ *\r
+ */\r
+ public List<SearchResult> search(String text, int page, int pageSize, List<String> repositories) {\r
+ if (ArrayUtils.isEmpty(repositories)) {\r
+ return null;\r
+ }\r
+ return search(text, page, pageSize, repositories.toArray(new String[0]));\r
+ }\r
+\r
+ /**\r
+ * Searches the specified repositories for the given text or query\r
+ *\r
+ * @param text\r
+ * if the text is null or empty, null is returned\r
+ * @param page\r
+ * the page number to retrieve. page is 1-indexed.\r
+ * @param pageSize\r
+ * the number of elements to return for this page\r
+ * @param repositories\r
+ * a list of repositories to search. if no repositories are\r
+ * specified null is returned.\r
+ * @return a list of SearchResults in order from highest to the lowest score\r
+ *\r
+ */\r
+ public List<SearchResult> search(String text, int page, int pageSize, String... repositories) {\r
+ if (StringUtils.isEmpty(text)) {\r
+ return null;\r
+ }\r
+ if (ArrayUtils.isEmpty(repositories)) {\r
+ return null;\r
+ }\r
+ Set<SearchResult> results = new LinkedHashSet<SearchResult>();\r
+ StandardAnalyzer analyzer = new StandardAnalyzer(LUCENE_VERSION);\r
+ try {\r
+ // default search checks summary and content\r
+ BooleanQuery query = new BooleanQuery();\r
+ QueryParser qp;\r
+ qp = new QueryParser(LUCENE_VERSION, FIELD_SUMMARY, analyzer);\r
+ qp.setAllowLeadingWildcard(true);\r
+ query.add(qp.parse(text), Occur.SHOULD);\r
+\r
+ qp = new QueryParser(LUCENE_VERSION, FIELD_CONTENT, analyzer);\r
+ qp.setAllowLeadingWildcard(true);\r
+ query.add(qp.parse(text), Occur.SHOULD);\r
+\r
+ IndexSearcher searcher;\r
+ if (repositories.length == 1) {\r
+ // single repository search\r
+ searcher = getIndexSearcher(repositories[0]);\r
+ } else {\r
+ // multiple repository search\r
+ List<IndexReader> readers = new ArrayList<IndexReader>();\r
+ for (String repository : repositories) {\r
+ IndexSearcher repositoryIndex = getIndexSearcher(repository);\r
+ readers.add(repositoryIndex.getIndexReader());\r
+ }\r
+ IndexReader[] rdrs = readers.toArray(new IndexReader[readers.size()]);\r
+ MultiSourceReader reader = new MultiSourceReader(rdrs);\r
+ searcher = new IndexSearcher(reader);\r
+ }\r
+\r
+ Query rewrittenQuery = searcher.rewrite(query);\r
+ logger.debug(rewrittenQuery.toString());\r
+\r
+ TopScoreDocCollector collector = TopScoreDocCollector.create(5000, true);\r
+ searcher.search(rewrittenQuery, collector);\r
+ int offset = Math.max(0, (page - 1) * pageSize);\r
+ ScoreDoc[] hits = collector.topDocs(offset, pageSize).scoreDocs;\r
+ int totalHits = collector.getTotalHits();\r
+ for (int i = 0; i < hits.length; i++) {\r
+ int docId = hits[i].doc;\r
+ Document doc = searcher.doc(docId);\r
+ SearchResult result = createSearchResult(doc, hits[i].score, offset + i + 1, totalHits);\r
+ if (repositories.length == 1) {\r
+ // single repository search\r
+ result.repository = repositories[0];\r
+ } else {\r
+ // multi-repository search\r
+ MultiSourceReader reader = (MultiSourceReader) searcher.getIndexReader();\r
+ int index = reader.getSourceIndex(docId);\r
+ result.repository = repositories[index];\r
+ }\r
+ String content = doc.get(FIELD_CONTENT);\r
+ result.fragment = getHighlightedFragment(analyzer, query, content, result);\r
+ results.add(result);\r
+ }\r
+ } catch (Exception e) {\r
+ logger.error(MessageFormat.format("Exception while searching for {0}", text), e);\r
+ }\r
+ return new ArrayList<SearchResult>(results);\r
+ }\r
+\r
+ /**\r
+ *\r
+ * @param analyzer\r
+ * @param query\r
+ * @param content\r
+ * @param result\r
+ * @return\r
+ * @throws IOException\r
+ * @throws InvalidTokenOffsetsException\r
+ */\r
+ private String getHighlightedFragment(Analyzer analyzer, Query query,\r
+ String content, SearchResult result) throws IOException, InvalidTokenOffsetsException {\r
+ if (content == null) {\r
+ content = "";\r
+ }\r
+\r
+ int fragmentLength = SearchObjectType.commit == result.type ? 512 : 150;\r
+\r
+ QueryScorer scorer = new QueryScorer(query, "content");\r
+ Fragmenter fragmenter = new SimpleSpanFragmenter(scorer, fragmentLength);\r
+\r
+ // use an artificial delimiter for the token\r
+ String termTag = "!!--[";\r
+ String termTagEnd = "]--!!";\r
+ SimpleHTMLFormatter formatter = new SimpleHTMLFormatter(termTag, termTagEnd);\r
+ Highlighter highlighter = new Highlighter(formatter, scorer);\r
+ highlighter.setTextFragmenter(fragmenter);\r
+\r
+ String [] fragments = highlighter.getBestFragments(analyzer, "content", content, 3);\r
+ if (ArrayUtils.isEmpty(fragments)) {\r
+ if (SearchObjectType.blob == result.type) {\r
+ return "";\r
+ }\r
+ // clip commit message\r
+ String fragment = content;\r
+ if (fragment.length() > fragmentLength) {\r
+ fragment = fragment.substring(0, fragmentLength) + "...";\r
+ }\r
+ return "<pre class=\"text\">" + StringUtils.escapeForHtml(fragment, true) + "</pre>";\r
+ }\r
+\r
+ // make sure we have unique fragments\r
+ Set<String> uniqueFragments = new LinkedHashSet<String>();\r
+ for (String fragment : fragments) {\r
+ uniqueFragments.add(fragment);\r
+ }\r
+ fragments = uniqueFragments.toArray(new String[uniqueFragments.size()]);\r
+\r
+ StringBuilder sb = new StringBuilder();\r
+ for (int i = 0, len = fragments.length; i < len; i++) {\r
+ String fragment = fragments[i];\r
+ String tag = "<pre class=\"text\">";\r
+\r
+ // resurrect the raw fragment from removing the artificial delimiters\r
+ String raw = fragment.replace(termTag, "").replace(termTagEnd, "");\r
+\r
+ // determine position of the raw fragment in the content\r
+ int pos = content.indexOf(raw);\r
+\r
+ // restore complete first line of fragment\r
+ int c = pos;\r
+ while (c > 0) {\r
+ c--;\r
+ if (content.charAt(c) == '\n') {\r
+ break;\r
+ }\r
+ }\r
+ if (c > 0) {\r
+ // inject leading chunk of first fragment line\r
+ fragment = content.substring(c + 1, pos) + fragment;\r
+ }\r
+\r
+ if (SearchObjectType.blob == result.type) {\r
+ // count lines as offset into the content for this fragment\r
+ int line = Math.max(1, StringUtils.countLines(content.substring(0, pos)));\r
+\r
+ // create fragment tag with line number and language\r
+ String lang = "";\r
+ String ext = StringUtils.getFileExtension(result.path).toLowerCase();\r
+ if (!StringUtils.isEmpty(ext)) {\r
+ // maintain leading space!\r
+ lang = " lang-" + ext;\r
+ }\r
+ tag = MessageFormat.format("<pre class=\"prettyprint linenums:{0,number,0}{1}\">", line, lang);\r
+\r
+ }\r
+\r
+ sb.append(tag);\r
+\r
+ // replace the artificial delimiter with html tags\r
+ String html = StringUtils.escapeForHtml(fragment, false);\r
+ html = html.replace(termTag, "<span class=\"highlight\">").replace(termTagEnd, "</span>");\r
+ sb.append(html);\r
+ sb.append("</pre>");\r
+ if (i < len - 1) {\r
+ sb.append("<span class=\"ellipses\">...</span><br/>");\r
+ }\r
+ }\r
+ return sb.toString();\r
+ }\r
+\r
+ /**\r
+ * Simple class to track the results of an index update.\r
+ */\r
+ private class IndexResult {\r
+ long startTime = System.currentTimeMillis();\r
+ long endTime = startTime;\r
+ boolean success;\r
+ int branchCount;\r
+ int commitCount;\r
+ int blobCount;\r
+\r
+ void add(IndexResult result) {\r
+ this.branchCount += result.branchCount;\r
+ this.commitCount += result.commitCount;\r
+ this.blobCount += result.blobCount;\r
+ }\r
+\r
+ void success() {\r
+ success = true;\r
+ endTime = System.currentTimeMillis();\r
+ }\r
+\r
+ float duration() {\r
+ return (endTime - startTime)/1000f;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Custom subclass of MultiReader to identify the source index for a given\r
+ * doc id. This would not be necessary of there was a public method to\r
+ * obtain this information.\r
+ *\r
+ */\r
+ private class MultiSourceReader extends MultiReader {\r
+\r
+ final Method method;\r
+\r
+ MultiSourceReader(IndexReader[] subReaders) {\r
+ super(subReaders);\r
+ Method m = null;\r
+ try {\r
+ m = MultiReader.class.getDeclaredMethod("readerIndex", int.class);\r
+ m.setAccessible(true);\r
+ } catch (Exception e) {\r
+ logger.error("Error getting readerIndex method", e);\r
+ }\r
+ method = m;\r
+ }\r
+\r
+ int getSourceIndex(int docId) {\r
+ int index = -1;\r
+ try {\r
+ Object o = method.invoke(this, docId);\r
+ index = (Integer) o;\r
+ } catch (Exception e) {\r
+ logger.error("Error getting source index", e);\r
+ }\r
+ return index;\r
+ }\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.service;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.Date;\r
+import java.util.HashSet;\r
+import java.util.List;\r
+import java.util.Properties;\r
+import java.util.Queue;\r
+import java.util.Set;\r
+import java.util.concurrent.ConcurrentLinkedQueue;\r
+import java.util.regex.Pattern;\r
+\r
+import javax.mail.Authenticator;\r
+import javax.mail.Message;\r
+import javax.mail.PasswordAuthentication;\r
+import javax.mail.Session;\r
+import javax.mail.Transport;\r
+import javax.mail.internet.InternetAddress;\r
+import javax.mail.internet.MimeMessage;\r
+\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+import com.gitblit.IStoredSettings;\r
+import com.gitblit.Keys;\r
+import com.gitblit.utils.StringUtils;\r
+\r
+/**\r
+ * The mail service handles sending email messages asynchronously from a queue.\r
+ *\r
+ * @author James Moger\r
+ *\r
+ */\r
+public class MailService implements Runnable {\r
+\r
+ private final Logger logger = LoggerFactory.getLogger(MailService.class);\r
+\r
+ private final Queue<Message> queue = new ConcurrentLinkedQueue<Message>();\r
+\r
+ private final Session session;\r
+\r
+ private final IStoredSettings settings;\r
+\r
+ public MailService(IStoredSettings settings) {\r
+ this.settings = settings;\r
+\r
+ final String mailUser = settings.getString(Keys.mail.username, null);\r
+ final String mailPassword = settings.getString(Keys.mail.password, null);\r
+ final boolean smtps = settings.getBoolean(Keys.mail.smtps, false);\r
+ boolean authenticate = !StringUtils.isEmpty(mailUser) && !StringUtils.isEmpty(mailPassword);\r
+ String server = settings.getString(Keys.mail.server, "");\r
+ if (StringUtils.isEmpty(server)) {\r
+ session = null;\r
+ return;\r
+ }\r
+ int port = settings.getInteger(Keys.mail.port, 25);\r
+ boolean isGMail = false;\r
+ if (server.equals("smtp.gmail.com")) {\r
+ port = 465;\r
+ isGMail = true;\r
+ }\r
+\r
+ Properties props = new Properties();\r
+ props.setProperty("mail.smtp.host", server);\r
+ props.setProperty("mail.smtp.port", String.valueOf(port));\r
+ props.setProperty("mail.smtp.auth", String.valueOf(authenticate));\r
+ props.setProperty("mail.smtp.auths", String.valueOf(authenticate));\r
+\r
+ if (isGMail || smtps) {\r
+ props.setProperty("mail.smtp.starttls.enable", "true");\r
+ props.put("mail.smtp.socketFactory.port", String.valueOf(port));\r
+ props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");\r
+ props.put("mail.smtp.socketFactory.fallback", "false");\r
+ }\r
+\r
+ if (!StringUtils.isEmpty(mailUser) && !StringUtils.isEmpty(mailPassword)) {\r
+ // SMTP requires authentication\r
+ session = Session.getInstance(props, new Authenticator() {\r
+ @Override\r
+ protected PasswordAuthentication getPasswordAuthentication() {\r
+ PasswordAuthentication passwordAuthentication = new PasswordAuthentication(\r
+ mailUser, mailPassword);\r
+ return passwordAuthentication;\r
+ }\r
+ });\r
+ } else {\r
+ // SMTP does not require authentication\r
+ session = Session.getInstance(props);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Indicates if the mail executor can send emails.\r
+ *\r
+ * @return true if the mail executor is ready to send emails\r
+ */\r
+ public boolean isReady() {\r
+ return session != null;\r
+ }\r
+\r
+\r
+ /**\r
+ * Create a message.\r
+ *\r
+ * @param toAddresses\r
+ * @return a message\r
+ */\r
+ public Message createMessage(String... toAddresses) {\r
+ return createMessage(Arrays.asList(toAddresses));\r
+ }\r
+\r
+ /**\r
+ * Create a message.\r
+ *\r
+ * @param toAddresses\r
+ * @return a message\r
+ */\r
+ public Message createMessage(List<String> toAddresses) {\r
+ MimeMessage message = new MimeMessage(session);\r
+ try {\r
+ String fromAddress = settings.getString(Keys.mail.fromAddress, null);\r
+ if (StringUtils.isEmpty(fromAddress)) {\r
+ fromAddress = "gitblit@gitblit.com";\r
+ }\r
+ InternetAddress from = new InternetAddress(fromAddress, "Gitblit");\r
+ message.setFrom(from);\r
+\r
+ // determine unique set of addresses\r
+ Set<String> uniques = new HashSet<String>();\r
+ for (String address : toAddresses) {\r
+ uniques.add(address.toLowerCase());\r
+ }\r
+\r
+ Pattern validEmail = Pattern\r
+ .compile("^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$");\r
+ List<InternetAddress> tos = new ArrayList<InternetAddress>();\r
+ for (String address : uniques) {\r
+ if (StringUtils.isEmpty(address)) {\r
+ continue;\r
+ }\r
+ if (validEmail.matcher(address).find()) {\r
+ try {\r
+ tos.add(new InternetAddress(address));\r
+ } catch (Throwable t) {\r
+ }\r
+ }\r
+ }\r
+ message.setRecipients(Message.RecipientType.BCC,\r
+ tos.toArray(new InternetAddress[tos.size()]));\r
+ message.setSentDate(new Date());\r
+ } catch (Exception e) {\r
+ logger.error("Failed to properly create message", e);\r
+ }\r
+ return message;\r
+ }\r
+\r
+ /**\r
+ * Returns the status of the mail queue.\r
+ *\r
+ * @return true, if the queue is empty\r
+ */\r
+ public boolean hasEmptyQueue() {\r
+ return queue.isEmpty();\r
+ }\r
+\r
+ /**\r
+ * Queue's an email message to be sent.\r
+ *\r
+ * @param message\r
+ * @return true if the message was queued\r
+ */\r
+ public boolean queue(Message message) {\r
+ if (!isReady()) {\r
+ return false;\r
+ }\r
+ try {\r
+ message.saveChanges();\r
+ } catch (Throwable t) {\r
+ logger.error("Failed to save changes to message!", t);\r
+ }\r
+ queue.add(message);\r
+ return true;\r
+ }\r
+\r
+ @Override\r
+ public void run() {\r
+ if (!queue.isEmpty()) {\r
+ if (session != null) {\r
+ // send message via mail server\r
+ List<Message> failures = new ArrayList<Message>();\r
+ Message message = null;\r
+ while ((message = queue.poll()) != null) {\r
+ try {\r
+ if (settings.getBoolean(Keys.mail.debug, false)) {\r
+ logger.info("send: " + StringUtils.trimString(message.getSubject(), 60));\r
+ }\r
+ Transport.send(message);\r
+ } catch (Throwable e) {\r
+ logger.error("Failed to send message", e);\r
+ failures.add(message);\r
+ }\r
+ }\r
+\r
+ // push the failures back onto the queue for the next cycle\r
+ queue.addAll(failures);\r
+ }\r
+ }\r
+ }\r
+\r
+ public void sendNow(Message message) throws Exception {\r
+ Transport.send(message);\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2013 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.service;\r
+\r
+import java.text.MessageFormat;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.HashSet;\r
+import java.util.List;\r
+import java.util.Set;\r
+import java.util.concurrent.atomic.AtomicBoolean;\r
+\r
+import org.eclipse.jgit.api.Git;\r
+import org.eclipse.jgit.lib.RefUpdate.Result;\r
+import org.eclipse.jgit.lib.Repository;\r
+import org.eclipse.jgit.lib.StoredConfig;\r
+import org.eclipse.jgit.transport.FetchResult;\r
+import org.eclipse.jgit.transport.RemoteConfig;\r
+import org.eclipse.jgit.transport.TrackingRefUpdate;\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+import com.gitblit.IStoredSettings;\r
+import com.gitblit.Keys;\r
+import com.gitblit.manager.IRepositoryManager;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.JGitUtils;\r
+\r
+/**\r
+ * The Mirror service handles periodic fetching of mirrored repositories.\r
+ *\r
+ * @author James Moger\r
+ *\r
+ */\r
+public class MirrorService implements Runnable {\r
+\r
+ private final Logger logger = LoggerFactory.getLogger(MirrorService.class);\r
+\r
+ private final Set<String> repairAttempted = Collections.synchronizedSet(new HashSet<String>());\r
+\r
+ private final IStoredSettings settings;\r
+\r
+ private final IRepositoryManager repositoryManager;\r
+\r
+ private AtomicBoolean running = new AtomicBoolean(false);\r
+\r
+ private AtomicBoolean forceClose = new AtomicBoolean(false);\r
+\r
+ private final UserModel gitblitUser;\r
+\r
+ public MirrorService(\r
+ IStoredSettings settings,\r
+ IRepositoryManager repositoryManager) {\r
+\r
+ this.settings = settings;\r
+ this.repositoryManager = repositoryManager;\r
+ this.gitblitUser = new UserModel("gitblit");\r
+ this.gitblitUser.displayName = "Gitblit";\r
+ }\r
+\r
+ public boolean isReady() {\r
+ return settings.getBoolean(Keys.git.enableMirroring, false);\r
+ }\r
+\r
+ public boolean isRunning() {\r
+ return running.get();\r
+ }\r
+\r
+ public void close() {\r
+ forceClose.set(true);\r
+ }\r
+\r
+ @Override\r
+ public void run() {\r
+ if (!isReady()) {\r
+ return;\r
+ }\r
+\r
+ running.set(true);\r
+\r
+ for (String repositoryName : repositoryManager.getRepositoryList()) {\r
+ if (forceClose.get()) {\r
+ break;\r
+ }\r
+ if (repositoryManager.isCollectingGarbage(repositoryName)) {\r
+ logger.debug("mirror is skipping {} garbagecollection", repositoryName);\r
+ continue;\r
+ }\r
+ RepositoryModel model = null;\r
+ Repository repository = null;\r
+ try {\r
+ model = repositoryManager.getRepositoryModel(repositoryName);\r
+ if (!model.isMirror && !model.isBare) {\r
+ // repository must be a valid bare git mirror\r
+ logger.debug("mirror is skipping {} !mirror !bare", repositoryName);\r
+ continue;\r
+ }\r
+\r
+ repository = repositoryManager.getRepository(repositoryName);\r
+ if (repository == null) {\r
+ logger.warn(MessageFormat.format("MirrorExecutor is missing repository {0}?!?", repositoryName));\r
+ continue;\r
+ }\r
+\r
+ // automatically repair (some) invalid fetch ref specs\r
+ if (!repairAttempted.contains(repositoryName)) {\r
+ repairAttempted.add(repositoryName);\r
+ JGitUtils.repairFetchSpecs(repository);\r
+ }\r
+\r
+ // find the first mirror remote - there should only be one\r
+ StoredConfig rc = repository.getConfig();\r
+ RemoteConfig mirror = null;\r
+ List<RemoteConfig> configs = RemoteConfig.getAllRemoteConfigs(rc);\r
+ for (RemoteConfig config : configs) {\r
+ if (config.isMirror()) {\r
+ mirror = config;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (mirror == null) {\r
+ // repository does not have a mirror remote\r
+ logger.debug("mirror is skipping {} no mirror remote found", repositoryName);\r
+ continue;\r
+ }\r
+\r
+ logger.debug("checking {} remote {} for ref updates", repositoryName, mirror.getName());\r
+ final boolean testing = false;\r
+ Git git = new Git(repository);\r
+ FetchResult result = git.fetch().setRemote(mirror.getName()).setDryRun(testing).call();\r
+ Collection<TrackingRefUpdate> refUpdates = result.getTrackingRefUpdates();\r
+ if (refUpdates.size() > 0) {\r
+ for (TrackingRefUpdate ru : refUpdates) {\r
+ StringBuilder sb = new StringBuilder();\r
+ sb.append("updated mirror ");\r
+ sb.append(repositoryName);\r
+ sb.append(" ");\r
+ sb.append(ru.getRemoteName());\r
+ sb.append(" -> ");\r
+ sb.append(ru.getLocalName());\r
+ if (ru.getResult() == Result.FORCED) {\r
+ sb.append(" (forced)");\r
+ }\r
+ sb.append(" ");\r
+ sb.append(ru.getOldObjectId() == null ? "" : ru.getOldObjectId().abbreviate(7).name());\r
+ sb.append("..");\r
+ sb.append(ru.getNewObjectId() == null ? "" : ru.getNewObjectId().abbreviate(7).name());\r
+ logger.info(sb.toString());\r
+ }\r
+ }\r
+ } catch (Exception e) {\r
+ logger.error("Error updating mirror " + repositoryName, e);\r
+ } finally {\r
+ // cleanup\r
+ if (repository != null) {\r
+ repository.close();\r
+ }\r
+ }\r
+ }\r
+\r
+ running.set(false);\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.servlet;\r
+\r
+import java.io.IOException;\r
+import java.text.MessageFormat;\r
+\r
+import javax.servlet.FilterChain;\r
+import javax.servlet.ServletException;\r
+import javax.servlet.ServletRequest;\r
+import javax.servlet.ServletResponse;\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpServletResponse;\r
+\r
+import com.gitblit.manager.IRepositoryManager;\r
+import com.gitblit.manager.IRuntimeManager;\r
+import com.gitblit.manager.ISessionManager;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.StringUtils;\r
+\r
+/**\r
+ * The AccessRestrictionFilter is an AuthenticationFilter that confirms that the\r
+ * requested repository can be accessed by the anonymous or named user.\r
+ *\r
+ * The filter extracts the name of the repository from the url and determines if\r
+ * the requested action for the repository requires a Basic authentication\r
+ * prompt. If authentication is required and no credentials are stored in the\r
+ * "Authorization" header, then a basic authentication challenge is issued.\r
+ *\r
+ * http://en.wikipedia.org/wiki/Basic_access_authentication\r
+ *\r
+ * @author James Moger\r
+ *\r
+ */\r
+public abstract class AccessRestrictionFilter extends AuthenticationFilter {\r
+\r
+ protected final IRuntimeManager runtimeManager;\r
+\r
+ protected final IRepositoryManager repositoryManager;\r
+\r
+ protected AccessRestrictionFilter(\r
+ IRuntimeManager runtimeManager,\r
+ ISessionManager sessionManager,\r
+ IRepositoryManager repositoryManager) {\r
+ super(sessionManager);\r
+ this.runtimeManager = runtimeManager;\r
+ this.repositoryManager = repositoryManager;\r
+ }\r
+\r
+ /**\r
+ * Extract the repository name from the url.\r
+ *\r
+ * @param url\r
+ * @return repository name\r
+ */\r
+ protected abstract String extractRepositoryName(String url);\r
+\r
+ /**\r
+ * Analyze the url and returns the action of the request.\r
+ *\r
+ * @param url\r
+ * @return action of the request\r
+ */\r
+ protected abstract String getUrlRequestAction(String url);\r
+\r
+ /**\r
+ * Determine if a non-existing repository can be created using this filter.\r
+ *\r
+ * @return true if the filter allows repository creation\r
+ */\r
+ protected abstract boolean isCreationAllowed();\r
+\r
+ /**\r
+ * Determine if the action may be executed on the repository.\r
+ *\r
+ * @param repository\r
+ * @param action\r
+ * @return true if the action may be performed\r
+ */\r
+ protected abstract boolean isActionAllowed(RepositoryModel repository, String action);\r
+\r
+ /**\r
+ * Determine if the repository requires authentication.\r
+ *\r
+ * @param repository\r
+ * @param action\r
+ * @return true if authentication required\r
+ */\r
+ protected abstract boolean requiresAuthentication(RepositoryModel repository, String action);\r
+\r
+ /**\r
+ * Determine if the user can access the repository and perform the specified\r
+ * action.\r
+ *\r
+ * @param repository\r
+ * @param user\r
+ * @param action\r
+ * @return true if user may execute the action on the repository\r
+ */\r
+ protected abstract boolean canAccess(RepositoryModel repository, UserModel user, String action);\r
+\r
+ /**\r
+ * Allows a filter to create a repository, if one does not exist.\r
+ *\r
+ * @param user\r
+ * @param repository\r
+ * @param action\r
+ * @return the repository model, if it is created, null otherwise\r
+ */\r
+ protected RepositoryModel createRepository(UserModel user, String repository, String action) {\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * doFilter does the actual work of preprocessing the request to ensure that\r
+ * the user may proceed.\r
+ *\r
+ * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,\r
+ * javax.servlet.ServletResponse, javax.servlet.FilterChain)\r
+ */\r
+ @Override\r
+ public void doFilter(final ServletRequest request, final ServletResponse response,\r
+ final FilterChain chain) throws IOException, ServletException {\r
+\r
+ HttpServletRequest httpRequest = (HttpServletRequest) request;\r
+ HttpServletResponse httpResponse = (HttpServletResponse) response;\r
+\r
+ String fullUrl = getFullUrl(httpRequest);\r
+ String repository = extractRepositoryName(fullUrl);\r
+\r
+ if (repositoryManager.isCollectingGarbage(repository)) {\r
+ logger.info(MessageFormat.format("ARF: Rejecting request for {0}, busy collecting garbage!", repository));\r
+ httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);\r
+ return;\r
+ }\r
+\r
+ // Determine if the request URL is restricted\r
+ String fullSuffix = fullUrl.substring(repository.length());\r
+ String urlRequestType = getUrlRequestAction(fullSuffix);\r
+\r
+ UserModel user = getUser(httpRequest);\r
+\r
+ // Load the repository model\r
+ RepositoryModel model = repositoryManager.getRepositoryModel(repository);\r
+ if (model == null) {\r
+ if (isCreationAllowed()) {\r
+ if (user == null) {\r
+ // challenge client to provide credentials for creation. send 401.\r
+ if (runtimeManager.isDebugMode()) {\r
+ logger.info(MessageFormat.format("ARF: CREATE CHALLENGE {0}", fullUrl));\r
+ }\r
+ httpResponse.setHeader("WWW-Authenticate", CHALLENGE);\r
+ httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);\r
+ return;\r
+ } else {\r
+ // see if we can create a repository for this request\r
+ model = createRepository(user, repository, urlRequestType);\r
+ }\r
+ }\r
+\r
+ if (model == null) {\r
+ // repository not found. send 404.\r
+ logger.info(MessageFormat.format("ARF: {0} ({1})", fullUrl,\r
+ HttpServletResponse.SC_NOT_FOUND));\r
+ httpResponse.sendError(HttpServletResponse.SC_NOT_FOUND);\r
+ return;\r
+ }\r
+ }\r
+\r
+ // Confirm that the action may be executed on the repository\r
+ if (!isActionAllowed(model, urlRequestType)) {\r
+ logger.info(MessageFormat.format("ARF: action {0} on {1} forbidden ({2})",\r
+ urlRequestType, model, HttpServletResponse.SC_FORBIDDEN));\r
+ httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);\r
+ return;\r
+ }\r
+\r
+ // Wrap the HttpServletRequest with the AccessRestrictionRequest which\r
+ // overrides the servlet container user principal methods.\r
+ // JGit requires either:\r
+ //\r
+ // 1. servlet container authenticated user\r
+ // 2. http.receivepack = true in each repository's config\r
+ //\r
+ // Gitblit must conditionally authenticate users per-repository so just\r
+ // enabling http.receivepack is insufficient.\r
+ AuthenticatedRequest authenticatedRequest = new AuthenticatedRequest(httpRequest);\r
+ if (user != null) {\r
+ authenticatedRequest.setUser(user);\r
+ }\r
+\r
+ // BASIC authentication challenge and response processing\r
+ if (!StringUtils.isEmpty(urlRequestType) && requiresAuthentication(model, urlRequestType)) {\r
+ if (user == null) {\r
+ // challenge client to provide credentials. send 401.\r
+ if (runtimeManager.isDebugMode()) {\r
+ logger.info(MessageFormat.format("ARF: CHALLENGE {0}", fullUrl));\r
+ }\r
+ httpResponse.setHeader("WWW-Authenticate", CHALLENGE);\r
+ httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);\r
+ return;\r
+ } else {\r
+ // check user access for request\r
+ if (user.canAdmin() || canAccess(model, user, urlRequestType)) {\r
+ // authenticated request permitted.\r
+ // pass processing to the restricted servlet.\r
+ newSession(authenticatedRequest, httpResponse);\r
+ logger.info(MessageFormat.format("ARF: {0} ({1}) authenticated", fullUrl,\r
+ HttpServletResponse.SC_CONTINUE));\r
+ chain.doFilter(authenticatedRequest, httpResponse);\r
+ return;\r
+ }\r
+ // valid user, but not for requested access. send 403.\r
+ if (runtimeManager.isDebugMode()) {\r
+ logger.info(MessageFormat.format("ARF: {0} forbidden to access {1}",\r
+ user.username, fullUrl));\r
+ }\r
+ httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);\r
+ return;\r
+ }\r
+ }\r
+\r
+ if (runtimeManager.isDebugMode()) {\r
+ logger.info(MessageFormat.format("ARF: {0} ({1}) unauthenticated", fullUrl,\r
+ HttpServletResponse.SC_CONTINUE));\r
+ }\r
+ // unauthenticated request permitted.\r
+ // pass processing to the restricted servlet.\r
+ chain.doFilter(authenticatedRequest, httpResponse);\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.servlet;\r
+\r
+import java.io.IOException;\r
+import java.security.Principal;\r
+import java.util.Enumeration;\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+\r
+import javax.servlet.Filter;\r
+import javax.servlet.FilterChain;\r
+import javax.servlet.FilterConfig;\r
+import javax.servlet.ServletException;\r
+import javax.servlet.ServletRequest;\r
+import javax.servlet.ServletResponse;\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpServletRequestWrapper;\r
+import javax.servlet.http.HttpServletResponse;\r
+import javax.servlet.http.HttpSession;\r
+\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+import com.gitblit.Constants;\r
+import com.gitblit.manager.ISessionManager;\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.DeepCopier;\r
+import com.gitblit.utils.StringUtils;\r
+\r
+/**\r
+ * The AuthenticationFilter is a servlet filter that preprocesses requests that\r
+ * match its url pattern definition in the web.xml file.\r
+ *\r
+ * http://en.wikipedia.org/wiki/Basic_access_authentication\r
+ *\r
+ * @author James Moger\r
+ *\r
+ */\r
+public abstract class AuthenticationFilter implements Filter {\r
+\r
+ protected static final String CHALLENGE = "Basic realm=\"" + Constants.NAME + "\"";\r
+\r
+ protected static final String SESSION_SECURED = "com.gitblit.secured";\r
+\r
+ protected transient Logger logger = LoggerFactory.getLogger(getClass());\r
+\r
+ protected final ISessionManager sessionManager;\r
+\r
+ protected AuthenticationFilter(ISessionManager sessionManager) {\r
+ this.sessionManager = sessionManager;\r
+ }\r
+\r
+ /**\r
+ * doFilter does the actual work of preprocessing the request to ensure that\r
+ * the user may proceed.\r
+ *\r
+ * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,\r
+ * javax.servlet.ServletResponse, javax.servlet.FilterChain)\r
+ */\r
+ @Override\r
+ public abstract void doFilter(final ServletRequest request, final ServletResponse response,\r
+ final FilterChain chain) throws IOException, ServletException;\r
+\r
+ /**\r
+ * Allow the filter to require a client certificate to continue processing.\r
+ *\r
+ * @return true, if a client certificate is required\r
+ */\r
+ protected boolean requiresClientCertificate() {\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Returns the full relative url of the request.\r
+ *\r
+ * @param httpRequest\r
+ * @return url\r
+ */\r
+ protected String getFullUrl(HttpServletRequest httpRequest) {\r
+ String servletUrl = httpRequest.getContextPath() + httpRequest.getServletPath();\r
+ String url = httpRequest.getRequestURI().substring(servletUrl.length());\r
+ String params = httpRequest.getQueryString();\r
+ if (url.length() > 0 && url.charAt(0) == '/') {\r
+ url = url.substring(1);\r
+ }\r
+ String fullUrl = url + (StringUtils.isEmpty(params) ? "" : ("?" + params));\r
+ return fullUrl;\r
+ }\r
+\r
+ /**\r
+ * Returns the user making the request, if the user has authenticated.\r
+ *\r
+ * @param httpRequest\r
+ * @return user\r
+ */\r
+ protected UserModel getUser(HttpServletRequest httpRequest) {\r
+ UserModel user = sessionManager.authenticate(httpRequest, requiresClientCertificate());\r
+ return user;\r
+ }\r
+\r
+ /**\r
+ * Taken from Jetty's LoginAuthenticator.renewSessionOnAuthentication()\r
+ */\r
+ protected void newSession(HttpServletRequest request, HttpServletResponse response) {\r
+ HttpSession oldSession = request.getSession(false);\r
+ if (oldSession != null && oldSession.getAttribute(SESSION_SECURED) == null) {\r
+ synchronized (this) {\r
+ Map<String, Object> attributes = new HashMap<String, Object>();\r
+ Enumeration<String> e = oldSession.getAttributeNames();\r
+ while (e.hasMoreElements()) {\r
+ String name = e.nextElement();\r
+ attributes.put(name, oldSession.getAttribute(name));\r
+ oldSession.removeAttribute(name);\r
+ }\r
+ oldSession.invalidate();\r
+\r
+ HttpSession newSession = request.getSession(true);\r
+ newSession.setAttribute(SESSION_SECURED, Boolean.TRUE);\r
+ for (Map.Entry<String, Object> entry : attributes.entrySet()) {\r
+ newSession.setAttribute(entry.getKey(), entry.getValue());\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)\r
+ */\r
+ @Override\r
+ public void init(final FilterConfig config) throws ServletException {\r
+ }\r
+\r
+ /**\r
+ * @see javax.servlet.Filter#destroy()\r
+ */\r
+ @Override\r
+ public void destroy() {\r
+ }\r
+\r
+ /**\r
+ * Wraps a standard HttpServletRequest and overrides user principal methods.\r
+ */\r
+ public static class AuthenticatedRequest extends HttpServletRequestWrapper {\r
+\r
+ private UserModel user;\r
+\r
+ public AuthenticatedRequest(HttpServletRequest req) {\r
+ super(req);\r
+ user = DeepCopier.copy(UserModel.ANONYMOUS);\r
+ }\r
+\r
+ UserModel getUser() {\r
+ return user;\r
+ }\r
+\r
+ void setUser(UserModel user) {\r
+ this.user = user;\r
+ }\r
+\r
+ @Override\r
+ public String getRemoteUser() {\r
+ return user.username;\r
+ }\r
+\r
+ @Override\r
+ public boolean isUserInRole(String role) {\r
+ if (role.equals(Constants.ADMIN_ROLE)) {\r
+ return user.canAdmin();\r
+ }\r
+ // Gitblit does not currently use actual roles in the traditional\r
+ // servlet container sense. That is the reason this is marked\r
+ // deprecated, but I may want to revisit this.\r
+ return user.canAccessRepository(role);\r
+ }\r
+\r
+ @Override\r
+ public Principal getUserPrincipal() {\r
+ return user;\r
+ }\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\r
+ * Copyright 2013 gitblit.com.\r
+ * and other copyright owners as documented in the project's IP log.\r
+ *\r
+ * This program and the accompanying materials are made available\r
+ * under the terms of the Eclipse Distribution License v1.0 which\r
+ * accompanies this distribution, is reproduced below, and is\r
+ * available at http://www.eclipse.org/org/documents/edl-v10.php\r
+ *\r
+ * All rights reserved.\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.servlet;\r
+\r
+import java.awt.BasicStroke;\r
+import java.awt.Color;\r
+import java.awt.Graphics;\r
+import java.awt.Graphics2D;\r
+import java.awt.RenderingHints;\r
+import java.awt.Stroke;\r
+import java.awt.image.BufferedImage;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+import java.io.Serializable;\r
+import java.util.ArrayList;\r
+import java.util.LinkedList;\r
+import java.util.List;\r
+import java.util.Set;\r
+import java.util.TreeSet;\r
+\r
+import javax.imageio.ImageIO;\r
+import javax.inject.Inject;\r
+import javax.inject.Singleton;\r
+import javax.servlet.ServletException;\r
+import javax.servlet.http.HttpServlet;\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpServletResponse;\r
+\r
+import org.eclipse.jgit.lib.Ref;\r
+import org.eclipse.jgit.lib.Repository;\r
+import org.eclipse.jgit.revplot.AbstractPlotRenderer;\r
+import org.eclipse.jgit.revplot.PlotCommit;\r
+import org.eclipse.jgit.revplot.PlotCommitList;\r
+import org.eclipse.jgit.revplot.PlotLane;\r
+import org.eclipse.jgit.revplot.PlotWalk;\r
+import org.eclipse.jgit.revwalk.RevCommit;\r
+\r
+import com.gitblit.Constants;\r
+import com.gitblit.IStoredSettings;\r
+import com.gitblit.Keys;\r
+import com.gitblit.Keys.web;\r
+import com.gitblit.manager.IRepositoryManager;\r
+import com.gitblit.manager.IRuntimeManager;\r
+import com.gitblit.utils.JGitUtils;\r
+import com.gitblit.utils.StringUtils;\r
+\r
+/**\r
+ * Handles requests for branch graphs\r
+ *\r
+ * @author James Moger\r
+ *\r
+ */\r
+@Singleton\r
+public class BranchGraphServlet extends HttpServlet {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ private static final int LANE_WIDTH = 14;\r
+\r
+ // must match tr.commit css height\r
+ private static final int ROW_HEIGHT = 24;\r
+\r
+ private static final int RIGHT_PAD = 2;\r
+\r
+ private final Stroke[] strokeCache;\r
+\r
+ private final IStoredSettings settings;\r
+\r
+ private final IRepositoryManager repositoryManager;\r
+\r
+ @Inject\r
+ public BranchGraphServlet(\r
+ IRuntimeManager runtimeManager,\r
+ IRepositoryManager repositoryManager) {\r
+\r
+ super();\r
+ this.settings = runtimeManager.getSettings();\r
+ this.repositoryManager = repositoryManager;\r
+\r
+ strokeCache = new Stroke[4];\r
+ for (int i = 1; i < strokeCache.length; i++)\r
+ strokeCache[i] = new BasicStroke(i);\r
+ }\r
+\r
+ /**\r
+ * Returns an url to this servlet for the specified parameters.\r
+ *\r
+ * @param baseURL\r
+ * @param repository\r
+ * @param objectId\r
+ * @param numberCommits\r
+ * @return an url\r
+ */\r
+ public static String asLink(String baseURL, String repository, String objectId, int numberCommits) {\r
+ if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {\r
+ baseURL = baseURL.substring(0, baseURL.length() - 1);\r
+ }\r
+ return baseURL + Constants.BRANCH_GRAPH_PATH + "?r=" + repository\r
+ + (objectId == null ? "" : ("&h=" + objectId))\r
+ + (numberCommits > 0 ? ("&l=" + numberCommits) : "");\r
+ }\r
+\r
+ @Override\r
+ protected long getLastModified(HttpServletRequest req) {\r
+ String repository = req.getParameter("r");\r
+ String objectId = req.getParameter("h");\r
+ Repository r = null;\r
+ try {\r
+ r = repositoryManager.getRepository(repository);\r
+ if (StringUtils.isEmpty(objectId)) {\r
+ objectId = JGitUtils.getHEADRef(r);\r
+ }\r
+ RevCommit commit = JGitUtils.getCommit(r, objectId);\r
+ return JGitUtils.getCommitDate(commit).getTime();\r
+ } finally {\r
+ if (r != null) {\r
+ r.close();\r
+ }\r
+ }\r
+ }\r
+\r
+ @Override\r
+ protected void doGet(HttpServletRequest request, HttpServletResponse response)\r
+ throws ServletException, IOException {\r
+ InputStream is = null;\r
+ Repository r = null;\r
+ PlotWalk rw = null;\r
+ try {\r
+ String repository = request.getParameter("r");\r
+ String objectId = request.getParameter("h");\r
+ String length = request.getParameter("l");\r
+\r
+ r = repositoryManager.getRepository(repository);\r
+\r
+ rw = new PlotWalk(r);\r
+ if (StringUtils.isEmpty(objectId)) {\r
+ objectId = JGitUtils.getHEADRef(r);\r
+ }\r
+\r
+ rw.markStart(rw.lookupCommit(r.resolve(objectId)));\r
+\r
+ // default to the items-per-page setting, unless specified\r
+ int maxCommits = settings.getInteger(Keys.web.itemsPerPage, 50);\r
+ int requestedCommits = maxCommits;\r
+ if (!StringUtils.isEmpty(length)) {\r
+ int l = Integer.parseInt(length);\r
+ if (l > 0) {\r
+ requestedCommits = l;\r
+ }\r
+ }\r
+\r
+ // fetch the requested commits plus some extra so that the last\r
+ // commit displayed *likely* has correct lane assignments\r
+ CommitList commitList = new CommitList();\r
+ commitList.source(rw);\r
+ commitList.fillTo(2*Math.max(requestedCommits, maxCommits));\r
+\r
+ // determine the appropriate width for the image\r
+ int numLanes = 1;\r
+ int numCommits = Math.min(requestedCommits, commitList.size());\r
+ if (numCommits > 1) {\r
+ // determine graph width\r
+ Set<String> parents = new TreeSet<String>();\r
+ for (int i = 0; i < commitList.size(); i++) {\r
+ PlotCommit<Lane> commit = commitList.get(i);\r
+ boolean checkLane = false;\r
+\r
+ if (i < numCommits) {\r
+ // commit in visible list\r
+ checkLane = true;\r
+\r
+ // remember parents\r
+ for (RevCommit p : commit.getParents()) {\r
+ parents.add(p.getName());\r
+ }\r
+ } else if (parents.contains(commit.getName())) {\r
+ // commit outside visible list, but it is a parent of a\r
+ // commit in the visible list so we need to know it's lane\r
+ // assignment\r
+ checkLane = true;\r
+ }\r
+\r
+ if (checkLane) {\r
+ int pos = commit.getLane().getPosition();\r
+ numLanes = Math.max(numLanes, pos + 1);\r
+ }\r
+ }\r
+ }\r
+\r
+ int graphWidth = numLanes * LANE_WIDTH + RIGHT_PAD;\r
+ int rowHeight = ROW_HEIGHT;\r
+\r
+ // create an image buffer and render the lanes\r
+ BufferedImage image = new BufferedImage(graphWidth, rowHeight*numCommits, BufferedImage.TYPE_INT_ARGB);\r
+\r
+ Graphics2D g = null;\r
+ try {\r
+ g = image.createGraphics();\r
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);\r
+ LanesRenderer renderer = new LanesRenderer();\r
+ for (int i = 0; i < commitList.size(); i++) {\r
+ PlotCommit<Lane> commit = commitList.get(i);\r
+ Graphics row = g.create(0, i*rowHeight, graphWidth, rowHeight);\r
+ try {\r
+ renderer.paint(row, commit, rowHeight, graphWidth);\r
+ } finally {\r
+ row.dispose();\r
+ row = null;\r
+ }\r
+ }\r
+ } finally {\r
+ if (g != null) {\r
+ g.dispose();\r
+ g = null;\r
+ }\r
+ }\r
+\r
+ // write the image buffer to the client\r
+ response.setContentType("image/png");\r
+ if (numCommits > 1) {\r
+ response.setHeader("Cache-Control", "public, max-age=60, must-revalidate");\r
+ response.setDateHeader("Last-Modified", JGitUtils.getCommitDate(commitList.get(0)).getTime());\r
+ }\r
+ OutputStream os = response.getOutputStream();\r
+ ImageIO.write(image, "png", os);\r
+ os.flush();\r
+ image.flush();\r
+ image = null;\r
+ } catch (Exception e) {\r
+ e.printStackTrace();\r
+ } finally {\r
+ if (is != null) {\r
+ is.close();\r
+ is = null;\r
+ }\r
+ if (rw != null) {\r
+ rw.dispose();\r
+ rw = null;\r
+ }\r
+ if (r != null) {\r
+ r.close();\r
+ r = null;\r
+ }\r
+ }\r
+ }\r
+\r
+ private Stroke stroke(final int width) {\r
+ if (width < strokeCache.length)\r
+ return strokeCache[width];\r
+ return new BasicStroke(width);\r
+ }\r
+\r
+ static class CommitList extends PlotCommitList<Lane> {\r
+ final List<Color> laneColors;\r
+ final LinkedList<Color> colors;\r
+\r
+ CommitList() {\r
+ laneColors = new ArrayList<Color>();\r
+ laneColors.add(new Color(133, 166, 214));\r
+ laneColors.add(new Color(221, 205, 93));\r
+ laneColors.add(new Color(199, 134, 57));\r
+ laneColors.add(new Color(131, 150, 98));\r
+ laneColors.add(new Color(197, 123, 127));\r
+ laneColors.add(new Color(139, 136, 140));\r
+ laneColors.add(new Color(48, 135, 144));\r
+ laneColors.add(new Color(190, 93, 66));\r
+ laneColors.add(new Color(143, 163, 54));\r
+ laneColors.add(new Color(180, 148, 74));\r
+ laneColors.add(new Color(101, 101, 217));\r
+ laneColors.add(new Color(72, 153, 119));\r
+ laneColors.add(new Color(23, 101, 160));\r
+ laneColors.add(new Color(132, 164, 118));\r
+ laneColors.add(new Color(255, 230, 59));\r
+ laneColors.add(new Color(136, 176, 70));\r
+ laneColors.add(new Color(255, 138, 1));\r
+ laneColors.add(new Color(123, 187, 95));\r
+ laneColors.add(new Color(233, 88, 98));\r
+ laneColors.add(new Color(93, 158, 254));\r
+ laneColors.add(new Color(175, 215, 0));\r
+ laneColors.add(new Color(140, 134, 142));\r
+ laneColors.add(new Color(232, 168, 21));\r
+ laneColors.add(new Color(0, 172, 191));\r
+ laneColors.add(new Color(251, 58, 4));\r
+ laneColors.add(new Color(63, 64, 255));\r
+ laneColors.add(new Color(27, 194, 130));\r
+ laneColors.add(new Color(0, 104, 183));\r
+\r
+ colors = new LinkedList<Color>();\r
+ repackColors();\r
+ }\r
+\r
+ private void repackColors() {\r
+ colors.addAll(laneColors);\r
+ }\r
+\r
+ @Override\r
+ protected Lane createLane() {\r
+ final Lane lane = new Lane();\r
+ if (colors.isEmpty())\r
+ repackColors();\r
+ lane.color = colors.removeFirst();\r
+ return lane;\r
+ }\r
+\r
+ @Override\r
+ protected void recycleLane(final Lane lane) {\r
+ colors.add(lane.color);\r
+ }\r
+ }\r
+\r
+ static class Lane extends PlotLane {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ Color color;\r
+\r
+ @Override\r
+ public boolean equals(Object o) {\r
+ return super.equals(o) && color.equals(((Lane)o).color);\r
+ }\r
+\r
+ @Override\r
+ public int hashCode() {\r
+ return super.hashCode() ^ color.hashCode();\r
+ }\r
+ }\r
+\r
+ class LanesRenderer extends AbstractPlotRenderer<Lane, Color> implements Serializable {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ final Color commitDotFill = new Color(220, 220, 220);\r
+\r
+ final Color commitDotOutline = new Color(110, 110, 110);\r
+\r
+ transient Graphics2D g;\r
+\r
+ void paint(Graphics in, PlotCommit<Lane> commit, int h, int w) {\r
+ g = (Graphics2D) in.create();\r
+ try {\r
+ if (commit != null)\r
+ paintCommit(commit, h);\r
+ } finally {\r
+ g.dispose();\r
+ g = null;\r
+ }\r
+ }\r
+\r
+ @Override\r
+ protected void drawLine(Color color, int x1, int y1, int x2, int y2, int width) {\r
+ if (y1 == y2) {\r
+ x1 -= width / 2;\r
+ x2 -= width / 2;\r
+ } else if (x1 == x2) {\r
+ y1 -= width / 2;\r
+ y2 -= width / 2;\r
+ }\r
+\r
+ g.setColor(color);\r
+ g.setStroke(stroke(width));\r
+ g.drawLine(x1, y1, x2, y2);\r
+ }\r
+\r
+ @Override\r
+ protected void drawCommitDot(int x, int y, int w, int h) {\r
+ g.setColor(commitDotFill);\r
+ g.setStroke(strokeCache[2]);\r
+ g.fillOval(x + 2, y + 1, w - 2, h - 2);\r
+ g.setColor(commitDotOutline);\r
+ g.drawOval(x + 2, y + 1, w - 2, h - 2);\r
+ }\r
+\r
+ @Override\r
+ protected void drawBoundaryDot(int x, int y, int w, int h) {\r
+ drawCommitDot(x, y, w, h);\r
+ }\r
+\r
+ @Override\r
+ protected void drawText(String msg, int x, int y) {\r
+ }\r
+\r
+ @Override\r
+ protected Color laneColor(Lane myLane) {\r
+ return myLane != null ? myLane.color : Color.black;\r
+ }\r
+\r
+ @Override\r
+ protected int drawLabel(int x, int y, Ref ref) {\r
+ return 0;\r
+ }\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.servlet;\r
+\r
+import javax.inject.Inject;\r
+import javax.inject.Singleton;\r
+\r
+import com.gitblit.Constants.AccessRestrictionType;\r
+import com.gitblit.manager.IRepositoryManager;\r
+import com.gitblit.manager.IRuntimeManager;\r
+import com.gitblit.manager.ISessionManager;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.UserModel;\r
+\r
+/**\r
+ * The DownloadZipFilter is an AccessRestrictionFilter which ensures that zip\r
+ * requests for view-restricted repositories have proper authentication\r
+ * credentials and are authorized.\r
+ *\r
+ * @author James Moger\r
+ *\r
+ */\r
+@Singleton\r
+public class DownloadZipFilter extends AccessRestrictionFilter {\r
+\r
+ @Inject\r
+ public DownloadZipFilter(\r
+ IRuntimeManager runtimeManager,\r
+ ISessionManager sessionManager,\r
+ IRepositoryManager repositoryManager) {\r
+\r
+ super(runtimeManager, sessionManager, repositoryManager);\r
+ }\r
+\r
+ /**\r
+ * Extract the repository name from the url.\r
+ *\r
+ * @param url\r
+ * @return repository name\r
+ */\r
+ @Override\r
+ protected String extractRepositoryName(String url) {\r
+ int a = url.indexOf("r=");\r
+ String repository = url.substring(a + 2);\r
+ if (repository.indexOf('&') > -1) {\r
+ repository = repository.substring(0, repository.indexOf('&'));\r
+ }\r
+ return repository;\r
+ }\r
+\r
+ /**\r
+ * Analyze the url and returns the action of the request.\r
+ *\r
+ * @param url\r
+ * @return action of the request\r
+ */\r
+ @Override\r
+ protected String getUrlRequestAction(String url) {\r
+ return "DOWNLOAD";\r
+ }\r
+\r
+ /**\r
+ * Determine if a non-existing repository can be created using this filter.\r
+ *\r
+ * @return true if the filter allows repository creation\r
+ */\r
+ @Override\r
+ protected boolean isCreationAllowed() {\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Determine if the action may be executed on the repository.\r
+ *\r
+ * @param repository\r
+ * @param action\r
+ * @return true if the action may be performed\r
+ */\r
+ @Override\r
+ protected boolean isActionAllowed(RepositoryModel repository, String action) {\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Determine if the repository requires authentication.\r
+ *\r
+ * @param repository\r
+ * @param action\r
+ * @return true if authentication required\r
+ */\r
+ @Override\r
+ protected boolean requiresAuthentication(RepositoryModel repository, String action) {\r
+ return repository.accessRestriction.atLeast(AccessRestrictionType.VIEW);\r
+ }\r
+\r
+ /**\r
+ * Determine if the user can access the repository and perform the specified\r
+ * action.\r
+ *\r
+ * @param repository\r
+ * @param user\r
+ * @param action\r
+ * @return true if user may execute the action on the repository\r
+ */\r
+ @Override\r
+ protected boolean canAccess(RepositoryModel repository, UserModel user, String action) {\r
+ return user.canView(repository);\r
+ }\r
+\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.servlet;\r
+\r
+import java.io.IOException;\r
+import java.text.MessageFormat;\r
+import java.text.ParseException;\r
+import java.util.Date;\r
+\r
+import javax.inject.Inject;\r
+import javax.inject.Singleton;\r
+import javax.servlet.ServletException;\r
+import javax.servlet.http.HttpServlet;\r
+import javax.servlet.http.HttpServletResponse;\r
+\r
+import org.eclipse.jgit.lib.Repository;\r
+import org.eclipse.jgit.revwalk.RevCommit;\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+import com.gitblit.Constants;\r
+import com.gitblit.IStoredSettings;\r
+import com.gitblit.Keys;\r
+import com.gitblit.Keys.web;\r
+import com.gitblit.manager.IRepositoryManager;\r
+import com.gitblit.manager.IRuntimeManager;\r
+import com.gitblit.utils.CompressionUtils;\r
+import com.gitblit.utils.JGitUtils;\r
+import com.gitblit.utils.MarkdownUtils;\r
+import com.gitblit.utils.StringUtils;\r
+\r
+/**\r
+ * Streams out a zip file from the specified repository for any tree path at any\r
+ * revision.\r
+ *\r
+ * @author James Moger\r
+ *\r
+ */\r
+@Singleton\r
+public class DownloadZipServlet extends HttpServlet {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ private transient Logger logger = LoggerFactory.getLogger(DownloadZipServlet.class);\r
+\r
+ private final IStoredSettings settings;\r
+\r
+ private final IRepositoryManager repositoryManager;\r
+\r
+ public static enum Format {\r
+ zip(".zip"), tar(".tar"), gz(".tar.gz"), xz(".tar.xz"), bzip2(".tar.bzip2");\r
+\r
+ public final String extension;\r
+\r
+ Format(String ext) {\r
+ this.extension = ext;\r
+ }\r
+\r
+ public static Format fromName(String name) {\r
+ for (Format format : values()) {\r
+ if (format.name().equalsIgnoreCase(name)) {\r
+ return format;\r
+ }\r
+ }\r
+ return zip;\r
+ }\r
+ }\r
+\r
+ @Inject\r
+ public DownloadZipServlet(\r
+ IRuntimeManager runtimeManager,\r
+ IRepositoryManager repositoryManager) {\r
+\r
+ super();\r
+ this.settings = runtimeManager.getSettings();\r
+ this.repositoryManager = repositoryManager;\r
+ }\r
+\r
+ /**\r
+ * Returns an url to this servlet for the specified parameters.\r
+ *\r
+ * @param baseURL\r
+ * @param repository\r
+ * @param objectId\r
+ * @param path\r
+ * @param format\r
+ * @return an url\r
+ */\r
+ public static String asLink(String baseURL, String repository, String objectId, String path, Format format) {\r
+ if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {\r
+ baseURL = baseURL.substring(0, baseURL.length() - 1);\r
+ }\r
+ return baseURL + Constants.ZIP_PATH + "?r=" + repository\r
+ + (path == null ? "" : ("&p=" + path))\r
+ + (objectId == null ? "" : ("&h=" + objectId))\r
+ + (format == null ? "" : ("&format=" + format.name()));\r
+ }\r
+\r
+ /**\r
+ * Creates a zip stream from the repository of the requested data.\r
+ *\r
+ * @param request\r
+ * @param response\r
+ * @throws javax.servlet.ServletException\r
+ * @throws java.io.IOException\r
+ */\r
+ private void processRequest(javax.servlet.http.HttpServletRequest request,\r
+ javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,\r
+ java.io.IOException {\r
+ if (!settings.getBoolean(Keys.web.allowZipDownloads, true)) {\r
+ logger.warn("Zip downloads are disabled");\r
+ response.sendError(HttpServletResponse.SC_FORBIDDEN);\r
+ return;\r
+ }\r
+\r
+ Format format = Format.zip;\r
+ String repository = request.getParameter("r");\r
+ String basePath = request.getParameter("p");\r
+ String objectId = request.getParameter("h");\r
+ String f = request.getParameter("format");\r
+ if (!StringUtils.isEmpty(f)) {\r
+ format = Format.fromName(f);\r
+ }\r
+\r
+ try {\r
+ String name = repository;\r
+ if (name.indexOf('/') > -1) {\r
+ name = name.substring(name.lastIndexOf('/') + 1);\r
+ }\r
+ name = StringUtils.stripDotGit(name);\r
+\r
+ if (!StringUtils.isEmpty(basePath)) {\r
+ name += "-" + basePath.replace('/', '_');\r
+ }\r
+ if (!StringUtils.isEmpty(objectId)) {\r
+ name += "-" + objectId;\r
+ }\r
+\r
+ Repository r = repositoryManager.getRepository(repository);\r
+ if (r == null) {\r
+ if (repositoryManager.isCollectingGarbage(repository)) {\r
+ error(response, MessageFormat.format("# Error\nGitblit is busy collecting garbage in {0}", repository));\r
+ return;\r
+ } else {\r
+ error(response, MessageFormat.format("# Error\nFailed to find repository {0}", repository));\r
+ return;\r
+ }\r
+ }\r
+ RevCommit commit = JGitUtils.getCommit(r, objectId);\r
+ if (commit == null) {\r
+ error(response, MessageFormat.format("# Error\nFailed to find commit {0}", objectId));\r
+ r.close();\r
+ return;\r
+ }\r
+ Date date = JGitUtils.getCommitDate(commit);\r
+\r
+ String contentType = "application/octet-stream";\r
+ response.setContentType(contentType + "; charset=" + response.getCharacterEncoding());\r
+ response.setHeader("Content-Disposition", "attachment; filename=\"" + name + format.extension + "\"");\r
+ response.setDateHeader("Last-Modified", date.getTime());\r
+ response.setHeader("Cache-Control", "no-cache");\r
+ response.setHeader("Pragma", "no-cache");\r
+ response.setDateHeader("Expires", 0);\r
+\r
+ try {\r
+ switch (format) {\r
+ case zip:\r
+ CompressionUtils.zip(r, basePath, objectId, response.getOutputStream());\r
+ break;\r
+ case tar:\r
+ CompressionUtils.tar(r, basePath, objectId, response.getOutputStream());\r
+ break;\r
+ case gz:\r
+ CompressionUtils.gz(r, basePath, objectId, response.getOutputStream());\r
+ break;\r
+ case xz:\r
+ CompressionUtils.xz(r, basePath, objectId, response.getOutputStream());\r
+ break;\r
+ case bzip2:\r
+ CompressionUtils.bzip2(r, basePath, objectId, response.getOutputStream());\r
+ break;\r
+ }\r
+\r
+ response.flushBuffer();\r
+ } catch (IOException t) {\r
+ String message = t.getMessage() == null ? "" : t.getMessage().toLowerCase();\r
+ if (message.contains("reset") || message.contains("broken pipe")) {\r
+ logger.error("Client aborted zip download: " + message);\r
+ } else {\r
+ logger.error("Failed to write attachment to client", t);\r
+ }\r
+ } catch (Throwable t) {\r
+ logger.error("Failed to write attachment to client", t);\r
+ }\r
+\r
+ // close the repository\r
+ r.close();\r
+ } catch (Throwable t) {\r
+ logger.error("Failed to write attachment to client", t);\r
+ }\r
+ }\r
+\r
+ private void error(HttpServletResponse response, String mkd) throws ServletException,\r
+ IOException, ParseException {\r
+ String content = MarkdownUtils.transformMarkdown(mkd);\r
+ response.setContentType("text/html; charset=" + Constants.ENCODING);\r
+ response.getWriter().write(content);\r
+ }\r
+\r
+ @Override\r
+ protected void doPost(javax.servlet.http.HttpServletRequest request,\r
+ javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,\r
+ java.io.IOException {\r
+ processRequest(request, response);\r
+ }\r
+\r
+ @Override\r
+ protected void doGet(javax.servlet.http.HttpServletRequest request,\r
+ javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,\r
+ java.io.IOException {\r
+ processRequest(request, response);\r
+ }\r
+}\r
--- /dev/null
+/*
+ * Copyright 2013 Laurens Vrijnsen
+ * Copyright 2013 gitblit.com.
+ *
+ * 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.gitblit.servlet;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+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;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.gitblit.IStoredSettings;
+import com.gitblit.Keys;
+import com.gitblit.Keys.web;
+import com.gitblit.manager.IRuntimeManager;
+import com.gitblit.manager.ISessionManager;
+import com.gitblit.models.UserModel;
+
+/**
+ * This filter enforces authentication via HTTP Basic Authentication, if the settings indicate so.
+ * It looks at the settings "web.authenticateViewPages" and "web.enforceHttpBasicAuthentication"; if
+ * both are true, any unauthorized access will be met with a HTTP Basic Authentication header.
+ *
+ * @author Laurens Vrijnsen
+ *
+ */
+@Singleton
+public class EnforceAuthenticationFilter implements Filter {
+
+ protected transient Logger logger = LoggerFactory.getLogger(getClass());
+
+ private final IStoredSettings settings;
+
+ private final ISessionManager sessionManager;
+
+ @Inject
+ public EnforceAuthenticationFilter(
+ IRuntimeManager runtimeManager,
+ ISessionManager sessionManager) {
+
+ super();
+ this.settings = runtimeManager.getSettings();
+ this.sessionManager = sessionManager;
+ }
+
+ /*
+ * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
+ */
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ /*
+ * This does the actual filtering: is the user authenticated? If not, enforce HTTP authentication (401)
+ *
+ * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
+ */
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+
+ Boolean mustForceAuth = settings.getBoolean(Keys.web.authenticateViewPages, false)
+ && settings.getBoolean(Keys.web.enforceHttpBasicAuthentication, false);
+
+ HttpServletRequest httpRequest = (HttpServletRequest) request;
+ HttpServletResponse httpResponse = (HttpServletResponse) response;
+ UserModel user = sessionManager.authenticate(httpRequest);
+
+ if (mustForceAuth && (user == null)) {
+ // not authenticated, enforce now:
+ logger.debug(MessageFormat.format("EnforceAuthFilter: user not authenticated for URL {0}!", request.toString()));
+ String challenge = MessageFormat.format("Basic realm=\"{0}\"", settings.getString(Keys.web.siteName, ""));
+ httpResponse.setHeader("WWW-Authenticate", challenge);
+ httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+ return;
+
+ } else {
+ // user is authenticated, or don't care, continue handling
+ chain.doFilter(request, response);
+ }
+ }
+
+
+ /*
+ * @see javax.servlet.Filter#destroy()
+ */
+ @Override
+ public void destroy() {
+ }
+}
--- /dev/null
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.servlet;\r
+\r
+import java.io.File;\r
+import java.text.MessageFormat;\r
+import java.util.ArrayList;\r
+import java.util.Date;\r
+import java.util.HashMap;\r
+import java.util.HashSet;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Set;\r
+\r
+import javax.inject.Inject;\r
+import javax.inject.Singleton;\r
+import javax.servlet.http.HttpServletResponse;\r
+\r
+import com.gitblit.Constants;\r
+import com.gitblit.IStoredSettings;\r
+import com.gitblit.Keys;\r
+import com.gitblit.Constants.FederationRequest;\r
+import com.gitblit.Keys.federation;\r
+import com.gitblit.Keys.git;\r
+import com.gitblit.Keys.groovy;\r
+import com.gitblit.manager.IFederationManager;\r
+import com.gitblit.manager.IRepositoryManager;\r
+import com.gitblit.manager.IRuntimeManager;\r
+import com.gitblit.manager.IUserManager;\r
+import com.gitblit.models.FederationModel;\r
+import com.gitblit.models.FederationProposal;\r
+import com.gitblit.models.TeamModel;\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.FederationUtils;\r
+import com.gitblit.utils.FileUtils;\r
+import com.gitblit.utils.HttpUtils;\r
+import com.gitblit.utils.StringUtils;\r
+import com.gitblit.utils.TimeUtils;\r
+\r
+/**\r
+ * Handles federation requests.\r
+ *\r
+ * @author James Moger\r
+ *\r
+ */\r
+@Singleton\r
+public class FederationServlet extends JsonServlet {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ private final IStoredSettings settings;\r
+\r
+ private final IUserManager userManager;\r
+\r
+ private final IRepositoryManager repositoryManager;\r
+\r
+ private final IFederationManager federationManager;\r
+\r
+ @Inject\r
+ public FederationServlet(\r
+ IRuntimeManager runtimeManager,\r
+ IUserManager userManager,\r
+ IRepositoryManager repositoryManager,\r
+ IFederationManager federationManager) {\r
+\r
+ super();\r
+ this.settings = runtimeManager.getSettings();\r
+ this.userManager = userManager;\r
+ this.repositoryManager = repositoryManager;\r
+ this.federationManager = federationManager;\r
+ }\r
+\r
+ /**\r
+ * Processes a federation request.\r
+ *\r
+ * @param request\r
+ * @param response\r
+ * @throws javax.servlet.ServletException\r
+ * @throws java.io.IOException\r
+ */\r
+\r
+ @Override\r
+ protected void processRequest(javax.servlet.http.HttpServletRequest request,\r
+ javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,\r
+ java.io.IOException {\r
+\r
+ FederationRequest reqType = FederationRequest.fromName(request.getParameter("req"));\r
+ logger.info(MessageFormat.format("Federation {0} request from {1}", reqType,\r
+ request.getRemoteAddr()));\r
+\r
+ if (FederationRequest.POKE.equals(reqType)) {\r
+ // Gitblit always responds to POKE requests to verify a connection\r
+ logger.info("Received federation POKE from " + request.getRemoteAddr());\r
+ return;\r
+ }\r
+\r
+ if (!settings.getBoolean(Keys.git.enableGitServlet, true)) {\r
+ logger.warn(Keys.git.enableGitServlet + " must be set TRUE for federation requests.");\r
+ response.sendError(HttpServletResponse.SC_FORBIDDEN);\r
+ return;\r
+ }\r
+\r
+ String uuid = settings.getString(Keys.federation.passphrase, "");\r
+ if (StringUtils.isEmpty(uuid)) {\r
+ logger.warn(Keys.federation.passphrase\r
+ + " is not properly set! Federation request denied.");\r
+ response.sendError(HttpServletResponse.SC_FORBIDDEN);\r
+ return;\r
+ }\r
+\r
+ if (FederationRequest.PROPOSAL.equals(reqType)) {\r
+ // Receive a gitblit federation proposal\r
+ FederationProposal proposal = deserialize(request, response, FederationProposal.class);\r
+ if (proposal == null) {\r
+ return;\r
+ }\r
+\r
+ // reject proposal, if not receipt prohibited\r
+ if (!settings.getBoolean(Keys.federation.allowProposals, false)) {\r
+ logger.error(MessageFormat.format("Rejected {0} federation proposal from {1}",\r
+ proposal.tokenType.name(), proposal.url));\r
+ response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);\r
+ return;\r
+ }\r
+\r
+ // poke the origin Gitblit instance that is proposing federation\r
+ boolean poked = false;\r
+ try {\r
+ poked = FederationUtils.poke(proposal.url);\r
+ } catch (Exception e) {\r
+ logger.error("Failed to poke origin", e);\r
+ }\r
+ if (!poked) {\r
+ logger.error(MessageFormat.format("Failed to send federation poke to {0}",\r
+ proposal.url));\r
+ response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);\r
+ return;\r
+ }\r
+\r
+ String url = HttpUtils.getGitblitURL(request);\r
+ federationManager.submitFederationProposal(proposal, url);\r
+ logger.info(MessageFormat.format(\r
+ "Submitted {0} federation proposal to pull {1} repositories from {2}",\r
+ proposal.tokenType.name(), proposal.repositories.size(), proposal.url));\r
+ response.setStatus(HttpServletResponse.SC_OK);\r
+ return;\r
+ }\r
+\r
+ if (FederationRequest.STATUS.equals(reqType)) {\r
+ // Receive a gitblit federation status acknowledgment\r
+ String remoteId = StringUtils.decodeFromHtml(request.getParameter("url"));\r
+ String identification = MessageFormat.format("{0} ({1})", remoteId,\r
+ request.getRemoteAddr());\r
+\r
+ // deserialize the status data\r
+ FederationModel results = deserialize(request, response, FederationModel.class);\r
+ if (results == null) {\r
+ return;\r
+ }\r
+\r
+ // setup the last and netx pull dates\r
+ results.lastPull = new Date();\r
+ int mins = TimeUtils.convertFrequencyToMinutes(results.frequency);\r
+ results.nextPull = new Date(System.currentTimeMillis() + (mins * 60 * 1000L));\r
+\r
+ // acknowledge the receipt of status\r
+ federationManager.acknowledgeFederationStatus(identification, results);\r
+ logger.info(MessageFormat.format(\r
+ "Received status of {0} federated repositories from {1}", results\r
+ .getStatusList().size(), identification));\r
+ response.setStatus(HttpServletResponse.SC_OK);\r
+ return;\r
+ }\r
+\r
+ // Determine the federation tokens for this gitblit instance\r
+ String token = request.getParameter("token");\r
+ List<String> tokens = federationManager.getFederationTokens();\r
+ if (!tokens.contains(token)) {\r
+ logger.warn(MessageFormat.format(\r
+ "Received Federation token ''{0}'' does not match the server tokens", token));\r
+ response.sendError(HttpServletResponse.SC_FORBIDDEN);\r
+ return;\r
+ }\r
+\r
+ Object result = null;\r
+ if (FederationRequest.PULL_REPOSITORIES.equals(reqType)) {\r
+ String gitblitUrl = HttpUtils.getGitblitURL(request);\r
+ result = federationManager.getRepositories(gitblitUrl, token);\r
+ } else {\r
+ if (FederationRequest.PULL_SETTINGS.equals(reqType)) {\r
+ // pull settings\r
+ if (!federationManager.validateFederationRequest(reqType, token)) {\r
+ // invalid token to pull users or settings\r
+ logger.warn(MessageFormat.format(\r
+ "Federation token from {0} not authorized to pull SETTINGS",\r
+ request.getRemoteAddr()));\r
+ response.sendError(HttpServletResponse.SC_FORBIDDEN);\r
+ return;\r
+ }\r
+ Map<String, String> map = new HashMap<String, String>();\r
+ List<String> keys = settings.getAllKeys(null);\r
+ for (String key : keys) {\r
+ map.put(key, settings.getString(key, ""));\r
+ }\r
+ result = map;\r
+ } else if (FederationRequest.PULL_USERS.equals(reqType)) {\r
+ // pull users\r
+ if (!federationManager.validateFederationRequest(reqType, token)) {\r
+ // invalid token to pull users or settings\r
+ logger.warn(MessageFormat.format(\r
+ "Federation token from {0} not authorized to pull USERS",\r
+ request.getRemoteAddr()));\r
+ response.sendError(HttpServletResponse.SC_FORBIDDEN);\r
+ return;\r
+ }\r
+ List<String> usernames = userManager.getAllUsernames();\r
+ List<UserModel> users = new ArrayList<UserModel>();\r
+ for (String username : usernames) {\r
+ UserModel user = userManager.getUserModel(username);\r
+ if (!user.excludeFromFederation) {\r
+ users.add(user);\r
+ }\r
+ }\r
+ result = users;\r
+ } else if (FederationRequest.PULL_TEAMS.equals(reqType)) {\r
+ // pull teams\r
+ if (!federationManager.validateFederationRequest(reqType, token)) {\r
+ // invalid token to pull teams\r
+ logger.warn(MessageFormat.format(\r
+ "Federation token from {0} not authorized to pull TEAMS",\r
+ request.getRemoteAddr()));\r
+ response.sendError(HttpServletResponse.SC_FORBIDDEN);\r
+ return;\r
+ }\r
+ List<String> teamnames = userManager.getAllTeamNames();\r
+ List<TeamModel> teams = new ArrayList<TeamModel>();\r
+ for (String teamname : teamnames) {\r
+ TeamModel user = userManager.getTeamModel(teamname);\r
+ teams.add(user);\r
+ }\r
+ result = teams;\r
+ } else if (FederationRequest.PULL_SCRIPTS.equals(reqType)) {\r
+ // pull scripts\r
+ if (!federationManager.validateFederationRequest(reqType, token)) {\r
+ // invalid token to pull script\r
+ logger.warn(MessageFormat.format(\r
+ "Federation token from {0} not authorized to pull SCRIPTS",\r
+ request.getRemoteAddr()));\r
+ response.sendError(HttpServletResponse.SC_FORBIDDEN);\r
+ return;\r
+ }\r
+ Map<String, String> scripts = new HashMap<String, String>();\r
+\r
+ Set<String> names = new HashSet<String>();\r
+ names.addAll(settings.getStrings(Keys.groovy.preReceiveScripts));\r
+ names.addAll(settings.getStrings(Keys.groovy.postReceiveScripts));\r
+ for (TeamModel team : userManager.getAllTeams()) {\r
+ names.addAll(team.preReceiveScripts);\r
+ names.addAll(team.postReceiveScripts);\r
+ }\r
+ File scriptsFolder = repositoryManager.getHooksFolder();\r
+ for (String name : names) {\r
+ File file = new File(scriptsFolder, name);\r
+ if (!file.exists() && !file.getName().endsWith(".groovy")) {\r
+ file = new File(scriptsFolder, name + ".groovy");\r
+ }\r
+ if (file.exists()) {\r
+ // read the script\r
+ String content = FileUtils.readContent(file, "\n");\r
+ scripts.put(name, content);\r
+ } else {\r
+ // missing script?!\r
+ logger.warn(MessageFormat.format("Failed to find push script \"{0}\"", name));\r
+ }\r
+ }\r
+ result = scripts;\r
+ }\r
+ }\r
+\r
+ // send the result of the request\r
+ serialize(response, result);\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.servlet;\r
+\r
+import java.text.MessageFormat;\r
+\r
+import javax.inject.Inject;\r
+import javax.inject.Singleton;\r
+\r
+import com.gitblit.Constants;\r
+import com.gitblit.GitBlitException;\r
+import com.gitblit.IStoredSettings;\r
+import com.gitblit.Keys;\r
+import com.gitblit.Constants.AccessRestrictionType;\r
+import com.gitblit.Constants.AuthorizationControl;\r
+import com.gitblit.Keys.git;\r
+import com.gitblit.manager.IRepositoryManager;\r
+import com.gitblit.manager.IRuntimeManager;\r
+import com.gitblit.manager.ISessionManager;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.StringUtils;\r
+\r
+/**\r
+ * The GitFilter is an AccessRestrictionFilter which ensures that Git client\r
+ * requests for push, clone, or view restricted repositories are authenticated\r
+ * and authorized.\r
+ *\r
+ * @author James Moger\r
+ *\r
+ */\r
+@Singleton\r
+public class GitFilter extends AccessRestrictionFilter {\r
+\r
+ protected static final String gitReceivePack = "/git-receive-pack";\r
+\r
+ protected static final String gitUploadPack = "/git-upload-pack";\r
+\r
+ protected static final String[] suffixes = { gitReceivePack, gitUploadPack, "/info/refs", "/HEAD",\r
+ "/objects" };\r
+\r
+ private final IStoredSettings settings;\r
+\r
+ @Inject\r
+ public GitFilter(\r
+ IRuntimeManager runtimeManager,\r
+ ISessionManager sessionManager,\r
+ IRepositoryManager repositoryManager) {\r
+\r
+ super(runtimeManager, sessionManager, repositoryManager);\r
+ this.settings = runtimeManager.getSettings();\r
+ }\r
+\r
+ /**\r
+ * Extract the repository name from the url.\r
+ *\r
+ * @param cloneUrl\r
+ * @return repository name\r
+ */\r
+ public static String getRepositoryName(String value) {\r
+ String repository = value;\r
+ // get the repository name from the url by finding a known url suffix\r
+ for (String urlSuffix : suffixes) {\r
+ if (repository.indexOf(urlSuffix) > -1) {\r
+ repository = repository.substring(0, repository.indexOf(urlSuffix));\r
+ }\r
+ }\r
+ return repository;\r
+ }\r
+\r
+ /**\r
+ * Extract the repository name from the url.\r
+ *\r
+ * @param url\r
+ * @return repository name\r
+ */\r
+ @Override\r
+ protected String extractRepositoryName(String url) {\r
+ return GitFilter.getRepositoryName(url);\r
+ }\r
+\r
+ /**\r
+ * Analyze the url and returns the action of the request. Return values are\r
+ * either "/git-receive-pack" or "/git-upload-pack".\r
+ *\r
+ * @param serverUrl\r
+ * @return action of the request\r
+ */\r
+ @Override\r
+ protected String getUrlRequestAction(String suffix) {\r
+ if (!StringUtils.isEmpty(suffix)) {\r
+ if (suffix.startsWith(gitReceivePack)) {\r
+ return gitReceivePack;\r
+ } else if (suffix.startsWith(gitUploadPack)) {\r
+ return gitUploadPack;\r
+ } else if (suffix.contains("?service=git-receive-pack")) {\r
+ return gitReceivePack;\r
+ } else if (suffix.contains("?service=git-upload-pack")) {\r
+ return gitUploadPack;\r
+ } else {\r
+ return gitUploadPack;\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Determine if a non-existing repository can be created using this filter.\r
+ *\r
+ * @return true if the server allows repository creation on-push\r
+ */\r
+ @Override\r
+ protected boolean isCreationAllowed() {\r
+ return settings.getBoolean(Keys.git.allowCreateOnPush, true);\r
+ }\r
+\r
+ /**\r
+ * Determine if the repository can receive pushes.\r
+ *\r
+ * @param repository\r
+ * @param action\r
+ * @return true if the action may be performed\r
+ */\r
+ @Override\r
+ protected boolean isActionAllowed(RepositoryModel repository, String action) {\r
+ // the log here has been moved into ReceiveHook to provide clients with\r
+ // error messages\r
+ return true;\r
+ }\r
+\r
+ @Override\r
+ protected boolean requiresClientCertificate() {\r
+ return settings.getBoolean(Keys.git.requiresClientCertificate, false);\r
+ }\r
+\r
+ /**\r
+ * Determine if the repository requires authentication.\r
+ *\r
+ * @param repository\r
+ * @param action\r
+ * @return true if authentication required\r
+ */\r
+ @Override\r
+ protected boolean requiresAuthentication(RepositoryModel repository, String action) {\r
+ if (gitUploadPack.equals(action)) {\r
+ // send to client\r
+ return repository.accessRestriction.atLeast(AccessRestrictionType.CLONE);\r
+ } else if (gitReceivePack.equals(action)) {\r
+ // receive from client\r
+ return repository.accessRestriction.atLeast(AccessRestrictionType.PUSH);\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Determine if the user can access the repository and perform the specified\r
+ * action.\r
+ *\r
+ * @param repository\r
+ * @param user\r
+ * @param action\r
+ * @return true if user may execute the action on the repository\r
+ */\r
+ @Override\r
+ protected boolean canAccess(RepositoryModel repository, UserModel user, String action) {\r
+ if (!settings.getBoolean(Keys.git.enableGitServlet, true)) {\r
+ // Git Servlet disabled\r
+ return false;\r
+ }\r
+ if (action.equals(gitReceivePack)) {\r
+ // Push request\r
+ if (user.canPush(repository)) {\r
+ return true;\r
+ } else {\r
+ // user is unauthorized to push to this repository\r
+ logger.warn(MessageFormat.format("user {0} is not authorized to push to {1}",\r
+ user.username, repository));\r
+ return false;\r
+ }\r
+ } else if (action.equals(gitUploadPack)) {\r
+ // Clone request\r
+ if (user.canClone(repository)) {\r
+ return true;\r
+ } else {\r
+ // user is unauthorized to clone this repository\r
+ logger.warn(MessageFormat.format("user {0} is not authorized to clone {1}",\r
+ user.username, repository));\r
+ return false;\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * An authenticated user with the CREATE role can create a repository on\r
+ * push.\r
+ *\r
+ * @param user\r
+ * @param repository\r
+ * @param action\r
+ * @return the repository model, if it is created, null otherwise\r
+ */\r
+ @Override\r
+ protected RepositoryModel createRepository(UserModel user, String repository, String action) {\r
+ boolean isPush = !StringUtils.isEmpty(action) && gitReceivePack.equals(action);\r
+ if (isPush) {\r
+ if (user.canCreate(repository)) {\r
+ // user is pushing to a new repository\r
+ // validate name\r
+ if (repository.startsWith("../")) {\r
+ logger.error(MessageFormat.format("Illegal relative path in repository name! {0}", repository));\r
+ return null;\r
+ }\r
+ if (repository.contains("/../")) {\r
+ logger.error(MessageFormat.format("Illegal relative path in repository name! {0}", repository));\r
+ return null;\r
+ }\r
+\r
+ // confirm valid characters in repository name\r
+ Character c = StringUtils.findInvalidCharacter(repository);\r
+ if (c != null) {\r
+ logger.error(MessageFormat.format("Invalid character '{0}' in repository name {1}!", c, repository));\r
+ return null;\r
+ }\r
+\r
+ // create repository\r
+ RepositoryModel model = new RepositoryModel();\r
+ model.name = repository;\r
+ model.addOwner(user.username);\r
+ model.projectPath = StringUtils.getFirstPathElement(repository);\r
+ if (model.isUsersPersonalRepository(user.username)) {\r
+ // personal repository, default to private for user\r
+ model.authorizationControl = AuthorizationControl.NAMED;\r
+ model.accessRestriction = AccessRestrictionType.VIEW;\r
+ } else {\r
+ // common repository, user default server settings\r
+ model.authorizationControl = AuthorizationControl.fromName(settings.getString(Keys.git.defaultAuthorizationControl, ""));\r
+ model.accessRestriction = AccessRestrictionType.fromName(settings.getString(Keys.git.defaultAccessRestriction, "PUSH"));\r
+ }\r
+\r
+ // create the repository\r
+ try {\r
+ repositoryManager.updateRepositoryModel(model.name, model, true);\r
+ logger.info(MessageFormat.format("{0} created {1} ON-PUSH", user.username, model.name));\r
+ return repositoryManager.getRepositoryModel(model.name);\r
+ } catch (GitBlitException e) {\r
+ logger.error(MessageFormat.format("{0} failed to create repository {1} ON-PUSH!", user.username, model.name), e);\r
+ }\r
+ } else {\r
+ logger.warn(MessageFormat.format("{0} is not permitted to create repository {1} ON-PUSH!", user.username, repository));\r
+ }\r
+ }\r
+\r
+ // repository could not be created or action was not a push\r
+ return null;\r
+ }\r
+}\r
--- /dev/null
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * 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.gitblit.servlet;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.servlet.ServletContext;
+import javax.servlet.annotation.WebListener;
+
+import com.gitblit.Constants;
+import com.gitblit.DaggerModule;
+import com.gitblit.FileSettings;
+import com.gitblit.IStoredSettings;
+import com.gitblit.Keys;
+import com.gitblit.WebXmlSettings;
+import com.gitblit.dagger.DaggerContextListener;
+import com.gitblit.git.GitServlet;
+import com.gitblit.manager.IFederationManager;
+import com.gitblit.manager.IGitblitManager;
+import com.gitblit.manager.IManager;
+import com.gitblit.manager.INotificationManager;
+import com.gitblit.manager.IProjectManager;
+import com.gitblit.manager.IRepositoryManager;
+import com.gitblit.manager.IRuntimeManager;
+import com.gitblit.manager.IServicesManager;
+import com.gitblit.manager.ISessionManager;
+import com.gitblit.manager.IUserManager;
+import com.gitblit.utils.ContainerUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.GitblitWicketFilter;
+
+import dagger.ObjectGraph;
+
+/**
+ * This class is the main entry point for the entire webapp. It is a singleton
+ * created manually by Gitblit GO or dynamically by the WAR/Express servlet
+ * container. This class instantiates and starts all managers followed by
+ * instantiating and registering all servlets and filters.
+ *
+ * Leveraging Servlet 3 and Dagger static dependency injection allows Gitblit to
+ * be modular and completely code-driven rather then relying on the fragility of
+ * a web.xml descriptor and the static & monolithic design previously used.
+ *
+ * @author James Moger
+ *
+ */
+@WebListener
+public class GitblitContext extends DaggerContextListener {
+
+ private static GitblitContext gitblit;
+
+ private final List<IManager> managers = new ArrayList<IManager>();
+
+ private final IStoredSettings goSettings;
+
+ private final File goBaseFolder;
+
+ /**
+ * Construct a Gitblit WAR/Express context.
+ */
+ public GitblitContext() {
+ this.goSettings = null;
+ this.goBaseFolder = null;
+ gitblit = this;
+ }
+
+ /**
+ * Construct a Gitblit GO context.
+ *
+ * @param settings
+ * @param baseFolder
+ */
+ public GitblitContext(IStoredSettings settings, File baseFolder) {
+ this.goSettings = settings;
+ this.goBaseFolder = baseFolder;
+ gitblit = this;
+ }
+
+ /**
+ * This method is only used for unit and integration testing.
+ *
+ * @param managerClass
+ * @return a manager
+ */
+ @SuppressWarnings("unchecked")
+ public static <X extends IManager> X getManager(Class<X> managerClass) {
+ for (IManager manager : gitblit.managers) {
+ if (managerClass.isAssignableFrom(manager.getClass())) {
+ return (X) manager;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns Gitblit's Dagger injection modules.
+ */
+ @Override
+ protected Object [] getModules() {
+ return new Object [] { new DaggerModule() };
+ }
+
+ /**
+ * Prepare runtime settings and start all manager instances.
+ */
+ @Override
+ protected void beforeServletInjection(ServletContext context) {
+ ObjectGraph injector = getInjector(context);
+
+ // create the runtime settings object
+ IStoredSettings runtimeSettings = injector.get(IStoredSettings.class);
+ final File baseFolder;
+
+ if (goSettings != null) {
+ // Gitblit GO
+ baseFolder = configureGO(context, goSettings, goBaseFolder, runtimeSettings);
+ } else {
+ // servlet container
+ WebXmlSettings webxmlSettings = new WebXmlSettings(context);
+ String contextRealPath = context.getRealPath("/");
+ File contextFolder = (contextRealPath != null) ? new File(contextRealPath) : null;
+
+ if (!StringUtils.isEmpty(System.getenv("OPENSHIFT_DATA_DIR"))) {
+ // RedHat OpenShift
+ baseFolder = configureExpress(context, webxmlSettings, contextFolder, runtimeSettings);
+ } else {
+ // standard WAR
+ baseFolder = configureWAR(context, webxmlSettings, contextFolder, runtimeSettings);
+ }
+
+ // Test for Tomcat forward-slash/%2F issue and auto-adjust settings
+ ContainerUtils.CVE_2007_0450.test(runtimeSettings);
+ }
+
+ // Manually configure IRuntimeManager
+ logManager(IRuntimeManager.class);
+ IRuntimeManager runtime = injector.get(IRuntimeManager.class);
+ runtime.setBaseFolder(baseFolder);
+ runtime.getStatus().isGO = goSettings != null;
+ runtime.getStatus().servletContainer = context.getServerInfo();
+ runtime.start();
+ managers.add(runtime);
+
+ // start all other managers
+ startManager(injector, INotificationManager.class);
+ startManager(injector, IUserManager.class);
+ startManager(injector, ISessionManager.class);
+ startManager(injector, IRepositoryManager.class);
+ startManager(injector, IProjectManager.class);
+ startManager(injector, IGitblitManager.class);
+ startManager(injector, IFederationManager.class);
+ startManager(injector, IServicesManager.class);
+
+ logger.info("");
+ logger.info("All managers started.");
+ logger.info("");
+ }
+
+ protected <X extends IManager> X startManager(ObjectGraph injector, Class<X> clazz) {
+ logManager(clazz);
+ X x = injector.get(clazz);
+ x.start();
+ managers.add(x);
+ return x;
+ }
+
+ protected void logManager(Class<? extends IManager> clazz) {
+ logger.info("");
+ logger.info("----[{}]----", clazz.getName());
+ }
+
+ /**
+ * Instantiate and inject all filters and servlets into the container using
+ * the servlet 3 specification.
+ */
+ @Override
+ protected void injectServlets(ServletContext context) {
+ // access restricted servlets
+ serve(context, Constants.GIT_PATH, GitServlet.class, GitFilter.class);
+ serve(context, Constants.PAGES, PagesServlet.class, PagesFilter.class);
+ serve(context, Constants.RPC_PATH, RpcServlet.class, RpcFilter.class);
+ serve(context, Constants.ZIP_PATH, DownloadZipServlet.class, DownloadZipFilter.class);
+ serve(context, Constants.SYNDICATION_PATH, SyndicationServlet.class, SyndicationFilter.class);
+
+ // servlets
+ serve(context, Constants.FEDERATION_PATH, FederationServlet.class);
+ serve(context, Constants.SPARKLESHARE_INVITE_PATH, SparkleShareInviteServlet.class);
+ serve(context, Constants.BRANCH_GRAPH_PATH, BranchGraphServlet.class);
+ file(context, "/robots.txt", RobotsTxtServlet.class);
+ file(context, "/logo.png", LogoServlet.class);
+
+ // optional force basic authentication
+ filter(context, "/*", EnforceAuthenticationFilter.class, null);
+
+ // Wicket
+ String toIgnore = StringUtils.flattenStrings(getRegisteredPaths(), ",");
+ Map<String, String> params = new HashMap<String, String>();
+ params.put(GitblitWicketFilter.FILTER_MAPPING_PARAM, "/*");
+ params.put(GitblitWicketFilter.IGNORE_PATHS_PARAM, toIgnore);
+ filter(context, "/*", GitblitWicketFilter.class, params);
+ }
+
+ /**
+ * Gitblit is being shutdown either because the servlet container is
+ * shutting down or because the servlet container is re-deploying Gitblit.
+ */
+ @Override
+ protected void destroyContext(ServletContext context) {
+ logger.info("Gitblit context destroyed by servlet container.");
+ for (IManager manager : managers) {
+ logger.debug("stopping {}", manager.getClass().getSimpleName());
+ manager.stop();
+ }
+ }
+
+ /**
+ * Configures Gitblit GO
+ *
+ * @param context
+ * @param settings
+ * @param baseFolder
+ * @param runtimeSettings
+ * @return the base folder
+ */
+ protected File configureGO(
+ ServletContext context,
+ IStoredSettings goSettings,
+ File goBaseFolder,
+ IStoredSettings runtimeSettings) {
+
+ logger.debug("configuring Gitblit GO");
+
+ // merge the stored settings into the runtime settings
+ //
+ // if runtimeSettings is also a FileSettings w/o a specified target file,
+ // the target file for runtimeSettings is set to "localSettings".
+ runtimeSettings.merge(goSettings);
+ File base = goBaseFolder;
+ return base;
+ }
+
+
+ /**
+ * Configures a standard WAR instance of Gitblit.
+ *
+ * @param context
+ * @param webxmlSettings
+ * @param contextFolder
+ * @param runtimeSettings
+ * @return the base folder
+ */
+ protected File configureWAR(
+ ServletContext context,
+ WebXmlSettings webxmlSettings,
+ File contextFolder,
+ IStoredSettings runtimeSettings) {
+
+ // Gitblit is running in a standard servlet container
+ logger.debug("configuring Gitblit WAR");
+ logger.info("WAR contextFolder is " + ((contextFolder != null) ? contextFolder.getAbsolutePath() : "<empty>"));
+
+ String path = webxmlSettings.getString(Constants.baseFolder, Constants.contextFolder$ + "/WEB-INF/data");
+
+ if (path.contains(Constants.contextFolder$) && contextFolder == null) {
+ // warn about null contextFolder (issue-199)
+ logger.error("");
+ logger.error(MessageFormat.format("\"{0}\" depends on \"{1}\" but \"{2}\" is returning NULL for \"{1}\"!",
+ Constants.baseFolder, Constants.contextFolder$, context.getServerInfo()));
+ logger.error(MessageFormat.format("Please specify a non-parameterized path for <context-param> {0} in web.xml!!", Constants.baseFolder));
+ logger.error(MessageFormat.format("OR configure your servlet container to specify a \"{0}\" parameter in the context configuration!!", Constants.baseFolder));
+ logger.error("");
+ }
+
+ try {
+ // try to lookup JNDI env-entry for the baseFolder
+ InitialContext ic = new InitialContext();
+ Context env = (Context) ic.lookup("java:comp/env");
+ String val = (String) env.lookup("baseFolder");
+ if (!StringUtils.isEmpty(val)) {
+ path = val;
+ }
+ } catch (NamingException n) {
+ logger.error("Failed to get JNDI env-entry: " + n.getExplanation());
+ }
+
+ File base = com.gitblit.utils.FileUtils.resolveParameter(Constants.contextFolder$, contextFolder, path);
+ base.mkdirs();
+
+ // try to extract the data folder resource to the baseFolder
+ File localSettings = new File(base, "gitblit.properties");
+ if (!localSettings.exists()) {
+ extractResources(context, "/WEB-INF/data/", base);
+ }
+
+ // delegate all config to baseFolder/gitblit.properties file
+ FileSettings fileSettings = new FileSettings(localSettings.getAbsolutePath());
+
+ // merge the stored settings into the runtime settings
+ //
+ // if runtimeSettings is also a FileSettings w/o a specified target file,
+ // the target file for runtimeSettings is set to "localSettings".
+ runtimeSettings.merge(fileSettings);
+
+ return base;
+ }
+
+ /**
+ * Configures an OpenShift instance of Gitblit.
+ *
+ * @param context
+ * @param webxmlSettings
+ * @param contextFolder
+ * @param runtimeSettings
+ * @return the base folder
+ */
+ private File configureExpress(
+ ServletContext context,
+ WebXmlSettings webxmlSettings,
+ File contextFolder,
+ IStoredSettings runtimeSettings) {
+
+ // Gitblit is running in OpenShift/JBoss
+ logger.debug("configuring Gitblit Express");
+ String openShift = System.getenv("OPENSHIFT_DATA_DIR");
+ File base = new File(openShift);
+ logger.info("EXPRESS contextFolder is " + contextFolder.getAbsolutePath());
+
+ // Copy the included scripts to the configured groovy folder
+ String path = webxmlSettings.getString(Keys.groovy.scriptsFolder, "groovy");
+ File localScripts = com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, base, path);
+ if (!localScripts.exists()) {
+ File warScripts = new File(contextFolder, "/WEB-INF/data/groovy");
+ if (!warScripts.equals(localScripts)) {
+ try {
+ com.gitblit.utils.FileUtils.copy(localScripts, warScripts.listFiles());
+ } catch (IOException e) {
+ logger.error(MessageFormat.format(
+ "Failed to copy included Groovy scripts from {0} to {1}",
+ warScripts, localScripts));
+ }
+ }
+ }
+
+ // merge the WebXmlSettings into the runtime settings (for backwards-compatibilty)
+ runtimeSettings.merge(webxmlSettings);
+
+ // settings are to be stored in openshift/gitblit.properties
+ File localSettings = new File(base, "gitblit.properties");
+ FileSettings fileSettings = new FileSettings(localSettings.getAbsolutePath());
+
+ // merge the stored settings into the runtime settings
+ //
+ // if runtimeSettings is also a FileSettings w/o a specified target file,
+ // the target file for runtimeSettings is set to "localSettings".
+ runtimeSettings.merge(fileSettings);
+
+ return base;
+ }
+
+ protected void extractResources(ServletContext context, String path, File toDir) {
+ for (String resource : context.getResourcePaths(path)) {
+ // extract the resource to the directory if it does not exist
+ File f = new File(toDir, resource.substring(path.length()));
+ if (!f.exists()) {
+ InputStream is = null;
+ OutputStream os = null;
+ try {
+ if (resource.charAt(resource.length() - 1) == '/') {
+ // directory
+ f.mkdirs();
+ extractResources(context, resource, f);
+ } else {
+ // file
+ f.getParentFile().mkdirs();
+ is = context.getResourceAsStream(resource);
+ os = new FileOutputStream(f);
+ byte [] buffer = new byte[4096];
+ int len = 0;
+ while ((len = is.read(buffer)) > -1) {
+ os.write(buffer, 0, len);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ logger.error("Failed to find resource \"" + resource + "\"", e);
+ } catch (IOException e) {
+ logger.error("Failed to copy resource \"" + resource + "\" to " + f, e);
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ if (os != null) {
+ try {
+ os.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright 2013 gitblit.com.
+ *
+ * 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.gitblit.servlet;
+
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.Filter;
+import javax.servlet.FilterRegistration;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.ServletRegistration;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Injection context listener instantiates and injects servlets, filters, and
+ * anything else you might want into a servlet context. This class provides
+ * convenience methods for servlet & filter registration and also tracks
+ * registered paths.
+ *
+ * @author James Moger
+ *
+ */
+public abstract class InjectionContextListener implements ServletContextListener {
+
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private final List<String> registeredPaths = new ArrayList<String>();
+
+ protected final List<String> getRegisteredPaths() {
+ return registeredPaths;
+ }
+
+ /**
+ * Hook for subclasses to manipulate context initialization before
+ * standard initialization procedure.
+ *
+ * @param context
+ */
+ protected void beforeServletInjection(ServletContext context) {
+ // NOOP
+ }
+
+ /**
+ * Hook for subclasses to instantiate and inject servlets and filters
+ * into the servlet context.
+ *
+ * @param context
+ */
+ protected abstract void injectServlets(ServletContext context);
+
+ /**
+ * Hook for subclasses to manipulate context initialization after
+ * servlet registration.
+ *
+ * @param context
+ */
+ protected void afterServletInjection(ServletContext context) {
+ // NOOP
+ }
+
+ /**
+ * Configure Gitblit from the web.xml, if no configuration has already been
+ * specified.
+ *
+ * @see ServletContextListener.contextInitialize(ServletContextEvent)
+ */
+ @Override
+ public final void contextInitialized(ServletContextEvent contextEvent) {
+ ServletContext context = contextEvent.getServletContext();
+ beforeServletInjection(context);
+ injectServlets(context);
+ afterServletInjection(context);
+ }
+
+
+ /**
+ * Registers a file path.
+ *
+ * @param context
+ * @param file
+ * @param servletClass
+ */
+ protected void file(ServletContext context, String file, Class<? extends Servlet> servletClass) {
+ file(context, file, servletClass, null);
+ }
+
+ /**
+ * Registers a file path with init parameters.
+ *
+ * @param context
+ * @param file
+ * @param servletClass
+ * @param initParams
+ */
+ protected void file(ServletContext context, String file, Class<? extends Servlet> servletClass, Map<String, String> initParams) {
+ Servlet servlet = instantiate(context, servletClass);
+ ServletRegistration.Dynamic d = context.addServlet(sanitize(servletClass.getSimpleName() + file), servlet);
+ d.addMapping(file);
+ if (initParams != null) {
+ d.setInitParameters(initParams);
+ }
+ registeredPaths.add(file);
+ }
+
+ /**
+ * Serves a path (trailing wildcard will be appended).
+ *
+ * @param context
+ * @param route
+ * @param servletClass
+ */
+ protected void serve(ServletContext context, String route, Class<? extends Servlet> servletClass) {
+ serve(context, route, servletClass, (Class<Filter>) null);
+ }
+
+ /**
+ * Serves a path (trailing wildcard will be appended) with init parameters.
+ *
+ * @param context
+ * @param route
+ * @param servletClass
+ * @param initParams
+ */
+ protected void serve(ServletContext context, String route, Class<? extends Servlet> servletClass, Map<String, String> initParams) {
+ Servlet servlet = instantiate(context, servletClass);
+ ServletRegistration.Dynamic d = context.addServlet(sanitize(servletClass.getSimpleName() + route), servlet);
+ d.addMapping(route + "*");
+ if (initParams != null) {
+ d.setInitParameters(initParams);
+ }
+ registeredPaths.add(route);
+ }
+
+ /**
+ * Serves a path (trailing wildcard will be appended) and also maps a filter
+ * to that path.
+ *
+ * @param context
+ * @param route
+ * @param servletClass
+ * @param filterClass
+ */
+ protected void serve(ServletContext context, String route, Class<? extends Servlet> servletClass, Class<? extends Filter> filterClass) {
+ Servlet servlet = instantiate(context, servletClass);
+ ServletRegistration.Dynamic d = context.addServlet(sanitize(servletClass.getSimpleName() + route), servlet);
+ d.addMapping(route + "*");
+ if (filterClass != null) {
+ filter(context, route + "*", filterClass);
+ }
+ registeredPaths.add(route);
+ }
+
+ /**
+ * Registers a path filter.
+ *
+ * @param context
+ * @param route
+ * @param filterClass
+ */
+ protected void filter(ServletContext context, String route, Class<? extends Filter> filterClass) {
+ filter(context, route, filterClass, null);
+ }
+
+ /**
+ * Registers a path filter with init parameters.
+ *
+ * @param context
+ * @param route
+ * @param filterClass
+ * @param initParams
+ */
+ protected void filter(ServletContext context, String route, Class<? extends Filter> filterClass, Map<String, String> initParams) {
+ Filter filter = instantiate(context, filterClass);
+ FilterRegistration.Dynamic d = context.addFilter(sanitize(filterClass.getSimpleName() + route), filter);
+ d.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, route);
+ if (initParams != null) {
+ d.setInitParameters(initParams);
+ }
+ }
+
+ /**
+ * Limit the generated servlet/filter names to alpha-numeric values with a
+ * handful of acceptable other characters.
+ *
+ * @param name
+ * @return a sanitized name
+ */
+ protected String sanitize(String name) {
+ StringBuilder sb = new StringBuilder();
+ for (char c : name.toCharArray()) {
+ if (Character.isLetterOrDigit(c)) {
+ sb.append(c);
+ } else if ('-' == c) {
+ sb.append(c);
+ } else if ('*' == c) {
+ sb.append("all");
+ } else if ('.' == c) {
+ sb.append('.');
+ } else {
+ sb.append('_');
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Instantiates an object.
+ *
+ * @param clazz
+ * @return the object
+ */
+ protected <X> X instantiate(ServletContext context, Class<X> clazz) {
+ try {
+ return clazz.newInstance();
+ } catch (Throwable t) {
+ logger.error(null, t);
+ }
+ return null;
+ }
+}
--- /dev/null
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.servlet;\r
+\r
+import java.io.BufferedReader;\r
+import java.io.IOException;\r
+import java.lang.reflect.Type;\r
+import java.text.MessageFormat;\r
+\r
+import javax.servlet.ServletException;\r
+import javax.servlet.http.HttpServlet;\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpServletResponse;\r
+\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+import com.gitblit.Constants;\r
+import com.gitblit.utils.JsonUtils;\r
+import com.gitblit.utils.StringUtils;\r
+\r
+/**\r
+ * Servlet class for interpreting json requests.\r
+ *\r
+ * @author James Moger\r
+ *\r
+ */\r
+public abstract class JsonServlet extends HttpServlet {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ protected final int forbiddenCode = HttpServletResponse.SC_FORBIDDEN;\r
+\r
+ protected final int notAllowedCode = HttpServletResponse.SC_METHOD_NOT_ALLOWED;\r
+\r
+ protected final int failureCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;\r
+\r
+ protected final Logger logger;\r
+\r
+ public JsonServlet() {\r
+ super();\r
+ logger = LoggerFactory.getLogger(getClass());\r
+ }\r
+\r
+ /**\r
+ * Processes an gson request.\r
+ *\r
+ * @param request\r
+ * @param response\r
+ * @throws javax.servlet.ServletException\r
+ * @throws java.io.IOException\r
+ */\r
+ protected abstract void processRequest(HttpServletRequest request, HttpServletResponse response)\r
+ throws ServletException, IOException;\r
+\r
+ @Override\r
+ protected void doPost(HttpServletRequest request, HttpServletResponse response)\r
+ throws ServletException, java.io.IOException {\r
+ processRequest(request, response);\r
+ }\r
+\r
+ @Override\r
+ protected void doGet(HttpServletRequest request, HttpServletResponse response)\r
+ throws ServletException, IOException {\r
+ processRequest(request, response);\r
+ }\r
+\r
+ protected <X> X deserialize(HttpServletRequest request, HttpServletResponse response,\r
+ Class<X> clazz) throws IOException {\r
+ String json = readJson(request, response);\r
+ if (StringUtils.isEmpty(json)) {\r
+ return null;\r
+ }\r
+\r
+ X object = JsonUtils.fromJsonString(json.toString(), clazz);\r
+ return object;\r
+ }\r
+\r
+ protected <X> X deserialize(HttpServletRequest request, HttpServletResponse response, Type type)\r
+ throws IOException {\r
+ String json = readJson(request, response);\r
+ if (StringUtils.isEmpty(json)) {\r
+ return null;\r
+ }\r
+\r
+ X object = JsonUtils.fromJsonString(json.toString(), type);\r
+ return object;\r
+ }\r
+\r
+ private String readJson(HttpServletRequest request, HttpServletResponse response)\r
+ throws IOException {\r
+ BufferedReader reader = request.getReader();\r
+ StringBuilder json = new StringBuilder();\r
+ String line = null;\r
+ while ((line = reader.readLine()) != null) {\r
+ json.append(line);\r
+ }\r
+ reader.close();\r
+\r
+ if (json.length() == 0) {\r
+ logger.error(MessageFormat.format("Failed to receive json data from {0}",\r
+ request.getRemoteAddr()));\r
+ response.setStatus(HttpServletResponse.SC_BAD_REQUEST);\r
+ return null;\r
+ }\r
+ return json.toString();\r
+ }\r
+\r
+ protected void serialize(HttpServletResponse response, Object o) throws IOException {\r
+ if (o != null) {\r
+ // Send JSON response\r
+ String json = JsonUtils.toJsonString(o);\r
+ response.setCharacterEncoding(Constants.ENCODING);\r
+ response.setContentType("application/json");\r
+ response.getWriter().append(json);\r
+ }\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2013 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.servlet;\r
+\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+\r
+import javax.inject.Inject;\r
+import javax.inject.Singleton;\r
+import javax.servlet.ServletContext;\r
+import javax.servlet.ServletException;\r
+import javax.servlet.http.HttpServlet;\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpServletResponse;\r
+\r
+import com.gitblit.Keys;\r
+import com.gitblit.Keys.web;\r
+import com.gitblit.manager.IRuntimeManager;\r
+\r
+/**\r
+ * Handles requests for logo.png\r
+ *\r
+ * @author James Moger\r
+ *\r
+ */\r
+@Singleton\r
+public class LogoServlet extends HttpServlet {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ private static final long lastModified = System.currentTimeMillis();\r
+\r
+ private final IRuntimeManager runtimeManager;\r
+\r
+ @Inject\r
+ public LogoServlet(IRuntimeManager runtimeManager) {\r
+ super();\r
+ this.runtimeManager = runtimeManager;\r
+ }\r
+\r
+ @Override\r
+ protected long getLastModified(HttpServletRequest req) {\r
+ File file = runtimeManager.getFileOrFolder(Keys.web.headerLogo, "${baseFolder}/logo.png");\r
+ if (file.exists()) {\r
+ return Math.max(lastModified, file.lastModified());\r
+ } else {\r
+ return lastModified;\r
+ }\r
+ }\r
+\r
+ @Override\r
+ protected void doGet(HttpServletRequest request, HttpServletResponse response)\r
+ throws ServletException, IOException {\r
+ InputStream is = null;\r
+ try {\r
+ String contentType = null;\r
+ File file = runtimeManager.getFileOrFolder(Keys.web.headerLogo, "${baseFolder}/logo.png");\r
+ if (file.exists()) {\r
+ // custom logo\r
+ ServletContext context = request.getSession().getServletContext();\r
+ contentType = context.getMimeType(file.getName());\r
+ response.setContentLength((int) file.length());\r
+ response.setDateHeader("Last-Modified", Math.max(lastModified, file.lastModified()));\r
+ is = new FileInputStream(file);\r
+ } else {\r
+ // default logo\r
+ response.setDateHeader("Last-Modified", lastModified);\r
+ is = getClass().getResourceAsStream("/logo.png");\r
+ }\r
+ if (contentType == null) {\r
+ contentType = "image/png";\r
+ }\r
+ response.setContentType(contentType);\r
+ response.setHeader("Cache-Control", "public, max-age=3600, must-revalidate");\r
+ OutputStream os = response.getOutputStream();\r
+ byte[] buf = new byte[4096];\r
+ int bytesRead = is.read(buf);\r
+ while (bytesRead != -1) {\r
+ os.write(buf, 0, bytesRead);\r
+ bytesRead = is.read(buf);\r
+ }\r
+ os.flush();\r
+ } catch (Exception e) {\r
+ e.printStackTrace();\r
+ } finally {\r
+ if (is != null) {\r
+ is.close();\r
+ }\r
+ }\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2012 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.servlet;\r
+\r
+import javax.inject.Inject;\r
+import javax.inject.Singleton;\r
+\r
+import org.eclipse.jgit.lib.Repository;\r
+\r
+import com.gitblit.Constants.AccessRestrictionType;\r
+import com.gitblit.manager.IRepositoryManager;\r
+import com.gitblit.manager.IRuntimeManager;\r
+import com.gitblit.manager.ISessionManager;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.UserModel;\r
+\r
+/**\r
+ * The PagesFilter is an AccessRestrictionFilter which ensures the gh-pages\r
+ * requests for a view-restricted repository are authenticated and authorized.\r
+ *\r
+ * @author James Moger\r
+ *\r
+ */\r
+@Singleton\r
+public class PagesFilter extends AccessRestrictionFilter {\r
+\r
+ @Inject\r
+ public PagesFilter(IRuntimeManager runtimeManager,\r
+ ISessionManager sessionManager,\r
+ IRepositoryManager repositoryManager) {\r
+\r
+ super(runtimeManager, sessionManager, repositoryManager);\r
+ }\r
+\r
+ /**\r
+ * Extract the repository name from the url.\r
+ *\r
+ * @param url\r
+ * @return repository name\r
+ */\r
+ @Override\r
+ protected String extractRepositoryName(String url) {\r
+ // get the repository name from the url by finding a known url suffix\r
+ String repository = "";\r
+ Repository r = null;\r
+ int offset = 0;\r
+ while (r == null) {\r
+ int slash = url.indexOf('/', offset);\r
+ if (slash == -1) {\r
+ repository = url;\r
+ } else {\r
+ repository = url.substring(0, slash);\r
+ }\r
+ r = repositoryManager.getRepository(repository, false);\r
+ if (r == null) {\r
+ // try again\r
+ offset = slash + 1;\r
+ } else {\r
+ // close the repo\r
+ r.close();\r
+ }\r
+ if (repository.equals(url)) {\r
+ // either only repository in url or no repository found\r
+ break;\r
+ }\r
+ }\r
+ return repository;\r
+ }\r
+\r
+ /**\r
+ * Analyze the url and returns the action of the request.\r
+ *\r
+ * @param cloneUrl\r
+ * @return action of the request\r
+ */\r
+ @Override\r
+ protected String getUrlRequestAction(String suffix) {\r
+ return "VIEW";\r
+ }\r
+\r
+ /**\r
+ * Determine if a non-existing repository can be created using this filter.\r
+ *\r
+ * @return true if the filter allows repository creation\r
+ */\r
+ @Override\r
+ protected boolean isCreationAllowed() {\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Determine if the action may be executed on the repository.\r
+ *\r
+ * @param repository\r
+ * @param action\r
+ * @return true if the action may be performed\r
+ */\r
+ @Override\r
+ protected boolean isActionAllowed(RepositoryModel repository, String action) {\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Determine if the repository requires authentication.\r
+ *\r
+ * @param repository\r
+ * @param action\r
+ * @return true if authentication required\r
+ */\r
+ @Override\r
+ protected boolean requiresAuthentication(RepositoryModel repository, String action) {\r
+ return repository.accessRestriction.atLeast(AccessRestrictionType.VIEW);\r
+ }\r
+\r
+ /**\r
+ * Determine if the user can access the repository and perform the specified\r
+ * action.\r
+ *\r
+ * @param repository\r
+ * @param user\r
+ * @param action\r
+ * @return true if user may execute the action on the repository\r
+ */\r
+ @Override\r
+ protected boolean canAccess(RepositoryModel repository, UserModel user, String action) {\r
+ return user.canView(repository);\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2012 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.servlet;\r
+\r
+import java.io.IOException;\r
+import java.text.MessageFormat;\r
+import java.text.ParseException;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+import java.util.Set;\r
+import java.util.TreeSet;\r
+\r
+import javax.inject.Inject;\r
+import javax.inject.Singleton;\r
+import javax.servlet.ServletContext;\r
+import javax.servlet.ServletException;\r
+import javax.servlet.http.HttpServlet;\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpServletResponse;\r
+\r
+import org.eclipse.jgit.lib.Repository;\r
+import org.eclipse.jgit.revwalk.RevCommit;\r
+import org.eclipse.jgit.revwalk.RevTree;\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+import com.gitblit.Constants;\r
+import com.gitblit.IStoredSettings;\r
+import com.gitblit.Keys;\r
+import com.gitblit.Keys.web;\r
+import com.gitblit.manager.IRepositoryManager;\r
+import com.gitblit.manager.IRuntimeManager;\r
+import com.gitblit.models.PathModel;\r
+import com.gitblit.models.RefModel;\r
+import com.gitblit.utils.ArrayUtils;\r
+import com.gitblit.utils.ByteFormat;\r
+import com.gitblit.utils.JGitUtils;\r
+import com.gitblit.utils.MarkdownUtils;\r
+import com.gitblit.utils.StringUtils;\r
+import com.gitblit.wicket.MarkupProcessor;\r
+import com.gitblit.wicket.MarkupProcessor.MarkupDocument;\r
+\r
+/**\r
+ * Serves the content of a gh-pages branch.\r
+ *\r
+ * @author James Moger\r
+ *\r
+ */\r
+@Singleton\r
+public class PagesServlet extends HttpServlet {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ private transient Logger logger = LoggerFactory.getLogger(PagesServlet.class);\r
+\r
+ private final IStoredSettings settings;\r
+\r
+ private final IRepositoryManager repositoryManager;\r
+\r
+ @Inject\r
+ public PagesServlet(\r
+ IRuntimeManager runtimeManager,\r
+ IRepositoryManager repositoryManager) {\r
+\r
+ super();\r
+ this.settings = runtimeManager.getSettings();\r
+ this.repositoryManager = repositoryManager;\r
+ }\r
+\r
+ /**\r
+ * Returns an url to this servlet for the specified parameters.\r
+ *\r
+ * @param baseURL\r
+ * @param repository\r
+ * @param path\r
+ * @return an url\r
+ */\r
+ public static String asLink(String baseURL, String repository, String path) {\r
+ if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {\r
+ baseURL = baseURL.substring(0, baseURL.length() - 1);\r
+ }\r
+ return baseURL + Constants.PAGES + repository + "/" + (path == null ? "" : ("/" + path));\r
+ }\r
+\r
+ /**\r
+ * Retrieves the specified resource from the gh-pages branch of the\r
+ * repository.\r
+ *\r
+ * @param request\r
+ * @param response\r
+ * @throws javax.servlet.ServletException\r
+ * @throws java.io.IOException\r
+ */\r
+ private void processRequest(HttpServletRequest request, HttpServletResponse response)\r
+ throws ServletException, IOException {\r
+ String path = request.getPathInfo();\r
+ if (path.toLowerCase().endsWith(".git")) {\r
+ // forward to url with trailing /\r
+ // this is important for relative pages links\r
+ response.sendRedirect(request.getServletPath() + path + "/");\r
+ return;\r
+ }\r
+ if (path.charAt(0) == '/') {\r
+ // strip leading /\r
+ path = path.substring(1);\r
+ }\r
+\r
+ // determine repository and resource from url\r
+ String repository = "";\r
+ String resource = "";\r
+ Repository r = null;\r
+ int offset = 0;\r
+ while (r == null) {\r
+ int slash = path.indexOf('/', offset);\r
+ if (slash == -1) {\r
+ repository = path;\r
+ } else {\r
+ repository = path.substring(0, slash);\r
+ }\r
+ r = repositoryManager.getRepository(repository, false);\r
+ offset = slash + 1;\r
+ if (offset > 0) {\r
+ resource = path.substring(offset);\r
+ }\r
+ if (repository.equals(path)) {\r
+ // either only repository in url or no repository found\r
+ break;\r
+ }\r
+ }\r
+\r
+ ServletContext context = request.getSession().getServletContext();\r
+\r
+ try {\r
+ if (r == null) {\r
+ // repository not found!\r
+ String mkd = MessageFormat.format(\r
+ "# Error\nSorry, no valid **repository** specified in this url: {0}!",\r
+ repository);\r
+ error(response, mkd);\r
+ return;\r
+ }\r
+\r
+ // retrieve the content from the repository\r
+ RefModel pages = JGitUtils.getPagesBranch(r);\r
+ RevCommit commit = JGitUtils.getCommit(r, pages.getObjectId().getName());\r
+\r
+ if (commit == null) {\r
+ // branch not found!\r
+ String mkd = MessageFormat.format(\r
+ "# Error\nSorry, the repository {0} does not have a **gh-pages** branch!",\r
+ repository);\r
+ error(response, mkd);\r
+ r.close();\r
+ return;\r
+ }\r
+\r
+ MarkupProcessor processor = new MarkupProcessor(settings);\r
+ String [] encodings = settings.getStrings(Keys.web.blobEncodings).toArray(new String[0]);\r
+\r
+ RevTree tree = commit.getTree();\r
+\r
+ String res = resource;\r
+ if (res.endsWith("/")) {\r
+ res = res.substring(0, res.length() - 1);\r
+ }\r
+ Set<String> names = new TreeSet<String>();\r
+ for (PathModel entry : JGitUtils.getFilesInPath(r, res, commit)) {\r
+ names.add(entry.name);\r
+ }\r
+\r
+ byte[] content = null;\r
+ if (names.isEmpty()) {\r
+ // not a path, a specific resource\r
+ try {\r
+ String contentType = context.getMimeType(resource);\r
+ if (contentType == null) {\r
+ contentType = "text/plain";\r
+ }\r
+ if (contentType.startsWith("text")) {\r
+ content = JGitUtils.getStringContent(r, tree, resource, encodings).getBytes(\r
+ Constants.ENCODING);\r
+ } else {\r
+ content = JGitUtils.getByteContent(r, tree, resource, false);\r
+ }\r
+ response.setContentType(contentType);\r
+ } catch (Exception e) {\r
+ }\r
+ } else {\r
+ // path\r
+ List<String> extensions = new ArrayList<String>();\r
+ extensions.add("html");\r
+ extensions.add("htm");\r
+ extensions.addAll(processor.getMarkupExtensions());\r
+ for (String ext : extensions) {\r
+ String file = "index." + ext;\r
+\r
+ if (names.contains(file)) {\r
+ String stringContent = JGitUtils.getStringContent(r, tree, file, encodings);\r
+ if (stringContent == null) {\r
+ continue;\r
+ }\r
+ content = stringContent.getBytes(Constants.ENCODING);\r
+ if (content != null) {\r
+ resource = file;\r
+ // assume text/html unless the servlet container\r
+ // overrides\r
+ response.setContentType("text/html; charset=" + Constants.ENCODING);\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ // no content, try custom 404 page\r
+ if (ArrayUtils.isEmpty(content)) {\r
+ String ext = StringUtils.getFileExtension(resource);\r
+ if (StringUtils.isEmpty(ext)) {\r
+ // document list\r
+ response.setContentType("text/html");\r
+ response.getWriter().append("<style>table th, table td { min-width: 150px; text-align: left; }</style>");\r
+ response.getWriter().append("<table>");\r
+ response.getWriter().append("<thead><tr><th>path</th><th>mode</th><th>size</th></tr>");\r
+ response.getWriter().append("</thead>");\r
+ response.getWriter().append("<tbody>");\r
+ String pattern = "<tr><td><a href=\"{0}\">{0}</a></td><td>{1}</td><td>{2}</td></tr>";\r
+ final ByteFormat byteFormat = new ByteFormat();\r
+ List<PathModel> entries = JGitUtils.getFilesInPath(r, resource, commit);\r
+ for (PathModel entry : entries) {\r
+ response.getWriter().append(MessageFormat.format(pattern, entry.name, JGitUtils.getPermissionsFromMode(entry.mode), byteFormat.format(entry.size)));\r
+ }\r
+ response.getWriter().append("</tbody>");\r
+ response.getWriter().append("</table>");\r
+ } else {\r
+ // 404\r
+ String custom404 = JGitUtils.getStringContent(r, tree, "404.html", encodings);\r
+ if (!StringUtils.isEmpty(custom404)) {\r
+ content = custom404.getBytes(Constants.ENCODING);\r
+ }\r
+\r
+ // still no content\r
+ if (ArrayUtils.isEmpty(content)) {\r
+ String str = MessageFormat.format(\r
+ "# Error\nSorry, the requested resource **{0}** was not found.",\r
+ resource);\r
+ content = MarkdownUtils.transformMarkdown(str).getBytes(Constants.ENCODING);\r
+ }\r
+\r
+ try {\r
+ // output the content\r
+ logger.warn("Pages 404: " + resource);\r
+ response.setStatus(HttpServletResponse.SC_NOT_FOUND);\r
+ response.getOutputStream().write(content);\r
+ response.flushBuffer();\r
+ } catch (Throwable t) {\r
+ logger.error("Failed to write page to client", t);\r
+ }\r
+ }\r
+ return;\r
+ }\r
+\r
+ // check to see if we should transform markup files\r
+ String ext = StringUtils.getFileExtension(resource);\r
+ if (processor.getMarkupExtensions().contains(ext)) {\r
+ String markup = new String(content, Constants.ENCODING);\r
+ MarkupDocument markupDoc = processor.parse(repository, commit.getName(), resource, markup);\r
+ content = markupDoc.html.getBytes("UTF-8");\r
+ response.setContentType("text/html; charset=" + Constants.ENCODING);\r
+ }\r
+\r
+ try {\r
+ // output the content\r
+ response.setHeader("Cache-Control", "public, max-age=3600, must-revalidate");\r
+ response.setDateHeader("Last-Modified", JGitUtils.getCommitDate(commit).getTime());\r
+ response.getOutputStream().write(content);\r
+ response.flushBuffer();\r
+ } catch (Throwable t) {\r
+ logger.error("Failed to write page to client", t);\r
+ }\r
+\r
+ // close the repository\r
+ r.close();\r
+ } catch (Throwable t) {\r
+ logger.error("Failed to write page to client", t);\r
+ }\r
+ }\r
+\r
+ private void error(HttpServletResponse response, String mkd) throws ServletException,\r
+ IOException, ParseException {\r
+ String content = MarkdownUtils.transformMarkdown(mkd);\r
+ response.setContentType("text/html; charset=" + Constants.ENCODING);\r
+ response.getWriter().write(content);\r
+ }\r
+\r
+ @Override\r
+ protected void doPost(HttpServletRequest request, HttpServletResponse response)\r
+ throws ServletException, IOException {\r
+ processRequest(request, response);\r
+ }\r
+\r
+ @Override\r
+ protected void doGet(HttpServletRequest request, HttpServletResponse response)\r
+ throws ServletException, IOException {\r
+ processRequest(request, response);\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2012 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.servlet;\r
+\r
+import java.io.File;\r
+import java.io.IOException;\r
+\r
+import javax.inject.Inject;\r
+import javax.inject.Singleton;\r
+import javax.servlet.ServletException;\r
+import javax.servlet.http.HttpServlet;\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpServletResponse;\r
+\r
+import com.gitblit.Keys;\r
+import com.gitblit.Keys.web;\r
+import com.gitblit.Keys.web.robots;\r
+import com.gitblit.manager.IRuntimeManager;\r
+import com.gitblit.utils.FileUtils;\r
+\r
+/**\r
+ * Handles requests for robots.txt\r
+ *\r
+ * @author James Moger\r
+ *\r
+ */\r
+@Singleton\r
+public class RobotsTxtServlet extends HttpServlet {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ private final IRuntimeManager runtimeManager;\r
+\r
+ @Inject\r
+ public RobotsTxtServlet(IRuntimeManager runtimeManager) {\r
+ super();\r
+ this.runtimeManager = runtimeManager;\r
+ }\r
+\r
+ @Override\r
+ protected void doPost(HttpServletRequest request, HttpServletResponse response)\r
+ throws ServletException, java.io.IOException {\r
+ processRequest(request, response);\r
+ }\r
+\r
+ @Override\r
+ protected void doGet(HttpServletRequest request, HttpServletResponse response)\r
+ throws ServletException, IOException {\r
+ processRequest(request, response);\r
+ }\r
+\r
+ protected void processRequest(javax.servlet.http.HttpServletRequest request,\r
+ javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,\r
+ java.io.IOException {\r
+ File file = runtimeManager.getFileOrFolder(Keys.web.robots.txt, null);\r
+ String content = "";\r
+ if (file.exists()) {\r
+ content = FileUtils.readContent(file, "\n");\r
+ }\r
+ response.getWriter().append(content);\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.servlet;\r
+\r
+import java.io.IOException;\r
+import java.text.MessageFormat;\r
+\r
+import javax.inject.Inject;\r
+import javax.inject.Singleton;\r
+import javax.servlet.FilterChain;\r
+import javax.servlet.ServletException;\r
+import javax.servlet.ServletRequest;\r
+import javax.servlet.ServletResponse;\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpServletResponse;\r
+\r
+import com.gitblit.Constants;\r
+import com.gitblit.IStoredSettings;\r
+import com.gitblit.Keys;\r
+import com.gitblit.Constants.RpcRequest;\r
+import com.gitblit.Keys.web;\r
+import com.gitblit.manager.IRuntimeManager;\r
+import com.gitblit.manager.ISessionManager;\r
+import com.gitblit.models.UserModel;\r
+\r
+/**\r
+ * The RpcFilter is a servlet filter that secures the RpcServlet.\r
+ *\r
+ * The filter extracts the rpc request type from the url and determines if the\r
+ * requested action requires a Basic authentication prompt. If authentication is\r
+ * required and no credentials are stored in the "Authorization" header, then a\r
+ * basic authentication challenge is issued.\r
+ *\r
+ * http://en.wikipedia.org/wiki/Basic_access_authentication\r
+ *\r
+ * @author James Moger\r
+ *\r
+ */\r
+@Singleton\r
+public class RpcFilter extends AuthenticationFilter {\r
+\r
+ private final IStoredSettings settings;\r
+\r
+ private final IRuntimeManager runtimeManager;\r
+\r
+ @Inject\r
+ public RpcFilter(\r
+ IRuntimeManager runtimeManager,\r
+ ISessionManager sessionManager) {\r
+\r
+ super(sessionManager);\r
+ this.settings = runtimeManager.getSettings();\r
+ this.runtimeManager = runtimeManager;\r
+ }\r
+\r
+ /**\r
+ * doFilter does the actual work of preprocessing the request to ensure that\r
+ * the user may proceed.\r
+ *\r
+ * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,\r
+ * javax.servlet.ServletResponse, javax.servlet.FilterChain)\r
+ */\r
+ @Override\r
+ public void doFilter(final ServletRequest request, final ServletResponse response,\r
+ final FilterChain chain) throws IOException, ServletException {\r
+\r
+ HttpServletRequest httpRequest = (HttpServletRequest) request;\r
+ HttpServletResponse httpResponse = (HttpServletResponse) response;\r
+\r
+ String fullUrl = getFullUrl(httpRequest);\r
+ RpcRequest requestType = RpcRequest.fromName(httpRequest.getParameter("req"));\r
+ if (requestType == null) {\r
+ httpResponse.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);\r
+ return;\r
+ }\r
+\r
+ boolean adminRequest = requestType.exceeds(RpcRequest.LIST_SETTINGS);\r
+\r
+ // conditionally reject all rpc requests\r
+ if (!settings.getBoolean(Keys.web.enableRpcServlet, true)) {\r
+ logger.warn(Keys.web.enableRpcServlet + " must be set TRUE for rpc requests.");\r
+ httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);\r
+ return;\r
+ }\r
+\r
+ boolean authenticateView = settings.getBoolean(Keys.web.authenticateViewPages, false);\r
+ boolean authenticateAdmin = settings.getBoolean(Keys.web.authenticateAdminPages, true);\r
+\r
+ // Wrap the HttpServletRequest with the RpcServletRequest which\r
+ // overrides the servlet container user principal methods.\r
+ AuthenticatedRequest authenticatedRequest = new AuthenticatedRequest(httpRequest);\r
+ UserModel user = getUser(httpRequest);\r
+ if (user != null) {\r
+ authenticatedRequest.setUser(user);\r
+ }\r
+\r
+ // conditionally reject rpc management/administration requests\r
+ if (adminRequest && !settings.getBoolean(Keys.web.enableRpcManagement, false)) {\r
+ logger.warn(MessageFormat.format("{0} must be set TRUE for {1} rpc requests.",\r
+ Keys.web.enableRpcManagement, requestType.toString()));\r
+ httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);\r
+ return;\r
+ }\r
+\r
+ // BASIC authentication challenge and response processing\r
+ if ((adminRequest && authenticateAdmin) || (!adminRequest && authenticateView)) {\r
+ if (user == null) {\r
+ // challenge client to provide credentials. send 401.\r
+ if (runtimeManager.isDebugMode()) {\r
+ logger.info(MessageFormat.format("RPC: CHALLENGE {0}", fullUrl));\r
+\r
+ }\r
+ httpResponse.setHeader("WWW-Authenticate", CHALLENGE);\r
+ httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);\r
+ return;\r
+ } else {\r
+ // check user access for request\r
+ if (user.canAdmin() || canAccess(user, requestType)) {\r
+ // authenticated request permitted.\r
+ // pass processing to the restricted servlet.\r
+ newSession(authenticatedRequest, httpResponse);\r
+ logger.info(MessageFormat.format("RPC: {0} ({1}) authenticated", fullUrl,\r
+ HttpServletResponse.SC_CONTINUE));\r
+ chain.doFilter(authenticatedRequest, httpResponse);\r
+ return;\r
+ }\r
+ // valid user, but not for requested access. send 403.\r
+ if (runtimeManager.isDebugMode()) {\r
+ logger.info(MessageFormat.format("RPC: {0} forbidden to access {1}",\r
+ user.username, fullUrl));\r
+ }\r
+ httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);\r
+ return;\r
+ }\r
+ }\r
+\r
+ if (runtimeManager.isDebugMode()) {\r
+ logger.info(MessageFormat.format("RPC: {0} ({1}) unauthenticated", fullUrl,\r
+ HttpServletResponse.SC_CONTINUE));\r
+ }\r
+ // unauthenticated request permitted.\r
+ // pass processing to the restricted servlet.\r
+ chain.doFilter(authenticatedRequest, httpResponse);\r
+ }\r
+\r
+ private boolean canAccess(UserModel user, RpcRequest requestType) {\r
+ switch (requestType) {\r
+ case GET_PROTOCOL:\r
+ return true;\r
+ case LIST_REPOSITORIES:\r
+ return true;\r
+ default:\r
+ return user.canAdmin();\r
+ }\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.servlet;\r
+\r
+import java.io.IOException;\r
+import java.text.MessageFormat;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+import javax.inject.Inject;\r
+import javax.inject.Singleton;\r
+import javax.servlet.ServletException;\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpServletResponse;\r
+\r
+import org.eclipse.jgit.lib.Repository;\r
+\r
+import com.gitblit.Constants;\r
+import com.gitblit.GitBlitException;\r
+import com.gitblit.IStoredSettings;\r
+import com.gitblit.Keys;\r
+import com.gitblit.Constants.RpcRequest;\r
+import com.gitblit.Keys.federation;\r
+import com.gitblit.Keys.realm;\r
+import com.gitblit.Keys.web;\r
+import com.gitblit.manager.IFederationManager;\r
+import com.gitblit.manager.IGitblitManager;\r
+import com.gitblit.manager.IRepositoryManager;\r
+import com.gitblit.manager.IRuntimeManager;\r
+import com.gitblit.manager.IUserManager;\r
+import com.gitblit.models.RefModel;\r
+import com.gitblit.models.RegistrantAccessPermission;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.ServerSettings;\r
+import com.gitblit.models.TeamModel;\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.DeepCopier;\r
+import com.gitblit.utils.HttpUtils;\r
+import com.gitblit.utils.JGitUtils;\r
+import com.gitblit.utils.RpcUtils;\r
+import com.gitblit.utils.StringUtils;\r
+\r
+/**\r
+ * Handles remote procedure calls.\r
+ *\r
+ * @author James Moger\r
+ *\r
+ */\r
+@Singleton\r
+public class RpcServlet extends JsonServlet {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ public static final int PROTOCOL_VERSION = 6;\r
+\r
+ private final IStoredSettings settings;\r
+\r
+ private final IRuntimeManager runtimeManager;\r
+\r
+ private final IUserManager userManager;\r
+\r
+ private final IRepositoryManager repositoryManager;\r
+\r
+ private final IFederationManager federationManager;\r
+\r
+ private final IGitblitManager gitblitManager;\r
+\r
+ @Inject\r
+ public RpcServlet(\r
+ IRuntimeManager runtimeManager,\r
+ IUserManager userManager,\r
+ IRepositoryManager repositoryManager,\r
+ IFederationManager federationManager,\r
+ IGitblitManager gitblitManager) {\r
+\r
+ super();\r
+\r
+ this.settings = runtimeManager.getSettings();\r
+ this.runtimeManager = runtimeManager;\r
+ this.userManager = userManager;\r
+ this.repositoryManager = repositoryManager;\r
+ this.federationManager = federationManager;\r
+ this.gitblitManager = gitblitManager;\r
+ }\r
+\r
+ /**\r
+ * Processes an rpc request.\r
+ *\r
+ * @param request\r
+ * @param response\r
+ * @throws javax.servlet.ServletException\r
+ * @throws java.io.IOException\r
+ */\r
+ @Override\r
+ protected void processRequest(HttpServletRequest request, HttpServletResponse response)\r
+ throws ServletException, IOException {\r
+ RpcRequest reqType = RpcRequest.fromName(request.getParameter("req"));\r
+ String objectName = request.getParameter("name");\r
+ logger.info(MessageFormat.format("Rpc {0} request from {1}", reqType,\r
+ request.getRemoteAddr()));\r
+\r
+ UserModel user = (UserModel) request.getUserPrincipal();\r
+\r
+ boolean allowManagement = user != null && user.canAdmin()\r
+ && settings.getBoolean(Keys.web.enableRpcManagement, false);\r
+\r
+ boolean allowAdmin = user != null && user.canAdmin()\r
+ && settings.getBoolean(Keys.web.enableRpcAdministration, false);\r
+\r
+ Object result = null;\r
+ if (RpcRequest.GET_PROTOCOL.equals(reqType)) {\r
+ // Return the protocol version\r
+ result = PROTOCOL_VERSION;\r
+ } else if (RpcRequest.LIST_REPOSITORIES.equals(reqType)) {\r
+ // Determine the Gitblit clone url\r
+ String gitblitUrl = HttpUtils.getGitblitURL(request);\r
+ StringBuilder sb = new StringBuilder();\r
+ sb.append(gitblitUrl);\r
+ sb.append(Constants.GIT_PATH);\r
+ sb.append("{0}");\r
+ String cloneUrl = sb.toString();\r
+\r
+ // list repositories\r
+ List<RepositoryModel> list = repositoryManager.getRepositoryModels(user);\r
+ Map<String, RepositoryModel> repositories = new HashMap<String, RepositoryModel>();\r
+ for (RepositoryModel model : list) {\r
+ String url = MessageFormat.format(cloneUrl, model.name);\r
+ repositories.put(url, model);\r
+ }\r
+ result = repositories;\r
+ } else if (RpcRequest.LIST_BRANCHES.equals(reqType)) {\r
+ // list all local branches in all repositories accessible to user\r
+ Map<String, List<String>> localBranches = new HashMap<String, List<String>>();\r
+ List<RepositoryModel> models = repositoryManager.getRepositoryModels(user);\r
+ for (RepositoryModel model : models) {\r
+ if (!model.hasCommits) {\r
+ // skip empty repository\r
+ continue;\r
+ }\r
+ if (model.isCollectingGarbage) {\r
+ // skip garbage collecting repository\r
+ logger.warn(MessageFormat.format("Temporarily excluding {0} from RPC, busy collecting garbage", model.name));\r
+ continue;\r
+ }\r
+ // get local branches\r
+ Repository repository = repositoryManager.getRepository(model.name);\r
+ List<RefModel> refs = JGitUtils.getLocalBranches(repository, false, -1);\r
+ if (model.showRemoteBranches) {\r
+ // add remote branches if repository displays them\r
+ refs.addAll(JGitUtils.getRemoteBranches(repository, false, -1));\r
+ }\r
+ if (refs.size() > 0) {\r
+ List<String> branches = new ArrayList<String>();\r
+ for (RefModel ref : refs) {\r
+ branches.add(ref.getName());\r
+ }\r
+ localBranches.put(model.name, branches);\r
+ }\r
+ repository.close();\r
+ }\r
+ result = localBranches;\r
+ } else if (RpcRequest.GET_USER.equals(reqType)) {\r
+ if (StringUtils.isEmpty(objectName)) {\r
+ if (UserModel.ANONYMOUS.equals(user)) {\r
+ response.sendError(forbiddenCode);\r
+ } else {\r
+ // return the current user, reset credentials\r
+ UserModel requestedUser = DeepCopier.copy(user);\r
+ result = requestedUser;\r
+ }\r
+ } else {\r
+ if (user.canAdmin() || objectName.equals(user.username)) {\r
+ // return the specified user\r
+ UserModel requestedUser = userManager.getUserModel(objectName);\r
+ if (requestedUser == null) {\r
+ response.setStatus(failureCode);\r
+ } else {\r
+ result = requestedUser;\r
+ }\r
+ } else {\r
+ response.sendError(forbiddenCode);\r
+ }\r
+ }\r
+ } else if (RpcRequest.LIST_USERS.equals(reqType)) {\r
+ // list users\r
+ List<String> names = userManager.getAllUsernames();\r
+ List<UserModel> users = new ArrayList<UserModel>();\r
+ for (String name : names) {\r
+ users.add(userManager.getUserModel(name));\r
+ }\r
+ result = users;\r
+ } else if (RpcRequest.LIST_TEAMS.equals(reqType)) {\r
+ // list teams\r
+ List<String> names = userManager.getAllTeamNames();\r
+ List<TeamModel> teams = new ArrayList<TeamModel>();\r
+ for (String name : names) {\r
+ teams.add(userManager.getTeamModel(name));\r
+ }\r
+ result = teams;\r
+ } else if (RpcRequest.CREATE_REPOSITORY.equals(reqType)) {\r
+ // create repository\r
+ RepositoryModel model = deserialize(request, response, RepositoryModel.class);\r
+ try {\r
+ repositoryManager.updateRepositoryModel(model.name, model, true);\r
+ } catch (GitBlitException e) {\r
+ response.setStatus(failureCode);\r
+ }\r
+ } else if (RpcRequest.EDIT_REPOSITORY.equals(reqType)) {\r
+ // edit repository\r
+ RepositoryModel model = deserialize(request, response, RepositoryModel.class);\r
+ // name specifies original repository name in event of rename\r
+ String repoName = objectName;\r
+ if (repoName == null) {\r
+ repoName = model.name;\r
+ }\r
+ try {\r
+ repositoryManager.updateRepositoryModel(repoName, model, false);\r
+ } catch (GitBlitException e) {\r
+ response.setStatus(failureCode);\r
+ }\r
+ } else if (RpcRequest.DELETE_REPOSITORY.equals(reqType)) {\r
+ // delete repository\r
+ RepositoryModel model = deserialize(request, response, RepositoryModel.class);\r
+ repositoryManager.deleteRepositoryModel(model);\r
+ } else if (RpcRequest.CREATE_USER.equals(reqType)) {\r
+ // create user\r
+ UserModel model = deserialize(request, response, UserModel.class);\r
+ try {\r
+ gitblitManager.updateUserModel(model.username, model, true);\r
+ } catch (GitBlitException e) {\r
+ response.setStatus(failureCode);\r
+ }\r
+ } else if (RpcRequest.EDIT_USER.equals(reqType)) {\r
+ // edit user\r
+ UserModel model = deserialize(request, response, UserModel.class);\r
+ // name parameter specifies original user name in event of rename\r
+ String username = objectName;\r
+ if (username == null) {\r
+ username = model.username;\r
+ }\r
+ try {\r
+ gitblitManager.updateUserModel(username, model, false);\r
+ } catch (GitBlitException e) {\r
+ response.setStatus(failureCode);\r
+ }\r
+ } else if (RpcRequest.DELETE_USER.equals(reqType)) {\r
+ // delete user\r
+ UserModel model = deserialize(request, response, UserModel.class);\r
+ if (!userManager.deleteUser(model.username)) {\r
+ response.setStatus(failureCode);\r
+ }\r
+ } else if (RpcRequest.CREATE_TEAM.equals(reqType)) {\r
+ // create team\r
+ TeamModel model = deserialize(request, response, TeamModel.class);\r
+ try {\r
+ gitblitManager.updateTeamModel(model.name, model, true);\r
+ } catch (GitBlitException e) {\r
+ response.setStatus(failureCode);\r
+ }\r
+ } else if (RpcRequest.EDIT_TEAM.equals(reqType)) {\r
+ // edit team\r
+ TeamModel model = deserialize(request, response, TeamModel.class);\r
+ // name parameter specifies original team name in event of rename\r
+ String teamname = objectName;\r
+ if (teamname == null) {\r
+ teamname = model.name;\r
+ }\r
+ try {\r
+ gitblitManager.updateTeamModel(teamname, model, false);\r
+ } catch (GitBlitException e) {\r
+ response.setStatus(failureCode);\r
+ }\r
+ } else if (RpcRequest.DELETE_TEAM.equals(reqType)) {\r
+ // delete team\r
+ TeamModel model = deserialize(request, response, TeamModel.class);\r
+ if (!userManager.deleteTeam(model.name)) {\r
+ response.setStatus(failureCode);\r
+ }\r
+ } else if (RpcRequest.LIST_REPOSITORY_MEMBERS.equals(reqType)) {\r
+ // get repository members\r
+ RepositoryModel model = repositoryManager.getRepositoryModel(objectName);\r
+ result = repositoryManager.getRepositoryUsers(model);\r
+ } else if (RpcRequest.SET_REPOSITORY_MEMBERS.equals(reqType)) {\r
+ // rejected since 1.2.0\r
+ response.setStatus(failureCode);\r
+ } else if (RpcRequest.LIST_REPOSITORY_MEMBER_PERMISSIONS.equals(reqType)) {\r
+ // get repository member permissions\r
+ RepositoryModel model = repositoryManager.getRepositoryModel(objectName);\r
+ result = repositoryManager.getUserAccessPermissions(model);\r
+ } else if (RpcRequest.SET_REPOSITORY_MEMBER_PERMISSIONS.equals(reqType)) {\r
+ // set the repository permissions for the specified users\r
+ RepositoryModel model = repositoryManager.getRepositoryModel(objectName);\r
+ Collection<RegistrantAccessPermission> permissions = deserialize(request, response, RpcUtils.REGISTRANT_PERMISSIONS_TYPE);\r
+ result = repositoryManager.setUserAccessPermissions(model, permissions);\r
+ } else if (RpcRequest.LIST_REPOSITORY_TEAMS.equals(reqType)) {\r
+ // get repository teams\r
+ RepositoryModel model = repositoryManager.getRepositoryModel(objectName);\r
+ result = repositoryManager.getRepositoryTeams(model);\r
+ } else if (RpcRequest.SET_REPOSITORY_TEAMS.equals(reqType)) {\r
+ // rejected since 1.2.0\r
+ response.setStatus(failureCode);\r
+ } else if (RpcRequest.LIST_REPOSITORY_TEAM_PERMISSIONS.equals(reqType)) {\r
+ // get repository team permissions\r
+ RepositoryModel model = repositoryManager.getRepositoryModel(objectName);\r
+ result = repositoryManager.getTeamAccessPermissions(model);\r
+ } else if (RpcRequest.SET_REPOSITORY_TEAM_PERMISSIONS.equals(reqType)) {\r
+ // set the repository permissions for the specified teams\r
+ RepositoryModel model = repositoryManager.getRepositoryModel(objectName);\r
+ Collection<RegistrantAccessPermission> permissions = deserialize(request, response, RpcUtils.REGISTRANT_PERMISSIONS_TYPE);\r
+ result = repositoryManager.setTeamAccessPermissions(model, permissions);\r
+ } else if (RpcRequest.LIST_FEDERATION_REGISTRATIONS.equals(reqType)) {\r
+ // return the list of federation registrations\r
+ if (allowAdmin) {\r
+ result = federationManager.getFederationRegistrations();\r
+ } else {\r
+ response.sendError(notAllowedCode);\r
+ }\r
+ } else if (RpcRequest.LIST_FEDERATION_RESULTS.equals(reqType)) {\r
+ // return the list of federation result registrations\r
+ if (allowAdmin && federationManager.canFederate()) {\r
+ result = federationManager.getFederationResultRegistrations();\r
+ } else {\r
+ response.sendError(notAllowedCode);\r
+ }\r
+ } else if (RpcRequest.LIST_FEDERATION_PROPOSALS.equals(reqType)) {\r
+ // return the list of federation proposals\r
+ if (allowAdmin && federationManager.canFederate()) {\r
+ result = federationManager.getPendingFederationProposals();\r
+ } else {\r
+ response.sendError(notAllowedCode);\r
+ }\r
+ } else if (RpcRequest.LIST_FEDERATION_SETS.equals(reqType)) {\r
+ // return the list of federation sets\r
+ if (allowAdmin && federationManager.canFederate()) {\r
+ String gitblitUrl = HttpUtils.getGitblitURL(request);\r
+ result = federationManager.getFederationSets(gitblitUrl);\r
+ } else {\r
+ response.sendError(notAllowedCode);\r
+ }\r
+ } else if (RpcRequest.LIST_SETTINGS.equals(reqType)) {\r
+ // return the server's settings\r
+ ServerSettings serverSettings = runtimeManager.getSettingsModel();\r
+ if (allowAdmin) {\r
+ // return all settings\r
+ result = serverSettings;\r
+ } else {\r
+ // anonymous users get a few settings to allow browser launching\r
+ List<String> keys = new ArrayList<String>();\r
+ keys.add(Keys.web.siteName);\r
+ keys.add(Keys.web.mountParameters);\r
+ keys.add(Keys.web.syndicationEntries);\r
+\r
+ if (allowManagement) {\r
+ // keys necessary for repository and/or user management\r
+ keys.add(Keys.realm.minPasswordLength);\r
+ keys.add(Keys.realm.passwordStorage);\r
+ keys.add(Keys.federation.sets);\r
+ }\r
+ // build the settings\r
+ ServerSettings managementSettings = new ServerSettings();\r
+ for (String key : keys) {\r
+ managementSettings.add(serverSettings.get(key));\r
+ }\r
+ if (allowManagement) {\r
+ managementSettings.pushScripts = serverSettings.pushScripts;\r
+ }\r
+ result = managementSettings;\r
+ }\r
+ } else if (RpcRequest.EDIT_SETTINGS.equals(reqType)) {\r
+ // update settings on the server\r
+ if (allowAdmin) {\r
+ Map<String, String> map = deserialize(request, response,\r
+ RpcUtils.SETTINGS_TYPE);\r
+ runtimeManager.updateSettings(map);\r
+ } else {\r
+ response.sendError(notAllowedCode);\r
+ }\r
+ } else if (RpcRequest.LIST_STATUS.equals(reqType)) {\r
+ // return the server's status information\r
+ if (allowAdmin) {\r
+ result = runtimeManager.getStatus();\r
+ } else {\r
+ response.sendError(notAllowedCode);\r
+ }\r
+ } else if (RpcRequest.CLEAR_REPOSITORY_CACHE.equals(reqType)) {\r
+ // clear the repository list cache\r
+ if (allowManagement) {\r
+ repositoryManager.resetRepositoryListCache();\r
+ } else {\r
+ response.sendError(notAllowedCode);\r
+ }\r
+ }\r
+\r
+ // send the result of the request\r
+ serialize(response, result);\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2013 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.servlet;\r
+\r
+import java.io.IOException;\r
+import java.text.MessageFormat;\r
+\r
+import javax.inject.Inject;\r
+import javax.inject.Singleton;\r
+import javax.servlet.ServletException;\r
+import javax.servlet.http.HttpServlet;\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpServletResponse;\r
+\r
+import com.gitblit.Constants;\r
+import com.gitblit.IStoredSettings;\r
+import com.gitblit.Keys;\r
+import com.gitblit.Keys.fanout;\r
+import com.gitblit.manager.IRepositoryManager;\r
+import com.gitblit.manager.IRuntimeManager;\r
+import com.gitblit.manager.ISessionManager;\r
+import com.gitblit.manager.IUserManager;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.StringUtils;\r
+\r
+/**\r
+ * Handles requests for Sparkleshare Invites\r
+ *\r
+ * @author James Moger\r
+ *\r
+ */\r
+@Singleton\r
+public class SparkleShareInviteServlet extends HttpServlet {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ private final IStoredSettings settings;\r
+\r
+ private final IUserManager userManager;\r
+\r
+ private final ISessionManager sessionManager;\r
+\r
+ private final IRepositoryManager repositoryManager;\r
+\r
+ @Inject\r
+ public SparkleShareInviteServlet(\r
+ IRuntimeManager runtimeManager,\r
+ IUserManager userManager,\r
+ ISessionManager sessionManager,\r
+ IRepositoryManager repositoryManager) {\r
+\r
+ super();\r
+ this.settings = runtimeManager.getSettings();\r
+ this.userManager = userManager;\r
+ this.sessionManager = sessionManager;\r
+ this.repositoryManager = repositoryManager;\r
+ }\r
+\r
+ @Override\r
+ protected void doPost(HttpServletRequest request, HttpServletResponse response)\r
+ throws ServletException, java.io.IOException {\r
+ processRequest(request, response);\r
+ }\r
+\r
+ @Override\r
+ protected void doGet(HttpServletRequest request, HttpServletResponse response)\r
+ throws ServletException, IOException {\r
+ processRequest(request, response);\r
+ }\r
+\r
+ protected void processRequest(javax.servlet.http.HttpServletRequest request,\r
+ javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,\r
+ java.io.IOException {\r
+\r
+ // extract repo name from request\r
+ String repoUrl = request.getPathInfo().substring(1);\r
+\r
+ // trim trailing .xml\r
+ if (repoUrl.endsWith(".xml")) {\r
+ repoUrl = repoUrl.substring(0, repoUrl.length() - 4);\r
+ }\r
+\r
+ String servletPath = Constants.GIT_PATH;\r
+\r
+ int schemeIndex = repoUrl.indexOf("://") + 3;\r
+ String host = repoUrl.substring(0, repoUrl.indexOf('/', schemeIndex));\r
+ String path = repoUrl.substring(repoUrl.indexOf(servletPath) + servletPath.length());\r
+ String username = null;\r
+ int fetchIndex = repoUrl.indexOf('@');\r
+ if (fetchIndex > -1) {\r
+ username = repoUrl.substring(schemeIndex, fetchIndex);\r
+ }\r
+ UserModel user;\r
+ if (StringUtils.isEmpty(username)) {\r
+ user = sessionManager.authenticate(request);\r
+ } else {\r
+ user = userManager.getUserModel(username);\r
+ }\r
+ if (user == null) {\r
+ user = UserModel.ANONYMOUS;\r
+ username = "";\r
+ }\r
+\r
+ // ensure that the requested repository exists\r
+ RepositoryModel model = repositoryManager.getRepositoryModel(path);\r
+ if (model == null) {\r
+ response.setStatus(HttpServletResponse.SC_NOT_FOUND);\r
+ response.getWriter().append(MessageFormat.format("Repository \"{0}\" not found!", path));\r
+ return;\r
+ }\r
+\r
+ StringBuilder sb = new StringBuilder();\r
+ sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");\r
+ sb.append("<sparkleshare><invite>\n");\r
+ sb.append(MessageFormat.format("<address>{0}</address>\n", host));\r
+ sb.append(MessageFormat.format("<remote_path>{0}{1}</remote_path>\n", servletPath, model.name));\r
+ if (settings.getInteger(Keys.fanout.port, 0) > 0) {\r
+ // Gitblit is running it's own fanout service for pubsub notifications\r
+ sb.append(MessageFormat.format("<announcements_url>tcp://{0}:{1}</announcements_url>\n", request.getServerName(), settings.getString(Keys.fanout.port, "")));\r
+ }\r
+ sb.append("</invite></sparkleshare>\n");\r
+\r
+ // write invite to client\r
+ response.setContentType("application/xml");\r
+ response.setContentLength(sb.length());\r
+ response.getWriter().append(sb.toString());\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.servlet;\r
+\r
+import java.io.IOException;\r
+import java.text.MessageFormat;\r
+\r
+import javax.inject.Inject;\r
+import javax.inject.Singleton;\r
+import javax.servlet.FilterChain;\r
+import javax.servlet.ServletException;\r
+import javax.servlet.ServletRequest;\r
+import javax.servlet.ServletResponse;\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpServletResponse;\r
+\r
+import com.gitblit.Constants.AccessRestrictionType;\r
+import com.gitblit.manager.IProjectManager;\r
+import com.gitblit.manager.IRepositoryManager;\r
+import com.gitblit.manager.IRuntimeManager;\r
+import com.gitblit.manager.ISessionManager;\r
+import com.gitblit.models.ProjectModel;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.UserModel;\r
+\r
+/**\r
+ * The SyndicationFilter is an AuthenticationFilter which ensures that feed\r
+ * requests for projects or view-restricted repositories have proper authentication\r
+ * credentials and are authorized for the requested feed.\r
+ *\r
+ * @author James Moger\r
+ *\r
+ */\r
+@Singleton\r
+public class SyndicationFilter extends AuthenticationFilter {\r
+\r
+ private final IRuntimeManager runtimeManager;\r
+ private final IRepositoryManager repositoryManager;\r
+ private final IProjectManager projectManager;\r
+\r
+ @Inject\r
+ public SyndicationFilter(\r
+ IRuntimeManager runtimeManager,\r
+ ISessionManager sessionManager,\r
+ IRepositoryManager repositoryManager,\r
+ IProjectManager projectManager) {\r
+\r
+ super(sessionManager);\r
+ this.runtimeManager = runtimeManager;\r
+ this.repositoryManager = repositoryManager;\r
+ this.projectManager = projectManager;\r
+ }\r
+\r
+ /**\r
+ * Extract the repository name from the url.\r
+ *\r
+ * @param url\r
+ * @return repository name\r
+ */\r
+ protected String extractRequestedName(String url) {\r
+ if (url.indexOf('?') > -1) {\r
+ return url.substring(0, url.indexOf('?'));\r
+ }\r
+ return url;\r
+ }\r
+\r
+ /**\r
+ * doFilter does the actual work of preprocessing the request to ensure that\r
+ * the user may proceed.\r
+ *\r
+ * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,\r
+ * javax.servlet.ServletResponse, javax.servlet.FilterChain)\r
+ */\r
+ @Override\r
+ public void doFilter(final ServletRequest request, final ServletResponse response,\r
+ final FilterChain chain) throws IOException, ServletException {\r
+\r
+ HttpServletRequest httpRequest = (HttpServletRequest) request;\r
+ HttpServletResponse httpResponse = (HttpServletResponse) response;\r
+\r
+ String fullUrl = getFullUrl(httpRequest);\r
+ String name = extractRequestedName(fullUrl);\r
+\r
+ ProjectModel project = projectManager.getProjectModel(name);\r
+ RepositoryModel model = null;\r
+\r
+ if (project == null) {\r
+ // try loading a repository model\r
+ model = repositoryManager.getRepositoryModel(name);\r
+ if (model == null) {\r
+ // repository not found. send 404.\r
+ logger.info(MessageFormat.format("ARF: {0} ({1})", fullUrl,\r
+ HttpServletResponse.SC_NOT_FOUND));\r
+ httpResponse.sendError(HttpServletResponse.SC_NOT_FOUND);\r
+ return;\r
+ }\r
+ }\r
+\r
+ // Wrap the HttpServletRequest with the AccessRestrictionRequest which\r
+ // overrides the servlet container user principal methods.\r
+ // JGit requires either:\r
+ //\r
+ // 1. servlet container authenticated user\r
+ // 2. http.receivepack = true in each repository's config\r
+ //\r
+ // Gitblit must conditionally authenticate users per-repository so just\r
+ // enabling http.receivepack is insufficient.\r
+ AuthenticatedRequest authenticatedRequest = new AuthenticatedRequest(httpRequest);\r
+ UserModel user = getUser(httpRequest);\r
+ if (user != null) {\r
+ authenticatedRequest.setUser(user);\r
+ }\r
+\r
+ // BASIC authentication challenge and response processing\r
+ if (model != null) {\r
+ if (model.accessRestriction.atLeast(AccessRestrictionType.VIEW)) {\r
+ if (user == null) {\r
+ // challenge client to provide credentials. send 401.\r
+ if (runtimeManager.isDebugMode()) {\r
+ logger.info(MessageFormat.format("ARF: CHALLENGE {0}", fullUrl));\r
+ }\r
+ httpResponse.setHeader("WWW-Authenticate", CHALLENGE);\r
+ httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);\r
+ return;\r
+ } else {\r
+ // check user access for request\r
+ if (user.canView(model)) {\r
+ // authenticated request permitted.\r
+ // pass processing to the restricted servlet.\r
+ newSession(authenticatedRequest, httpResponse);\r
+ logger.info(MessageFormat.format("ARF: {0} ({1}) authenticated", fullUrl,\r
+ HttpServletResponse.SC_CONTINUE));\r
+ chain.doFilter(authenticatedRequest, httpResponse);\r
+ return;\r
+ }\r
+ // valid user, but not for requested access. send 403.\r
+ if (runtimeManager.isDebugMode()) {\r
+ logger.info(MessageFormat.format("ARF: {0} forbidden to access {1}",\r
+ user.username, fullUrl));\r
+ }\r
+ httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);\r
+ return;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (runtimeManager.isDebugMode()) {\r
+ logger.info(MessageFormat.format("ARF: {0} ({1}) unauthenticated", fullUrl,\r
+ HttpServletResponse.SC_CONTINUE));\r
+ }\r
+ // unauthenticated request permitted.\r
+ // pass processing to the restricted servlet.\r
+ chain.doFilter(authenticatedRequest, httpResponse);\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.servlet;\r
+\r
+import java.text.MessageFormat;\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.Collections;\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+import javax.inject.Inject;\r
+import javax.inject.Singleton;\r
+import javax.servlet.http.HttpServlet;\r
+\r
+import org.eclipse.jgit.lib.ObjectId;\r
+import org.eclipse.jgit.lib.Repository;\r
+import org.eclipse.jgit.revwalk.RevCommit;\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+import com.gitblit.Constants;\r
+import com.gitblit.IStoredSettings;\r
+import com.gitblit.Keys;\r
+import com.gitblit.manager.IProjectManager;\r
+import com.gitblit.manager.IRepositoryManager;\r
+import com.gitblit.manager.IRuntimeManager;\r
+import com.gitblit.models.FeedEntryModel;\r
+import com.gitblit.models.ProjectModel;\r
+import com.gitblit.models.RefModel;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.servlet.AuthenticationFilter.AuthenticatedRequest;\r
+import com.gitblit.utils.HttpUtils;\r
+import com.gitblit.utils.JGitUtils;\r
+import com.gitblit.utils.MessageProcessor;\r
+import com.gitblit.utils.StringUtils;\r
+import com.gitblit.utils.SyndicationUtils;\r
+\r
+/**\r
+ * SyndicationServlet generates RSS 2.0 feeds and feed links.\r
+ *\r
+ * Access to this servlet is protected by the SyndicationFilter.\r
+ *\r
+ * @author James Moger\r
+ *\r
+ */\r
+@Singleton\r
+public class SyndicationServlet extends HttpServlet {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ private transient Logger logger = LoggerFactory.getLogger(SyndicationServlet.class);\r
+\r
+ private final IStoredSettings settings;\r
+\r
+ private final IRepositoryManager repositoryManager;\r
+\r
+ private final IProjectManager projectManager;\r
+\r
+ @Inject\r
+ public SyndicationServlet(\r
+ IRuntimeManager runtimeManager,\r
+ IRepositoryManager repositoryManager,\r
+ IProjectManager projectManager) {\r
+\r
+ super();\r
+ this.settings = runtimeManager.getSettings();\r
+ this.repositoryManager = repositoryManager;\r
+ this.projectManager = projectManager;\r
+ }\r
+\r
+ /**\r
+ * Create a feed link for the specified repository and branch/tag/commit id.\r
+ *\r
+ * @param baseURL\r
+ * @param repository\r
+ * the repository name\r
+ * @param objectId\r
+ * the branch, tag, or first commit for the feed\r
+ * @param length\r
+ * the number of commits to include in the feed\r
+ * @return an RSS feed url\r
+ */\r
+ public static String asLink(String baseURL, String repository, String objectId, int length) {\r
+ if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {\r
+ baseURL = baseURL.substring(0, baseURL.length() - 1);\r
+ }\r
+ StringBuilder url = new StringBuilder();\r
+ url.append(baseURL);\r
+ url.append(Constants.SYNDICATION_PATH);\r
+ url.append(repository);\r
+ if (!StringUtils.isEmpty(objectId) || length > 0) {\r
+ StringBuilder parameters = new StringBuilder("?");\r
+ if (StringUtils.isEmpty(objectId)) {\r
+ parameters.append("l=");\r
+ parameters.append(length);\r
+ } else {\r
+ parameters.append("h=");\r
+ parameters.append(objectId);\r
+ if (length > 0) {\r
+ parameters.append("&l=");\r
+ parameters.append(length);\r
+ }\r
+ }\r
+ url.append(parameters);\r
+ }\r
+ return url.toString();\r
+ }\r
+\r
+ /**\r
+ * Determines the appropriate title for a feed.\r
+ *\r
+ * @param repository\r
+ * @param objectId\r
+ * @return title of the feed\r
+ */\r
+ public static String getTitle(String repository, String objectId) {\r
+ String id = objectId;\r
+ if (!StringUtils.isEmpty(id)) {\r
+ if (id.startsWith(org.eclipse.jgit.lib.Constants.R_HEADS)) {\r
+ id = id.substring(org.eclipse.jgit.lib.Constants.R_HEADS.length());\r
+ } else if (id.startsWith(org.eclipse.jgit.lib.Constants.R_REMOTES)) {\r
+ id = id.substring(org.eclipse.jgit.lib.Constants.R_REMOTES.length());\r
+ } else if (id.startsWith(org.eclipse.jgit.lib.Constants.R_TAGS)) {\r
+ id = id.substring(org.eclipse.jgit.lib.Constants.R_TAGS.length());\r
+ }\r
+ }\r
+ return MessageFormat.format("{0} ({1})", repository, id);\r
+ }\r
+\r
+ /**\r
+ * Generates the feed content.\r
+ *\r
+ * @param request\r
+ * @param response\r
+ * @throws javax.servlet.ServletException\r
+ * @throws java.io.IOException\r
+ */\r
+ private void processRequest(javax.servlet.http.HttpServletRequest request,\r
+ javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,\r
+ java.io.IOException {\r
+\r
+ String servletUrl = request.getContextPath() + request.getServletPath();\r
+ String url = request.getRequestURI().substring(servletUrl.length());\r
+ if (url.charAt(0) == '/' && url.length() > 1) {\r
+ url = url.substring(1);\r
+ }\r
+ String repositoryName = url;\r
+ String objectId = request.getParameter("h");\r
+ String l = request.getParameter("l");\r
+ String page = request.getParameter("pg");\r
+ String searchString = request.getParameter("s");\r
+ Constants.SearchType searchType = Constants.SearchType.COMMIT;\r
+ if (!StringUtils.isEmpty(request.getParameter("st"))) {\r
+ Constants.SearchType type = Constants.SearchType.forName(request.getParameter("st"));\r
+ if (type != null) {\r
+ searchType = type;\r
+ }\r
+ }\r
+ int length = settings.getInteger(Keys.web.syndicationEntries, 25);\r
+ if (StringUtils.isEmpty(objectId)) {\r
+ objectId = org.eclipse.jgit.lib.Constants.HEAD;\r
+ }\r
+ if (!StringUtils.isEmpty(l)) {\r
+ try {\r
+ length = Integer.parseInt(l);\r
+ } catch (NumberFormatException x) {\r
+ }\r
+ }\r
+ int offset = 0;\r
+ if (!StringUtils.isEmpty(page)) {\r
+ try {\r
+ offset = length * Integer.parseInt(page);\r
+ } catch (NumberFormatException x) {\r
+ }\r
+ }\r
+\r
+ response.setContentType("application/rss+xml; charset=UTF-8");\r
+\r
+ boolean isProjectFeed = false;\r
+ String feedName = null;\r
+ String feedTitle = null;\r
+ String feedDescription = null;\r
+\r
+ List<String> repositories = null;\r
+ if (repositoryName.indexOf('/') == -1 && !repositoryName.toLowerCase().endsWith(".git")) {\r
+ // try to find a project\r
+ UserModel user = null;\r
+ if (request instanceof AuthenticatedRequest) {\r
+ user = ((AuthenticatedRequest) request).getUser();\r
+ }\r
+ ProjectModel project = projectManager.getProjectModel(repositoryName, user);\r
+ if (project != null) {\r
+ isProjectFeed = true;\r
+ repositories = new ArrayList<String>(project.repositories);\r
+\r
+ // project feed\r
+ feedName = project.name;\r
+ feedTitle = project.title;\r
+ feedDescription = project.description;\r
+ }\r
+ }\r
+\r
+ if (repositories == null) {\r
+ // could not find project, assume this is a repository\r
+ repositories = Arrays.asList(repositoryName);\r
+ }\r
+\r
+\r
+ boolean mountParameters = settings.getBoolean(Keys.web.mountParameters, true);\r
+ String urlPattern;\r
+ if (mountParameters) {\r
+ // mounted parameters\r
+ urlPattern = "{0}/commit/{1}/{2}";\r
+ } else {\r
+ // parameterized parameters\r
+ urlPattern = "{0}/commit/?r={1}&h={2}";\r
+ }\r
+ String gitblitUrl = HttpUtils.getGitblitURL(request);\r
+ char fsc = settings.getChar(Keys.web.forwardSlashCharacter, '/');\r
+\r
+ List<FeedEntryModel> entries = new ArrayList<FeedEntryModel>();\r
+\r
+ for (String name : repositories) {\r
+ Repository repository = repositoryManager.getRepository(name);\r
+ RepositoryModel model = repositoryManager.getRepositoryModel(name);\r
+\r
+ if (repository == null) {\r
+ if (model.isCollectingGarbage) {\r
+ logger.warn(MessageFormat.format("Temporarily excluding {0} from feed, busy collecting garbage", name));\r
+ }\r
+ continue;\r
+ }\r
+ if (!isProjectFeed) {\r
+ // single-repository feed\r
+ feedName = model.name;\r
+ feedTitle = model.name;\r
+ feedDescription = model.description;\r
+ }\r
+\r
+ List<RevCommit> commits;\r
+ if (StringUtils.isEmpty(searchString)) {\r
+ // standard log/history lookup\r
+ commits = JGitUtils.getRevLog(repository, objectId, offset, length);\r
+ } else {\r
+ // repository search\r
+ commits = JGitUtils.searchRevlogs(repository, objectId, searchString, searchType,\r
+ offset, length);\r
+ }\r
+ Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository, model.showRemoteBranches);\r
+ MessageProcessor processor = new MessageProcessor(settings);\r
+\r
+ // convert RevCommit to SyndicatedEntryModel\r
+ for (RevCommit commit : commits) {\r
+ FeedEntryModel entry = new FeedEntryModel();\r
+ entry.title = commit.getShortMessage();\r
+ entry.author = commit.getAuthorIdent().getName();\r
+ entry.link = MessageFormat.format(urlPattern, gitblitUrl,\r
+ StringUtils.encodeURL(model.name.replace('/', fsc)), commit.getName());\r
+ entry.published = commit.getCommitterIdent().getWhen();\r
+ entry.contentType = "text/html";\r
+ String message = processor.processCommitMessage(model, commit.getFullMessage());\r
+ entry.content = message;\r
+ entry.repository = model.name;\r
+ entry.branch = objectId;\r
+ entry.tags = new ArrayList<String>();\r
+\r
+ // add commit id and parent commit ids\r
+ entry.tags.add("commit:" + commit.getName());\r
+ for (RevCommit parent : commit.getParents()) {\r
+ entry.tags.add("parent:" + parent.getName());\r
+ }\r
+\r
+ // add refs to tabs list\r
+ List<RefModel> refs = allRefs.get(commit.getId());\r
+ if (refs != null && refs.size() > 0) {\r
+ for (RefModel ref : refs) {\r
+ entry.tags.add("ref:" + ref.getName());\r
+ }\r
+ }\r
+ entries.add(entry);\r
+ }\r
+ }\r
+\r
+ // sort & truncate the feed\r
+ Collections.sort(entries);\r
+ if (entries.size() > length) {\r
+ // clip the list\r
+ entries = entries.subList(0, length);\r
+ }\r
+\r
+ String feedLink;\r
+ if (isProjectFeed) {\r
+ // project feed\r
+ if (mountParameters) {\r
+ // mounted url\r
+ feedLink = MessageFormat.format("{0}/project/{1}", gitblitUrl,\r
+ StringUtils.encodeURL(feedName));\r
+ } else {\r
+ // parameterized url\r
+ feedLink = MessageFormat.format("{0}/project/?p={1}", gitblitUrl,\r
+ StringUtils.encodeURL(feedName));\r
+ }\r
+ } else {\r
+ // repository feed\r
+ if (mountParameters) {\r
+ // mounted url\r
+ feedLink = MessageFormat.format("{0}/summary/{1}", gitblitUrl,\r
+ StringUtils.encodeURL(feedName));\r
+ } else {\r
+ // parameterized url\r
+ feedLink = MessageFormat.format("{0}/summary/?r={1}", gitblitUrl,\r
+ StringUtils.encodeURL(feedName));\r
+ }\r
+ }\r
+\r
+ try {\r
+ SyndicationUtils.toRSS(gitblitUrl, feedLink, getTitle(feedTitle, objectId),\r
+ feedDescription, entries, response.getOutputStream());\r
+ } catch (Exception e) {\r
+ logger.error("An error occurred during feed generation", e);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ protected void doPost(javax.servlet.http.HttpServletRequest request,\r
+ javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,\r
+ java.io.IOException {\r
+ processRequest(request, response);\r
+ }\r
+\r
+ @Override\r
+ protected void doGet(javax.servlet.http.HttpServletRequest request,\r
+ javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,\r
+ java.io.IOException {\r
+ processRequest(request, response);\r
+ }\r
+}\r
import org.apache.wicket.markup.html.link.ExternalLink;\r
\r
import com.gitblit.Keys;\r
-import com.gitblit.SyndicationServlet;\r
import com.gitblit.models.ProjectModel;\r
import com.gitblit.models.RepositoryModel;\r
import com.gitblit.models.UserModel;\r
+import com.gitblit.servlet.SyndicationServlet;\r
import com.gitblit.utils.MarkdownUtils;\r
import com.gitblit.utils.StringUtils;\r
import com.gitblit.wicket.CacheControl;\r
import com.gitblit.Constants;\r
import com.gitblit.GitBlitException;\r
import com.gitblit.Keys;\r
-import com.gitblit.PagesServlet;\r
-import com.gitblit.SyndicationServlet;\r
import com.gitblit.models.ProjectModel;\r
import com.gitblit.models.RefModel;\r
import com.gitblit.models.RepositoryModel;\r
import com.gitblit.models.SubmoduleModel;\r
import com.gitblit.models.UserModel;\r
import com.gitblit.models.UserRepositoryPreferences;\r
+import com.gitblit.servlet.PagesServlet;\r
+import com.gitblit.servlet.SyndicationServlet;\r
import com.gitblit.utils.ArrayUtils;\r
import com.gitblit.utils.DeepCopier;\r
import com.gitblit.utils.JGitUtils;\r
import org.eclipse.jgit.lib.Repository;\r
\r
import com.gitblit.Constants;\r
-import com.gitblit.SyndicationServlet;\r
import com.gitblit.models.RefModel;\r
import com.gitblit.models.RepositoryModel;\r
import com.gitblit.models.UserModel;\r
+import com.gitblit.servlet.SyndicationServlet;\r
import com.gitblit.utils.CommitCache;\r
import com.gitblit.utils.JGitUtils;\r
import com.gitblit.utils.RefLogUtils;\r
import org.apache.wicket.markup.repeater.data.DataView;\r
import org.apache.wicket.markup.repeater.data.ListDataProvider;\r
\r
-import com.gitblit.DownloadZipServlet;\r
-import com.gitblit.DownloadZipServlet.Format;\r
+import com.gitblit.servlet.DownloadZipServlet;\r
+import com.gitblit.servlet.DownloadZipServlet.Format;\r
import com.gitblit.Keys;\r
\r
public class CompressedDownloadsPanel extends BasePanel {\r
import org.eclipse.jgit.lib.Repository;\r
import org.eclipse.jgit.revwalk.RevCommit;\r
\r
-import com.gitblit.BranchGraphServlet;\r
import com.gitblit.Constants;\r
import com.gitblit.Keys;\r
import com.gitblit.models.RefModel;\r
+import com.gitblit.servlet.BranchGraphServlet;\r
import com.gitblit.utils.JGitUtils;\r
import com.gitblit.utils.StringUtils;\r
import com.gitblit.wicket.ExternalImage;\r
\r
import com.gitblit.Constants.AccessRestrictionType;\r
import com.gitblit.Keys;\r
-import com.gitblit.SyndicationServlet;\r
import com.gitblit.models.RepositoryModel;\r
import com.gitblit.models.UserModel;\r
+import com.gitblit.servlet.SyndicationServlet;\r
import com.gitblit.utils.ArrayUtils;\r
import com.gitblit.utils.StringUtils;\r
import com.gitblit.wicket.GitBlitWebSession;\r
\r
import com.gitblit.Constants.AccessRestrictionType;\r
import com.gitblit.Keys;\r
-import com.gitblit.SyndicationServlet;\r
import com.gitblit.models.ProjectModel;\r
import com.gitblit.models.RepositoryModel;\r
import com.gitblit.models.UserModel;\r
+import com.gitblit.servlet.SyndicationServlet;\r
import com.gitblit.utils.ArrayUtils;\r
import com.gitblit.utils.ModelUtils;\r
import com.gitblit.utils.StringUtils;\r
import org.junit.runners.Suite;\r
import org.junit.runners.Suite.SuiteClasses;\r
\r
-import com.gitblit.GitBlit;\r
import com.gitblit.GitBlitException;\r
import com.gitblit.GitBlitServer;\r
import com.gitblit.manager.IRepositoryManager;\r
import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.servlet.GitblitContext;\r
import com.gitblit.utils.JGitUtils;\r
\r
/**\r
\r
private static void showRemoteBranches(String repositoryName) {\r
try {\r
- IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);\r
+ IRepositoryManager repositoryManager = GitblitContext.getManager(IRepositoryManager.class);\r
RepositoryModel model = repositoryManager.getRepositoryModel(repositoryName);\r
model.showRemoteBranches = true;\r
repositoryManager.updateRepositoryModel(model.name, model, false);\r
\r
private static void automaticallyTagBranchTips(String repositoryName) {\r
try {\r
- IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);\r
+ IRepositoryManager repositoryManager = GitblitContext.getManager(IRepositoryManager.class);\r
RepositoryModel model = repositoryManager.getRepositoryModel(repositoryName);\r
model.useIncrementalPushTags = true;\r
repositoryManager.updateRepositoryModel(model.name, model, false);\r
*/
package com.gitblit.tests;
-import com.gitblit.GitBlit;
import com.gitblit.IStoredSettings;
import com.gitblit.manager.IFederationManager;
import com.gitblit.manager.IGitblitManager;
import com.gitblit.manager.IRuntimeManager;
import com.gitblit.manager.ISessionManager;
import com.gitblit.manager.IUserManager;
+import com.gitblit.servlet.GitblitContext;
public class GitblitUnitTest extends org.junit.Assert {
}
public static IRuntimeManager runtime() {
- return GitBlit.getManager(IRuntimeManager.class);
+ return GitblitContext.getManager(IRuntimeManager.class);
}
public static INotificationManager notifier() {
- return GitBlit.getManager(INotificationManager.class);
+ return GitblitContext.getManager(INotificationManager.class);
}
public static IUserManager users() {
- return GitBlit.getManager(IUserManager.class);
+ return GitblitContext.getManager(IUserManager.class);
}
public static ISessionManager session() {
- return GitBlit.getManager(ISessionManager.class);
+ return GitblitContext.getManager(ISessionManager.class);
}
public static IRepositoryManager repositories() {
- return GitBlit.getManager(IRepositoryManager.class);
+ return GitblitContext.getManager(IRepositoryManager.class);
}
public static IProjectManager projects() {
- return GitBlit.getManager(IProjectManager.class);
+ return GitblitContext.getManager(IProjectManager.class);
}
public static IFederationManager federation() {
- return GitBlit.getManager(IFederationManager.class);
+ return GitblitContext.getManager(IFederationManager.class);
}
public static IGitblitManager gitblit() {
- return GitBlit.getManager(IGitblitManager.class);
+ return GitblitContext.getManager(IGitblitManager.class);
}
}
import org.junit.Test;\r
\r
import com.gitblit.Keys;\r
-import com.gitblit.LuceneExecutor;\r
import com.gitblit.manager.RepositoryManager;\r
import com.gitblit.manager.RuntimeManager;\r
import com.gitblit.manager.UserManager;\r
import com.gitblit.models.RefModel;\r
import com.gitblit.models.RepositoryModel;\r
import com.gitblit.models.SearchResult;\r
+import com.gitblit.service.LuceneService;\r
import com.gitblit.tests.mock.MemorySettings;\r
import com.gitblit.utils.FileUtils;\r
import com.gitblit.utils.JGitUtils;\r
*/\r
public class LuceneExecutorTest extends GitblitUnitTest {\r
\r
- LuceneExecutor lucene;\r
+ LuceneService lucene;\r
\r
- private LuceneExecutor newLuceneExecutor() {\r
+ private LuceneService newLuceneExecutor() {\r
MemorySettings settings = new MemorySettings();\r
settings.put(Keys.git.repositoriesFolder, GitBlitSuite.REPOSITORIES);\r
RuntimeManager runtime = new RuntimeManager(settings);\r
UserManager users = new UserManager(runtime);\r
RepositoryManager repos = new RepositoryManager(runtime, users);\r
- return new LuceneExecutor(settings, repos);\r
+ return new LuceneService(settings, repos);\r
}\r
\r
private RepositoryModel newRepositoryModel(Repository repository) {\r
\r
import com.gitblit.FileSettings;\r
import com.gitblit.Keys;\r
-import com.gitblit.MailExecutor;\r
+import com.gitblit.service.MailService;\r
\r
public class MailTest extends GitblitUnitTest {\r
\r
@Test\r
public void testSendMail() throws Exception {\r
FileSettings settings = new FileSettings("mailtest.properties");\r
- MailExecutor mail = new MailExecutor(settings);\r
+ MailService mail = new MailService(settings);\r
Message message = mail.createMessage(settings.getStrings(Keys.mail.adminAddresses));\r
message.setSubject("Test");\r
message.setText("Lägger till andra stycket i ny fil. UTF-8 encoded");\r
import com.gitblit.GitBlitException.NotAllowedException;\r
import com.gitblit.GitBlitException.UnauthorizedException;\r
import com.gitblit.Keys;\r
-import com.gitblit.RpcServlet;\r
import com.gitblit.models.FederationModel;\r
import com.gitblit.models.FederationProposal;\r
import com.gitblit.models.FederationSet;\r
import com.gitblit.models.ServerStatus;\r
import com.gitblit.models.TeamModel;\r
import com.gitblit.models.UserModel;\r
+import com.gitblit.servlet.RpcServlet;\r
import com.gitblit.utils.RpcUtils;\r
\r
/**\r
import com.beust.jcommander.JCommander;
import com.beust.jcommander.ParameterException;
-import com.gitblit.GitBlit;
import com.gitblit.GitBlitServer;
import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
+import com.gitblit.servlet.GitblitContext;
public class GitBlitServer4UITests extends GitBlitServer {
}
@Override
- protected GitBlit newGitblit(IStoredSettings settings, File baseFolder) {
+ protected GitblitContext newGitblit(IStoredSettings settings, File baseFolder) {
settings.overrideSetting(Keys.web.allowLuceneIndexing, false);
- return new GitBlit(settings, baseFolder);
+ return new GitblitContext(settings, baseFolder);
}
}