content: /**
   * ﻿Copyright (C) 2007 - 2019 52°North Initiative for Geospatial Open Source
   * Software GmbH
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 as published
   * by the Free Software Foundation.
   *
   * If the program is linked with libraries which are licensed under one of
   * the following licenses, the combination of the program with the linked
   * library is not considered a "derivative work" of the program:
   *
   *       • Apache License, version 2.0
   *       • Apache Software License, version 1.0
   *       • GNU Lesser General Public License, version 3
   *       • Mozilla Public License, versions 1.0, 1.1 and 2.0
   *       • Common Development and Distribution License (CDDL), version 1.0
   *
   * Therefore the distribution of the program linked with libraries licensed
   * under the aforementioned licenses, is permitted by the copyright holders
   * if the distribution is compliant with both the GNU General Public
   * License version 2 and the aforementioned licenses.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
   * Public License for more details.
   */
  package org.n52.wps.webadmin;
  
  import java.io.BufferedWriter;
  import java.io.File;
  import java.io.FileWriter;
  import java.io.IOException;
  import java.io.PrintWriter;
  import java.io.UnsupportedEncodingException;
  import java.lang.reflect.Method;
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.net.URLClassLoader;
  import java.net.URLDecoder;
  import java.nio.charset.Charset;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.List;
  
  import javax.servlet.ServletInputStream;
  import javax.servlet.http.HttpServletRequest;
  import javax.tools.JavaCompiler;
  import javax.tools.JavaFileObject;
  import javax.tools.StandardJavaFileManager;
  import javax.tools.ToolProvider;
  
  import org.apache.commons.lang.SystemUtils;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.n52.wps.PropertyDocument.Property;
  import org.n52.wps.commons.WPSConfig;
  
  /**
   * This Bean handles the fileupload of the xml configuration file
   * 
   * @author Florian van Keulen
   */
  public class ConfigUploadBean {
      private static transient Logger LOGGER = LoggerFactory.getLogger(ConfigUploadBean.class);
  
      private String savePath, filepath, filename, xmlFilepath, xmlFilename, realSavePath, boundary;
  
      private final String filenamePrefix = "userConf_";
  
      private int i, boundaryLength;
  
      private byte[] line;
  
      public String getFilenamePrefix()
      {
          return filenamePrefix;
      }
  
      private void setFilename(String s)
      {
          if (s == null) {
              return;
          }
  
          int pos = s.indexOf("filename=\"");
          if (pos != -1) {
              filepath = s.substring(pos + 10, s.length() - 1);
              // Windows browsers include the full path on the client
              // But Linux/Unix and Mac browsers only send the filename
              // test if this is from a Windows browser
              pos = filepath.lastIndexOf("\\");
              if (pos != -1) {
                  filename = filepath.substring(pos + 1);
              } else {
                  filename = filepath;
              }
          }
      }
  
      private void setXMLFilename(String s)
      {
          if (s == null) {
              return;
          }
  
          int pos = s.indexOf("filename=\"");
          if (pos != -1) {
              xmlFilepath = s.substring(pos + 10, s.length() - 1);
              // Windows browsers include the full path on the client
              // But Linux/Unix and Mac browsers only send the filename
              // test if this is from a Windows browser
              pos = xmlFilepath.lastIndexOf("\\");
              if (pos != -1) {
                  xmlFilename = xmlFilepath.substring(pos + 1);
              } else {
                  xmlFilename = xmlFilepath;
              }
          }
      }
  
      public void doUpload(HttpServletRequest request) throws IOException
      {
          savePath = WPSConfig.getConfigPath();
          // get rid of the filename
          // How is the path on a windows machine? may be better using:
          // savePath = savePath.substring(0, savePath.length() -
          // "wps_config.xml".length());
          savePath = savePath.substring(0, savePath.lastIndexOf(File.separator) + 1);
          ServletInputStream in = request.getInputStream();
  
          line = new byte[128];
          i = in.readLine(line, 0, 128);
          if (i < 3) {
              return;
          }
          boundaryLength = i - 2;
  
          int classnameupcoming = 4;
  
          boundary = new String(line, 0, boundaryLength); // -2 discards
          // the newline
          // character
  
          /*
           * the type of the uploaded file until now only processes (.java/.zip)
           * or R scripts are supported
           */
          String type = "";
  
          String realSavePath = "";
          while (i != -1) {
              String newLine = new String(line, 0, i);
              System.out.println(newLine);
              if (newLine.contains("processName")) {
                  type = "javaProcess";
                  // we set a flag to know that the next string is the fully
                  // qualified classname
                  classnameupcoming--;
              } else if (newLine.contains("rProcessName")) {
                  type = "rScript";
                  // we set a flag to know that the next string is the fully
                  // qualified classname
                  classnameupcoming--;
  
              }
              if (classnameupcoming < 4) {
                  classnameupcoming--;
              }
              if (classnameupcoming == 0) {
                  // // then parse classname
                  // String classname = newLine;
                  // String[] classnameArr = classname.replace(".",
                  // ",").split(",");
                  // String classdir = new String();
                  // for (int c = 0; c < classnameArr.length - 1; c++) {
                  // if (c == 0) {
                  // classdir = classdir + classnameArr[c];
                  // } else {
                  // classdir = classdir + File.separator + classnameArr[c];
                  // }
                  //
                  // }
                  // realSavePath = new File(savePath).getParentFile()
                  // + "/WEB-INF/classes/";
                  // realSavePath = realSavePath + classdir + File.separator;
                  // new File(realSavePath).mkdirs();
  
                  try {
                      handleUpload(in, type, newLine);
                      break;
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }
  
              if (newLine.startsWith("Content-Disposition: form-data; name=\"")) {
                  if (newLine.contains("processDescriptionFile")) {
                      if (newLine.indexOf("filename=\"") != -1) {
                          setXMLFilename(new String(line, 0, i - 2));
                          if (xmlFilename == null) {
                              return;
                          }
  
                          if (!xmlFilename.equals("")) {
  
                              // this is the file content
                              i = in.readLine(line, 0, 128);
                              // next line
                              i = in.readLine(line, 0, 128);
                              // blank line
                              i = in.readLine(line, 0, 128);
                              newLine = new String(line, 0, i);
                              // add the prefix to the filename
  
                              PrintWriter pw = null;
                              if (realSavePath.length() > 0) {
                                  pw = new PrintWriter(new BufferedWriter(new FileWriter((realSavePath == null ? "" : realSavePath) + xmlFilename)));
                              } else {
                                  xmlFilename = filenamePrefix + xmlFilename;
                                  pw = new PrintWriter(new BufferedWriter(new FileWriter((savePath == null ? "" : savePath) + filename)));
                              }
  
                              while (i != -1 && !newLine.startsWith(boundary)) {
                                  // the problem is the last line of the file
                                  // content
                                  // contains the new line character.
                                  // So, we need to check if the current line is
                                  // the last line.
                                  i = in.readLine(line, 0, 128);
                                  if ((i == boundaryLength + 2 || i == boundaryLength + 4) // +
                                          // 4
                                          // is
                                          // eof
                                          && (new String(line, 0, i).startsWith(boundary))) {
  
                                      pw.print(newLine.substring(0, newLine.length() - 2));
                                  } else {
                                      pw.print(newLine);
                                  }
  
                                  newLine = new String(line, 0, i);
                              }
                              pw.close();
                          }
  
                      }
  
                  }
  
                  if (newLine.contains("processFile")) {
                      // we upload files not config docuemnts. Therefore store is
                      // somewhere else.
                      // realSavePath = new
                      // File(savePath).getParentFile().getAbsolutePath()+"/WEB-INF/classes/";
  
                      /**
                       * TODO: dir structure according to fully qual classname
                       */
  
                      if (newLine.indexOf("filename=\"") != -1) {
                          setFilename(new String(line, 0, i - 2));
                          if (filename == null) {
                              return;
                          }
  
                          // this is the file content
                          i = in.readLine(line, 0, 128);
                          // next line
                          i = in.readLine(line, 0, 128);
                          // blank line
                          i = in.readLine(line, 0, 128);
                          newLine = new String(line, 0, i);
                          // add the prefix to the filename
  
                          PrintWriter pw = null;
                          if (realSavePath.length() > 0) {
                              pw = new PrintWriter(new BufferedWriter(new FileWriter((realSavePath == null ? "" : realSavePath) + filename)));
                          } else {
                              filename = filenamePrefix + filename;
                              pw = new PrintWriter(new BufferedWriter(new FileWriter((savePath == null ? "" : savePath) + filename)));
                          }
  
                          while (i != -1 && !newLine.startsWith(boundary)) {
                              // the problem is the last line of the file content
                              // contains the new line character.
                              // So, we need to check if the current line is
                              // the last line.
                              i = in.readLine(line, 0, 128);
                              if ((i == boundaryLength + 2 || i == boundaryLength + 4) // +
                                      // 4
                                      // is
                                      // eof
                                      && (new String(line, 0, i).startsWith(boundary))) {
  
                                  pw.print(newLine.substring(0, newLine.length() - 2));
                              } else {
                                  pw.print(newLine);
                              }
  
                              newLine = new String(line, 0, i);
                          }
                          pw.close();
  
                      }
  
                  }
  
              }
  
              i = in.readLine(line, 0, 128);
          } // end while
          // if (realSavePath.length() > 0) {
          // LOGGER.info("User Configuration file received and saved at: "
          // + realSavePath + filename);
          // } else {
          // LOGGER.info("User Configuration file received and saved at: "
          // + savePath + filename);
          // }
      }
  
      private void handleUpload(ServletInputStream in,
              String type,
              String newLine) throws Exception
      {
  
          if (type.equals("javaProcess")) {
  
              // then parse classname
              String classname = newLine;
              String[] classnameArr = classname.replace(".", ",").split(",");
              String classdir = new String();
              for (int c = 0; c < classnameArr.length - 1; c++) {
                  if (c == 0) {
                      classdir = classdir + classnameArr[c];
                  } else {
                      classdir = classdir + File.separator + classnameArr[c];
                  }
  
              }
              realSavePath = new File(savePath).getParentFile() + "/WEB-INF/classes/";
              realSavePath = realSavePath + classdir + File.separator;
              new File(realSavePath).mkdirs();
  
              while (i != -1) {
  
                  newLine = new String(line, 0, i);
                  System.out.println(newLine);
  
                  if (newLine.startsWith("Content-Disposition: form-data; name=\"")) {
                      if (newLine.contains("processFile")) {
                          // we upload files not config docuements. Therefore
                          // store is
                          // somewhere else.
                          // realSavePath = new
                          // File(savePath).getParentFile().getAbsolutePath()+"/WEB-INF/classes/";
  
                          /**
                           * TODO: dir structure according to fully qual classname
                           */
  
                          if (newLine.indexOf("filename=\"") != -1) {
                              setFilename(new String(line, 0, i - 2));
                              if (filename == null) {
                                  return;
                              }
  
                              // this is the file content
                              i = in.readLine(line, 0, 128);
                              // next line
                              i = in.readLine(line, 0, 128);
                              // blank line
                              i = in.readLine(line, 0, 128);
                              newLine = new String(line, 0, i);
                              // add the prefix to the filename
  
                              PrintWriter pw = null;
                              if (realSavePath.length() > 0) {
                                  pw = new PrintWriter(new BufferedWriter(new FileWriter((realSavePath == null ? "" : realSavePath) + filename)));
                              } else {
                                  filename = filenamePrefix + filename;
                                  pw = new PrintWriter(new BufferedWriter(new FileWriter((savePath == null ? "" : savePath) + filename)));
                              }
  
                              while (i != -1 && !newLine.startsWith(boundary)) {
                                  // the problem is the last line of the file
                                  // content
                                  // contains the new line character.
                                  // So, we need to check if the current line is
                                  // the last line.
                                  i = in.readLine(line, 0, 128);
                                  if ((i == boundaryLength + 2 || i == boundaryLength + 4) // +
                                          // 4
                                          // is
                                          // eof
                                          && (new String(line, 0, i).startsWith(boundary))) {
  
                                      pw.print(newLine.substring(0, newLine.length() - 2));
                                  } else {
                                      pw.print(newLine);
                                  }
  
                                  newLine = new String(line, 0, i);
                              }
                              pw.close();
                          }
  
                      }
  
                  }
  
                  i = in.readLine(line, 0, 128);
              } // end while
              if (realSavePath.length() > 0) {
                  LOGGER.info("User Configuration file received and saved at: " + realSavePath + filename);
              } else {
                  LOGGER.info("User Configuration file received and saved at: " + savePath + filename);
              }
              if (realSavePath.length() > 0) {
                  compile(realSavePath + filename);
              }
  
          } else if (type.equals("rScript")) {
              // if processName the filename is used as ID, otherwise the script will be
              // renamed because r process IDs are derived from the filenames
              String processName = newLine.trim();
  
              /*
               * FIXME This is very dirty. Changed from staticly referencing the
               * 52n-wps-r module (!!) to generic string property resolving.
               * Nevertheless, this is sort of hard-coded. The webadmin module
               * should be completely separated from others.
               */
              String scriptDir = "";
              Property[] rConfig = WPSConfig.getInstance().getPropertiesForRepositoryClass("org.n52.wps.server.r.LocalRAlgorithmRepository");
              for (Property property : rConfig) {
                  if (property.getName().equalsIgnoreCase("R_scriptDirectory")) {
                      scriptDir = property.getStringValue();
                  }
              }
  
              File basedir = new File(savePath).getParentFile();
              realSavePath = new File(basedir, scriptDir).getAbsolutePath()+File.separator;
              new File(realSavePath).mkdirs();
  
              while (i != -1) {
  
                  newLine = new String(line, 0, i);
                  System.out.println(newLine);
  
                  if (newLine.startsWith("Content-Disposition: form-data; name=\"")) {
                      if (newLine.contains("rProcessFile")) {
  
                          if (newLine.indexOf("filename=\"") != -1) {
  
                              // filename is either the process name or the
                              // original file name
                              if (processName != null && !processName.isEmpty()) {
                                  filename = processName + ".R";
                              } else
                                  setFilename(new String(line, 0, i - 2));
  
                              if (filename == null) {
                                  return;
                              }
  
                              // this is the file content
                              i = in.readLine(line, 0, 128);
                              // next line
                              i = in.readLine(line, 0, 128);
                              // blank line
                              i = in.readLine(line, 0, 128);
                              newLine = new String(line, 0, i);
                              // add the prefix to the filename
  
                              PrintWriter pw = null;
                              File destFile = null;
                              if (realSavePath.length() > 0) {
                              	destFile = new File((realSavePath == null ? "" : realSavePath) + filename);
                                  pw = new PrintWriter(new BufferedWriter(new FileWriter(destFile)));
                              } else {
                                  filename = filenamePrefix + filename;
                                  destFile = new File((savePath == null ? "" : savePath) + filename);
                                  pw = new PrintWriter(new BufferedWriter(new FileWriter(destFile)));
                              }
  
                              while (i != -1 && !newLine.startsWith(boundary)) {
                                  // the problem is the last line of the file
                                  // content
                                  // contains the new line character.
                                  // So, we need to check if the current line is
                                  // the last line.
                                  i = in.readLine(line, 0, 128);
                                  if ((i == boundaryLength + 2 || i == boundaryLength + 4) // +
                                          // 4
                                          // is
                                          // eof
                                          && (new String(line, 0, i).startsWith(boundary))) {
  
                                      pw.print(newLine.substring(0, newLine.length() - 2));
                                  } else {
                                      pw.print(newLine);
                                  }
  
                                  newLine = new String(line, 0, i);
                              }
                              pw.close();
  
                          }
  
                          /*
                           * FIXME This is very dirty. The webadmin module should
                           * be completely separated from others. This one points
                           * out the need for a generic
                           * "reload your configuration" convenience method.
                           * Changed to java Reflections to remove the static
                           * 52n-wps-r module reference (though, very hardcoded).
                           */
                          {
                              LOGGER.info("R script uploaded, call to RPropertyChangeManager");
                              Class<?> clazz = Class.forName("org.n52.wps.server.r.RPropertyChangeManager");
                              Method method = clazz.getMethod("getInstance", null);
                              Object instance = method.invoke(null, null);
                              Method method2 = instance.getClass().getMethod("updateRepositoryConfiguration", null);
                              method2.invoke(instance, null);
                              // RPropertyChangeManager rmanager =
                              // RPropertyChangeManager.getInstance();
                              // LOGGER.info("R script uploaded, call to RPropertyChangeManager");
                              // rmanager.addUnregisteredScripts();
                          }
  
                      }
  
                  }
  
                  i = in.readLine(line, 0, 128);
              } // end while
              if (realSavePath.length() > 0) {
                  LOGGER.info("User Configuration file received and saved at: " + realSavePath + filename);
              } else {
                  LOGGER.info("User Configuration file received and saved at: " + savePath + filename);
              }
              if (realSavePath.length() > 0) {
              }
          }
  
      }
  
      public void compile(String fileName)
      {
          ClassLoader cl = this.getClass().getClassLoader();
          List<URL> classpath = new ArrayList<URL>();
          if (cl instanceof URLClassLoader) {
              URLClassLoader cl2 = (URLClassLoader) cl;
              for (URL jar : cl2.getURLs()) {
                  classpath.add(jar);
              }
          }
          String classPath = System.getProperty("java.class.path");
          for (String path : classPath.split(File.pathSeparator)) {
              try {
                  classpath.add(new URL("file:" + path));
              } catch (MalformedURLException e) {
                  System.err.println("Wrong url: " + e.getMessage());
                  e.printStackTrace();
              }
          }
  
          StringBuffer sb = new StringBuffer();
          for (URL jar : classpath) {
              if (SystemUtils.IS_OS_WINDOWS == false) {
                  sb.append(jar.getPath());
                  sb.append(File.pathSeparatorChar);
              } else {
                  sb.append(jar.getPath().substring(1));
                  sb.append(File.pathSeparatorChar);
              }
          }
          String ops[] = new String[] { "-classpath", sb.toString() };
  
          List<String> opsIter = new ArrayList<String>();
          try {
              for (String s : ops) {
                  // XXX test usage, removed use of deprecated method
                  // ((ArrayList) opsIter).add(URLDecoder.decode(s));
                  ((ArrayList) opsIter).add(URLDecoder.decode(s, Charset.forName("UTF-8").toString()));
              }
          } catch (UnsupportedEncodingException e) {
              LOGGER.warn(e.getMessage(), e);
          }
  
          File[] files1 = new File[1];
          files1[0] = new File(fileName);
  
          JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
          StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
  
          Iterable<? extends JavaFileObject> compilationUnits1 = fileManager.getJavaFileObjectsFromFiles(Arrays.asList(files1));
  
          compiler.getTask(null, fileManager, null, opsIter, null, compilationUnits1).call();
  
          try {
              fileManager.close();
          } catch (IOException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }
  
      }
  }
  