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
|
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)
|
||||||
|
|||||||
@ -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>
|
||||||
#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);
|
}
|
||||||
|
snprintf(buffer, sizeof(buffer), "LIST\n");
|
||||||
|
printf("%s", buffer);
|
||||||
break;
|
break;
|
||||||
case READ:
|
case READ:
|
||||||
printf("Username: ");
|
if (!isLoggedIn) {
|
||||||
fgets(username, BUF - 1, stdin);
|
printf("Please login first.\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
printf("Message Number: ");
|
printf("Message Number: ");
|
||||||
fgets(msgNum, 9, stdin);
|
fgets(msgNum, 9, stdin);
|
||||||
snprintf(buffer, sizeof(buffer), "READ\n%s%s", username, msgNum);
|
snprintf(buffer, sizeof(buffer), "READ\n%s", msgNum);
|
||||||
break;
|
break;
|
||||||
case DEL:
|
case DEL:
|
||||||
printf("Username: ");
|
if (!isLoggedIn) {
|
||||||
fgets(username, BUF - 1, stdin);
|
printf("Please login first.\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
printf("Message Number: ");
|
printf("Message Number: ");
|
||||||
fgets(msgNum, 9, stdin);
|
fgets(msgNum, 9, stdin);
|
||||||
snprintf(buffer, sizeof(buffer), "DEL\n%s%s", username, msgNum);
|
snprintf(buffer, sizeof(buffer), "DEL\n%s", msgNum);
|
||||||
break;
|
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");
|
||||||
@ -270,3 +307,75 @@ 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
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 "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),
|
||||||
|
|||||||
@ -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 "";
|
|
||||||
}
|
|
||||||
|
|
||||||
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";
|
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(
|
const char* ldapUri = "ldap://ldap.technikum-wien.at:389";
|
||||||
new struct mail(saveToFile(user_handler::getInstance().getSpoolDir()/"messages", received.at(4)), received.at(3), received.at(1))
|
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, 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
|
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);
|
||||||
|
if (currentUser == nullptr) {
|
||||||
return "0\n";
|
return "0\n";
|
||||||
|
}
|
||||||
|
|
||||||
inbox = user->getMails();
|
maillist inbox = currentUser->getMails();
|
||||||
std::string response = std::to_string(std::count_if(inbox.begin(), inbox.end(), [](auto& mail) { return !mail->deleted; })) + "\n";
|
std::string response = std::to_string(std::count_if(inbox.begin(), inbox.end(), [](auto& mail) { return !mail->deleted; })) + "\n";
|
||||||
for ( auto mail : inbox ) {
|
for (auto& mail : inbox) {
|
||||||
if (mail->deleted)
|
if (!mail->deleted) {
|
||||||
continue;
|
|
||||||
response.append(std::to_string(mail->id)).append(": ").append(mail->subject).append("\n");
|
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;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string cmdDEL(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());
|
||||||
user* user;
|
return "ERR\n";
|
||||||
|
}
|
||||||
|
|
||||||
char* p;
|
char* p;
|
||||||
|
long mailId = strtol(received.at(1).c_str(), &p, 10);
|
||||||
if(received.size() < 3 ||
|
if (*p != 0) {
|
||||||
!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";
|
return "ERR\n";
|
||||||
|
}
|
||||||
|
|
||||||
return "OK\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
|
// 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);
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user