/**
 * ********************************************************************************
 * 프로그램명 : HttpFileUploadDir.java 작 성 자 : 강원중 작 성 일 : 2003. 7.23 최신변경일 : 2013.
 * 1.11
 **********************************************************************************
 */
package kr.co.kihyun.beans.totsys.board;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Arrays;

import javax.jdo.PersistenceManager;
import javax.jdo.Transaction;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import kr.co.kihyun.beans.entity.TotReport;
import kr.co.kihyun.beans.entity.util.PMF;
import kr.co.kihyun.beans.user.HttpSSOLogin;
import kr.co.kihyun.db.CommonDBManager;
import kr.co.kihyun.io.FileUtil;
import kr.co.kihyun.io.MultipartRequest;
import kr.co.kihyun.lang.MInteger;
import kr.co.kihyun.lang.MLong;
import kr.co.kihyun.lang.MString;
import kr.co.kihyun.moumi.MoumiConfig;
//import kr.co.kihyun.text.html.ServletUtil;
import kr.co.kihyun.util.MRandom;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@WebServlet("/servlet/kr.co.kihyun.beans.totsys.board.HttpFileUploadDir")
public class HttpFileUploadDir extends HttpServlet {

    /**
     *
     */
    private static final long serialVersionUID = 1L;
    private static final Logger LOG = LoggerFactory.getLogger(HttpFileUploadDir.class);

    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

        res.setContentType("text/html;charset=UTF-8");

        MultipartRequest multi = null;
        String svrFilename = "";
        String usrFilename = "";
        String boardGroupID = "";
        String connURL = "";
        String mode = "";
        String usID = HttpSSOLogin.getLoginID(req);
//        String dtID = HttpSSOLogin.getDeptID(req);
        String dtID = "";
        Long docID = null;
        String reportID = null;
        String boardID = "";
        String msg = "";
        String maxSize = "3";
        String checkSize = "";
        boolean isExtNullOk = true;   //7.+++
        boolean isExtFileOk = true;   //7.+++
        boolean isDirectoryOk = true;  //3.+++

        Long currentFileSize = 0L;    // 업로드할 파일의 크기(byte)를 저장할 변수. 2014.09.02 by youngjun cho.
        Long accumulateFileSize = 0L;     // 업로드된 파일들의 총 누적 크기(byte)를 저장할 변수. 2014.09.02 by youngjun cho.

        CommonDBManager acdbm1=new CommonDBManager();
	    PersistenceManager pm = PMF.get().getPersistenceManager();
		Transaction tx = pm.currentTransaction();


