]> source.dussan.org Git - gitblit.git/commitdiff
Moved servlets and services to separate packages
authorJames Moger <james.moger@gitblit.com>
Thu, 21 Nov 2013 23:32:21 +0000 (18:32 -0500)
committerJames Moger <james.moger@gitblit.com>
Fri, 29 Nov 2013 16:05:51 +0000 (11:05 -0500)
Change-Id: I5f0f50f4ae7d332e9f724a2e6f074fa71f646035

72 files changed:
src/main/java/com/gitblit/AccessRestrictionFilter.java [deleted file]
src/main/java/com/gitblit/AuthenticationFilter.java [deleted file]
src/main/java/com/gitblit/BranchGraphServlet.java [deleted file]
src/main/java/com/gitblit/Constants.java
src/main/java/com/gitblit/DaggerModule.java
src/main/java/com/gitblit/DownloadZipFilter.java [deleted file]
src/main/java/com/gitblit/DownloadZipServlet.java [deleted file]
src/main/java/com/gitblit/EnforceAuthenticationFilter.java [deleted file]
src/main/java/com/gitblit/FederationClient.java
src/main/java/com/gitblit/FederationPullExecutor.java [deleted file]
src/main/java/com/gitblit/FederationServlet.java [deleted file]
src/main/java/com/gitblit/GCExecutor.java [deleted file]
src/main/java/com/gitblit/GitBlit.java [deleted file]
src/main/java/com/gitblit/GitBlitServer.java
src/main/java/com/gitblit/GitFilter.java [deleted file]
src/main/java/com/gitblit/InjectionContextListener.java [deleted file]
src/main/java/com/gitblit/JsonServlet.java [deleted file]
src/main/java/com/gitblit/LogoServlet.java [deleted file]
src/main/java/com/gitblit/LuceneExecutor.java [deleted file]
src/main/java/com/gitblit/MailExecutor.java [deleted file]
src/main/java/com/gitblit/MirrorExecutor.java [deleted file]
src/main/java/com/gitblit/PagesFilter.java [deleted file]
src/main/java/com/gitblit/PagesServlet.java [deleted file]
src/main/java/com/gitblit/RobotsTxtServlet.java [deleted file]
src/main/java/com/gitblit/RpcFilter.java [deleted file]
src/main/java/com/gitblit/RpcServlet.java [deleted file]
src/main/java/com/gitblit/SparkleShareInviteServlet.java [deleted file]
src/main/java/com/gitblit/SyndicationFilter.java [deleted file]
src/main/java/com/gitblit/SyndicationServlet.java [deleted file]
src/main/java/com/gitblit/authority/GitblitAuthority.java
src/main/java/com/gitblit/dagger/DaggerContextListener.java
src/main/java/com/gitblit/manager/NotificationManager.java
src/main/java/com/gitblit/manager/RepositoryManager.java
src/main/java/com/gitblit/manager/ServicesManager.java
src/main/java/com/gitblit/service/FederationPullService.java [new file with mode: 0644]
src/main/java/com/gitblit/service/GarbageCollectorService.java [new file with mode: 0644]
src/main/java/com/gitblit/service/LuceneService.java [new file with mode: 0644]
src/main/java/com/gitblit/service/MailService.java [new file with mode: 0644]
src/main/java/com/gitblit/service/MirrorService.java [new file with mode: 0644]
src/main/java/com/gitblit/servlet/AccessRestrictionFilter.java [new file with mode: 0644]
src/main/java/com/gitblit/servlet/AuthenticationFilter.java [new file with mode: 0644]
src/main/java/com/gitblit/servlet/BranchGraphServlet.java [new file with mode: 0644]
src/main/java/com/gitblit/servlet/DownloadZipFilter.java [new file with mode: 0644]
src/main/java/com/gitblit/servlet/DownloadZipServlet.java [new file with mode: 0644]
src/main/java/com/gitblit/servlet/EnforceAuthenticationFilter.java [new file with mode: 0644]
src/main/java/com/gitblit/servlet/FederationServlet.java [new file with mode: 0644]
src/main/java/com/gitblit/servlet/GitFilter.java [new file with mode: 0644]
src/main/java/com/gitblit/servlet/GitblitContext.java [new file with mode: 0644]
src/main/java/com/gitblit/servlet/InjectionContextListener.java [new file with mode: 0644]
src/main/java/com/gitblit/servlet/JsonServlet.java [new file with mode: 0644]
src/main/java/com/gitblit/servlet/LogoServlet.java [new file with mode: 0644]
src/main/java/com/gitblit/servlet/PagesFilter.java [new file with mode: 0644]
src/main/java/com/gitblit/servlet/PagesServlet.java [new file with mode: 0644]
src/main/java/com/gitblit/servlet/RobotsTxtServlet.java [new file with mode: 0644]
src/main/java/com/gitblit/servlet/RpcFilter.java [new file with mode: 0644]
src/main/java/com/gitblit/servlet/RpcServlet.java [new file with mode: 0644]
src/main/java/com/gitblit/servlet/SparkleShareInviteServlet.java [new file with mode: 0644]
src/main/java/com/gitblit/servlet/SyndicationFilter.java [new file with mode: 0644]
src/main/java/com/gitblit/servlet/SyndicationServlet.java [new file with mode: 0644]
src/main/java/com/gitblit/wicket/pages/ProjectPage.java
src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
src/main/java/com/gitblit/wicket/panels/BranchesPanel.java
src/main/java/com/gitblit/wicket/panels/CompressedDownloadsPanel.java
src/main/java/com/gitblit/wicket/panels/LogPanel.java
src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.java
src/main/java/com/gitblit/wicket/panels/RepositoriesPanel.java
src/test/java/com/gitblit/tests/GitBlitSuite.java
src/test/java/com/gitblit/tests/GitblitUnitTest.java
src/test/java/com/gitblit/tests/LuceneExecutorTest.java
src/test/java/com/gitblit/tests/MailTest.java
src/test/java/com/gitblit/tests/RpcTests.java
src/test/java/de/akquinet/devops/GitBlitServer4UITests.java

