Compare commits

...

10 Commits

Author SHA1 Message Date
10cd5d01a9 removed readme and macos file 2023-12-17 20:21:23 +01:00
e8a7cd68d7 detach instead of join
forgot to push
2023-11-27 21:18:53 +01:00
2c7b64233d remove failed attempts after success 2023-11-19 23:34:01 +01:00
61c41aefeb added ip banning 2023-11-19 21:49:40 +01:00
f66d895326 some cleanup 2023-11-19 18:32:56 +01:00
ad9ce0d2d0 fixed memory leaks and boolean 2023-11-19 17:49:27 +01:00
5124ff12e9 fixed login
has memory leaks
2023-11-19 17:43:31 +01:00
Patrik Karasek
e8f7d8e55d Login fehler 2023-11-18 19:26:59 +01:00
Patrik Karasek
1d9bf5d298 LDAP Login 2023-11-18 16:31:54 +01:00
8272a16e90 fix to range 2023-11-18 15:20:14 +01:00
7 changed files with 419 additions and 133 deletions

View File

@ -10,7 +10,7 @@ BUILD_DIR = build
SOURCES_CLIENT = client.cpp SOURCES_CLIENT = client.cpp
OBJS_CLIENT = $(addprefix $(BUILD_DIR)/,$(SOURCES_CLIENT:.cpp=.o)) OBJS_CLIENT = $(addprefix $(BUILD_DIR)/,$(SOURCES_CLIENT:.cpp=.o))
SOURCES_SERVER = server.cpp user.cpp user_handler.cpp mail.cpp SOURCES_SERVER = server.cpp user.cpp user_handler.cpp mail.cpp ip_ban.cpp
OBJS_SERVER = $(addprefix $(BUILD_DIR)/,$(SOURCES_SERVER:.cpp=.o)) OBJS_SERVER = $(addprefix $(BUILD_DIR)/,$(SOURCES_SERVER:.cpp=.o))
all: $(BUILD_DIR) $(TARGET) all: $(BUILD_DIR) $(TARGET)

View File

@ -1,9 +0,0 @@
# Documentation
### File access
Files are stored with their SHA-1 value as their name, with the first two digits being the subfolder name. For each user files belonging to them a represented in a .json file, which is managed by a central process; on runtime contents are stored in memory for thread safety.
Potential issues:
- Accessing actual files
- Safing on exit

View File

