Repository navigation
Expand file tree
/
Copy pathFTPython.py
More file actions
350 lines (291 loc) · 11.3 KB
/
Copy pathFTPython.py
File metadata and controls
350 lines (291 loc) · 11.3 KB
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
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
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
# This File handles the FTP Project for the summer Agile class at PSU :w17
# Imports
import ftplib as ft
import os
import sqlite3
import base64
from Crypto.Cipher.XOR import new
# State vars
ftp_connection = None
connection_name = None
total_bytes_transferred = 0
encryption_key = None
# Constants
DB_Name = "connections.db"
create_table = """CREATE TABLE IF NOT EXISTS connections (connection_name text PRIMARY KEY, connection_address text NOT NULL,
port integer, user_name text, encrypted_password text); """
insert_into_table = """insert into connections values (?, ?, ?, ?, ?);"""
get_connections = """SELECT * from connections;"""
load_connection = """SELECT * from connections c WHERE c.connection_name=?;"""
# Deletes a file or folder specified by the user
def delete(targetType, name):
if targetType == "file":
try:
ftp_connection.delete(name)
print("Successfully able to delete ", name)
except ft.all_errors as err:
print("Unable to delete file " + name + ": ", err)
elif targetType == "folder":
try:
ftp_connection.rmd(name)
print("Successfully able to delete ", name)
except ft.all_errors as err:
print("Unable to delete folder " + name + ": ", err)
else:
print("invalid input, please designate either 'file' or 'folder'")
# Creates a folder specified by the user
def create(name):
try:
ftp_connection.mkd(name)
except ft.all_errors as err:
print("unable to create directory " + name + ": ", err)
#Changes a file's permissions, where change is the chmod xxxx fileName
def change_permissions(change):
success_or_fail = ftp_connection.sendCommand(change)
if success_or_fail is False:
print("Failed to change permissions.")
else:
print("Successfully changed permissions.")
# Connects to the host and updates the global ftp connection.
def connect(host, port=20, username="", password="", account_info=""):
print("Trying to connect with user: ", username)
print("Trying to connect with host: ", host)
print("Trying to connect with port: ", port)
print("Trying to connect with password: ", password)
global ftp_connection
try:
ftp_connection = ft.FTP()
print(ftp_connection.connect(host, port))
print(ftp_connection.login(username, password, account_info))
print(ftp_connection.pwd())
except ft.all_errors as err:
ftp_connection = None
print("Connection failed: ", err)
# Places file(s) on the connected server
def put(files):
global total_bytes_transferred
try:
for file in files:
total_bytes_transferred = 0
ftp_connection.storbinary("STOR " + os.path.basename(file), open(file, 'r+b'),
8192, g_print_progress(file, os.path.getsize(file)))
print("")
except AttributeError as err:
print("File not found")
# Change directory (currently this is a relative path)
def cd(path):
ftp_connection.cwd(path)
# List files in current directory
# Will not list . and .. - restriction of os.listdir command
def list_files(option):
if option == "local":
# print(os.listdir())
for i in os.listdir():
print(i)
print('\n')
elif option == "remote":
if ftp_connection is not None:
ftp_connection.dir()
else:
print("No Connection")
else:
print("Entered an Invalid option")
# Gets file(s) from the connected server
def get(files):
global total_bytes_transferred
try:
for file in files:
total_bytes_transferred = 0
store_file = open(os.path.basename(file), 'w+b')
ftp_connection.retrbinary("RETR " + file,
g_write_and_print_progress(store_file, file, ftp_connection.size(file)))
print("")
store_file.close()
except AttributeError as err:
print("File not found")
# Generates and returns the function to write the transferred bytes to
# a file and print the progress of the file transfer.
# This generation is necessary to use the returned function as the callback function for the
# get command, since the callback function is only allowed to take a single argument.
def g_write_and_print_progress(store_file, filename, file_size):
def write_and_print_progress(bytes_transferred):
store_file.write(bytes_transferred)
print_function = g_print_progress(filename, file_size)
print_function(bytes_transferred)
return write_and_print_progress
# Generates and returns the function to calculate and print the progress of a file transfer.
# The print formatting was found on https://stackoverflow.com/questions/3173320/text-progress-bar-in-the-console
# This generates the callback function for the put command.
#
# Note: This bugs in Pycharm as the lines don't always overwrite each other properly. This
# is an issue with Pycharm. If you run this in a normal terminal, it works fine.
def g_print_progress(filename, file_size):
def print_progress(bytes_transferred):
global total_bytes_transferred
total_bytes_transferred = total_bytes_transferred + len(bytes_transferred)
percent_done = float(total_bytes_transferred) / float(file_size)
print("\r{1} [{0:10s}] {2:.1f}%".format('#' * int(percent_done * 10), filename, percent_done * 100),
end="", flush=True)
return print_progress
def rename(option, old, new):
try:
if option == "local":
os.rename(old, new)
elif option == "remote":
ftp_connection.rename(old, new)
except FileNotFoundError:
print("Rename: Invalid input")
# Prints the Basic Menu
def help_menu():
print("Options : \n"
"connect <host [port] username password> \n"
"put <filename filename ...>\n"
"get <filename filename ...>\n"
"cd <path>\n"
"rename <local/remote fromFilename toFilename>\n"
"create <directoryName>"
"delete <file/folder targetName> \n"
"list remote \n"
"list local \n"
"save connection\n"
"show connections\n"
"close \n"
"quit \n"
"help \n"
"change <chmod xxx file_name> \n")
# Creates the Database if one doesn't exist yet and makes a basic table
def db_create():
db_connection = sqlite3.connect(DB_Name)
if db_connection is not None:
db_connection.execute(create_table)
else:
print("Unable to connect to Database!")
# Prompts the user for the info to save the ftp connection in the local database
# will encrypt the password before storing it.
def save_connection():
handle = input("Nickname: ")
host = input("Hostname: ")
port = input("Port (optional): ")
username = input("Username(optional): ")
password = input("Password(optional): ")
key = input("Database Key (required if password is supplied): ")
encode = None
if len(password) > 0:
if len(key) > 0:
cypher = new(key)
encode = cypher.encrypt(password)
else:
print("Error: Missing Key for password encryption Try again")
return
try:
con = sqlite3.connect(DB_Name)
c = con.cursor()
c.execute(insert_into_table, (handle, host, port, username, encode))
con.commit()
con.close()
except sqlite3.Error as err:
print(err)
def show_connections():
con = sqlite3.connect(DB_Name)
c = con.cursor()
c.execute(get_connections)
output = c.fetchall()
for item in output:
print(item)
def load_con():
nickname = input("Enter the nickname of the connection you want to load ")
con = sqlite3.connect(DB_Name)
c = con.cursor()
c.execute(load_connection, (nickname,))
to_load = c.fetchone()
key = input("Enter the Key that was used to encrypt the password: ")
cypher = new(key)
password = cypher.decrypt(to_load[-1]).decode('ascii')
print(to_load)
print(password)
connect(to_load[1], to_load[2], to_load[3], password)
# Parses user input
def parse_input():
u_input = input("input: ")
u_input = u_input.split()
u_input[0] = u_input[0].lower()
global ftp_connection
try:
if u_input[0] == "quit":
return True
elif u_input[0] == "connect":
try:
if len(u_input) == 4:
connect(u_input[1], 20, u_input[2], u_input[3])
elif len(u_input) == 5:
connect(u_input[1], int(u_input[2]), u_input[3], u_input[4])
elif len(u_input) == 2:
connect(u_input[1])
else:
print("Missing Arguments")
except ValueError as err:
print("Invalid format use the help command to see the arguments")
elif u_input[0] == "put":
if len(u_input) < 2:
print("You need to supply a filename to upload")
else:
del u_input[0]
put(u_input)
elif u_input[0] == "get":
if len(u_input) < 2:
print("You need to supply a file to download")
else:
del u_input[0]
get(u_input)
elif u_input[0] == "cd":
if len(u_input) < 2:
print("You need to supply a directory to go to")
else:
cd(u_input[1])
elif u_input[0] == "rename":
rename(u_input[1], u_input[2], u_input[3])
elif u_input[0] == "close":
if ftp_connection is not None:
ftp_connection.close()
ftp_connection = None
else:
print("No Connection to Close")
elif u_input[0] == "list":
if len(u_input) >= 2:
list_files(u_input[1])
else:
print("List requires an argument of 'local' or 'remote'.")
elif u_input[0] == "help":
help_menu()
elif u_input[0] == "save":
save_connection()
elif u_input[0] == "show":
show_connections()
elif u_input[0] == "delete":
if len(u_input) >= 2:
delete(u_input[1], u_input[2])
else:
print("delete requires arguments file/folder and a filename/foldername respectively")
elif u_input[0] == "createdirectory":
if len(u_input) >= 2:
create(u_input[1])
else:
print("createdirectory requires a name of the directory to create")
elif u_input[0] == "change":
if u_input[1] is None or u_input[2] is None or u_input[3] is None:
print("Error. Must provide permissions and file name.")
else:
change_permissions(u_input[1] + " " + u_input[2] + " " + u_input[3])
elif u_input[0] == "load":
load_con()
else:
print("Invalid command. Type help to display a help menu")
except ft.all_errors as err:
print("Error: ", err)
return False
if __name__ == "__main__":
done = False
db_create()
print("Welcome to our basic FTP client.\nType help to display a help menu\n")
while not done:
done = parse_input()