system.netencoding.pp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2019 by Michael Van Canneyt, member of the
  4. Free Pascal development team
  5. VCL compatible TNetEncoding unit
  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. {$mode objfpc}
  13. {$H+}
  14. unit System.NetEncoding;
  15. interface
  16. {$IFDEF FPC_DOTTEDUNITS}
  17. uses System.SysUtils, System.Classes, System.Types, System.Hash.Base64;
  18. {$ELSE FPC_DOTTEDUNITS}
  19. uses Sysutils, Classes, Types, Base64;
  20. {$ENDIF FPC_DOTTEDUNITS}
  21. type
  22. // Not used here
  23. EHTTPException = class(Exception);
  24. UnsafeChar = Byte;
  25. TUnsafeChars = set of UnsafeChar;
  26. TURLEncoding = Class;
  27. { TNetEncoding }
  28. TNetEncoding = class
  29. private
  30. type
  31. TStandardEncoding = (
  32. seBase64,
  33. seBase64String,
  34. seBase64URL,
  35. seHTML,
  36. seURL);
  37. Class var
  38. FStdEncodings : Array[TStandardEncoding] of TNetEncoding;
  39. Class Function GetStdEncoding(aIndex : TStandardEncoding) : TNetEncoding; Static;
  40. Class Destructor Destroy;
  41. class function GetURLEncoding: TURLEncoding; static;
  42. protected
  43. // These must be implemented by descendents
  44. Function DoDecode(const aInput: RawByteString): RawByteString; overload; virtual; abstract;
  45. Function DoEncode(const aInput: RawByteString): RawByteString; overload; virtual; abstract;
  46. // These can be overridden by descendents for effiency
  47. Function DoDecode(const aInput: UnicodeString): UnicodeString; overload; virtual;
  48. Function DoEncode(const aInput: UnicodeString): UnicodeString; overload; virtual;
  49. Function DoDecode(const aInput, aOutput: TStream): Integer; overload; virtual;
  50. Function DoEncode(const aInput, aOutput: TStream): Integer; overload; virtual;
  51. Function DoDecode(const aInput: array of Byte): TBytes; overload; virtual;
  52. Function DoEncode(const aInput: array of Byte): TBytes; overload; virtual;
  53. Function DoDecodeStringToBytes(const aInput: RawByteString): TBytes; virtual; overload;
  54. Function DoDecodeStringToBytes(const aInput: UnicodeString): TBytes; virtual; overload;
  55. Function DoEncodeBytesToString(const aInput: array of Byte): UnicodeString; overload; virtual;
  56. Function DoEncodeBytesToString(const aInput: Pointer; Size: Integer): UnicodeString; overload; virtual;
  57. public
  58. Class Procedure FreeStdEncodings;
  59. // Public stubs, they call the Do* versions
  60. // Stream
  61. Function Decode(const aInput, aOutput: TStream): Integer; overload;
  62. Function Encode(const aInput, aOutput: TStream): Integer; overload;
  63. // TBytes
  64. Function Decode(const aInput: array of Byte): TBytes; overload;
  65. Function Encode(const aInput: array of Byte): TBytes; overload;
  66. // Strings
  67. Function Decode(const aInput: UnicodeString): UnicodeString; overload;
  68. Function Encode(const aInput: UnicodeString): UnicodeString; overload;
  69. Function Decode(const aInput: RawByteString): RawByteString; overload;
  70. Function Encode(const aInput: RawByteString): RawByteString; overload;
  71. // UnicodeString to Bytes
  72. Function DecodeStringToBytes(const aInput: UnicodeString): TBytes;
  73. Function DecodeStringToBytes(const aInput: RawByteString): TBytes;
  74. Function EncodeBytesToString(const aInput: array of Byte): UnicodeString; overload;
  75. Function EncodeBytesToString(const aInput: Pointer; Size: Integer): UnicodeString; overload;
  76. // Default instances
  77. class property Base64: TNetEncoding Index seBase64 read GetStdEncoding;
  78. class property Base64URL: TNetEncoding Index seBase64URL read GetStdEncoding;
  79. class property Base64String: TNetEncoding Index seBase64String read GetStdEncoding;
  80. class property HTML: TNetEncoding Index seHTML read GetStdEncoding;
  81. class property URL: TURLEncoding read GetURLEncoding;
  82. end;
  83. { TCustomBase64Encoding }
  84. TCustomBase64Encoding = class(TNetEncoding)
  85. protected const
  86. kCharsPerLine = 76;
  87. kLineSeparator = #13#10;
  88. protected
  89. FCharsPerline: Integer;
  90. FLineSeparator: UnicodeString;
  91. FPadEnd: Boolean;
  92. function CreateDecoder(const aInput: TStream) : TBase64DecodingStream; virtual;
  93. function CreateEncoder(const aOutput: TStream) : TBase64EncodingStream; virtual;
  94. protected
  95. Function DoDecode(const aInput, aOutput: TStream): Integer; overload; override;
  96. Function DoEncode(const aInput, aOutput: TStream): Integer; overload; override;
  97. Function DoDecode(const aInput: RawByteString): RawByteString; overload; override;
  98. Function DoEncode(const aInput: RawByteString): RawByteString; overload; override;
  99. Function DoDecode(const aInput: array of Byte): TBytes; overload; override;
  100. Function DoEncode(const aInput: array of Byte): TBytes; overload; override;
  101. end;
  102. { TBase64Encoding }
  103. TBase64Encoding = class(TCustomBase64Encoding)
  104. public
  105. constructor Create; overload; virtual;
  106. constructor Create(CharsPerLine: Integer); overload; virtual;
  107. constructor Create(CharsPerLine: Integer; LineSeparator: UnicodeString); overload; virtual;
  108. constructor Create(CharsPerLine: Integer; LineSeparator: RawByteString); overload;
  109. end;
  110. { TBase64URLEncoding }
  111. TBase64URLEncoding = class(TBase64Encoding)
  112. function CreateDecoder(const aInput: TStream) : TBase64DecodingStream; override;
  113. function CreateEncoder(const aOutput: TStream) : TBase64EncodingStream; override;
  114. end;
  115. { TBase64StringEncoding }
  116. TBase64StringEncoding = class(TCustomBase64Encoding)
  117. public
  118. constructor Create; overload; virtual;
  119. end;
  120. { TURLEncoding }
  121. TURLEncoding = class(TNetEncoding)
  122. protected
  123. Function DoEncode(const aInput: RawBytestring): RawBytestring; overload; override;
  124. Function DoDecode(const aInput: RawBytestring): RawBytestring; overload; override;
  125. Public
  126. Type
  127. UnsafeChar = Byte;
  128. TUnsafeChars = set of UnsafeChar;
  129. TEncodeOption = (SpacesAsPlus, EncodePercent);
  130. TEncodeOptions = set of TEncodeOption;
  131. TDecodeOption = (PlusAsSpaces);
  132. TDecodeOptions = set of TDecodeOption;
  133. Public
  134. function Encode(const aInput: string; const aSet: TUnsafeChars; const aOptions: TEncodeOptions; aEncoding: TEncoding = nil): string; overload;
  135. function EncodeQuery(const aInput: string; const aExtraUnsafeChars: TUnsafeChars): string;
  136. function EncodePath(const aPath: string; const aExtraUnsafeChars: TUnsafeChars): string;
  137. class function URIDecode(const aValue: string; aPlusAsSpaces: Boolean): string;
  138. end;
  139. THTMLEncoding = class(TNetEncoding)
  140. protected
  141. Function DoDecode(const aInput: UnicodeString): UnicodeString; override;
  142. Function DoDecode(const aInput: RawBytestring): RawBytestring; overload; override;
  143. Function DoEncode(const aInput: UnicodeString): UnicodeString; override;
  144. Function DoEncode(const aInput: RawBytestring): RawBytestring; overload; override;
  145. end;
  146. implementation
  147. {$IFDEF FPC_DOTTEDUNITS}
  148. uses FpWeb.Http.Protocol, Html.Defs, Xml.Read;
  149. {$ELSE FPC_DOTTEDUNITS}
  150. uses httpprotocol, HTMLDefs, xmlread;
  151. {$ENDIF FPC_DOTTEDUNITS}
  152. Resourcestring
  153. sInvalidHTMLEntity = 'Invalid HTML encoded character: %s';
  154. { TCustomBase64Encoding }
  155. function TCustomBase64Encoding.CreateDecoder(const aInput: TStream) : TBase64DecodingStream;
  156. begin
  157. Result:=TBase64DecodingStream.Create(aInput,bdmMIME);
  158. end;
  159. function TCustomBase64Encoding.CreateEncoder(const aOutput: TStream) : TBase64EncodingStream;
  160. begin
  161. Result:=TBase64EncodingStream.Create(aOutput,FCharsPerline,FLineSeparator,FPadEnd);
  162. end;
  163. function TCustomBase64Encoding.DoDecode(const aInput, aOutput: TStream): Integer;
  164. Var
  165. S : TBase64DecodingStream;
  166. begin
  167. S:=CreateDecoder(aInput);
  168. try
  169. Result:=S.Size;
  170. aOutput.CopyFrom(S,Result);
  171. finally
  172. S.Free;
  173. end;
  174. end;
  175. function TCustomBase64Encoding.DoDecode(const aInput: array of Byte): TBytes;
  176. var
  177. Instream : TBytesStream;
  178. Outstream : TBytesStream;
  179. Decoder : TBase64DecodingStream;
  180. const
  181. cPad: AnsiChar = '=';
  182. begin
  183. if Length(aInput)=0 then
  184. Exit(nil);
  185. Instream:=TBytesStream.Create;
  186. try
  187. Instream.WriteBuffer(aInput[0], Length(aInput));
  188. while Instream.Size mod 4 > 0 do
  189. Instream.WriteBuffer(cPad, 1);
  190. Instream.Position:=0;
  191. Outstream:=TBytesStream.Create;
  192. try
  193. Decoder:=CreateDecoder(Instream);
  194. try
  195. Outstream.CopyFrom(Decoder,Decoder.Size);
  196. Result:=Outstream.Bytes;
  197. SetLength(Result,Outstream.Size);
  198. finally
  199. Decoder.Free;
  200. end;
  201. finally
  202. Outstream.Free;
  203. end;
  204. finally
  205. Instream.Free;
  206. end;
  207. end;
  208. function TCustomBase64Encoding.DoEncode(const aInput, aOutput: TStream): Integer;
  209. Var
  210. S : TBase64EncodingStream;
  211. begin
  212. S:=CreateEncoder(aOutput); //,FCharsPerline,FLineSeparator,FPadEnd);
  213. try
  214. Result:=S.CopyFrom(aInput,0);
  215. finally
  216. S.Free;
  217. end;
  218. end;
  219. function TCustomBase64Encoding.DoEncode(const aInput: array of Byte): TBytes;
  220. var
  221. Outstream : TBytesStream;
  222. Encoder : TBase64EncodingStream;
  223. begin
  224. if Length(aInput)=0 then
  225. Exit(nil);
  226. Outstream:=TBytesStream.Create;
  227. try
  228. Encoder:=CreateEncoder(outstream);
  229. try
  230. Encoder.Write(aInput[0],Length(aInput));
  231. finally
  232. Encoder.Free;
  233. end;
  234. Result:=Outstream.Bytes;
  235. SetLength(Result,Outstream.Size);
  236. finally
  237. Outstream.free;
  238. end;
  239. end;
  240. function TCustomBase64Encoding.DoDecode(const aInput: RawByteString): RawByteString;
  241. begin
  242. Result:=DecodeStringBase64(aInput,False);
  243. end;
  244. function TCustomBase64Encoding.DoEncode(const aInput: RawByteString): RawByteString;
  245. var
  246. Outstream : TStringStream;
  247. Encoder : TBase64EncodingStream;
  248. begin
  249. if Length(aInput)=0 then
  250. Exit('');
  251. Outstream:=TStringStream.Create('');
  252. try
  253. Encoder:=CreateEncoder(outstream);
  254. try
  255. Encoder.Write(aInput[1],Length(aInput));
  256. finally
  257. Encoder.Free;
  258. end;
  259. Result:=Outstream.DataString;
  260. finally
  261. Outstream.free;
  262. end;
  263. end;
  264. { TBase64Encoding }
  265. constructor TBase64Encoding.Create(CharsPerLine: Integer);
  266. begin
  267. Create(CharsPerLine, kLineSeparator);
  268. end;
  269. constructor TBase64Encoding.Create(CharsPerLine: Integer; LineSeparator: UnicodeString);
  270. begin
  271. inherited Create;
  272. FCharsPerline:=CharsPerLine;
  273. FLineSeparator:=LineSeparator;
  274. FPadEnd:=True;
  275. end;
  276. constructor TBase64Encoding.Create(CharsPerLine: Integer; LineSeparator: RawByteString);
  277. begin
  278. Create(CharsPerLine, UTF8Decode(LineSeparator));
  279. end;
  280. constructor TBase64Encoding.Create;
  281. begin
  282. Create(kCharsPerLine, kLineSeparator);
  283. end;
  284. { TBase64URLEncoding }
  285. function TBase64URLEncoding.CreateDecoder(const aInput: TStream): TBase64DecodingStream;
  286. begin
  287. Result:=TBase64URLDecodingStream.Create(aInput,bdmMIME);
  288. end;
  289. function TBase64URLEncoding.CreateEncoder(const aOutput: TStream): TBase64EncodingStream;
  290. begin
  291. Result:=TBase64URLEncodingStream.Create(aOutput,FCharsPerline,FLineSeparator,FPadEnd);
  292. end;
  293. { TBase64StringEncoding }
  294. constructor TBase64StringEncoding.Create;
  295. begin
  296. inherited Create;
  297. FCharsPerline:=0;
  298. FLineSeparator:='';
  299. FPadEnd:=True;
  300. end;
  301. { ---------------------------------------------------------------------
  302. TNetEncoding
  303. ---------------------------------------------------------------------}
  304. class procedure TNetEncoding.FreeStdEncodings;
  305. Var
  306. I : TStandardEncoding;
  307. begin
  308. For I in TStandardEncoding do
  309. FreeAndNil(FStdEncodings[i]);
  310. end;
  311. class destructor TNetEncoding.Destroy;
  312. begin
  313. FreeStdEncodings;
  314. end;
  315. class function TNetEncoding.GetURLEncoding: TURLEncoding;
  316. begin
  317. Result:=TURLEncoding(GetStdEncoding(seURL));
  318. end;
  319. class function TNetEncoding.GetStdEncoding(aIndex: TStandardEncoding): TNetEncoding;
  320. begin
  321. Result:=FStdEncodings[aIndex];
  322. if Assigned(Result) then
  323. begin
  324. {$ifdef FPC_HAS_FEATURE_THREADING}
  325. ReadDependencyBarrier; // Read Result contents (by caller) after Result pointer.
  326. {$endif}
  327. Exit;
  328. end;
  329. case aIndex of
  330. seBase64: Result:=TBase64Encoding.Create;
  331. seBase64String: Result:=TBase64StringEncoding.Create;
  332. seBase64URL: Result:=TBase64URLEncoding.Create;
  333. seHTML: Result:=THTMLEncoding.Create;
  334. seURL: Result:=TURLEncoding.Create;
  335. end;
  336. {$ifdef FPC_HAS_FEATURE_THREADING}
  337. WriteBarrier; // Write FStdEncodings[aIndex] after Result contents.
  338. if InterlockedCompareExchange(Pointer(FStdEncodings[aIndex]), Pointer(Result), nil) <> nil then
  339. begin
  340. Result.Free;
  341. Result := FStdEncodings[aIndex];
  342. end;
  343. {$else}
  344. FStdEncodings[aIndex] := Result;
  345. {$endif}
  346. end;
  347. // Public API
  348. function TNetEncoding.Encode(const aInput: array of Byte): TBytes;
  349. begin
  350. Result:=DoEncode(aInput);
  351. end;
  352. function TNetEncoding.Encode(const aInput, aOutput: TStream): Integer;
  353. begin
  354. Result:=DoEncode(aInput, aOutput);
  355. end;
  356. function TNetEncoding.Decode(const aInput: RawByteString): RawByteString;
  357. begin
  358. Result:=DoDecode(aInput);
  359. end;
  360. function TNetEncoding.Encode(const aInput: RawByteString): RawByteString;
  361. begin
  362. Result:=DoEncode(aInput);
  363. end;
  364. function TNetEncoding.Encode(const aInput: UnicodeString): UnicodeString;
  365. begin
  366. Result:=DoEncode(aInput);
  367. end;
  368. function TNetEncoding.EncodeBytesToString(const aInput: array of Byte): UnicodeString;
  369. begin
  370. Result:=DoEncodeBytesToString(aInput);
  371. end;
  372. function TNetEncoding.EncodeBytesToString(const aInput: Pointer; Size: Integer): UnicodeString;
  373. begin
  374. Result:=DoEncodeBytesToString(aInput, Size);
  375. end;
  376. function TNetEncoding.Decode(const aInput, aOutput: TStream): Integer;
  377. begin
  378. Result:=DoDecode(aInput,aOutput);
  379. end;
  380. function TNetEncoding.Decode(const aInput: UnicodeString): UnicodeString;
  381. begin
  382. Result:=DoDecode(aInput);
  383. end;
  384. function TNetEncoding.DecodeStringToBytes(const aInput: UnicodeString): TBytes;
  385. begin
  386. Result:=DoDecodeStringToBytes(aInput);
  387. end;
  388. function TNetEncoding.DecodeStringToBytes(const aInput: RawByteString): TBytes;
  389. begin
  390. Result:=DoDecodeStringToBytes(aInput);
  391. end;
  392. function TNetEncoding.Decode(const aInput: array of Byte): TBytes;
  393. begin
  394. Result:=DoDecode(aInput);
  395. end;
  396. // Protected
  397. function TNetEncoding.DoDecode(const aInput: UnicodeString): UnicodeString;
  398. Var
  399. U : UTF8String;
  400. begin
  401. U:=UTF8Encode(aInput);
  402. Result:=UTF8Decode(DoDecode(U));
  403. end;
  404. function TNetEncoding.DoEncode(const aInput: UnicodeString): UnicodeString;
  405. Var
  406. U : UTF8String;
  407. begin
  408. U:=UTF8Encode(aInput);
  409. Result:=UTF8Decode(DoEncode(U));
  410. end;
  411. function TNetEncoding.DoDecode(const aInput: array of Byte): TBytes;
  412. begin
  413. if Length(aInput)=0 then
  414. Result:=Default(TBytes)
  415. else
  416. Result:=TEncoding.UTF8.GetBytes(DoDecode(UTF8ToString(aInput)));
  417. end;
  418. function TNetEncoding.DoDecode(const aInput, aOutput: TStream): Integer;
  419. var
  420. Src,Dest: TBytes;
  421. Len : Integer;
  422. begin
  423. Result:=0;
  424. Len:=aInput.Size;
  425. if Len<>0 then
  426. begin
  427. Src:=Default(TBytes);
  428. SetLength(Src,Len);
  429. aInput.ReadBuffer(Src,Len);
  430. Dest:=DoDecode(Src);
  431. Result:=Length(Dest);
  432. aOutput.WriteBuffer(Dest,Result);
  433. end
  434. end;
  435. function TNetEncoding.DoDecodeStringToBytes(const aInput: UnicodeString): TBytes;
  436. begin
  437. Result:=TEncoding.UTF8.GetBytes(DoDecode(aInput));
  438. end;
  439. function TNetEncoding.DoEncode(const aInput: array of Byte): TBytes;
  440. begin
  441. if Length(aInput)=0 then
  442. Result:=Default(TBytes)
  443. else
  444. Result:=TEncoding.UTF8.GetBytes(DoEncode(UTF8ToString(aInput)))
  445. end;
  446. function TNetEncoding.DoDecodeStringToBytes(const aInput: RawByteString): TBytes;
  447. Var
  448. U : RawByteString;
  449. begin
  450. U:=AInput;
  451. UniqueString(U);
  452. SetCodePage(U,CP_UTF8,True);
  453. Result:=DoDecodeStringToBytes(UTF8Decode(U));
  454. end;
  455. function TNetEncoding.DoEncodeBytesToString(const aInput: array of Byte): UnicodeString;
  456. begin
  457. Result:=TEncoding.UTF8.GetString(DoEncode(aInput));
  458. end;
  459. function TNetEncoding.DoEncodeBytesToString(const aInput: Pointer; Size: Integer): UnicodeString;
  460. Var
  461. Src : TBytes;
  462. begin
  463. Src:=Default(TBytes);
  464. SetLength(Src,Size);
  465. Move(aInput^,Src[0],Size);
  466. Result:=DoEncodeBytesToString(Src);
  467. end;
  468. function TNetEncoding.DoEncode(const aInput, aOutput: TStream): Integer;
  469. var
  470. InBuf: array of Byte;
  471. OutBuf: TBytes;
  472. begin
  473. if aInput.Size > 0 then
  474. begin
  475. SetLength(InBuf, aInput.Size);
  476. aInput.Read(InBuf[0], aInput.Size);
  477. OutBuf:=DoEncode(InBuf);
  478. Result:=Length(OutBuf);
  479. aOutput.Write(OutBuf, Result);
  480. SetLength(InBuf, 0);
  481. end
  482. else
  483. Result:=0;
  484. end;
  485. { TBase64Encoding }
  486. { TURLEncoding }
  487. function TURLEncoding.DoDecode(const aInput: RawBytestring): RawBytestring;
  488. begin
  489. Result:=HTTPDecode(aInput);
  490. end;
  491. function TURLEncoding.Encode(const aInput: string; const aSet: TUnsafeChars; const aOptions: TEncodeOptions; aEncoding: TEncoding): string;
  492. var
  493. S : TUnsafeChars;
  494. begin
  495. S:=aSet;
  496. if (TEncodeOption.EncodePercent in aOptions) then
  497. S:=aSet+[Ord('%')];
  498. Result:=HttpEncode(aInput,S,TEncodeOption.SpacesAsPlus in aOptions);
  499. end;
  500. function TURLEncoding.DoEncode(const aInput: RawBytestring): RawBytestring;
  501. begin
  502. Result:=HTTPEncode(aInput)
  503. end;
  504. function TURLEncoding.EncodeQuery(const aInput: string; const aExtraUnsafeChars: TUnsafeChars): string;
  505. const
  506. QueryUnsafeChars: TUnsafeChars = [Ord('''')+Ord('%')];
  507. var
  508. Unsafe: TUnsafeChars;
  509. begin
  510. Unsafe:=QueryUnsafeChars+aExtraUnsafeChars;
  511. Result:=HTTPEncode(aInput,Unsafe,True);
  512. end;
  513. function TURLEncoding.EncodePath(const aPath: string; const aExtraUnsafeChars: TUnsafeChars): string;
  514. var
  515. lPaths: TStringDynArray;
  516. I,Last: Integer;
  517. LUnsafeChars: TUnsafeChars;
  518. begin
  519. if APath = '' then
  520. Exit('/');
  521. Result:='';
  522. lPaths:=APath.Split(['/'], TStringSplitOptions.ExcludeEmpty);
  523. Last:=Length(lPaths)-1;
  524. for I:=0 to Last do
  525. Result:=Result+'/'+HTTPEncode(LPaths[I],aExtraUnsafeChars,True);
  526. end;
  527. class function TURLEncoding.URIDecode(const aValue: string; aPlusAsSpaces: Boolean): string;
  528. begin
  529. Result:=HTTPDecode(aValue,aPlusAsSpaces);
  530. end;
  531. { THTMLEncoding }
  532. Function THTMLEncoding.DoEncode(const aInput: UnicodeString): UnicodeString;
  533. Var
  534. S : UTF8String;
  535. begin
  536. S:=UTF8Encode(aInput);
  537. Result:=UTF8Decode(DoEncode(S));
  538. end;
  539. Function THTMLEncoding.DoEncode(const aInput: RawByteString): RawByteString;
  540. var
  541. Src, Curr, OrigDest,Dest : PAnsiChar;
  542. Procedure CopyData(S : String);
  543. Var
  544. len : integer;
  545. begin
  546. Len:=(Curr-Src);
  547. if Len>0 then
  548. Move(Src^,Dest^,Len);
  549. Src:=Curr;
  550. Inc(Src);
  551. inc(Dest,Len);
  552. Len:=Length(S);
  553. if Len>0 then
  554. Move(S[1],Dest^,Len);
  555. inc(Dest,Len);
  556. end;
  557. begin
  558. SetLength(Result,Length(aInput)*6);
  559. if Length(aInput)=0 then exit;
  560. Src:=PAnsiChar(aInput);
  561. Curr:=Src;
  562. OrigDest:=PAnsiChar(Result);
  563. Dest:=OrigDest;
  564. // Convert: &, <, >, "
  565. while Curr^<>#0 do
  566. begin
  567. case Curr^ of
  568. '&': CopyData('&amp;');
  569. '<': CopyData('&lt;');
  570. '>': CopyData('&gt;');
  571. '"': CopyData('&quot;');
  572. end;
  573. Inc(Curr);
  574. end;
  575. CopyData('');
  576. SetLength(Result,Dest-OrigDest);
  577. end;
  578. Function THTMLEncoding.DoDecode(const aInput: RawByteString): RawByteString;
  579. Var
  580. S : RawByteString;
  581. begin
  582. S:=aInput;
  583. UniqueString(S);
  584. SetCodePage(S,CP_UTF8,true);
  585. Result:=UTF8Encode(DoDecode(UTF8Decode(S)));
  586. end;
  587. Function THTMLEncoding.DoDecode(const aInput: UnicodeString): UnicodeString;
  588. var
  589. Src, Curr, Dest : PWideChar;
  590. Procedure CopyData(S : UnicodeString);
  591. Var
  592. len : integer;
  593. begin
  594. Len:=(Curr-Src);
  595. if Len>0 then
  596. begin
  597. Move(Src^,Dest^,Len*Sizeof(UnicodeChar));
  598. inc(Dest,Len);
  599. end;
  600. Len:=Length(S);
  601. if Len>0 then
  602. begin
  603. Move(S[1],Dest^,Len*Sizeof(UnicodeChar));
  604. inc(Dest,Len);
  605. end;
  606. end;
  607. Var
  608. Len : Integer;
  609. U : UnicodeChar;
  610. US : Unicodestring;
  611. Ent,OrigDest : PWideChar;
  612. begin
  613. SetLength(Result, Length(aInput));
  614. if Length(Result)=0 then exit;
  615. Src:=PWideChar(aInput);
  616. OrigDest:=PWideChar(Result);
  617. Dest:=OrigDest;
  618. Curr:=Src;
  619. while Curr^ <> #0 do
  620. begin
  621. If Curr^='&' then
  622. begin
  623. CopyData('');
  624. Src:=Curr;
  625. Ent:=Curr;
  626. While Not (Ent^ in [#0,';']) do
  627. Inc(Ent);
  628. Len:=Ent-Curr-1;
  629. SetLength(US,Len);
  630. if Len>0 then
  631. Move(Curr[1],US[1],Len*SizeOf(UnicodeChar));
  632. if not ResolveHTMLEntityReference(US,U) then
  633. raise EConvertError.CreateFmt(sInvalidHTMLEntity,[US]);
  634. CopyData(U);
  635. Curr:=Ent;
  636. Src:=Curr;
  637. Inc(Src);
  638. end;
  639. Inc(Curr);
  640. end;
  641. CopyData('');
  642. SetLength(Result,Dest-OrigDest);
  643. end;
  644. end.