added LIST, READ, DEL command

This commit is contained in:
Benedikt Galbavy 2023-10-18 12:52:33 +02:00
parent e0501259a7
commit a1099d0ee1
7 changed files with 170 additions and 28 deletions

View File

@ -3,13 +3,15 @@
mail::mail(std::string filename, std::string subject) :
filename(filename),
timestamp(std::time(NULL)),
subject(subject)
subject(subject),
deleted(false)
{}
mail::mail(std::string filename, int64_t timestamp, std::string subject) :
filename(filename),
timestamp(timestamp),
subject(subject)
subject(subject),
deleted(false)
{}
/*
@ -39,6 +41,7 @@ json mail::mailToJson()
jsonfile["recipients"] = this->recipients;
jsonfile["subject"] = this->subject;
jsonfile["filename"] = this->filename;
jsonfile["deleted"] = this->deleted;
return jsonfile;
}

5
mail.h
View File

@ -9,7 +9,6 @@
#include <nlohmann/json.hpp>
namespace fs = std::filesystem;
using json = nlohmann::json;
struct mail {
@ -22,6 +21,8 @@ struct mail {
std::vector<std::string> recipients;
std::string subject;
bool deleted;
mail(std::string filename, std::string subject);
mail(std::string filename, int64_t timestamp, std::string subject);
@ -33,8 +34,6 @@ struct mail {
return left.timestamp > this->timestamp;
}
fs::path getPath() { return this->filename; };
json mailToJson();
void remove();

View File

@ -3,7 +3,7 @@
#include "mail.h"
#include <boost/algorithm/string/predicate.hpp>
#include <algorithm>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
@ -15,6 +15,7 @@
#include <locale>
#include <nlohmann/json.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/compute/detail/sha1.hpp>
#include <netinet/in.h>
@ -50,7 +51,13 @@ std::string get_sha1(const std::string& p_arg);
void *clientCommunication(void *data);
void signalHandler(int sig);
std::string cmdSEND(std::vector<std::string>& received);
std::string cmdLIST(std::vector<std::string>& received);
std::string cmdREAD(std::vector<std::string>& received);
std::string cmdDEL(std::vector<std::string>& received);
inline void exiting();
inline std::string read_file(std::string_view path);
user_handler* user_handler::instancePtr = nullptr;
@ -193,6 +200,8 @@ void *clientCommunication(void *data)
std::string incomplete_message = "";
do {
std::string response;
size = recv(*current_socket, buffer, BUF - 1, 0);
if (size == -1) {
if (abortRequested) {
@ -217,7 +226,7 @@ void *clientCommunication(void *data)
buffer[size] = '\0';
std::stringstream ss(incomplete_message.append(buffer));
std::stringstream ss(incomplete_message.append(buffer)); //append instead of adding more lines to fix potentially split lines
std::string line;
std::vector<std::string> lines;
@ -233,7 +242,7 @@ void *clientCommunication(void *data)
else if (boost::iequals(lines.at(0), "LIST")) cmd = LIST;
else if (boost::iequals(lines.at(0), "READ")) cmd = READ;
else if (boost::iequals(lines.at(0), "DEL")) cmd = DEL;
else if (boost::iequals(lines.at(0), "QUIT")) cmd = QUIT;
else if (boost::iequals(lines.at(0), "QUIT")) break;
else continue; // TODO: error message
switch (cmd) {
@ -243,38 +252,29 @@ void *clientCommunication(void *data)
continue; // issues if command end is never received
}
if (lines.at(3).length() > 80) {
// send error
break;
}
if (lines.size() > 5) {
for (std::vector<std::string>::iterator it = lines.begin() + 5; it != lines.end() && *it != "."; it++) {
lines.at(4).append("\n").append(*it);
}
}
user_handler::getInstance()->getUser(lines.at(1))->sendMail(
new struct mail(saveToFile(user_handler::getInstance()->getSpoolDir()/"messages", lines.at(4)), lines.at(3)),
{lines.at(2)}
);
response = cmdSEND(lines);
break;
case LIST:
response = cmdLIST(lines);
break;
case READ:
response = cmdREAD(lines);
break;
case DEL:
response = cmdDEL(lines);
break;
case QUIT:
break;
}
if (send(*current_socket, "OK\n", 3, 0) == -1) {
if (send(*current_socket, response.c_str(), response.size(), 0) == -1) {
perror("send answer failed");
return NULL;
}
incomplete_message.clear();
} while (strcmp(buffer, "quit") != 0 && !abortRequested);
} while (!abortRequested);
// closes/frees the descriptor if not already
if (*current_socket != -1) {
@ -353,4 +353,106 @@ inline void exiting()
{
user_handler::getInstance()->saveAll();
printf("Saving... \n");
}
std::string cmdSEND(std::vector<std::string>& received)
{
if (received.at(3).length() > 80)
return "ERR\n";
if (received.size() > 5) {
for (std::vector<std::string>::iterator it = received.begin() + 5; it != received.end() && *it != "."; it++) {
received.at(4).append("\n").append(*it);
}
}
user_handler* user_handler = user_handler::getInstance();
user_handler->getOrCreateUser(received.at(1))->sendMail(
new struct mail(saveToFile(user_handler->getSpoolDir()/"messages", received.at(4)), received.at(3)),
{received.at(2)}
);
return "OK\n"; // TODO: error handling
}
std::string cmdLIST(std::vector<std::string>& received)
{
maillist inbox;
user* user;
if (received.size() < 2 || (user = user_handler::getInstance()->getUser(received.at(1))) == nullptr)
return "0\n";
inbox = user->getMails();
std::string response = std::to_string(std::count_if(inbox.begin(), inbox.end(), [](auto& mail) { return !mail->deleted; })) + "\n";
for ( auto mail : inbox ) {
if (mail->deleted)
continue;
response.append(std::to_string(mail->id)).append(": ").append(mail->subject).append("\n");
}
return response;
}
std::string cmdREAD(std::vector<std::string>& received)
{
std::string response = "OK\n";
user_handler* user_handler = user_handler::getInstance();
user* user;
mail* mail;
char* p;
if(received.size() < 3 ||
!isInteger(received.at(2)) ||
(user = user_handler->getUser(received.at(1))) == nullptr ||
(mail = user->getMail(strtoul(received.at(2).c_str(), &p, 10))) == nullptr ||
mail->deleted)
return "ERR\n";
try {
std::string path_str = user_handler->getSpoolDir()/"messages"/mail->filename;
response.append(read_file(path_str)).append("\n");
}
catch (...) { // TODO: more specific error handling - then again, it will respond with ERR either way
return "ERR\n";
}
return response;
}
std::string cmdDEL(std::vector<std::string>& received)
{
user_handler* user_handler = user_handler::getInstance();
user* user;
char* p;
if(received.size() < 3 ||
!isInteger(received.at(2)) ||
(user = user_handler->getUser(received.at(1))) == nullptr ||
(user->delMail(strtoul(received.at(2).c_str(), &p, 10))) == false)
return "ERR\n";
return "OK\n";
}
// https://stackoverflow.com/a/116220
inline std::string read_file(std::string_view path)
{
constexpr auto read_size = std::size_t(4096);
auto stream = std::ifstream(path.data());
stream.exceptions(std::ios_base::badbit);
if (not stream) {
throw std::ios_base::failure("file does not exist");
}
auto out = std::string();
auto buf = std::string(read_size, '\0');
while (stream.read(& buf[0], read_size)) {
out.append(buf, 0, stream.gcount());
}
out.append(buf, 0, stream.gcount());
return out;
}

View File

@ -26,6 +26,7 @@ user::user(fs::path user_data_json)
mail->id = mail_json["id"];
mail->sender = mail_json["sender"];
mail->recipients = mail_json["recipients"].get<std::vector<std::string>>();
mail->deleted = mail_json["deleted"];
this->inbox.insert(mail);
}
@ -39,6 +40,7 @@ user::user(fs::path user_data_json)
mail->id = mail_json["id"];
mail->sender = mail_json["sender"];
mail->recipients = mail_json["recipients"].get<std::vector<std::string>>();
mail->deleted = mail_json["deleted"];
this->sent.insert(mail);
}
@ -79,7 +81,7 @@ void user::sendMail(mail* mail, std::vector<std::string> recipients)
std::vector<user*> users;
for ( auto& name : recipients) {
// TODO: error handling for non existing user
users.push_back(user_handler->getUser(name));
users.push_back(user_handler->getOrCreateUser(name));
}
mail->sender = this->name;
@ -101,9 +103,31 @@ mail* user::getMail(u_int id)
return it == this->inbox.end() ? nullptr : (*it)->filename.empty() ? nullptr : *it;
}
bool user::delMail(u_int id)
{
maillist::iterator it = std::find_if(this->inbox.begin(), this->inbox.end(), [id](auto& i){ return (*i)(id); });
bool success = true;
if (it == this->inbox.end() ||
(*it)->deleted)
return false;
if (!(*it)->filename.empty())
success = fs::remove(user_handler::getInstance()->getSpoolDir()/"messages"/(*it)->filename);
if (success) {
this->user_data["mails"]["received"][std::to_string((*it)->id)]["subject"] = "";
this->user_data["mails"]["received"][std::to_string((*it)->id)]["filename"] = "";
this->user_data["mails"]["received"][std::to_string((*it)->id)]["deleted"] = true;
(*it)->deleted = true; // other info will be deleted on shutdown
}
return success;
}
void user::saveToFile()
{
printf("%s\n", this->user_data.dump().c_str());
std::fstream fs(this->file_location);
fs << this->user_data.dump();
}

1
user.h
View File

@ -24,6 +24,7 @@ public:
void sendMail(mail* mail, std::vector<std::string> recipients);
mail* getMail(u_int id);
bool delMail(u_int id);
maillist getMails() { return this->inbox; };
void saveToFile();

View File

@ -14,6 +14,17 @@ user_handler::user_handler()
}
user* user_handler::getUser(std::string name)
{
if (this->users.find(name) == this->users.end()) {
if (!fs::exists(this->spool_dir/"users"/(name+".json")))
return nullptr;
this->users[name] = new user(this->spool_dir/"users"/(name+".json"));
}
return this->users[name];
}
user* user_handler::getOrCreateUser(std::string name)
{
if (this->users.find(name) == this->users.end()) {
this->users[name] = fs::exists(this->spool_dir/"users"/(name+".json")) ?

View File

@ -26,7 +26,9 @@ public:
void setSpoolDir(fs::path p) { this->spool_dir = p; };
fs::path getSpoolDir() { return this->spool_dir; };
user* getUser(std::string name);
user* getOrCreateUser(std::string name);
void saveAll();