BrookMediaTypes.pas 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894
  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); 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 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; 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 := AType.Length > 3;
  340. end;
  341. class function TBrookMediaTypes.IsText(const AType: string): Boolean;
  342. begin
  343. Result := AType.StartsWith('text/') and IsValid(AType);
  344. end;
  345. class function TBrookMediaTypes.IsExt(const AExt: string): Boolean;
  346. begin
  347. Result := (Length(AExt) > 0) and (AExt <> '.') and (AExt <> '..');
  348. end;
  349. class function TBrookMediaTypes.NormalizeExt(const AExt: string): string;
  350. begin
  351. Result := AExt;
  352. if (Length(AExt) > 0) and (AExt[1] <> '.') then
  353. Result := Concat('.', Result);
  354. end;
  355. procedure TBrookMediaTypes.CheckExt(const AExt: string);
  356. begin
  357. if AExt.IsEmpty then
  358. raise EArgumentException.Create(SBrookEmptyMediaExt);
  359. if not IsExt(AExt) then
  360. raise EBrookMediaTypes.CreateFmt(SBrookInvalidMediaExt, [AExt]);
  361. end;
  362. procedure TBrookMediaTypes.CheckPrepared;
  363. begin
  364. if not IsPrepared then
  365. Prepare;
  366. end;
  367. procedure TBrookMediaTypes.CheckType(const AType: string);
  368. begin
  369. if AType.IsEmpty then
  370. raise EArgumentException.Create(SBrookEmptyMediaType);
  371. if not IsValid(AType) then
  372. raise EBrookMediaTypes.CreateResFmt(@SBrookInvalidMediaType, [AType]);
  373. end;
  374. function TBrookMediaTypes.GetHandle: Pointer;
  375. begin
  376. Result := FHandle;
  377. end;
  378. procedure TBrookMediaTypes.SetDefaultType(const AValue: string);
  379. begin
  380. if FDefaultType = AValue then
  381. Exit;
  382. CheckType(AValue);
  383. FDefaultType := AValue;
  384. end;
  385. procedure TBrookMediaTypes.Add(const AExt, AType: string);
  386. begin
  387. CheckExt(AExt);
  388. CheckType(AType);
  389. FCache.AddOrSet(NormalizeExt(AExt), AType);
  390. end;
  391. procedure TBrookMediaTypes.Remove(const AExt: string);
  392. begin
  393. CheckExt(AExt);
  394. FCache.Remove(NormalizeExt(AExt));
  395. end;
  396. function TBrookMediaTypes.TryType(const AExt: string;
  397. out AType: string): Boolean;
  398. begin
  399. CheckExt(AExt);
  400. CheckPrepared;
  401. Result := FCache.TryValue(NormalizeExt(AExt), AType);
  402. end;
  403. function TBrookMediaTypes.Find(const AExt, ADefType: string): string;
  404. begin
  405. CheckExt(AExt);
  406. CheckType(ADefType);
  407. CheckPrepared;
  408. if not FCache.TryValue(NormalizeExt(AExt), Result) then
  409. Result := ADefType;
  410. end;
  411. function TBrookMediaTypes.Find(const AExt: string): string;
  412. begin
  413. Result := Find(AExt, FDefaultType);
  414. end;
  415. function TBrookMediaTypes.Count: Integer;
  416. begin
  417. Result := FCache.Count;
  418. end;
  419. procedure TBrookMediaTypes.Clear;
  420. begin
  421. FCache.Clear;
  422. end;
  423. { TBrookMediaTypesParser }
  424. constructor TBrookMediaTypesParser.Create(AReader: TBrookTextReader;
  425. ATypes: TBrookMediaTypes);
  426. begin
  427. inherited Create;
  428. if not Assigned(AReader) then
  429. raise EArgumentNilException.CreateFmt(SParamIsNil, ['AReader']);
  430. if not Assigned(ATypes) then
  431. raise EArgumentNilException.CreateFmt(SParamIsNil, ['ATypes']);
  432. FReader := AReader;
  433. FTypes := ATypes;
  434. end;
  435. procedure TBrookMediaTypesParser.Parse;
  436. var
  437. VPair, VExtensions: TArray<string>;
  438. VLine, VMediaType: string;
  439. VSep: Char;
  440. VSameSep: Boolean;
  441. I: Integer;
  442. begin
  443. FTypes.Cache.Clear;
  444. VSep := #0;
  445. VSameSep := False;
  446. while not FReader.EOF do
  447. begin
  448. VLine := FReader.Read;
  449. if (Length(VLine) > 0) and (VLine[1] <> '#') then
  450. begin
  451. if VSep = #0 then
  452. begin
  453. if Pos(#9, VLine) > 0 then
  454. VSep := #9
  455. else
  456. begin
  457. if Pos(' ', VLine) = 0 then
  458. Continue;
  459. VSep := ' ';
  460. end;
  461. VSameSep := VSep = ' ';
  462. end;
  463. VPair := VLine.Split([VSep], TStringSplitOptions.ExcludeEmpty);
  464. if Length(VPair) > 1 then
  465. begin
  466. VMediaType := VPair[0];
  467. if VSameSep then
  468. for I := Succ(Low(VPair)) to High(VPair) do
  469. FTypes.Add(VPair[I], VMediaType)
  470. else
  471. begin
  472. VExtensions := VPair[1].Split([' '], TStringSplitOptions.ExcludeEmpty);
  473. for I := Low(VExtensions) to High(VExtensions) do
  474. FTypes.Add(VExtensions[I], VMediaType);
  475. end;
  476. end;
  477. end;
  478. end;
  479. end;
  480. { TBrookMediaTypesParserNginx }
  481. procedure TBrookMediaTypesParserNginx.Parse;
  482. var
  483. I: Integer;
  484. VPair: TArray<string>;
  485. VLine, VMediaType: string;
  486. begin
  487. while not FReader.EOF do
  488. begin
  489. VLine := Trim(FReader.Read);
  490. System.Delete(VLine, Length(VLine), Length(';'));
  491. if (Length(VLine) > 0) and (VLine[1] <> '#') and (VLine <> 'types ') then
  492. begin
  493. VPair := VLine.Split([' '], TStringSplitOptions.ExcludeEmpty);
  494. if Length(VPair) > 1 then
  495. begin
  496. VMediaType := VPair[0];
  497. for I := 1 to High(VPair) do
  498. FTypes.Add(VPair[I], VMediaType);
  499. end;
  500. end;
  501. end;
  502. end;
  503. { TBrookMediaTypesPath }
  504. constructor TBrookMediaTypesPath.Create(const AFileName: string);
  505. begin
  506. inherited Create;
  507. FFileName := AFileName;
  508. FReader := CreateReader;
  509. FParser := CreateParser;
  510. end;
  511. constructor TBrookMediaTypesPath.Create;
  512. begin
  513. Create(GetFileName);
  514. end;
  515. destructor TBrookMediaTypesPath.Destroy;
  516. begin
  517. FReader.Free;
  518. FParser.Free;
  519. inherited Destroy;
  520. end;
  521. function TBrookMediaTypesPath.CreateReader: TBrookTextReader;
  522. begin
  523. Result := TBrookFileReader.Create(FFileName);
  524. end;
  525. function TBrookMediaTypesPath.CreateParser: TBrookMediaTypesParser;
  526. begin
  527. Result := TBrookMediaTypesParser.Create(FReader, Self);
  528. end;
  529. class function TBrookMediaTypesPath.GetDescription: string;
  530. begin
  531. Result := 'Path';
  532. end;
  533. class function TBrookMediaTypesPath.GetFileName: TFileName;
  534. begin
  535. Result := GBrookMIMEFileName;
  536. end;
  537. function TBrookMediaTypesPath.IsPrepared: Boolean;
  538. begin
  539. Result := FPrepared;
  540. end;
  541. procedure TBrookMediaTypesPath.Prepare;
  542. begin
  543. if FPrepared then
  544. Exit;
  545. FParser.Parse;
  546. FPrepared := True;
  547. end;
  548. procedure TBrookMediaTypesPath.Clear;
  549. begin
  550. if not FPrepared then
  551. Exit;
  552. inherited Clear;
  553. FPrepared := False;
  554. end;
  555. { TBrookMediaTypesApache }
  556. class function TBrookMediaTypesApache.GetDescription: string;
  557. begin
  558. Result := 'Apache';
  559. end;
  560. { TBrookMediaTypesNginx }
  561. function TBrookMediaTypesNginx.CreateParser: TBrookMediaTypesParser;
  562. begin
  563. Result := TBrookMediaTypesParserNginx.Create(FReader, Self);
  564. end;
  565. class function TBrookMediaTypesNginx.GetDescription: string;
  566. begin
  567. Result := 'Nginx';
  568. end;
  569. class function TBrookMediaTypesNginx.GetFileName: TFileName;
  570. begin
  571. Result := Concat(
  572. {$IFDEF UNIX}'/etc/nginx/'{$ELSE}ExtractFilePath(ParamStr(0)){$ENDIF},
  573. BROOK_MIME_FILE);
  574. end;
  575. { TBrookMediaTypesWindows }
  576. class function TBrookMediaTypesWindows.GetDescription: string;
  577. begin
  578. Result := 'Windows';
  579. end;
  580. { TBrookMediaTypesUnix }
  581. class function TBrookMediaTypesUnix.GetDescription: string;
  582. begin
  583. Result := 'Unix';
  584. end;
  585. class function TBrookMediaTypesUnix.GetFileName: TFileName;
  586. var
  587. FNs: TArray<TFileName>;
  588. FN: TFileName;
  589. begin
  590. FNs := TArray<TFileName>.Create(
  591. Concat('/etc/', BROOK_MIME_FILE)
  592. // Put other 'mime.types' paths here...
  593. );
  594. for FN in FNs do
  595. if FileExists(FN) then
  596. Exit(FN);
  597. Result := BROOK_MIME_FILE;
  598. end;
  599. { TBrookMIME }
  600. constructor TBrookMIME.Create(AOwner: TComponent);
  601. begin
  602. inherited Create(AOwner);
  603. SgLib.UnloadEvents.Add(InternalLibUnloadEvent, Self);
  604. FDefaultType := BROOK_CT_OCTET_STREAM;
  605. FFileName := GBrookMIMEFileName;
  606. FProvider := BROOK_MIME_PROVIDER;
  607. end;
  608. destructor TBrookMIME.Destroy;
  609. begin
  610. try
  611. SetActive(False);
  612. SgLib.UnloadEvents.Remove(InternalLibUnloadEvent);
  613. finally
  614. inherited Destroy;
  615. end;
  616. end;
  617. function TBrookMIME.GetProviderClass: TBrookMediaTypesClass;
  618. var
  619. D: string;
  620. C: TPersistentClass;
  621. begin
  622. D := Concat(BROOK_MIME_TAG, FProvider);
  623. C := Classes.GetClass(D);
  624. if Assigned(C) and (not C.InheritsFrom(TBrookMediaTypes)) then
  625. raise EInvalidCast.CreateFmt(SBrookInvalidMIMEProviderClass, [C.ClassName]);
  626. Result := TBrookMediaTypesClass(C);
  627. if not Assigned(Result) then
  628. raise EClassNotFound.CreateFmt(SBrookUnknownMIMEProvider, [FProvider]);
  629. end;
  630. function TBrookMIME.CreateTypes(const AFileName: TFileName): TBrookMediaTypes;
  631. var
  632. T: TBrookMediaTypesClass;
  633. begin
  634. T := GetProviderClass;
  635. if T.InheritsFrom(TBrookMediaTypesPath) then
  636. Exit(TBrookMediaTypesPathClass(T).Create(AFileName));
  637. Result := T.Create;
  638. Result.DefaultType := FDefaultType;
  639. end;
  640. procedure TBrookMIME.CheckProvider;
  641. begin
  642. if FProvider.IsEmpty then
  643. raise EArgumentException.Create(SBrookEmptyMIMEProvider);
  644. end;
  645. procedure TBrookMIME.CheckActive;
  646. begin
  647. if not Active then
  648. raise EInvalidOpException.Create(SBrookInactiveMIMETypes);
  649. end;
  650. procedure TBrookMIME.CheckInactive;
  651. begin
  652. if (not (csLoading in ComponentState)) and Active then
  653. raise EInvalidOpException.Create(SBrookActiveMIMETypes);
  654. end;
  655. procedure TBrookMIME.Loaded;
  656. begin
  657. inherited Loaded;
  658. try
  659. if FStreamedActive then
  660. SetActive(True);
  661. except
  662. if csDesigning in ComponentState then
  663. begin
  664. if Assigned(ApplicationHandleException) then
  665. ApplicationHandleException(ExceptObject)
  666. else
  667. ShowException(ExceptObject, ExceptAddr);
  668. end
  669. else
  670. raise;
  671. end;
  672. end;
  673. procedure TBrookMIME.InternalLibUnloadEvent(ASender: TObject);
  674. begin
  675. if Assigned(ASender) then
  676. TBrookMIME(ASender).Close;
  677. end;
  678. function TBrookMIME.GetHandle: Pointer;
  679. begin
  680. Result := FTypes.Handle;
  681. end;
  682. procedure TBrookMIME.DoOpen;
  683. begin
  684. if Assigned(FTypes) then
  685. Exit;
  686. CheckProvider;
  687. FTypes := CreateTypes(FFileName);
  688. FActive := True;
  689. end;
  690. procedure TBrookMIME.DoClose;
  691. begin
  692. if not Assigned(FTypes) then
  693. Exit;
  694. FTypes.Destroy;
  695. FTypes := nil;
  696. FActive := False;
  697. end;
  698. function TBrookMIME.IsActiveStored: Boolean;
  699. begin
  700. Result := FActive;
  701. end;
  702. function TBrookMIME.GetTypes: TBrookMediaTypes;
  703. begin
  704. CheckActive;
  705. Result := FTypes;
  706. end;
  707. function TBrookMIME.IsDefaultTypeStored: Boolean;
  708. begin
  709. Result := FDefaultType <> BROOK_CT_OCTET_STREAM;
  710. end;
  711. function TBrookMIME.IsFileNameStored: Boolean;
  712. begin
  713. Result := FFileName <> GBrookMIMEFileName;
  714. end;
  715. function TBrookMIME.IsProviderStored: Boolean;
  716. begin
  717. Result := FProvider <> BROOK_MIME_PROVIDER;
  718. end;
  719. procedure TBrookMIME.SetActive(AValue: Boolean);
  720. begin
  721. if AValue = FActive then
  722. Exit;
  723. if csDesigning in ComponentState then
  724. begin
  725. if not (csLoading in ComponentState) then
  726. begin
  727. if not FileExists(FFileName) then
  728. raise EFOpenError.CreateFmt(SFOpenError, [FFileName]);
  729. SgLib.Check;
  730. end;
  731. FActive := AValue;
  732. end
  733. else
  734. if AValue then
  735. begin
  736. if csReading in ComponentState then
  737. FStreamedActive := True
  738. else
  739. DoOpen;
  740. end
  741. else
  742. DoClose;
  743. end;
  744. procedure TBrookMIME.SetDefaultType(const AValue: string);
  745. begin
  746. if FDefaultType = AValue then
  747. Exit;
  748. if not FStreamedActive then
  749. CheckInactive;
  750. if FDefaultType.IsEmpty or (not TBrookMediaTypes.IsValid(AValue)) then
  751. FDefaultType := BROOK_CT_OCTET_STREAM
  752. else
  753. FDefaultType := AValue;
  754. end;
  755. procedure TBrookMIME.SetFileName(const AValue: TFileName);
  756. begin
  757. if FFileName = AValue then
  758. Exit;
  759. if not FStreamedActive then
  760. CheckInactive;
  761. FFileName := AValue;
  762. if FFileName = EmptyStr then
  763. FFileName := GBrookMIMEFileName;
  764. end;
  765. procedure TBrookMIME.SetProvider(const AValue: string);
  766. begin
  767. if FProvider = AValue then
  768. Exit;
  769. if not FStreamedActive then
  770. CheckInactive;
  771. FProvider := AValue;
  772. if FProvider.IsEmpty then
  773. FProvider := BROOK_MIME_PROVIDER;
  774. end;
  775. procedure TBrookMIME.Open;
  776. begin
  777. SetActive(True);
  778. end;
  779. procedure TBrookMIME.Close;
  780. begin
  781. SetActive(False);
  782. end;
  783. initialization
  784. GBrookMIMEFileName := Concat(
  785. {$IF DEFINED(UNIX) OR DEFINED(POSIX)}'/etc/'{$ELSE}ExtractFilePath(ParamStr(0)){$ENDIF},
  786. BROOK_MIME_FILE);
  787. RegisterClassAlias(TBrookMediaTypesPath, TBrookMediaTypesPath.GetRegisterAlias);
  788. RegisterClassAlias(TBrookMediaTypesApache, TBrookMediaTypesApache.GetRegisterAlias);
  789. RegisterClassAlias(TBrookMediaTypesNginx, TBrookMediaTypesNginx.GetRegisterAlias);
  790. RegisterClassAlias(TBrookMediaTypesWindows, TBrookMediaTypesWindows.GetRegisterAlias);
  791. RegisterClassAlias(TBrookMediaTypesUnix, TBrookMediaTypesUnix.GetRegisterAlias);
  792. finalization
  793. UnregisterClass(TBrookMediaTypesPath);
  794. UnregisterClass(TBrookMediaTypesApache);
  795. UnregisterClass(TBrookMediaTypesNginx);
  796. UnregisterClass(TBrookMediaTypesWindows);
  797. UnregisterClass(TBrookMediaTypesUnix);
  798. end.