#include "../include/Server.h" #include "../include/base64.h" #include using namespace boost::asio; using ip::tcp; /*************** * con_handler * ***************/ con_handler::con_handler( basic_socket_acceptor::executor_type &io_service) : sock(io_service) { // disable indentation for json this->jsonStringBuilder.settings_["indentation"] = ""; } con_handler::~con_handler() {} con_handler::pointer con_handler::create(basic_socket_acceptor::executor_type &io_service) { return pointer(new con_handler(io_service)); } tcp::socket &con_handler::socket() { return sock; } void con_handler::start() { read(&con_handler::handle_read_version); } void con_handler::handle_read_version(const boost::system::error_code &err, size_t bytes_transferred) { if (!err) { // set up json stuff JSONCPP_STRING err; Json::Value root; Json::CharReaderBuilder builder; const std::unique_ptr reader(builder.newCharReader()); // parse data if (!reader->parse(this->data, this->data + bytes_transferred, &root, &err)) { std::cerr << "Json error: " << err << std::endl << "data: " << this->data; sock.close(); } // create answer Json::Value answer; answer["version"] = this->protocolVersion; // check version string if (root["version"].compare(this->protocolVersion) == 0) { answer["accept"] = true; read(&con_handler::handle_read_login); // send answer sendJson(answer); } else { answer["accept"] = false; // send answer sendJson(answer); ; // close connection sock.close(); } } else { std::cerr << "error: " << err.message() << std::endl; sock.close(); } } void con_handler::handle_read_login(const boost::system::error_code &err, size_t bytes_transferred) { if (!err) { // set up json stuff JSONCPP_STRING err; Json::Value root; Json::CharReaderBuilder builder; const std::unique_ptr reader(builder.newCharReader()); // parse data if (!reader->parse(this->data, this->data + bytes_transferred, &root, &err)) { std::cerr << "Json error: " << err << std::endl << "data: " << this->data; sock.close(); } Json::Value answer; // user credentials // TODO check user credentials!!! if (root["user"].compare("user") == 0 && root["pass"].compare("pass") == 0) { answer["accept"] = true; // read next data read(&con_handler::handle_read_command); // send answer sendJson(answer); } else { answer["accept"] = false; // send answer sendJson(answer); // close connection sock.close(); } } else { std::cerr << "error: " << err.message() << std::endl; sock.close(); } } void con_handler::handle_read_command(const boost::system::error_code &err, size_t bytes_transferred) { if (!err) { // set up json stuff JSONCPP_STRING err; Json::Value root; Json::CharReaderBuilder builder; const std::unique_ptr reader(builder.newCharReader()); // parse data if (!reader->parse(this->data, this->data + bytes_transferred, &root, &err)) { std::cerr << "Json error: " << err << std::endl << "data: " << this->data; sock.close(); } Json::Value answer; // check command if (root["command"].compare("status") == 0) { // read next data read(&con_handler::handle_read_command); answer["command"] = "status"; // TODO answer a real status message std::string response; if (this->getFile.is_open() && this->putFile.is_open()) { response = "download and upload running"; } else if (this->putFile.is_open()) { response = "upload running"; } else if (this->getFile.is_open()) { response = "download running"; } else { response = "ok"; } answer["response"] = response; // send answer sendJson(answer); } else if (root["command"].compare("list") == 0) { // read next data read(&con_handler::handle_read_command); answer["command"] = "list"; // TODO look for real data Json::Value array; array.append("some"); array.append("important"); array.append("data"); answer["names"] = array; answer["remaining"] = 0; // send answer sendJson(answer); } else if (root["command"].compare("put") == 0) { // read next data read(&con_handler::handle_read_command); answer["command"] = "put"; if (this->putFile.is_open() && root["cancel"].asBool() == true) { // cancel upload this->putFile.close(); std::remove(this->putFileName.c_str()); } else if (this->putFile.is_open()) { // upload chunks // decode base64 string const std::vector data = base64::decodeVector(root["data"].asString()); // write the vector data to the output file std::ostream_iterator output_iterator(putFile); std::copy(data.begin(), data.end(), output_iterator); // close file if put sends remaining = 0 if (root["remaining"].asInt() == 0) { this->putFile.close(); } } else { // start upload const std::string filename = root["file"].asString(); if (filename.find("/") != std::string::npos) { // slashes in file names are illegal answer["accept"] = false; // send answer sendJson(answer); } else { // build file path this->putFileName = this->fileDirectory; this->putFileName.append(filename); // open file and test if it already exists this->putFile.open(this->putFileName, std::ios::app | std::ios::binary); if (this->putFile.tellp() != std::ios::beg) { // file already exists this->putFile.close(); answer["accept"] = false; // send answer sendJson(answer); } else { // accept file answer["accept"] = true; // send answer sendJson(answer); } } } } else if (root["command"].compare("get") == 0) { // read next data read(&con_handler::handle_read_command); answer["command"] = "get"; // a get request is already being progressed if (this->getFile.is_open()) { answer["accept"] = false; // send answer sendJson(answer); } else { // open file const std::string filename = root["file"].asString(); if (filename.find("/") != std::string::npos) { // slashes in file names are illegal answer["accept"] = false; // send answer sendJson(answer); } else { std::string file = this->fileDirectory; file.append(filename); this->getFile = std::ifstream(file, std::ios::ate | std::ios::binary); if (this->getFile.is_open() == 0) { // file does not exist or cannot be opened answer["accept"] = false; // send answer sendJson(answer); } else { answer["accept"] = true; sendJson(answer); answer = Json::Value(); answer["command"] = "get"; // get size of file size_t size = this->getFile.tellg(); this->getFile.seekg(std::ios::beg); char fileBuffer[max_data_length + 1]; while (size_t read = this->getFile.readsome(fileBuffer, max_data_length)) { fileBuffer[read] = 0; size -= read; int remaining = size / max_data_length + (size % max_data_length == 0 ? 0 : 1); // store binary data in vector because a string whould end with // '\0' std::vector data; data.assign(fileBuffer, fileBuffer + read); answer["remaining"] = remaining; answer["cancel"] = false; answer["data"] = base64::encodeVector(data); sendJson(answer); } this->getFile.close(); } } } } else if (root["command"].compare("close") == 0) { answer["command"] = "close"; answer["response"] = "bye"; // send answer sendJson(answer); // close connection sock.close(); } else { // TODO handle error // close connection sock.close(); } } else { std::cerr << "error: " << err.message() << std::endl; sock.close(); } } void con_handler::handle_write(const boost::system::error_code &err, size_t bytes_transferred) { if (!err) { std::cout << "Hello World!" << std::endl; } else { std::cerr << "error: " << err.message() << std::endl; sock.close(); } } void con_handler::read(void (con_handler::*handler)( const boost::system::error_code &err, size_t bytes_transferred)) { sock.async_read_some(buffer(data, max_length), boost::bind(handler, shared_from_this(), placeholders::error, placeholders::bytes_transferred)); } void con_handler::sendJson(const Json::Value &json) { std::string jsonString = Json::writeString(jsonStringBuilder, json); jsonString.append("\n"); sock.async_write_some(buffer(jsonString, max_length), boost::bind(&con_handler::handle_write, shared_from_this(), placeholders::error, placeholders::bytes_transferred)); } /********** * Server * **********/ void Server::start_accept() { auto executor = acceptor_.get_executor(); con_handler::pointer connection = con_handler::create(executor); acceptor_.async_accept(connection->socket(), boost::bind(&Server::handle_accept, this, connection, placeholders::error)); } Server::Server(io_service &io_service) : acceptor_(io_service, tcp::endpoint(tcp::v4(), 1234)) { start_accept(); } Server::~Server() {} void Server::handle_accept(con_handler::pointer connection, const boost::system::error_code &err) { if (!err) { connection->start(); } start_accept(); }