Compare commits
10 Commits
25ef503889
...
10cd5d01a9
| Author | SHA1 | Date | |
|---|---|---|---|
| 10cd5d01a9 | |||
| e8a7cd68d7 | |||
| 2c7b64233d | |||
| 61c41aefeb | |||
| f66d895326 | |||
| ad9ce0d2d0 | |||
| 5124ff12e9 | |||
|
|
e8f7d8e55d | ||
|
|
1d9bf5d298 | ||
| 8272a16e90 |
2
Makefile
2
Makefile
@ -10,7 +10,7 @@ BUILD_DIR = build
|
||||
SOURCES_CLIENT = client.cpp
|
||||
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))
|
||||
|
||||
all: $(BUILD_DIR) $(TARGET)
|
||||
|
||||
@ -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
|
||||
@ -10,6 +10,8 @@
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
|
||||
#include <termios.h>
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define BUF 1024
|
||||
@ -18,7 +20,8 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
enum commands {
|
||||
SEND = 1,
|
||||
LOGIN = 1,
|
||||
SEND,
|
||||
LIST,
|
||||
READ,
|
||||
DEL,
|
||||
@ -27,6 +30,7 @@ enum commands {
|
||||
|
||||
inline bool isInteger(const std::string & s);
|
||||
void printUsage();
|
||||
const std::string getpass();
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
@ -107,10 +111,12 @@ int main(int argc, char **argv)
|
||||
}
|
||||
*/
|
||||
|
||||
bool isLoggedIn = false;
|
||||
std::string loggedInUsername;
|
||||
|
||||
do {
|
||||
printf("Please specify a command (SEND, LIST, READ, DEL, QUIT): ");
|
||||
if (fgets(buffer, BUF - 1, stdin) != NULL)
|
||||
{
|
||||
printf("Please specify a command (%s): ", isLoggedIn ? "SEND, LIST, READ, DEL, QUIT" : "LOGIN, QUIT");
|
||||
if (fgets(buffer, BUF - 1, stdin) != NULL) {
|
||||
size = strlen(buffer);
|
||||
if (buffer[size - 2] == '\r' && buffer[size - 1] == '\n')
|
||||
{
|
||||
@ -124,19 +130,39 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
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, "READ") == 0) cmd = READ;
|
||||
else if (strcmp(buffer, "DEL") == 0) cmd = DEL;
|
||||
else if (strcmp(buffer, "QUIT") == 0) break;
|
||||
|
||||
char username[BUF], msgNum[10];
|
||||
char msgNum[10];
|
||||
std::string username, password;
|
||||
|
||||
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:
|
||||
char sender[BUF], receiver[BUF], subject[81], message[BUF * 10];
|
||||
printf("Sender: ");
|
||||
fgets(sender, BUF - 1, stdin);
|
||||
if (!isLoggedIn) {
|
||||
printf("Please login first.\n\n");
|
||||
continue;;
|
||||
}
|
||||
char receiver[BUF], subject[81], message[BUF * 10];
|
||||
printf("Receiver: ");
|
||||
fgets(receiver, BUF - 1, stdin);
|
||||
printf("Subject: ");
|
||||
@ -144,37 +170,42 @@ int main(int argc, char **argv)
|
||||
printf("Message (send by typing \".\" in a seperate line): ");
|
||||
char line[BUF];
|
||||
message[0] = '\0';
|
||||
while (true)
|
||||
{
|
||||
while (true) {
|
||||
fgets(line, BUF - 1, stdin);
|
||||
if (strcmp(line, ".\n") == 0)
|
||||
break;
|
||||
strcat(message, line);
|
||||
}
|
||||
snprintf(buffer, sizeof(buffer), "SEND\n%s%s%s%s.\n", sender, receiver, subject, message);
|
||||
break;
|
||||
snprintf(buffer, sizeof(buffer), "SEND\n%s%s%s.\n", receiver, subject, message);
|
||||
break;
|
||||
case LIST:
|
||||
char username[BUF];
|
||||
printf("Username: ");
|
||||
fgets(username, BUF - 1, stdin);
|
||||
snprintf(buffer, sizeof(buffer), "LIST\n%s", username);
|
||||
break;
|
||||
if (!isLoggedIn) {
|
||||
printf("Please login first.\n");
|
||||
continue;
|
||||
}
|
||||
snprintf(buffer, sizeof(buffer), "LIST\n");
|
||||
printf("%s", buffer);
|
||||
break;
|
||||
case READ:
|
||||
printf("Username: ");
|
||||
fgets(username, BUF - 1, stdin);
|
||||
printf("Message Number: ");
|
||||
fgets(msgNum, 9, stdin);
|
||||
snprintf(buffer, sizeof(buffer), "READ\n%s%s", username, msgNum);
|
||||
break;
|
||||
if (!isLoggedIn) {
|
||||
printf("Please login first.\n");
|
||||
continue;
|
||||
}
|
||||
printf("Message Number: ");
|
||||
fgets(msgNum, 9, stdin);
|
||||
snprintf(buffer, sizeof(buffer), "READ\n%s", msgNum);
|
||||
break;
|
||||
case DEL:
|
||||
printf("Username: ");
|
||||
fgets(username, BUF - 1, stdin);
|
||||
printf("Message Number: ");
|
||||
fgets(msgNum, 9, stdin);
|
||||
snprintf(buffer, sizeof(buffer), "DEL\n%s%s", username, msgNum);
|
||||
break;
|
||||
if (!isLoggedIn) {
|
||||
printf("Please login first.\n");
|
||||
continue;
|
||||
}
|
||||
printf("Message Number: ");
|
||||
fgets(msgNum, 9, stdin);
|
||||
snprintf(buffer, sizeof(buffer), "DEL\n%s", msgNum);
|
||||
break;
|
||||
case QUIT:
|
||||
// will break out of loop before quit
|
||||
// should never be reached;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -227,6 +258,12 @@ int main(int argc, char **argv)
|
||||
{
|
||||
buffer[size] = '\0';
|
||||
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
|
||||
{
|
||||
fprintf(stderr, "<< Server error occured, abort\n");
|
||||
@ -270,3 +307,75 @@ void printUsage()
|
||||
{
|
||||
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
59
server/ip_ban.cpp
Normal 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
48
server/ip_ban.h
Normal 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;
|
||||
|
||||
};
|
||||
@ -1,5 +1,4 @@
|
||||
#include "mail.h"
|
||||
#include <mutex>
|
||||
|
||||
mail::mail(std::string filename, std::string subject, std::string sender) :
|
||||
filename(filename),
|
||||
|
||||
@ -1,8 +1,15 @@
|
||||
#include "ip_ban.h"
|
||||
#include "user.h"
|
||||
#include "user_handler.h"
|
||||
|
||||
#include "mail.h"
|
||||
|
||||
|
||||
#include <ldap.h>
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <ranges>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
@ -21,7 +28,6 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include <locale>
|
||||
|
||||
@ -31,6 +37,9 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <signal.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define BUF 1024
|
||||
|
||||
@ -49,7 +58,7 @@ bool abortRequested = false;
|
||||
int create_socket = -1;
|
||||
int new_socket = -1;
|
||||
|
||||
std::vector<pthread_t> threads;
|
||||
// std::vector<pthread_t> threads;
|
||||
|
||||
void printUsage();
|
||||
inline bool isInteger(const std::string & s);
|
||||
@ -63,15 +72,23 @@ std::string getSha1(const std::string& p_arg);
|
||||
void *clientCommunication(void *data);
|
||||
void signalHandler(int sig);
|
||||
|
||||
std::string cmdLOGIN(std::vector<std::string>& received);
|
||||
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);
|
||||
std::string cmdLOGIN(std::vector<std::string>& received, std::string& loggedInUsername, const std::string& ip);
|
||||
std::string cmdSEND(std::vector<std::string>& received, const std::string& loggedInUsername);
|
||||
std::string cmdLIST(std::vector<std::string>& received, const std::string& loggedInUsername);
|
||||
std::string cmdREAD(std::vector<std::string>& received, const std::string& loggedInUsername);
|
||||
std::string cmdDEL(std::vector<std::string>& received, const std::string& loggedInUsername);
|
||||
|
||||
|
||||
inline void exiting();
|
||||
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[])
|
||||
{
|
||||
if (argc < 3 ||
|
||||
@ -97,6 +114,8 @@ int main (int argc, char* argv[])
|
||||
|
||||
user_handler::getInstance().setSpoolDir(spool_dir);
|
||||
|
||||
ip_ban::getInstance().loadFile(spool_dir / "bans.json");
|
||||
|
||||
std::atexit(exiting);
|
||||
|
||||
char* p;
|
||||
@ -172,8 +191,9 @@ int main (int argc, char* argv[])
|
||||
ntohs(cliaddress.sin_port));
|
||||
// clientCommunication(&new_socket); // returnValue can be ignored
|
||||
pthread_t tid;
|
||||
pthread_create(&tid, NULL, clientCommunication, (void *)new int(new_socket));
|
||||
threads.push_back(tid);
|
||||
pthread_create(&tid, NULL, clientCommunication, static_cast<void *>(new args{new_socket, inet_ntoa(cliaddress.sin_addr), spool_dir}));
|
||||
// threads.push_back(tid);
|
||||
pthread_detach(tid);
|
||||
new_socket = -1;
|
||||
}
|
||||
|
||||
@ -210,12 +230,17 @@ void printUsage()
|
||||
|
||||
void *clientCommunication(void *data)
|
||||
{
|
||||
args* args = (struct args*) data;
|
||||
|
||||
char buffer[BUF];
|
||||
int size;
|
||||
int *current_socket = (int *)data;
|
||||
int *current_socket = &args->socket;
|
||||
std::string ip = args->ip;
|
||||
|
||||
std::string incomplete_message = "";
|
||||
|
||||
std::string loggedInUsername;
|
||||
|
||||
do {
|
||||
std::string response;
|
||||
|
||||
@ -251,7 +276,6 @@ void *clientCommunication(void *data)
|
||||
lines.push_back(line);
|
||||
}
|
||||
|
||||
|
||||
enum commands cmd;
|
||||
|
||||
// can't wait for reflections (maybe c++26?)
|
||||
@ -265,27 +289,29 @@ void *clientCommunication(void *data)
|
||||
|
||||
switch (cmd) {
|
||||
case LOGIN:
|
||||
response = cmdLOGIN(lines);
|
||||
response = cmdLOGIN(lines, loggedInUsername, ip);
|
||||
break;
|
||||
case SEND:
|
||||
if (lines.size() < 5 || lines.back().compare(".") != 0) {
|
||||
incomplete_message = buffer;
|
||||
continue; // issues if command end is never received
|
||||
}
|
||||
|
||||
response = cmdSEND(lines);
|
||||
break;
|
||||
incomplete_message = buffer;
|
||||
continue;
|
||||
}
|
||||
response = cmdSEND(lines, loggedInUsername);
|
||||
break;
|
||||
case LIST:
|
||||
response = cmdLIST(lines);
|
||||
response = cmdLIST(lines, loggedInUsername);
|
||||
break;
|
||||
case READ:
|
||||
response = cmdREAD(lines);
|
||||
response = cmdREAD(lines, loggedInUsername);
|
||||
break;
|
||||
case DEL:
|
||||
response = cmdDEL(lines);
|
||||
response = cmdDEL(lines, loggedInUsername);
|
||||
break;
|
||||
case QUIT:
|
||||
break;
|
||||
default:
|
||||
// invalid command
|
||||
response = "ERR\n";
|
||||
}
|
||||
|
||||
if (send(*current_socket, response.c_str(), response.size(), 0) == -1) {
|
||||
@ -308,7 +334,7 @@ void *clientCommunication(void *data)
|
||||
*current_socket = -1;
|
||||
}
|
||||
|
||||
delete(current_socket);
|
||||
delete(args);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -374,104 +400,157 @@ std::string getSha1(const std::string& str)
|
||||
|
||||
inline void exiting()
|
||||
{
|
||||
for (auto& thread : threads) {
|
||||
/*for (auto& thread : threads) {
|
||||
pthread_join(thread, NULL);
|
||||
}
|
||||
}*/
|
||||
|
||||
user_handler::getInstance().saveAll();
|
||||
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
|
||||
return "";
|
||||
}
|
||||
if (received.size() < 3) {
|
||||
return "ERR\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)
|
||||
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";
|
||||
}
|
||||
|
||||
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::getInstance().getOrCreateUser(received.at(2))->addMail(
|
||||
new struct mail(saveToFile(user_handler::getInstance().getSpoolDir()/"messages", received.at(4)), received.at(3), received.at(1))
|
||||
);
|
||||
|
||||
return "OK\n"; // TODO: error handling
|
||||
}
|
||||
|
||||
std::string cmdLIST(std::vector<std::string>& received)
|
||||
{
|
||||
// TODO: change user to be implicit from currently logged in; replace received.at(1)
|
||||
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)
|
||||
{
|
||||
// TODO: change user to be implicit from currently logged in; replace received.at(1) and move received.at(2) one forward
|
||||
std::string response = "OK\n";
|
||||
|
||||
user* user;
|
||||
mail* mail;
|
||||
|
||||
char* p;
|
||||
|
||||
if(received.size() < 3 ||
|
||||
!isInteger(received.at(2)) ||
|
||||
(user = user_handler::getInstance().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::getInstance().getSpoolDir()/"messages"/mail->filename;
|
||||
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)
|
||||
{
|
||||
// TODO: change user to be implicit from currently logged in; replace received.at(1) and move received.at(2) one forward
|
||||
user* user;
|
||||
|
||||
char* p;
|
||||
|
||||
if(received.size() < 3 ||
|
||||
!isInteger(received.at(2)) ||
|
||||
(user = user_handler::getInstance().getUser(received.at(1))) == nullptr ||
|
||||
(user->delMail(strtoul(received.at(2).c_str(), &p, 10))) == false)
|
||||
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, const std::string& loggedInUsername) {
|
||||
|
||||
if (loggedInUsername.empty() || received.size() < 4) {
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
std::string cmdLIST(std::vector<std::string>& received, const std::string& loggedInUsername) {
|
||||
if (loggedInUsername.empty()) {
|
||||
printf("%s %zu\n", loggedInUsername.c_str(), received.size());
|
||||
return "ERR\n";
|
||||
}
|
||||
|
||||
user* currentUser = user_handler::getInstance().getUser(loggedInUsername);
|
||||
if (currentUser == nullptr) {
|
||||
return "0\n";
|
||||
}
|
||||
|
||||
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, const std::string& loggedInUsername) {
|
||||
if (loggedInUsername.empty() || received.size() < 2) {
|
||||
printf("%s %zu\n", loggedInUsername.c_str(), received.size());
|
||||
return "ERR\n";
|
||||
}
|
||||
|
||||
char* p;
|
||||
long mailId = strtol(received.at(1).c_str(), &p, 10);
|
||||
if (*p != 0) {
|
||||
return "ERR\n";
|
||||
}
|
||||
|
||||
user* currentUser = user_handler::getInstance().getUser(loggedInUsername);
|
||||
if (currentUser == nullptr) {
|
||||
return "ERR\n";
|
||||
}
|
||||
|
||||
mail* selectedMail = currentUser->getMail(mailId);
|
||||
if (selectedMail == nullptr || selectedMail->deleted) {
|
||||
return "ERR\n";
|
||||
}
|
||||
|
||||
std::string mailContent = read_file((user_handler::getInstance().getSpoolDir() / "messages" / selectedMail->filename).string());
|
||||
return "OK\n" + mailContent;
|
||||
}
|
||||
|
||||
std::string cmdDEL(std::vector<std::string>& received, const std::string& loggedInUsername) {
|
||||
if (loggedInUsername.empty() || received.size() < 2) {
|
||||
printf("%s %zu\n", loggedInUsername.c_str(), received.size());
|
||||
return "ERR\n";
|
||||
}
|
||||
|
||||
char* p;
|
||||
long mailId = strtol(received.at(1).c_str(), &p, 10);
|
||||
if (*p != 0) {
|
||||
return "ERR\n";
|
||||
}
|
||||
|
||||
user* currentUser = user_handler::getInstance().getUser(loggedInUsername);
|
||||
if (currentUser == nullptr) {
|
||||
return "ERR\n";
|
||||
}
|
||||
|
||||
bool deleteResult = currentUser->delMail(mailId);
|
||||
return deleteResult ? "OK\n" : "ERR\n";
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/116220
|
||||
inline std::string read_file(std::string_view path)
|
||||
{
|
||||
@ -500,5 +579,6 @@ bool ichar_equals(char a, char b)
|
||||
|
||||
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);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user