196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project/* ==================================================================== 296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Copyright (c) 2006 J.T. Beetstra 396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * 496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Permission is hereby granted, free of charge, to any person obtaining 596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * a copy of this software and associated documentation files (the 696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * "Software"), to deal in the Software without restriction, including 796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * without limitation the rights to use, copy, modify, merge, publish, 896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * distribute, sublicense, and/or sell copies of the Software, and to 996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * permit persons to whom the Software is furnished to do so, subject to 1096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * the following conditions: 1196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * 1296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * The above copyright notice and this permission notice shall be 1396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * included in all copies or substantial portions of the Software. 1496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * 1596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 1696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 1796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 1896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 1996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 2096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 2196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 2296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * ==================================================================== 2396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 2496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 2596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectpackage com.beetstra.jutf7; 2696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 2796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.nio.ByteBuffer; 2896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.nio.CharBuffer; 2996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.nio.charset.CharsetDecoder; 3096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.nio.charset.CoderResult; 3196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 3296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project/** 3396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * <p> 3496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * The CharsetDecoder used to decode both variants of the UTF-7 charset and the 3596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * modified-UTF-7 charset. 3696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * </p> 3796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * 3896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * @author Jaap Beetstra 3996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 4096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectclass UTF7StyleCharsetDecoder extends CharsetDecoder { 4196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private final Base64Util base64; 4296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private final byte shift; 4396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private final byte unshift; 4496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private final boolean strict; 4596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private boolean base64mode; 4696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private int bitsRead; 4796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private int tempChar; 4896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private boolean justShifted; 4996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private boolean justUnshifted; 5096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 5196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project UTF7StyleCharsetDecoder(UTF7StyleCharset cs, Base64Util base64, boolean strict) { 5296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project super(cs, 0.6f, 1.0f); 5396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project this.base64 = base64; 5496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project this.strict = strict; 5596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project this.shift = cs.shift(); 5696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project this.unshift = cs.unshift(); 5796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 5896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 5996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /* 6096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * (non-Javadoc) 6196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * @see java.nio.charset.CharsetDecoder#decodeLoop(java.nio.ByteBuffer, 6296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * java.nio.CharBuffer) 6396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 6496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { 6596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project while (in.hasRemaining()) { 6696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project byte b = in.get(); 6796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (base64mode) { 6896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (b == unshift) { 6996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (base64bitsWaiting()) 7096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return malformed(in); 7196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (justShifted) { 7296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (!out.hasRemaining()) 7396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return overflow(in); 7496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project out.put((char)shift); 7596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } else 7696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project justUnshifted = true; 7796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project setUnshifted(); 7896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } else { 7996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (!out.hasRemaining()) 8096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return overflow(in); 8196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project CoderResult result = handleBase64(in, out, b); 8296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (result != null) 8396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return result; 8496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 8596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project justShifted = false; 8696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } else { 8796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (b == shift) { 8896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project base64mode = true; 8996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (justUnshifted && strict) 9096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return malformed(in); 9196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project justShifted = true; 9296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project continue; 9396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 9496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (!out.hasRemaining()) 9596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return overflow(in); 9696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project out.put((char)b); 9796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project justUnshifted = false; 9896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 9996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 10096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return CoderResult.UNDERFLOW; 10196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 10296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 10396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private CoderResult overflow(ByteBuffer in) { 10496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project in.position(in.position() - 1); 10596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return CoderResult.OVERFLOW; 10696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 10796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 10896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /** 10996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * <p> 11096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Decodes a byte in <i>base 64 mode</i>. Will directly write a character to 11196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * the output buffer if completed. 11296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * </p> 11396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * 11496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * @param in The input buffer 11596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * @param out The output buffer 11696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * @param lastRead Last byte read from the input buffer 11796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * @return CoderResult.malformed if a non-base 64 character was encountered 11896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * in strict mode, null otherwise 11996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 12096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private CoderResult handleBase64(ByteBuffer in, CharBuffer out, byte lastRead) { 12196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project CoderResult result = null; 12296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project int sextet = base64.getSextet(lastRead); 12396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (sextet >= 0) { 12496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project bitsRead += 6; 12596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (bitsRead < 16) { 12696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project tempChar += sextet << (16 - bitsRead); 12796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } else { 12896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project bitsRead -= 16; 12996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project tempChar += sextet >> (bitsRead); 13096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project out.put((char)tempChar); 13196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project tempChar = (sextet << (16 - bitsRead)) & 0xFFFF; 13296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 13396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } else { 13496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (strict) 13596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return malformed(in); 13696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project out.put((char)lastRead); 13796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (base64bitsWaiting()) 13896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project result = malformed(in); 13996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project setUnshifted(); 14096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 14196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return result; 14296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 14396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 14496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /* 14596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * (non-Javadoc) 14696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * @see java.nio.charset.CharsetDecoder#implFlush(java.nio.CharBuffer) 14796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 14896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project protected CoderResult implFlush(CharBuffer out) { 14996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if ((base64mode && strict) || base64bitsWaiting()) 15096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return CoderResult.malformedForLength(1); 15196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return CoderResult.UNDERFLOW; 15296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 15396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 15496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /* 15596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * (non-Javadoc) 15696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * @see java.nio.charset.CharsetDecoder#implReset() 15796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 15896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project protected void implReset() { 15996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project setUnshifted(); 16096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project justUnshifted = false; 16196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 16296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 16396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /** 16496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * <p> 16596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Resets the input buffer position to just before the last byte read, and 16696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * returns a result indicating to skip the last byte. 16796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * </p> 16896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * 16996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * @param in The input buffer 17096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * @return CoderResult.malformedForLength(1); 17196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 17296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private CoderResult malformed(ByteBuffer in) { 17396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project in.position(in.position() - 1); 17496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return CoderResult.malformedForLength(1); 17596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 17696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 17796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /** 17896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * @return True if there are base64 encoded characters waiting to be written 17996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 18096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private boolean base64bitsWaiting() { 18196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return tempChar != 0 || bitsRead >= 6; 18296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 18396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 18496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /** 18596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * <p> 18696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Updates internal state to reflect the decoder is no longer in <i>base 64 18796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * mode</i> 18896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * </p> 18996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 19096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private void setUnshifted() { 19196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project base64mode = false; 19296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project bitsRead = 0; 19396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project tempChar = 0; 19496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 19596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project} 196