Home | History | Annotate | Download | only in util
      1 /**
      2  * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
      3  * you may not use this file except in compliance with the License.
      4  * You may obtain a copy of the License at
      5  *
      6  *     http://www.apache.org/licenses/LICENSE-2.0
      7  *
      8  * Unless required by applicable law or agreed to in writing, software
      9  * distributed under the License is distributed on an "AS IS" BASIS,
     10  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     11  * See the License for the specific language governing permissions and
     12  * limitations under the License.
     13  */
     14 package org.jivesoftware.smack.util;
     15 
     16 
     17 import java.io.ByteArrayOutputStream;
     18 import java.io.DataOutputStream;
     19 import java.io.IOException;
     20 
     21 /**
     22  * Base32 string encoding is useful for when filenames case-insensitive filesystems are encoded.
     23  * Base32 representation takes roughly 20% more space then Base64.
     24  *
     25  * @author Florian Schmaus
     26  * Based on code by Brian Wellington (bwelling (at) xbill.org)
     27  * @see <a href="http://en.wikipedia.org/wiki/Base32">Base32 Wikipedia entry<a>
     28  *
     29  */
     30 public class Base32Encoder implements StringEncoder {
     31 
     32     private static Base32Encoder instance = new Base32Encoder();
     33     private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ2345678";
     34 
     35     private Base32Encoder() {
     36         // Use getInstance()
     37     }
     38 
     39     public static Base32Encoder getInstance() {
     40         return instance;
     41     }
     42 
     43     @Override
     44     public String decode(String str) {
     45         ByteArrayOutputStream bs = new ByteArrayOutputStream();
     46         byte[] raw = str.getBytes();
     47         for (int i = 0; i < raw.length; i++) {
     48             char c = (char) raw[i];
     49             if (!Character.isWhitespace(c)) {
     50                 c = Character.toUpperCase(c);
     51                 bs.write((byte) c);
     52             }
     53         }
     54 
     55         while (bs.size() % 8 != 0)
     56             bs.write('8');
     57 
     58         byte[] in = bs.toByteArray();
     59 
     60         bs.reset();
     61         DataOutputStream ds = new DataOutputStream(bs);
     62 
     63         for (int i = 0; i < in.length / 8; i++) {
     64             short[] s = new short[8];
     65             int[] t = new int[5];
     66 
     67             int padlen = 8;
     68             for (int j = 0; j < 8; j++) {
     69                 char c = (char) in[i * 8 + j];
     70                 if (c == '8')
     71                     break;
     72                 s[j] = (short) ALPHABET.indexOf(in[i * 8 + j]);
     73                 if (s[j] < 0)
     74                     return null;
     75                 padlen--;
     76             }
     77             int blocklen = paddingToLen(padlen);
     78             if (blocklen < 0)
     79                 return null;
     80 
     81             // all 5 bits of 1st, high 3 (of 5) of 2nd
     82             t[0] = (s[0] << 3) | s[1] >> 2;
     83             // lower 2 of 2nd, all 5 of 3rd, high 1 of 4th
     84             t[1] = ((s[1] & 0x03) << 6) | (s[2] << 1) | (s[3] >> 4);
     85             // lower 4 of 4th, high 4 of 5th
     86             t[2] = ((s[3] & 0x0F) << 4) | ((s[4] >> 1) & 0x0F);
     87             // lower 1 of 5th, all 5 of 6th, high 2 of 7th
     88             t[3] = (s[4] << 7) | (s[5] << 2) | (s[6] >> 3);
     89             // lower 3 of 7th, all of 8th
     90             t[4] = ((s[6] & 0x07) << 5) | s[7];
     91 
     92             try {
     93                 for (int j = 0; j < blocklen; j++)
     94                     ds.writeByte((byte) (t[j] & 0xFF));
     95             } catch (IOException e) {
     96             }
     97         }
     98 
     99         return new String(bs.toByteArray());
    100     }
    101 
    102     @Override
    103     public String encode(String str) {
    104         byte[] b = str.getBytes();
    105         ByteArrayOutputStream os = new ByteArrayOutputStream();
    106 
    107         for (int i = 0; i < (b.length + 4) / 5; i++) {
    108             short s[] = new short[5];
    109             int t[] = new int[8];
    110 
    111             int blocklen = 5;
    112             for (int j = 0; j < 5; j++) {
    113                 if ((i * 5 + j) < b.length)
    114                     s[j] = (short) (b[i * 5 + j] & 0xFF);
    115                 else {
    116                     s[j] = 0;
    117                     blocklen--;
    118                 }
    119             }
    120             int padlen = lenToPadding(blocklen);
    121 
    122             // convert the 5 byte block into 8 characters (values 0-31).
    123 
    124             // upper 5 bits from first byte
    125             t[0] = (byte) ((s[0] >> 3) & 0x1F);
    126             // lower 3 bits from 1st byte, upper 2 bits from 2nd.
    127             t[1] = (byte) (((s[0] & 0x07) << 2) | ((s[1] >> 6) & 0x03));
    128             // bits 5-1 from 2nd.
    129             t[2] = (byte) ((s[1] >> 1) & 0x1F);
    130             // lower 1 bit from 2nd, upper 4 from 3rd
    131             t[3] = (byte) (((s[1] & 0x01) << 4) | ((s[2] >> 4) & 0x0F));
    132             // lower 4 from 3rd, upper 1 from 4th.
    133             t[4] = (byte) (((s[2] & 0x0F) << 1) | ((s[3] >> 7) & 0x01));
    134             // bits 6-2 from 4th
    135             t[5] = (byte) ((s[3] >> 2) & 0x1F);
    136             // lower 2 from 4th, upper 3 from 5th;
    137             t[6] = (byte) (((s[3] & 0x03) << 3) | ((s[4] >> 5) & 0x07));
    138             // lower 5 from 5th;
    139             t[7] = (byte) (s[4] & 0x1F);
    140 
    141             // write out the actual characters.
    142             for (int j = 0; j < t.length - padlen; j++) {
    143                 char c = ALPHABET.charAt(t[j]);
    144                 os.write(c);
    145             }
    146         }
    147         return new String(os.toByteArray());
    148     }
    149 
    150     private static int lenToPadding(int blocklen) {
    151         switch (blocklen) {
    152         case 1:
    153             return 6;
    154         case 2:
    155             return 4;
    156         case 3:
    157             return 3;
    158         case 4:
    159             return 1;
    160         case 5:
    161             return 0;
    162         default:
    163             return -1;
    164         }
    165     }
    166 
    167     private static int paddingToLen(int padlen) {
    168         switch (padlen) {
    169         case 6:
    170             return 1;
    171         case 4:
    172             return 2;
    173         case 3:
    174             return 3;
    175         case 1:
    176             return 4;
    177         case 0:
    178             return 5;
    179         default:
    180             return -1;
    181         }
    182     }
    183 
    184 }
    185