forked from guardhunt/SocketEmail_ServerClient
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver_email.py
More file actions
266 lines (232 loc) · 11.6 KB
/
server_email.py
File metadata and controls
266 lines (232 loc) · 11.6 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
import socket
import sys
import pickle
import time
import random
from threading import Thread
import hashlib
class socket_server(): #class format for the server side of the socket
def __init__(self, ip, port): #initilize with ip and port
self.usernames = {"huntc": "123", "peterm" : "123", "jamest" : "123", "tuckerm" : "123", "lydiar" : "123", "masterl" : "123", "cassym" : "123"} #list of usernames and passwords hashed together
self.accounts = {} #empty list for the accounts that will be filled later by a pickle file
self.ip = ip #takes in the ip argument and sets the ip for the connection
self.port = port #takes in the port argument and sets the socket port connection
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #the socket object, standard library setup
self.server_address = (self.ip, self.port) #creates the server address with IP and Port used from earlier
self.sock.bind(self.server_address) #bind the socket using the server_address
def create_accounts(self):
"""
Create accoutns dictionary from pickle file
Called from function(s):
-main()
"""
self.accounts = pickle.load(open("accounts.p", "rb"))
def connect(self):
"""
Create connection on client request, test for data, split data recievd, test username, call appropriate command function.
Called from functions(s):
-main()
"""
connection, self.server_address = self.sock.accept()
try:
data = connection.recv(1024) #buffer for the amount of bytes that can be recieved
if data:
data.decode('utf-8') #decode using standard U.S. utf-8
data = data.split(":") #split the data at every colon
#check if username and are correct exists
if data[0] in self.usernames: #takes first element of two and checks if it is in the list of usernames
if self.usernames[data[0]] == data[1]: #if the list of usernames has a hash to a password that matches the one given in data.
session_id = random.randint(1000000000, 9999999999) #creates a random integer id between these two numbers to use as a session identifier
print("Session ID Sent: " + str(session_id)) #print the identifier on the server side
connection.send(b"OK:" + str(session_id)) #send the session id over the connection
self.manage_session(session_id, data[0]) #calls message session using the session id and the username from data to create a session class
else: #if password doesn't match prints password incorrect on server side and sends a KO to the client
connection.send(b"KO")
print("Password sent incorrect")
else: #if ussername doesn't exist prints username incorrect on server side and sends a KO to the client
connection.send(b"KO")
print("Username sent incorrect")
finally:
pass
connection.close() #close socket connection
print("Connection Closed")
return
def manage_session(self, session_id, un):
"""
Mannage incoming commands from client after handshake and confirm Session ID
"""
session_handle = Session(session_id, un, self.usernames) #creates a handle for a session class that uses the session identifier, the username, and the list of usernames
session_handle.populate_accounts() #populate accounts from the pickle file
while session_handle.logged_on == True:
self.sock.listen(1) #listens for a single byte
connection, self.server_address = self.sock.accept() #tries to accept the connection
try:
data = connection.recv(1024) #tries to recieve up to 1024 bytes from the client
if data: #checks to see if there is data to recieve
print("Session CMD Recieved: Func-manage_session()")
data.decode('utf-8') #decodes using standard U.S. utf-8
#testing messageddd and checksum data
full_data = data.split("#") #splits off the checksum half of data
if self.try_checksum(full_data) == False: #checks the checksum
connection.send(b"KO") #sends KO to client side if try_checksum comes back with False
print("Checksum ERROR!")
else: #if try_checksum is equal to True
#testing message data only
data = full_data[0].split(":") #split the message using colons that isn't the checksum
#check if the session ID is the same
if str(data[0]) == str(session_handle.ID): #if the first element of data is the session ID handle the message using standard commands
print("ID Recieved Correct. Handling CMD Message")
session_handle.handle_msg(data, connection)
else: #if ID is no included or inccorrect
print("ID Recieved Inccorect. Reponding to Client")
connection.send(b"KO")
print("ID match error")
finally:
pass
print("Session handled and user logged off: listening for new connection")
session_handle.update_accounts() #updates the pickle file with the newly added messages etc
return
def check_sum(self, message): #Help from Wade Rutherford who used this method with MD5
return hashlib.sha1(message).hexdigest() #encrypts using SHA1 and hexdigests so that it easier to pass along
def try_checksum(self, message): #takes the checksum sent from the client and tests it with the check_sum of the message send.
sent_checksum = message[1]
test_checksum = self.check_sum(message[0])
if sent_checksum == test_checksum: #if both checksumns match then return true else false
return True
else:
return False
class Session(): #creates a session class for the user using a session identifier
def __init__(self, session_id, un, usernames): #creates a session using the session id, username, and the list of usernames
self.name = ""
self.accounts = {} #empty to be filled with pickle file data
self.logged_on = True #loggon on set to true
self.ID = session_id
self.un = un
self.usernames = usernames
def populate_accounts(self):
self.accounts = pickle.load(open("accounts.p", "rb")) #populate the empty accounts with the data from the pickle file
def handle_msg(self, data, connection): #list of commands the user can use to check messages or send them
#for email functionality
if "email" in data[1]:
print ("email cmd recieved")
self.emailcmd(connection, data)
#for get message functionality
elif "getmsg" in data[1]:
print("Get msg. cmd recieved")
self.getmsgcmd(connection, data)
#for count functionality
elif "count" in data[1]:
print("count cmd recieved")
self.countcmd(connection, data)
#for deleat message functionality
elif "delmsg" in data[1]:
print("delmsg cmd recieved")
self.delmsgcmd(connection, data)
#for dump message functionality
elif "dump" in data[1]:
print("dump cmd recieved")
self.dumpcmd(connection, data)
elif "logoff" in data[1]:
print("logoff cmd recieved")
self.logoffcmd(connection)
elif "help" in data[1]:
self.help_cmd(connection)
else:
print("NA cmd recieved")
connection.send(b"KO")
def update_accounts(self):
"""
-Sesion() function dumps all new account information before closing
"""
pickle.dump(self.accounts, open("accounts.p", "wb")) #updates the pickle file with any new info recently added after logoff
def emailcmd(self, connection, data):
"""
-Session() function adds the sent email to specified user's account
-Data in form of [ID, CMD, recipient username, subject, message]
"""
if data[2] not in self.usernames:
connection.send(b"Recipient Name Not Recognized: " + str(data[2]))
else:
#append email contnets to accounts dictionary value with key being the recipient
#email values logged in form of [sender, subject, message, date/time]
self.accounts[data[2]].append([self.un, data[3], data[4], time.strftime("%c")])
self.update_accounts()
connection.send(b"OK: Email Sent")
def getmsgcmd(self, connection, data):
"""
-Session() function returns with all emails from specified sender
-Data in form of [ID, CMD, requested sender username]
"""
response = ""
if data[2] not in self.usernames:
connection.send(b"Sender Name Not Recognized: " + str(data[2]))
else:
for email in self.accounts[self.un]:
if email[0] == data[2]:
#response sent in form of [subject, message, date/time]
response += ("Subject: " + str(email[1]) + "\n" + "Message: " + str(email[2]) + "\n" + str(email[3])+ "\n \n")
connection.send(b"Emails From:" + str(data[2])+ ":\n" + response)
def countcmd(self, connection, data):
"""
-Session() function returns total number of user's emails
"""
count = 0
for email in self.accounts[self.un]:
count += 1
connection.send("Total emails recived: " + str(count))
def delmsgcmd(self, connection, data):
"""
-Session() function deleats message, with specified sender and subject
-Data in the form [ID, CMD, sender username, subject]
"""
counter = 0
sent = False
for email in self.accounts[self.un]: # for all emails in account
if data[2] in email[0]: # if specified username is in email username slot
if data[3] in email[1]: # if specifed subject is in email subject slot
self.accounts[self.un].pop(counter) # pop email at index location of counter in list of eamils
connection.send("Email deleated. Subject: " + data[3])
print("Email deleated. Subject: " + data[3])
sent = True
counter += 1
if sent == False:
print("Email not found. Subject: " + data[3])
connection.send("KO. Email not found.")
def dumpcmd(self, connection, data):
"""
-Session() function sends client all emails in thir own account
"""
emails = ""
for email in self.accounts[self.un]:
emails += ("Sender: " + str(email[0]) + "\n" + "Subject: " + str(email[1]) + "\n" + "Message: " + str(email[2]) + "\n" + str(email[3])+ "\n \n")
connection.send(b"All emails recived:\n" + emails)
def help_cmd(self, connection):
"""
-Session() function returns all command prompts to client
"""
commands = "\nCommands: \nGet help: help \nSend email: email \nSearch for emails from user: getmsg \nGet number of mesages in inbox: count \nDeleat mesage by sender and subject: delmsg \nSee full email inbox: dump \nLog off of server: logoff\n"
connection.send(commands)
def logoffcmd(self, connection):
"""
-Session() function allows for session loop to exit and responds with OK
"""
self.logged_on = False
connection.send(b"OK")
print("User Loged off")
def main():
"""
-Create server socket for handshake
-Create server class instace and populate accounts dictionary
-Listen for connection and launch server on connect
"""
sockip = ("localhost")
sockport = (1503)
sock_server = socket_server(sockip, sockport)
sock_server.create_accounts()
print("[Socket Server Started on Port " + str(sockport) + "]")
#loop allows server to contune running for multible connections from client
while True:
sock_server.sock.listen(1)
t = Thread(sock_server.connect())
t.start()
main()