1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.codec.net;
  17. import java.io.UnsupportedEncodingException;
  18. import org.apache.commons.codec.DecoderException;
  19. import org.apache.commons.codec.EncoderException;
  20. /**
  21. * <p>
  22. * Implements methods common to all codecs defined in RFC 1522.
  23. * </p>
  24. *
  25. * <p>
  26. * <a href="http://www.ietf.org/rfc/rfc1522.txt">RFC 1522</a>
  27. * describes techniques to allow the encoding of non-ASCII text in
  28. * various portions of a RFC 822 [2] message header, in a manner which
  29. * is unlikely to confuse existing message handling software.
  30. * </p>
  31. * @see <a href="http://www.ietf.org/rfc/rfc1522.txt">
  32. * MIME (Multipurpose Internet Mail Extensions) Part Two:
  33. * Message Header Extensions for Non-ASCII Text</a>
  34. * </p>
  35. *
  36. * @author Apache Software Foundation
  37. * @since 1.3
  38. * @version $Id: RFC1522Codec.java,v 1.2 2004/04/09 22:21:43 ggregory Exp $
  39. */
  40. abstract class RFC1522Codec {
  41. /**
  42. * Applies an RFC 1522 compliant encoding scheme to the given string of text with the
  43. * given charset. This method constructs the "encoded-word" header common to all the
  44. * RFC 1522 codecs and then invokes {@link #doEncoding(byte [])} method of a concrete
  45. * class to perform the specific enconding.
  46. *
  47. * @param text a string to encode
  48. * @param charset a charset to be used
  49. *
  50. * @return RFC 1522 compliant "encoded-word"
  51. *
  52. * @throws EncoderException thrown if there is an error conidition during the Encoding
  53. * process.
  54. * @throws UnsupportedEncodingException thrown if charset is not supported
  55. *
  56. * @see <a href="http://java.sun.com/j2se/1.3/docs/api/java/lang/package-summary.html#charenc">JRE character
  57. * encoding names</a>
  58. */
  59. protected String encodeText(final String text, final String charset)
  60. throws EncoderException, UnsupportedEncodingException
  61. {
  62. if (text == null) {
  63. return null;
  64. }
  65. StringBuffer buffer = new StringBuffer();
  66. buffer.append("=?");
  67. buffer.append(charset);
  68. buffer.append('?');
  69. buffer.append(getEncoding());
  70. buffer.append('?');
  71. byte [] rawdata = doEncoding(text.getBytes(charset));
  72. buffer.append(new String(rawdata, StringEncodings.US_ASCII));
  73. buffer.append("?=");
  74. return buffer.toString();
  75. }
  76. /**
  77. * Applies an RFC 1522 compliant decoding scheme to the given string of text. This method
  78. * processes the "encoded-word" header common to all the RFC 1522 codecs and then invokes
  79. * {@link #doEncoding(byte [])} method of a concrete class to perform the specific deconding.
  80. *
  81. * @param text a string to decode
  82. *
  83. * @throws DecoderException thrown if there is an error conidition during the Decoding
  84. * process.
  85. * @throws UnsupportedEncodingException thrown if charset specified in the "encoded-word"
  86. * header is not supported
  87. */
  88. protected String decodeText(final String text)
  89. throws DecoderException, UnsupportedEncodingException
  90. {
  91. if (text == null) {
  92. return null;
  93. }
  94. if ((!text.startsWith("=?")) || (!text.endsWith("?="))) {
  95. throw new DecoderException("RFC 1522 violation: malformed encoded content");
  96. }
  97. int termnator = text.length() - 2;
  98. int from = 2;
  99. int to = text.indexOf("?", from);
  100. if ((to == -1) || (to == termnator)) {
  101. throw new DecoderException("RFC 1522 violation: charset token not found");
  102. }
  103. String charset = text.substring(from, to);
  104. if (charset.equals("")) {
  105. throw new DecoderException("RFC 1522 violation: charset not specified");
  106. }
  107. from = to + 1;
  108. to = text.indexOf("?", from);
  109. if ((to == -1) || (to == termnator)) {
  110. throw new DecoderException("RFC 1522 violation: encoding token not found");
  111. }
  112. String encoding = text.substring(from, to);
  113. if (!getEncoding().equalsIgnoreCase(encoding)) {
  114. throw new DecoderException("This codec cannot decode " +
  115. encoding + " encoded content");
  116. }
  117. from = to + 1;
  118. to = text.indexOf("?", from);
  119. byte[] data = text.substring(from, to).getBytes(StringEncodings.US_ASCII);
  120. data = doDecoding(data);
  121. return new String(data, charset);
  122. }
  123. /**
  124. * Returns the codec name (referred to as encoding in the RFC 1522)
  125. *
  126. * @return name of the codec
  127. */
  128. protected abstract String getEncoding();
  129. /**
  130. * Encodes an array of bytes using the defined encoding scheme
  131. *
  132. * @param bytes Data to be encoded
  133. *
  134. * @return A byte array containing the encoded data
  135. *
  136. * @throws EncoderException thrown if the Encoder encounters a failure condition
  137. * during the encoding process.
  138. */
  139. protected abstract byte[] doEncoding(byte[] bytes) throws EncoderException;
  140. /**
  141. * Decodes an array of bytes using the defined encoding scheme
  142. *
  143. * @param bytes Data to be decoded
  144. *
  145. * @return a byte array that contains decoded data
  146. *
  147. * @throws DecoderException A decoder exception is thrown if a Decoder encounters a
  148. * failure condition during the decode process.
  149. */
  150. protected abstract byte[] doDecoding(byte[] bytes) throws DecoderException;
  151. }