From a1099d0ee169307aaee6c69ad9dc837991f87835 Mon Sep 17 00:00:00 2001 From: Benedikt Galbavy Date: Wed, 18 Oct 2023 12:52:33 +0200 Subject: [PATCH] added LIST, READ, DEL command --- mail.cpp | 7 ++- mail.h | 5 +- server.cpp | 144 ++++++++++++++++++++++++++++++++++++++++------- user.cpp | 28 ++++++++- user.h | 1 + user_handler.cpp | 11 ++++ user_handler.h | 2 + 7 files changed, 170 insertions(+), 28 deletions(-) diff --git a/mail.cpp b/mail.cpp index 8bcecb8..c9e8e49 100644 --- a/mail.cpp +++ b/mail.cpp @@ -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; } \ No newline at end of file diff --git a/mail.h b/mail.h index 3212244..958d6da 100644 --- a/mail.h +++ b/mail.h @@ -9,7 +9,6 @@ #include -namespace fs = std::filesystem; using json = nlohmann::json; struct mail { @@ -22,6 +21,8 @@ struct mail { std::vector 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(); diff --git a/server.cpp b/server.cpp index 2eb3b50..d7ecc06 100644 --- a/server.cpp +++ b/server.cpp @@ -3,7 +3,7 @@ #include "mail.h" -#include +#include #include #include #include @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -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& received); +std::string cmdLIST(std::vector& received); +std::string cmdREAD(std::vector& received); +std::string cmdDEL(std::vector& 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 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::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& received) +{ + if (received.at(3).length() > 80) + return "ERR\n"; + + if (received.size() > 5) { + for (std::vector::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& 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& 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& 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; } \ No newline at end of file diff --git a/user.cpp b/user.cpp index eda9493..ad51508 100644 --- a/user.cpp +++ b/user.cpp @@ -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>(); + 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>(); + mail->deleted = mail_json["deleted"]; this->sent.insert(mail); } @@ -79,7 +81,7 @@ void user::sendMail(mail* mail, std::vector recipients) std::vector 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(); } \ No newline at end of file diff --git a/user.h b/user.h index 4f35b6c..8743843 100644 --- a/user.h +++ b/user.h @@ -24,6 +24,7 @@ public: void sendMail(mail* mail, std::vector recipients); mail* getMail(u_int id); + bool delMail(u_int id); maillist getMails() { return this->inbox; }; void saveToFile(); diff --git a/user_handler.cpp b/user_handler.cpp index c5f6461..9866fbf 100644 --- a/user_handler.cpp +++ b/user_handler.cpp @@ -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")) ? diff --git a/user_handler.h b/user_handler.h index 8c6c9b2..2589c9c 100644 --- a/user_handler.h +++ b/user_handler.h @@ -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();