BrookMediaTypes.pas 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899
  1. (* _ _
  2. * | |__ _ __ ___ ___ | | __
  3. * | '_ \| '__/ _ \ / _ \| |/ /
  4. * | |_) | | | (_) | (_) | <
  5. * |_.__/|_| \___/ \___/|_|\_\
  6. *
  7. * Microframework which helps to develop web Pascal applications.
  8. *
  9. * Copyright (c) 2012-2021 Silvio Clecio <[email protected]>
  10. *
  11. * Brook framework is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU Lesser General Public
  13. * License as published by the Free Software Foundation; either
  14. * version 2.1 of the License, or (at your option) any later version.
  15. *
  16. * Brook framework is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. * Lesser General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Lesser General Public
  22. * License along with Brook framework; if not, write to the Free Software
  23. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  24. *)
  25. { Contains classes for media types parsing. }
  26. unit BrookMediaTypes;
  27. {$I BrookDefines.inc}
  28. interface
  29. uses
  30. RTLConsts,
  31. SysUtils,
  32. Classes,
  33. libsagui,
  34. BrookExtra,
  35. BrookReader,
  36. BrookHandledClasses,
  37. BrookStringMap;
  38. const
  39. { Default MIME types file name. }
  40. BROOK_MIME_FILE = 'mime.types';
  41. { Register prefix for MIME types class. }
  42. BROOK_MIME_TAG = 'BrookMIME_';
  43. { Default MIME provider. }
  44. BROOK_MIME_PROVIDER =
  45. {$IF DEFINED(MSWINDOWS)}
  46. 'Windows'
  47. {$ELSEIF DEFINED(UNIX) OR DEFINED(POSIX)}
  48. 'Unix'
  49. {$ELSE}
  50. 'Path'
  51. {$ENDIF};
  52. resourcestring
  53. { Error message @code('Invalid media type: <media-type>.'). }
  54. SBrookInvalidMediaType = 'Invalid media type: %s.';
  55. { Error message @code('Invalid media extension: <ext>.'). }
  56. SBrookInvalidMediaExt = 'Invalid media extension: %s.';
  57. { Error message @code('Empty media type'). }
  58. SBrookEmptyMediaType = 'Empty media type.';
  59. { Error message @code('Empty media extension'). }
  60. SBrookEmptyMediaExt = 'Empty media extension.';
  61. { Error message @code('Active MIME types'). }
  62. SBrookActiveMIMETypes = 'Active MIME types.';
  63. { Error message @code('Inactive MIME types'). }
  64. SBrookInactiveMIMETypes = 'Inactive MIME types.';
  65. { Error message @code('Empty MIME provider'). }
  66. SBrookEmptyMIMEProvider = 'Empty MIME provider.';
  67. { Error message @code('Invalid MIME provider class: <class-name>.'). }
  68. SBrookInvalidMIMEProviderClass = 'Invalid MIME provider class: %s.';
  69. { Error message @code('Unknown MIME provider: <unknown-mime>.'). }
  70. SBrookUnknownMIMEProvider = 'Unknown MIME provider: %s.';
  71. type
  72. { Handles exceptions related to media type classes. }
  73. EBrookMediaTypes = class(Exception);
  74. { Cached abstract class to register, add, remove, find a media type. }
  75. TBrookMediaTypes = class abstract(TBrookHandledPersistent)
  76. private
  77. FCache: TBrookStringMap;
  78. FDefaultType: string;
  79. FHandle: Psg_strmap;
  80. procedure SetDefaultType(const AValue: string);
  81. protected
  82. function CreateCache: TBrookStringMap; virtual;
  83. function IsPrepared: Boolean; virtual; abstract;
  84. function GetHandle: Pointer; override;
  85. procedure CheckExt(const AExt: string); {$IFNDEF DEBUG}inline;{$ENDIF}
  86. procedure CheckType(const AType: string); {$IFNDEF DEBUG}inline;{$ENDIF}
  87. procedure CheckPrepared; {$IFNDEF DEBUG}inline;{$ENDIF}
  88. property Cache: TBrookStringMap read FCache;
  89. public
  90. { Creates an instance of @code(TBrookMediaTypes). }
  91. constructor Create; virtual;
  92. { Destroys an instance of @code(TBrookMediaTypes). }
  93. destructor Destroy; override;
  94. { Returns the alias name for media type source.
  95. @returns(Media type source alias.) }
  96. class function GetRegisterAlias: string; virtual;
  97. { Returns the description of the media types source.
  98. @returns(Description of the media types source.) }
  99. class function GetDescription: string; virtual; abstract;
  100. { Returns @True if a string represents a media type,
  101. e.g @code('text/plain').
  102. @param(AType[in] Media type identifier.)
  103. @returns(@True if a string represents a media type.) }
  104. class function IsValid(const AType: string): Boolean; static;
  105. {$IFNDEF DEBUG}inline;{$ENDIF}
  106. { Returns @True if a string represents a text media type,
  107. e.g. @code('text/plain').
  108. @param(AType[in] Media type identifier.)
  109. @returns(@True if a string represents a text media type.) }
  110. class function IsText(const AType: string): Boolean; static;
  111. {$IFNDEF DEBUG}inline;{$ENDIF}
  112. { Returns @True if a string represents a file extension,
  113. e.g. @code('text/plain').
  114. @param(AExt[in] File extension.)
  115. @returns(@True if a string represents a file extension.) }
  116. class function IsExt(const AExt: string): Boolean; static;
  117. {$IFNDEF DEBUG}inline;{$ENDIF}
  118. { Normalizes file extension by adding a dot, e.g. a @code('pas') will be
  119. normalized to @code('.pas').
  120. @param(AExt[in] File extension.)
  121. @returns(Normalized file extension.) }
  122. class function NormalizeExt(const AExt: string): string; static;
  123. {$IFNDEF DEBUG}inline;{$ENDIF}
  124. { Prepares the media types source. }
  125. procedure Prepare; virtual; abstract;
  126. { Adds a new media type to the cache.
  127. @param(AExt[in] File extension.)
  128. @param(AType[in] Media type identifier.) }
  129. procedure Add(const AExt, AType: string); virtual;
  130. { Removes a media type from the cache.
  131. @param(AExt[in] File extension.) }
  132. procedure Remove(const AExt: string); virtual;
  133. { If the cache is not prepared yet, this method prepares it automatically
  134. and tries to find a media type identifier by file extension.
  135. @param(AExt[in] File extension.)
  136. @param(AType[in] Media type identifier.)
  137. @returns(@True if the media type identifier is found.) }
  138. function TryType(const AExt: string; out AType: string): Boolean; virtual;
  139. { Finds a media type identifier by file extension. If the cache is not
  140. prepared yet, this method prepares it automatically. If a media type
  141. identifier is not found, the @code(ADefType) is returned instead.
  142. @param(AExt[in] File extension.)
  143. @param(ADefType[in] Default media type identifier.)
  144. @returns(Media type identifier.) }
  145. function Find(const AExt, ADefType: string): string; overload; virtual;
  146. { Finds a media type identifier by file extension. If the cache is not
  147. prepared yet, this method prepares it automatically. If a media type
  148. identifier is not found, the @code(DefaultType) is returned instead.
  149. @param(AExt[in] File extension.)
  150. @returns(Media type identifier.) }
  151. function Find(const AExt: string): string; overload; virtual;
  152. { Counts all media type identifiers present in the cache.
  153. @return(All media type identifiers present in the cache.) }
  154. function Count: Integer; virtual;
  155. { Clears all media type identifiers present in the cache. }
  156. procedure Clear; virtual;
  157. { Default media type identifier returned by @code(Find). }
  158. property DefaultType: string read FDefaultType write SetDefaultType;
  159. { @True if the media types cache is prepared. }
  160. property Prepared: Boolean read IsPrepared;
  161. end;
  162. { Class-reference for @code(TBrookMediaTypes). }
  163. TBrookMediaTypesClass = class of TBrookMediaTypes;
  164. { Base class containing a basic media types parser. }
  165. TBrookMediaTypesParser = class(TPersistent)
  166. private
  167. FReader: TBrookTextReader;
  168. FTypes: TBrookMediaTypes;
  169. public
  170. { Creates an instance of @code(TBrookMediaTypesParser). }
  171. constructor Create(AReader: TBrookTextReader;
  172. ATypes: TBrookMediaTypes); virtual;
  173. { Parses a media types source passed by @code(Reader). }
  174. procedure Parse; virtual;
  175. { Line reader containing a media types source. }
  176. property Reader: TBrookTextReader read FReader;
  177. { Cached list containing all parsed media types. }
  178. property Types: TBrookMediaTypes read FTypes;
  179. end;
  180. { Media types parser for
  181. @html(<a href="https://github.com/nginx/nginx/blob/master/conf/mime.types">Nginx mime.types</a>). }
  182. TBrookMediaTypesParserNginx = class(TBrookMediaTypesParser)
  183. public
  184. { Parses a Nginx media types source. }
  185. procedure Parse; override;
  186. end;
  187. { Media types provider from the @code(mime.types) file. }
  188. TBrookMediaTypesPath = class(TBrookMediaTypes)
  189. private
  190. FParser: TBrookMediaTypesParser;
  191. FReader: TBrookTextReader;
  192. FFileName: string;
  193. FPrepared: Boolean;
  194. protected
  195. function CreateReader: TBrookTextReader; virtual;
  196. function CreateParser: TBrookMediaTypesParser; virtual;
  197. function IsPrepared: Boolean; override;
  198. public
  199. { Creates an instance of @code(TBrookMediaTypesPath).
  200. @param(AFileName[in] Media types file.) }
  201. constructor Create(const AFileName: string); reintroduce; overload; virtual;
  202. { Creates an instance of @code(TBrookMediaTypesPath). }
  203. constructor Create; overload; override;
  204. { Destroys an instance of @code(TBrookMediaTypesPath). }
  205. destructor Destroy; override;
  206. { Returns the description of the media types source.
  207. @returns(Description of the media types source.) }
  208. class function GetDescription: string; override;
  209. { Returns the file name of the media types source.
  210. @returns(File name of the media types source.) }
  211. class function GetFileName: TFileName; virtual;
  212. { Prepares the media types source. }
  213. procedure Prepare; override;
  214. { Clears the media types source. }
  215. procedure Clear; override;
  216. { Line reader containing a media types source. }
  217. property Reader: TBrookTextReader read FReader;
  218. { Media types parser containing a media types source. }
  219. property Parser: TBrookMediaTypesParser read FParser;
  220. { File name of the media types source. }
  221. property FileName: string read FFileName;
  222. end;
  223. { Class-reference for @code(TBrookMediaTypesPath). }
  224. TBrookMediaTypesPathClass = class of TBrookMediaTypesPath;
  225. { Media types provider from the
  226. @html(<a href="https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types">Apache mime.types</a>). }
  227. TBrookMediaTypesApache = class(TBrookMediaTypesPath)
  228. public
  229. { Returns the description of the media types source.
  230. @returns(Description of the media types source.) }
  231. class function GetDescription: string; override;
  232. end;
  233. { Media types provider from the
  234. @html(<a href="https://github.com/nginx/nginx/blob/master/conf/mime.types">Nginx mime.types</a>). }
  235. TBrookMediaTypesNginx = class(TBrookMediaTypesPath)
  236. protected
  237. function CreateParser: TBrookMediaTypesParser; override;
  238. public
  239. { Returns the description of the media types source.
  240. @returns(Description of the media types source.) }
  241. class function GetDescription: string; override;
  242. { Returns the file name of the media types source.
  243. @returns(File name of the media types source.) }
  244. class function GetFileName: TFileName; override;
  245. end;
  246. { Media types provider from the Windows registry. }
  247. TBrookMediaTypesWindows = class(TBrookMediaTypesPath)
  248. public
  249. { Returns the description of the media types source.
  250. @returns(Description of the media types source.) }
  251. class function GetDescription: string; override;
  252. end;
  253. { Media types provider from the @code(/etc/mime.types). }
  254. TBrookMediaTypesUnix = class(TBrookMediaTypesPath)
  255. public
  256. { Returns the description of the media types source.
  257. @returns(Description of the media types source.) }
  258. class function GetDescription: string; override;
  259. { Returns the file name of the media types source.
  260. @returns(File name of the media types source.) }
  261. class function GetFileName: TFileName; override;
  262. end;
  263. { Provides all registered media types in any supported platform. }
  264. TBrookMIME = class(TBrookHandledComponent)
  265. private
  266. FDefaultType: string;
  267. FFileName: TFileName;
  268. FTypes: TBrookMediaTypes;
  269. FActive: Boolean;
  270. FStreamedActive: Boolean;
  271. FProvider: string;
  272. function GetTypes: TBrookMediaTypes;
  273. function IsActiveStored: Boolean;
  274. function IsDefaultTypeStored: Boolean;
  275. function IsFileNameStored: Boolean;
  276. function IsProviderStored: Boolean;
  277. procedure SetActive(AValue: Boolean);
  278. procedure SetDefaultType(const AValue: string);
  279. procedure SetFileName(const AValue: TFileName);
  280. procedure SetProvider(const AValue: string);
  281. procedure InternalLibUnloadEvent(ASender: TObject);
  282. protected
  283. procedure Loaded; override;
  284. function CreateTypes(const AFileName: TFileName): TBrookMediaTypes; virtual;
  285. function GetHandle: Pointer; override;
  286. procedure DoOpen; virtual;
  287. procedure DoClose; virtual;
  288. procedure CheckProvider; {$IFNDEF DEBUG}inline;{$ENDIF}
  289. procedure CheckActive; {$IFNDEF DEBUG}inline;{$ENDIF}
  290. procedure CheckInactive; {$IFNDEF DEBUG}inline;{$ENDIF}
  291. public
  292. { Creates an instance of @code(TBrookMIME).
  293. @param(AOwner[in] Owner component.) }
  294. constructor Create(AOwner: TComponent); override;
  295. { Destroys an instance of @code(TBrookMIME). }
  296. destructor Destroy; override;
  297. { Gets a media type class from the classes register. }
  298. function GetProviderClass: TBrookMediaTypesClass;
  299. {$IFNDEF DEBUG}inline;{$ENDIF}
  300. { Opens the media types provider. }
  301. procedure Open;
  302. { Closes the media types provider. }
  303. procedure Close;
  304. { Cached list containing the parsed media types. }
  305. property Types: TBrookMediaTypes read GetTypes;
  306. published
  307. { Activates the cached media types provider. }
  308. property Active: Boolean read FActive write SetActive stored IsActiveStored;
  309. { Default media type identifier returned by @code(TBrookMediaTypes.Find). }
  310. property DefaultType: string read FDefaultType write SetDefaultType
  311. stored IsDefaultTypeStored;
  312. { File name of the media types source. }
  313. property FileName: TFileName read FFileName write SetFileName
  314. stored IsFileNameStored;
  315. { Media types provider description. }
  316. property Provider: string read FProvider write SetProvider
  317. stored IsProviderStored;
  318. end;
  319. implementation
  320. var
  321. GBrookMIMEFileName: TFileName;
  322. { TBrookMediaTypes }
  323. constructor TBrookMediaTypes.Create;
  324. begin
  325. inherited Create;
  326. FCache := CreateCache;
  327. SetDefaultType(BROOK_CT_OCTET_STREAM);
  328. end;
  329. destructor TBrookMediaTypes.Destroy;
  330. begin
  331. FCache.Free;
  332. inherited Destroy;
  333. end;
  334. function TBrookMediaTypes.CreateCache: TBrookStringMap;
  335. begin
  336. Result := TBrookStringMap.Create(@FHandle);
  337. end;
  338. class function TBrookMediaTypes.GetRegisterAlias: string;
  339. begin
  340. Result := Concat(BROOK_MIME_TAG, GetDescription);
  341. end;
  342. class function TBrookMediaTypes.IsValid(const AType: string): Boolean;
  343. begin
  344. Result := AType.Length > 3;
  345. end;
  346. class function TBrookMediaTypes.IsText(const AType: string): Boolean;
  347. begin
  348. Result := AType.StartsWith('text/') and IsValid(AType);
  349. end;
  350. class function TBrookMediaTypes.IsExt(const AExt: string): Boolean;
  351. begin
  352. Result := (Length(AExt) > 0) and (AExt <> '.') and (AExt <> '..');
  353. end;
  354. class function TBrookMediaTypes.NormalizeExt(const AExt: string): string;
  355. begin
  356. Result := AExt;
  357. if (Length(AExt) > 0) and (AExt[1] <> '.') then
  358. Result := Concat('.', Result);
  359. end;
  360. procedure TBrookMediaTypes.CheckExt(const AExt: string);
  361. begin
  362. if AExt.IsEmpty then
  363. raise EArgumentException.Create(SBrookEmptyMediaExt);
  364. if not IsExt(AExt) then
  365. raise EBrookMediaTypes.CreateFmt(SBrookInvalidMediaExt, [AExt]);
  366. end;
  367. procedure TBrookMediaTypes.CheckPrepared;
  368. begin
  369. if not IsPrepared then
  370. Prepare;
  371. end;
  372. procedure TBrookMediaTypes.CheckType(const AType: string);
  373. begin
  374. if AType.IsEmpty then
  375. raise EArgumentException.Create(SBrookEmptyMediaType);
  376. if not IsValid(AType) then
  377. raise EBrookMediaTypes.CreateResFmt(@SBrookInvalidMediaType, [AType]);
  378. end;
  379. function TBrookMediaTypes.GetHandle: Pointer;
  380. begin
  381. Result := FHandle;
  382. end;
  383. procedure TBrookMediaTypes.SetDefaultType(const AValue: string);
  384. begin
  385. if FDefaultType = AValue then
  386. Exit;
  387. CheckType(AValue);
  388. FDefaultType := AValue;
  389. end;
  390. procedure TBrookMediaTypes.Add(const AExt, AType: string);
  391. begin
  392. CheckExt(AExt);
  393. CheckType(AType);
  394. FCache.AddOrSet(NormalizeExt(AExt), AType);
  395. end;
  396. procedure TBrookMediaTypes.Remove(const AExt: string);
  397. begin
  398. CheckExt(AExt);
  399. FCache.Remove(NormalizeExt(AExt));
  400. end;
  401. function TBrookMediaTypes.TryType(const AExt: string;
  402. out AType: string): Boolean;
  403. begin
  404. CheckExt(AExt);
  405. CheckPrepared;
  406. Result := FCache.TryValue(NormalizeExt(AExt), AType);
  407. end;
  408. function TBrookMediaTypes.Find(const AExt, ADefType: string): string;
  409. begin
  410. CheckExt(AExt);
  411. CheckType(ADefType);
  412. CheckPrepared;
  413. if not FCache.TryValue(NormalizeExt(AExt), Result) then
  414. Result := ADefType;
  415. end;
  416. function TBrookMediaTypes.Find(const AExt: string): string;
  417. begin
  418. Result := Find(AExt, FDefaultType);
  419. end;
  420. function TBrookMediaTypes.Count: Integer;
  421. begin
  422. Result := FCache.Count;
  423. end;
  424. procedure TBrookMediaTypes.Clear;
  425. begin
  426. FCache.Clear;
  427. end;
  428. { TBrookMediaTypesParser }
  429. constructor TBrookMediaTypesParser.Create(AReader: TBrookTextReader;
  430. ATypes: TBrookMediaTypes);
  431. begin
  432. inherited Create;
  433. if not Assigned(AReader) then
  434. raise EArgumentNilException.CreateFmt(SParamIsNil, ['AReader']);
  435. if not Assigned(ATypes) then
  436. raise EArgumentNilException.CreateFmt(SParamIsNil, ['ATypes']);
  437. FReader := AReader;
  438. FTypes := ATypes;
  439. end;
  440. procedure TBrookMediaTypesParser.Parse;
  441. var
  442. VPair, VExtensions: TArray<string>;
  443. VLine, VMediaType: string;
  444. VSep: Char;
  445. VSameSep: Boolean;
  446. I: Integer;
  447. begin
  448. FTypes.Cache.Clear;
  449. VSep := #0;
  450. VSameSep := False;
  451. while not FReader.EOF do
  452. begin
  453. VLine := FReader.Read;
  454. if (Length(VLine) > 0) and (VLine[1] <> '#') then
  455. begin
  456. if VSep = #0 then
  457. begin
  458. if Pos(#9, VLine) > 0 then
  459. VSep := #9
  460. else
  461. begin
  462. if Pos(' ', VLine) = 0 then
  463. Continue;
  464. VSep := ' ';
  465. end;
  466. VSameSep := VSep = ' ';
  467. end;
  468. VPair := VLine.Split([VSep], TStringSplitOptions.ExcludeEmpty);
  469. if Length(VPair) > 1 then
  470. begin
  471. VMediaType := VPair[0];
  472. if VSameSep then
  473. for I := Succ(Low(VPair)) to High(VPair) do
  474. FTypes.Add(VPair[I], VMediaType)
  475. else
  476. begin
  477. VExtensions := VPair[1].Split([' '], TStringSplitOptions.ExcludeEmpty);
  478. for I := Low(VExtensions) to High(VExtensions) do
  479. FTypes.Add(VExtensions[I], VMediaType);
  480. end;
  481. end;
  482. end;
  483. end;
  484. end;
  485. { TBrookMediaTypesParserNginx }
  486. procedure TBrookMediaTypesParserNginx.Parse;
  487. var
  488. I: Integer;
  489. VPair: TArray<string>;
  490. VLine, VMediaType: string;
  491. begin
  492. while not FReader.EOF do
  493. begin
  494. VLine := Trim(FReader.Read);
  495. System.Delete(VLine, Length(VLine), Length(';'));
  496. if (Length(VLine) > 0) and (VLine[1] <> '#') and (VLine <> 'types ') then
  497. begin
  498. VPair := VLine.Split([' '], TStringSplitOptions.ExcludeEmpty);
  499. if Length(VPair) > 1 then
  500. begin
  501. VMediaType := VPair[0];
  502. for I := 1 to High(VPair) do
  503. FTypes.Add(VPair[I], VMediaType);
  504. end;
  505. end;
  506. end;
  507. end;
  508. { TBrookMediaTypesPath }
  509. constructor TBrookMediaTypesPath.Create(const AFileName: string);
  510. begin
  511. inherited Create;
  512. FFileName := AFileName;
  513. FReader := CreateReader;
  514. FParser := CreateParser;
  515. end;
  516. constructor TBrookMediaTypesPath.Create;
  517. begin
  518. Create(GetFileName);
  519. end;
  520. destructor TBrookMediaTypesPath.Destroy;
  521. begin
  522. FReader.Free;
  523. FParser.Free;
  524. inherited Destroy;
  525. end;
  526. function TBrookMediaTypesPath.CreateReader: TBrookTextReader;
  527. begin
  528. Result := TBrookFileReader.Create(FFileName);
  529. end;
  530. function TBrookMediaTypesPath.CreateParser: TBrookMediaTypesParser;
  531. begin
  532. Result := TBrookMediaTypesParser.Create(FReader, Self);
  533. end;
  534. class function TBrookMediaTypesPath.GetDescription: string;
  535. begin
  536. Result := 'Path';
  537. end;
  538. class function TBrookMediaTypesPath.GetFileName: TFileName;
  539. begin
  540. Result := GBrookMIMEFileName;
  541. end;
  542. function TBrookMediaTypesPath.IsPrepared: Boolean;
  543. begin
  544. Result := FPrepared;
  545. end;
  546. procedure TBrookMediaTypesPath.Prepare;
  547. begin
  548. if FPrepared then
  549. Exit;
  550. FParser.Parse;
  551. FPrepared := True;
  552. end;
  553. procedure TBrookMediaTypesPath.Clear;
  554. begin
  555. if not FPrepared then
  556. Exit;
  557. inherited Clear;
  558. FPrepared := False;
  559. end;
  560. { TBrookMediaTypesApache }
  561. class function TBrookMediaTypesApache.GetDescription: string;
  562. begin
  563. Result := 'Apache';
  564. end;
  565. { TBrookMediaTypesNginx }
  566. function TBrookMediaTypesNginx.CreateParser: TBrookMediaTypesParser;
  567. begin
  568. Result := TBrookMediaTypesParserNginx.Create(FReader, Self);
  569. end;
  570. class function TBrookMediaTypesNginx.GetDescription: string;
  571. begin
  572. Result := 'Nginx';
  573. end;
  574. class function TBrookMediaTypesNginx.GetFileName: TFileName;
  575. begin
  576. Result := Concat(
  577. {$IFDEF UNIX}'/etc/nginx/'{$ELSE}ExtractFilePath(ParamStr(0)){$ENDIF},
  578. BROOK_MIME_FILE);
  579. end;
  580. { TBrookMediaTypesWindows }
  581. class function TBrookMediaTypesWindows.GetDescription: string;
  582. begin
  583. Result := 'Windows';
  584. end;
  585. { TBrookMediaTypesUnix }
  586. class function TBrookMediaTypesUnix.GetDescription: string;
  587. begin
  588. Result := 'Unix';
  589. end;
  590. class function TBrookMediaTypesUnix.GetFileName: TFileName;
  591. var
  592. FNs: TArray<TFileName>;
  593. FN: TFileName;
  594. begin
  595. FNs := TArray<TFileName>.Create(
  596. Concat('/etc/', BROOK_MIME_FILE)
  597. // Put other 'mime.types' paths here...
  598. );
  599. for FN in FNs do
  600. if FileExists(FN) then
  601. Exit(FN);
  602. Result := BROOK_MIME_FILE;
  603. end;
  604. { TBrookMIME }
  605. constructor TBrookMIME.Create(AOwner: TComponent);
  606. begin
  607. inherited Create(AOwner);
  608. SgLib.UnloadEvents.Add(InternalLibUnloadEvent, Self);
  609. FDefaultType := BROOK_CT_OCTET_STREAM;
  610. FFileName := GBrookMIMEFileName;
  611. FProvider := BROOK_MIME_PROVIDER;
  612. end;
  613. destructor TBrookMIME.Destroy;
  614. begin
  615. try
  616. SetActive(False);
  617. SgLib.UnloadEvents.Remove(InternalLibUnloadEvent);
  618. finally
  619. inherited Destroy;
  620. end;
  621. end;
  622. function TBrookMIME.GetProviderClass: TBrookMediaTypesClass;
  623. var
  624. D: string;
  625. C: TPersistentClass;
  626. begin
  627. D := Concat(BROOK_MIME_TAG, FProvider);
  628. C := Classes.GetClass(D);
  629. if Assigned(C) and (not C.InheritsFrom(TBrookMediaTypes)) then
  630. raise EInvalidCast.CreateFmt(SBrookInvalidMIMEProviderClass, [C.ClassName]);
  631. Result := TBrookMediaTypesClass(C);
  632. if not Assigned(Result) then
  633. raise EClassNotFound.CreateFmt(SBrookUnknownMIMEProvider, [FProvider]);
  634. end;
  635. function TBrookMIME.CreateTypes(const AFileName: TFileName): TBrookMediaTypes;
  636. var
  637. T: TBrookMediaTypesClass;
  638. begin
  639. T := GetProviderClass;
  640. if T.InheritsFrom(TBrookMediaTypesPath) then
  641. Exit(TBrookMediaTypesPathClass(T).Create(AFileName));
  642. Result := T.Create;
  643. Result.DefaultType := FDefaultType;
  644. end;
  645. procedure TBrookMIME.CheckProvider;
  646. begin
  647. if FProvider.IsEmpty then
  648. raise EArgumentException.Create(SBrookEmptyMIMEProvider);
  649. end;
  650. procedure TBrookMIME.CheckActive;
  651. begin
  652. if not Active then
  653. raise EInvalidOpException.Create(SBrookInactiveMIMETypes);
  654. end;
  655. procedure TBrookMIME.CheckInactive;
  656. begin
  657. if (not (csLoading in ComponentState)) and Active then
  658. raise EInvalidOpException.Create(SBrookActiveMIMETypes);
  659. end;
  660. procedure TBrookMIME.Loaded;
  661. begin
  662. inherited Loaded;
  663. try
  664. if FStreamedActive then
  665. SetActive(True);
  666. except
  667. if csDesigning in ComponentState then
  668. begin
  669. if Assigned(ApplicationHandleException) then
  670. ApplicationHandleException(ExceptObject)
  671. else
  672. ShowException(ExceptObject, ExceptAddr);
  673. end
  674. else
  675. raise;
  676. end;
  677. end;
  678. procedure TBrookMIME.InternalLibUnloadEvent(ASender: TObject);
  679. begin
  680. if Assigned(ASender) then
  681. TBrookMIME(ASender).Close;
  682. end;
  683. function TBrookMIME.GetHandle: Pointer;
  684. begin
  685. Result := FTypes.Handle;
  686. end;
  687. procedure TBrookMIME.DoOpen;
  688. begin
  689. if Assigned(FTypes) then
  690. Exit;
  691. CheckProvider;
  692. FTypes := CreateTypes(FFileName);
  693. FActive := True;
  694. end;
  695. procedure TBrookMIME.DoClose;
  696. begin
  697. if not Assigned(FTypes) then
  698. Exit;
  699. FTypes.Destroy;
  700. FTypes := nil;
  701. FActive := False;
  702. end;
  703. function TBrookMIME.IsActiveStored: Boolean;
  704. begin
  705. Result := FActive;
  706. end;
  707. function TBrookMIME.GetTypes: TBrookMediaTypes;
  708. begin
  709. CheckActive;
  710. Result := FTypes;
  711. end;
  712. function TBrookMIME.IsDefaultTypeStored: Boolean;
  713. begin
  714. Result := FDefaultType <> BROOK_CT_OCTET_STREAM;
  715. end;
  716. function TBrookMIME.IsFileNameStored: Boolean;
  717. begin
  718. Result := FFileName <> GBrookMIMEFileName;
  719. end;
  720. function TBrookMIME.IsProviderStored: Boolean;
  721. begin
  722. Result := FProvider <> BROOK_MIME_PROVIDER;
  723. end;
  724. procedure TBrookMIME.SetActive(AValue: Boolean);
  725. begin
  726. if AValue = FActive then
  727. Exit;
  728. if csDesigning in ComponentState then
  729. begin
  730. if not (csLoading in ComponentState) then
  731. begin
  732. if not FileExists(FFileName) then
  733. raise EFOpenError.CreateFmt(SFOpenError, [FFileName]);
  734. SgLib.Check;
  735. end;
  736. FActive := AValue;
  737. end
  738. else
  739. if AValue then
  740. begin
  741. if csReading in ComponentState then
  742. FStreamedActive := True
  743. else
  744. DoOpen;
  745. end
  746. else
  747. DoClose;
  748. end;
  749. procedure TBrookMIME.SetDefaultType(const AValue: string);
  750. begin
  751. if FDefaultType = AValue then
  752. Exit;
  753. if not FStreamedActive then
  754. CheckInactive;
  755. if FDefaultType.IsEmpty or (not TBrookMediaTypes.IsValid(AValue)) then
  756. FDefaultType := BROOK_CT_OCTET_STREAM
  757. else
  758. FDefaultType := AValue;
  759. end;
  760. procedure TBrookMIME.SetFileName(const AValue: TFileName);
  761. begin
  762. if FFileName = AValue then
  763. Exit;
  764. if not FStreamedActive then
  765. CheckInactive;
  766. FFileName := AValue;
  767. if FFileName = EmptyStr then
  768. FFileName := GBrookMIMEFileName;
  769. end;
  770. procedure TBrookMIME.SetProvider(const AValue: string);
  771. begin
  772. if FProvider = AValue then
  773. Exit;
  774. if not FStreamedActive then
  775. CheckInactive;
  776. FProvider := AValue;
  777. if FProvider.IsEmpty then
  778. FProvider := BROOK_MIME_PROVIDER;
  779. end;
  780. procedure TBrookMIME.Open;
  781. begin
  782. SetActive(True);
  783. end;
  784. procedure TBrookMIME.Close;
  785. begin
  786. SetActive(False);
  787. end;
  788. initialization
  789. GBrookMIMEFileName := Concat(
  790. {$IF DEFINED(UNIX) OR DEFINED(POSIX)}'/etc/'{$ELSE}ExtractFilePath(ParamStr(0)){$ENDIF},
  791. BROOK_MIME_FILE);
  792. RegisterClassAlias(TBrookMediaTypesPath, TBrookMediaTypesPath.GetRegisterAlias);
  793. RegisterClassAlias(TBrookMediaTypesApache, TBrookMediaTypesApache.GetRegisterAlias);
  794. RegisterClassAlias(TBrookMediaTypesNginx, TBrookMediaTypesNginx.GetRegisterAlias);
  795. RegisterClassAlias(TBrookMediaTypesWindows, TBrookMediaTypesWindows.GetRegisterAlias);
  796. RegisterClassAlias(TBrookMediaTypesUnix, TBrookMediaTypesUnix.GetRegisterAlias);
  797. finalization
  798. UnregisterClass(TBrookMediaTypesPath);
  799. UnregisterClass(TBrookMediaTypesApache);
  800. UnregisterClass(TBrookMediaTypesNginx);
  801. UnregisterClass(TBrookMediaTypesWindows);
  802. UnregisterClass(TBrookMediaTypesUnix);
  803. end.