/* * Copyright (c) 2006 Bjorn Andersson , Erik Ekman * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include // For FreeBSD #ifndef MIN #define MIN(a,b) ((a)<(b)?(a):(b)) #endif #define SPACING 63 #define ENC_CHUNK 8 #define RAW_CHUNK 5 static const char base32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ98765-"; static const char padder[] = " 1234"; static char reverse32[128]; static int reverse_init = 0; /* Eat 5 bytes from src, write 8 bytes to dest */ static void encode_chunk(char *dest, char *src) { unsigned char c; *dest++ = base32[(*src & 0xF8) >> 3]; // 1111 1000 first byte c = (*src++ & 0x07) << 2; // 0000 0111 first byte c |= ((*src & 0xC0) >> 6); // 1100 0000 second byte *dest++ = base32[(int) c]; *dest++ = base32[(*src & 0x3E) >> 1]; // 0011 1110 second byte c = (*src++ & 0x01) << 4; // 0000 0001 second byte c |= ((*src & 0xF0) >> 4); // 1111 0000 third byte *dest++ = base32[(int) c]; c = (*src++ & 0x0F) << 1; // 0000 1111 third byte c |= ((*src & 0x80) >> 7); // 1000 0000 fourth byte *dest++ = base32[(int) c]; *dest++ = base32[(*src & 0x7C) >> 2]; // 0111 1100 fourth byte c = (*src++ & 0x03) << 3; // 0000 0011 fourth byte c |= ((*src & 0xE0) >> 5); // 1110 0000 fifth byte *dest++ = base32[(int) c]; *dest++ = base32[*src++ & 0x1F]; // 0001 1111 fifth byte } /* Eat 8 bytes from src, write 5 bytes to dest */ static void decode_chunk(char *dest, char *src) { unsigned char c; int i; if (!reverse_init) { for (i = 0; i < 32; i++) { c = base32[i]; reverse32[(int) c] = i; } reverse_init = 1; } c = reverse32[(int) *src++] << 3; // Take bits 11111 from byte 1 c |= (reverse32[(int) *src] & 0x1C) >> 2; // Take bits 11100 from byte 2 *dest++ = c; c = (reverse32[(int) *src++] & 0x3) << 6; // Take bits 00011 from byte 2 c |= reverse32[(int) *src++] << 1; // Take bits 11111 from byte 3 c |= (reverse32[(int) *src] & 0x10) >> 4; // Take bits 10000 from byte 4 *dest++ = c; c = (reverse32[(int) *src++] & 0xF) << 4; // Take bits 01111 from byte 4 c |= (reverse32[(int) *src] & 0x1E) >> 1; // Take bits 11110 from byte 5 *dest++ = c; c = reverse32[(int) *src++] << 7; // Take bits 00001 from byte 5 c |= reverse32[(int) *src++] << 2; // Take bits 11111 from byte 6 c |= (reverse32[(int) *src] & 0x18) >> 3; // Take bits 11000 from byte 7 *dest++ = c; c = (reverse32[(int) *src++] & 0x7) << 5; // Take bits 00111 from byte 7 c |= reverse32[(int) *src++]; // Take bits 11111 from byte 8 *dest++ = c; } int encode_data(char *buf, int len, int space, char *dest, char flag) { int final; int write; int realwrite; int chunks; int leftovers; int i; char encoded[255]; char padding[5]; char *pp; char *dp; char *ep; space -= space / SPACING; chunks = (space - 1) / ENC_CHUNK; while ((chunks + 1) * ENC_CHUNK + 1 > space) { chunks--; } write = RAW_CHUNK * chunks; write = MIN(write, len); // do not use more bytes than is available; final = (write == len); // is this the last block? chunks = write / RAW_CHUNK; leftovers = write % RAW_CHUNK; // flag is special character to be placed first in the encoded data if (flag != 0) { *dest = flag; } else { // First byte is 0 for middle packet and 1 for last packet *dest = '0' + final; } dest++; memset(encoded, 0, sizeof(encoded)); ep = encoded; dp = buf; for (i = 0; i < chunks; i++) { encode_chunk(ep, dp); ep += ENC_CHUNK; dp += RAW_CHUNK; } realwrite = ENC_CHUNK * chunks; memset(padding, 0, sizeof(padding)); pp = padding; if (leftovers) { pp += RAW_CHUNK - leftovers; memcpy(pp, dp, leftovers); pp = padding; *ep++ = padder[leftovers]; encode_chunk(ep, pp); realwrite += ENC_CHUNK + 1; // plus padding character } ep = encoded; if (len > 0) { for (i = 1; i <= realwrite; i++) { if (i % SPACING == 0) { *dest++ = '.'; } *dest++ = *ep++; } } return write; } int decode_data(char *dest, int size, const char *src, char *srcend) { int len; int i; int chunks; int padded; char encoded[255]; char padding[5]; char *pp; char *ep; // Copy flag len = 1; *dest = *src; dest++; src++; memset(encoded, 0, sizeof(encoded)); ep = encoded; while(len < size && src < srcend) { if(*src == '.') { src++; continue; } *ep++ = *src++; } chunks = strlen(encoded) / 8; padded = strlen(encoded) % 8; ep = encoded; for (i = 0; i < chunks-1; i++) { decode_chunk(dest, ep); dest += RAW_CHUNK; ep += ENC_CHUNK; len += RAW_CHUNK; } // Read last chunk: if (padded) { pp = padding; padded = *ep++ - '0'; decode_chunk(pp, ep); pp += RAW_CHUNK - padded; memcpy(dest, pp, padded); len += padded; } else { decode_chunk(dest, ep); len += RAW_CHUNK; } return len; }