diff --git a/src/main/java/com/gitblit/AccessRestrictionFilter.java b/src/main/java/com/gitblit/AccessRestrictionFilter.java
deleted file mode 100644 (file)
index 5f0baed..0000000
+++ /dev/null
@@ -1,245 +0,0 @@
-/*\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
diff --git a/src/main/java/com/gitblit/AuthenticationFilter.java b/src/main/java/com/gitblit/AuthenticationFilter.java
deleted file mode 100644 (file)
index 96d880f..0000000
+++ /dev/null
@@ -1,194 +0,0 @@
-/*\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
diff --git a/src/main/java/com/gitblit/BranchGraphServlet.java b/src/main/java/com/gitblit/BranchGraphServlet.java
deleted file mode 100644 (file)
index 58a5778..0000000
+++ /dev/null
@@ -1,405 +0,0 @@
-/*\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
index 1451ccf1a011a752f1075ae0ff78488536cad75b..43c60a39616497e3c9e0dff92ac0bf92b1189aa8 100644 (file)
@@ -395,7 +395,7 @@ public class Constants {
        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
index 0cbb739aa433fed665abdfcb7fb1b4d1f02eb6b1..1fad779fb6f792e4500611bc8c393a0ff95166ee 100644 (file)
@@ -38,6 +38,21 @@ import com.gitblit.manager.RuntimeManager;
 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;
 
diff --git a/src/main/java/com/gitblit/DownloadZipFilter.java b/src/main/java/com/gitblit/DownloadZipFilter.java
deleted file mode 100644 (file)
index 914d89e..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-/*\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
diff --git a/src/main/java/com/gitblit/DownloadZipServlet.java b/src/main/java/com/gitblit/DownloadZipServlet.java
deleted file mode 100644 (file)
index d629dcf..0000000
+++ /dev/null
@@ -1,232 +0,0 @@
-/*\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
diff --git a/src/main/java/com/gitblit/EnforceAuthenticationFilter.java b/src/main/java/com/gitblit/EnforceAuthenticationFilter.java
deleted file mode 100644 (file)
index 48fc005..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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() {
-       }
-}
index f32fa0f239e1875e1c0453dfabf6a1afc798719b..66b378ab696389b196eecf9170c612d7fb4e618f 100644 (file)
@@ -29,6 +29,7 @@ import com.gitblit.manager.RepositoryManager;
 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
@@ -95,7 +96,7 @@ public class FederationClient {
                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
diff --git a/src/main/java/com/gitblit/FederationPullExecutor.java b/src/main/java/com/gitblit/FederationPullExecutor.java
deleted file mode 100644 (file)
index bbe73cf..0000000
+++ /dev/null
@@ -1,486 +0,0 @@
-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
diff --git a/src/main/java/com/gitblit/FederationServlet.java b/src/main/java/com/gitblit/FederationServlet.java
deleted file mode 100644 (file)
index 31e3c0e..0000000
+++ /dev/null
@@ -1,290 +0,0 @@
-/*\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
diff --git a/src/main/java/com/gitblit/GCExecutor.java b/src/main/java/com/gitblit/GCExecutor.java
deleted file mode 100644 (file)
index 3ab9895..0000000
+++ /dev/null
@@ -1,246 +0,0 @@
-/*\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
diff --git a/src/main/java/com/gitblit/GitBlit.java b/src/main/java/com/gitblit/GitBlit.java
deleted file mode 100644 (file)
index ca676ff..0000000
+++ /dev/null
@@ -1,426 +0,0 @@
-/*
- * 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
-                                               }
-                                       }
-                               }
-                       }
-               }
-       }
-}
index fe29804de2c679de0854aac330aa391d69c29be4..522fb57979c2e3b5bde16a535dee52df28479d9e 100644 (file)
@@ -61,6 +61,7 @@ import com.beust.jcommander.ParameterException;
 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
@@ -410,7 +411,7 @@ public class GitBlitServer {
                }\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
@@ -429,8 +430,8 @@ public class GitBlitServer {
                }\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
diff --git a/src/main/java/com/gitblit/GitFilter.java b/src/main/java/com/gitblit/GitFilter.java
deleted file mode 100644 (file)
index ba8443d..0000000
+++ /dev/null
@@ -1,265 +0,0 @@
-/*\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
diff --git a/src/main/java/com/gitblit/InjectionContextListener.java b/src/main/java/com/gitblit/InjectionContextListener.java
deleted file mode 100644 (file)
index 712ae64..0000000
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * 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;
-       }
-}
diff --git a/src/main/java/com/gitblit/JsonServlet.java b/src/main/java/com/gitblit/JsonServlet.java
deleted file mode 100644 (file)
index 286b139..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-/*\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
diff --git a/src/main/java/com/gitblit/LogoServlet.java b/src/main/java/com/gitblit/LogoServlet.java
deleted file mode 100644 (file)
index 17b05cf..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/*\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
diff --git a/src/main/java/com/gitblit/LuceneExecutor.java b/src/main/java/com/gitblit/LuceneExecutor.java
deleted file mode 100644 (file)
index b7b71c5..0000000
+++ /dev/null
@@ -1,1252 +0,0 @@
-/*\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
diff --git a/src/main/java/com/gitblit/MailExecutor.java b/src/main/java/com/gitblit/MailExecutor.java
deleted file mode 100644 (file)
index b1ba3b6..0000000
+++ /dev/null
@@ -1,227 +0,0 @@
-/*\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
diff --git a/src/main/java/com/gitblit/MirrorExecutor.java b/src/main/java/com/gitblit/MirrorExecutor.java
deleted file mode 100644 (file)
index 6c951b9..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-/*\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
diff --git a/src/main/java/com/gitblit/PagesFilter.java b/src/main/java/com/gitblit/PagesFilter.java
deleted file mode 100644 (file)
index a322af2..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-/*\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
diff --git a/src/main/java/com/gitblit/PagesServlet.java b/src/main/java/com/gitblit/PagesServlet.java
deleted file mode 100644 (file)
index ba919e0..0000000
+++ /dev/null
@@ -1,314 +0,0 @@
-/*\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
diff --git a/src/main/java/com/gitblit/RobotsTxtServlet.java b/src/main/java/com/gitblit/RobotsTxtServlet.java
deleted file mode 100644 (file)
index c07aa1d..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*\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
diff --git a/src/main/java/com/gitblit/RpcFilter.java b/src/main/java/com/gitblit/RpcFilter.java
deleted file mode 100644 (file)
index c4b6451..0000000
+++ /dev/null
@@ -1,165 +0,0 @@
-/*\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
diff --git a/src/main/java/com/gitblit/RpcServlet.java b/src/main/java/com/gitblit/RpcServlet.java
deleted file mode 100644 (file)
index a3629b9..0000000
+++ /dev/null
@@ -1,406 +0,0 @@
-/*\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
diff --git a/src/main/java/com/gitblit/SparkleShareInviteServlet.java b/src/main/java/com/gitblit/SparkleShareInviteServlet.java
deleted file mode 100644 (file)
index 1cd997d..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-/*\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
diff --git a/src/main/java/com/gitblit/SyndicationFilter.java b/src/main/java/com/gitblit/SyndicationFilter.java
deleted file mode 100644 (file)
index 10b8810..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-/*\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
diff --git a/src/main/java/com/gitblit/SyndicationServlet.java b/src/main/java/com/gitblit/SyndicationServlet.java
deleted file mode 100644 (file)
index 397545f..0000000
+++ /dev/null
@@ -1,349 +0,0 @@
-/*\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
index 668ca98216a02aca0ca18a47db913aebf95f62c5..36c016de749498a2676234f5f916a78992cf23b4 100644 (file)
@@ -90,10 +90,10 @@ import com.gitblit.FileSettings;
 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
@@ -131,7 +131,7 @@ public class GitblitAuthority extends JFrame implements X509Log {
 \r
        private TableRowSorter<UserCertificateTableModel> defaultSorter;\r
 \r
-       private MailExecutor mail;\r
+       private MailService mail;\r
 \r
        private JButton certificateDefaultsButton;\r
 \r
@@ -258,7 +258,7 @@ public class GitblitAuthority extends JFrame implements X509Log {
                        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
index 3c2688603cab155bc6652d06cc49a6340acbab31..6a04e4a7085a83e8a28d44ff98f0aa4183213ee8 100644 (file)
@@ -18,7 +18,7 @@ package com.gitblit.dagger;
 import javax.servlet.ServletContext;
 import javax.servlet.ServletContextEvent;
 
-import com.gitblit.InjectionContextListener;
+import com.gitblit.servlet.InjectionContextListener;
 
 import dagger.ObjectGraph;
 
index e38e1f1fbecdfd0febad6d96073ffd29ecc9e21f..a226d1a08b10ba685bc5517c4668a89414b6b286 100644 (file)
@@ -32,7 +32,7 @@ import org.slf4j.LoggerFactory;
 
 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
@@ -50,11 +50,11 @@ public class NotificationManager implements INotificationManager {
 
        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
index 4845e23e04350112d61a0d20f825f76911ca339b..438d885c092a6a563c927aecc04ba87a97720c49 100644 (file)
@@ -63,12 +63,9 @@ import com.gitblit.Constants.CommitMessageRenderer;
 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;
@@ -77,6 +74,9 @@ import com.gitblit.models.RepositoryModel;
 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;
@@ -118,11 +118,11 @@ public class RepositoryManager implements IRepositoryManager {
 
        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,
@@ -1644,7 +1644,7 @@ public class RepositoryManager implements IRepositoryManager {
        }
 
        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);
@@ -1652,7 +1652,7 @@ public class RepositoryManager implements IRepositoryManager {
 
        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();
@@ -1680,7 +1680,7 @@ public class RepositoryManager implements IRepositoryManager {
        }
 
        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) {
index 82a8a0430b078964dab261817e9f50fe209f0ef2..d04b27743a3f306022e99dce6b88cb3d94f04d2f 100644 (file)
@@ -28,7 +28,6 @@ import org.slf4j.Logger;
 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;
@@ -37,6 +36,7 @@ import com.gitblit.fanout.FanoutService;
 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;
 
@@ -163,7 +163,7 @@ public class ServicesManager implements IServicesManager {
                }
        }
 
-       private class FederationPuller extends FederationPullExecutor {
+       private class FederationPuller extends FederationPullService {
 
                public FederationPuller(FederationModel registration) {
                        super(Arrays.asList(registration));
diff --git a/src/main/java/com/gitblit/service/FederationPullService.java b/src/main/java/com/gitblit/service/FederationPullService.java
new file mode 100644 (file)
index 0000000..91fe015
--- /dev/null
@@ -0,0 +1,491 @@
+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
diff --git a/src/main/java/com/gitblit/service/GarbageCollectorService.java b/src/main/java/com/gitblit/service/GarbageCollectorService.java
new file mode 100644 (file)
index 0000000..8dbd8d8
--- /dev/null
@@ -0,0 +1,249 @@
+/*\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
diff --git a/src/main/java/com/gitblit/service/LuceneService.java b/src/main/java/com/gitblit/service/LuceneService.java
new file mode 100644 (file)
index 0000000..97fe9e1
--- /dev/null
@@ -0,0 +1,1254 @@
+/*\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
diff --git a/src/main/java/com/gitblit/service/MailService.java b/src/main/java/com/gitblit/service/MailService.java
new file mode 100644 (file)
index 0000000..1d5e91f
--- /dev/null
@@ -0,0 +1,229 @@
+/*\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
diff --git a/src/main/java/com/gitblit/service/MirrorService.java b/src/main/java/com/gitblit/service/MirrorService.java
new file mode 100644 (file)
index 0000000..9833d93
--- /dev/null
@@ -0,0 +1,178 @@
+/*\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
diff --git a/src/main/java/com/gitblit/servlet/AccessRestrictionFilter.java b/src/main/java/com/gitblit/servlet/AccessRestrictionFilter.java
new file mode 100644 (file)
index 0000000..d5ded33
--- /dev/null
@@ -0,0 +1,245 @@
+/*\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
diff --git a/src/main/java/com/gitblit/servlet/AuthenticationFilter.java b/src/main/java/com/gitblit/servlet/AuthenticationFilter.java
new file mode 100644 (file)
index 0000000..214f204
--- /dev/null
@@ -0,0 +1,195 @@
+/*\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
diff --git a/src/main/java/com/gitblit/servlet/BranchGraphServlet.java b/src/main/java/com/gitblit/servlet/BranchGraphServlet.java
new file mode 100644 (file)
index 0000000..3efe60d
--- /dev/null
@@ -0,0 +1,409 @@
+/*\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
diff --git a/src/main/java/com/gitblit/servlet/DownloadZipFilter.java b/src/main/java/com/gitblit/servlet/DownloadZipFilter.java
new file mode 100644 (file)
index 0000000..f2064e3
--- /dev/null
@@ -0,0 +1,123 @@
+/*\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
diff --git a/src/main/java/com/gitblit/servlet/DownloadZipServlet.java b/src/main/java/com/gitblit/servlet/DownloadZipServlet.java
new file mode 100644 (file)
index 0000000..d26f73e
--- /dev/null
@@ -0,0 +1,236 @@
+/*\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
diff --git a/src/main/java/com/gitblit/servlet/EnforceAuthenticationFilter.java b/src/main/java/com/gitblit/servlet/EnforceAuthenticationFilter.java
new file mode 100644 (file)
index 0000000..d690fd2
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * 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() {
+       }
+}
diff --git a/src/main/java/com/gitblit/servlet/FederationServlet.java b/src/main/java/com/gitblit/servlet/FederationServlet.java
new file mode 100644 (file)
index 0000000..e86e5d6
--- /dev/null
@@ -0,0 +1,296 @@
+/*\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
diff --git a/src/main/java/com/gitblit/servlet/GitFilter.java b/src/main/java/com/gitblit/servlet/GitFilter.java
new file mode 100644 (file)
index 0000000..f39d68f
--- /dev/null
@@ -0,0 +1,270 @@
+/*\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
diff --git a/src/main/java/com/gitblit/servlet/GitblitContext.java b/src/main/java/com/gitblit/servlet/GitblitContext.java
new file mode 100644 (file)
index 0000000..7325012
--- /dev/null
@@ -0,0 +1,432 @@
+/*
+ * 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
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+}
diff --git a/src/main/java/com/gitblit/servlet/InjectionContextListener.java b/src/main/java/com/gitblit/servlet/InjectionContextListener.java
new file mode 100644 (file)
index 0000000..b0e1098
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * 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;
+       }
+}
diff --git a/src/main/java/com/gitblit/servlet/JsonServlet.java b/src/main/java/com/gitblit/servlet/JsonServlet.java
new file mode 100644 (file)
index 0000000..abc0f29
--- /dev/null
@@ -0,0 +1,131 @@
+/*\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
diff --git a/src/main/java/com/gitblit/servlet/LogoServlet.java b/src/main/java/com/gitblit/servlet/LogoServlet.java
new file mode 100644 (file)
index 0000000..e91fad0
--- /dev/null
@@ -0,0 +1,107 @@
+/*\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
diff --git a/src/main/java/com/gitblit/servlet/PagesFilter.java b/src/main/java/com/gitblit/servlet/PagesFilter.java
new file mode 100644 (file)
index 0000000..23e7859
--- /dev/null
@@ -0,0 +1,141 @@
+/*\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
diff --git a/src/main/java/com/gitblit/servlet/PagesServlet.java b/src/main/java/com/gitblit/servlet/PagesServlet.java
new file mode 100644 (file)
index 0000000..6146f13
--- /dev/null
@@ -0,0 +1,318 @@
+/*\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
diff --git a/src/main/java/com/gitblit/servlet/RobotsTxtServlet.java b/src/main/java/com/gitblit/servlet/RobotsTxtServlet.java
new file mode 100644 (file)
index 0000000..c93675a
--- /dev/null
@@ -0,0 +1,75 @@
+/*\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
diff --git a/src/main/java/com/gitblit/servlet/RpcFilter.java b/src/main/java/com/gitblit/servlet/RpcFilter.java
new file mode 100644 (file)
index 0000000..02f419f
--- /dev/null
@@ -0,0 +1,169 @@
+/*\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
diff --git a/src/main/java/com/gitblit/servlet/RpcServlet.java b/src/main/java/com/gitblit/servlet/RpcServlet.java
new file mode 100644 (file)
index 0000000..3a115b1
--- /dev/null
@@ -0,0 +1,413 @@
+/*\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
diff --git a/src/main/java/com/gitblit/servlet/SparkleShareInviteServlet.java b/src/main/java/com/gitblit/servlet/SparkleShareInviteServlet.java
new file mode 100644 (file)
index 0000000..4b8b24f
--- /dev/null
@@ -0,0 +1,142 @@
+/*\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
diff --git a/src/main/java/com/gitblit/servlet/SyndicationFilter.java b/src/main/java/com/gitblit/servlet/SyndicationFilter.java
new file mode 100644 (file)
index 0000000..adf9ba9
--- /dev/null
@@ -0,0 +1,168 @@
+/*\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
diff --git a/src/main/java/com/gitblit/servlet/SyndicationServlet.java b/src/main/java/com/gitblit/servlet/SyndicationServlet.java
new file mode 100644 (file)
index 0000000..739ee2d
--- /dev/null
@@ -0,0 +1,352 @@
+/*\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
index 22396bec0f82b95a32144557f64ab7124e79a922..d2f2fd2f49c2a0e627e336a21a95d86bd1b40a77 100644 (file)
@@ -26,10 +26,10 @@ import org.apache.wicket.markup.html.basic.Label;
 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
index 0552c3042312834d48df7f016a2aa6534dc79b83..dd6763d52b855b01b2aa7013f984b6e549a65bbd 100644 (file)
@@ -48,14 +48,14 @@ import org.slf4j.LoggerFactory;
 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
index 7fec0eae17571143bdc35ff4cb972a67ff1ab8f5..28751fab2383441eb4cfc7c1adba462207de4c38 100644 (file)
@@ -37,10 +37,10 @@ import org.eclipse.jgit.lib.Ref;
 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
index 72a032e008477eec640a6481fd4b98ea08200b4b..0d5864e446b4ec9d6b3d246babab0c42735a63c2 100644 (file)
@@ -22,8 +22,8 @@ import org.apache.wicket.markup.repeater.Item;
 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
index 7c91d22d073f8a9ab117ec097db7a5b61c733f23..f8d980e53243c4278c9de83eeb01c1a66e7472ed 100644 (file)
@@ -32,10 +32,10 @@ import org.eclipse.jgit.lib.ObjectId;
 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
index 1c79760a9dbc461e1e869a022f20228a58b4446c..a0f8ac480739925f4b8233ebb5643e7328128fc3 100644 (file)
@@ -29,9 +29,9 @@ import org.apache.wicket.markup.html.panel.Fragment;
 \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
index 8de84927245af96dcb75eaab6e575c5e8a1e392b..be5d960baf457771feef3df2bf5a39dbae5921e8 100644 (file)
@@ -44,10 +44,10 @@ import org.apache.wicket.model.Model;
 \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
index f623032ccffc6cad5304d6aba8c596bb3d023ae2..57e94e51a56e7172b77f1c5322fdac2e970780a4 100644 (file)
@@ -33,11 +33,11 @@ import org.junit.runner.RunWith;
 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
@@ -180,7 +180,7 @@ public class GitBlitSuite {
 \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
@@ -191,7 +191,7 @@ public class GitBlitSuite {
 \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
index fc70e107454ad08bd1ac81bb9eac2998c27a3b3b..500e9b9f8a540626893c86e178473c5d6dd3de61 100644 (file)
@@ -15,7 +15,6 @@
  */
 package com.gitblit.tests;
 
