diff --git a/.github/workflows/blank.yml b/.github/workflows/blank.yml new file mode 100644 index 0000000..967b856 --- /dev/null +++ b/.github/workflows/blank.yml @@ -0,0 +1,70 @@ +name: CI_client + +on: + push: + branches: [ client ] + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: install cppcheck + run: sudo apt-get install -y cppcheck + + - name: install valgrind + run: sudo apt install valgrind + + - name: install gcovr and lcov + run: | + sudo apt install gcovr + sudo apt install lcov + + - name: run cppcheck + run: cd client/src && cppcheck *.cpp + + - name: build + run: | + cd client + mkdir build + cd build + cmake .. + make + + #- name: test socket + # run: | + # cd client/build + # valgrind --leak-check=full ./test/socket_tests + + #- name: test response + # run: | + # cd client/build + # valgrind --leak-check=full ./test/response_tests + + #- name: test request + # run: | + # cd client/build + # valgrind --leak-check=full ./test/request_tests + + #- name: test client + # run: | + # cd client/build + # valgrind --leak-check=full ./test/client_tests + + #- name: test application + # run: | + # cd client/build + # valgrind --leak-check=full ./test/application_tests + + - name: coverage + run: | + cd client/build + make gcov + make lcov + - name: archive code coverage results + uses: actions/upload-artifact@v2 + with: + name: code-coverage-report_linear + path: client/build/test/lcoverage \ No newline at end of file diff --git a/.gitignore b/.gitignore index 259148f..3cd64ce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,17 @@ +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps +build/ +.vscode/ + # Prerequisites *.d diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt new file mode 100644 index 0000000..8256b2f --- /dev/null +++ b/client/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.16.3) + +project(link_share) + + + +add_subdirectory(src) + +enable_testing() +#add_subdirectory(test) diff --git a/client/include/client.hpp b/client/include/client.hpp new file mode 100644 index 0000000..a845b17 --- /dev/null +++ b/client/include/client.hpp @@ -0,0 +1,24 @@ +#pragma once +#include +#include + +#include "socket.hpp" +#include "socket.hpp" + + + +class Client { +public: + Client(const std::string& _host, int _port); + ~Client() {} + void Connect(); + void Close(); + void writeToServer(std::string& req); + std::string readFromServer(bool* endFlag); + std::vector readFileBodyFromServer(bool* endFlag); +private: + const std::string& host; + int port; + Socket sock; +}; + diff --git a/client/include/inputUtils.hpp b/client/include/inputUtils.hpp new file mode 100644 index 0000000..82214bd --- /dev/null +++ b/client/include/inputUtils.hpp @@ -0,0 +1,29 @@ +#pragma once +#include +#include + +template +void writeData(T* key); + +template +void writeData(T* key, Args... args); + +void fillObject(std::string* str); +void fillObject(std::vector* str); + + +std::string createRoomInput(); +std::string deleteRoomInput(); +std::string addUsersInput(); +std::string deleteUsersInput(); +std::string addLinkInput(); +std::string deleteLinkInput(); +std::string makeSnapshotInput(); +std::string logInInput(); +std::string signUpInput(); +std::string downloadSnapshotInput(); +std::string getUserRoomInput(); +std::string getUserLinksInput(); +std::string getLinkSnapshotsInput(); + +#include "inputUtils.tpp" \ No newline at end of file diff --git a/client/include/inputUtils.tpp b/client/include/inputUtils.tpp new file mode 100644 index 0000000..343d3a9 --- /dev/null +++ b/client/include/inputUtils.tpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +template +void writeData(T* key) { + fillObject(key); +} + +template +void writeData(T* key, Args... args) { + fillObject(key); + writeData(args...); +} \ No newline at end of file diff --git a/client/include/link.hpp b/client/include/link.hpp new file mode 100644 index 0000000..d7a1b06 --- /dev/null +++ b/client/include/link.hpp @@ -0,0 +1,20 @@ +#pragma once +#include + +class LinkImpl; + +class Link { +public: + Link(std::string& name, std::string& url, std::string& uuid, std::string& description); + Link(Link& link); + ~Link(); + std::string GetLinkName(); + std::string GetLinkInfo(); + std::shared_ptr getLinkImpl(); + std::string GetSnapshotUuid(); + void SetLinkInfo(); + void AddSnapshot(const std::string& uuid); +private: + std::shared_ptr linkImpl; +}; + diff --git a/client/include/model.hpp b/client/include/model.hpp new file mode 100644 index 0000000..071d1fe --- /dev/null +++ b/client/include/model.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include "room.hpp" +#include "userinfo.hpp" + + +typedef struct recFile +{ + std::string name; + std::vector body; +} recFile; + + +template +class ModelImpl; + +template +class Model { +public: + Model(); + ~Model(); + std::string GetRoomInfoStr(const std::string& roomName); + std::string GetCurrentRoomInfoStr(); + std::string GetLinkInfoStr(const std::string& linkName); + std::string GetLinkSnapshotInfoStr(const std::string& linkName); + void SetUserInfo(std::shared_ptr info); + std::string GetUserInfoStr(); + void AddUsers(std::vector users); + void AddSnapshotUuid(const std::string& linkname, const std::string& uuid); + void RemoveUsers(std::vector users); + void AddLink(std::shared_ptr newLink); + void RemoveLink(const std::string& linkName); + void AddRoom(std::shared_ptr newRoom); + void RemoveRoom(const std::string& roomName); + std::string FormRequest(std::string& action); + void HandleResponse(std::string& response); + void HandleFile(recFile& newFile); + bool IsHandlerRecievingFiles(); + bool IsServRequired(); + bool IsLogin(); +private: + std::shared_ptr> modelImpl; +}; + +#include "model.tpp" \ No newline at end of file diff --git a/client/include/model.tpp b/client/include/model.tpp new file mode 100644 index 0000000..f478616 --- /dev/null +++ b/client/include/model.tpp @@ -0,0 +1,318 @@ +#pragma once + +#include +#include +#include + +#include "model.hpp" +#include "room.hpp" +#include "link.hpp" +#include "userinfo.hpp" +#include "requestHandler.hpp" +#include "utils.h" + + +template +class ModelImpl { +public: + ModelImpl(); + + std::string formRequest(std::string& action, Model& model); + void handleResponse(std::string& response, Model& model); + void handleFile(recFile& newFile); + + void SetUserInfoFromStr(std::shared_ptr uinfo) { + info = uinfo; + } + + std::string getUserInfoStr() { + return info->getInfoStr(); + } + + + std::string getCurrentRoomInfoStr() { + std::string ret = currentRoom->GetRoomInfoStr(); + return ret; + } + + std::string getLinkInfoStr(const std::string& linkName) { + std::string ret = currentRoom->GetLinkInfoStr(linkName); + return ret; + } + + std::string getLinkSnapshotInfoStr(const std::string& linkName) { + std::string ret = currentRoom->GetLinkSnapshotInfoStr(linkName); + return ret; + } + + std::string getRoomInfoStr(const std::string& roomName) { + std::string ret; + for (auto i : rooms) { + if (i->GetRoomName() == roomName) { + ret = i->GetRoomInfoStr(); + break; + } + } + if (ret.empty()) { + throw std::runtime_error("Room is not found"); + } + + return ret; + } + + void addUsers(std::vector users) { + currentRoom->AddUsers(users); + } + + void addSnapshotUuid(const std::string& linkname, const std::string& uuid) { + currentRoom->AddSnapshot(linkname, uuid); + } + + void removeUsers(std::vector users) { + currentRoom->RemoveUsers(users); + } + + void addLink(std::shared_ptr newLink) { + currentRoom->AddLink(newLink); + } + + void removeLink(const std::string& linkName) { + currentRoom->RemoveLink(linkName); + } + + void addRoom(std::shared_ptr newRoom) { + if (currentRoom == nullptr) { + currentRoom = newRoom; + } else { + rooms.push_back(newRoom); + } + } + + void removeRoom(const std::string& roomName) { + auto it = std::find_if(rooms.begin(), rooms.end(), + [roomName](std::shared_ptr room) { return room->GetRoomName() == roomName; }); + rooms.erase(it); + } + + std::shared_ptr GetMainRoom() { + return mainRoom; + } + + std::vector> GetRooms() { + return rooms; + } + + bool isHandlerRecievingFiles() { + return currentHandler->RecievingFiles(); + } + + bool isServRequired() { + return currentHandler->ServRequired(); + } + + bool isLogin() { + return currentHandler->IsLogin(); + } +private: + std::shared_ptr> CreateRequestHandler(std::string& action, Model& model); + std::shared_ptr> currentHandler; + + std::shared_ptr info; + std::shared_ptr mainRoom; + std::shared_ptr currentRoom; + std::vector> rooms; +}; + +template +ModelImpl::ModelImpl() +: info(nullptr), + mainRoom(std::make_shared()), + currentRoom(nullptr) {} + +template +std::shared_ptr> ModelImpl::CreateRequestHandler(std::string& action, Model& model) { + //std::cout << action << std::endl; + std::string type; + fillDataFromJson(action, "command", &type); + + std::shared_ptr> handler; + + switch (atoi(type.c_str()) ) + { + case 0: + handler = std::make_shared>(false, true, false); + break; + case 1: + handler = std::make_shared>(false, true, false); + break; + case 2: + handler = std::make_shared>(false, true, false); + break; + case 3: + handler = std::make_shared>(false, true, false); + break; + case 4: + handler = std::make_shared>(false, true, false); + break; + case 5: + handler = std::make_shared>(true, true, false); + break; + case 6: + handler = std::make_shared>(true, true, false); + break; + case 7: + handler = std::make_shared>(false, true, true); + break; + case 8: + handler = std::make_shared>(false, true, false); + break; + case 9: + handler = std::make_shared>(true, true, false); + break; + case 10: + handler = std::make_shared>(true, true, false); + break; + case 11: + handler = std::make_shared>(true, true, false); + break; + case 12: + handler = std::make_shared>(true, true, false); + break; + default: + break; + } + + handler->FillRequest(action, model); + + return handler; +} + +template +std::string ModelImpl::formRequest(std::string& action, Model& model) { + currentHandler = CreateRequestHandler(action, model); + std::string req = currentHandler->GetRequestToSend(); + return req; +} + +template +void ModelImpl::handleResponse(std::string& response, Model& model) { + if (!currentHandler->HandleResponse(response)) { + currentHandler->DoLogic(model); + } +} + +template +void ModelImpl::handleFile(recFile& newFile) { + currentHandler->RecieveFile(newFile); +} + +template +Model::Model() : modelImpl(new ModelImpl) {} +template +Model::~Model() {} + +template +std::string Model::GetCurrentRoomInfoStr() { + std::string ret = modelImpl->getCurrentRoomInfoStr(); + return ret; +} + +template +std::string Model::GetLinkInfoStr(const std::string& linkName) { + std::string ret = modelImpl->getLinkInfoStr(linkName); + return ret; +} + +template +std::string Model::GetLinkSnapshotInfoStr(const std::string& linkName) { + std::string ret = modelImpl->getLinkSnapshotInfoStr(linkName); + return ret; +} + +template +std::string Model::GetRoomInfoStr(const std::string& roomName) { + std::string ret = modelImpl->getRoomInfoStr(roomName); + return ret; +} + +template +void Model::SetUserInfo(std::shared_ptr info) { + modelImpl->SetUserInfoFromStr(info); + } + +/* void Model::PassAction(std::string& action) { + modelImpl->passAction(action, *this); +} */ + +template +std::string Model::FormRequest(std::string& action) { + std::string ret = modelImpl->formRequest(action, *this); + return ret; +} + +template +void Model::HandleResponse(std::string& response) { + modelImpl->handleResponse(response, *this); +} + +template +void Model::HandleFile(recFile& newFile) { + modelImpl->handleFile(newFile); +} + +template +std::string Model::GetUserInfoStr() { + std::string ret = modelImpl->getUserInfoStr(); + return ret; +} + +template +void Model::AddUsers(std::vector users) { + modelImpl->addUsers(users); +} + +template +void Model::AddSnapshotUuid(const std::string& linkname, const std::string& uuid) { + modelImpl->addSnapshotUuid(linkname, uuid); +} + +template +void Model::RemoveUsers(std::vector users) { + modelImpl->removeUsers(users); +} + +template +void Model::AddLink(std::shared_ptr newLink) { + modelImpl->addLink(newLink); +} + + +template +void Model::RemoveLink(const std::string& linkName) { + modelImpl->removeLink(linkName); +} + +template +void Model::AddRoom(std::shared_ptr newRoom) { + modelImpl->addRoom(newRoom); +} + +template +void Model::RemoveRoom(const std::string& roomName) { + modelImpl->removeRoom(roomName); +} + +template +bool Model::IsHandlerRecievingFiles() { + return modelImpl->isHandlerRecievingFiles(); +} + +template +bool Model::IsServRequired() { + return modelImpl->isServRequired(); +} + +template +bool Model::IsLogin() { + return modelImpl->isLogin(); +} \ No newline at end of file diff --git a/client/include/presenter.hpp b/client/include/presenter.hpp new file mode 100644 index 0000000..a5d9bed --- /dev/null +++ b/client/include/presenter.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include "client.hpp" +#include "model.hpp" +#include "view.hpp" + +template +class Presenter { +public: + Presenter(const std::string& host, const size_t port); + Presenter(Presenter& pr) = delete; + void connect(); + void disconnect(); + Presenter& operator=(Presenter& pr) = delete; + ~Presenter(); + void run(); +private: + Client client; + Model model; + ConsoleView view; +}; + +#include "presenter.tpp" \ No newline at end of file diff --git a/client/include/presenter.tpp b/client/include/presenter.tpp new file mode 100644 index 0000000..4c4feca --- /dev/null +++ b/client/include/presenter.tpp @@ -0,0 +1,125 @@ +/* #include "presenter.hpp" */ +#pragma once + +#include +#include + +#include "utils.h" +#include "requestHandler.hpp" + +template +Presenter::Presenter(const std::string &host, const size_t port) : client(host, port) +{ /* client.Connect(); */ +} + +template +Presenter::~Presenter() +{ +} + +template +void Presenter::connect() +{ + client.Connect(); +} + +template +void Presenter::disconnect() +{ + client.Close(); +} + +template +void Presenter::run() { + std::string action = view.GetRequest(); + while(!action.empty()) { + connect(); + std::string request = model.FormRequest(action); + + client.writeToServer(request); + bool endFlag = false; + std::string response = client.readFromServer(&endFlag); + + //std::cout << std::endl << response << std::endl; + + model.HandleResponse(response); + while(!endFlag) { + if (model.IsHandlerRecievingFiles()) { + recFile newFile; + newFile.name = client.readFromServer(&endFlag); + //std::cout << "FILENAME: " << newFile.name.substr(0, 80) << std::endl << std::endl << std::endl; + newFile.body = client.readFileBodyFromServer(&endFlag); + + model.HandleFile(newFile); + } else { + std::string response = client.readFromServer(&endFlag); + + //std::cout << std::endl << response << std::endl; + + model.HandleResponse(response); + } + } + action = view.GetRequest(); + disconnect(); + } +} + + +/* template +void Presenter::run() +{ + std::string action = view.GetRequest(); + bool loginFlag = false; + std::string request, response; + while (!action.empty()) + { + bool endFlag = false; + if (loginFlag) + { + connect(); + std::string login, token; + fillDataFromJson(request, "login", &login); + fillDataFromJson(response, "uuid", &token); + action = "{\"command\": 10,\"login\": \"" + login + "\",\"token\": \"" + token + "\"}"; + request = model.FormRequest(action); + client.writeToServer(request); + response = client.readFromServer(&endFlag); + model.HandleResponse(response); + loginFlag = false; + disconnect(); + } + if (!loginFlag) { + connect(); + request = model.FormRequest(action); + client.writeToServer(request); + response = client.readFromServer(&endFlag); + model.HandleResponse(response); + + if (model.IsLogin()) + { + loginFlag = true; + } + + while (!endFlag) + { + if (model.IsHandlerRecievingFiles()) + { + recFile newFile; + newFile.name = client.readFromServer(&endFlag); + newFile.body = client.readFileBodyFromServer(&endFlag); + + model.HandleFile(newFile); + } + else + { + std::string response = client.readFromServer(&endFlag); + + model.HandleResponse(response); + } + } + disconnect(); + } + action = view.GetRequest(); + } +} + */ \ No newline at end of file diff --git a/client/include/requestHandler.hpp b/client/include/requestHandler.hpp new file mode 100644 index 0000000..b3c18c1 --- /dev/null +++ b/client/include/requestHandler.hpp @@ -0,0 +1,198 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "model.hpp" + +typedef enum ExitStatus { + SUCCESS, + FAILURE +} ExitStatus; + + +template +class RequestHandler { +public: + RequestHandler(bool recvFiles, bool servIsReq, bool isLogin) : recievingFiles(recvFiles), servIsRequired(servIsReq) , isLogin(isLogin){} + virtual ExitStatus FillRequest(std::string action, Model& model) = 0; + virtual ExitStatus HandleResponse(std::string& responseBody); + virtual ExitStatus DoLogic(Model& app) = 0; + virtual ExitStatus RecieveFile(recFile& newFile) { return SUCCESS;} + bool RecievingFiles() { return recievingFiles; } + bool ServRequired() { return servIsRequired; } + bool IsLogin() { return isLogin; } + std::string& GetRequestToSend(); +protected: + std::string requestToSend; + std::shared_ptr parser; + bool recievingFiles; + bool servIsRequired; + bool isLogin; +}; + +template +class AddUsersReqHandler : public RequestHandler { +public: + AddUsersReqHandler(bool recvFiles, bool servIsReq, bool isLogin) : RequestHandler(recvFiles, servIsReq, isLogin) {}; + ExitStatus FillRequest(std::string action, Model& model); + ExitStatus DoLogic(Model& app); +private: + std::vector users; + std::string uuid; +}; + +template +class RemoveUsersReqHandler : public RequestHandler { +public: + RemoveUsersReqHandler(bool recvFiles, bool servIsReq, bool isLogin) : RequestHandler(recvFiles, servIsReq, isLogin) {}; + ExitStatus FillRequest(std::string action, Model& model); + ExitStatus DoLogic(Model& app); +private: + std::vector users; + std::string uuid; +}; + +template +class AddLinkReqHandler : public RequestHandler { +public: + AddLinkReqHandler(bool recvFiles, bool servIsReq, bool isLogin) : RequestHandler(recvFiles, servIsReq, isLogin) {}; + ExitStatus FillRequest(std::string action, Model& model); + ExitStatus HandleResponse(std::string& responseBody); + ExitStatus DoLogic(Model& app); +private: + std::string uuid; + std::string linkName; + std::string url; + std::string description; +}; + +template +class RemoveLinkReqHandler : public RequestHandler { +public: + RemoveLinkReqHandler(bool recvFiles, bool servIsReq, bool isLogin) : RequestHandler(recvFiles, servIsReq, isLogin) {}; + ExitStatus FillRequest(std::string action, Model& model); + ExitStatus DoLogic(Model& app); +private: + std::string linkName; + std::string uuid; +}; + +template +class MakeSnapshotReqHandler : public RequestHandler { +public: + MakeSnapshotReqHandler(bool recvFiles, bool servIsReq, bool isLogin) : RequestHandler(recvFiles, servIsReq, isLogin) {}; + ExitStatus FillRequest(std::string action, Model& model); + ExitStatus HandleResponse(std::string& responseBody); + ExitStatus DoLogic(Model& app); +private: + std::string linkName; + std::string uuid; +}; + + +template +class CreateRoomReqHandler : public RequestHandler { +public: + CreateRoomReqHandler(bool recvFiles, bool servIsReq, bool isLogin) : RequestHandler(recvFiles, servIsReq, isLogin) {}; + ExitStatus FillRequest(std::string action, Model& model); + ExitStatus HandleResponse(std::string& responseBody); + ExitStatus DoLogic(Model& app); +private: + std::string roomName; + std::string roomHost; + std::string uuid; + bool isPrivate; +}; + +template +class RemoveRoomReqHandler : public RequestHandler { +public: + RemoveRoomReqHandler(bool recvFiles, bool servIsReq, bool isLogin) : RequestHandler(recvFiles, servIsReq, isLogin) {}; + ExitStatus FillRequest(std::string action, Model& model); + ExitStatus DoLogic(Model& app); +private: + std::string roomName; + std::string uuid; +}; + +template +class LogInReqHandler : public RequestHandler { +public: + LogInReqHandler(bool recvFiles, bool servIsReq, bool isLogin) : RequestHandler(recvFiles, servIsReq, isLogin) {}; + ExitStatus FillRequest(std::string action, Model& model); + ExitStatus HandleResponse(std::string& responseBody); + ExitStatus DoLogic(Model& app); +private: + std::string uuid; + std::string login; + std::string password; +}; + +template +class SignUpReqHandler : public RequestHandler { +public: + SignUpReqHandler(bool recvFiles, bool servIsReq, bool isLogin) : RequestHandler(recvFiles, servIsReq, isLogin) {}; + ExitStatus FillRequest(std::string action, Model& model); + //ExitStatus HandleResponse(std::string& responseBody); + ExitStatus DoLogic(Model& app); +private: + std::string login; + std::string password; +}; + +template +class DownloadSnapshotReqHandler : public RequestHandler { +public: + DownloadSnapshotReqHandler(bool recvFiles, bool servIsReq, bool isLogin) : RequestHandler(recvFiles, servIsReq, isLogin) {}; + ExitStatus FillRequest(std::string action, Model& model); + //ExitStatus HandleResponse(std::string& responseBody); + ExitStatus DoLogic(Model& app); + ExitStatus RecieveFile(recFile& newFile); +private: + std::string linkName; + std::string uuid; + std::string filesdir; +}; + +template +class GetUserRoomReqHandler : public RequestHandler { +public: + GetUserRoomReqHandler(bool recvFiles, bool servIsReq, bool isLogin) : RequestHandler(recvFiles, servIsReq, isLogin) {}; + ExitStatus FillRequest(std::string action, Model& model); + ExitStatus HandleResponse(std::string& responseBody); + ExitStatus DoLogic(Model& app); +private: + std::string uuid; +}; + +template +class GetUserLinksReqHandler : public RequestHandler { +public: + GetUserLinksReqHandler(bool recvFiles, bool servIsReq, bool isLogin) : RequestHandler(recvFiles, servIsReq, isLogin) {}; + ExitStatus FillRequest(std::string action, Model& model); + ExitStatus HandleResponse(std::string& responseBody); + ExitStatus DoLogic(Model& app); +private: + std::vector> map; + std::vector links; + std::string uuid; +}; + +template +class GetLinkSnapshotReqHandler : public RequestHandler { +public: + GetLinkSnapshotReqHandler(bool recvFiles, bool servIsReq, bool isLogin) : RequestHandler(recvFiles, servIsReq, isLogin) {}; + ExitStatus FillRequest(std::string action, Model& model); + ExitStatus HandleResponse(std::string& responseBody); + ExitStatus DoLogic(Model& app); +private: + std::string linkName; + std::string uuid; + std::vector> map; +}; + +#include "requestHandler.tpp" \ No newline at end of file diff --git a/client/include/requestHandler.tpp b/client/include/requestHandler.tpp new file mode 100644 index 0000000..308ed12 --- /dev/null +++ b/client/include/requestHandler.tpp @@ -0,0 +1,502 @@ +#pragma once +/* #include "requestHandler.hpp" */ +#include "utils.h" +#include "room.hpp" +#include "userinfo.hpp" +#include "model.hpp" +#include +#include +#include + + + +template +std::string& RequestHandler::GetRequestToSend() { + return requestToSend; +} + +template +ExitStatus RequestHandler::HandleResponse(std::string& responseBody) { + try { + RequestHandler::parser = std::make_shared(responseBody); + } + catch (...) { + throw std::runtime_error("Failed to parse JSON!"); + } + std::string error; + if (!RequestHandler::parser->get_value("error", error)) { + throw std::runtime_error("Failed to parse JSON!"); + } + if (error.empty()) { + std::cout << "success!" << std::endl; + } else { + return FAILURE; + } + + return SUCCESS; +} + +template +ExitStatus AddUsersReqHandler::FillRequest(std::string action, Model& model) { + fillDataFromJson(action, "users", &users); + + std::string userInfo = model.GetUserInfoStr(); + std::string login, token; + fillDataFromJson(userInfo, "name", &login, "uuid", &token); + + std::string roomInfo = model.GetCurrentRoomInfoStr(); + fillDataFromJson(roomInfo, "uuid", &uuid); + + RequestHandler::requestToSend = packToJsonString("command", 2, "login", login, "token", token, "uuid", uuid, "users", users); + return SUCCESS; +} + +template +ExitStatus AddUsersReqHandler::DoLogic(Model &model) { + model.AddUsers(users); + return SUCCESS; +} + +template +ExitStatus RemoveUsersReqHandler::FillRequest(std::string action, Model& model) { + fillDataFromJson(action, "users", &users); + + std::string userInfo = model.GetUserInfoStr(); + std::string login, token; + fillDataFromJson(userInfo, "name", &login, "uuid", &token); + + std::string roomInfo = model.GetCurrentRoomInfoStr(); + fillDataFromJson(roomInfo, "uuid", &uuid); + + RequestHandler::requestToSend = packToJsonString("command", 3, "login", login, "token", token, "uuid", uuid, "users", users); + return SUCCESS; +} + +//ExitStatus LogOutReqHandler::HandleResponse(std::string &responseBody) { return SUCCESS; } +template +ExitStatus RemoveUsersReqHandler::DoLogic(Model &model) { + model.RemoveUsers(users); + return SUCCESS; +} + +template +ExitStatus AddLinkReqHandler::FillRequest(std::string action, Model& model) { + + fillDataFromJson(action, "name", &linkName, "url", &url, "description", &description); + + std::string info = model.GetUserInfoStr(); + std::string login, token; + fillDataFromJson(info, "name", &login, "uuid", &token); + + std::string roomInfo = model.GetCurrentRoomInfoStr(); + fillDataFromJson(roomInfo, "uuid", &uuid); + + RequestHandler::requestToSend = packToJsonString("command", 4, "login", login, "token", token, "uuid", uuid, "name", linkName, "url", url, "description", description); + + return SUCCESS; +} + +template +ExitStatus AddLinkReqHandler::HandleResponse(std::string &responseBody) { + try { + RequestHandler::parser = std::make_shared(responseBody); + } + + catch (...) { + throw std::runtime_error("Failed to parse JSON!"); + } + + std::string error; + if (!RequestHandler::parser->get_value("error", error)) { + throw std::runtime_error("Failed to parse JSON!"); + } + if (error.empty()) { + (RequestHandler::parser->get_value("uuid", uuid)); + std::cout << "success!" << std::endl; + } else { + return FAILURE; + } + + return SUCCESS; +} +template +ExitStatus AddLinkReqHandler::DoLogic(Model &model) { + std::shared_ptr newLink = std::make_shared(linkName, url, uuid, description); + + model.AddLink(newLink); + return SUCCESS; +} + +template +ExitStatus RemoveLinkReqHandler::FillRequest(std::string action, Model& model) { + fillDataFromJson(action, "name", &linkName); + + std::string info = model.GetUserInfoStr(); + std::string login, token; + fillDataFromJson(info, "name", &login, "uuid", &token); + + std::string linkInfo = model.GetLinkInfoStr(linkName); + fillDataFromJson(linkInfo, "uuid", &uuid); + RequestHandler::requestToSend = packToJsonString("command", 5, "login", login, "token", token, "uuid", uuid); + + return SUCCESS; +} + +template +ExitStatus RemoveLinkReqHandler::DoLogic(Model &model) { + model.RemoveLink(linkName); + return SUCCESS; +} + +template +ExitStatus MakeSnapshotReqHandler::FillRequest(std::string action, Model& model) { + fillDataFromJson(action, "name", &linkName); + + std::string info = model.GetUserInfoStr(); + std::string login, token; + fillDataFromJson(info, "name", &login, "uuid", &token); + + std::string linkInfo = model.GetLinkInfoStr(linkName); + fillDataFromJson(linkInfo, "uuid", &uuid); + + RequestHandler::requestToSend = packToJsonString("command", 6, "login", login, "token", token, "uuid", uuid); + + return SUCCESS; +} + +template +ExitStatus MakeSnapshotReqHandler::HandleResponse(std::string &responseBody) { + try { + RequestHandler::parser = std::make_shared(responseBody); + } + + catch (...) { + throw std::runtime_error("Failed to parse JSON!"); + } + + std::string error; + if (!RequestHandler::parser->get_value("error", error)) { + throw std::runtime_error("Failed to parse JSON!"); + } + if (error.empty()) { + (RequestHandler::parser->get_value("uuid", uuid)); + //!" << std::endl; + } else { + return FAILURE; + } + + return SUCCESS; +} + + +template +ExitStatus MakeSnapshotReqHandler::DoLogic(Model &model) { + model.AddSnapshotUuid(linkName, uuid); + return SUCCESS; +} + + +template +ExitStatus CreateRoomReqHandler::FillRequest(std::string action, Model& model) { + fillDataFromJson(action, "name", &roomName, "private", &isPrivate); + + std::string info = model.GetUserInfoStr(); + std::string login, token; + fillDataFromJson(info, "name", &login, "uuid", &token); + roomHost = login; + + RequestHandler::requestToSend = packToJsonString("command", 0, "login", login, "token", token, "name", + roomName, "private", isPrivate); + return SUCCESS; +} + +template +ExitStatus CreateRoomReqHandler::HandleResponse(std::string &responseBody) { + try { + RequestHandler::parser = std::make_shared(responseBody); + } + catch (...) { + throw std::runtime_error("Failed to parse JSON!"); + } + + std::string error; + if (!RequestHandler::parser->get_value("error", error)) { + throw std::runtime_error("Failed to parse JSON!"); + } + if (error.empty()) { + (RequestHandler::parser->get_value("uuid", uuid)); + } else { + return FAILURE; + } + std::cout << "success!" << std::endl; + return SUCCESS; +} + +template +ExitStatus CreateRoomReqHandler::DoLogic(Model &model) { + std::shared_ptr newRoom = std::make_shared(roomName, roomHost, uuid, isPrivate); + model.AddRoom(newRoom); + return SUCCESS; +} + +template +ExitStatus RemoveRoomReqHandler::FillRequest(std::string action, Model& model) { + fillDataFromJson(action, "name", &roomName); + + std::string userInfo = model.GetUserInfoStr(); + std::string login, token; + fillDataFromJson(userInfo, "name", &login, "uuid", &token); + + std::string roomInfo = model.GetRoomInfoStr(roomName); + fillDataFromJson(roomInfo, "uuid", &uuid); + + RequestHandler::requestToSend = packToJsonString("command", 1, "login", login, "token", token, "uuid", uuid); + return SUCCESS; +} +//ExitStatus RemoveRoomReqHandler::HandleResponse(std::string &responseBody) { return SUCCESS; } + +template +ExitStatus RemoveRoomReqHandler::DoLogic(Model &model) { + model.RemoveRoom(roomName); + return SUCCESS; +} + + +template +ExitStatus LogInReqHandler::FillRequest(std::string action, Model& model) { + fillDataFromJson(action, "login", &login, "password", &password); + RequestHandler::requestToSend = packToJsonString("command", 7,"login", login, "password", password); + + return SUCCESS; +} +template +ExitStatus LogInReqHandler::HandleResponse(std::string& responseBody) { + try { + RequestHandler::parser = std::make_shared(responseBody); + } + catch (...) { + throw std::runtime_error("Failed to parse JSON!"); + } + + std::string error; + if (!RequestHandler::parser->get_value("error", error)) { + throw std::runtime_error("Failed to parse JSON!"); + } + if (error.empty()) { + (RequestHandler::parser->get_value("uuid", uuid)); + } else { + return FAILURE; + } + std::cout << "success!" << std::endl; + return SUCCESS; +} + +template +ExitStatus LogInReqHandler::DoLogic(Model &model) { + //std::string info = packToJsonString("login", login, "password", password, "uuid", uuid); + + std::shared_ptr newUserinfo = std::make_shared(login, password, uuid); + + model.SetUserInfo(newUserinfo); + return SUCCESS; +} + + +template +ExitStatus SignUpReqHandler::FillRequest(std::string action, Model& model) { + fillDataFromJson(action, "login", &login, "password", &password); + RequestHandler::requestToSend = packToJsonString("command", 8, "login", login, "password", password); + return SUCCESS; +} + +//ExitStatus SignUpReqHandler::HandleResponse(std::string &responseBody) { return SUCCESS; } + +template +ExitStatus SignUpReqHandler::DoLogic(Model &model) { return SUCCESS; } + + +template +ExitStatus DownloadSnapshotReqHandler::FillRequest(std::string action, Model& model) { + fillDataFromJson(action, "uuid", &uuid, "filesdir", &filesdir); + + std::string info = model.GetUserInfoStr(); + std::string login, token; + fillDataFromJson(info, "name", &login, "uuid", &token); + + RequestHandler::requestToSend = packToJsonString("command", 9, "login", login, "token", token, "uuid", uuid); + + return SUCCESS; +} + + +template +ExitStatus DownloadSnapshotReqHandler::DoLogic(Model &model) { + std::string mainDir(filesdir); + std::experimental::filesystem::create_directories(mainDir); + + std::string staticDir = mainDir + "/static"; + std::experimental::filesystem::create_directories(staticDir); + return SUCCESS; +} + +template +ExitStatus DownloadSnapshotReqHandler::RecieveFile(recFile& newFile) { + std::string mainDir(filesdir); + std::string staticDir = mainDir + "/static"; + std::string pathToNewFile(mainDir); + if (newFile.name.find("index") != std::string::npos) { + pathToNewFile += newFile.name; + } else { + pathToNewFile += "/static" + newFile.name; + } + + std::ofstream file(pathToNewFile, std::ios::binary); + for (auto i : newFile.body) { + file.write(&i, 1); + } + return SUCCESS; +} + +template +ExitStatus GetUserRoomReqHandler::FillRequest(std::string action, Model& model) { + std::string info = model.GetUserInfoStr(); + std::string login, token; + fillDataFromJson(info, "name", &login, "uuid", &token); + RequestHandler::requestToSend = packToJsonString("command", 10, "login", login, "token", token); + return SUCCESS; +} + +template +ExitStatus GetUserRoomReqHandler::HandleResponse(std::string &responseBody) { + try { + RequestHandler::parser = std::make_shared(responseBody); + } + catch (...) { + throw std::runtime_error("Failed to parse JSON!"); + } + + std::string error; + if (!RequestHandler::parser->get_value("error", error)) { + throw std::runtime_error("Failed to parse JSON!"); + } + if (error.empty()) { + (RequestHandler::parser->get_value("uuid", uuid)); + } else { + return FAILURE; + } + std::cout << "success!" << std::endl; + return SUCCESS; +} + +template +ExitStatus GetUserRoomReqHandler::DoLogic(Model &model) { + std::string roomName = "roomName", roomHost = "roomHost"; + std::shared_ptr newRoom = std::make_shared(roomName, roomHost, uuid, true); + model.AddRoom(newRoom); + return SUCCESS; +} + + +template +ExitStatus GetUserLinksReqHandler::FillRequest(std::string action, Model& model) { + std::string info = model.GetUserInfoStr(); + std::string login, token; + fillDataFromJson(info, "name", &login, "uuid", &token); + + std::string roomInfo = model.GetCurrentRoomInfoStr(); + fillDataFromJson(roomInfo, "uuid", &uuid); + + RequestHandler::requestToSend = packToJsonString("command", 11, "login", login, "token", token, "uuid", uuid); + return SUCCESS; +} + +template +ExitStatus GetUserLinksReqHandler::HandleResponse(std::string &responseBody) { + try { + RequestHandler::parser = std::make_shared(responseBody); + } + catch (...) { + throw std::runtime_error("Failed to parse JSON!"); + } + + std::string error; + if (!RequestHandler::parser->get_value("error", error)) { + throw std::runtime_error("Failed to parse JSON!"); + } + if (error.empty()) { + if (!(RequestHandler::parser->get_value("objects", map))) { + std::cout << "You don't have links" << std::endl; + return FAILURE; + } + } else { + return FAILURE; + } + std::cout << "success!" << std::endl; + return SUCCESS; +} + +template +ExitStatus GetUserLinksReqHandler::DoLogic(Model &model) { + for (auto i : map) { + std::string description = i.find("description")->second; + std::string uuid = i.find("link_uuid")->second; + std::string linkname = i.find("name")->second; + std::string url = i.find("url")->second; + + std::cout << "Name: " << linkname << std::endl; + + std::shared_ptr newLink = std::make_shared(linkname, url, uuid, description); + model.AddLink(newLink); + } + return SUCCESS; +} + + +template +ExitStatus GetLinkSnapshotReqHandler::FillRequest(std::string action, Model& model) { + std::string info = model.GetUserInfoStr(); + std::string login, token; + fillDataFromJson(info, "name", &login, "uuid", &token); + + fillDataFromJson(action, "name", &linkName); + std::string linkInfo = model.GetLinkInfoStr(linkName); + fillDataFromJson(linkInfo, "uuid", &uuid); + + RequestHandler::requestToSend = packToJsonString("command", 12, "login", login, "token", token, "uuid", uuid); + return SUCCESS; +} + +template +ExitStatus GetLinkSnapshotReqHandler::HandleResponse(std::string &responseBody) { + try { + RequestHandler::parser = std::make_shared(responseBody); + } + catch (...) { + throw std::runtime_error("Failed to parse JSON!"); + } + + std::string error; + if (!RequestHandler::parser->get_value("error", error)) { + throw std::runtime_error("Failed to parse JSON!"); + } + if (error.empty()) { + if(!RequestHandler::parser->get_value("objects", map)) { + std::cout << "You don't have snapshots" < +ExitStatus GetLinkSnapshotReqHandler::DoLogic(Model &model) { + for (auto i : map) { + std::string snapuuid = i.find("snapshot_uuid")->second; + std::string snapdate = i.find("snapshot_date")->second; + std::cout << "Snap: "<< snapuuid << " Date: " << snapdate << std::endl; + model.AddSnapshotUuid(linkName, snapuuid); + } + return SUCCESS; +} \ No newline at end of file diff --git a/client/include/room.hpp b/client/include/room.hpp new file mode 100644 index 0000000..0907c6a --- /dev/null +++ b/client/include/room.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +#include "link.hpp" + +class RoomImpl; + +class Room { +public: + Room(); + + Room(std::string& roomName, std::string& roomId, std::string& uuid, bool isPrivate); + std::string GetRoomInfoStr(); + std::string GetRoomHost(); + std::string GetRoomName(); + std::string GetLinkInfoStr(const std::string& linkName); + std::string GetLinkSnapshotInfoStr(const std::string& linkName); + void AddUsers(std::vector users); + void AddSnapshot(const std::string& linkname, const std::string& uuid); + void RemoveUsers(std::vector users); + void AddLink(std::shared_ptr newLink); + void RemoveLink(const std::string& linkName); + void addParticipant(std::string& newPart); + void removeParticipant(std::string& partName); + std::string archiveLink(std::string& linkName); +private: + std::shared_ptr roomImpl; +}; + diff --git a/client/include/socket.hpp b/client/include/socket.hpp new file mode 100644 index 0000000..08c792e --- /dev/null +++ b/client/include/socket.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include // close() +#include +#include +#include + +class Socket +{ +public: + Socket() : sd (-1) {} + Socket(int _sd) : sd(_sd) {} + ~Socket() { + if (sd > 0) { + ::close(sd); + } + } + + int GetSd() { + return sd; + } + void Connect(const std::string& host, int port); + void Send(const std::string& str); + std::string Recv(); + std::string RecvFile(bool* endFlag); + std::vector RecvFileVec(bool* endFlag); + void Close(); +private: + int sd; +}; \ No newline at end of file diff --git a/client/include/userinfo.hpp b/client/include/userinfo.hpp new file mode 100644 index 0000000..08a257f --- /dev/null +++ b/client/include/userinfo.hpp @@ -0,0 +1,17 @@ +#pragma once +#include + + +class UserInfo{ +public: + UserInfo(); + UserInfo(std::string name, std::string password, std::string uuid); + std::string getInfoStr(); + void setUserInfo(const std::string& str); + ~UserInfo(); + +private: + std::string name; + std::string password; + std::string uuid; +}; \ No newline at end of file diff --git a/client/include/utils.h b/client/include/utils.h new file mode 100644 index 0000000..73ad9a7 --- /dev/null +++ b/client/include/utils.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include +#include +#include + +const std::string separator = "{}[]\" "; + + +// Все, что связано с сериализацией в JSON + + +std::string boolToString(bool value); + +std::string serialize(const std::string &key, const std::vector vec); + +std::string serialize(const std::string &key, const std::string &value); + +std::string serialize(const std::string &key, const char* value); + +std::string serialize(const std::string &key, int value); + +std::string serialize(const std::string &key, bool value); + +template +std::string serializeData(T key, K value); + +template +std::string serializeData(T key, K value, Args... args); + +template +std::string packToJsonString(T key, K value, Args... args); + + + +// Все, что связано с распаковкой JSON'а + + +std::vector request_split(const std::string& data); + +std::vector splitString(const std::string& data); + +void fillData(const std::string& jsonStr, const std::string& key, std::vector* inputVec); + +void fillData(const std::string& jsonStr, const std::string& key, std::string* inputStr); + +void fillData(const std::string& jsonStr, const std::string& key, int* val); + +void fillData(const std::string& jsonStr, const std::string& key, bool* val); + +template +void fillDataFromJson(const std::string& jsonStr, const T& key, K* inputVal); + +template +void fillDataFromJson(const std::string& jsonStr, const T& key, K* inputVal, Args... args); + +#include "utils.tpp" \ No newline at end of file diff --git a/client/include/utils.tpp b/client/include/utils.tpp new file mode 100644 index 0000000..6765fe0 --- /dev/null +++ b/client/include/utils.tpp @@ -0,0 +1,40 @@ +#pragma once + + + +template +std::string serializeData(T key, K value) { + return serialize(key, value); +} + +template +std::string serializeData(T key, K value, Args... args) { + return serialize(key, value) + serializeData(args...); +} + + +template +std::string packToJsonString(T key, K value, Args... args) { + std::string jsonStr(""); + jsonStr = "{"; + jsonStr += serializeData(key, value, args...); + jsonStr.pop_back(); + jsonStr += "}"; + return jsonStr; +} + +// Все, что связано с распаковкой JSON'а + + + +template +void fillDataFromJson(const std::string& jsonStr, const T& key, K* inputVal) { + fillData(jsonStr, key, inputVal); +} + +template +void fillDataFromJson(const std::string& jsonStr, const T& key, K* inputVal, Args... args) { + fillData(jsonStr, key, inputVal); + fillDataFromJson(jsonStr, args...); +} + diff --git a/client/include/view.hpp b/client/include/view.hpp new file mode 100644 index 0000000..efb5d6b --- /dev/null +++ b/client/include/view.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + + +class IView { +public: + virtual ~IView() = 0; +}; + + +class ConsoleView : public IView { +public: + ConsoleView() = default; + ~ConsoleView() = default; + std::string GetRequest(); + void PrintCommands(); +}; \ No newline at end of file diff --git a/client/src/CMakeLists.txt b/client/src/CMakeLists.txt new file mode 100644 index 0000000..7267751 --- /dev/null +++ b/client/src/CMakeLists.txt @@ -0,0 +1,11 @@ +add_executable(main main.cpp) +include_directories("${PROJECT_SOURCE_DIR}/include") + +add_library(client STATIC utils.cpp inputUtils.cpp client.cpp view.cpp socket.cpp link.cpp room.cpp userinfo.cpp) +target_include_directories(client PUBLIC "${PROJECT_SOURCE_DIR}/include") + +find_package(Boost REQUIRED COMPONENTS system thread) +message(STATUS "Boost version: ${Boost_VERSION}") +target_link_libraries(main PUBLIC ${Boost_LIBRARIES} PUBLIC client stdc++fs) + +add_definitions(-Wall -Werror -Wpedantic -g) diff --git a/client/src/client.cpp b/client/src/client.cpp new file mode 100644 index 0000000..d2e18d1 --- /dev/null +++ b/client/src/client.cpp @@ -0,0 +1,24 @@ +#include "client.hpp" +#include + +Client::Client(const std::string& _host, int _port) : host(_host), port(_port), sock(-1) {} + +void Client::Connect() { + sock.Connect(host, port); +} + +void Client::Close() { + sock.Close(); +} + +std::string Client::readFromServer(bool* endFlag) { + return sock.RecvFile(endFlag); +} + +std::vector Client::readFileBodyFromServer(bool* endFlag) { + return sock.RecvFileVec(endFlag); +} + +void Client::writeToServer(std::string& req) { + sock.Send(req); +} \ No newline at end of file diff --git a/client/src/inputUtils.cpp b/client/src/inputUtils.cpp new file mode 100644 index 0000000..f39b7c5 --- /dev/null +++ b/client/src/inputUtils.cpp @@ -0,0 +1,154 @@ +#include "inputUtils.hpp" +#include "utils.h" +#include + +void fillObject(std::string *str) +{ + //std::cin >> *str; + char c = 'a'; + while ((c = getchar()) != '\n') + { + //c = getchar(); + //std::cout << "Char = " << c << '\n'; + (*str).push_back(c); + } + //std::cout << "String = " << (*str) << '\n'; + +} +void fillObject(std::vector *vec) +{ + size_t amount = 0; + std::string newObj; + std::cin >> amount; + while (amount > 0) + { + //std::getline(std::cin, newObj/* , '\n' */); + //std::cout << newObj << std::endl; + char c = 'a'; + while ((c = getchar()) != '\n') + { + // = getchar(); + newObj.push_back(c); + } + //std::cin.getline(*str); + //std::cin >> newObj; + vec->push_back(newObj); + --amount; + } +} + +/* std::string createRoomInput() { + std::string name; + std::string isPrivate; + + writeData(&name, &isPrivate); + + std::string ret = packToJsonString("command", "0", "name", name, "private", isPrivate); + return ret; +} */ + +/* std::string deleteRoomInput() { + std::string name; + std::string host; + + writeData(&name, &host); + + std::string ret = packToJsonString("command","1", "name", name, "host", host); + return ret; +} */ + +/* std::string addUsersInput() { + std::vector vec; + writeData(&vec); + + std::string ret = packToJsonString("command","2", "users", vec); + return ret; +} */ + +/* std::string deleteUsersInput() { + std::vector vec; + writeData(&vec); + + std::string ret = packToJsonString("command","3", "users", vec); + return ret; +} */ +std::string addLinkInput() +{ + std::string name; + std::string url; + std::string description; + + writeData(&name, &url, &description); + + std::string ret = packToJsonString("command", "4", "name", name, "url", url, "description", description); + return ret; +} +std::string deleteLinkInput() +{ + std::string name; + + writeData(&name); + + std::string ret = packToJsonString("command", "5", "name", name); + return ret; +} +std::string makeSnapshotInput() +{ + std::string name; + + writeData(&name); + + std::string ret = packToJsonString("command", "6", "name", name); + return ret; +} +std::string logInInput() +{ + std::string login; + std::string password; + + writeData(&login, &password); + + std::string ret = packToJsonString("command", "7", "login", login, "password", password); + return ret; +} +std::string signUpInput() +{ + std::string login; + std::string password; + + writeData(&login, &password); + + std::string ret = packToJsonString("command", "8", "login", login, "password", password); + return ret; +} + +std::string downloadSnapshotInput() +{ + std::string uuid, filesdir; + + writeData(&uuid, &filesdir); + + std::string ret = packToJsonString("command", "9", "uuid", uuid, "filesdir", filesdir); + return ret; +} + +std::string getUserRoomInput() +{ + std::string ret = packToJsonString("command", "10"); + return ret; +} + +std::string getUserLinksInput() +{ + std::string ret = packToJsonString("command", "11"); + return ret; +} + +std::string getLinkSnapshotsInput() +{ + std::string name; + + writeData(&name); + std::string ret = packToJsonString("command", "12", "name", name); + return ret; +} diff --git a/client/src/link.cpp b/client/src/link.cpp new file mode 100644 index 0000000..de7b1df --- /dev/null +++ b/client/src/link.cpp @@ -0,0 +1,80 @@ +#include + +#include "client.hpp" +#include "link.hpp" +#include "utils.h" + + +class LinkImpl { +public: + LinkImpl(std::string name, std::string url, std::string uuid, std::string description); + std::string getLinkName(); + std::string getLinkInfo(); + std::string getSnapshotUuid(); + void setLinkInfo(); + void addSnaphot(const std::string& uuid); +private: + std::string linkname; + std::string url; + std::string uuid; + std::string description; + std::vector snapshotPath; +}; + +LinkImpl::LinkImpl(std::string name, std::string url, std::string uuid, std::string description) +: linkname(std::move(name)), +url(std::move(url)), +uuid(std::move(uuid)), +description(std::move(description)) {} + +std::string LinkImpl::getLinkName() { + return linkname; +} + +std::string LinkImpl::getLinkInfo() { + std::string ret = packToJsonString("linkname", linkname, "url", url, "uuid", uuid, "description", description); + return ret; +} + +std::string LinkImpl::getSnapshotUuid() { + std::string ret = snapshotPath[0]; + return ret; +} + +void LinkImpl::setLinkInfo() { + +} + +void LinkImpl::addSnaphot(const std::string& uuid) { + snapshotPath.push_back(uuid); +} + +Link::Link(std::string& name, std::string& url, std::string& uuid, std::string& description) +: linkImpl(new LinkImpl(name, url, uuid, description)){} +Link::~Link() {} + +Link::Link(Link& link) : linkImpl(link.getLinkImpl()) {} + +std::shared_ptr Link::getLinkImpl() { + return linkImpl; +} + +std::string Link::GetLinkName() { + std::string ret = linkImpl->getLinkName(); + return ret; +} + +std::string Link::GetLinkInfo() { + std::string ret = linkImpl->getLinkInfo(); + return ret; +} + +std::string Link::GetSnapshotUuid() { + std::string ret = linkImpl->getSnapshotUuid(); + return ret; +} + +void Link::AddSnapshot(const std::string& uuid) { + linkImpl->addSnaphot(uuid); +} + diff --git a/client/src/main.cpp b/client/src/main.cpp new file mode 100644 index 0000000..47d5cdd --- /dev/null +++ b/client/src/main.cpp @@ -0,0 +1,79 @@ +#include "presenter.hpp" + +#include +#include +#include +#include +#include +#include +#include + + +const std::string host = "localhost"; +const size_t port = 13201; + +class JsonParser { +public: + explicit JsonParser(std::string& json_str) { + std::stringstream ss; + + ss << json_str; + + try { + boost::property_tree::read_json(ss, pt); + } + catch (...) { + throw std::runtime_error("Failed to parse json!"); + } + } + + template + bool get_value(const char* key, T& value) { + if (boost::optional v = pt.get_optional(key)) { + value = *v; + return true; + } else { + return false; + } + } + + bool get_value(const char* key, std::vector& value) { + if (pt.get_child_optional(key)) { + BOOST_FOREACH (boost::property_tree::ptree::value_type& field, pt.get_child(key)) + { + value.push_back(field.second.data()); + } + return true; + } + return false; + } + + bool get_value(const char* key, std::vector>& value) { + if (pt.get_child_optional(key)) { + BOOST_FOREACH (boost::property_tree::ptree::value_type& objs, pt.get_child(key)) + { + std::map object; + + BOOST_FOREACH (boost::property_tree::ptree::value_type& obj, objs.second) { + + object[obj.first] = obj.second.data(); + } + value.push_back(object); + } + + return true; + } + return false; + } + +private: + boost::property_tree::ptree pt; +}; + + +int main() { + + Presenter pr(host, port); + pr.run(); + return 0; +} \ No newline at end of file diff --git a/client/src/room.cpp b/client/src/room.cpp new file mode 100644 index 0000000..6cc0c24 --- /dev/null +++ b/client/src/room.cpp @@ -0,0 +1,177 @@ +#include + +#include "link.hpp" +#include "room.hpp" +#include "utils.h" + + + +class RoomImpl { +public: + RoomImpl(std::string& name, std::string& host, std::string& uuid, bool isPrivate); + std::string getRoomInfoStr(); + std::string getRoomHost(); + std::string getRoomName(); + std::string getLinkInfoStr(const std::string& linkName); + std::string getLinkSnapshotInfoStr(const std::string& linkName); + void addUsers(std::vector users); + void addSnapshot(const std::string& linkname, const std::string& uuid); + void removeUsers(std::vector users); + void addLink(std::shared_ptr newLink); + void removeLink(const std::string& linkName); + void addParticipant(std::string& newPart); + void removeParticipant(std::string& partName); + std::string archiveLink(std::string& linkName); + +private: + std::string roomName = ""; + std::string roomHost = ""; + std::string uuid = ""; + bool isPrivate = true; + std::vector participants; + std::vector> links; +}; + + +Room::Room() {} + +Room::Room(std::string& name, std::string& host, std::string& uuid, bool isPrivate) +/* : roomImpl(std::make_shared(name, host, uuid, isPrivate)) */ { + roomImpl = std::make_shared(name, host, uuid, isPrivate); +} + +std::string Room::GetRoomInfoStr() { + return roomImpl->getRoomInfoStr(); +} + +std::string Room::GetRoomHost() { + std::string ret = roomImpl->getRoomHost(); + return ret; +} +std::string Room::GetRoomName() { + std::string ret = roomImpl->getRoomName(); + return ret; +} + +std::string Room::GetLinkInfoStr(const std::string& linkName) { + std::string ret = roomImpl->getLinkInfoStr(linkName); + return ret; +} + +std::string Room::GetLinkSnapshotInfoStr(const std::string& linkName) { + std::string ret = roomImpl->getLinkSnapshotInfoStr(linkName); + return ret; +} + +void Room::AddUsers(std::vector users) { + roomImpl->addUsers(users); +} + +void Room::RemoveUsers(std::vector users) { + roomImpl->removeUsers(users); +} + +void Room::AddLink(std::shared_ptr newLink) { + roomImpl->addLink(newLink); +} + +void Room::AddSnapshot(const std::string& linkname, const std::string& uuid) { + roomImpl->addSnapshot(linkname, uuid); +} + +void Room::RemoveLink(const std::string& linkName) { + roomImpl->removeLink(linkName); +} + +void Room::addParticipant(std::string& newPart) {} +void Room::removeParticipant(std::string& partName) {} + +std::string Room::archiveLink(std::string& linkName) { + return "str"; +} + + +RoomImpl::RoomImpl(std::string& name, std::string& host, std::string& uuid, bool isPrivate) +: roomName(name), +roomHost(host), +uuid(uuid), +isPrivate(isPrivate) {} + +std::string RoomImpl::getRoomInfoStr() { + std::string ret = packToJsonString("name", roomName, "password", roomHost, "uuid", uuid, "private", isPrivate); + return ret; +} + +std::string RoomImpl::getRoomHost() { + return roomHost; +} + +std::string RoomImpl::getRoomName() { + return roomName; +} + +std::string RoomImpl::getLinkInfoStr(const std::string& linkName) { + std::string ret; + for(auto i : links) { + if (i->GetLinkName() == linkName) { + ret = i->GetLinkInfo(); + } + } + if (ret.empty()) { + throw std::runtime_error("Link was not found");; + } + return ret; +} + +std::string RoomImpl::getLinkSnapshotInfoStr(const std::string& linkName) { + std::string ret; + for(auto i : links) { + if (i->GetLinkName() == linkName) { + ret = i->GetSnapshotUuid(); + } + } + if (ret.empty()) { + throw std::runtime_error("Link was not found");; + } + return ret; +} + +void RoomImpl::addUsers(std::vector users) { + for (auto i : users) { + auto it = std::find(participants.begin(), participants.end(), i); + if (it == participants.end()) { + participants.push_back(i); + } + } +} + +void RoomImpl::addSnapshot(const std::string& linkname, const std::string& uuid) { + auto it = std::find_if(links.begin(), links.end(), + [linkname](std::shared_ptr link) { return link->GetLinkName() == linkname; }); + (*it)->AddSnapshot(uuid); +} + +void RoomImpl::removeUsers(std::vector users) { + for (auto i : users) { + participants.erase(std::remove(participants.begin(), participants.end(), i), participants.end()); + } +} + + + +void RoomImpl::addLink(std::shared_ptr newLink) { + links.push_back(newLink); +} + +void RoomImpl::removeLink(const std::string& linkName) { + + auto it = std::find_if(links.begin(), links.end(), + [linkName](std::shared_ptr link) { return link->GetLinkName() == linkName; }); + links.erase(it); + +} + + +void RoomImpl::addParticipant(std::string& newPart) {} +void RoomImpl::removeParticipant(std::string& partName) {} +std::string RoomImpl::archiveLink(std::string& linkName) { return "str"; } \ No newline at end of file diff --git a/client/src/socket.cpp b/client/src/socket.cpp new file mode 100644 index 0000000..41fc4a0 --- /dev/null +++ b/client/src/socket.cpp @@ -0,0 +1,229 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "socket.hpp" + +#define BUFSIZE 400 + +struct sockaddr_in resolve(const char* host, int port) { + struct hostent* hp = gethostbyname(host); + if (hp == NULL) + throw std::runtime_error("resolve error: " + std::string(strerror(errno))); + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + memcpy(&addr.sin_addr, hp->h_addr, hp->h_length); + + return addr; +} + +void set_non_blocked_impl(int sd, bool opt) { + int flags = fcntl(sd, F_GETFL, 0); + int new_flags = (opt) ? (flags | O_NONBLOCK) : (flags & ~O_NONBLOCK); + if (fcntl(sd, F_SETFL, new_flags) == -1) + throw std::runtime_error("make nonblocked: " + + std::string(strerror(errno))); +} + +void Socket::Connect(const std::string& host, int port) { + struct sockaddr_in addr = resolve(host.data(), port); + + int _sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (_sd <= 0) + throw std::runtime_error("error to create socket: " + + std::string(strerror(errno))); + + + int connected = ::connect(_sd, (struct sockaddr*)&addr, sizeof(addr)); + if (connected == -1) { + ::close(_sd); + throw std::runtime_error("connect error: " + std::string(strerror(errno))); + } + + //set_non_blocked_impl(_sd, true); + + struct timeval tv; + tv.tv_sec = 3; + tv.tv_usec = 0; + + if (setsockopt(_sd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0) { + throw std::runtime_error("set rcvtimeout: " + std::string(strerror(errno))); + } + + sd = _sd; +} + + +std::vector form_packages(std::string data, char status) { + std::vector pkgs; + + std::string pkg; + if (data.size() <= BUFSIZE-1) { + pkg += status; + pkg += data; + + for (size_t i = 0; i < BUFSIZE - 1 - data.size(); i++) { + pkg += '\x1A'; + } + pkgs.push_back(pkg); + pkg.clear(); + } else { + while(!data.empty()) { + if (data.size() <= BUFSIZE-1) { + pkg += status; + } else { + pkg += 'c'; + } + + pkg += data.substr(0, BUFSIZE-1); + data.erase(0, BUFSIZE-1); + + while (pkg.size() < BUFSIZE) { + pkg += '\x1A'; + } + + pkgs.push_back(pkg); + pkg.clear(); + } + } + + return pkgs; +} + +void Socket::Send(const std::string& data) { + std::vector pkgs = form_packages(data, 'f'); + for(auto& str : pkgs) { + size_t left = str.size(); + + ssize_t sent = 0; + + while (left > 0) { + sent = ::send(sd, str.data() + sent, str.size() - sent, 0); + if (-1 == sent) + throw std::runtime_error("write failed: " + std::string(strerror(errno))); + + left -= sent; + } + } +} + + + + + +std::string Socket::Recv() { + char buf[256]; + std::string ret; + + while (true) { + int n = ::recv(sd, buf, sizeof(buf), MSG_NOSIGNAL); + if (-1 == n && errno != EAGAIN) { + throw std::runtime_error("read failed: " + std::string(strerror(errno))); + } + if (0 == n || -1 == n) { + break; + } + ret.append(buf, n); + while (ret.back() == '\r' || ret.back() == '\n') { + ret.pop_back(); + } + } + + return ret; +} + +std::string Socket::RecvFile(bool* endFlag) { + char buf[BUFSIZE]; + std::string ret; + + while (true) { + int n = ::recv(sd, buf, BUFSIZE, MSG_NOSIGNAL); + std::string str(buf); + if (n > 0) + if (n != BUFSIZE && n != -1) { + char* bufPtr = buf + n; + n = ::recv(sd, bufPtr, BUFSIZE - n, MSG_NOSIGNAL); + } + + if (-1 == n && errno != EAGAIN) { + throw std::runtime_error("read failed: " + std::string(strerror(errno))); + } + if (-1 == n && ret.empty()) { + continue; + } + if (0 == n || -1 == n) { + break; + } + ret.append(buf, 1, BUFSIZE - 1); + while (ret.back() == '\x1A') { + ret.pop_back(); + } + + + if (buf[0] == 'e') { + + break; + } else if (buf[0] == 'f') { + *endFlag = true; + break; + } + } + return ret; +} + + +std::vector Socket::RecvFileVec(bool* endFlag) { + char buf[BUFSIZE]; + std::vector ret; + size_t i = 0; + while (true) { + int n = ::recv(sd, buf, sizeof(buf), MSG_NOSIGNAL); + std::string str(buf); + if (n > 0) { + } + + if (n != BUFSIZE && n != -1) { + char* bufPtr = buf + n; + n = ::recv(sd, bufPtr, BUFSIZE - n, MSG_NOSIGNAL); + } + if (-1 == n && errno != EAGAIN) { + throw std::runtime_error("read failed: " + std::string(strerror(errno))); + } + if (-1 == n && ret.empty() && i < 3) { + sleep(2); + i++; + continue; + } + if (0 == n || -1 == n) { + break; + } + + std::copy(&buf[1], &buf[BUFSIZE], std::back_inserter(ret)); + while (ret.back() == '\x1A') { + ret.pop_back(); + } + + if (buf[0] == 'e') { + break; + } else if (buf[0] == 'f') { + *endFlag = true; + break; + } + } + return ret; +} + +void Socket::Close() { + ::close(sd); +} \ No newline at end of file diff --git a/client/src/userinfo.cpp b/client/src/userinfo.cpp new file mode 100644 index 0000000..135b2c2 --- /dev/null +++ b/client/src/userinfo.cpp @@ -0,0 +1,25 @@ +#include "userinfo.hpp" +#include "utils.h" +#include + +UserInfo::UserInfo() : name("default"), password("default"), uuid("default") {} +UserInfo::UserInfo(std::string name, std::string password, std::string uuid) +: name(std::move(name)), + password(std::move(password)), + uuid(std::move(uuid)) { +} + +std::string UserInfo::getInfoStr() { + std::string ret = packToJsonString("name", name, "password", password, "uuid", uuid); + return ret; +} + +void UserInfo::setUserInfo(const std::string& str) { + if (str.empty()) { + throw std::runtime_error("User info is empty!"); + } + fillDataFromJson(str, "name", &name, "password", &password, "uuid", &uuid); +} + + +UserInfo::~UserInfo() {} diff --git a/client/src/utils.cpp b/client/src/utils.cpp new file mode 100644 index 0000000..dfa9d92 --- /dev/null +++ b/client/src/utils.cpp @@ -0,0 +1,183 @@ +#include "utils.h" + +/* std::vector request_split(const std::string& data) { + std::vector elements; + + std::string element = ""; + + bool special_block = false; + + for(auto it = data.begin(); it != data.end(); it++) { + if (*it == '{' || *it == '}' || *it == '[' || *it == ']') { + if (*it == '[' || *it == '{') { + special_block = true; + } else { + special_block = false; + } + } + + if (*it == ',' && !special_block) { + elements.push_back(element); + element = ""; + } else { + element += *it; + } + } + + return elements; +} */ + + +std::vector splitString(const std::string& data) { + /* if (data.empty()) { + throw std::runtime_error("Empty values in serialization"); + } */ + /* std::cout << data << std::endl; + */ + std::vector elements; + + std::string element = ""; + std::string tempStr(data); + + auto newEnd = std::remove_if(tempStr.begin(), tempStr.end(), + [](char c) { return separator.find(c) != std::string::npos; }); + tempStr.erase(newEnd, tempStr.end()); + + bool elemStarted = false; + + for(auto it = tempStr.begin(); it != tempStr.end(); it++) { + if ((*it == ':' || *it == ',') /* && !special_block */) { + if (*it == ':') { + if (!elemStarted) { + elements.push_back(element); + element = ""; + elemStarted = true; + } else { + element += *it; + } + } + if (*it == ',') { + elemStarted = false; + elements.push_back(element); + element = ""; + } + + } else { + element += *it; + } + } + elements.push_back(element); + + /* for (auto &i : elements) { + std::cout << i << std::endl; + } */ + + return elements; +} + + +std::string boolToString(bool value) { + return value ? "true" : "false"; +} + +bool stringToBool(const std::string value) { + return (value == "true") ? true : false; +} + +std::string serialize(const std::string &key, const std::vector vec) { + /* if (key.empty() || vec.empty()) { + throw std::runtime_error("Empty values in serialization"); + } */ + + std::string serialized = "\"" + key + "\": [ "; + for (auto &v: vec) { + serialized += "\"" + v + "\", "; + } + //serialized.erase(serialized.size() - 2, 1); + serialized.pop_back(); + serialized.pop_back(); + serialized += " ] "; + + return serialized; +} + +std::string serialize(const std::string &key, const std::string &value) { + /* if (key.empty() || value.empty()) { + throw std::runtime_error("Empty values in serialization"); + } */ + + std::string serialized = "\"" + key + "\": \"" + value + "\","; + return serialized; +} + +std::string serialize(const std::string &key, const char* value) { + if (key.empty()) { + throw std::runtime_error("Empty values in serialization"); + } + std::string serialized = "\"" + key + "\": \"" + std::string(value) + "\","; + return serialized; +} + +std::string serialize(const std::string &key, int value) { + if (key.empty()) { + throw std::runtime_error("Empty values in serialization"); + } + std::string serialized = "\"" + key + "\": " + std::to_string(value) + ","; + return serialized; +} + +std::string serialize(const std::string &key, bool value) { + if (key.empty()) { + throw std::runtime_error("Empty values in serialization"); + } + std::string serialized = "\"" + key + "\": " + boolToString(value) + ","; + return serialized; +} + + + + +void fillData(const std::string& jsonStr, const std::string& key, std::vector* inputVec) { + auto vec = splitString(jsonStr); + for (auto it = std::find(vec.begin(), vec.end(), key) + 1; it != vec.end(); it++) { + inputVec->push_back(*it); + } +} + + + +void fillData(const std::string& jsonStr, const std::string& key, std::string* inputStr) { + auto vec = splitString(jsonStr); + + auto it = std::find(vec.begin(), vec.end(), key) + 1; + if (it != vec.end()) { + *inputStr = *it; + } +} + + +void fillData(const std::string& jsonStr, const std::string& key, bool* val) { + auto vec = splitString(jsonStr); + + auto it = std::find(vec.begin(), vec.end(), key) + 1; + if (it != vec.end()) { + *val = stringToBool((*it)); + } +} + + +/* void fillData(const std::string& jsonStr, const std::string& key, int* val) { + auto vec = request_split(jsonStr); + for (auto &i : vec) { + std::cout << i << std::endl; + } + + auto it = std::find(vec.begin(), vec.end(), key) + 1; + if (it != vec.end()) { + std::cout << "AAAAAAAAAA" << std::endl; + + *val = atoi((*it).c_str()); + } + + +} */ \ No newline at end of file diff --git a/client/src/view.cpp b/client/src/view.cpp new file mode 100644 index 0000000..5656c9c --- /dev/null +++ b/client/src/view.cpp @@ -0,0 +1,154 @@ +#include "view.hpp" +#include "utils.h" +#include "inputUtils.hpp" +#include + +IView::~IView() {} + + +/* enum RequestCommand { + CREATE_ROOM, + DELETE_ROOM, + ADD_USERS, + DELETE_USERS, + ADD_LINK, + DELETE_LINK, + MAKE_SNAPSHOT, + LOG_IN_USER, + SIGN_UP_USER, + DOWNLOAD_SNAPSHOT, + GET_USER_ROOM, + GET_USER_LINKS, + GET_LINK_SNAPSHOTS +}; */ + +enum RequestCommand { + ADD_LINK, + DELETE_LINK, + MAKE_SNAPSHOT, + LOG_IN_USER, + SIGN_UP_USER, + DOWNLOAD_SNAPSHOT, + GET_USER_ROOM, + GET_USER_LINKS, + GET_LINK_SNAPSHOTS +}; + + +std::string ConsoleView::GetRequest() { + PrintCommands(); + + std::string inputStr(""), appendStr(" "); + int key = 0; + std::cin >> key; + getchar(); + /* int a = 0; + std::cin >> a; */ + switch (key) { + /* case CREATE_ROOM: { + std::cout << "Write name of room and is it private(true/false)" << std::endl; + inputStr = createRoomInput(); + } + break; + case DELETE_ROOM: { + std::cout << "Write name and host of room" << std::endl; + inputStr = deleteRoomInput(); + } + break; + case ADD_USERS: { + std::cout << "Write amount of users and list of users" << std::endl; + inputStr = addUsersInput(); + } + break; + case DELETE_USERS: { + std::cout << "Write amount of users and list of users" << std::endl; + inputStr = deleteUsersInput(); + } + break; */ + case ADD_LINK: { + std::cout << "Write name, url and description of link" << std::endl; + inputStr = addLinkInput(); + break; + } + case DELETE_LINK: { + std::cout << "Write name of link" << std::endl; + inputStr = deleteLinkInput(); + break; + } + case MAKE_SNAPSHOT: { + std::cout << "Write name of link" << std::endl; + inputStr = makeSnapshotInput(); + break; + } + case LOG_IN_USER: { + std::cout << "Write login and password" << std::endl; + inputStr = logInInput(); + break; + } + case SIGN_UP_USER: { + std::cout << "Write login and password" << std::endl; + inputStr = signUpInput(); + break; + } + case DOWNLOAD_SNAPSHOT: { + std::cout << "Write uuid of snapshot and directory " << std::endl; + inputStr = downloadSnapshotInput(); + break; + } + case GET_USER_ROOM: { + //std::cout << "Write name of link and directory " << std::endl; + inputStr = getUserRoomInput(); + break; + } + case GET_USER_LINKS: { + inputStr = getUserLinksInput(); + break; + } + case GET_LINK_SNAPSHOTS: { + std::cout << "Write name of link" << std::endl; + inputStr = getLinkSnapshotsInput(); + break; + } + case -1: + break; + default: + std::cout << "Wrong command. Try again" << std::endl; + break; + } + return inputStr; +} + +/* void ConsoleView::PrintCommands() { + std::cout << std::endl; + std::cout << "Please choose command type:" << std::endl; + std::cout << "- 0.Create Room" << std::endl + << "- 1.Remove Room" << std::endl + << "- 2.Add users" << std::endl + << "- 3.Remove users" << std::endl + << "- 4.Add link" << std::endl + << "- 5.Remove link" << std::endl + << "- 6.Make snapshot" << std::endl + << "- 7.Log in" << std::endl + << "- 8.Sign up" << std::endl + << "- 9.Download snapshot" << std::endl + << "- 10.Get user room" << std::endl + << "- 11.Get user links" << std::endl + << "- 12.Get link snapshots" << std::endl + << "- (-1).Exit" << std::endl; +} */ + + +void ConsoleView::PrintCommands() { + std::cout << std::endl; + std::cout << "Please choose command type:" << std::endl; + std::cout << "- 0.Add link" << std::endl + << "- 1.Remove link" << std::endl + << "- 2.Make snapshot" << std::endl + << "- 3.Log in" << std::endl + << "- 4.Sign up" << std::endl + << "- 5.Download snapshot" << std::endl + << "- 6.Create link storage" << std::endl + << "- 7.Get user links" << std::endl + << "- 8.Get link snapshots" << std::endl + << "- (-1).Exit" << std::endl; +} \ No newline at end of file diff --git a/client/test/CMakeLists.txt b/client/test/CMakeLists.txt new file mode 100644 index 0000000..6e2d7b2 --- /dev/null +++ b/client/test/CMakeLists.txt @@ -0,0 +1,60 @@ +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/test) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/test) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/test) + + +include(FetchContent) +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.8.0 +) + +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +set(BUILD_GMOCK OFF CACHE BOOL "" FORCE) +set(BUILD_GTEST ON CACHE BOOL "" FORCE) + +FetchContent_MakeAvailable(googletest) + +include_directories("${PROJECT_SOURCE_DIR}/include") + +file(GLOB sources "${PROJECT_SOURCE_DIR}/src/*.cpp") +file(GLOB tests "${PROJECT_SOURCE_DIR}/test/*.cpp") + +#add_definitions(-Wall -Werror -Wpedantic) + +foreach(file ${tests}) + set(name) + get_filename_component(name ${file} NAME_WE) + add_executable("${name}_tests" + ${sources} + ${file} + "${PROJECT_SOURCE_DIR}/test/main.cpp") + target_link_libraries("${name}_tests" gtest gtest_main) + add_test(NAME ${name} COMMAND "${name}_tests") +endforeach() + +add_definitions(-fprofile-arcs -ftest-coverage) + +add_custom_target(gcov + COMMAND ${CMAKE_MAKE_PROGRAM} test + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test +) +add_custom_command(TARGET gcov + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src/gcoverage +) + +add_custom_target(lcov + COMMAND mkdir -p lcoverage + ) +add_custom_command(TARGET lcov + COMMAND cd ${CMAKE_BINARY_DIR}/test + COMMAND lcov --capture --directory ${CMAKE_BINARY_DIR}/test/CMakeFiles/ --output-file lcoverage/main_coverage.info + COMMAND lcov --remove lcoverage/main_coverage.info '/usr/*' --output-file lcoverage/main_coverage.info + COMMAND lcov --remove lcoverage/main_coverage.info '${CMAKE_BINARY_DIR}/_deps/*' --output-file lcoverage/main_coverage.info + COMMAND genhtml lcoverage/main_coverage.info --output-directory lcoverage +) + +set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES gcoverage) + +target_link_libraries(gtest_main -fprofile-arcs) \ No newline at end of file diff --git a/client/test/application.cpp b/client/test/application.cpp new file mode 100644 index 0000000..a87c220 --- /dev/null +++ b/client/test/application.cpp @@ -0,0 +1,4 @@ +#include "gtest/gtest.h" + +#include "model.hpp" + diff --git a/client/test/client.cpp b/client/test/client.cpp new file mode 100644 index 0000000..5565471 --- /dev/null +++ b/client/test/client.cpp @@ -0,0 +1,13 @@ +#include "gtest/gtest.h" + +#include +#include "client.hpp" +#include "socket.hpp" + + + +/* TEST(ClientTest, ReadFromServerTest) { + Client client; + + ASSERT_EQ(client.readFromServer(), "str"); +} */ diff --git a/client/test/main.cpp b/client/test/main.cpp new file mode 100644 index 0000000..ff6474f --- /dev/null +++ b/client/test/main.cpp @@ -0,0 +1,6 @@ +#include "gtest/gtest.h" + +int test_main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/client/test/request.cpp b/client/test/request.cpp new file mode 100644 index 0000000..37fd370 --- /dev/null +++ b/client/test/request.cpp @@ -0,0 +1,56 @@ +#include "gtest/gtest.h" + +#include "requestHandler.hpp" +#include +#include +#include + + +TEST(RequestTest, logInTest) { + std::stringstream is; + LogInReqHandler req; + + //ASSERT_EQ(req.buildRequestFromInput(is), REQ_SUCCESS); +} + +TEST(RequestTest, logOutTest) { + std::stringstream is; + LogOutReqHandler req; + + //ASSERT_EQ(req.buildRequestFromInput(is), REQ_SUCCESS); +} + +TEST(RequestTest, CreateRoomTest) { + std::stringstream is; + CreateRoomReqHandler req; + + // ASSERT_EQ(req.buildRequestFromInput(is), REQ_SUCCESS); +} + +TEST(RequestTest, RemoveRoomTest) { + std::stringstream is; + RemoveRoomReqHandler req; + + //ASSERT_EQ(req.buildRequestFromInput(is), REQ_SUCCESS); +} + +TEST(RequestTest, AddLinkTest) { + std::stringstream is; + AddLinkReqHandler req; + + //ASSERT_EQ(req.buildRequestFromInput(is), REQ_SUCCESS); +} + +TEST(RequestTest, RemoveLinkTest) { + std::stringstream is; + RemoveLinkReqHandler req; + + // ASSERT_EQ(req.buildRequestFromInput(is), REQ_SUCCESS); +} + +TEST(RequestTest, ArchiveLinkTest) { + std::stringstream is; + ArchiveLinkReqHandler req; + + // ASSERT_EQ(req.buildRequestFromInput(is), REQ_SUCCESS); +} diff --git a/client/test/socket.cpp b/client/test/socket.cpp new file mode 100644 index 0000000..22c5d76 --- /dev/null +++ b/client/test/socket.cpp @@ -0,0 +1,11 @@ + +#include "gtest/gtest.h" + +#include "socket.hpp" + +/* +TEST(SocketTest, RecvTest) { + Socket sock; + + ASSERT_STREQ(sock.Recv().c_str(), "str"); +} */ \ No newline at end of file