123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- {
- $Id$
- This file is part of the Free Component Library (FCL)
- Copyright (c) 1999-2000 by Michael Van Canneyt and Florian Klaempfl
- base64 encoder & decoder (c) 1999 Sebastian Guenther
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- **********************************************************************}
- // Encoding and decoding streams for base64 data as described in RFC2045
- {$MODE objfpc}
- {$H+}
- unit base64;
- interface
- uses classes;
- type
- TBase64EncodingStream = class(TStream)
- protected
- OutputStream: TStream;
- TotalBytesProcessed, BytesWritten: LongWord;
- Buf: array[0..2] of Byte;
- BufSize: Integer; // # of bytes used in Buf
- public
- constructor Create(AOutputStream: TStream);
- destructor Destroy; override;
- function Read(var Buffer; Count: Longint): Longint; override;
- function Write(const Buffer; Count: Longint): Longint; override;
- function Seek(Offset: Longint; Origin: Word): Longint; override;
- end;
- TBase64DecodingStream = class(TStream)
- protected
- InputStream: TStream;
- CurPos, InputStreamSize: LongInt;
- Buf: array[0..2] of Byte;
- BufPos: Integer; // Offset of byte which is to be read next
- fEOF: Boolean;
- public
- constructor Create(AInputStream: TStream);
- procedure Reset;
- function Read(var Buffer; Count: Longint): Longint; override;
- function Write(const Buffer; Count: Longint): Longint; override;
- function Seek(Offset: Longint; Origin: Word): Longint; override;
- property EOF: Boolean read fEOF;
- end;
- implementation
- const
- EncodingTable: PChar =
- 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
- DecTable: array[Byte] of Byte =
- (99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, // 0-15
- 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, // 16-31
- 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 62, 99, 99, 99, 63, // 32-47
- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 99, 99, 99, 00, 99, 99, // 48-63
- 99, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, // 64-79
- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 99, 99, 99, 99, 99, // 80-95
- 99, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96-111
- 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 99, 99, 99, 99, 99, // 112-127
- 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
- 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
- 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
- 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
- 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
- 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
- 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
- 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99);
- constructor TBase64EncodingStream.Create(AOutputStream: TStream);
- begin
- inherited Create;
- OutputStream := AOutputStream;
- end;
- destructor TBase64EncodingStream.Destroy;
- var
- WriteBuf: array[0..3] of Char;
- begin
- // Fill output to multiple of 4
- case (TotalBytesProcessed mod 3) of
- 1: begin
- WriteBuf[0] := EncodingTable[Buf[0] shr 2];
- WriteBuf[1] := EncodingTable[(Buf[0] and 3) shl 4];
- WriteBuf[2] := '=';
- WriteBuf[3] := '=';
- OutputStream.Write(WriteBuf, 4);
- end;
- 2: begin
- WriteBuf[0] := EncodingTable[Buf[0] shr 2];
- WriteBuf[1] := EncodingTable[(Buf[0] and 3) shl 4 or (Buf[1] shr 4)];
- WriteBuf[2] := EncodingTable[(Buf[1] and 15) shl 2];
- WriteBuf[3] := '=';
- OutputStream.Write(WriteBuf, 4);
- end;
- end;
- inherited Destroy;
- end;
- function TBase64EncodingStream.Read(var Buffer; Count: Longint): Longint;
- begin
- raise EStreamError.Create('Invalid stream operation');
- end;
- function TBase64EncodingStream.Write(const Buffer; Count: Longint): Longint;
- var
- ReadNow: LongInt;
- p: Pointer;
- WriteBuf: array[0..3] of Char;
- begin
- Inc(TotalBytesProcessed, Count);
- Result := Count;
- p := @Buffer;
- while count > 0 do begin
- // Fetch data into the Buffer
- ReadNow := 3 - BufSize;
- if ReadNow > Count then break; // Not enough data available
- Move(p^, Buf[BufSize], ReadNow);
- Inc(p, ReadNow);
- Dec(Count, ReadNow);
- // Encode the 3 bytes in Buf
- WriteBuf[0] := EncodingTable[Buf[0] shr 2];
- WriteBuf[1] := EncodingTable[(Buf[0] and 3) shl 4 or (Buf[1] shr 4)];
- WriteBuf[2] := EncodingTable[(Buf[1] and 15) shl 2 or (Buf[2] shr 6)];
- WriteBuf[3] := EncodingTable[Buf[2] and 63];
- OutputStream.Write(WriteBuf, 4);
- Inc(BytesWritten, 4);
- BufSize := 0;
- end;
- Move(p^, Buf[BufSize], count);
- Inc(BufSize, count);
- end;
- function TBase64EncodingStream.Seek(Offset: Longint; Origin: Word): Longint;
- begin
- Result := BytesWritten;
- if BufSize > 0 then
- Inc(Result, 4);
- // This stream only supports the Seek modes needed for determining its size
- if not ((((Origin = soFromCurrent) or (Origin = soFromEnd)) and (Offset = 0))
- or ((Origin = soFromBeginning) and (Offset = Result))) then
- raise EStreamError.Create('Invalid stream operation');
- end;
- constructor TBase64DecodingStream.Create(AInputStream: TStream);
- begin
- inherited Create;
- InputStream := AInputStream;
- Reset;
- end;
- procedure TBase64DecodingStream.Reset;
- begin
- InputStreamSize := -1;
- BufPos := 3;
- fEOF := False;
- end;
- function TBase64DecodingStream.Read(var Buffer; Count: Longint): Longint;
- var
- p: PChar;
- b: Char;
- ReadBuf: array[0..3] of Byte;
- ToRead, OrgToRead, HaveRead, ReadOK, i, j: Integer;
- begin
- if Count <= 0 then exit(0);
- if InputStreamSize <> -1 then begin
- if CurPos + Count > InputStreamSize then
- Count := InputStreamSize - CurPos;
- if Count <= 0 then exit(0);
- end;
- Result := 0;
- p := PChar(@Buffer);
- while (Count > 0) and not fEOF do begin
- if BufPos > 2 then begin
- BufPos := 0;
- // Read the next 4 valid bytes
- ToRead := 4;
- ReadOK := 0;
- while ToRead > 0 do begin
- OrgToRead := ToRead;
- HaveRead := InputStream.Read(ReadBuf[ReadOK], ToRead);
- //WriteLn('ToRead = ', ToRead, ', HaveRead = ', HaveRead, ', ReadOK=', ReadOk);
- if HaveRead > 0 then begin
- i := ReadOk;
- while i <= HaveRead do begin
- ReadBuf[i] := DecTable[ReadBuf[i]];
- if ReadBuf[i] = 99 then
- for j := i to 3 do
- ReadBuf[i] := ReadBuf[i + 1]
- else begin
- Inc(i);
- Inc(ReadOK);
- Dec(ToRead);
- end;
- end;
- end;
- if HaveRead <> OrgToRead then begin
- //WriteLn('Ende? ReadOK=', ReadOK, ', count=', Count);
- for i := ReadOK to 3 do
- ReadBuf[i] := Ord('=');
- fEOF := True;
- if ReadOK < 2 then exit; // Not enough data available in input stream
- break;
- end;
- end;
- // Check for fill bytes
- if (Count >= 2) and (ReadBuf[3] = Ord('=')) then begin
- //WriteLn('Endemarkierung!');
- fEOF := True;
- if ReadBuf[2] = Ord('=') then
- Count := 1
- else
- Count := 2;
- end;
- // Decode the 4 bytes in the buffer to 3 undecoded bytes
- Buf[0] := ReadBuf[0] shl 2 or ReadBuf[1] shr 4;
- Buf[1] := (ReadBuf[1] and 15) shl 4 or ReadBuf[2] shr 2;
- Buf[2] := (ReadBuf[2] and 3) shl 6 or ReadBuf[3];
- end;
- p[0] := Chr(Buf[BufPos]);
- Inc(p);
- Inc(BufPos);
- Inc(CurPos);
- Dec(Count);
- Inc(Result);
- end;
- end;
- function TBase64DecodingStream.Write(const Buffer; Count: Longint): Longint;
- begin
- raise EStreamError.Create('Invalid stream operation');
- end;
- function TBase64DecodingStream.Seek(Offset: Longint; Origin: Word): Longint;
- var
- ipos: LongInt;
- endbytes: array[0..1] of Char;
- begin
- {This will work only if the input stream supports seeking / Size. If not, the
- input stream will raise an exception; we don't handle them here but pass them
- to the caller.}
- if InputStreamSize = -1 then begin
- ipos := InputStream.Position;
- InputStreamSize := ((InputStream.Size - ipos + 3) div 4) * 3;
- InputStream.Seek(-2, soFromEnd);
- InputStream.Read(endbytes, 2);
- InputStream.Position := ipos;
- if endbytes[1] = '=' then begin
- Dec(InputStreamSize);
- if endbytes[0] = '=' then
- Dec(InputStreamSize);
- end;
- end;
- // This stream only supports the Seek modes needed for determining its size
- if (Origin = soFromCurrent) and (Offset = 0) then
- Result := CurPos
- else if (Origin = soFromEnd) and (Offset = 0) then
- Result := InputStreamSize
- else if (Origin = soFromBeginning) and (Offset = CurPos) then
- Result := CurPos
- else
- raise EStreamError.Create('Invalid stream operation');
- end;
- end.
- {
- $Log$
- Revision 1.3 2002-09-07 15:15:24 peter
- * old logs removed and tabs fixed
- }
|