001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.portlet;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.servlet.BrowserSnifferUtil;
020    import com.liferay.portal.kernel.servlet.HttpHeaders;
021    import com.liferay.portal.kernel.util.ArrayUtil;
022    import com.liferay.portal.kernel.util.FileUtil;
023    import com.liferay.portal.kernel.util.GetterUtil;
024    import com.liferay.portal.kernel.util.HttpUtil;
025    import com.liferay.portal.kernel.util.PortalUtil;
026    import com.liferay.portal.kernel.util.PropsUtil;
027    import com.liferay.portal.kernel.util.StreamUtil;
028    import com.liferay.portal.kernel.util.StringBundler;
029    import com.liferay.portal.kernel.util.StringPool;
030    import com.liferay.portal.kernel.util.StringUtil;
031    import com.liferay.portal.kernel.util.Validator;
032    
033    import java.io.File;
034    import java.io.FileInputStream;
035    import java.io.IOException;
036    import java.io.InputStream;
037    import java.io.OutputStream;
038    
039    import java.nio.channels.Channels;
040    import java.nio.channels.FileChannel;
041    
042    import javax.portlet.MimeResponse;
043    import javax.portlet.PortletRequest;
044    import javax.portlet.ResourceResponse;
045    
046    import javax.servlet.http.HttpServletRequest;
047    
048    /**
049     * @author Brian Wing Shun Chan
050     */
051    public class PortletResponseUtil {
052    
053            public static void sendFile(
054                            PortletRequest portletRequest, MimeResponse mimeResponse,
055                            String fileName, byte[] bytes)
056                    throws IOException {
057    
058                    sendFile(portletRequest, mimeResponse, fileName, bytes, null);
059            }
060    
061            public static void sendFile(
062                            PortletRequest portletRequest, MimeResponse mimeResponse,
063                            String fileName, byte[] bytes, String contentType)
064                    throws IOException {
065    
066                    sendFile(
067                            portletRequest, mimeResponse, fileName, bytes, contentType, null);
068            }
069    
070            public static void sendFile(
071                            PortletRequest portletRequest, MimeResponse mimeResponse,
072                            String fileName, byte[] bytes, String contentType,
073                            String contentDispositionType)
074                    throws IOException {
075    
076                    setHeaders(
077                            portletRequest, mimeResponse, fileName, contentType,
078                            contentDispositionType);
079    
080                    write(mimeResponse, bytes);
081            }
082    
083            public static void sendFile(
084                            PortletRequest portletRequest, MimeResponse mimeResponse,
085                            String fileName, InputStream inputStream)
086                    throws IOException {
087    
088                    sendFile(portletRequest, mimeResponse, fileName, inputStream, null);
089            }
090    
091            public static void sendFile(
092                            PortletRequest portletRequest, MimeResponse mimeResponse,
093                            String fileName, InputStream inputStream, int contentLength,
094                            String contentType)
095                    throws IOException {
096    
097                    sendFile(
098                            portletRequest, mimeResponse, fileName, inputStream, contentLength,
099                            contentType, null);
100            }
101    
102            public static void sendFile(
103                            PortletRequest portletRequest, MimeResponse mimeResponse,
104                            String fileName, InputStream inputStream, int contentLength,
105                            String contentType, String contentDispositionType)
106                    throws IOException {
107    
108                    setHeaders(
109                            portletRequest, mimeResponse, fileName, contentType,
110                            contentDispositionType);
111    
112                    write(mimeResponse, inputStream, contentLength);
113            }
114    
115            public static void sendFile(
116                            PortletRequest portletRequest, MimeResponse mimeResponse,
117                            String fileName, InputStream inputStream, String contentType)
118                    throws IOException {
119    
120                    sendFile(
121                            portletRequest, mimeResponse, fileName, inputStream, 0,
122                            contentType);
123            }
124    
125            public static void write(MimeResponse mimeResponse, byte[] bytes)
126                    throws IOException {
127    
128                    write(mimeResponse, bytes, 0, 0);
129            }
130    
131            public static void write(
132                            MimeResponse mimeResponse, byte[] bytes, int offset,
133                            int contentLength)
134                    throws IOException {
135    
136                    // LEP-3122
137    
138                    if (!mimeResponse.isCommitted()) {
139    
140                            // LEP-536
141    
142                            if (contentLength == 0) {
143                                    contentLength = bytes.length;
144                            }
145    
146                            if (mimeResponse instanceof ResourceResponse) {
147                                    ResourceResponse resourceResponse =
148                                            (ResourceResponse)mimeResponse;
149    
150                                    resourceResponse.setContentLength(contentLength);
151                            }
152    
153                            OutputStream outputStream = mimeResponse.getPortletOutputStream();
154    
155                            outputStream.write(bytes, offset, contentLength);
156                    }
157            }
158    
159            public static void write(MimeResponse mimeResponse, byte[][] bytesArray)
160                    throws IOException {
161    
162                    // LEP-3122
163    
164                    if (mimeResponse.isCommitted()) {
165                            return;
166                    }
167    
168                    // LEP-536
169    
170                    long contentLength = 0;
171    
172                    for (byte[] bytes : bytesArray) {
173                            contentLength += bytes.length;
174                    }
175    
176                    if (mimeResponse instanceof ResourceResponse) {
177                            ResourceResponse resourceResponse = (ResourceResponse)mimeResponse;
178    
179                            setContentLength(resourceResponse, contentLength);
180                    }
181    
182                    OutputStream outputStream = mimeResponse.getPortletOutputStream();
183    
184                    for (byte[] bytes : bytesArray) {
185                            outputStream.write(bytes);
186                    }
187            }
188    
189            public static void write(MimeResponse mimeResponse, File file)
190                    throws IOException {
191    
192                    FileInputStream fileInputStream = new FileInputStream(file);
193    
194                    try (FileChannel fileChannel = fileInputStream.getChannel()) {
195                            long contentLength = fileChannel.size();
196    
197                            if (mimeResponse instanceof ResourceResponse) {
198                                    ResourceResponse resourceResponse =
199                                            (ResourceResponse)mimeResponse;
200    
201                                    setContentLength(resourceResponse, contentLength);
202                            }
203    
204                            fileChannel.transferTo(
205                                    0, contentLength,
206                                    Channels.newChannel(mimeResponse.getPortletOutputStream()));
207                    }
208            }
209    
210            public static void write(MimeResponse mimeResponse, InputStream inputStream)
211                    throws IOException {
212    
213                    write(mimeResponse, inputStream, 0);
214            }
215    
216            public static void write(
217                            MimeResponse mimeResponse, InputStream inputStream,
218                            int contentLength)
219                    throws IOException {
220    
221                    if (mimeResponse.isCommitted()) {
222                            StreamUtil.cleanUp(inputStream);
223    
224                            return;
225                    }
226    
227                    if (contentLength > 0) {
228                            if (mimeResponse instanceof ResourceResponse) {
229                                    ResourceResponse resourceResponse =
230                                            (ResourceResponse)mimeResponse;
231    
232                                    resourceResponse.setContentLength(contentLength);
233                            }
234                    }
235    
236                    StreamUtil.transfer(inputStream, mimeResponse.getPortletOutputStream());
237            }
238    
239            public static void write(MimeResponse mimeResponse, String s)
240                    throws IOException {
241    
242                    write(mimeResponse, s.getBytes(StringPool.UTF8));
243            }
244    
245            protected static void setContentLength(
246                    ResourceResponse response, long contentLength) {
247    
248                    response.setProperty(
249                            HttpHeaders.CONTENT_LENGTH, String.valueOf(contentLength));
250            }
251    
252            protected static void setHeaders(
253                    PortletRequest portletRequest, MimeResponse mimeResponse,
254                    String fileName, String contentType, String contentDispositionType) {
255    
256                    if (_log.isDebugEnabled()) {
257                            _log.debug("Sending file of type " + contentType);
258                    }
259    
260                    // LEP-2201
261    
262                    if (Validator.isNotNull(contentType)) {
263                            mimeResponse.setContentType(contentType);
264                    }
265    
266                    mimeResponse.setProperty(
267                            HttpHeaders.CACHE_CONTROL, HttpHeaders.CACHE_CONTROL_PRIVATE_VALUE);
268    
269                    if (Validator.isNull(fileName)) {
270                            return;
271                    }
272    
273                    String contentDispositionFileName = "filename=\"" + fileName + "\"";
274    
275                    // If necessary for non-ASCII characters, encode based on RFC 2184.
276                    // However, not all browsers support RFC 2184. See LEP-3127.
277    
278                    boolean ascii = true;
279    
280                    for (int i = 0; i < fileName.length(); i++) {
281                            if (!Validator.isAscii(fileName.charAt(i))) {
282                                    ascii = false;
283    
284                                    break;
285                            }
286                    }
287    
288                    try {
289                            if (!ascii) {
290                                    String encodedFileName = HttpUtil.encodeURL(fileName, true);
291    
292                                    HttpServletRequest request = PortalUtil.getHttpServletRequest(
293                                            portletRequest);
294    
295                                    if (BrowserSnifferUtil.isIe(request)) {
296                                            contentDispositionFileName =
297                                                    "filename=\"" + encodedFileName + "\"";
298                                    }
299                                    else {
300                                            contentDispositionFileName =
301                                                    "filename*=UTF-8''" + encodedFileName;
302                                    }
303                            }
304                    }
305                    catch (Exception e) {
306                            if (_log.isWarnEnabled()) {
307                                    _log.warn(e);
308                            }
309                    }
310    
311                    if (Validator.isNull(contentDispositionType)) {
312                            String extension = GetterUtil.getString(
313                                    FileUtil.getExtension(fileName));
314    
315                            extension = StringUtil.toLowerCase(extension);
316    
317                            String[] mimeTypesContentDispositionInline = null;
318    
319                            try {
320                                    mimeTypesContentDispositionInline = PropsUtil.getArray(
321                                            "mime.types.content.disposition.inline");
322                            }
323                            catch (Exception e) {
324                                    mimeTypesContentDispositionInline = new String[0];
325                            }
326    
327                            if (ArrayUtil.contains(
328                                            mimeTypesContentDispositionInline, extension)) {
329    
330                                    contentDispositionType = HttpHeaders.CONTENT_DISPOSITION_INLINE;
331                            }
332                            else {
333                                    contentDispositionType =
334                                            HttpHeaders.CONTENT_DISPOSITION_ATTACHMENT;
335                            }
336                    }
337    
338                    StringBundler sb = new StringBundler(4);
339    
340                    sb.append(contentDispositionType);
341                    sb.append(StringPool.SEMICOLON);
342                    sb.append(StringPool.SPACE);
343                    sb.append(contentDispositionFileName);
344    
345                    mimeResponse.setProperty(
346                            HttpHeaders.CONTENT_DISPOSITION, sb.toString());
347            }
348    
349            private static final Log _log = LogFactoryUtil.getLog(
350                    PortletResponseUtil.class);
351    
352    }