IdCoderQuotedPrintable.pas 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  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. IdStream,
  83. SysUtils;
  84. type
  85. TIdDecoderQuotedPrintable = class(TIdDecoder)
  86. public
  87. procedure Decode(ASrcStream: TStream; const ABytes: Integer = -1); override;
  88. end;
  89. TIdEncoderQuotedPrintable = class(TIdEncoder)
  90. public
  91. procedure Encode(ASrcStream, ADestStream: TStream; const ABytes: Integer = -1); override;
  92. end;
  93. implementation
  94. uses
  95. IdException,
  96. IdGlobal,
  97. IdGlobalProtocols;
  98. { TIdDecoderQuotedPrintable }
  99. procedure TIdDecoderQuotedPrintable.Decode(ASrcStream: TStream; const ABytes: Integer = -1);
  100. var
  101. LBuffer: TIdBytes;
  102. i : Integer;
  103. B, DecodedByte : Byte;
  104. LBufferLen: Integer;
  105. LBufferIndex: Integer;
  106. LPos: integer;
  107. procedure StripEOLChars;
  108. var
  109. j: Integer;
  110. begin
  111. for j := 1 to 2 do begin
  112. if (LBufferIndex >= LBufferLen) or (not ByteIsInEOL(LBuffer, LBufferIndex)) then begin
  113. Break;
  114. end;
  115. Inc(LBufferIndex);
  116. end;
  117. end;
  118. function TrimRightWhiteSpace(const ABuf: TIdBytes): TIdBytes;
  119. var
  120. LSaveBytes: TIdBytes;
  121. li, LLen: Integer;
  122. begin
  123. SetLength(LSaveBytes, 0);
  124. LLen := Length(ABuf);
  125. for li := Length(ABuf)-1 downto 0 do begin
  126. case ABuf[li] of
  127. 9, 32: ;
  128. 10, 13:
  129. begin
  130. //BGO: TODO: Change this
  131. InsertByte(LSaveBytes, ABuf[li], 0);
  132. end;
  133. else
  134. begin
  135. Break;
  136. end;
  137. end;
  138. Dec(LLen);
  139. end;
  140. SetLength(Result, LLen + Length(LSaveBytes));
  141. if LLen > 0 then begin
  142. CopyTIdBytes(ABuf, 0, Result, 0, LLen);
  143. end;
  144. if Length(LSaveBytes) > 0 then begin
  145. CopyTIdBytes(LSaveBytes, 0, Result, LLen, Length(LSaveBytes));
  146. end;
  147. end;
  148. procedure WriteByte(AValue: Byte; AWriteEOL: Boolean);
  149. var
  150. LTemp: TIdBytes;
  151. begin
  152. SetLength(LTemp, iif(AWriteEOL, 3, 1));
  153. LTemp[0] := AValue;
  154. if AWriteEOL then begin
  155. LTemp[1] := Ord(CR);
  156. LTemp[2] := Ord(LF);
  157. end;
  158. TIdStreamHelper.Write(FStream, LTemp);
  159. end;
  160. begin
  161. LBufferLen := IndyLength(ASrcStream, ABytes);
  162. if LBufferLen <= 0 then begin
  163. Exit;
  164. end;
  165. SetLength(LBuffer, LBufferLen);
  166. TIdStreamHelper.ReadBytes(ASrcStream, LBuffer, LBufferLen);
  167. { when decoding a Quoted-Printable body, any trailing
  168. white space on a line must be deleted, - RFC 1521}
  169. LBuffer := TrimRightWhiteSpace(LBuffer);
  170. LBufferLen := Length(LBuffer);
  171. LBufferIndex := 0;
  172. while LBufferIndex < LBufferLen do begin
  173. LPos := ByteIndex(Ord('='), LBuffer, LBufferIndex);
  174. if LPos = -1 then begin
  175. if Assigned(FStream) then begin
  176. TIdStreamHelper.Write(FStream, LBuffer, -1, LBufferIndex);
  177. end;
  178. Break;
  179. end;
  180. if Assigned(FStream) then begin
  181. TIdStreamHelper.Write(FStream, LBuffer, LPos-LBufferIndex, LBufferIndex);
  182. end;
  183. LBufferIndex := LPos+1;
  184. // process any following hexidecimal representation
  185. if LBufferIndex < LBufferLen then begin
  186. i := 0;
  187. DecodedByte := 0;
  188. while LBufferIndex < LBufferLen do begin
  189. B := LBuffer[LBufferIndex];
  190. case B of
  191. 48..57: //0-9 {Do not Localize}
  192. DecodedByte := (DecodedByte shl 4) or (B - 48); {Do not Localize}
  193. 65..70: //A-F {Do not Localize}
  194. DecodedByte := (DecodedByte shl 4) or (B - 65 + 10); {Do not Localize}
  195. 97..102://a-f {Do not Localize}
  196. DecodedByte := (DecodedByte shl 4) or (B - 97 + 10); {Do not Localize}
  197. else
  198. Break;
  199. end;
  200. Inc(i);
  201. Inc(LBufferIndex);
  202. if i > 1 then begin
  203. Break;
  204. end;
  205. end;
  206. if i > 0 then begin
  207. //if =20 + EOL, this is a hard line break after a space
  208. if (DecodedByte = 32) and (LBufferIndex < LBufferLen) and ByteIsInEOL(LBuffer, LBufferIndex) then begin
  209. if Assigned(FStream) then begin
  210. WriteByte(DecodedByte, True);
  211. end;
  212. StripEOLChars;
  213. end else begin
  214. if Assigned(FStream) then begin
  215. WriteByte(DecodedByte, False);
  216. end;
  217. end;
  218. end else begin
  219. //ignore soft line breaks -
  220. StripEOLChars;
  221. end;
  222. end;
  223. end;
  224. end;
  225. { TIdEncoderQuotedPrintable }
  226. function CharToHex(const AChar: Char): String;
  227. begin
  228. Result := '=' + ByteToHex(Ord(AChar)); {do not localize}
  229. end;
  230. procedure TIdEncoderQuotedPrintable.Encode(ASrcStream, ADestStream: TStream; const ABytes: Integer = -1);
  231. const
  232. SafeChars = '!"#$%&''()*+,-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmonpqrstuvwxyz{|}~';
  233. HalfSafeChars = #9' ';
  234. // Rule #2, #3
  235. var
  236. I, CurrentLen: Integer;
  237. LSourceSize: TIdStreamSize;
  238. S, SourceLine: String;
  239. LEncoding: IIdTextEncoding;
  240. begin
  241. //ie while not eof
  242. LSourceSize := ASrcStream.Size;
  243. if ASrcStream.Position < LSourceSize then begin
  244. LEncoding := IndyTextEncoding_8Bit;
  245. repeat
  246. SourceLine := ReadLnFromStream(ASrcStream, -1, False, LEncoding{$IFDEF STRING_IS_ANSI}, LEncoding{$ENDIF});
  247. CurrentLen := 0;
  248. for I := 1 to Length(SourceLine) do begin
  249. if not CharIsInSet(SourceLine, I, SafeChars) then
  250. begin
  251. if CharIsInSet(SourceLine, I, HalfSafeChars) and (I < Length(SourceLine)) then begin
  252. S := SourceLine[I];
  253. end else begin
  254. S := CharToHex(SourceLine[I]);
  255. end;
  256. end
  257. else if ((CurrentLen = 0) or (CurrentLen >= 70)) and (SourceLine[I] = '.') then begin {do not localize}
  258. S := CharToHex(SourceLine[I]);
  259. end else begin
  260. S := SourceLine[I];
  261. end;
  262. WriteStringToStream(ADestStream, S);
  263. Inc(CurrentLen, Length(S));
  264. if CurrentLen >= 70 then begin
  265. WriteStringToStream(ADestStream, '='+EOL); {Do not Localize}
  266. CurrentLen := 0;
  267. end;
  268. end;
  269. WriteStringToStream(ADestStream, EOL);
  270. until ASrcStream.Position >= LSourceSize;
  271. end;
  272. end;
  273. end.