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) :
|
mail::mail(std::string filename, std::string subject) :
|
||||||
filename(filename),
|
filename(filename),
|
||||||
timestamp(std::time(NULL)),
|
timestamp(std::time(NULL)),
|
||||||
subject(subject)
|
subject(subject),
|
||||||
|
deleted(false)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
mail::mail(std::string filename, int64_t timestamp, std::string subject) :
|
mail::mail(std::string filename, int64_t timestamp, std::string subject) :
|
||||||
filename(filename),
|
filename(filename),
|
||||||
timestamp(timestamp),
|
timestamp(timestamp),
|
||||||
subject(subject)
|
subject(subject),
|
||||||
|
deleted(false)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -39,6 +41,7 @@ json mail::mailToJson()
|
|||||||
jsonfile["recipients"] = this->recipients;
|
jsonfile["recipients"] = this->recipients;
|
||||||
jsonfile["subject"] = this->subject;
|
jsonfile["subject"] = this->subject;
|
||||||
jsonfile["filename"] = this->filename;
|
jsonfile["filename"] = this->filename;
|
||||||
|
jsonfile["deleted"] = this->deleted;
|
||||||
|
|
||||||
return jsonfile;
|
return jsonfile;
|
||||||
}
|
}
|
||||||
5
mail.h
5
mail.h
@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
struct mail {
|
struct mail {
|
||||||
@ -22,6 +21,8 @@ struct mail {
|
|||||||
std::vector<std::string> recipients;
|
std::vector<std::string> recipients;
|
||||||
std::string subject;
|
std::string subject;
|
||||||
|
|
||||||
|
bool deleted;
|
||||||
|
|
||||||
mail(std::string filename, std::string subject);
|
mail(std::string filename, std::string subject);
|
||||||
mail(std::string filename, int64_t timestamp, std::string subject);
|
mail(std::string filename, int64_t timestamp, std::string subject);
|
||||||
|
|
||||||
@ -33,8 +34,6 @@ struct mail {
|
|||||||
return left.timestamp > this->timestamp;
|
return left.timestamp > this->timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::path getPath() { return this->filename; };
|
|
||||||
|
|
||||||
json mailToJson();
|
json mailToJson();
|
||||||
|
|
||||||
void remove();
|
void remove();
|
||||||
|
|||||||
144
server.cpp
144
server.cpp
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include "mail.h"
|
#include "mail.h"
|
||||||
|
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <algorithm>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
#include <locale>
|
#include <locale>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/compute/detail/sha1.hpp>
|
#include <boost/compute/detail/sha1.hpp>
|
||||||
|
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
@ -50,7 +51,13 @@ std::string get_sha1(const std::string& p_arg);
|
|||||||
void *clientCommunication(void *data);
|
void *clientCommunication(void *data);
|
||||||
void signalHandler(int sig);
|
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 void exiting();
|
||||||
|
inline std::string read_file(std::string_view path);
|
||||||
|
|
||||||
user_handler* user_handler::instancePtr = nullptr;
|
user_handler* user_handler::instancePtr = nullptr;
|
||||||
|
|
||||||
@ -193,6 +200,8 @@ void *clientCommunication(void *data)
|
|||||||
std::string incomplete_message = "";
|
std::string incomplete_message = "";
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
std::string response;
|
||||||
|
|
||||||
size = recv(*current_socket, buffer, BUF - 1, 0);
|
size = recv(*current_socket, buffer, BUF - 1, 0);
|
||||||
if (size == -1) {
|
if (size == -1) {
|
||||||
if (abortRequested) {
|
if (abortRequested) {
|
||||||
@ -217,7 +226,7 @@ void *clientCommunication(void *data)
|
|||||||
|
|
||||||
buffer[size] = '\0';
|
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::string line;
|
||||||
|
|
||||||
std::vector<std::string> lines;
|
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), "LIST")) cmd = LIST;
|
||||||
else if (boost::iequals(lines.at(0), "READ")) cmd = READ;
|
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), "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
|
else continue; // TODO: error message
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
@ -243,38 +252,29 @@ void *clientCommunication(void *data)
|
|||||||
continue; // issues if command end is never received
|
continue; // issues if command end is never received
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lines.at(3).length() > 80) {
|
response = cmdSEND(lines);
|
||||||
// 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)}
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case LIST:
|
case LIST:
|
||||||
|
response = cmdLIST(lines);
|
||||||
|
break;
|
||||||
case READ:
|
case READ:
|
||||||
|
response = cmdREAD(lines);
|
||||||
|
break;
|
||||||
case DEL:
|
case DEL:
|
||||||
|
response = cmdDEL(lines);
|
||||||
|
break;
|
||||||
case QUIT:
|
case QUIT:
|
||||||
break;
|
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");
|
perror("send answer failed");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
incomplete_message.clear();
|
incomplete_message.clear();
|
||||||
|
|
||||||
} while (strcmp(buffer, "quit") != 0 && !abortRequested);
|
} while (!abortRequested);
|
||||||
|
|
||||||
// closes/frees the descriptor if not already
|
// closes/frees the descriptor if not already
|
||||||
if (*current_socket != -1) {
|
if (*current_socket != -1) {
|
||||||
@ -354,3 +354,105 @@ inline void exiting()
|
|||||||
user_handler::getInstance()->saveAll();
|
user_handler::getInstance()->saveAll();
|
||||||
printf("Saving... \n");
|
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->id = mail_json["id"];
|
||||||
mail->sender = mail_json["sender"];
|
mail->sender = mail_json["sender"];
|
||||||
mail->recipients = mail_json["recipients"].get<std::vector<std::string>>();
|
mail->recipients = mail_json["recipients"].get<std::vector<std::string>>();
|
||||||
|
mail->deleted = mail_json["deleted"];
|
||||||
|
|
||||||
this->inbox.insert(mail);
|
this->inbox.insert(mail);
|
||||||
}
|
}
|
||||||
@ -39,6 +40,7 @@ user::user(fs::path user_data_json)
|
|||||||
mail->id = mail_json["id"];
|
mail->id = mail_json["id"];
|
||||||
mail->sender = mail_json["sender"];
|
mail->sender = mail_json["sender"];
|
||||||
mail->recipients = mail_json["recipients"].get<std::vector<std::string>>();
|
mail->recipients = mail_json["recipients"].get<std::vector<std::string>>();
|
||||||
|
mail->deleted = mail_json["deleted"];
|
||||||
|
|
||||||
this->sent.insert(mail);
|
this->sent.insert(mail);
|
||||||
}
|
}
|
||||||
@ -79,7 +81,7 @@ void user::sendMail(mail* mail, std::vector<std::string> recipients)
|
|||||||
std::vector<user*> users;
|
std::vector<user*> users;
|
||||||
for ( auto& name : recipients) {
|
for ( auto& name : recipients) {
|
||||||
// TODO: error handling for non existing user
|
// 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;
|
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;
|
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()
|
void user::saveToFile()
|
||||||
{
|
{
|
||||||
printf("%s\n", this->user_data.dump().c_str());
|
|
||||||
std::fstream fs(this->file_location);
|
std::fstream fs(this->file_location);
|
||||||
fs << this->user_data.dump();
|
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);
|
void sendMail(mail* mail, std::vector<std::string> recipients);
|
||||||
|
|
||||||
mail* getMail(u_int id);
|
mail* getMail(u_int id);
|
||||||
|
bool delMail(u_int id);
|
||||||
maillist getMails() { return this->inbox; };
|
maillist getMails() { return this->inbox; };
|
||||||
|
|
||||||
void saveToFile();
|
void saveToFile();
|
||||||
|
|||||||
@ -14,6 +14,17 @@ user_handler::user_handler()
|
|||||||
}
|
}
|
||||||
|
|
||||||
user* user_handler::getUser(std::string name)
|
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()) {
|
if (this->users.find(name) == this->users.end()) {
|
||||||
this->users[name] = fs::exists(this->spool_dir/"users"/(name+".json")) ?
|
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; };
|
void setSpoolDir(fs::path p) { this->spool_dir = p; };
|
||||||
fs::path getSpoolDir() { return this->spool_dir; };
|
fs::path getSpoolDir() { return this->spool_dir; };
|
||||||
|
|
||||||
user* getUser(std::string name);
|
user* getUser(std::string name);
|
||||||
|
user* getOrCreateUser(std::string name);
|
||||||
|
|
||||||
void saveAll();
|
void saveAll();
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user