-import com.gitblit.GitBlit;
 import com.gitblit.IStoredSettings;
 import com.gitblit.manager.IFederationManager;
 import com.gitblit.manager.IGitblitManager;
@@ -25,6 +24,7 @@ import com.gitblit.manager.IRepositoryManager;
 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 {
@@ -34,34 +34,34 @@ 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);
        }
 }
index 4041a0603c8e3c66e56a89f09074b8a2bcd1c4d8..8ffe84697ac80236b556ef5d8713b33b9f239178 100644 (file)
@@ -24,13 +24,13 @@ import org.junit.Before;
 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
@@ -43,15 +43,15 @@ import com.gitblit.utils.JGitUtils;
  */\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
index 7598568bca2a2f87d146fd69f1ad9ec65b41ad77..df09ca59fa8393a3121205c7ca090a2ac307dadd 100644 (file)
@@ -21,14 +21,14 @@ import org.junit.Test;
 \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
index 33e8505e2fe89f25d18cae78130e5699d4bfdccc..e1ba9072c1b4b60c3661f3f7a47196549000e43c 100644 (file)
@@ -35,7 +35,6 @@ import com.gitblit.Constants.RegistrantType;
 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
@@ -45,6 +44,7 @@ import com.gitblit.models.ServerSettings;
 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
index 6ee98a038a690591b8265dd4da81444623ac67a9..5e361b99c323bbe33f114335f3180602cfe3afeb 100644 (file)
@@ -6,10 +6,10 @@ import java.util.List;
 
 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 {
 
@@ -54,8 +54,8 @@ 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);
        }
 }