diff --git a/README.md b/README.md index f1c324e..1d0d25b 100644 --- a/README.md +++ b/README.md @@ -1 +1,158 @@ -# Networks Lab 2019 (Higher School of Economics) +# Сервис учета ошибок "Bug tracking" + +## Формат сообщения + + header: int message type + body: content depends on message type + +## Описание запросов + +Разработчики и тестеры идентифицируются по id. Каждая зарегистрированная ошибка также имеет id + +- Запрос на авторизацю пользователя + + type: 100 + content: int user_id + + - Успех + + type: 101 + content: int user_role + + - Пользователь не найден + + type: 102 + content: int user_id + + - Пользователь уже авторизован + + type: 103 + content: int user_id + int user_role + +- Выдача тестеру списка ошибок: bug_status - 0 - активная, 1 - закрытая + + type: 200 + content: bug_status + + - Успех + + type: 201 + content: int number of bugs + [ + int bug_id + int project_id + int description length + char[] description + ] + +- Подтверждение/отклонение исправления активной ошибки: verification_code - 0 - отклонение, 1 - подтверждение + + type: 300 + content: int bug_id + int verification_code + + - Успех + + type: 301 + content: int bug_id + int verification_code + + - Ошибка уже не активна + + type: 302 + content: int bug_status + + - Ошибки с таким кодом не существует + + type: 303 + content: int bug_id + + - Ошибки еще не исправлена + + type: 304 + content: int bug_id + + - Неизвстный код верификации + + type: 305 + content: int verification_code + + +- Выдача ошибок разработчику + + type: 400 + content: + + - Успех + + type: 401 + content: int number of bugs + [ + int bug_id + int description length + char[] description + ] + - Неизвестный id разработчика + + type: 402 + content: int user_id + +- Исправления активной ошибки разработчиком + + type: 500 + content: int bug_id + + - Успех + + type: 501 + content: int bug_id + + - Задача уже закрыта + + type: 502 + content: int bug_status + + - Задача не найдена + + type: 503 + content: int bug_id + +- Прием новой ошибки + + type: 600 + content: + int bug_id + int developer_id + int project_id + int description length + char[] description + + - Успех + + type: 601 + content: int bug_id + + - Ошибка с таким id уже существует + + type: 602 + content: int bug_id + +- Отключение клиента + + type: 700 + content: + +- Сообщение о неизвестном типе запроса + + type: 1 + content: int type + +- Сообщение об отутсвии авторизации + + type: 2 + +- Сообщение об отутсвии прав (неверная роль; 0 - developer, 1 - tester) + + type: 3 + content: required role diff --git a/tcp/client/CMakeLists.txt b/tcp/client/CMakeLists.txt new file mode 100644 index 0000000..905d4d9 --- /dev/null +++ b/tcp/client/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.13) +project(client) + +set(CMAKE_CXX_STANDARD 17) + +add_executable(client src/main.cpp src/client.cpp src/client.h) \ No newline at end of file diff --git a/tcp/client/src/client.cpp b/tcp/client/src/client.cpp new file mode 100644 index 0000000..67185fb --- /dev/null +++ b/tcp/client/src/client.cpp @@ -0,0 +1,371 @@ +#include +#include +#include "client.h" + +client::client(const char *host, uint16_t port_number) { + sockfd = socket(AF_INET, SOCK_STREAM, 0); + hostent* server = gethostbyname(host); + + if (sockfd < 0) { + std::cerr << "ERROR opening socket\n"; + exit(1); + } + + if (server == nullptr) { + std::cerr << "ERROR, no such host\n"; + exit(0); + } + + sockaddr_in serv_addr{}; + bzero((char *) &serv_addr, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + bcopy(server->h_addr, (char *) &serv_addr.sin_addr.s_addr, (size_t) server->h_length); + serv_addr.sin_port = htons(port_number); + + /* Now connect to the server */ + if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { + std::cerr << "ERROR connecting\n"; + exit(1); + } + std::cout << "Connection established\n"; +} + +void client::registerUser() { + int usr_id; + std::cin >> usr_id; + sendNum(REGISTER_USER); + sendNum(usr_id); + int answer_type = getNum(); + switch (answer_type) { + case REGISTER_USER_SUCC: { + int role = getNum(); + std::cout << "Register user with id: " + std::to_string(role) + '\n'; + break; + } + case REGISTER_USER_UNDEFINED_ID: { + int type = getNum(); + std::cout << "Can't register user id " + std::to_string(type) + '\n'; + break; + } + case REGISTER_USER_AUTORIZE: { + int user_id = getNum(); + int user_role = getNum(); + std::cout << "User with id " << user_id << " and role " << user_role << " already authorized\n"; + break; + } + default: { + std::cout << "Undefined server code\n"; + } + } +} + +int client::getNum() { + int num; + int n = read(sockfd, &num, sizeof(int32_t)); + if (n < 0) { + terminate(); + } + return ntohl(num); +} + +void client::sendNum(int num) { + num = htonl(num); + write(sockfd, &num, sizeof(int32_t)); +} + +void client::sendString(std::string string_to_send) { + int length = string_to_send.length(); + sendNum(length); + write(sockfd, string_to_send.c_str(), length); +} + +std::string client::getString() { + int length = getNum(); + char* buf = new char[length + 1]; + int n = read(sockfd, buf, length); + buf[length] = '\0'; + std::string ans(buf); + delete[] buf; + if (n < 0) { + terminate(); + } + return ans; +} + +void client::terminate() { + std::cout << "Program end\n"; + exit(0); +} + +void client::getBugsTester() { + std::string bug_type; + bug_status bug_code = UNDEFINED; + std::cin >> bug_type; + if (bug_type == "active") { + bug_code = ACTIVE; + } + if (bug_type == "close") { + bug_code = CLOSE; + } + if (bug_code == UNDEFINED) { + std::cout << "Undefined type of bug: " + bug_type; + } + sendNum(GET_TESTER_BUG_LIST); + sendNum(bug_code); + int type = getNum(); + switch (type) { + case GET_TESTER_BUG_LIST_SUCC: { + int bug_count = getNum(); + for (int i = 0; i < bug_count; ++i) { + int bug_id = getNum(); + int project_id = getNum(); + std::string description = getString(); + std::cout << "Bug with id: " << bug_id << "project id: " << project_id << " description: " << description; + } + break; + } + case UNKNOWN_REQUEST: { + int ver_code = getNum(); + std::cout << "Unknown request code " << ver_code << '\n'; + break; + } + case UNAUTORIZE: { + std::cout << "You not authorize\n"; + break; + } + case CHECK_RIGHTS: { + std::cout << "You don't have rights\n"; + break; + } + default: { + std::cout << "Undefined server code\n"; + } + } +} + +void client::confirmBugTester() { + int bug_id; + std::cin >> bug_id; + sendNum(PROCESS_BUG_TESTER); + sendNum(bug_id); + sendNum(ACCEPT); + int type = getNum(); + switch (type) { + case PROCESS_BUG_TESTER_SUCC: { + bug_id = getNum(); + std::cout << "Accept bug with id: " << bug_id << '\n'; + break; + } + case PROCESS_BUG_TESTER_NON_ACTIVE_BUG: { + int bug_status = getNum(); + std::cout << "With bug was close with status " << bug_status << '\n'; + break; + } + case PROCESS_BUG_TESTER_NO_BUG_ID: { + bug_id = getNum(); + std::cout << "No bug with id: " << bug_id << '\n'; + break; + } + case PROCESS_BUG_TESTER_NOT_FIX: { + bug_id = getNum(); + std::cout << "Bug with id: " << bug_id << ' not fix' << '\n'; + break; + } + case PROCESS_BUG_TESTER_UNKNOWN_CODE: { + int ver_code = getNum(); + std::cout << "Unknown verify code " << ver_code << '\n'; + break; + } + case UNKNOWN_REQUEST: { + int ver_code = getNum(); + std::cout << "Unknown request code " << ver_code << '\n'; + break; + } + case UNAUTORIZE: { + std::cout << "You not authorize\n"; + break; + } + case CHECK_RIGHTS: { + std::cout << "You don't have rights\n"; + break; + } + default: { + std::cout << "Undefined server code\n"; + } + } +} + +void client::rejectBugTester() { + int bug_id; + std::cin >> bug_id; + sendNum(PROCESS_BUG_TESTER); + sendNum(bug_id); + sendNum(DECLINE); + int type = getNum(); + switch (type) { + case PROCESS_BUG_TESTER_SUCC: { + bug_id = getNum(); + std::cout << "Accept bug with id: " << bug_id << '\n'; + break; + } + case PROCESS_BUG_TESTER_NON_ACTIVE_BUG: { + int bug_status = getNum(); + std::cout << "With bug was close with status " << bug_status << '\n'; + break; + } + case PROCESS_BUG_TESTER_NO_BUG_ID: { + bug_id = getNum(); + std::cout << "No bug with id: " << bug_id << '\n'; + break; + } + case PROCESS_BUG_TESTER_NOT_FIX: { + bug_id = getNum(); + std::cout << "Bug with id: " << bug_id << ' not fix' << '\n'; + break; + } + case PROCESS_BUG_TESTER_UNKNOWN_CODE: { + int ver_code = getNum(); + std::cout << "Unknown verify code " << ver_code << '\n'; + break; + } + case UNKNOWN_REQUEST: { + int ver_code = getNum(); + std::cout << "Unknown request code " << ver_code << '\n'; + break; + } + case UNAUTORIZE: { + std::cout << "You not authorize\n"; + break; + } + case CHECK_RIGHTS: { + std::cout << "You don't have rights\n"; + break; + } + default: { + std::cout << "Undefined server code\n"; + } + } +} + +void client::getBugsDeveloper() { + sendNum(GET_DEVELOPER_BUGS); + int type = getNum(); + switch (type) { + case GET_DEVELOPER_BUGS_SUCC: { + int bugs_count = getNum(); + for (int i = 0; i < bugs_count; ++i) { + int bug_id = getNum(); + std::string description = getString(); + std::cout << "Bug with id: " << bug_id << " with description: " << description << '\n'; + } + break; + } + case GET_DEVELOPER_BUGS_NO_ID: { + int developer_id = getNum(); + std::cout << "No developer with id: " << developer_id << '\n'; + break; + } + case UNKNOWN_REQUEST: { + int ver_code = getNum(); + std::cout << "Unknown request code " << ver_code << '\n'; + break; + } + case UNAUTORIZE: { + std::cout << "You not authorize\n"; + break; + } + case CHECK_RIGHTS: { + std::cout << "You don't have rights\n"; + break; + } + default: { + std::cout << "Undefined server code\n"; + break; + } + } +} + +void client::closeBugDeveloper() { + int bud_id; + std::cin >> bud_id; + sendNum(CLOSE_BUG_DEVELOPER); + sendNum(bud_id); + int type = getNum(); + switch (type) { + case CLOSE_BUG_DEVELOPER_SUCC: { + bud_id = getNum(); + std::cout << "Clode bug with id: " << bud_id << '\n'; + break; + } + case CLOSE_BUG_DEVELOPER_NO_ID: { + bud_id = getNum(); + std::cout << "No bug with id: " << bud_id << '\n'; + break; + } + case CLOSE_BUG_DEVELOPER_ALREADY_CLOSE: { + int bug_status = getNum(); + std::cout << "Bug already close : " << bug_status << '\n'; + break; + } + case UNKNOWN_REQUEST: { + int ver_code = getNum(); + std::cout << "Unknown request code " << ver_code << '\n'; + break; + } + case UNAUTORIZE: { + std::cout << "You not authorize\n"; + break; + } + case CHECK_RIGHTS: { + std::cout << "You don't have rights\n"; + break; + } + default: { + std::cout << "Undefined server code\n"; + break; + } + } +} + +void client::addNewBug() { + int developer_id = 0, project_id = 0, bug_id = 0; + std::string description; + std::cout << bug_id << developer_id << project_id << description; + sendNum(SEND_NEW_BUG); + sendNum(bug_id); + sendNum(developer_id); + sendNum(project_id); + sendString(description); + int type = getNum(); + switch (type) { + case SEND_NEW_BUG_SUCC: { + int bug_id = getNum(); + std::cout << "Create new bug with id: " << bug_id << '\n'; + break; + } + case SEND_NEW_BUG_EXIST: { + int bug_id = getNum(); + std::cout << "Bud with id: " << bug_id << "exist \n"; + break; + } + case UNKNOWN_REQUEST: { + int ver_code = getNum(); + std::cout << "Unknown request code " << ver_code << '\n'; + break; + } + case UNAUTORIZE: { + std::cout << "You not authorize\n"; + break; + } + case CHECK_RIGHTS: { + std::cout << "You don't have rights\n"; + break; + } + default:break; + } +} + +void client::quit() { + sendNum(CLIENT_TERMINATE); + terminate(); +} diff --git a/tcp/client/src/client.h b/tcp/client/src/client.h new file mode 100644 index 0000000..e2da73a --- /dev/null +++ b/tcp/client/src/client.h @@ -0,0 +1,63 @@ +#ifndef CLIENT_TESTER_H +#define CLIENT_TESTER_H + +#include +#include +#include +#include +#include + +enum bug_status {ACTIVE = 0, CLOSE = 1, UNDEFINED = 3}; +enum verification_code {DECLINE = 0, ACCEPT = 1}; +enum message_code { + REGISTER_USER = 100, + REGISTER_USER_SUCC = 101, + REGISTER_USER_UNDEFINED_ID = 102, + REGISTER_USER_AUTORIZE = 103, + GET_TESTER_BUG_LIST = 200, + GET_TESTER_BUG_LIST_SUCC = 201, + PROCESS_BUG_TESTER = 300, + PROCESS_BUG_TESTER_SUCC = 301, + PROCESS_BUG_TESTER_NON_ACTIVE_BUG = 302, + PROCESS_BUG_TESTER_NO_BUG_ID = 303, + PROCESS_BUG_TESTER_NOT_FIX = 304, + PROCESS_BUG_TESTER_UNKNOWN_CODE = 305, + GET_DEVELOPER_BUGS = 400, + GET_DEVELOPER_BUGS_SUCC = 401, + GET_DEVELOPER_BUGS_NO_ID = 402, + CLOSE_BUG_DEVELOPER = 500, + CLOSE_BUG_DEVELOPER_SUCC = 501, + CLOSE_BUG_DEVELOPER_NO_ID = 502, + CLOSE_BUG_DEVELOPER_ALREADY_CLOSE = 503, + SEND_NEW_BUG = 600, + SEND_NEW_BUG_SUCC = 601, + SEND_NEW_BUG_EXIST = 602, + CLIENT_TERMINATE = 700, + UNKNOWN_REQUEST = 1, + UNAUTORIZE = 2, + CHECK_RIGHTS = 3, +}; + +class client { + public: + client(const char* host, uint16_t port_number); + void registerUser(); + void getBugsTester(); + void confirmBugTester(); + void rejectBugTester(); + void getBugsDeveloper(); + void closeBugDeveloper(); + void addNewBug(); + void quit(); + + private: + int sockfd; + int getNum(); + void sendNum(int num); + void sendString(std::string string_to_send); + std::string getString(); + void terminate(); +}; + + +#endif //CLIENT_TESTER_H diff --git a/tcp/client/src/main.cpp b/tcp/client/src/main.cpp new file mode 100644 index 0000000..78f09e3 --- /dev/null +++ b/tcp/client/src/main.cpp @@ -0,0 +1,40 @@ +#include +#include +#include "client.h" + +int main(int argc, char *argv[]) { + if (argc < 2) { + std::cout << "Invalid arguments"; + exit(0); + } + int port_number = std::stoi(argv[2]); + client cl(argv[1], port_number); + while (true) { + std::string command; + std::cin >> command; + if (command == "create_new_bug") { + cl.addNewBug(); + } + if (command == "register_user") { + cl.registerUser(); + } + if (command == "get_bugs") { + cl.getBugsTester(); + } + if (command == "get_developer_bugs") { + cl.getBugsDeveloper(); + } + if (command == "accept_bug") { + cl.confirmBugTester(); + } + if (command == "reject_bug") { + cl.rejectBugTester(); + } + if (command == "close_bug") { + cl.closeBugDeveloper(); + } + if (command == "quit") { + cl.quit(); + } + } +} \ No newline at end of file diff --git a/tcp/server/CMakeLists.txt b/tcp/server/CMakeLists.txt new file mode 100644 index 0000000..1116470 --- /dev/null +++ b/tcp/server/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.13) +project(BugTrackingServer) +find_package (Threads) + +set(CMAKE_CXX_STANDARD 14) + +add_executable(server src/main.cpp + src/BugTrackingServer.cpp include/BugTrackingServer.h + src/UserService.cpp include/UserService.h + include/Bug.h + include/User.h +) +target_link_libraries (server ${CMAKE_THREAD_LIBS_INIT}) \ No newline at end of file diff --git a/tcp/server/include/Bug.h b/tcp/server/include/Bug.h new file mode 100644 index 0000000..9c18b71 --- /dev/null +++ b/tcp/server/include/Bug.h @@ -0,0 +1,18 @@ +#ifndef BUG_TRACKING_SERVER_BUG_H +#define BUG_TRACKING_SERVER_BUG_H + + +struct Bug { + enum BugStatus { + NEW, QA, FIXED + }; + + uint32_t id; + uint32_t developer_id; + uint32_t project_id; + BugStatus status; + std::string description; +}; + + +#endif diff --git a/tcp/server/include/BugTrackingServer.h b/tcp/server/include/BugTrackingServer.h new file mode 100644 index 0000000..94c0fb1 --- /dev/null +++ b/tcp/server/include/BugTrackingServer.h @@ -0,0 +1,66 @@ +#ifndef NETWORKS_BUG_TRACKING_SERVER_H +#define NETWORKS_BUG_TRACKING_SERVER_H + +#include +#include +#include "UserService.h" +#include "Bug.h" + +class BugTrackingServer { +private: + class Client { + public: + const int sock_fd; + User user; + + explicit Client(int socket_fd, UserService* userService); + + bool isAuthorized(); + + bool authorize(int id); + private: + UserService* _userService; + }; + +public: + explicit BugTrackingServer(UserService* userService); + + void initServer(uint16_t port_number); + + bool authorize(Client& client); + + bool bugsTesterList(Client& client); + + bool bugVerification(Client& client); + + bool bugsDeveloperList(Client& client); + + bool bugFix(Client& client); + + bool bugRegister(Client& client); + + bool close(Client& client); + + bool banClient(int id); + +private: + UserService* _userService; + std::map _clients; + std::mutex _clients_mutex; + + std::map _bugs; + std::mutex _bugs_mutex; + + void process_client(int sock_fd); + + static bool writeInt32(int sock_fd, uint32_t number); + + static bool writeString(int sock_fd, std::string data); + + static bool readInt32(int sock_fd, uint32_t& dst); + + static bool readString(int sock_fd, std::string& dst); +}; + + +#endif \ No newline at end of file diff --git a/tcp/server/include/User.h b/tcp/server/include/User.h new file mode 100644 index 0000000..ab637b9 --- /dev/null +++ b/tcp/server/include/User.h @@ -0,0 +1,15 @@ +#ifndef BUG_TRACKING_SERVER_USER_H +#define BUG_TRACKING_SERVER_USER_H + + +struct User { + enum Role { + NONE, DEVELOPER, TESTER + }; + + int id = -1; + Role role = Role::NONE; +}; + + +#endif diff --git a/tcp/server/include/UserService.h b/tcp/server/include/UserService.h new file mode 100644 index 0000000..9cc532c --- /dev/null +++ b/tcp/server/include/UserService.h @@ -0,0 +1,19 @@ +#ifndef BUG_TRACKING_SERVER_USERS_SERVER_H +#define BUG_TRACKING_SERVER_USERS_SERVER_H + +#include +#include +#include "User.h" + +class UserService { +public: + explicit UserService(std::string filename); + + User getUser(int id); + +private: + std::map _users; + std::mutex _users_mutex; +}; + +#endif diff --git a/tcp/server/src/BugTrackingServer.cpp b/tcp/server/src/BugTrackingServer.cpp new file mode 100644 index 0000000..1c5117b --- /dev/null +++ b/tcp/server/src/BugTrackingServer.cpp @@ -0,0 +1,398 @@ +#include "../include/BugTrackingServer.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +BugTrackingServer::BugTrackingServer(UserService* userService) : _userService(userService) {} + +void BugTrackingServer::initServer(uint16_t port_number) { + int socket_fd = socket(AF_INET, SOCK_STREAM, 0); + if (socket_fd < 0) { + perror("ERROR opening socket"); + exit(1); + } + + sockaddr_in server_addr = {0}, cli_addr = {0}; + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = INADDR_ANY; + server_addr.sin_port = htons(port_number); + + bzero((char *) &server_addr, sizeof(server_addr)); + + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = INADDR_ANY; + server_addr.sin_port = htons(port_number); + + if (bind(socket_fd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) { + perror("ERROR on binding"); + exit(1); + } + listen(socket_fd, 5); + + while (true) { + unsigned int client = sizeof(cli_addr); + int new_socket_fd = accept(socket_fd, (struct sockaddr*) &cli_addr, &client); + if (new_socket_fd < 0) { + perror("ERROR on accept"); + exit(1); + } + std::thread thread(&BugTrackingServer::process_client, this, new_socket_fd); + thread.detach(); + } +} + +void BugTrackingServer::process_client(int sock_fd) { + std::cout << "New client connected on " << sock_fd << ".\n"; + Client client = Client(sock_fd, _userService); + _clients_mutex.lock(); + _clients[sock_fd] = &client; + _clients_mutex.unlock(); + uint32_t code; + while (true) { + if (!readInt32(sock_fd, code)) { + std::cout << "Failed to process message from client " << sock_fd << ". Closing connection.\n"; + close(client); + break; + } else { + if (code == 100) { + authorize(client); + } else if (code == 200) { + bugsTesterList(client); + } else if (code == 300) { + bugVerification(client); + } else if (code == 400) { + bugsDeveloperList(client); + } else if (code == 500) { + bugFix(client); + } else if (code == 600) { + bugRegister(client); + } else if (code == 700) { + close(client); + break; + } else { + std::cout << "Client with id " << sock_fd << " has sent unknown request "; + writeInt32(sock_fd, 1); + } + } + } +} + +bool BugTrackingServer::authorize(BugTrackingServer::Client& client) { + int sock_fd = client.sock_fd; + uint32_t id; + readInt32(sock_fd, id); + if (client.isAuthorized()) { + std::cout << "Client on socket " << sock_fd << " is already authorized with id " << client.user.id; + writeInt32(sock_fd, 101); + writeInt32(sock_fd, static_cast(client.user.id)); + writeInt32(sock_fd, client.user.role); + return false; + } + if (client.authorize(id)) { + writeInt32(sock_fd, 101); + writeInt32(sock_fd, client.user.role); + std::cout << "Client on socket " << sock_fd << " has authorized with id " << client.user.id; + std::cout << " and role " << client.user.role << "\n"; + return true; + } else { + writeInt32(sock_fd, 102); + writeInt32(sock_fd, id); + std::cout << "Client on socket " << sock_fd << " has failed authorization\n"; + return false; + } +} + +bool BugTrackingServer::bugsTesterList(BugTrackingServer::Client &client) { + int sock_fd = client.sock_fd; + uint32_t bugStatus; + readInt32(sock_fd, bugStatus); + if (!client.isAuthorized()) { + std::cout << "Request from unauthorized client " << client.sock_fd << "\n"; + writeInt32(sock_fd, 2); + return false; + } + if (client.user.role != User::Role::TESTER) { + std::cout << "User with id " << client.user.id << " is not tester and cannot see bugs\n"; + writeInt32(sock_fd, 3); + writeInt32(sock_fd, User::Role::TESTER); + return false; + } + std::vector bugsList; + _bugs_mutex.lock(); + for (const auto &entry : _bugs) { + if (entry.second.developer_id == client.user.id && entry.second.status == bugStatus) { + bugsList.push_back(entry.second); + } + } + _bugs_mutex.unlock(); + std::cout << "Tester with id " << client.user.id << " requested bugs with status " << bugStatus << "\n"; + writeInt32(sock_fd, static_cast(bugsList.size())); + for (const auto &bug : bugsList) { + writeInt32(sock_fd, bug.id); + writeString(sock_fd, bug.description); + } + return true; +} + +bool BugTrackingServer::bugVerification(BugTrackingServer::Client &client) { + int sock_fd = client.sock_fd; + uint32_t bugId, verificationCode; + readInt32(sock_fd, bugId); + readInt32(sock_fd, verificationCode); + if (!client.isAuthorized()) { + std::cout << "Request from unauthorized client " << client.sock_fd << "\n"; + writeInt32(sock_fd, 2); + return false; + } + if (client.user.role != User::Role::TESTER) { + std::cout << "Client with id " << client.user.id << " is not tester and cannot verify bug: " << bugId << "\n"; + writeInt32(sock_fd, 3); + writeInt32(sock_fd, User::Role::TESTER); + return false; + } + _bugs_mutex.lock(); + if (_bugs.find(bugId) == _bugs.end()) { + _bugs_mutex.unlock(); + std::cout << "Client with id " << client.user.id << " tried to verify non existing bug with id " << bugId << "\n"; + writeInt32(sock_fd, 303); + writeInt32(sock_fd, bugId); + return false; + } else { + if (_bugs[bugId].status == Bug::BugStatus::NEW) { + _bugs_mutex.unlock(); + std::cout << "Tester with id " << client.user.id << " tried to verify not fixed bug with id " << bugId << "\n"; + writeInt32(sock_fd, 304); + writeInt32(sock_fd, bugId); + return false; + } else if (_bugs[bugId].status == Bug::BugStatus::QA){ + if (verificationCode == 0) { + _bugs[bugId].status = Bug::BugStatus::NEW; + _bugs_mutex.unlock(); + std::cout << "Tester with id " << client.user.id << " has rejected bug fix with id " << bugId << "\n"; + } else if (verificationCode == 1) { + _bugs[bugId].status = Bug::BugStatus::FIXED; + _bugs_mutex.unlock(); + std::cout << "Tester with id " << client.user.id << " has approved bug fix with id " << bugId << "\n"; + } else { + _bugs_mutex.unlock(); + std::cout << "Tester with id " << client.user.id << " has sent illegal verification code " << verificationCode << "\n"; + writeInt32(sock_fd, 304); + writeInt32(sock_fd, verificationCode); + return false; + } + writeInt32(sock_fd, 301); + writeInt32(sock_fd, bugId); + writeInt32(sock_fd, verificationCode); + return true; + } else { + std::cout << "Tester with id " << client.user.id << " tried to verify closed bug with id " << bugId << "\n"; + writeInt32(sock_fd, 302); + writeInt32(sock_fd, bugId); + return false; + } + } +} + +bool BugTrackingServer::bugsDeveloperList(BugTrackingServer::Client &client) { + int sock_fd = client.sock_fd; + if (!client.isAuthorized()) { + std::cout << "Request from unauthorized client " << client.sock_fd << "\n"; + writeInt32(sock_fd, 2); + return false; + } + if (client.user.role != User::Role::DEVELOPER) { + std::cout << "User with id " << client.user.id << " is not developer and does not have bugs\n"; + writeInt32(sock_fd, 3); + writeInt32(sock_fd, User::Role::DEVELOPER); + return false; + } + std::vector clientBugs; + _bugs_mutex.lock(); + for (const auto &entry : _bugs) { + if (entry.second.developer_id == client.user.id && entry.second.status == Bug::BugStatus::NEW) { + clientBugs.push_back(entry.second); + } + } + _bugs_mutex.unlock(); + std::cout << "Developer with id " << client.user.id << " requested his bug-list\n"; + writeInt32(sock_fd, static_cast(clientBugs.size())); + for (const auto &bug : clientBugs) { + writeInt32(sock_fd, bug.id); + writeString(sock_fd, bug.description); + } + return true; +} + +bool BugTrackingServer::bugFix(BugTrackingServer::Client &client) { + int sock_fd = client.sock_fd; + uint32_t bugId; + readInt32(sock_fd, bugId); + if (!client.isAuthorized()) { + std::cout << "Request from unauthorized client " << client.sock_fd << "\n"; + writeInt32(sock_fd, 2); + return false; + } + if (client.user.role != User::Role::DEVELOPER) { + std::cout << "User with id " << client.user.id << " is not developer and cannot fix bug: " << bugId << "\n"; + writeInt32(sock_fd, 3); + writeInt32(sock_fd, User::Role::DEVELOPER); + return false; + } + _bugs_mutex.lock(); + if (_bugs.find(bugId) == _bugs.end()) { + _bugs_mutex.unlock(); + std::cout << "Developer with id " << client.user.id << " tried to fix non existing bug with id " << bugId << "\n"; + writeInt32(sock_fd, 503); + writeInt32(sock_fd, bugId); + return false; + } else { + if (_bugs[bugId].status != Bug::BugStatus::NEW) { + Bug::BugStatus status = _bugs[bugId].status; + _bugs_mutex.unlock(); + std::cout << "Developer with id " << client.user.id << " tried to fix not actual bug with id " << bugId << "\n"; + writeInt32(sock_fd, 502); + writeInt32(sock_fd, status); + return false; + } else { + _bugs[bugId].status = Bug::BugStatus::QA; + _bugs_mutex.unlock(); + std::cout << "Developer with id " << client.user.id << " has fixed bug with id " << bugId << "\n"; + writeInt32(sock_fd, 501); + writeInt32(sock_fd, bugId); + return true; + } + } +} + +bool BugTrackingServer::bugRegister(BugTrackingServer::Client &client) { + int sock_fd = client.sock_fd; + std::string description; + Bug bug; + readInt32(sock_fd, bug.id); + readInt32(sock_fd, bug.developer_id); + readInt32(sock_fd, bug.project_id); + readString(sock_fd, bug.description); + if (!client.isAuthorized()) { + std::cout << "Request from unauthorized client " << client.sock_fd << "\n"; + writeInt32(sock_fd, 2); + return false; + } + if (client.user.role != User::Role::TESTER) { + std::cout << "User with id " << client.user.id << " is not tester and cannot register bugs" << bug.id << "\n"; + writeInt32(sock_fd, 3); + writeInt32(sock_fd, User::Role::TESTER); + return false; + } + _bugs_mutex.lock(); + if (_bugs.find(bug.id) != _bugs.end()) { + _bugs_mutex.unlock(); + std::cout << "Tester with id " << client.user.id << " tried to register existing bug with id " << bug.id << "\n"; + writeInt32(sock_fd, 602); + writeInt32(sock_fd, bug.id); + return false; + } else { + _bugs[bug.id] = bug; + _bugs_mutex.unlock(); + writeInt32(sock_fd, 601); + writeInt32(sock_fd, bug.id); + std::cout << "Tester with id " << client.user.id << " has registered new bug with id " << bug.id << '\n'; + return true; + } +} + +bool BugTrackingServer::close(BugTrackingServer::Client &client) { + _clients_mutex.lock(); + if (_clients.find(client.sock_fd) == _clients.end()) { + _clients_mutex.unlock(); + std::cout << "Client with socket " << client.sock_fd << " is already disconnected\n"; + return false; + } else { + _clients.erase(client.sock_fd); + _clients_mutex.unlock(); + std::cout << "Client with socket " << client.sock_fd << " has been disconnected\n"; + return true; + } +} + +bool BugTrackingServer::banClient(int id) { + _clients_mutex.lock(); + if (_clients.find(id) == _clients.end()) { + _clients_mutex.unlock(); + std::cout << "Client with id " << id << " was not found\n"; + return false; + } else { + Client* client = _clients[id]; + _clients_mutex.unlock(); + std::cout << "Client with id " << id << " has been banned\n"; + return close(*client); + } +} + +/* READING FUNCTIONS */ + +bool BugTrackingServer::readInt32(int sock_fd, uint32_t& dst) { + ssize_t n = read(sock_fd, &dst, sizeof(uint32_t)); + if (n < 0) { + return false; + } + dst = ntohl(dst); + return true; +} + +bool BugTrackingServer::readString(int sock_fd, std::string& dst) { + uint32_t length; + if (!readInt32(sock_fd, length)) { + return false; + } + char buf[length + 1]; + buf[length] = '\0'; + ssize_t n = read(sock_fd, buf, length); + if (n < 0) { + return false; + } + dst = std::string(buf); + return true; +} + +/* WRITING FUNCTIONS */ + +bool BugTrackingServer::writeInt32(int sock_fd, uint32_t number) { + uint32_t value = htonl(number); + return ::write(sock_fd, &value, sizeof(uint32_t)) >= 0; +} + +bool BugTrackingServer::writeString(int sock_fd, std::string data) { + auto length = static_cast(data.length()); + if (!writeInt32(sock_fd, length)) { + return false; + } + ssize_t written = ::write(sock_fd, data.c_str(), length); + return written >= 0; +} + +/* CLIENT FUNCTIONS */ + +BugTrackingServer::Client::Client( + int socket_fd, + UserService* userService +) : sock_fd(socket_fd), _userService(userService) {} + +bool BugTrackingServer::Client::isAuthorized() { + return user.role != User::Role::NONE; +} + +bool BugTrackingServer::Client::authorize(int id) { + User newUser = _userService->getUser(id); + if (newUser.role == User::Role::NONE) { + return false; + } + user = newUser; + return true; +} \ No newline at end of file diff --git a/tcp/server/src/UserService.cpp b/tcp/server/src/UserService.cpp new file mode 100644 index 0000000..449e835 --- /dev/null +++ b/tcp/server/src/UserService.cpp @@ -0,0 +1,32 @@ +#include +#include +#include "../include/UserService.h" + +UserService::UserService(std::string filename) { + std::ifstream in; + in.open(filename); + if (!in) { + std::cerr << "File was not found\n"; + return; + } + int id, role; + while (!in.eof()) { + in >> id >> role; + _users[id] = User(); + _users[id].id = id; + _users[id].role = static_cast(role); + } + in.close(); +} + +User UserService::getUser(int id) { + User user; + _users_mutex.lock(); + if (_users.find(id) == _users.end()) { + user = _users[id]; + } else { + user = User(); + } + _users_mutex.unlock(); + return user; +} \ No newline at end of file diff --git a/tcp/server/src/main.cpp b/tcp/server/src/main.cpp new file mode 100644 index 0000000..7639285 --- /dev/null +++ b/tcp/server/src/main.cpp @@ -0,0 +1,31 @@ +#include +#include +#include +#include "../include/BugTrackingServer.h" + +int main(int argc, char* argv[]) { + if (argc != 3) { + std::cout << "Invalid arguments number\n"; + return 0; + } + UserService userService(argv[2]); + + uint16_t port_number = static_cast(std::stoi(argv[1])); + + std::cout << "Initializing server on port " << port_number << "\n"; + BugTrackingServer server(&userService); + std::thread server_thread(&BugTrackingServer::initServer, &server, port_number); + server_thread.detach(); + + std::string in; + int id; + while (in != "exit") { + std::cin >> in; + if (in == "ban") { + std::cin >> id; + server.banClient(id); + } + } + std::cout << "Shutting down server\n"; + close(port_number); +} \ No newline at end of file