James Moger
2012-01-05 cb285cbfddfc0b633d6b8cdb4dc0d2bd2b8b51ef
src/com/gitblit/FederationServlet.java
@@ -15,31 +15,28 @@
 */
package com.gitblit;
import java.io.BufferedReader;
import java.io.File;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gitblit.Constants.FederationRequest;
import com.gitblit.Constants.FederationToken;
import com.gitblit.models.FederationModel;
import com.gitblit.models.FederationProposal;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.FederationUtils;
import com.gitblit.utils.FileUtils;
import com.gitblit.utils.HttpUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.utils.TimeUtils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
/**
 * Handles federation requests.
@@ -47,69 +44,37 @@
 * @author James Moger
 * 
 */
public class FederationServlet extends HttpServlet {
public class FederationServlet extends JsonServlet {
   private static final long serialVersionUID = 1L;
   private transient Logger logger = LoggerFactory.getLogger(FederationServlet.class);
   public FederationServlet() {
      super();
   }
   /**
    * Returns an url to this servlet for the specified parameters.
    *
    * @param sourceURL
    *            the url of the source gitblit instance
    * @param token
    *            the federation token of the source gitblit instance
    * @param req
    *            the pull type request
    */
   public static String asFederationLink(String sourceURL, String token, FederationRequest req) {
      return asFederationLink(sourceURL, null, token, req, null);
   }
   /**
    *
    * @param remoteURL
    *            the url of the remote gitblit instance
    * @param tokenType
    *            the type of federation token of a gitblit instance
    * @param token
    *            the federation token of a gitblit instance
    * @param req
    *            the pull type request
    * @param myURL
    *            the url of this gitblit instance
    * @return
    */
   public static String asFederationLink(String remoteURL, FederationToken tokenType,
         String token, FederationRequest req, String myURL) {
      if (remoteURL.length() > 0 && remoteURL.charAt(remoteURL.length() - 1) == '/') {
         remoteURL = remoteURL.substring(0, remoteURL.length() - 1);
      }
      if (req == null) {
         req = FederationRequest.PULL_REPOSITORIES;
      }
      return remoteURL + Constants.FEDERATION_PATH + "?req=" + req.name().toLowerCase()
            + (token == null ? "" : ("&token=" + token))
            + (tokenType == null ? "" : ("&tokenType=" + tokenType.name().toLowerCase()))
            + (myURL == null ? "" : ("&url=" + StringUtils.encodeURL(myURL)));
   }
   /**
    * Returns the list of repositories for federation requests.
    * Processes a federation request.
    * 
    * @param request
    * @param response
    * @throws javax.servlet.ServletException
    * @throws java.io.IOException
    */
   private void processRequest(javax.servlet.http.HttpServletRequest request,
   @Override
   protected void processRequest(javax.servlet.http.HttpServletRequest request,
         javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
         java.io.IOException {
      FederationRequest reqType = FederationRequest.fromName(request.getParameter("req"));
      logger.info(MessageFormat.format("Federation {0} request from {1}", reqType,
            request.getRemoteAddr()));
      if (FederationRequest.POKE.equals(reqType)) {
         // Gitblit always responds to POKE requests to verify a connection
         logger.info("Received federation POKE from " + request.getRemoteAddr());
         return;
      }
      if (!GitBlit.getBoolean(Keys.git.enableGitServlet, true)) {
         logger.warn(Keys.git.enableGitServlet + " must be set TRUE for federation requests.");
         response.sendError(HttpServletResponse.SC_FORBIDDEN);
@@ -124,38 +89,32 @@
         return;
      }
      String token = request.getParameter("token");
      FederationRequest reqType = FederationRequest.fromName(request.getParameter("req"));
      logger.info(MessageFormat.format("Federation {0} request from {1}", reqType,
            request.getRemoteAddr()));
      if (FederationRequest.PROPOSAL.equals(reqType)) {
         // Receive a gitblit federation proposal
         BufferedReader reader = request.getReader();
         StringBuilder json = new StringBuilder();
         String line = null;
         while ((line = reader.readLine()) != null) {
            json.append(line);
         }
         reader.close();
         // check to see if we have proposal data
         if (json.length() == 0) {
            logger.error(MessageFormat.format("Failed to receive proposal data from {0}",
                  request.getRemoteAddr()));
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
         FederationProposal proposal = deserialize(request, response, FederationProposal.class);
         if (proposal == null) {
            return;
         }
         // deserialize the proposal
         Gson gson = new Gson();
         FederationProposal proposal = gson.fromJson(json.toString(), FederationProposal.class);
         // reject proposal, if not receipt prohibited
         if (!GitBlit.getBoolean(Keys.federation.allowProposals, false)) {
            logger.error(MessageFormat.format("Rejected {0} federation proposal from {1}",
                  proposal.tokenType.name(), proposal.url));
            response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
            return;
         }
         // poke the origin Gitblit instance that is proposing federation
         boolean poked = false;
         try {
            poked = FederationUtils.poke(proposal.url);
         } catch (Exception e) {
            logger.error("Failed to poke origin", e);
         }
         if (!poked) {
            logger.error(MessageFormat.format("Failed to send federation poke to {0}",
                  proposal.url));
            response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
            return;
         }
@@ -173,25 +132,13 @@
         String remoteId = StringUtils.decodeFromHtml(request.getParameter("url"));
         String identification = MessageFormat.format("{0} ({1})", remoteId,
               request.getRemoteAddr());
         BufferedReader reader = request.getReader();
         StringBuilder json = new StringBuilder();
         String line = null;
         while ((line = reader.readLine()) != null) {
            json.append(line);
         }
         reader.close();
         // check to see if we have repository data
         if (json.length() == 0) {
            logger.error(MessageFormat.format(
                  "Failed to receive pulled repositories list from {0}", identification));
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
         // deserialize the status data
         FederationModel results = deserialize(request, response, FederationModel.class);
         if (results == null) {
            return;
         }
         // deserialize the status data
         Gson gson = new Gson();
         FederationModel results = gson.fromJson(json.toString(), FederationModel.class);
         // setup the last and netx pull dates
         results.lastPull = new Date();
         int mins = TimeUtils.convertFrequencyToMinutes(results.frequency);
@@ -207,6 +154,7 @@
      }
      // Determine the federation tokens for this gitblit instance
      String token = request.getParameter("token");
      List<String> tokens = GitBlit.self().getFederationTokens();
      if (!tokens.contains(token)) {
         logger.warn(MessageFormat.format(
@@ -255,28 +203,62 @@
               }
            }
            result = users;
         } else if (FederationRequest.PULL_TEAMS.equals(reqType)) {
            // pull teams
            if (!GitBlit.self().validateFederationRequest(reqType, token)) {
               // invalid token to pull teams
               logger.warn(MessageFormat.format(
                     "Federation token from {0} not authorized to pull TEAMS",
                     request.getRemoteAddr()));
               response.sendError(HttpServletResponse.SC_FORBIDDEN);
               return;
            }
            List<String> teamnames = GitBlit.self().getAllTeamnames();
            List<TeamModel> teams = new ArrayList<TeamModel>();
            for (String teamname : teamnames) {
               TeamModel user = GitBlit.self().getTeamModel(teamname);
               teams.add(user);
            }
            result = teams;
         } else if (FederationRequest.PULL_SCRIPTS.equals(reqType)) {
            // pull scripts
            if (!GitBlit.self().validateFederationRequest(reqType, token)) {
               // invalid token to pull script
               logger.warn(MessageFormat.format(
                     "Federation token from {0} not authorized to pull SCRIPTS",
                     request.getRemoteAddr()));
               response.sendError(HttpServletResponse.SC_FORBIDDEN);
               return;
            }
            Map<String, String> scripts = new HashMap<String, String>();
            Set<String> names = new HashSet<String>();
            names.addAll(GitBlit.getStrings(Keys.groovy.preReceiveScripts));
            names.addAll(GitBlit.getStrings(Keys.groovy.postReceiveScripts));
            for (TeamModel team :  GitBlit.self().getAllTeams()) {
               names.addAll(team.preReceiveScripts);
               names.addAll(team.postReceiveScripts);
            }
            File scriptsFolder = GitBlit.getFileOrFolder(Keys.groovy.scriptsFolder, "groovy");
            for (String name : names) {
               File file = new File(scriptsFolder, name);
               if (!file.exists() && !file.getName().endsWith(".groovy")) {
                  file = new File(scriptsFolder, name + ".groovy");
               }
               if (file.exists()) {
                  // read the script
                  String content = FileUtils.readContent(file, "\n");
                  scripts.put(name, content);
               } else {
                  // missing script?!
                  logger.warn(MessageFormat.format("Failed to find push script \"{0}\"", name));
               }
            }
            result = scripts;
         }
      }
      if (result != null) {
         // Send JSON response
         Gson gson = new GsonBuilder().setPrettyPrinting().create();
         String json = gson.toJson(result);
         response.getWriter().append(json);
      }
   }
   @Override
   protected void doPost(javax.servlet.http.HttpServletRequest request,
         javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
         java.io.IOException {
      processRequest(request, response);
   }
   @Override
   protected void doGet(javax.servlet.http.HttpServletRequest request,
         javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
         java.io.IOException {
      processRequest(request, response);
      // send the result of the request
      serialize(response, result);
   }
}