View difference between Paste ID: sHRw0WxH and 5Z7uvCXQ
SHOW: | | - or go back to the newest paste.
1
local ftp_client = [[
2
-- socket_client class(client)
3
local socket_client = {
4
	_modem         = false;
5
	_channel       = 0;
6
	_reply_channel = 0;
7
	_side 	       = "top";
8
	_stack		   = {};
9
}
10
socket_client.__index = socket_client
11
12
socket_client.__call = function(self, ch, rep_ch)
13
	local sides = {
14
		"right", "left",
15
		"top", "bottom",
16
		"front", "back"
17
	}
18
19
	local m = nil
20
	local side = "none"
21
	for k, v in ipairs(sides) do
22
		if peripheral.getType(v) == "modem" then
23
			side = v
24
			break
25
		end
26
	end
27
28
	if side == "none" then
29
		return -1
30
	end
31
32
	m = peripheral.wrap(side)
33
34
	local t = {
35
		_channel = ch;
36
		_reply_channel = rep_ch;
37
		_side 	 = s or "top";
38
		_modem 	 = m;
39
	}
40
	setmetatable(t, socket_client)
41
42
	return t
43
end
44
45
function socket_client:connect(timeout)
46
	if not self._modem then return end
47
48
	self:write("request_connect")
49
	local timer_id = 0
50
	if timeout then timer_id = os.startTimer(timeout) end
51
52
	while true do
53
		local ev, p1, p2, p3, p4 = os.pullEvent()
54
55
		if timeout and ev == "timer" and p1 == timer_id then
56
			return false
57
		elseif ev == "modem_message" then
58
			if p4:find("success") ~= nil then
59
				return true
60
			end
61
		end
62
	end
63
64
	self._modem.closeAll()
65
66
	return false
67
end
68
69
function socket_client:listen()
70
	if not self._modem then return end
71
72
	self._modem.open(self._channel)
73
	self._modem.open(self._reply_channel)
74
75
	while true do
76
		local ev, p1, p2, p3, p4 = os.pullEvent("modem_message")
77
		if p2 == self._reply_channel then
78
			return p4
79
		end
80
	end
81
82
	self._modem.closeAll()
83
84
	return ""
85
end
86
87
function socket_client:read()
88
	return self:listen()
89
end
90
91
function socket_client:write(msg)
92
	if not self._modem then return end
93
94
	self._modem.open(self._channel)
95
	self._modem.transmit(self._channel, self._reply_channel, msg)
96
	self._modem.closeAll()
97
end
98
setmetatable(socket_client, socket_client)
99
100
local text_utils = {}
101
text_utils.last = function(str, tok)
102
	local pos = -1
103
104
	while str:find(tok, pos + 1) ~= nil do
105
		pos = str:find(tok, pos + 1)
106
	end
107
108
	return (pos < 0 and 0 or pos - 1)
109
end
110
111
function string:split(sep)
112
        local sep, fields = sep or ":", {}
113
        local pattern = string.format("([^%s]+)", sep)
114
        self:gsub(pattern, function(c) fields[#fields+1] = c end)
115
        return fields
116
end
117
118
local arg = {...}
119
120
if arg[1] ~= nil and arg[1]:lower() == "help" then
121
	print("Usage:\nFTP_client [server channel] [client channel]")
122
	return
123
end
124
125
local ch = arg[1] == nil and 12 or (tonumber(arg[1]) == nil and 12 or tonumber(arg[1]))
126
local reply_ch = arg[2] == nil and 13 or (tonumber(arg[2]) == nil and 13 or tonumber(arg[2]))
127
local socket = socket_client(ch, reply_ch)
128
129
if type(socket) == "number" and socket == -1 then
130
	print("No modem found.")
131
	return
132
end
133
134
local remote_dir = "/"
135
local local_dir = shell.getRunningProgram():sub(0, text_utils.last(shell.getRunningProgram(), "/"))
136
local history = {}
137
138
local function cmd_cd(c, str)
139
	socket:write("cmd:" .. str)
140
	local d = socket:read(1)
141
	if d == -1 then
142
		print("Error reading from server")
143
	else
144
		remote_dir = d
145
	end
146
147
	return socket:read(1)
148
end
149
150
local function cmd_ls(c, str)
151
	socket:write("cmd:" .. str)
152
	local l = socket:read(1)
153
	if l == -1 then
154
		print("Error reading from server.")
155
	else
156
		if l ~= "" then
157
			print(l)
158
		end
159
	end
160
end
161
162
local function cmd_lls(c, str)
163
	local p = c[2]
164
	if not fs.exists(local_dir .. (p and "/" .. p or "")) then
165
		return "no_such_file"
166
	end
167
168
	local l = fs.list(local_dir .. (p and "/" .. p or ""))
169
	local s = ""
170
	for k, v in ipairs(l) do
171
		s = s .. v .. "\n"
172
	end
173
	s = s:sub(1, #s - 1)
174
	print(s)
175
end
176
177
local function cmd_mkdir(c, str)
178
	socket:write("cmd:" .. str)
179
	local l = socket:read(1)
180
	if l == -1 then
181
		print("Error reading from server.")
182
	else
183
		return l
184
	end
185
end
186
187
local function cmd_lcd(c, str)
188
	local p = c[2]
189
	if p == ".." then
190
		local_dir = local_dir:sub(1, text_utils.last(local_dir, "/"))
191
	elseif p ~= "." then
192
		if fs.isDir(local_dir .. "/" .. p) then
193
			local_dir = local_dir .. "/" .. p
194
		else
195
			return "no_such_file"
196
		end
197
	end
198
199
	return local_dir .. "/"
200
end
201
202
local function cmd_delete(c, str)
203
	socket:write("cmd:" .. str)
204
	local l = socket:read(1)
205
	if l == -1 then
206
		print("Error reading from server.")
207
	else
208
		return l
209
	end
210
end
211
212
local function cmd_rmdir(c, str)
213
	socket:write("cmd:" .. str)
214
	local l = socket:read(1)
215
	if l == -1 then
216
		print("Error reading from server.")
217
	else
218
		return l
219
	end
220
end
221
222
local function cmd_put(c, str)
223
	local t = os.time()
224
	if not c[2] then
225
		return "wrong_args"
226
	end
227
228
	if fs.isDir(local_dir .. "/" .. c[2]) then
229
		return "custom_err Cannot upload directory. Use 'rput' or 'mput' instead."
230
	end
231
232
	if not fs.exists(local_dir .. "/" .. c[2]) then
233
		return "no_such_file"
234
	end
235
236
	socket:write("cmd:" .. str)
237
	local l = socket:read(1)
238
	if l == -1 then
239
		print("Error reading from server.")
240
		return "unknown_error"
241
	elseif l == "ready" then
242
		local f = io.open(local_dir .. "/" .. c[2], "r")
243
		local data = f:read("*a")
244
		f:close()
245
246
		socket:write(data)
247
248
		local res = socket:read(1)
249
		if res == -1 then
250
			return "unknown_error"
251
		else
252
			return (res == "" and "custom_err Wrote " .. #data .. " bytes in " .. (os.time() - t) * 0.0138 .. " seconds." or res)
253
		end
254
	else
255
		return l
256
	end
257
end
258
259
local function putfiles(dir, abs)
260
	local ret = cmd_mkdir({}, "mkdir " .. abs)
261
	if ret ~= "" then return ret end
262
263
	local l = fs.list(dir)
264
265
	for k, v in ipairs(l) do
266
		if fs.isDir(local_dir .. "/" .. dir .. "/" .. v) then
267
			putfiles(local_dir .. "/" .. dir .. "/" .. v, abs .. "/" .. v)
268
		else
269
			local ret = cmd_put({"put", local_dir .. "/" .. dir .. "/" .. v, abs .. "/" .. v}, "put asd " .. abs .. "/" .. v)
270
			if ret ~= "" then
271
				return ret
272
			end
273
		end
274
	end
275
end
276
277
local function cmd_rput(c, str)
278
	if not c[2] then
279
		return "wrong_args"
280
	end
281
282
	if not fs.exists(local_dir .. "/" .. c[2]) then
283
		return "no_such_file"
284
	end
285
286
	if not fs.isDir(local_dir .. "/" .. c[2]) then
287
		return "custom_err Given path is not a directory. Use 'put' instead."
288
	end
289
290
	ret = putfiles(local_dir .. "/" .. c[2], c[3] or c[2])
291
	return ret
292
end
293
294
local function cmd_get(c, str)
295
	if not c[2] then
296
		return "wrong_args"
297
	end
298
299
	if fs.isDir(local_dir .. "/" .. (c[3] or c[2])) then
300
		return "custom_err Cannot overwrite directory."
301
	end
302
303
	if fs.exists(local_dir .. "/" .. (c[3] or c[2])) then
304
		io.write("Overwrite file '" .. (c[3] or c[2]) .. "? (y/n) ")
305
		local ans = io.read()
306
307
		if ans:lower() ~= "y" and ans:lower() ~= "yes" then
308
			return ""
309
		end
310
	end
311
312
	socket:write("cmd:" .. str)
313
314
	local ret = socket:read()
315
	if ret == "ok" then
316
		socket:write("ready")
317
		local data = socket:read()
318
319
		local f = io.open(local_dir .. "/" .. (c[3] or c[2]), "w")
320
		f:write(data)
321
		f:close()
322
323
		ret = socket:read()
324
		return ret
325
	else
326
		return ret
327
	end
328
end
329
330
local function cmd_rget(c, str)
331
	if not c[2] then
332
		return "wrong_args"
333
	end
334
335
	if fs.isDir(local_dir .. "/" .. (c[3] or c[2])) then
336
		return "custom_err Cannot overwrite directory."
337
	end
338
339
	if fs.exists(local_dir .. "/" .. (c[3] or c[2])) then
340
		io.write("Overwrite file '" .. (c[3] or c[2]) .. "? (y/n) ")
341
		local ans = io.read()
342
343
		if ans:lower() ~= "y" and ans:lower() ~= "yes" then
344
			return ""
345
		end
346
	end
347
348
	socket:write("cmd:" .. str)
349
350
	local ret = socket:read()
351
352
	if ret == "ok" then
353
		socket:write("ready")
354
		local p = socket:read()
355
		while p ~= "__getfiles_done__" do
356
			if p:sub(1, 1) == "d" then
357
				fs.makeDir(local_dir .. "/" .. p:sub(2))
358
			else
359
				local data = socket:read()
360
				local f = io.open(local_dir .. "/" .. p:sub(2), "w")
361
				f:write(data)
362
				f:close()
363
			end
364
365
			p = socket:read()
366
		end
367
368
		ret = socket:read()
369
370
		return ret
371
	else
372
		return ret
373
	end
374
end
375
376
local help_text = {
377
	["cd"] = "Usage:\ncd <dir>\n\nChanges directory to <dir> on remote\nmachine.";
378
379
	["lcd"] = "Usage:\ncd <dir>\n\nChanges directory to <dir> on local\nmachine.";
380
381
	["ls"] = "Usage:\nls [dir]\n\nLists files in current directory on remote\nmachine.\nIf [dir] is provided, files in that directory\nwill be listed instead.";
382
383
	["lls"] = "Usage:\nls [dir]\n\nLists files in current directory on local\nmachine.\nIf [dir] is provided, files in that directory\nwill be listed instead.";
384
385
	["quit"] = "Usage:\nquit|bye\n\nCloses the connection and exists the program.";
386
387
	["pwd"] = "Usage:\npwd\n\nPrints the current directory on the remote\nmachine.";
388
389
	["mkdir"] = "Usage:\nmkdir <dir>\n\nCreates the directory <dir> on the remote\nmachine.";
390
391
	["delete"] = "Usage:\ndelete <file>\n\nDeletes the file <file> on the remote machine.\nCannot delete directories.";
392
393
	["rmdir"] = "Usage:\nrmdir <dir>\n\nRemoves the directory <dir> on the remote\nmachine. Cannot delete files.";
394
395
	["put"] = "Usage:\nput <local/path> [remote/path]\n\nUploads a file to the remote machine.\nIf [remote/path] is specified, it will upload\nto that filename, otherwise it will upload to\nas <local/path> on remote machine.\nCannot upload directories.";
396
397
	["rput"] = "Usage:\nrput|mput <local/path> [remote/path]\n\nUploads a directory to the remote machine.\nIf [remote/path] is specified, it will upload\nto that filename, otherwise it will upload to\nas <local/path> on remote machine.\nCannot upload single files.";
398
399
	["get"] = "Usage:\nget <remote/path> [local/path]\n\nDownloads a single file from <remote/path> on\nthe remote machine. If [local/path] is specified,\nit will save there, otherwise in <remote/path>.\nCannot download directories. Use 'rget' or 'mget'.";
400
401
	["rget"] = "Usage:\nrget|mget <remote/path>\n\nDownloads the directory <remote/path> from the\nremote machine. The directory is saved as\n<remote/path>";
402
}
403
help_text["bye"] = help_text["quit"]
404
help_text["mput"] = help_text["rput"]
405
help_text["mget"] = help_text["rget"]
406
407
local function cmd_help(c, str)
408
	if c[2] ~= nil then
409
		local text = help_text[c[2] ]
410
		if text == nil then
411
			return "custom_err No help available for '" .. c[2] .. "'"
412
		end
413
414
		print(text .. "\n")
415
416
		return ""
417
	end
418
419
	print("Help for FTP:\ncd <dir>      - change directory on remote\nlcd <dir>     - change directory locally\nls [dir]      - list files on remote\nlls [dir]     - list files locally\nquit/bye      - close connection\npwd           - print current remote dir\nmkdir <dir>   - create directory on remote\ndelete <file> - remove file on remote\nrmdir <dir>   - remove directory on remote\nput <file> [remote_file] - upload file\nrput/mput <dir> [remote_dir] - upload dir\nget <remote_file> [file] - download file\nhelp          - show this screen\nhelp <cmd>    - help for specific command\n")
420
	return ""
421
end
422
423
local function main()
424
	print("Connecting...")
425
	if socket:connect(2) == false then
426
		return false
427
	end
428
429
	while true do
430
		io.write(local_dir .. "> ")
431
		local cmd = read(nil, history)
432
		table.insert(history, cmd)
433
434
		local w = string.split(cmd, " ")
435
436
437
		local ret = ""
438
439
		if w[1] ~= nil then
440
			if w[1]:lower() == "cd" then
441
				ret = cmd_cd(w, cmd)
442
			elseif w[1]:lower() == "ls" then
443
				ret = cmd_ls(w, cmd)
444
			elseif w[1]:lower() == "lls" then
445
				ret = cmd_lls(w, cmd)
446
			elseif w[1]:lower() == "lcd" then
447
				ret = cmd_lcd(w, cmd)
448
			elseif w[1]:lower() == "quit" or w[1]:lower() == "bye" then
449
				socket:write("request_disconnect")
450
				return true
451
			elseif w[1]:lower() == "pwd" then
452
				print(remote_dir)
453
			elseif w[1]:lower() == "mkdir" then
454
				ret = cmd_mkdir(w, cmd)
455
			elseif w[1]:lower() == "delete" then
456
				ret = cmd_delete(w, cmd)
457
			elseif w[1]:lower() == "rmdir" then
458
				ret = cmd_rmdir(w, cmd)
459
			elseif w[1]:lower() == "put" then
460
				ret = cmd_put(w, cmd)
461
			elseif w[1]:lower() == "mput" or w[1]:lower() == "rput" then
462
				ret = cmd_rput(w, cmd)
463
			elseif w[1]:lower() == "get" then
464
				ret = cmd_get(w, cmd)
465
			elseif w[1]:lower() == "rget" or w[1]:lower() == "mget" then
466
				ret = cmd_rget(w, cmd)
467
			elseif w[1]:lower() == "help" or w[1] == "?" then
468
				ret = cmd_help(w, cmd)
469
			else
470
				ret = "unknown_command"
471
			end
472
		end
473
		
474
		if ret == "unknown_error" then
475
			print("Unknown error.")
476
		elseif ret == "unknown_command" then
477
			print("Unknown command. Try 'help'")
478
		elseif ret == "wrong_args" then
479
			print("Insufficient arguments. Try 'help'")
480
		elseif ret == "no_such_file" then
481
			print("No such file or directory.")
482
		elseif ret == "invalid_path" then
483
			print("Invalid path.")
484
		elseif ret == "already_exists" then
485
			print("File or directory already exists.")
486
		elseif ret == "not_allowed" then
487
			print("Insufficient permissions.")
488
		elseif type(ret) == "string" and ret:sub(0, string.len("custom_err")) == "custom_err" then
489
			print(ret:sub(string.len("custom_err") + 2))
490
		end
491
	end
492
493
	return true
494
end
495
496
term.clear()
497
term.setCursorPos(1, 1)
498
print("Welcome to FTP!\nFor a list of commands, run 'help'")
499
if main() == true then
500
	term.clear()
501
	term.setCursorPos(1, 1)
502
	print("Thanks for using FTP!")
503
else
504
	print("Unable to connect.")
505
end
506
]]
507
508
local ftp_server = [[
509
local text_utils = {}
510
text_utils.last = function(str, tok)
511
	local pos = -1
512
513
	while str:find(tok, pos + 1) ~= nil do
514
		pos = str:find(tok, pos + 1)
515
	end
516
517
	return (pos < 0 and 0 or pos - 1)
518
end
519
520
function string:split(sep)
521
        local sep, fields = sep or ":", {}
522
        local pattern = string.format("([^%s]+)", sep)
523
        self:gsub(pattern, function(c) fields[#fields+1] = c end)
524
        return fields
525
end
526
527
-- Socket server class
528
local socket_server = {
529
	_modem	       = false;
530
	_channel       = -1;
531
	_reply_channel = 0;
532
	_side 	       = "top";
533
	_connected     = false;
534
}
535
socket_server.__index = socket_server
536
537
socket_server.__call = function(self, rep_ch)
538
	local sides = {
539
		"right", "left",
540
		"top", "bottom",
541
		"front", "back"
542
	}
543
544
	local m = nil
545
	local side = "none"
546
	for k, v in ipairs(sides) do
547
		if peripheral.getType(v) == "modem" then
548
			side = v
549
			break
550
		end
551
	end
552
553
	if side == "none" then
554
		return -1
555
	end
556
557
	m = peripheral.wrap(side)
558
559
	local t = {
560
		_side 	 	   = s or "top";
561
		_reply_channel = rep_ch;
562
		_modem 	  	   = m;
563
	}
564
	setmetatable(t, socket_server)
565
566
	return t
567
end
568
569
function socket_server:accept(ch, timeout)
570
	if self._connected then return end
571
	if not self._modem then return end
572
573
	self._modem.open(ch)
574
575
	local timer_id = 0
576
	if timeout then timer_id = os.startTimer(timeout) end
577
578
	while true do
579
		local ev, p1, p2, p3, p4 = os.pullEvent()
580
581
		if timeout and ev == "timer" and p1 == timer_id then
582
			return false
583
		elseif ev == "modem_message" then
584
			if p4:find("request_connect") ~= nil then
585
				print("Client connected on channel " .. p2)
586
				self._channel = p2
587
				self._connected = true
588
				self._modem.transmit(self._channel, self._reply_channel, "success")
589
				return true
590
			end
591
		end
592
	end
593
594
	self._modem.closeAll()
595
596
	return false
597
end
598
599
function socket_server:accept_any(timeout)
600
	if self._connected then return end
601
	if not self._modem then return end
602
603
	for i = 0, 1000 do
604
		self._modem.open(i)
605
	end
606
607
	local timer_id = 0
608
	if timeout then timer_id = os.startTimer(timeout) end
609
610
	while true do
611
		local ev, p1, p2, p3, p4 = os.pullEvent()
612
613
		if timeout and ev == "timer" and p1 == timer_id then
614
			return false
615
		elseif ev == "modem_message" then
616
			if p4:find("request_connect") ~= nil then
617
				print("Client connected on channel " .. p3)
618
				self._channel = p3
619
				self._connected = true
620
				self._modem.transmit(self._channel, self._reply_channel, "success")
621
				return true
622
			end
623
		end
624
	end
625
626
	self._modem.closeAll()
627
628
	return false
629
end
630
631
function socket_server:listen()
632
	if not self._modem then return end
633
634
	while true do
635
		if self._channel ~= -1 then
636
			self._modem.open(self._channel)
637
			self._modem.open(self._reply_channel)
638
639
			local ev, p1, p2, p3, p4 = os.pullEvent("modem_message")
640
			if p2 == self._reply_channel then
641
				return p4
642
			end
643
644
			self._modem.closeAll()
645
		end
646
	end
647
648
	return ""
649
end
650
651
function socket_server:read()
652
	local r = self:listen()
653
	if r == "request_disconnect" then
654
		print("Client on channel ".. self._channel .. " disconnected.")
655
		self._connected = false
656
	end
657
	return r
658
end
659
660
function socket_server:write(msg)
661
	if not self._modem then return end
662
663
	self._modem.open(self._channel)
664
	self._modem.transmit(self._channel, self._reply_channel, msg)
665
	self._modem.closeAll()
666
end
667
setmetatable(socket_server, socket_server)
668
669
local arg = {...}
670
671
_print = function(...)
672
	if not bg then print(...) end
673
end
674
675
if arg[2] == nil and arg[1] ~= nil and (arg[1]:lower() == "-b" or arg[1]:lower() == "--background") then
676
	arg[3] = arg[1]
677
end
678
679
if arg[1] ~= nil and arg[1]:lower() == "help" then
680
	_print("Usage:\nFTP_server [channel] [options]\nIf the option -b is provided, the server will run in the background")
681
	return
682
end
683
684
local ch = arg[1] == nil and 12 or (tonumber(arg[1]) == nil and 12 or tonumber(arg[1]))
685
686
local server = socket_server(ch)
687
688
if type(server) == "number" and server == -1 then
689
	_print("No modem found.")
690
	return
691
end
692
693
local dir = ""
694
local bg = false
695
696
local function cd(p)
697
	if p == ".." then
698
		dir = dir:sub(1, text_utils.last(dir, "/"))
699
	elseif p ~= "." then
700
		if fs.isDir(dir .. "/" .. p) then
701
			dir = dir .. "/" .. p
702
		else
703
			server:write(dir .. "/")
704
			return "no_such_file"
705
		end
706
	end
707
708
	server:write(dir .. "/")
709
	return dir .. "/"
710
end
711
712
local function ls(p)
713
	if not fs.exists(dir .. (p and "/" .. p or "")) then
714
		server:write("")
715
		return "no_such_file"
716
	end
717
718
	local l = fs.list(dir .. (p and "/" .. p or ""))
719
	local s = ""
720
	for k, v in ipairs(l) do
721
		s = s .. v .. "\n"
722
	end
723
	s = s:sub(1, #s - 1)
724
	server:write(s)
725
726
	return ""
727
end
728
729
local function mkdir(p)
730
	if fs.exists(dir .. "/" .. p) then
731
		return "already_exists"
732
	end
733
734
	fs.makeDir(dir .. "/" .. p)
735
736
	return ""
737
end
738
739
local function delete(p)
740
	if not fs.exists(dir .. "/" .. p) then
741
		return "no_such_file"
742
	elseif fs.isReadOnly(dir .. "/" .. p) then
743
		return "not_allowed"
744
	elseif fs.isDir(dir .. "/" .. p) then
745
		return "custom_err Cannot remove a directory. Use 'rmdir' instead."
746
	end
747
748
	fs.delete(dir .. "/" .. p)
749
750
	return ""
751
end
752
753
local function rmdir(p)
754
	if not fs.exists(dir .. "/" .. p) then
755
		return "no_such_file"
756
	elseif fs.isReadOnly(dir .. "/" .. p) then
757
		return "not_allowed"
758
	elseif not fs.isDir(dir .. "/" .. p) then
759
		return "custom_err Cannot remove a file. Use 'delete' instead."
760
	end
761
762
	fs.delete(dir .. "/" .. p)
763
764
	return ""
765
end
766
767
local function put(p)
768
	if fs.isDir(dir .. "/" .. p) then
769
		return "custom_err Cannot overwrite directory."
770
	end
771
772
	server:write("ready")
773
	local data = server:read()
774
	local f = io.open(dir .. "/" .. p, "w")
775
	f:write(data)
776
	f:close()
777
778
	return ""
779
end
780
781
local function get(p)
782
	if not fs.exists(dir .. "/" .. p) then
783
		return "no_such_file"
784
	end
785
786
	if fs.isDir(dir .. "/" .. p) then
787
		return "custom_err Cannot download directory. Use 'rget' or 'mget' instead."
788
	end
789
790
	server:write("ok")
791
	local ret = server:read()
792
	if ret ~= "ready" then
793
		return "unknown_err"
794
	end
795
796
	local f = io.open(dir .. "/" .. p, "r")
797
	local data = f:read("*a")
798
	f:close()
799
	server:write(data)
800
801
	return ""
802
end
803
804
local function rget(p)
805
	if not fs.exists(dir .. "/" .. p) then
806
		return "no_such_file"
807
	end
808
809
	if not fs.isDir(dir .. "/" .. p) then
810
		return "custom_err Cannot download single files.\nUse 'get' instead."
811
	end
812
813
	server:write("ok")
814
815
	local function getfiles(path)
816
		server:write("d" .. path)
817
		local l = fs.list(dir .. "/" .. path)
818
819
		for k, v in ipairs(l) do
820
			if fs.isDir(dir .. "/" .. path .. "/" .. v) then
821
				getfiles(dir .. "/" .. path .. "/" .. v)
822
			else
823
				server:write("f" .. path .. "/" .. v)
824
825
				local ret = server:read()
826
				if ret ~= "ready" then
827
					return ret
828
				end
829
830
				local f = io.open(dir .. "/" .. path .. "/" .. v, "r")
831
				local data = f:read("*a")
832
				f:close()
833
834
				server:write(data)
835
			end
836
		end
837
	end
838
839
	getfiles(p)
840
	server:write("__getfiles_done__")
841
end
842
843
local function main()
844
	server:accept_any()
845
846
	while true do
847
		if not server._connected then
848
			server:accept_any()
849
		end
850
851
		local msg = server:read()
852
853
		if msg:sub(1, 4) == "cmd:" then
854
			local cmd = msg:sub(5)
855
			_print("Command: " .. cmd)
856
			cmd = string.split(cmd, " ")
857
858
			if cmd[1]:lower() == "cd" then
859
				if not cmd[2] then
860
					server:write("wrong_args")
861
				else
862
					local ret = cd(cmd[2])
863
					server:write(ret)
864
				end
865
			elseif cmd[1]:lower() == "ls" then
866
				local ret = ls(cmd[2] or "")
867
				server:write(ret)
868
			elseif cmd[1]:lower() == "mkdir" then
869
				if not cmd[2] then
870
					server:write("wrong_args")
871
				else
872
					local ret = mkdir(cmd[2])
873
					server:write(ret)
874
				end
875
			elseif cmd[1]:lower() == "delete" then
876
				if not cmd[2] then
877
					server:write("wrong_args")
878
				else
879
					local ret = delete(cmd[2])
880
					server:write(ret)
881
				end
882
			elseif cmd[1]:lower() == "rmdir" then
883
				if not cmd[2] then
884
					server:write("wrong_args")
885
				else
886
					local ret = rmdir(cmd[2])
887
					server:write(ret)
888
				end
889
			elseif cmd[1]:lower() == "put" then
890
				if not cmd[2] then
891
					server:write("wrong_args")
892
				else
893
					local ret = put((cmd[3] ~= nil and cmd[3] or cmd[2]))
894
					server:write(ret)
895
				end
896
			elseif cmd[1]:lower() == "get" then
897
				if not cmd[2] then
898
					server:write("wrong_args")
899
				else
900
					local ret = get(cmd[2])
901
					server:write(ret)
902
				end
903
			elseif cmd[1]:lower() == "rget" or cmd[1]:lower() == "mget" then
904
				if not cmd[2] then
905
					server:write("wrong_args")
906
				else
907
					local ret = rget((cmd[3] ~= nil and cmd[3] or cmd[2]))
908
					server:write(ret)
909
				end
910
			else
911
				server:write("unknown_command")
912
			end
913
		end
914
	end
915
916
	return ""
917
end
918
919
if arg[3] ~= nil and (arg[3]:lower() == "-b" or arg[3] == "--background") then
920
	bg = true
921
	term.clear()
922
	term.setCursorPos(1, 1)
923
	parallel.waitForAll(main, function() shell.run("shell") end)
924
else
925
	main()
926
end
927
]]
928
929
local arg = {...}
930
931
if not arg[1] then print("Usage:\ninstall server|client") else
932
	if arg[1]:lower() == "c" or arg[1]:lower() == "client" then
933
		local f = io.open("ftp_client", "w")
934
		f:write(ftp_client)
935
		f:close()
936
		print("Installed to 'ftp_client'.\nRun 'ftp_client help' for information about using it.")
937
	elseif arg[1]:lower() == "s" or arg[1]:lower() == "server" then
938
		local f = io.open("ftp_server", "w")
939
		f:write(ftp_server)
940
		f:close()
941-
		print("Installed to 'ftp_server'.\nRun 'ftp_server' help' for information about using it.")
941+
		print("Installed to 'ftp_server'.\nRun 'ftp_server help' for information about using it.")
942
	end
943
end