added LIST, READ, DEL command
This commit is contained in:
parent
e0501259a7
commit
a1099d0ee1
7
mail.cpp
7
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;
|
||||
}
|
||||
5
mail.h
5
mail.h
@ -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();
|
||||
|
||||
144
server.cpp
144
server.cpp
@ -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;
|
||||
}
|
||||
28
user.cpp
28
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<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
1
user.h
@ -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();
|
||||
|
||||
@ -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")) ?
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user