base64.pp 8.5 KB

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