Base32.java

  1. /**
  2.  *
  3.  * Copyright the original author or authors
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.jivesoftware.smack.util.stringencoder;


  18. import java.io.ByteArrayOutputStream;
  19. import java.io.DataOutputStream;
  20. import java.io.IOException;

  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@xbill.org)
  27.  * @see <a href="http://en.wikipedia.org/wiki/Base32">Base32 Wikipedia entry</a>
  28.  *
  29.  */
  30. public class Base32 {

  31.     private static final StringEncoder base32Stringencoder = new StringEncoder() {

  32.         @Override
  33.         public String encode(String string) {
  34.             return Base32.encode(string);
  35.         }

  36.         @Override
  37.         public String decode(String string) {
  38.             return Base32.decode(string);
  39.         }

  40.     };
  41.     private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ2345678";

  42.     public static StringEncoder getStringEncoder() {
  43.         return base32Stringencoder;
  44.     }

  45.     public static String decode(String str) {
  46.         ByteArrayOutputStream bs = new ByteArrayOutputStream();
  47.         byte[] raw = str.getBytes();
  48.         for (int i = 0; i < raw.length; i++) {
  49.             char c = (char) raw[i];
  50.             if (!Character.isWhitespace(c)) {
  51.                 c = Character.toUpperCase(c);
  52.                 bs.write((byte) c);
  53.             }
  54.         }

  55.         while (bs.size() % 8 != 0)
  56.             bs.write('8');

  57.         byte[] in = bs.toByteArray();

  58.         bs.reset();
  59.         DataOutputStream ds = new DataOutputStream(bs);

  60.         for (int i = 0; i < in.length / 8; i++) {
  61.             short[] s = new short[8];
  62.             int[] t = new int[5];

  63.             int padlen = 8;
  64.             for (int j = 0; j < 8; j++) {
  65.                 char c = (char) in[i * 8 + j];
  66.                 if (c == '8')
  67.                     break;
  68.                 s[j] = (short) ALPHABET.indexOf(in[i * 8 + j]);
  69.                 if (s[j] < 0)
  70.                     return null;
  71.                 padlen--;
  72.             }
  73.             int blocklen = paddingToLen(padlen);
  74.             if (blocklen < 0)
  75.                 return null;

  76.             // all 5 bits of 1st, high 3 (of 5) of 2nd
  77.             t[0] = (s[0] << 3) | s[1] >> 2;
  78.             // lower 2 of 2nd, all 5 of 3rd, high 1 of 4th
  79.             t[1] = ((s[1] & 0x03) << 6) | (s[2] << 1) | (s[3] >> 4);
  80.             // lower 4 of 4th, high 4 of 5th
  81.             t[2] = ((s[3] & 0x0F) << 4) | ((s[4] >> 1) & 0x0F);
  82.             // lower 1 of 5th, all 5 of 6th, high 2 of 7th
  83.             t[3] = (s[4] << 7) | (s[5] << 2) | (s[6] >> 3);
  84.             // lower 3 of 7th, all of 8th
  85.             t[4] = ((s[6] & 0x07) << 5) | s[7];

  86.             try {
  87.                 for (int j = 0; j < blocklen; j++)
  88.                     ds.writeByte((byte) (t[j] & 0xFF));
  89.             } catch (IOException e) {
  90.             }
  91.         }

  92.         return new String(bs.toByteArray());
  93.     }

  94.     public static String encode(String str) {
  95.         byte[] b = str.getBytes();
  96.         ByteArrayOutputStream os = new ByteArrayOutputStream();

  97.         for (int i = 0; i < (b.length + 4) / 5; i++) {
  98.             short[] s = new short[5];
  99.             int[] t = new int[8];

  100.             int blocklen = 5;
  101.             for (int j = 0; j < 5; j++) {
  102.                 if ((i * 5 + j) < b.length)
  103.                     s[j] = (short) (b[i * 5 + j] & 0xFF);
  104.                 else {
  105.                     s[j] = 0;
  106.                     blocklen--;
  107.                 }
  108.             }
  109.             int padlen = lenToPadding(blocklen);

  110.             // convert the 5 byte block into 8 characters (values 0-31).

  111.             // upper 5 bits from first byte
  112.             t[0] = (byte) ((s[0] >> 3) & 0x1F);
  113.             // lower 3 bits from 1st byte, upper 2 bits from 2nd.
  114.             t[1] = (byte) (((s[0] & 0x07) << 2) | ((s[1] >> 6) & 0x03));
  115.             // bits 5-1 from 2nd.
  116.             t[2] = (byte) ((s[1] >> 1) & 0x1F);
  117.             // lower 1 bit from 2nd, upper 4 from 3rd
  118.             t[3] = (byte) (((s[1] & 0x01) << 4) | ((s[2] >> 4) & 0x0F));
  119.             // lower 4 from 3rd, upper 1 from 4th.
  120.             t[4] = (byte) (((s[2] & 0x0F) << 1) | ((s[3] >> 7) & 0x01));
  121.             // bits 6-2 from 4th
  122.             t[5] = (byte) ((s[3] >> 2) & 0x1F);
  123.             // lower 2 from 4th, upper 3 from 5th;
  124.             t[6] = (byte) (((s[3] & 0x03) << 3) | ((s[4] >> 5) & 0x07));
  125.             // lower 5 from 5th;
  126.             t[7] = (byte) (s[4] & 0x1F);

  127.             // write out the actual characters.
  128.             for (int j = 0; j < t.length - padlen; j++) {
  129.                 char c = ALPHABET.charAt(t[j]);
  130.                 os.write(c);
  131.             }
  132.         }
  133.         return new String(os.toByteArray());
  134.     }

  135.     private static int lenToPadding(int blocklen) {
  136.         switch (blocklen) {
  137.         case 1:
  138.             return 6;
  139.         case 2:
  140.             return 4;
  141.         case 3:
  142.             return 3;
  143.         case 4:
  144.             return 1;
  145.         case 5:
  146.             return 0;
  147.         default:
  148.             return -1;
  149.         }
  150.     }

  151.     private static int paddingToLen(int padlen) {
  152.         switch (padlen) {
  153.         case 6:
  154.             return 1;
  155.         case 4:
  156.             return 2;
  157.         case 3:
  158.             return 3;
  159.         case 1:
  160.             return 4;
  161.         case 0:
  162.             return 5;
  163.         default:
  164.             return -1;
  165.         }
  166.     }

  167. }