client.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. #include <arpa/inet.h>
  2. #include <math.h>
  3. #include <netdb.h>
  4. #include <string.h>
  5. #include <sys/socket.h>
  6. #include <sys/types.h>
  7. #include <unistd.h>
  8. #include <QByteArray>
  9. #include <QDebug>
  10. #include <algorithm>
  11. #include <cstring>
  12. #include <functional>
  13. #include <future>
  14. #include <include/client/client.hpp>
  15. #include <iostream>
  16. #include <vector>
  17. #define FLATBUFFERS_DEBUG_VERIFICATION_FAILURE
  18. #include <headers/kmessage_codec.hpp>
  19. #include <headers/instatask_generated.h>
  20. using namespace KData;
  21. using namespace IGData;
  22. static const int MAX_PACKET_SIZE = 4096;
  23. static const int HEADER_SIZE = 4;
  24. flatbuffers::FlatBufferBuilder builder(1024);
  25. /**
  26. * @param [in] {std::function<void()>} cb A non-returning function to be called without parameter
  27. * @returns {MessageHandler} A message loop handler
  28. */
  29. Client::MessageHandler Client::createMessageHandler(std::function<void()> cb) {
  30. return MessageHandler(cb);
  31. }
  32. /**
  33. * @brief Client::Client
  34. * @constructor
  35. * @param [in] {QWidget*} parent
  36. * @param [in] {int} count
  37. * @param [in] {char**} arguments
  38. */
  39. Client::Client(QWidget *parent, int count, char** arguments) : QDialog(parent), argc(count), argv(arguments), m_client_socket_fd(-1), m_outbound_task(nullptr), executing(false), m_commands({}) {
  40. qRegisterMetaType<QVector<QString>>("QVector<QString>");
  41. }
  42. /**
  43. * @brief Client::~Client
  44. * @destructor
  45. */
  46. Client::~Client() {
  47. closeConnection();
  48. }
  49. /**
  50. * @brief Client::handleMessages
  51. */
  52. void Client::handleMessages() {
  53. uint8_t receive_buffer[MAX_PACKET_SIZE];
  54. for (;;) {
  55. memset(receive_buffer, 0, MAX_PACKET_SIZE);
  56. ssize_t bytes_received = 0;
  57. bytes_received = recv(m_client_socket_fd, receive_buffer, MAX_PACKET_SIZE, 0);
  58. if (bytes_received == 0) { // Finish message loop
  59. break;
  60. }
  61. size_t end_idx = findNullIndex(receive_buffer);
  62. std::string data_string{receive_buffer, receive_buffer + end_idx};
  63. qDebug() << "Received data from KServer: \n" << data_string.c_str();
  64. if (isPong(data_string.c_str())) {
  65. qDebug() << "Server returned pong";
  66. continue;
  67. }
  68. StringVec s_v{};
  69. if (isNewSession(data_string.c_str())) { // Session Start
  70. m_commands = getArgMap(data_string.c_str());
  71. for (const auto& [k, v] : m_commands) { // Receive available commands
  72. s_v.push_back(v.data());
  73. }
  74. emit Client::messageReceived(COMMANDS_UPDATE_TYPE, "New Session", s_v); // Update UI
  75. } else if (serverWaitingForFile(data_string.c_str())) { // Server expects a file
  76. processFileQueue();
  77. } else if (isEvent(data_string.c_str())) { // Receiving event
  78. QString event = getEvent(data_string.c_str());
  79. QVector<QString> args = getArgs(data_string.c_str());
  80. emit Client::messageReceived(EVENT_UPDATE_TYPE, event, args); // Update UI (event)
  81. if (isUploadCompleteEvent(event.toUtf8().constData())) { // Upload complete
  82. if (!args.isEmpty()) {
  83. sent_files.at(sent_files.size() - 1).timestamp =
  84. std::stoi(args.at(0).toUtf8().constData()); // mark file with server-generated timestamp
  85. if (outgoing_files.isEmpty()) { // Task files are all sent
  86. sendTaskEncoded(m_outbound_task); // Send remaining task data to complete scheduling
  87. file_was_sent = false;
  88. } else { // Begin file upload operation. Task will be sent after all outgoing files are sent.
  89. sendEncoded(
  90. createOperation("FileUpload", {"Subsequent file"}));
  91. }
  92. }
  93. }
  94. }
  95. std::string formatted_json = getJsonString(data_string);
  96. emit Client::messageReceived(MESSAGE_UPDATE_TYPE, QString::fromUtf8(formatted_json.data(), formatted_json.size()), {});
  97. }
  98. memset(receive_buffer, 0, 2048);
  99. ::close(m_client_socket_fd);
  100. }
  101. /**
  102. * @brief Client::processFileQueue
  103. */
  104. void Client::processFileQueue() {
  105. Scheduler::KFileData outgoing_file = outgoing_files.dequeue();
  106. sendFileEncoded(outgoing_file.bytes);
  107. sent_files.push_back(SentFile{
  108. .name = outgoing_file.name,
  109. .type = outgoing_file.type
  110. });
  111. }
  112. /**
  113. * @brief Client::start
  114. */
  115. void Client::start() {
  116. if (m_client_socket_fd == -1) {
  117. m_client_socket_fd = socket(AF_INET, SOCK_STREAM, 0);
  118. if (m_client_socket_fd != -1) {
  119. sockaddr_in server_socket;
  120. char* end;
  121. server_socket.sin_family = AF_INET;
  122. auto port_value = strtol(argv[2], &end, 10);
  123. if (port_value < 0 || end == argv[2]) {
  124. return;
  125. }
  126. int socket_option = 1;
  127. // Free up the port to begin listening again
  128. setsockopt(m_client_socket_fd, SOL_SOCKET, SO_REUSEADDR, &socket_option,
  129. sizeof(socket_option));
  130. server_socket.sin_port = htons(port_value);
  131. inet_pton(AF_INET, argv[1], &server_socket.sin_addr.s_addr);
  132. if (::connect(m_client_socket_fd, reinterpret_cast<sockaddr*>(&server_socket),
  133. sizeof(server_socket)) != -1) {
  134. std::string start_operation_string = createOperation("start", {});
  135. // Send operation as an encoded message
  136. sendEncoded(start_operation_string);
  137. // Delegate message handling to its own thread
  138. std::function<void()> message_send_fn = [this]() {
  139. this->handleMessages();
  140. };
  141. MessageHandler message_handler = createMessageHandler(message_send_fn);
  142. // Handle received messages on separate thread
  143. std::thread (message_handler).detach();
  144. } else {
  145. qDebug() << errno;
  146. ::close(m_client_socket_fd);
  147. }
  148. } else {
  149. qDebug() << "Failed to create new connection";
  150. }
  151. } else {
  152. qDebug() << "Connection already in progress";
  153. }
  154. }
  155. /**
  156. * @brief Client::sendMessage
  157. * @param [in] {const QString&} The message to send
  158. */
  159. void Client::sendMessage(const QString& s) {
  160. if (m_client_socket_fd != -1) {
  161. std::string json_string {"{\"type\":\"custom\", \"message\": \""};
  162. json_string += s.toUtf8().data();
  163. json_string += "\", \"args\":\"placeholder\"}";
  164. // Send custom message as an encoded message
  165. sendEncoded(json_string);
  166. } else {
  167. qDebug() << "You must first open a connection";
  168. }
  169. }
  170. /**
  171. * @brief Client::sendEncoded
  172. * @param [in] {std::string message} The message to send
  173. */
  174. void Client::sendEncoded(std::string message) {
  175. std::vector<uint8_t> fb_byte_vector{message.begin(), message.end()};
  176. auto byte_vector = builder.CreateVector(fb_byte_vector);
  177. auto k_message = CreateMessage(builder, 69, byte_vector);
  178. builder.Finish(k_message);
  179. uint8_t* encoded_message_buffer = builder.GetBufferPointer();
  180. uint32_t size = builder.GetSize();
  181. uint8_t send_buffer[MAX_PACKET_SIZE];
  182. memset(send_buffer, 0, MAX_PACKET_SIZE);
  183. send_buffer[0] = (size & 0xFF) >> 24;
  184. send_buffer[1] = (size & 0xFF) >> 16;
  185. send_buffer[2] = (size & 0xFF) >> 8;
  186. send_buffer[3] = (size & 0xFF);
  187. send_buffer[4] = (TaskCode::GENMSGBYTE & 0xFF);
  188. std::memcpy(send_buffer + 5, encoded_message_buffer, size);
  189. qDebug() << "Sending encoded message";
  190. std::string message_to_send{};
  191. for (unsigned int i = 0; i < (size + 5); i++) {
  192. message_to_send += (char)*(send_buffer + i);
  193. }
  194. qDebug() << "Encoded message size: " << (size + 5);
  195. // Send start operation
  196. ::send(m_client_socket_fd, send_buffer, size + 5, 0);
  197. builder.Clear();
  198. }
  199. /**
  200. * @brief getTaskFileInfo
  201. * @param [in] {std::vector<SentFile>} files The files to produce an information string from
  202. * @return std::string A string with the following format denoting each file:
  203. * `1580057341filename|image::`
  204. */
  205. std::string getTaskFileInfo(std::vector<SentFile> files) {
  206. std::string info{};
  207. for (const auto& f : files) {
  208. info += std::to_string(f.timestamp);
  209. info += f.name.toUtf8().constData();
  210. info += "|";
  211. if (f.type == Scheduler::FileType::VIDEO) {
  212. info += "video";
  213. } else {
  214. info += "image";
  215. }
  216. info += ":";
  217. }
  218. qDebug() << "File Info: " << info.c_str();
  219. return info;
  220. }
  221. /**
  222. * @brief Client::sendTaskEncoded
  223. * @param [in] {Scheduler::Task*} task The task arguments
  224. */
  225. void Client::sendTaskEncoded(Scheduler::Task* task) {
  226. if (task->getType() == Scheduler::TaskType::INSTAGRAM) {
  227. const auto hashtags = KString{std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("hashtags"))};
  228. flatbuffers::Offset<IGTask> ig_task =
  229. CreateIGTask(
  230. builder,
  231. 96,
  232. builder.CreateString(getTaskFileInfo(sent_files)),
  233. builder.CreateString(std::string{std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("datetime")).toUtf8().constData()}),
  234. builder.CreateString(
  235. std::string{std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("description")).toUtf8().constData()}),
  236. builder.CreateString(hashtags
  237. ),
  238. builder.CreateString(
  239. KString{std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("requested_by_phrase"))}),
  240. builder.CreateString(
  241. KString{std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("requested_by_phrase"))}),
  242. builder.CreateString(
  243. KString{std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("promote_share"))}),
  244. builder.CreateString(
  245. KString{std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("link_in_bio"))}),
  246. std::get<Scheduler::VariantIndex::BOOLEAN>(task->getTaskArgument("is_video")),
  247. std::get<Scheduler::VariantIndex::INTEGER>(task->getTaskArgument("mask")),
  248. builder.CreateString(
  249. KString{std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("header"))}),
  250. builder.CreateString(
  251. KString{std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("user"))}));
  252. builder.Finish(ig_task);
  253. uint8_t* encoded_message_buffer = builder.GetBufferPointer();
  254. uint32_t size = builder.GetSize();
  255. uint8_t send_buffer[MAX_PACKET_SIZE];
  256. memset(send_buffer, 0, MAX_PACKET_SIZE);
  257. send_buffer[0] = (size >> 24) & 0xFF;
  258. send_buffer[1] = (size >> 16) & 0xFF;
  259. send_buffer[2] = (size >> 8) & 0xFF;
  260. send_buffer[3] = size & 0xFF;
  261. send_buffer[4] = (TaskCode::IGTASKBYTE & 0xFF);
  262. std::memcpy(send_buffer + 5, encoded_message_buffer, size);
  263. qDebug() << "Ready to send:";
  264. std::string message_to_send{};
  265. for (unsigned int i = 0; i < (size + 5); i++) {
  266. message_to_send += (char)*(send_buffer + i);
  267. }
  268. qDebug() << "Final size: " << (size + 5);
  269. // Send start operation
  270. ::send(m_client_socket_fd, send_buffer, size + 5, 0);
  271. // Cleanup and process queue
  272. builder.Clear();
  273. sent_files.clear();
  274. m_outbound_task = nullptr;
  275. if (!m_task_queue.isEmpty()) {
  276. m_outbound_task = m_task_queue.dequeue();
  277. // TODO work from here
  278. if (m_outbound_task->hasFiles() && !outgoing_files.empty()) {
  279. qDebug() << "There are still outgoing files left over from last "
  280. "task which were never sent. They are being deleted";
  281. outgoing_files.clear();
  282. }
  283. sendFiles(m_outbound_task);
  284. }
  285. }
  286. }
  287. /**
  288. * @brief Client::sendPackets
  289. * @param [in] {uint8_t*} data A pointer to a buffer of bytes
  290. * @param [in] {int} size The size of the buffer to be packetized and sent
  291. */
  292. void Client::sendPackets(uint8_t* data, int size) {
  293. uint32_t total_size = static_cast<uint32_t>(size + HEADER_SIZE);
  294. uint32_t total_packets = static_cast<uint32_t>(ceil(
  295. static_cast<double>(
  296. static_cast<double>(total_size) / static_cast<double>(MAX_PACKET_SIZE)) // total size / packet
  297. )
  298. );
  299. uint32_t idx = 0;
  300. for (; idx < total_packets; idx++) {
  301. bool is_first_packet = (idx == 0);
  302. bool is_last_packet = (idx == (total_packets - 1));
  303. if (is_first_packet) {
  304. uint32_t first_packet_size =
  305. std::min(size + HEADER_SIZE, MAX_PACKET_SIZE);
  306. uint8_t packet[first_packet_size];
  307. packet[0] = (total_size >> 24) & 0xFF;
  308. packet[1] = (total_size >> 16) & 0xFF;
  309. packet[2] = (total_size >> 8) & 0xFF;
  310. packet[3] = (total_size) & 0xFF;
  311. std::memcpy(packet + HEADER_SIZE, data, first_packet_size - HEADER_SIZE);
  312. /**
  313. * SEND PACKET !!!
  314. */
  315. ::send(m_client_socket_fd, packet, first_packet_size, 0);
  316. if (is_last_packet) {
  317. break;
  318. }
  319. continue;
  320. }
  321. int offset = (idx * MAX_PACKET_SIZE) - HEADER_SIZE;
  322. uint32_t packet_size = std::min(size - offset, MAX_PACKET_SIZE);
  323. uint8_t packet[packet_size];
  324. std::memcpy(packet, data + offset, packet_size);
  325. /**
  326. * SEND PACKET !!!
  327. */
  328. ::send(m_client_socket_fd, packet, packet_size, 0);
  329. if (is_last_packet) {
  330. // cleanup
  331. qDebug() << "Last packet of file sent";
  332. file_was_sent = true;
  333. }
  334. }
  335. }
  336. void Client::ping() {
  337. if (m_client_socket_fd != -1) { // if we have active connection
  338. if (outgoing_files.size() == 0 || file_was_sent) {
  339. /* 1st condition: we aren't sending file packets
  340. 2nd condition: we were sending packets, but the file is complete and we want
  341. to ping in case the server is unresponsive */
  342. uint8_t send_buffer[5];
  343. memset(send_buffer, 0, 5);
  344. send_buffer[4] = (TaskCode::PINGBYTE & 0xFF);
  345. qDebug() << "Pinging server";
  346. ::send(m_client_socket_fd, send_buffer, 5, 0);
  347. }
  348. }
  349. }
  350. /**
  351. * @brief Client::sendFileEncoded
  352. * @param [in] {QByteArray} bytes An array of bytes to send
  353. */
  354. void Client::sendFileEncoded(QByteArray bytes) {
  355. sendPackets(reinterpret_cast<uint8_t*>(bytes.data()), bytes.size());
  356. }
  357. /**
  358. * @brief Client::closeConnection
  359. */
  360. void Client::closeConnection() {
  361. if (m_client_socket_fd != -1) {
  362. std::string stop_operation_string = createOperation("stop", {});
  363. // Send operation as an encoded message
  364. sendEncoded(stop_operation_string);
  365. // Clean up socket file descriptor
  366. ::shutdown(m_client_socket_fd, SHUT_RDWR);
  367. ::close(m_client_socket_fd);
  368. m_client_socket_fd = -1;
  369. return;
  370. }
  371. qDebug() << "There is no active connection to close";
  372. }
  373. /**
  374. * @brief Client::setSelectedApp
  375. * @param [in] TYPE SHOULD CHANGE app_names
  376. */
  377. void Client::setSelectedApp(std::vector<QString> app_names) {
  378. selected_commands.clear();
  379. for (const auto& name : app_names) {
  380. qDebug() << "Matching mask to " << name;
  381. for (const auto& command : m_commands) {
  382. if (command.second.c_str() == name.toUtf8()) {
  383. selected_commands.push_back(command.first);
  384. }
  385. }
  386. }
  387. }
  388. /**
  389. * @brief Client::getSelectedApp
  390. * @returns {int} The mask representing the selected application
  391. */
  392. int Client::getSelectedApp() {
  393. if (selected_commands.size() == 1) {
  394. return selected_commands.at(0);
  395. } else {
  396. QMessageBox::warning(this, tr("App Selection Error"), tr("Unable to retrieve app selection"));
  397. }
  398. return -1;
  399. }
  400. /**
  401. * @brief Client::getAppName
  402. * @param [in] {int} mask The mask representing the application
  403. * @returns {QString} The application name
  404. */
  405. QString Client::getAppName(int mask) {
  406. auto app = m_commands.find(mask);
  407. if (app != m_commands.end()) {
  408. return QString{app->second.c_str()};
  409. }
  410. return QString{""};
  411. }
  412. /**
  413. * @brief Client::execute
  414. */
  415. void Client::execute() {
  416. if (!selected_commands.empty()) {
  417. executing = true;
  418. for (const auto& command : selected_commands) {
  419. auto app_name = getAppName(command);
  420. auto message = app_name + " pending";
  421. auto request_id = QUuid::createUuid().toString(QUuid::StringFormat::WithoutBraces);
  422. emit Client::messageReceived(PROCESS_REQUEST_TYPE, message, { QString{command}, app_name, request_id });
  423. std::string execute_operation = createOperation("Execute", {std::to_string(command), std::string(request_id.toUtf8().constData())});
  424. sendEncoded(execute_operation);
  425. }
  426. }
  427. }
  428. /**
  429. * @brief Client::scheduleTask
  430. * @param [in] {std::vector<std::string>} task_args The task arguments
  431. * @param [in] {bool} file_pending A boolean indicating whether there are
  432. * files being sent for this task
  433. */
  434. void Client::scheduleTask(Scheduler::Task* task) {
  435. if (m_outbound_task == nullptr) {
  436. m_outbound_task = std::move(task);
  437. if (m_outbound_task->hasFiles()) {
  438. sendFiles(m_outbound_task);
  439. } else {
  440. qDebug() << "Requesting a task to be scheduled";
  441. sendTaskEncoded(m_outbound_task);
  442. }
  443. } else {
  444. m_task_queue.enqueue(task);
  445. }
  446. }
  447. /**
  448. * @brief Client::sendFiles
  449. * @param [in] {QVector<const QByteArray} files The files to be sent
  450. */
  451. void Client::sendFiles(Scheduler::Task* task) {
  452. if (outgoing_files.isEmpty()) {
  453. file_was_sent = false;
  454. for (const auto& file : task->getFiles()) {
  455. outgoing_files.enqueue(std::move(file));
  456. }
  457. std::string send_file_operation = createOperation("FileUpload", {});
  458. sendEncoded(send_file_operation);
  459. } else {
  460. m_task_queue.enqueue(task);
  461. qDebug() << "Still attempting to send a different file";
  462. }
  463. }