-
Notifications
You must be signed in to change notification settings - Fork 0
/
ftp.py
234 lines (217 loc) · 9.44 KB
/
ftp.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
_C = False
_B = None
_A = '/'
import _thread
import gc
import network
import os
import socket
DATA_PORT = 13333
class FtpTiny:
def __init__(A):
A.dorun = True;A.isrunning = _C;A.cwd = os.getcwd();A.ftpsocket = _B;A.datasocket = _B;A.dataclient = _B
def start_listen(A):
B = '0.0.0.0';A.ftpsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM);A.datasocket = socket.socket(
socket.AF_INET, socket.SOCK_STREAM);A.ftpsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
1);A.datasocket.setsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR,
1);A.ftpsocket.bind(
socket.getaddrinfo(B, 21)[0][4]);A.datasocket.bind(
socket.getaddrinfo(B, DATA_PORT)[0][4]);A.ftpsocket.listen(1);A.datasocket.listen(
1);A.datasocket.settimeout(10);A.lastpayload = ''
def send_list_data(A, client):
for B in os.listdir(A.cwd): C = os.stat(A.get_absolute_path(B));D = 'drwxr-xr-x' if C[
0] & 61440 == 16384 else '-rw-r--r--';E = \
C[6];F = '{} 1 owner group {:>13} Jan 1 1980 {}'.format(D, E, B);A.sendcmdline(client, F)
def send_file_data(C, path, client):
with open(path) as B:
A = B.read(128)
while len(A) > 0:
client.sendall(A)
if len(A) == 128:
A = B.read(128)
else:
A = []
def save_file_data(D, path, client):
B = client;
B.settimeout(.5)
with open(path, 'w') as C:
try:
A = B.recv(128)
while A and len(A) > 0:
C.write(A)
if len(A) == 128:
A = B.recv(128)
else:
A = _B
except Exception as E:
pass
def get_absolute_path(C, payload):
B = payload;
A = B
if not B.startswith(_A):
if len(C.cwd) > 1:
A = C.cwd + _A + B
else:
A = C.cwd + B
if len(A) > 1: return A.rstrip(_A)
return A
def stop(A):
A.dorun = _C;A.thread = 0;print('FTP off\n')
def start(A):
if not A.isrunning:
_thread.stack_size(2048)
A.dorun = True;B = _thread.start_new_thread(runserver, (A,));A.thread = B;print('FTP online\n')
else:
print('An instance is already running.')
def sendcmdline(A, cl, txt):
cl.sendall(txt);cl.sendall('\r\n')
def closeclient(A):
if A.dataclient: A.dataclient.close();A.dataclient = _B
def client(A, cl):
return A.dataclient if A.dataclient else cl
def _handle_command(A, cl, command, payload):
M = 'Failed to delete folder: ';
L = '550 Failed to delete file.';
K = '550 Failed to send file';
J = '226 Transfer complete.';
I = '502';
H = 'SYST';
F = payload;
C = command;
B = cl
if C == 'USER':
A.sendcmdline(B, '230 Logged in.')
elif C == H:
A.sendcmdline(B, '215 ESP32 MicroPython')
elif C == H:
A.sendcmdline(B, I)
elif C == 'PWD':
A.sendcmdline(B, '257 "{}"'.format(A.cwd))
elif C == 'CWD':
D = A.get_absolute_path(F)
try:
os.chdir(D);A.sendcmdline(B, '250 Directory changed successfully')
except:
A.sendcmdline(B, '550 Failed to change directory')
finally:
A.cwd = os.getcwd()
elif C == 'EPSV':
A.sendcmdline(B, I)
elif C == 'TYPE':
A.sendcmdline(B, '200 Transfer mode set')
elif C == 'SIZE':
D = A.get_absolute_path(F)
try:
N = os.stat(D)[6];A.sendcmdline(B, '213 {}'.format(N))
except:
A.sendcmdline(B, '550 Could not get file size')
elif C == 'QUIT':
A.sendcmdline(B, '221 Bye.')
elif C == 'PASV':
O = network.WLAN().ifconfig()[0];A.sendcmdline(B, '227 Entering Passive Mode ({},{},{}).'.format(
O.replace('.', ','), DATA_PORT >> 8, DATA_PORT % 256));A.dataclient, P = A.datasocket.accept();print(
'FTP Data connection from:', P)
elif C == 'LIST':
try:
A.send_list_data(A.client(B));A.closeclient();A.sendcmdline(B,
'150 Here comes the directory listing.');A.sendcmdline(
B, '226 Listed.')
except:
A.sendcmdline(B, '550 Failed to list directory')
finally:
A.closeclient()
elif C == 'RETR':
try:
A.send_file_data(A.get_absolute_path(F), A.client(B));A.closeclient();A.sendcmdline(B,
'150 Opening data connection.');A.sendcmdline(
B, J)
except:
A.sendcmdline(B, K)
A.closeclient()
elif C == 'STOR':
try:
A.sendcmdline(B, '150 Ok to send data.');A.save_file_data(A.get_absolute_path(F),
A.client(B));A.closeclient();print(
'Finished receiving file');A.sendcmdline(B, J)
except Exception as E:
print('Failed to receive file: ' + str(E));A.sendcmdline(B, K)
finally:
print('Finally closing dataclient');A.closeclient()
elif C == 'DELE':
try:
D = A.get_absolute_path(F);os.remove(D);print('Deleted file: ' + D);A.sendcmdline(B,
'250 File deleted ok.')
except Exception as E:
print('Failed to delete file: ' + str(E));A.sendcmdline(B, L)
finally:
A.closeclient()
elif C == 'MKD':
try:
D = A.get_absolute_path(F);os.mkdir(D);print('Create folder: ' + D);A.sendcmdline(B,
'257 Path created ok.')
except Exception as E:
print('Failed to create folder: ' + str(E));A.sendcmdline(B, '550 Failed to create folder.')
finally:
A.closeclient()
elif C == 'RMD':
try:
D = A.get_absolute_path(F);os.rmdir(D);print('Deleted folder: ' + D);A.sendcmdline(B,
'250 Folder deleted ok.')
except Exception as E:
print(M + str(E));A.sendcmdline(B, L)
finally:
A.closeclient()
elif C == 'CDUP':
try:
if A.cwd and len(A.cwd) > 1:
Q = A.cwd.split(_A);G = _A + _A.join(Q[:-1])
else:
G = _A
os.chdir(G);
A.cwd = G;
print('Go to parent: ' + G);
A.sendcmdline(B, '250 Went to parent folder.')
except Exception as E:
print(M + str(E));A.sendcmdline(B, '550 Failed to go to parent.')
finally:
A.closeclient()
elif C == 'RNFR':
A.lastpayload = F;A.sendcmdline(B, '226 Starting rename.')
elif C == 'RNTO':
if A.lastpayload:
try:
os.rename(A.lastpayload, F);A.sendcmdline(B, '250 Renamed file.')
except Exception as E:
print('Failed to rename file: ' + str(E));A.sendcmdline(B, '550 Failed to rename file.')
finally:
A.closeclient();A.lastpayload = _B
else:
A.sendcmdline(B, '502 Unsupported command.');print('Unsupported command {} with payload {}'.format(C, F))
def dolisten(A):
A.isrunning = True
try:
A.start_listen()
while A.dorun:
B, G = A.ftpsocket.accept();
B.settimeout(300)
try:
print('FTP connection from:', G);
A.sendcmdline(B, '220 Hello. Welcome to FtpTiny.')
while A.dorun:
E = B.readline().decode('utf-8').replace('\r\n', '')
if len(E) <= 0: print('Client is gone');break
C, F = (E.split(' ') + [''])[:2];
C = C.upper();
print('Command={}, Payload={}'.format(C, F));
A._handle_command(B, C, F);
gc.collect()
except Exception as D:
print(str(D))
finally:
print('Closing dataclient socket');B.close()
except Exception as D:
print('TinyFtp error: ' + str(D))
finally:
A.isrunning = _C;A.closeclient();A.datasocket.close();A.ftpsocket.close();gc.collect()
def runserver(myself): myself.dolisten()