client.cpp 16 KB

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