From 978cb423736d59789b16468907b8ff4d10cc1821 Mon Sep 17 00:00:00 2001 From: Arnaud Bouchez Date: Mon, 30 Dec 2019 11:19:12 +0100 Subject: [PATCH] introducing new ThirdParty sample from https://github.com/krkodil/mORMot --- .../ThirdPartyDemos/KroKodil/README.md | 16 + .../ThirdPartyDemos/KroKodil/moRMOt.iml | 16 + .../Samples/ThirdPartyDemos/KroKodil/pom.xml | 28 ++ .../ThirdPartyDemos/KroKodil/server.png | Bin 0 -> 44361 bytes .../KroKodil/src/delphi/CalcInterface.pas | 31 ++ .../KroKodil/src/delphi/Client.dpr | 52 ++++ .../KroKodil/src/delphi/Client.res | Bin 0 -> 1540 bytes .../KroKodil/src/delphi/ClientForm.dfm | 213 +++++++++++++ .../KroKodil/src/delphi/ClientForm.pas | 283 ++++++++++++++++++ .../KroKodil/src/delphi/FileCollect.pas | 73 +++++ .../KroKodil/src/delphi/REST.bdsgroup | 20 ++ .../KroKodil/src/delphi/REST.bpg | 23 ++ .../KroKodil/src/delphi/SampleData.pas | 39 +++ .../KroKodil/src/delphi/Server.dpr | 36 +++ .../KroKodil/src/delphi/Server.res | Bin 0 -> 1540 bytes .../KroKodil/src/delphi/ServerForm.dfm | 47 +++ .../KroKodil/src/delphi/ServerForm.pas | 191 ++++++++++++ .../KroKodil/src/delphi/bin/SHA Client.lnk | Bin 0 -> 1184 bytes .../KroKodil/src/delphi/bin/SHA Server.lnk | Bin 0 -> 1184 bytes .../KroKodil/src/delphi/bin/SSL Client.lnk | Bin 0 -> 1192 bytes .../KroKodil/src/delphi/bin/SSL Server.lnk | Bin 0 -> 1184 bytes .../KroKodil/src/delphi/bin/server.db3 | Bin 0 -> 40960 bytes .../KroKodil/src/delphi/bin/users.json | 10 + .../KroKodil/src/delphi/ssl/SignRoot.pvk | Bin 0 -> 1196 bytes .../KroKodil/src/delphi/ssl/signroot.cer | Bin 0 -> 830 bytes .../KroKodil/src/delphi/ssl/ssl.bat | 6 + .../KroKodil/src/main/java/CRC32.java | 59 ++++ .../KroKodil/src/main/java/RESTClient.java | 47 +++ .../KroKodil/src/main/java/SynClient.java | 203 +++++++++++++ .../KroKodil/src/main/java/SynTest.java | 28 ++ SynopseCommit.inc | 2 +- 31 files changed, 1422 insertions(+), 1 deletion(-) create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/README.md create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/moRMOt.iml create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/pom.xml create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/server.png create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/CalcInterface.pas create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/Client.dpr create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/Client.res create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/ClientForm.dfm create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/ClientForm.pas create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/FileCollect.pas create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/REST.bdsgroup create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/REST.bpg create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/SampleData.pas create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/Server.dpr create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/Server.res create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/ServerForm.dfm create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/ServerForm.pas create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/bin/SHA Client.lnk create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/bin/SHA Server.lnk create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/bin/SSL Client.lnk create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/bin/SSL Server.lnk create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/bin/server.db3 create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/bin/users.json create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/ssl/SignRoot.pvk create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/ssl/signroot.cer create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/ssl/ssl.bat create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/main/java/CRC32.java create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/main/java/RESTClient.java create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/main/java/SynClient.java create mode 100644 SQLite3/Samples/ThirdPartyDemos/KroKodil/src/main/java/SynTest.java diff --git a/SQLite3/Samples/ThirdPartyDemos/KroKodil/README.md b/SQLite3/Samples/ThirdPartyDemos/KroKodil/README.md new file mode 100644 index 000000000..8e9560935 --- /dev/null +++ b/SQLite3/Samples/ThirdPartyDemos/KroKodil/README.md @@ -0,0 +1,16 @@ +# mORMot +Java client for Synopse mORMot server + +Java REST client for fantastic Synopse API. This client is focused to Interface based services. + + +Example usage: +
+SynClient client = new SynClient("127.0.0.1", 8080, "service", false);
+if (client.login("User", "synopse")) {
+        JSONArray params = new JSONArray().put(2).put(3);  // Add(2, 3)
+        JSONObject response = client.call("Calculator.Add", params);
+        System.out.println("Summ: " + response);
+        client.logout();
+}
+
diff --git a/SQLite3/Samples/ThirdPartyDemos/KroKodil/moRMOt.iml b/SQLite3/Samples/ThirdPartyDemos/KroKodil/moRMOt.iml new file mode 100644 index 000000000..f19831a3d --- /dev/null +++ b/SQLite3/Samples/ThirdPartyDemos/KroKodil/moRMOt.iml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SQLite3/Samples/ThirdPartyDemos/KroKodil/pom.xml b/SQLite3/Samples/ThirdPartyDemos/KroKodil/pom.xml new file mode 100644 index 000000000..d57bddcee --- /dev/null +++ b/SQLite3/Samples/ThirdPartyDemos/KroKodil/pom.xml @@ -0,0 +1,28 @@ + + + 4.0.0 + + groupId + moRMOt + 1.0-SNAPSHOT + + + + + + com.squareup.okhttp3 + okhttp + 3.14.2 + + + + org.json + json + 20180813 + + + + + \ No newline at end of file diff --git a/SQLite3/Samples/ThirdPartyDemos/KroKodil/server.png b/SQLite3/Samples/ThirdPartyDemos/KroKodil/server.png new file mode 100644 index 0000000000000000000000000000000000000000..0b5312e7f2573a458ae074154068fcbc885359e3 GIT binary patch literal 44361 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 bWU~?Mq2z~A$}27);9-8m%B1*^bM$`z>(nh_ literal 0 HcmV?d00001 diff --git a/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/CalcInterface.pas b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/CalcInterface.pas new file mode 100644 index 000000000..342ed9933 --- /dev/null +++ b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/CalcInterface.pas @@ -0,0 +1,31 @@ +unit CalcInterface; + +interface + +uses FileCollect; + +/// some common definitions shared by both client and server side + +type + ICalculator = interface(IInvokable) + ['{9A60C8ED-CEB2-4E09-87D4-4A16F496E5FE}'] + function Add(n1,n2: integer): integer; + function GetFileList(path: String; out lst: TFlileCollection): Boolean; + end; + +const + ROOT_NAME = 'root'; + PORT_HTTP = '8080'; + PORT_HTTPS = '8443'; + APPLICATION_NAME = 'RestService'; + SERVICE_NAME = 'service'; + +implementation + +uses + mORMot; + +initialization + // so that we could use directly ICalculator instead of TypeInfo(ICalculator) + TInterfaceFactory.RegisterInterfaces([TypeInfo(ICalculator)]); +end. diff --git a/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/Client.dpr b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/Client.dpr new file mode 100644 index 000000000..53fb50fa8 --- /dev/null +++ b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/Client.dpr @@ -0,0 +1,52 @@ +{ + Synopse mORMot framework + + Sample 04 - HTTP Client-Server + purpose of this sample is to show HTTP Client/Server SQLite3 database usage: + + - a TSQLSampleRecord class is defined in shared unit SampleData.pas + - this sample uses two projects, Project04Client.dpr and Project04Server.dpr + - a SQLite3 server is initialized in Project04Server + - the CreateMissingTables method will create all necessary tables in the + SQLite3 database + - one or more client instances can be run in Project04Client + - the purpose of the Client form in Unit1.pas is to add a record to the + database; the Time field is filled with the current date and time + - the 'Find a previous message' button show how to perform a basic query + - since the framework use UTF-8 encoding, we use some basic functions for + fast conversion to/from the User Interface; in real applications, + you should better use our SQLite3i18n unit and the corresponding + TLanguageFile.StringToUTF8() and TLanguageFile.UTF8ToString() methods + - note that you didn't need to write any SQL statement, only define a + class and call some methods; even the query was made very easy (just an + obvious WHERE clause to write) + - thanks to the true object oriented modeling of the framework, the same + exact Unit1 is used for both static in-memory database engine, or + with SQLite3 database storage, in local mode or in Client/Server mode: + only the TForm1.Database object creation instance was modified + - look at the tiny size of the EXE (even with SQLite3 engine embedded), less + than 400KB for the server, and 80KB for the client, with LVCL :) + + Version 1.0 - February 07, 2010 + + Version 1.16 + - added authentication to the remote process + +} + +program Client; + +uses + Forms, + SysUtils, + ClientForm in 'ClientForm.pas' {Form1}, + SampleData in 'SampleData.pas', + CalcInterface in 'CalcInterface.pas'; + +{$R *.res} + +begin + Application.Initialize; + Application.CreateForm(TForm1, Form1); + Application.Run; +end. diff --git a/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/Client.res b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/Client.res new file mode 100644 index 0000000000000000000000000000000000000000..4933876c4c09925cf185c9c5fe89f5a565bf5616 GIT binary patch literal 1540 zcmZuw&ubGw7=4>87>U#rgcdIiOKHV}c1=M+uoYvapjNE(TB>c=Vw;8-v-P4WJ-CoV z|CQEUgh7ZW{{zoLFW$_pg2nNDGdoFI`jMBJH{bhy%*+k|6bY>@I6Y;*GR~i+#xuS` zA>%vIHBO4eEa4$tX3Y841o%UE!94RKi6oa3*;dlMbUa=)+mxzJIqWp8d0=r zc%J@D97iu3^&XGoxE4g~CLoun(C|i@K&jFw-OdlkC#)VUFdD}}90VpFITqNDYm$OF)Zrr4rSaL`r>W@jhs+lpk2i zI}(51H6%pr08LM8hfY>&K{vanyF<|3OZeE$Vdjqj7!#Zf;2-D2jDVk)U`+4s?dzug zt~)1L-TC0@pZ6})b%DqdyF`chMjUCakFT(cDMITZJi&d;<0RdY&e^=Fux#qhaW3}N|IMw)TlCWEcAPO@K)wg@ z4%!d1u!uTp*gypl<~VL*jhQQ0$2P^Ejb;kd-03Z1nmx$<13NG+rdE|THyB&#X>Z{o)lR!aZH?-_Cd3U^x44ZvCeHRCz1EGs!up$M zx|lFMJf=S~Ug@~XUS-D-UNh2~?X|CaLB$GI8Mno;hBxfSnbj_&IppM|uIpV`{}gPG zvIws+ir{Y;18ndaE#Y0cRa7~H>nb63s2?DW*)+Oj`gUFQZxk3eUxVFY Pg;ASS4&Fz`Kl1+rLy;*^ literal 0 HcmV?d00001 diff --git a/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/ClientForm.dfm b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/ClientForm.dfm new file mode 100644 index 000000000..4ad693b5b --- /dev/null +++ b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/ClientForm.dfm @@ -0,0 +1,213 @@ +object Form1: TForm1 + Left = 231 + Top = 220 + BorderStyle = bsSingle + ClientHeight = 397 + ClientWidth = 966 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -13 + Font.Name = 'Tahoma' + Font.Style = [] + OldCreateOrder = False + OnCreate = FormCreate + OnDestroy = FormDestroy + PixelsPerInch = 96 + TextHeight = 16 + object StatusBar1: TStatusBar + Left = 0 + Top = 378 + Width = 966 + Height = 19 + Panels = <> + SimplePanel = True + end + object Panel1: TPanel + Left = 0 + Top = 51 + Width = 966 + Height = 327 + Align = alBottom + BevelOuter = bvNone + Caption = 'Panel1' + TabOrder = 1 + object Label1: TLabel + Left = 16 + Top = 8 + Width = 67 + Height = 16 + Caption = 'Your name:' + end + object Label2: TLabel + Left = 16 + Top = 56 + Width = 86 + Height = 16 + Caption = 'Your message:' + end + object lblA: TLabel + Left = 312 + Top = 34 + Width = 17 + Height = 16 + Caption = 'A=' + end + object lblB: TLabel + Left = 312 + Top = 66 + Width = 16 + Height = 16 + Caption = 'B=' + end + object lblResult: TLabel + Left = 604 + Top = 72 + Width = 184 + Height = 16 + Caption = 'Enter numbers, then Call Server' + end + object QuestionMemo: TMemo + Left = 16 + Top = 72 + Width = 257 + Height = 193 + TabOrder = 0 + end + object NameEdit: TEdit + Left = 16 + Top = 24 + Width = 81 + Height = 24 + TabOrder = 1 + end + object AddButton: TButton + Left = 16 + Top = 280 + Width = 145 + Height = 25 + Caption = 'Add the message' + TabOrder = 2 + OnClick = AddButtonClick + end + object QuitButton: TButton + Left = 197 + Top = 280 + Width = 75 + Height = 25 + Caption = 'Quit' + TabOrder = 3 + OnClick = QuitButtonClick + end + object FindButton: TButton + Left = 104 + Top = 24 + Width = 169 + Height = 25 + Caption = 'Find a previous message' + TabOrder = 4 + OnClick = FindButtonClick + end + object edtA: TEdit + Left = 336 + Top = 32 + Width = 153 + Height = 24 + TabOrder = 5 + Text = '2' + end + object edtB: TEdit + Left = 336 + Top = 64 + Width = 153 + Height = 24 + TabOrder = 6 + Text = '3' + end + object btnCall: TButton + Left = 496 + Top = 32 + Width = 97 + Height = 57 + Caption = 'Call Server' + TabOrder = 7 + OnClick = btnCallClick + end + object Button1: TButton + Left = 616 + Top = 125 + Width = 75 + Height = 25 + Caption = 'GetFileList' + TabOrder = 8 + OnClick = Button1Click + end + object Edit1: TEdit + Left = 312 + Top = 125 + Width = 297 + Height = 24 + TabOrder = 9 + Text = '.\' + end + object ListView1: TListView + Left = 312 + Top = 155 + Width = 625 + Height = 151 + Columns = < + item + Caption = 'Name' + Width = 200 + end + item + Caption = 'Size' + Width = 90 + end + item + Caption = 'Modified' + Width = 140 + end + item + Caption = 'Version' + Width = 150 + end> + TabOrder = 10 + ViewStyle = vsReport + end + end + object Panel2: TPanel + Left = 8 + Top = 8 + Width = 361 + Height = 41 + BevelOuter = bvNone + Caption = 'Panel2' + TabOrder = 2 + object txtUser: TEdit + Left = 8 + Top = 9 + Width = 121 + Height = 24 + TabOrder = 0 + Text = 'User' + end + object txtPassword: TEdit + Left = 144 + Top = 9 + Width = 121 + Height = 24 + TabOrder = 1 + Text = 'synopse' + end + object btnLogin: TButton + Left = 272 + Top = 7 + Width = 75 + Height = 25 + Caption = 'Login' + TabOrder = 2 + OnClick = btnLoginClick + end + end +end diff --git a/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/ClientForm.pas b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/ClientForm.pas new file mode 100644 index 000000000..5ddcc21c2 --- /dev/null +++ b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/ClientForm.pas @@ -0,0 +1,283 @@ +unit ClientForm; + +interface + +uses + {$ifdef MSWINDOWS} + Windows, + Messages, + Graphics, + {$endif} + Classes, SysUtils, Forms, Controls, Dialogs, StdCtrls, + SynCommons, + SynLog, + mORMot, + mORMotHttpClient, + SampleData, ComCtrls, ExtCtrls; + +type + + { TForm1 } + + TForm1 = class(TForm) + StatusBar1: TStatusBar; + Panel1: TPanel; + Label1: TLabel; + Label2: TLabel; + lblA: TLabel; + lblB: TLabel; + lblResult: TLabel; + QuestionMemo: TMemo; + NameEdit: TEdit; + AddButton: TButton; + QuitButton: TButton; + FindButton: TButton; + edtA: TEdit; + edtB: TEdit; + btnCall: TButton; + Button1: TButton; + Edit1: TEdit; + ListView1: TListView; + Panel2: TPanel; + txtUser: TEdit; + txtPassword: TEdit; + btnLogin: TButton; + procedure AddButtonClick(Sender: TObject); + procedure FindButtonClick(Sender: TObject); + procedure btnLoginClick(Sender: TObject); + procedure FormDestroy(Sender: TObject); + procedure QuitButtonClick(Sender: TObject); + procedure btnCallClick(Sender: TObject); + procedure Button1Click(Sender: TObject); + procedure FormCreate(Sender: TObject); + private + procedure ClientFailed(Sender: TSQLRestClientURI; E: Exception; + Call: PSQLRestURIParams); + public + { public declarations } + Port: String; + Client: TSQLRest; + Model: TSQLModel; + svcClient: TSQLRestClientURI; + svcModel: TSQLModel; + end; + +var + Form1: TForm1; + +implementation + +{$ifdef FPC} +{$R *.lfm} +{$else} +{$R *.dfm} +{$endif} + +{ TForm1 } + +uses + CalcInterface, FileCollect; + +var + startTime64, endTime64, frequency64: Int64; + elapsedSeconds: single; + +procedure EnableControls(const ctrl: TWinControl; enbl: Boolean); +var + i: Integer; +begin + for i:=0 to ctrl.ControlCount -1 do + TWinControl(ctrl.Controls[i]).Enabled := enbl; +end; + +procedure TForm1.btnLoginClick(Sender: TObject); +begin + EnableControls(Panel2, False); + try + if TSQLHttpClient(Client).SetUser(txtUser.Text, txtPassword.Text) then + begin + if svcClient.SetUser(txtUser.Text, txtPassword.Text) then + begin + svcClient.ServiceDefine([ICalculator],sicShared); + StatusBar1.SimpleText := 'Connected.'; + EnableControls(Panel1, True); + end; + end; + except + on E: Exception do + begin + EnableControls(Panel2, True); + ShowMessage(E.Message); + end; + end; + +end; + +procedure TForm1.AddButtonClick(Sender: TObject); +var Rec: TSQLSampleRecord; +begin + Rec := TSQLSampleRecord.Create; + try + // we use explicit StringToUTF8() for conversion below + // a real application should use TLanguageFile.StringToUTF8() in mORMoti18n + Rec.Name := StringToUTF8(NameEdit.Text); + Rec.Question := StringToUTF8(QuestionMemo.Text); + if Client.Add(Rec,true)=0 then + ShowMessage('Error adding the data') else begin + NameEdit.Text := ''; + QuestionMemo.Text := ''; + NameEdit.SetFocus; + end; + finally + Rec.Free; + end; +end; + +procedure TForm1.FindButtonClick(Sender: TObject); +var Rec: TSQLSampleRecord; +begin + QueryPerformanceCounter(startTime64); + Rec := TSQLSampleRecord.Create(Client,'Name=?',[StringToUTF8(NameEdit.Text)]); + try + if Rec.ID=0 then + QuestionMemo.Text := 'Not found' + else + QuestionMemo.Text := UTF8ToString(Rec.Question); + finally + Rec.Free; + end; + QueryPerformanceCounter(endTime64); + elapsedSeconds := (endTime64 - startTime64) / frequency64; + StatusBar1.SimpleText := FormatFloat('0.000 msec.', 1000*elapsedSeconds); +end; + +procedure TForm1.FormDestroy(Sender: TObject); +begin + Client.Free; + Model.Free; + + svcClient.Free; + svcModel.Free; +end; + +procedure TForm1.QuitButtonClick(Sender: TObject); +begin + Close; +end; + +procedure TForm1.btnCallClick(Sender: TObject); +var a,b: integer; + err: integer; + I: ICalculator; +begin + QueryPerformanceCounter(startTime64); + val(edtA.Text,a,err); + if err<>0 then begin + edtA.SetFocus; + exit; + end; + val(edtB.Text,b,err); + if err<>0 then begin + edtB.SetFocus; + exit; + end; + + if svcClient.Services['Calculator'].Get(I) then + lblResult.Caption := 'A + B = ' +IntToStr(I.Add(a,b)); + + QueryPerformanceCounter(endTime64); + elapsedSeconds := (endTime64 - startTime64) / frequency64; + StatusBar1.SimpleText := FormatFloat('0.000 msec.', 1000*elapsedSeconds); +end; + +procedure TForm1.ClientFailed(Sender: TSQLRestClientURI; E: Exception; + Call: PSQLRestURIParams); +var + values: TPUtf8CharDynArray; +begin + + if (E<>nil) then + StatusBar1.SimpleText := 'HTTP Error: '+e.Message + else if (Call<>nil) then + begin + JSONDecode(Call.OutBody,['errorCode', 'errorText'],@values,false); + StatusBar1.SimpleText := 'HTTP Error: '+values[0]+', '+values[1]; + end; +end; + +procedure TForm1.Button1Click(Sender: TObject); +var + calc: ICalculator; + lst: TFlileCollection; + i: Integer; +begin + QueryPerformanceCounter(startTime64); + if svcClient.Services['Calculator'].Get(calc) then + begin + lst:= TFlileCollection.Create; + try + if calc.GetFileList(Edit1.Text, lst) then + begin + ListView1.Items.Clear; + for i:=0 to lst.Count-1 do + with ListView1.Items.Add do + begin + Caption := lst.Items[i].Name; + SubItems.Add(IntToStr(lst.Items[i].Size)); + SubItems.Add(DateTimeToStr(lst.Items[i].ModificationDate)); + SubItems.Add(lst.Items[i].Version); + end; + end + else + ShowMessage('Empty or invalid path!'); + finally + lst.Free; + end; + end; + QueryPerformanceCounter(endTime64); + elapsedSeconds := (endTime64 - startTime64) / frequency64; + StatusBar1.SimpleText := FormatFloat('0.000 msec.', 1000*elapsedSeconds); +end; + +procedure TForm1.FormCreate(Sender: TObject); +var + Server: AnsiString; + aHttps: Boolean; +begin + EnableControls(Panel1, False); + QueryPerformanceFrequency(frequency64); + + + {with TSQLLog.Family do + begin + Level := LOG_VERBOSE; + EchoToConsole := LOG_VERBOSE; // log all events to the console + end; + } + + Caption := ' Sample HTTP Client'; + Port := '8080'; + + if ParamCount=0 then + Server := 'localhost' + else + Server := AnsiString(Paramstr(1)); + + aHttps := (ParamCount>1) and (AnsiLowerCase(Paramstr(2))='ssl'); + if aHttps then + begin + Caption := Caption + ' SSL'; + Port := '8443'; + end; + + Model := CreateSampleModel; // from SampleData unit + Client := TSQLHttpsClient.Create(Server,Port,Model,aHttps); + TSQLHttpClient(Client).OnFailed := ClientFailed; + + svcModel := TSQLModel.Create([],SERVICE_NAME); + svcClient := TSQLHttpsClient.Create(Server,Port,svcModel,aHttps); + svcClient.OnFailed := ClientFailed +end; + +end. + diff --git a/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/FileCollect.pas b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/FileCollect.pas new file mode 100644 index 000000000..f10fd4d9d --- /dev/null +++ b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/FileCollect.pas @@ -0,0 +1,73 @@ +unit FileCollect; + +interface + +uses + Classes, + SynCommons, + mORMot; + +type + TFileItem = class; + TFlileCollection = class(TInterfacedCollection) + private + function GetItem(aIndex: Integer): TFileItem; + protected + class function GetClass: TCollectionItemClass; override; + public + function Add: TFileItem; + property Items[aIndex: Integer]: TFileItem read GetItem; default; + end; + + TFileItem = class(TCollectionItem) + private + FName:String; + FSize:Cardinal; + FModificationDate: TDateTime; + FVersion: String; + public + procedure Assign(Source:TPersistent);override; + published + property Name: String read FName write FName; + property Size: Cardinal read FSize write FSize; + property ModificationDate: TDateTime read FModificationDate write FModificationDate; + property Version: String read FVersion write FVersion; + end; + +implementation + +{ TFileItem } + +procedure TFileItem.Assign(Source: TPersistent); +begin + if Source is TFileItem then + with Source as TFileItem do + begin + Self.FName:=Name; + Self.FSize:=Size; + Self.FModificationDate:=ModificationDate; + Self.FVersion:=Version; + end + else + inherited Assign(source); +end; + +{ TFlileCollection } + +function TFlileCollection.Add: TFileItem; +begin + Result := TFileItem(inherited Add); +end; + +class function TFlileCollection.GetClass: TCollectionItemClass; +begin + Result := TFileItem; +end; + +function TFlileCollection.GetItem(aIndex: Integer): TFileItem; +begin + Result := TFileItem(inherited GetItem(aIndex)); +end; + +end. + \ No newline at end of file diff --git a/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/REST.bdsgroup b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/REST.bdsgroup new file mode 100644 index 000000000..300659469 --- /dev/null +++ b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/REST.bdsgroup @@ -0,0 +1,20 @@ + + + + + + + + + + + + + Project04ServerStatic.bdsproj + Project04Client.bdsproj + Project04ServerStatic.exe Project04Client.exe + + + + diff --git a/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/REST.bpg b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/REST.bpg new file mode 100644 index 000000000..85f2ec3db --- /dev/null +++ b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/REST.bpg @@ -0,0 +1,23 @@ +#------------------------------------------------------------------------------ +VERSION = BWS.01 +#------------------------------------------------------------------------------ +!ifndef ROOT +ROOT = $(MAKEDIR)\.. +!endif +#------------------------------------------------------------------------------ +MAKE = $(ROOT)\bin\make.exe -$(MAKEFLAGS) -f$** +DCC = $(ROOT)\bin\dcc32.exe $** +BRCC = $(ROOT)\bin\brcc32.exe $** +#------------------------------------------------------------------------------ +PROJECTS = Server.exe Client.exe +#------------------------------------------------------------------------------ +default: $(PROJECTS) +#------------------------------------------------------------------------------ + +Server.exe: Server.dpr + $(DCC) + +Client.exe: Client.dpr + $(DCC) + + diff --git a/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/SampleData.pas b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/SampleData.pas new file mode 100644 index 000000000..a2ce6f788 --- /dev/null +++ b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/SampleData.pas @@ -0,0 +1,39 @@ +/// it's a good practice to put all data definition into a stand-alone unit +// - this unit will be shared between client and server +unit SampleData; + +interface + +uses + SynCommons, + mORMot; + +type + /// here we declare the class containing the data + // - it just has to inherits from TSQLRecord, and the published + // properties will be used for the ORM (and all SQL creation) + // - the beginning of the class name must be 'TSQL' for proper table naming + // in client/server environnment + TSQLSampleRecord = class(TSQLRecord) + private + fQuestion: RawUTF8; + fName: RawUTF8; + fTime: TModTime; + published + property Time: TModTime read fTime write fTime; + property Name: RawUTF8 read fName write fName; + property Question: RawUTF8 read fQuestion write fQuestion; + end; + +/// an easy way to create a database model for client and server +function CreateSampleModel: TSQLModel; + + +implementation + +function CreateSampleModel: TSQLModel; +begin + result := TSQLModel.Create([TSQLSampleRecord]); +end; + +end. diff --git a/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/Server.dpr b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/Server.dpr new file mode 100644 index 000000000..07f39f0e3 --- /dev/null +++ b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/Server.dpr @@ -0,0 +1,36 @@ +{ + Synopse mORMot framework + + Sample 04 - HTTP Client-Server + purpose of this sample is to show how to serve files in + addition to RESTful Client/Server of a SQLite3 database + + This sample will serve as REST the data as defined in SampleData, + and serve 'www' sub-folder content within localhost:8080/static + + It is IMHO preferred and less-error prone to define a method-based service, + then let the method return the file using Ctxt.ReturnFile() method. + + See also https://synopse.info/forum/viewtopic.php?id=1896 + + Version 1.18 + - added Project04ServerStatic.dpr program + +} + +program Server; + +uses + FastMM4, + Forms, + ServerForm in 'ServerForm.pas' {Form1}, + SampleData in 'SampleData.pas', + CalcInterface in 'CalcInterface.pas'; + +{$R *.res} + +begin + Application.Initialize; + Application.CreateForm(TForm1, Form1); + Application.Run; +end. diff --git a/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/Server.res b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/Server.res new file mode 100644 index 0000000000000000000000000000000000000000..f262e363e5b8f6e1e588bc1bd917c168e39c228b GIT binary patch literal 1540 zcmZuw&ubGw7=4>87>U#rgcdIiOKHV}c1;mPuoYvapjNE(TB>c=Vw;8-v-P4WJ-CoV z|CQEUgh7ZW{{zoLFW$_pg2nNDGdoFI`jMBJH{bhy%*+k|6bY>@I6Y;*GR~i+#xuS` zA>%vIHBO4eEa4$tX3Y841o%UE!94RKi6oa3*;dlMbUa=)+mxzJIqWp8d0=r zc%J@D97iu3^&XGoxE4g~CLoun(C|i@K&jFw-OdlkC#)VUFdD}}90VpFITqNDYm$OF)Zrr4rSaL`r>W@jhs+lpk2i zI}(51H6%pr08LM8hfY>&K{vanyF<|3OZeE$Vdjqj7!#Zf;2-D2jDVk)U`+4s?dzug zt~)1L-TC0@pZ6})b%DqdyF`chMjUCakFT(cDMITZJi&d;<0RdY&e^=Fux#qhaW3}N|IMw)TlA4R*Rjg3|p1WQl zTkDm$ZZs}unVz?JWolJfbAz#!p7s_lQth-$)Yho(YeL*$b&K1$W8!QN(rex5E3ChH zri%&F!(;j*{WIg;WZbl;A^-sa} z_>9_ZW_!o|*Uxkao@tI 0; + FindClose(SR); + end; +end; + +{ TForm1 } + +procedure TForm1.Button1Click(Sender: TObject); +begin + Close; +end; + +procedure TForm1.FormCreate(Sender: TObject); +var + aHttpServerSecurity: TSQLHttpServerSecurity; +begin + + with TSQLLog.Family do begin + Level := LOG_VERBOSE; + EchoToConsole := LOG_VERBOSE; // log all events to the console + end; + TSQLLog.Add.Log(sllInfo,'Starting'); + + Port := PORT_HTTP; + aHttpServerSecurity := secNone; + + Label1.Caption := 'HTTP Server'; + + if (ParamCount>0) then + begin + if (Paramstr(1)='ssl') then + aHttpServerSecurity := secSSL; + if (Paramstr(1)='sha') or (Paramstr(1)='aes') then + aHttpServerSecurity := secSynShaAes; + Label1.Caption := Label1.Caption +' '+AnsiUpperCase( Paramstr(1) ); + end; + + if aHttpServerSecurity <> secNone then + Port := PORT_HTTPS; + + Label2.Caption := 'HTTP Server is running on port: '+Port; + + dbModel := CreateSampleModel; + DB := TSQLRestServerDB.Create(dbModel,'server.db3',true); + DB.CreateMissingTables; + + svcModel := TSQLModel.Create([],SERVICE_NAME); + SVC := TSQLRestServerFullMemory.Create(svcModel,'users.json',false,true); + SVC.ServiceDefine(TServiceCalculator,[ICalculator],sicShared); + + Server := TCustomHttpServer.Create(Port,[DB,SVC],'+',useHttpApiRegisteringURI,32,aHttpServerSecurity,'static'); + Server.AccessControlAllowOrigin := '*'; // allow cross-site AJAX queries + +end; + +procedure TForm1.FormDestroy(Sender: TObject); +begin + Server.Free; + SVC.Free; + DB.Free; + svcModel.Free; + dbModel.Free; +end; + + +{ TCustomHttpServer } + +function TCustomHttpServer.Request(Ctxt: THttpServerRequest): Cardinal; +var + FileName: TFileName; +begin + if (Ctxt.Method='GET') and + IdemPChar(pointer(Ctxt.URL),'/STATIC/') and + (PosEx('..',Ctxt.URL)=0) then + begin + // http.sys will send the specified file from kernel mode + FileName := '..\www\'+UTF8ToString(Copy(Ctxt.URL,9,maxInt)); + Ctxt.OutContent := StringToUTF8(FileName); + Ctxt.OutContentType := HTTP_RESP_STATICFILE; + Result := 200; // THttpApiServer.Execute will return 404 if not found + end + else + Result := inherited Request(Ctxt); // call the associated TSQLRestServer instance(s) +end; + + + + +end. diff --git a/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/bin/SHA Client.lnk b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/bin/SHA Client.lnk new file mode 100644 index 0000000000000000000000000000000000000000..498e4f17f6ff865fe12f0fc55dd953ea1d1c53ca GIT binary patch literal 1184 zcmb7CT}YEr7=C63CX$;jT4p%|Vg0SCWi$v}1Ban)&6_ShVmd$T8@i1)V%`LLl@WMT zH_?_vX+*an6fWs55*htS7FkeNR)|+tH}$;Vx2DA+J0H(^-}AojdCqg*^K}3)SxYzr z)B23IhbV)c{>O_e*>v!Q>6iUbduMBJ_N57*tMy8gQD2w`O;*$e-urX*MYm?Y`KkKX3{N1?(eAy#P#cdg#ZtDVbK|Jw_OL2bFA_^vH|w4U zaen>K5JoqWv@t}HV9ti)dG@sb&9E6kHIW2b3e3%;5*Ox)h%ogd}0M!o>$ECKTAsTK%VyjTJ<3&A=As zDhag8bYuL?g8R8ig0e}^Vx~%5-QO%ut4zvF=p8G?m8TH#UtCkKy%_OLe9cwgO7!h& zXS`PmM8kH%=!qSUb@x%OVT=(PIb=K%b{xI>%yNGY)Bbr|Zh-NfQX+@Q5wb$>ZUvs-|?Vf;Y4@#CQ@51F#B-@!YW#yS_Y#)8#xT?TU$gIU!hwoC$f!g$VL!Mf-nrP zyeQa`eiWqJ2#S_;QxVkYkF2mRx-vrSrXPrU&i74SvFPU==jT1=eV_L^=X~n`V6v7` z0~5MN+f7u$M*q_I;At)Q%rs{|p1K?MbC1_+^^+#!{Jc(?tf=$6Rr9uKXUKf1@w&is^1K6FrEOK=aHL+BreEfJ%apeDP9DL!z-|okHIse zy3=O(Lt$+F3CEoloChUw{Nxm7b{RA@LK>G*U1=M9!B}4~<_IQ(z^(#XMopa%5wv^n z%BfdTikFY9V(8PggE~n8YAIgIYKqh$b~8oReu{-6Xz@Ftgqao`M{6|N8wzwKLXmJ> zljdlwODkqo4F@@wda(vBIGOuh_cgLHx~IXYzns17=7b)R-eO?nK1nqAJU(T%{`!o& z)_i5QtaC8?{_1YVtAt@4MauH9+z5II7vL}yHolmUk1=EH0g|0-#|GZhKNJC}rj+Ih ztYB^_1udd3j2@qIJ~2s9F6mh)(o4f%B2J5_g9irQ@T9oXektUGa_glF1MacU`Q?}6 z$JVzqepC(!2brSL2OI2v-8(76Si>~($Y@W%zH|7o<=S&hsFOAM6yy3IQ66cc{Oee> z|LQLvDy&Xgepn-i3j1W^Hl^U|rYaj1Th5;A{$!v0 Kdizg#0r>%T#_k~i literal 0 HcmV?d00001 diff --git a/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/bin/SSL Client.lnk b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/bin/SSL Client.lnk new file mode 100644 index 0000000000000000000000000000000000000000..07aa1b491e4f78555916722b688c0e3e2f211d3c GIT binary patch literal 1192 zcmb7CT}V@57=C63CQ_SQWM(-7Vg0S6meC;41`b0zYunCnTvKb2ZL>`{__mA@c3dZAWn+7Z%|DWGu~xnWr1f;`qM$bYsHf=(Q6fs1@Bo*6YT zU`Lk`!-3y$0t15cq##ZgImMVQhlYB{;4(6ue}ZrT~?c5T%MDeTdyhk#&ONpsb@L9~mh-EqEQxiA2v-$G!#0qf>9rHf1AEWHrDhsSp7S3HXreFsyhG9ncB;Hi=&T^Ey*v6anj!aj7*SZ zT}QA(S*#sj2vpi{Ef$3@Wk20L%y_AEP)%`Bf`p2rmtYZ&MB&90A`I+et@(IlV;Q9| zcVIJfnbnX5Mjm=FF03 L_WQ$s@#CQ`Eumi@Q_VHK@UErU_u8aWK>TU$gIU!hy8He?&ykc}Xi1YsCn zc~P(>{U}Jc5fm-y#v&-`kF2mRx-vrSrXPrU&iBo^!szE6=jT1=eV_L^=X~n`V5%*n z2F7%awp*x#mHy??{xe$snQ6{`JUt%qbC1_+^^+#!d{L)OwW#;JRVS>|j*$7%i%M)# zlGF|P96h?WQ>Q3E9mPvoO_4gpZl=iEPpP2@THlv< z(j1L-X{D^HVJGK>UbMgk2XnvczD71i_cR#wm$R1%&gl{9Ee3|}lSHG><5OlEZp^sr z%vWd2I{S0)ukB{MN*LBtq%051jUr6A0EeNl@uh@(j2UAOknCJLHt?SQp$JGdWnrGc z3g(tl&?@TU@bM|f6O#nxlAeVky)^tK;<&xn4*vroD&SU2{E2P;OYL#NE zC$!T}_rmCDN%AhClzgmdgb+n;3vzRUPewe>f!x!H_^hZxKKSr?DEtcvzVnpuz3?BQ z-@{)oejEB`@xh`;o&UyZT*iR%)$W(wrP?6Qx8&z^`t}%IJ%g6+8Dk?UHdJ zoQ%QQRgKeLsc3vQ`l;WiCL^?ewHEtE3Qf`OSyk9af$4R)DV+-JGi!F{=-`ppuP!gs zFLFKO`Jt{zNc5UgKA+vS3S}`(T}v;VZB@^$t?SNsUFkD zZP4oa-MW@97mF5m?Wk7Q_eIt^YYyI+%lV;I;I^MrunGTZQe1= zvpeFk@v^jbGlxCy+^wv~=o#8hvAh#q_xaTPG9|XF`S4th=Z=kjuVWt{I{?RVC*~jI znO}`W=##@Sm0g9Ez>aucz#2(kOUv$$+3gui)r4ubI_-MvLUj5)w`I9TLtYuRcP4l= z?(wO5u6#u!n)|R~- zlS!k44E3w4tMtjbU1rU=xI7nC(U3hn8CR~9HhE?3dQYN-L5P$##AOHafKmY;|fB*y_0D*ZC2vINba!w*&{`TI_@uW5WXZ(9P{^vi+ z9RdU(009U<00Izz00bZa0SG_<0&j%?XKXb%xwK$WVawE6{J}z$MeL*DN)K!N@8NmE zzvEB2AtMM7fB*y_009U<00Izz00bZa0SHVJc;DkCDqY{R7Ta%c#Nx5k?L;~g+Zg_( zn3pV3!}{gUc04t4)lD}gmQ}oDh2E?6FGcsWmf7iUr!uklsN5;(-Ewcs`F)jEa?OiY zdm**yW^yuL-CIBK_wZ+g|HYs2pZE{c8i;ccfB*y_009U<00Izz00bZa0SLT-0zsxy z`*nmTz?P`>>VbvX3U%KmC;_Gh?Nh5$1Rwwb2tWV=5P$##AOHaf+!%pZ&i`Zm|BZPT&`=0K00Izz00bZa z0SG_<0uY!tf&a1oAM^k7*7;};1Rwwb2tWV=5P$##AOHaf+(3bA&Hpq0E8+j}KjjVq k0uX=z1Rwwb2tWV=5P$##AOL|m5b!ce8M8hWg|T4pUwIupq5uE@ literal 0 HcmV?d00001 diff --git a/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/bin/users.json b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/bin/users.json new file mode 100644 index 000000000..3abeaafee --- /dev/null +++ b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/bin/users.json @@ -0,0 +1,10 @@ +[{"AuthGroup":[ +{"RowID":1,"Ident":"Admin","SessionTimeout":10,"AccessRights":"47,1-256,0,1-256,0,1-256,0,1-256,0"}, +{"RowID":2,"Ident":"Supervisor","SessionTimeout":60,"AccessRights":"46,1-256,0,3-256,0,3-256,0,3-256,0"}, +{"RowID":3,"Ident":"User","SessionTimeout":60,"AccessRights":"14,3-256,0,3-256,0,3-256,0,3-256,0"}, +{"RowID":4,"Ident":"Guest","SessionTimeout":60,"AccessRights":"0,3-256,0,0,0,0"}] +},{"AuthUser":[ +{"RowID":1,"LogonName":"Admin","DisplayName":"Admin","PasswordHashHexa":"67aeea294e1cb515236fd7829c55ec820ef888e8e221814d24d83b3dc4d825dd","GroupRights":1,"Data":null}, +{"RowID":2,"LogonName":"Supervisor","DisplayName":"Supervisor","PasswordHashHexa":"67aeea294e1cb515236fd7829c55ec820ef888e8e221814d24d83b3dc4d825dd","GroupRights":2,"Data":null}, +{"RowID":3,"LogonName":"User","DisplayName":"User","PasswordHashHexa":"67aeea294e1cb515236fd7829c55ec820ef888e8e221814d24d83b3dc4d825dd","GroupRights":3,"Data":null}] +}] \ No newline at end of file diff --git a/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/ssl/SignRoot.pvk b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/ssl/SignRoot.pvk new file mode 100644 index 0000000000000000000000000000000000000000..773f50bb39c06d374f8fcfb4d6c5c21f8ca0ade6 GIT binary patch literal 1196 zcmV;d1XKGS@wKo30000200000000000001#1ONaB0ssI2Bme+XQ$aES2mk;90096` z27i|9FECx|T`+wP9{N)*9ay>jeH~nw!}akUbVLM)x00k8P0kD{kLmO=l4pjZ7>Il+Jd`2H(SXR<05!PmN0Mzp|JH)o3-RNL;YbN6|9T5NB6sFpM| zKN5wAj{xuElgHPBgOZONj`b8BnVZJwaF!yNdvakO8g{Vu5F#9dNx-1=BEqyA8NS-8 zY>b>OoLqu(Ir03YM!yiyBKr6`vOkN_2X2u(Om^U%(EydqsdHWvc3f`>ZXkEAg=QQ(D-`08dj0ml8BeooGPQnWd=>PB`Z&9;ze=v!bq8S?sJ!yn#{J#;3S$Hy?{r@^z`msTA zIgReT)5xNOQQyNGL)e{^ZZp=q>+yE=RTgqrDaVhYhA~UlJlj|t0uUY}1nqSSUfG;y zf%}dkp`xka%DHBBW!r0i0H(?cDL|*ff35B0ZM??fN-+nkAH_Y&z6UZY3ZO-CTykZQ z?gwwswYf{Hz)=Z22$>1=PFQDp>6l^iw-a?OTV7ZEeUSp(DNKI|ht9=iG6o@Av<+|6 z@Yp|SQ}2R2h3YM~QR>CFowt+gSEZIUh0gUAA4pCare~1Q(@3~~=WJ0Ie^KDELQ1h8 z)CW@#Nl_1ImZ>XqzpXK9zYI=UMeg}48sgt6bM2SE`A{#n6AzEMJm1tC$!2VU5w?Y;cloSN^8;*G0fVA9q{njluS0+rg@>3Q$7MS zwXjxiJO(IyjKk9UfyPcDTYX(?RNqLsIyEj*oZ1a#i+Ji=(Yy=Rgo8PvCi%^%WA2zY zzaG7j*W)ND1jqr-2ZEOWPCkWH1jM*tOz?~cunn`EUTPwE4c2k5*&^G;(pBTaL%U{S z7Pp#5%8$(-ZLw72X9hPmD2voTrmrpdo0OG7i#0AmnrGhE3765q^HWL{rmyf8&pVDZ z@hg|k7|~)bR948n&-p;o*8zBkYd?UsFG8R<7w_@8$)&9zC4*6^J$Q};a57Ygs`{rG zVoRchH+bRN>dO%U{3*z%ucbTaze^8_pFOz$#eyT-wcD)?Xe`*#Tee4WxD+`g@}6(%}eJy`YDr2h)1xXsGOi8&Hr zS#ZhF{dk;>nzX+Xo|yXLB;Gp3GXCw%i`J3 literal 0 HcmV?d00001 diff --git a/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/ssl/signroot.cer b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/delphi/ssl/signroot.cer new file mode 100644 index 0000000000000000000000000000000000000000..183fa6056bc56b34bd9d4230c8471fe59b7262ad GIT binary patch literal 830 zcmXqLVzx49Vp3bc%*4pVB#`=>%jRfn*i=u)GRCA}6=nlYHdbvuW+quy1_L!i6$2$U z=1>-9VQH7tG6m<XKoAvh?qea@QAN{Ql0<8>}^xv^+Z`#ZxZsriHv`6MosMm7mgBH_#O#3X? za64R+>CHI%dr$Au2W37s6Z6kIR;q<{J>a|6=r1o&%$cJ45SRgK>~~{0S0~z0`o;fZz(TdYiep4^G!%F z_Wwc+Vq>5QUvdD(1tWv9z?s!qn`Z1!|5_!|-M>}6QelmrL65qWPqtan(H)#$^IY4s zWTw?Q+QqOp$3Qo^;`jbPW!+3aFBf7#H zeHN#z+VJmt{;HMEr!;QZZ_RA=US!sw^HE4%X0mO5*z=pUO@HO|N)K5mH!0+P`ku5f z&RBg}T}#bfRhu1u--gO2pA;6ky6&#wx0TX1ecb!hzCTN;im_S#_wz%!v}<=iU6lQ} zHl^50cqPA|;JY7Pn>Q}qxqmzVNw=1ZmaiF|^4#VwUTg5Y@c$>TH_r{kg=>8)&0xFFFFFF) ; + } + + public static long calculate(String str, long crc){ + + crc ^= 0xFFFFFFFF; + if (crc < 0) { + crc += 4294967296L; + } + + for (int n=0; n< str.length(); n++) { + crc= Crc32Add(crc, str.codePointAt(n)); + } + + crc ^= 0xFFFFFFFF; + if (crc < 0) { + crc += 4294967296L; + } + return crc; + } +} diff --git a/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/main/java/RESTClient.java b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/main/java/RESTClient.java new file mode 100644 index 000000000..72d07ad7c --- /dev/null +++ b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/main/java/RESTClient.java @@ -0,0 +1,47 @@ +import okhttp3.*; +import org.json.JSONArray; +import org.json.JSONObject; + +public class RESTClient { + + public int statusCode; + private String server; + public static final MediaType JSON = MediaType.get("application/json"); + + private OkHttpClient client; + + public RESTClient(String server) { + this.server = server; + this.client = new OkHttpClient(); + } + + public String doGet(String url) throws Exception { + Request request = new Request.Builder() + .url(server + url) + .build(); + + Response response = client.newCall(request).execute(); + statusCode = response.code(); + if (response.body() != null) { + return response.body().string(); + } + return ""; + } + + public JSONObject doPost(String url, final JSONArray data) throws Exception { + //System.out.println("Post data: " + data.toString() ); + RequestBody body = RequestBody.create(JSON, data.toString()); + Request request = new Request.Builder() + .url(server + url) + .post(body) + .build(); + + Response response = client.newCall(request).execute(); + statusCode = response.code(); + if (response.body() != null) { + String json = response.body().string(); + return new JSONObject(json); + } + return new JSONObject(); + } +} diff --git a/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/main/java/SynClient.java b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/main/java/SynClient.java new file mode 100644 index 000000000..4338a2f4d --- /dev/null +++ b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/main/java/SynClient.java @@ -0,0 +1,203 @@ +import org.json.JSONArray; +import org.json.JSONObject; + +import java.security.MessageDigest; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + +class SynException extends Exception { + public SynException(int errorCode, String errorText) { + super(String.format("Server side error: %d %s", errorCode, errorText)); + } +} + +public class SynClient { + + private String user; + private String root; + private RESTClient http; + + private long userID; + private String logonDisplay; + private int timeout; + private String version; + + private long sessionID = 0; + private String passwordHashHexa = ""; + private String sessionIDHexa8 = ""; + private long sessionPrivateKey = 0; + private long sessionTickCountOffset = 0; + private long serverTimeStampOffset; + private long lastSessionTickCount = 0; + + public static int HTTP_OK = 200; + public static int HTTP_FORBIDDEN = 403; + + public SynClient(String host, int port, String root, boolean ssl){ + this.root = root; + String baseUrl = (ssl?"https":"http") + "://" + host + ":" + port + "/"; + this.http = new RESTClient(baseUrl); + } + + public long getTimestamp() { + return this.nowAsMormotTime() + this.serverTimeStampOffset; + } + public boolean login(String user, String password) throws Exception { + this.user = user; + this.passwordHashHexa = SHA256("salt" + password); + this.sessionTickCountOffset = System.currentTimeMillis(); + return doAuth(); + } + + public JSONObject call(String function, JSONArray data) throws Exception { + JSONObject response = http.doPost(signUrl(root + "/" + function), data); + if (http.statusCode == HTTP_FORBIDDEN) { + if (doAuth()) { + response = http.doPost(signUrl(root + "/" + function), data); + } + } + + if (http.statusCode != HTTP_OK) + throw new SynException(response.getInt("errorCode"), + response.getString("errorText") ); + + return response; + } + + public void logout() throws Exception { + String url = signUrl(root + "/auth?UserName="+ this.user + "&Session=" + this.sessionID); + String response = http.doGet(url); + if (http.statusCode == HTTP_OK) { + sessionID = 0; + passwordHashHexa = ""; + sessionIDHexa8 = ""; + sessionPrivateKey = 0; + } else { + JSONObject json = new JSONObject(response); + throw new SynException(json.getInt("errorCode"), + json.getString("errorText")); + } + + } + + private String byteToHex(byte[] bts) { + StringBuilder des = new StringBuilder(); + String tmp; + for (byte bt : bts) { + tmp = (Integer.toHexString(bt & 0xFF)); + if (tmp.length() == 1) { + des.append("0"); + } + des.append(tmp); + } + return des.toString(); + } + + private String SHA256(String nonce) { + try { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + byte[] bytes = nonce.getBytes(); + md.update(bytes); + return byteToHex(md.digest()); + }catch (java.security.NoSuchAlgorithmException err) { + err.printStackTrace(); + } + return ""; + } + + private String padL(String s, int count) { + String result = String.format("%"+count+"s", s).replace(' ','0'); + if (result.length() > count) + return result.substring(result.length() - count); + else + return result; + } + + private String binaryString(int i, int digits) { + return padL(Integer.toBinaryString(i), digits); + } + + private long nowAsMormotTime() { + Calendar cal = Calendar.getInstance(); + String clientTime = binaryString( cal.get(Calendar.YEAR), 13); + clientTime += binaryString( cal.get(Calendar.MONTH), 4); + clientTime += binaryString( cal.get(Calendar.DAY_OF_MONTH)-1, 5); + + clientTime += binaryString( cal.get(Calendar.HOUR_OF_DAY), 5); + clientTime += binaryString( cal.get(Calendar.MINUTE), 6); + clientTime += binaryString( cal.get(Calendar.SECOND), 6); + + return Long.valueOf(clientTime, 2); + } + + private void gotTimestamp(String timestamp) { + serverTimeStampOffset = Long.parseLong(timestamp) - nowAsMormotTime(); + } + + private String calcClientNonce () { + DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String clientNonce = df.format(new Date()); + return SHA256(clientNonce); + } + + private long gotSession(String sessionKey) { + int i = sessionKey.indexOf('+'); + sessionID = Long.valueOf(sessionKey.substring(0, i)); + sessionIDHexa8 = padL(Long.toHexString(sessionID), 8); + + long r = CRC32.calculate(sessionKey, 0); + sessionPrivateKey = CRC32.calculate(passwordHashHexa, r); + + return sessionPrivateKey; + } + + private boolean doAuth() throws Exception { + String timeStamp = http.doGet(root + "/TimeStamp"); + + gotTimestamp(timeStamp); + + String response = http.doGet(root + "/auth?UserName="+this.user); + JSONObject json = new JSONObject(response); + if (http.statusCode != HTTP_OK) + throw new SynException(json.getInt("errorCode"), json.getString("errorText") ); + + String serverNonce = json.getString("result"); + String clientNonce = calcClientNonce(); + response = http.doGet(root +"/auth"+ + "?UserName="+ user + + "&Password="+ SHA256(root + serverNonce + clientNonce + user + passwordHashHexa)+ + "&ClientNonce=" + clientNonce); + + json = new JSONObject(response); + if (http.statusCode != HTTP_OK) + throw new SynException(json.getInt("errorCode"), json.getString("errorText") ); + + userID = json.getInt("logonid"); + logonDisplay = json.getString("logondisplay"); + version = json.getString("version"); + timeout = json.getInt("timeout"); + if (timeout == 0) timeout = 60; + + return gotSession(json.getString("result")) > 0; + } + + private String signUrl(String url) { + long ticks = System.currentTimeMillis() >> 8; // 256 ms resolution; + + if (lastSessionTickCount == ticks) + lastSessionTickCount += 1; + else + lastSessionTickCount = ticks; + + String nonce = padL(Long.toHexString(lastSessionTickCount), 8); + + long signature = CRC32.calculate(url, CRC32.calculate(nonce, sessionPrivateKey)); + String sessionSignature = sessionIDHexa8 + nonce +padL(Long.toHexString(signature), 8); + + return url + (url.contains("?") ? "&session_signature=": + "?session_signature=") + sessionSignature; + } + +} \ No newline at end of file diff --git a/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/main/java/SynTest.java b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/main/java/SynTest.java new file mode 100644 index 000000000..a42ed4720 --- /dev/null +++ b/SQLite3/Samples/ThirdPartyDemos/KroKodil/src/main/java/SynTest.java @@ -0,0 +1,28 @@ +import org.json.JSONArray; +import org.json.JSONObject; + +public class SynTest { + + public static void main(String[] args) throws Exception { + + JSONArray params; + JSONObject response; + + SynClient client = new SynClient("127.0.0.1", 8080, "service", false); + if (client.login("User", "synopse")) { + System.out.println("Timestamp: " + client.getTimestamp()); + + //TEST 1 + params = new JSONArray().put(2).put(3); // Add(2, 3) + response = client.call("Calculator.Add", params); + System.out.println("Summ: " + response); + + //TEST 2 + params = new JSONArray().put(".\\"); // GetFileList('.\') + response = client.call("Calculator.GetFileList", params); + System.out.println("List: " + response); + + client.logout(); + } + } +} diff --git a/SynopseCommit.inc b/SynopseCommit.inc index 6e2f7680a..96caa555c 100644 --- a/SynopseCommit.inc +++ b/SynopseCommit.inc @@ -1 +1 @@ -'1.18.5515' +'1.18.5517'