BrookMediaTypes.pas 26 KB

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