type
TBase64 = class(TObject)
private
FFilterDecodeInput: boolean;
function ValueToCharacter(Value: byte; var Character: char): boolean;
function CharacterToValue(Character: char; var Value: byte): boolean;
function FilterLine(InputData: string): string;
public
function EncodeData(InputData: string; var OutputData: string): byte;
function DecodeData(InputData: string; var OutputData: string): byte;
constructor Create;
published
property FilterDecodeInput: boolean read FFilterDecodeInput write FFilterDecodeInput;
end;
const
BASE64_OK = 0; // no errors, conversion successful
BASE64_ERROR = 1; // unknown error (e.g. can't encode octet in input stream) -> error in implementation
BASE64_INVALID = 2; // invalid characters in input string (may occur only when filterdecodeinput=false)
BASE64_LENGTH = 3; // input data length is not a Base64 length (mod 4)
BASE64_DATALEFT = 4; // too much input data left (receveived 'end of encoded data' but not end of input string)
BASE64_PADDING = 5; // wrong padding (input data isn't closed with correct padding characters)
constructor TBase64.Create;
begin
inherited Create;
FFilterDecodeInput := True;
end;
function TBase64.ValueToCharacter(Value: byte; var Character: char): boolean;
begin
Result := True;
if (Value > AlphabetLength-1) then Result := False
else Character := Alphabet[Value + 1];
end;
function TBase64.CharacterToValue(Character: char; var Value: byte): boolean;
begin
Result := True;
Value := Pos(Character, Alphabet);
if Value = 0 then Result := False
else Value := Value - 1;
end;
function TBase64.EncodeData(InputData: string; var OutputData: string): byte;
var
i, InputLength: integer;
CurrentB, PreviousB, c: Byte;
s: char;
begin
OutPutData := '';
InputLength := Length(InputData);
i := 1;
if InputLength = 0 then begin
Result := BASE64_OK;
Exit;
end;
repeat // process first group
CurrentB := Ord(InputData[i]);
Inc(i);
InputLength := InputLength - 1;
c := (CurrentB shr 2);
if not(ValueToCharacter(c, s)) then begin
Result := BASE64_ERROR;
Exit;
end;
OutPutData := OutPutData + s;
PreviousB := CurrentB;
// process second group
if InputLength = 0 then CurrentB := 0
else begin
CurrentB := Ord(InputData[i]);
Inc(i);
end;
InputLength := InputLength - 1;
c := (PreviousB and $03) shl 4 + (CurrentB shr 4);
if not(ValueToCharacter(c, s)) then begin
Result := BASE64_ERROR;
Exit;
end;
OutPutData := OutPutData + s;
PreviousB := CurrentB;
// process third group
if InputLength < 0 then s := pad
else begin
if InputLength = 0 then CurrentB := 0
else begin
CurrentB := Ord(InputData[i]);
Inc(i);
end;
InputLength := InputLength - 1;
c := (PreviousB and $0F) shl 2 + (CurrentB shr 6);
if not(ValueToCharacter(c, s)) then begin
Result := BASE64_ERROR;
Exit;
end;
end;
OutPutData := OutPutData + s;
// process fourth group
if InputLength < 0 then s := pad
else begin
c := (CurrentB and $3F);
if not(ValueToCharacter(c, s)) then begin
Result := BASE64_ERROR;
Exit;
end;
end;
OutPutData := OutPutData + s;
until InputLength <= 0;
Result := BASE64_OK;
end;
function TBase64.FilterLine(InputData: string): string;
var
f: byte;
i: integer;
begin
Result := '';
for i := 1 to Length(InputData) do
if CharacterToValue(InputData[i], f) or (InputData[i] = pad) then
Result := Result + InputData[i];
end;
function TBase64.DecodeData(InputData: string; var OutputData: string): byte;
var
i, InputLength: integer;
CurrentB, PreviousB, c: byte;
s: char;
begin
if InputData = '' then begin
Result := BASE64_OK;
Exit;
end;
OutPutData:='';
if FilterDecodeInput then InputData := FilterLine(InputData);
InputLength := Length(InputData);
if InputLength mod 4 <> 0 then begin
Result := BASE64_LENGTH;
Exit;
end;
i := 0;
repeat // process first byte
Inc(i);
s := InputData[i];
if not(CharacterToValue(s, CurrentB)) then begin
Result := BASE64_INVALID;
Exit;
end;
Inc(i);
s := InputData[i];
if not(CharacterToValue(s, PreviousB)) then begin
Result := BASE64_INVALID;
Exit;
end;
// process second Byte
Inc(i);
s := InputData[i];
if s = pad then begin
if i <> InputLength-1 then begin
Result := BASE64_DATALEFT;
Exit; // too much data left
end else if InputData[i + 1] <> pad then begin
Result := BASE64_PADDING;
Exit;
end; // last char has to be a pad
end else begin
if not(CharacterToValue(s, CurrentB)) then begin
Result := BASE64_INVALID;
Exit;
end;
c := (PreviousB shl 4) + (CurrentB shr 2);
OutPutData := OutPutData + Chr(c);
end;
// process third Byte
Inc(i);
s := InputData[i];
if s = pad then begin
if i <> InputLength then begin
Result := BASE64_DATALEFT;
Exit;
end; // too much data Left
end else begin
if not(CharacterToValue(s, PreviousB)) then begin
Result := BASE64_INVALID;
Exit;
end;
c := (CurrentB shl 6) + (PreviousB);
OutPutData := OutPutData + Chr(c);
end;