base64.pp 9.0 KB

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