@ -10,6 +10,8 @@
#include <string> #include <string>
#include <string.h> #include <string.h>
#include <termios.h>
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#define BUF 1024 #define BUF 1024
@ -18,7 +20,8 @@
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
enum commands { enum commands {
SEND = 1, LOGIN = 1,
SEND,
LIST, LIST,
READ, READ,
DEL, DEL,
@ -27,6 +30,7 @@ enum commands {
inline bool isInteger(const std::string & s); inline bool isInteger(const std::string & s);
void printUsage(); void printUsage();
const std::string getpass();
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
@ -107,10 +111,12 @@ int main(int argc, char **argv)
} }
*/ */
bool isLoggedIn = false;
std::string loggedInUsername;
do { do {
printf("Please specify a command (SEND, LIST, READ, DEL, QUIT): "); printf("Please specify a command (%s): ", isLoggedIn ? "SEND, LIST, READ, DEL, QUIT" : "LOGIN, QUIT");
if (fgets(buffer, BUF - 1, stdin) != NULL) if (fgets(buffer, BUF - 1, stdin) != NULL) {
{
size = strlen(buffer); size = strlen(buffer);
if (buffer[size - 2] == '\r' && buffer[size - 1] == '\n') if (buffer[size - 2] == '\r' && buffer[size - 1] == '\n')
{ {
@ -124,19 +130,39 @@ int main(int argc, char **argv)
} }
enum commands cmd; enum commands cmd;
if (strcmp(buffer, "SEND") == 0) cmd = SEND; if (strcmp(buffer, "LOGIN") == 0) cmd = LOGIN;
else if (strcmp(buffer, "SEND") == 0) cmd = SEND;
else if (strcmp(buffer, "LIST") == 0) cmd = LIST; else if (strcmp(buffer, "LIST") == 0) cmd = LIST;
else if (strcmp(buffer, "READ") == 0) cmd = READ; else if (strcmp(buffer, "READ") == 0) cmd = READ;
else if (strcmp(buffer, "DEL") == 0) cmd = DEL; else if (strcmp(buffer, "DEL") == 0) cmd = DEL;
else if (strcmp(buffer, "QUIT") == 0) break; else if (strcmp(buffer, "QUIT") == 0) break;
char username[BUF], msgNum[10]; char msgNum[10];
std::string username, password;
switch (cmd) { switch (cmd) {
case LOGIN:
if (isLoggedIn) {
printf("Already logged in as %s\n", loggedInUsername.c_str());
continue;
}
printf("LDAP Username: ");
fgets(buffer, BUF - 1, stdin);
username = buffer;
username.pop_back();
printf("Password: ");
password = getpass();
snprintf(buffer, sizeof(buffer), "LOGIN\n%s\n%s\n", username.c_str(), password.c_str());
break;
case SEND: case SEND:
char sender[BUF], receiver[BUF], subject[81], message[BUF * 10]; if (!isLoggedIn) {
printf("Sender: "); printf("Please login first.\n\n");
fgets(sender, BUF - 1, stdin); continue;;
}
char receiver[BUF], subject[81], message[BUF * 10];
printf("Receiver: "); printf("Receiver: ");
fgets(receiver, BUF - 1, stdin); fgets(receiver, BUF - 1, stdin);
printf("Subject: "); printf("Subject: ");
@ -144,37 +170,42 @@ int main(int argc, char **argv)
printf("Message (send by typing \".\" in a seperate line): "); printf("Message (send by typing \".\" in a seperate line): ");
char line[BUF]; char line[BUF];
message[0] = '\0'; message[0] = '\0';
while (true) while (true) {
{
fgets(line, BUF - 1, stdin); fgets(line, BUF - 1, stdin);
if (strcmp(line, ".\n") == 0) if (strcmp(line, ".\n") == 0)
break; break;
strcat(message, line); strcat(message, line);
} }
snprintf(buffer, sizeof(buffer), "SEND\n%s%s%s%s.\n", sender, receiver, subject, message); snprintf(buffer, sizeof(buffer), "SEND\n%s%s%s.\n", receiver, subject, message);
break; break;
case LIST: case LIST:
char username[BUF]; if (!isLoggedIn) {
printf("Username: "); printf("Please login first.\n");
fgets(username, BUF - 1, stdin); continue;
snprintf(buffer, sizeof(buffer), "LIST\n%s", username); }
break; snprintf(buffer, sizeof(buffer), "LIST\n");
printf("%s", buffer);
break;
case READ: case READ:
printf("Username: "); if (!isLoggedIn) {
fgets(username, BUF - 1, stdin); printf("Please login first.\n");
printf("Message Number: "); continue;
fgets(msgNum, 9, stdin); }
snprintf(buffer, sizeof(buffer), "READ\n%s%s", username, msgNum); printf("Message Number: ");
break; fgets(msgNum, 9, stdin);
snprintf(buffer, sizeof(buffer), "READ\n%s", msgNum);
break;
case DEL: case DEL:
printf("Username: "); if (!isLoggedIn) {
fgets(username, BUF - 1, stdin); printf("Please login first.\n");
printf("Message Number: "); continue;
fgets(msgNum, 9, stdin); }
snprintf(buffer, sizeof(buffer), "DEL\n%s%s", username, msgNum); printf("Message Number: ");
break; fgets(msgNum, 9, stdin);
snprintf(buffer, sizeof(buffer), "DEL\n%s", msgNum);
break;
case QUIT: case QUIT:
// will break out of loop before quit // should never be reached;
break; break;
} }
@ -227,6 +258,12 @@ int main(int argc, char **argv)
{ {
buffer[size] = '\0'; buffer[size] = '\0';
printf("<< %s\n", buffer); // ignore error printf("<< %s\n", buffer); // ignore error
if (cmd == LOGIN && strcmp("OK\n", buffer) == 0) {
loggedInUsername = username;
isLoggedIn = true;
}
/*if (strcmp("OK", buffer) != 0) // needs proper verification, since responses vary between commands /*if (strcmp("OK", buffer) != 0) // needs proper verification, since responses vary between commands
{ {
fprintf(stderr, "<< Server error occured, abort\n"); fprintf(stderr, "<< Server error occured, abort\n");
@ -269,4 +306,76 @@ inline bool isInteger(const std::string & s)
void printUsage() void printUsage()
{ {
printf("Usage: <twmailer-server [host/ip (default: 127.0.0.1)] [port]>\n"); printf("Usage: <twmailer-server [host/ip (default: 127.0.0.1)] [port]>\n");
}
int getch()
{
int ch;
// https://man7.org/linux/man-pages/man3/termios.3.html
struct termios t_old, t_new;
// https://man7.org/linux/man-pages/man3/termios.3.html
// tcgetattr() gets the parameters associated with the object referred
// by fd and stores them in the termios structure referenced by
// termios_p
tcgetattr(STDIN_FILENO, &t_old);
// copy old to new to have a base for setting c_lflags
t_new = t_old;
// https://man7.org/linux/man-pages/man3/termios.3.html
//
// ICANON Enable canonical mode (described below).
// * Input is made available line by line (max 4096 chars).
// * In noncanonical mode input is available immediately.
//
// ECHO Echo input characters.
t_new.c_lflag &= ~(ICANON | ECHO);
// sets the attributes
// TCSANOW: the change occurs immediately.
tcsetattr(STDIN_FILENO, TCSANOW, &t_new);
ch = getchar();
// reset stored attributes
tcsetattr(STDIN_FILENO, TCSANOW, &t_old);
return ch;
}
const std::string getpass()
{
int show_asterisk = 0;
const char BACKSPACE = 127;
const char RETURN = 10;
unsigned char ch = 0;
std::string password;
while ((ch = getch()) != RETURN)
{
if (ch == BACKSPACE)
{
if (password.length() != 0)
{
if (show_asterisk)
{
printf("\b \b"); // backslash: \b
}
password.resize(password.length() - 1);
}
}
else
{
password += ch;
if (show_asterisk)
{
printf("*");
}
}
}
printf("\n");
return password;
} }

59
server/ip_ban.cpp Normal file
View File

@ -0,0 +1,59 @@
#include "ip_ban.h"
#include <chrono>
#include <filesystem>
#include <iterator>
#include <fstream>
ip_ban::ip_ban() : m_ban()
{
this->file = fs::path();
}
ip_ban::~ip_ban()
{
if (this->file.empty())
return;
json jsonfile = this->ban_list;
std::ofstream ofs(this->file, std::ofstream::out | std::ofstream::trunc);
ofs << jsonfile.dump();
}
void ip_ban::loadFile(fs::path file)
{
this->file = file;
if (fs::exists(file)) {
std::ifstream ifs(file);
this->ban_list = json::parse(ifs);
}
}
void ip_ban::failedAttempt(std::string username, std::string ip)
{
std::unique_lock<std::shared_mutex> lock(this->m_ban);
std::map<std::string, std::pair<std::map<std::string, ushort>, time_t>>::iterator it = this->ban_list.insert({ip, {{}, 0}}).first;
if (++it->second.first.insert({username, 0}).first->second >= MAX_ATTEMPTS) { // increase attempt count && check if reached MAX_ATTEMPTS
for (auto& pair : it->second.first ) { // reset attempts to 0 for all names
pair.second = 0;
}
(*it).second.second = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) + BAN_TIME; // set unban time to current time + BAN_TIME seconds
}
}
void ip_ban::success(std::string ip)
{
std::unique_lock<std::shared_mutex> lock(this->m_ban);
this->ban_list.erase(ip);
}
bool ip_ban::checkBanned(std::string ip)
{
std::shared_lock<std::shared_mutex> lock(this->m_ban);
std::map<std::string, std::pair<std::map<std::string, ushort>, time_t>>::iterator it;
return (it = this->ban_list.find(ip)) != this->ban_list.end() && (*it).second.second > std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
}

48
server/ip_ban.h Normal file
View File

@ -0,0 +1,48 @@
#pragma once
#include <filesystem>
#include <map>
#include <mutex>
#include <shared_mutex>
#include <string>
#include <chrono>
#include <nlohmann/json.hpp>
#define BAN_TIME 60
#define MAX_ATTEMPTS 3
namespace fs = std::filesystem;
using json = nlohmann::json;
class ip_ban {
public:
static ip_ban& getInstance() {
static ip_ban instance;
return instance;
};
ip_ban(ip_ban const&) = delete;
ip_ban(ip_ban&&) = delete;
ip_ban& operator=(ip_ban const&) = delete;
ip_ban& operator=(ip_ban &&) = delete;
void loadFile(fs::path file);
void failedAttempt(std::string username, std::string ip);
void success(std::string ip);
bool checkBanned(std::string ip);
protected:
ip_ban();
~ip_ban();
std::shared_mutex m_ban;
fs::path file;
std::map<std::string, std::pair<std::map<std::string, ushort>, time_t>> ban_list;
};

View File

@ -1,5 +1,4 @@
#include "mail.h" #include "mail.h"
#include <mutex>
mail::mail(std::string filename, std::string subject, std::string sender) : mail::mail(std::string filename, std::string subject, std::string sender) :
filename(filename), filename(filename),

View File

@ -1,8 +1,15 @@
#include "ip_ban.h"
#include "user.h" #include "user.h"
#include "user_handler.h" #include "user_handler.h"
#include "mail.h" #include "mail.h"
#include <ldap.h>
#include <algorithm>
#include <ranges>
#include <cstddef> #include <cstddef>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
@ -21,7 +28,6 @@
#include <string> #include <string>
#include <thread> #include <thread>
#include <vector> #include <vector>
#include <algorithm>
#include <locale> #include <locale>
@ -31,6 +37,9 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <signal.h> #include <signal.h>
#include <openssl/sha.h> #include <openssl/sha.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define BUF 1024 #define BUF 1024
@ -49,7 +58,7 @@ bool abortRequested = false;
int create_socket = -1; int create_socket = -1;
int new_socket = -1; int new_socket = -1;
std::vector<pthread_t> threads; // std::vector<pthread_t> threads;
void printUsage(); void printUsage();
inline bool isInteger(const std::string & s); inline bool isInteger(const std::string & s);
@ -63,15 +72,23 @@ std::string getSha1(const std::string& p_arg);
void *clientCommunication(void *data); void *clientCommunication(void *data);
void signalHandler(int sig); void signalHandler(int sig);
std::string cmdLOGIN(std::vector<std::string>& received); std::string cmdLOGIN(std::vector<std::string>& received, std::string& loggedInUsername, const std::string& ip);
std::string cmdSEND(std::vector<std::string>& received); std::string cmdSEND(std::vector<std::string>& received, const std::string& loggedInUsername);
std::string cmdLIST(std::vector<std::string>& received); std::string cmdLIST(std::vector<std::string>& received, const std::string& loggedInUsername);
std::string cmdREAD(std::vector<std::string>& received); std::string cmdREAD(std::vector<std::string>& received, const std::string& loggedInUsername);
std::string cmdDEL(std::vector<std::string>& received); std::string cmdDEL(std::vector<std::string>& received, const std::string& loggedInUsername);
inline void exiting(); inline void exiting();
inline std::string read_file(std::string_view path); inline std::string read_file(std::string_view path);
struct args
{
int socket;
std::string ip;
fs::path spool_dir;
};
int main (int argc, char* argv[]) int main (int argc, char* argv[])
{ {
if (argc < 3 || if (argc < 3 ||
@ -97,6 +114,8 @@ int main (int argc, char* argv[])
user_handler::getInstance().setSpoolDir(spool_dir); user_handler::getInstance().setSpoolDir(spool_dir);
ip_ban::getInstance().loadFile(spool_dir / "bans.json");
std::atexit(exiting); std::atexit(exiting);
char* p; char* p;
@ -172,8 +191,9 @@ int main (int argc, char* argv[])
ntohs(cliaddress.sin_port)); ntohs(cliaddress.sin_port));
// clientCommunication(&new_socket); // returnValue can be ignored // clientCommunication(&new_socket); // returnValue can be ignored
pthread_t tid; pthread_t tid;
pthread_create(&tid, NULL, clientCommunication, (void *)new int(new_socket)); pthread_create(&tid, NULL, clientCommunication, static_cast<void *>(new args{new_socket, inet_ntoa(cliaddress.sin_addr), spool_dir}));
threads.push_back(tid); // threads.push_back(tid);
pthread_detach(tid);
new_socket = -1; new_socket = -1;
} }
@ -210,12 +230,17 @@ void printUsage()
void *clientCommunication(void *data) void *clientCommunication(void *data)
{ {
args* args = (struct args*) data;
char buffer[BUF]; char buffer[BUF];
int size; int size;
int *current_socket = (int *)data; int *current_socket = &args->socket;
std::string ip = args->ip;
std::string incomplete_message = ""; std::string incomplete_message = "";
std::string loggedInUsername;
do { do {
std::string response; std::string response;
@ -251,7 +276,6 @@ void *clientCommunication(void *data)
lines.push_back(line); lines.push_back(line);
} }
enum commands cmd; enum commands cmd;
// can't wait for reflections (maybe c++26?) // can't wait for reflections (maybe c++26?)
@ -265,27 +289,29 @@ void *clientCommunication(void *data)
switch (cmd) { switch (cmd) {
case LOGIN: case LOGIN:
response = cmdLOGIN(lines); response = cmdLOGIN(lines, loggedInUsername, ip);
break; break;
case SEND: case SEND:
if (lines.size() < 5 || lines.back().compare(".") != 0) { if (lines.size() < 5 || lines.back().compare(".") != 0) {
incomplete_message = buffer; incomplete_message = buffer;
continue; // issues if command end is never received continue;
} }
response = cmdSEND(lines, loggedInUsername);
response = cmdSEND(lines); break;
break;
case LIST: case LIST:
response = cmdLIST(lines); response = cmdLIST(lines, loggedInUsername);
break; break;
case READ: case READ:
response = cmdREAD(lines); response = cmdREAD(lines, loggedInUsername);
break; break;
case DEL: case DEL:
response = cmdDEL(lines); response = cmdDEL(lines, loggedInUsername);
break; break;
case QUIT: case QUIT:
break; break;
default:
// invalid command
response = "ERR\n";
} }
if (send(*current_socket, response.c_str(), response.size(), 0) == -1) { if (send(*current_socket, response.c_str(), response.size(), 0) == -1) {
@ -308,7 +334,7 @@ void *clientCommunication(void *data)
*current_socket = -1; *current_socket = -1;
} }
delete(current_socket); delete(args);
return NULL; return NULL;
} }
@ -374,102 +400,155 @@ std::string getSha1(const std::string& str)
inline void exiting() inline void exiting()
{ {
for (auto& thread : threads) { /*for (auto& thread : threads) {
pthread_join(thread, NULL); pthread_join(thread, NULL);
} }*/
user_handler::getInstance().saveAll(); user_handler::getInstance().saveAll();
printf("Saving... \n"); printf("Saving... \n");
} }
std::string cmdLOGIN(std::vector<std::string>& received) std::string cmdLOGIN(std::vector<std::string>& received, std::string& loggedInUsername, const std::string& ip)
{ {
// code if (received.size() < 3) {
return ""; return "ERR\n";
}
const char* ldapUri = "ldap://ldap.technikum-wien.at:389";
const int ldapVersion = LDAP_VERSION3;
LDAP* ldapHandle;
int rc = ldap_initialize(&ldapHandle, ldapUri);
if (rc != LDAP_SUCCESS) {
return "ERR\n";
}
rc = ldap_set_option(ldapHandle, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion);
if (rc != LDAP_OPT_SUCCESS) {
ldap_unbind_ext_s(ldapHandle, NULL, NULL);
return "ERR\n";
}
rc = ldap_start_tls_s(ldapHandle, NULL, NULL);
if (rc != LDAP_SUCCESS) {
ldap_unbind_ext_s(ldapHandle, NULL, NULL);
return "ERR\n";
}
std::string ldapBindUser = "uid=" + received.at(1) + ",ou=people,dc=technikum-wien,dc=at";
std::string ldapBindPassword = received.at(2);
BerValue bindCredentials;
bindCredentials.bv_val = (char*) ldapBindPassword.c_str();
bindCredentials.bv_len = ldapBindPassword.length();
rc = ldap_sasl_bind_s(ldapHandle, ldapBindUser.c_str(), LDAP_SASL_SIMPLE, &bindCredentials, NULL, NULL, NULL);
if (rc != LDAP_SUCCESS) {
ip_ban::getInstance().failedAttempt(received.at(1), ip);
ldap_unbind_ext_s(ldapHandle, NULL, NULL);
return "ERR\n";
}
if (ip_ban::getInstance().checkBanned(ip)) {
ldap_unbind_ext_s(ldapHandle, NULL, NULL);
return "ERR\n";
}
loggedInUsername = received.at(1);
ip_ban::getInstance().success(ip);
ldap_unbind_ext_s(ldapHandle, NULL, NULL);
return "OK\n";
} }
std::string cmdSEND(std::vector<std::string>& received)
{
// TODO: change sender to be implicit from currently logged in; replace received.at(1) and move all other received one forward
if (received.at(3).length() > 80)
return "ERR\n";
if (received.size() > 5) { std::string cmdSEND(std::vector<std::string>& received, const std::string& loggedInUsername) {
for (std::vector<std::string>::iterator it = received.begin() + 5; it != received.end() && *it != "."; it++) {
received.at(4).append("\n").append(*it);
}
}
user_handler::getInstance().getOrCreateUser(received.at(2))->addMail( if (loggedInUsername.empty() || received.size() < 4) {
new struct mail(saveToFile(user_handler::getInstance().getSpoolDir()/"messages", received.at(4)), received.at(3), received.at(1)) return "ERR\n";
); }
std::string receiver = received.at(1);
std::string subject = received.at(2);
std::string message = received.at(3);
if (subject.length() > 80) {
return "ERR\n";
}
user_handler::getInstance().getOrCreateUser(receiver)->addMail(
new struct mail(saveToFile(user_handler::getInstance().getSpoolDir()/"messages", message), subject, loggedInUsername)
);
return "OK\n"; // TODO: error handling return "OK\n"; // TODO: error handling
} }
std::string cmdLIST(std::vector<std::string>& received) std::string cmdLIST(std::vector<std::string>& received, const std::string& loggedInUsername) {
{ if (loggedInUsername.empty()) {
// TODO: change user to be implicit from currently logged in; replace received.at(1) printf("%s %zu\n", loggedInUsername.c_str(), received.size());
maillist inbox; return "ERR\n";
user* user; }
if (received.size() < 2 || (user = user_handler::getInstance().getUser(received.at(1))) == nullptr) user* currentUser = user_handler::getInstance().getUser(loggedInUsername);
return "0\n"; if (currentUser == 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; maillist inbox = currentUser->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) {
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 cmdREAD(std::vector<std::string>& received, const std::string& loggedInUsername) {
{ if (loggedInUsername.empty() || received.size() < 2) {
// TODO: change user to be implicit from currently logged in; replace received.at(1) and move received.at(2) one forward printf("%s %zu\n", loggedInUsername.c_str(), received.size());
std::string response = "OK\n"; return "ERR\n";
}
user* user; char* p;
mail* mail; long mailId = strtol(received.at(1).c_str(), &p, 10);
if (*p != 0) {
return "ERR\n";
}
char* p; user* currentUser = user_handler::getInstance().getUser(loggedInUsername);
if (currentUser == nullptr) {
return "ERR\n";
}
if(received.size() < 3 || mail* selectedMail = currentUser->getMail(mailId);
!isInteger(received.at(2)) || if (selectedMail == nullptr || selectedMail->deleted) {
(user = user_handler::getInstance().getUser(received.at(1))) == nullptr || return "ERR\n";
(mail = user->getMail(strtoul(received.at(2).c_str(), &p, 10))) == nullptr || }
mail->deleted)
return "ERR\n";
try { std::string mailContent = read_file((user_handler::getInstance().getSpoolDir() / "messages" / selectedMail->filename).string());
std::string path_str = user_handler::getInstance().getSpoolDir()/"messages"/mail->filename; return "OK\n" + mailContent;
std::unique_lock<std::mutex> lock(mail->m_file);
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) std::string cmdDEL(std::vector<std::string>& received, const std::string& loggedInUsername) {
{ if (loggedInUsername.empty() || received.size() < 2) {
// TODO: change user to be implicit from currently logged in; replace received.at(1) and move received.at(2) one forward printf("%s %zu\n", loggedInUsername.c_str(), received.size());
user* user; return "ERR\n";
}
char* p; char* p;
long mailId = strtol(received.at(1).c_str(), &p, 10);
if (*p != 0) {
return "ERR\n";
}
if(received.size() < 3 || user* currentUser = user_handler::getInstance().getUser(loggedInUsername);
!isInteger(received.at(2)) || if (currentUser == nullptr) {
(user = user_handler::getInstance().getUser(received.at(1))) == nullptr || return "ERR\n";
(user->delMail(strtoul(received.at(2).c_str(), &p, 10))) == false) }
return "ERR\n";
return "OK\n"; bool deleteResult = currentUser->delMail(mailId);
return deleteResult ? "OK\n" : "ERR\n";
} }
// https://stackoverflow.com/a/116220 // https://stackoverflow.com/a/116220
@ -500,5 +579,6 @@ bool ichar_equals(char a, char b)
bool iequals(std::string_view lhs, std::string_view rhs) bool iequals(std::string_view lhs, std::string_view rhs)
{ {
return std::ranges::equal(lhs, rhs, ichar_equals); // return std::ranges::equal(lhs, rhs, ichar_equals);
return std::equal(lhs.begin(), lhs.end(), rhs.begin(), ichar_equals);
} }