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 */ 14package org.jivesoftware.smack.util; 15 16 17import java.io.ByteArrayOutputStream; 18import java.io.DataOutputStream; 19import 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@xbill.org) 27 * @see <a href="http://en.wikipedia.org/wiki/Base32">Base32 Wikipedia entry<a> 28 * 29 */ 30public 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