        try {

            maxSize = req.getParameter("maxSize");
            if (maxSize == null || maxSize.equals("")) {
                maxSize = "10";
            }
            int tmpSize = Integer.parseInt(maxSize) * 1024 * 1024; //사이즈를 정해준다.
            checkSize = String.valueOf(tmpSize);

            multi = new MultipartRequest(req, tmpSize);

            mode = MString.checkNull(multi.getParameter("mode"));
            boardGroupID = multi.getParameter("boardGroupID");
            usrFilename = multi.getParameter("appendFile");

            //20170818 파일 검사수행 이전으로 옮김 by wonseok Lee. false 리턴시 첨부파일 관련 아래의 ID들을 이전페이지로 전달키 위해서임
            docID = MLong.parseLong(multi.getParameter("docID"));
           	reportID = MString.checkNull(multi.getParameter("reportID"));
            boardID = MString.checkNull(multi.getParameter("boardID"));
            dtID = MString.checkNull(multi.getParameter("dtID"));


            //7.위험한 형식 파일 업로드(MultipartRequest)_CWE-434 : Add by KWON,HAN
            int extIndex = 0 ;
            extIndex = usrFilename.lastIndexOf('.');
            if(extIndex < 0 ) {
                //LOG.debug("7.위험한 형식 파일 업로드(MultipartRequest)_CWE-434 : Not Test / usrFilename: {}", usrFilename);
                isExtNullOk = false;
                //out.print("<script language='javascript'>alert('확장자가 없는 파일입니다.');</script>");
                return;
            }

            //v2 22.정수형 오버플로우_CWE-190 : Update by YOUNGJUN,CHO
            if (extIndex < Integer.MAX_VALUE) {
                //String file_ext = usrFilename.substring(usrFilename.lastIndexOf('.') + 1);
                String file_ext = usrFilename.substring(extIndex + 1);

                if( file_ext.equalsIgnoreCase("exe") || file_ext.equalsIgnoreCase("bat") || file_ext.equalsIgnoreCase("sh") ) {
                    //LOG.debug("7.위험한 형식 파일 업로드(MultipartRequest)_CWE-434 : Not Test / usrFilename: {}", usrFilename);
                    //out.print("<script language='javascript'>alert('확장자가 [exe, bat, sh]는 업로드가 제한된 파일입니다.');</script>");
                    isExtFileOk = false;
                    return;
                }
            } else {
                //LOG.debug("v2 22.정수형 오버플로우_CWE-190 : Not Test / usrFilename: {}", usrFilename);
                //out.print("<script language='javascript'>alert('확장자가 [exe, bat, sh]는 업로드가 제한된 파일입니다.');</script>");
                isExtFileOk = false;
                return;
            }
            //================================================ v2 22.정수형 오버플로우_CWE-190 : Update by YOUNGJUN,CHO
            //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7.위험한 형식 파일 업로드(MultipartRequest)_CWE-434 : Add by KWON,HAN

            // 2014.09.02 by youngjun cho.
            // 업로드된 파일들의 총 누적 크기(byte)를 jsp 의 hidden value 에 저장 후 파라미터로 받아온다.
            //accumulateFileSize = MLong.parseLong(multi.getParameter("accumulateFileSize"));

            //LOG.debug("docID: {}, boardID: {}, mode: {}", new Object[] {docID, boardID, mode});
            if (!MString.isNull(usrFilename)) {
                String[] usrFilenamePath = usrFilename.split("\\\\");
                usrFilename = usrFilenamePath[usrFilenamePath.length - 1];

                String imsi = "";
//                String[] filter_word = {" ", "\\?", "\\/", "\\~", "\\!", "\\@", "\\#", "\\$", "\\%", "\\^", "\\&", "\\*", "\\(", "\\)", "\\+", "\\=", "\\|", "\\\\", "\\}", "\\]", "\\{", "\\[", "\\\"", "\\'", "\\:", "\\;", "\\<", "\\,", "\\>", "\\?", "\\/"};
                String[] filter_word = {"\\?","\\/","\\~","\\!","\\@","\\#","\\$","\\%","\\^","\\&","\\*","\\+","\\=","\\|","\\\\","\\\"","\\'","\\:","\\;","\\,"};
                for (int ii = 0; ii < filter_word.length; ii++) {
                    imsi = usrFilename.replaceAll(filter_word[ii], "_");
                    usrFilename = imsi;
                }

                LOG.debug("usrFilename: {}", usrFilename);
            }

            File path;
            //svrFilename = FileUtil.getName(usrFilename, Integer.toString(MRandom.getInt(100))); //두자리의 랜덤 수치를 얻음

            if (docID != null) {
                path = MoumiConfig.getFileDirectory();
                LOG.debug("MoumiConfig.getFileDirectory(): " + MoumiConfig.getFileDirectory());
            } else if (reportID != null) {
                path = MoumiConfig.getFileDirectory();
                LOG.debug("MoumiConfig.getFileDirectory(): " + MoumiConfig.getFileDirectory());
//			} else if (!MString.isNull(boardID)) {
//				path = new File(MoumiConfig.getBoardFileRoot(), boardID.toString());
//				LOG.debug("MoumiConfig.getBoardFileRoot(): " + MoumiConfig.getBoardFileRoot());
            } else if ("csv".equals(mode)) {
                path = MoumiConfig.getCsvFileRoot();
                LOG.debug("MoumiConfig.getCsvFileRoot(): " + MoumiConfig.getCsvFileRoot());
            } else {
                path = MoumiConfig.getFileDirectory();
                LOG.debug("MoumiConfig.getFileDirectory(): " + MoumiConfig.getFileDirectory());
            }

            LOG.debug("passLine: {}");
            //20170809_02 by wonseok Lee. 접수자료 > 자료입력 > 파일제출시 파일 업로드 수정, 파일직접 다운로드를 위해 업로드하는 파일을 MoumiConfig.getFileDirectory()2/DOCID/REPORTID/DeptID 디렉토리에 저장한다.

            if(mode.equals("repoper")) {
            	svrFilename = usrFilename;

                LOG.debug("Long.toString(docID): {}",Long.toString(docID));
                LOG.debug("Long.toString(reportID): {}",reportID);
                LOG.debug("Long.toString(dtID): {}",dtID);
            	path = new File(MoumiConfig.getFileDirectory()+"/"+Long.toString(docID)+"/"+reportID+"/"+dtID);
            }

            LOG.debug("passLine: {}");
            if (!path.isDirectory()) {
                if (!path.mkdirs()) {
                    throw new ServletException(path + " is not a directory.");
                }
            }
            accumulateFileSize = FileUtils.sizeOfDirectory(path);


            FileItem upFile = multi.getFileItem("appendFile");
            LOG.debug("UPFILE: {}", upFile);

            // 업로드할 파일의 크기를 구한다. (2014.09.02 by youngjun cho.)
            currentFileSize = upFile.getSize();
            LOG.debug("UPFILE currentFileSize : {}", currentFileSize);
            accumulateFileSize = accumulateFileSize + currentFileSize;

            LOG.debug("passLine: {}");

            LOG.debug("accumulateFileSize : {}"+ accumulateFileSize);
            // 제한용량 이상이면 "-66" 을 리턴한다. (2014.09.02 by youngjun cho.)
            //if (tmpSize <= accumulateFileSize) {
            if (accumulateFileSize > 3096000) { //(20170817 by wonseok Lee.) maxSize를 1로하고 1MB 이상의 첨부파일을 첨부하면  다음 exception 발생함=>org.apache.commons.fileupload.FileUploadBase$FileSizeLimitExceededException:
                msg = "-66";
                return;
            }

            //3.디렉토리 경로 조작(getParameter)_CWE-22/23/36 : Add by KWON,HAN
            LOG.debug("svrFilename: {}", svrFilename);
            if(svrFilename.contains("..") || svrFilename. contains("/")) { // 특수문자열 검증
                //LOG.debug("3.디렉토리 경로 조작(getParameter)_CWE-22/23/36 : Test OK {}", svrFilename);
                isDirectoryOk = false;
                return;
            }
            //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
            //20170814 if문 추가 by wonseok Lee. 업로드 파일이 중복이면 삭제하고 write.
            if(mode.equals("repoper")) {
    	        File existFile = new File(path+"/"+svrFilename);
            	if(existFile.isFile()) {
            		existFile.delete();
            	}
            }

            File attachFile = new File(path, svrFilename);
            LOG.debug("path writed to attachFile: {}/{}", attachFile);
            upFile.write(attachFile);
            LOG.debug("UPFILE writed to svrFilename: {}/{}", path, svrFilename);

// ** 첨부파일을 DB에 저장하는 기능 **
// *  파일서버 경로에 저장하도록 개선하여 사용하지 않도록 주석 처리 17.12.14 jskim
//			tx.begin();
//			TotReport totReport = pm.getObjectById(TotReport.class, Long.parseLong(reportID));
//
//	       	FileInputStream fis1 = null;
//            try {
//
//		       	byte[] content1 = new byte[(int)attachFile.length()];
//	            Byte[] contentObj1 = new Byte[content1.length];
//
//	            fis1 = new FileInputStream(attachFile);
//	            fis1.read(content1);//파일의 내용을 읽어온다.
//
//	            for (int i = 0; i < content1.length; i++) {
//	            	contentObj1[i] = content1[i];
//	            }
//
//	            totReport.addAttachment(svrFilename, Arrays.asList(contentObj1)); //파일명과 내용을 HashMap에 입력 한다.
//	            pm.makePersistent(totReport);
//	            content1 = null;
//	            contentObj1 = null;
//	            tx.commit();
//
//            }catch(IOException e){
//            	e.printStackTrace();
//            }finally{
//            	fis1.close();
//            }


           	StringBuilder sql = new StringBuilder(250);

           	sql.delete(0, sql.length());
           	sql.append("UPDATE MOUMI_TOT_REPORT SET ATTACHMENTS_PATH = ATTACHMENTS_PATH|| ? WHERE ID = ? ");
            acdbm1.execUpdate(sql.toString(), svrFilename+";", reportID);
            acdbm1.pstmt.close();

            sql.delete(0, sql.length());
            sql.append("UPDATE MOUMI_TOT_REPORT_VERSION SET ATTACHMENTS_PATH = ATTACHMENTS_PATH|| ? WHERE REPORT_ID = ? AND ID=(SELECT MAX(ID) FROM MOUMI_TOT_REPORT_VERSION  WHERE DEL_TYPE = 'N'  AND REPORT_ID = ? )");
            acdbm1.execUpdate(sql.toString(), svrFilename+";", reportID, reportID);
            acdbm1.pstmt.close();

            acdbm1.commit();
            svrFilename = URLEncoder.encode(svrFilename, "UTF-8");
            usrFilename = URLEncoder.encode(usrFilename, "UTF-8");


        } catch (Exception ex) {
            ex.printStackTrace();

			if (tx.isActive())
				tx.rollback();
        	acdbm1.rollback();

            msg = ex.getMessage();
            if (msg.indexOf(checkSize) > 0) {
                msg = "-99";
            } else {
                msg = "-88";
            }
        } finally {

			pm.close();
			acdbm1.execClose();

            connURL = "/totsys/common/inc/board/doc/write_file_upload_dir.jsp?execMode=up&svrFilename="
                    + svrFilename + "&usrFilename=" + usrFilename + "&boardGroupID=" + boardGroupID + "&docID=" + docID
                    + "&reportID=" + reportID+ "&dtID=" + dtID + "&boardID=" + boardID + "&mode=" + mode + "&MSG=" + msg + "&maxSize=" + maxSize + "&currentFileSize=" + currentFileSize;
            if (connURL.length() > 2047) {
                msg = "-77";
            }
            if(!isExtNullOk) {
                msg="-33";
            } //7.+++
            if(!isExtFileOk) {
                msg="-55";
            } //7.+++
            if(!isDirectoryOk) {
                msg="-44";
            } //3.+++
            if (!msg.equals("")) {
                svrFilename = "";
                usrFilename = "";
                currentFileSize = 0L;  // 에러인 경우 파일이 업로드되지 않으므로 파일 크기를 0으로 리턴한다. (2014.09.02 by youngjun cho.)
            }


            if (svrFilename != null || usrFilename != null) {
                String filtered_svrFilename = svrFilename.replaceAll("\r","").replaceAll("\n","");
                String filtered_usrFilename = usrFilename.replaceAll("\r","").replaceAll("\n","");
                String allowURL[] = { "/totsys/common/inc/board/doc/write_file_upload_dir.jsp" };
                connURL = allowURL[0] + "?execMode=up&svrFilename="
                        + filtered_svrFilename + "&usrFilename=" + filtered_usrFilename + "&boardGroupID=" + boardGroupID + "&docID=" + docID
                        + "&reportID=" + reportID+ "&dtID=" + dtID + "&boardID=" + boardID + "&mode=" + mode + "&MSG=" + msg + "&maxSize=" + maxSize + "&currentFileSize=" + currentFileSize;
                LOG.debug("v2 3.신뢰되지 않는 URL 주소로 자동 접속 연결_CWE-601 : HttpFileUploadDir.doPost() connURL={} : Test OK", connURL);
                //=========================================================================
            }
            //========================================
            res.sendRedirect(connURL);
        }
    }
}
