filestoraged/src/server/handlers.cr

186 lines
5.0 KiB
Crystal

require "dodb"
require "base64"
require "../common/utils"
# XXX TODO FIXME: architectural questions
# wonder why I should keep the user upload and download requests
# the server can be just for uploads, delegating downloads to HTTP
# reception of a file chunk
def hdl_transfer(message : FileStorage::Transfer, user : User) : FileStorage::Response
# We received a message containing a chunk of file.
mid = message.mid
mid ||= "no message id"
# Get the transfer info from the db
transfer_info = Context.db_by_filedigest.get message.filedigest
if transfer_info.nil?
# The user has to send an upload request before sending anything
# If not the case, it should be discarded
raise "file not recorded"
end
chunk_number = message.chunk.n
data = Base64.decode message.data
# TODO: verify that the chunk sent was really missing
if transfer_info.chunks.select(chunk_number).size > 0
write_a_chunk user.uid.to_s, transfer_info.file_info, chunk_number, data
else
raise "non existent chunk or already uploaded"
end
remove_chunk_from_db transfer_info, chunk_number
# TODO: verify the digest, if no more chunks
FileStorage::Response.new mid, "Ok"
rescue e
puts "Error handling transfer: #{e.message}"
FileStorage::Response.new mid.not_nil!, "Not Ok", "Unexpected error: #{e.message}"
end
# the client sent an upload request
def hdl_upload(request : FileStorage::UploadRequest, user : User) : FileStorage::Response
mid = request.mid
mid ||= "no message id"
puts "hdl upload: mid=#{request.mid}"
pp! request
# TODO: verify the rights and quotas of the user
# First: check if the file already exists
transfer_info = Context.db_by_filedigest.get? request.file.digest
if transfer_info.nil?
# In case file informations aren't already registered
# which is normal at this point
transfer_info = TransferInfo.new user.uid, request.file
Context.db << transfer_info
else
# File information already exists, request may be duplicated
# In this case: ignore the upload request
end
# file_info.name
# file_info.size
# file_info.nb_chunks
# file_info.digest
# file_info.tags
FileStorage::Response.new request.mid, "Upload OK"
rescue e
puts "Error handling transfer: #{e.message}"
FileStorage::Response.new mid.not_nil!, "Not Ok", "Unexpected error: #{e.message}"
end
# TODO
# the client sent a download request
def hdl_download(request : FileStorage::DownloadRequest,
user : User) : FileStorage::Response
puts "hdl download: mid=#{request.mid}"
pp! request
FileStorage::Response.new request.mid, "Download OK"
end
# Entry point for request management
# Each request should have a response.
# Then, responses are sent in a single message.
def hdl_requests(requests : Array(FileStorage::Request),
user : User,
event : IPC::Event::Message) : Array(FileStorage::Response)
puts "hdl request"
responses = Array(FileStorage::Response).new
requests.each do |request|
case request
when FileStorage::DownloadRequest
responses << hdl_download request, user
when FileStorage::UploadRequest
responses << hdl_upload request, user
else
raise "request not understood"
end
puts
end
responses
end
# store the client in connected_users and users_status
# if already in users_status:
# check if the requests are the same
# if not: add them to the user structure in users_status
def hdl_authentication(event : IPC::Event::Message)
authentication_message =
FileStorage::Authentication.from_json(
String.new event.message.payload
)
userid = authentication_message.token.uid
puts "user authentication: #{userid}"
# Is the user already recorded in users_status?
if Context.users_status[userid]?
puts "We already knew this user"
Context.connected_users[event.connection.fd] = userid
# TODO
pp! Context.connected_users
pp! Context.users_status[userid]
else
# AuthenticationMessage includes requests.
new_user =
User.new authentication_message.token,
authentication_message.uploads,
authentication_message.downloads
Context.connected_users[event.connection.fd] = userid
# record the new user in users_status
Context.users_status[userid] = new_user
puts "New user is: #{new_user.token.login}"
end
# The user is now connected.
user = Context.users_status[userid]
# We verify the user's rights to upload files.
# TODO RIGHTS
# if user wants to upload but not allowed to: Response
# if user wants to get a file but not allowed to: Response
# The user is authorized to upload files.
# TODO: quotas
# Quotas are not defined yet.
responses = hdl_requests [ authentication_message.uploads, authentication_message.downloads ].flatten,
Context.users_status[userid],
event
# Sending a response, containing a response for each request.
# The response is "Ok" when the message is well received and authorized.
response = FileStorage::Responses.new authentication_message.mid, "Ok", responses
event.connection.send FileStorage::MessageType::Responses.to_u8, response.to_json
pp! FileStorage::MessageType::Responses.to_u8
pp! response
end