137 | + account: see all transfers in ~progress http://www.rejetto.com/forum/index.php/topic,9325.msg1053416/topicseen.html#msg1053416
138 | + limit account to a host mask
139 | + tray icons for uploads
140 | + MAC filter
141 | + if robots.txt does not exist, an option "don't be listed on search engines"
142 | + menu -> limits -> temporarily disable all limits
143 | + menu -> limits -> max speed for each address
144 | + option to display counter for folders as number of accesses instead of total access to files in it
145 | + autodisabling accounts www.rejetto.com/forum/?topic=5379
146 | + on update, propose a link to the "what's new" page
147 | + add special folder (expert mode): let you specify a special folder, like documents, or manually enter a path (useful for relative paths), or GUID
148 | + logs rotation (hfs always append)
149 | + double address bar, one for LAN and one for the Internet
150 | + per user diff-tpl (apply both folder and user diff-tpl.s, and let the user decide priority, default:user,folder)
151 | + "Folder image mask", a file mask indicating the external file that should be used as icon (~img_folder)
152 | + to be able to add icons from multi-icon files (like shell32.dll)
153 | + installer (saving to registry, and making ini-file the new default, don't ask for shell menu)
154 | + support unicode filenames (FindFirstFileW. Cannot be done because widgets don't support unicode)
155 | * show "(home)" instead of "/" in VFS
156 | + show updateinfo notice also in autocheck
157 | + top 10 downloaded files
158 | + after the self test fails, and a router problem is detected, prompt a wizard for portforwad.com (extract routers list)
159 | + export vfs map as txt/html
160 | + specific message for each disabled account
161 | + search for files including meta information (id3 tag) www.rejetto.com/forum/?topic=5312
162 | + multiupload using flash
163 | ? support shortcut to folders in real-folders
164 | * save custom icons in VFS only once
165 | + DNS reverse for ips www.rejetto.com/forum/?topic=4970
166 | + UPnP (www.upnpworks.alcidelic.com/links.html) (www.whitebear.ch) (http://miniupnp.free.fr/) (http://miniupnp.free.fr/files/xchat-upnp20061022.patch)
167 | + UPnP Media Server www.rejetto.com/forum/?topic=5538
168 | + save to registry in different strings
169 | + loading cfg, if version < current then warn user
170 | + auto-ban IP if: bad login X times (with IP exclusion mask) [macros?]
171 | + deny listing and download for uploading files
172 | + download/upload quota (global and account-based)
173 | + purge VFS from unexistant items: files, real folders, virtual folders, empty folders
174 | + custom browser [with an help on how to get a new browser window]
175 | + admin panel (with stats, and maybe commands)
176 | + limit bandwidth/downloads/kb by user, by ip
177 | + external text files with list of authorized IP addresses/accounts
178 | + periodic password changer? (t=1061)
179 | + thumbnails mode (create thumbnails on addition)
180 | ? socks5
181 | + SSI
182 | + support file renaming notification
183 | + multi-language
184 | + zip format for folder archives
185 | * stop using /~commands and move all of them in the standard ?name=value form
186 |
187 | VER 3
188 | + new kind of folder (no more real/virtual folders)
189 |
190 | VER 3.5
191 | + search for files
192 | + file/folder properties in a window (collect menu commands)
193 | + undo (multilevel)
194 |
195 | VER 4
196 | + PHP/ASP via CGI (http://cgi-spec.golux.com/)
197 |
198 | VER 5
199 | + ftp / webdav
200 |
--------------------------------------------------------------------------------
/recompile data.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 | BuiltTime.exe
3 | @REM Copy rsvars.bat from Delphi bin directory
4 | @ECHO SET variable D_COMPONENTS with path for components
5 | @call rsvars.bat
6 | @ECHO compiling
7 | %BDS%\bin\brcc32 res\data.rc -fodata.res
8 | exit;
9 |
10 | %BDS%\bin\dcc32.exe hfs.dpr -$W+ --no-config -M -Q -TX.exe -AForms=VCL.Forms;Generics.Collections=System.Generics.Collections;Generics.Defaults=System.Generics.Defaults;WinTypes=Winapi.Windows;WinProcs=Winapi.Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE -DDEBUG -DUSE_SYMCRYPTO -I"%BDS%\Lib\Debug";"%BDS%\lib\Win32\release";%USERPROFILE%\Documents\Embarcadero\Studio\20.0\Imports;"%BDS%\Imports";C:\Users\Public\Documents\Embarcadero\Studio\20.0\Dcp;"%BDS%\include";srv;..\RnQ\For.RnQ;%D_COMPONENTS%\other\compiled;%D_COMPONENTS%\fastmm4;%D_COMPONENTS%\kdl;%D_COMPONENTS%\ICSv8\source;%D_COMPONENTS%\jcl\source\windows;%D_COMPONENTS%\jcl\source\include;%D_COMPONENTS%\jcl\source\common -LEC:\Users\Public\Documents\Embarcadero\Studio\20.0\Bpl -LNC:\Users\Public\Documents\Embarcadero\Studio\20.0\Dcp -NSData.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Data;Datasnap;Web;Soap;Winapi;System.Win; -O"%BDS%\Lib\Debug";"%BDS%\lib\Win32\release";"%BDS%\Imports";C:\Users\Public\Documents\Embarcadero\Studio\20.0\Dcp;"%BDS%\include";jcl;srv;..\RnQ\For.RnQ;..\RnQ\For.RnQ\zip;..\RnQ\For.RnQ\RTL;..\RnQ\for.RnQ\External\mORMot2\src\core;..\RnQ\for.RnQ\External\mORMot2\src\crypt;%D_COMPONENTS%\ICSv8\source;%D_COMPONENTS%\jcl\source\windows;%D_COMPONENTS%\jcl\source\include;%D_COMPONENTS%\jcl\source\common -R"%BDS%\Lib\Debug";"%BDS%\lib\Win32\release";"%BDS%\Imports";C:\Users\Public\Documents\Embarcadero\Studio\20.0\Dcp;"%BDS%\include";jcl;srv;..\RnQ\For.RnQ;..\RnQ\For.RnQ\zip;..\RnQ\For.RnQ\RTL;..\RnQ\for.RnQ\External\mORMot2\src\core;..\RnQ\for.RnQ\External\mORMot2\src\crypt;%D_COMPONENTS%\ICSSv8\source;%D_COMPONENTS%\jcl\source\windows;%D_COMPONENTS%\jcl\source\include;%D_COMPONENTS%\jcl\source\common -U"%BDS%\Lib\Debug";"%BDS%\lib\Win32\release";"%BDS%\Imports";C:\Users\Public\Documents\Embarcadero\Studio\20.0\Dcp;"%BDS%\include";jcl;srv;..\RnQ\For.RnQ;..\RnQ\For.RnQ\zip;..\RnQ\For.RnQ\RTL;..\RnQ\for.RnQ\External\mORMot2\src\core;..\RnQ\for.RnQ\External\mORMot2\src\crypt;%D_COMPONENTS%\ICSv8\source;%D_COMPONENTS%\jcl\source\windows;%D_COMPONENTS%\jcl\source\include;%D_COMPONENTS%\jcl\source\common -K00400000 --description:"HFS ~ HTTP File Server - www.rejetto.com/hfs" -GD -NBC:\Users\Public\Documents\Embarcadero\Studio\20.0\Dcp -NOUnits -NUUnits -NHC:\Users\Public\Documents\Embarcadero\Studio\20.0\hpp\Win32
11 |
12 | exit;
13 | %BDS%\bin\dcc32.exe hfs.dpr -$W+ --no-config -M -Q -TX.exe -AForms=VCL.Forms;Generics.Collections=System.Generics.Collections;Generics.Defaults=System.Generics.Defaults;WinTypes=Winapi.Windows;WinProcs=Winapi.Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE
14 | -DDEBUG -DUSE_SYMCRYPTO
15 | -I"%BDS%\Lib\Debug";"%BDS%\lib\Win32\release";%USERPROFILE%\Documents\Embarcadero\Studio\20.0\Imports;"%BDS%\Imports";C:\Users\Public\Documents\Embarcadero\Studio\20.0\Dcp;"%BDS%\include";..\RnQ\For.RnQ;%D_COMPONENTS%\other\compiled;%D_COMPONENTS%\fastmm4;%D_COMPONENTS%\kdl;%D_COMPONENTS%\icsv9\source;lib;
16 | -LEC:\Users\Public\Documents\Embarcadero\Studio\20.0\Bpl
17 | -LNC:\Users\Public\Documents\Embarcadero\Studio\20.0\Dcp
18 | -NSData.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Data;Datasnap;Web;Soap;Winapi;System.Win;
19 | -O"%BDS%\Lib\Debug";"%BDS%\lib\Win32\release";"%BDS%\Imports";C:\Users\Public\Documents\Embarcadero\Studio\20.0\Dcp;"%BDS%\include";jcl;..\RnQ\For.RnQ;..\RnQ\For.RnQ\zip;..\RnQ\For.RnQ\RTL;..\RnQ\for.RnQ\External\mORMot2\src\core;%D_COMPONENTS%\icsv9\source;lib;srv;
20 | -R"%BDS%\Lib\Debug";"%BDS%\lib\Win32\release";"%BDS%\Imports";C:\Users\Public\Documents\Embarcadero\Studio\20.0\Dcp;"%BDS%\include";jcl;..\RnQ\For.RnQ;..\RnQ\For.RnQ\zip;..\RnQ\For.RnQ\RTL;..\RnQ\for.RnQ\External\mORMot2\src\core;%D_COMPONENTS%\icsv9\source;lib;srv;
21 | -U"%BDS%\Lib\Debug";"%BDS%\lib\Win32\release";"%BDS%\Imports";C:\Users\Public\Documents\Embarcadero\Studio\20.0\Dcp;"%BDS%\include";jcl;..\RnQ\For.RnQ;..\RnQ\For.RnQ\zip;..\RnQ\For.RnQ\RTL;..\RnQ\for.RnQ\External\mORMot2\src\core;%D_COMPONENTS%\icsv9\source;lib;srv;
22 | -K00400000 --description:"HFS ~ HTTP File Server - www.rejetto.com/hfs" -GD -NBC:\Users\Public\Documents\Embarcadero\Studio\20.0\Dcp -NOUnits -NUUnits
23 | -NHC:\Users\Public\Documents\Embarcadero\Studio\20.0\hpp\Win32
24 |
--------------------------------------------------------------------------------
/res/NoMacros.tpl:
--------------------------------------------------------------------------------
1 | []
2 |
3 | %folder%
4 |
5 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
22 |
23 |
113 |
114 |
115 |
116 | %files%
117 |
118 |
119 |
121 |
122 |
123 | [files]
124 |
128 | Files for download:
129 | %list%
130 |
131 | [special:alias|cache]
132 |
133 | [file.jpg = file.JPG = file.jpeg = file.png = file.gif = file.tif = file.bmp = file.webp]
134 |
136 |
137 | [file.mp4 = file.m4v = file.mkv = file.flv = file.avi = file.wmv = file.webm = file.mov]
138 |
140 |
141 | [file.mp3 = file.m4a = file.wma = file.flac = file.ogg = file.aac]
142 |
144 |
145 | [file.doc = file.odt = file.docx = file.xls = file.ods = file.xlsx = file.pdf = file.mobi = file.epub = file.lit = file.txt]
146 |
148 |
149 | [file]
150 |
152 |
153 | [link]
154 |
156 |
157 | [folder]
158 |
161 |
162 | [nofiles]
163 | %url%
⇦ Back
164 |
165 | [api level]
166 | 999
167 |
168 | [error-page]
169 | %content%
170 |
171 | [overload]
172 |
173 | [max contemp downloads]
174 |
175 | [server is busy|public]
176 |
177 | Busy
178 |
Server is busy.
Returning to previous page...
179 |
180 | [not found]
181 |
182 | 404
183 |
Folder Not Found.
Returning to previous page...
184 |
185 | [404|public]
186 |
187 | 404
188 |
File Not Found.
Returning to previous page...
189 |
190 | [unauth]
191 |
192 | [deny]
193 |
194 | [ban]
195 |
196 | [upload|public]
197 | Upload to: %folder%
198 |
199 |
200 |
201 |
202 | Upload to: %folder%
⇦ Back
203 | Upload is not available to due to high server load.
Automatically retrying in seconds...
204 |
205 | :}|{:
206 |
You can upload files into the
%diskfree% available space.
207 | Results page appears after uploads complete
208 |
209 |
210 | [upload-results]
211 |
212 |
213 | Upload results for: %folder%
214 |
215 | Upload results for: %folder%
216 |
217 | [upload-success]
218 | SUCCESS! Uploaded: %item-name% - %item-size%
219 |
220 | [upload-failed]
221 | Error: %item-name%: - %reason%
222 |
223 | [special:import]
224 |
225 | [+special:strings]
226 | option.comment=0
227 |
228 | [newfile]
229 |
230 | [ajax.changepwd|public|no log]
231 |
232 | [login|public]
233 |
234 |
--------------------------------------------------------------------------------
/res/RapidD.public:
--------------------------------------------------------------------------------
1 | {
2 | "Version": 1,
3 | "Serial": "44069D67AF0C8D47260FC6D54CF8D7D8",
4 | "Issuer": "rapid d",
5 | "IssueDate": "2022-05-18",
6 | "ValidityStart": "2022-05-01",
7 | "ValidityEnd": "2036-01-08",
8 | "AuthoritySerial": "44069D67AF0C8D47260FC6D54CF8D7D8",
9 | "AuthorityIssuer": "rapid d",
10 | "IsSelfSigned": true,
11 | "Base64": "AQBECDMIuxtEBp1nrwyNRyYPxtVM+NfYUOxkkSAAAAAAAAAAAAAAAEQGnWevDI1HJg/G1Uz419hQ7GSRIAAAAAAAAAAAAAAAAjFtx1cv3TMLgsf6vpKPe0Fyn3N+lBcvJ5p0wsR30LN50kuPgTMOvB5xVtagqaKSWs3uCEmOvwPLXMLPbMZaQM9o9p+Pucwtd8RgSEfIJR8rIBrfXQQjAJn1t6pvam9YhyXGsWIgAAAA+1Bfb+5Ie5viQLWyC/36BXo5/Xs1DRtEVhE+U8Pwm7w="
12 | }
--------------------------------------------------------------------------------
/res/WindowsXP.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 | Windows Shell
9 |
10 |
11 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
31 | True/PM
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/res/alias.txt:
--------------------------------------------------------------------------------
1 | var length=length|var=$1
2 | cache=trim|{.set|#cache.tmp|{.from table|$1|$2.}.} {.if not|{.^#cache.tmp.}|{:{.set|#cache.tmp|{.dequote|$3.}.}{.set table|$1|$2={.^#cache.tmp.}.}:}.} {.^#cache.tmp.} {.set|#cache.tmp.}
3 | is substring=pos|$1|$2
4 | set append=set|$1|$2|mode=append
5 | 123 if 2=if|$2|$1$2$3
6 | between=if|{.$1 < $3.}|{:{.and|{.$1 <= $2.}|{.$2 <= $3.}:}|{:{.and|{.$3 <= $2.}|{.$2 <= $1.}:}
7 | between!=if|{.$1 < $3.}|{:{.and|{.$1 < $2.}|{.$2 < $3.}:}|{:{.and|{.$3 < $2.}|{.$2 < $1.}:}
8 | file changed=if| {.{.filetime|$1.} > {.^#file changed.$1.}.}|{: {.set|#file changed.$1|{.filetime|$1.}.} {.if|$2|{:{.load|$1|var=$2.}:}.} 1:}
9 | play system event=play
10 | redirect=add header|Location: $1
11 | chop={.cut|{.calc|{.pos|$2|var=$1.}+{.length|$2.}.}||var=$1|remainder=#chop.tmp.}{.^#chop.tmp.}
--------------------------------------------------------------------------------
/res/data.rc:
--------------------------------------------------------------------------------
1 | 1 24 "WindowsXP.manifest"
2 | defaultTpl TEXT default.tpl
3 | // dmBrowserTpl TEXT dmBrowser.tpl
4 | // defaultTpl ZTEXT default.tpl.gz
5 | dmBrowserTpl ZTEXT dmBrowser.tpl.gz
6 | filelistTpl TEXT filelist.tpl
7 | noMacrosTpl TEXT NoMacros.tpl
8 | alias TEXT alias.txt
9 | IPservices TEXT ipservices.txt
10 | // jquery TEXT jquery.min.js
11 | jquery ZTEXT jquery.min.js.gz
12 | //numbers IMAGE numbers.png
13 | numbers32 IMAGE numbers32.png
14 | RDpubkey TEXT RapidD.public
--------------------------------------------------------------------------------
/res/dmBrowser.tpl:
--------------------------------------------------------------------------------
1 |
2 |
3 | HFS %folder%
4 |
5 | %up%
6 | %files%
7 |
8 |
9 |
10 | [up]
11 | UP
12 |
13 | [nofiles]
14 | No files
15 |
16 | [files]
17 | %list%
18 |
19 | [file]
20 | %item-name%
21 |
22 | [folder]
23 | %item-name%
24 |
25 | [comment]
26 |
27 |
28 | [error-page]
29 |
30 | %content%
31 |
32 |
33 |
34 | [not found]
35 | 404 - Not found
36 | go to root
37 |
38 | [overload]
39 | Server busy
40 | Please, retry later.
41 |
--------------------------------------------------------------------------------
/res/dmBrowser.tpl.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drapid/HFS/06a40abe58c47b63bbe60fd63767d33c123d8d44/res/dmBrowser.tpl.gz
--------------------------------------------------------------------------------
/res/filelist.tpl:
--------------------------------------------------------------------------------
1 | %files%
2 |
3 | [files]
4 | %list%
5 |
6 | [file]
7 | %item-full-url%
8 |
9 | [folder]
10 | %item-full-url%
11 |
12 |
--------------------------------------------------------------------------------
/res/fontello.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "css_prefix_text": "fa-",
4 | "css_use_suffix": false,
5 | "hinting": true,
6 | "units_per_em": 1000,
7 | "ascent": 850,
8 | "glyphs": [
9 | {
10 | "uid": "823a9e02e643318116fea40a00190e4e",
11 | "css": "asterisk",
12 | "code": 59392,
13 | "src": "fontawesome"
14 | },
15 | {
16 | "uid": "43ab845088317bd348dee1d975700c48",
17 | "css": "check-circled",
18 | "code": 59393,
19 | "src": "fontawesome"
20 | },
21 | {
22 | "uid": "8b80d36d4ef43889db10bc1f0dc9a862",
23 | "css": "user",
24 | "code": 59394,
25 | "src": "fontawesome"
26 | },
27 | {
28 | "uid": "598a5f2bcf3521d1615de8e1881ccd17",
29 | "css": "clock",
30 | "code": 59395,
31 | "src": "fontawesome"
32 | },
33 | {
34 | "uid": "9a76bc135eac17d2c8b8ad4a5774fc87",
35 | "css": "download",
36 | "code": 59396,
37 | "src": "fontawesome"
38 | },
39 | {
40 | "uid": "eeec3208c90b7b48e804919d0d2d4a41",
41 | "css": "upload",
42 | "code": 59397,
43 | "src": "fontawesome"
44 | },
45 | {
46 | "uid": "98d9c83c1ee7c2c25af784b518c522c5",
47 | "css": "ban",
48 | "code": 59398,
49 | "src": "fontawesome"
50 | },
51 | {
52 | "uid": "d35a1d35efeb784d1dc9ac18b9b6c2b6",
53 | "css": "edit",
54 | "code": 59399,
55 | "src": "fontawesome"
56 | },
57 | {
58 | "uid": "dd6c6b221a1088ff8a9b9cd32d0b3dd5",
59 | "css": "check",
60 | "code": 59400,
61 | "src": "fontawesome"
62 | },
63 | {
64 | "uid": "f8aa663c489bcbd6e68ec8147dca841e",
65 | "css": "folder",
66 | "code": 59401,
67 | "src": "fontawesome"
68 | },
69 | {
70 | "uid": "197375a3cea8cb90b02d06e4ddf1433d",
71 | "css": "globe",
72 | "code": 59402,
73 | "src": "fontawesome"
74 | },
75 | {
76 | "uid": "d7271d490b71df4311e32cdacae8b331",
77 | "css": "home",
78 | "code": 59403,
79 | "src": "fontawesome"
80 | },
81 | {
82 | "uid": "f2aa28a2548ed3d2be718d087b65ee21",
83 | "css": "key",
84 | "code": 59404,
85 | "src": "fontawesome"
86 | },
87 | {
88 | "uid": "c1f1975c885aa9f3dad7810c53b82074",
89 | "css": "lock",
90 | "code": 59405,
91 | "src": "fontawesome"
92 | },
93 | {
94 | "uid": "a73c5deb486c8d66249811642e5d719a",
95 | "css": "refresh",
96 | "code": 59406,
97 | "src": "fontawesome"
98 | },
99 | {
100 | "uid": "09feb4465d9bd1364f4e301c9ddbaa92",
101 | "css": "retweet",
102 | "code": 59407,
103 | "src": "fontawesome"
104 | },
105 | {
106 | "uid": "474656633f79ea2f1dad59ff63f6bf07",
107 | "css": "star",
108 | "code": 59408,
109 | "src": "fontawesome"
110 | },
111 | {
112 | "uid": "0f4cae16f34ae243a6144c18a003f2d8",
113 | "css": "cancel-circled",
114 | "code": 59409,
115 | "src": "fontawesome"
116 | },
117 | {
118 | "uid": "7f3d8ff1d5f6ee019f0c00ed7a86caec",
119 | "css": "truck",
120 | "code": 59410,
121 | "src": "fontawesome"
122 | },
123 | {
124 | "uid": "559647a6f430b3aeadbecd67194451dd",
125 | "css": "menu",
126 | "code": 61641,
127 | "src": "fontawesome"
128 | },
129 | {
130 | "uid": "3a26448b711645ba1abfc86c1a6e2f30",
131 | "css": "coffee",
132 | "code": 61684,
133 | "src": "fontawesome"
134 | },
135 | {
136 | "uid": "ab95e1351ebaec5850101097cbf7097f",
137 | "css": "quote-left",
138 | "code": 61709,
139 | "src": "fontawesome"
140 | },
141 | {
142 | "uid": "e80ae555c1413a4ec18b33fb348b4049",
143 | "css": "file-archive",
144 | "code": 61894,
145 | "src": "fontawesome"
146 | },
147 | {
148 | "uid": "bbfb51903f40597f0b70fd75bc7b5cac",
149 | "css": "trash",
150 | "code": 61944,
151 | "src": "fontawesome"
152 | },
153 | {
154 | "uid": "818981e2ad316f18ae61cfa805d41309",
155 | "css": "user-circle",
156 | "code": 62141,
157 | "src": "fontawesome"
158 | },
159 | {
160 | "uid": "5278ef7773e948d56c4d442c8c8c98cf",
161 | "css": "lightbulb",
162 | "code": 61675,
163 | "src": "fontawesome"
164 | },
165 | {
166 | "uid": "56a21935a5d4d79b2e91ec00f760b369",
167 | "css": "sort",
168 | "code": 61660,
169 | "src": "fontawesome"
170 | },
171 | {
172 | "uid": "0cd2582b8c93719d066ee0affd02ac78",
173 | "css": "sort-alt-up",
174 | "code": 61792,
175 | "src": "fontawesome"
176 | },
177 | {
178 | "uid": "27b13eff5eb0ca15e01a6e65ffe6eeec",
179 | "css": "sort-alt-down",
180 | "code": 61793,
181 | "src": "fontawesome"
182 | },
183 | {
184 | "uid": "9dd9e835aebe1060ba7190ad2b2ed951",
185 | "css": "search",
186 | "code": 59411,
187 | "src": "fontawesome"
188 | },
189 | {
190 | "uid": "0d20938846444af8deb1920dc85a29fb",
191 | "css": "logout",
192 | "code": 59412,
193 | "src": "fontawesome"
194 | }
195 | ]
196 | }
--------------------------------------------------------------------------------
/res/hfs_Icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drapid/HFS/06a40abe58c47b63bbe60fd63767d33c123d8d44/res/hfs_Icon.ico
--------------------------------------------------------------------------------
/res/ipservices.txt:
--------------------------------------------------------------------------------
1 | http://hfsservice.rejetto.com/ip.php|!
2 | http://checkip.dyndns.org|:
3 | http://checkip.amazonaws.com|
4 | http://whatismyip.akamai.com|
5 | http://bot.whatismyipaddress.com|
6 |
--------------------------------------------------------------------------------
/res/jquery.min.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drapid/HFS/06a40abe58c47b63bbe60fd63767d33c123d8d44/res/jquery.min.js.gz
--------------------------------------------------------------------------------
/res/numbers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drapid/HFS/06a40abe58c47b63bbe60fd63767d33c123d8d44/res/numbers.png
--------------------------------------------------------------------------------
/res/numbers32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drapid/HFS/06a40abe58c47b63bbe60fd63767d33c123d8d44/res/numbers32.png
--------------------------------------------------------------------------------
/res/shell.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drapid/HFS/06a40abe58c47b63bbe60fd63767d33c123d8d44/res/shell.png
--------------------------------------------------------------------------------
/runscriptDlg.dfm:
--------------------------------------------------------------------------------
1 | object runScriptFrm: TrunScriptFrm
2 | Left = 0
3 | Top = 0
4 | Caption = 'Run script'
5 | ClientHeight = 312
6 | ClientWidth = 544
7 | Color = clBtnFace
8 | Font.Charset = DEFAULT_CHARSET
9 | Font.Color = clWindowText
10 | Font.Height = -11
11 | Font.Name = 'Tahoma'
12 | Font.Style = []
13 | OldCreateOrder = False
14 | PixelsPerInch = 96
15 | TextHeight = 13
16 | object resultBox: TMemo
17 | Left = 0
18 | Top = 41
19 | Width = 544
20 | Height = 271
21 | Align = alClient
22 | Lines.Strings = (
23 | 'Write your script in the external editor, then click Run.'
24 | 'In this box will see the result of the script you run.')
25 | TabOrder = 0
26 | end
27 | object Panel1: TPanel
28 | Left = 0
29 | Top = 0
30 | Width = 544
31 | Height = 41
32 | Align = alTop
33 | BevelOuter = bvNone
34 | TabOrder = 1
35 | object sizeLbl: TLabel
36 | Left = 503
37 | Top = 24
38 | Width = 32
39 | Height = 13
40 | Alignment = taRightJustify
41 | Caption = 'Size: 0'
42 | end
43 | object runBtn: TButton
44 | Left = 16
45 | Top = 10
46 | Width = 75
47 | Height = 25
48 | Caption = '&Run'
49 | TabOrder = 0
50 | OnClick = runBtnClick
51 | end
52 | object autorunChk: TCheckBox
53 | Left = 104
54 | Top = 16
55 | Width = 169
56 | Height = 17
57 | Caption = '&Auto run at every saving'
58 | Checked = True
59 | State = cbChecked
60 | TabOrder = 1
61 | end
62 | end
63 | end
64 |
--------------------------------------------------------------------------------
/runscriptDlg.pas:
--------------------------------------------------------------------------------
1 | unit runscriptDlg;
2 |
3 | interface
4 |
5 | uses
6 | SysUtils, Variants, Classes, Graphics, Controls, Forms,
7 | Dialogs, StdCtrls, ExtCtrls;
8 |
9 | type
10 | TrunScriptFrm = class(TForm)
11 | resultBox: TMemo;
12 | Panel1: TPanel;
13 | runBtn: TButton;
14 | autorunChk: TCheckBox;
15 | sizeLbl: TLabel;
16 | procedure runBtnClick(Sender: TObject);
17 | private
18 | { Private declarations }
19 | public
20 | { Public declarations }
21 | end;
22 |
23 | var
24 | runScriptFrm: TrunScriptFrm;
25 |
26 | implementation
27 |
28 | {$R *.dfm}
29 |
30 | uses
31 | main, srvUtils, srvClassesLib, scriptLib, RDFileUtil;
32 |
33 | procedure TrunScriptFrm.runBtnClick(Sender: TObject);
34 | var
35 | tpl: Ttpl;
36 | begin
37 | tpl := Ttpl.create;
38 | try
39 | try
40 | tpl.fullText := loadFile(tempScriptFilename);
41 | resultBox.text := runScript(mainFrm.fileSrv, tpl[''], NIL, tpl);
42 | sizeLbl.Caption := getTill(':', sizeLbl.Caption)+': '+intToStr(length(resultBox.text));
43 | except
44 | on e:Exception do
45 | resultBox.text:=e.message
46 | end;
47 | finally
48 | tpl.free
49 | end;
50 | end;
51 |
52 | end.
53 |
--------------------------------------------------------------------------------
/srv/hsUtils.pas:
--------------------------------------------------------------------------------
1 | {
2 | Copyright (C) 2002-2020 Massimo Melina (www.rejetto.com)
3 |
4 | This program is free software; you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation; either version 2 of the License, or
7 | (at your option) any later version.
8 |
9 | This program is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with this program; if not, write to the Free Software
16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 |
18 |
19 | HTTP Server Utils
20 |
21 | }
22 | {$I- }
23 |
24 | unit HSUtils;
25 | {$INCLUDE defs.inc }
26 | {$I NoRTTI.inc}
27 |
28 | interface
29 |
30 | uses
31 | classes, messages,
32 | contnrs, strUtils,
33 | types
34 | ;
35 |
36 |
37 | // decode/decode url
38 | function decodeURL(const url: String; utf8: Boolean=TRUE): UnicodeString; OverLoad;
39 | function decodeURL(const url: RawByteString): UnicodeString; OverLoad;
40 | function encodeURL(const url: String; nonascii: Boolean=TRUE; spaces: Boolean=TRUE;
41 | htmlEncoding: Boolean=FALSE):string; OverLoad;
42 | function encodeURL(const url: RawByteString; nonascii: Boolean=TRUE; spaces: Boolean=TRUE;
43 | unicode: boolean=FALSE): RawByteString; OverLoad;
44 | // returns true if address is not suitable for the internet
45 | function isLocalIP(const ip: String): Boolean;
46 | // ensure a string ends with a specific string
47 | procedure includeTrailingString(var s: UnicodeString; const ss: UnicodeString); OverLoad;
48 | procedure includeTrailingString(var s: RawByteString; const ss: RawByteString); OverLoad;
49 | // gets unicode code for specified character
50 | function charToUnicode(c: WideChar): dword; OverLoad;
51 | function charToUnicode(c: AnsiChar): dword; OverLoad;
52 | // this version of pos() is able to skip the pattern if inside quotes
53 | {$IFDEF UNICODE}
54 | function nonQuotedPos(const ss, s: String; ofs: Integer=1; const quote: String='"'; const unquote: String='"'): Integer; OverLoad;
55 | {$ENDIF UNICODE}
56 | function nonQuotedPos(const ss, s: RawByteString; ofs: integer=1; const quote: RawByteString='"'; const unquote: RawByteString='"'): Integer; OverLoad;
57 | // case insensitive version
58 | //function ipos(ss, s:string; ofs:integer=1):integer; overload;
59 | function getNameOf(const s: String): String; OverLoad; // colon included
60 | function getNameOf(const s: RawByteString): RawByteString; OverLoad; // colon included
61 | function namePos(const name: string; const headers:string; from:integer=1):integer; OverLoad;
62 | function namePos(const name: RawByteString; const headers: RawByteString; from: integer=1):integer; OverLoad;
63 |
64 | implementation
65 |
66 | uses
67 | Windows, sysutils,
68 | {$IFDEF UNICODE}
69 | AnsiStrings,
70 | // AnsiClasses,
71 | {$ENDIF UNICODE}
72 | OverbyteIcsWSocket,
73 | RDUtils,
74 | srvConst;
75 |
76 | const
77 | HEADER_LIMITER: RawByteString = CRLFA+CRLFA;
78 | MAX_REQUEST_LENGTH = 64*1024;
79 | MAX_INPUT_BUFFER_LENGTH = 256*1024;
80 | HexCharsW: set of Char = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
81 | 'A', 'B', 'C', 'D', 'E', 'F']; //
82 | procedure includeTrailingString(var s: UnicodeString; const ss: UnicodeString);
83 | begin if copy(s, length(s)-length(ss)+1, length(ss)) <> ss then s:=s+ss end;
84 |
85 | procedure includeTrailingString(var s: RawByteString; const ss: RawByteString);
86 | begin
87 | if copy(s, length(s)-length(ss)+1, length(ss)) <> ss then
88 | s:=s+ss
89 | end;
90 |
91 | function charToUnicode(c: WideChar):dword;
92 | begin stringToWideChar(c,@result,4) end;
93 |
94 | function charToUnicode(c: AnsiChar):dword;
95 | begin stringToWideChar(c,@result,4) end;
96 |
97 | function isLocalIP(const ip:string):boolean;
98 | var
99 | r: record d,c,b,a:byte end;
100 | begin
101 | if ip = '::1' then
102 | exit(TRUE);
103 | if ip = '' then
104 | exit(False);
105 | {$IFDEF FPC}
106 | dword(r) := WSocket_ntohl(WSocket_inet_addr(@ip[1]));
107 | {$ELSE FPC}
108 | dword(r) := dword(WSocket_ntohl(WSocket_inet_addr(ansiString(ip))));
109 | {$ENDIF FPC}
110 | result:=(r.a in [0,10,23,127])
111 | or (r.a = 192) and ((r.b = 168) or (r.b = 0) and (r.c = 2))
112 | or (r.a = 169) and (r.b = 254)
113 | or (r.a = 172) and (r.b in [16..31])
114 | end; // isLocalIP
115 |
116 | function min(a,b:integer):integer;
117 | begin if a < b then result:=a else result:=b end;
118 |
119 |
120 |
121 | {$IFDEF UNICODE}
122 | function nonQuotedPos(const ss, s: String; ofs: Integer=1; const quote: String='"'; const unquote: String='"'): Integer; OverLoad;
123 | var
124 | qpos: integer;
125 | begin
126 | repeat
127 | result := posEx(ss, s, ofs);
128 | if result = 0 then
129 | exit;
130 |
131 | repeat
132 | qpos := posEx(quote, s, ofs);
133 | if qpos = 0 then
134 | exit; // there's no quoting, our result will fit
135 | if qpos > result then
136 | exit; // the quoting doesn't affect the piece, accept the result
137 | ofs := posEx(unquote, s, qpos+1);
138 | if ofs = 0 then
139 | exit; // it is not closed, we don't consider it quoting
140 | inc(ofs);
141 | until ofs > result; // this quoting was short, let's see if we have another
142 | until false;
143 | end; // nonQuotedPos
144 | {$ENDIF UNICODE}
145 |
146 | function nonQuotedPos(const ss, s: RawByteString; ofs: integer=1; const quote: RawByteString='"'; const unquote: RawByteString='"'):integer; OverLoad;
147 | var
148 | qpos: integer;
149 | begin
150 | repeat
151 | result:=posEx(ss, s, ofs);
152 | if result = 0 then exit;
153 |
154 | repeat
155 | qpos:=posEx(quote, s, ofs);
156 | if qpos = 0 then exit; // there's no quoting, our result will fit
157 | if qpos > result then exit; // the quoting doesn't affect the piece, accept the result
158 | ofs:=posEx(unquote, s, qpos+1);
159 | if ofs = 0 then exit; // it is not closed, we don't consider it quoting
160 | inc(ofs);
161 | until ofs > result; // this quoting was short, let's see if we have another
162 | until false;
163 | end; // nonQuotedPos
164 |
165 | function decodeURL(const url: string; utf8: boolean=TRUE): UnicodeString;
166 | var
167 | i, l: integer;
168 | c: char;
169 | resA: RawByteString;
170 | ca: AnsiChar;
171 | c1, c2: Char;
172 | hv: Boolean;
173 | begin
174 | setLength(result, length(url));
175 | if length(url) = 0 then
176 | Exit;
177 | setLength(resA, length(url));
178 | l := 0;
179 | i := 1;
180 | while i<=length(url) do
181 | begin
182 | hv := False;
183 | if (url[i] = '%') and (i+2 <= length(url)) then
184 | begin
185 | c1 := url[i+1];
186 | c2 := url[i+2];
187 | if (c1 in HexCharsW) and
188 | (c2 in HexCharsW) then
189 | try
190 | if utf8 then
191 | ca := AnsiChar(strToInt( '$'+c1+c2 ))
192 | else
193 | c := char(strToInt( '$'+c1+c2 ));
194 | inc(i,2); // three chars for one
195 | hv := True;
196 | except
197 | hv := False;
198 | end;
199 | end;
200 |
201 | if not hv then
202 | if utf8 then
203 | ca := AnsiChar(url[i])
204 | else
205 | c := url[i];
206 |
207 | inc(i);
208 | inc(l);
209 | if utf8 then
210 | resA[l] := ca
211 | else
212 | result[l] := c;
213 | end;
214 | if utf8 then
215 | begin
216 | setLength(resA, l);
217 | Result := UnUTF(resA);
218 | end
219 | else
220 | setLength(result, l);
221 | end; // decodeURL
222 |
223 | function decodeURL(const url: RawByteString): UnicodeString;
224 | var
225 | i, l: integer;
226 | resA: RawByteString;
227 | c: AnsiChar;
228 | begin
229 | setLength(result, length(url));
230 | setLength(resA, length(url));
231 | l := 0;
232 | i := 1;
233 | while i<=length(url) do
234 | begin
235 | if (url[i] = '%') and (i+2 <= length(url)) then
236 | try
237 | c := AnsiChar(strToIntA(RawByteString('$')+url[i+1]+url[i+2] ));
238 | inc(i,2); // three chars for one
239 | except
240 | c := url[i];
241 | end
242 | else
243 | c := url[i];
244 |
245 | inc(i);
246 | inc(l);
247 | resA[l] := c;
248 | end;
249 | setLength(resA, l);
250 | Result := UnUTF(resA);
251 | end; // decodeURL
252 |
253 |
254 | function encodeURL(const url:string; nonascii:boolean=TRUE; spaces:boolean=TRUE;
255 | htmlEncoding:boolean=FALSE):string;
256 | var
257 | i: integer;
258 | encodePerc, encodeHTML: TcharSetW;
259 | encodePercA: TcharSetA;
260 | a: RawByteString;
261 | begin
262 | result:='';
263 | if url = '' then
264 | exit;
265 | encodeHTML:=[];
266 | encodePercA := [];
267 | if nonascii then
268 | encodePercA:=[#0..#31,'#','%','?','"','''','&','<','>',':'] + [#128..#255];
269 | encodePerc:=[#0..#31,'#','%','?','"','''','&','<','>',':'];
270 | // actually ':' needs encoding only in relative url
271 | if spaces then include(encodePerc,' ');
272 | if not htmlEncoding then
273 | begin
274 | encodePerc:=encodePerc+encodeHTML;
275 | encodeHTML:=[];
276 | end;
277 | if nonascii then
278 | begin
279 | a:=UTF8encode(url); // couldn't find a better way to force url to have the UTF8 encoding
280 | for i:=1 to length(a) do
281 | if a[i] in encodePercA then
282 | result:=result+'%'+intToHex(ord(a[i]),2)
283 | else if a[i] in encodeHTML then
284 | result:=result+''+intToStr(charToUnicode(a[i]))+';'
285 | else
286 | result:=result+a[i];
287 | end
288 | else
289 | for i:=1 to length(url) do
290 | if url[i] in encodePerc then
291 | result:=result+'%'+intToHex(ord(url[i]),2)
292 | else if url[i] in encodeHTML then
293 | result:=result+''+intToStr(charToUnicode(url[i]))+';'
294 | else
295 | result:=result+url[i];
296 | end; // encodeURL
297 |
298 | function encodeURL(const url: RawByteString; nonascii:boolean=TRUE; spaces:boolean=TRUE;
299 | unicode:boolean=FALSE): RawByteString;
300 | var
301 | i: integer;
302 | encodePerc, encodeUni: set of AnsiChar;
303 | begin
304 | result := '';
305 | encodeUni := [];
306 | if nonascii then
307 | encodeUni:=[#128..#255];
308 | encodePerc := [#0..#31,'#','%','?','"','''','&','<','>',':'];
309 | // actually ':' needs encoding only in relative url
310 | if spaces then
311 | include(encodePerc,' ');
312 | if not unicode then
313 | begin
314 | encodePerc:=encodePerc+encodeUni;
315 | encodeUni:=[];
316 | end;
317 | for i:=1 to length(url) do
318 | if url[i] in encodePerc then
319 | result := result+'%'+IntToHexA(ord(url[i]),2)
320 | else if url[i] in encodeUni then
321 | result := result+''+IntToStrA(Byte(url[i]))+';'
322 | else
323 | result := result+url[i];
324 | end; // encodeURL
325 |
326 | function replyHeader_IntPositive(const name: String; int: Int64): String;
327 | begin
328 | result := '';
329 | if int >= 0 then
330 | result := name+': '+intToStr(int)+CRLF;
331 | end;
332 |
333 | {
334 | function replyHeader_Str(const name:string; const str:string):string;
335 | begin
336 | result:='';
337 | if str > '' then result:=name+': '+str+CRLF;
338 | end;
339 | }
340 | function replyHeader_Str(const name:RawByteString; const str:RawByteString): RawByteString; OverLoad;
341 | begin
342 | result:='';
343 | if str > '' then result:=name+': '+str+CRLFA;
344 | end;
345 |
346 | function replyHeader_Str(const name:RawByteString; const str:String): RawByteString; OverLoad;
347 | begin
348 | result:='';
349 | if str > '' then result:=name+': '+ StrToUTF8(str)+CRLFA;
350 | end;
351 |
352 | function getNameOf(const s:string):string; // colon included
353 | begin result:=copy(s, 1, pos(':', s)) end;
354 |
355 | function getNameOf(const s: RawByteString): RawByteString; // colon included
356 | begin result:=copy(s, 1, pos(RawByteString(':'), s)) end;
357 |
358 | // return 0 if not found
359 | function namePos(const name:string; const headers:string; from:integer=1):integer;
360 | begin
361 | result:=from;
362 | repeat
363 | result:=ipos(name, headers, result);
364 | until (result<=1) // both not found and found at the start of the string
365 | or (headers[result-1] = #10) // or start of the line
366 | end; // namePos
367 |
368 | function namePos(const name: RawByteString; const headers: RawByteString; from: integer=1):integer; OverLoad;
369 | begin
370 | result := from;
371 | repeat
372 | result := ipos(name, headers, result);
373 | until (result<=1) // both not found and found at the start of the string
374 | or (headers[result-1] = #10) // or start of the line
375 | end; // namePos
376 |
377 | end.
378 |
--------------------------------------------------------------------------------
/srv/netUtils.pas:
--------------------------------------------------------------------------------
1 | {$INCLUDE defs.inc }
2 | unit netUtils;
3 | {$I NoRTTI.inc}
4 |
5 | interface
6 |
7 | uses
8 | Classes, Windows,
9 | OverbyteIcshttpProt,
10 | {$IFNDEF FPC}
11 | OverbyteIcsUtils,
12 | OverbyteIcsTypes,
13 | {$ENDIF FPC}
14 | Types,
15 | srvClassesLib;
16 |
17 | type
18 | TProgressFunc = function(p: real): Boolean of object;
19 |
20 | function httpGetStr(const url: string; from: int64=0; size: int64=-1): string;
21 | function httpGet(const url:string; from:int64=0; size:int64=-1): RawByteString;
22 | // function httpGetFile1(const url, filename: string; var errMsg: String; notify: TdocDataEvent=NIL): Boolean;
23 | // function httpGetFileWithCheck1(const url, filename: string; var errMsg: String; notify: TdocDataEvent=NIL): Boolean;
24 | function httpGetFile(const url, filename: string; var errMsg: String; notify: TProgressFunc=NIL): Boolean;
25 | function httpGetFileWithCheck(const url, filename: string; var errMsg: String; notify: TProgressFunc=NIL): Boolean;
26 |
27 | function httpFileSize(const url: string): int64;
28 | function getIPs(): TStringDynArray;
29 | function getLocalIPs({$IFDEF USE_IPv6}const ASocketFamily: TSocketFamily = sfIPv4 {$ENDIF USE_IPv6}): TStringDynArray;
30 | function findRedirection(var h, p: String; const agent: String): Boolean;
31 | function checkHTTPSCanWork(var missing: TStringDynArray): Boolean; OverLoad;
32 | function checkHTTPSCanWork(): Boolean; OverLoad;
33 | function getExternalAddress(var res: String; provider: PString=NIL; doLogFunc: TAdd2LogEvent = NIL): Boolean;
34 | // an ip address where we are listening
35 | function getIP(): String;
36 |
37 | {$IFDEF USE_IPv6}
38 | const
39 | sfIPv4 = TSocketFamily.sfIPv4;
40 | sfIPv6 = TSocketFamily.sfIPv6;
41 | sfAny = TSocketFamily.sfAny;
42 | {$ENDIF USE_IPv6}
43 |
44 | type
45 | TBoolFunc = function(): Boolean;
46 |
47 | {$IFDEF USE_IPv6}
48 | ThttpClient = class(TSslHttpCli)
49 | {$ELSE not USE_IPv6}
50 | ThttpClient = class(THttpCli)
51 | {$ENDIF USE_IPv6}
52 | private
53 | fCanHTTPS: TBoolFunc;
54 | fAgent: String;
55 | fOnProgress: TProgressFunc;
56 | constructor Create(AOwner: TComponent); override;
57 | procedure onHttpGetUpdate(sender: TObject; buffer: Pointer; len: Integer);
58 | public
59 | destructor Destroy; OverRide;
60 | class function createURL(const url: String; canHTTPS: TBoolFunc): ThttpClient;
61 | end;
62 | var
63 | autoDownloadLibs: TBoolFunc;
64 |
65 | implementation
66 |
67 | uses
68 | sysutils, StrUtils,
69 | RDUtils, RDFileUtil, RnQCrypt,
70 | OverbyteIcsWSocket,
71 | {$IFNDEF FPC}
72 | {$IFDEF USE_SSL}
73 | OverbyteIcsSslBase,
74 | OverbyteIcsSSLEAY,
75 | {$ENDIF USE_SSL}
76 | {$ENDIF ~FPC}
77 | srvConst, srvUtils, srvVars,
78 | HSUtils;
79 |
80 | resourcestring
81 | unsignesErr = 'Signature is not valid';
82 |
83 | function httpGetStr(const url: String; from:int64=0; size:int64=-1): String;
84 | var
85 | reply: Tstringstream;
86 | begin
87 | if size = 0 then
88 | exit('');
89 | reply := TStringStream.Create('');
90 | with ThttpClient.createURL(url, autoDownloadLibs) do
91 | try
92 | rcvdStream := reply;
93 | if (from <> 0) or (size > 0) then
94 | contentRangeBegin := intToStr(from);
95 | if size > 0 then
96 | contentRangeEnd := intToStr(from+size-1);
97 | get();
98 | result := reply.dataString;
99 | if sameText('utf-8', reGet(ContentType, '; *charset=(.+) *($|;)')) then
100 | Result:=UTF8ToString(result);
101 | finally
102 | reply.free;
103 | Free;
104 | end
105 | end; // httpGetStr
106 |
107 | function httpGet(const url: string; from: int64=0; size: int64=-1): RawByteString;
108 | var
109 | fs: TMemoryStream;
110 | httpCli: ThttpClient;
111 | begin
112 | if size = 0 then
113 | begin
114 | result:='';
115 | exit;
116 | end;
117 |
118 | // Result := LoadFromURLStr(url, from, size);
119 | fs := nil;
120 | Result := '';
121 | httpCli := ThttpClient.createURL(url, autoDownloadLibs);
122 | if Assigned(httpCli) then
123 | with httpCli do
124 | try
125 | fs := TMemoryStream.Create;
126 | rcvdStream := fs;
127 | if (from <> 0) or (size > 0) then
128 | contentRangeBegin := intToStr(from);
129 | if size > 0 then
130 | contentRangeEnd := intToStr(from+size-1);
131 |
132 | if size >= 0 then
133 | begin
134 | httpCli.Head;
135 | if httpCli.ContentLength < from then
136 | Exit;
137 | end;
138 |
139 | get();
140 | if fs.Size > 0 then
141 | begin
142 | SetLength(Result, fs.Size);
143 | fs.Seek(0, soFromBeginning);
144 | fs.Read(Result[1], Length(Result));
145 | end;
146 | finally
147 | fs.free;
148 | Free;
149 | end
150 |
151 |
152 | end; // httpGet
153 |
154 | function httpFileSize(const url: string): int64;
155 | var
156 | httpCli: ThttpClient;
157 | begin
158 | Result := -1;
159 | httpCli := ThttpClient.createURL(url, autoDownloadLibs);
160 | if Assigned(httpCli) then
161 | with httpCli do
162 | try
163 | try
164 | head();
165 | result := contentLength
166 | except result:=-1
167 | end;
168 | finally free
169 | end;
170 | end; // httpFileSize
171 |
172 | function httpGetFile1(const url, filename: string; var errMsg: String; notify: TdocDataEvent=NIL): Boolean;
173 | var
174 | httpCli: ThttpClient;
175 | supposed: int64;
176 | reply: Tfilestream;
177 | begin
178 | supposed := 0;
179 | httpCli := ThttpClient.createURL(url, autoDownloadLibs);
180 | if Assigned(httpCli) then
181 | with httpCli do
182 | try
183 | reply := NIL;
184 | reply := TfileStream.Create(filename, fmCreate);
185 | rcvdStream := reply;
186 | onDocData := notify;
187 | result := TRUE;
188 | try
189 | get()
190 | except
191 | result := FALSE;
192 | errMsg := ReasonPhrase;
193 | end;
194 | supposed := ContentLength;
195 | finally
196 | if Assigned(reply) then
197 | reply.free;
198 | free;
199 | end;
200 | result := result and (sizeOfFile(filename)=supposed);
201 | if not result then
202 | deleteFile(filename);
203 | end; // httpGetFile
204 |
205 | function httpGetFile(const url, filename: string; var errMsg: String; notify: TProgressFunc=NIL): Boolean;
206 | var
207 | httpCli: ThttpClient;
208 | supposed: int64;
209 | reply: Tfilestream;
210 | begin
211 | supposed := 0;
212 | httpCli := ThttpClient.createURL(url, autoDownloadLibs);
213 | if Assigned(httpCli) then
214 | with httpCli do
215 | try
216 | reply := NIL;
217 | reply := TfileStream.Create(filename, fmCreate);
218 | rcvdStream := reply;
219 | fOnProgress := notify;
220 | onDocData := httpCli.onHttpGetUpdate;
221 | result := TRUE;
222 | try
223 | get()
224 | except
225 | result := FALSE;
226 | errMsg := ReasonPhrase;
227 | end;
228 | supposed := ContentLength;
229 | finally
230 | if Assigned(reply) then
231 | reply.free;
232 | free;
233 | end;
234 | result := result and (sizeOfFile(filename)=supposed);
235 | if not result then
236 | deleteFile(filename);
237 | end; // httpGetFile
238 |
239 | function httpGetRaw(const url: string; maxSize: Int64; var ResultRaw: RawByteString; var errMsg: String; notify: TdocDataEvent=NIL): Boolean;
240 | var
241 | httpCli: ThttpClient;
242 | supposed: int64;
243 | reply: TMemoryStream;
244 | begin
245 | supposed := 0;
246 | ResultRaw := '';
247 | httpCli := ThttpClient.createURL(url, autoDownloadLibs);
248 | if Assigned(httpCli) then
249 | with httpCli do
250 | try
251 | reply := TMemoryStream.Create;
252 | rcvdStream := reply;
253 | onDocData := notify;
254 | result := TRUE;
255 | try
256 | get()
257 | except
258 | result := FALSE;
259 | errMsg := ReasonPhrase;
260 | end;
261 | supposed := ContentLength;
262 | if result then
263 | begin
264 | SetLength(ResultRaw, reply.Size);
265 | if reply.Size > 0 then
266 | CopyMemory(@ResultRaw[1], reply.Memory, reply.Size);
267 | end;
268 | finally
269 | if Assigned(reply) then
270 | reply.free;
271 | free;
272 | end;
273 | result := result and (Length(ResultRaw)=supposed);
274 | if not result then
275 | ResultRaw := '';
276 | end; // httpGetRaw
277 |
278 |
279 | function httpGetFileWithCheck(const url, filename: string; var errMsg: String; notify: TProgressFunc=NIL): Boolean;
280 | const
281 | sigFileExt = '.sig';
282 | // tmpSubFolder = 'tmp.download';
283 | var
284 | // tmpFolder: String;
285 | tmpFile: String;
286 | resultFile: String;
287 | pubKey: RawByteString;
288 | sign64: RawByteString;
289 | begin
290 | // tmpFolder := ExtractFileDir(filename) + tmpSubFolder + PathDelim;
291 | resultFile := ExtractFileName(filename);
292 | tmpFile := filename + '.downloading';
293 |
294 | // if not DirectoryExists(tmpFolder, false) then
295 | // CreateDirRecursive(tmpFolder);
296 |
297 | Result := httpGetFile(url, tmpFile, errMsg, notify);
298 | if Result then
299 | begin
300 | Result := httpGetRaw(url + sigFileExt, 5555, sign64, errMsg);
301 | end;
302 | if Result then
303 | begin
304 | pubKey := getRes('RDpubkey');
305 | Result := verifyEccSignFile(tmpFile, sign64, pubKey);
306 | if not Result then
307 | errMsg := unsignesErr;
308 | end;
309 | if not result then
310 | begin
311 | if FileExists(tmpFile, false) then
312 | begin
313 | deleteFile(tmpFile);
314 | if FileExists(tmpFile + sigFileExt, false) then
315 | deleteFile(tmpFile + sigFileExt);
316 | end;
317 | end
318 | else
319 | begin
320 | MoveFile(PChar(tmpFile), PChar(filename));
321 | end;
322 | end; // httpGetFileWithCheck
323 |
324 | function httpGetFileWithCheck1(const url, filename: string; var errMsg: String; notify: TdocDataEvent=NIL): Boolean;
325 | const
326 | sigFileExt = '.sig';
327 | // tmpSubFolder = 'tmp.download';
328 | var
329 | // tmpFolder: String;
330 | tmpFile: String;
331 | resultFile: String;
332 | pubKey: RawByteString;
333 | sign64: RawByteString;
334 | begin
335 | // tmpFolder := ExtractFileDir(filename) + tmpSubFolder + PathDelim;
336 | resultFile := ExtractFileName(filename);
337 | tmpFile := filename + '.downloading';
338 |
339 | // if not DirectoryExists(tmpFolder, false) then
340 | // CreateDirRecursive(tmpFolder);
341 |
342 | Result := httpGetFile1(url, tmpFile, errMsg, notify);
343 | if Result then
344 | begin
345 | Result := httpGetRaw(url + sigFileExt, 5555, sign64, errMsg);
346 | end;
347 | if Result then
348 | begin
349 | pubKey := getRes('RDpubkey');
350 | Result := verifyEccSignFile(tmpFile, sign64, pubKey);
351 | if not Result then
352 | errMsg := unsignesErr;
353 | end;
354 | if not result then
355 | begin
356 | if FileExists(tmpFile, false) then
357 | begin
358 | deleteFile(tmpFile);
359 | if FileExists(tmpFile + sigFileExt, false) then
360 | deleteFile(tmpFile + sigFileExt);
361 | end;
362 | end
363 | else
364 | begin
365 | MoveFile(PChar(tmpFile), PChar(filename));
366 | end;
367 | end; // httpGetFileWithCheck
368 |
369 | function getIPs(): TStringDynArray;
370 | {$IFDEF USE_IPv6}
371 | var
372 | a6: TStringDynArray;
373 | I: Integer;
374 | {$ENDIF USE_IPv6}
375 | begin
376 | try
377 | {$IFDEF USE_IPv6}
378 | result := listToArray(localIPlist(sfIPv4));
379 | a6 := listToArray(localIPlist(sfIPv6));
380 | if Length(a6) > 0 then
381 | begin
382 | for I := Low(a6) to High(a6) do
383 | a6[i] := '[' + a6[i] + ']';
384 | Result := Result + a6;
385 | end;
386 | {$ELSE USE_IPv6}
387 | result := listToArray(localIPlist);
388 | {$ENDIF USE_IPv6}
389 | except
390 | result := NIL
391 | end;
392 | end;
393 |
394 | function getLocalIPs({$IFDEF USE_IPv6}const ASocketFamily: TSocketFamily = sfIPv4 {$ENDIF USE_IPv6}): TStringDynArray;
395 | begin
396 | result := listToArray(localIPlist({$IFDEF USE_IPv6}ASocketFamily{$ENDIF USE_IPv6}));
397 | end;
398 |
399 | function getIP(): String;
400 | var
401 | i: integer;
402 | ips: Tstrings;
403 | begin
404 | ips := LocalIPlist();
405 | case ips.count of
406 | 0: result := '';
407 | 1: result := ips[0];
408 | else
409 | i:=0;
410 | while (i < ips.count-1) and isLocalIP(ips[i]) do
411 | inc(i);
412 | result := ips[i];
413 | end;
414 | end; // getIP
415 |
416 |
417 | function findRedirection(var h, p: String; const agent: String): Boolean;
418 | var
419 | http: THttpCli;
420 | begin
421 | result := FALSE;
422 | http := Thttpcli.create(NIL);
423 | try
424 | http.url := h;
425 | http.agent := agent; //HFS_HTTP_AGENT;
426 | try
427 | http.get()
428 | except // a redirection will result in an exception
429 | if (http.statusCode < 300) or (http.statusCode >= 400) then
430 | exit;
431 | result := TRUE;
432 | h := http.hostname;
433 | p := http.ctrlSocket.Port;
434 | end;
435 | finally
436 | http.free
437 | end
438 | end;
439 |
440 | function checkHTTPSCanWork(var missing: TStringDynArray): Boolean;
441 | {$IFDEF USE_SSL}
442 | var
443 | files: array of string; // = ['libcrypto-1_1.dll','libssl-1_1.dll'];
444 | // missing: TStringDynArray;
445 | {$ENDIF ~USE_SSL}
446 | begin
447 | {$IFDEF USE_SSL}
448 | missing := NIL;
449 | // m := NIL;
450 | SetLength(files, 2);
451 | files[0] := GLIBEAY_300DLL_Name;
452 | files[1] := GSSLEAY_300DLL_Name;
453 | for var s in files do
454 | if not FileExists(s) and not dllIsPresent(s) then
455 | addString(s, missing);
456 | if missing=NIL then
457 | exit(TRUE);
458 | {$ENDIF USE_SSL}
459 | // m := missing;
460 | Result := False;
461 | end;
462 |
463 | function checkHTTPSCanWork(): Boolean; OverLoad;
464 | var
465 | m: TStringDynArray;
466 | begin
467 | Result := checkHTTPSCanWork(m);
468 | end;
469 |
470 |
471 | class function ThttpClient.createURL(const url: String; canHTTPS: TBoolFunc): ThttpClient;
472 | begin
473 | if startsText('https://', url)
474 | and not (Assigned(CanHTTPS) and canHTTPS()) then
475 | exit(NIL);
476 | result := ThttpClient.Create(NIL);
477 | result.URL := url;
478 | result.fCanHTTPS := canHTTPS;
479 | result.fAgent := HFS_HTTP_AGENT;
480 | result.Agent := HFS_HTTP_AGENT;
481 | {$IFDEF USE_SSL}
482 | if checkHTTPSCanWork() then
483 | result.SslContext := TSslContext.Create(NIL)
484 | else
485 | begin
486 | result.followRelocation := False;
487 | result.SslContext := NIL;
488 | result.CtrlSocket.SslEnable := False;
489 | end;
490 | {$ENDIF USE_SSL}
491 | end;
492 |
493 | constructor ThttpClient.create(AOwner: TComponent);
494 | begin
495 | inherited;
496 | {$IFDEF USE_SSL}
497 | followRelocation := TRUE;
498 | {$ENDIF USE_SSL}
499 | end; // create
500 |
501 | destructor ThttpClient.Destroy;
502 | begin
503 | {$IFDEF USE_SSL}
504 | if Assigned(SslContext) then
505 | SslContext.free;
506 | SslContext:=NIl;
507 | {$ENDIF USE_SSL}
508 | inherited destroy;
509 | end;
510 |
511 | procedure ThttpClient.onHttpGetUpdate(sender: TObject; buffer: Pointer; len: integer);
512 | var
513 | prg: Real;
514 | begin
515 | if Assigned(fOnProgress) then
516 | with sender as ThttpCli do
517 | begin
518 | prg := safeDiv(0.0+RcvdCount, contentLength);
519 | if not fOnProgress(prg) then
520 | abort();
521 | end;
522 | end; // onHttpGetUpdate
523 |
524 | //function getExternalAddress(var res: String; provider: PString=NIL; doLog: Boolean = false): Boolean;
525 | function getExternalAddress(var res: String; provider: PString=NIL; doLogFunc: TAdd2LogEvent = NIL): Boolean;
526 |
527 | procedure loadIPservices(src: String='');
528 | var
529 | l:string;
530 | sA: RawByteString;
531 | begin
532 | if src = '' then
533 | begin
534 | if now()-IPservicesTime < 1 then exit; // once a day
535 | IPservicesTime:=now();
536 | try
537 | sA := trim(httpGet(IP_SERVICES_URL));
538 | except
539 | exit
540 | end;
541 | src := (UnUTF(sA));
542 | end;
543 | IPservices := NIL;
544 | while src > '' do
545 | begin
546 | l := chopLine(src);
547 | if ansiStartsText('http://', l) then
548 | addString(l, IPservices);
549 | end;
550 | end; // loadIPservices
551 |
552 | const {$J+}
553 | lastProvider: string = ''; // this acts as a static variable
554 | var
555 | s, mark, addr: string;
556 | sA: RawByteString;
557 | i: integer;
558 | begin
559 | result := FALSE;
560 | if customIPservice > '' then
561 | s := customIPservice
562 | else
563 | begin
564 | loadIPservices();
565 | if IPservices = NIL then
566 | loadIPservices(UnUTF(getRes('IPservices')));
567 | if IPservices = NIL then
568 | exit;
569 |
570 | repeat
571 | s := IPservices[random(length(IPservices))];
572 | until s <> lastProvider;
573 | lastProvider:=s;
574 | end;
575 | addr := chop('|', s);
576 | if assigned(provider) then
577 | provider^ := addr;
578 | mark := s;
579 | try
580 | sA := httpGet(addr);
581 | s := UnUTF(sA);
582 | except
583 | exit
584 | end;
585 | if mark > '' then
586 | chop(mark, s);
587 | s := trim(s);
588 | if s = '' then
589 | exit;
590 | // try to determine length
591 | i := 1;
592 | while (i < length(s)) and (i < 15) and (s[i+1] in ['0'..'9','.']) do
593 | inc(i);
594 | while (i > 0) and (s[i] = '.') do
595 | dec(i);
596 | setLength(s,i);
597 | result := checkAddressSyntax(s, false) and not isLocalIP(s);
598 | if not result then
599 | exit;
600 | if (res <> s) and Assigned(doLogFunc) then //mainFrm.logOtherEventsChk.checked then
601 | doLogFunc('New external address: '+s+' via '+hostFromURL(addr));
602 | res := s;
603 | end; // getExternalAddress
604 |
605 |
606 | initialization
607 | autoDownloadLibs := NIL;
608 | end.
609 |
--------------------------------------------------------------------------------
/srv/parserLib.pas:
--------------------------------------------------------------------------------
1 | unit parserLib;
2 | {$INCLUDE defs.inc }
3 | {$I NoRTTI.inc}
4 |
5 | interface
6 |
7 | uses
8 | strutils, sysutils, classes, types, windows,
9 | srvClassesLib,
10 | serverLib;
11 |
12 | type
13 |
14 |
15 | // TPars = TStringList;
16 | TPars = TPars2;
17 |
18 | EtplError = class(Exception)
19 | pos, row, col: integer;
20 | code: string;
21 | constructor Create(const msg, code: String; row, col: Integer);
22 | end;
23 |
24 |
25 | const
26 | MARKER_OPEN = UnicodeString('{.');
27 | MARKER_CLOSE = UnicodeString('.}');
28 | MARKER_SEP = UnicodeString('|');
29 | MARKER_QUOTE = UnicodeString('{:');
30 | MARKER_UNQUOTE = UnicodeString(':}');
31 | MARKERS: array [0..4] of UnicodeString = ( MARKER_OPEN, MARKER_CLOSE, MARKER_SEP, MARKER_QUOTE, MARKER_UNQUOTE );
32 | ID2TAG_1Chars = [WideChar('{'), '.', ':'];
33 |
34 | AMARKER_OPEN = RawByteString('{.');
35 | AMARKER_CLOSE = RawByteString('.}');
36 | AMARKER_SEP = RawByteString('|');
37 | AMARKER_QUOTE = RawByteString('{:');
38 | AMARKER_UNQUOTE = RawByteString(':}');
39 | AMARKERS: array [0..4] of RawByteString = ( MARKER_OPEN, MARKER_CLOSE, MARKER_SEP, MARKER_QUOTE, MARKER_UNQUOTE );
40 |
41 | function isAnyMacroIn(const s: RawByteString): Boolean; inline;
42 | function anyMacroMarkerIn(const s: String): Boolean;
43 | function findMacroMarker(const s: string; ofs:integer=1): integer;
44 | procedure applyMacrosAndSymbols(fs: TFileServer; var txt: UnicodeString; cb: TmacroCB; cbData: PMacroData; removeQuotings: Boolean=TRUE);
45 |
46 | function macroQuote(s: UnicodeString): UnicodeString;
47 | function macroDequote(s: UnicodeString): UnicodeString; OverLoad;
48 | {$IFNDEF UNICODE}
49 | function macroDequote(s: String): String; OverLoad;
50 | {$ENDIF UNICODE}
51 | function validUsername(const s: String; acceptEmpty: Boolean=FALSE): Boolean;
52 |
53 | implementation
54 | uses
55 | srvUtils, HSUtils;
56 |
57 | const
58 | MAX_RECUR_LEVEL = 50;
59 | type
60 | TparserIdsStack = array [1..MAX_RECUR_LEVEL] of UnicodeString;
61 |
62 | constructor EtplError.create(const msg, code: String; row, col: Integer);
63 | begin
64 | inherited create(msg);
65 | self.row := row;
66 | self.col := col;
67 | self.code := code;
68 | end;
69 |
70 | procedure applyMacrosAndSymbols2(fs: TFileServer; var pTxt: UnicodeString; cb: TmacroCB; cbData: Pointer; var idsStack: TparserIdsStack; recurLevel: integer=0);
71 | const
72 | // we don't track SEPs, they are handled just before the callback
73 | QUOTE_ID = 0; // QUOTE must come before OPEN because it is a substring
74 | UNQUOTE_ID = 1;
75 | OPEN_ID = 2;
76 | CLOSE_ID = 3;
77 | MAX_MARKER_ID = 3;
78 | {$IFDEF FPC}
79 | function alreadyRecurredOn(const s: UnicodeString): Boolean; OverLoad;
80 | var
81 | i: integer;
82 | begin
83 | //result := TRUE;
84 | if recurLevel > 1 then
85 | for i:=recurLevel downto 1 do
86 | if UnicodeSameText(s, idsStack[i]) then
87 | exit(True);
88 | result:=FALSE;
89 | end; // alreadyRecurredOn
90 | {$ENDIF FPC}
91 |
92 | function alreadyRecurredOn(const s: String): Boolean; OverLoad;
93 | var
94 | i: integer;
95 | begin
96 | //result := TRUE;
97 | if recurLevel > 1 then
98 | for i:=recurLevel downto 1 do
99 | if sameText(s, idsStack[i]) then
100 | exit(True);
101 | result:=FALSE;
102 | end; // alreadyRecurredOn
103 |
104 | procedure handleSymbols();
105 | var
106 | b, e, l : integer;
107 | s, newS: UnicodeString;
108 | begin
109 | e := 0;
110 | l := length(pTxt);
111 | while e < l do
112 | begin
113 | // search for next symbol
114 | b := posEx(UnicodeString('%'), pTxt, e+1);
115 | if b = 0 then
116 | break;
117 | e := b+1;
118 | if pTxt[e] = '%' then
119 | begin // we don't accept %% as a symbol. so, restart parsing from the second %
120 | e := b;
121 | continue;
122 | end;
123 | if not (pTxt[e] in ['_','a'..'z','A'..'Z']) then
124 | continue; // first valid character
125 | while (e < l) and (pTxt[e] in ['0'..'9','a'..'z','A'..'Z','-','_']) do
126 | inc(e);
127 | if pTxt[e] <> '%' then
128 | continue;
129 | // found!
130 | s := substr(pTxt, b, e);
131 | if alreadyRecurredOn(s) then
132 | continue; // the user probably didn't meant to create an infinite loop
133 |
134 | newS := cb(fs, s, NIL, cbData);
135 | if s = newS then
136 | continue;
137 |
138 | idsStack[recurLevel] := s; // keep track of what we recur on
139 | // apply translation, and eventually recur
140 | try
141 | applyMacrosAndSymbols2(fs, newS, cb, cbData, idsStack, recurLevel);
142 | except
143 | end;
144 | idsStack[recurLevel] := '';
145 | inc(e, replace(pTxt, newS, b, e));
146 | l := length(pTxt);
147 | end;
148 | end; // handleSymbols
149 |
150 | procedure handleMacros();
151 | var
152 | pars: TPars;
153 |
154 | function expand(from, to_: Integer): Integer;
155 | var
156 | s, eFullMacro: UnicodeString;
157 | i, o, q, u: integer;
158 | begin
159 | result:=0;
160 | eFullMacro := substr(pTxt, from+length(MARKER_OPEN), to_-length(MARKER_CLOSE));
161 | if alreadyRecurredOn(eFullMacro) then
162 | exit; // the user probably didn't meant to create an infinite loop
163 |
164 | // let's find the SEPs to build 'pars'
165 | pars.clear();
166 | i := 1; // char pointer from where we shall copy the macro parameter
167 | o := 0;
168 | q := posEx(MARKER_QUOTE, eFullMacro); // q points to _QUOTE
169 | repeat
170 | o := posEx(MARKER_SEP, eFullMacro, o+1);
171 | if o = 0 then
172 | break;
173 | if (q > 0) and (q < o) then // this SEP is possibly quoted
174 | begin
175 | // update 'q' and 'u'
176 | repeat
177 | u := posEx(MARKER_UNQUOTE, eFullMacro, q);
178 | if u = 0 then
179 | exit; // macro quoting not properly closed
180 | q:=posEx(MARKER_QUOTE, eFullMacro, q+1); // update q for next cycle
181 | // if we find other _QUOTEs before _UNQUOTE, then they are stacked, and we must go through the same number of both markers
182 | while (q > 0) and (q < u) do
183 | begin
184 | u := posEx(MARKER_UNQUOTE, eFullMacro, u+1);
185 | if u = 0 then
186 | exit; // macro quoting not properly closed
187 | q := posEx(MARKER_QUOTE, eFullMacro, q+1);
188 | end;
189 | until (q = 0) or (o < q);
190 | // eventually skip this chunk of string
191 | if o < u then
192 | begin // yes, this SEP is quoted
193 | o:=u;
194 | continue;
195 | end;
196 | end;
197 | // ok, that's a valid SEP, so we collect this as a parameter
198 | pars.add(substr(eFullMacro, i, o-1));
199 | i:=o+length(MARKER_SEP);
200 | until false;
201 | pars.add(substr(eFullMacro, i, length(eFullMacro))); // last piece
202 | // ok, 'pars' has now been built
203 |
204 | // do the call, recur, and replace with the result
205 | s := cb(fs, eFullMacro, pars, cbData);
206 | idsStack[recurLevel] := eFullMacro; // keep track of what we recur on
207 | if s > '' then
208 | try
209 | try
210 | applyMacrosAndSymbols2(fs, s, cb, cbData, idsStack, recurLevel)
211 | except
212 | end;
213 | finally
214 | idsStack[recurLevel]:=''
215 | end;
216 | result := replace(pTxt, s, from, to_);
217 | end; // expand
218 |
219 | const
220 | ID2TAG: array [0..MAX_MARKER_ID] of string = (MARKER_QUOTE, MARKER_UNQUOTE, MARKER_OPEN, MARKER_CLOSE);
221 | ID2TAGU: array [0..MAX_MARKER_ID] of UnicodeString = (MARKER_QUOTE, MARKER_UNQUOTE, MARKER_OPEN, MARKER_CLOSE);
222 | type
223 | TstackItem = record
224 | pos: integer;
225 | row, col: word;
226 | quote: boolean;
227 | end;
228 | var
229 | i, lastNL, row, m, t: integer;
230 | stack: array of TstackItem;
231 | Nstack: integer;
232 | {$IFDEF FPC}
233 | ch: UnicodeChar;
234 | {$ELSE}
235 | ch: Char;
236 | {$ENDIF FPC}
237 | begin
238 | if pTxt > '' then
239 | begin
240 | setLength(stack, length(pTxt) div length(MARKER_OPEN)); // it will never need more than this
241 | Nstack:=0;
242 | pars := TPars.Create;
243 | try
244 | i:=1;
245 | row:=1;
246 | lastNL:=0;
247 | while i <= length(pTxt) do
248 | begin
249 | ch := pTxt[i];
250 | if ch = #10 then
251 | begin
252 | inc(row);
253 | lastNL:=i;
254 | end;
255 | if not (ch in ID2TAG_1Chars) then
256 | begin
257 | Inc(i);
258 | Continue;
259 | end;
260 | for m:=0 to MAX_MARKER_ID do
261 | begin
262 | if not strAt(pTxt, ID2TAGU[m], i) then
263 | continue;
264 | case m of
265 | QUOTE_ID,
266 | OPEN_ID:
267 | begin
268 | if (m = OPEN_ID) and (Nstack > 0) and stack[Nstack-1].quote then
269 | continue; // don't consider quoted OPEN markers
270 | stack[Nstack].pos := i;
271 | stack[Nstack].quote := m=QUOTE_ID;
272 | stack[Nstack].row := row;
273 | stack[Nstack].col := i-lastNL;
274 | inc(Nstack);
275 | end;
276 | CLOSE_ID:
277 | begin
278 | if Nstack = 0 then
279 | raise EtplError.create('unmatched marker', copy(pTxt,i,30), row, i-lastNL);
280 | if (Nstack > 0) and stack[Nstack-1].quote then
281 | continue; // don't consider quoted CLOSE markers
282 | t := length(MARKER_CLOSE);
283 | inc(i, t-1+expand(stack[Nstack-1].pos, i+t-1));
284 | dec(Nstack);
285 | end;
286 | UNQUOTE_ID:
287 | begin
288 | if (Nstack = 0) or not stack[Nstack-1].quote then
289 | continue;
290 | dec(Nstack);
291 | end;
292 | end;
293 | end;//for
294 | inc(i);
295 | end;
296 | finally
297 | pars.free
298 | end;
299 | if Nstack > 0 then
300 | with stack[Nstack-1] do
301 | raise EtplError.create('unmatched marker', copy(pTxt,pos,30), row, col)
302 | end;
303 | end; // handleMacros
304 |
305 | begin
306 | if recurLevel > MAX_RECUR_LEVEL then
307 | exit;
308 | inc(recurLevel);
309 | handleSymbols();
310 | handleMacros();
311 | end; //applyMacrosAndSymbols2
312 |
313 | procedure applyMacrosAndSymbols(fs: TFileServer; var txt: UnicodeString; cb: TmacroCB; cbData: PMacroData; removeQuotings: Boolean=TRUE);
314 | var
315 | idsStack: TparserIdsStack;
316 | begin
317 | enforceNUL(txt);
318 | applyMacrosAndSymbols2(fs, txt, cb, cbData, idsStack);
319 | if removeQuotings then
320 | txt := xtpl(txt, [MARKER_QUOTE, '', MARKER_UNQUOTE, ''])
321 | end;
322 |
323 | function findMacroMarker(const s: String; ofs: Integer=1): Integer;
324 | begin result:=reMatch(s, '\{[.:]|[.:]\}|\|', 'm!', ofs) end;
325 |
326 | function isAnyMacroIn(const s: RawByteString): Boolean; inline;
327 | begin
328 | result := pos(AMARKER_OPEN, s) > 0
329 | end;
330 |
331 | function anyMacroMarkerIn(const s: String): Boolean;
332 | begin result:=findMacroMarker(s) > 0 end;
333 | {$IFDEF FPC}
334 | function isMacroQuoted(const s: UnicodeString): Boolean; OverLoad;
335 | begin result := AnsiStartsStr(MARKER_QUOTE, s) and ansiEndsStr(MARKER_UNQUOTE, s) end; //?????
336 | {$ENDIF FPC}
337 |
338 | function isMacroQuoted(const s: String): Boolean; OverLoad;
339 | begin result:=ansiStartsStr(MARKER_QUOTE, s) and ansiEndsStr(MARKER_UNQUOTE, s) end;
340 |
341 | function macroQuote(s: UnicodeString): UnicodeString;
342 | var
343 | t: UnicodeString;
344 | begin
345 | enforceNUL(s);
346 | if not anyMacroMarkerIn(s) then
347 | begin
348 | result := s;
349 | exit;
350 | end;
351 | // an UNQUOTE would invalidate our quoting, so let's encode any of it
352 | t := MARKER_UNQUOTE;
353 | replace(t, ''+intToStr(charToUnicode(t[1]))+';', 1,1);
354 | result := MARKER_QUOTE+xtpl(s, [MARKER_UNQUOTE, t])+MARKER_UNQUOTE
355 | end; // macroQuote
356 |
357 | function macroDequote(s: UnicodeString): UnicodeString;
358 | begin
359 | result := s;
360 | s := trim(s);
361 | if isMacroQuoted(s) then
362 | result := copy(s, length(MARKER_QUOTE)+1, length(s)-length(MARKER_QUOTE)-length(MARKER_UNQUOTE) );
363 | end; // macroDequote
364 |
365 | {$IFNDEF UNICODE}
366 | function macroDequote(s: String): String;
367 | begin
368 | result:=s;
369 | s:=trim(s);
370 | if isMacroQuoted(s) then
371 | result:=copy(s, length(MARKER_QUOTE)+1, length(s)-length(MARKER_QUOTE)-length(MARKER_UNQUOTE) );
372 | end; // macroDequote
373 | {$ENDIF UNICODE}
374 |
375 | function validUsername(const s: String; acceptEmpty: Boolean=FALSE): Boolean;
376 | begin
377 | result := (s = '') and acceptEmpty
378 | or (s > '') and not anyCharIn('/\:?*"<>|;&',s) and (length(s) <= 40)
379 | and not anyMacroMarkerIn(s) // mod by mars
380 | end;
381 |
382 |
383 | end.
384 |
--------------------------------------------------------------------------------
/srv/srvConst.pas:
--------------------------------------------------------------------------------
1 | unit srvConst;
2 | {$I NoRTTI.inc}
3 |
4 | interface
5 | uses
6 | Graphics,
7 | Types, SysUtils;
8 |
9 | const
10 | VERSION = '2.5.0 Alpha4 by RD' {$IFDEF CPUX64 } +' x64' {$ENDIF} {$IFDEF FPC } +' FPC' {$ENDIF};
11 | VERSION_BUILD = '325';
12 | VERSION_STABLE = {$IFDEF STABLE } TRUE {$ELSE} FALSE {$ENDIF};
13 | HFS_HTTP_AGENT = 'HFS/'+VERSION;
14 | CURRENT_VFS_FORMAT: integer = 1;
15 | CRLF = #13#10;
16 | CRLFA = RawByteString(#13#10);
17 | TAB = #9;
18 | G_VAR_PREFIX = '#';
19 | HOURS = 24;
20 | MINUTES = HOURS*60;
21 | SECONDS = MINUTES*60; // Tdatetime * SECONDS = time in seconds
22 | KILO = 1024;
23 | MEGA = KILO*KILO;
24 | CORRUPTED_EXT = '.corrupted';
25 | COMMENT_FILE_EXT = '.comment';
26 | COMMENTS_FILE = 'hfs.comments.txt';
27 | DESCRIPT_ION = 'descript.ion';
28 | DIFF_TPL_FILE = 'hfs.diff.tpl';
29 | FILELIST_TPL_FILE = 'hfs.filelist.tpl';
30 | MACROS_LOG_FILE = 'macros-log.html';
31 | PROTECTED_FILES_MASK = 'hfs.*;*.htm*;descript.ion;*.comment;*.md5;*.corrupted;*.lnk';
32 | SESSION_COOKIE = 'HFS_SID_';
33 | VFS_FILE_IDENTIFIER = 'HFS.VFS';
34 | STARTING_SNDBUF = 32000;
35 | COMPRESSION_THRESHOLD = 10*KILO; // if more than X bytes, VFS files are compressed
36 | BYTES_GROUPING_THRESHOLD: TDateTime = 1/SECONDS; // group bytes in log
37 | DOWNLOAD_MIN_REFRESH_TIME: TDateTime = 1/(5*SECONDS); // 5 Hz
38 | sendGraphWidth = 512;
39 | sendGraphHeight = 32;
40 |
41 | IP_SERVICES_URL = 'http://hfsservice.rejetto.com/ipservices.php';
42 | SELF_TEST_URL = 'http://hfstest.rejetto.com/';
43 |
44 | ETA_FRAME = 5; // time frame for ETA (in seconds)
45 |
46 | USER_ANONYMOUS = '@anonymous';
47 | USER_ANYONE = '@anyone';
48 | USER_ANY_ACCOUNT = '@any account';
49 |
50 | DEFAULT_MIME = 'application/octet-stream';
51 | DEFAULT_MIME_TYPES: array [0..29] of string = (
52 | '*.htm;*.html', 'text/html',
53 | '*.jpg;*.jpeg;*.jpe', 'image/jpeg',
54 | '*.gif', 'image/gif',
55 | '*.png', 'image/png',
56 | '*.bmp', 'image/bmp',
57 | '*.ico', 'image/x-icon',
58 | '*.mpeg;*.mpg;*.mpe', 'video/mpeg',
59 | '*.avi', 'video/x-msvideo',
60 | '*.txt', 'text/plain',
61 | '*.css', 'text/css',
62 | '*.js', 'text/javascript',
63 | '*.mkv', 'video/x-matroska',
64 | '*.webp', 'image/webp',
65 | '*.heic', 'image/heic',
66 | '*.heif', 'image/heif'
67 | );
68 | thumbsShowToExtDefaultStr = '.jpg; .jpeg; .png; .gif; .webp; .bmp; .ico';
69 |
70 | DOW2STR: array [1..7] of string=( 'Sun','Mon','Tue','Wed','Thu','Fri','Sat' );
71 | MONTH2STR: array [1..12] of string = ( 'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec' );
72 |
73 | //const
74 | // libsBaseUrl = 'http://rejetto.com/hfs/';
75 |
76 | type
77 | TcharSetA = TSysCharSet; //set of char;
78 | TcharSetW = set of Char deprecated 'Holds Char values in the ordinal range of 0..255 only.'; //set of char;
79 | PstringDynArray = ^TstringDynArray;
80 | {$IFDEF FPC}
81 | TUnicodeStringDynArray = array of UnicodeString;
82 | TProc = procedure();
83 | TProc = procedure(param: TParam);
84 | TProcO = procedure(param: TParam) of Object;
85 | {$ELSE FPC}
86 | TUnicodeStringDynArray = TStringDynArray;
87 | TUnicodeSearchRec = TSearchRec;
88 | TProcedureOfObject = procedure() of Object;
89 | {$ENDIF FPC}
90 |
91 | Paccount = ^Taccount;
92 | Taccount = record // user/pass profile
93 | user, pwd, redir, notes: string;
94 | wasUser: string; // used in user renaming panel
95 | enabled, noLimits, group: boolean;
96 | link: TStringDynArray;
97 | end;
98 | Taccounts = array of Taccount;
99 |
100 | TdownloadingWhat = ( DW_UNK, DW_FILE, DW_FOLDERPAGE, DW_ICON, DW_ERROR, DW_ARCHIVE );
101 |
102 | TpreReply = (PR_NONE, PR_BAN, PR_OVERLOAD);
103 |
104 | type
105 | TaccountRecursionStopCase = (ARSC_REDIR, ARSC_NOLIMITS, ARSC_IN_SET);
106 |
107 | const
108 | ILLEGAL_FILE_CHARS = [#0..#31,'/','\',':','?','*','"','<','>','|'];
109 | ENCODED_TABLE_HEADER = 'this is an encoded table'+CRLF;
110 |
111 | const // Messages
112 | MSG_SPEED_KBS = '%.1f kB/s';
113 |
114 | resourcestring
115 | MSG_MAX_CON = 'Max connections';
116 | MSG_MAX_CON_SING = 'Max connections from single address';
117 | MSG_MAX_SIM_ADDR = 'Max simultaneous addresses';
118 | MSG_MAX_SIM_ADDR_DL = 'Max simultaneous addresses downloading';
119 | MSG_MAX_SIM_DL_SING = 'Max simultaneous downloads from single address';
120 | MSG_MAX_SIM_DL = 'Max simultaneous downloads';
121 |
122 | implementation
123 |
124 | end.
125 |
--------------------------------------------------------------------------------
/srv/srvVars.pas:
--------------------------------------------------------------------------------
1 | unit srvVars;
2 | {$I NoRTTI.inc}
3 |
4 | interface
5 | uses
6 | Classes, Types, iniFiles, regexpr,
7 | hsLib, srvClassesLib, srvConst;
8 |
9 | // global variables
10 | var
11 | globalLimiter: TspeedLimiter;
12 | ip2obj: THashedStringList;
13 | sessions: Tsessions;
14 | etags: THashedStringList;
15 | forwardedMask: string;
16 | defaultIP: string; // the IP address to use forming URLs
17 | autoupdatedFiles: TstringToIntHash; // download counter for temp Tfile.s
18 | updateASAP: string;
19 | iconsCache: TiconsCache;
20 | filesStayFlaggedForMinutes: integer;
21 | autoFingerprint: integer; // create fingerprint on file addition
22 | toAddFingerPrint: TStringList;
23 | usersInVFS: TusersInVFS; // keeps track of user/pwd in the VFS
24 | loadingVFS: record
25 | resetLetBrowse, unkFK, disableAutosave, visOnlyAnon, bakAvailable, useBackup, macrosFound: boolean;
26 | build: string;
27 | end;
28 | VFSmodified: boolean; // TRUE if the VFS changes have not been saved
29 | VFScounterMod: boolean; // if any counter has changed
30 | // listenOn: string; // interfaces HFS should listen on
31 | // port: string;
32 | lastEverySec: TDateTime;
33 | lastActivityTime: Tdatetime; // used for the "no download timeout"
34 | lastFilelistTpl: Tdatetime;
35 | upTime: Tdatetime; // the server is up since...
36 | inTotalOfs, outTotalOfs: int64; // used to cumulate in/out totals
37 | hitsLogged, downloadsLogged, uploadsLogged: integer;
38 | dontLogAddressMask: string;
39 | renamePartialUploads: string;
40 | ipsEverConnected: THashedStringList;
41 | toDelete: Tlist; // connections pending for deletion
42 | customIPservice: string;
43 | mimeTypes, address2name, IPservices: TUnicodeStringDynArray;
44 | thumbsShowToExt: TStringDynArray;
45 | thumbsShowToExtStr: String;
46 | IPservicesTime: TdateTime;
47 | uploadPaths: TstringDynArray;
48 | minDiskSpace: int64; // in MB. an int32 would suffice, but an int64 will save us
49 | selfTesting: boolean;
50 | banlist: array of record ip,comment: String; end;
51 | noReplyBan: boolean;
52 | allowedReferer: string; // check over the Refer header field
53 | speedLimit: real; // overall limit, Kb/s --- it virtualizes the value of globalLimiter.maxSpeed, that's actually set to zero when streaming is paused
54 | speedLimitIP: real;
55 | openInBrowser: string; // to not send the "attachment" suggestion in header
56 | inBrowserIfMIME: boolean;
57 |
58 | maxConnections: integer; // max number of connections (total)
59 | maxConnectionsIP: integer; // ...from a single address
60 | maxContempDLs: integer; // max number of contemporaneous downloads
61 | maxContempDLsIP: integer; // ...from a single address
62 | maxContempDLsUser: integer; // ...from a single user
63 | maxIPs: integer; // max number of different addresses connected
64 | maxIPsDLing: integer; // max number of different addresses downloading
65 |
66 | tplFilename: UnicodeString; // when empty, we are using the default tpl
67 | dmBrowserTpl, filelistTpl: Ttpl;
68 | noMacrosTpl: Ttpl;
69 | accounts: Taccounts;
70 |
71 | var
72 | runningOnRemovable: boolean;
73 | exePath: string;
74 | cfgPath, tmpPath: string;
75 | GMToffset: integer; // in minutes
76 | externalIP: string;
77 |
78 | var
79 | onlyDotsRE: TRegExpr;
80 | graph: record
81 | rate: integer; // update speed
82 | lastOut, lastIn: int64; // save bytesSent and bytesReceived last values
83 | maxV: int64; // max value in scale
84 | size: integer; // height of the box
85 | samplesIn, samplesOut: array [0..3000] of int64; // 1 sample, 1 pixel
86 | beforeRecalcMax: integer; // countdown
87 | end;
88 | flashOn: string; // describes when to flash the taskbar
89 | logFile: record
90 | filename: string;
91 | apacheFormat: string;
92 | apacheZoneString: string;
93 | end;
94 | setThreadExecutionState: function(d:dword):dword; stdcall; // as variable, because not available on Win95
95 |
96 | function applyThumbsExtStr(str: String): Boolean;
97 |
98 | implementation
99 | uses
100 | SysUtils, srvUtils;
101 |
102 | function applyThumbsExtStr(str: String): Boolean;
103 | var
104 | arr: TStringDynArray;
105 | begin
106 | try
107 | arr := split(';', str, False);
108 | for var I := Low(arr) to High(arr) do
109 | arr[i] := Trim(arr[i]);
110 | sortArray(arr);
111 | Result := True;
112 | except
113 | Result := False;
114 | end;
115 | if Result then
116 | begin
117 | thumbsShowToExt := arr;
118 | thumbsShowToExtStr := str;
119 | end;
120 | end;
121 |
122 |
123 |
124 | INITIALIZATION
125 |
126 | MIMEtypes := toSA([
127 | '*.htm;*.html', 'text/html',
128 | '*.jpg;*.jpeg;*.jpe', 'image/jpeg',
129 | '*.gif', 'image/gif',
130 | '*.png', 'image/png',
131 | '*.bmp', 'image/bmp',
132 | '*.ico', 'image/x-icon',
133 | '*.mpeg;*.mpg;*.mpe', 'video/mpeg',
134 | '*.avi', 'video/x-msvideo',
135 | '*.txt', 'text/plain',
136 | '*.css', 'text/css',
137 | '*.js', 'text/javascript',
138 | '*.mkv', 'video/x-matroska',
139 | '*.mp3', 'audio/mp3',
140 | '*.mp4', 'video/mp4',
141 | '*.m3u8', 'application/x-mpegURL',
142 | '*.webp', 'image/webp'
143 | ]);
144 |
145 | applyThumbsExtStr(thumbsShowToExtDefaultStr);
146 |
147 | globalLimiter := TspeedLimiter.create();
148 | iconsCache := TiconsCache.create();
149 |
150 | FINALIZATION
151 |
152 | if Assigned(globalLimiter) then
153 | FreeAndNil(globalLimiter);
154 | iconsCache.free;
155 |
156 | end.
157 |
--------------------------------------------------------------------------------