content: /* The contents of this file are subject to the terms
   * of the Common Development and Distribution License
   * (the License). You may not use this file except in
   * compliance with the License.
   *
   * You can obtain a copy of the License at
   * https://faban.dev.java.net/public/CDDLv1.0.html or
   * install_dir/license.txt
   * See the License for the specific language governing
   * permission and limitations under the License.
   *
   * When distributing Covered Code, include this CDDL
   * Header Notice in each file and include the License file
   * at faban/src/legal/CDDLv1.0.txt.
   * If applicable, add the following below the CDDL Header,
   * with the fields enclosed by brackets [] replaced by
   * your own identifying information:
   * "Portions Copyrighted [year] [name of copyright owner]"
   *
   * $Id$
   *
   * Copyright 2005-2009 Sun Microsystems Inc. All Rights Reserved
   */
  package com.sun.faban.harness.webclient;
  
  import com.sun.faban.harness.common.Config;
  import com.sun.faban.harness.common.RunId;
  import org.apache.commons.fileupload.DiskFileUpload;
  import org.apache.commons.fileupload.FileItem;
  import org.apache.commons.fileupload.FileUploadException;
  import org.apache.commons.httpclient.HttpClient;
  import org.apache.commons.httpclient.HttpStatus;
  import org.apache.commons.httpclient.methods.PostMethod;
  import org.apache.commons.httpclient.methods.multipart.FilePart;
  import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
  import org.apache.commons.httpclient.methods.multipart.Part;
  import org.apache.commons.httpclient.methods.multipart.StringPart;
  
  import javax.servlet.ServletException;
  import javax.servlet.http.HttpServlet;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  import java.io.File;
  import java.io.IOException;
  import java.net.URL;
  import java.util.ArrayList;
  import java.util.Iterator;
  import java.util.List;
  import java.util.logging.Logger;
  
  import static com.sun.faban.harness.util.FileHelper.*;
  
  /**
   * The RunUploader represents the upload servlet as well as an upload client
   * maintained in a single class.
   *
   * @author Akara Sucharitakul
   */
  public class RunUploader extends HttpServlet {
  
      static Logger logger = Logger.getLogger(Deployer.class.getName());
  
      /**
       * Post method to upload the run.
       * @param request The servlet request
       * @param response The servlet response
       * @throws ServletException If the servlet fails
       * @throws IOException If there is an I/O error
       */
      public void doPost(HttpServletRequest request,
                            HttpServletResponse response)
              throws ServletException, IOException {
  
          String host = null;
          String key = null;
          boolean origin = false; // Whether the upload is to the original
          // run requestor. If so, key is needed.
  
          DiskFileUpload fu = new DiskFileUpload();
          // No maximum size
          fu.setSizeMax(-1);
          // maximum size that will be stored in memory
          fu.setSizeThreshold(4096);
          // the location for saving data that is larger than getSizeThreshold()
          fu.setRepositoryPath(Config.TMP_DIR);
  
          List fileItems = null;
          try {
              fileItems = fu.parseRequest(request);
          } catch (FileUploadException e) {
              throw new ServletException(e);
          }
          // assume we know there are two files. The first file is a small
          // text file, the second is unknown and is written to a file on
          // the server
          for (Iterator i = fileItems.iterator(); i.hasNext();) {
              FileItem item = (FileItem) i.next();
              String fieldName = item.getFieldName();
              if (item.isFormField()) {
                  if ("host".equals(fieldName)) {
                      host = item.getString();
                  } else if ("key".equals(fieldName)) {
                      key = item.getString();
                  } else if ("origin".equals(fieldName)) {
                      String value = item.getString();
                      origin = Boolean.parseBoolean(value);
                  }
                  continue;
              }
  
              if (host == null) {
                  logger.warning("Host not received on upload request!");
                  response.sendError(HttpServletResponse.SC_FORBIDDEN);
                  break;
              }
  
              // The host, origin, key info must be here before we receive
              // any file.
              if (origin) {
                  if (Config.daemonMode != Config.DaemonModes.POLLEE) {
                      logger.warning("Origin upload requested. Not pollee!");
                      response.sendError(HttpServletResponse.SC_FORBIDDEN);
                      break;
                  }
                  if (key == null) {
                      logger.warning("Origin upload requested. No key!");
                      response.sendError(HttpServletResponse.SC_FORBIDDEN);
                      break;
                  }
                  if (!RunRetriever.authenticate(host, key)) {
                      logger.warning("Origin upload requested. " +
                              "Host/key mismatch: " +host + '/' + key + "!");
                      response.sendError(HttpServletResponse.SC_FORBIDDEN);
                      break;
                  }
              }
  
              if (!"jarfile".equals(fieldName)) // ignore
                  continue;
  
              String fileName = item.getName();
  
              if (fileName == null) // We don't process files without names
                  continue;
  
              // Now, this name may have a path attached, dependent on the
              // source browser. We need to cover all possible clients...
              char[] pathSeparators = {'/', '\\'};
              // Well, if there is another separator we did not account for,
              // just add it above.
  
              for (int j = 0; j < pathSeparators.length; j++) {
                  int idx = fileName.lastIndexOf(pathSeparators[j]);
                  if (idx != -1) {
                      fileName = fileName.substring(idx + 1);
                      break;
                  }
              }
  
              // Ignore all non-jarfiles.
              if (!fileName.toLowerCase().endsWith(".jar"))
                  continue;
              File uploadFile = new File(Config.TMP_DIR, host + '.' + fileName);
              try {
                  item.write(uploadFile);
              } catch (Exception e) {
                  throw new ServletException(e);
              }
              File runTmp = unjarTmp(uploadFile);
  
              String runId = null;
  
              if (origin) {
                  // Change origin file to know where this run came from.
                  File metaInf = new File(runTmp, "META-INF");
                  File originFile = new File(metaInf, "origin");
                  if (!originFile.exists()) {
                      logger.warning("Origin upload requested. Origin file" +
                                     "does not exist!");
                      response.sendError(
                              HttpServletResponse.SC_NOT_ACCEPTABLE,
                              "Origin file does not exist!");
                      break;
                  }
  
                  RunId origRun;
                  try {
                      origRun = new RunId(readStringFromFile(originFile).trim());
                  } catch (IndexOutOfBoundsException e) {
                      response.sendError(HttpServletResponse.SC_NOT_ACCEPTABLE,
                              "Origin file error. " + e.getMessage());
                      break;
                  }
  
                  runId = origRun.getBenchName() + '.' + origRun.getRunSeq();
                  String localHost = origRun.getHostName();
                  if (!localHost.equals(Config.FABAN_HOST)) {
                      logger.warning("Origin upload requested. Origin host " +
                                     localHost + " does not match this host " +
                                     Config.FABAN_HOST + '!');
                      response.sendError(HttpServletResponse.SC_FORBIDDEN);
                      break;
                  }
                  writeStringToFile(runTmp.getName(), originFile);
              }  else {
                  runId = runTmp.getName();
              }
  
              if (recursiveCopy(runTmp, new File(Config.OUT_DIR, runId))) {
                  uploadFile.delete();
                  recursiveDelete(runTmp);
              } else {
                  logger.warning("Origin upload requested. Copy error!");
                  response.sendError(HttpServletResponse.SC_NOT_ACCEPTABLE);
                  break;
              }
  
              response.setStatus(HttpServletResponse.SC_CREATED);
              break;
          }
      }
  
  
      /**
       * Client call to upload the run back to the originating server.
       * This method does nothing if the run is local.
       * @param runId The id of the run
       * @throws IOException If the upload fails
       */
      public static void uploadIfOrigin(String runId) throws IOException {
  
          // 1. Check origin
          File originFile = new File(Config.OUT_DIR + File.separator + runId +
                      File.separator + "META-INF" + File.separator + "origin");
  
          if (!originFile.isFile())
              return; // Is local run, do nothing.
  
          String originSpec = readStringFromFile(originFile);
          int idx = originSpec.lastIndexOf('.');
          if (idx == -1) { // This is wrong, we do not accept this.
              logger.severe("Bad origin spec.");
              return;
          }
          idx = originSpec.lastIndexOf('.', idx - 1);
          if (idx == -1) {
              logger.severe("Bad origin spec.");
              return;
          }
  
          String host = originSpec.substring(0, idx);
          String key = null;
          URL target = null;
          String proxyHost = null;
          int proxyPort = -1;
  
          // Search the poll hosts for this origin.
          for (int i = 0; i < Config.pollHosts.length; i++) {
              Config.HostInfo pollHost = Config.pollHosts[i];
              if (host.equals(pollHost.name)) {
                  key = pollHost.key;
                  target = new URL(pollHost.url, "upload");
                  proxyHost = pollHost.proxyHost;
                  proxyPort = pollHost.proxyPort;
                  break;
              }
          }
  
          if (key == null) {
              logger.severe("Origin host/url/key not found!");
              return;
          }
  
          // 2. Jar up the run
          String[] files = new File(Config.OUT_DIR, runId).list();
          File jarFile = new File(Config.TMP_DIR, runId + ".jar");
          jar(Config.OUT_DIR + runId, files, jarFile.getAbsolutePath());
  
          // 3. Upload the run
          ArrayList<Part> params = new ArrayList<Part>();
          //MultipartPostMethod post = new MultipartPostMethod(target.toString());
          params.add(new StringPart("host", Config.FABAN_HOST));
          params.add(new StringPart("key", key));
          params.add(new StringPart("origin", "true"));
          params.add(new FilePart("jarfile", jarFile));
          Part[] parts = new Part[params.size()];
          parts = params.toArray(parts);
          PostMethod post = new PostMethod(target.toString());
          post.setRequestEntity(
                  new MultipartRequestEntity(parts, post.getParams()));
          HttpClient client = new HttpClient();
          if (proxyHost != null)
              client.getHostConfiguration().setProxy(proxyHost, proxyPort);
          client.getHttpConnectionManager().getParams().
                  setConnectionTimeout(5000);
          int status = client.executeMethod(post);
          if (status == HttpStatus.SC_FORBIDDEN)
              logger.severe("Server " + host + " denied permission to upload run "
                              + runId + '!');
          else if (status == HttpStatus.SC_NOT_ACCEPTABLE)
              logger.severe("Run " + runId + " origin error!");
          else if (status != HttpStatus.SC_CREATED)
              logger.severe("Server responded with status code " +
                      status + ". Status code 201 (SC_CREATED) expected.");
          jarFile.delete();
      }
  }
  