#include #include #include #include #include #include #include #include #include #include #include "qmlhandler.h" #include #include #include using boost::asio::buffer; using namespace std; int inpipefd[2]; int outpipefd[2]; char buf[1025]; QUrl sendFileUrl = QUrl(""); QString _IP = ""; bool _CLI_RUNNING = false; bool _RESTART = false; bool programActive = true; QMLHandler::QMLHandler(QObject *parent) : QObject(parent) {} void QMLHandler::closeCLI() { close(outpipefd[1]); close(inpipefd[0]); _CLI_RUNNING = false; } void QMLHandler::reopenCLI(QString ip) { if (_CLI_RUNNING) { closeCLI(); } _IP = ip; pid_t pid = 0; pipe(inpipefd); pipe(outpipefd); pid = fork(); if (pid == 0) { // Child dup2(outpipefd[0], STDIN_FILENO); dup2(inpipefd[1], STDOUT_FILENO); // dup2(inpipefd[1], STDERR_FILENO); // ask kernel to deliver SIGTERM in case the parent dies prctl(PR_SET_PDEATHSIG, SIGTERM); // Set the path to the CLI - pass argument h (help) for now // TODO: Change hardcoded path execl("../../cli/build/ccats-cli", "ccats-cli", ip.toUtf8().constData(), "--machine", (char *)NULL); exit(1); } _CLI_RUNNING = true; close(outpipefd[0]); close(inpipefd[1]); std::thread(&QMLHandler::readPipeLoop, this, pid).detach(); } std::vector tokenizeByNewlines(std::string in) { vector res; size_t index; for (index = in.find("\n"); index != std::string::npos; index = in.find("\n")) { if (index != 0) res.push_back(in.substr(0, index)); in = in.substr(index + 1); } if (in.length() > 0) res.push_back(in); return res; } void QMLHandler::onExit() { write(outpipefd[1], "disconnect\n", strlen("disconnect\n")); } // This method gets a string and tries to read it as Json // If it fails to do so, return and do nothing, else handle the content void QMLHandler::handleJSON(string buffer) { Json::Value root; Json::CharReaderBuilder builder; Json::CharReader *reader = builder.newCharReader(); string jsonError; // Try to parse the string as Json and store the result of the pasring in a // boolean bool parsingSuccessful = reader->parse(buffer.c_str(), buffer.c_str() + buffer.size(), &root, &jsonError); // If the string is not correct Json, return if (!parsingSuccessful) { return; } const Json::Value command = root["command"]; string cmd = command.asString(); qInfo() << QString::fromStdString("Received command " + cmd); if (!cmd.compare("status")) { emit footerSetStatus(root["response"].asString().c_str()); } else if (!cmd.compare("close")) { programActive = false; } else if (!cmd.compare("list")) { if (root["accept"] == true) { emit receivingClearFileList(); // Get the array of file Names auto fileNames = root["names"]; for (int i = 0; i < fileNames.size(); i++) { emit receivingListFile(QString::fromStdString(fileNames[i].asString().c_str())); } } else { emit log(root["error"].asString().c_str()); } } else if (!cmd.compare("connect")) { if (root["accept"].asBool()) { } else { emit ipPopupSetStatus(root["error"].asString().c_str()); closeCLI(); emit ipPopupEnableConnectButton(); } } else if (!cmd.compare("version")) { if (root["accept"] == true) { emit ipPopupClose(); emit loginSignupPopupOpen(); } else { QString errorMessage = QString::fromStdString( string("Version mismatch: \nClient: " + root["clientversion"].asString() + "\nServer: " + root["serverversion"].asString())); emit ipPopupSetStatus(errorMessage); reopenCLI(_IP); emit ipPopupEnableConnectButton(); } } else if (!cmd.compare("login")) { if (root["accept"] == true) { emit loginSignupPopupClose(); } else { emit loginSetStatus(root["error"].asString().c_str()); reopenCLI(_IP); emit loginEnableLoginButton(); } } else if (!cmd.compare("signup")) { if (root["accept"] == true) { emit loginSignupPopupClose(); } else { emit loginSetStatus(root["error"].asString().c_str()); reopenCLI(_IP); emit signupEnableRegisterButton(); } } else if (!cmd.compare("put")) { if (root["accept"] == false) { QString errorMessage = QString::fromStdString(string("Error when uploading file " + root["file"].asString() + ":\n" + root["error"].asString())); emit log(errorMessage); } } else if (!cmd.compare("putdata")) { // TODO: Show speed and handle Error } else if (!cmd.compare("get")) { if (root["accept"] == false) { QString errorMessage = QString::fromStdString(string("Error when downloading file " + root["file"].asString() + ":\n" + root["error"].asString())); emit log(errorMessage); } } else if (!cmd.compare("getdata")) { // TODO: Show speed and handle Error } else if (!cmd.compare("deleteme")) { if (root["accept"] == true) { _RESTART = true; emit closeWindow(); } } } // This method is a loop which runs in a seperate thread. // If will read the Input Pipe, which containts the string that get printed // on stdout by the CLI/Server, and calls handleJSON with the read content // one it reads a newline. void QMLHandler::readPipeLoop(pid_t childid) { unsigned int readOffset = 0; unsigned int pollCount = 0; struct pollfd inPipeStatus; inPipeStatus.fd = inpipefd[0]; inPipeStatus.events = POLLIN; vector inputs; string pipeInput; while (programActive && _CLI_RUNNING) { inputs = vector(); poll(&inPipeStatus, 1, 100); if (inPipeStatus.revents & POLLIN) { readOffset += read(inpipefd[0], buf + readOffset, 1); pollCount = 0; } else { pollCount++; } if (pollCount > 9 && (readOffset || pipeInput.size())) { pipeInput.append(buf); inputs = tokenizeByNewlines(pipeInput); for (string s : inputs) { emit log(QString::fromStdString(s)); qInfo() << QString::fromStdString(s); handleJSON(s); } pipeInput = string(); memset(buf, 0, 1025); pollCount = 0; readOffset = 0; if (waitpid(childid, NULL, WNOHANG)) { // nonzero means error or childid has changed state // for us that means child has exited -> CLI is dead break; } } // Fixme if (readOffset >= 1024) { pipeInput.append(buf); readOffset = 0; pollCount = 0; memset(buf, 0, 1025); } } } // ### QML Handlers ### // Sending void QMLHandler::onSendingSelectFileButton(QUrl url) { sendFileUrl = url.toLocalFile(); emit log("File Selected: " + sendFileUrl.toString()); emit sendingSetFileUrlText("Selected File: " + sendFileUrl.toString()); emit sendingEnableSendButton(); } void QMLHandler::onSendingSendFileButton() { QString command = "put " + sendFileUrl.toString() + "\n"; write(outpipefd[1], command.toUtf8().constData(), strlen(command.toUtf8().constData())); } void QMLHandler::onSendingClearSelectionButton() { sendFileUrl = QUrl(""); emit log("Cleared Selection"); emit sendingSetFileUrlText("Selected File: None"); emit sendingDisableSendButton(); } // Receiving void QMLHandler::onReceivingListFilesButton() { write(outpipefd[1], "list\n", strlen("list\n")); } void QMLHandler::onReceivingGetFileButton(QString fileName) { QString command = "get " + fileName + "\n"; write(outpipefd[1], command.toUtf8().constData(), strlen(command.toUtf8().constData())); } // Messages void QMLHandler::onMessagesSendButton(QString msg) { emit message(msg); } // Settings void QMLHandler::onSettingsDeleteMeButton() { write(outpipefd[1], "deleteme\n", strlen("deleteme\n")); } // Ip Popup void QMLHandler::onIpPopupConnectButton(QString ip) { reopenCLI(ip); emit ipPopupDisableConnectButton(); } // Login void QMLHandler::onLoginLoginButton(QString username, QString password) { QString command = "login " + username + " " + password + "\n"; write(outpipefd[1], command.toUtf8().constData(), strlen(command.toUtf8().constData())); emit loginDisableLoginButton(); } // Signup void QMLHandler::onSignupRegisterButton(QString username, QString passwordOne, QString passwordTwo) { if (QString::compare(passwordOne, passwordTwo, Qt::CaseSensitive)) { emit signupSetStatus("Passwords don't match"); return; } QString command = "signup " + username + " " + passwordOne + "\n"; write(outpipefd[1], command.toUtf8().constData(), strlen(command.toUtf8().constData())); emit signupDisableRegisterButton(); } // Footer void QMLHandler::onFooterGetStatusButton() { write(outpipefd[1], "status\n", strlen("status\n")); }