IdCoderQuotedPrintable.pas 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. {
  2. $Project$
  3. $Workfile$
  4. $Revision$
  5. $DateUTC$
  6. $Id$
  7. This file is part of the Indy (Internet Direct) project, and is offered
  8. under the dual-licensing agreement described on the Indy website.
  9. (http://www.indyproject.org/)
  10. Copyright:
  11. (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
  12. }
  13. {
  14. $Log$
  15. }
  16. {
  17. Rev 1.20 10/26/2004 11:08:10 PM JPMugaas
  18. Updated refs.
  19. Rev 1.19 27.08.2004 22:03:22 Andreas Hausladen
  20. Optimized encoders
  21. speed optimization ("const" for string parameters)
  22. Rev 1.18 24/08/2004 10:33:44 CCostelloe
  23. Was too slow (~45 mins for 2MB down to ~1sec)
  24. Rev 1.17 2004.05.20 1:39:22 PM czhower
  25. Last of the IdStream updates
  26. Rev 1.16 2004.05.20 11:37:10 AM czhower
  27. IdStreamVCL
  28. Rev 1.15 2004.05.20 11:13:14 AM czhower
  29. More IdStream conversions
  30. Rev 1.14 2004.05.19 3:06:54 PM czhower
  31. IdStream / .NET fix
  32. Rev 1.13 2/19/2004 11:52:02 PM JPMugaas
  33. Removed IFDEF's. Moved some functions into IdGlobalProtocols for reuse
  34. elsewhere.
  35. Rev 1.12 2004.02.03 5:45:00 PM czhower
  36. Name changes
  37. Rev 1.11 2004.02.03 2:12:06 PM czhower
  38. $I path change
  39. Rev 1.10 1/22/2004 3:59:14 PM SPerry
  40. fixed set problems
  41. Rev 1.9 11/10/2003 7:41:30 PM BGooijen
  42. Did all todo's ( TStream to TIdStream mainly )
  43. Rev 1.8 2003.10.17 6:14:44 PM czhower
  44. Fix to match new IdStream
  45. Rev 1.7 2003.10.12 3:38:26 PM czhower
  46. Added path to .inc
  47. Rev 1.6 10/12/2003 1:33:42 PM BGooijen
  48. Compiles on D7 now too
  49. Rev 1.5 10/12/2003 12:02:50 PM BGooijen
  50. DotNet
  51. Rev 1.4 6/13/2003 12:07:44 PM JPMugaas
  52. QP was broken again.
  53. Rev 1.3 6/13/2003 07:58:50 AM JPMugaas
  54. Should now compile with new decoder design.
  55. Rev 1.2 6/13/2003 06:17:06 AM JPMugaas
  56. Should now compil,e.
  57. Rev 1.1 12.6.2003 ã. 12:00:28 DBondzhev
  58. Fix for . at the begining of new line
  59. Rev 1.0 11/14/2002 02:15:00 PM JPMugaas
  60. 2002-08-13/14 - Johannes Berg
  61. completely rewrote the Encoder. May do the Decoder later.
  62. The encoder will add an EOL to the end of the file if it had no EOL
  63. at start. I can't avoid this due to the design of IdStream.ReadLn,
  64. but its also no problem, because in transmission this would happen
  65. anyway.
  66. 9-17-2001 - J. Peter Mugaas
  67. made the interpretation of =20 + EOL to mean a hard line break
  68. soft line breaks are now ignored. It does not make much sense
  69. in plain text. Soft breaks do not indicate the end of paragraphs unlike
  70. hard line breaks that do end paragraphs.
  71. 3-24-2001 - J. Peter Mugaas
  72. Rewrote the Decoder according to a new design.
  73. 3-25-2001 - J. Peter Mugaas
  74. Rewrote the Encoder according to the new design
  75. }
  76. unit IdCoderQuotedPrintable;
  77. interface
  78. {$i IdCompilerDefines.inc}
  79. uses
  80. Classes,
  81. IdCoder,
  82. SysUtils;
  83. type
  84. TIdDecoderQuotedPrintable = class(TIdDecoder)
  85. public
  86. procedure Decode(ASrcStream: TStream; const ABytes: Integer = -1); override;
  87. end;
  88. TIdEncoderQuotedPrintable = class(TIdEncoder)
  89. public
  90. procedure Encode(ASrcStream, ADestStream: TStream; const ABytes: Integer = -1); override;
  91. end;
  92. implementation
  93. uses
  94. IdException,
  95. IdGlobal,
  96. IdGlobalProtocols;
  97. { TIdDecoderQuotedPrintable }
  98. procedure TIdDecoderQuotedPrintable.Decode(ASrcStream: TStream; const ABytes: Integer = -1);
  99. var
  100. LBuffer: TIdBytes;
  101. i : Integer;
  102. B, DecodedByte : Byte;
  103. LBufferLen: Integer;
  104. LBufferIndex: Integer;
  105. LPos: integer;
  106. procedure StripEOLChars;
  107. var
  108. j: Integer;
  109. begin
  110. for j := 1 to 2 do begin
  111. if (LBufferIndex >= LBufferLen) or (not ByteIsInEOL(LBuffer, LBufferIndex)) then begin
  112. Break;
  113. end;
  114. Inc(LBufferIndex);
  115. end;
  116. end;
  117. function TrimRightWhiteSpace(const ABuf: TIdBytes): TIdBytes;
  118. var
  119. LSaveBytes: TIdBytes;
  120. li, LLen, LSaveBytesLen: Integer;
  121. begin
  122. SetLength(LSaveBytes, 0);
  123. LLen := Length(ABuf);
  124. for li := Length(ABuf)-1 downto 0 do begin
  125. case ABuf[li] of
  126. 9, 32: ;
  127. 10, 13:
  128. begin
  129. //BGO: TODO: Change this
  130. InsertByte(LSaveBytes, ABuf[li], 0);
  131. end;
  132. else
  133. begin
  134. Break;
  135. end;
  136. end;
  137. Dec(LLen);
  138. end;
  139. LSaveBytesLen := Length(LSaveBytes);
  140. SetLength(Result, LLen + LSaveBytesLen);
  141. if LLen > 0 then begin
  142. CopyTIdBytes(ABuf, 0, Result, 0, LLen);
  143. end;
  144. if LSaveBytesLen > 0 then begin
  145. CopyTIdBytes(LSaveBytes, 0, Result, LLen, LSaveBytesLen);
  146. end;
  147. end;
  148. procedure WriteByte(AValue: Byte; AWriteEOL: Boolean);
  149. var
  150. LTemp: array[0..2] of Byte;
  151. LLen: Integer;
  152. begin
  153. LLen := iif(AWriteEOL, 3, 1);
  154. LTemp[0] := AValue;
  155. if AWriteEOL then begin
  156. LTemp[1] := Ord(CR);
  157. LTemp[2] := Ord(LF);
  158. end;
  159. FStream.WriteBuffer(LTemp, LLen);
  160. end;
  161. begin
  162. LBufferLen := IndyLength(ASrcStream, ABytes);
  163. if LBufferLen <= 0 then begin
  164. Exit;
  165. end;
  166. SetLength(LBuffer, LBufferLen);
  167. ASrcStream.ReadBuffer(LBuffer[0], LBufferLen);
  168. { when decoding a Quoted-Printable body, any trailing
  169. white space on a line must be deleted, - RFC 1521}
  170. LBuffer := TrimRightWhiteSpace(LBuffer);
  171. LBufferLen := Length(LBuffer);
  172. LBufferIndex := 0;
  173. while LBufferIndex < LBufferLen do begin
  174. LPos := ByteIndex(Ord('='), LBuffer, LBufferIndex);
  175. if LPos = -1 then begin
  176. if Assigned(FStream) then begin
  177. FStream.WriteBuffer(LBuffer[LBufferIndex], Length(LBuffer)-LBufferIndex);
  178. end;
  179. Break;
  180. end;
  181. if Assigned(FStream) then begin
  182. FStream.WriteBuffer(LBuffer[LBufferIndex], LPos-LBufferIndex);
  183. end;
  184. LBufferIndex := LPos+1;
  185. // process any following hexidecimal representation
  186. if LBufferIndex < LBufferLen then begin
  187. i := 0;
  188. DecodedByte := 0;
  189. while LBufferIndex < LBufferLen do begin
  190. B := LBuffer[LBufferIndex];
  191. case B of
  192. 48..57: //0-9 {Do not Localize}
  193. DecodedByte := (DecodedByte shl 4) or (B - 48); {Do not Localize}
  194. 65..70: //A-F {Do not Localize}
  195. DecodedByte := (DecodedByte shl 4) or (B - 65 + 10); {Do not Localize}
  196. 97..102://a-f {Do not Localize}
  197. DecodedByte := (DecodedByte shl 4) or (B - 97 + 10); {Do not Localize}
  198. else
  199. Break;
  200. end;
  201. Inc(i);
  202. Inc(LBufferIndex);
  203. if i > 1 then begin
  204. Break;
  205. end;
  206. end;
  207. if i > 0 then begin
  208. //if =20 + EOL, this is a hard line break after a space
  209. if (DecodedByte = 32) and (LBufferIndex < LBufferLen) and ByteIsInEOL(LBuffer, LBufferIndex) then begin
  210. if Assigned(FStream) then begin
  211. WriteByte(DecodedByte, True);
  212. end;
  213. StripEOLChars;
  214. end else begin
  215. if Assigned(FStream) then begin
  216. WriteByte(DecodedByte, False);
  217. end;
  218. end;
  219. end else begin
  220. //ignore soft line breaks -
  221. StripEOLChars;
  222. end;
  223. end;
  224. end;
  225. end;
  226. { TIdEncoderQuotedPrintable }
  227. function CharToHex(const AChar: Char): String;
  228. begin
  229. Result := '=' + ByteToHex(Ord(AChar)); {do not localize}
  230. end;
  231. procedure TIdEncoderQuotedPrintable.Encode(ASrcStream, ADestStream: TStream; const ABytes: Integer = -1);
  232. const
  233. SafeChars = '!"#$%&''()*+,-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmonpqrstuvwxyz{|}~';
  234. HalfSafeChars = #9' ';
  235. // Rule #2, #3
  236. var
  237. I, CurrentLen: Integer;
  238. LSourceSize: Int64;
  239. S, SourceLine: String;
  240. LEncoding: IIdTextEncoding;
  241. begin
  242. //ie while not eof
  243. LSourceSize := ASrcStream.Size;
  244. if ASrcStream.Position < LSourceSize then begin
  245. LEncoding := IndyTextEncoding_8Bit;
  246. repeat
  247. SourceLine := ReadLnFromStream(ASrcStream, -1, False, LEncoding);
  248. CurrentLen := 0;
  249. for I := 1 to Length(SourceLine) do begin
  250. if not CharIsInSet(SourceLine, I, SafeChars) then
  251. begin
  252. if CharIsInSet(SourceLine, I, HalfSafeChars) and (I < Length(SourceLine)) then begin
  253. S := SourceLine[I];
  254. end else begin
  255. S := CharToHex(SourceLine[I]);
  256. end;
  257. end
  258. else if ((CurrentLen = 0) or (CurrentLen >= 70)) and (SourceLine[I] = '.') then begin {do not localize}
  259. S := CharToHex(SourceLine[I]);
  260. end else begin
  261. S := SourceLine[I];
  262. end;
  263. WriteStringToStream(ADestStream, S);
  264. Inc(CurrentLen, Length(S));
  265. if CurrentLen >= 70 then begin
  266. WriteStringToStream(ADestStream, '='+EOL); {Do not Localize}
  267. CurrentLen := 0;
  268. end;
  269. end;
  270. WriteStringToStream(ADestStream, EOL);
  271. until ASrcStream.Position >= LSourceSize;
  272. end;
  273. end;
  274. end.