001/* 002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Florent Guillaume 018 * 019 * $Id$ 020 */ 021 022package org.nuxeo.common.utils; 023 024import static org.nuxeo.common.utils.UserAgentMatcher.isSafari5; 025import static org.nuxeo.common.utils.UserAgentMatcher.isMSIE6or7; 026 027import java.io.UnsupportedEncodingException; 028 029/** 030 * RFC-2231 specifies how a MIME parameter value, like {@code Content-Disposition}'s {@code filename}, can be encoded to 031 * contain arbitrary character sets. 032 * 033 * @author Florent Guillaume 034 */ 035public class RFC2231 { 036 037 private static final String UTF8 = "UTF-8"; 038 039 private static final byte[] UNKNOWN_BYTES = { '?' }; 040 041 // Utility class 042 private RFC2231() { 043 } 044 045 /** 046 * Does a simple %-escaping of the UTF-8 bytes of the value. Keep only some know safe characters. 047 * 048 * @param buf the buffer to which escaped chars are appended 049 * @param value the value to escape 050 */ 051 public static void percentEscape(StringBuilder buf, String value) { 052 byte[] bytes; 053 try { 054 bytes = value.getBytes(UTF8); 055 } catch (UnsupportedEncodingException e) { 056 // cannot happen with UTF-8 057 bytes = UNKNOWN_BYTES; 058 } 059 for (byte b : bytes) { 060 if (b < '+' || b == ';' || b == ',' || b == '\\' || b > 'z') { 061 buf.append('%'); 062 String s = Integer.toHexString(b & 0xff).toUpperCase(); 063 if (s.length() < 2) { 064 buf.append('0'); 065 } 066 buf.append(s); 067 } else { 068 buf.append((char) b); 069 } 070 } 071 } 072 073 /** 074 * Encodes a {@code Content-Disposition} header. For some user agents the full RFC-2231 encoding won't be performed 075 * as they don't understand it. 076 * 077 * @param filename the filename 078 * @param inline {@code true} for an inline disposition, {@code false} for an attachment 079 * @param userAgent the userAgent 080 * @return a full string to set as value of a {@code Content-Disposition} header 081 */ 082 public static String encodeContentDisposition(String filename, boolean inline, String userAgent) { 083 StringBuilder buf = new StringBuilder(inline ? "inline; " : "attachment; "); 084 if (userAgent == null) { 085 userAgent = ""; 086 } 087 if (isMSIE6or7(userAgent)) { 088 // MSIE understands straight %-encoding 089 buf.append("filename="); 090 percentEscape(buf, filename); 091 } else { 092 // proper RFC2231 093 buf.append("filename*=UTF-8''"); 094 percentEscape(buf, filename); 095 } 096 return buf.toString(); 097 } 098 099}