├── .gitattributes
├── Bin
└── fftp.cfg
├── LICENSE.md
├── LightFTP_win.sha256
├── README.md
└── Source
├── fftp.sln
└── fftp
├── debug.conf
├── fftp.vcxproj
├── fftp.vcxproj.filters
├── fftp.vcxproj.user
├── ftpserv.c
├── ftpserv.h
├── main.c
└── minirtl
├── _strcat.c
├── _strcmp.c
├── _strcmpi.c
├── _strcpy.c
├── _strend.c
├── _strlen.c
├── _strncmpi.c
├── _strncpy.c
├── cmdline.c
├── cmdline.h
├── minirtl.h
├── rtltypes.h
├── strtou64.c
├── strtoul.c
├── u64tostr.c
└── ultostr.c
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/Bin/fftp.cfg:
--------------------------------------------------------------------------------
1 | [ftpconfig]
2 | port=5551
3 | maxusers=8
4 | interface=0.0.0.0
5 | external_ip=99.99.99.99
6 | local_mask=255.255.255.0
7 | minport=10000
8 | maxport=20000
9 | logfilepath=C:\TEMP
10 |
11 | [user1]
12 | pswd=password1
13 | accs=upload
14 | root=C:\Downloads\
15 |
16 | [anonymous]
17 | pswd=*
18 | accs=readonly
19 | root=C:\Downloads\
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) LightFTP 2007 - 2022 Project authors
2 |
3 | Redistribution and use in source and binary forms, with or without
4 | modification, are permitted provided that the following conditions are met:
5 |
6 | * Redistributions of source code must retain the above copyright notice, this
7 | list of conditions and the following disclaimer.
8 |
9 | * Redistributions in binary form must reproduce the above copyright notice,
10 | this list of conditions and the following disclaimer in the documentation
11 | and/or other materials provided with the distribution.
12 |
13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
22 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 |
--------------------------------------------------------------------------------
/LightFTP_win.sha256:
--------------------------------------------------------------------------------
1 | cdb10fa83d7980d2d27e7fb36682659ed481368ae7d923a3b6578765f93d922f *Bin\fftp.cfg
2 | 71f29f165b2554ec548bc56a113f38c0c993c2b34265acfd6d4c64a05f3db1ef *Source\fftp.sln
3 | 8708f082b6072e0ab2c55984c2bd252f80049f2d4dceabf6fd8cf4815331cf7f *Source\fftp\debug.conf
4 | 6315323d32acdc93160ed785f82631e686645370ae62082199f5c8000fc98e08 *Source\fftp\fftp.vcxproj
5 | 016e2f6abf2fa7ee4ca49c518c3169cec6ba2ee15178b631b1313e22bbfcf123 *Source\fftp\fftp.vcxproj.filters
6 | c62aab3e5ead5d888a83355bfc322c86f873476213d4cfc961537731687dd2b0 *Source\fftp\fftp.vcxproj.user
7 | 6145d9d5e7aaa668ae010c05c09a92473918b54119dd55141be9b62333e23966 *Source\fftp\ftpserv.c
8 | b50cb4bd51f5713d5189fab776e0cf654074ceb05532a9436744aeaa8ddba905 *Source\fftp\ftpserv.h
9 | 159f10f1dc5e4be259a37a625174d1e643ec70a9708197e8e20931ebcd61c3f9 *Source\fftp\main.c
10 | 893b90b942372928009bad64f166c7018701497e4f7cd1753cdc44f76da06707 *Source\fftp\minirtl\cmdline.c
11 | bd6fe82852c4fcdfab559defa33ea394b752a4e4a5ac0653ae20c4a94b0175ed *Source\fftp\minirtl\cmdline.h
12 | 107245437ed86b6f1e839b2d3d9bbadb3d9980046cb5c7001f985fed3627962f *Source\fftp\minirtl\minirtl.h
13 | b9de99d3447bb1a125cb92aa1b3f9b56a59522436f1a1a97f23aac9cee90341c *Source\fftp\minirtl\rtltypes.h
14 | 0320808115d42f04f63a382e8f386aa9bc77ba879892f5ccc94c40378b5131c8 *Source\fftp\minirtl\strtou64.c
15 | c51beca480d6e6f88174698503c0856c56488a59101d259c068dccb0902b01ec *Source\fftp\minirtl\strtoul.c
16 | 4d15af5a22467795c5367c3956746d01424795784f62ca3f30e4619c063338a5 *Source\fftp\minirtl\u64tostr.c
17 | 9cbedf9b92abaef3ea28de28dd523ac44079592178ef727c7003c339a5a54712 *Source\fftp\minirtl\ultostr.c
18 | 83772aa217508279294d91af5cfabec9b5e00b836a2e2f5fe37cf1ebc2905a52 *Source\fftp\minirtl\_strcat.c
19 | 2a67c7690ec6df8e233207116b0e4fe76c02ae43595d9e606e123572b6ac88a1 *Source\fftp\minirtl\_strcmp.c
20 | ef1b18997ea473ac8d516ef60efc64b9175418b8f078e088d783fdaef2544969 *Source\fftp\minirtl\_strcmpi.c
21 | 969b35213fa23ff50a169e5498a97f28bc6f5820b447b78ec9dc6910dd8cc3e8 *Source\fftp\minirtl\_strcpy.c
22 | 27159b8ff67d3f8e6c7fdb4b57b9f57f899bdfedf92cf10276269245c6f4e066 *Source\fftp\minirtl\_strend.c
23 | 60f19c6b805801e13824c4d9d44748da8245cd936971411d3d36b873121888eb *Source\fftp\minirtl\_strlen.c
24 | 87cc72bb8e3f1534bee09ee278ecd928d975ebb94aeffc767b67249815a0bf3a *Source\fftp\minirtl\_strncmpi.c
25 | 0434d69daa20fbf87d829ffc17e43dcc2db3386aff434af888011fdec2f645a4 *Source\fftp\minirtl\_strncpy.c
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # LightFTP
3 | * Small x86-32/x64 Windows FTP Server
4 |
5 | # Warning
6 |
7 | This is archive repository of LightFTP Windows only edition. It is archived for historical purposes and no longer maintained, thus it may have bugs and security problems. If you need actual version of LightFTP use https://github.com/hfiref0x/LightFTP
8 |
9 | # System Requirements
10 |
11 | * x86-32/x64 Windows Vista/7/8/8.1/10.
12 | * No admin privileges required. FTP server must be allowed in firewall.
13 |
14 | # Configuration
15 |
16 | Stored in fftp.cfg file, contain configuration section named ftpconfig and number of sections describing users and their privileges.
17 |
18 | ftpconfig section values:
19 | * port = unsigned integer, connection port number, allowed range 0..65535
20 | * maxusers = unsigned integer, maximum number of users allowed to connect
21 | * interface = string, network interface to bind ftp server to, for all interfaces use "0.0.0.0"
22 | * logfilepath = string, path to ftp log file, e.g. C:\TEMP, logged data append to the end of file
23 |
24 | User section values:
25 | * pswd = string, user password, specify * as any password
26 | * accs = string, access right, see "Available user access rights"
27 | * root = string, path to user home directory
28 |
29 | Available user access rights:
30 | * admin - read, write, append, delete, rename
31 | * readonly - browse and download
32 | * upload - creating new directories, store new files, append disabled
33 | * Note: any other access right is similar to banned (user not allowed to connect).
34 |
35 | Example of configuration file can be found in Bin directory.
36 |
37 | # Build
38 |
39 | * LightFTP comes with full source code, written in C.
40 | * In order to build from source in Windows you need Microsoft Visual Studio 2013/2015 and later versions. Source files (including VS project) located in Source/Windows directory.
41 |
42 |
43 | # Authors
44 |
45 | (c) 2007 - 2022 LightFTP Project
--------------------------------------------------------------------------------
/Source/fftp.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.23107.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fftp", "fftp\fftp.vcxproj", "{65ED3B21-ABBD-4030-9F93-163805E8B9FC}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|x64 = Debug|x64
11 | Debug|x86 = Debug|x86
12 | Release|x64 = Release|x64
13 | Release|x86 = Release|x86
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {65ED3B21-ABBD-4030-9F93-163805E8B9FC}.Debug|x64.ActiveCfg = Debug|x64
17 | {65ED3B21-ABBD-4030-9F93-163805E8B9FC}.Debug|x64.Build.0 = Debug|x64
18 | {65ED3B21-ABBD-4030-9F93-163805E8B9FC}.Debug|x86.ActiveCfg = Debug|x64
19 | {65ED3B21-ABBD-4030-9F93-163805E8B9FC}.Debug|x86.Build.0 = Debug|x64
20 | {65ED3B21-ABBD-4030-9F93-163805E8B9FC}.Release|x64.ActiveCfg = Release|x64
21 | {65ED3B21-ABBD-4030-9F93-163805E8B9FC}.Release|x64.Build.0 = Release|x64
22 | {65ED3B21-ABBD-4030-9F93-163805E8B9FC}.Release|x86.ActiveCfg = Release|Win32
23 | {65ED3B21-ABBD-4030-9F93-163805E8B9FC}.Release|x86.Build.0 = Release|Win32
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------
/Source/fftp/debug.conf:
--------------------------------------------------------------------------------
1 | [ftpconfig]
2 | port=21
3 | maxusers=8
4 | interface=0.0.0.0
5 | external_ip=99.99.99.99
6 | local_mask=255.255.255.0
7 | minport=10000
8 | maxport=20000
9 | logfilepath=C:\LOGS
10 |
11 | [anonymous]
12 | pswd=*
13 | accs=readonly
14 | root=Z:\FTP
15 |
16 | [uploader]
17 | pswd=uploader-password
18 | accs=upload
19 | root=Z:\FTP
20 |
21 | [root]
22 | pswd=admin-password
23 | accs=admin
24 | root=Z:\FTP
--------------------------------------------------------------------------------
/Source/fftp/fftp.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | {65ED3B21-ABBD-4030-9F93-163805E8B9FC}
23 | fftp
24 | 8.1
25 |
26 |
27 |
28 | Application
29 | true
30 | v140
31 | Unicode
32 |
33 |
34 | Application
35 | false
36 | v140
37 | true
38 | Unicode
39 |
40 |
41 | Application
42 | true
43 | v140
44 | Unicode
45 |
46 |
47 | Application
48 | false
49 | v140
50 | true
51 | Unicode
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | AllRules.ruleset
73 | true
74 | .\output\$(Platform)\$(Configuration)\
75 | .\output\$(Platform)\$(Configuration)\
76 |
77 |
78 | AllRules.ruleset
79 | true
80 | false
81 | .\output\$(Platform)\$(Configuration)\
82 | .\output\$(Platform)\$(Configuration)\
83 |
84 |
85 | AllRules.ruleset
86 | true
87 | .\output\$(Platform)\$(Configuration)\
88 | .\output\$(Platform)\$(Configuration)\
89 | false
90 |
91 |
92 | AllRules.ruleset
93 | true
94 | false
95 | .\output\$(Platform)\$(Configuration)\
96 | .\output\$(Platform)\$(Configuration)\
97 |
98 |
99 |
100 | Level4
101 | Disabled
102 | true
103 | true
104 | true
105 | true
106 | All
107 | true
108 | CompileAsC
109 | 6258
110 |
111 |
112 | true
113 |
114 |
115 | false
116 |
117 |
118 | false
119 |
120 |
121 |
122 |
123 | Level4
124 | Disabled
125 | true
126 | true
127 | true
128 | true
129 | All
130 | true
131 | CompileAsC
132 | 6258
133 |
134 |
135 | true
136 | 6.3
137 | true
138 | true
139 | Console
140 |
141 |
142 |
143 |
144 | false
145 |
146 |
147 | false
148 |
149 |
150 | false
151 |
152 |
153 |
154 |
155 | Level4
156 | Full
157 | true
158 | true
159 | true
160 | true
161 | true
162 | true
163 | All
164 | true
165 | CompileAsC
166 | Speed
167 | true
168 | Guard
169 | 6258
170 |
171 |
172 | false
173 | true
174 | true
175 | 6.3
176 | true
177 | true
178 | Console
179 | main
180 | true
181 |
182 |
183 | false
184 |
185 |
186 | false
187 |
188 |
189 | false
190 |
191 |
192 |
193 |
194 | Level4
195 | Full
196 | true
197 | true
198 | true
199 | true
200 | true
201 | true
202 | All
203 | true
204 | CompileAsC
205 | Speed
206 | true
207 | Guard
208 | false
209 | MultiThreaded
210 | 6258
211 |
212 |
213 | false
214 | true
215 | true
216 | 6.3
217 | true
218 | true
219 | Console
220 | true
221 | true
222 | main
223 | true
224 |
225 | false
226 |
227 |
228 | false
229 |
230 |
231 | false
232 |
233 |
234 | false
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
--------------------------------------------------------------------------------
/Source/fftp/fftp.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;hm;inl;inc;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 | {81073c34-4db9-483c-a154-1e9e30a78992}
18 |
19 |
20 | {96e240ff-d46f-4802-bd54-6836c2bbf393}
21 |
22 |
23 |
24 |
25 | Source Files
26 |
27 |
28 | Source Files
29 |
30 |
31 | Source Files\rtl_c
32 |
33 |
34 | Source Files\rtl_c
35 |
36 |
37 | Source Files\rtl_c
38 |
39 |
40 | Source Files\rtl_c
41 |
42 |
43 | Source Files\rtl_c
44 |
45 |
46 | Source Files\rtl_c
47 |
48 |
49 | Source Files\rtl_c
50 |
51 |
52 | Source Files\rtl_c
53 |
54 |
55 | Source Files\rtl_c
56 |
57 |
58 | Source Files\rtl_c
59 |
60 |
61 | Source Files\rtl_c
62 |
63 |
64 | Source Files\rtl_c
65 |
66 |
67 | Source Files\rtl_c
68 |
69 |
70 |
71 |
72 | Header Files
73 |
74 |
75 | Header Files\rtl_h
76 |
77 |
78 | Header Files\rtl_h
79 |
80 |
81 | Header Files\rtl_h
82 |
83 |
84 |
--------------------------------------------------------------------------------
/Source/fftp/fftp.vcxproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | .\debug.conf
5 | WindowsLocalDebugger
6 |
7 |
--------------------------------------------------------------------------------
/Source/fftp/ftpserv.c:
--------------------------------------------------------------------------------
1 | // ftpserv.c - simple ftp server
2 | #include
3 | #include "minirtl\minirtl.h"
4 | #include "minirtl\cmdline.h"
5 | #include "ftpserv.h"
6 |
7 | FTP_CONFIG g_cfg;
8 | HANDLE g_LogHandle;
9 | LONG g_NewID = 0;
10 | ULONG g_NextPort = 0;
11 |
12 | BOOL sendstring(SOCKET s, const char *Buffer)
13 | {
14 | return ( send(s, Buffer, (int)_strlen_a(Buffer), 0) >= 0 );
15 | }
16 |
17 | BOOL writeconsolestr(const char *Buffer)
18 | {
19 | DWORD bytesIO, l;
20 |
21 | l = (DWORD)_strlen_a(Buffer);
22 |
23 | if ( (g_LogHandle != NULL) && (g_LogHandle != INVALID_HANDLE_VALUE) )
24 | WriteFile(g_LogHandle, Buffer, l, &bytesIO, NULL);
25 |
26 | return WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), Buffer, l, &bytesIO, NULL);
27 | }
28 |
29 | BOOL writelogentry(PFTPCONTEXT context, char *logtext1, char *logtext2)
30 | {
31 | char cvbuf[32], textbuf[MAX_PATH*4];
32 | SYSTEMTIME tm;
33 |
34 | GetLocalTime(&tm);
35 |
36 | textbuf[0] = 0;
37 |
38 | if ( tm.wDay < 10 )
39 | _strcat_a(textbuf, "0");
40 | ultostr_a(tm.wDay, cvbuf);
41 | _strcat_a(textbuf, cvbuf);
42 | _strcat_a(textbuf, "-");
43 | if ( tm.wMonth < 10 )
44 | _strcat_a(textbuf, "0");
45 | ultostr_a(tm.wMonth, cvbuf);
46 | _strcat_a(textbuf, cvbuf);
47 | _strcat_a(textbuf, "-");
48 | ultostr_a(tm.wYear, cvbuf);
49 | _strcat_a(textbuf, cvbuf);
50 | _strcat_a(textbuf, " ");
51 |
52 | if ( tm.wHour < 10 )
53 | _strcat_a(textbuf, "0");
54 | ultostr_a(tm.wHour, cvbuf);
55 | _strcat_a(textbuf, cvbuf);
56 | _strcat_a(textbuf, ":");
57 | if ( tm.wMinute < 10 )
58 | _strcat_a(textbuf, "0");
59 | ultostr_a(tm.wMinute, cvbuf);
60 | _strcat_a(textbuf, cvbuf);
61 | _strcat_a(textbuf, ":");
62 | if ( tm.wSecond < 10 )
63 | _strcat_a(textbuf, "0");
64 | ultostr_a(tm.wSecond, cvbuf);
65 | _strcat_a(textbuf, cvbuf);
66 |
67 | _strcat_a(textbuf, " S-id=");
68 | ultostr_a(context->SessionID, _strend_a(textbuf));
69 | _strcat_a(textbuf, ": ");
70 | _strcat_a(textbuf, logtext1);
71 | _strcat_a(textbuf, logtext2);
72 | _strcat_a(textbuf, CRLF);
73 |
74 | return writeconsolestr(textbuf);
75 | }
76 |
77 | void unixpath(char *s)
78 | {
79 | while ( *s != 0 ) {
80 | if ( *s == '\\' )
81 | *s = '/';
82 | s++;
83 | }
84 | }
85 |
86 | void ntpath(char *s)
87 | {
88 | while ( *s != 0 ) {
89 | if ( *s == '/' )
90 | *s = '\\';
91 | s++;
92 | }
93 | }
94 |
95 | void nolastslash(char *s)
96 | {
97 | if ( s == NULL )
98 | return;
99 |
100 | if ( *s == 0 )
101 | return;
102 |
103 | if ( (*s == '\\') && (s[1] == 0) )
104 | return;
105 |
106 | while ( s[1] != 0 )
107 | s++;
108 |
109 | if ( *s == '\\' )
110 | *s = 0;
111 | }
112 |
113 | void addlastslash(char *s)
114 | {
115 | if ( s == NULL )
116 | return;
117 |
118 | if ( *s == 0 ) {
119 | *s = '\\';
120 | s[1] = 0;
121 | return;
122 | }
123 |
124 | while ( s[1] != 0 )
125 | s++;
126 |
127 | if ( *s == '\\' )
128 | return;
129 |
130 | s[1] = '\\';
131 | s[2] = 0;
132 | }
133 |
134 | void formatpath(char *s) // removes multiple slashes, dots
135 | {
136 | char *d = s, *p = s;
137 |
138 | while ( *p != 0 ) {
139 | if ( *p == '\\' )
140 | if ( p[1] == '.' ) {
141 | if ( p[2] == '.' )
142 | if ( p[3] == '\\' ) {
143 | while ( d < p+3 ) {
144 | *d = '\\';
145 | d++;
146 | }
147 | p = s;
148 | d = s;
149 | continue;
150 | }
151 | } else
152 | if ( p[1] != '\\' )
153 | d = p;
154 | p++;
155 | }
156 |
157 | d = s;
158 | p = s;
159 |
160 | while ( *p != 0 ) {
161 | if ( *p == '\\' ) {
162 | if ( p[1] == '\\' ) {
163 | p++;
164 | continue;
165 | }
166 |
167 | if ( p[1] == '.' ) {
168 | if ( p[2] == '\\' ) {
169 | p += 2;
170 | continue;
171 | }
172 | }
173 | }
174 |
175 | *d = *p;
176 | d++;
177 | p++;
178 | }
179 | *d = 0;
180 | }
181 |
182 | void filepath(char *s)
183 | {
184 | char *p = s;
185 |
186 | if ( (*s == '\\') && (s[1] == 0) )
187 | return;
188 |
189 | while ( *s != 0 ) {
190 | if ( *s == '\\' )
191 | p = s;
192 | s++;
193 | }
194 | *p = 0;
195 | }
196 |
197 | void finalpath(LPCSTR RootDir, LPCSTR CurrentDir, LPSTR Params, LPSTR Buffer)
198 | {
199 | char *root = Buffer;
200 |
201 | _strcpy_a(Buffer, RootDir);
202 | root = _strend_a(Buffer);
203 |
204 | if ( Params == NULL ) {
205 | addlastslash(root);
206 | _strcat_a(root, CurrentDir);
207 | addlastslash(root);
208 | formatpath(root);
209 | return;
210 | }
211 |
212 | ntpath(Params);
213 | if ( *Params != '\\' ) {
214 | addlastslash(root);
215 | _strcat_a(root, CurrentDir);
216 | }
217 |
218 | _strcat_a(root, Params);
219 | addlastslash(root);
220 | formatpath(root);
221 | formatpath(Buffer);
222 | }
223 |
224 | BOOL list_sub(SOCKET s, WIN32_FIND_DATA *fdata)
225 | {
226 | TCHAR textbuf[MAX_PATH];
227 | CHAR sendbuf[MAX_PATH];
228 | FILETIME ltm;
229 | ULARGE_INTEGER sz, deltatime;
230 | SYSTEMTIME tm;
231 |
232 | if ( _strcmp(fdata->cFileName, TEXT(".")) == 0 )
233 | return TRUE;
234 | if ( _strcmp(fdata->cFileName, TEXT("..")) == 0 )
235 | return TRUE;
236 |
237 | RtlSecureZeroMemory(<m, sizeof(ltm));
238 | RtlSecureZeroMemory(&tm, sizeof(tm));
239 |
240 | if ((fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
241 | _strcpy(textbuf, TEXT("-rw-rw-rw- 1"));
242 | else
243 | _strcpy(textbuf, TEXT("drwxrwxrwx 2"));
244 |
245 | _strcat(textbuf, TEXT(" 9001 9001 "));
246 |
247 | sz.LowPart = fdata->nFileSizeLow;
248 | sz.HighPart = fdata->nFileSizeHigh;
249 | u64tostr(sz.QuadPart, _strend(textbuf));
250 | _strcat(textbuf, TEXT(" "));
251 |
252 | GetSystemTimeAsFileTime(<m);
253 | sz.HighPart = fdata->ftLastWriteTime.dwHighDateTime;
254 | sz.LowPart = fdata->ftLastWriteTime.dwLowDateTime;
255 | deltatime.HighPart = ltm.dwHighDateTime;
256 | deltatime.LowPart = ltm.dwLowDateTime;
257 | deltatime.QuadPart -= sz.QuadPart;
258 |
259 | FileTimeToLocalFileTime(&fdata->ftLastWriteTime, <m);
260 | FileTimeToSystemTime(<m, &tm);
261 |
262 | _strncpy(_strend(textbuf), 4, &shortmonths[(tm.wMonth-1) * 3], 4);
263 | _strcat(textbuf, TEXT(" "));
264 |
265 | if ( tm.wDay < 10 )
266 | _strcat(textbuf, TEXT("0"));
267 | ultostr(tm.wDay, _strend(textbuf));
268 | _strcat(textbuf, TEXT(" "));
269 |
270 | if (deltatime.QuadPart < (180*24*60*60*10000000ui64)) {
271 | if (tm.wHour < 10)
272 | _strcat(textbuf, TEXT("0"));
273 | ultostr(tm.wHour, _strend(textbuf));
274 | _strcat(textbuf, TEXT(":"));
275 |
276 | if (tm.wMinute < 10)
277 | _strcat(textbuf, TEXT("0"));
278 | ultostr(tm.wMinute, _strend(textbuf));
279 | } else {
280 | ultostr(tm.wYear, _strend(textbuf));
281 | }
282 | _strcat(textbuf, TEXT(" "));
283 |
284 | WideCharToMultiByte(CP_UTF8, 0, textbuf, MAX_PATH, sendbuf, MAX_PATH, NULL, NULL);
285 | if ( !sendstring(s, sendbuf) )
286 | return FALSE;
287 | WideCharToMultiByte(CP_UTF8, 0, fdata->cFileName, MAX_PATH, sendbuf, MAX_PATH, NULL, NULL);
288 | if ( !sendstring(s, sendbuf) )
289 | return FALSE;
290 |
291 | return sendstring(s, CRLF);
292 | }
293 |
294 | BOOL mlsd_sub(SOCKET s, WIN32_FIND_DATA *fdata)
295 | {
296 | TCHAR textbuf[MAX_PATH];
297 | CHAR sendbuf[MAX_PATH];
298 | ULARGE_INTEGER sz;
299 | SYSTEMTIME tm;
300 |
301 | if (_strcmp(fdata->cFileName, TEXT(".")) == 0)
302 | return TRUE;
303 | if (_strcmp(fdata->cFileName, TEXT("..")) == 0)
304 | return TRUE;
305 |
306 | _strcpy(textbuf, TEXT("type="));
307 | if ((fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
308 | _strcat(textbuf, TEXT("file;size="));
309 | else
310 | _strcat(textbuf, TEXT("dir;sizd="));
311 |
312 | sz.LowPart = fdata->nFileSizeLow;
313 | sz.HighPart = fdata->nFileSizeHigh;
314 | u64tostr(sz.QuadPart, _strend(textbuf));
315 | _strcat(textbuf, TEXT(";modify="));
316 |
317 | RtlSecureZeroMemory(&tm, sizeof(tm));
318 | FileTimeToSystemTime(&fdata->ftLastWriteTime, &tm);
319 |
320 | ultostr(tm.wYear, _strend(textbuf));
321 | if (tm.wMonth < 10)
322 | _strcat(textbuf, TEXT("0"));
323 | ultostr(tm.wMonth, _strend(textbuf));
324 | if (tm.wDay < 10)
325 | _strcat(textbuf, TEXT("0"));
326 | ultostr(tm.wDay, _strend(textbuf));
327 | if (tm.wHour < 10)
328 | _strcat(textbuf, TEXT("0"));
329 | ultostr(tm.wHour, _strend(textbuf));
330 | if (tm.wMinute < 10)
331 | _strcat(textbuf, TEXT("0"));
332 | ultostr(tm.wMinute, _strend(textbuf));
333 | if (tm.wSecond < 10)
334 | _strcat(textbuf, TEXT("0"));
335 | ultostr(tm.wSecond, _strend(textbuf));
336 | _strcat(textbuf, TEXT("; "));
337 |
338 | WideCharToMultiByte(CP_UTF8, 0, textbuf, MAX_PATH, sendbuf, MAX_PATH, NULL, NULL);
339 | if (!sendstring(s, sendbuf))
340 | return FALSE;
341 | WideCharToMultiByte(CP_UTF8, 0, fdata->cFileName, MAX_PATH, sendbuf, MAX_PATH, NULL, NULL);
342 | if (!sendstring(s, sendbuf))
343 | return FALSE;
344 |
345 | return sendstring(s, CRLF);
346 | }
347 |
348 | SOCKET create_datasocket(IN PFTPCONTEXT context)
349 | {
350 | SOCKET clientsocket = INVALID_SOCKET;
351 | struct sockaddr_in laddr;
352 | int asz;
353 |
354 | RtlSecureZeroMemory(&laddr, sizeof(laddr));
355 |
356 | switch ( context->Mode ) {
357 | case MODE_NORMAL:
358 | clientsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
359 | if ( clientsocket == INVALID_SOCKET )
360 | return INVALID_SOCKET;
361 |
362 | laddr.sin_family = AF_INET;
363 | laddr.sin_port = (u_short)context->DataPort;
364 | laddr.sin_addr.S_un.S_addr = context->DataIPv4;
365 | if ( connect(clientsocket, (const struct sockaddr *)&laddr, sizeof(laddr)) == SOCKET_ERROR ) {
366 | closesocket(clientsocket);
367 | return INVALID_SOCKET;
368 | }
369 | break;
370 |
371 | case MODE_PASSIVE:
372 | asz = sizeof(laddr);
373 | clientsocket = accept(context->DataSocket, (struct sockaddr *)&laddr, &asz);
374 | if ( clientsocket == INVALID_SOCKET )
375 | return INVALID_SOCKET;
376 | context->DataIPv4 = 0;
377 | context->DataPort = 0;
378 | context->Mode = MODE_NORMAL;
379 | closesocket(context->DataSocket);
380 | context->DataSocket = 0;
381 | break;
382 |
383 | default:
384 | return INVALID_SOCKET;
385 | }
386 | return clientsocket;
387 | }
388 |
389 | DWORD WINAPI list_thread(IN PFTPCONTEXT context)
390 | {
391 | SOCKET clientsocket, control;
392 | HANDLE File;
393 | BOOL sendok = FALSE;
394 | WIN32_FIND_DATA fdata;
395 |
396 | EnterCriticalSection(&context->MTLock);
397 | control = context->ControlSocket;
398 | clientsocket = create_datasocket(context);
399 | if ( clientsocket == INVALID_SOCKET )
400 | goto error_exit;
401 |
402 | RtlSecureZeroMemory(&fdata, sizeof(fdata));
403 | File = FindFirstFile(context->TextBuffer, &fdata);
404 | if ( File != INVALID_HANDLE_VALUE ) {
405 | sendok = list_sub(clientsocket, &fdata);
406 | while ( FindNextFile(File, &fdata) && (!context->Stop) && sendok )
407 | sendok = list_sub(clientsocket, &fdata);
408 | FindClose(File);
409 | }
410 | sendok = ( context->Stop == FALSE ) && ( sendok != FALSE );
411 |
412 | error_exit:
413 | if ( clientsocket != INVALID_SOCKET )
414 | closesocket(clientsocket);
415 |
416 | writelogentry(context, " LIST complete", NULL);
417 |
418 | CloseHandle(context->WorkerThread);
419 | context->WorkerThread = NULL;
420 | LeaveCriticalSection(&context->MTLock);
421 |
422 | if ( clientsocket == INVALID_SOCKET ) {
423 | sendstring(control, error451);
424 | } else {
425 | if ( sendok )
426 | sendstring(control, success226);
427 | else
428 | sendstring(control, error426);
429 | }
430 |
431 | return 0;
432 | }
433 |
434 | DWORD WINAPI mlsd_thread(IN PFTPCONTEXT context)
435 | {
436 | SOCKET clientsocket, control;
437 | HANDLE File;
438 | BOOL sendok = FALSE;
439 | WIN32_FIND_DATA fdata;
440 |
441 | EnterCriticalSection(&context->MTLock);
442 | control = context->ControlSocket;
443 | clientsocket = create_datasocket(context);
444 | if (clientsocket == INVALID_SOCKET)
445 | goto error_exit;
446 |
447 | RtlSecureZeroMemory(&fdata, sizeof(fdata));
448 | File = FindFirstFile(context->TextBuffer, &fdata);
449 | if (File != INVALID_HANDLE_VALUE) {
450 | sendok = mlsd_sub(clientsocket, &fdata);
451 | while (FindNextFile(File, &fdata) && (!context->Stop) && sendok)
452 | sendok = mlsd_sub(clientsocket, &fdata);
453 | FindClose(File);
454 | }
455 | sendok = (context->Stop == FALSE) && (sendok != FALSE);
456 |
457 | error_exit:
458 | if (clientsocket != INVALID_SOCKET)
459 | closesocket(clientsocket);
460 |
461 | writelogentry(context, " LIST complete", NULL);
462 |
463 | CloseHandle(context->WorkerThread);
464 | context->WorkerThread = NULL;
465 | LeaveCriticalSection(&context->MTLock);
466 |
467 | if (clientsocket == INVALID_SOCKET) {
468 | sendstring(control, error451);
469 | }
470 | else {
471 | if (sendok)
472 | sendstring(control, success226);
473 | else
474 | sendstring(control, error426);
475 | }
476 |
477 | return 0;
478 | }
479 |
480 | DWORD WINAPI retr_thread(IN PFTPCONTEXT context)
481 | {
482 | SOCKET clientsocket, control;
483 | LPSTR textbuf = NULL;
484 | BOOL sendok = FALSE;
485 | int asz;
486 | LARGE_INTEGER lsz, dt0, dt1;
487 | FILETIME txtime;
488 |
489 | EnterCriticalSection(&context->MTLock);
490 | control = context->ControlSocket;
491 |
492 | clientsocket = create_datasocket(context);
493 | if ( clientsocket == INVALID_SOCKET )
494 | goto error_exit;
495 |
496 | textbuf = (char *)VirtualAlloc(NULL, TRANSMIT_BUFFER_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
497 | if ( textbuf == NULL )
498 | goto error_exit;
499 |
500 | lsz.QuadPart = 0;
501 | GetFileSizeEx(context->FileHandle, &lsz);
502 |
503 | if ( lsz.QuadPart == 0 ) {
504 | sendok = TRUE;
505 | goto error_exit;
506 | }
507 |
508 | GetSystemTimeAsFileTime(&txtime);
509 | dt0.HighPart = txtime.dwHighDateTime;
510 | dt0.LowPart = txtime.dwLowDateTime;
511 | lsz.QuadPart = 0;
512 |
513 | SetFilePointerEx(context->FileHandle, context->RestPoint, NULL, FILE_BEGIN);
514 | while ( !context->Stop ) {
515 | asz = 0;
516 | if ( !ReadFile(context->FileHandle, textbuf, TRANSMIT_BUFFER_SIZE, (LPDWORD)&asz, NULL) ) {
517 | sendok = FALSE;
518 | break;
519 | }
520 |
521 | if ( asz > 0 )
522 | sendok = (send(clientsocket, textbuf, asz, 0) == asz);
523 | else
524 | break;
525 |
526 | if ( !sendok )
527 | break;
528 |
529 | lsz.QuadPart += asz;
530 | GetSystemTimeAsFileTime(&txtime);
531 | dt1.HighPart = txtime.dwHighDateTime;
532 | dt1.LowPart = txtime.dwLowDateTime;
533 | dt1.QuadPart -= dt0.QuadPart;
534 | if (dt1.QuadPart > 100000000ui64) { // 10 seconds
535 | _strcpy_a(textbuf, " retr speed: ");
536 | u64tostr_a(((10000000ui64*lsz.QuadPart) / dt1.QuadPart) / 1024, _strend_a(textbuf));
537 | _strcat_a(textbuf, " kBytes/s");
538 | writelogentry(context, textbuf, NULL);
539 | lsz.QuadPart = 0;
540 | dt0.HighPart = txtime.dwHighDateTime;
541 | dt0.LowPart = txtime.dwLowDateTime;
542 | }
543 | }
544 | sendok = ( context->Stop == FALSE ) && ( sendok != FALSE );
545 |
546 | error_exit:
547 | CloseHandle(context->FileHandle);
548 | context->FileHandle = INVALID_HANDLE_VALUE;
549 |
550 | if ( textbuf != NULL )
551 | VirtualFree(textbuf, 0, MEM_RELEASE);
552 |
553 | if ( clientsocket != INVALID_SOCKET )
554 | closesocket(clientsocket);
555 |
556 | writelogentry(context, " RETR complete", NULL);
557 |
558 | CloseHandle(context->WorkerThread);
559 | context->WorkerThread = NULL;
560 | LeaveCriticalSection(&context->MTLock);
561 |
562 | if ( clientsocket == INVALID_SOCKET )
563 | sendstring(control, error451);
564 | else {
565 | if ( sendok )
566 | sendstring(control, success226);
567 | else
568 | sendstring(control, error426);
569 | }
570 |
571 | return 0;
572 | }
573 |
574 | DWORD WINAPI stor_thread(IN PFTPCONTEXT context)
575 | {
576 | SOCKET clientsocket, control;
577 | LPSTR textbuf = NULL;
578 | BOOL sendok = FALSE;
579 | int asz, iobytes;
580 |
581 | EnterCriticalSection(&context->MTLock);
582 | control = context->ControlSocket;
583 |
584 | clientsocket = create_datasocket(context);
585 | if ( clientsocket == INVALID_SOCKET )
586 | goto error_exit;
587 |
588 | textbuf = (char *)VirtualAlloc(NULL, TRANSMIT_BUFFER_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
589 | if ( textbuf == NULL )
590 | goto error_exit;
591 |
592 | while ( !context->Stop )
593 | {
594 | asz = 0;
595 | iobytes = recv(clientsocket, textbuf, TRANSMIT_BUFFER_SIZE, 0);
596 | if ( (iobytes > 0) && (iobytes != SOCKET_ERROR) )
597 | WriteFile(context->FileHandle, textbuf, iobytes, (LPDWORD)&asz, NULL);
598 | else
599 | break;
600 |
601 | sendok = (iobytes == asz);
602 |
603 | if ( !sendok )
604 | break;
605 | }
606 | sendok = ( context->Stop == FALSE ) && ( sendok != FALSE );
607 |
608 | error_exit:
609 | CloseHandle(context->FileHandle);
610 | context->FileHandle = INVALID_HANDLE_VALUE;
611 |
612 | if ( textbuf != NULL )
613 | VirtualFree(textbuf, 0, MEM_RELEASE);
614 |
615 | if ( clientsocket != INVALID_SOCKET )
616 | closesocket(clientsocket);
617 |
618 | writelogentry(context, " STOR complete", NULL);
619 |
620 | CloseHandle(context->WorkerThread);
621 | context->WorkerThread = NULL;
622 | LeaveCriticalSection(&context->MTLock);
623 |
624 | if ( clientsocket == INVALID_SOCKET )
625 | sendstring(control, error451);
626 | else {
627 | if ( sendok )
628 | sendstring(control, success226);
629 | else
630 | sendstring(control, error426);
631 | }
632 |
633 | return 0;
634 | }
635 |
636 | void StopWorkerThread(IN PFTPCONTEXT context)
637 | {
638 | if ( context->WorkerThread != NULL ) {
639 | context->Stop = TRUE; // trying to stop gracefully
640 | if ( WaitForSingleObjectEx(context->WorkerThread, 4000, FALSE) == STATUS_TIMEOUT ) {
641 | // fail? ok, will do bad things
642 | TerminateThread(context->WorkerThread, 0);
643 | }
644 | CloseHandle(context->WorkerThread);
645 | }
646 |
647 | if ( context->FileHandle != INVALID_HANDLE_VALUE ) {
648 | CloseHandle(context->FileHandle);
649 | context->FileHandle = INVALID_HANDLE_VALUE;
650 | }
651 |
652 | context->DataIPv4 = 0;
653 | context->DataPort = 0;
654 |
655 | if ( context->DataSocket != 0 ) {
656 | closesocket(context->DataSocket);
657 | context->DataSocket = 0;
658 | }
659 | context->WorkerThread = NULL;
660 | }
661 |
662 | /* FTP COMMANDS BEGIN */
663 |
664 | BOOL ftpSTOR(IN PFTPCONTEXT context, IN const char *params)
665 | {
666 | HANDLE wth;
667 | DWORD fileaccess;
668 | CHAR textbuf[MAX_PATH*3];
669 | TCHAR filename[MAX_PATH];
670 |
671 | if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
672 | return sendstring(context->ControlSocket, error530);
673 | if ( context->Access < FTP_ACCESS_CREATENEW )
674 | return sendstring(context->ControlSocket, error550_r);
675 | if ( params == NULL )
676 | return sendstring(context->ControlSocket, error501);
677 | if ( context->WorkerThread != NULL )
678 | return sendstring(context->ControlSocket, error550_t);
679 |
680 | RtlSecureZeroMemory(textbuf, sizeof(textbuf));
681 | finalpath(context->RootDir, context->CurrentDir, (char *)params, textbuf);
682 | nolastslash(textbuf);
683 |
684 | if (MultiByteToWideChar(CP_UTF8, 0, textbuf, MAX_PATH, filename, MAX_PATH) <= 0)
685 | filename[0] = 0;
686 |
687 | if ( context->Access > FTP_ACCESS_CREATENEW )
688 | fileaccess = CREATE_ALWAYS;
689 | else
690 | fileaccess = CREATE_NEW;
691 |
692 | context->FileHandle = CreateFile(filename, SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, fileaccess, FILE_ATTRIBUTE_NORMAL, NULL);
693 | if ( context->FileHandle == INVALID_HANDLE_VALUE )
694 | return sendstring(context->ControlSocket, error550);
695 |
696 | sendstring(context->ControlSocket, interm150);
697 | writelogentry(context, " STOR: ", (char *)params);
698 | context->Stop = FALSE;
699 |
700 | wth = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&stor_thread, (LPVOID)context, 0, NULL);
701 | if ( wth == NULL ) {
702 | CloseHandle(context->FileHandle);
703 | context->FileHandle = INVALID_HANDLE_VALUE;
704 | sendstring(context->ControlSocket, error451);
705 | }
706 | else
707 | context->WorkerThread = wth;
708 |
709 | return TRUE;
710 | }
711 |
712 | BOOL ftpAPPE(IN PFTPCONTEXT context, IN const char *params)
713 | {
714 | HANDLE wth;
715 | LARGE_INTEGER fptr;
716 | CHAR textbuf[MAX_PATH*3];
717 | TCHAR filename[MAX_PATH];
718 |
719 | if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
720 | return sendstring(context->ControlSocket, error530);
721 | if ( context->Access < FTP_ACCESS_FULL )
722 | return sendstring(context->ControlSocket, error550_r);
723 | if ( params == NULL )
724 | return sendstring(context->ControlSocket, error501);
725 | if ( context->WorkerThread != NULL )
726 | return sendstring(context->ControlSocket, error550_t);
727 |
728 | RtlSecureZeroMemory(textbuf, sizeof(textbuf));
729 | finalpath(context->RootDir, context->CurrentDir, (char *)params, textbuf);
730 | nolastslash(textbuf);
731 |
732 | if (MultiByteToWideChar(CP_UTF8, 0, textbuf, MAX_PATH, filename, MAX_PATH) <= 0)
733 | filename[0] = 0;
734 |
735 | context->FileHandle = CreateFile(filename, SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
736 | if ( context->FileHandle == INVALID_HANDLE_VALUE )
737 | return sendstring(context->ControlSocket, error550);
738 |
739 | fptr.QuadPart = 0;
740 | SetFilePointerEx(context->FileHandle, fptr, NULL, FILE_END);
741 | sendstring(context->ControlSocket, interm150);
742 | writelogentry(context, " APPE: ", (char *)params);
743 | context->Stop = FALSE;
744 |
745 | wth = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&stor_thread, (LPVOID)context, 0, NULL);
746 | if ( wth == NULL ) {
747 | CloseHandle(context->FileHandle);
748 | context->FileHandle = INVALID_HANDLE_VALUE;
749 | sendstring(context->ControlSocket, error451);
750 | } else
751 | context->WorkerThread = wth;
752 |
753 | return TRUE;
754 | }
755 |
756 | BOOL ftpRETR(IN PFTPCONTEXT context, IN const char *params)
757 | {
758 | CHAR textbuf[MAX_PATH*3];
759 | TCHAR filename[MAX_PATH];
760 | HANDLE wth;
761 |
762 | if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
763 | return sendstring(context->ControlSocket, error530);
764 | if ( params == NULL )
765 | return sendstring(context->ControlSocket, error501);
766 | if ( context->WorkerThread != NULL )
767 | return sendstring(context->ControlSocket, error550_t);
768 |
769 | RtlSecureZeroMemory(textbuf, sizeof(textbuf));
770 | finalpath(context->RootDir, context->CurrentDir, (char *)params, textbuf);
771 | nolastslash(textbuf);
772 |
773 | if (MultiByteToWideChar(CP_UTF8, 0, textbuf, MAX_PATH, filename, MAX_PATH) <= 0)
774 | filename[0] = 0;
775 |
776 | context->FileHandle = CreateFile(filename, SYNCHRONIZE | GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
777 | if ( context->FileHandle == INVALID_HANDLE_VALUE )
778 | return sendstring(context->ControlSocket, error550);
779 |
780 | sendstring(context->ControlSocket, interm150);
781 | writelogentry(context, " RETR: ", (char *)params);
782 | context->Stop = FALSE;
783 |
784 | wth = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&retr_thread, (LPVOID)context, 0, NULL);
785 | if ( wth == NULL ) {
786 | CloseHandle(context->FileHandle);
787 | context->FileHandle = INVALID_HANDLE_VALUE;
788 | sendstring(context->ControlSocket, error451);
789 | } else
790 | context->WorkerThread = wth;
791 |
792 | return TRUE;
793 | }
794 |
795 | BOOL ftpLIST(IN PFTPCONTEXT context, IN const char *params)
796 | {
797 | WIN32_FILE_ATTRIBUTE_DATA adata;
798 | HANDLE wth;
799 | CHAR textbuf[MAX_PATH*3];
800 |
801 | if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
802 | return sendstring(context->ControlSocket, error530);
803 | if (context->WorkerThread != NULL)
804 | return sendstring(context->ControlSocket, error550_t);
805 |
806 | context->FileHandle = INVALID_HANDLE_VALUE;
807 |
808 | RtlSecureZeroMemory(textbuf, sizeof(textbuf));
809 | RtlSecureZeroMemory(context->TextBuffer, sizeof(context->TextBuffer));
810 |
811 | if ( params != NULL )
812 | if ( (params[0] == '-') && (params[1] == 'l') && (params[2] == 0) )
813 | params = NULL;
814 |
815 | finalpath(context->RootDir, context->CurrentDir, (char *)params, textbuf);
816 |
817 | if (MultiByteToWideChar(CP_UTF8, 0, textbuf, MAX_PATH, context->TextBuffer, MAX_PATH) <= 0)
818 | context->TextBuffer[0] = 0;
819 |
820 | if ( !GetFileAttributesEx(context->TextBuffer, GetFileExInfoStandard, &adata) )
821 | return sendstring(context->ControlSocket, error550);
822 | if ( (adata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0 )
823 | return sendstring(context->ControlSocket, error550);
824 |
825 | _strcat(context->TextBuffer, TEXT("*"));
826 |
827 | sendstring(context->ControlSocket, interm150);
828 | writelogentry(context, " LIST: ", (char *)params);
829 | context->Stop = FALSE;
830 |
831 | wth = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&list_thread, (LPVOID)context, 0, NULL);
832 | if ( wth == NULL )
833 | sendstring(context->ControlSocket, error451);
834 | else
835 | context->WorkerThread = wth;
836 |
837 | return TRUE;
838 | }
839 |
840 | BOOL ftpPASV(IN PFTPCONTEXT context, IN const char *params)
841 | {
842 | SOCKET datasocket;
843 | struct sockaddr_in laddr;
844 | int socketret = SOCKET_ERROR;
845 | CHAR textbuf[MAX_PATH];
846 | ULONG c;
847 |
848 | UNREFERENCED_PARAMETER(params);
849 |
850 | if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
851 | return sendstring(context->ControlSocket, error530);
852 | if (context->WorkerThread != NULL)
853 | return sendstring(context->ControlSocket, error550_t);
854 | if ( context->DataSocket != 0 )
855 | closesocket(context->DataSocket);
856 |
857 | datasocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
858 | if (datasocket == INVALID_SOCKET)
859 | return sendstring(context->ControlSocket, error451);
860 |
861 | RtlSecureZeroMemory(&laddr, sizeof(laddr));
862 | for (c = g_cfg.PasvPortBase; c <= g_cfg.PasvPortMax; c++) {
863 | RtlSecureZeroMemory(&laddr, sizeof(laddr));
864 | laddr.sin_family = AF_INET;
865 |
866 | laddr.sin_port = htons((u_short)(g_cfg.PasvPortBase +
867 | (InterlockedIncrement(&g_NewID) % (g_cfg.PasvPortMax-g_cfg.PasvPortBase))));
868 |
869 | laddr.sin_addr.S_un.S_addr = context->ServerIPv4;
870 | socketret = bind(datasocket, (struct sockaddr *)&laddr, sizeof(laddr));
871 | if ( socketret == 0 )
872 | break;
873 | }
874 |
875 | if ( socketret != 0 ) {
876 | closesocket(datasocket);
877 | return sendstring(context->ControlSocket, error451);
878 | }
879 | socketret = listen(datasocket, SOMAXCONN);
880 | if (socketret != 0) {
881 | closesocket(datasocket);
882 | return sendstring(context->ControlSocket, error451);
883 | }
884 |
885 | if ((context->ClientIPv4 & g_cfg.LocalIPMask) == (context->ServerIPv4 & g_cfg.LocalIPMask))
886 | {
887 | context->DataIPv4 = context->ServerIPv4;
888 | writelogentry(context, " local client.", NULL);
889 | }
890 | else
891 | {
892 | context->DataIPv4 = g_cfg.ExternalInterface;
893 | writelogentry(context, " nonlocal client.", NULL);
894 | }
895 |
896 | context->DataPort = laddr.sin_port;
897 | context->DataSocket = datasocket;
898 | context->Mode = MODE_PASSIVE;
899 |
900 | sendstring(context->ControlSocket, success227);
901 | for (c = 0; c < 4; c++) {
902 | ultostr_a((context->DataIPv4 >> (c*8)) & 0xff, textbuf);
903 | _strcat_a(textbuf, ",");
904 | sendstring(context->ControlSocket, textbuf);
905 | }
906 |
907 | ultostr_a(context->DataPort & 0xff, textbuf);
908 | _strcat_a(textbuf, ",");
909 | sendstring(context->ControlSocket, textbuf);
910 | ultostr_a((context->DataPort >> 8) & 0xff, textbuf);
911 | _strcat_a(textbuf, ").");
912 | _strcat_a(textbuf, CRLF);
913 |
914 | writelogentry(context, " entering passive mode", NULL);
915 |
916 | return sendstring(context->ControlSocket, textbuf);
917 | }
918 |
919 | BOOL ftpPORT(IN PFTPCONTEXT context, IN const char *params)
920 | {
921 | int c;
922 | ULONG DataIP = 0, DataP = 0;
923 | char *p = (char *)params;
924 |
925 | if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
926 | return sendstring(context->ControlSocket, error530);
927 |
928 | if ( params == NULL )
929 | return sendstring(context->ControlSocket, error501);
930 |
931 | for (c = 0; c < 4; c++) {
932 | ((BYTE *)&DataIP)[c] = (BYTE)strtoul_a(p);
933 | while ( (*p >= '0') && (*p <= '9') )
934 | p++;
935 | if ( *p == 0 )
936 | break;
937 | p++;
938 | }
939 |
940 | for (c = 0; c < 2; c++) {
941 | ((BYTE *)&DataP)[c] = (BYTE)strtoul_a(p);
942 | while ( (*p >= '0') && (*p <= '9') )
943 | p++;
944 | if ( *p == 0 )
945 | break;
946 | p++;
947 | }
948 |
949 | if ( DataIP != context->ClientIPv4 )
950 | return sendstring(context->ControlSocket, error501);
951 |
952 | context->DataIPv4 = DataIP;
953 | context->DataPort = DataP;
954 | context->Mode = MODE_NORMAL;
955 |
956 | return sendstring(context->ControlSocket, success200);
957 | }
958 |
959 | BOOL ftpREST(IN PFTPCONTEXT context, IN const char *params)
960 | {
961 | CHAR textbuf[MAX_PATH];
962 |
963 | if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
964 | return sendstring(context->ControlSocket, error530);
965 |
966 | if ( params == NULL )
967 | return sendstring(context->ControlSocket, error501);
968 |
969 | context->RestPoint.QuadPart = strtou64_a((char *)params);
970 | sendstring(context->ControlSocket, interm350);
971 | u64tostr_a(context->RestPoint.QuadPart, textbuf);
972 | _strcat_a(textbuf, CRLF);
973 | return sendstring(context->ControlSocket, textbuf);
974 | }
975 |
976 | BOOL ftpCWD(IN PFTPCONTEXT context, IN const char *params)
977 | {
978 | WIN32_FILE_ATTRIBUTE_DATA adata;
979 | CHAR textbuf[MAX_PATH*3];
980 | TCHAR dirname[MAX_PATH];
981 |
982 | if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
983 | return sendstring(context->ControlSocket, error530);
984 | if ( params == NULL )
985 | return sendstring(context->ControlSocket, error501);
986 |
987 | RtlSecureZeroMemory(textbuf, sizeof(textbuf));
988 | finalpath(context->RootDir, context->CurrentDir, (char *)params, textbuf);
989 |
990 | if (MultiByteToWideChar(CP_UTF8, 0, textbuf, MAX_PATH, dirname, MAX_PATH) <= 0)
991 | dirname[0] = 0;
992 |
993 | if ( !GetFileAttributesEx(dirname, GetFileExInfoStandard, &adata) )
994 | return sendstring(context->ControlSocket, error550);
995 | if ( (adata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0 )
996 | return sendstring(context->ControlSocket, error550);
997 |
998 | RtlSecureZeroMemory(textbuf, sizeof(textbuf));
999 | if ( *params != '\\' ) {
1000 | _strcpy_a(textbuf, context->CurrentDir);
1001 | addlastslash(textbuf);
1002 | }
1003 | _strcat_a(textbuf, params);
1004 | addlastslash(textbuf);
1005 | formatpath(textbuf);
1006 | _strcpy_a(context->CurrentDir, textbuf);
1007 |
1008 | writelogentry(context, " CWD: ", (char *)params);
1009 |
1010 | return sendstring(context->ControlSocket, success250);
1011 | }
1012 |
1013 | BOOL ftpDELE(IN PFTPCONTEXT context, IN const char *params)
1014 | {
1015 | CHAR textbuf[MAX_PATH*3];
1016 | TCHAR filename[MAX_PATH];
1017 |
1018 | if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
1019 | return sendstring(context->ControlSocket, error530);
1020 | if ( context->Access < FTP_ACCESS_FULL )
1021 | return sendstring(context->ControlSocket, error550_r);
1022 | if ( params == NULL )
1023 | return sendstring(context->ControlSocket, error501);
1024 |
1025 | RtlSecureZeroMemory(textbuf, sizeof(textbuf));
1026 | finalpath(context->RootDir, context->CurrentDir, (char *)params, textbuf);
1027 | nolastslash(textbuf);
1028 |
1029 | if (MultiByteToWideChar(CP_UTF8, 0, textbuf, MAX_PATH, filename, MAX_PATH) <= 0)
1030 | filename[0] = 0;
1031 |
1032 | if ( DeleteFile(filename) ) {
1033 | sendstring(context->ControlSocket, success250);
1034 | writelogentry(context, " DELE: ", (char *)params);
1035 | }
1036 | else
1037 | sendstring(context->ControlSocket, error550_r);
1038 |
1039 | return TRUE;
1040 | }
1041 |
1042 | BOOL ftpRMD(IN PFTPCONTEXT context, IN const char *params)
1043 | {
1044 | CHAR textbuf[MAX_PATH*3];
1045 | TCHAR filename[MAX_PATH];
1046 |
1047 | if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
1048 | return sendstring(context->ControlSocket, error530);
1049 | if ( context->Access < FTP_ACCESS_FULL )
1050 | return sendstring(context->ControlSocket, error550_r);
1051 | if ( params == NULL )
1052 | return sendstring(context->ControlSocket, error501);
1053 |
1054 | RtlSecureZeroMemory(textbuf, sizeof(textbuf));
1055 | finalpath(context->RootDir, context->CurrentDir, (char *)params, textbuf);
1056 |
1057 | if (MultiByteToWideChar(CP_UTF8, 0, textbuf, MAX_PATH, filename, MAX_PATH) <= 0)
1058 | filename[0] = 0;
1059 |
1060 | if ( RemoveDirectory(filename) ) {
1061 | sendstring(context->ControlSocket, success250);
1062 | writelogentry(context, " RMD: ", (char *)params);
1063 | }
1064 | else
1065 | sendstring(context->ControlSocket, error550_r);
1066 |
1067 | return TRUE;
1068 | }
1069 |
1070 | BOOL ftpMKD(IN PFTPCONTEXT context, IN const char *params)
1071 | {
1072 | CHAR textbuf[MAX_PATH*3];
1073 | TCHAR filename[MAX_PATH];
1074 |
1075 | if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
1076 | return sendstring(context->ControlSocket, error530);
1077 | if ( context->Access < FTP_ACCESS_CREATENEW )
1078 | return sendstring(context->ControlSocket, error550_r);
1079 | if ( params == NULL )
1080 | return sendstring(context->ControlSocket, error501);
1081 |
1082 | RtlSecureZeroMemory(textbuf, sizeof(textbuf));
1083 | finalpath(context->RootDir, context->CurrentDir, (char *)params, textbuf);
1084 |
1085 | if (MultiByteToWideChar(CP_UTF8, 0, textbuf, MAX_PATH, filename, MAX_PATH) <= 0)
1086 | filename[0] = 0;
1087 |
1088 | if ( CreateDirectory(filename, NULL) ) {
1089 | sendstring(context->ControlSocket, success257);
1090 | writelogentry(context, " MKD: ", (char *)params);
1091 | }
1092 | else
1093 | sendstring(context->ControlSocket, error550);
1094 |
1095 | return TRUE;
1096 | }
1097 |
1098 | BOOL ftpSIZE(IN PFTPCONTEXT context, IN const char *params)
1099 | {
1100 | WIN32_FILE_ATTRIBUTE_DATA adata;
1101 | ULARGE_INTEGER sz;
1102 | CHAR textbuf[MAX_PATH*3];
1103 | TCHAR filename[MAX_PATH];
1104 |
1105 | if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
1106 | return sendstring(context->ControlSocket, error530);
1107 | if ( params == NULL )
1108 | return sendstring(context->ControlSocket, error501);
1109 |
1110 | RtlSecureZeroMemory(textbuf, sizeof(textbuf));
1111 | finalpath(context->RootDir, context->CurrentDir, (char *)params, textbuf);
1112 | nolastslash(textbuf);
1113 |
1114 | if (MultiByteToWideChar(CP_UTF8, 0, textbuf, MAX_PATH, filename, MAX_PATH) <= 0)
1115 | filename[0] = 0;
1116 |
1117 | if ( !GetFileAttributesEx(filename, GetFileExInfoStandard, &adata) )
1118 | return sendstring(context->ControlSocket, error550);
1119 |
1120 | sz.HighPart = adata.nFileSizeHigh;
1121 | sz.LowPart = adata.nFileSizeLow;
1122 |
1123 | sendstring(context->ControlSocket, "213 ");
1124 | u64tostr_a(sz.QuadPart, textbuf);
1125 | _strcat_a(textbuf, CRLF);
1126 |
1127 | sendstring(context->ControlSocket, textbuf);
1128 | return TRUE;
1129 | }
1130 |
1131 | BOOL ftpUSER(IN PFTPCONTEXT context, IN const char *params)
1132 | {
1133 | CHAR textbuf[MAX_PATH*3];
1134 |
1135 | if ( params == NULL )
1136 | return sendstring(context->ControlSocket, error501);
1137 |
1138 | context->Access = FTP_ACCESS_NOT_LOGGED_IN;
1139 | _strncpy_a(context->UserName, 256, params, 256);
1140 |
1141 | writelogentry(context, " USER: ", (char *)params);
1142 |
1143 | _strcpy_a(textbuf, interm331);
1144 | _strcat_a(textbuf, (char *)params);
1145 | _strcat_a(textbuf, interm331_tail);
1146 | return sendstring(context->ControlSocket, textbuf);
1147 | }
1148 |
1149 | BOOL ftpQUIT(IN PFTPCONTEXT context, IN const char *params)
1150 | {
1151 | UNREFERENCED_PARAMETER(params);
1152 |
1153 | writelogentry(context, " QUIT", NULL);
1154 | sendstring(context->ControlSocket, success221);
1155 | return FALSE;
1156 | }
1157 |
1158 | BOOL ftpNOOP(IN PFTPCONTEXT context, IN const char *params)
1159 | {
1160 | UNREFERENCED_PARAMETER(params);
1161 |
1162 | return sendstring(context->ControlSocket, success200);
1163 | }
1164 |
1165 | BOOL ftpPASS(IN PFTPCONTEXT context, IN const char *params)
1166 | {
1167 | BOOL cond = FALSE, found = FALSE;
1168 | CHAR temptext[256];
1169 |
1170 | if ( params == NULL )
1171 | return sendstring(context->ControlSocket, error501);
1172 |
1173 | RtlSecureZeroMemory(temptext, sizeof(temptext));
1174 | if (!ParseConfig(g_cfg.ConfigFile, context->UserName, "pswd", temptext, 256))
1175 | return sendstring(context->ControlSocket, error530_r);
1176 |
1177 | if ( (_strcmp_a(temptext, params) != 0) && (temptext[0] != '*') )
1178 | return sendstring(context->ControlSocket, error530_r);
1179 |
1180 | RtlSecureZeroMemory(context->RootDir, sizeof(context->RootDir));
1181 | RtlSecureZeroMemory(temptext, sizeof(temptext));
1182 |
1183 | ParseConfig(g_cfg.ConfigFile, context->UserName, "root", context->RootDir, MAX_PATH);
1184 | ParseConfig(g_cfg.ConfigFile, context->UserName, "accs", temptext, 256);
1185 |
1186 | context->Access = FTP_ACCESS_NOT_LOGGED_IN;
1187 |
1188 | do {
1189 | if (_strcmpi_a(temptext, "admin") == 0) {
1190 | context->Access = FTP_ACCESS_FULL;
1191 | found = TRUE;
1192 | break;
1193 | }
1194 |
1195 | if (_strcmpi_a(temptext, "upload") == 0) {
1196 | context->Access = FTP_ACCESS_CREATENEW;
1197 | found = TRUE;
1198 | break;
1199 | }
1200 |
1201 | if (_strcmpi_a(temptext, "readonly") == 0) {
1202 | context->Access = FTP_ACCESS_READONLY;
1203 | found = TRUE;
1204 | break;
1205 | }
1206 |
1207 | } while (cond);
1208 |
1209 | if (found == FALSE)
1210 | return sendstring(context->ControlSocket, error530_b);
1211 |
1212 | writelogentry(context, " PASS->logon successful", NULL);
1213 | return sendstring(context->ControlSocket, success230);
1214 | }
1215 |
1216 | BOOL ftpSYST(IN PFTPCONTEXT context, IN const char *params)
1217 | {
1218 | UNREFERENCED_PARAMETER(params);
1219 |
1220 | return sendstring(context->ControlSocket, success215);
1221 | }
1222 |
1223 | BOOL ftpFEAT(IN PFTPCONTEXT context, IN const char *params)
1224 | {
1225 | UNREFERENCED_PARAMETER(params);
1226 |
1227 | sendstring(context->ControlSocket, success211);
1228 | return sendstring(context->ControlSocket, success211_end);
1229 | }
1230 |
1231 | BOOL ftpPWD(IN PFTPCONTEXT context, IN const char *params)
1232 | {
1233 | CHAR textbuf[MAX_PATH];
1234 |
1235 | UNREFERENCED_PARAMETER(params);
1236 |
1237 | if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
1238 | return sendstring(context->ControlSocket, error530);
1239 |
1240 | sendstring(context->ControlSocket, "257 \"");
1241 | _strcpy_a(textbuf, context->CurrentDir);
1242 | nolastslash(textbuf);
1243 | unixpath(textbuf);
1244 | sendstring(context->ControlSocket, textbuf);
1245 | sendstring(context->ControlSocket, "\" is a current directory.\r\n");
1246 |
1247 | return TRUE;
1248 | }
1249 |
1250 | BOOL ftpTYPE(IN PFTPCONTEXT context, IN const char *params)
1251 | {
1252 | if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
1253 | return sendstring(context->ControlSocket, error530);
1254 |
1255 | if (params == NULL)
1256 | return sendstring(context->ControlSocket, error501);
1257 |
1258 | switch (*params)
1259 | {
1260 | case 'A':
1261 | case 'a':
1262 | return sendstring(context->ControlSocket, success200_1);
1263 | case 'I':
1264 | case 'i':
1265 | return sendstring(context->ControlSocket, success200_2);
1266 | }
1267 |
1268 | return sendstring(context->ControlSocket, error501);
1269 | }
1270 |
1271 | BOOL ftpABOR(IN PFTPCONTEXT context, IN const char *params)
1272 | {
1273 | UNREFERENCED_PARAMETER(params);
1274 |
1275 | if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
1276 | return sendstring(context->ControlSocket, error530);
1277 |
1278 | StopWorkerThread(context);
1279 | return sendstring(context->ControlSocket, success226);
1280 | }
1281 |
1282 | BOOL ftpCDUP(IN PFTPCONTEXT context, IN const char *params)
1283 | {
1284 | CHAR textbuf[MAX_PATH];
1285 |
1286 | UNREFERENCED_PARAMETER(params);
1287 |
1288 | if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
1289 | return sendstring(context->ControlSocket, error530);
1290 |
1291 | RtlSecureZeroMemory(textbuf, sizeof(textbuf));
1292 | _strcpy_a(textbuf, context->CurrentDir);
1293 | nolastslash(textbuf);
1294 | filepath(textbuf);
1295 | addlastslash(textbuf);
1296 | _strcpy_a(context->CurrentDir, textbuf);
1297 |
1298 | writelogentry(context, " CDUP", NULL);
1299 | return sendstring(context->ControlSocket, success250);
1300 | }
1301 |
1302 | BOOL ftpRNFR(IN PFTPCONTEXT context, IN const char *params)
1303 | {
1304 | WIN32_FILE_ATTRIBUTE_DATA adata;
1305 | CHAR textbuf[MAX_PATH*3];
1306 | TCHAR filename[MAX_PATH];
1307 |
1308 | if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
1309 | return sendstring(context->ControlSocket, error530);
1310 | if ( context->Access < FTP_ACCESS_FULL )
1311 | return sendstring(context->ControlSocket, error550_r);
1312 | if ( params == NULL )
1313 | return sendstring(context->ControlSocket, error501);
1314 |
1315 | RtlSecureZeroMemory(context->RenFrom, sizeof(context->RenFrom));
1316 | finalpath(context->RootDir, context->CurrentDir, (char *)params, textbuf);
1317 | nolastslash(textbuf);
1318 |
1319 | if (MultiByteToWideChar(CP_UTF8, 0, textbuf, MAX_PATH, filename, MAX_PATH) <= 0)
1320 | filename[0] = 0;
1321 |
1322 | if ( GetFileAttributesEx(filename, GetFileExInfoStandard, &adata) == 0 )
1323 | return sendstring(context->ControlSocket, error550);
1324 |
1325 | _strncpy(context->RenFrom, MAX_PATH, filename, MAX_PATH);
1326 |
1327 | writelogentry(context, " RNFR: ", (char *)params);
1328 | return sendstring(context->ControlSocket, interm350_ren);
1329 | }
1330 |
1331 | BOOL ftpRNTO(IN PFTPCONTEXT context, IN const char *params)
1332 | {
1333 | WIN32_FILE_ATTRIBUTE_DATA adata;
1334 | CHAR textbuf[MAX_PATH*3];
1335 | TCHAR filename[MAX_PATH];
1336 |
1337 | if ( context->Access == FTP_ACCESS_NOT_LOGGED_IN )
1338 | return sendstring(context->ControlSocket, error530);
1339 | if ( context->Access < FTP_ACCESS_FULL )
1340 | return sendstring(context->ControlSocket, error550_r);
1341 | if ( params == NULL )
1342 | return sendstring(context->ControlSocket, error501);
1343 |
1344 | finalpath(context->RootDir, context->CurrentDir, (char *)params, textbuf);
1345 | nolastslash(textbuf);
1346 |
1347 | if (MultiByteToWideChar(CP_UTF8, 0, textbuf, MAX_PATH, filename, MAX_PATH) <= 0)
1348 | filename[0] = 0;
1349 |
1350 | if ( GetFileAttributesEx(filename, GetFileExInfoStandard, &adata) != 0 )
1351 | return sendstring(context->ControlSocket, error550);
1352 | if ( !MoveFileEx(context->RenFrom, filename, MOVEFILE_COPY_ALLOWED) )
1353 | return sendstring(context->ControlSocket, error550);
1354 |
1355 | RtlSecureZeroMemory(context->RenFrom, sizeof(context->RenFrom));
1356 |
1357 | writelogentry(context, " RNTO: ", (char *)params);
1358 | return sendstring(context->ControlSocket, success250);
1359 | }
1360 |
1361 | BOOL ftpOPTS(IN PFTPCONTEXT context, IN const char *params)
1362 | {
1363 | UNREFERENCED_PARAMETER(params);
1364 | UNREFERENCED_PARAMETER(context);
1365 |
1366 | return sendstring(context->ControlSocket, success200);
1367 | }
1368 |
1369 | BOOL ftpMLSD(IN PFTPCONTEXT context, IN const char *params)
1370 | {
1371 | WIN32_FILE_ATTRIBUTE_DATA adata;
1372 | HANDLE wth;
1373 | CHAR textbuf[MAX_PATH * 3];
1374 |
1375 | UNREFERENCED_PARAMETER(params);
1376 |
1377 | if (context->Access == FTP_ACCESS_NOT_LOGGED_IN)
1378 | return sendstring(context->ControlSocket, error530);
1379 | if (context->WorkerThread != NULL)
1380 | return sendstring(context->ControlSocket, error550_t);
1381 |
1382 | context->FileHandle = INVALID_HANDLE_VALUE;
1383 |
1384 | RtlSecureZeroMemory(textbuf, sizeof(textbuf));
1385 | RtlSecureZeroMemory(context->TextBuffer, sizeof(context->TextBuffer));
1386 |
1387 | finalpath(context->RootDir, context->CurrentDir, NULL, textbuf);
1388 | if (MultiByteToWideChar(CP_UTF8, 0, textbuf, MAX_PATH, context->TextBuffer, MAX_PATH) <= 0)
1389 | context->TextBuffer[0] = 0;
1390 |
1391 | if (!GetFileAttributesEx(context->TextBuffer, GetFileExInfoStandard, &adata))
1392 | return sendstring(context->ControlSocket, error550);
1393 | if ((adata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
1394 | return sendstring(context->ControlSocket, error550);
1395 |
1396 | _strcat(context->TextBuffer, TEXT("*"));
1397 |
1398 | sendstring(context->ControlSocket, interm150);
1399 | writelogentry(context, " MLSD-LIST", (char *)params);
1400 | context->Stop = FALSE;
1401 |
1402 | wth = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&mlsd_thread, (LPVOID)context, 0, NULL);
1403 | if (wth == NULL)
1404 | sendstring(context->ControlSocket, error451);
1405 | else
1406 | context->WorkerThread = wth;
1407 |
1408 | return TRUE;
1409 | }
1410 |
1411 | /* FTP COMMANDS END */
1412 |
1413 | char recvchar(SOCKET s)
1414 | {
1415 | char c = 0;
1416 |
1417 | if ( recv(s, &c, 1, 0) != 1 )
1418 | return 0;
1419 |
1420 | return c;
1421 | }
1422 |
1423 | int recvcmd(SOCKET s, char *buffer, ULONG maxlen)
1424 | {
1425 | char r;
1426 | ULONG c;
1427 |
1428 | // skip first spaces or nonliteral characters, that is possible
1429 | do {
1430 | r = recvchar(s);
1431 | if ( r == 0 )
1432 | return FALSE;
1433 | } while ( !( ((r>='a') && (r<='z')) || ((r>='A') && (r<='Z')) ) );
1434 |
1435 | for (c=0; c> (c*8)) & 0xff, _strend_a(params));
1522 |
1523 | if (c < 3)
1524 | _strcat_a(params, ".");
1525 | else
1526 | _strcat_a(params, ":");
1527 | }
1528 | ultostr_a(((laddr.sin_port >> 8) & 0xff) + ((laddr.sin_port << 8) & 0xff00), _strend_a(params));
1529 |
1530 | writelogentry(&ctx, "<- New user IP=", params);
1531 |
1532 | while ( ctx.ControlSocket != INVALID_SOCKET ) {
1533 | RtlSecureZeroMemory(cmd, sizeof(cmd));
1534 | c = recvcmd(ctx.ControlSocket, cmd, 7);
1535 | if ( c == 0 )
1536 | break;
1537 |
1538 | if ( c == 1 )
1539 | p = NULL;
1540 | else {
1541 | p = params;
1542 | RtlSecureZeroMemory(params, sizeof(params));
1543 | if ( !recvparams(ctx.ControlSocket, params, MAX_PATH*2) )
1544 | break;
1545 | }
1546 |
1547 | cmdindex = -1;
1548 | for (i=0; i
2 | #include
3 | #include "minirtl\minirtl.h"
4 | #include "minirtl\cmdline.h"
5 | #include "ftpserv.h"
6 |
7 | #if !defined UNICODE
8 | #error only UNICODE build is supported
9 | #endif
10 |
11 | DWORD dwMainThreadId = 0;
12 | HANDLE g_Thread = NULL;
13 |
14 | BOOL WINAPI ConHandler(
15 | _In_ DWORD dwCtrlType
16 | )
17 | {
18 | switch (dwCtrlType) {
19 | case CTRL_C_EVENT:
20 | case CTRL_BREAK_EVENT:
21 | case CTRL_CLOSE_EVENT:
22 | case CTRL_LOGOFF_EVENT:
23 | case CTRL_SHUTDOWN_EVENT:
24 |
25 | closesocket(g_cfg.ListeningSocket);
26 | g_cfg.ListeningSocket = INVALID_SOCKET;
27 | if (WaitForSingleObjectEx(g_Thread, 4000, FALSE) == STATUS_TIMEOUT)
28 | TerminateThread(g_Thread, 0);
29 |
30 | PostThreadMessage(dwMainThreadId, WM_QUIT, 0, 0);
31 | return TRUE;
32 | }
33 |
34 | return FALSE;
35 | }
36 |
37 | int ParseConfig(const char *pcfg, const char *section_name, const char *key_name, char *value, unsigned long value_size_max)
38 | {
39 | unsigned long p = 0, sp;
40 | char vname[256];
41 |
42 | if (value_size_max == 0)
43 | return 0;
44 | --value_size_max;
45 |
46 | while (pcfg[p] != 0)
47 | {
48 | while ((pcfg[p] != '[') && (pcfg[p] != 0)) // skip all characters before first '['
49 | ++p;
50 |
51 | if (pcfg[p] == 0) // we got eof so quit
52 | break;
53 |
54 | if ((pcfg[p] == '\r') || (pcfg[p] == '\n')) // newline - start over again
55 | continue;
56 |
57 | ++p; // skip '[' that we found
58 |
59 | sp = 0;
60 | while ((pcfg[p] != ']') && (pcfg[p] != 0) && (pcfg[p] != '\r') && (pcfg[p] != '\n') && (sp < 255))
61 | {
62 | vname[sp] = pcfg[p];
63 | ++sp;
64 | ++p;
65 | }
66 | vname[sp] = 0;
67 |
68 | if (pcfg[p] == 0)
69 | break;
70 |
71 | if ((pcfg[p] == '\r') || (pcfg[p] == '\n')) // newline - start over again
72 | continue;
73 |
74 | ++p; // skip ']' that we found
75 |
76 | if (strcmp(vname, section_name) == 0)
77 | {
78 | do {
79 | while ((pcfg[p] == ' ') || (pcfg[p] == '\r') || (pcfg[p] == '\n'))
80 | ++p;
81 |
82 | if ((pcfg[p] == 0) || (pcfg[p] == '['))
83 | break;
84 |
85 | sp = 0;
86 | while ((pcfg[p] != '=') && (pcfg[p] != 0) && (pcfg[p] != '\r') && (pcfg[p] != '\n') && (sp < 255))
87 | {
88 | vname[sp] = pcfg[p];
89 | ++sp;
90 | ++p;
91 | }
92 | vname[sp] = 0;
93 |
94 | if (pcfg[p] == 0)
95 | break;
96 | ++p;
97 |
98 | if (strcmp(vname, key_name) == 0)
99 | {
100 | sp = 0;
101 | while ((pcfg[p] != '\r') && (pcfg[p] != '\n') && (pcfg[p] != 0))
102 | {
103 | if (sp < value_size_max)
104 | value[sp] = pcfg[p];
105 | else
106 | return 0;
107 | ++sp;
108 | ++p;
109 | }
110 | value[sp] = 0;
111 | return 1;
112 | }
113 | else
114 | {
115 | while ((pcfg[p] != '\r') && (pcfg[p] != '\n') && (pcfg[p] != 0))
116 | ++p;
117 | }
118 |
119 | } while (pcfg[p] != 0);
120 | }
121 | else
122 | {
123 | // parse and skip all
124 | do {
125 | while ((pcfg[p] == ' ') || (pcfg[p] == '\r') || (pcfg[p] == '\n'))
126 | ++p;
127 |
128 | if ((pcfg[p] == 0) || (pcfg[p] == '['))
129 | break;
130 |
131 | while ((pcfg[p] != '=') && (pcfg[p] != 0) && (pcfg[p] != '\r') && (pcfg[p] != '\n'))
132 | ++p;
133 |
134 | if (pcfg[p] == 0)
135 | break;
136 | ++p;
137 |
138 | while ((pcfg[p] != '\r') && (pcfg[p] != '\n') && (pcfg[p] != 0))
139 | ++p;
140 |
141 | } while (pcfg[p] != 0);
142 | }
143 | }
144 |
145 | return 0;
146 | }
147 |
148 | char *InitConfig(LPTSTR cfg_filename)
149 | {
150 | BOOL cond = FALSE;
151 | ULONG iobytes;
152 | HANDLE f = INVALID_HANDLE_VALUE;
153 | char *buffer = NULL;
154 | LARGE_INTEGER fsz;
155 |
156 | f = CreateFile(cfg_filename, GENERIC_READ | SYNCHRONIZE,
157 | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
158 |
159 | do {
160 |
161 | if (f == INVALID_HANDLE_VALUE)
162 | break;
163 |
164 | fsz.QuadPart = 0;
165 | if (!GetFileSizeEx(f, &fsz))
166 | break;
167 |
168 | fsz.LowPart += 1;
169 | buffer = (char *)VirtualAlloc(NULL, fsz.LowPart, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
170 | if (buffer == NULL)
171 | break;
172 |
173 | if (!ReadFile(f, buffer, fsz.LowPart - 1, &iobytes, NULL))
174 | break;
175 |
176 | buffer[fsz.LowPart - 1] = 0;
177 |
178 | } while (cond);
179 |
180 | if (f != INVALID_HANDLE_VALUE )
181 | CloseHandle(f);
182 |
183 | return buffer;
184 | }
185 |
186 | void main()
187 | {
188 | WSADATA wsdat1;
189 | int wsaerr;
190 | MSG msg1;
191 | BOOL rv;
192 | ULARGE_INTEGER UT;
193 | FILETIME t;
194 | char *cfg = NULL, textbuf[MAX_PATH];
195 | TCHAR ConfigFilePath[MAX_PATH];
196 |
197 | __security_init_cookie();
198 |
199 | dwMainThreadId = GetCurrentThreadId();
200 | SetConsoleCtrlHandler(&ConHandler, TRUE);
201 |
202 | RtlSecureZeroMemory(&wsdat1, sizeof(wsdat1));
203 | wsaerr = WSAStartup(0x0001, &wsdat1);
204 |
205 | while (wsaerr == 0)
206 | {
207 | RtlSecureZeroMemory(ConfigFilePath, sizeof(ConfigFilePath));
208 | GetCommandLineParam(GetCommandLine(), 1, ConfigFilePath, MAX_PATH, NULL);
209 | if (ConfigFilePath[0] == 0) {
210 | GetModuleFileName(NULL, ConfigFilePath, MAX_PATH);
211 | ExtractFilePath(ConfigFilePath, ConfigFilePath);
212 | _strcat(ConfigFilePath, CONFIG_FILE_NAME);
213 | }
214 |
215 | cfg = InitConfig(ConfigFilePath);
216 | if (cfg == NULL)
217 | {
218 | writeconsolestr("Could not find configuration file\r\n\r\n Usage: fftp [CONFIGFILE]\r\n\r\n");
219 | break;
220 | }
221 |
222 | g_cfg.ConfigFile = cfg;
223 |
224 | g_cfg.BindToInterface = inet_addr("127.0.0.1");
225 | if (ParseConfig(cfg, CONFIG_SECTION_NAME, "interface", textbuf, MAX_PATH))
226 | g_cfg.BindToInterface = inet_addr(textbuf);
227 |
228 | g_cfg.ExternalInterface = inet_addr("0.0.0.0");
229 | if (ParseConfig(cfg, CONFIG_SECTION_NAME, "external_ip", textbuf, MAX_PATH))
230 | g_cfg.ExternalInterface = inet_addr(textbuf);
231 |
232 | g_cfg.LocalIPMask = inet_addr("255.255.255.0");
233 | if (ParseConfig(cfg, CONFIG_SECTION_NAME, "local_mask", textbuf, MAX_PATH))
234 | g_cfg.LocalIPMask = inet_addr(textbuf);
235 |
236 | g_cfg.Port = DEFAULT_FTP_PORT;
237 | if (ParseConfig(cfg, CONFIG_SECTION_NAME, "port", textbuf, MAX_PATH))
238 | g_cfg.Port = strtoul_a(textbuf);
239 |
240 | g_cfg.MaxUsers = 1;
241 | if (ParseConfig(cfg, CONFIG_SECTION_NAME, "maxusers", textbuf, MAX_PATH))
242 | g_cfg.MaxUsers = strtoul_a(textbuf);
243 |
244 | g_cfg.PasvPortBase = 100;
245 | if (ParseConfig(cfg, CONFIG_SECTION_NAME, "minport", textbuf, MAX_PATH))
246 | g_cfg.PasvPortBase = strtoul_a(textbuf);
247 |
248 | g_cfg.PasvPortMax = 65535;
249 | if (ParseConfig(cfg, CONFIG_SECTION_NAME, "maxport", textbuf, MAX_PATH))
250 | g_cfg.PasvPortMax = strtoul_a(textbuf);
251 |
252 | g_LogHandle = INVALID_HANDLE_VALUE;
253 | RtlSecureZeroMemory(&textbuf, sizeof(textbuf));
254 | if (ParseConfig(cfg, CONFIG_SECTION_NAME, "logfilepath", textbuf, MAX_PATH))
255 | {
256 | GetSystemTimeAsFileTime(&t);
257 | UT.LowPart = t.dwLowDateTime;
258 | UT.HighPart = t.dwHighDateTime;
259 | _strcat_a(textbuf, "\\ftplog-");
260 | u64tostr_a(UT.QuadPart, _strend_a(textbuf));
261 | _strcat_a(textbuf, ".txt");
262 | g_LogHandle = CreateFileA(textbuf, GENERIC_WRITE | SYNCHRONIZE,
263 | FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
264 | }
265 | else
266 | {
267 | writeconsolestr("Error: logfilepath section is not found in configuration\r\n");
268 | break;
269 | }
270 |
271 | if (g_LogHandle == INVALID_HANDLE_VALUE)
272 | {
273 | writeconsolestr("Error: Failed to open / create log file. Please check logfilepath\r\n");
274 | break;
275 | }
276 |
277 | writeconsolestr("Log file : ");
278 | writeconsolestr(textbuf);
279 | writeconsolestr(CRLF);
280 |
281 | writeconsolestr("Config file : ");
282 | WideCharToMultiByte(CP_UTF8, 0, ConfigFilePath, MAX_PATH, textbuf, MAX_PATH, NULL, NULL);
283 | writeconsolestr(textbuf);
284 | writeconsolestr(CRLF);
285 |
286 | writeconsolestr("Interface : ");
287 | ultostr_a(g_cfg.BindToInterface & 0xff, textbuf);
288 | _strcat_a(textbuf, ".");
289 | ultostr_a((g_cfg.BindToInterface >> 8) & 0xff, _strend_a(textbuf));
290 | _strcat_a(textbuf, ".");
291 | ultostr_a((g_cfg.BindToInterface >> 16) & 0xff, _strend_a(textbuf));
292 | _strcat_a(textbuf, ".");
293 | ultostr_a((g_cfg.BindToInterface >> 24) & 0xff, _strend_a(textbuf));
294 | _strcat_a(textbuf, CRLF);
295 | writeconsolestr(textbuf);
296 |
297 | writeconsolestr("Port : ");
298 | ultostr_a(g_cfg.Port, textbuf);
299 | _strcat_a(textbuf, CRLF);
300 | writeconsolestr(textbuf);
301 |
302 | g_Thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ftpmain, NULL, 0, NULL);
303 | if (g_Thread == NULL)
304 | {
305 | writeconsolestr("Error: Failed to create main server thread\r\n");
306 | break;
307 | }
308 |
309 | /* common message loop for Windows application */
310 | do {
311 | rv = GetMessage(&msg1, NULL, 0, 0);
312 |
313 | if (rv == -1)
314 | break;
315 |
316 | TranslateMessage(&msg1);
317 | DispatchMessage(&msg1);
318 | } while (rv != 0);
319 |
320 | CloseHandle(g_Thread);
321 |
322 | if ((g_LogHandle != NULL) && (g_LogHandle != INVALID_HANDLE_VALUE))
323 | CloseHandle(g_LogHandle);
324 |
325 | OutputDebugString(TEXT("\r\nNormal exit\r\n"));
326 | break;
327 | }
328 |
329 | WSACleanup();
330 | ExitProcess(1);
331 | }
332 |
--------------------------------------------------------------------------------
/Source/fftp/minirtl/_strcat.c:
--------------------------------------------------------------------------------
1 | #include "rtltypes.h"
2 |
3 | char *_strcat_a(char *dest, const char *src)
4 | {
5 | if ( (dest==0) || (src==0) )
6 | return dest;
7 |
8 | while ( *dest!=0 )
9 | dest++;
10 |
11 | while ( *src!=0 ) {
12 | *dest = *src;
13 | dest++;
14 | src++;
15 | }
16 |
17 | *dest = 0;
18 | return dest;
19 | }
20 |
21 | wchar_t *_strcat_w(wchar_t *dest, const wchar_t *src)
22 | {
23 | if ( (dest==0) || (src==0) )
24 | return dest;
25 |
26 | while ( *dest!=0 )
27 | dest++;
28 |
29 | while ( *src!=0 ) {
30 | *dest = *src;
31 | dest++;
32 | src++;
33 | }
34 |
35 | *dest = 0;
36 | return dest;
37 | }
38 |
--------------------------------------------------------------------------------
/Source/fftp/minirtl/_strcmp.c:
--------------------------------------------------------------------------------
1 | #include "rtltypes.h"
2 |
3 | int _strcmp_a(const char *s1, const char *s2)
4 | {
5 | char c1, c2;
6 |
7 | if ( s1==s2 )
8 | return 0;
9 |
10 | if ( s1==0 )
11 | return -1;
12 |
13 | if ( s2==0 )
14 | return 1;
15 |
16 | do {
17 | c1 = *s1;
18 | c2 = *s2;
19 | s1++;
20 | s2++;
21 | } while ( (c1 != 0) && (c1 == c2) );
22 |
23 | return (int)(c1 - c2);
24 | }
25 |
26 | int _strcmp_w(const wchar_t *s1, const wchar_t *s2)
27 | {
28 | wchar_t c1, c2;
29 |
30 | if ( s1==s2 )
31 | return 0;
32 |
33 | if ( s1==0 )
34 | return -1;
35 |
36 | if ( s2==0 )
37 | return 1;
38 |
39 | do {
40 | c1 = *s1;
41 | c2 = *s2;
42 | s1++;
43 | s2++;
44 | } while ( (c1 != 0) && (c1 == c2) );
45 |
46 | return (int)(c1 - c2);
47 | }
48 |
--------------------------------------------------------------------------------
/Source/fftp/minirtl/_strcmpi.c:
--------------------------------------------------------------------------------
1 | #include "rtltypes.h"
2 |
3 | int _strcmpi_a(const char *s1, const char *s2)
4 | {
5 | char c1, c2;
6 |
7 | if ( s1==s2 )
8 | return 0;
9 |
10 | if ( s1==0 )
11 | return -1;
12 |
13 | if ( s2==0 )
14 | return 1;
15 |
16 | do {
17 | c1 = locase_a(*s1);
18 | c2 = locase_a(*s2);
19 | s1++;
20 | s2++;
21 | } while ( (c1 != 0) && (c1 == c2) );
22 |
23 | return (int)(c1 - c2);
24 | }
25 |
26 | int _strcmpi_w(const wchar_t *s1, const wchar_t *s2)
27 | {
28 | wchar_t c1, c2;
29 |
30 | if ( s1==s2 )
31 | return 0;
32 |
33 | if ( s1==0 )
34 | return -1;
35 |
36 | if ( s2==0 )
37 | return 1;
38 |
39 | do {
40 | c1 = locase_w(*s1);
41 | c2 = locase_w(*s2);
42 | s1++;
43 | s2++;
44 | } while ( (c1 != 0) && (c1 == c2) );
45 |
46 | return (int)(c1 - c2);
47 | }
48 |
--------------------------------------------------------------------------------
/Source/fftp/minirtl/_strcpy.c:
--------------------------------------------------------------------------------
1 | #include "rtltypes.h"
2 |
3 | char *_strcpy_a(char *dest, const char *src)
4 | {
5 | char *p;
6 |
7 | if ( (dest==0) || (src==0) )
8 | return dest;
9 |
10 | if (dest == src)
11 | return dest;
12 |
13 | p = dest;
14 | while ( *src!=0 ) {
15 | *p = *src;
16 | p++;
17 | src++;
18 | }
19 |
20 | *p = 0;
21 | return dest;
22 | }
23 |
24 | wchar_t *_strcpy_w(wchar_t *dest, const wchar_t *src)
25 | {
26 | wchar_t *p;
27 |
28 | if ((dest == 0) || (src == 0))
29 | return dest;
30 |
31 | if (dest == src)
32 | return dest;
33 |
34 | p = dest;
35 | while ( *src!=0 ) {
36 | *p = *src;
37 | p++;
38 | src++;
39 | }
40 |
41 | *p = 0;
42 | return dest;
43 | }
44 |
--------------------------------------------------------------------------------
/Source/fftp/minirtl/_strend.c:
--------------------------------------------------------------------------------
1 | #include "rtltypes.h"
2 |
3 | char *_strend_a(const char *s)
4 | {
5 | if ( s==0 )
6 | return 0;
7 |
8 | while ( *s!=0 )
9 | s++;
10 |
11 | return (char *)s;
12 | }
13 |
14 | wchar_t *_strend_w(const wchar_t *s)
15 | {
16 | if ( s==0 )
17 | return 0;
18 |
19 | while ( *s!=0 )
20 | s++;
21 |
22 | return (wchar_t *)s;
23 | }
24 |
--------------------------------------------------------------------------------
/Source/fftp/minirtl/_strlen.c:
--------------------------------------------------------------------------------
1 | #include "rtltypes.h"
2 |
3 | size_t _strlen_a(const char *s)
4 | {
5 | char *s0 = (char *)s;
6 |
7 | if ( s==0 )
8 | return 0;
9 |
10 | while ( *s!=0 )
11 | s++;
12 |
13 | return (s-s0);
14 | }
15 |
16 | size_t _strlen_w(const wchar_t *s)
17 | {
18 | wchar_t *s0 = (wchar_t *)s;
19 |
20 | if ( s==0 )
21 | return 0;
22 |
23 | while ( *s!=0 )
24 | s++;
25 |
26 | return (s-s0);
27 | }
28 |
--------------------------------------------------------------------------------
/Source/fftp/minirtl/_strncmpi.c:
--------------------------------------------------------------------------------
1 | #include "rtltypes.h"
2 |
3 | int _strncmpi_a(const char *s1, const char *s2, size_t cchars)
4 | {
5 | char c1, c2;
6 |
7 | if ( s1==s2 )
8 | return 0;
9 |
10 | if ( s1==0 )
11 | return -1;
12 |
13 | if ( s2==0 )
14 | return 1;
15 |
16 | if ( cchars==0 )
17 | return 0;
18 |
19 | do {
20 | c1 = locase_a(*s1);
21 | c2 = locase_a(*s2);
22 | s1++;
23 | s2++;
24 | cchars--;
25 | } while ( (c1 != 0) && (c1 == c2) && (cchars>0) );
26 |
27 | return (int)(c1 - c2);
28 | }
29 |
30 | int _strncmpi_w(const wchar_t *s1, const wchar_t *s2, size_t cchars)
31 | {
32 | wchar_t c1, c2;
33 |
34 | if ( s1==s2 )
35 | return 0;
36 |
37 | if ( s1==0 )
38 | return -1;
39 |
40 | if ( s2==0 )
41 | return 1;
42 |
43 | if ( cchars==0 )
44 | return 0;
45 |
46 | do {
47 | c1 = locase_w(*s1);
48 | c2 = locase_w(*s2);
49 | s1++;
50 | s2++;
51 | cchars--;
52 | } while ( (c1 != 0) && (c1 == c2) && (cchars>0) );
53 |
54 | return (int)(c1 - c2);
55 | }
56 |
--------------------------------------------------------------------------------
/Source/fftp/minirtl/_strncpy.c:
--------------------------------------------------------------------------------
1 | #include "rtltypes.h"
2 |
3 | char *_strncpy_a(char *dest, size_t ccdest, const char *src, size_t ccsrc)
4 | {
5 | char *p;
6 |
7 | if ( (dest==0) || (src==0) || (ccdest==0) )
8 | return dest;
9 |
10 | ccdest--;
11 | p = dest;
12 |
13 | while ( (*src!=0) && (ccdest>0) && (ccsrc>0) ) {
14 | *p = *src;
15 | p++;
16 | src++;
17 | ccdest--;
18 | ccsrc--;
19 | }
20 |
21 | *p = 0;
22 | return dest;
23 | }
24 |
25 | wchar_t *_strncpy_w(wchar_t *dest, size_t ccdest, const wchar_t *src, size_t ccsrc)
26 | {
27 | wchar_t *p;
28 |
29 | if ( (dest==0) || (src==0) || (ccdest==0) )
30 | return dest;
31 |
32 | ccdest--;
33 | p = dest;
34 |
35 | while ( (*src!=0) && (ccdest>0) && (ccsrc>0) ) {
36 | *p = *src;
37 | p++;
38 | src++;
39 | ccdest--;
40 | ccsrc--;
41 | }
42 |
43 | *p = 0;
44 | return dest;
45 | }
46 |
--------------------------------------------------------------------------------
/Source/fftp/minirtl/cmdline.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | BOOL GetCommandLineParamW(
4 | IN LPCWSTR CmdLine,
5 | IN ULONG ParamIndex,
6 | OUT LPWSTR Buffer,
7 | IN ULONG BufferSize,
8 | OUT PULONG ParamLen
9 | )
10 | {
11 | ULONG c, plen = 0;
12 | TCHAR divider;
13 |
14 | if (ParamLen != NULL)
15 | *ParamLen = 0;
16 |
17 | if (CmdLine == NULL) {
18 | if ((Buffer != NULL) && (BufferSize > 0))
19 | *Buffer = 0;
20 | return FALSE;
21 | }
22 |
23 | for (c = 0; c <= ParamIndex; c++) {
24 | plen = 0;
25 |
26 | while (*CmdLine == ' ')
27 | CmdLine++;
28 |
29 | switch (*CmdLine) {
30 | case 0:
31 | goto zero_term_exit;
32 |
33 | case '"':
34 | CmdLine++;
35 | divider = '"';
36 | break;
37 |
38 | default:
39 | divider = ' ';
40 | }
41 |
42 | while ((*CmdLine != '"') && (*CmdLine != divider) && (*CmdLine != 0)) {
43 | plen++;
44 | if (c == ParamIndex)
45 | if ((plen < BufferSize) && (Buffer != NULL)) {
46 | *Buffer = *CmdLine;
47 | Buffer++;
48 | }
49 | CmdLine++;
50 | }
51 |
52 | if (*CmdLine != 0)
53 | CmdLine++;
54 | }
55 |
56 | zero_term_exit:
57 |
58 | if ((Buffer != NULL) && (BufferSize > 0))
59 | *Buffer = 0;
60 |
61 | if (ParamLen != NULL)
62 | *ParamLen = plen;
63 |
64 | if (plen < BufferSize)
65 | return TRUE;
66 | else
67 | return FALSE;
68 | }
69 |
70 | BOOL GetCommandLineParamA(
71 | IN LPCSTR CmdLine,
72 | IN ULONG ParamIndex,
73 | OUT LPSTR Buffer,
74 | IN ULONG BufferSize,
75 | OUT PULONG ParamLen
76 | )
77 | {
78 | ULONG c, plen = 0;
79 | TCHAR divider;
80 |
81 | if (CmdLine == NULL)
82 | return FALSE;
83 |
84 | if (ParamLen != NULL)
85 | *ParamLen = 0;
86 |
87 | for (c = 0; c <= ParamIndex; c++) {
88 | plen = 0;
89 |
90 | while (*CmdLine == ' ')
91 | CmdLine++;
92 |
93 | switch (*CmdLine) {
94 | case 0:
95 | goto zero_term_exit;
96 |
97 | case '"':
98 | CmdLine++;
99 | divider = '"';
100 | break;
101 |
102 | default:
103 | divider = ' ';
104 | }
105 |
106 | while ((*CmdLine != '"') && (*CmdLine != divider) && (*CmdLine != 0)) {
107 | plen++;
108 | if (c == ParamIndex)
109 | if ((plen < BufferSize) && (Buffer != NULL)) {
110 | *Buffer = *CmdLine;
111 | Buffer++;
112 | }
113 | CmdLine++;
114 | }
115 |
116 | if (*CmdLine != 0)
117 | CmdLine++;
118 | }
119 |
120 | zero_term_exit:
121 |
122 | if ((Buffer != NULL) && (BufferSize > 0))
123 | *Buffer = 0;
124 |
125 | if (ParamLen != NULL)
126 | *ParamLen = plen;
127 |
128 | if (plen < BufferSize)
129 | return TRUE;
130 | else
131 | return FALSE;
132 | }
133 |
134 | char *ExtractFilePathA(const char *FileName, char *FilePath)
135 | {
136 | char *p = (char *)FileName, *p0 = (char *)FileName;
137 |
138 | if ((FileName == 0) || (FilePath == 0))
139 | return 0;
140 |
141 | while (*FileName != 0) {
142 | if (*FileName == '\\')
143 | p = (char *)FileName + 1;
144 | FileName++;
145 | }
146 |
147 | while (p0 < p) {
148 | *FilePath = *p0;
149 | FilePath++;
150 | p0++;
151 | }
152 |
153 | *FilePath = 0;
154 |
155 | return FilePath;
156 | }
157 |
158 | wchar_t *ExtractFilePathW(const wchar_t *FileName, wchar_t *FilePath)
159 | {
160 | wchar_t *p = (wchar_t *)FileName, *p0 = (wchar_t *)FileName;
161 |
162 | if ((FileName == 0) || (FilePath == 0))
163 | return 0;
164 |
165 | while (*FileName != 0) {
166 | if (*FileName == '\\')
167 | p = (wchar_t *)FileName + 1;
168 | FileName++;
169 | }
170 |
171 | while (p0 < p) {
172 | *FilePath = *p0;
173 | FilePath++;
174 | p0++;
175 | }
176 |
177 | *FilePath = 0;
178 |
179 | return FilePath;
180 | }
181 |
--------------------------------------------------------------------------------
/Source/fftp/minirtl/cmdline.h:
--------------------------------------------------------------------------------
1 | #ifndef _CMDLINEH_
2 | #define _CMDLINEH_
3 |
4 | BOOL GetCommandLineParamW(
5 | IN LPCWSTR CmdLine,
6 | IN ULONG ParamIndex,
7 | OUT LPWSTR Buffer,
8 | IN ULONG BufferSize,
9 | OUT PULONG ParamLen
10 | );
11 |
12 | BOOL GetCommandLineParamA(
13 | IN LPCSTR CmdLine,
14 | IN ULONG ParamIndex,
15 | OUT LPSTR Buffer,
16 | IN ULONG BufferSize,
17 | OUT PULONG ParamLen
18 | );
19 |
20 | char *ExtractFilePathA(const char *FileName, char *FilePath);
21 | wchar_t *ExtractFilePathW(const wchar_t *FileName, wchar_t *FilePath);
22 |
23 | #ifdef UNICODE
24 |
25 | #define ExtractFilePath ExtractFilePathW
26 | #define GetCommandLineParam GetCommandLineParamW
27 |
28 | #else // ANSI
29 |
30 | #define ExtractFilePath ExtractFilePathA
31 | #define GetCommandLineParam GetCommandLineParamA
32 |
33 | #endif
34 |
35 | #endif /* _CMDLINEH_ */
36 |
--------------------------------------------------------------------------------
/Source/fftp/minirtl/minirtl.h:
--------------------------------------------------------------------------------
1 | /*
2 | Module name:
3 | minirtl.h
4 |
5 | Description:
6 | header for string handling and conversion routines
7 |
8 | Date:
9 | 1 Mar 2015
10 | */
11 |
12 | #ifndef _MINIRTL_
13 | #define _MINIRTL_
14 |
15 | // string copy/concat/length
16 |
17 | char *_strend_a(const char *s);
18 | wchar_t *_strend_w(const wchar_t *s);
19 |
20 | char *_strcpy_a(char *dest, const char *src);
21 | wchar_t *_strcpy_w(wchar_t *dest, const wchar_t *src);
22 |
23 | char *_strcat_a(char *dest, const char *src);
24 | wchar_t *_strcat_w(wchar_t *dest, const wchar_t *src);
25 |
26 | char *_strncpy_a(char *dest, size_t ccdest, const char *src, size_t ccsrc);
27 | wchar_t *_strncpy_w(wchar_t *dest, size_t ccdest, const wchar_t *src, size_t ccsrc);
28 |
29 | size_t _strlen_a(const char *s);
30 | size_t _strlen_w(const wchar_t *s);
31 |
32 | // comparing
33 |
34 | int _strcmp_a(const char *s1, const char *s2);
35 | int _strcmp_w(const wchar_t *s1, const wchar_t *s2);
36 |
37 | int _strncmp_a(const char *s1, const char *s2, size_t cchars);
38 | int _strncmp_w(const wchar_t *s1, const wchar_t *s2, size_t cchars);
39 |
40 | int _strcmpi_a(const char *s1, const char *s2);
41 | int _strcmpi_w(const wchar_t *s1, const wchar_t *s2);
42 |
43 | int _strncmpi_a(const char *s1, const char *s2, size_t cchars);
44 | int _strncmpi_w(const wchar_t *s1, const wchar_t *s2, size_t cchars);
45 |
46 | char *_strstr_a(const char *s, const char *sub_s);
47 | wchar_t *_strstr_w(const wchar_t *s, const wchar_t *sub_s);
48 |
49 | char *_strstri_a(const char *s, const char *sub_s);
50 | wchar_t *_strstri_w(const wchar_t *s, const wchar_t *sub_s);
51 |
52 | // conversion of integer types to string, returning string length
53 |
54 | size_t ultostr_a(unsigned long x, char *s);
55 | size_t ultostr_w(unsigned long x, wchar_t *s);
56 |
57 | size_t ultohex_a(unsigned long x, char *s);
58 | size_t ultohex_w(unsigned long x, wchar_t *s);
59 |
60 | size_t itostr_a(int x, char *s);
61 | size_t itostr_w(int x, wchar_t *s);
62 |
63 | size_t i64tostr_a(signed long long x, char *s);
64 | size_t i64tostr_w(signed long long x, wchar_t *s);
65 |
66 | size_t u64tostr_a(unsigned long long x, char *s);
67 | size_t u64tostr_w(unsigned long long x, wchar_t *s);
68 |
69 | size_t u64tohex_a(unsigned long long x, char *s);
70 | size_t u64tohex_w(unsigned long long x, wchar_t *s);
71 |
72 | // string to integers conversion
73 |
74 | unsigned long strtoul_a(char *s);
75 | unsigned long strtoul_w(wchar_t *s);
76 |
77 | unsigned long long strtou64_a(char *s);
78 | unsigned long long strtou64_w(wchar_t *s);
79 |
80 | unsigned long hextoul_a(char *s);
81 | unsigned long hextoul_w(wchar_t *s);
82 |
83 | int strtoi_a(char *s);
84 | int strtoi_w(wchar_t *s);
85 |
86 | signed long long strtoi64_a(char *s);
87 | signed long long strtoi64_w(wchar_t *s);
88 |
89 | unsigned long long hextou64_a(char *s);
90 | unsigned long long hextou64_w(wchar_t *s);
91 |
92 | /* =================================== */
93 |
94 | #ifdef UNICODE
95 |
96 | #define _strend _strend_w
97 | #define _strcpy _strcpy_w
98 | #define _strcat _strcat_w
99 | #define _strlen _strlen_w
100 | #define _strncpy _strncpy_w
101 |
102 | #define _strcmp _strcmp_w
103 | #define _strncmp _strncmp_w
104 | #define _strcmpi _strcmpi_w
105 | #define _strncmpi _strncmpi_w
106 | #define _strstr _strstr_w
107 | #define _strstri _strstri_w
108 |
109 | #define ultostr ultostr_w
110 | #define ultohex ultohex_w
111 | #define itostr itostr_w
112 | #define i64tostr i64tostr_w
113 | #define u64tostr u64tostr_w
114 | #define u64tohex u64tohex_w
115 |
116 | #define strtoul strtoul_w
117 | #define hextoul hextoul_w
118 | #define strtoi strtoi_w
119 | #define strtoi64 strtoi64_w
120 | #define strtou64 strtou64_w
121 | #define hextou64 hextou64_w
122 |
123 | #else // ANSI
124 |
125 | #define _strend _strend_a
126 | #define _strcpy _strcpy_a
127 | #define _strcat _strcat_a
128 | #define _strlen _strlen_a
129 | #define _strncpy _strncpy_a
130 | #define _strcmp _strcmp_a
131 |
132 | #define _strcmp _strcmp_a
133 | #define _strncmp _strncmp_a
134 | #define _strcmpi _strcmpi_a
135 | #define _strncmpi _strncmpi_a
136 | #define _strstr _strstr_a
137 | #define _strstri _strstri_a
138 |
139 | #define ultostr ultostr_a
140 | #define ultohex ultohex_a
141 | #define itostr itostr_a
142 | #define i64tostr i64tostr_a
143 | #define u64tostr u64tostr_a
144 | #define u64tohex u64tohex_a
145 |
146 | #define strtoul strtoul_a
147 | #define hextoul hextoul_a
148 | #define strtoi strtoi_a
149 | #define strtoi64 strtoi64_a
150 | #define strtou64 strtou64_a
151 | #define hextou64 hextou64_a
152 |
153 | #endif
154 |
155 | #endif /* _MINIRTL_ */
156 |
--------------------------------------------------------------------------------
/Source/fftp/minirtl/rtltypes.h:
--------------------------------------------------------------------------------
1 | #ifndef _WCHAR_T_DEFINED
2 | typedef unsigned short wchar_t;
3 | #define _WCHAR_T_DEFINED
4 | #endif /* _WCHAR_T_DEFINED */
5 |
6 | #ifndef _SIZE_T_DEFINED
7 | #ifdef _WIN64
8 | typedef unsigned __int64 size_t;
9 | #else /* _WIN64 */
10 | typedef __w64 unsigned int size_t;
11 | #endif /* _WIN64 */
12 | #define _SIZE_T_DEFINED
13 | #endif /* _SIZE_T_DEFINED */
14 |
15 | __forceinline char locase_a(char c)
16 | {
17 | if ((c >= 'A') && (c <= 'Z'))
18 | return c + 0x20;
19 | else
20 | return c;
21 | }
22 |
23 | __forceinline wchar_t locase_w(wchar_t c)
24 | {
25 | if ((c >= 'A') && (c <= 'Z'))
26 | return c + 0x20;
27 | else
28 | return c;
29 | }
30 |
31 | __forceinline char byteabs(char x) {
32 | if (x < 0)
33 | return -x;
34 | return x;
35 | }
36 |
37 | __forceinline int _isdigit_a(char x) {
38 | return ((x >= '0') && (x <= '9'));
39 | }
40 |
41 | __forceinline int _isdigit_w(wchar_t x) {
42 | return ((x >= L'0') && (x <= L'9'));
43 | }
44 |
--------------------------------------------------------------------------------
/Source/fftp/minirtl/strtou64.c:
--------------------------------------------------------------------------------
1 | #include "rtltypes.h"
2 |
3 | unsigned long long strtou64_a(char *s)
4 | {
5 | unsigned long long a = 0;
6 | char c;
7 |
8 | if (s == 0)
9 | return 0;
10 |
11 | while (*s != 0) {
12 | c = *s;
13 | if (_isdigit_w(c))
14 | a = (a*10)+(c-'0');
15 | else
16 | break;
17 | s++;
18 | }
19 | return a;
20 | }
21 |
22 | unsigned long long strtou64_w(wchar_t *s)
23 | {
24 | unsigned long long a = 0;
25 | wchar_t c;
26 |
27 | if (s == 0)
28 | return 0;
29 |
30 | while (*s != 0) {
31 | c = *s;
32 | if (_isdigit_w(c))
33 | a = (a*10)+(c-L'0');
34 | else
35 | break;
36 | s++;
37 | }
38 | return a;
39 | }
40 |
--------------------------------------------------------------------------------
/Source/fftp/minirtl/strtoul.c:
--------------------------------------------------------------------------------
1 | #include "rtltypes.h"
2 |
3 | unsigned long strtoul_a(char *s)
4 | {
5 | unsigned long a = 0;
6 | char c;
7 |
8 | if (s == 0)
9 | return 0;
10 |
11 | while (*s != 0) {
12 | c = *s;
13 | if (_isdigit_a(c))
14 | a = (a*10)+(c-'0');
15 | else
16 | break;
17 | s++;
18 | }
19 | return a;
20 | }
21 |
22 | unsigned long strtoul_w(wchar_t *s)
23 | {
24 | unsigned long a = 0;
25 | wchar_t c;
26 |
27 | if (s == 0)
28 | return 0;
29 |
30 | while (*s != 0) {
31 | c = *s;
32 | if (_isdigit_w(c))
33 | a = (a*10)+(c-L'0');
34 | else
35 | break;
36 | s++;
37 | }
38 | return a;
39 | }
40 |
--------------------------------------------------------------------------------
/Source/fftp/minirtl/u64tostr.c:
--------------------------------------------------------------------------------
1 | #include "rtltypes.h"
2 |
3 | size_t u64tostr_a(unsigned long long x, char *s)
4 | {
5 | unsigned long long t = x;
6 | size_t i, r=1;
7 |
8 | while ( t >= 10 ) {
9 | t /= 10;
10 | r++;
11 | }
12 |
13 | if (s == 0)
14 | return r;
15 |
16 | for (i = r; i != 0; i--) {
17 | s[i-1] = (char)(x % 10) + '0';
18 | x /= 10;
19 | }
20 |
21 | s[r] = (char)0;
22 | return r;
23 | }
24 |
25 | size_t u64tostr_w(unsigned long long x, wchar_t *s)
26 | {
27 | unsigned long long t = x;
28 | size_t i, r=1;
29 |
30 | while ( t >= 10 ) {
31 | t /= 10;
32 | r++;
33 | }
34 |
35 | if (s == 0)
36 | return r;
37 |
38 | for (i = r; i != 0; i--) {
39 | s[i-1] = (wchar_t)(x % 10) + L'0';
40 | x /= 10;
41 | }
42 |
43 | s[r] = (wchar_t)0;
44 | return r;
45 | }
46 |
--------------------------------------------------------------------------------
/Source/fftp/minirtl/ultostr.c:
--------------------------------------------------------------------------------
1 | #include "rtltypes.h"
2 |
3 | size_t ultostr_a(unsigned long x, char *s)
4 | {
5 | unsigned long t=x;
6 | size_t i, r=1;
7 |
8 | while ( t >= 10 ) {
9 | t /= 10;
10 | r++;
11 | }
12 |
13 | if (s == 0)
14 | return r;
15 |
16 | for (i = r; i != 0; i--) {
17 | s[i-1] = (char)(x % 10) + '0';
18 | x /= 10;
19 | }
20 |
21 | s[r] = (char)0;
22 | return r;
23 | }
24 |
25 | size_t ultostr_w(unsigned long x, wchar_t *s)
26 | {
27 | unsigned long t=x;
28 | size_t i, r=1;
29 |
30 | while ( t >= 10 ) {
31 | t /= 10;
32 | r++;
33 | }
34 |
35 | if (s == 0)
36 | return r;
37 |
38 | for (i = r; i != 0; i--) {
39 | s[i-1] = (wchar_t)(x % 10) + L'0';
40 | x /= 10;
41 | }
42 |
43 | s[r] = (wchar_t)0;
44 | return r;
45 | }
46 |
--------------------------------------------------------------------------------