base64.pp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 1999-2000 by Michael Van Canneyt and Florian Klaempfl
  4. base64 encoder & decoder (c) 1999 Sebastian Guenther
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. // Encoding and decoding streams for base64 data as described in RFC2045
  12. {$MODE objfpc}
  13. {$H+}
  14. unit base64;
  15. interface
  16. uses classes;
  17. type
  18. TBase64EncodingStream = class(TStream)
  19. protected
  20. OutputStream: TStream;
  21. TotalBytesProcessed, BytesWritten: LongWord;
  22. Buf: array[0..2] of Byte;
  23. BufSize: Integer; // # of bytes used in Buf
  24. public
  25. constructor Create(AOutputStream: TStream);
  26. destructor Destroy; override;
  27. function Read(var Buffer; Count: Longint): Longint; override;
  28. function Write(const Buffer; Count: Longint): Longint; override;
  29. function Seek(Offset: Longint; Origin: Word): Longint; override;
  30. end;
  31. TBase64DecodingStream = class(TStream)
  32. protected
  33. InputStream: TStream;
  34. CurPos, InputStreamSize: LongInt;
  35. Buf: array[0..2] of Byte;
  36. BufPos: Integer; // Offset of byte which is to be read next
  37. fEOF: Boolean;
  38. public
  39. constructor Create(AInputStream: TStream);
  40. procedure Reset;
  41. function Read(var Buffer; Count: Longint): Longint; override;
  42. function Write(const Buffer; Count: Longint): Longint; override;
  43. function Seek(Offset: Longint; Origin: Word): Longint; override;
  44. property EOF: Boolean read fEOF;
  45. end;
  46. implementation
  47. const
  48. EncodingTable: PChar =
  49. 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  50. DecTable: array[Byte] of Byte =
  51. (99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, // 0-15
  52. 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, // 16-31
  53. 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 62, 99, 99, 99, 63, // 32-47
  54. 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 99, 99, 99, 00, 99, 99, // 48-63
  55. 99, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, // 64-79
  56. 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 99, 99, 99, 99, 99, // 80-95
  57. 99, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96-111
  58. 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 99, 99, 99, 99, 99, // 112-127
  59. 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
  60. 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
  61. 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
  62. 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
  63. 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
  64. 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
  65. 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
  66. 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99);
  67. constructor TBase64EncodingStream.Create(AOutputStream: TStream);
  68. begin
  69. inherited Create;
  70. OutputStream := AOutputStream;
  71. end;
  72. destructor TBase64EncodingStream.Destroy;
  73. var
  74. WriteBuf: array[0..3] of Char;
  75. begin
  76. // Fill output to multiple of 4
  77. case (TotalBytesProcessed mod 3) of
  78. 1: begin
  79. WriteBuf[0] := EncodingTable[Buf[0] shr 2];
  80. WriteBuf[1] := EncodingTable[(Buf[0] and 3) shl 4];
  81. WriteBuf[2] := '=';
  82. WriteBuf[3] := '=';
  83. OutputStream.Write(WriteBuf, 4);
  84. end;
  85. 2: begin
  86. WriteBuf[0] := EncodingTable[Buf[0] shr 2];
  87. WriteBuf[1] := EncodingTable[(Buf[0] and 3) shl 4 or (Buf[1] shr 4)];
  88. WriteBuf[2] := EncodingTable[(Buf[1] and 15) shl 2];
  89. WriteBuf[3] := '=';
  90. OutputStream.Write(WriteBuf, 4);
  91. end;
  92. end;
  93. inherited Destroy;
  94. end;
  95. function TBase64EncodingStream.Read(var Buffer; Count: Longint): Longint;
  96. begin
  97. raise EStreamError.Create('Invalid stream operation');
  98. end;
  99. function TBase64EncodingStream.Write(const Buffer; Count: Longint): Longint;
  100. var
  101. ReadNow: LongInt;
  102. p: Pointer;
  103. WriteBuf: array[0..3] of Char;
  104. begin
  105. Inc(TotalBytesProcessed, Count);
  106. Result := Count;
  107. p := @Buffer;
  108. while count > 0 do begin
  109. // Fetch data into the Buffer
  110. ReadNow := 3 - BufSize;
  111. if ReadNow > Count then break; // Not enough data available
  112. Move(p^, Buf[BufSize], ReadNow);
  113. Inc(p, ReadNow);
  114. Dec(Count, ReadNow);
  115. // Encode the 3 bytes in Buf
  116. WriteBuf[0] := EncodingTable[Buf[0] shr 2];
  117. WriteBuf[1] := EncodingTable[(Buf[0] and 3) shl 4 or (Buf[1] shr 4)];
  118. WriteBuf[2] := EncodingTable[(Buf[1] and 15) shl 2 or (Buf[2] shr 6)];
  119. WriteBuf[3] := EncodingTable[Buf[2] and 63];
  120. OutputStream.Write(WriteBuf, 4);
  121. Inc(BytesWritten, 4);
  122. BufSize := 0;
  123. end;
  124. Move(p^, Buf[BufSize], count);
  125. Inc(BufSize, count);
  126. end;
  127. function TBase64EncodingStream.Seek(Offset: Longint; Origin: Word): Longint;
  128. begin
  129. Result := BytesWritten;
  130. if BufSize > 0 then
  131. Inc(Result, 4);
  132. // This stream only supports the Seek modes needed for determining its size
  133. if not ((((Origin = soFromCurrent) or (Origin = soFromEnd)) and (Offset = 0))
  134. or ((Origin = soFromBeginning) and (Offset = Result))) then
  135. raise EStreamError.Create('Invalid stream operation');
  136. end;
  137. constructor TBase64DecodingStream.Create(AInputStream: TStream);
  138. begin
  139. inherited Create;
  140. InputStream := AInputStream;
  141. Reset;
  142. end;
  143. procedure TBase64DecodingStream.Reset;
  144. begin
  145. InputStreamSize := -1;
  146. BufPos := 3;
  147. fEOF := False;
  148. end;
  149. function TBase64DecodingStream.Read(var Buffer; Count: Longint): Longint;
  150. var
  151. p: PChar;
  152. b: Char;
  153. ReadBuf: array[0..3] of Byte;
  154. ToRead, OrgToRead, HaveRead, ReadOK, i, j: Integer;
  155. begin
  156. if Count <= 0 then exit(0);
  157. if InputStreamSize <> -1 then begin
  158. if CurPos + Count > InputStreamSize then
  159. Count := InputStreamSize - CurPos;
  160. if Count <= 0 then exit(0);
  161. end;
  162. Result := 0;
  163. p := PChar(@Buffer);
  164. while (Count > 0) and not fEOF do begin
  165. if BufPos > 2 then begin
  166. BufPos := 0;
  167. // Read the next 4 valid bytes
  168. ToRead := 4;
  169. ReadOK := 0;
  170. while ToRead > 0 do begin
  171. OrgToRead := ToRead;
  172. HaveRead := InputStream.Read(ReadBuf[ReadOK], ToRead);
  173. //WriteLn('ToRead = ', ToRead, ', HaveRead = ', HaveRead, ', ReadOK=', ReadOk);
  174. if HaveRead > 0 then begin
  175. i := ReadOk;
  176. while i <= HaveRead do begin
  177. ReadBuf[i] := DecTable[ReadBuf[i]];
  178. if ReadBuf[i] = 99 then
  179. for j := i to 3 do
  180. ReadBuf[i] := ReadBuf[i + 1]
  181. else begin
  182. Inc(i);
  183. Inc(ReadOK);
  184. Dec(ToRead);
  185. end;
  186. end;
  187. end;
  188. if HaveRead <> OrgToRead then begin
  189. //WriteLn('Ende? ReadOK=', ReadOK, ', count=', Count);
  190. for i := ReadOK to 3 do
  191. ReadBuf[i] := Ord('=');
  192. fEOF := True;
  193. if ReadOK < 2 then exit; // Not enough data available in input stream
  194. break;
  195. end;
  196. end;
  197. // Check for fill bytes
  198. if (Count >= 2) and (ReadBuf[3] = Ord('=')) then begin
  199. //WriteLn('Endemarkierung!');
  200. fEOF := True;
  201. if ReadBuf[2] = Ord('=') then
  202. Count := 1
  203. else
  204. Count := 2;
  205. end;
  206. // Decode the 4 bytes in the buffer to 3 undecoded bytes
  207. Buf[0] := ReadBuf[0] shl 2 or ReadBuf[1] shr 4;
  208. Buf[1] := (ReadBuf[1] and 15) shl 4 or ReadBuf[2] shr 2;
  209. Buf[2] := (ReadBuf[2] and 3) shl 6 or ReadBuf[3];
  210. end;
  211. p[0] := Chr(Buf[BufPos]);
  212. Inc(p);
  213. Inc(BufPos);
  214. Inc(CurPos);
  215. Dec(Count);
  216. Inc(Result);
  217. end;
  218. end;
  219. function TBase64DecodingStream.Write(const Buffer; Count: Longint): Longint;
  220. begin
  221. raise EStreamError.Create('Invalid stream operation');
  222. end;
  223. function TBase64DecodingStream.Seek(Offset: Longint; Origin: Word): Longint;
  224. var
  225. ipos: LongInt;
  226. endbytes: array[0..1] of Char;
  227. begin
  228. {This will work only if the input stream supports seeking / Size. If not, the
  229. input stream will raise an exception; we don't handle them here but pass them
  230. to the caller.}
  231. if InputStreamSize = -1 then begin
  232. ipos := InputStream.Position;
  233. InputStreamSize := ((InputStream.Size - ipos + 3) div 4) * 3;
  234. InputStream.Seek(-2, soFromEnd);
  235. InputStream.Read(endbytes, 2);
  236. InputStream.Position := ipos;
  237. if endbytes[1] = '=' then begin
  238. Dec(InputStreamSize);
  239. if endbytes[0] = '=' then
  240. Dec(InputStreamSize);
  241. end;
  242. end;
  243. // This stream only supports the Seek modes needed for determining its size
  244. if (Origin = soFromCurrent) and (Offset = 0) then
  245. Result := CurPos
  246. else if (Origin = soFromEnd) and (Offset = 0) then
  247. Result := InputStreamSize
  248. else if (Origin = soFromBeginning) and (Offset = CurPos) then
  249. Result := CurPos
  250. else
  251. raise EStreamError.Create('Invalid stream operation');
  252. end;
  253. end.