| /** |
| * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.jivesoftware.smack.util; |
| |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.DataOutputStream; |
| import java.io.IOException; |
| |
| /** |
| * Base32 string encoding is useful for when filenames case-insensitive filesystems are encoded. |
| * Base32 representation takes roughly 20% more space then Base64. |
| * |
| * @author Florian Schmaus |
| * Based on code by Brian Wellington (bwelling@xbill.org) |
| * @see <a href="http://en.wikipedia.org/wiki/Base32">Base32 Wikipedia entry<a> |
| * |
| */ |
| public class Base32Encoder implements StringEncoder { |
| |
| private static Base32Encoder instance = new Base32Encoder(); |
| private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ2345678"; |
| |
| private Base32Encoder() { |
| // Use getInstance() |
| } |
| |
| public static Base32Encoder getInstance() { |
| return instance; |
| } |
| |
| @Override |
| public String decode(String str) { |
| ByteArrayOutputStream bs = new ByteArrayOutputStream(); |
| byte[] raw = str.getBytes(); |
| for (int i = 0; i < raw.length; i++) { |
| char c = (char) raw[i]; |
| if (!Character.isWhitespace(c)) { |
| c = Character.toUpperCase(c); |
| bs.write((byte) c); |
| } |
| } |
| |
| while (bs.size() % 8 != 0) |
| bs.write('8'); |
| |
| byte[] in = bs.toByteArray(); |
| |
| bs.reset(); |
| DataOutputStream ds = new DataOutputStream(bs); |
| |
| for (int i = 0; i < in.length / 8; i++) { |
| short[] s = new short[8]; |
| int[] t = new int[5]; |
| |
| int padlen = 8; |
| for (int j = 0; j < 8; j++) { |
| char c = (char) in[i * 8 + j]; |
| if (c == '8') |
| break; |
| s[j] = (short) ALPHABET.indexOf(in[i * 8 + j]); |
| if (s[j] < 0) |
| return null; |
| padlen--; |
| } |
| int blocklen = paddingToLen(padlen); |
| if (blocklen < 0) |
| return null; |
| |
| // all 5 bits of 1st, high 3 (of 5) of 2nd |
| t[0] = (s[0] << 3) | s[1] >> 2; |
| // lower 2 of 2nd, all 5 of 3rd, high 1 of 4th |
| t[1] = ((s[1] & 0x03) << 6) | (s[2] << 1) | (s[3] >> 4); |
| // lower 4 of 4th, high 4 of 5th |
| t[2] = ((s[3] & 0x0F) << 4) | ((s[4] >> 1) & 0x0F); |
| // lower 1 of 5th, all 5 of 6th, high 2 of 7th |
| t[3] = (s[4] << 7) | (s[5] << 2) | (s[6] >> 3); |
| // lower 3 of 7th, all of 8th |
| t[4] = ((s[6] & 0x07) << 5) | s[7]; |
| |
| try { |
| for (int j = 0; j < blocklen; j++) |
| ds.writeByte((byte) (t[j] & 0xFF)); |
| } catch (IOException e) { |
| } |
| } |
| |
| return new String(bs.toByteArray()); |
| } |
| |
| @Override |
| public String encode(String str) { |
| byte[] b = str.getBytes(); |
| ByteArrayOutputStream os = new ByteArrayOutputStream(); |
| |
| for (int i = 0; i < (b.length + 4) / 5; i++) { |
| short s[] = new short[5]; |
| int t[] = new int[8]; |
| |
| int blocklen = 5; |
| for (int j = 0; j < 5; j++) { |
| if ((i * 5 + j) < b.length) |
| s[j] = (short) (b[i * 5 + j] & 0xFF); |
| else { |
| s[j] = 0; |
| blocklen--; |
| } |
| } |
| int padlen = lenToPadding(blocklen); |
| |
| // convert the 5 byte block into 8 characters (values 0-31). |
| |
| // upper 5 bits from first byte |
| t[0] = (byte) ((s[0] >> 3) & 0x1F); |
| // lower 3 bits from 1st byte, upper 2 bits from 2nd. |
| t[1] = (byte) (((s[0] & 0x07) << 2) | ((s[1] >> 6) & 0x03)); |
| // bits 5-1 from 2nd. |
| t[2] = (byte) ((s[1] >> 1) & 0x1F); |
| // lower 1 bit from 2nd, upper 4 from 3rd |
| t[3] = (byte) (((s[1] & 0x01) << 4) | ((s[2] >> 4) & 0x0F)); |
| // lower 4 from 3rd, upper 1 from 4th. |
| t[4] = (byte) (((s[2] & 0x0F) << 1) | ((s[3] >> 7) & 0x01)); |
| // bits 6-2 from 4th |
| t[5] = (byte) ((s[3] >> 2) & 0x1F); |
| // lower 2 from 4th, upper 3 from 5th; |
| t[6] = (byte) (((s[3] & 0x03) << 3) | ((s[4] >> 5) & 0x07)); |
| // lower 5 from 5th; |
| t[7] = (byte) (s[4] & 0x1F); |
| |
| // write out the actual characters. |
| for (int j = 0; j < t.length - padlen; j++) { |
| char c = ALPHABET.charAt(t[j]); |
| os.write(c); |
| } |
| } |
| return new String(os.toByteArray()); |
| } |
| |
| private static int lenToPadding(int blocklen) { |
| switch (blocklen) { |
| case 1: |
| return 6; |
| case 2: |
| return 4; |
| case 3: |
| return 3; |
| case 4: |
| return 1; |
| case 5: |
| return 0; |
| default: |
| return -1; |
| } |
| } |
| |
| private static int paddingToLen(int padlen) { |
| switch (padlen) { |
| case 6: |
| return 1; |
| case 4: |
| return 2; |
| case 3: |
| return 3; |
| case 1: |
| return 4; |
| case 0: |
| return 5; |
| default: |
| return -1; |
| } |
| } |
| |
| } |