394 lines
9.9 KiB
C
394 lines
9.9 KiB
C
/**
|
|
* Copyright (C) 2008 Happy Fish / YuQing
|
|
*
|
|
* FastDFS may be copied only under the terms of the GNU General
|
|
* Public License V3, which may be found in the FastDFS source kit.
|
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
|
**/
|
|
|
|
#include <time.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include "base64.h"
|
|
|
|
/**
|
|
* Marker value for chars we just ignore, e.g. \n \r high ascii
|
|
*/
|
|
#define BASE64_IGNORE -1
|
|
|
|
/**
|
|
* Marker for = trailing pad
|
|
*/
|
|
#define BASE64_PAD -2
|
|
|
|
/**
|
|
* determines how long the lines are that are generated by encode.
|
|
* Ignored by decode.
|
|
* @param length 0 means no newlines inserted. Must be a multiple of 4.
|
|
*/
|
|
void base64_set_line_length(struct base64_context *context, const int length)
|
|
{
|
|
context->line_length = (length / 4) * 4;
|
|
}
|
|
|
|
/**
|
|
* How lines are separated.
|
|
* Ignored by decode.
|
|
* @param context->line_separator may be "" but not null.
|
|
* Usually contains only a combination of chars \n and \r.
|
|
* Could be any chars not in set A-Z a-z 0-9 + /.
|
|
*/
|
|
void base64_set_line_separator(struct base64_context *context, \
|
|
const char *pLineSeparator)
|
|
{
|
|
context->line_sep_len = snprintf(context->line_separator, \
|
|
sizeof(context->line_separator), "%s", pLineSeparator);
|
|
}
|
|
|
|
void base64_init_ex(struct base64_context *context, const int nLineLength, \
|
|
const unsigned char chPlus, const unsigned char chSplash, \
|
|
const unsigned char chPad)
|
|
{
|
|
int i;
|
|
|
|
memset(context, 0, sizeof(struct base64_context));
|
|
|
|
context->line_length = nLineLength;
|
|
context->line_separator[0] = '\n';
|
|
context->line_separator[1] = '\0';
|
|
context->line_sep_len = 1;
|
|
|
|
// build translate valueToChar table only once.
|
|
// 0..25 -> 'A'..'Z'
|
|
for (i=0; i<=25; i++)
|
|
{
|
|
context->valueToChar[i] = (char)('A'+i);
|
|
}
|
|
// 26..51 -> 'a'..'z'
|
|
for (i=0; i<=25; i++ )
|
|
{
|
|
context->valueToChar[i+26] = (char)('a'+i);
|
|
}
|
|
// 52..61 -> '0'..'9'
|
|
for (i=0; i<=9; i++ )
|
|
{
|
|
context->valueToChar[i+52] = (char)('0'+i);
|
|
}
|
|
context->valueToChar[62] = chPlus;
|
|
context->valueToChar[63] = chSplash;
|
|
|
|
memset(context->charToValue, BASE64_IGNORE, sizeof(context->charToValue));
|
|
for (i=0; i<64; i++ )
|
|
{
|
|
context->charToValue[context->valueToChar[i]] = i;
|
|
}
|
|
|
|
context->pad_ch = chPad;
|
|
context->charToValue[chPad] = BASE64_PAD;
|
|
}
|
|
|
|
int base64_get_encode_length(struct base64_context *context, const int nSrcLen)
|
|
{
|
|
// Each group or partial group of 3 bytes becomes four chars
|
|
// covered quotient
|
|
int outputLength;
|
|
|
|
outputLength = ((nSrcLen + 2) / 3) * 4;
|
|
|
|
// account for trailing newlines, on all but the very last line
|
|
if (context->line_length != 0)
|
|
{
|
|
int lines = (outputLength + context->line_length - 1) /
|
|
context->line_length - 1;
|
|
if ( lines > 0 )
|
|
{
|
|
outputLength += lines * context->line_sep_len;
|
|
}
|
|
}
|
|
|
|
return outputLength;
|
|
}
|
|
|
|
/**
|
|
* Encode an arbitrary array of bytes as base64 printable ASCII.
|
|
* It will be broken into lines of 72 chars each. The last line is not
|
|
* terminated with a line separator.
|
|
* The output will always have an even multiple of data characters,
|
|
* exclusive of \n. It is padded out with =.
|
|
*/
|
|
char *base64_encode_ex(struct base64_context *context, const char *src, \
|
|
const int nSrcLen, char *dest, int *dest_len, const bool bPad)
|
|
{
|
|
int linePos;
|
|
int leftover;
|
|
int combined;
|
|
char *pDest;
|
|
int c0, c1, c2, c3;
|
|
unsigned char *pRaw;
|
|
unsigned char *pEnd;
|
|
const char *ppSrcs[2];
|
|
int lens[2];
|
|
char szPad[3];
|
|
int k;
|
|
int loop;
|
|
|
|
if (nSrcLen <= 0)
|
|
{
|
|
*dest = '\0';
|
|
*dest_len = 0;
|
|
return dest;
|
|
}
|
|
|
|
linePos = 0;
|
|
lens[0] = (nSrcLen / 3) * 3;
|
|
lens[1] = 3;
|
|
leftover = nSrcLen - lens[0];
|
|
ppSrcs[0] = src;
|
|
ppSrcs[1] = szPad;
|
|
|
|
szPad[0] = szPad[1] = szPad[2] = '\0';
|
|
switch (leftover)
|
|
{
|
|
case 0:
|
|
default:
|
|
loop = 1;
|
|
break;
|
|
case 1:
|
|
loop = 2;
|
|
szPad[0] = src[nSrcLen-1];
|
|
break;
|
|
case 2:
|
|
loop = 2;
|
|
szPad[0] = src[nSrcLen-2];
|
|
szPad[1] = src[nSrcLen-1];
|
|
break;
|
|
}
|
|
|
|
pDest = dest;
|
|
for (k=0; k<loop; k++)
|
|
{
|
|
pEnd = (unsigned char *)ppSrcs[k] + lens[k];
|
|
for (pRaw=(unsigned char *)ppSrcs[k]; pRaw<pEnd; pRaw+=3)
|
|
{
|
|
// Start a new line if next 4 chars won't fit on the current line
|
|
// We can't encapsulete the following code since the variable need to
|
|
// be local to this incarnation of encode.
|
|
linePos += 4;
|
|
if (linePos > context->line_length)
|
|
{
|
|
if (context->line_length != 0)
|
|
{
|
|
memcpy(pDest, context->line_separator, context->line_sep_len);
|
|
pDest += context->line_sep_len;
|
|
}
|
|
linePos = 4;
|
|
}
|
|
|
|
// get next three bytes in unsigned form lined up,
|
|
// in big-endian order
|
|
combined = ((*pRaw) << 16) | ((*(pRaw+1)) << 8) | (*(pRaw+2));
|
|
|
|
// break those 24 bits into a 4 groups of 6 bits,
|
|
// working LSB to MSB.
|
|
c3 = combined & 0x3f;
|
|
combined >>= 6;
|
|
c2 = combined & 0x3f;
|
|
combined >>= 6;
|
|
c1 = combined & 0x3f;
|
|
combined >>= 6;
|
|
c0 = combined & 0x3f;
|
|
|
|
// Translate into the equivalent alpha character
|
|
// emitting them in big-endian order.
|
|
*pDest++ = context->valueToChar[c0];
|
|
*pDest++ = context->valueToChar[c1];
|
|
*pDest++ = context->valueToChar[c2];
|
|
*pDest++ = context->valueToChar[c3];
|
|
}
|
|
}
|
|
|
|
*pDest = '\0';
|
|
*dest_len = pDest - dest;
|
|
|
|
// deal with leftover bytes
|
|
switch (leftover)
|
|
{
|
|
case 0:
|
|
default:
|
|
// nothing to do
|
|
break;
|
|
case 1:
|
|
// One leftover byte generates xx==
|
|
if (bPad)
|
|
{
|
|
*(pDest-1) = context->pad_ch;
|
|
*(pDest-2) = context->pad_ch;
|
|
}
|
|
else
|
|
{
|
|
*(pDest-2) = '\0';
|
|
*dest_len -= 2;
|
|
}
|
|
break;
|
|
case 2:
|
|
// Two leftover bytes generates xxx=
|
|
if (bPad)
|
|
{
|
|
*(pDest-1) = context->pad_ch;
|
|
}
|
|
else
|
|
{
|
|
*(pDest-1) = '\0';
|
|
*dest_len -= 1;
|
|
}
|
|
break;
|
|
} // end switch;
|
|
|
|
return dest;
|
|
}
|
|
|
|
char *base64_decode_auto(struct base64_context *context, const char *src, \
|
|
const int nSrcLen, char *dest, int *dest_len)
|
|
{
|
|
int nRemain;
|
|
int nPadLen;
|
|
int nNewLen;
|
|
char tmpBuff[256];
|
|
char *pBuff;
|
|
|
|
nRemain = nSrcLen % 4;
|
|
if (nRemain == 0)
|
|
{
|
|
return base64_decode(context, src, nSrcLen, dest, dest_len);
|
|
}
|
|
|
|
nPadLen = 4 - nRemain;
|
|
nNewLen = nSrcLen + nPadLen;
|
|
if (nNewLen <= sizeof(tmpBuff))
|
|
{
|
|
pBuff = tmpBuff;
|
|
}
|
|
else
|
|
{
|
|
pBuff = (char *)malloc(nNewLen);
|
|
if (pBuff == NULL)
|
|
{
|
|
fprintf(stderr, "Can't malloc %d bytes\n", \
|
|
nSrcLen + nPadLen + 1);
|
|
*dest_len = 0;
|
|
*dest = '\0';
|
|
return dest;
|
|
}
|
|
}
|
|
|
|
memcpy(pBuff, src, nSrcLen);
|
|
memset(pBuff + nSrcLen, context->pad_ch, nPadLen);
|
|
|
|
base64_decode(context, pBuff, nNewLen, dest, dest_len);
|
|
|
|
if (pBuff != tmpBuff)
|
|
{
|
|
free(pBuff);
|
|
}
|
|
|
|
return dest;
|
|
}
|
|
|
|
/**
|
|
* decode a well-formed complete base64 string back into an array of bytes.
|
|
* It must have an even multiple of 4 data characters (not counting \n),
|
|
* padded out with = as needed.
|
|
*/
|
|
char *base64_decode(struct base64_context *context, const char *src, \
|
|
const int nSrcLen, char *dest, int *dest_len)
|
|
{
|
|
// tracks where we are in a cycle of 4 input chars.
|
|
int cycle;
|
|
|
|
// where we combine 4 groups of 6 bits and take apart as 3 groups of 8.
|
|
int combined;
|
|
|
|
// will be an even multiple of 4 chars, plus some embedded \n
|
|
int dummies;
|
|
int value;
|
|
unsigned char *pSrc;
|
|
unsigned char *pSrcEnd;
|
|
char *pDest;
|
|
|
|
cycle = 0;
|
|
combined = 0;
|
|
dummies = 0;
|
|
pDest = dest;
|
|
pSrcEnd = (unsigned char *)src + nSrcLen;
|
|
for (pSrc=(unsigned char *)src; pSrc<pSrcEnd; pSrc++)
|
|
{
|
|
value = context->charToValue[*pSrc];
|
|
switch (value)
|
|
{
|
|
case BASE64_IGNORE:
|
|
// e.g. \n, just ignore it.
|
|
break;
|
|
case BASE64_PAD:
|
|
value = 0;
|
|
dummies++;
|
|
// fallthrough
|
|
default:
|
|
/* regular value character */
|
|
switch (cycle)
|
|
{
|
|
case 0:
|
|
combined = value;
|
|
cycle = 1;
|
|
break;
|
|
case 1:
|
|
combined <<= 6;
|
|
combined |= value;
|
|
cycle = 2;
|
|
break;
|
|
case 2:
|
|
combined <<= 6;
|
|
combined |= value;
|
|
cycle = 3;
|
|
break;
|
|
case 3:
|
|
combined <<= 6;
|
|
combined |= value;
|
|
// we have just completed a cycle of 4 chars.
|
|
// the four 6-bit values are in combined in big-endian order
|
|
// peel them off 8 bits at a time working lsb to msb
|
|
// to get our original 3 8-bit bytes back
|
|
|
|
*pDest++ = (char)(combined >> 16);
|
|
*pDest++ = (char)((combined & 0x0000FF00) >> 8);
|
|
*pDest++ = (char)(combined & 0x000000FF);
|
|
|
|
cycle = 0;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
} // end for
|
|
|
|
if (cycle != 0)
|
|
{
|
|
*dest = '\0';
|
|
*dest_len = 0;
|
|
fprintf(stderr, "Input to decode not an even multiple of " \
|
|
"4 characters; pad with %c\n", context->pad_ch);
|
|
return dest;
|
|
}
|
|
|
|
*dest_len = (pDest - dest) - dummies;
|
|
*(dest + (*dest_len)) = '\0';
|
|
|
|
return dest;
|
|
}
|
|
|