├── .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 | --------------------------------------------------------------------------------