IdCoderHeader.pas 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. { $HDR$}
  2. {**********************************************************************}
  3. { Unit archived using Team Coherence }
  4. { Team Coherence is Copyright 2002 by Quality Software Components }
  5. { }
  6. { For further information / comments, visit our WEB site at }
  7. { http://www.TeamCoherence.com }
  8. {**********************************************************************}
  9. {}
  10. { $Log: 10093: IdCoderHeader.pas
  11. {
  12. { Rev 1.1 8/15/04 5:23:48 PM RLebeau
  13. { Updated DecodeHeader() to use IdGlobal.PosIdx() now instead of local
  14. { PosStartAt()
  15. }
  16. {
  17. { Rev 1.0 2002.11.12 10:32:34 PM czhower
  18. }
  19. unit IdCoderHeader;
  20. //TODO: Optimize and restructure code
  21. //TODO: Redo this unit to fit with the new coders and use the exisiting MIME stuff
  22. {
  23. 2001-Nov-18 Peter Mee
  24. - Fixed multiple QP decoding in single header.
  25. 11-10-2001 - J. Peter Mugaas
  26. - tiny fix for 8bit header encoding suggested by Andrew P.Rybin}
  27. interface
  28. uses
  29. IdEMailAddress;
  30. type
  31. TTransfer = (bit7, bit8, iso2022jp);
  32. CSET = set of Char;
  33. // Procs
  34. function EncodeAddressItem(EmailAddr:TIdEmailAddressItem; const HeaderEncoding: Char;
  35. TransferHeader: TTransfer; MimeCharSet: string): string;
  36. function EncodeHeader(const Header: string; specials : CSET; const HeaderEncoding: Char;
  37. TransferHeader: TTransfer; MimeCharSet: string): string;
  38. function Encode2022JP(const S: string): string;
  39. function EncodeAddress(EmailAddr:TIdEMailAddressList; const HeaderEncoding: Char;
  40. TransferHeader: TTransfer; MimeCharSet: string): string;
  41. function DecodeHeader(Header: string):string;
  42. function Decode2022JP(const S: string): string;
  43. Procedure DecodeAddress(EMailAddr : TIdEmailAddressItem);
  44. Procedure DecodeAddresses(AEMails : String; EMailAddr : TIdEmailAddressList);
  45. procedure InitializeISO(var TransferHeader: TTransfer; var HeaderEncoding: char;
  46. var CharSet: string);
  47. implementation
  48. uses
  49. IdGlobal,
  50. SysUtils;
  51. const
  52. csSPECIALS: CSET = ['(', ')', '[', ']', '<', '>', ':', ';', '.', ',', '@', '\', '"']; {Do not Localize}
  53. kana_tbl : array[#$A1..#$DF] of Word = (
  54. $2123,$2156,$2157,$2122,$2126,$2572,$2521,$2523,$2525,$2527,
  55. $2529,$2563,$2565,$2567,$2543,$213C,$2522,$2524,$2526,$2528,
  56. $252A,$252B,$252D,$252F,$2531,$2533,$2535,$2537,$2539,$253B,
  57. $253D,$253F,$2541,$2544,$2546,$2548,$254A,$254B,$254C,$254D,
  58. $254E,$254F,$2552,$2555,$2558,$255B,$255E,$255F,$2560,$2561,
  59. $2562,$2564,$2566,$2568,$2569,$256A,$256B,$256C,$256D,$256F,
  60. $2573,$212B,$212C);
  61. vkana_tbl : array[#$A1..#$DF] of Word = (
  62. $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,
  63. $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$2574,$0000,
  64. $0000,$252C,$252E,$2530,$2532,$2534,$2536,$2538,$253A,$253C,
  65. $253E,$2540,$2542,$2545,$2547,$2549,$0000,$0000,$0000,$0000,
  66. $0000,$2550,$2553,$2556,$2559,$255C,$0000,$0000,$0000,$0000,
  67. $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,
  68. $0000,$0000,$0000);
  69. sj1_tbl : array[#128..#255] of Byte = (
  70. $00,$21,$23,$25,$27,$29,$2B,$2D,$2F,$31,$33,$35,$37,$39,$3B,$3D,
  71. $3F,$41,$43,$45,$47,$49,$4B,$4D,$4F,$51,$53,$55,$57,$59,$5B,$5D,
  72. $00,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,
  73. $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,
  74. $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,
  75. $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,
  76. $5F,$61,$63,$65,$67,$69,$6B,$6D,$6F,$71,$73,$75,$77,$79,$7B,$7D,
  77. $02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$00,$00,$00);
  78. sj2_tbl : array[Char] of Word = (
  79. $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,
  80. $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,
  81. $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,
  82. $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,
  83. $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,
  84. $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,
  85. $0000,$0000,$0000,$0000,$0021,$0022,$0023,$0024,$0025,$0026,
  86. $0027,$0028,$0029,$002A,$002B,$002C,$002D,$002E,$002F,$0030,
  87. $0031,$0032,$0033,$0034,$0035,$0036,$0037,$0038,$0039,$003A,
  88. $003B,$003C,$003D,$003E,$003F,$0040,$0041,$0042,$0043,$0044,
  89. $0045,$0046,$0047,$0048,$0049,$004A,$004B,$004C,$004D,$004E,
  90. $004F,$0050,$0051,$0052,$0053,$0054,$0055,$0056,$0057,$0058,
  91. $0059,$005A,$005B,$005C,$005D,$005E,$005F,$0000,$0060,$0061,
  92. $0062,$0063,$0064,$0065,$0066,$0067,$0068,$0069,$006A,$006B,
  93. $006C,$006D,$006E,$006F,$0070,$0071,$0072,$0073,$0074,$0075,
  94. $0076,$0077,$0078,$0079,$007A,$007B,$007C,$007D,$007E,$0121,
  95. $0122,$0123,$0124,$0125,$0126,$0127,$0128,$0129,$012A,$012B,
  96. $012C,$012D,$012E,$012F,$0130,$0131,$0132,$0133,$0134,$0135,
  97. $0136,$0137,$0138,$0139,$013A,$013B,$013C,$013D,$013E,$013F,
  98. $0140,$0141,$0142,$0143,$0144,$0145,$0146,$0147,$0148,$0149,
  99. $014A,$014B,$014C,$014D,$014E,$014F,$0150,$0151,$0152,$0153,
  100. $0154,$0155,$0156,$0157,$0158,$0159,$015A,$015B,$015C,$015D,
  101. $015E,$015F,$0160,$0161,$0162,$0163,$0164,$0165,$0166,$0167,
  102. $0168,$0169,$016A,$016B,$016C,$016D,$016E,$016F,$0170,$0171,
  103. $0172,$0173,$0174,$0175,$0176,$0177,$0178,$0179,$017A,$017B,
  104. $017C,$017D,$017E,$0000,$0000,$0000);
  105. base64_tbl: array [0..63] of Char = (
  106. 'A','B','C','D','E','F','G','H', {Do not Localize}
  107. 'I','J','K','L','M','N','O','P', {Do not Localize}
  108. 'Q','R','S','T','U','V','W','X', {Do not Localize}
  109. 'Y','Z','a','b','c','d','e','f', {Do not Localize}
  110. 'g','h','i','j','k','l','m','n', {Do not Localize}
  111. 'o','p','q','r','s','t','u','v', {Do not Localize}
  112. 'w','x','y','z','0','1','2','3', {Do not Localize}
  113. '4','5','6','7','8','9','+','/'); {Do not Localize}
  114. function EncodeAddressItem(EmailAddr:TIdEmailAddressItem; const HeaderEncoding: Char;
  115. TransferHeader: TTransfer; MimeCharSet: string): string;
  116. var
  117. S : string;
  118. I : Integer;
  119. NeedEncode : Boolean;
  120. begin
  121. if EmailAddr.Name <> '' then {Do not Localize}
  122. begin
  123. NeedEncode := False;
  124. for I := 1 to Length(EmailAddr.Name) do
  125. begin
  126. if (EmailAddr.Name[I] < #32) or (EmailAddr.Name[I] >= #127) then
  127. begin
  128. NeedEncode := True;
  129. Break;
  130. end;
  131. end;
  132. if NeedEncode then
  133. S := EncodeHeader(EmailAddr.Name, csSPECIALS, HeaderEncoding, TransferHeader, MimeCharSet)
  134. else
  135. begin { quoted string }
  136. S := '"'; {Do not Localize}
  137. for I := 1 to Length(EmailAddr.Name) do
  138. begin { quote special characters }
  139. if (EmailAddr.Name[I] = '\') or (EmailAddr.Name[I] = '"') then S := S + '\'; {Do not Localize}
  140. S := S + EmailAddr.Name[I];
  141. end;
  142. S := S + '"'; {Do not Localize}
  143. end;
  144. Result := Format('%s <%s>', [S, EmailAddr.Address]) {Do not Localize}
  145. end
  146. else Result := Format('%s', [EmailAddr.Address]); {Do not Localize}
  147. end;
  148. function B64(AChar: Char): Byte;
  149. //TODO: Make this use the more efficient MIME Coder
  150. var
  151. i: Integer;
  152. begin
  153. for i := Low(base64_tbl) to High(base64_tbl) do begin
  154. if AChar = base64_tbl[i] then begin
  155. Result := i;
  156. exit;
  157. end;
  158. end;
  159. Result := 0;
  160. end;
  161. function DecodeHeader(Header: string):string;
  162. function FindEncodingStart(const AStr: String; AStartPos: Cardinal): Cardinal;
  163. begin
  164. Result := PosIdx('=?', AStr, AStartPos);
  165. end;
  166. var
  167. i, l: Integer;
  168. HeaderEncoding,
  169. HeaderCharSet,
  170. LHeader,
  171. s: string;
  172. a3: array [1..3] of byte;
  173. a4: array [1..4] of byte;
  174. encodingstartpos,encodingendpos: Cardinal;
  175. substring: string;
  176. EncodingFound : Boolean;
  177. begin
  178. LHeader := UpperCase(Header);
  179. // Get the Charset part.
  180. encodingstartpos := FindEncodingStart(LHeader, 1);
  181. while encodingstartpos > 0 do
  182. begin
  183. // Assume we will find the encoding
  184. EncodingFound := True;
  185. //we need 3 more question marks first and after that a '?=' {Do not Localize}
  186. //to find the end of the substring, we can't just search for '?=', {Do not Localize}
  187. //example: '=?ISO-8859-1?Q?=E4?=' {Do not Localize}
  188. encodingendpos := PosIdx('?', LHeader, encodingstartpos+2); {Do not Localize}
  189. if encodingendpos = 0 then begin
  190. EncodingFound := False;
  191. end;
  192. if EncodingFound then
  193. begin
  194. encodingendpos := PosIdx('?', LHeader, encodingendpos+1); {Do not Localize}
  195. if encodingendpos = 0 then begin
  196. EncodingFound := False;
  197. end;
  198. end;
  199. if EncodingFound then
  200. begin
  201. encodingendpos := PosIdx('?=', LHeader, encodingendpos+1); {Do not Localize}
  202. if encodingendpos > 0 then begin
  203. substring := Copy(Header, encodingstartpos, encodingendpos-encodingstartpos+2);
  204. //now decode the substring
  205. for i := 1 to 3 do
  206. begin
  207. l := Pos('?', substring); {Do not Localize}
  208. substring := Copy(substring, l+1, Length(substring) - l + 1 );
  209. if i = 1 then begin
  210. HeaderCharSet := Copy(substring, 1, Pos('?', substring)-1) {Do not Localize}
  211. end else if i = 2 then begin
  212. HeaderEncoding := Copy(substring, 1, 1);
  213. end;
  214. end;
  215. //now Substring needs to end with '?=' otherwise give up! {Do not Localize}
  216. if Copy(substring, Length(substring)-1, 2) <> '?=' then begin {Do not Localize}
  217. EncodingFound := False;
  218. end;
  219. if EncodingFound then begin
  220. // Get the HeaderEncoding
  221. if AnsiSameText(HeaderEncoding, 'Q') then {Do not Localize}
  222. begin
  223. i := 1;
  224. s := ''; {Do not Localize}
  225. repeat // substring can be accessed by index here, because we know that it ends with '?=' {Do not Localize}
  226. if substring[i] = '_' then begin {Do not Localize}
  227. s := s + ' '; {Do not Localize}
  228. end else if (substring[i] = '=') and (Length(substring) >= (i+2+2)) then begin //make sure we can access i+2 and '?=' is still beyond {Do not Localize}
  229. s := s + chr(StrToInt('$' + substring[i+1] + substring[i+2])); {Do not Localize}
  230. inc(i, 2);
  231. end else begin
  232. s := s + substring[i];
  233. end;
  234. Inc(i);
  235. until (substring[i] = '?') and (substring[i+1] = '=') {Do not Localize}
  236. end
  237. else if AnsiSameText(HeaderEncoding, 'B') then begin
  238. while Length(substring) >= 4 do begin
  239. a4[1] := b64(substring[1]);
  240. a4[2] := b64(substring[2]);
  241. a4[3] := b64(substring[3]);
  242. a4[4] := b64(substring[4]);
  243. a3[1] := Byte((a4[1] shl 2) or (a4[2] shr 4));
  244. a3[2] := Byte((a4[2] shl 4) or (a4[3] shr 2));
  245. a3[3] := Byte((a4[3] shl 6) or (a4[4] shr 0));
  246. substring := Copy(substring, 5, Length(substring));
  247. s := s + CHR(a3[1]) + CHR(a3[2]) + CHR(a3[3]);
  248. end;
  249. end else
  250. begin
  251. EncodingFound := False;
  252. end;
  253. end;
  254. if EncodingFound then
  255. begin
  256. if AnsiSameText(HeaderCharSet, 'ISO-2022-JP') then begin {Do not Localize}
  257. substring := Decode2022JP(s);
  258. end
  259. {$IFDEF VCL6ORABOVE}
  260. else if AnsiSameText(HeaderCharSet, 'UTF-8') then begin {Do not Localize}
  261. substring := UTF8Decode(s);
  262. end
  263. {$ENDIF}
  264. else
  265. begin
  266. substring := s;
  267. end;
  268. //replace old substring in header with decoded one:
  269. Header := Copy(Header, 1, encodingstartpos - 1)
  270. + substring + Copy(Header, encodingendpos + 2, MaxInt);
  271. substring := ''; {Do not Localize}
  272. LHeader := UpperCase(Header);
  273. end;
  274. end;
  275. end;
  276. encodingstartpos := FindEncodingStart(LHeader, encodingstartpos+1);
  277. end;
  278. //there might be #0's in header when this it b64 encoded, e.g with: {Do not Localize}
  279. //decodeheader('"Fernando Corti=?ISO-8859-1?B?8Q==?=a" <[email protected]>'); {Do not Localize}
  280. while Pos(#0, Header) > 0 do begin
  281. Delete(Header, Pos(#0, Header), 1);
  282. end;
  283. Result := Header;
  284. end;
  285. {
  286. function DecodeHeader(Header: string):string;
  287. var
  288. i, l: Integer;
  289. HeaderEncoding,
  290. HeaderCharSet,
  291. s: string;
  292. a3: array [1..3] of byte;
  293. a4: array [1..4] of byte;
  294. begin
  295. // Get the Charset part.
  296. if Pos('=?ISO', UpperCase(Header)) > 0 then
  297. begin
  298. for i := 1 to 3 do begin
  299. l := Pos('?', Header);
  300. Header := Copy(Header, l+1, Length(Header) - l + 1 );
  301. if i = 1 then HeaderCharSet := Copy(Header, 1, Pos('?', Header)-1)
  302. else if i = 2 then HeaderEncoding := Header[1];
  303. end;
  304. // Get the HeaderEncoding
  305. if AnsiSameText(HeaderEncoding, 'Q') then begin
  306. i := 1;
  307. repeat
  308. if Header[i] = '_' then
  309. s := s + ' '
  310. else if Header[i] = '=' then begin
  311. s := s + chr(StrToInt('$' + Header[i+1] + Header[i+2]));
  312. inc(i,2);
  313. end else
  314. s := s + Header[i];
  315. inc(i);
  316. until (Header[i]='?') and (Header[i+1]='=')
  317. end
  318. else begin
  319. while Length(Header) >= 4 do begin
  320. a4[1] := b64(Header[1]);
  321. a4[2] := b64(Header[2]);
  322. a4[3] := b64(Header[3]);
  323. a4[4] := b64(Header[4]);
  324. a3[1] := (a4[1] shl 2) or (a4[2] shr 4);
  325. a3[2] := (a4[2] shl 4) or (a4[3] shr 2);
  326. a3[3] := (a4[3] shl 6) or (a4[4] shr 0);
  327. Header := Copy(Header, 5, Length(Header));
  328. s := s + CHR(a3[1]) + CHR(a3[2]) + CHR(a3[3]);
  329. end;
  330. end;
  331. if AnsiSameText(HeaderCharSet, 'ISO-2022-JP') then
  332. result := Decode2022JP(s)
  333. else
  334. Result := s;
  335. end
  336. else
  337. Result := Header;
  338. end;
  339. }
  340. { convert Shift_JIS to ISO-2022-JP (RFC 1468) }
  341. function Decode2022JP(const S: string): string;
  342. var
  343. T : string;
  344. I, L : integer;
  345. isK : Boolean;
  346. K1, K2 : byte;
  347. K3 : byte;
  348. begin
  349. T := ''; {Do not Localize}
  350. isK := False;
  351. L := length(S);
  352. I := 1;
  353. while I <= L do
  354. begin
  355. if S[I] = #27 then
  356. begin
  357. Inc(I);
  358. if I+1 <= L then
  359. begin
  360. if Copy(S, I, 2) = '$B' then {Do not Localize}
  361. begin
  362. isK := True;
  363. end
  364. else
  365. begin
  366. if Copy(S, I, 2) = '(B' then {Do not Localize}
  367. begin
  368. isK := False;
  369. end;
  370. end;
  371. Inc(I, 2); { TODO -oTArisawa : Check RFC 1468}
  372. end;
  373. end
  374. else
  375. begin
  376. if isK then
  377. begin
  378. if I+1 <= L then
  379. begin
  380. K1 := byte(S[I]);
  381. K2 := byte(S[I + 1]);
  382. K3:= (K1 - 1) shr 1;
  383. if K1 < 95 then
  384. K3:= K3 + 113
  385. else
  386. K3 := K3 + 177;
  387. if (K1 mod 2) = 1 then
  388. begin
  389. if K2 < 96 Then
  390. K2 := K2 + 31
  391. else
  392. K2 := K2 + 32
  393. end
  394. else
  395. K2 := K2 + 126;
  396. T := T + char(K3) + char(k2);
  397. Inc(I,2);
  398. end
  399. else
  400. Inc(I); { invalid DBCS }
  401. end
  402. else
  403. begin
  404. T := T + S[I];
  405. Inc(I);
  406. end;
  407. end;
  408. end;
  409. Result := T;
  410. end;
  411. procedure InitializeISO(var TransferHeader: TTransfer; var HeaderEncoding: char;
  412. var CharSet: string);
  413. begin
  414. TransferHeader := bit8; { header part conversion type }
  415. HeaderEncoding := 'B'; { base64 / quoted-printable } {Do not Localize}
  416. case GetSystemLocale of
  417. csGB2312: CharSet := 'GB2312'; {Do not Localize}
  418. csBig5: CharSet := 'Big5'; {Do not Localize}
  419. csIso2022jp:
  420. begin
  421. CharSet := 'ISO-2022-JP'; {Do not Localize}
  422. TransferHeader := iso2022jp { header needs conversion }
  423. end;
  424. csEUCKR: CharSet := 'EUC-KR'; {Do not Localize}
  425. else
  426. CharSet := 'ISO-8859-1'; {Do not Localize}
  427. HeaderEncoding := 'Q'; {Do not Localize}
  428. end;
  429. end;
  430. Procedure DecodeAddress(EMailAddr : TIdEmailAddressItem);
  431. begin
  432. EMailAddr.Name := DecodeHeader(EMailAddr.Name);
  433. end;
  434. Procedure DecodeAddresses(AEMails : String; EMailAddr : TIdEmailAddressList);
  435. var idx : Integer;
  436. begin
  437. idx := 0;
  438. EMailAddr.EMailAddresses := AEMails;
  439. while idx < EMailAddr.Count do
  440. begin
  441. DecodeAddress(EMailAddr[idx]);
  442. inc(idx);
  443. end;
  444. end;
  445. function EncodeAddress(EmailAddr:TIdEMailAddressList; const HeaderEncoding: Char;
  446. TransferHeader: TTransfer; MimeCharSet: string): string;
  447. var idx : Integer;
  448. begin
  449. Result := ''; {Do not Localize}
  450. idx := 0;
  451. while ( idx < EmailAddr.Count ) do
  452. begin
  453. Result := Result + ', ' + EncodeAddressItem(EMailAddr[idx], HeaderEncoding, TransferHeader, MimeCharSet); {Do not Localize}
  454. Inc ( idx );
  455. end; // while ( idx < EncodeAddress.Count ) do
  456. {Remove the first comma and the following space ', ' } {Do not Localize}
  457. System.Delete ( Result, 1, 2 );
  458. end;
  459. { convert Shift_JIS to ISO-2022-JP (RFC 1468) }
  460. function Encode2022JP(const S: string): string;
  461. const
  462. desig_asc = #27'(B'; {Do not Localize}
  463. desig_jis = #27'$B'; {Do not Localize}
  464. var
  465. T: string;
  466. I, L: Integer;
  467. isK: Boolean;
  468. K1: Byte;
  469. K2, K3: Word;
  470. begin
  471. T := ''; {Do not Localize}
  472. isK := False;
  473. L := Length(S);
  474. I := 1;
  475. while I <= L do
  476. begin
  477. if S[I] < #128 then {Do not Localize}
  478. begin
  479. if isK then
  480. begin
  481. T := T + desig_asc;
  482. isK := False;
  483. end;
  484. T := T + S[I];
  485. INC(I);
  486. end else begin
  487. K1 := sj1_tbl[S[I]];
  488. case K1 of
  489. 0: INC(I); { invalid SBCS }
  490. 2: INC(I, 2); { invalid DBCS }
  491. 1:
  492. begin { halfwidth katakana }
  493. if not isK then begin
  494. T := T + desig_jis;
  495. isK := True;
  496. end;
  497. { simple SBCS -> DBCS conversion }
  498. K2 := kana_tbl[S[I]];
  499. if (I < L) and (Ord(S[I+1]) AND $FE = $DE) then
  500. begin { convert kana + voiced mark to voiced kana }
  501. K3 := vkana_tbl[S[I]];
  502. case S[I+1] of
  503. #$DE: { voiced }
  504. if K3 <> 0 then
  505. begin
  506. K2 := K3;
  507. INC(I);
  508. end;
  509. #$DF: { semivoiced }
  510. if (K3 >= $2550) and (K3 <= $255C) then
  511. begin
  512. K2 := K3 + 1;
  513. INC(I);
  514. end;
  515. end;
  516. end;
  517. T := T + Chr(K2 SHR 8) + Chr(K2 AND $FF);
  518. INC(I);
  519. end;
  520. else { DBCS }
  521. if (I < L) then begin
  522. K2 := sj2_tbl[S[I + 1]];
  523. if K2 <> 0 then
  524. begin
  525. if not isK then begin
  526. T := T + desig_jis;
  527. isK := True;
  528. end;
  529. T := T + Chr(K1 + K2 SHR 8) + Chr(K2 AND $FF);
  530. end;
  531. end;
  532. INC(I, 2);
  533. end;
  534. end;
  535. end;
  536. if isK then
  537. T := T + desig_asc;
  538. Result := T;
  539. end;
  540. { encode a header field if non-ASCII characters are used }
  541. function EncodeHeader(const Header: string; specials : CSET; const HeaderEncoding: Char;
  542. TransferHeader: TTransfer; MimeCharSet: string): string;
  543. const
  544. SPACES: set of Char = [' ', #9, #10, #13]; {Do not Localize}
  545. var
  546. S, T: string;
  547. L, P, Q, R: Integer;
  548. B0, B1, B2: Integer;
  549. InEncode: Integer;
  550. NeedEncode: Boolean;
  551. csNeedEncode, csReqQuote: CSET;
  552. BeginEncode, EndEncode: string;
  553. procedure EncodeWord(P: Integer);
  554. const
  555. MaxEncLen = 75;
  556. var
  557. Q: Integer;
  558. EncLen: Integer;
  559. Enc1: string;
  560. begin
  561. T := T + BeginEncode;
  562. if L < P then P := L + 1;
  563. Q := InEncode;
  564. InEncode := 0;
  565. EncLen := Length(BeginEncode) + 2;
  566. if AnsiSameText(HeaderEncoding, 'Q') then { quoted-printable } {Do not Localize}
  567. begin
  568. while Q < P do
  569. begin
  570. if not (S[Q] in csReqQuote) then
  571. begin
  572. Enc1 := S[Q]
  573. end
  574. else
  575. begin
  576. if S[Q] = ' ' then {Do not Localize}
  577. Enc1 := '_' {Do not Localize}
  578. else
  579. Enc1 := '=' + IntToHex(Ord(S[Q]), 2); {Do not Localize}
  580. end;
  581. if EncLen + Length(Enc1) > MaxEncLen then
  582. begin
  583. T := T + EndEncode + #13#10#9 + BeginEncode;
  584. EncLen := Length(BeginEncode) + 2;
  585. end;
  586. T := T + Enc1;
  587. INC(EncLen, Length(Enc1));
  588. INC(Q);
  589. end;
  590. end
  591. else
  592. begin { base64 }
  593. while Q < P do
  594. begin
  595. if EncLen + 4 > MaxEncLen then
  596. begin
  597. T := T + EndEncode + #13#10#9 + BeginEncode;
  598. EncLen := Length(BeginEncode) + 2;
  599. end;
  600. B0 := Ord(S[Q]);
  601. case P - Q of
  602. 1: T := T + base64_tbl[B0 SHR 2] + base64_tbl[B0 AND $03 SHL 4] + '=='; {Do not Localize}
  603. 2:
  604. begin
  605. B1 := Ord(S[Q + 1]);
  606. T := T + base64_tbl[B0 SHR 2] +
  607. base64_tbl[B0 AND $03 SHL 4 + B1 SHR 4] +
  608. base64_tbl[B1 AND $0F SHL 2] + '='; {Do not Localize}
  609. end;
  610. else
  611. B1 := Ord(S[Q + 1]);
  612. B2 := Ord(S[Q + 2]);
  613. T := T + base64_tbl[B0 SHR 2] +
  614. base64_tbl[B0 AND $03 SHL 4 + B1 SHR 4] +
  615. base64_tbl[B1 AND $0F SHL 2 + B2 SHR 6] +
  616. base64_tbl[B2 AND $3F];
  617. end;
  618. INC(EncLen, 4);
  619. INC(Q, 3);
  620. end;
  621. end;
  622. T := T + EndEncode;
  623. end;
  624. begin
  625. case TransferHeader of
  626. iso2022jp:
  627. S := Encode2022JP(Header);
  628. else
  629. S := Header;
  630. end;
  631. {Suggested by Andrew P.Rybin for easy 8bit support}
  632. if HeaderEncoding='8' then begin //UpCase('8')='8' {Do not Localize}
  633. Result:=S;
  634. EXIT;
  635. end;//if
  636. csNeedEncode := [#0..#31, #127..#255] + specials;
  637. csReqQuote := csNeedEncode + ['?', '=', '_']; {Do not Localize}
  638. BeginEncode := '=?' + MimeCharSet + '?' + HeaderEncoding + '?'; {Do not Localize}
  639. EndEncode := '?='; {Do not Localize}
  640. L := Length(S);
  641. P := 1;
  642. T := ''; {Do not Localize}
  643. InEncode := 0;
  644. while P <= L do
  645. begin
  646. Q := P;
  647. while (P <= L) and (S[P] in SPACES) do
  648. INC(P);
  649. R := P;
  650. NeedEncode := False;
  651. while (P <= L) and not (S[P] in SPACES) do
  652. begin
  653. if S[P] in csNeedEncode then
  654. begin
  655. NeedEncode := True;
  656. end;
  657. INC(P);
  658. end;
  659. if NeedEncode then
  660. begin
  661. if InEncode = 0 then
  662. begin
  663. T := T + Copy(S, Q, R - Q);
  664. InEncode := R;
  665. end;
  666. end
  667. else
  668. begin
  669. if InEncode <> 0 then
  670. begin
  671. EncodeWord(Q);
  672. end;
  673. T := T + Copy(S, Q, P - Q);
  674. end;
  675. end;
  676. if InEncode <> 0 then
  677. begin
  678. EncodeWord(P);
  679. end;
  680. Result := T;
  681. end;
  682. end.