diff --git a/.gitignore b/.gitignore
index e32cb3801..af427fc96 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,7 +5,7 @@
*.ppu
*.rst
*.cgi
-*.exe
+# *.exe
*.log
*.bak*
fp.ini
@@ -20,13 +20,19 @@ fp.dsk
*.lps
*.res
mangadownloader/lib
+mangadownloader/languages/
updater/lib
+updater/languages/
updaterslim/lib
bin/
Release/
+3rd/
# compiled languages files
*.mo
# lazarus auto generated resources
*.lrt
+
+# auto generated by git2revision
+baseunits/revision.inc
diff --git a/3rd/internettools b/3rd/internettools
deleted file mode 160000
index 4f7490dd0..000000000
--- a/3rd/internettools
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 4f7490dd05ed6f2eeffa157f7921f46ae835ce53
diff --git a/README.md b/README.md
index 2948a7ed5..e9d60d19b 100644
--- a/README.md
+++ b/README.md
@@ -1,37 +1,59 @@
-# The Free Manga Downloader (FMD)
+# Free Manga Downloader (FMD)
-(Forked from https://sf.net/p/fmd)
+(Forked from https://github.com/riderkick/FMD)
-## Download the latest release
+# DISCONTINUED, PLEASE MOVE ON TO FMD2
-[![Latest release](https://img.shields.io/github/release/riderkick/FMD.svg)](https://github.com/riderkick/FMD/releases/latest) [![Download latest release (Win32)](https://img.shields.io/github/downloads/riderkick/FMD/latest/fmd_0.9.158.0.7z.svg?label=Win32)](https://github.com/riderkick/FMD/releases/download/0.9.158.0/fmd_0.9.158.0.7z) [![Download latest release (Win64)](https://img.shields.io/github/downloads/riderkick/FMD/latest/fmd_0.9.158.0_Win64.7z.svg?label=Win64)](https://github.com/riderkick/FMD/releases/download/0.9.158.0/fmd_0.9.158.0_Win64.7z)
+This fork of FMD will not be continued any further. You can still use it but it won't get any updates anymore.
+You can still find us on our Discord server and talk about FMD or even FMD2, but you should still report bugs, etc. to its repository instead.
-## Content
+FMD2 can be found here: https://github.com/dazedcat19/FMD2
+
+
+
+## Download
-- [About FMD](#about-fmd)
-- [Build instructions](#build-instructions)
-- [Localization](#localization)
+[![Latest release](https://img.shields.io/github/release/fmd-project-team/FMD.svg)](https://github.com/fmd-project-team/FMD/releases/latest)
## About FMD
-The Free Manga Downloader is a free open source application written in Object Pascal for managing and downloading manga from various websites. The source code was released under the GPLv2 license. FMD homesite is at https://github.com/riderkick/FMD or http://sf.net/p/newfmd.
-
+This is an active fork of the Free Manga Downloader which is a free open source application written in Object Pascal for managing and downloading manga from various websites. The source code was released under the GPLv2 license.
+
## Build instructions
-In order to build FMD from the source code, you must install the latest version of Lazarus and Free Pascal Compiler from http://www.lazarus-ide.org/. Then you must install the following 3rd party libraries and components:
-
- - [RichMemo](https://sourceforge.net/p/lazarus-ccr/svn/HEAD/tree/components/richmemo/)
- - [Virtual TreeView](https://github.com/blikblum/VirtualTreeView-Lazarus/tree/lazarus-v4) (and `lclextensions` from the Releases page)
- - [Synapse](https://sourceforge.net/p/synalist/code/HEAD/tree/trunk/) (at least revision `r160`)
- - [InternetTools](https://github.com/benibela/internettools)
- - [MultiLog](https://github.com/blikblum/multilog)
- - [DCPCypt](https://sourceforge.net/projects/lazarus-ccr/)
-
-After everything is installed, open the file `md.lpi` by using Lazarus IDE. Make sure to add `ssl_openssl` to uses list of the `laz_synapse` package.
-Then select `Run -> Build` to build the source code. If everything is ok, the binary file should be in `FMD_source_code_folder/bin`.
-
-If InternetTools fail to compile (incompatible PPU), make sure to compile them individually first.
-
+In order to build FMD from the source code, you must install the latest version of Lazarus and Free Pascal Compiler:
+[![Lazarus IDE 2.0.6](https://img.shields.io/badge/Lazarus%20IDE-2.0.6-Blue.svg)](http://www.lazarus-ide.org/)
+
+To compile FMD some packages and components are needed. You can download and install most of them by the built-in Online Package Manager (OPM).
+The following packages and components are used for building FMD:
+![Synapse 40.1](https://img.shields.io/badge/Synapse-OPM%20(40.1)-Blue.svg) (Compile before "InternetTools")
+![DCPCrypt 2.0.4.1](https://img.shields.io/badge/DCPCrypt-OPM%20(2.0.4.1)-Blue.svg)
+![RichMemo (18.01.2020)](https://img.shields.io/badge/RichMemo-OPM%20(18.01.2020)-Blue.svg)
+![LCL Extensions 0.6.1](https://img.shields.io/badge/LCL%20Extensions-OPM%20(0.6.1)-Blue.svg) (Compile before "Virtual TreeView")
+![Virtual TreeView 5.5.3.1](https://img.shields.io/badge/Virtual%20TreeView-OPM%20(5.5.3.1)-Blue.svg)
+[![MultiLog (02.12.2019)](https://img.shields.io/badge/MultiLog-git%20master%20commit%20fd700fa5343c1b0e08063f88a1e6761036b10efc%20(02.12.2019)-Blue.svg)](https://github.com/blikblum/multilog)
+[![InternetTools (19.01.2020)](https://img.shields.io/badge/InternetTools-git%20master%20commit%20b834f9a3699e7d01bbd0cfafa8c4f7f75cff274d%20(19.01.2020)-Blue.svg)](https://github.com/benibela/internettools)
+
+After everything is installed, open the file `md.lpi` by using Lazarus IDE.
+Make sure to add `ssl_openssl` to the uses list of `Synapse` and compile the package again.
+To compile and build the source code of FMD select `Run -> Build`. If everything is ok, the binary file should be in `FMD_source_code_folder/bin`.
+
+If `InternetTools` fails to compile because of a missing or incompatible PPU, make sure to compile `Synapse` first.
+
+Some other external 3rd party tools and libraries are used as well:
+[![7-Zip](https://img.shields.io/badge/7--Zip%20(Standalone)-19.00-Blue.svg)](https://www.7-zip.org)
+[![Duktape](https://img.shields.io/badge/Duktape-2.5.0-Blue.svg)](https://github.com/grijjy/DelphiDuktape)
+[![WebP (libwebp)](https://img.shields.io/badge/WebP%20(libwebp)-1.1.0-Blue.svg)](https://github.com/webmproject/libwebp/)
+[![Lua](https://img.shields.io/badge/Lua-5.3.3-Blue.svg)](http://luabinaries.sourceforge.net/)
+[![OpenSSL](https://img.shields.io/badge/OpenSSL-1.1.1d-Blue.svg)](https://www.openssl.org/)
+[![SQLite](https://img.shields.io/badge/SQLite-3.30.1-Blue.svg)](https://www.sqlite.org/)
+
+These tools and libraries are not part of the source. You have to either download pre-compiled binaries, compile them yourself or just copy them from the latest FMD releases.
+
## Localization
-Translations are stored inside `languages` folder with `.po` extension. In order to translate FMD to your native languages you can copy `fmd.po` and rename it to `fmd.xx.po`, where `xx` stand for two-letter language code. Additionally you can add country code at the end of language code. For reference you can look at http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes and http://en.wikipedia.org/wiki/ISO_3166-1. For example `id_ID` will be recognized as `Bahasa Indonesia (Indonesia)`. To translate the content of the file you need to use translation tools like [Poedit](https://poedit.net). Once you have finished translating all of its content you can launch FMD and it will automatically detect your new languages upon startup.
+Translations are stored inside `languages` folder with `.po` extension.
+In order to translate FMD to your native language you can copy `fmd.po` and rename it to `fmd.xx.po`, where `xx` stand for two-letter language code.
+Additionally you can add country code at the end of language code. For reference you can look at http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes and http://en.wikipedia.org/wiki/ISO_3166-1. For example `id_ID` will be recognized as `Bahasa Indonesia (Indonesia)`.
+To translate the content of the file you need to use translation tools like [Poedit](https://poedit.net).
+Once you have finished translating all of its content you can launch FMD and it will automatically detect your new languages upon startup.
diff --git a/baseunits/BaseCrypto.pas b/baseunits/BaseCrypto.pas
index 25c651bca..21bab4ff9 100644
--- a/baseunits/BaseCrypto.pas
+++ b/baseunits/BaseCrypto.pas
@@ -168,12 +168,13 @@ function AESDecryptCBCMD5Base64ZerosPadding(const s, key, iv: String): String;
function MD5Hex(const s: String): String;
var
- h: array[0 .. 15] of Byte;
+ h: TBytes;
begin
with TDCP_md5.Create(nil) do
try
Init;
UpdateStr(s);
+ SetLength(h, 16);
Final(h);
finally
Free;
diff --git a/baseunits/CheckUpdate.pas b/baseunits/CheckUpdate.pas
index 33474b785..5cdf262c6 100644
--- a/baseunits/CheckUpdate.pas
+++ b/baseunits/CheckUpdate.pas
@@ -105,19 +105,22 @@ procedure TCheckUpdateThread.Execute;
FChangelog := '';
Synchronize(@SyncStartUpdate);
if not Terminated and FHTTP.Get(UPDATE_URL) then
- with TStringList.Create do try
- LoadFromStream(FHTTP.Document);
- if Count <> 0 then begin
- NameValueSeparator := '=';
- FNewVersionString := Trim(Values['VERSION']);
- if not TryStrToProgramVersion(FNewVersionString, FNewVersionNumber) then
- FNewVersionNumber := StrToProgramVersion('0.0.0.0');
- if NewerVersion(FNewVersionNumber, FMD_VERSION_NUMBER) then
- FUpdateURL := Trim(Values[UpperCase(FMD_TARGETOS)]);
+ begin
+ with TStringList.Create do try
+ LoadFromStream(FHTTP.Document);
+ if Count <> 0 then begin
+ NameValueSeparator := '=';
+ FNewVersionString := Trim(Values['VERSION']);
+ if not TryStrToProgramVersion(FNewVersionString, FNewVersionNumber) then
+ FNewVersionNumber := StrToProgramVersion('0.0.0.0');
+ if NewerVersion(FNewVersionNumber, FMD_VERSION_NUMBER) then
+ FUpdateURL := Trim(Values[UpperCase(FMD_TARGETOS)]);
+ end;
+ finally
+ Free;
end;
- finally
- Free;
end;
+
if not Terminated and (FUpdateURL <> '') and FHTTP.Get(CHANGELOG_URL) then
FChangelog := StreamToString(FHTTP.Document);
Synchronize(@SyncEndUpdate);
diff --git a/baseunits/DBDataProcess.pas b/baseunits/DBDataProcess.pas
index e3f06d99c..e544f391f 100644
--- a/baseunits/DBDataProcess.pas
+++ b/baseunits/DBDataProcess.pas
@@ -264,16 +264,10 @@ procedure TDBDataProcess.ResetRecNo(Dataset: TDataSet);
procedure TDBDataProcess.GoToRecNo(const ARecIndex: Integer);
begin
- if FRecNo<>ARecIndex then
+ if FQuery.RecNo<>(ARecIndex+1) then
begin
- if FRecNo=ARecIndex+1 then
- FQuery.Prior
- else
- if FRecNo=ARecIndex-1 then
- FQuery.Next
- else
- FQuery.RecNo:=ARecIndex+1;
FRecNo:=ARecIndex;
+ FQuery.RecNo:=ARecIndex+1;
end;
end;
@@ -831,6 +825,8 @@ function TDBDataProcess.DeleteData(const RecIndex: Integer): Boolean;
Dec(FRecordCount);
Result := True;
except
+ on E: Exception do
+ Logger.SendException(ClassName+'['+Website+'].DeleteData.Error!',E);
end;
end;
diff --git a/baseunits/DBUpdater.pas b/baseunits/DBUpdater.pas
index 80f90fd15..54ed88696 100644
--- a/baseunits/DBUpdater.pas
+++ b/baseunits/DBUpdater.pas
@@ -5,42 +5,28 @@
interface
uses
- Classes, SysUtils, httpsendthread, BaseThread, FMDOptions, process, ComCtrls,
- Controls, Dialogs, StdCtrls, Buttons, blcksock;
+ Classes, SysUtils, httpsendthread, FMDOptions, StatusBarDownload,
+ process, ComCtrls, Controls, Dialogs, Buttons;
type
{ TDBUpdaterThread }
- TDBUpdaterThread = class(TBaseThread)
+ TDBUpdaterThread = class(TStatusBarDownload)
private
- FStatusBar: TStatusBar;
- FProgressBar: TProgressBar;
- FButtonCancel: TSpeedButton;
- FHTTP: THTTPSendThread;
- FTotalSize: Integer;
- FCurrentSize: Integer;
+ FCurrentId: Integer;
FCurrentName: String;
FFailedList: TStringList;
- FCurrentId: Integer;
- FStatusText: String;
protected
- procedure ButtonCancelClick(Sender: TObject);
- procedure HTTPSockOnStatus(Sender: TObject; Reason: THookSocketReason;
- const Value: String);
procedure HTTPRedirected(const AHTTP: THTTPSendThread; const URL: String);
protected
procedure SyncStart;
procedure SyncFinal;
- procedure SyncStartDownload;
- procedure SyncUpdateProgress;
- procedure SyncUpdateStatus;
procedure SyncUpdateHint;
procedure SyncShowFailed;
procedure SyncCloseUsed;
procedure SyncReopenUsed;
procedure SyncRemoveAttached;
- procedure UpdateStatusText(const S: String);
procedure Execute; override;
public
Items: TStringList;
@@ -76,31 +62,6 @@ function GetDBURL(const AName: String): String;
{ TDBUpdaterThread }
-procedure TDBUpdaterThread.ButtonCancelClick(Sender: TObject);
-begin
- Self.Terminate;
-end;
-
-procedure TDBUpdaterThread.HTTPSockOnStatus(Sender: TObject;
- Reason: THookSocketReason; const Value: String);
-begin
- if Terminated then
- Exit;
- if Reason = HR_ReadCount then
- begin
- if FTotalSize = 0 then
- FTotalSize := StrToIntDef(Trim(FHTTP.Headers.Values['Content-Length']), 0);
- Inc(FCurrentSize, StrToInt(Value));
- Synchronize(@SyncUpdateProgress);
- end
- else
- if Reason = HR_Connect then
- begin
- FCurrentSize := 0;
- FTotalSize := 0;
- end;
-end;
-
procedure TDBUpdaterThread.HTTPRedirected(const AHTTP: THTTPSendThread;
const URL: String);
begin
@@ -111,110 +72,16 @@ procedure TDBUpdaterThread.HTTPRedirected(const AHTTP: THTTPSendThread;
procedure TDBUpdaterThread.SyncStart;
begin
DBUpdaterThread := Self;
-
- FStatusBar := TStatusBar.Create(FormMain);
- with FStatusBar do
- begin
- Parent := FormMain;
- SimplePanel := False;
- with Panels.Add do // panel for progress bar
- Width := 100;
- Panels.Add; // panel for progress text
- Panels.Add; // panel for status text
- end;
-
- FProgressBar := TProgressBar.Create(FormMain);
- with FProgressBar do
- begin
- Parent := FStatusBar;
- Align := alNone;
- Smooth := True;
- Style := pbstNormal;
- Min := 0;
- Width := FStatusBar.Panels[0].Width - 10;
- Anchors := [akTop, akLeft, akBottom];
- AnchorSideTop.Control := FStatusBar;
- AnchorSideTop.Side := asrTop;
- AnchorSideLeft.Control := FStatusBar;
- AnchorSideLeft.Side := asrTop;
- AnchorSideBottom.Control := FStatusBar;
- AnchorSideBottom.Side := asrBottom;
- BorderSpacing.Top := 2;
- BorderSpacing.Left := 5;
- BorderSpacing.Bottom := 2;
- end;
-
- FButtonCancel := TSpeedButton.Create(FormMain);
- with FButtonCancel do
- begin
- Parent := FStatusBar;
- Align := alNone;
- AutoSize := True;
- Caption := RS_ButtonCancel;
- ShowCaption := True;
- Flat := True;
- Anchors := [akTop, akRight, akBottom];
- AnchorSideTop.Control := FStatusBar;
- AnchorSideTop.Side := asrTop;
- AnchorSideRight.Control := FStatusBar;
- AnchorSideRight.Side := asrRight;
- AnchorSideBottom.Control := FStatusBar;
- AnchorSideBottom.Side := asrBottom;
- BorderSpacing.Top := 2;
- BorderSpacing.Right := 5;
- BorderSpacing.Bottom := 2;
- OnClick := @ButtonCancelClick;
- end;
end;
procedure TDBUpdaterThread.SyncFinal;
begin
DBUpdaterThread := nil;
- FHTTP.Sock.OnStatus := nil;
- FreeAndNil(FStatusBar);
- FreeAndNil(FProgressBar);
- FreeAndNil(FButtonCancel);
-end;
-
-procedure TDBUpdaterThread.SyncStartDownload;
-begin
- FCurrentSize := 0;
- FTotalSize := 0;
- FProgressBar.Max := 0;
- FProgressBar.Position := 0;
- FStatusBar.Panels[1].Text := '';
- FStatusBar.Panels[1].Width := 0;
- FStatusBar.Panels[2].Text := Format('[%d/%d] ' + RS_Downloading,
- [FCurrentId + 1, Items.Count,
- FCurrentName + DBDATA_EXT]);
-end;
-
-procedure TDBUpdaterThread.SyncUpdateProgress;
-var
- s: String;
-begin
- if FStatusBar = nil then
- Exit;
- if FProgressBar.Max <> FTotalSize then
- FProgressBar.Max := FTotalSize;
- if FProgressBar.Position <> FCurrentSize then
- FProgressBar.Position := FCurrentSize;
-
- s := FormatByteSize(FCurrentSize);
- if FTotalSize <> 0 then
- s += '/' + FormatByteSize(FTotalSize);
- FStatusBar.Panels[1].Width := FStatusBar.Canvas.TextWidth(s) + 10;
- FStatusBar.Panels[1].Text := s;
-end;
-
-procedure TDBUpdaterThread.SyncUpdateStatus;
-begin
- FStatusBar.Panels[2].Text := FStatusText;
end;
procedure TDBUpdaterThread.SyncUpdateHint;
begin
- FStatusBar.Hint := Trim(Items.Text);
+ StatusBar.Hint := Trim(Items.Text);
end;
procedure TDBUpdaterThread.SyncShowFailed;
@@ -240,17 +107,9 @@ procedure TDBUpdaterThread.SyncRemoveAttached;
dataProcess.RemoveFilter;
end;
-procedure TDBUpdaterThread.UpdateStatusText(const S: String);
-begin
- if FStatusText = S then
- Exit;
- FStatusText := S;
- Synchronize(@SyncUpdateStatus);
-end;
-
procedure TDBUpdaterThread.Execute;
var
- currentfilename, lurl: String;
+ currentfilename: String;
cont: Boolean;
used: Boolean;
begin
@@ -262,9 +121,9 @@ procedure TDBUpdaterThread.Execute;
Break;
try
FCurrentName := Items[FCurrentId];
- Synchronize(@SyncStartDownload);
- lurl := GetDBURL(FCurrentName);
- if FHTTP.GET(GetDBURL(FCurrentName)) and (FHTTP.ResultCode < 300) then
+ UpdateStatusText(Format('[%d/%d] ' +
+ RS_Downloading, [FCurrentId + 1, Items.Count, FCurrentName + DBDATA_EXT]));
+ if HTTP.GET(GetDBURL(FCurrentName)) and (HTTP.ResultCode < 300) then
begin
cont := True;
// save to data folder
@@ -274,7 +133,7 @@ procedure TDBUpdaterThread.Execute;
DeleteFile(currentfilename);
if not FileExists(currentfilename) then
begin
- FHTTP.Document.SaveToFile(currentfilename);
+ HTTP.Document.SaveToFile(currentfilename);
if not FileExists(currentfilename) then
begin
FFailedList.Add(Format(RS_FailedToSave, [FCurrentName]));
@@ -331,8 +190,8 @@ procedure TDBUpdaterThread.Execute;
end;
end
else
- FFailedList.Add(Format(RS_FailedDownload, [FCurrentName, FHTTP.ResultCode,
- FHTTP.ResultString]));
+ FFailedList.Add(Format(RS_FailedDownload, [FCurrentName, HTTP.ResultCode,
+ HTTP.ResultString]));
except
on E: Exception do
FFailedList.Add(E.Message);
@@ -343,13 +202,9 @@ procedure TDBUpdaterThread.Execute;
constructor TDBUpdaterThread.Create;
begin
- inherited Create(True);
- FreeOnTerminate := True;
+ inherited Create(True, FormMain, FormMain.IconList, 24);
FFailedList := TStringList.Create;
- FHTTP := THTTPSendThread.Create(Self);
- FHTTP.UserAgent := UserAgentCURL;
- FHTTP.Sock.OnStatus := @HTTPSockOnStatus;
- FHTTP.OnRedirected:=@HTTPRedirected;
+ HTTP.OnRedirected := @HTTPRedirected;
Items := TStringList.Create;
Synchronize(@SyncStart);
end;
@@ -359,7 +214,6 @@ destructor TDBUpdaterThread.Destroy;
if (not Terminated) and (FFailedList.Count <> 0) then
Synchronize(@SyncShowFailed);
Synchronize(@SyncFinal);
- FHTTP.Free;
FFailedList.Free;
FreeAndNil(Items);
inherited Destroy;
@@ -401,7 +255,6 @@ procedure TDBUpdaterThread.Add(const S: TStrings);
procedure TDBUpdaterThread.UpdateStatus;
begin
- SyncUpdateStatus;
SyncUpdateHint;
end;
diff --git a/baseunits/DownloadsDB.pas b/baseunits/DownloadsDB.pas
index ef35063a3..434b6400a 100644
--- a/baseunits/DownloadsDB.pas
+++ b/baseunits/DownloadsDB.pas
@@ -18,14 +18,29 @@ TDownloadsDB = class(TSQliteData)
procedure SetAutoCommitCount(AValue: Integer);
public
constructor Create(const AFilename: String);
- function Open: Boolean;
- function Add(var Adlid: Integer;
- const Aenabled: Boolean;
- const Aorder, Ataskstatus, Achapterptr, Anumberofpages, Acurrentpage: Integer;
- const Awebsite, Alink, Atitle, Astatus, Aprogress, Asaveto: String;
- const Adatetime: TDateTime;
- const Achapterslinks, Achaptersnames, Apagelinks, Apagecontainerlinks, Afilenames, Acustomfilenames,
- Achaptersstatus: String): Boolean;
+ procedure InternalAdd(
+ const Aenabled:Boolean;
+ const Aorder,Ataskstatus,Achapterptr,Anumberofpages,Acurrentpage:Integer;
+ const Awebsite,Alink,Atitle,Astatus,Aprogress,Asaveto:String;
+ const Adatetime:TDateTime;
+ const Achapterslinks,Achaptersnames,Apagelinks,Apagecontainerlinks,Afilenames,Acustomfilenames,Achaptersstatus:String); inline;
+ procedure InternalUpdate(
+ const Adlid:Integer;
+ const Aenabled:Boolean;
+ const Aorder,Ataskstatus,Achapterptr,Anumberofpages,Acurrentpage:Integer;
+ const Awebsite,Alink,Atitle,Astatus,Aprogress,Asaveto:String;
+ const Adatetime:TDateTime;
+ const Achapterslinks,Achaptersnames,Apagelinks,Apagecontainerlinks,Afilenames,Acustomfilenames,Achaptersstatus:String); inline;
+ procedure InternalUpdateOrderEnabled(const Adlid,AOrder:Integer;const Aenabled:Boolean);
+ procedure InternalUpdateOrder(const Adlid,AOrder:Integer);
+ procedure InternalUpdateEnabled(const Adlid:Integer;const Aenabled:Boolean);
+ function Add(
+ var Adlid:Integer;
+ const Aenabled:Boolean;
+ const Aorder,Ataskstatus,Achapterptr,Anumberofpages,Acurrentpage:Integer;
+ const Awebsite,Alink,Atitle,Astatus,Aprogress,Asaveto:String;
+ const Adatetime:TDateTime;
+ const Achapterslinks,Achaptersnames,Apagelinks,Apagecontainerlinks,Afilenames,Acustomfilenames,Achaptersstatus:String):Boolean;
procedure Delete(const ADlId: Integer);
procedure Commit; override;
procedure Close; override;
@@ -99,93 +114,116 @@ constructor TDownloadsDB.Create(const AFilename: String);
SelectParams := 'SELECT ' + FieldsParams + ' FROM '+QuotedStrD(TableName)+' ORDER BY "order"';
end;
-function TDownloadsDB.Open: Boolean;
+procedure TDownloadsDB.InternalAdd(
+ const Aenabled:Boolean;
+ const Aorder,Ataskstatus,Achapterptr,Anumberofpages,Acurrentpage:Integer;
+ const Awebsite,Alink,Atitle,Astatus,Aprogress,Asaveto:String;
+ const Adatetime:TDateTime;
+ const Achapterslinks,Achaptersnames,Apagelinks,Apagecontainerlinks,Afilenames,Acustomfilenames,Achaptersstatus:String);
begin
- Result := inherited Open(False, False);
- Table.SQL.Text := 'SELECT * FROM ' + QuotedStrD(TableName);
- Table.Open;
- if Table.Active then
- begin
- // convert table, replace failedchapterlink, failedchaptername with chaptersstatus
- if (Table.Fields.Count = 22) and (Table.Fields[20].FieldName = 'failedchapterlinks') then
- begin
- Table.Close;
- with Connection do
- begin
- ExecuteDirect('DROP TABLE IF EXISTS ' + QuotedStrD('temp' + TableName));
- ExecuteDirect('CREATE TABLE ' + QuotedStrD('temp' + TableName) + ' (' + CreateParams + ')');
- ExecuteDirect('INSERT INTO ' + QuotedStrD('temp' + TableName) + ' (' + FieldsParams + ') SELECT ' +
- '"dlid","enabled","order","taskstatus","chapterptr","numberofpages","currentpage","website","link","title","status","progress","saveto","datetime","chapterslinks"||"failedchapterlinks","chaptersnames"||"failedchapternames","pagelinks","pagecontainerlinks","filenames","customfilenames",""'
- + ' FROM "' + TableName + '"');
- ExecuteDirect('DROP TABLE ' + QuotedStrD(TableName));
- ExecuteDirect('ALTER TABLE ' + QuotedStrD('temp' + TableName) + ' RENAME TO ' + QuotedStrD(TableName));
- Transaction.Commit;
- end;
- end;
- end;
- CloseTable;
+ Connection.ExecuteDirect('INSERT INTO "downloads" ("enabled","order","taskstatus","chapterptr","numberofpages","currentpage","website","link","title","status","progress","saveto","datetime","chapterslinks","chaptersnames","pagelinks","pagecontainerlinks","filenames","customfilenames","chaptersstatus")' +
+ ' VALUES (' +
+ QuotedStr(Aenabled) + ', ' +
+ QuotedStr(Aorder) + ', ' +
+ QuotedStr(Ataskstatus) + ', ' +
+ QuotedStr(Achapterptr) + ', ' +
+ QuotedStr(Anumberofpages) + ', ' +
+ QuotedStr(Acurrentpage) + ', ' +
+ QuotedStr(Awebsite) + ', ' +
+ QuotedStr(Alink) + ', ' +
+ QuotedStr(Atitle) + ', ' +
+ QuotedStr(Astatus) + ', ' +
+ QuotedStr(Aprogress) + ', ' +
+ QuotedStr(Asaveto) + ', ' +
+ QuotedStr(Adatetime) + ', ' +
+ QuotedStr(Achapterslinks) + ', ' +
+ QuotedStr(Achaptersnames) + ', ' +
+ QuotedStr(Apagelinks) + ', ' +
+ QuotedStr(Apagecontainerlinks) + ', ' +
+ QuotedStr(Afilenames) + ', ' +
+ QuotedStr(Acustomfilenames) + ', ' +
+ QuotedStr(Achaptersstatus) +
+ ')');
+end;
+
+procedure TDownloadsDB.InternalUpdate(
+ const Adlid:Integer;
+ const Aenabled:Boolean;
+ const Aorder,Ataskstatus,Achapterptr,Anumberofpages,Acurrentpage:Integer;
+ const Awebsite,Alink,Atitle,Astatus,Aprogress,Asaveto:String;
+ const Adatetime:TDateTime;
+ const Achapterslinks,Achaptersnames,Apagelinks,Apagecontainerlinks,Afilenames,Acustomfilenames,Achaptersstatus:String);
+begin
+ Connection.ExecuteDirect('UPDATE "downloads" SET ' +
+ '"enabled"=' + QuotedStr(Aenabled) + ', ' +
+ '"order"=' + QuotedStr(Aorder) + ', ' +
+ '"taskstatus"=' + QuotedStr(Ataskstatus) + ', ' +
+ '"chapterptr"=' + QuotedStr(Achapterptr) + ', ' +
+ '"numberofpages"=' + QuotedStr(Anumberofpages) + ', ' +
+ '"currentpage"=' + QuotedStr(Acurrentpage) + ', ' +
+ '"website"=' + QuotedStr(Awebsite) + ', ' +
+ '"link"=' + QuotedStr(Alink) + ', ' +
+ '"title"=' + QuotedStr(Atitle) + ', ' +
+ '"status"=' + QuotedStr(Astatus) + ', ' +
+ '"progress"=' + QuotedStr(Aprogress) + ', ' +
+ '"saveto"=' + QuotedStr(Asaveto) + ', ' +
+ '"datetime"=' + QuotedStr(Adatetime) + ', ' +
+ '"chapterslinks"=' + QuotedStr(Achapterslinks) + ', ' +
+ '"chaptersnames"=' + QuotedStr(Achaptersnames) + ', ' +
+ '"pagelinks"=' + QuotedStr(Apagelinks) + ', ' +
+ '"pagecontainerlinks"=' + QuotedStr(Apagecontainerlinks) + ', ' +
+ '"filenames"=' + QuotedStr(Afilenames) + ', ' +
+ '"customfilenames"=' + QuotedStr(Acustomfilenames) + ', ' +
+ '"chaptersstatus"=' + QuotedStr(Achaptersstatus) +
+ ' WHERE "dlid"=' + QuotedStr(Adlid));
+end;
+
+procedure TDownloadsDB.InternalUpdateOrderEnabled(const Adlid, AOrder: Integer;
+ const Aenabled: Boolean);
+begin
+ Connection.ExecuteDirect('UPDATE "downloads" SET ' +
+ '"enabled"=' + QuotedStr(Aenabled) + ', ' +
+ '"order"=' + QuotedStr(Aorder) +
+ ' WHERE "dlid"=' + QuotedStr(Adlid));
+end;
+
+procedure TDownloadsDB.InternalUpdateOrder(const Adlid, AOrder: Integer);
+begin
+ Connection.ExecuteDirect('UPDATE "downloads" SET ' +
+ '"order"='+QuotedStr(AOrder)+
+ ' WHERE "dlid"=' + QuotedStr(Adlid));
+end;
+
+procedure TDownloadsDB.InternalUpdateEnabled(const Adlid: Integer;
+ const Aenabled: Boolean);
+begin
+ Connection.ExecuteDirect('UPDATE "downloads" SET ' +
+ '"enabled"=' +QuotedStr(Aenabled)+
+ ' WHERE "dlid"=' + QuotedStr(Adlid));
end;
-function TDownloadsDB.Add(var Adlid: Integer; const Aenabled: Boolean;
- const Aorder, Ataskstatus, Achapterptr, Anumberofpages,
- Acurrentpage: Integer; const Awebsite, Alink, Atitle, Astatus, Aprogress,
- Asaveto: String; const Adatetime: TDateTime; const Achapterslinks,
- Achaptersnames, Apagelinks, Apagecontainerlinks, Afilenames,
- Acustomfilenames, Achaptersstatus: String): Boolean;
+function TDownloadsDB.Add(
+ var Adlid:Integer;
+ const Aenabled:Boolean;
+ const Aorder,Ataskstatus,Achapterptr,Anumberofpages,Acurrentpage:Integer;
+ const Awebsite,Alink,Atitle,Astatus,Aprogress,Asaveto:String;
+ const Adatetime:TDateTime;
+ const Achapterslinks,Achaptersnames,Apagelinks,Apagecontainerlinks,Afilenames,Acustomfilenames,Achaptersstatus:String):Boolean;
begin
Result := False;
if not Connection.Connected then Exit;
try
if Adlid = -1 then
begin
- Connection.ExecuteDirect('INSERT INTO "downloads" ("enabled","order","taskstatus","chapterptr","numberofpages","currentpage","website","link","title","status","progress","saveto","datetime","chapterslinks","chaptersnames","pagelinks","pagecontainerlinks","filenames","customfilenames","chaptersstatus")' +
- ' VALUES (' +
- QuotedStr(Aenabled) + ', ' +
- QuotedStr(Aorder) + ', ' +
- QuotedStr(Ataskstatus) + ',' +
- QuotedStr(Achapterptr) + ',' +
- QuotedStr(Anumberofpages) + ',' +
- QuotedStr(Acurrentpage) + ',' +
- QuotedStr(Awebsite) + ', ' +
- QuotedStr(Alink) + ', ' +
- QuotedStr(Atitle) + ', ' +
- QuotedStr(Astatus) + ', ' +
- QuotedStr(Aprogress) + ', ' +
- QuotedStr(Asaveto) + ', ' +
- QuotedStr(Adatetime) + ', ' +
- QuotedStr(Achapterslinks) + ', ' +
- QuotedStr(Achaptersnames) + ', ' +
- QuotedStr(Apagelinks) + ', ' +
- QuotedStr(Apagecontainerlinks) + ', ' +
- QuotedStr(Afilenames) + ', ' +
- QuotedStr(Acustomfilenames) + ', ' +
- QuotedStr(Achaptersstatus) +
- ')');
+ InternalAdd(Aenabled,Aorder,Ataskstatus,Achapterptr,Anumberofpages,Acurrentpage,Awebsite,
+ Alink,Atitle,Astatus,Aprogress,Asaveto,Adatetime,Achapterslinks,Achaptersnames,Apagelinks,
+ Apagecontainerlinks,Afilenames,Acustomfilenames,Achaptersstatus);
Adlid := Connection.GetInsertID;
end
else
- Connection.ExecuteDirect('UPDATE "downloads" SET ' +
- '"enabled"=' + QuotedStr(Aenabled) + ', ' +
- '"order"=' + QuotedStr(Aorder) + ', ' +
- '"taskstatus"=' + QuotedStr(Ataskstatus) + ',' +
- '"chapterptr"=' + QuotedStr(Achapterptr) + ',' +
- '"numberofpages"=' + QuotedStr(Anumberofpages) + ',' +
- '"currentpage"=' + QuotedStr(Acurrentpage) + ',' +
- '"website"=' + QuotedStr(Awebsite) + ', ' +
- '"link"=' + QuotedStr(Alink) + ', ' +
- '"title"=' + QuotedStr(Atitle) + ', ' +
- '"status"=' + QuotedStr(Astatus) + ', ' +
- '"progress"=' + QuotedStr(Aprogress) + ', ' +
- '"saveto"=' + QuotedStr(Asaveto) + ', ' +
- '"datetime"=' + QuotedStr(Adatetime) + ', ' +
- '"chapterslinks"=' + QuotedStr(Achapterslinks) + ', ' +
- '"chaptersnames"=' + QuotedStr(Achaptersnames) + ', ' +
- '"pagelinks"=' + QuotedStr(Apagelinks) + ', ' +
- '"pagecontainerlinks"=' + QuotedStr(Apagecontainerlinks) + ', ' +
- '"filenames"=' + QuotedStr(Afilenames) + ', ' +
- '"customfilenames"=' + QuotedStr(Acustomfilenames) + ', ' +
- '"chaptersstatus"=' + QuotedStr(Achaptersstatus) +
- ' WHERE "dlid"=' + QuotedStr(Adlid));
+ InternalUpdate(Adlid,Aenabled,Aorder,Ataskstatus,Achapterptr,Anumberofpages,Acurrentpage,Awebsite,
+ Alink,Atitle,Astatus,Aprogress,Asaveto,Adatetime,Achapterslinks,Achaptersnames,Apagelinks,
+ Apagecontainerlinks,Afilenames,Acustomfilenames,Achaptersstatus);
Inc(FCommitCount);
if FCommitCount >= FAutoCommitCount then
Commit;
diff --git a/baseunits/Duktape.Api.pas b/baseunits/Duktape.Api.pas
index 67a1cc3a9..2bc33f37b 100644
--- a/baseunits/Duktape.Api.pas
+++ b/baseunits/Duktape.Api.pas
@@ -1,36 +1,3 @@
-{
-
-https://github.com/grijjy/DelphiDuktape
-
-DelphiDuktape is licensed under the Simplified BSD License.
-
--------------------------------------------------------------------------------
-
-Copyright (c) 2018 by Grijjy, Inc.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-}
-
unit Duktape.Api;
interface
@@ -48,9 +15,12 @@ interface
{$ELSEIF Defined(IOS)}
LIB_DUKTAPE = 'libduktape_ios.a';
PREFIX = '';
- {$ELSEIF Defined(MACOS)}
+ {$ELSEIF Defined(MACOS32)}
LIB_DUKTAPE = 'libduktape_osx32.dylib';
PREFIX = '_';
+ {$ELSEIF Defined(MACOS64)}
+ LIB_DUKTAPE = 'libduktape_osx64.a';
+ PREFIX = '';
{$ELSEIF Defined(LINUX)}
LIB_DUKTAPE = 'libduktape_linux64.so';
PREFIX = '';
@@ -61,7 +31,7 @@ interface
(*
* duk_config.h configuration header generated by genconfig.py.
*
- * Git commit a459cf3c9bd1779fc01b435d69302b742675a08f (v2.2.0).
+ * Git commit d7fdb67f18561a50e06bafd196c6b423af9ad6fe (v2.3.0).
* Git branch: master
*
* Supported platforms:
@@ -309,7 +279,7 @@ interface
*
* (http://opensource.org/licenses/MIT)
*
- * Copyright (c) 2013-2017 by Duktape authors (see AUTHORS.rst)
+ * Copyright (c) 2013-2018 by Duktape authors (see AUTHORS.rst)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -379,6 +349,14 @@ interface
* * Steven Don (https://github.com/shdon)
* * Simon Stone (https://github.com/sstone1)
* * \J. McC. (https://github.com/jmhmccr)
+ * * Jakub Nowakowski (https://github.com/jimvonmoon)
+ * * Tommy Nguyen (https://github.com/tn0502)
+ * * Fabrice Fontaine (https://github.com/ffontaine)
+ * * Christopher Hiller (https://github.com/boneskull)
+ * * Gonzalo Diethelm (https://github.com/gonzus)
+ * * Michal Kasperek (https://github.com/michalkas)
+ * * Andrew Janke (https://github.com/apjanke)
+ * * Steve Fan (https://github.com/stevefan1999)
*
* Other contributions
* ===================
@@ -417,6 +395,8 @@ interface
* * https://github.com/chris-y
* * Laurent Zubiaur (https://github.com/lzubiaur)
* * Neil Kolban (https://github.com/nkolban)
+ * * Wilhelm Wanecek (https://github.com/wanecek)
+ * * Andrew Janke (https://github.com/apjanke)
*
* If you are accidentally missing from this list, send me an e-mail
* (``sami.vaarala@iki.fi``) and I'll fix the omission.
@@ -432,21 +412,21 @@ interface
(* Duktape version, (major * 10000) + (minor * 100) + patch. Allows C code
* to #if (DUK_VERSION >= NNN) against Duktape API version. The same value
- * is also available to Ecmascript code in Duktape.version. Unofficial
+ * is also available to ECMAscript code in Duktape.version. Unofficial
* development snapshots have 99 for patch level (e.g. 0.10.99 would be a
* development version after 0.10.0 but before the next official release).
*)
const
- DUK_VERSION = 20200;
+ DUK_VERSION = 20300;
(* Git commit, describe, and branch for Duktape build. Useful for
* non-official snapshot builds so that application code can easily log
- * which Duktape snapshot was used. Not available in the Ecmascript
+ * which Duktape snapshot was used. Not available in the ECMAscript
* environment.
*)
const
- DUK_GIT_COMMIT = 'a459cf3c9bd1779fc01b435d69302b742675a08f';
- DUK_GIT_DESCRIBE = 'v2.2.0';
+ DUK_GIT_COMMIT = 'd7fdb67f18561a50e06bafd196c6b423af9ad6fe';
+ DUK_GIT_DESCRIBE = 'v2.3.0';
DUK_GIT_BRANCH = 'master';
(*
@@ -554,12 +534,12 @@ TDukTimeComponents = record
const
DUK_TYPE_MIN = 0;
DUK_TYPE_NONE = 0; (* no value, e.g. invalid index *)
- DUK_TYPE_UNDEFINED = 1; (* Ecmascript undefined *)
- DUK_TYPE_NULL = 2; (* Ecmascript null *)
- DUK_TYPE_BOOLEAN = 3; (* Ecmascript boolean: 0 or 1 *)
- DUK_TYPE_NUMBER = 4; (* Ecmascript number: double *)
- DUK_TYPE_STRING = 5; (* Ecmascript string: CESU-8 / extended UTF-8 encoded *)
- DUK_TYPE_OBJECT = 6; (* Ecmascript object: includes objects, arrays, functions, threads *)
+ DUK_TYPE_UNDEFINED = 1; (* ECMAScript undefined *)
+ DUK_TYPE_NULL = 2; (* ECMAScript null *)
+ DUK_TYPE_BOOLEAN = 3; (* ECMAScript boolean: 0 or 1 *)
+ DUK_TYPE_NUMBER = 4; (* ECMAScript number: double *)
+ DUK_TYPE_STRING = 5; (* ECMAScript string: CESU-8 / extended UTF-8 encoded *)
+ DUK_TYPE_OBJECT = 6; (* ECMAScript object: includes objects, arrays, functions, threads *)
DUK_TYPE_BUFFER = 7; (* fixed or dynamic, garbage collected byte buffer *)
DUK_TYPE_POINTER = 8; (* raw void pointer *)
DUK_TYPE_LIGHTFUNC = 9; (* lightweight function pointer *)
@@ -839,7 +819,10 @@ function duk_push_lstring(ctx: PDukContext; const str: MarshaledAString; len: TD
procedure duk_push_pointer(ctx: PDukContext; p: Pointer); cdecl external LIB_DUKTAPE name PREFIX + 'duk_push_pointer';
function duk_push_sprintf(ctx: PDukContext; const fmt: MarshaledAString): MarshaledAString; cdecl; varargs external LIB_DUKTAPE name PREFIX + 'duk_push_sprintf';
+function duk_push_literal(ctx: PDukContext; const str: MarshaledAString): MarshaledAString; cdecl external LIB_DUKTAPE name PREFIX + 'duk_push_string';
+
procedure duk_push_this(ctx: PDukContext); cdecl external LIB_DUKTAPE name PREFIX + 'duk_push_this';
+procedure duk_push_new_target(ctx: PDukContext); cdecl external LIB_DUKTAPE name PREFIX + 'duk_push_new_target';
procedure duk_push_current_function(ctx: PDukContext); cdecl external LIB_DUKTAPE name PREFIX + 'duk_push_current_function';
procedure duk_push_current_thread(ctx: PDukContext); cdecl external LIB_DUKTAPE name PREFIX + 'duk_push_current_thread';
procedure duk_push_global_object(ctx: PDukContext); cdecl external LIB_DUKTAPE name PREFIX + 'duk_push_global_object';
@@ -1110,28 +1093,34 @@ procedure duk_config_buffer(ctx: PDukContext; idx: TDukIdx; ptr: Pointer; len: T
(*
* Property access
*
- * The basic function assumes key is on stack. The _string variant takes
- * a C string as a property name, while the _index variant takes an array
- * index as a property name (e.g. 123 is equivalent to the key "123").
+ * The basic function assumes key is on stack. The _(l)string variant takes
+ * a C string as a property name; the _literal variant takes a C literal.
+ * The _index variant takes an array index as a property name (e.g. 123 is
+ * equivalent to the key "123"). The _heapptr variant takes a raw, borrowed
+ * heap pointer.
*)
function duk_get_prop(ctx: PDukContext; obj_idx: TDukIdx): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_get_prop';
function duk_get_prop_string(ctx: PDukContext; obj_idx: TDukIdx; const key: MarshaledAString): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_get_prop_string';
+function duk_get_prop_literal(ctx: PDukContext; obj_idx: TDukIdx; const key: MarshaledAString): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_get_prop_string';
function duk_get_prop_lstring(ctx: PDukContext; obj_idx: TDukIdx; const key: MarshaledAString; key_len: TDukSize): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_get_prop_lstring';
function duk_get_prop_index(ctx: PDukContext; obj_idx: TDukIdx; arr_idx: TDukUArrIdx): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_get_prop_index';
function duk_get_prop_heapptr(ctx: PDukContext; obj_idx: TDukIdx; ptr: Pointer): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_get_prop_heapptr';
function duk_put_prop(ctx: PDukContext; obj_idx: TDukIdx): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_put_prop';
function duk_put_prop_string(ctx: PDukContext; obj_idx: TDukIdx; const key: MarshaledAString): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_put_prop_string';
+function duk_put_prop_literal(ctx: PDukContext; obj_idx: TDukIdx; const key: MarshaledAString): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_put_prop_string';
function duk_put_prop_lstring(ctx: PDukContext; obj_idx: TDukIdx; const key: MarshaledAString; key_len: TDukSize): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_put_prop_lstring';
function duk_put_prop_index(ctx: PDukContext; obj_idx: TDukIdx; arr_idx: TDukUArrIdx): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_put_prop_index';
function duk_put_prop_heapptr(ctx: PDukContext; obj_idx: TDukIdx; ptr: Pointer): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_put_prop_heapptr';
function duk_del_prop(ctx: PDukContext; obj_idx: TDukIdx): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_del_prop';
function duk_del_prop_string(ctx: PDukContext; obj_idx: TDukIdx; const key: MarshaledAString): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_del_prop_string';
+function duk_del_prop_literal(ctx: PDukContext; obj_idx: TDukIdx; const key: MarshaledAString): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_del_prop_string';
function duk_del_prop_lstring(ctx: PDukContext; obj_idx: TDukIdx; const key: MarshaledAString; key_len: TDukSize): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_del_prop_lstring';
function duk_del_prop_index(ctx: PDukContext; obj_idx: TDukIdx; arr_idx: TDukUArrIdx): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_del_prop_index';
function duk_del_prop_heapptr(ctx: PDukContext; obj_idx: TDukIdx; ptr: Pointer): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_del_prop_heapptr';
function duk_has_prop(ctx: PDukContext; obj_idx: TDukIdx): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_has_prop';
function duk_has_prop_string(ctx: PDukContext; obj_idx: TDukIdx; const key: MarshaledAString): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_has_prop_string';
+function duk_has_prop_literal(ctx: PDukContext; obj_idx: TDukIdx; const key: MarshaledAString): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_has_prop_string';
function duk_has_prop_lstring(ctx: PDukContext; obj_idx: TDukIdx; const key: MarshaledAString; key_len: TDukSize): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_has_prop_lstring';
function duk_has_prop_index(ctx: PDukContext; obj_idx: TDukIdx; arr_idx: TDukUArrIdx): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_has_prop_index';
function duk_has_prop_heapptr(ctx: PDukContext; obj_idx: TDukIdx; ptr: Pointer): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_has_prop_heapptr';
@@ -1140,9 +1129,13 @@ procedure duk_get_prop_desc(ctx: PDukContext; obj_idx: TDukIdx; flags: TDukUInt)
procedure duk_def_prop(ctx: PDukContext; obj_idx: TDukIdx; flags: TDukUInt); cdecl external LIB_DUKTAPE name PREFIX + 'duk_def_prop';
function duk_get_global_string(ctx: PDukContext; const key: MarshaledAString): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_get_global_string';
+function duk_get_global_literal(ctx: PDukContext; const key: MarshaledAString): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_get_global_string';
+function duk_get_global_heapptr(ctx: PDukContext; const ptr: Pointer): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_get_global_heapptr';
function duk_get_global_lstring(ctx: PDukContext; const key: MarshaledAString; key_len: TDukSize): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_get_global_lstring';
function duk_put_global_string(ctx: PDukContext; const key: MarshaledAString): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_put_global_string';
+function duk_put_global_literal(ctx: PDukContext; const key: MarshaledAString): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_put_global_string';
function duk_put_global_lstring(ctx: PDukContext; const key: MarshaledAString; key_len: TDukSize): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_put_global_lstring';
+function duk_put_global_heapptr(ctx: PDukContext; const ptr: Pointer): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_put_global_heapptr';
(*
* Inspection
@@ -1209,7 +1202,7 @@ procedure duk_trim(ctx: PDukContext; idx: TDukIdx); cdecl external LIB_DUKTAPE n
function duk_char_code_at(ctx: PDukContext; idx: TDukIdx; char_offset: TDukSize): TDukCodepoint; cdecl external LIB_DUKTAPE name PREFIX + 'duk_char_code_at';
(*
- * Ecmascript operators
+ * ECMAScript operators
*)
function duk_equals(ctx: PDukContext; idx1: TDukIdx; idx2: TDukIdx): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_equals';
@@ -1217,6 +1210,11 @@ function duk_strict_equals(ctx: PDukContext; idx1: TDukIdx; idx2: TDukIdx): TDuk
function duk_samevalue(ctx: PDukContext; idx1: TDukIdx; idx2: TDukIdx): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_samevalue';
function duk_instanceof(ctx: PDukContext; idx1: TDukIdx; idx2: TDukIdx): TDukBool; cdecl external LIB_DUKTAPE name PREFIX + 'duk_instanceof';
+(*
+ * Random
+ *)
+function duk_random(ctx: PDukContext): TDukDouble; cdecl external LIB_DUKTAPE name PREFIX + 'duk_random';
+
(*
* Function (method) calls
*)
@@ -1319,7 +1317,7 @@ function duk_components_to_time(ctx: PDukContext; comp: PDukTimeComponents): TDu
DUK_DATE_MSEC_HOUR = 60 * 60 * 1000;
DUK_DATE_MSEC_DAY = 24 * 60 * 60 * 1000;
-(* Ecmascript date range is 100 million days from Epoch:
+(* ECMAScript date range is 100 million days from Epoch:
* > 100e6 * 24 * 60 * 60 * 1000 // 100M days in millisecs
* 8640000000000000
* (= 8.64e15)
@@ -1328,7 +1326,7 @@ function duk_components_to_time(ctx: PDukContext; comp: PDukTimeComponents): TDu
DUK_DATE_MSEC_100M_DAYS = 8.64e15;
DUK_DATE_MSEC_100M_DAYS_LEEWAY = 8.64e15 + 24 * 3600e3;
-(* Ecmascript year range:
+(* ECMAScript year range:
* > new Date(100e6 * 24 * 3600e3).toISOString()
* '+275760-09-13T00:00:00.000Z'
* > new Date(-100e6 * 24 * 3600e3).toISOString()
@@ -1339,7 +1337,7 @@ function duk_components_to_time(ctx: PDukContext; comp: PDukTimeComponents): TDu
DUK_DATE_MAX_ECMA_YEAR = 275760;
(* Part indices for internal breakdowns. Part order from DUK_DATE_IDX_YEAR
- * to DUK_DATE_IDX_MILLISECOND matches argument ordering of Ecmascript API
+ * to DUK_DATE_IDX_MILLISECOND matches argument ordering of ECMAScript API
* calls (like Date constructor call). Some functions in duk_bi_date.c
* depend on the specific ordering, so change with care. 16 bits are not
* enough for all parts (year, specifically).
diff --git a/baseunits/FMDOptions.pas b/baseunits/FMDOptions.pas
index 17df664b4..11413e13a 100644
--- a/baseunits/FMDOptions.pas
+++ b/baseunits/FMDOptions.pas
@@ -25,7 +25,6 @@ TIniFileRun = class(IniFiles.TMemIniFile)
TFMDDo = (DO_NOTHING, DO_EXIT, DO_POWEROFF, DO_HIBERNATE, DO_UPDATE);
const
- FMD_REVISION = '$WCREV$';
FMD_INSTANCE = '_FreeMangaDownloaderInstance_';
FMD_TARGETOS = {$i %FPCTARGETOS%};
FMD_TARGETCPU = {$i %FPCTARGETCPU%};
@@ -46,7 +45,6 @@ TIniFileRun = class(IniFiles.TMemIniFile)
ZIP_EXE = '7za.exe';
RUN_EXE = '.run';
-
SOCKHEARTBEATRATE = 500;
{$IFDEF WINDOWS}
{$IFDEF WIN32}
@@ -62,6 +60,11 @@ TIniFileRun = class(IniFiles.TMemIniFile)
MAX_CONNECTIONPERHOSTLIMIT = 32;
{$ENDIF}
+ BACKUP_FILE_PREFIX = 'fmdbackup_';
+ BACKUP_FILE_EXT = '7z';
+
+{$i revision.inc}
+
var
FMD_VERSION_NUMBER: TProgramVersion;
FMD_VERSION_STRING,
@@ -95,7 +98,10 @@ TIniFileRun = class(IniFiles.TMemIniFile)
EXTRAS_FOLDER,
MANGAFOXTEMPLATE_FOLDER,
LUA_WEBSITEMODULE_FOLDER,
- LUA_WEBSITEMODULE_FILE: String;
+ LUA_REPO_FOLDER,
+ LUA_REPO_FILE,
+ LUA_REPO_WORK_FILE,
+ BACKUP_FOLDER: String;
// ini files
revisionfile,
@@ -103,13 +109,13 @@ TIniFileRun = class(IniFiles.TMemIniFile)
configfile: TIniFileRun;
// base url, should be in base.ini
- DEFAULT_SELECTED_WEBSITES: String = 'MangaFox,MangaHere,MangaInn,MangaReader';
+ DEFAULT_SELECTED_WEBSITES: String = 'MangaDex,MangaHere,MangaInn,MangaReader';
DB_URL: String = 'https://sourceforge.net/projects/newfmd/files/data/.7z/download';
- UPDATE_URL: String = 'https://raw.githubusercontent.com/riderkick/FMD/master/update';
- CHANGELOG_URL: String = 'https://raw.githubusercontent.com/riderkick/FMD/master/changelog.txt';
+ UPDATE_URL: String = 'https://raw.githubusercontent.com/fmd-project-team/FMD/master/update';
+ CHANGELOG_URL: String = 'https://raw.githubusercontent.com/fmd-project-team/FMD/master/changelog.txt';
UPDATE_PACKAGE_NAME: String = 'updatepackage.7z';
- MODULES_URL: String = 'https://api.github.com/repos/riderkick/FMD/contents/lua/modules';
- MODULES_URL2: String = 'https://github.com/riderkick/FMD/file-list/master/lua/modules';
+ MODULES_URL: String = 'https://api.github.com/repos/fmd-project-team/FMD/contents/lua/modules';
+ MODULES_URL2: String = 'https://github.com/fmd-project-team/FMD/file-list/master/lua/modules';
currentWebsite: String;
@@ -119,6 +125,7 @@ TIniFileRun = class(IniFiles.TMemIniFile)
// general
OptionLetFMDDo: TFMDDo = DO_NOTHING;
OptionDeleteCompletedTasksOnClose: Boolean = False;
+ OptionSortDownloadsWhenAddingNewDownloadTasks: Boolean = False;
// saveto
OptionChangeUnicodeCharacter: Boolean = False;
@@ -148,10 +155,17 @@ TIniFileRun = class(IniFiles.TMemIniFile)
OptionConnectionTimeout: Integer = 30;
OptionRetryFailedTask: Integer = 1;
OptionAlwaysStartTaskFromFailedChapters: Boolean = True;
+ OptionEnableCloudflareBypass: Boolean = True;
+ OptionAutomaticallyDisableCloudflareBypass: Boolean = False;
// view
OptionEnableLoadCover: Boolean = False;
OptionShowBalloonHint: Boolean = True;
+ OptionShowFavoritesTabOnNewManga: Boolean = False;
+ OptionShowDownloadsTabOnNewTasks: Boolean = True;
+
+ // favorites (context menu settings)
+ OptionDefaultAction: Integer = 0;
// updates
OptionAutoCheckLatestVersion: Boolean = True;
@@ -183,6 +197,7 @@ TIniFileRun = class(IniFiles.TMemIniFile)
CL_BSOdd: TColor = clBtnFace;
CL_BSEven: TColor = clWindow;
CL_BSSortedColumn: TColor = $F8E6D6;
+ CL_BSEnabledWebsiteSettings: TColor = clYellow;
//mangalist color
CL_MNNewManga: TColor = $FDC594;
@@ -207,7 +222,7 @@ procedure DoRestartFMD;
implementation
-uses FMDVars, UTF8Process;
+uses FMDVars, process, UTF8Process;
{ TIniFileRun }
@@ -237,10 +252,12 @@ destructor TIniFileRun.Destroy;
procedure TIniFileRun.UpdateFile;
begin
if CacheUpdates and (Dirty = False) then Exit;
- inherited UpdateFile;
+ EnterCriticalSection(FCSLock);
try
+ inherited UpdateFile;
CopyFile(FileName, FRealFileName, [cffOverwriteFile, cffPreserveTime, cffCreateDestDirectory]);
- except
+ finally
+ LeaveCriticalSection(FCSLock);
end;
end;
@@ -295,11 +312,12 @@ procedure SetFMDdirectory(const ADir: String);
README_FILE := FMD_DIRECTORY + 'readme.rtf';
EXTRAS_FOLDER := FMD_DIRECTORY + 'extras' + PathDelim;
MANGAFOXTEMPLATE_FOLDER := EXTRAS_FOLDER + 'mangafoxtemplate' + PathDelim;
- DEFAULT_LOG_FILE := FMD_DIRECTORY + FMD_EXENAME + '.log';
+ DEFAULT_LOG_FILE := FMD_EXENAME + '.log';
CURRENT_UPDATER_EXE := FMD_DIRECTORY + UPDATER_EXE;
OLD_CURRENT_UPDATER_EXE := FMD_DIRECTORY + OLD_UPDATER_EXE;
CURRENT_ZIP_EXE := FMD_DIRECTORY + ZIP_EXE;
+ BACKUP_FOLDER := FMD_DIRECTORY + 'backup' + PathDelim;
ReadBaseFile;
end;
@@ -314,7 +332,8 @@ procedure SetAppDataDirectory(const ADir: String);
CONFIG_FILE := CONFIG_FOLDER + 'config.ini';
ACCOUNTS_FILE := CONFIG_FOLDER + 'accounts.db';
MODULES_FILE := CONFIG_FOLDER + 'modules.json';
- LUA_WEBSITEMODULE_FILE := CONFIG_FOLDER + 'luamodules.json';
+ LUA_REPO_FILE := CONFIG_FOLDER + 'lua.json';
+ LUA_REPO_WORK_FILE := CONFIG_FOLDER + 'lua_repo.json';
DATA_FOLDER := APPDATA_DIRECTORY + 'data' + PathDelim;
@@ -327,6 +346,7 @@ procedure SetAppDataDirectory(const ADir: String);
FAVORITESDB_FILE := WORK_FOLDER + 'favorites.db';
LUA_WEBSITEMODULE_FOLDER := FMD_DIRECTORY + 'lua' + PathDelim + 'modules' + PathDelim;
+ LUA_REPO_FOLDER := FMD_DIRECTORY + 'lua' + PathDelim;
SetIniFiles;
end;
@@ -340,15 +360,18 @@ procedure RestartFMD;
procedure DoRestartFMD;
var
p: TProcessUTF8;
- i: Integer;
begin
p := TProcessUTF8.Create(nil);
try
p.InheritHandles := False;
- p.CurrentDirectory := ExtractFilePath(Application.ExeName);
+ p.CurrentDirectory := FMD_DIRECTORY;
p.Executable := Application.ExeName;
- for i := 1 to ParamCount do
- p.Parameters.Add(ParamStrUTF8(i));
+ p.Options := [];
+ p.InheritHandles := False;
+ p.Parameters.AddStrings(AppParams);
+ {$ifdef windows}
+ p.Parameters.Add('--dorestart-handle=' + IntToStr(Integer(Application.Handle)));
+ {$ifend}
p.Execute;
finally
p.Free;
diff --git a/baseunits/FMDVars.pas b/baseunits/FMDVars.pas
index adc93738b..c5c089de1 100644
--- a/baseunits/FMDVars.pas
+++ b/baseunits/FMDVars.pas
@@ -7,7 +7,7 @@ interface
uses
frmMain, uDownloadsManager, uFavoritesManager, uUpdateThread, DBDataProcess,
uSilentThread, uBaseUnit, uGetMangaInfosThread, CheckUpdate,
- FMDOptions, DBUpdater, SelfUpdater, FileChannel, simpleipc;
+ FMDOptions, DBUpdater, SelfUpdater, Classes, FileChannel, simpleipc;
var
FormMain: TMainForm;
@@ -20,6 +20,7 @@ interface
//Instance
FMDInstance: TSimpleIPCServer;
+ AppParams:TStringList;
// update fmd through main thread
DoAfterFMD: TFMDDo;
@@ -28,10 +29,6 @@ interface
// file logger
FileLogger: TFileChannel;
- // status in status bar update
- ulTotalPtr,
- ulWorkPtr: Integer;
-
// download manager
DLManager: TDownloadManager;
@@ -62,5 +59,12 @@ interface
implementation
+initialization
+ AppParams:=TStringList.Create;
+ AppParams.NameValueSeparator:='=';
+
+finalization
+ AppParams.Free;
+
end.
diff --git a/baseunits/FavoritesDB.pas b/baseunits/FavoritesDB.pas
index c46555ea5..163b57d01 100644
--- a/baseunits/FavoritesDB.pas
+++ b/baseunits/FavoritesDB.pas
@@ -13,20 +13,19 @@ interface
TFavoritesDB = class(TSQliteData)
private
- FNFEnabled: Boolean;
FCommitCount: Integer;
FAutoCommitCount: Integer;
procedure SetAutoCommitCount(AValue: Integer);
- protected
- function ConvertNewTableIF: Boolean; override;
public
constructor Create(const AFilename: String);
- function Add(const AOrder: Integer;
- const AEnabled: Boolean;
- const AWebsite, ALink, ATitle, ACurrentChapter, ADownloadedChapterList, ASaveTo: String): Boolean;
+ procedure InternalUpdate(const AOrder:Integer;const AEnabled:Boolean;
+ const AWebsite,ALink,ATitle,ACurrentChapter,ADownloadedChapterList,ASaveTo: String); inline;
+ procedure InternalAdd(const AOrder:Integer;const AEnabled:Boolean;
+ const AWebsite,ALink,ATitle,ACurrentChapter,ADownloadedChapterList,ASaveTo: String); inline;
+ function Add(const AOrder:Integer;const AEnabled:Boolean;
+ const AWebsite,ALink,ATitle,ACurrentChapter,ADownloadedChapterList,ASaveTo:String):Boolean;
procedure Delete(const AWebsite, ALink: String);
procedure Commit; override;
- function Open: Boolean;
procedure Close; override;
property AutoCommitCount: Integer read FAutoCommitCount write SetAutoCommitCount;
end;
@@ -52,16 +51,9 @@ procedure TFavoritesDB.SetAutoCommitCount(AValue: Integer);
FAutoCommitCount := AValue;
end;
-function TFavoritesDB.ConvertNewTableIF: Boolean;
-begin
- Result := Table.Fields.Count < 9;
- FNFEnabled := Table.Fields.Count = 8;
-end;
-
constructor TFavoritesDB.Create(const AFilename: String);
begin
inherited Create;
- FNFEnabled := False;
FCommitCount := 0;
FAutoCommitCount := 500;
Filename := AFilename;
@@ -81,26 +73,44 @@ constructor TFavoritesDB.Create(const AFilename: String);
SelectParams := 'SELECT * FROM ' + QuotedStrD(TableName) + ' ORDER BY "order"';
end;
-function TFavoritesDB.Add(const AOrder: Integer; const AEnabled: Boolean;
- const AWebsite, ALink, ATitle, ACurrentChapter, ADownloadedChapterList,
- ASaveTo: String): Boolean;
+procedure TFavoritesDB.InternalUpdate(const AOrder:Integer;const AEnabled:Boolean;
+ const AWebsite,ALink,ATitle,ACurrentChapter,ADownloadedChapterList,ASaveTo:String);
+begin
+ Connection.ExecuteDirect('UPDATE "favorites" SET ' +
+ '"order"='+QuotedStr(AOrder)+', '+
+ '"enabled"='+QuotedStr(AEnabled)+', '+
+ '"title"='+QuotedStr(ATitle)+', '+
+ '"currentchapter"='+QuotedStr(ACurrentChapter)+', '+
+ '"downloadedchapterlist"='+QuotedStr(ADownloadedChapterList)+', '+
+ '"saveto"='+QuotedStr(ASaveTo)+
+ ' WHERE "websitelink"='+QuotedStr(LowerCase(AWebsite+ALink)));
+end;
+
+procedure TFavoritesDB.InternalAdd(const AOrder:Integer;const AEnabled:Boolean;
+ const AWebsite,ALink,ATitle,ACurrentChapter,ADownloadedChapterList,ASaveTo:String);
+begin
+ Connection.ExecuteDirect('INSERT OR REPLACE INTO "favorites" (' +
+ FieldsParams +
+ ') VALUES (' +
+ QuotedStr(LowerCase(AWebsite + ALink)) + ', ' +
+ QuotedStr(AOrder) + ', ' +
+ QuotedStr(AEnabled) + ', ' +
+ QuotedStr(AWebsite) + ', ' +
+ QuotedStr(ALink) + ', ' +
+ QuotedStr(ATitle) + ', ' +
+ QuotedStr(ACurrentChapter) + ', ' +
+ QuotedStr(ADownloadedChapterList) + ', ' +
+ QuotedStr(ASaveTo) + ')');
+end;
+
+function TFavoritesDB.Add(const AOrder:Integer;const AEnabled:Boolean;
+ const AWebsite,ALink,ATitle,ACurrentChapter,ADownloadedChapterList,ASaveTo:String):Boolean;
begin
Result := False;
if (AWebsite = '') or (ALink = '') then Exit;
if not Connection.Connected then Exit;
try
- Connection.ExecuteDirect('INSERT OR REPLACE INTO "favorites" (' +
- FieldsParams +
- ') VALUES (' +
- QuotedStr(LowerCase(AWebsite + ALink)) + ', ' +
- QuotedStr(AOrder) + ', ' +
- QuotedStr(AEnabled) + ', ' +
- QuotedStr(AWebsite) + ', ' +
- QuotedStr(ALink) + ', ' +
- QuotedStr(ATitle) + ', ' +
- QuotedStr(ACurrentChapter) + ', ' +
- QuotedStr(ADownloadedChapterList) + ', ' +
- QuotedStr(ASaveTo) + ')');
+ InternalAdd(AOrder,AEnabled,AWebsite,ALink,ATitle,ACurrentChapter,ADownloadedChapterList,ASaveTo);
Result := True;
Inc(FCommitCount);
if FCommitCount >= FAutoCommitCount then
@@ -142,19 +152,6 @@ procedure TFavoritesDB.Commit;
end;
end;
-function TFavoritesDB.Open: Boolean;
-begin
- Result := inherited Open(True, False);
- if FNFEnabled then
- try
- Connection.ExecuteDirect('UPDATE "favorites" SET "enabled"=''1''');
- Transaction.Commit;
- finally
- FNFEnabled := False;
- end;
- CloseTable;
-end;
-
procedure TFavoritesDB.Close;
begin
if FCommitCount <> 0 then
diff --git a/baseunits/GitHubRepoV3.pas b/baseunits/GitHubRepoV3.pas
new file mode 100644
index 000000000..c842f613f
--- /dev/null
+++ b/baseunits/GitHubRepoV3.pas
@@ -0,0 +1,229 @@
+unit GitHubRepoV3;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, fgl, httpsendthread, BaseThread, fpjson;
+
+type
+{ TGitHubRepo }
+
+ TTreeItem = class
+ path,
+ sha: String;
+ end;
+
+ TTreeItems = specialize TFPGObjectList;
+
+ TGitHubRepo = class
+ private
+ fdirty:boolean;
+ ConfigFile: String;
+ WorkFile: String;
+ HTTP: THTTPSendThread;
+ protected
+ procedure ReadWorkFile;
+ procedure WriteWorkFile;
+ public
+ api_url,
+ download_url,
+ owner,
+ name,
+ ref,
+ path: String;
+
+ last_commit_sha,
+ last_commit_etag,
+ tree_sha,
+ tree_etag: String;
+
+ Tree: TTreeItems;
+ public
+ constructor Create(const AConfigFile, AWorkFile: String; const AThread: TBaseThread = nil);
+ destructor Destroy; override;
+ function GetLastCommit: String;
+ function GetTree: Boolean;
+ function GetUpdate: Boolean;
+ function GetDownloadURL(const AName: String): String;
+ end;
+
+implementation
+
+uses jsonparser, jsonscanner, jsonConf, IniFiles, uBaseUnit;
+
+function AppendURLDelim(const URL: String): String;
+begin
+ Result := URL;
+ if (URL <> '') and (URL[Length(URL)] <> '/') then
+ Result := URL + '/';
+end;
+
+{ TGitHubRepo }
+
+procedure TGitHubRepo.ReadWorkFile;
+begin
+ if FileExists(WorkFile) then
+ with TJSONConfig.Create(nil) do
+ begin
+ try
+ Filename := WorkFile;
+ last_commit_sha := GetValue('last_commit_sha', '');
+ last_commit_etag := GetValue('last_commit_etag', '');
+ fdirty := false;
+ except
+ end;
+ Free;
+ end;
+end;
+
+procedure TGitHubRepo.WriteWorkFile;
+begin
+ if not fdirty then Exit;
+ if FileExists(WorkFile) then DeleteFile(WorkFile);
+ with TJSONConfig.Create(nil) do
+ begin
+ try
+ Filename := WorkFile;
+ FormatOptions := AsCompressedJSON;
+ Formatted := True;
+ SetValue('last_commit_sha' , last_commit_sha);
+ SetValue('last_commit_etag', last_commit_etag);
+ fdirty:=false;
+ except
+ end;
+ Free;
+ end;
+end;
+
+constructor TGitHubRepo.Create(const AConfigFile, AWorkFile: String;
+ const AThread: TBaseThread);
+begin
+ ConfigFile := AConfigFile;
+ WorkFile := AWorkFile;
+
+ HTTP := THTTPSendThread.Create(AThread);
+ HTTP.FollowRedirection := False;
+ HTTP.UserAgent := UserAgentCURL;
+ HTTP.ResetBasic;
+
+ if FileExists(ConfigFile) then
+ with TIniFile.Create(ConfigFile) do
+ try
+ api_url := ReadString ('GitHub', 'api_url' , '');
+ download_url := ReadString ('GitHub', 'download_url', '');
+ owner := ReadString ('GitHub', 'owner' , '');
+ name := ReadString ('GitHub', 'name' , '');
+ ref := ReadString ('GitHub', 'ref' , '');
+ path := ReadString ('GitHub', 'path' , '');
+ finally
+ Free;
+ end;
+ if api_url = '' then api_url := 'https://api.github.com/';
+ if ref = '' then ref := 'master';
+
+ ReadWorkFile;
+ Tree:=TTreeItems.Create;
+end;
+
+destructor TGitHubRepo.Destroy;
+begin
+ Tree.Free;
+ HTTP.Free;
+ WriteWorkFile;
+ inherited Destroy;
+end;
+
+function TGitHubRepo.GetLastCommit: String;
+var
+ s: String;
+ d: TJSONData;
+begin
+ Result:='';
+ HTTP.ResetBasic;
+ // use conditional etag, ignore if return 304 not modified
+ // https://developer.github.com/v3/#conditional-requests
+ if last_commit_etag<>'' then HTTP.Headers.Values['If-None-Match']:=' '+last_commit_etag;
+ s:=AppendURLDelim(api_url)+'repos/'+owner+'/'+name+'/commits?sha='+ref+'&per_page=1';
+ if path<>'' then s+='&path='+path;
+ if HTTP.GET(s) then
+ begin
+ s:=Trim(HTTP.Headers.Values['ETag']);
+ if s<>'' then last_commit_etag := s;
+ d:=GetJSON(HTTP.Document);
+ if Assigned(d) then
+ begin
+ try
+ if d.JSONType=jtArray then
+ last_commit_sha:=TJSONObject(TJSONArray(d).Items[0]).Get('sha');
+ except
+ end;
+ d.Free;
+ end;
+ end;
+ Result:=last_commit_sha;
+end;
+
+function TGitHubRepo.GetTree: Boolean;
+var
+ d: TJSONData;
+ a: TJSONArray;
+ s: String;
+ i: Integer;
+ item: TTreeItem;
+begin
+ Result:=false;
+ HTTP.ResetBasic;
+ s:=last_commit_sha; if s='' then s:=ref;
+ s:=AppendURLDelim(api_url)+'repos/'+owner+'/'+name+'/git/trees/'+s+':'+path+'?recursive=1';
+ if HTTP.GET(s) then
+ begin
+ d:=GetJSON(HTTP.Document);
+ if Assigned(d) then
+ begin
+ try
+ a:=TJSONArray(d.GetPath('tree'));
+ Tree.Clear;
+ for i:=0 to a.Count-1 do
+ with TJSONObject(a.Items[i]) do
+ begin
+ s:=Get('type');
+ if s<>'tree' then
+ begin
+ item:=TTreeItem.Create;
+ item.path := Get('path');
+ item.sha := Get('sha');
+ Tree.Add(item);
+ end;
+ end;
+ except
+ end;
+ d.Free;
+ end;
+ Result:=Tree.Count<>0;
+ end
+end;
+
+function TGitHubRepo.GetUpdate: Boolean;
+var
+ old_commit_sha, new_commit_sha: String;
+begin
+ result:=false;
+ old_commit_sha:=last_commit_sha;
+ new_commit_sha:=GetLastCommit;
+ if (new_commit_sha<>'') and (new_commit_sha<>old_commit_sha) then
+ result:=GetTree;
+ fdirty:=result;
+end;
+
+function TGitHubRepo.GetDownloadURL(const AName: String): String;
+var
+ lpath: String;
+begin
+ lpath:=path; if lpath<>'' then lpath:=lpath+'/';
+ Result:=AppendURLDelim(download_url)+owner+'/'+name+'/'+ref+'/'+lpath+AName;
+end;
+
+end.
+
diff --git a/baseunits/GithubRepo.pas b/baseunits/GithubRepo.pas
new file mode 100644
index 000000000..2c6db38a1
--- /dev/null
+++ b/baseunits/GithubRepo.pas
@@ -0,0 +1,271 @@
+unit GitHubRepo;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, httpsendthread, BaseThread, fpjson;
+
+type
+{ TGitHubRepo }
+
+ TGitHubRepo = class
+ private
+ ConfigFile: String;
+ HTTP: THTTPSendThread;
+ Tree: TJSONArray;
+ Props: TJSONObject;
+ protected
+ procedure SetAuth;
+ function QueryLastCommit: String;
+ function QueryTree: String;
+ function QueryProps(const ANames: TStrings): String;
+ public
+ api_url,
+ download_url,
+ owner,
+ name,
+ token,
+ ref,
+ path,
+ last_commit: String;
+ max_deep: Integer;
+ constructor Create(const AConfigFile: String; const AThread: TBaseThread = nil);
+ destructor Destroy; override;
+ function GetLastCommit: String;
+ function GetTree: TJSONArray;
+ function GetDownloadURL(const AName: String): String;
+ function GetProps(const ANames: TStrings): TJSONObject;
+ end;
+
+implementation
+
+uses jsonparser, jsonscanner, IniFiles, uBaseUnit;
+
+{ TGitHubRepo }
+
+procedure TGitHubRepo.SetAuth;
+begin
+ HTTP.Headers.Values['Authorization']:=' bearer '+token;
+end;
+
+function TGitHubRepo.QueryLastCommit: String;
+var
+ lpath: String;
+begin
+ lpath:=path; if lpath<>'' then lpath := ', path: "'+lpath+'"';
+ Result := '{"query": "'+StringToJSONString(
+ '{'+
+ 'repository(owner: "'+owner+'", name: "'+name+'") {'+
+ 'ref(qualifiedName: "'+ref+'") {'+
+ 'target {'+
+ '... on Commit {'+
+ 'history(first: 1'+lpath+'){ '+
+ 'nodes {'+
+ 'oid'+
+ '}'+
+ '}'+
+ '}'+
+ '}'+
+ '}'+
+ '}'+
+ '}'
+ )+'"}';
+end;
+
+function TGitHubRepo.QueryTree: String;
+
+ function onTree(const x: Integer):string;
+ begin
+ Result:= '... on Tree {'+
+ 'entries {'+
+ 'oid '+
+ 'name ';
+ if x>1 then
+ Result+= 'object {'+
+ onTree(x-1)+
+ '}';
+ Result+= '}'+
+ '}';
+ end;
+begin
+ Result := '{"query": "'+StringToJSONString(
+ '{'+
+ 'repository(owner: "'+owner+'", name: "'+name+'") {'+
+ 'object(expression: "'+last_commit+':'+path+'") {'+
+ onTree(max_deep)+
+ '}'+
+ '}'+
+ '}'
+ )+'"}';
+end;
+
+function TGitHubRepo.QueryProps(const ANames: TStrings): String;
+ function onProps:String;
+ var
+ i: Integer;
+ lpath: String;
+ begin
+ Result:='';
+ lpath :=path; if lpath<>'' then lpath+='/';
+ for i:=0 to ANames.Count-1 do
+ begin
+ Result += 'p'+IntToStr(i)+': history(path: "'+lpath+ANames[i]+'", first: 1) {'+
+ 'nodes {'+
+ 'message '+
+ 'committedDate'+
+ '}'+
+ '} ';
+ end;
+ end;
+begin
+ Result := '{"query": "'+StringToJSONString(
+ '{'+
+ 'repository(owner: "'+owner+'", name: "'+name+'") {'+
+ 'object(oid: "'+last_commit+'") {'+
+ '... on Commit {'+
+ onProps+
+ '}'+
+ '}'+
+ '}'+
+ '}'
+ )+'"}';
+end;
+
+constructor TGitHubRepo.Create(const AConfigFile: String;
+ const AThread: TBaseThread);
+begin
+ ConfigFile := AConfigFile;
+ HTTP := THTTPSendThread.Create(AThread);
+ if FileExists(ConfigFile) then
+ with TIniFile.Create(ConfigFile) do
+ try
+ api_url := ReadString ('GitHub', 'api_url' , '');
+ download_url := ReadString ('GitHub', 'download_url', '');
+ owner := ReadString ('GitHub', 'owner' , '');
+ name := ReadString ('GitHub', 'name' , '');
+ token := DecryptString(ReadString ('GitHub', 'token' , ''));
+ ref := ReadString ('GitHub', 'ref' , '');
+ path := ReadString ('GitHub', 'path' , '');
+ max_deep := ReadInteger ('GitHub', 'max_deep' , 2);
+ finally
+ Free;
+ end;
+ if api_url = '' then api_url := 'https://api.github.com/graphql';
+ if ref = '' then ref := 'master';
+ if max_deep < 1 then max_deep := 1;
+ last_commit:= ref;
+end;
+
+destructor TGitHubRepo.Destroy;
+begin
+ if Assigned(Tree) then Tree.Free;
+ if Assigned(Props) then Props.Free;
+ HTTP.Free;
+ inherited Destroy;
+end;
+
+function TGitHubRepo.GetLastCommit: String;
+var
+ d: TJSONData;
+ a: TJSONArray;
+begin
+ last_commit:='';
+ HTTP.Reset;
+ SetAuth;
+ d:=nil;
+ if HTTP.POST(api_url, QueryLastCommit) then
+ with TJSONParser.Create(HTTP.Document, [joUTF8]) do
+ try
+ d:=Parse;
+ finally
+ free;
+ end;
+ if Assigned(d) then
+ try
+ a:=TJSONArray(d.GetPath('data.repository.ref.target.history.nodes'));
+ if Assigned(a) then
+ last_commit:=TJSONObject(a.Items[0]).Get('oid','');
+ except
+ end;
+ d.free;
+ Result := last_commit;
+end;
+
+function TGitHubRepo.GetTree: TJSONArray;
+var
+ d: TJSONData;
+ a: TJSONArray;
+begin
+ result:=nil;
+ HTTP.Reset;
+ SetAuth;
+ if last_commit='' then
+ last_commit := 'master';
+ d:=nil;
+ if HTTP.POST(api_url, QueryTree) then
+ with TJSONParser.Create(HTTP.Document, [joUTF8]) do
+ try
+ d:=Parse;
+ finally
+ free;
+ end;
+ if Assigned(Tree) then
+ FreeAndNil(Tree);
+ if Assigned(d) then
+ begin
+ try
+ a:=TJSONArray(d.GetPath('data.repository.object.entries'));
+ if Assigned(a) then
+ Tree:=TJSONArray(a.Clone);
+ except
+ end;
+ d.free;
+ end;
+ Result := Tree;
+end;
+
+function TGitHubRepo.GetDownloadURL(const AName: String): String;
+var
+ lpath: String;
+begin
+ lpath:=path; if lpath<>'' then lpath:=lpath+'/';
+ Result:=AppendURLDelim(download_url)+owner+'/'+name+'/'+ref+'/'+lpath+AName;
+end;
+
+function TGitHubRepo.GetProps(const ANames: TStrings): TJSONObject;
+var
+ d: TJSONData;
+ o: TJSONObject;
+begin
+ result:=nil;
+ HTTP.Reset;
+ SetAuth;
+ if last_commit='' then
+ last_commit := 'master';
+ d:=nil;
+ if HTTP.POST(api_url, QueryProps(ANames)) then
+ with TJSONParser.Create(HTTP.Document, [joUTF8]) do
+ try
+ d:=Parse;
+ finally
+ free;
+ end;
+ if Assigned(Props) then
+ FreeAndNil(Props);
+ if Assigned(d) then
+ begin
+ try
+ o:=TJSONObject(d.GetPath('data.repository.object'));
+ if Assigned(o) then
+ Props:=TJSONObject(o.Clone);
+ except
+ end;
+ d.free;
+ end;
+ Result := Props;
+end;
+
+end.
+
diff --git a/baseunits/Img2Pdf.pas b/baseunits/Img2Pdf.pas
index 63d8d1245..39618fc08 100644
--- a/baseunits/Img2Pdf.pas
+++ b/baseunits/Img2Pdf.pas
@@ -36,7 +36,7 @@ interface
uses
Classes, SysUtils, LazFileUtils, LazUTF8Classes, FPimage, ImgInfos, MemBitmap,
FPReadJPEG, FPWriteJPEG, FPReadPNG, JPEGLib, JdAPImin, JDataSrc, Jerror,
- zstream;
+ zstream, AnimatedGif, MultiLog;
type
TCompressionQuality = 0..100;
@@ -377,6 +377,38 @@ procedure WEBPToPageInfo(const PageInfo: TPageInfo);
end;
end;
+{ --- Does not work yet. --- }
+
+{ procedure GIFToPageInfo(const PageInfo: TPageInfo); }
+{ var }
+ { AMS: TMemoryStreamUTF8; }
+ { MBM: TAnimatedGif; }
+ { WRT: TFPWriterPNG; }
+{ begin }
+ { AMS := TMemoryStreamUTF8.Create; }
+ { try }
+ { MBM := TAnimatedGif.Create(PageInfo.FileName); }
+ { MBM.CurrentImage := 0; }
+ { try }
+ { if Assigned(MBM) then }
+ { try }
+ { WRT := TFPWriterPNG.create; }
+ { WRT.Indexed := False; }
+ { WRT.UseAlpha := MBM.MemBitmap.HasTransparentPixels; }
+ { WRT.CompressionLevel := clnone; }
+ { MBM.MemBitmap.SaveToStream(AMS, WRT); }
+ { finally }
+ { WRT.Free; }
+ { end; }
+ { finally }
+ { MBM.Free; }
+ { end; }
+ { PNGToPageInfo(PageInfo, AMS); }
+ { finally }
+ { AMS.Free; }
+ { end; }
+{ end; }
+
procedure ImageToPageInfo(const PageInfo: TPageInfo);
var
IMG: TFPCustomImage;
@@ -444,6 +476,9 @@ procedure TPageInfo.LoadImageData;
else
if Ext = 'webp' then
WEBPToPageInfo(Self)
+ { else }
+ { if Ext = 'gif' then }
+ { GIFToPageInfo(Self) }
else
ImageToPageInfo(Self);
except
diff --git a/baseunits/ImgInfos.pas b/baseunits/ImgInfos.pas
index 13bff4399..dd1b60c2b 100644
--- a/baseunits/ImgInfos.pas
+++ b/baseunits/ImgInfos.pas
@@ -56,10 +56,10 @@ TImageHandlerRec = record
TimageHandlerMgr = class
private
- FList: array of TImageHandlerRec;
FEmptyHandlerRec: TImageHandlerRec;
function GetCount: Integer;
public
+ List: array of TImageHandlerRec;
constructor Create;
destructor Destroy; override;
procedure Add(const ReaderClass: TFPCustomImageReaderClass;
@@ -82,7 +82,7 @@ TimageHandlerMgr = class
function GetImageFileWriterClass(const FileName: String): TFPCustomImageWriterClass; inline;
function GetImageExtWriterClass(const Ext: String): TFPCustomImageWriterClass; inline;
function GetImageWriterExt(const Ext: String): String; inline;
- public
+ published
property Count: Integer read GetCount;
end;
@@ -180,7 +180,7 @@ function GetImageWriterExt(const Ext: String): String;
function TimageHandlerMgr.GetCount: Integer;
begin
- Result := Length(FList);
+ Result := Length(List);
end;
constructor TimageHandlerMgr.Create;
@@ -190,7 +190,7 @@ constructor TimageHandlerMgr.Create;
destructor TimageHandlerMgr.Destroy;
begin
- SetLength(Flist, 0);
+ SetLength(List, 0);
inherited Destroy;
end;
@@ -202,17 +202,17 @@ procedure TimageHandlerMgr.Add(const ReaderClass: TFPCustomImageReaderClass;
var
i: Integer;
begin
- i := Length(FList);
- SetLength(FList, i + 1);
- FList[i].ReaderClass := ReaderClass;
- FList[i].WriterClass := WriterClass;
- FList[i].CheckImageStream := CheckImageStreamFunc;
- FList[i].GetImageStreamSize := GetImageStreamSizeProc;
- FList[i].Ext := Ext;
+ i := Length(List);
+ SetLength(List, i + 1);
+ List[i].ReaderClass := ReaderClass;
+ List[i].WriterClass := WriterClass;
+ List[i].CheckImageStream := CheckImageStreamFunc;
+ List[i].GetImageStreamSize := GetImageStreamSizeProc;
+ List[i].Ext := Ext;
if WExt <> '' then
- FList[i].WExt := WExt
+ List[i].WExt := WExt
else
- FList[i].WExt := Ext;
+ List[i].WExt := Ext;
end;
function TimageHandlerMgr.GetImageHandlerByStream(const Stream: TStream): PImageHandlerRec;
@@ -225,12 +225,12 @@ function TimageHandlerMgr.GetImageHandlerByStream(const Stream: TStream): PImage
if Stream.Size = 0 then Exit;
P := Stream.Position;
try
- for i := Low(FList) to High(Flist) do
+ for i := Low(List) to High(List) do
begin
Stream.Position := 0;
- if FList[i].CheckImageStream(Stream) then
+ if List[i].CheckImageStream(Stream) then
begin
- Result := @FList[i];
+ Result := @List[i];
Break;
end;
end;
@@ -258,10 +258,10 @@ function TimageHandlerMgr.GetImageHandlerByExt(const Ext: String): PImageHandler
i: Integer;
begin
Result := @FEmptyHandlerRec;
- for i := Low(FList) to High(Flist) do
- if Ext = FList[i].Ext then
+ for i := Low(List) to High(List) do
+ if Ext = List[i].Ext then
begin
- Result := @FList[i];
+ Result := @List[i];
Break;
end;
end;
@@ -633,10 +633,10 @@ initialization
ImageHandlerMgr := TimageHandlerMgr.Create;
ImageHandlerMgr.Add(TFPReaderJPEG, TFPWriterJPEG, @JPEGCheckImageStream, @JPEGGetImageSize, 'jpg');
ImageHandlerMgr.Add(TFPReaderPNG, TFPWriterPNG, @PNGCheckImageStream, @PNGGetImageSize, 'png');
+ ImageHandlerMgr.Add(nil, nil, @WEBPCheckImageStream, @WEBPGetImageSize, 'webp');
ImageHandlerMgr.Add(TFPReaderGif, TFPWriterPNG, @GIFCheckImageStream, @GIFGetImageSize, 'gif', 'png');
ImageHandlerMgr.Add(TFPReaderBMP, TFPWriterBMP, @BMPCheckImageStream, @BMPGetImageSize, 'bmp');
ImageHandlerMgr.Add(TFPReaderTiff, TFPWriterTiff, @TIFFCheckImageStream, @TIFFGetImageSize, 'tif');
- ImageHandlerMgr.Add(nil, nil, @WEBPCheckImageStream, @WEBPGetImageSize, 'webp');
finalization
ImageHandlerMgr.Free;
diff --git a/baseunits/ModuleList.inc b/baseunits/ModuleList.inc
index a20acb166..0f831860d 100644
--- a/baseunits/ModuleList.inc
+++ b/baseunits/ModuleList.inc
@@ -1,52 +1,16 @@
-uses
- MangaFox,
- Mangacan,
- MangaReader,
- MangaLife,
- MangaHere,
- MangaTr,
- Madokami,
- RawSenManga,
- KissManga,
- MangaHome,
- MangaChanRU,
- MintMangaRU,
- MangaHubRU,
- AcademyVN,
- Webtoons,
- Tsumino,
- WebtoonTr,
- KuManga,
- SenManga,
- MangaInn,
- LeoManga,
- MangaIndo,
- GoodManga,
- MangaZuki,
- ReadMangaToday,
- MangaOnlineBR,
- Tapas,
- Taadd,
- NineManga,
- BlogTruyen,
- MangaAe,
- MangaTube,
- Mangaf,
- MangaRock,
- PsychoPlay,
- MangaWindow,
- TranslateWebtoon,
- // Raw Official
- SundayWebEvery,
- TonariNoYoungJump,
- YoungAceUp,
- NewType,
- Comico,
- Shogakukan,
- // Adult
- WPAdultSiteSkins,
- EHentai,
- Luscious,
- EightMuses,
- HentaiCafe,
- Hentai2Read;
+BlogTruyen,
+Comico,
+EHentai,
+EightMuses,
+Hentai2Read,
+KissManga,
+Madokami,
+MangaFox,
+MangaHome,
+RawSenManga,
+SundayWebEvery,
+Tapas,
+TonariNoYoungJump,
+TranslateWebtoon,
+Tsumino,
+WPAdultSiteSkins;
diff --git a/baseunits/SQLiteData.pas b/baseunits/SQLiteData.pas
index 1f6efaec8..0a649f29f 100644
--- a/baseunits/SQLiteData.pas
+++ b/baseunits/SQLiteData.pas
@@ -5,15 +5,18 @@
interface
uses
- SysUtils, LazFileUtils, strutils, sqlite3conn, sqldb;
+ SysUtils, Classes, LazFileUtils, strutils, sqlite3conn, sqldb, SQLite3Dyn;
type
{ TSQLite3ConnectionH }
TSQLite3ConnectionH = class(TSQLite3Connection)
+ protected
+ procedure DoInternalDisconnect; override;
public
property Handle read GetHandle;
+ property Statements;
end;
TExceptionEvent = procedure(Sender: TObject; E: Exception) of object;
@@ -28,7 +31,7 @@ TSQliteData = class
FOnError: TExceptionEvent;
FTrans: TSQLTransaction;
FQuery: TSQLQuery;
- FFilename: String;
+ FFileName: String;
FTableName: String;
FCreateParams: String;
FSelectParams: String;
@@ -65,7 +68,7 @@ TSQliteData = class
property Connection: TSQLite3ConnectionH read FConn;
property Transaction: TSQLTransaction read FTrans;
property Table: TSQLQuery read FQuery;
- property Filename: String read FFilename write FFilename;
+ property Filename: String read FFileName write FFileName;
property TableName: String read FTableName write FTableName;
property CreateParams: String read FCreateParams write SetCreateParams;
property SelectParams: String read FSelectParams write SetSelectParams;
@@ -84,6 +87,7 @@ function QuotedStrD(const S: Integer): String; overload; inline;
implementation
+
function QuotedStr(const S: Integer): String;
begin
Result := AnsiQuotedStr(IntToStr(S), '''');
@@ -94,9 +98,26 @@ function QuotedStr(const S: Boolean): String;
Result := AnsiQuotedStr(BoolToStr(S, '1', '0'), '''');
end;
+function ToStrZeroPad(const i, len: Word): String;
+begin
+ Result:=IntToStr(i);
+ if Length(Result) nil then
+ begin
+ checkerror(sqlite3_close_v2(lhandle));
+ ReleaseSQLite;
+ end;
+end;
+
{ TSQliteData }
procedure TSQliteData.DoOnError(E: Exception);
@@ -163,9 +208,9 @@ procedure TSQliteData.SetSelectParams(AValue: String);
function TSQliteData.OpenDB: Boolean;
begin
Result := False;
- if FFilename = '' then Exit;
+ if FFileName = '' then Exit;
try
- FConn.DatabaseName := FFilename;
+ FConn.DatabaseName := FFileName;
FConn.Connected := True;
FTrans.Active := True;
except
@@ -274,7 +319,7 @@ constructor TSQliteData.Create;
AutoApplyUpdates := True;
FAutoVacuum := True;
FRecordCount := 0;
- FFilename := '';
+ FFileName := '';
FTableName := 'maintable';
FCreateParams := '';
end;
@@ -292,8 +337,8 @@ function TSQliteData.Open(const AOpenTable: Boolean;
const AGetRecordCount: Boolean): Boolean;
begin
Result := False;
- if (FFilename = '') or (FCreateParams = '') then Exit;
- if FileExistsUTF8(FFilename) then
+ if (FFileName = '') or (FCreateParams = '') then Exit;
+ if FileExistsUTF8(FFileName) then
Result := OpenDB
else
Result := CreateDB;
diff --git a/baseunits/SelfUpdater.pas b/baseunits/SelfUpdater.pas
index aad9ecbf1..c5b83a2ea 100644
--- a/baseunits/SelfUpdater.pas
+++ b/baseunits/SelfUpdater.pas
@@ -5,38 +5,24 @@
interface
uses
- Classes, SysUtils, httpsendthread, BaseThread, FMDOptions, process, ComCtrls, Controls,
- Dialogs, StdCtrls, Buttons, Forms, blcksock;
+ Classes, SysUtils, httpsendthread, FMDOptions, StatusBarDownload, process,
+ Controls, Dialogs, Forms;
type
{ TSelfUpdaterThread }
- TSelfUpdaterThread = class(TBaseThread)
+ TSelfUpdaterThread = class(TStatusBarDownload)
private
- FStatusBar: TStatusBar;
- FProgressBar: TProgressBar;
- FButtonCancel: TSpeedButton;
- FHTTP: THTTPSendThread;
- FTotalSize: Integer;
- FCurrentSize: Integer;
FFailedMessage: String;
- FStatusText: String;
protected
- procedure ButtonCancelClick(Sender: TObject);
- procedure HTTPSockOnStatus(Sender: TObject; Reason: THookSocketReason;
- const Value: String);
procedure HTTPRedirected(const AHTTP: THTTPSendThread; const URL: String);
protected
procedure SyncStart;
procedure SyncFinal;
- procedure SyncStartDownload;
- procedure SyncUpdateProgress;
- procedure SyncUpdateStatus;
procedure SyncShowFailed;
procedure SyncFinishRestart;
procedure ProceedUpdate;
- procedure UpdateStatusText(const S: String);
procedure Execute; override;
public
UpdateURL: String;
@@ -64,31 +50,6 @@ implementation
{ TSelfUpdaterThread }
-procedure TSelfUpdaterThread.ButtonCancelClick(Sender: TObject);
-begin
- Self.Terminate;
-end;
-
-procedure TSelfUpdaterThread.HTTPSockOnStatus(Sender: TObject;
- Reason: THookSocketReason; const Value: String);
-begin
- if Terminated then
- Exit;
- if Reason = HR_ReadCount then
- begin
- if FTotalSize = 0 then
- FTotalSize := StrToIntDef(Trim(FHTTP.Headers.Values['Content-Length']), 0);
- Inc(FCurrentSize, StrToInt(Value));
- Synchronize(@SyncUpdateProgress);
- end
- else
- if Reason = HR_Connect then
- begin
- FCurrentSize := 0;
- FTotalSize := 0;
- end;
-end;
-
procedure TSelfUpdaterThread.HTTPRedirected(const AHTTP: THTTPSendThread;
const URL: String);
begin
@@ -98,105 +59,13 @@ procedure TSelfUpdaterThread.HTTPRedirected(const AHTTP: THTTPSendThread;
procedure TSelfUpdaterThread.SyncStart;
begin
SelfUpdaterThread := Self;
-
- FStatusBar := TStatusBar.Create(FormMain);
- with FStatusBar do
- begin
- Parent := FormMain;
- SimplePanel := False;
- with Panels.Add do // panel for progress bar
- Width := 100;
- Panels.Add; // panel for progress text
- Panels.Add; // panel for status text
- end;
-
- FProgressBar := TProgressBar.Create(FormMain);
- with FProgressBar do
- begin
- Parent := FStatusBar;
- Align := alNone;
- Smooth := True;
- Style := pbstNormal;
- Min := 0;
- Width := FStatusBar.Panels[0].Width - 10;
- Anchors := [akTop, akLeft, akBottom];
- AnchorSideTop.Control := FStatusBar;
- AnchorSideTop.Side := asrTop;
- AnchorSideLeft.Control := FStatusBar;
- AnchorSideLeft.Side := asrTop;
- AnchorSideBottom.Control := FStatusBar;
- AnchorSideBottom.Side := asrBottom;
- BorderSpacing.Top := 2;
- BorderSpacing.Left := 5;
- BorderSpacing.Bottom := 2;
- end;
-
- FButtonCancel := TSpeedButton.Create(FormMain);
- with FButtonCancel do
- begin
- Parent := FStatusBar;
- Align := alNone;
- AutoSize := True;
- Caption := RS_ButtonCancel;
- ShowCaption := True;
- Flat := True;
- Anchors := [akTop, akRight, akBottom];
- AnchorSideTop.Control := FStatusBar;
- AnchorSideTop.Side := asrTop;
- AnchorSideRight.Control := FStatusBar;
- AnchorSideRight.Side := asrRight;
- AnchorSideBottom.Control := FStatusBar;
- AnchorSideBottom.Side := asrBottom;
- BorderSpacing.Top := 2;
- BorderSpacing.Right := 5;
- BorderSpacing.Bottom := 2;
- OnClick := @ButtonCancelClick;
- end;
end;
procedure TSelfUpdaterThread.SyncFinal;
begin
- FHTTP.Sock.OnStatus := nil;
- FreeAndNil(FStatusBar);
- FreeAndNil(FProgressBar);
- FreeAndNil(FButtonCancel);
SelfUpdaterThread := nil;
end;
-procedure TSelfUpdaterThread.SyncStartDownload;
-begin
- FCurrentSize := 0;
- FTotalSize := 0;
- FProgressBar.Max := 0;
- FProgressBar.Position := 0;
- FStatusBar.Panels[1].Text := '';
- FStatusBar.Panels[1].Width := 0;
- SyncUpdateStatus;
-end;
-
-procedure TSelfUpdaterThread.SyncUpdateProgress;
-var
- s: String;
-begin
- if FStatusBar = nil then
- Exit;
- if FProgressBar.Max <> FTotalSize then
- FProgressBar.Max := FTotalSize;
- if FProgressBar.Position <> FCurrentSize then
- FProgressBar.Position := FCurrentSize;
-
- s := FormatByteSize(FCurrentSize);
- if FTotalSize <> 0 then
- s += '/' + FormatByteSize(FTotalSize);
- FStatusBar.Panels[1].Width := FStatusBar.Canvas.TextWidth(s) + 10;
- FStatusBar.Panels[1].Text := s;
-end;
-
-procedure TSelfUpdaterThread.SyncUpdateStatus;
-begin
- FStatusBar.Panels[2].Text := FStatusText;
-end;
-
procedure TSelfUpdaterThread.SyncShowFailed;
begin
MessageDlg(RS_FailedTitle, FFailedMessage, mtError, [mbOK], 0);
@@ -239,36 +108,27 @@ procedure TSelfUpdaterThread.ProceedUpdate;
FFailedMessage := Format(RS_MissingFile, [OLD_CURRENT_UPDATER_EXE]);
end;
-procedure TSelfUpdaterThread.UpdateStatusText(const S: String);
-begin
- if FStatusText = S then
- Exit;
- FStatusText := S;
- Synchronize(@SyncUpdateStatus);
-end;
-
procedure TSelfUpdaterThread.Execute;
begin
DownloadSuccess := False;
if UpdateURL = '' then
Exit;
try
- FStatusText := Format(RS_Downloading, [UpdateURL]);
- Synchronize(@SyncStartDownload);
- if FHTTP.GET(UpdateURL) and (FHTTP.ResultCode < 300) then
+ UpdateStatusText(Format(RS_Downloading, [UpdateURL]));
+ if HTTP.GET(UpdateURL) and (HTTP.ResultCode < 300) then
begin
DownloadSuccess := True;
Filename := FMD_DIRECTORY + UPDATE_PACKAGE_NAME;
if FileExists(Filename) then
DeleteFile(Filename);
if not FileExists(Filename) then
- FHTTP.Document.SaveToFile(Filename);
+ HTTP.Document.SaveToFile(Filename);
if FileExists(Filename) then
DeleteFile(Filename);
if not FileExists(Filename) then
begin
- FHTTP.Document.SaveToFile(Filename);
+ HTTP.Document.SaveToFile(Filename);
if not FileExists(Filename) then
begin
FFailedMessage := Format(RS_FailedToSave, [Filename]);
@@ -289,7 +149,7 @@ procedure TSelfUpdaterThread.Execute;
end
else
FFailedMessage := Format(RS_FailedDownload, [NewVersionString,
- FHTTP.ResultCode, FHTTP.ResultString]);
+ HTTP.ResultCode, HTTP.ResultString]);
except
on E: Exception do
FFailedMessage := E.Message;
@@ -298,13 +158,9 @@ procedure TSelfUpdaterThread.Execute;
constructor TSelfUpdaterThread.Create;
begin
- inherited Create(True);
- FreeOnTerminate := True;
+ inherited Create(True, FormMain, FormMain.IconList, 24);
FFailedMessage := '';
- FHTTP := THTTPSendThread.Create(Self);
- FHTTP.UserAgent := UserAgentCURL;
- FHTTP.Sock.OnStatus := @HTTPSockOnStatus;
- FHTTP.OnRedirected := @HTTPRedirected;
+ HTTP.OnRedirected := @HTTPRedirected;
Synchronize(@SyncStart);
end;
@@ -316,7 +172,6 @@ destructor TSelfUpdaterThread.Destroy;
if DownloadSuccess then
Synchronize(@SyncFinishRestart);
Synchronize(@SyncFinal);
- FHTTP.Free;
inherited Destroy;
end;
diff --git a/baseunits/SimpleException/SimpleException.pas b/baseunits/SimpleException/SimpleException.pas
index 4518ba882..dcff26366 100644
--- a/baseunits/SimpleException/SimpleException.pas
+++ b/baseunits/SimpleException/SimpleException.pas
@@ -31,6 +31,7 @@
unit SimpleException;
{$mode objfpc}{$H+}
+{$define MULTILOG}
interface
@@ -525,6 +526,7 @@ procedure TSimpleException.CreateExceptionReport;
if Assigned(FLastException) then
begin
FLastReport := FLastReport +
+ 'Exception Object : ' + HexStr(FLastException) + LineEnding +
'Exception Class : ' + FLastException.ClassName + LineEnding +
'Message : ' + FLastException.Message + LineEnding;
end;
@@ -534,7 +536,7 @@ procedure TSimpleException.CreateExceptionReport;
if Logger.Enabled then
begin
if Assigned(FLastException) then
- Logger.SendExceptionStr(FLastException.ClassName + ' - ' + FLastException.Message, S)
+ Logger.SendExceptionStr(HexStr(FLastException) + ' ' + FLastException.ClassName + ' - ' + FLastException.Message, S)
else
Logger.SendExceptionStr('Program exception!', S);
end
diff --git a/baseunits/SimpleTranslator.pas b/baseunits/SimpleTranslator.pas
index cd9bd514e..ebac7b4b8 100644
--- a/baseunits/SimpleTranslator.pas
+++ b/baseunits/SimpleTranslator.pas
@@ -26,7 +26,7 @@ interface
uses
Classes, SysUtils, strutils, gettext, LazFileUtils, LazUTF8, LCLTranslator,
- Translations, LResources, Forms;
+ Translations, LResources, Forms, LCLVersion;
type
TLanguageItem = record
@@ -499,7 +499,7 @@ TLanguageItem = record
function SetLang(const lang: string; appname: string = ''): Boolean;
function SetLangByIndex(const Index: Integer): Boolean;
- function GetDefaultLang: string;
+ function GetDefaultLang: string; inline;
implementation
@@ -768,11 +768,11 @@ function SetLangByIndex(const Index: Integer): Boolean;
function GetDefaultLang: string;
begin
- {$IF FPC_FULLVERSION >= 20701}
+ {$if lcl_fullversion > 2000600}
+ Result := LCLTranslator.SetDefaultLang('');
+ {$else}
Result := LCLTranslator.GetDefaultLang;
- {$ELSE}
- Result := '';
- {$ENDIF}
+ {$ifend}
end;
initialization
diff --git a/baseunits/StatusBarDownload.pas b/baseunits/StatusBarDownload.pas
new file mode 100644
index 000000000..c4adae427
--- /dev/null
+++ b/baseunits/StatusBarDownload.pas
@@ -0,0 +1,274 @@
+unit StatusBarDownload;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, BaseThread, httpsendthread, blcksock, ExtCtrls, Forms,
+ Controls, Buttons, Graphics, ComCtrls;
+
+type
+
+ { TStatusBarDownload }
+
+ TStatusBarDownload = class(TBaseThread)
+ private
+ FOwnerForm: TForm;
+ FImageList: TImageList;
+ FStatusBar: TPanel;
+ FButtonCancel: TSpeedButton;
+ FButtonCancelImageIndex: Integer;
+
+ FControlMargin: Integer;
+ FResized: Boolean;
+ FProgressBarRect,
+ FProgressBarPercentsRect,
+ FStatusTextRect: TRect;
+ FProgressText,
+ FStatusText: String;
+
+ FTimerRepaint: TTimer;
+ FNeedRepaint: Boolean;
+
+ FHTTP: THTTPSendThread;
+ FTotalSize,
+ FCurrentSize: Integer;
+ FPercents: Double;
+ protected
+ procedure SyncCreate;
+ procedure SyncDestroy;
+ procedure TimerRepaintTimer(Sender: TObject);
+ procedure StatusBarPaint(Sender: TObject);
+ procedure StatusBarRezise(Sender: TObject);
+ procedure ButtonCancelClick(Sender: TObject);
+ procedure HTTPSockOnStatus(Sender: TObject; Reason: THookSocketReason;
+ const Value: String);
+ procedure UpdateStatusText(AStatusText: String);
+ public
+ constructor Create(CreateSuspended: Boolean = True; AOwnerForm: TForm = nil;
+ AImageList: TImageList = nil; AButtonCancelImageIndex: Integer = -1);
+ destructor Destroy; override;
+ published
+ property HTTP: THTTPSendThread read FHTTP;
+ property StatusBar: TPanel read FStatusBar;
+ end;
+
+implementation
+
+uses uBaseUnit;
+
+const
+ CL_ProgressBarBaseLine = $bcbcbc;
+ CL_ProgressBarBase = $e6e6e6;
+ CL_ProgressBarLine = $25b006;
+ CL_ProgressBar = $42d932;
+
+{ TStatusBarDownload }
+
+procedure TStatusBarDownload.SyncCreate;
+var
+ txtHeight: Integer;
+begin
+ FControlMargin := FOwnerForm.ScaleFontTo96(2);
+ FStatusBar := TPanel.Create(nil);
+ with FStatusBar do begin
+ Parent := FOwnerForm;
+ DoubleBuffered := True;
+ Align := alBottom;
+ AutoSize := False;
+ txtHeight := Canvas.GetTextHeight('A');
+ Height := txtHeight + (FControlMargin * 4);
+ Caption := '';
+ Color := clBtnFace;
+ BevelOuter := bvNone;
+ BevelInner := bvNone;
+ BorderStyle := bsNone;
+ BorderSpacing.Top := FControlMargin;
+ OnPaint := @StatusBarPaint;
+ OnResize := @StatusBarRezise;
+ Canvas.Brush.Style := bsSolid;
+ Canvas.Pen.Style := psSolid;
+ FResized := True;
+ end;
+
+ FButtonCancel := TSpeedButton.Create(FStatusBar);
+ with FButtonCancel do begin
+ Parent := FStatusBar;
+ Align := alNone;
+ AutoSize := False;
+ Flat := True;
+ Anchors := [akTop, akRight, akBottom];
+ AnchorSideTop.Control := FStatusBar;
+ AnchorSideTop.Side := asrTop;
+ BorderSpacing.Top := FControlMargin;
+ AnchorSideRight.Control := FStatusBar;
+ AnchorSideRight.Side := asrRight;
+ BorderSpacing.Right := FControlMargin;
+ AnchorSideBottom.Control := FStatusBar;
+ AnchorSideBottom.Side := asrBottom;
+ BorderSpacing.Bottom := FControlMargin;
+ Width := Height;
+ OnClick := @ButtonCancelClick;
+ if Assigned(FImageList) and (FButtonCancelImageIndex > -1) then begin
+ Images := FImageList;
+ ImageIndex := FButtonCancelImageIndex;
+ end;
+ end;
+
+ StatusBarRezise(FStatusBar);
+
+ FTimerRepaint := TTimer.Create(FStatusBar);
+ FTimerRepaint.Interval := 1000;
+ FTimerRepaint.OnTimer := @TimerRepaintTimer;
+ FTimerRepaint.Enabled := True;
+ FNeedRepaint := True;
+end;
+
+procedure TStatusBarDownload.SyncDestroy;
+begin
+ FStatusBar.Free;
+end;
+
+procedure TStatusBarDownload.TimerRepaintTimer(Sender: TObject);
+begin
+ if FNeedRepaint then
+ begin
+ FNeedRepaint := False;
+ FStatusBar.Repaint;
+ end;
+end;
+
+procedure TStatusBarDownload.StatusBarPaint(Sender: TObject);
+var
+ txtWidth, txtHeight: integer;
+begin
+ with FStatusBar.Canvas do begin
+ Pen.Color := clActiveBorder;
+ Line(0,0,FStatusBar.ClientRect.Right,0);
+
+ if FResized then begin
+ FProgressBarRect := FStatusBar.ClientRect;
+ FProgressBarRect.Inflate(-FControlMargin, -(FControlMargin * 2));
+ FStatusTextRect := FStatusBar.ClientRect;
+ FProgressBarRect.Width := GetTextWidth('_999.99 MB/999.99 MB_');
+ FStatusTextRect.Left := FProgressBarRect.Right + (FControlMargin * 2);
+ FStatusTextRect.Right := FButtonCancel.Left - FControlMargin;
+ FResized := False;
+ end;
+
+ Brush.Style := bsSolid;
+ Pen.Style := psSolid;
+
+ Pen.Color := CL_ProgressBarBaseLine;
+ Brush.Color := CL_ProgressBarBase;
+ Rectangle(FProgressBarRect);
+
+ if FPercents > 0 then begin
+ FProgressBarPercentsRect := FProgressBarRect;
+ FProgressBarPercentsRect.Right :=
+ Round((FProgressBarPercentsRect.Right - FProgressBarPercentsRect.Left) * FPercents) + FProgressBarPercentsRect.Left;
+
+ Pen.Color := CL_ProgressBarLine;
+ Brush.Color := CL_ProgressBar;
+
+ Frame(FProgressBarPercentsRect);
+ FProgressBarPercentsRect.Inflate(-2, -2);
+ GradientFill(FProgressBarPercentsRect, BlendColor(Brush.Color, CL_ProgressBarBase, 128), Brush.Color, gdHorizontal);
+
+ Brush.Style := bsClear;
+
+ GetTextSize(FProgressText, txtWidth, txtHeight);
+ TextRect(FProgressBarRect, FProgressBarRect.Left + ((FProgressBarRect.Right - FProgressBarRect.Left - txtWidth) div 2),
+ FProgressBarRect.Top + ((FProgressBarRect.Bottom - FProgressBarRect.Top - txtHeight) div 2), FProgressText);
+ end;
+ txtHeight := GetTextHeight(FStatusText);
+ TextRect(FStatusTextRect, FStatusTextRect.Left, FStatusTextRect.Top + ((FStatusTextRect.Bottom - FStatusTextRect.Top - txtHeight) div 2), FStatusText);
+ end;
+end;
+
+procedure TStatusBarDownload.StatusBarRezise(Sender: TObject);
+begin
+ FResized := True;
+end;
+
+procedure TStatusBarDownload.ButtonCancelClick(Sender: TObject);
+begin
+ Self.Terminate;
+end;
+
+procedure TStatusBarDownload.HTTPSockOnStatus(Sender: TObject;
+ Reason: THookSocketReason; const Value: String);
+begin
+ if Terminated then
+ Exit;
+ if Reason = HR_ReadCount then
+ begin
+ FNeedRepaint := True;
+ if FTotalSize = 0 then
+ begin
+ FTotalSize := StrToIntDef(Trim(FHTTP.Headers.Values['Content-Length']), 0);
+ FPercents := 0;
+ end;
+
+ Inc(FCurrentSize, StrToInt(Value));
+ if (FCurrentSize <> 0) then
+ begin
+ if FTotalSize < FCurrentSize then
+ FPercents := 0
+ else
+ FPercents := FCurrentSize / FTotalSize;
+ end;
+ FProgressText := FormatByteSize(FCurrentSize);
+ if FTotalSize <> 0 then
+ FProgressText := FProgressText + '/' + FormatByteSize(FTotalSize);
+ end
+ else
+ if Reason = HR_Connect then
+ begin
+ FNeedRepaint := True;
+ FCurrentSize := 0;
+ FTotalSize := 0;
+ end;
+end;
+
+procedure TStatusBarDownload.UpdateStatusText(AStatusText: String);
+begin
+ if AStatusText <> FStatusText then
+ begin
+ FStatusText := AStatusText;
+ FNeedRepaint := True;
+ end;
+end;
+
+constructor TStatusBarDownload.Create(CreateSuspended: Boolean;
+ AOwnerForm: TForm; AImageList: TImageList; AButtonCancelImageIndex: Integer);
+begin
+ inherited Create(CreateSuspended);
+ FreeOnTerminate := True;
+ FOwnerForm := AOwnerForm;
+ FImageList := AImageList;
+ FButtonCancelImageIndex := AButtonCancelImageIndex;
+ FProgressText := '';
+ FStatusText := '';
+
+ FHTTP := THTTPSendThread.Create(Self);
+ FHTTP.UserAgent := UserAgentCURL;
+ FHTTP.Sock.OnStatus := @HTTPSockOnStatus;
+ FTotalSize := 0;
+ FTotalSize := 0;
+ FPercents := 0;
+
+ Synchronize(@SyncCreate);
+end;
+
+destructor TStatusBarDownload.Destroy;
+begin
+ Synchronize(@SyncDestroy);
+
+ FHTTP.Free;
+ inherited Destroy;
+end;
+
+end.
+
diff --git a/baseunits/WebsiteModules.pas b/baseunits/WebsiteModules.pas
index 48654d25c..0f946df7d 100644
--- a/baseunits/WebsiteModules.pas
+++ b/baseunits/WebsiteModules.pas
@@ -11,8 +11,8 @@ interface
uses
Classes, SysUtils, fgl, uData, uDownloadsManager, FMDOptions, httpsendthread,
- WebsiteModulesSettings, LazLogger, Cloudflare, RegExpr, fpjson, jsonparser,
- jsonscanner, fpjsonrtti;
+ WebsiteModulesSettings, Process, Multilog, LazLogger, Cloudflare, RegExpr, fpjson, jsonparser,
+ jsonscanner, fpjsonrtti, uBaseUnit, httpcookiemanager, syncobjs;
const
MODULE_NOT_FOUND = -1;
@@ -73,16 +73,18 @@ TWebsiteOptionItem = record
TWebsiteModuleAccount = class
private
- FCookies: String;
FEnabled: Boolean;
FPassword: String;
FStatus: TAccountStatus;
FUsername: String;
+ public
+ Guardian: TCriticalSection;
+ constructor Create;
+ destructor Destroy; override;
published
property Enabled: Boolean read FEnabled write FEnabled;
property Username: String read FUsername write FUsername;
property Password: String read FPassword write FPassword;
- property Cookies: String read FCookies write FCookies;
property Status: TAccountStatus read FStatus write FStatus;
end;
@@ -99,6 +101,7 @@ TModuleContainer = class
FTotalDirectory: Integer;
FCloudflareCF: TCFProps;
FCloudflareEnabled: Boolean;
+ FCookieManager: THTTPCookieManager;
procedure SetAccountSupport(AValue: Boolean);
procedure SetCloudflareEnabled(AValue: Boolean);
procedure CheckCloudflareEnabled(const AHTTP: THTTPSendThread);
@@ -107,6 +110,7 @@ TModuleContainer = class
procedure AddOption(const AOptionType: TWebsiteOptionType;
const ABindValue: Pointer; const AName: String; const ACaption: PString; const AItems: PString = nil);
public
+ Guardian: TCriticalSection;
Tag: Integer;
TagPtr: Pointer;
Website: String;
@@ -165,6 +169,7 @@ TModuleContainer = class
property Settings: TWebsiteModuleSettings read FSettings write FSettings;
property AccountSupport: Boolean read FAccountSupport write SetAccountSupport;
property Account: TWebsiteModuleAccount read FAccount write FAccount;
+ property CookieManager: THTTPCookieManager read FCookieManager;
end;
TModuleContainers = specialize TFPGList;
@@ -296,6 +301,7 @@ function CleanOptionName(const S: String): String;
implementation
+uses
{$I ModuleList.inc}
var
@@ -321,6 +327,19 @@ function CleanOptionName(const S: String): String;
Inc(i);
end;
+{ TWebsiteModuleAccount }
+
+constructor TWebsiteModuleAccount.Create;
+begin
+ Guardian := TCriticalSection.Create;
+end;
+
+destructor TWebsiteModuleAccount.Destroy;
+begin
+ Guardian.Free;
+ inherited Destroy;
+end;
+
{ TModuleContainer }
procedure TModuleContainer.SetCloudflareEnabled(AValue: Boolean);
@@ -328,7 +347,7 @@ procedure TModuleContainer.SetCloudflareEnabled(AValue: Boolean);
if FCloudflareEnabled = AValue then Exit;
FCloudflareEnabled := AValue;
if FCloudflareEnabled then
- FCloudflareCF := TCFProps.Create
+ FCloudflareCF := TCFProps.Create(self)
else
begin
FCloudflareCF.Free;
@@ -377,6 +396,7 @@ procedure TModuleContainer.SetTotalDirectory(AValue: Integer);
constructor TModuleContainer.Create;
begin
+ Guardian := TCriticalSection.Create;
FSettings := TWebsiteModuleSettings.Create;
FID := -1;
ActiveTaskCount := 0;
@@ -389,6 +409,7 @@ constructor TModuleContainer.Create;
TotalDirectory := 1;
CurrentDirectoryIndex := 0;
CloudflareEnabled := True;
+ FCookieManager := THTTPCookieManager.Create;
end;
destructor TModuleContainer.Destroy;
@@ -400,6 +421,8 @@ destructor TModuleContainer.Destroy;
if Assigned(FAccount) then
FAccount.Free;
FSettings.Free;
+ Guardian.Free;
+ FCookieManager.Free;
inherited Destroy;
end;
@@ -431,19 +454,21 @@ procedure TModuleContainer.PrepareHTTP(const AHTTP: THTTPSendThread);
var
s: String;
begin
+ AHTTP.CookieManager := FCookieManager;
+ //todo: replace it with website challenges, there is more than cloudflare
CheckCloudflareEnabled(AHTTP);
- if not Settings.Enabled then Exit;
+ if not Settings.Enabled then exit;
with Settings.HTTP do
begin
+ if Cookies<>'' then
+ AHTTP.MergeCookies(Cookies);
if UserAgent<>'' then
AHTTP.UserAgent:=UserAgent;
- if Cookies<>'' then
- AHTTP.Cookies.Text:=Cookies;
with Proxy do
begin
s:='';
case Proxy.ProxyType of
- ptDirect:AHTTP.SetNoProxy;
+ ptDefault,ptDirect:AHTTP.SetNoProxy;
ptHTTP:s:='HTTP';
ptSOCKS4:s:='SOCKS4';
ptSOCKS5:s:='SOCKS5';
@@ -845,7 +870,10 @@ function TWebsiteModules.Login(const AHTTP: THTTPSendThread;
if ModuleExist(ModuleId) then
with FModuleList[ModuleId] do
if Assigned(OnLogin) then
+ begin
+ PrepareHTTP(AHTTP);
Result := OnLogin(AHTTP, FModuleList[ModuleId]);
+ end;
end;
function TWebsiteModules.Login(const AHTTP: THTTPSendThread;
@@ -914,6 +942,8 @@ procedure TWebsiteModules.LoadFromFile;
fs: TFileStream;
jp: TJSONParser;
jo, jo2: TJSONObject;
+ j_cookies: TJSONArray;
+ c: THTTPCookie;
begin
if FModuleList.Count=0 then Exit;
if not FileExists(MODULES_FILE) then Exit;
@@ -934,6 +964,7 @@ procedure TWebsiteModules.LoadFromFile;
if (ja<>nil) and (ja.Count<>0) then
try
jd:=TJSONDeStreamer.Create(nil);
+ jd.Options:=jd.Options+[jdoIgnorePropertyErrors];
for i:=FModuleList.Count-1 downto 0 do
with FModuleList[i] do
begin
@@ -965,7 +996,23 @@ procedure TWebsiteModules.LoadFromFile;
begin
jo2:=jo.Get('Account',TJSONObject(nil));
if jo2<>nil then
+ begin
jd.JSONToObject(jo2,Account);
+ if Account.Username<>'' then Account.Username := DecryptString(Account.Username);
+ if Account.Password<>'' then Account.Password := DecryptString(Account.Password);
+ if Account.Status=asChecking then
+ Account.Status:=asUnknown;
+ end;
+ end;
+ j_cookies:=jo.Get('Cookies',TJSONArray(nil));
+ if Assigned(j_cookies) then
+ begin
+ for k:=0 to j_cookies.Count-1 do
+ begin
+ c:=THTTPCookie.Create;
+ CookieManager.Cookies.Add(c);
+ jd.JSONToObject(TJSONObject(j_cookies.Items[k]), c);
+ end;
end;
ja.Delete(j);
end;
@@ -980,14 +1027,15 @@ procedure TWebsiteModules.SaveToFile;
var
i, j: Integer;
js: TJSONStreamer;
- ja: TJSONArray;
- fs: TFileStream;
+ ja, j_cookies: TJSONArray;
+ fs: TMemoryStream;
jo: TJSONObject;
jo2: TJSONObject;
begin
if FModuleList.Count=0 then Exit;
ja:=TJSONArray.Create;
js:=TJSONStreamer.Create(nil);
+ js.Options:=js.Options+[jsoDateTimeAsString];
try
for i:=0 to FModuleList.Count-1 do
with FModuleList[i] do
@@ -1009,11 +1057,24 @@ procedure TWebsiteModules.SaveToFile;
end;
end;
if Account<>nil then
- jo.Add('Account',js.ObjectToJSON(Account));
+ begin
+ jo2:=js.ObjectToJSON(Account);
+ jo2.Strings['Username']:=EncryptString(Account.Username);
+ jo2.Strings['Password']:=EncryptString(Account.Password);
+ jo.Add('Account',jo2);
+ end;
+ j_cookies:=TJSONArray.Create;
+ for j:=0 to CookieManager.Cookies.Count-1 do
+ begin
+ if CookieManager.Cookies[j].Persistent then
+ j_cookies.Add(js.ObjectToJSON(CookieManager.Cookies[j]));
+ end;
+ jo.Add('Cookies', j_cookies);
end;
- fs:=TFileStream.Create(MODULES_FILE,fmCreate);
+ fs:=TMemoryStream.Create;
try
ja.DumpJSON(fs);
+ fs.SaveToFile(MODULES_FILE);
finally
fs.Free;
end;
diff --git a/baseunits/WebsiteModulesSettings.pas b/baseunits/WebsiteModulesSettings.pas
index 1c84a0654..9fbd9e439 100644
--- a/baseunits/WebsiteModulesSettings.pas
+++ b/baseunits/WebsiteModulesSettings.pas
@@ -42,6 +42,18 @@ THTTPSettings = class
property UserAgent: String read FUserAgent write FUserAgent;
property Proxy: TProxySettings read FProxy write FProxy;
end;
+
+ { TOverrideSettings }
+
+ TOverrideSettings = class
+ private
+ FSaveToPath: String;
+ public
+ constructor Create;
+ destructor Destroy; override;
+ published
+ property SaveToPath: String read FSaveToPath write FSaveToPath;
+ end;
{ TWebsiteModuleSettings }
@@ -49,6 +61,7 @@ TWebsiteModuleSettings = class
private
FEnabled: Boolean;
FHTTP: THTTPSettings;
+ FOverrideSettings: TOverrideSettings;
FMaxConnectionLimit: Integer;
FMaxTaskLimit: Integer;
FMaxThreadPerTaskLimit: Integer;
@@ -65,6 +78,7 @@ TWebsiteModuleSettings = class
property UpdateListNumberOfThread: Integer read FUpdateListNumberOfThread write FUpdateListNumberOfThread default 0;
property UpdateListDirectoryPageNumber: Integer read FUpdateListDirectoryPageNumber write FUpdateListDirectoryPageNumber default 0;
property HTTP: THTTPSettings read FHTTP write FHTTP;
+ property OverrideSettings: TOverrideSettings read FOverrideSettings write FOverrideSettings;
end;
implementation
@@ -82,16 +96,30 @@ destructor THTTPSettings.Destroy;
inherited Destroy;
end;
+{ TOverrideSettings }
+
+constructor TOverrideSettings.Create;
+begin
+
+end;
+
+destructor TOverrideSettings.Destroy;
+begin
+ inherited Destroy;
+end;
+
{ TWebsiteModuleSettings }
constructor TWebsiteModuleSettings.Create;
begin
HTTP:=THTTPSettings.Create;
+ OverrideSettings:=TOverrideSettings.Create;
end;
destructor TWebsiteModuleSettings.Destroy;
begin
HTTP.Free;
+ OverrideSettings.Free;
inherited Destroy;
end;
diff --git a/baseunits/httpcookiemanager.pas b/baseunits/httpcookiemanager.pas
new file mode 100644
index 000000000..b95b9d6ae
--- /dev/null
+++ b/baseunits/httpcookiemanager.pas
@@ -0,0 +1,259 @@
+unit httpcookiemanager;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, fgl, StrUtils, DateUtils, syncobjs, synautil, httpsend;
+
+type
+
+ { THTTPCookie }
+
+ THTTPCookie = class
+ private
+ FName,
+ FValue,
+ FDomain,
+ FPath,
+ FSameSite: String;
+ FExpires: TDateTime;
+ FHostOnly,
+ FHttpOnly,
+ FSecure,
+ FPersistent: Boolean;
+ published
+ property Name: String read FName write FName;
+ property Value: String read FValue write FValue;
+ property Domain: String read FDomain write FDomain;
+ property Path: String read FPath write FPath;
+ property SameSite: String read FSameSite write FSameSite;
+ property Expires: TDateTime read FExpires write FExpires;
+ property HostOnly: Boolean read FHostOnly write FHostOnly;
+ property HttpOnly: Boolean read FHttpOnly write FHttpOnly;
+ property Secure: Boolean read FSecure write FSecure;
+ property Persistent: Boolean read FPersistent write FPersistent;
+ end;
+
+ THTTPCookies = specialize TFPGObjectList;
+
+ { THTTPCookieManager }
+
+ THTTPCookieManager = class
+ private
+ FCookies: THTTPCookies;
+ FGuardian: TCriticalSection;
+ protected
+ procedure AddServerCookie(const AURL, ACookie: String; const AServerDate: TDateTime);
+ public
+ constructor Create;
+ destructor Destroy; override;
+ procedure AddServerCookies(const AURL: String; const AHTTP: THTTPSend);
+ procedure SetCookies(const AURL: String; const AHTTP: THTTPSend);
+ procedure Clear;
+ published
+ property Cookies: THTTPCookies read FCookies;
+ end;
+
+implementation
+
+{ defined in RFC 6265 https://tools.ietf.org/html/rfc6265 }
+
+{ THTTPCookieManager }
+
+constructor THTTPCookieManager.Create;
+begin
+ FGuardian := TCriticalSection.Create;
+ FCookies := THTTPCookies.Create(True);
+end;
+
+destructor THTTPCookieManager.Destroy;
+begin
+ FCookies.Free;
+ FGuardian.Free;
+ inherited Destroy;
+end;
+
+procedure THTTPCookieManager.AddServerCookie(const AURL, ACookie: String;
+ const AServerDate: TDateTime);
+var
+ s, n, ni, v: String;
+ c: THTTPCookie;
+ Prot, User, Pass, Host, Port, Path, Para: String;
+ i: Integer;
+begin
+ if Trim(ACookie) = '' then Exit;
+ n := Trim(SeparateLeft(ACookie, '='));
+ for i := 0 to FCookies.Count-1 do
+ begin
+ if n = FCookies[i].Name then
+ begin
+ FCookies.Delete(i);
+ Break;
+ end;
+ end;
+ c := THTTPCookie.Create;
+ FCookies.Add(c);
+ for s in ACookie.Split([';']) do
+ begin
+ n := Trim(SeparateLeft(s,'='));
+ v := Trim(SeparateRight(s, '='));
+ ni := LowerCase(n);
+ if ni = 'domain' then
+ {
+ leading %x2E (".") is ignored per revised specification
+ https://tools.ietf.org/html/rfc6265#section-4.1.2.3
+ }
+ c.Domain := LowerCase(TrimLeftSet(v, ['.']))
+ else if ni = 'path' then
+ c.Path := v
+ else if ni = 'expires' then
+ begin
+ c.Expires := DecodeRfcDateTime(v);
+ c.Persistent := True;
+ end
+ else if ni = 'max-age' then
+ begin
+ c.Expires := IncSecond(AServerDate, StrToIntDef(v, 0));
+ c.Persistent := True;
+ end
+ else if ni = 'secure' then
+ c.Secure := True
+ else if ni = 'httponly' then
+ c.HttpOnly := True
+ else if ni = 'samesite' then
+ c.SameSite := LowerCase(v)
+ else
+ begin
+ c.Name := n;
+ c.Value := v;
+ end;
+ end;
+
+ ParseURL(AURL, Prot, User, Pass, Host, Port, Path, Para);
+ if c.Domain = '' then
+ c.Domain := LowerCase(Host);
+ if c.Path = '' then
+ c.Path := Path;
+ if c.SameSite = '' then
+ c.SameSite := 'none';
+end;
+
+procedure THTTPCookieManager.AddServerCookies(const AURL: String;
+ const AHTTP: THTTPSend);
+var
+ i: Integer;
+ s: String;
+ d: TDateTime;
+begin
+ FGuardian.Enter;
+ try
+ s := Trim(AHTTP.Headers.Values['Date']);
+ if s <> '' then
+ d := DecodeRfcDateTime(s)
+ else
+ d := Now;
+ for i := 0 to AHTTP.Headers.Count - 1 do
+ begin
+ if Pos('set-cookie', LowerCase(AHTTP.Headers[i])) = 1 then
+ begin
+ AddServerCookie(AURL, Trim(AHTTP.Headers.ValueFromIndex[i]), d);
+ end;
+ end;
+ finally
+ FGuardian.Leave;
+ end;
+end;
+
+function CharEquals(const AString: string; const ACharPos: Integer; const AValue: Char): Boolean;
+begin
+ if ACharPos < 1 then Exit(False);
+ Result := (ACharPos <= Length(AString)) and (AString[ACharPos] = AValue);
+end;
+
+procedure THTTPCookieManager.SetCookies(const AURL: String;
+ const AHTTP: THTTPSend);
+var
+ Prot, User, Pass, Host, Port, Path, Para: String;
+ i: Integer;
+ c: THTTPCookie;
+
+ function IsPathMatch: Boolean;
+ begin
+ Result := SameText(Path, c.Path) or
+ ( Path.StartsWith(c.Path) and
+ ( c.Path.EndsWith('/') or CharEquals(Path, Length(c.Path), '/') )
+ );
+ end;
+
+ function IsDomainMatch: Boolean;
+ begin
+ Result := False;
+ if (Host <> '') and (c.Domain <> '') then
+ begin
+ if SameText(Host, c.Domain) then
+ Result := True
+ else
+ if Host.EndsWith(c.Domain) then
+ begin
+ if Copy(Host, 1, Length(Host)-Length(c.Domain)).EndsWith('.') then
+ Result := True;
+ end;
+ end;
+ end;
+
+ function MatchesHost: Boolean;
+ begin
+ if c.HostOnly then
+ Result := SameText(Host, c.Domain)
+ else
+ Result := IsDomainMatch;
+ end;
+
+ function IsHTTP: Boolean;
+ begin
+ Result := (Prot = 'http') or (Prot = 'https');
+ end;
+
+begin
+ if FCookies.Count = 0 then Exit;
+ FGuardian.Enter;
+ try
+ ParseURL(AURL, Prot, User, Pass, Host, Port, Path, Para);
+ Prot := LowerCase(Prot);
+ Host := LowerCase(Host);
+ i := 0;
+ while i <= FCookies.Count - 1 do
+ begin
+ c := FCookies[i];
+ if (c.Persistent) and (c.Expires <= Now) then
+ FCookies.Delete(i)
+ else
+ begin
+ Inc(i);
+ if MatchesHost and IsPathMatch and
+ ((not c.Secure) or (c.Secure and c.Secure)) and
+ ((not c.HttpOnly) or (c.HttpOnly and IsHTTP)) then
+ begin
+ AHTTP.Cookies.Values[c.Name] := c.Value;
+ end;
+ end;
+ end;
+ finally
+ FGuardian.Leave;
+ end;
+end;
+
+procedure THTTPCookieManager.Clear;
+begin
+ FGuardian.Enter;
+ try
+ FCookies.Clear;
+ finally
+ FGuardian.Leave;
+ end;
+end;
+
+end.
+
diff --git a/baseunits/httpsendthread.pas b/baseunits/httpsendthread.pas
index 377cbfc6e..b7f481e57 100644
--- a/baseunits/httpsendthread.pas
+++ b/baseunits/httpsendthread.pas
@@ -6,7 +6,7 @@ interface
uses
Classes, SysUtils, httpsend, synautil, synacode, ssl_openssl, blcksock,
- GZIPUtils, BaseThread, dateutils, strutils;
+ GZIPUtils, BaseThread, httpcookiemanager, dateutils, strutils;
const
@@ -61,11 +61,11 @@ THTTPSendThread = class(THTTPSend)
FFollowRedirection: Boolean;
FMaxRedirect: Integer;
FAllowServerErrorResponse: Boolean;
- FCookiesExpires: TDateTime;
procedure SetTimeout(AValue: Integer);
procedure OnOwnerTerminate(Sender: TObject);
protected
- procedure ParseCookiesExpires;
+ procedure SetHTTPCookies;
+ procedure ParseHTTPCookies;
function InternalHTTPRequest(const Method, URL: String; const Response: TObject = nil): Boolean;
public
constructor Create(AOwner: TBaseThread = nil);
@@ -77,6 +77,9 @@ THTTPSendThread = class(THTTPSend)
function POST(const URL: String; const POSTData: String = ''; const Response: TObject = nil): Boolean;
function XHR(const URL: String; const Response: TObject = nil): Boolean;
function GetCookies: String;
+ procedure MergeCookies(const ACookies: String);
+ function GetLastModified: TDateTime;
+ function GetOriginalFileName: String;
function ThreadTerminated: Boolean;
procedure RemoveCookie(const CookieName: String);
procedure SetProxy(const ProxyType, Host, Port, User, Pass: String);
@@ -84,19 +87,21 @@ THTTPSendThread = class(THTTPSend)
procedure SetNoProxy;
procedure SetDefaultProxy;
procedure Reset;
+ procedure ResetBasic;
+ procedure SaveDocumentToFile(const AFileName: String; const ATryOriginalFileName: Boolean = False; const ALastModified: TDateTime = -1);
property Timeout: Integer read FTimeout write SetTimeout;
property RetryCount: Integer read FRetryCount write FRetryCount;
property GZip: Boolean read FGZip write FGZip;
property FollowRedirection: Boolean read FFollowRedirection write FFollowRedirection;
property AllowServerErrorResponse: Boolean read FAllowServerErrorResponse write FAllowServerErrorResponse;
property Thread: TBaseThread read FOwner;
- property CookiesExpires: TDateTime read FCookiesExpires;
property MaxRedirect: Integer read FMaxRedirect write FMaxRedirect;
public
BeforeHTTPMethod: THTTPMethodEvent;
AfterHTTPMethod: THTTPMethodEvent;
OnHTTPRequest: THTTPRequestEvent;
OnRedirected: THTTPMethodRedirectEvent;
+ CookieManager: THTTPCookieManager;
property LastURL: String read FURL;
end;
@@ -117,10 +122,10 @@ function FormatByteSize(const ABytes: Integer; AShowPerSecond: Boolean = False):
const
UserAgentSynapse = 'Mozilla/4.0 (compatible; Synapse)';
- UserAgentCURL = 'curl/7.58.0';
+ UserAgentCURL = 'curl/7.68.0';
UserAgentGooglebot = 'Mozilla/5.0 (compatible; Googlebot/2.1; http://www.google.com/bot.html)';
UserAgentMSIE = 'Mozilla/5.0 (Windows NT 10.0; Win64; Trident/7.0; rv:11.0) like Gecko';
- UserAgentFirefox = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0';
+ UserAgentFirefox = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0';
UserAgentChrome = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36';
UserAgentVivaldi = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.90 Safari/537.36 Vivaldi/1.91.867.3';
UserAgentOpera = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 OPR/45.0.2552.888';
@@ -379,29 +384,16 @@ procedure THTTPSendThread.OnOwnerTerminate(Sender: TObject);
Sock.AbortSocket;
end;
-procedure THTTPSendThread.ParseCookiesExpires;
-var
- i, p: Integer;
- c: TDateTime;
- s: String;
+procedure THTTPSendThread.SetHTTPCookies;
begin
- FCookiesExpires := 0.0;
- for i := 0 to FHeaders.Count-1 do
- if Pos('set-cookie', LowerCase(FHeaders[i])) = 1 then
- begin
- s := SeparateRight(FHeaders[i], ':');
- p := Pos('expires', lowercase(s));
- if p <> 0 then
- begin
- s := Copy(s, p, Length(s));
- s := SeparateLeft(SeparateRight(s,'='),';');
- s := Trim(SeparateLeft(s, 'GMT'));
- c := DecodeRfcDateTime(s);
- if (FCookiesExpires = 0.0) or (c < FCookiesExpires) then
- FCookiesExpires := c;
- end;
- end;
- write
+ if Assigned(CookieManager) then
+ CookieManager.SetCookies(FURL, Self);
+end;
+
+procedure THTTPSendThread.ParseHTTPCookies;
+begin
+ if Assigned(CookieManager) then
+ CookieManager.AddServerCookies(FURL, Self);
end;
function THTTPSendThread.InternalHTTPRequest(const Method, URL: String;
@@ -422,6 +414,7 @@ constructor THTTPSendThread.Create(AOwner: TBaseThread);
Protocol := '1.1';
Headers.NameValueSeparator := ':';
Cookies.NameValueSeparator := '=';
+ Cookies.Delimiter := ';';
FGZip := True;
FFollowRedirection := True;
FAllowServerErrorResponse := False;
@@ -467,9 +460,9 @@ function THTTPSendThread.HTTPMethod(const Method, URL: string): Boolean;
aurl:=URL;
if Assigned(BeforeHTTPMethod) then
BeforeHTTPMethod(Self, amethod, aurl);
- FCookiesExpires := 0.0;
+ SetHTTPCookies;
Result := inherited HTTPMethod(amethod, aurl);
- ParseCookiesExpires;
+ ParseHTTPCookies;
if Assigned(AfterHTTPMethod) then
AfterHTTPMethod(Self, amethod, aurl);
end;
@@ -619,6 +612,37 @@ function THTTPSendThread.GetCookies: String;
end;
end;
+procedure THTTPSendThread.MergeCookies(const ACookies: String);
+var
+ s: String;
+begin
+ for s in ACookies.Split(';') do
+ begin
+ if Pos('=', s) > 0 then
+ Cookies.Values[SeparateLeft(s,'=')] := SeparateRight(s,'=');
+ end;
+end;
+
+function THTTPSendThread.GetLastModified: TDateTime;
+var
+ s: String;
+begin
+ Result := Now;
+ s := Trim(Headers.Values['last-modified']);
+ if s <> '' then
+ Result := DecodeRfcDateTime(s);
+end;
+
+function THTTPSendThread.GetOriginalFileName: String;
+var
+ s: String;
+begin
+ Result := '';
+ s := Headers.Values['content-disposition'];
+ if s <> '' then
+ Result := GetBetween('filename="', '"', s);
+end;
+
function THTTPSendThread.ThreadTerminated: Boolean;
begin
if Assigned(FOwner) then
@@ -729,6 +753,30 @@ procedure THTTPSendThread.Reset;
if FGZip then Headers.Values['Accept-Encoding'] := ' gzip, deflate';
end;
+procedure THTTPSendThread.ResetBasic;
+begin
+ Clear;
+ if FGZip then Headers.Values['Accept-Encoding'] := ' gzip, deflate';
+end;
+
+procedure THTTPSendThread.SaveDocumentToFile(const AFileName: String;
+ const ATryOriginalFileName: Boolean; const ALastModified: TDateTime);
+var
+ f: String;
+ d: TDateTime;
+begin
+ d := ALastModified;
+ if d = -1 then
+ d := GetLastModified;
+ f := '';
+ if ATryOriginalFileName then
+ f := GetOriginalFileName;
+ if f = '' then f := AFileName;
+ Document.SaveToFile(f);
+ if FileExists(AFileName) then
+ FileSetDate(AFileName, DateTimeToFileDate(d));
+end;
+
initialization
InitCriticalSection(CS_ALLHTTPSendThread);
ALLHTTPSendThread := TFPList.Create;
diff --git a/baseunits/lua/LuaBase.pas b/baseunits/lua/LuaBase.pas
index 14b087b19..1be9109bc 100644
--- a/baseunits/lua/LuaBase.pas
+++ b/baseunits/lua/LuaBase.pas
@@ -23,8 +23,9 @@ function LuaLoadFromStream(L: Plua_State; AStream: TMemoryStream; AName: PAnsiCh
implementation
uses
- LuaStrings, LuaBaseUnit, LuaRegExpr, LuaSynaUtil, LuaSynaCode, MultiLog,
- LuaCrypto, LuaImagePuzzle, LuaDuktape;
+ LuaStrings, LuaBaseUnit, LuaRegExpr, LuaPCRE2, LuaSynaUtil, LuaSynaCode,
+ MultiLog, LuaCrypto, LuaImagePuzzle, LuaDuktape, LuaCriticalSection,
+ LuaLogger, LuaUtils, LuaMemoryStream;
function luabase_print(L: Plua_State): Integer; cdecl;
var
@@ -36,7 +37,7 @@ function luabase_print(L: Plua_State): Integer; cdecl;
LUA_TBOOLEAN:
Logger.Send(BoolToStr(lua_toboolean(L, i), 'true', 'false'));
else
- Logger.Send(lua_tostring(L, i));
+ Logger.Send(luaGetString(L, i));
end;
end;
@@ -53,10 +54,12 @@ procedure LuaBaseRegister(L: Plua_State);
luaBaseUnitRegister(L);
luaRegExprRegister(L);
+ luaPCRE2Register(L);
luaSynaUtilRegister(L);
luaSynaCodeRegister(L);
luaCryptoRegister(L);
luaDuktapeRegister(L);
+ luaLoggerRegister(L);
luaClassRegisterAll(L);
end;
@@ -151,7 +154,7 @@ function LuaDumpFileToStream(L: Plua_State; AFilename: String;
except
Result.Free;
Result := nil;
- Logger.SendError(lua_tostring(L, -1));
+ Logger.SendError(luaGetString(L, -1));
end;
end;
diff --git a/baseunits/lua/LuaBaseUnit.pas b/baseunits/lua/LuaBaseUnit.pas
index 74480e510..4101e2988 100644
--- a/baseunits/lua/LuaBaseUnit.pas
+++ b/baseunits/lua/LuaBaseUnit.pas
@@ -16,19 +16,19 @@ implementation
function lua_pos(L: Plua_State): Integer; cdecl;
begin
- lua_pushinteger(L, Pos(lua_tostring(L, 1), lua_tostring(L, 2)));
+ lua_pushinteger(L, Pos(luaGetString(L, 1), luaGetString(L, 2)));
Result := 1;
end;
function lua_trim(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, Trim(lua_tostring(L, 1)));
+ lua_pushstring(L, Trim(luaGetString(L, 1)));
Result := 1;
end;
function lua_maybefillhost(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, MaybeFillHost(lua_tostring(L, 1), lua_tostring(L, 2)));
+ lua_pushstring(L, MaybeFillHost(luaGetString(L, 1), luaGetString(L, 2)));
Result := 1;
end;
@@ -44,10 +44,10 @@ function lua_invertstrings(L: Plua_State): Integer; cdecl;
function lua_mangainfostatusifpos(L: Plua_State): Integer; cdecl;
begin
case lua_gettop(L) of
- 3: lua_pushstring(L, MangaInfoStatusIfPos(lua_tostring(L, 1),
- lua_tostring(L, 2), lua_tostring(L, 3)));
- 2: lua_pushstring(L, MangaInfoStatusIfPos(lua_tostring(L, 1), lua_tostring(L, 2)));
- 1: lua_pushstring(L, MangaInfoStatusIfPos(lua_tostring(L, 1)));
+ 3: lua_pushstring(L, MangaInfoStatusIfPos(luaGetString(L, 1),
+ luaGetString(L, 2), luaGetString(L, 3)));
+ 2: lua_pushstring(L, MangaInfoStatusIfPos(luaGetString(L, 1), luaGetString(L, 2)));
+ 1: lua_pushstring(L, MangaInfoStatusIfPos(luaGetString(L, 1)));
else
Exit(0);
end;
@@ -56,49 +56,49 @@ function lua_mangainfostatusifpos(L: Plua_State): Integer; cdecl;
function lua_appendurldelim(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, AppendURLDelim(lua_tostring(L, 1)));
+ lua_pushstring(L, AppendURLDelim(luaGetString(L, 1)));
Result := 1;
end;
function lua_removeurldelim(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, RemoveURLDelim(lua_tostring(L, 1)));
+ lua_pushstring(L, RemoveURLDelim(luaGetString(L, 1)));
Result := 1;
end;
function lua_appendurldelimleft(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, AppendURLDelimLeft(lua_tostring(L, 1)));
+ lua_pushstring(L, AppendURLDelimLeft(luaGetString(L, 1)));
Result := 1;
end;
function lua_removeurldelimleft(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, RemoveURLDelimLeft(lua_tostring(L, 1)));
+ lua_pushstring(L, RemoveURLDelimLeft(luaGetString(L, 1)));
Result := 1;
end;
function lua_regexprgetmatch(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, RegExprGetMatch(lua_tostring(L, 1), lua_tostring(L, 2), lua_tointeger(L, 3)));
+ lua_pushstring(L, RegExprGetMatch(luaGetString(L, 1), luaGetString(L, 2), lua_tointeger(L, 3)));
Result := 1;
end;
function lua_htmldecode(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, HTMLDecode(lua_tostring(L, 1)));
+ lua_pushstring(L, HTMLDecode(luaGetString(L, 1)));
Result := 1;
end;
function lua_htmlencode(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, EscapeHTML(lua_tostring(L, 1)));
+ lua_pushstring(L, EscapeHTML(luaGetString(L, 1)));
Result := 1;
end;
function lua_urldecode(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, URLDecode(lua_tostring(L, 1)));
+ lua_pushstring(L, URLDecode(luaGetString(L, 1)));
Result := 1;
end;
@@ -112,7 +112,7 @@ function lua_incstr(L: Plua_State): Integer; cdecl;
if lua_isinteger(L, 1) then
lua_pushstring(L, IncStr(lua_tointeger(L, 1), n))
else
- lua_pushstring(L, IncStr(lua_tostring(L, 1), n));
+ lua_pushstring(L, IncStr(luaGetString(L, 1), n));
Result := 1;
end;
@@ -127,6 +127,12 @@ function lua_streamtostring(L: Plua_State): Integer; cdecl;
Result := 0;
end;
+function lua_stringtostream(L: Plua_State): Integer; cdecl;
+begin
+ Result := 0;
+ StringToStream(luaGetString(L, 1), TStream(luaGetUserData(L, 2)));
+end;
+
function lua_round(L: Plua_State): Integer; cdecl;
begin
lua_pushinteger(L, round(lua_tonumber(L, 1)));
@@ -145,6 +151,62 @@ function lua_getcurrenttime(L: Plua_State): Integer; cdecl;
Result := 1;
end;
+function lua_encryptstring(L: Plua_State): Integer; cdecl;
+begin
+ lua_pushstring(L, EncryptString(luaGetString(L, 1)));
+ Result := 1;
+end;
+
+function lua_decryptstring(L: Plua_State): Integer; cdecl;
+begin
+ lua_pushstring(L, DecryptString(luaGetString(L, 1)));
+ Result := 1;
+end;
+
+function lua_Base64Encode(L: Plua_State): Integer; cdecl;
+var
+ obj: TObject;
+begin
+ Result := 0;
+ if lua_isstring(L, 1) then
+ begin
+ lua_pushstring(L, Base64Encode(String(luaGetString(L, 1))));
+ Result := 1;
+ end
+ else
+ if lua_isuserdata(L, 1) then
+ begin
+ obj := TObject(luaGetUserData(L, 1));
+ if obj is TStream then
+ begin
+ lua_pushboolean(L, Base64Encode(TStream(obj)));
+ Result := 1;
+ end;
+ end;
+end;
+
+function lua_Base64Decode(L: Plua_State): Integer; cdecl;
+var
+ obj: TObject;
+begin
+ Result := 0;
+ if lua_isstring(L, 1) then
+ begin
+ lua_pushstring(L, Base64Decode(String(luaGetString(L, 1))));
+ Result := 1;
+ end
+ else
+ if lua_isuserdata(L, 1) then
+ begin
+ obj := TObject(luaGetUserData(L, 1));
+ if obj is TStream then
+ begin
+ lua_pushboolean(L, Base64Decode(TStream(obj)));
+ Result := 1;
+ end;
+ end;
+end;
+
procedure luaBaseUnitRegister(L: Plua_State);
begin
luaPushFunctionGlobal(L, 'Pos', @lua_pos);
@@ -162,9 +224,14 @@ procedure luaBaseUnitRegister(L: Plua_State);
luaPushFunctionGlobal(L, 'URLDecode', @lua_urldecode);
luaPushFunctionGlobal(L, 'IncStr', @lua_incstr);
luaPushFunctionGlobal(L, 'StreamToString', @lua_streamtostring);
+ luaPushFunctionGlobal(L, 'StringToStream', @lua_stringtostream);
luaPushFunctionGlobal(L, 'Round', @lua_round);
luaPushFunctionGlobal(L, 'TrimStrings', @lua_trimstrings);
luaPushFunctionGlobal(L, 'GetCurrentTime', @lua_getcurrenttime);
+ luaPushFunctionGlobal(L, 'EncryptString', @lua_encryptstring);
+ luaPushFunctionGlobal(L, 'DecryptString', @lua_decryptstring);
+ luaPushFunctionGlobal(L, 'Base64Encode', @lua_Base64Encode);
+ luaPushFunctionGlobal(L, 'Base64Decode', @lua_Base64Decode);
end;
end.
diff --git a/baseunits/lua/LuaClass.pas b/baseunits/lua/LuaClass.pas
index 59cef5d62..686a56e6c 100644
--- a/baseunits/lua/LuaClass.pas
+++ b/baseunits/lua/LuaClass.pas
@@ -95,7 +95,7 @@ function __index(L: Plua_State): Integer; cdecl;
Exit;
lua_getmetatable(L, 1); // 1 should be userdata
- lua_pushstring(L, AnsiLowerCase(lua_tostring(L, 2))); // 2 should be the key
+ lua_pushstring(L, AnsiLowerCase(luaGetString(L, 2))); // 2 should be the key
lua_rawget(L, -2); // get metatable[key]
if lua_istable(L, -1) then
@@ -127,7 +127,7 @@ function __newindex(L: Plua_State): Integer; cdecl;
Exit(0);
lua_getmetatable(L, 1);
- lua_pushstring(L, AnsiLowerCase(lua_tostring(L, 2)));
+ lua_pushstring(L, AnsiLowerCase(luaGetString(L, 2)));
lua_rawget(L, -2);
if lua_istable(L, -1) then
@@ -161,7 +161,7 @@ function __indexarray(L: Plua_State): Integer; cdecl;
begin
Result := 1;
- if lua_tostring(L, 2) = '__get' then
+ if luaGetString(L, 2) = '__get' then
begin
lua_pushvalue(L, 1);
Exit;
@@ -177,7 +177,7 @@ function __newindexarray(L: Plua_State): Integer; cdecl;
begin
Result := 1;
- if lua_tostring(L, 2) = '__set' then
+ if luaGetString(L, 2) = '__set' then
begin
lua_pushvalue(L, 1);
Exit;
@@ -438,7 +438,7 @@ function luaclass_string_get(L: Plua_State): Integer; cdecl;
function luaclass_string_set(L: Plua_State): Integer; cdecl;
begin
Result := 0;
- String(luaClassGetClosure(L)^) := lua_tostring(L, -1);
+ String(luaClassGetClosure(L)^) := luaGetString(L, -1);
end;
function luaclass_int_get(L: Plua_State): Integer; cdecl;
diff --git a/baseunits/lua/LuaCriticalSection.pas b/baseunits/lua/LuaCriticalSection.pas
new file mode 100644
index 000000000..36c14a826
--- /dev/null
+++ b/baseunits/lua/LuaCriticalSection.pas
@@ -0,0 +1,59 @@
+unit LuaCriticalSection;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, lua53;
+
+procedure luaCriticalSectionAddMetaTable(L: Plua_State; Obj: Pointer; MetaTable,
+ UserData: Integer; AutoFree: Boolean = False);
+
+implementation
+
+uses
+ syncobjs, LuaClass;
+
+type
+ TUserData = TCriticalSection;
+
+function lua_tryenter(L: Plua_State): Integer; cdecl;
+begin
+ lua_pushboolean(L, TUserData(luaClassGetObject(L)).TryEnter);
+ Result := 1;
+end;
+
+function lua_enter(L: Plua_State): Integer; cdecl;
+begin
+ Result := 0;
+ TUserData(luaClassGetObject(L)).Enter;
+end;
+
+function lua_Leave(L: Plua_State): Integer; cdecl;
+begin
+ Result := 0;
+ TUserData(luaClassGetObject(L)).Leave;
+end;
+
+const
+ methods: packed array [0..3] of luaL_Reg = (
+ (name: 'TryEnter'; func: @lua_tryenter),
+ (name: 'Enter'; func: @lua_enter),
+ (name: 'Leave'; func: @lua_Leave),
+ (name: nil; func: nil)
+ );
+
+procedure luaCriticalSectionAddMetaTable(L: Plua_State; Obj: Pointer; MetaTable,
+ UserData: Integer; AutoFree: Boolean = False);
+begin
+ with TUserData(Obj) do
+ begin
+ luaClassAddFunction(L, MetaTable, UserData, methods);
+ end;
+end;
+
+initialization
+ luaClassRegister(TCriticalSection, @luaCriticalSectionAddMetaTable);
+
+end.
diff --git a/baseunits/lua/LuaCrypto.pas b/baseunits/lua/LuaCrypto.pas
index ca3b65fbb..80da87429 100644
--- a/baseunits/lua/LuaCrypto.pas
+++ b/baseunits/lua/LuaCrypto.pas
@@ -16,25 +16,25 @@ implementation
function crypto_hextostr(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, HexToStr(lua_tostring(L, 1)));
+ lua_pushstring(L, HexToStr(luaGetString(L, 1)));
Result := 1;
end;
function crypto_strtohexstr(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, StrToHexStr(lua_tostring(L, 1)));
+ lua_pushstring(L, StrToHexStr(luaGetString(L, 1)));
Result := 1;
end;
function crypto_md5hex(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, MD5Hex(lua_tostring(L, 1)));
+ lua_pushstring(L, MD5Hex(luaGetString(L, 1)));
Result := 1;
end;
function crypto_aesdecryptcbc(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, AESDecryptCBC(lua_tostring(L, 1), lua_tostring(L, 2), lua_tostring(L, 3)));
+ lua_pushstring(L, AESDecryptCBC(luaGetString(L, 1), luaGetString(L, 2), luaGetString(L, 3)));
Result := 1;
end;
diff --git a/baseunits/lua/LuaDuktape.pas b/baseunits/lua/LuaDuktape.pas
index bb0e50f9e..4cd1165b5 100644
--- a/baseunits/lua/LuaDuktape.pas
+++ b/baseunits/lua/LuaDuktape.pas
@@ -15,7 +15,7 @@ implementation
function lua_execjs(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, ExecJS(lua_tostring(L, 1)));
+ lua_pushstring(L, ExecJS(luaGetString(L, 1)));
Result := 1;
end;
diff --git a/baseunits/lua/LuaHTTPSend.pas b/baseunits/lua/LuaHTTPSend.pas
index bbccc9aee..effa35b98 100644
--- a/baseunits/lua/LuaHTTPSend.pas
+++ b/baseunits/lua/LuaHTTPSend.pas
@@ -13,33 +13,33 @@ procedure luaHTTPSendThreadAddMetaTable(L: Plua_State; Obj: Pointer; MetaTable,
implementation
uses
- uBaseUnit, httpsendthread, LuaClass;
+ uBaseUnit, httpsendthread, LuaClass, LuaUtils;
type
TUserData = THTTPSendThread;
function http_get(L: Plua_State): Integer; cdecl;
begin
- lua_pushboolean(L, TUserData(luaClassGetObject(L)).GET(lua_tostring(L, 1)));
+ lua_pushboolean(L, TUserData(luaClassGetObject(L)).GET(luaGetString(L, 1)));
Result := 1;
end;
function http_post(L: Plua_State): Integer; cdecl;
begin
- lua_pushboolean(L, TUserData(luaClassGetObject(L)).POST(lua_tostring(L, 1),
- lua_tostring(L, 2)));
+ lua_pushboolean(L, TUserData(luaClassGetObject(L)).POST(luaGetString(L, 1),
+ luaGetString(L, 2)));
Result := 1;
end;
function http_head(L: Plua_State): Integer; cdecl;
begin
- lua_pushboolean(L, TUserData(luaClassGetObject(L)).head(lua_tostring(L, 1)));
+ lua_pushboolean(L, TUserData(luaClassGetObject(L)).head(luaGetString(L, 1)));
Result := 1;
end;
function http_xhr(L: Plua_State): Integer; cdecl;
begin
- lua_pushboolean(L, TUserData(luaClassGetObject(L)).XHR(lua_tostring(L, 1)));
+ lua_pushboolean(L, TUserData(luaClassGetObject(L)).XHR(luaGetString(L, 1)));
Result := 1;
end;
@@ -51,8 +51,8 @@ function http_reset(L: Plua_State): Integer; cdecl;
function http_getcookies(L: Plua_State): Integer; cdecl;
begin
- Result := 0;
- TUserData(luaClassGetObject(L)).GetCookies;
+ lua_pushstring(L, TUserData(luaClassGetObject(L)).GetCookies);
+ Result := 1;
end;
function http_threadterminated(L: Plua_State): Integer; cdecl;
@@ -64,8 +64,8 @@ function http_threadterminated(L: Plua_State): Integer; cdecl;
function http_setproxy(L: Plua_State): Integer; cdecl;
begin
Result := 0;
- TUserData(luaClassGetObject(L)).SetProxy(lua_tostring(L, 1), lua_tostring(L, 2),
- lua_tostring(L, 3), lua_tostring(L, 4), lua_tostring(L, 5));
+ TUserData(luaClassGetObject(L)).SetProxy(luaGetString(L, 1), luaGetString(L, 2),
+ luaGetString(L, 3), luaGetString(L, 4), luaGetString(L, 5));
end;
function http_threadlasturl(L: Plua_State): Integer; cdecl;
@@ -116,7 +116,7 @@ procedure luaHTTPSendThreadAddMetaTable(L: Plua_State; Obj: Pointer; MetaTable,
luaClassAddObject(L, MetaTable, Cookies, 'Cookies');
luaClassAddStringProperty(L, MetaTable, 'MimeType', @TUserData(Obj).MimeType);
luaClassAddStringProperty(L, MetaTable, 'UserAgent', @TUserData(Obj).UserAgent);
- luaClassAddUserData(L, MetaTable, TUserData(Obj).Document, 'Document');
+ luaClassAddObject(L, MetaTable, TUserData(Obj).Document, 'Document');
end;
end;
diff --git a/baseunits/lua/LuaIXQValue.pas b/baseunits/lua/LuaIXQValue.pas
index 486393fb4..7c1fee665 100644
--- a/baseunits/lua/LuaIXQValue.pas
+++ b/baseunits/lua/LuaIXQValue.pas
@@ -21,7 +21,7 @@ procedure luaIXQValuePush(L: Plua_State; Obj: TLuaIXQValue); inline;
implementation
uses
- LuaClass;
+ LuaClass, LuaUtils;
type
TUserData = TLuaIXQValue;
@@ -42,7 +42,31 @@ function ixqvalue_tostring(L: Plua_State): Integer; cdecl;
function ixqvalue_getattribute(L: Plua_State): Integer; cdecl;
begin
lua_pushstring(L, TUserData(luaClassGetObject(L)).FIXQValue.toNode.getAttribute(
- lua_tostring(L, 1)));
+ luaGetString(L, 1)));
+ Result := 1;
+end;
+
+function ixqvalue_getproperty(L: Plua_State): Integer; cdecl;
+begin
+ luaIXQValuePush(L, TUserData.Create(TUserData(luaClassGetObject(L)).FIXQValue.getProperty(luaGetString(L, 1))));
+ Result := 1;
+end;
+
+function ixqvalue_innerHTML(L: Plua_State): Integer; cdecl;
+begin
+ lua_pushstring(L, TUserData(luaClassGetObject(L)).FIXQValue.toNode.innerHTML());
+ Result := 1;
+end;
+
+function ixqvalue_outerHTML(L: Plua_State): Integer; cdecl;
+begin
+ lua_pushstring(L, TUserData(luaClassGetObject(L)).FIXQValue.toNode.outerHTML());
+ Result := 1;
+end;
+
+function ixqvalue_innerText(L: Plua_State): Integer; cdecl;
+begin
+ lua_pushstring(L, TUserData(luaClassGetObject(L)).FIXQValue.toNode.innerText());
Result := 1;
end;
@@ -59,9 +83,13 @@ function ixqvalue_get(L: Plua_State): Integer; cdecl;
end;
const
- methods: packed array [0..2] of luaL_Reg = (
- (name: 'GetAttribute'; func: @ixqvalue_getattribute),
- (name: 'Get'; func: @ixqvalue_get),
+ methods: packed array [0..6] of luaL_Reg = (
+ (name: 'get'; func: @ixqvalue_get),
+ (name: 'getAttribute'; func: @ixqvalue_getattribute),
+ (name: 'getProperty'; func: @ixqvalue_getproperty),
+ (name: 'innerHTML'; func: @ixqvalue_innerHTML),
+ (name: 'outerHTML'; func: @ixqvalue_outerHTML),
+ (name: 'innerText'; func: @ixqvalue_innerText),
(name: nil; func: nil)
);
props: packed array [0..2] of luaL_Reg_prop = (
diff --git a/baseunits/lua/LuaLogger.pas b/baseunits/lua/LuaLogger.pas
new file mode 100644
index 000000000..2c360887c
--- /dev/null
+++ b/baseunits/lua/LuaLogger.pas
@@ -0,0 +1,48 @@
+unit LuaLogger;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, lua53;
+
+procedure luaLoggerRegister(L: Plua_State);
+
+implementation
+
+uses
+ MultiLog, LuaClass, LuaUtils;
+
+function logger_send(L: Plua_State): Integer; cdecl;
+begin
+ Result := 0;
+ Logger.Send(luaGetString(L, 1));
+end;
+
+function logger_sendwarning(L: Plua_State): Integer; cdecl;
+begin
+ Result := 0;
+ Logger.SendWarning(luaGetString(L, 1));
+end;
+
+function logger_senderror(L: Plua_State): Integer; cdecl;
+begin
+ Result := 0;
+ Logger.SendError(luaGetString(L, 1));
+end;
+
+const
+ methods: packed array [0..3] of luaL_Reg = (
+ (name: 'Send'; func: @logger_send),
+ (name: 'SendWarning'; func: @logger_sendwarning),
+ (name: 'SendError'; func: @logger_senderror),
+ (name: nil; func: nil)
+ );
+
+procedure luaLoggerRegister(L: Plua_State);
+begin
+ luaClassNewLib(L, 'logger', methods);
+end;
+
+end.
diff --git a/baseunits/lua/LuaMemoryStream.pas b/baseunits/lua/LuaMemoryStream.pas
new file mode 100644
index 000000000..35908209d
--- /dev/null
+++ b/baseunits/lua/LuaMemoryStream.pas
@@ -0,0 +1,90 @@
+unit LuaMemoryStream;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, lua53;
+
+procedure luaMemoryStreamAddMetaTable(L: Plua_State; Obj: Pointer; MetaTable,
+ UserData: Integer; AutoFree: Boolean = False);
+
+implementation
+
+uses
+ uBaseUnit, LuaClass, LuaUtils;
+
+type
+ TUserData = TMemoryStream;
+
+function mem_toString(L: Plua_State): Integer; cdecl;
+begin
+ lua_pushstring(L, StreamToString(TUserData(luaClassGetObject(L))));
+ Result := 1;
+end;
+
+function mem_readAnsiString(L: Plua_State): Integer; cdecl;
+begin
+ lua_pushstring(L, TUserData(luaClassGetObject(L)).ReadAnsiString);
+ Result := 1;
+end;
+
+function mem_writeAnsiString(L: Plua_State): Integer; cdecl;
+begin
+ Result := 0;
+ TUserData(luaClassGetObject(L)).WriteAnsiString(luaGetString(L, 1));
+end;
+
+function mem_loadFromFile(L: Plua_State): Integer; cdecl;
+begin
+ Result := 0;
+ TUserData(luaClassGetObject(L)).LoadFromFile(luaGetString(L, 1));
+end;
+
+function mem_saveToFile(L: Plua_State): Integer; cdecl;
+begin
+ Result := 0;
+ TUserData(luaClassGetObject(L)).SaveToFile(luaGetString(L, 1));
+end;
+
+function mem_getSize(L: Plua_State): Integer; cdecl;
+begin
+ lua_pushinteger(L, TUserData(luaClassGetObject(L)).Size);
+ Result := 1;
+end;
+
+function mem_setSize(L: Plua_State): Integer; cdecl;
+begin
+ Result := 0;
+ TUserData(luaClassGetObject(L)).Size := lua_tointeger(L, 1);
+end;
+
+const
+ methods: packed array [0..5] of luaL_Reg = (
+ (name: 'ToString'; func: @mem_tostring),
+ (name: 'ReadAnsiString'; func: @mem_readAnsiString),
+ (name: 'WriteAnsiString'; func: @mem_writeAnsiString),
+ (name: 'LoadFromFile'; func: @mem_loadFromFile),
+ (name: 'SaveToFile'; func: @mem_saveToFile),
+ (name: nil; func: nil)
+ );
+ props: packed array[0..1] of luaL_Reg_prop = (
+ (name: 'Size'; funcget: @mem_getSize; funcset: @mem_setSize),
+ (name: nil; funcget: nil; funcset: nil)
+ );
+
+procedure luaMemoryStreamAddMetaTable(L: Plua_State; Obj: Pointer; MetaTable,
+ UserData: Integer; AutoFree: Boolean);
+begin
+ with TUserData(Obj) do
+ begin
+ luaClassAddFunction(L, MetaTable, UserData, methods);
+ luaClassAddProperty(L, MetaTable, UserData, props);
+ end;
+end;
+
+initialization
+ luaClassRegister(TMemoryStream, @luaMemoryStreamAddMetaTable);
+
+end.
diff --git a/baseunits/lua/LuaPCRE2.pas b/baseunits/lua/LuaPCRE2.pas
new file mode 100644
index 000000000..e1de1c0b6
--- /dev/null
+++ b/baseunits/lua/LuaPCRE2.pas
@@ -0,0 +1,44 @@
+unit LuaPCRE2;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, lua53;
+
+procedure luaPCRE2Register(L: Plua_State);
+
+implementation
+
+uses
+ pcre2, LuaClass, LuaUtils;
+
+function re_match(L: Plua_State): Integer; cdecl;
+begin
+ lua_pushboolean(L, PCRE2Match(luaGetString(L, 1), luaGetString(L, 2)));
+ Result := 1;
+end;
+
+function re_replace(L: Plua_State): Integer; cdecl;
+begin
+ if lua_gettop(L)=4 then
+ lua_pushstring(L, PCRE2Replace(luaGetString(L, 1), luaGetString(L, 2), luaGetString(L, 3),lua_toboolean(L,4)))
+ else
+ lua_pushstring(L, PCRE2Replace(luaGetString(L, 1), luaGetString(L, 2), luaGetString(L, 3)));
+ Result := 1;
+end;
+
+const
+ methods: packed array [0..2] of luaL_Reg = (
+ (name: 'match'; func: @re_match),
+ (name: 'replace'; func: @re_replace),
+ (name: nil; func: nil)
+ );
+
+procedure luaPCRE2Register(L: Plua_State);
+begin
+ luaClassNewLib(L,'re',methods);
+end;
+
+end.
diff --git a/baseunits/lua/LuaRegExpr.pas b/baseunits/lua/LuaRegExpr.pas
index ec3910ff8..c7680df9d 100644
--- a/baseunits/lua/LuaRegExpr.pas
+++ b/baseunits/lua/LuaRegExpr.pas
@@ -16,13 +16,13 @@ implementation
function re_exec(L: Plua_State): Integer; cdecl;
begin
- lua_pushboolean(L, ExecRegExpr(lua_tostring(L, 1), lua_tostring(L, 2)));
+ lua_pushboolean(L, ExecRegExpr(luaGetString(L, 1), luaGetString(L, 2)));
Result := 1;
end;
function re_replace(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, ReplaceRegExpr(lua_tostring(L, 1), lua_tostring(L, 2), lua_tostring(L, 3), True));
+ lua_pushstring(L, ReplaceRegExpr(luaGetString(L, 1), luaGetString(L, 2), luaGetString(L, 3), True));
Result := 1;
end;
diff --git a/baseunits/lua/LuaStrings.pas b/baseunits/lua/LuaStrings.pas
index 23b52c7dc..32391fd5d 100644
--- a/baseunits/lua/LuaStrings.pas
+++ b/baseunits/lua/LuaStrings.pas
@@ -29,7 +29,7 @@ function strings_create(L: Plua_State): Integer; cdecl;
function strings_loadfromfile(L: Plua_State): Integer; cdecl;
begin
Result := 0;
- TUserData(luaClassGetObject(L)).LoadFromFile(lua_tostring(L, 1));
+ TUserData(luaClassGetObject(L)).LoadFromFile(luaGetString(L, 1));
end;
function strings_loadfromstream(L: Plua_State): Integer; cdecl;
@@ -41,7 +41,7 @@ function strings_loadfromstream(L: Plua_State): Integer; cdecl;
function strings_settext(L: Plua_State): Integer; cdecl;
begin
Result := 0;
- TUserData(luaClassGetObject(L)).Text := lua_tostring(L, 1);
+ TUserData(luaClassGetObject(L)).Text := luaGetString(L, 1);
end;
function strings_gettext(L: Plua_State): Integer; cdecl;
@@ -52,7 +52,7 @@ function strings_gettext(L: Plua_State): Integer; cdecl;
function strings_setcommatext(L: Plua_State): Integer; cdecl;
begin
- TUserData(luaClassGetObject(L)).CommaText := lua_tostring(L, 1);
+ TUserData(luaClassGetObject(L)).CommaText := luaGetString(L, 1);
Result := 0;
end;
@@ -64,13 +64,13 @@ function strings_getcommatext(L: Plua_State): Integer; cdecl;
function strings_add(L: Plua_State): Integer; cdecl;
begin
- TUserData(luaClassGetObject(L)).Add(lua_tostring(L, 1));
+ TUserData(luaClassGetObject(L)).Add(luaGetString(L, 1));
Result := 0;
end;
function strings_addtext(L: Plua_State): Integer; cdecl;
begin
- TUserData(luaClassGetObject(L)).AddText(lua_tostring(L, 1));
+ TUserData(luaClassGetObject(L)).AddText(luaGetString(L, 1));
Result := 0;
end;
@@ -84,7 +84,32 @@ function strings_get(L: Plua_State): Integer; cdecl;
function strings_set(L: Plua_State): Integer; cdecl;
begin
Result := 0;
- TUserData(luaClassGetObject(L)).Strings[lua_tointeger(L, 1)] := lua_tostring(L, 2);
+ TUserData(luaClassGetObject(L)).Strings[lua_tointeger(L, 1)] := luaGetString(L, 2);
+end;
+
+function strings_getdelimitedtext(L: Plua_State): Integer; cdecl;
+begin
+ lua_pushstring(L, TUserData(luaClassGetObject(L)).DelimitedText);
+ Result := 1;
+end;
+
+function strings_setdelimitedtext(L: Plua_State): Integer; cdecl;
+begin
+ TUserData(luaClassGetObject(L)).DelimitedText := luaGetString(L, 1);
+ Result := 0;
+end;
+
+function strings_getdelimiter(L: Plua_State): Integer; cdecl;
+begin
+ lua_pushstring(L, PAnsiChar(
+ String(TUserData(luaClassGetObject(L)).Delimiter)));
+ Result := 1;
+end;
+
+function strings_setdelimiter(L: Plua_State): Integer; cdecl;
+begin
+ Result := 0;
+ TUserData(luaClassGetObject(L)).Delimiter := String(luaGetString(L, 1))[1];
end;
function strings_namevalueseparatorget(L: Plua_State): Integer; cdecl;
@@ -97,20 +122,20 @@ function strings_namevalueseparatorget(L: Plua_State): Integer; cdecl;
function strings_namevalueseparatorset(L: Plua_State): Integer; cdecl;
begin
Result := 0;
- TUserData(luaClassGetObject(L)).NameValueSeparator := String(lua_tostring(L, 1))[1];
+ TUserData(luaClassGetObject(L)).NameValueSeparator := String(luaGetString(L, 1))[1];
end;
function strings_valuesget(L: Plua_State): Integer; cdecl;
begin
lua_pushstring(L, PAnsiChar(
- TUserData(luaClassGetObject(L)).Values[lua_tostring(L, 1)]));
+ TUserData(luaClassGetObject(L)).Values[luaGetString(L, 1)]));
Result := 1;
end;
function strings_valuesset(L: Plua_State): Integer; cdecl;
begin
Result := 0;
- TUserData(luaClassGetObject(L)).Values[lua_tostring(L, 1)] := lua_tostring(L, 2);
+ TUserData(luaClassGetObject(L)).Values[luaGetString(L, 1)] := luaGetString(L, 2);
end;
function strings_getcount(L: Plua_State): Integer; cdecl;
@@ -139,13 +164,13 @@ function strings_delete(L: Plua_State): Integer; cdecl;
function strings_indexof(L: Plua_State): Integer; cdecl;
begin
- lua_pushinteger(L, TUserData(luaClassGetObject(L)).IndexOf(lua_tostring(L, 1)));
+ lua_pushinteger(L, TUserData(luaClassGetObject(L)).IndexOf(luaGetString(L, 1)));
Result := 1;
end;
function strings_indexofname(L: Plua_State): Integer; cdecl;
begin
- lua_pushinteger(L, TUserData(luaClassGetObject(L)).IndexOfName(lua_tostring(L, 1)));
+ lua_pushinteger(L, TUserData(luaClassGetObject(L)).IndexOfName(luaGetString(L, 1)));
Result := 1;
end;
@@ -172,12 +197,13 @@ function strings_indexofname(L: Plua_State): Integer; cdecl;
(name: 'IndexOfName'; func: @strings_indexofname),
(name: nil; func: nil)
);
- props: packed array[0..4] of lual_Reg_prop = (
+ props: packed array[0..6] of lual_Reg_prop = (
(name: 'Text'; funcget: @strings_gettext; funcset: @strings_settext),
(name: 'CommaText'; funcget: @strings_getcommatext; funcset: @strings_setcommatext),
(name: 'Count'; funcget: @strings_getcount; funcset: nil),
- (name: 'NameValueSeparator'; funcget: @strings_namevalueseparatorget;
- funcset: @strings_namevalueseparatorset),
+ (name: 'DelimitedText'; funcget: @strings_getdelimitedtext; funcset: @strings_setdelimitedtext),
+ (name: 'Delimiter'; funcget: @strings_getdelimiter; funcset: @strings_setdelimiter),
+ (name: 'NameValueSeparator'; funcget: @strings_namevalueseparatorget; funcset: @strings_namevalueseparatorset),
(name: nil; funcget: nil; funcset: nil)
);
arrprops: packed array[0..2] of lual_Reg_prop = (
diff --git a/baseunits/lua/LuaStringsStorage.pas b/baseunits/lua/LuaStringsStorage.pas
index 23949907d..7d1fdf82b 100644
--- a/baseunits/lua/LuaStringsStorage.pas
+++ b/baseunits/lua/LuaStringsStorage.pas
@@ -38,7 +38,7 @@ procedure luaStringsStorageAddMetaTable(L: Plua_State; Obj: Pointer;
implementation
-uses LuaClass;
+uses LuaClass, LuaUtils;
{ TStringsStorage }
@@ -54,7 +54,12 @@ function TStringsStorage.GetText: String;
procedure TStringsStorage.SetText(AValue: String);
begin
- FStrings.Text := AValue;
+ EnterCriticalsection(FCS);
+ try
+ FStrings.Text := AValue;
+ finally
+ LeaveCriticalsection(FCS);
+ end;
end;
procedure TStringsStorage.SetValues(const AName: String; AValue: String);
@@ -115,20 +120,20 @@ function strings_destroy(L: Plua_State): Integer; cdecl;
function strings_getvalue(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, TUserData(luaClassGetObject(L)).GetValues(lua_tostring(L, 1)));
+ lua_pushstring(L, TUserData(luaClassGetObject(L)).GetValues(luaGetString(L, 1)));
Result := 1;
end;
function strings_setvalue(L: Plua_State): Integer; cdecl;
begin
Result := 0;
- TUserData(luaClassGetObject(L)).SetValues(lua_tostring(L, 1), lua_tostring(L, 2));
+ TUserData(luaClassGetObject(L)).SetValues(luaGetString(L, 1), luaGetString(L, 2));
end;
function strings_remove(L: Plua_State): Integer; cdecl;
begin
Result := 0;
- TUserData(luaClassGetObject(L)).Remove(lua_tostring(L, 1));
+ TUserData(luaClassGetObject(L)).Remove(luaGetString(L, 1));
end;
function strings_gettext(L: Plua_State): Integer; cdecl;
@@ -140,7 +145,7 @@ function strings_gettext(L: Plua_State): Integer; cdecl;
function strings_settext(L: Plua_State): Integer; cdecl;
begin
Result := 0;
- TUserData(luaClassGetObject(L)).Text := lua_tostring(L, 1);
+ TUserData(luaClassGetObject(L)).Text := luaGetString(L, 1);
end;
const
diff --git a/baseunits/lua/LuaSynaCode.pas b/baseunits/lua/LuaSynaCode.pas
index e41284791..ea2f61951 100644
--- a/baseunits/lua/LuaSynaCode.pas
+++ b/baseunits/lua/LuaSynaCode.pas
@@ -16,97 +16,97 @@ implementation
function lua_decodeurl(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, DecodeURL(lua_tostring(L, 1)));
+ lua_pushstring(L, DecodeURL(luaGetString(L, 1)));
Result := 1;
end;
function lua_encodeurl(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, EncodeURL(lua_tostring(L, 1)));
+ lua_pushstring(L, EncodeURL(luaGetString(L, 1)));
Result := 1;
end;
function lua_decodeuu(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, DecodeUU(lua_tostring(L, 1)));
+ lua_pushstring(L, DecodeUU(luaGetString(L, 1)));
Result := 1;
end;
function lua_encodeuu(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, EncodeUU(lua_tostring(L, 1)));
+ lua_pushstring(L, EncodeUU(luaGetString(L, 1)));
Result := 1;
end;
function lua_encodeurlelement(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, EncodeURLElement(lua_tostring(L, 1)));
+ lua_pushstring(L, EncodeURLElement(luaGetString(L, 1)));
Result := 1;
end;
function lua_decodebase64(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, DecodeBase64(lua_tostring(L, 1)));
+ lua_pushstring(L, DecodeBase64(luaGetString(L, 1)));
Result := 1;
end;
function lua_encodebase64(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, EncodeBase64(lua_tostring(L, 1)));
+ lua_pushstring(L, EncodeBase64(luaGetString(L, 1)));
Result := 1;
end;
function lua_crc16(L: Plua_State): Integer; cdecl;
begin
- lua_pushinteger(L, Crc16(lua_tostring(L, 1)));
+ lua_pushinteger(L, Crc16(luaGetString(L, 1)));
Result := 1;
end;
function lua_crc32(L: Plua_State): Integer; cdecl;
begin
- lua_pushinteger(L, Crc32(lua_tostring(L, 1)));
+ lua_pushinteger(L, Crc32(luaGetString(L, 1)));
Result := 1;
end;
function lua_md4(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, MD4(lua_tostring(L, 1)));
+ lua_pushstring(L, MD4(luaGetString(L, 1)));
Result := 1;
end;
function lua_md5(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, MD5(lua_tostring(L, 1)));
+ lua_pushstring(L, MD5(luaGetString(L, 1)));
Result := 1;
end;
function lua_hmac_md5(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, HMAC_MD5(lua_tostring(L, 1), lua_tostring(L, 2)));
+ lua_pushstring(L, HMAC_MD5(luaGetString(L, 1), luaGetString(L, 2)));
Result := 1;
end;
function lua_md5longhash(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, MD5LongHash(lua_tostring(L, 1), lua_tointeger(L, 2)));
+ lua_pushstring(L, MD5LongHash(luaGetString(L, 1), lua_tointeger(L, 2)));
Result := 1;
end;
function lua_sha1(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, SHA1(lua_tostring(L, 1)));
+ lua_pushstring(L, SHA1(luaGetString(L, 1)));
Result := 1;
end;
function lua_hmac_sha1(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, HMAC_SHA1(lua_tostring(L, 1), lua_tostring(L, 2)));
+ lua_pushstring(L, HMAC_SHA1(luaGetString(L, 1), luaGetString(L, 2)));
Result := 1;
end;
function lua_sha1longhash(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, SHA1LongHash(lua_tostring(L, 1), lua_tointeger(L, 2)));
+ lua_pushstring(L, SHA1LongHash(luaGetString(L, 1), lua_tointeger(L, 2)));
Result := 1;
end;
diff --git a/baseunits/lua/LuaSynaUtil.pas b/baseunits/lua/LuaSynaUtil.pas
index 5e6917940..5f6eea237 100644
--- a/baseunits/lua/LuaSynaUtil.pas
+++ b/baseunits/lua/LuaSynaUtil.pas
@@ -16,25 +16,25 @@ implementation
function lua_getbetween(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, GetBetween(lua_tostring(L, 1), lua_tostring(L, 2), lua_tostring(L, 3)));
+ lua_pushstring(L, GetBetween(luaGetString(L, 1), luaGetString(L, 2), luaGetString(L, 3)));
Result := 1;
end;
function lua_separateleft(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, SeparateLeft(lua_tostring(L, 1), lua_tostring(L, 2)));
+ lua_pushstring(L, SeparateLeft(luaGetString(L, 1), luaGetString(L, 2)));
Result := 1;
end;
function lua_separateright(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, SeparateRight(lua_tostring(L, 1), lua_tostring(L, 2)));
+ lua_pushstring(L, SeparateRight(luaGetString(L, 1), luaGetString(L, 2)));
Result := 1;
end;
function lua_replacestring(L: Plua_State): Integer; cdecl;
begin
- lua_pushstring(L, ReplaceString(lua_tostring(L, 1), lua_tostring(L, 2), lua_tostring(L, 3)));
+ lua_pushstring(L, ReplaceString(luaGetString(L, 1), luaGetString(L, 2), luaGetString(L, 3)));
Result := 1;
end;
diff --git a/baseunits/lua/LuaUtils.pas b/baseunits/lua/LuaUtils.pas
index 61fe06d39..25b64f1a4 100644
--- a/baseunits/lua/LuaUtils.pas
+++ b/baseunits/lua/LuaUtils.pas
@@ -28,6 +28,7 @@ procedure luaPushBooleanGlobal(L: Plua_State; Name: PAnsiChar; B: Boolean);
procedure luaPushUserData(L: Plua_State; U: Pointer); overload; inline;
procedure luaPushUserData(L: Plua_State; U: Pointer; var UIndex: Integer); overload; inline;
function luaGetUserData(L: Plua_State; idx: Integer): Pointer; inline;
+function luaGetString(L: Plua_State; idx: Integer): String; inline;
function LuaToString(L: Plua_State; Idx: Integer): String;
function LuaStackToString(L: Plua_State): String;
@@ -130,13 +131,21 @@ function luaGetUserData(L: Plua_State; idx: Integer): Pointer;
Result := PPointer(lua_touserdata(L, idx))^;
end;
+function luaGetString(L: Plua_State; idx: Integer): String;
+var
+ slen: size_t;
+begin
+ Result := lua_tolstring(L, idx, @slen);
+ SetLength(Result, slen);
+end;
+
function LuaToString(L: Plua_State; Idx: Integer): String;
begin
if lua_isuserdata(L, Idx) then
Result := 'userdata: ' + hexStr(lua_touserdata(L, Idx))
else
if lua_isstring(L, Idx) then
- Result := 'string: ' + lua_tostring(L, Idx)
+ Result := 'string: ' + luaGetString(L, Idx)
else
if lua_isinteger(L, Idx) then
Result := 'integer: ' + IntToStr(lua_tointeger(L, Idx))
diff --git a/baseunits/lua/LuaWebsiteModules.pas b/baseunits/lua/LuaWebsiteModules.pas
index 65bbe4e73..831e8a6c1 100644
--- a/baseunits/lua/LuaWebsiteModules.pas
+++ b/baseunits/lua/LuaWebsiteModules.pas
@@ -5,7 +5,7 @@
interface
uses
- Classes, SysUtils, fgl, lua53, LuaStringsStorage, WebsiteModules;
+ Classes, SysUtils, fgl, lua53, LuaStringsStorage, WebsiteModules, syncobjs;
type
TLuaWebsiteModulesContainer = class;
@@ -68,11 +68,40 @@ TLuaWebsiteModulesContainer = class
TLuaWebsiteModulesManager = class
public
Containers: TLuaWebsiteModulesContainers;
- TempModuleList: TLuaWebsiteModules;
constructor Create;
destructor Destroy; override;
end;
+ { TLuaWebsiteModulesLoader }
+
+ TLuaWebsiteModulesLoader = class
+ private
+ FFileList:TStringList;
+ FThreadCount,
+ FFileListIndex:Integer;
+ FFileListGuardian,
+ FMainGuardian:TCriticalSection;
+ protected
+ function GetFileName:String;
+ public
+ procedure ScanAndLoadFiles;
+ constructor Create;
+ destructor Destroy; override;
+ end;
+
+ { TLuaWebsiteModulesLoaderThread }
+
+ TLuaWebsiteModulesLoaderThread = class(TThread)
+ private
+ FOwner: TLuaWebsiteModulesLoader;
+ protected
+ procedure LoadLuaWebsiteModule(const AFileName:String);
+ procedure Execute; override;
+ public
+ constructor Create(const AOwner: TLuaWebsiteModulesLoader);
+ destructor Destroy; override;
+ end;
+
TOptionItem = class
Caption: String;
end;
@@ -107,8 +136,11 @@ implementation
uses
FMDOptions, FileUtil, MultiLog, LuaClass, LuaBase, LuaMangaInfo, LuaHTTPSend,
- LuaXQuery, LuaUtils, LuaDownloadTask, LuaUpdateListManager, LuaStrings, uData,
- uDownloadsManager, xquery, httpsendthread, FMDVars;
+ LuaXQuery, LuaUtils, LuaDownloadTask, LuaUpdateListManager, LuaStrings,
+ LuaCriticalSection, uData, uDownloadsManager, xquery, httpsendthread, FMDVars;
+
+threadvar
+ TempModules:TLuaWebsiteModules;
function DoBeforeUpdateList(const Module: TModuleContainer): Boolean;
var
@@ -127,7 +159,7 @@ function DoBeforeUpdateList(const Module: TModuleContainer): Boolean;
Result := lua_toboolean(l, -1);
except
on E: Exception do
- Logger.SendError(E.Message + ': ' + lua_tostring(l, -1));
+ Logger.SendError(E.Message + ': ' + luaGetString(L, -1));
end;
lua_close(l);
end;
@@ -150,7 +182,7 @@ function DoAfterUpdateList(const Module: TModuleContainer): Boolean;
Result := lua_toboolean(l, -1);
except
on E: Exception do
- Logger.SendError(E.Message + ': ' + lua_tostring(l, -1));
+ Logger.SendError(E.Message + ': ' + luaGetString(L, -1));
end;
lua_close(l);
end;
@@ -180,7 +212,7 @@ function DoGetDirectoryPageNumber(const MangaInfo: TMangaInformation;
Page := lua_tointeger(l, -1);
except
on E: Exception do
- Logger.SendError(E.Message + ': ' + lua_tostring(l, -1));
+ Logger.SendError(E.Message + ': ' + luaGetString(L, -1));
end;
lua_close(l);
end;
@@ -210,7 +242,7 @@ function DoGetNameAndLink(const MangaInfo: TMangaInformation;
Result := lua_tointeger(L, -1);
except
on E: Exception do
- Logger.SendError(E.Message + ': ' + lua_tostring(l, -1));
+ Logger.SendError(E.Message + ': ' + luaGetString(L, -1));
end;
lua_close(l);
end;
@@ -236,7 +268,7 @@ function DoGetInfo(const MangaInfo: TMangaInformation; const AURL: String;
Result := lua_tointeger(L, -1);
except
on E: Exception do
- Logger.SendError(E.Message + ': ' + lua_tostring(l, -1));
+ Logger.SendError(E.Message + ': ' + luaGetString(L, -1));
end;
lua_close(l);
end;
@@ -259,7 +291,7 @@ function DoTaskStart(const Task: TTaskContainer; const Module: TModuleContainer)
Result := lua_toboolean(l, -1);
except
on E: Exception do
- Logger.SendError(E.Message + ': ' + lua_tostring(l, -1));
+ Logger.SendError(E.Message + ': ' + luaGetString(L, -1));
end;
lua_close(l);
end;
@@ -285,7 +317,7 @@ function DoGetPageNumber(const DownloadThread: TDownloadThread;
Result := lua_toboolean(l, -1);
except
on E: Exception do
- Logger.SendError(E.Message + ': ' + lua_tostring(l, -1));
+ Logger.SendError(E.Message + ': ' + luaGetString(L, -1));
end;
lua_close(l);
end;
@@ -312,7 +344,7 @@ function DoGetImageURL(const DownloadThread: TDownloadThread; const AURL: String
Result := lua_toboolean(l, -1);
except
on E: Exception do
- Logger.SendError(E.Message + ': ' + lua_tostring(l, -1));
+ Logger.SendError(E.Message + ': ' + luaGetString(L, -1));
end;
lua_close(l);
end;
@@ -339,7 +371,7 @@ function DoBeforeDownloadImage(const DownloadThread: TDownloadThread;
Result := lua_toboolean(l, -1);
except
on E: Exception do
- Logger.SendError(E.Message + ': ' + lua_tostring(l, -1));
+ Logger.SendError(E.Message + ': ' + luaGetString(L, -1));
end;
lua_close(l);
end;
@@ -366,7 +398,7 @@ function DoDownloadImage(const DownloadThread: TDownloadThread;
Result := lua_toboolean(l, -1);
except
on E: Exception do
- Logger.SendError(E.Message + ': ' + lua_tostring(l, -1));
+ Logger.SendError(E.Message + ': ' + luaGetString(L, -1));
end;
lua_close(l);
end;
@@ -389,10 +421,10 @@ function DoSaveImage(const AHTTP: THTTPSendThread; const APath, AName: String;
LuaDoMe(l);
LuaCallFunction(l, OnSaveImage);
- Result := lua_tostring(l, -1);
+ Result := luaGetString(L, -1);
except
on E: Exception do
- Logger.SendError(E.Message + ': ' + lua_tostring(l, -1));
+ Logger.SendError(E.Message + ': ' + luaGetString(L, -1));
end;
lua_close(l);
end;
@@ -415,7 +447,7 @@ function DoAfterImageSaved(const AFilename: String; const Module: TModuleContain
Result := lua_toboolean(l, -1);
except
on E: Exception do
- Logger.SendError(E.Message + ': ' + lua_tostring(l, -1));
+ Logger.SendError(E.Message + ': ' + luaGetString(L, -1));
end;
lua_close(l);
end;
@@ -438,13 +470,26 @@ function DoLogin(const AHTTP: THTTPSendThread; const Module: TModuleContainer):
Result := lua_toboolean(l, -1);
except
on E: Exception do
- Logger.SendError(E.Message + ': ' + lua_tostring(l, -1));
+ Logger.SendError(E.Message + ': ' + luaGetString(L, -1));
end;
lua_close(l);
end;
end;
-function LoadLuaToWebsiteModules(AFilename: String): Boolean;
+procedure ScanLuaWebsiteModulesFile;
+begin
+ with TLuaWebsiteModulesLoader.Create do
+ try
+ ScanAndLoadFiles;
+ finally
+ Free;
+ end;
+end;
+
+{ TLuaWebsiteModulesLoaderThread }
+
+procedure TLuaWebsiteModulesLoaderThread.LoadLuaWebsiteModule(
+ const AFileName: String);
var
l: Plua_State;
c: TLuaWebsiteModulesContainer;
@@ -452,12 +497,11 @@ function LoadLuaToWebsiteModules(AFilename: String): Boolean;
i: Integer;
s: String;
begin
- Result := False;
- Logger.Send('Load lua website module', AFilename);
+ //Logger.Send('Load lua website module', AFilename);
try
l := LuaNewBaseState;
try
- m := LuaDumpFileToStream(l, AFilename);
+ m := LuaDumpFileToStream(l, AFileName);
if m <> nil then
begin
i := lua_pcall(l, 0, 0, 0);
@@ -467,26 +511,28 @@ function LoadLuaToWebsiteModules(AFilename: String): Boolean;
end;
except
on E: Exception do
- Logger.SendError(E.Message + ': ' + lua_tostring(L, -1));
+ Logger.SendError(E.Message + ': ' + luaGetString(L, -1));
end;
finally
lua_close(l);
end;
- if LuaWebsiteModulesManager.TempModuleList.Count <> 0 then
+ if TempModules.Count <> 0 then
with LuaWebsiteModulesManager do
begin
c := TLuaWebsiteModulesContainer.Create;
- c.FileName := AFilename;
+ c.FileName := AFileName;
c.ByteCode := m;
m := nil;
s := '';
+ FOwner.FMainGuardian.Enter;
Containers.Add(c);
- for i := 0 to TempModuleList.Count - 1 do
- with TempModuleList[i] do
+ FOwner.FMainGuardian.Leave;
+ for i := 0 to TempModules.Count - 1 do
+ with TempModules[i] do
begin
s += Module.Website + ', ';
- c.Modules.Add(TempModuleList[i]);
+ c.Modules.Add(TempModules[i]);
Container := c;
if OnBeforeUpdateList <> '' then
Module.OnBeforeUpdateList := @DoBeforeUpdateList;
@@ -515,47 +561,97 @@ function LoadLuaToWebsiteModules(AFilename: String): Boolean;
if OnLogin <> '' then
Module.OnLogin := @DoLogin;
end;
- TempModuleList.Clear;
+ TempModules.Clear;
SetLength(s, Length(s) - 2);
- Logger.Send('Loaded modules from ' + ExtractFileName(AFilename), s);
+ //Logger.Send('Loaded modules from ' + ExtractFileName(AFilename), s);
s := '';
end;
if m <> nil then
m.Free;
end;
-procedure ScanLuaWebsiteModulesFile;
+procedure TLuaWebsiteModulesLoaderThread.Execute;
var
- d: String;
- f: TStringList;
- i: Integer;
+ f:String;
begin
- d := LUA_WEBSITEMODULE_FOLDER;
+ TempModules:=TLuaWebsiteModules.Create;
try
- f := FindAllFiles(d, '*.lua;*.luac', False, faAnyFile);
- if f.Count > 0 then
- for i := 0 to f.Count - 1 do
- LoadLuaToWebsiteModules(f[i]);
+ f:=FOwner.GetFileName;
+ while f<>'' do begin
+ LoadLuaWebsiteModule(f);
+ f:=FOwner.GetFileName;
+ end;
finally
- f.Free;
+ TempModules.Free;
end;
end;
+constructor TLuaWebsiteModulesLoaderThread.Create(
+ const AOwner: TLuaWebsiteModulesLoader);
+begin
+ FOwner:=AOwner;
+ FOwner.FThreadCount:=InterLockedIncrement(FOwner.FThreadCount);
+ FreeOnTerminate:=True;
+ inherited Create(False);
+end;
+
+destructor TLuaWebsiteModulesLoaderThread.Destroy;
+begin
+ FOwner.FThreadCount:=InterLockedDecrement(FOwner.FThreadCount);
+ inherited Destroy;
+end;
+
+{ TLuaWebsiteModulesLoader }
+
+function TLuaWebsiteModulesLoader.GetFileName: String;
+begin
+ if FFileListIndex>FFileList.Count-1 then Exit('');
+ FFileListGuardian.Enter;
+ Result:=FFileList[FFileListIndex];
+ Inc(FFileListIndex);
+ FFileListGuardian.Leave;
+end;
+
+procedure TLuaWebsiteModulesLoader.ScanAndLoadFiles;
+var
+ i: Integer;
+begin
+ FindAllFiles(FFileList, LUA_WEBSITEMODULE_FOLDER, '*.lua;*.luac', False, faAnyFile);
+ if FFileList.Count=0 then Exit;
+ for i:=1 to GetCPUCount do
+ TLuaWebsiteModulesLoaderThread.Create(Self);
+ while FThreadCount<>0 do
+ Sleep(100);
+end;
+
+constructor TLuaWebsiteModulesLoader.Create;
+begin
+ FFileList:=TStringList.Create;
+ FThreadCount:=0;
+ FFileListIndex:=0;
+ FFileListGuardian:=TCriticalSection.Create;
+ FMainGuardian:=TCriticalSection.Create;
+end;
+
+destructor TLuaWebsiteModulesLoader.Destroy;
+begin
+ FFileList.Free;
+ FFileListGuardian.Free;
+ FMainGuardian.Free;
+ inherited Destroy;
+end;
+
{ TLuaWebsiteModulesManager }
constructor TLuaWebsiteModulesManager.Create;
begin
Containers := TLuaWebsiteModulesContainers.Create;
- TempModuleList := TLuaWebsiteModules.Create;
end;
destructor TLuaWebsiteModulesManager.Destroy;
var
i: Integer;
begin
- for i := 0 to TempModuleList.Count - 1 do
- TempModuleList[i].Free;
- TempModuleList.Free;
for i := 0 to Containers.Count - 1 do
Containers[i].Free;
Containers.Free;
@@ -587,7 +683,7 @@ destructor TLuaWebsiteModulesContainer.Destroy;
constructor TLuaWebsiteModule.Create;
begin
- LuaWebsiteModulesManager.TempModuleList.Add(Self);
+ TempModules.Add(Self);
Storage := TStringsStorage.Create;
Options := TStringList.Create;
Options.OwnsObjects := True;
@@ -702,28 +798,28 @@ function lua_addoptioncheckbox(L: Plua_State): Integer; cdecl;
begin
Result := 0;
TLuaWebsiteModule(luaClassGetObject(L)).AddOptionCheckBox(
- lua_tostring(L, 1), lua_tostring(L, 2), lua_toboolean(L, 3));
+ luaGetString(L, 1), luaGetString(L, 2), lua_toboolean(L, 3));
end;
function lua_addoptionedit(L: Plua_State): Integer; cdecl;
begin
Result := 0;
TLuaWebsiteModule(luaClassGetObject(L)).AddOptionEdit(
- lua_tostring(L, 1), lua_tostring(L, 2), lua_tostring(L, 3));
+ luaGetString(L, 1), luaGetString(L, 2), luaGetString(L, 3));
end;
function lua_addoptionspinedit(L: Plua_State): Integer; cdecl;
begin
Result := 0;
TLuaWebsiteModule(luaClassGetObject(L)).AddOptionSpinEdit(
- lua_tostring(L, 1), lua_tostring(L, 2), lua_tointeger(L, 3));
+ luaGetString(L, 1), luaGetString(L, 2), lua_tointeger(L, 3));
end;
function lua_addoptioncombobox(L: Plua_State): Integer; cdecl;
begin
Result := 0;
TLuaWebsiteModule(luaClassGetObject(L)).AddOptionComboBox(
- lua_tostring(L, 1), lua_tostring(L, 2), lua_tostring(L, 3), lua_tointeger(L, 4));
+ luaGetString(L, 1), luaGetString(L, 2), luaGetString(L, 3), lua_tointeger(L, 4));
end;
function lua_gettotaldirectory(L: Plua_State): Integer; cdecl;
@@ -738,6 +834,12 @@ function lua_settotaldirectory(L: Plua_State): Integer; cdecl;
TLuaWebsiteModule(luaClassGetObject(L)).Module.TotalDirectory := lua_tointeger(L, 1);
end;
+function lua_clearcookies(L: Plua_State): Integer; cdecl;
+begin
+ Result := 0;
+ TLuaWebsiteModule(luaClassGetObject(L)).Module.CookieManager.Clear;
+end;
+
function lua_getoption(L: Plua_State): Integer; cdecl;
var
m: TLuaWebsiteModule;
@@ -745,7 +847,7 @@ function lua_getoption(L: Plua_State): Integer; cdecl;
o: TObject;
begin
m := TLuaWebsiteModule(luaClassGetObject(L));
- i:=m.Options.IndexOf(lua_tostring(L, 1));
+ i:=m.Options.IndexOf(luaGetString(L, 1));
Result := 1;
if i = -1 then
lua_pushnil(L)
@@ -781,11 +883,12 @@ function lua_setaccountsupport(L: Plua_State): Integer; cdecl;
end;
const
- methods: packed array [0..5] of luaL_Reg = (
+ methods: packed array [0..6] of luaL_Reg = (
(name: 'AddOptionCheckBox'; func: @lua_addoptioncheckbox),
(name: 'AddOptionEdit'; func: @lua_addoptionedit),
(name: 'AddOptionSpinEdit'; func: @lua_addoptionspinedit),
(name: 'AddOptionCombobox'; func: @lua_addoptioncombobox),
+ (name: 'ClearCookies'; func: @lua_clearcookies),
(name: 'GetOption'; func: @lua_getoption),
(name: nil; func: nil)
);
@@ -798,8 +901,8 @@ procedure luaWebsiteModuleAccountAddMetaTable(L: Plua_State; Obj: Pointer;
luaClassAddBooleanProperty(L, MetaTable, 'Enabled', @Enabled);
luaClassAddStringProperty(L, MetaTable, 'Username', @Username);
luaClassAddStringProperty(L, MetaTable, 'Password', @Password);
- luaClassAddStringProperty(L, MetaTable, 'Cookies', @Cookies);
luaClassAddIntegerProperty(L, MetaTable, 'Status', @Status);
+ luaClassAddObject(L, MetaTable, Guardian, 'Guardian', @luaCriticalSectionAddMetaTable);
end;
end;
@@ -808,6 +911,7 @@ procedure luaWebsiteModuleAddMetaTable(L: Plua_State; Obj: Pointer;
begin
with TLuaWebsiteModule(Obj) do
begin
+ luaClassAddObject(L, MetaTable, Module.Guardian, 'Guardian', @luaCriticalSectionAddMetaTable);
luaClassAddStringProperty(L, MetaTable, 'Website', @Module.Website);
luaClassAddStringProperty(L, MetaTable, 'RootURL', @Module.RootURL);
luaClassAddStringProperty(L, MetaTable, 'Category', @Module.Category);
diff --git a/baseunits/lua/LuaXQuery.pas b/baseunits/lua/LuaXQuery.pas
index 2d53ce9af..e33629ce6 100644
--- a/baseunits/lua/LuaXQuery.pas
+++ b/baseunits/lua/LuaXQuery.pas
@@ -23,7 +23,7 @@ function xquery_create(L: Plua_State): Integer; cdecl;
if lua_gettop(L) = 1 then
begin
if lua_isstring(L, 1) then
- luaXQueryPush(L, TXQueryEngineHTML.Create(lua_tostring(L, 1)), '', True)
+ luaXQueryPush(L, TXQueryEngineHTML.Create(luaGetString(L, 1)), '', True)
else
if lua_isuserdata(L, 1) then
luaXQueryPush(L, TXQueryEngineHTML.Create(TStream(luaGetUserData(L, 1))),
@@ -41,7 +41,7 @@ function xquery_parsehtml(L: Plua_State): Integer; cdecl;
Result := 0;
u := TUserData(luaClassGetObject(L));
if lua_isstring(L, 1) then
- u.ParseHTML(lua_tostring(L, 1))
+ u.ParseHTML(luaGetString(L, 1))
else
if lua_isuserdata(L, 1) then
u.ParseHTML(TStream(luaGetUserData(L, 1)));
@@ -54,23 +54,44 @@ function xquery_xpath(L: Plua_State): Integer; cdecl;
begin
u := TUserData(luaClassGetObject(L));
if lua_gettop(L) = 2 then
- x := u.XPath(lua_tostring(L, 1), TLuaIXQValue(luaGetUserData(L, 2)).FIXQValue)
+ x := u.XPath(luaGetString(L, 1), TLuaIXQValue(luaGetUserData(L, 2)).FIXQValue)
else
- x := u.XPath(lua_tostring(L, 1));
+ x := u.XPath(luaGetString(L, 1));
luaIXQValuePush(L, TLuaIXQValue.Create(x));
Result := 1;
end;
+function xquery_xpathi(L: Plua_State): Integer; cdecl;
+var
+ u: TUserData;
+ x: IXQValue;
+ t, i: Integer;
+begin
+ u := TUserData(luaClassGetObject(L));
+ if lua_gettop(L) = 2 then
+ x := u.XPath(luaGetString(L, 1), TLuaIXQValue(luaGetUserData(L, 2)).FIXQValue)
+ else
+ x := u.XPath(luaGetString(L, 1));
+ lua_newtable(L);
+ t := lua_gettop(L);
+ for i := 1 to x.Count do
+ begin
+ luaIXQValuePush(L, TLuaIXQValue.Create(x.get(i)));
+ lua_rawseti(L, t, i);
+ end;
+ Result := 1;
+end;
+
function xquery_xpathstring(L: Plua_State): Integer; cdecl;
var
u: TUserData;
begin
u := TUserData(luaClassGetObject(L));
if lua_gettop(L) = 2 then
- lua_pushstring(L, u.XPathString(lua_tostring(L, 1),
+ lua_pushstring(L, u.XPathString(luaGetString(L, 1),
TLuaIXQValue(luaGetUserData(L, 2)).FIXQValue))
else
- lua_pushstring(L, u.XPathString(lua_tostring(L, 1)));
+ lua_pushstring(L, u.XPathString(luaGetString(L, 1)));
Result := 1;
end;
@@ -82,33 +103,33 @@ function xquery_xpathstringall(L: Plua_State): Integer; cdecl;
u := TUserData(luaClassGetObject(L));
case lua_gettop(L) of
1: begin
- lua_pushstring(L, u.XPathStringAll(lua_tostring(L, 1)));
+ lua_pushstring(L, u.XPathStringAll(luaGetString(L, 1)));
Result := 1;
end;
2: begin
if lua_isstring(L, 2) then
begin
- lua_pushstring(L, u.XPathStringAll(lua_tostring(L, 1), lua_tostring(L, 2)));
+ lua_pushstring(L, u.XPathStringAll(luaGetString(L, 1), luaGetString(L, 2)));
Result := 1;
end
else
if lua_isuserdata(L, 2) then
begin
- u.XPathStringAll(lua_tostring(L, 1), TStrings(luaGetUserData(L, 2)));
+ u.XPathStringAll(luaGetString(L, 1), TStrings(luaGetUserData(L, 2)));
Result := 0;
end;
end;
3: begin
if lua_isstring(L, 2) then
begin
- lua_pushstring(L, u.XPathStringAll(lua_tostring(L, 1), lua_tostring(L, 2),
+ lua_pushstring(L, u.XPathStringAll(luaGetString(L, 1), luaGetString(L, 2),
TLuaIXQValue(luaGetUserData(L, 3)).FIXQValue));
Result := 1;
end
else
if lua_isuserdata(L, 2) then
begin
- u.XPathStringAll(lua_tostring(L, 1), TStrings(luaGetUserData(L, 2)),
+ u.XPathStringAll(luaGetString(L, 1), TStrings(luaGetUserData(L, 2)),
TLuaIXQValue(luaGetUserData(L, 3)).FIXQValue);
Result := 0;
end;
@@ -122,9 +143,9 @@ function xquery_xpathhrefall(L: Plua_State): Integer; cdecl;
begin
u := TUserData(luaClassGetObject(L));
case lua_gettop(L) of
- 3: u.XPathHREFAll(lua_tostring(L, 1), TStrings(luaGetUserData(L, 2)),
+ 3: u.XPathHREFAll(luaGetString(L, 1), TStrings(luaGetUserData(L, 2)),
TStrings(luaGetUserData(L, 3)));
- 4: u.XPathHREFAll(lua_tostring(L, 1), TStrings(luaGetUserData(L, 2)),
+ 4: u.XPathHREFAll(luaGetString(L, 1), TStrings(luaGetUserData(L, 2)),
TStrings(luaGetUserData(L, 3)), TLuaIXQValue(luaGetUserData(L, 4)).FIXQValue)
end;
Result := 0;
@@ -136,9 +157,9 @@ function xquery_xpathhreftitleall(L: Plua_State): Integer; cdecl;
begin
u := TUserData(luaClassGetObject(L));
case lua_gettop(L) of
- 3: u.XPathHREFtitleAll(lua_tostring(L, 1), TStrings(luaGetUserData(L, 2)),
+ 3: u.XPathHREFtitleAll(luaGetString(L, 1), TStrings(luaGetUserData(L, 2)),
TStrings(luaGetUserData(L, 3)));
- 4: u.XPathHREFtitleAll(lua_tostring(L, 1), TStrings(luaGetUserData(L, 2)),
+ 4: u.XPathHREFtitleAll(luaGetString(L, 1), TStrings(luaGetUserData(L, 2)),
TStrings(luaGetUserData(L, 3)), TLuaIXQValue(luaGetUserData(L, 4)).FIXQValue)
end;
Result := 0;
@@ -150,10 +171,10 @@ function xquery_xpathcount(L: Plua_State): Integer; cdecl;
begin
u := TUserData(luaClassGetObject(L));
if lua_gettop(L) = 2 then
- lua_pushinteger(L, u.XPathCount(lua_tostring(L, 1),
+ lua_pushinteger(L, u.XPathCount(luaGetString(L, 1),
TLuaIXQValue(luaGetUserData(L, 2)).FIXQValue))
else
- lua_pushinteger(L, u.XPathCount(lua_tostring(L, 1)));
+ lua_pushinteger(L, u.XPathCount(luaGetString(L, 1)));
Result := 1;
end;
@@ -163,9 +184,10 @@ function xquery_xpathcount(L: Plua_State): Integer; cdecl;
(name: 'Create'; func: @xquery_create),
(name: nil; func: nil)
);
- methods: packed array [0..7] of luaL_Reg = (
+ methods: packed array [0..8] of luaL_Reg = (
(name: 'ParseHTML'; func: @xquery_parsehtml),
(name: 'XPath'; func: @xquery_xpath),
+ (name: 'XPathI'; func: @xquery_xpathi),
(name: 'XPathString'; func: @xquery_xpathstring),
(name: 'XpathStringAll'; func: @xquery_xpathstringall),
(name: 'XpathHREFAll'; func: @xquery_xpathhrefall),
diff --git a/baseunits/modules/AcademyVN.pas b/baseunits/modules/AcademyVN.pas
deleted file mode 100644
index 61b80afb6..000000000
--- a/baseunits/modules/AcademyVN.pas
+++ /dev/null
@@ -1,134 +0,0 @@
-unit AcademyVN;
-
-{$mode objfpc}{$H+}
-
-interface
-
-uses
- Classes, SysUtils, WebsiteModules, uData, uBaseUnit, uDownloadsManager,
- XQueryEngineHTML, httpsendthread, synautil;
-
-implementation
-
-const
- dirurl = '/manga/all';
-
-function GetDirectoryPageNumber(const MangaInfo: TMangaInformation;
- var Page: Integer; const WorkPtr: Integer; const Module: TModuleContainer): Integer;
-begin
- Result := NET_PROBLEM;
- Page := 1;
- if MangaInfo = nil then Exit(UNKNOWN_ERROR);
- if MangaInfo.FHTTP.GET(Module.RootURL + dirurl) then begin
- Result := NO_ERROR;
- with TXQueryEngineHTML.Create(MangaInfo.FHTTP.Document) do
- try
- page := StrToIntDef(XPathString('(//*[starts-with(@class,"pagination")]//a)[last()-1]'), 1);
- finally
- Free;
- end;
- end;
-end;
-
-function GetNameAndLink(const MangaInfo: TMangaInformation;
- const ANames, ALinks: TStringList; const AURL: String;
- const Module: TModuleContainer): Integer;
-var
- v: IXQValue;
- s: String;
-begin
- Result := NET_PROBLEM;
- if MangaInfo = nil then Exit(UNKNOWN_ERROR);
- s := Module.RootURL + dirurl;
- if AURL <> '0' then s += '?page=' + IncStr(AURL);
- if MangaInfo.FHTTP.GET(s) then begin
- Result := NO_ERROR;
- with TXQueryEngineHTML.Create(MangaInfo.FHTTP.Document) do
- try
- for v in XPath('//table[1]/tbody/tr/td[1]/a') do begin
- ALinks.Add(v.toNode.getAttribute('href'));
- ANames.Add(v.toString);
- end;
- finally
- Free;
- end;
- end;
-end;
-
-function GetInfo(const MangaInfo: TMangaInformation;
- const AURL: String; const Module: TModuleContainer): Integer;
-var
- v: IXQValue;
- s: String;
-begin
- Result := NET_PROBLEM;
- if MangaInfo = nil then Exit(UNKNOWN_ERROR);
- with MangaInfo.FHTTP, MangaInfo.mangaInfo do begin
- if GET(FillHost(Module.RootURL, AURL)) then begin
- Result := NO_ERROR;
- with TXQueryEngineHTML.Create(Document) do
- try
- coverLink := XPathString('//*[@class="__image"]/img/@src');
- if coverLink <> '' then coverLink := MaybeFillHost(Module.RootURL, coverLink);
- if title = '' then title := XPathString('//*[@class="__info"]/h3');
- authors := SeparateRight(XPathString('//*[@class="__info"]/p[starts-with(.,"Tác giả:")]'), ':');
- genres := SeparateRight(XPathString('//*[@class="__info"]/p[starts-with(.,"Thể loại:")]'), ':');
- s := XPathString('//*[@class="__info"]/p[starts-with(.,"Tình trạng:")]');
- if s <> '' then begin
- if (Pos('Đang tiến hành', s) > 0) or (Pos('Ngưng', s) > 0) then status := '1'
- else status := '0';
- end;
- summary := XPathString('//*[@class="__info"]/*[@class="__description"]');
- for v in XPath('//*[@class="table-scroll"]/table/tbody/tr/td[1]/a') do begin
- chapterLinks.Add(v.toNode.getAttribute('href'));
- chapterName.Add(v.toString);
- end;
- InvertStrings([chapterLinks, chapterName]);
- finally
- Free;
- end;
- end;
- end;
-end;
-
-function GetPageNumber(const DownloadThread: TDownloadThread;
- const AURL: String; const Module: TModuleContainer): Boolean;
-var
- v: IXQValue;
-begin
- Result := False;
- if DownloadThread = nil then Exit;
- with DownloadThread.FHTTP, DownloadThread.Task.Container do begin
- PageLinks.Clear;
- PageNumber := 0;
- if GET(FillHost(Module.RootURL, AURL)) then begin
- Result := True;
- with TXQueryEngineHTML.Create(Document) do
- try
- for v in XPath('//*[@class="manga-container"]/img/@src') do
- PageLinks.Add(v.toString);
- finally
- Free;
- end;
- end;
- end;
-end;
-
-procedure RegisterModule;
-begin
- with AddModule do
- begin
- Website := 'AcademyVN';
- RootURL := 'http://hocvientruyentranh.com';
- Category := 'Vietnamese';
- OnGetDirectoryPageNumber := @GetDirectoryPageNumber;
- OnGetNameAndLink := @GetNameAndLink;
- OnGetInfo := @GetInfo;
- OnGetPageNumber := @GetPageNumber;
- end;
-end;
-
-initialization
- RegisterModule;
-
-end.
diff --git a/baseunits/modules/Cloudflare.pas b/baseunits/modules/Cloudflare.pas
index 2255ff358..d5d1b8991 100644
--- a/baseunits/modules/Cloudflare.pas
+++ b/baseunits/modules/Cloudflare.pas
@@ -6,7 +6,7 @@ interface
uses
Classes, SysUtils, uBaseUnit, XQueryEngineHTML, httpsendthread, synautil,
- JSUtils, RegExpr, dateutils;
+ synacode, JSUtils, RegExpr, dateutils;
type
@@ -14,21 +14,20 @@ interface
TCFProps = class
public
- cf_clearance: string;
- Expires: TDateTime;
+ websitemodule: TObject;
CS: TRTLCriticalSection;
- constructor Create;
+ constructor Create(awebsitemodule: TObject);
destructor Destroy; override;
- procedure Reset;
- procedure AddCookiesTo(const ACookies: TStringList);
end;
function CFRequest(const AHTTP: THTTPSendThread; const Method, AURL: String; const Response: TObject; const CFProps: TCFProps): Boolean;
implementation
+uses WebsiteModules, MultiLog;
+
const
- MIN_WAIT_TIME = 4000;
+ MIN_WAIT_TIME = 5000;
function AntiBotActive(const AHTTP: THTTPSendThread): Boolean;
var
@@ -43,10 +42,13 @@ function AntiBotActive(const AHTTP: THTTPSendThread): Boolean;
s := '';
end;
-function JSGetAnsweredURL(const Source, URL: String; var OMethod, OURL: String;
+function JSGetAnsweredURL(const Source, URL: String; var OMethod, OURL, opostdata: String;
var OSleepTime: Integer): Boolean;
var
- s, meth, surl, jschl_vc, pass, jschl_answer: String;
+ meth, surl, r, jschl_vc, pass, jschl_answer,
+ body, javascript, challenge, innerHTML, i, k, domain: String;
+ re: TRegExpr;
+ ms: Integer;
begin
Result := False;
if (Source = '') or (URL = '') then Exit;
@@ -61,48 +63,74 @@ function JSGetAnsweredURL(const Source, URL: String; var OMethod, OURL: String;
try
meth := UpperCase(XPathString('//form[@id="challenge-form"]/@method'));
surl := XPathString('//form[@id="challenge-form"]/@action');
+ r:=xpathstring('//input[@name="r"]/@value');
jschl_vc := XPathString('//input[@name="jschl_vc"]/@value');
pass := XPathString('//input[@name="pass"]/@value');
finally
Free;
end;
- if (meth = '') or (surl = '') or (jschl_vc = '') or (pass = '') then Exit;
-
- s := Source;
- with TRegExpr.Create do
- try
- ModifierG := False;
- ModifierI := True;
-
- Expression := 'setTimeout\(function\(\)\{\s*var.*\w,\s+(\S.+a\.value =[^;]+;)';
- Exec(s);
- if SubExprMatchCount > 0 then
- begin
- s := Match[1];
- Expression := '\s{3,}[a-z](\s*=\s*document\.|\.).+;\r?\n';
- s := Replace(s, '', False);
- Expression := 't\s=\s*t\.firstChild.href;';
- s := Replace(s, ' t = "' + URL + '";', False);
- Expression := 'a\.value\s*=';
- s := Replace(s, 'a =', False);
- Expression := '^.*\.submit\(.*\},\s*(\d{4,})\).*$';
- OSleepTime := StrToIntDef(Replace(Source, '$1', True), MIN_WAIT_TIME);
- jschl_answer := ExecJS(s);
- end;
- finally
- Free;
+ if (meth = '') or (surl = '') or (r='') or (jschl_vc = '') or (pass = '') then Exit;
+
+ re:=TRegExpr.Create;
+ try
+ body:=source;
+ // main script
+ re.Expression := '\