Summary Table

Categories Total Count
PII 0
URL 0
DNS 0
EKL 0
IP 0
PORT 0
VsID 0
CF 0
AI 0
VPD 0
PL 0
Other 0

File Content

package gov.va.med.ars.file;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.MalformedURLException;

import javax.activation.MimetypesFileTypeMap;

import org.apache.commons.io.FilenameUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.PdfWriter;

import gov.va.med.ars.constants.ErrorMessages;
import gov.va.med.ars.exceptions.GenericException;
import gov.va.med.ars.model.request.AuditLogger;
import gov.va.med.ars.model.request.ExportAsPDF;
import gov.va.med.ars.service.IAuditLoggerService;

//@SuppressWarnings("resource") // Think about fixing this
@RestController
public class FileOperations {

private static final Logger logger = LogManager.getLogger(FileOperations.class);

@Autowired
IAuditLoggerService auditLoggerService;

// @SuppressWarnings("resource") // What about isr?
@PostMapping("/api/v1/file")
public ResponseEntity<?> fileOperator(@RequestBody AuditLogger auditLogger)
throws GenericException, IOException {
logger.debug("/api/v1/file");
// Follow naming conventions used by FilenameUtils
// Filename
// FullPath
// Path
// Name
// filename is like /fullPath/name.ext
String filename = auditLoggerService.getPathForTheAttachment(auditLogger);
logger.debug("getPathForTheAttachment=" + filename);
// filename = "\\u02\\attachments\\output\\86753092.001\\GIF.gif"
// You can fake this for testing with local "C:\\u02" etc.
// By the way, with single \ and u02 it becomes illegal Unicode character
// String filename = "/u02/attachments/output/86753174.1/2015103021083278.pdf";
// String filename = "C:\\data\\output\\415\\tif.tif";
// To "mock", mkdir "c:\\u02\\attachments\\output\\" then save attachments from DEV ARS into it.
// filename = "C:\\itext\\img\\puffer.gif"; // FAKE-out for dev testing ???
filename = FilenameUtils.normalize(filename);
logger.debug("normalized filename=" + filename);

if (filename == null) {
throw new GenericException(ErrorMessages.BAD_REQUEST, "Required Data is missing", HttpStatus.BAD_REQUEST);
}

// Take it one step at a time, so we can debug
File file = new File(filename); // Just a file path
try {
FileInputStream fis = new FileInputStream(file); // an actual input stream

// Open Declaration org.springframework.core.io.InputStreamResource
//
//
// Resource implementation for a given InputStream.
//
// Should only be used if no other specific Resource implementation is applicable.
// In particular, prefer ByteArrayResource or any of the file-based Resource implementations where possible.
//
// In contrast to other Resource implementations, this is a descriptor for an already opened resource -
// therefore returning true from isOpen().
// Do not use an InputStreamResource if you need to keep the resource descriptor somewhere,
// or if you need to read from a stream multiple times.
//
// See Also:
// ByteArrayResource
// ClassPathResource
// FileSystemResource
// UrlResource

InputStreamResource isr = new InputStreamResource(fis); // when fis is actually read, it should close fis.

MimetypesFileTypeMap mftm = new MimetypesFileTypeMap();
String contentType = mftm.getContentType(file); // should always return a valid MIME file type
logger.debug("contentType=" + contentType);

MediaType mediaType = MediaType.valueOf(contentType);
logger.debug("mediaType=" + mediaType);
String name = FilenameUtils.getName(filename);
String contentDisposition = name;
// This is the Proper MIME/HTTP Way, but we don't handle it that way in ARS_FRONTEND:
// contentDisposition = auditLogger.getIsDownload()
// ? String.join(";", "attachment", "filename=" + name)
// :"inline"; // don't use name

ResponseEntity<?> res = ResponseEntity
.ok()
.header("Content-Disposition", contentDisposition) // e.g., Content-Disposition=[GIF.gif]
// Content-Disposition didn't work for Chrome
// Also, should be "inline" or "attachment; filename=...", but frontend expects non-standard
.header("X-Attachment-Name", name) // This is supposed to work better than "Content-Disposition"
.contentLength(file.length())
.contentType(mediaType)
.body(isr);
return res;
}
catch (Exception ex) {
ResponseEntity<?> res = ResponseEntity
.status(HttpStatus.NOT_FOUND) // Is there something more specific, we can use?
.header("X-Attachment-Name", FilenameUtils.getName(filename))
.body(null);
return res;
}
}

@SuppressWarnings("resource") // ??? isr?
@PostMapping("/api/v1/exportAsPDF")
public ResponseEntity<?> exportAsPDF(@RequestBody ExportAsPDF request)
throws GenericException, DocumentException, MalformedURLException, IOException {
logger.debug("/api/v1/exportAsPDF");
// Follow naming conventions used by FilenameUtils
// Filename
// FullPath
// Path
// Name

String filename = auditLoggerService.getPathForExportAsPDF(request);
logger.debug("getPathForExportAsPDF=" + filename);
// filename = "\\u02\\attachments\\output\\86753092.001\\GIF.gif"
// You can fake this for testing with local "C:\\u02" etc.
// By the way, without the space after the \, \ u02 would be parsed by Java as an illegal Unicode character
// String filename = "/u02/attachments/output/86753174.1/2015103021083278.pdf";
// String filename = "C:\\data\\output\\415\\tif.tif";
filename = FilenameUtils.normalize(filename);
logger.debug("normalized filename=" + filename);

if (filename == null) {
throw new GenericException(ErrorMessages.BAD_REQUEST, "Required Data is missing", HttpStatus.BAD_REQUEST);
}

String pdfName = request.getAttachIdLx() + "-" + FilenameUtils.getName(filename) + ".pdf"; // 86753092.001-GIF.GIF.pdf

try (ByteArrayOutputStream pdfOs = new ByteArrayOutputStream())
{
Document document = new Document(PageSize.LETTER, 20, 20, 20, 20);
PdfWriter.getInstance(document, pdfOs);

document.open();
Image image = Image.getInstance(filename);

// Another way of scaling to fit the page.
// image.scaleToFit(docW, docH);
// But it changes aspect ratio. I think.

// scale image to fit within the margins of the page.
// We could scale-up small images, too??
float imgW = image.getWidth();
float imgH = image.getHeight();
float docW = document.getPageSize().getWidth() - document.leftMargin() - document.rightMargin();
float docH = document.getPageSize().getHeight() - document.topMargin() - document.bottomMargin();
if (imgW > docW || imgH > docH) {
try {
// We're reducing, but one of imgW or imgH could still be zero
float sclW = docW / imgW;
float sclH = docH / imgH;
float scale = Math.min(sclW, sclH);
image.scalePercent(scale * 100.0F);
} catch (Exception ex) {
/* ignore it. That is, image.scalePercent() is skipped. */
// logger.debug("/api/v1/exportAsPDF exception: ", ex);
}
}

document.add(image);
document.close();

// pdfOs has been created, now send it
byte[] bytes = pdfOs.toByteArray(); // lots of stack space? We have 25MB files, sometimes.
ByteArrayInputStream pdfFis = new ByteArrayInputStream(bytes); // for debug view
InputStreamResource pdfIsr = new InputStreamResource(pdfFis);
// We kind of follow MIME here and say that the file is an attachment.
// For export, it's not supposed to be "inline;"
// This needs to be parsed on the front-end!
ResponseEntity<InputStreamResource> resEntity =
ResponseEntity.ok()
// "Content-Disposition" is not "inline;" for an export file
.header("Content-Disposition", "attachment; filename=" + FilenameUtils.normalize(pdfName))
// "attachment; filename=" needs to be parsed off in the front-end
// "Content-Disposition" wasn't working for Google Chrome so I invent "X-Attachment-Name"
.header("X-Attachment-Name", FilenameUtils.normalize(pdfName))
.contentLength(bytes.length) // Is it necessary?
.contentType(MediaType.valueOf("application/pdf"))
.body(pdfIsr);
return resEntity;
}
catch (Exception ex) {
logger.error("/api/v1/exportAsPDF exception.getMessage: " + ex.getMessage());
logger.error("/api/v1/exportAsPDF exception: " + ex);
throw ex;
}
}

@InitBinder()
public void customizeBinding (WebDataBinder binder) {
binder.setDisallowedFields(new String[]{});
}
}