From 6e79a51bb2e8aa341aa87f8917bc0fcb3f05be1b Mon Sep 17 00:00:00 2001 From: tomas-sexenian Date: Mon, 18 Mar 2024 11:59:18 -0300 Subject: [PATCH 1/2] Encode pdf report output file name Issue:107307 --- .../main/java/com/genexus/GXWebReport.java | 186 ++++++++++-------- 1 file changed, 102 insertions(+), 84 deletions(-) diff --git a/java/src/main/java/com/genexus/GXWebReport.java b/java/src/main/java/com/genexus/GXWebReport.java index 00ce840b5..732c8ac90 100644 --- a/java/src/main/java/com/genexus/GXWebReport.java +++ b/java/src/main/java/com/genexus/GXWebReport.java @@ -4,33 +4,36 @@ import com.genexus.internet.HttpContext; import com.genexus.reports.*; import com.genexus.webpanels.GXWebProcedure; +import org.apache.logging.log4j.Logger; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + +public abstract class GXWebReport extends GXWebProcedure { + private static Logger log = org.apache.logging.log4j.LogManager.getLogger(GXWebReport.class); -public abstract class GXWebReport extends GXWebProcedure -{ public static final int OUTPUT_RVIEWER = 1; - public static final int OUTPUT_PDF = 2; + public static final int OUTPUT_PDF = 2; // Tiene que ser protected porque se pasa como par�metro en los reports protected GXReportMetadata reportMetadata; protected IReportHandler reportHandler; protected int lineHeight; - protected int Gx_line; - protected int P_lines; - protected int gxXPage; - protected int gxYPage; - protected int Gx_page; - protected String Gx_out = ""; // Esto est� asi porque no me pude deshacer de una comparacion contra Gx_out antes del ask. + protected int Gx_line; + protected int P_lines; + protected int gxXPage; + protected int gxYPage; + protected int Gx_page; + protected String Gx_out = ""; // Esto est� asi porque no me pude deshacer de una comparacion contra Gx_out antes del ask. protected String filename; protected String filetype; - public GXWebReport(HttpContext httpContext) - { + public GXWebReport(HttpContext httpContext) { super(httpContext); } - protected void initState(ModelContext context, UserInformation ui) - { + protected void initState(ModelContext context, UserInformation ui) { super.initState(context, ui); httpContext.setResponseBufferMode(HttpContext.ResponseBufferMode.ENABLED); @@ -45,53 +48,51 @@ else if (implementation.equals("ITEXT8")) initValues(); } - protected void preExecute() - { + protected void preExecute() { httpContext.setContentType("application/pdf"); httpContext.setStream(); ((GXReportPDFCommons) reportHandler).setOutputStream(httpContext.getOutputStream()); } - protected void setOutputFileName(String outputFileName){ + protected void setOutputFileName(String outputFileName) { filename = outputFileName; } - protected void setOutputType(String outputType){ + protected void setOutputType(String outputType) { filetype = outputType.toLowerCase(); } - private void initValues() - { - Gx_line = 0; - P_lines = 0; - gxXPage = 0; - gxYPage = 0; - Gx_page = 0; - Gx_out = ""; // Esto est� asi porque no me pude deshacer de una comparacion contra Gx_out antes del ask. - lineHeight = 0; + private void initValues() { + Gx_line = 0; + P_lines = 0; + gxXPage = 0; + gxYPage = 0; + Gx_page = 0; + Gx_out = ""; // Esto est� asi porque no me pude deshacer de una comparacion contra Gx_out antes del ask. + lineHeight = 0; } - public void setPrinter(IReportHandler reportHandler) - { + public void setPrinter(IReportHandler reportHandler) { this.reportHandler = reportHandler; } - public IReportHandler getPrinter() - { + public IReportHandler getPrinter() { return reportHandler; } - protected void GxEndPage() throws ProcessInterruptedException - { - if (reportHandler != null) + protected void GxEndPage() throws ProcessInterruptedException { + if (reportHandler != null) reportHandler.GxEndPage(); } - protected boolean initTextPrinter(String output, int gxXPage, int gxYPage, String iniFile, String form, String printer, int mode, int nPaperLength, int nPaperWidth, int nGridX, int nGridY, int nPageLines) - { - int x[] = {gxXPage}; - int y[] = {gxYPage}; + protected boolean initTextPrinter(String output, int gxXPage, int gxYPage, String iniFile, String form, String printer, int mode, int nPaperLength, int nPaperWidth, int nGridX, int nGridY, int nPageLines) { + int x[] = { + gxXPage + }; + int y[] = { + gxYPage + }; getPrinter().GxRVSetLanguage(localUtil._language); - boolean ret = getPrinter().GxPrTextInit(output, x, y, iniFile, form, printer, mode, nPaperLength, nPaperWidth, nGridX, nGridY, nPageLines); + boolean ret = getPrinter().GxPrTextInit(output, x, y, iniFile, form, printer, mode, nPaperLength, nPaperWidth, nGridX, nGridY, nPageLines); this.gxXPage = x[0]; this.gxYPage = y[0]; @@ -99,14 +100,17 @@ protected boolean initTextPrinter(String output, int gxXPage, int gxYPage, Strin return ret; } - protected boolean initPrinter(String output, int gxXPage, int gxYPage, String iniFile, String form, String printer, int mode, int orientation, int pageSize, int pageLength, int pageWidth, int scale, int copies, int defSrc, int quality, int color, int duplex) - { - int x[] = {gxXPage}; - int y[] = {gxYPage}; + protected boolean initPrinter(String output, int gxXPage, int gxYPage, String iniFile, String form, String printer, int mode, int orientation, int pageSize, int pageLength, int pageWidth, int scale, int copies, int defSrc, int quality, int color, int duplex) { + int x[] = { + gxXPage + }; + int y[] = { + gxYPage + }; setResponseOuputFileName(); getPrinter().GxRVSetLanguage(localUtil._language); - boolean ret = getPrinter().GxPrintInit(output, x, y, iniFile, form, printer, mode, orientation, pageSize, pageLength, pageWidth, scale, copies, defSrc, quality, color, duplex); + boolean ret = getPrinter().GxPrintInit(output, x, y, iniFile, form, printer, mode, orientation, pageSize, pageLength, pageWidth, scale, copies, defSrc, quality, color, duplex); this.gxXPage = x[0]; this.gxYPage = y[0]; @@ -114,71 +118,85 @@ protected boolean initPrinter(String output, int gxXPage, int gxYPage, String in return ret; } - private void setResponseOuputFileName(){ - String outputFileName = filename!=null ? filename : getClass().getSimpleName(); - String outputFileType = filetype!=null ? "." + filetype.toLowerCase(): ".pdf"; - httpContext.getResponse().addHeader("content-disposition", "inline; filename=" + outputFileName + outputFileType); + private void setResponseOuputFileName() { + String outputFileName = filename != null ? filename : getClass().getSimpleName(); + String outputFileType = filetype != null ? "." + filetype.toLowerCase() : ".pdf"; + + try { + // Encode the filename in UTF-8 according to RFC 5987 + String encodedFileName = URLEncoder.encode(outputFileName, "UTF-8").replace("+", "%20"); + String dispositionHeader = "inline; filename=\"" + asciiFileName(outputFileName) + + "\"; filename*=UTF-8''" + encodedFileName + outputFileType; + + httpContext.getResponse().addHeader("Content-Disposition", dispositionHeader); + } catch (UnsupportedEncodingException e) { + log.error("Failed to encode the name of the report", e); + } } - protected void endPrinter() - { + /** + * Returns an ASCII version of the filename. + * Non-ASCII characters are replaced with underscore to ensure ASCII compliance. + */ + private String asciiFileName(String fileName) { + StringBuilder asciiFileName = new StringBuilder(); + for (char character: fileName.toCharArray()) { + if ((character <= 127)) { + asciiFileName.append(character); + } else { + asciiFileName.append("_"); + } + } + return asciiFileName.toString(); + } + + protected void endPrinter() { getPrinter().GxEndPrinter(); } - protected int getOutputType() - { + protected int getOutputType() { return OUTPUT_RVIEWER; } - protected java.io.OutputStream getOutputStream() - { + protected java.io.OutputStream getOutputStream() { throw new RuntimeException("Output stream not set"); } - + //M�todos para la implementaci�n de reportes din�micos - protected void loadReportMetadata(String name) - { + protected void loadReportMetadata(String name) { reportMetadata = new GXReportMetadata(name, reportHandler); reportMetadata.load(); } - - protected int GxDrawDynamicGetPrintBlockHeight(int printBlock) - { + + protected int GxDrawDynamicGetPrintBlockHeight(int printBlock) { return reportMetadata.GxDrawGetPrintBlockHeight(printBlock); - } - - protected void GxDrawDynamicText(int printBlock, int controlId, int Gx_line) - { + } + + protected void GxDrawDynamicText(int printBlock, int controlId, int Gx_line) { reportMetadata.GxDrawText(printBlock, controlId, Gx_line); } - - protected void GxDrawDynamicText(int printBlock, int controlId, String value, int Gx_line) - { + + protected void GxDrawDynamicText(int printBlock, int controlId, String value, int Gx_line) { reportMetadata.GxDrawText(printBlock, controlId, Gx_line, value); } - - protected void GxDrawDynamicLine(int printBlock, int controlId, int Gx_line) - { + + protected void GxDrawDynamicLine(int printBlock, int controlId, int Gx_line) { reportMetadata.GxDrawLine(printBlock, controlId, Gx_line); } - - protected void GxDrawDynamicRect(int printBlock, int controlId, int Gx_line) - { + + protected void GxDrawDynamicRect(int printBlock, int controlId, int Gx_line) { reportMetadata.GxDrawRect(printBlock, controlId, Gx_line); - } - - protected void GxDrawDynamicBitMap(int printBlock, int controlId, String value, int Gx_line) - { + } + + protected void GxDrawDynamicBitMap(int printBlock, int controlId, String value, int Gx_line) { reportMetadata.GxDrawBitMap(printBlock, controlId, Gx_line, value, 0); } - - protected void GxDrawDynamicBitMap(int printBlock, int controlId, String value, int aspectRatio, int Gx_line) - { + + protected void GxDrawDynamicBitMap(int printBlock, int controlId, String value, int aspectRatio, int Gx_line) { reportMetadata.GxDrawBitMap(printBlock, controlId, Gx_line, value, aspectRatio); - } - - protected void cleanup( ) - { + } + + protected void cleanup() { super.cleanup(); } -} +} \ No newline at end of file From 29cd313e6c26aacd2bbe0ca5f67f29ae3bb56ee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Wed, 5 Feb 2025 13:52:21 -0300 Subject: [PATCH 2/2] Fix encoding --- .../main/java/com/genexus/GXWebReport.java | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/java/src/main/java/com/genexus/GXWebReport.java b/java/src/main/java/com/genexus/GXWebReport.java index 732c8ac90..c16cef78b 100644 --- a/java/src/main/java/com/genexus/GXWebReport.java +++ b/java/src/main/java/com/genexus/GXWebReport.java @@ -8,6 +8,7 @@ import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; public abstract class GXWebReport extends GXWebProcedure { private static Logger log = org.apache.logging.log4j.LogManager.getLogger(GXWebReport.class); @@ -107,7 +108,7 @@ protected boolean initPrinter(String output, int gxXPage, int gxYPage, String in int y[] = { gxYPage }; - setResponseOuputFileName(); + setResponseOutputFileName(); getPrinter().GxRVSetLanguage(localUtil._language); boolean ret = getPrinter().GxPrintInit(output, x, y, iniFile, form, printer, mode, orientation, pageSize, pageLength, pageWidth, scale, copies, defSrc, quality, color, duplex); @@ -118,19 +119,27 @@ protected boolean initPrinter(String output, int gxXPage, int gxYPage, String in return ret; } - private void setResponseOuputFileName() { - String outputFileName = filename != null ? filename : getClass().getSimpleName(); - String outputFileType = filetype != null ? "." + filetype.toLowerCase() : ".pdf"; + private void setResponseOutputFileName() { + String outputFileName = (filename != null ? filename : getClass().getSimpleName()); + String outputFileType = (filetype != null ? "." + filetype.toLowerCase() : ".pdf"); try { - // Encode the filename in UTF-8 according to RFC 5987 String encodedFileName = URLEncoder.encode(outputFileName, "UTF-8").replace("+", "%20"); - String dispositionHeader = "inline; filename=\"" + asciiFileName(outputFileName) + - "\"; filename*=UTF-8''" + encodedFileName + outputFileType; + boolean isAscii = StandardCharsets.US_ASCII.newEncoder().canEncode(outputFileName); + String fallbackFileName = outputFileName; + if (!isAscii) { + fallbackFileName = outputFileName.replaceAll("[^\\p{ASCII}]", ""); + if (fallbackFileName.isEmpty()) { + fallbackFileName = "download"; + } + } + String contentDispositionValue = + "inline; filename=\"" + fallbackFileName + outputFileType + "\"; " + + "filename*=UTF-8''" + encodedFileName + outputFileType; - httpContext.getResponse().addHeader("Content-Disposition", dispositionHeader); + httpContext.getResponse().addHeader("Content-Disposition", contentDispositionValue); } catch (UnsupportedEncodingException e) { - log.error("Failed to encode the name of the report", e); + httpContext.getResponse().addHeader("Content-Disposition", "inline; filename=\"download" + outputFileType + "\""); } }