001/** 002 * 003 * Copyright the original author or authors 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smack.util; 018 019 020import java.io.ByteArrayOutputStream; 021import java.io.DataOutputStream; 022import java.io.IOException; 023 024/** 025 * Base32 string encoding is useful for when filenames case-insensitive filesystems are encoded. 026 * Base32 representation takes roughly 20% more space then Base64. 027 * 028 * @author Florian Schmaus 029 * Based on code by Brian Wellington (bwelling@xbill.org) 030 * @see <a href="http://en.wikipedia.org/wiki/Base32">Base32 Wikipedia entry</a> 031 * 032 */ 033public class Base32Encoder implements StringEncoder { 034 035 private static Base32Encoder instance = new Base32Encoder(); 036 private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ2345678"; 037 038 private Base32Encoder() { 039 // Use getInstance() 040 } 041 042 public static Base32Encoder getInstance() { 043 return instance; 044 } 045 046 @Override 047 public String decode(String str) { 048 ByteArrayOutputStream bs = new ByteArrayOutputStream(); 049 byte[] raw = str.getBytes(); 050 for (int i = 0; i < raw.length; i++) { 051 char c = (char) raw[i]; 052 if (!Character.isWhitespace(c)) { 053 c = Character.toUpperCase(c); 054 bs.write((byte) c); 055 } 056 } 057 058 while (bs.size() % 8 != 0) 059 bs.write('8'); 060 061 byte[] in = bs.toByteArray(); 062 063 bs.reset(); 064 DataOutputStream ds = new DataOutputStream(bs); 065 066 for (int i = 0; i < in.length / 8; i++) { 067 short[] s = new short[8]; 068 int[] t = new int[5]; 069 070 int padlen = 8; 071 for (int j = 0; j < 8; j++) { 072 char c = (char) in[i * 8 + j]; 073 if (c == '8') 074 break; 075 s[j] = (short) ALPHABET.indexOf(in[i * 8 + j]); 076 if (s[j] < 0) 077 return null; 078 padlen--; 079 } 080 int blocklen = paddingToLen(padlen); 081 if (blocklen < 0) 082 return null; 083 084 // all 5 bits of 1st, high 3 (of 5) of 2nd 085 t[0] = (s[0] << 3) | s[1] >> 2; 086 // lower 2 of 2nd, all 5 of 3rd, high 1 of 4th 087 t[1] = ((s[1] & 0x03) << 6) | (s[2] << 1) | (s[3] >> 4); 088 // lower 4 of 4th, high 4 of 5th 089 t[2] = ((s[3] & 0x0F) << 4) | ((s[4] >> 1) & 0x0F); 090 // lower 1 of 5th, all 5 of 6th, high 2 of 7th 091 t[3] = (s[4] << 7) | (s[5] << 2) | (s[6] >> 3); 092 // lower 3 of 7th, all of 8th 093 t[4] = ((s[6] & 0x07) << 5) | s[7]; 094 095 try { 096 for (int j = 0; j < blocklen; j++) 097 ds.writeByte((byte) (t[j] & 0xFF)); 098 } catch (IOException e) { 099 } 100 } 101 102 return new String(bs.toByteArray()); 103 } 104 105 @Override 106 public String encode(String str) { 107 byte[] b = str.getBytes(); 108 ByteArrayOutputStream os = new ByteArrayOutputStream(); 109 110 for (int i = 0; i < (b.length + 4) / 5; i++) { 111 short s[] = new short[5]; 112 int t[] = new int[8]; 113 114 int blocklen = 5; 115 for (int j = 0; j < 5; j++) { 116 if ((i * 5 + j) < b.length) 117 s[j] = (short) (b[i * 5 + j] & 0xFF); 118 else { 119 s[j] = 0; 120 blocklen--; 121 } 122 } 123 int padlen = lenToPadding(blocklen); 124 125 // convert the 5 byte block into 8 characters (values 0-31). 126 127 // upper 5 bits from first byte 128 t[0] = (byte) ((s[0] >> 3) & 0x1F); 129 // lower 3 bits from 1st byte, upper 2 bits from 2nd. 130 t[1] = (byte) (((s[0] & 0x07) << 2) | ((s[1] >> 6) & 0x03)); 131 // bits 5-1 from 2nd. 132 t[2] = (byte) ((s[1] >> 1) & 0x1F); 133 // lower 1 bit from 2nd, upper 4 from 3rd 134 t[3] = (byte) (((s[1] & 0x01) << 4) | ((s[2] >> 4) & 0x0F)); 135 // lower 4 from 3rd, upper 1 from 4th. 136 t[4] = (byte) (((s[2] & 0x0F) << 1) | ((s[3] >> 7) & 0x01)); 137 // bits 6-2 from 4th 138 t[5] = (byte) ((s[3] >> 2) & 0x1F); 139 // lower 2 from 4th, upper 3 from 5th; 140 t[6] = (byte) (((s[3] & 0x03) << 3) | ((s[4] >> 5) & 0x07)); 141 // lower 5 from 5th; 142 t[7] = (byte) (s[4] & 0x1F); 143 144 // write out the actual characters. 145 for (int j = 0; j < t.length - padlen; j++) { 146 char c = ALPHABET.charAt(t[j]); 147 os.write(c); 148 } 149 } 150 return new String(os.toByteArray()); 151 } 152 153 private static int lenToPadding(int blocklen) { 154 switch (blocklen) { 155 case 1: 156 return 6; 157 case 2: 158 return 4; 159 case 3: 160 return 3; 161 case 4: 162 return 1; 163 case 5: 164 return 0; 165 default: 166 return -1; 167 } 168 } 169 170 private static int paddingToLen(int padlen) { 171 switch (padlen) { 172 case 6: 173 return 1; 174 case 4: 175 return 2; 176 case 3: 177 return 3; 178 case 1: 179 return 4; 180 case 0: 181 return 5; 182 default: 183 return -1; 184 } 185 } 186 187}